读目录(Content)
1.概述
scons是一个Python写的自动化构建工具,和GNU make相比优点明显:
A.移植性:python能运行的地方,就能运行scons
B. 扩展性:理论上scons只是提供了python的类,scons使用者可以在这个类的基础上做所有python能做的事情。比如想把一个已经使用了Makefile大型工程切换到scons,就可以保留原来的Makefile,并用python解析Makefile中的编译选项、源/目标文件等,作为参数传递给scons,完成编译。
C. 智能:Scons继承了autoconf/automake的功能,自动解析系统的include路径、typedef等;“以全局的观点来看所有的依赖关系”
2.scons文件
scons中可能出现的文件:
SConstruct,Sconstruct,sconstruct,SConscript
scons将在当前目录以下次序 SConstruct,Sconstruct,sconstruct 来搜索配置文件,从读取的第一个文件中读取相关配置。
在配置文件SConstruct中可以使用函数SConscript()函数来定附属的配置文件。按惯例,这些附属配置文件被命名为”SConscript”,当然也可以使用任意其它名字。
3.scons的命令行参数
1 scons: 执行SConstruct中脚本
2 scons -c clean
3 scons -Q 只显示编译信息,去除多余的打印信息
4 scons -Q --implicit-cache hello 保存依赖关系
5 --implicit-deps-changed 强制更新依赖关系
6 --implicit-deps-unchanged 强制使用原先的依赖关系,即使已经改变
4.SConstruct提供的方法
4.1 Program:生成可执行文件
1 Program('hello.c') //编译hello.c可执行文件,根据系统自动生成(hello.exe on Windows; hello on POSIX)
2 Program('hello','hello.c') //指定Output文件名(hello.exe on Windows; hello on POSIX)
3 Program(['hello.c', 'file1.c', 'file2.c']) //编译多个文件,Output文件名以第一个文件命名
4 Program(source = "hello.c",target = "hello")
5 Program(target = "hello" , source = "hello.c")
6 Program('hello', Split('hello.c file1.c file2.c')) //编译多个文件
7
8 Program(Glob("*.c"))
9 src = ["hello.c","foo.c"];Program(src)
4.2 Object:生成目标文件
1 Object('hello.c') //编译hello.c目标文件,根据系统自动生成(hello.obj on Windows; hello.o on POSIX)
4.3 Library:生成静态/动态库文件
1 Library('foo', ['f1.c', 'f2.c', 'f3.c']) //编译library
2 SharedLibrary('foo', ['f1.c', 'f2.c', 'f3.c']) //编译 shared library
3 StaticLibrary('bar', ['f4.c', 'f5.c', 'f6.c']) //编译 static library
库的使用:
1 Program('prog.c', LIBS=['foo', 'bar'], LIBPATH='.') //连接库,不需加后缀或是前缀
4.4 SourceSignatures:判断源文件是否修改
1 SourceSignatures('MD5') //根据内容是否改变,默认方式
2 SourceSignatures('timestamp') //根据修改时间
4.5 TargetSignatures:判断目标文件是否改变
1 TargetSignatures('build') //根据编译结果
2 TargetSignatures('content') //根据文件内容,如果只是加了句注释,将不会被重新编译
4.6 Ignore:忽略依赖关系
1 Ignore(hello, 'hello.h') //忽略某个依赖关系
4.7 Depends:明确依赖关系
1 Depends(hello, 'other_file') //明确依赖关系
4.8 SConscript:scons的配置文件
源文件的目录结构如下:
src:
| SConstruct
| test.cpp
| mA(目录):
| SConscript
| func.cpp
其中test.cpp为主文件,中调用func.cpp中定义的函数
SConstruct内容如下:
1 env = Environment()
2 flags = env.ParseFlags(['-pthread -I/usr/include/stlport ',' -L .'])
3 env.MergeFlags(class_flags)
4 subobj = SConscript(['mA/SConscript'])
5 obj = subobj + env.Object(Glob("*.cpp"))
6 env.Program("test",list(obj),LIBS = ['libstlport.a'])
mA/SConscrip如下:
1 obj = Object(Glob("*.cpp"))
2 Return("obj")
不出意外的话上边的工程编译可以通过,但是运行的时候会Aborted。因为test.cpp,mA/func.cpp都使用了包含string类型的那个类,但是由于编译环境的不同,test.cpp认为string变量的大小是24字节, mA/func.cpp认为string变量的大小是4个字节(libstlport.a捣的鬼)。
解决问题的办法就是环境变量输出,修改SConstruct和mA/SConscript如下:
SConstruct:
1 env = Environment()
2 flags = env.ParseFlags(['-pthread -I/usr/include/stlport ',' -L .'])
3 env.MergeFlags(class_flags)
4 Export('env')
5 subobj = SConscript(['mA/SConscript'],exports = 'env')
6 obj = subobj + env.Object(Glob("*.cpp"))
7 env.Program("test",list(obj),LIBS = ['libstlport.a'])
mA/SConscript:
1 Import('env')
2 obj = env.Object(Glob("*.cpp"))
3 Return("obj")
按照惯例,我们先来一个"Hello, World!"的例子,在你的测试目录下,编写一个HelloWorld.c
/* HelloWorld.c */
#include <stdio.h>
int main(int argc, char* argv[])
{
printf("Hello, world!\n");
return 0;
}
在同一级目录下,建立一个新文件SConstruct,编辑该文件,输入内容:
Program('HelloWorld.c')
在命令行下执行scons,一个名为HelloWorld.exe的可执行文件(在Unix下可执行文件为HelloWorld)被编译链接成功。第一次上手成功会给使用者带来莫大的成就感,提高该使用者继续发掘该工具的可能性。
SConstruct 是个什么文件?SConstruct之于Scons就好比Makefile之于Make;它是Scons的输入,SConstruct中的内容采用的是 Python的语法,而Python的语法比较简单,这样很容易被接受,而Program则只是一个方法调用。 Program('HelloWorld.c')意味着告诉Scons我要将HelloWorld.c编译成一个名为‘HelloWorld.exe'的 可执行文件,当然了Scons会自动分析HelloWorld.c,自动得出目标程序名字。
我们日常工作构建代码的类型不外乎如下几种: 简单一点的包括编译object文件、构建静态库、构建动态链接库和构建可执行程序;复杂的则是要对一个拥有众多目录和几十万、上百万行代码的项目进行整 体体系构建,而复杂的构建也是由一系列的简单构建组合而成的,我们先说说简单类构建。
HelloWorld例子只是一个最简单的由单个源文件构建程序的例子,现实中我们构建可执行程序可能依赖的不止是一个文件,可能还有头文件或链接其他第三方库;下面这个SConstruct文件中的语句就是一个稍微复杂些的例子:
Program(target = 'test', source = ['main.c', 'file1.c', 'file2.c'], LIBS = ['lib1', 'lib2'], LIBPATH = ['lib1/lib', 'lib2/lib'], CPPPATH = ['include', '/lib1/include', 'lib2/include'], CCFLAGS='-D_DEBUG')
这个例子中具备我们常用 的诸多元素,这些参数中:'test'是构建后的程序名,source是一个源文件数组,LIBPATH则是要链接库的目录数组,LIBS是要链接的具体 的库文件的名字。CPPPATH则是-I的替代品,是头文件所在目录的数组,CCFLAGS则是负责传递编译器的编译选项参数。
通过这些Keyword Arguments,Scons可以在用户和编译器之间传递信息,并控制编译器完成构建。同样的,编译目标文件,构建静态库、动态库可以由下面的一些builder来完成。
Library('foo', ['f1.c', 'f2.c', 'f3.c']) #生成名为foo的静态库,在Windows上是foo.lib,在unix上为libfoo.a
<=> StaticLibrary('foo', ['f1.c', 'f2.c', 'f3.c']) #生成名为foo的静态库,在Windows上是foo.lib,在unix上为libfoo.a
SharedLibrary('foo', ['f1.c', 'f2.c', 'f3.c']) #生成名为foo的动态库,在Windows上是foo.dll,在unix上为libfoo.so
Object('add.c') #生成名为add的目标文件,在Windows上是add.obj,在unix上为add.o