Chinaunix首页 | 论坛 | 博客

apt

  • 博客访问: 391868
  • 博文数量: 121
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 600
  • 用 户 组: 普通用户
  • 注册时间: 2015-04-10 15:52
文章分类

全部博文(121)

文章存档

2017年(2)

2016年(23)

2015年(96)

我的朋友

分类: Python/Ruby

2015-07-24 15:51:34

今天移植zxing到开发板,发现zxing的编译是通过scons编译的,特此学习一下。这篇是在别人基础上修改的,原文网址是http://blog.csdn.net/sealyao/article/details/6402257,加以整理,又加上了一点自己的东西。

一、概述

   scons是一个Python写的自动化构建工具,和GNU make相比优点明显:
    1、 移植性:python能运行的地方,就能运行scons。
    3、 智能:Scons继承了autoconf/automake的功能,自动解析系统的include路径、typedef等;“以全局的观点来看所有的依赖关系”

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

 

二、scons文件

    scons中可能出现的文件: SConstruct/Sconstruct/sconstruct和 SConscript
    这个文件是scons工具必备的,是编译的配置文件,相当于make中的makefile文件。  

    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:生成可执行文件
            (1)、指定目标文件的名字

当你调用Program编译方法的的时候,它编译出来的程序名字是和源文件名是一样的。下面的从hello.c源文件编译一个可执行程序的调用将会在POSIX系统里编译出一个名为hello的可执行程序,在windows系统里会编译出一个名为hello.exe的可执行程序。
Program('hello.c')
如果你想编译出来的程序的名字与源文件名字不一样,你只需要在源文件名的左边声明一个目标文件的名字就可以了:
Program('new_hello','hello.c')
现在在POSIX系统里运行scons,将会编译出一个名为new_hello的可执行程序:
% scons -Q
cc -o hello.o -c hello.c
cc -o new_hello hello.o

    (2)、编译多个源文件
通常情况下,你需要使用多个输入源文件编译一个程序。在SCons里,只需要就多个源文件放到一个Python列表中就行了,如下所示:
Program(['prog.c','file1.c','file2.c'])
运行scons编译:
% scons -Q
cc -o file1.o -c file1.c
cc -o file2.o -c file2.c
cc -o prog.o -c prog.c
cc -o prog prog.o file1.o file2.o
注意到SCons根据源文件列表中的第一个源文件来决定输出程序的名字。如果你想指定一个不同的程序名称,你可以在源文件列表的右边指定程序名,如下所示指定输出程序名为program:
Program('program',['prog.c','file1.c','file2.c'])

    (3)、使用Glob指定文件列表
你可以使用Glob函数,定义一个匹配规则来指定源文件列表,比如*,?以及[abc]等标准的shell模式。如下所示:
Program('program', Glob('*.c')) ,指定文件列表,编译该目录下所有的c文件。

   (4)、指定单个文件以及文件列表
有两种方式为一个程序指定源文件,一个是文件列表,使用方括号:
Program('hello', ['file1.c', 'file2.c'])
一个是单个文件:
Program('hello', 'hello.c')
也可以把单个文件放到一个列表中,
Program('hello', ['hello.c'])
对于单个文件,SCons函数支持两种方式。实际上,在内部,SCons把所有的输入都是看成列表的,只是在单个文件的时候,允许我们省略方括号
  (5)、使文件列表更易读
为了更容易处理文件名长列表,SCons提供了一个Split函数这个Split函数可以将一个用引号引起来,并且以空格或其他空白字符分隔开的字符串分割成一个文件名列表,示例如下:
Program('program', Split('main.c  file1.c  file2.c'))
或者
src_files=Split('main.c  file1.c  file2.c')
Program('program', src_files)
同时,Split允许我们创建一个文件列表跨跃多行,示例如下:
src_files=Split("""main.c
                         file1c
                         file2.c""")
Program('program', src_files)  ,可以理解为定义变量,并且给变量赋值。

  (6)、关键字参数
