请教Scons python 编译问题

请教Scons python 编译问题,第1张

一、概述

scons是一个Python写的自动化构建工具,和GNU make相比优点明显:

1、 移植性:python能运行的地方,就能运行scons

2、 扩展性:理论上scons只是提供了python的类,scons使用者可以在这个类的基础上做所有python能做的事情。比如想把一个已经使用了Makefile大型工程切换到scons,就可以保留原来的Makefile,并用python解析Makefile中的编译选项、源/目标文件等,作为参数传递给scons,完成编译。

3、 智能:Scons继承了autoconf/automake的功能,自动解析系统的include路径、typedef等;“以全局的观点来看所有的依赖关系

二、scons文件

scons中可能出现的文件:

SConstruct,Sconstruct,sconstruct,SConscript

scons将在当前目录以下次序 SConstruct,Sconstruct,sconstruct 来搜索配置文件,从读取的第一个文件中读取相关配置。

在配置文件SConstruct中可以使用函数SConscript()函数来定附属的配置文件。按惯例,这些附属配置文件被命名为”SConscript”,当然也可以使用任意其它名字。

三、scons的命令行参数

scons: 执行SConstruct中脚本

scons -c   clean

scons -Q  只显示编译信息,去除多余的打印信息

scons -Q   --implicit-cache hello 保存依赖关系

--implicit-deps-changed   强制更新依赖关系

--implicit-deps-unchanged  强制使用原先的依赖关系,即使已经改变

四、SConstruct提供的方法

1、Program:生成可执行文件

Program('hello.c')  编译hello.c可执行文件,根据系统自动生成(hello.exe on Windowshello on POSIX)

Program('hello','hello.c') 指定Output文件名(hello.exe on Windowshello on POSIX)

Program(['hello.c', 'file1.c', 'file2.c']) 编译多个文件,Output文件名以第一个文件命名

Program(source = "hello.c",target = "hello")

Program(target = "hello" , source = "hello.c")

Program('hello', Split('hello.c file1.c file2.c')) 编译多个文件

Program(Glob("*.c"))

src = ["hello.c","foo.c"]Program(src)

2、Object:生成目标文件

Object('hello.c') 编译hello.c目标文件,根据系统自动生成(hello.obj on Windowshello.o on POSIX)

3、Library:生成静态/动态库文件

Library('foo', ['f1.c', 'f2.c', 'f3.c']) 编译library

SharedLibrary('foo', ['f1.c', 'f2.c', 'f3.c']) 编译 shared library

StaticLibrary('bar', ['f4.c', 'f5.c', 'f6.c']) 编译 static library

库的使用:

Program('prog.c', LIBS=['foo', 'bar'], LIBPATH='.') 连接库,不需加后缀或是前缀

4、SourceSignatures:判断源文件是否修改

SourceSignatures('MD5')     根据内容是否改变,默认方式

SourceSignatures('timestamp') 根据修改时间

5、TargetSignatures:判断目标文件是否改变

TargetSignatures('build')   根据编译结果

TargetSignatures('content')  根据文件内容,如果只是加了句注释,将不会被重新编译

6、Ignore:忽略依赖关系

Ignore(hello, 'hello.h')  忽略某个依赖关系

7、Depends:明确依赖关系

Depends(hello, 'other_file') 明确依赖关系

8、SConscript:scons的配置文件。

源文件的目录结构如下:

src:

|    SConstruct

|    test.cpp

|    mA(目录):

|     SConscript

|     func.cpp

其中test.cpp为主文件,中调用func.cpp中定义的函数

SConstruct内容如下:

[cpp] view plaincopy

subobj = SConscript(['mA/SConscript'])

obj = subobj + Object(Glob("*.cpp"))

Program("test",list(obj))

  

    SConscript内容 :

       

[cpp] view plaincopy

obj = Object(Glob("*.cpp"))

Return("obj")

    

    上例中,在主目录中执行 scons就可以编译整个"工程"。SConstruct编译主目录中的test.cpp,并通过SConscript编译mA目录下的源文件,并最终生成可执行文件;SConscript用于编译mA中的func.cpp并把生成的func.o传递给主目录的SConstruct。

10.env:环境变量

环境变量用于设置在编译过程中的各种参数,可以用下面的SConstruct打印环境变量的所有信息(实际上env就是一个python字典)

可以使用如下的SConstruct查看环境变量的内容:

[cpp] view plaincopy

env = Environment()

dict = env.Dictionary()

keys = dict.keys()

keys.sort()

for key in keys:

print "construction variable = '%s', value = '%s'" % (key, dict[key])

      

     环境变量的使用:

         env = Environment()   #创建默认的环境变量,默认scons会按编译器的默认选项来进行编译

         import os

         env = Environment(CC = 'gcc',CCFLAGS = '-O2') #创建并设置环境 变量

         env.Program('foo.c')

环境变量的复制:

env = Environment(CC = 'gcc')

