Scons的使用

什么是Scons

​ SCons 是一个开源软件构建工具。将 SCons 视为经典 Make 实用程序的改进的跨平台替代品,具有类似于 autoconf/automake 和编译器缓存(如 ccache)的集成功能。简而言之,SCons 是一种更简单、更可靠、更快速的软件构建方式。

什么使Scons更好?

  • 配置文件是 Python 脚本——使用真正的编程语言的力量来解决构建问题。
  • 为 C、C++ 和 Fortran 内置了可靠、自动的依赖项分析——不再需要“make Depend”或“make clean”来获取所有依赖项。通过用户定义的其他语言或文件类型的依赖项扫描器,可以轻松扩展依赖项分析。
  • 内置支持 C、C++、D、Java、Fortran、Yacc、Lex、Qt 和 SWIG,以及构建 TeX 和 LaTeX 文档。可通过用户定义的其他语言或文件类型的构建器轻松扩展。
  • 从源代码和/或预构建目标的中央存储库构建。
  • 对 Microsoft Visual Studio 的内置支持,包括生成 .dsp、.dsw、.sln 和 .vcproj 文件。
  • 使用 MD5 签名可靠检测构建更改;对传统时间戳的可选、可配置支持。
  • 支持并行构建 - 类似于 make -j 但无论目录层次结构如何,都可以同时运行 N 个作业。
  • 用于查找 #include 文件、库、函数和 typedef 的集成 Autoconf 式支持。
  • 所有依赖项的全局视图——不再需要多次构建传递或重新排序目标来构建所有内容。
  • 能够在缓存中共享构建的文件以加速多个构建——类似于 ccache,但适用于任何类型的目标文件,而不仅仅是 C/C++ 编译。
  • 专为跨平台构建而设计,可用于 Linux、其他 POSIX 系统(包括 AIX、BSD 系统、HP/UX、IRIX 和 Solaris)、Windows 7/8/10、MacOS 和 OS/2 .

Scons从哪里来?

​ SCons 最初是作为 ScCons 构建工具设计的,它在 2000 年 8 月赢得了 Software Carpentry SC Build 竞赛。该设计反过来又基于 Cons 软件构建实用程序。该项目已重命名为 SCons 以反映它不再与 Software Carpentry 直接相关。

github源码

​ https://github.com/SCons/scons

​ 运行 SCons 需要 Python 3.6 或更高版本。运行标准 SCons 不应该有其他依赖项或要求。支持 Python 3.5 的最后一个版本是 4.2.0。

安装

​ windows安装示例:

# to do a system-level install:
$ python -m pip install --user scons

# Windows variant, assuming Python Laucher:
C:\Users\me> py -m pip install --user scons

# inside a virtualenv it's safe to use bare pip:
(myvenv) $ pip install scons

# install in a virtualenv from a wheel file:
(myvenv) $ pip install SCons-4.3.0-py3-none-any.whl

# install in a virtualenv from source directory:
(myvenv) $ pip install --editable .

​ ubuntu 安装Scons:

sudo apt-get install scons

Scons使用

​ Scons是一个开放源码、以Python语言编码的自动化构建工具,可用来替代make编写复杂的makefile。并且scons是跨平台的,只要scons脚本写的好,可以在Linux和Windows下随意编译。

单个文件

​ 一个最简单的项目, hello.c, 源码在 这里

#include <stdio.h>

int main()
{
    printf("lvguidong scons first demo\n");
    return 0;
}

编写SConstruct文件:

Program("hello.c")

对的, 就一行,就可构建程序了,再使用scons命令执行:

 D:\Scons-Tutorials\demo1> scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
cl /Fohello.obj /c hello.c /nologo
hello.c
link /nologo /OUT:hello.exe hello.obj
scons: done building targets.

这个比传统的makefile简单很多,SConstruct用python脚本语法编写,可以像编写python脚本一样来写,如要清理生成的文件,使用 scons -c

D:\Scons-Tutorials\demo1> scons -c
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Cleaning targets ...  
Removed hello.obj
Removed hello.exe
scons: done cleaning targets.

相关命令:

scons 不仅可以编写可执行的二进制文件,也可以编译其他类型,执行的类型有:
  • Program: 编译成可执行程序, 这是常用的类型
  • object: 只编译成目标文件,这种使用的较少,windows上产生 .obj 文件,linux系统中产生.o文件
  • Library: 编译成库文件,默认是指静态链接库
  • StaticLibrary: 显示的编译成静态链接库,与上面的 Library 效果一样。
  • SharedLibrary: windows产生 .dll 文件,linux产生 .so文件。