SCons允许使用Python关键字参数来标识输出文件和输入文件。输出文件是target,输入文件是source,示例如下:
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')

  (7)、编译多个程序
如果需要用同一个SConstruct文件编译多个文件,只需要调用Program方法多次:
Program('foo.c')
Program('bar', ['bar1.c', 'bar2.c'])

  (8)、在多个程序之间共享源文件
在多个程序之间共享源文件是很常见的代码重用方法。一种方式就是利用公共的源文件创建一个库文件,然后其他的程序可以链接这个库文件。
另一个更直接,但是不够便利的方式就是在每个程序的源文件列表中包含公共的文件,示例如下:
Program(Split('foo.c  common1.c  common2.c'))
Program('bar', Split('bar1.c  bar2.c  common1.c  common2.c'))
如果程序之间共享的源文件过多,可以简化:
common=['common1.c', 'common2.c']
foo_files=['foo.c'] + common
bar_files=['bar1.c', 'bar2.c'] + common
Program('foo', foo_files)
Program('bar', bar_files)

  
    2、Object:生成目标文件  
           这个相当于是上个过程的中间环节,只是生成目标文件,并没有进行连接。

           Object('hello.c') 编译hello.c目标文件,根据系统自动生成(hello.obj on Windows; hello.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

        库的使用:需要指明LIBS指明名称,LIBPATH指明路径

        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内容如下:

点击(此处)折叠或打开

  1. subobj = SConscript(['mA/SConscript'])
  2. obj = subobj + Object(Glob("*.cpp"))
  3. Program("test",list(obj))


       SConscript内容 :

点击(此处)折叠或打开

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


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

点击(此处)折叠或打开

  1. env = Environment()
  2. dict = env.Dictionary()
  3. keys = dict.keys()
  4. keys.sort()
  5. for key in keys:
  6.     print "construction variable = '%s', value = '%s'" % (key, dict[key])


    

点击(此处)折叠或打开

  1. 环境变量的使用:
  2.          env = Environment() #创建默认的环境变量,默认scons会按编译器的默认选项来进行编译
  3.          import os
  4.          env = Environment(CC = 'gcc',CCFLAGS = '-O2') #创建并设置环境 变量
  5.          env.Program('foo.c')
  6. 环境变量的复制:
  7.          env = Environment(CC = 'gcc')
  8.          opt = env.Clone(CCFLAGS = '-O2')
  9.          dbg = env.Clone(CCFLAGS = '-g')
  10. 环境变量的替换:
  11.          env = Environment(CCFLAGS = '-DDEFINE1')
  12.          env.Replace(CCFLAGS = '-DDEFINE2')
  13.          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类型的类。     


点击(此处)折叠或打开

  1. SConstruct如下:

  2. env = Environment()
  3. flags = env.ParseFlags(['-pthread -I/usr/include/stlport ',' -L .'])
  4. env.MergeFlags(class_flags)
  5. subobj = SConscript(['mA/SConscript'])
  6. obj = subobj + env.Object(Glob("*.cpp"))
  7. env.Program("test",list(obj),LIBS = ['libstlport.a'])

  8. mA/SConscrip如下:
  9.       

  10. obj = Object(Glob("*.cpp"))
  11. Return("obj")


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

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

点击(此处)折叠或打开

  1. SConstruct:
  2.        
  3. [cpp] view plaincopy
  4. env = Environment()
  5. flags = env.ParseFlags(['-pthread -I/usr/include/stlport ',' -L .'])
  6. env.MergeFlags(class_flags)
  7. Export('env')
  8. subobj = SConscript(['mA/SConscript'],exports = 'env')
  9. obj = subobj + env.Object(Glob("*.cpp"))
  10. env.Program("test",list(obj),LIBS = ['libstlport.a'])
  11.  
  12. mA/SConscript:

  13. Import('env') 
  14. obj = env.Object(Glob("*.cpp"))  
  15. Return("obj") 


     

参考博客:http://blog.csdn.net/andyelvis/article/category/948141

阅读(2292) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~