scons 语法
常用命令、函数与定义
- scons -c 可以清除生成的临时文件和目标文件,相当于make clean
- sdons -Q 将产生更少的输出信息
- defines= [‘MAIN_DEFINE’] 预定义宏MAIN_DEFINE
- env.Append(CCCOMSTR=“CC $SOURCES”)定义了编译时的显示格式,若不定义则编译选项非常长,冗余信息非常多,不利于查找有用的错误和警告。还有LINKCOMSTR = “Linking $TARGET”
- GetCurrentDir() 获得当前所在目录路径RTThread函数
- os.listdir()方法用于返回指定的文件夹包含的文件或文件夹的名字的列表。它不包括 . 和 … 即使它在文件夹中。
- os.path.join()拼接路径
- os.path.abspath(path)获取绝对路径,包括文件名
- os.getcwd()返回当前的工作目录
- os.path.dirname()获取当前路径,不包括文件名
参数 | 描述 |
---|---|
CCFLAGS | C 源文件编译参数 |
CCFLAGS | 头文件路径 |
CPPDEFINES | 链接时参数 |
LIBRARY | 包含此参数,则会将组件生成的目标文件打包成库文件 |
#使用示例
src = Glob('src/*.c')
paths = ['inc']
defines = ['MODULE1_DEF']
ccflags = []
env = Environment ()
env.Append(CPPPATH=paths)
env.Append(CPPDEFINES=defines)
env.Append(CCCOMSTR="CC $SOURCES")
obj =env.Object(src)
Return('obj')
- SConscript文件执行函数
SConscript(dirs,variant_dir,duplicate)
参数 | 描述 |
---|---|
dirs | SConscript 文件路径 |
variant_dir | 指定生成的目标文件的存放路径 |
duiplicate | 设定是否拷贝或链接源文件到 variant_dir |
#使用示例
src = Glob('*.c')
src += Glob('driver/src/*.c')
paths = ['driver/inc']
paths += ['../module1/inc']
paths += ['../module2/inc']
defines = ['MAIN_DEF']
ccflags = []
env = Environment ()
env.Append(CPPPATH=paths)
env.Append(CPPDEFINES=defines)
env.Append(CCCOMSTR="CC $SOURCES")
env.Append(LINKCOMSTR="LINK $TARGET")
objs = SConscript('../module1/SConscript',variant_dir='build/module1', duplicate=0)
objs += SConscript('../module2/SConscript',variant_dir='build/module2', duplicate=0)
objs += env.Object(src)
env.Program(target = 'module_test',source=objs)
#实现源文件和目标文件的目录分离在variant_dir='build/module2'处定义,duplicate=0选项是防止源文件被多余地复制到build文件夹下。
#模块编译的最大好处是,SConscript脚本可与模块的源代码连同一起不作任可更改用在别的地方,而不需要重写SConscript脚本
#除非显式的import 和export,那么各个SConscript、SConstruct脚本的变量互不干扰,可以同名。
单文件编译
- 在源文件目录下新建SConstruct文件(可以不区分大小写)
- Program(‘hello.c’),将hello.c编译成可执行程序,或者Program(‘newhello’,‘hello.c’),生成newhello的可执行文件
- Object(‘hello.c’),告诉编译器从指定文件编译出目标文件
- scons -c 清除scons编译的目标文件
SConstruct文件实际上是一个python脚本文件,可以使用python的注释 #
条用scons的函数实际上是要告诉scons我想要编译这个程序,并不是立即去编译。
- Default(target) 指定默认的目标文件 target是输出文件
Default(targets)
env.Default(targets)
Default('foo', 'bar', 'baz')
env.Default(['a', 'b', 'c'])
hello = env.Program('hello', 'hello.c')
env.Default(hello)
Default(None) #清除所有默认的target
编译多个源文件
Program('program',['prog.cpp','file1.cpp','file2.cpp'])
#你可以使用Glob函数,定义一个匹配规则来指定源文件列表,比如*,'?'以及[abc]等标准的shell模式
Program('program', Glob('*.cpp'))
#或者
Program('program', Split('main.cpp file1.cpp file2.cpp'))
#或者
src_files=Split('main.cpp file1.cpp file2.cpp')
Program('program', src_files)
#或者
src_files=Split('main.cpp file1.cpp file2.cpp')
Program(target='program', source=src_files)
#或者
src_files=Split('main.cpp file1.cpp file2.cpp')
Program(source=src_files, target='program')
- 如果需要用同一个SConstruct文件编译多个程序,只需要调用Program方法多次:
Program('foo.cpp')
Program('bar', ['bar1.cpp', 'bar2.cpp'])
#注意[],是列表的意思
- 多个程序之间代码复用库示例
common=['common1.c','common2.cpp']
foo_files=['foo.c']+common
bar_files=['bar1.c','bar2.c']+common
Program('foo',foo_files)
Program('bar',bar_files)
编译库文件
- 可以使用Library方法来编译库文件
Library('foo',['f1.c','f2.c','f3.c'])
- 或者使用源文件和目标文件混合使用
Library('foo', ['f1.c', 'f2.o', 'f3.c', 'f4.o'])
- 使用StaticLibrary显示编译静态库
StaticLibrary('foo', ['f1.cpp', 'f2.cpp', 'f3.cpp'])
编译动态库
SharedLibrary('foo', ['f1.cpp', 'f2.cpp', 'f3.cpp'])
链接库文件
- 使用 L I B S 变 量 指 定 库 文 件 , 使 用 LIBS变量指定库文件,使用 LIBS变量指定库文件,使用LIBPATH指定库文件的目录
Library('foo',['f1.c','f2.c','f3.c'])
Program('prog',LIBS=['foo','bar'],LIBPATH='.')
- 默认情况下,链接器只会在系统默认的库目录中寻找库文件,scons也会去 L I B P A T H 指 定 的 目 录 寻 找 库 文 件 , LIBPATH指定的目录寻找库文件, LIBPATH指定的目录寻找库文件,LIBPATH由一个目录列表组成
结点对象
- 编译方法返回目标节点列表
所有编译方法会返回一个节点对象列表,这些节点对象标识了那些将要被编译的目标文件,这些返回出来的节点可以作为参数传递给其他的编译方法。如下所示:
hello_list = Object('hello.cpp', CCFLAGS='-DHELLO')
goodbye_list = Object('goodbye.c', CCFLAGS='-DGOODBYE')
Program(hello_list + goodbye_list)
- scons的File和Dir函数分别返回一个文件和目录节点,只有显示构造节点类型传递给编译方法或其他函数时才需要手动调用这两个函数
hello=File(hello.c)
Program(hello_c)
- Entry(‘xx’)返回一个节点可以表示文件或一个目录
- 将一个节点的文件名当做一个字符串
如果你不是想打印文件名,而是做一些其他的事情,你可以使用内置的Python的str函数。例如,你想使用Python的os.path.exists判断一个文件是否存在
import os.path
program_list=Program('hello.c')
program_name=str(program_list[0])
if not os.path.exists(program_name):
print program_name,"does not exist!"
- GetBuildPath 从一个节点或字符串中获得路径
路径可以是文件或目录,不一定存在
env=Environment(VAR="value")
n=File("foo.cpp")
print env.GetBuildPath([n, "sub/dir/$VAR"])
#输出:
$ scons -Q
['foo.cpp', 'sub/dir/value']
scons: . is up to date.
依赖性
- 隐式依赖 $CPPPATH变量
Program('hello.c',CPPPATH='.')#告诉scons去当前目录查看被c源文件包含的文件
- $CPPPATH也可以是一个目录
Program('hello.cpp', CPPPATH=['include', '/home/project/inc'])
环境
External Environment(外部环境):
外部环境指的是在用户运行SCons的时候,用户环境中的变量的集合。这些变量在SConscript文件中通过Python的os.environ字典可以获得。
Construction Environment(构造环境):
一个构造环境是在一个SConscript文件中创建的一个唯一的对象,这个对象包含了一些值可以影响SCons编译一个目标的时候做什么动作,以及决定从那一个源中编译出目标文件。SCons一个强大的功能就是可以创建多个构造环境,包括从一个存在的构造环境中克隆一个新的自定义的构造环境。
Execution Environment(执行环境):
一个执行环境是SCons在执行一个外部命令编译一个或多个目标文件时设置的一些值。这和外部环境是不同的。
与Make不同,SCons不会自动在不同的环境之间拷贝或导入值。这是一个刻意的设计选择,保证了不管用户外部环境的值是怎么样的,编译总是可以重复的。这会避免编译中的一些问题,比如因为一个自定义的设置使得使用了一个不同的编译器或编译选项,开发者的本地代码编译成功,但是checked-in后编译不成功,因为使用了不同的环境变量设置。
- 使用外部环境的sconscript文件需要增加import os语句
创建一个构造环境:Environment函数
默认情况下,SCons基于你系统中工具的一个变量集合来初始化每一个新的构造环境。当你初始化一个构造环境时,你可以设置环境的构造变量来控制一个是如何编译的。例如:
import os
env=Environment(CC='gcc', CCFLAGS='-O2')
env.Program('foo.c')
#或者
env=Environment(CXX='/usr/local/bin/g++', CXXFLAGS='-02')
env.Program('foo.cpp')
- 控制默认构造环境的设置,使用DefaultEnvironment函数:
DefaultEnvironment(CC='/usr/local/bin/gcc')
#或者
env=DefaultEnvironment()
env['CC']='/usr/local/bin/gcc'
- 多个构造环境
opt=Environment(CCFLAGS='-O2')
dbg=Environment(CCFLAGS='-g')
opt.Program('foo','foo.c')
dbg.Program('bar','bar.c')
- 拷贝构造环境:Clone方法
env=Environment(CC='gcc')
opt=env.Clone(CCFLAGS='-O2')
dbg=env.Clone(CCFLAGS='-g')
env.Program('foo','foo.c')
o=opt.Object('foo-opt','foo.c')
opt.Program(o)
d=dbg.Object('foo-dbg','foo.c')
dbg.Program(d)
- 替换值:Replace方法
#可以使用Replace方法替换已经存在的构造变量:
env=Environment(CCFLAGS='-DDEFINE1');
env.Replace(CCFLAGS='-DDEFINE2');
env.Program('foo.c')
#也可以安全地针对那些不存在的构造变量调用Replace方法:
env=Environment()
env.Replace(NEW_VARIABLE='xyzzy')
print "NEW_VARIABLE = ", env['NEW_VARIABLE']
- SetDefault方法
env.SetDefault(SPECIAL_FLAG='-extra-option')
- 追加到值的末尾:Append方法
你可以追加一个值到一个已经存在的构造变量,使用Append方法:
env=Environment(CCFLAGS=['-DMY_VALUE'])
env.Append(CCFLAGS=['-DLAST'])
env.Program('foo.c')
- 追加唯一的值:AppendUnique方法
有时候仅仅只有在已经存在的构造变量没有包含某个值的时候,才会增加这个新值。可以使用AppendUnique方法:
env.AppendUnique(CCFLAGS=['-g'])
上面的例子,仅仅只有在$CCFLAGS没有包含-g值得时候才会增加-g。
- 在值的开始位置追加值:Prepend方法
对于一个存在的构造变量,你可以使用Prepend方法追加一个值到它的值的开始位置。
env=Environment(CCFLAGS=['-DMY_VALUE'])
env.Prepend(CCFLAGS=['-DFIRST'])
env.Program('foo.c')
% scons -Q
cc -o foo.o -c -DFIRST -DMY_VALUE foo.c
cc -o foo foo.o
如果构造变量不存在,Prepend方法会创建它。
- 在前面追加唯一值:PrependUnique方法
仅仅在一个构造变量存在的值中没有包含将要增加的值的时候,这个值才被追加到前面,可以使用PrependUnique方法;
env.PrependUnique(CCFLAGS=['-g'])
控制命令的执行环境
当SCons编译一个目标文件的时候,它不会使用你用来执行SCons的同样的外部环境来执行一些命令。它会使用 E N V 构 造 变 量 作 为 外 部 环 境 来 执 行 命 令 。 这 个 行 为 最 重 要 的 体 现 就 是 P A T H 环 境 变 量 , 它 决 定 了 操 作 系 统 将 去 哪 里 查 找 命 令 和 工 具 , 与 你 调 用 S C o n s 使 用 的 外 部 环 境 的 不 一 样 。 这 就 意 味 着 S C o n s 将 不 能 找 到 你 在 命 令 行 里 执 行 的 所 有 工 具 。 P A T H 环 境 变 量 的 默 认 值 是 / u s r / l o c a l / b i n : / b i n : / u s r / b i n 。 如 果 你 想 执 行 任 何 命 令 不 在 这 些 默 认 地 方 , 你 需 要 在 你 的 构 造 环 境 中 的 ENV构造变量作为外部环境来执行命令。 这个行为最重要的体现就是PATH环境变量,它决定了操作系统将去哪里查找命令和工具,与你调用SCons使用的外部环境的不一样。这就意味着SCons将不能找到你在命令行里执行的所有工具。 PATH环境变量的默认值是/usr/local/bin:/bin:/usr/bin。如果你想执行任何命令不在这些默认地方,你需要在你的构造环境中的 ENV构造变量作为外部环境来执行命令。这个行为最重要的体现就是PATH环境变量,它决定了操作系统将去哪里查找命令和工具,与你调用SCons使用的外部环境的不一样。这就意味着SCons将不能找到你在命令行里执行的所有工具。PATH环境变量的默认值是/usr/local/bin:/bin:/usr/bin。如果你想执行任何命令不在这些默认地方,你需要在你的构造环境中的ENV字典中设置PATH。
最简单的方式就是当你创建构造环境的时候初始化这些值:
path=[’/usr/local/bin’, ‘/usr/bin’]
env=Environment(ENV={‘PATH’:PATH})
以这种方式将一个字典赋值给 E N V 构 造 变 量 完 全 重 置 了 外 部 环 境 , 所 以 当 外 部 命 令 执 行 的 时 候 , 设 置 的 变 量 仅 仅 是 P A T H 的 值 。 如 果 你 想 使 用 ENV构造变量完全重置了外部环境,所以当外部命令执行的时候,设置的变量仅仅是PATH的值。如果你想使用 ENV构造变量完全重置了外部环境,所以当外部命令执行的时候,设置的变量仅仅是PATH的值。如果你想使用ENV中其余的值,仅仅只是设置PATH的值,你可以这样做:
env[‘ENV’][‘PATH’]=[’/usr/local/bin’,’/bin’,’/usr/bin’]
注意SCons允许你用一个字符串定义PATH中的目录,路径用路径分隔符分隔:
env[‘ENV’][‘PATH’]=’/usr/local/bin:/bin:/usr/bin’
- 从外部环境获得PATH值
你可能想获得外部的PATH来作为命令的执行环境。你可以使用来自os.environ的PATH值来初始化PATH变量:
import os
env=Environment(ENV={'PATH':os.environ['PATH']})
你设置可以设置整个的外部环境:
import os
env=Environment(ENV=os.environ)
- 在执行环境里增加PATH的值
常见的一个需求就是增加一个或多个自定义的目录到PATH变量中。
env=Environment(ENV=os.environ)
env.PrependENVPath('PATH','/usr/local/bin')
env.AppendENVPath('LIB','/usr/local/lib')