自动化构建工具-scons简介
2013-08-16 14:12阅读:
Windows用习惯了的人,在Linux强大的脚本面前,俨然菜鸟一只。工作却偏偏要跟Linux打交道,硬着头皮上吧。最近经常接触的就是用scons对java程序进行编译,做的最多的就是修改scons的配置文件SConstruct和SConscript。scons确实很好用,自动化的构建以及无需指定依赖关系莫过于是它最大的优点了。但是就是这一点,基于Python的SConstruct配置文件的内容就至关重要了。之前做的一个编译就是先对项目中的java文件用env.Java()进行编译,然后用env.Jar将class文件package成jar包。
使用的SConscript文件的python代码大致如下:
env=Environment(JAVASOURCEPATH='src')
srclist=[ 'src/a.java', #source file
list
'src/b.java']
compilelist=env.Java(target='classes',source=srclist)# compile the
source files
packagelist=env.Jar(target='dest.jar',source=compilelist)
env.Default(compilelist)
env.Default(packagelist)
抛砖引玉:
现在有个问题,我要对编译后的class文件增加一行用户信息(bash脚本addline.sh),人后再用env.Jar将修改后的class文件package成jar包。按照scons上面的说明,我们可以使用env.Command()来调用addline.sh对class文件进行修改。所以新的SConscript理论上应该是:
env=Environment(JAVASOURCEPATH='src')
srclist=[ 'src/a.java', #source file
list
'src/b.java']
compilelist=env.Java(target='classes',source=srclist)# compile the
source files
updatelist=env.Command('bash addline.sh compilelist') #assume this
line works
packagelist=env.Jar(target='dest.jar',source=compilelist)
env.Default(compilelist)
env.Default(updatelist)
env.Default(packagelist)
事实上执行scons以后,上面就会出现错误,提示找不到class文件。分析原因,在scons reading
SConscript文件之后,就开始自动检查依赖关系,发现bash命令可以首先执行,所以就先与env.Java()之前执行了env.Command()里面的命令,虽然我们用Default约束的执行的顺序,但是貌似没有起到作用。百思不得其解~最后还是用了一个很笨的方法,来解决的。说出来不怕笑话啊,就是把SConscript文件拆成了两个SConscript1和SConscript2,SConscript1完成env.Java();SConscript2
完成env.Command()和env.Jar()。如果哪位高手知道解决办法,还望指导一二。
下面是我收集的一些scons的相关资料,与大家共享。
一
scons简介
scons
是一个用Python
写的自动化构建工具。形象地说,scons
就是这样一个构建工具:你告诉它要做的任务,以及必要的输入和输出,至于怎么做这个任务,scons
自己去完成。
从构建的角度讲,scons
与GNU make
是同一类的工具。它是一种改进的、跨平台的GNU
make
的替代工具,其集成功能类似于autoconf/automake
。相比较而言,scons
是一个更简便、更可靠、更高效的编译软件。scons
还具有良好的夸平台性,可以运行在 Linux,
AIX, BSD, HP/UX, IRIX, Solaris, Windows, Mac OS X
和 OS/2
上。但用户需要安装python
和scons
之后才能运行。scons
与其它构建工具的一个显著的区别就是其配置文件是python
script
。scons
的构建思想与GNU
的make
是完全不同的,GNU
make
的核心是依赖关系,所以GNU
提供Automaker
来分析依赖性,辅助程序员产生makefile
。而scons
会自己找依赖关系,这个主要是借助于内部的scanner
来扫描有改动的文件,然后才去编译链接生成可执行程序。
二scons使用
2.1 基于python
的配置文件
scons
中可能出现的配置文件:SConstruct,SConstruct,SConstruct,SConscript
。scons
将在当前目录以下次序
SConstruct,SConstruct,SConstruct
来搜索配置文件,从读取的第一个文件中读取相关配置。在配置文件SConstruct
中可以使用函数SConscript()
函数来定附属的配置文件。按惯例,这些附属配置文件被命名为”SConscript”
,当然也可以使用任意其它名字。
scons
通过决定哪个具体的模块必须被rebuild
,并执行相关的命令来进行rebuild
。默认情况下,scons
将在当前目录以以下次序(SConstruct,SConstruct,SConstruct
)来搜索配置文件,从读取的第一个文件中读取相关配置。我们可以通过-f
选项来指定替代的配置文件,具体方法如下:
scons –f configfilename
scons
中的配置文件SConscript
由python
脚本编写,所以我们能使用python
脚本的灵活性来处理复杂的build
。scons
在读取和执行所有SConscript
文件之后,才对目标进行构建,具体流程,我们可以看一下示例:
$ scons foo.out
scons: Reading SConscript files
…
scons: done reading SConscript
files.
scons: Building targets ...
cp foo.in foo.out
scons: done building targets.
2.2 Environment
设定
Scons
需要在一个特定的Enviroment
(环境)中来构建软件,环境中定义了一些构建软件过程中需要的变量和变量的值。但scons
不会自动复制构建目标文件的外部环境,以保证无论何时调用scons
,构建的结果不会出现差异。无论何时创建enviroment
,你可以通过以下步骤从外部环境中复制环境变量PATH
的值到创建的enviroment
import os
env = Environment(ENV = {’PATH’ :
os.environ[’PATH’]})
相似的,可以复制诸如$PATH
,$HOME
,$JAVA_HOME
,$LANG
,$SHELL
,$TERM
等环境变量的值到创建的enviroment
import os
env = Environment(ENV = {’PATH’ :
os.environ[’PATH’],
‘HOME’:os.environ[’HOME’]})
或者你可以复制完整的一个外部enviroment
:
import os
env = Environment(ENV =
os.environ)
如果如上完全拷贝外部环境,我们必须保证外部环境中的环境变量被正确的设置。
2.3 Default
()来显示的指定默认目标
使用命令scons
将会build
当前目录下的所有的目标文件。如果在命令行中没有指定需要build
的目标,我们需要使用函数Default
()来显示的指定默认目标。即使在SConscript
file
中使用Default
指定build
目标,我们也可以在命令行中显示的指定当前目录(.
),来build
所有当前目录下的所有目标,具体命令格式如下
scons .
如果想build
当前目录以外的任何文件,可以使用绝对路径来指定build
目录,具体格式如下
scons /dir/
在windows
下也可以使用盘符,具体如下:
scons c:\ d:\
为了build
特定的目标,我们可以用指定目标为命令行参数,具体如下:
scons foo bar
如果想清除build
过程中产生的中间文件和目标文件,可以使用以下命令进行清除
scons –c .
2.4
示例
a) C
程序编译
这是一个用C
语言编写的著名的'Hello,World!'
程序:
int main()
{
printf('Hello, World!');
}
用SCons
编译它,需要在一个名为SConstruct
的文件中输入如下命令:
Program('hello.c')
这个短小的配置文件给了SCons
两条信息:你想编译什么(一个可执行程序),你编译的输入文件(hello.c
)。Program
是一个编译器方法(builder_method
),一个Python
调用告诉SCons
,你想编译一个可执行程序。
现在运行scons
命令编译这个程序。在Linux
或Unix
系统上,你会看到如下输出:
% scons
scons: Reading SConscript
files...
scons: done reading SConscript
files.
scons: Building targets...
cc -o hello.o -c hello.c
cc -o hello hello.o
scons: done building targets
b)java
程序编译
SCons
同样使得编译Java
也很容易了。不像Program
和Object
两个编译方法,Java
编译方法需要你指定一个目录,这个目录是用来存放编译后的class
文件的,以及一个存放.java
源文件的目录:
Java('classes', 'src')
如果src
目录仅仅包含一个hello.java
文件,那么运行scons
命令的输出会如下所示(在POSIX
系统里):
% scons
scons: Reading SConscript
files...
scons: done reading SConscript
files.
scons: Building targets...
javac -d classes -sourcepath src
src/hello.java
scons: done building targets.
c)
多源文件编译指定
Program('program', ['prog.c', 'file1.c', 'file2.c']) #
如果没有第一个参数,则以第二个参数(这是一个python
list
,用[
]表示)的第一个元素为program
的名字。如果你觉得列表里面每个文件都需要带一个引号太麻烦,可以利用Program('program',
Split('main.c file1.c file2.c')) #
这里的split
函数是返回一个列表也可以这么用来提高可读性
src_files = Split('main.c file1.c file2.c')
#中间多少个空格无所谓
Program('program', src_files)
也可利用Glob
函数获得名字列表,Golb('*.c')
返回规则匹配的string
列表,就是类似上面的'prog.c',
'file1.c', 'file2.c'
。
Program('program', Glob('*.c'))
两个关键字可以直接指明target
和source
,所以在Program
src_files = Split('main.c file1.c
file2.c')
Program(target = 'program', source =
src_files)
src_files = Split('main.c file1.c
file2.c')
Program(source = src_files, target =
'program') #可以调换参数顺序
d)
用Depends
函数显示地的指明依赖性
有时scons
扫描器检查不出一些文件的依赖性,可以利用Depends
函数显示地的指明依赖性:
hello = Program('hello.c')
Depends(hello, 'other_file')
如果想让某个依赖文件改变时不重编,可以用Ignore
函数设置忽略这些依赖性:
hello_obj=Object('hello.c')
hello = Program(hello_obj)
Ignore(hello_obj, 'hello.h')
Scons
没有明显的依赖定义,Scons
会为我们自动扫描依赖。我们只需告诉它构建出一个目标需要什么即可。Scons
检查依赖关系中的文件变化的方法,除了通过时间戳,还可以通过MD5
来判别,你可以通过设置Env
来决定使用哪个。另外更强大的是你也可以自己编写文件更新检查方法放到SConstruct
中被Scons
调用,这些都是高级一些的功能,这里不细说,详情可参见Scons
的doc
。
参考资料:
SCons User Guide 1.2.0
:http://www.scons.org/doc/HTML/scons-user/book1.html
http://blog.chinaunix.net/uid-7321232-id-2645448.html
http://wenku.baidu.com/view/f88d3b1fc5da50e2524d7fd9.html
http://550480286.blog.163.com/blog/static/99093247201077113029413/