构建工具scons让一切变得简单

你曾经为繁琐的makefile语法纠结过吗?你有想要摆脱VS的束缚,自己构建工程却又苦于无从下手吗?你有为同一个工程要在不同操作系统或不同平台下构建编译环境而苦恼过吗? 本文有我的选择,你的答案。

最近小试了一把用scons来构建编译环境,感觉很不错,跟大家分享一下。
先说明下什么是scons,scons是一个Python写的自动化构建工具,就比如老牌的cmake,或者如果经常跟google一些开源项目打交道的话,那肯定知道新近开始流行的gyp(google内部用的构建工具),scons实现跟它们有类似的功能。而scons又有区别于其他构建工具的特点,不得不承认,这些特点让我喜欢上了这个工具。
先简单总结下吸引我的几点:
1. 自动依赖分析
2. 工具本身由python实现,跨平台
3. 基于MD5识别构建文件的改变,并且可以自定义和扩展
4. 构建文件逻辑用python来写,功能强大,扩展性超强,跨平台
5. 简单易用(半小时内可以学会如何构建中小规模编译环境)
6. 官方提供的文档详细易理解(如果看过google的gyp的文档,那叫一个坑爹)

好了,进入正题,就让我们来循序渐进的来领略下scons的魅力吧。
首先是安装环境,
第一步当然是要保证系统中安装了python,貌似2.6和2.7下scons都没有问题,其他的自己尝试下吧。
第二步,安装scons。windows环境的话直接去官网下载exe直接安装即可。linux Debian系统下则更方便,可以直接用如下命令来安装:sudo apt-get install scons
安装完成后,在命令行验证一下,输入scons -v, 如果没有提示scons命令不存在则说明安装成功。不幸的是,windows下还真提示不存在了,怎么办?其实在python安装路径C:\Python27\Scripts下(我的python安装在C:\Python27)有个scons.bat,以后运行这个命令就可以(/scons.bat -v),为了方便可以把该路径追加到环境变量PATH中。
ok,环境搞定。

接着,一起来细细品味下scons吧。
1. 假设有如下【helloScons.c】文件:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
        printf("Hello, SCons!\n");
        return 0;
} 
用scons怎么编译它呢?首先在helloScons.c相同路径下新建【SConstruct】文件,内容如下:
Program('helloScons.c')

命令行,进入该文件相同路径,运行‘scons’(windows下运行scons.bat)。可以看到如下运行结果:
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
gcc -o helloScons.o -c helloScons.c
gcc -o helloScons helloScons.o
scons: done building targets.
编译完成,得到helloScons可执行文件。
如果我们希望生成其他名字的可执行文件呢?很简单,把【SConstruct】改成如下内容就可:
Program('newName', 'helloScons.c')
 
2. 我们稍微改动下,比如相同路径下再新建个文件extScons.c, extScons.h:
extScons.h
int add(int x, int y);

extScons.c
int add(int x, int y)
{
     return x + y;
}

然后修改下【helloScons.c】:
#include <stdio.h> 
#include <stdlib.h>
#include “extScons.h”
int main(int argc, char* argv[]) 
{ 
        printf("Hello, SCons %d!\n", add(1, 2)); 
        return 0; 
} 

SConstruct】文件怎么改呢?
Program(['helloScons.c', 'extScons.c'])
那如果你整个工程中有上百个.c,难道都要在SConstruct中一个一个写出来吗?当然不需要,下面这样也能达到跟上面一样的功能:
Program(Glob('*.c'))

3. 假设我们再变化一下,在当前路径下创建ext文件夹,把extScons.h和extScons.c移到ext文件夹里,【SConstruct】怎么写:
Program(['helloScons.c', 'ext/extScons.c'])
类似如果希望设置为文件夹下所有文件,也可以写成:
Program([Glob('*.c'), Glob('ext/*.c')])

4. 那么如果要把extScons.c编译成动态链接库.so,怎么做?
【SConstruct】文件如下:
SharedLibrary('ext/extScons.c')
Program(['helloScons.c'], LIBS=['extScons'], LIBPATH='./ext')
解释一下,SharedLibrary()指定把ext/extScons.c编译成动态链接库,如果想编译为静态链接库则使用StaticLibrary()。Program方法中,LIBS指定的是主程序helloScons.c需要使用的动态链接库libextScons.so,LIBPATH则指定的是libextScons.so的路径。
运行scons后,可以看到ext文件夹下生成了libextScons.so,主文件夹下生成了可执行程序helloScons。

5. 如果要把编译和链接两个步骤分开呢?没问题,scons提供了Object方法来编译生成.o(windows下为.obj)文件。
【SConstruct】文件如下:
import os
Object('ext/extScons.c')
Library('ext/extScons.o')
Object('helloScons.c')
Program(['helloScons.o'], LIBS=['extScons'], LIBPATH='./ext')
Object方法用来编译生成.o文件,Library和Program则用来链接.o生成静态链接库和可执行文件。
还可以再改善下如上构建代码:
import os
ext = Object('ext/extScons.c')
Library(ext)
main = Object('helloScons.c')
Program(main, LIBS=['extScons'], LIBPATH='./ext')
Object会返回编译产生的.o文件列表,可以直接把这个返回值传给Library和Program方法。在需要编译许多代码文件时,这点还是很有用的。

6. 如果需要在不同操作系统下编译链接不同的代码文件呢?scons可以很简单就实现。
我们再创建一个文件【helloSconsForWin.c】,作为windows环境下使用的主程序:
#include <stdio.h>
#include <stdlib.h>
#include "ext/extScons.h"
int main(int argc, char* argv[])
{
        printf("Hello, SCons in Windows %d!\n", add(1, 2));
        return 0;
}

同时,假设我们又希望在Linux下以动态链接的形式,而在windows下以静态链接的形式使用extScons【SConstruct】文件如下:
import os
if os.name == "posix":
     SharedLibrary('ext/extScons.c')
     Program(['helloScons.c'], LIBS=['extScons'], LIBPATH='./ext')
elif os.name == "nt":
     StaticLibrary('ext/extScons.c')
     Program(['helloSconsForWin.c'], LIBS=['extScons'], LIBPATH='./ext')
纯粹的python语法,使用了os模块,如果你熟悉python的话,编译和链接的逻辑你可以随心所欲的写。其实这一点是最吸引我的,因为我算是个python的fan,呵呵。

本文其实只是一些关于scons的最基本的说明,在这里我也只是抛砖引玉,scons还有很多很好的特性,大家可以参考官方的文档,如果有什么新体会新发现,也别忘了留言分享下吧。
posted @ 2012-08-01 09:04  MXi4oyu  阅读(638)  评论(0编辑  收藏  举报