zoukankan      html  css  js  c++  java
  • [zz]Scons简介

    [zz]Scons简介

    scons是一个Python写的自动化构建工具,从构建这个角度说,它跟GNU make是同一类的工具。它有什么好处呢?在它自己的网站上,当然写了一大堆了,快速、稳定、强大、跨平台、可扩展……。不过我们还是从自己的角度来看看它到底好在哪里。

    scons从目的而言跟GNU make是同一类的工具。但是实际上,它的思想是跟GNU make完全不同的。GNU make的核心是“依赖关系”,我要做的事情,就是告诉系统,一个目标依赖什么东西,并且,当被依赖的东西发生变化时,我要做什么。这样做可以解决相当多的问题,但是也带来了一个最大的问题:我如何判别这个目标依赖什么?

    对于一个两个,甚至十几个文件,我当然还比较容易搞清楚,谁依赖谁。但是当文件有成百上千个时,要分清楚谁依赖谁可就没这么容易了。尤其是 C/C++头文件的依赖,如果手工分析的话,工程量可是不小。为了解决这个问题,GNU又提供了另外一套工具:Automake,使用程序来分析依赖性,然后辅助你产生makefile。

    于是乎,就有人想了,既然如此,我干吗费那劲,用程序分析依赖性,然后生成一个文件,再交给另外一个程序去处理呢?既然依赖性需要用程序来分析,那么就直接交给构建工具本身去做不就好了吗?对的,这是一个非常自然的思路,于是,Java世界有了Ant,而Python世界有了scons。

    scons就是这样一个构建工具:你告诉它要做的任务,以及完成这个任务需要的输入,以及这个任务产生的输出,怎么做这个任务(当然其中就包括依赖性分析),就交给工具本身完成。

    说了这么多,我们来看看一个现实世界的scons是什么样子的。

    我们假设有一个C程序,由三个文件组成:
    1. //-----func.cpp
    2. int add(int x, int y)
    3. {
    4.     return x+y;
    5. }
    6. //----func.h
    7. #ifndef __FUNC_H__
    8. #define __FUNC_H__
    9. extern int add(int x, int y);
    10. #endif
    11. //-----main.cpp
    12. #include 
    13. #include "func.h"
    14. int main()
    15. {
    16.     printf("2+3=%d\n", add(2, 3));
    17. }
    复制代码
    然后我们写一个SConstruct文件(类似于GNU make的Makefile文件,是scons的默认文件名):
    1. Program('add_main', ['main.cpp', 'func.cpp'])
    复制代码
    然后执行scons,将会输出以下信息:
    1. scons: Reading SConscript files ...
    2. scons: done reading SConscript files.
    3. scons: Building targets ...
    4. g++ -o main.o -c main.cpp
    5. g++ -o func.o -c func.cpp
    6. g++ -o add_main main.o func.o
    7. scons: done building targets.
    复制代码
    这时,我们就会得到一个add_main的可执行程序。

    如果执行scons -c,我们会看到:
    1. scons: Reading SConscript files ...
    2. scons: done reading SConscript files.
    3. scons: Cleaning targets ...
    4. Removed main.o
    5. Removed func.o
    6. Removed add_main
    7. scons: done cleaning targets.
    复制代码
    生成的可执行程序连带中间结果都被清除了。

    这里我们可以看到,scons只需要描述任务,并不需要指定依赖关系,甚至我们都没有指出头文件,但是你修改func.h的时候仍然会触发构建。这是因为scons内部有个scanner,可以帮助扫描包含文件的关系。(我们可以编写自己的构建任务,当然也可以编写自己的scanner,有兴趣的可以看帮助,这里就多说了)

    我们还发现,这里我们根本没有指定编译器,也没有指定编译选项,但scons仍然很聪明的选择了g++(这是Linux上的结果,如果是 Windows,默认会选择cl也就是Visual C++),并且给出了正确的编译选项。事实上,这是因为scons内置提供了很多编译器及其对应选项的选择,然后对于不同的平台,会有一个默认项。我们当然也可以自己选择编译环境,比如在Windows下,我同时安装了VC和mingW,但是我想用mingW来编译而不是VC,就可以这样指定:
    1. import os
    2. env = Environment(ENV=os.environ, tools=['mingw'])
    3. env.Program('add_main', ['main.cpp', 'func.cpp'])
    复制代码
    这里出现了一个Environment的概念,Environment可以设置编译的环境。这是一个简介,所以对于它我们就不多说了,感兴趣的可以自行查阅资料。嘿嘿。

    在这里我们看到了一句熟悉的语句:import os。是的,SConstruct文件就是一个非常标准的Python程序,所以,Python能做什么,scons就能做什么。很好很强大阿,哈哈。(这里顺便说一句,我们也可以认为Makefile是shell程序,但是因为shell有平台相关性问题,我们很难写出一个通用平台的 Makefile,但是我们还是写的出一个通用平台的SConstruct的。)

    Program只是scons支持的构建任务其中的一种,用于根据后缀名自动构建C、C++、D和Fortran的可执行程序。scons还支持另外几十种构建目标,这里可以查看支持的列表。如果这里找不到的,还可以自己编写Builder和Scanner。

    接下来我想给出一个一直提但是一直没有给出结果的东西,就是对C++程序的单元测试。

    还是以刚刚那个小例子为例。我们对add函数作一个单元测试。我们知道,单元测试不是程序的一部分,所以需要一个独立的main函数。而被测试的单元是一样的。

    现在假设我们有一个测试函数:(用的是boost的test库,这个库的使用方法不再赘述)
    1. //--------test_main.cpp
    2. #include "func.h"
    3. #define BOOST_TEST_DYN_LINK
    4. #define BOOST_TEST_MAIN
    5. #include 
    6. BOOST_AUTO_TEST_CASE( add_test )
    7. {
    8.     BOOST_CHECK( add(2, 2) == 4 );
    9. }
    复制代码
    然后我们写一个SConstruct:
    1. #所有的需要测试的单元文件(去除两个主文件)
    2. import glob
    3. obj_files = glob.glob('*.cpp')
    4. obj_files.remove('main.cpp')
    5. obj_files.remove('test_main.cpp')
    6. common = Object(obj_files)
    7. Program('add_main', ['main.cpp'] + common)
    8. Program('unittest', ['test_main.cpp'] + common, LIBPATH='/usr/lib', LIBS=['libboost_unit_test_framework'] )
    9. Alias('test', 'unittest')
    10. Default('add_main')
    复制代码
    这里出现了几个新玩意儿,一个是Object,其实也很好理解,Object就是将指定的文件编译成目标文件(.o或者.obj),然后我们用了2 个Program,表示要生成两个可执行文件。在生成的时候,我们将通用的common附加到构建输入中。另一个是Default,这是表示默认的构建。当我们输入scons时,将构建add_main,而我们输入scons unittest时,则构建unittest。但是输入unittest感觉不太方便,我们想输入scons test来编译,但希望输出的文件名仍然是unittest,于是我们增加了Alias,将unittest取了一个别名叫test,这时,我们输入 scons test,仍然会构建unittest。

    我们注意到构建unittest时,使用了附加的信息,比如额外的库、额外的路径等等。还有为了方便起见,我们使用了Python标准库的glob函数展开文件通配符。

    从这个例子我们大约可以感受到scons的强大威力了。至于进一步的深入,就看各位自己的了。
  • 相关阅读:
    linux下小知识点积累
    马斯洛需求层次理论
    tar命令的小经验
    shell 和c语言的区别
    使用vue实现的品牌列表简单小例子
    vue的基本代码以及常见指令
    MVC和MVVM
    CSS3幽灵
    Web版App,原生App,混合App的区别以及优缺点
    常见的sql操作
  • 原文地址:https://www.cnblogs.com/changping/p/2030785.html
Copyright © 2011-2022 走看看