opt = env.Clone(CCFLAGS = '-O2')

dbg = env.Clone(CCFLAGS = '-g')

环境变量的替换:

env = Environment(CCFLAGS = '-DDEFINE1')

env.Replace(CCFLAGS = '-DDEFINE2')

env.Program('foo.c')

环境变量的输入输出:用于统一多目录源文件的编译选项,如:

src:

|    SConstruct

|    libstlport.a

|    test.cpp

|     include(目录):

|    foo.h

|    mA(目录):

|    SConscript

|    func.cpp

test.cpp和mA/func.cpp都引用了include/foo.h,test.cpp调用了mA/func.cpp的功能函数,其中include/foo.h中定义了一个包含string类型的类。

SConstruct如下:

[cpp] view plaincopy

env = Environment()

flags = env.ParseFlags(['-pthread -I/usr/include/stlport ',' -L .'])

env.MergeFlags(class_flags)

subobj = SConscript(['mA/SConscript'])

obj = subobj + env.Object(Glob("*.cpp"))

env.Program("test",list(obj),LIBS = ['libstlport.a'])

mA/SConscrip如下:

[cpp] view plaincopy

obj = Object(Glob("*.cpp"))

Return("obj")

  

不出意外的话上边的工程编译可以通过,但是运行的时候会Aborted。因为test.cpp,mA/func.cpp都使用了包含string类型的那个类,但是由于编译环境的不同,test.cpp认为string变量的大小是24字节, mA/func.cpp认为string变量的大小是4个字节(libstlport.a捣的鬼)。

解决问题的办法就是环境变量输出,修改SConstruct和mA/SConscript如下:

SConstruct:

[cpp] view plaincopy

env = Environment()

flags = env.ParseFlags(['-pthread -I/usr/include/stlport ',' -L .'])

env.MergeFlags(class_flags)

Export('env')

subobj = SConscript(['mA/SConscript'],exports = 'env')

obj = subobj + env.Object(Glob("*.cpp"))

env.Program("test",list(obj),LIBS = ['libstlport.a'])

 

    mA/SConscript:

scons由Sconstruct 作为入口,控制如何进行编译 *** 作。Sconstruct 本身是一个python文件,故需要遵循python的语法,以及能使用一些python的方法。(如我们可以用print 来debug)

这有一段很简单的hello.cpp

以及一个很简单的Sconstruct

Program是Scons中的一个编译方法(builder_method), 告诉Scons 我们想要把hello.cpp 编译成一个可执行文件。

保证Sconstruct 和hello.cpp 在同一个文件夹下,执行scons,就可以完成编译,生成可执行文件hello。

可以看到,我们只指定了一个cpp文件,scons会默认给可执行文件一个名字,以及完成.o文件的生成,非常智能。当然,我们也能指定输出文件的名字, Program("target_name", hello.cpp")

另外,除了Program ,还有其他很多builder_method, 如 Object , SharedLibrary , StaticLibrary , LoadableModule , StaticObject , CFile

编译多个文件非常简单

把Program改为 Library (或者StaticLibrary,这两者是一样的)即可。

这样就能得到一个静态库了。 如果如要一个动态库,则可以使用SharedLibrary。

上边我们学会如何编译一个库了,那么如何链接呢?也很简单,加个参数即可

Program 可以理解为python 的一个方法,很多参数都有默认值,我们要做的只用覆盖其默认值即可。如上,我们指定引入LIBS。同样的,LIBS参数也可以是个str,LIBPATH也可以是个list,放上所有要查找的路径,如['/usr/lib', '/usr/local/lib'],这里就不赘述了。

SCons很智能,只会编译需要编译的内容。比如我刚执行完scons,再次执行,则会提示 scons: . is up to date. 。 那么他是如何做到的呢?也不复杂,依赖一个Decider的方法,以及一个 .sconsign.dblite 文件。

默认情况下,如果文件的md5值改变了,才会重新编译。每次编译,SCons都会把md5存起来,再次执行时,如果md5没变,则不需要rebuild。

如果我们不希望使用md5,而是使用文件修改时间呢?很简单,增加 Decider('timestamp-newer') (默认情况下为md5)。也可以使用 'MD5-timestamp ,则他们一起变了才会rebuild。

我们前面也说到,Decider是一个方法,那很显然,我们也可以自己写个decider方法,详细的可以看 scons-user.pdf 6.1.5,这里不细写了。。

env分为三种

比如我们想通过一个debug字段来控制是否开启debug模式,怎么做呢?可以通过ARGUMENTS

scons debug=1 就可以了。

上面只是我在看的时候做的一个小结,详细的可以看(文档)[ https://scons.org/doc/production/PDF/scons-user.pdf] ,很多东西都没写,在日常工作中可以一点一点去体会。


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/tougao/12014864.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-20
下一篇 2023-05-20

发表评论

登录后才能评论

评论列表(0条)

保存