编译静态文件

​ 编译静态文件使用类型Libray,源码在 这里

​ 同样Scontruct一行:

Library("add.c")

编译动态文件

​ 编译静态文件使用类型 SharedLibray,源码在 这里

SharedLibrary("add.c")

多个文件

​ 如果你的项目由多个源文件组成,而且你想指定一些编译的宏定义,以及显式的指定使用某些库,这些对于 SCons 来说,都是非常简单的事情,

​ 1.使用Glob() 包含所需要的文件,并且生成自定义名称文件,源码在 这里

SOURDES = Glob('*.c')
SharedLibrary('myscons', SOURDES)

配置参数

​ makefile可以配置编译的参数,scons也可以,如下示例:

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

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

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

一个典型的示例

参考了zxing的 ScontructSConscript 文件

Scontruct:

SConscript('SConscript', variant_dir='build')

Alias('all', ['lib', 'tests', 'zxing'])
Default('all')

# Remove build folder on "scons -c all"
Clean('all', 'build')

Sconscript:

# -*- python -*-

#
# SConscript file to specify the build process, see:
# http://scons.org/doc/production/HTML/scons-man.html
#
Decider('MD5')
import platform
import fnmatch
import os

vars = Variables()
vars.Add(BoolVariable('DEBUG', 'Set to disable optimizations', True))
vars.Add(BoolVariable('PIC', 'Set to 1 for to always generate PIC code', False))
env = Environment(variables = vars)
#env.Replace(CXX = 'clang++')

# 编译选项
compile_options = {}
if platform.system() is 'Windows':
  compile_options['CXXFLAGS'] = '-D_CRT_SECURE_NO_WARNINGS /fp:fast /EHsc'
else:
  # Force ANSI (C++98) to ensure compatibility with MSVC.
  cxxflags = ['-ansi -pedantic']
  if env['DEBUG']:
    #compile_options['CPPDEFINES'] = '-DDEBUG'
    cxxflags.append('-O0 -g3 -ggdb')
    cxxflags.append('-Wall -Wextra -Werror')
    # -Werror
  else:
    cxxflags.append('-Os -g3 -ggdb -Wall -Wextra')
  if env['PIC']:
    cxxflags.append('-fPIC')
  compile_options['CXXFLAGS'] = ' '.join(cxxflags)
  compile_options['LINKFLAGS'] = '-ldl -L/usr/lib -L/opt/local/lib -L/usr/local/lib'

# 编译所有的源文件
def all_files(dir, ext='.cpp', level=6):
  files = []
  for i in range(1, level):
    files += Glob(dir + ('/*' * i) + ext)
  return files

# 查找所有lib文件
def all_libs(name, dir):
  matches = []
  for root, dirnames, filenames in os.walk(dir):
    for filename in fnmatch.filter(filenames, name):
      matches.append(os.path.join(root, filename))
  return matches

# Setup libiconv, if possible
libiconv_include = []
libiconv_libs = []
if all_libs('libiconv.*', '/opt/local/lib'):
  libiconv_include.append('/opt/local/include/')
  libiconv_libs.append('iconv')
else:
  if all_libs('libiconv.*', '/usr/lib'):
    libiconv_libs.append('iconv')

# 编译成静态库文件
# Add libzxing library.
libzxing_files = all_files('core/src')+all_files('core/src', '.cc')
libzxing_include = ['core/src']
if platform.system() is 'Windows':
  libzxing_files += all_files('core/src/win32')
  libzxing_include += ['core/src/win32']
libzxing = env.Library('zxing', source=libzxing_files,
  CPPPATH=libzxing_include + libiconv_libs, **compile_options)

# 编译二进制文件
# Add cli.
zxing_files = all_files('cli/src')
zxing = env.Program('zxing', zxing_files,
  CPPPATH=libzxing_include,
  LIBS=libzxing + libiconv_libs, **compile_options)

# Setup CPPUnit.
cppunit_include = ['/opt/local/include/']
cppunit_libs = ['cppunit']

# Add testrunner program.
test_files = all_files('core/tests/src')
test = env.Program('testrunner', test_files,
  CPPPATH=libzxing_include + cppunit_include,
  LIBS=libzxing + cppunit_libs, **compile_options)

# Setup some aliases.
Alias('lib', libzxing)
Alias('zxing', zxing)
Alias('tests', test)