【c++】Scons|scons对比make

目录

简略说明

什么是Scons

遗留疑问:Scons 和cmake 对比如何?

scons对比make

Scons使用

scons文件和命令

二、scons文件

三、scons的命令行参数

四、SConstruct提供的方法


简略说明

(摘自:https://www.cnblogs.com/m-zhang-yang/p/13228299.html)

Scons命令

scons:进行代码文件编译

scons -Q:进行代码文件编译,不显示Scons内部操作打印的信息,只显示编译信息

scons -c:清除编译中间文件和可执行文件

Scons编译脚本

Scons对应的编译脚本名称为SConstruct,就如同make对应的编译脚本为makefile

SConstruct脚本编写

编译函数

Program():执行编译操作,生成可执行文件

Library():执行编译操作,生成静态库

StaticLibrary():执行编译操作,生成静态库

SharedLibrary():执行编译操作,生成动态库

Environment():编译环境

编译参数

target,生成的执行文件名字

source,编译文件

LIBS,依赖库

LIBPATH,依赖库路径,有环境变量的可不添加,针对用户库或第三方库

CPPPATH,头文件路径

CCFLAGS,编译参数

其他函数

Split():将字符串分隔为列表

Glob('*.cpp'):加入所有文件

编译参数可以以任意顺序加入编译函数,编译函数依据编译参数对代码文件进行编译。

一个简单例子

将文件放在当前目录,头文件放在上级目录。

str = Split('main.cpp display.cpp')
Program('main',str,CPPPATH = '../'i,LIBS = 'pthread')

一个复杂项目的例子

大多项目都可以套用的格式,Program内参数多个文件需要以python列表形式作为入参,这里可以用Split对文件字符串分隔为列表输入。直接用Glob('*.cpp')包含当前目录所有需要编译的cpp文件。


  

TARGETNAME = 'server'

LIB = Split('jsoncpp logger ACE basetool tinyxml pthread dl rt')

FILELIST = Glob('*.cpp')

HEADFILEPATH = Split('../../common/include ../../3rd/ACE_wrappers ../../3rd/include/')

LIB_DIR = Split('../../3rd/lib ../../common/lib')

Program(target = TARGETNAME,source = FILELIST,LIBS = LIB,CPPPATH = HEADFILEPATH,LIBPATH = LIB_DIR)

     src:
     |    SConstruct
     |    libstlport.a
     |    test.cpp
     |     include(目录):
          |    foo.h
     |    mA(目录):
          |    SConscript
          |    func.cpp

什么是Scons

make

make 工具可以增量编译,缩短编译的时间,但 make 规则复杂,即使老鸟也望而生畏。

Scons是它的同类产品,做的事情跟make一样,但更简单,更容易。


Scons

Scons是一个开源、Python编写的自动化构建工具,并且scons是跨平台的,只要scons脚本写的好,可以在Linux和Windows下随意编译。

SCons 的设计目标就是让开发人员更容易、更可靠和更快速的建造软件。

遗留疑问:Scons 和cmake 对比如何?

待研究。

SCons, CMake 算是第三代构建工具的两个代表。

第一代 DSL 型构建语言 Makefile,是描述构建的规则和依赖关系的绝对王者。

第二代 autotools(automake) 系列,几乎是是整个 Linux 的构建工具,能完成对环境依赖项的检测。但是不是能够轻松驾驭的。如果用 -j 用得比较多,肯定会遇到:出现链接错误,再次运行就能正确的情形。(gn 有 link pool 的支持优雅解决了这个问题)。

SCons, CMake 算是第三代构建工具的两个代表了。
链接:https://www.zhihu.com/question/276415476/answer/557715322


《构建工具之shell,configure,make,cmake,scons,xmake简析总结》:

https://blog.csdn.net/shixin_0125/article/details/78424747

scons对比make

与传统的 make 工具比较,SCons 具有以下优点:

 1、 移植性:python能运行的地方,就能运行scons
 2、 扩展性:理论上scons只是提供了python的类,scons使用者可以在这个类的基础上做所有python能做的事情。比如想把一个已经使用了Makefile大型工程切换到scons,就可以保留原来的Makefile,并用python解析Makefile中的编译选项、源/目标文件等,作为参数传递给scons,完成编译。
 3、 智能:Scons继承了autoconf/automake的功能,自动解析系统的include路径、typedef等;“以全局的观点来看所有的依赖关系”

  • 使用 Python 脚本做为配置文件
  • 对于 C,C++ 和 Fortran, 内建支持可靠自动依赖分析 . 不用像 make 工具那样需要 执行"make depends"和"make clean"就可以获得所有的依赖关系。
  • 内建支持 C, C++, D, Java, Fortran, Yacc, Lex, Qt,SWIG 以及 Tex/Latex。 用户还可以根据自己的需要进行扩展以获得对需要编程语言的支持。
  • 支持 make -j 风格的并行建造。相比 make -j, SCons 可以同时运行 N 个工作,而 不用担心代码的层次结构。
  • 使用 Autoconf 风格查找头文件,函数库,函数和类型定义。
  • 良好的夸平台性。SCons 可以运行在 Linux, AIX, BSD, HP/UX, IRIX, Solaris, Windows, Mac OS X 和 OS/2 上。
     

Scons使用

安装

在 SCons 的官方网站上可以查每个平台的具体安装方法。

Win平台的下载和安装就不说了。

Linux下

方法1:

pip3  install scons

方法2:

下载tar包,执行以下命令即可:

tar -zxf scons-2.0.1.tar.gz
cd scons-2.0.1 
sudo python setup.py install

对于 Linux 来说,scons 会默认安装到 /usr/loca/bin 目录下,而在 Windows 平台上,则会被安装到 C:\Python25\Scripts 下。

使用

这里通过一个简单的示例来说明。

一个hello.c源文件,一个SConstruct文件,后面再解释。

SConstruct

Program("hello.c")

 hello.c

#include <stdio.h>

int main(void)
{
    printf("hello, world!\n");

    return 0;
}

运行Scons,得到如下:

$ scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
gcc -o hello.o -c hello.c
gcc -o hello hello.o
scons: done building targets.

$ ls
hello  hello.c  hello.o  SConstruct

$ ./hello 
hello, world!

就是这样,程序编译完成了。到这里Scons似乎与make没有什么两样,同样的操作,只是把scons换成make,SConstruct换成Makefile。

对,不同的地方就在于,下面的展示:

$ cat  SConstruct

Program("hello.c")

你没看错,就这一行,编译就完成了!!!

事实确实如此,它比传统的 Makefile 简单很多。SConstruct 以 Python 脚本的语法编写,你可以像编写 Python 脚本一样来编写它。

其中的 Program 是编译的类型,说明你准备想要建造一个可执行的二进制程序,它由 hello.c 文件来生成。

在这里,没有指定生成的可执行程序的名字,SCons 会把源代码文件名字的后缀去掉,用来作为可执行文件的名字。

我们甚至不需要像 Makefile 那样指定清理的动作,就可以执行清理任务。在 SCons 中,执行清理任务由参数 -c 指定,如下 :

$ scons -c


scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Cleaning targets ...
Removed hello.o
Removed hello
scons: done cleaning targets.

$ ls
hello.c  SConstruct
 

相关命令

SCons 支持的编译(目标文件)类型有:

  • Program: Program("hello.c")编译成可执行程序(在 Windows 平台上即是 exe 文件),这是最常用的一种编译类型。
  • Object:Object("hello.c") 只编译成目标文件。使用这种类型,编译结束后,只会产生目标文件。在 POSIX 系统中,目标文件以 .o 结尾,在 Windows 平台上以 .OBJ 结尾。
  • Library: Library("hello.c") 编译成库文件。SCons 默认编译的库是指静态链接库。
  • StaticLibrary: StaticLibrary("hello.c") 显示的编译成静态链接库,与上面的 Library 效果一样。
  • SharedLibrary: SharedLibrary("hello.c") 在 POSIX 系统上编译动态链接库,在 Windows 平台上编译 DLL。

如果不想使用 SConstruct 为你设置的默认可执行文件的名字,而是选择你自己喜欢的名字,如 myhello,可以把 SConstruct 的内容修改为 :

Program('myhello', 'helloscons.c')
 

其中 myhello就是你想要的可执行文件的名字.

$ scons  -Q
gcc -o hello.o -c hello.c
gcc -o myhello hello.o
 

Scons入门简介(https://blog.csdn.net/guotianqing/article/details/92003258)

其中,-Q 参数是减少编译时的由 scons 产生的冗余信息。

显式的指定使用某些库:

指定一些编译的宏定义,显式的指定使用某些库:

例子: helloscons2 由 3 个源文件生成 : helloscon2.c, file1.c, file2.c,指定编译选项,同时指定使用哪些具体的库,SConstruct 文件 如下:
 

Program('helloscons2', ['helloscons2.c', 'file1.c', 'file2.c'], 
       LIBS = 'm', 
       LIBPATH = ['/usr/lib', '/usr/local/lib'], 
       CCFLAGS = '-DHELLOSCONS')
 

该 SConstruct 文件指出,它将生成名叫 helloscons2 的程序,该程序由 helloscons2.c, file1.c 和 file2.c 组成。

如果源程序代码文件很多,glob(’*.c’) 来代替源代码列表,如下 :

Program('helloscons2', Glob('*.c'))

配置文件中 LIBS,LIBAPTH 和 CCFLAGS 是 SCons 内置的关键字,它们的作用如下:

LIBS: 显示的指明要在链接过程中使用的库,如果有多个库,应该把它们放在一个列表里面。这个例子里,我们使用一个称为 m 的库。
LIBPATH: 链接库的搜索路径,多个搜索路径放在一个列表中。这个例子里,库的搜索路径是 /usr/lib 和 /usr/local/lib。
CCFLAGS: 编译选项,可以指定需要的任意编译选项,如果有多个选项,应该放在一个列表中。这个例子里,编译选项是通过 -D 这个 gcc 的选项定义了一个宏 HELLOSCONS。
CPPPATH:指定头文件的路径


运行 scons 命令的时候,可以看到这些变量如何被使用的,让我们执行一下 scons 命令 :

$ scons -Q 
gcc -o file1.o -c -DHELLOSCONS file1.c 
gcc -o file2.o -c -DHELLOSCONS file2.c 
gcc -o helloscons2.o -c -DHELLOSCONS helloscons2.c 
gcc -o helloscons2 helloscons2.o file1.o file2.o -L/usr/lib -L/usr/local/lib -lm
 

 更多内容

以上只是入门,还有更多知识等待学习。

如,通过Environment指定编译环境,env = Environment(CC = 'g++'),指定使用g++编译程序。

示例如下:

env = Environment(CC = 'g++')
env.Program("client", "client.c", LIBS = 'm', CPPPATH = '../include', CCFLAGS = '-std=c++11')
 

摘抄自:https://blog.csdn.net/guotianqing/article/details/92003258

scons文件和命令

二、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 Windows; hello on POSIX)
    Program('hello','hello.c') 指定Output文件名(hello.exe on Windows; hello 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 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

    库的使用:

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

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

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

Program("test",list(obj))  

  
    SConscript内容 :    

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查看环境变量的内容:  

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

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

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:       

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:

Import('env')  

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

Return("obj")  

posted on 2022-10-04 01:21  bdy  阅读(935)  评论(0编辑  收藏  举报

导航