旧博文,搬到 csdn
原文:http://rebootcat.com/2020/08/30/scons/

前言

我是一个 linux c++ 开发者,但是一直对 Makefile 的语法很是头痛,每次都记不住,所以每次写 Makefile 都很痛苦,Makefile 里需要你自己编写依赖和推导规则,这个过程能不能简单点呢?

对于编译一个 C++ 工程来说,也许需要的就是头文件路径、库路径、编译参数,剩下的东西基本也不重要,这三样足够去编译一个工程了。所以有没有一个工具能简单点的去实现 C++ 项目的构建呢?

答案是有的,Scons 就是答案。

Scons

什么是 scons

这里直接引用官网的解释:

What is SCons?

SCons is an Open Source software construction tool—that is, a next-generation build tool. Think of SCons as an improved, cross-platform substitute for the classic Make utility with integrated functionality similar to autoconf/automake and compiler caches such as ccache. In short, SCons is an easier, more reliable and faster way to build software.

What makes SCons better?

  • Configuration files are Python scripts–use the power of a real programming language to solve build problems.
  • Reliable, automatic dependency analysis built-in for C, C++ and Fortran–no more “make depend” or “make clean” to get all of the dependencies. Dependency analysis is easily extensible through user-defined dependency Scanners for other languages or file types.
  • Built-in support for C, C++, D, Java, Fortran, Yacc, Lex, Qt and SWIG, and building TeX and LaTeX documents. Easily extensible through user-defined Builders for other languages or file types.
  • Building from central repositories of source code and/or pre-built targets.
  • Built-in support for fetching source files from SCCS, RCS, CVS, BitKeeper and Perforce.
  • Built-in support for Microsoft Visual Studio .NET and past Visual Studio versions, including generation of .dsp, .dsw, .sln and .vcproj files.
  • Reliable detection of build changes using MD5 signatures; optional, configurable support for traditional timestamps.
  • Improved support for parallel builds–like make -j but keeps N jobs running simultaneously regardless of directory hierarchy.
  • Integrated Autoconf-like support for finding #include files, libraries, functions and typedefs.
  • Global view of all dependencies–no more multiple build passes or reordering targets to build everything.
  • Ability to share built files in a cache to speed up multiple builds–like ccache but for any type of target file, not just C/C++ compilation.
  • Designed from the ground up for cross-platform builds, and known to work on Linux, other POSIX systems (including AIX, BSD systems, HP/UX, IRIX and Solaris), Windows NT, Mac OS X, and OS/2.

最大特点就是使用 Python 语法来编写编译构建脚本,并且支持依赖自动推导,支持编译 C/C++/D/Java/Fortran等项目,并且是跨平台的(因为 python 是跨平台的)。

所以如果你对 python 熟悉的话,而且你和我对 C++ Makefile 有一样的烦恼,那么这对你将是一个好消息。 你将可以用 python 来编写构建脚本,而且会很简单,对于复杂的大型项目也能快速构建好。(也许只要 30 分钟)

安装 scons

因为 scons 是基于 python 来构建的,所以毋容置疑,首先是需要准备好 python 环境,然后使用下述命令安装 scons 工具。

pip install scons

scons 使用语法

注:本文以一个多源文件,多目录结构的项目 mux 为例,介绍 cmake 的使用,相关源文件以及cmake 脚本可以直接查看源项目

scons 构建脚本由一个 SConstruct 文件和多个 SConscript 文件构成。

SConstruct 通常位于项目顶层目录,然后 SConscript 通常位于子目录(子模块)。

那么来看一下 SConstruct 脚本长啥样?

SConstruct

#!/usr/bin/env python
#-*- coding:utf-8 -*-import sys
import os
import platform
import reenv = Environment()
abs_path = os.getcwd()
print('workspace path:{0}'.format(abs_path))sbuild_dir = 'sbuild'headers = ['.', 'third-party/include']
libs = ['./third-party/lib']abs_headers = []
abs_libs = []for item in headers:abs_item = os.path.join(abs_path, item)abs_headers.append(abs_item)for item in libs:abs_item = os.path.join(abs_path, item)abs_libs.append(abs_item)build_dir = os.path.join(abs_path, sbuild_dir)
abs_libs.append(os.path.join(build_dir, 'lib'))CCFLAGS = '-ggdb -std=c++11'print('\nheaders path:')
print(abs_headers)
print('\n')print('libs path:')
print(abs_libs)
print('\n')print("begin load SConscript")env["headers"] = abs_headers
env["libs"]    = abs_libs
env["MUX_DIR"] = abs_path
env['ccflags'] = CCFLAGS
env['build_dir'] = build_dirExport('env')SConscript(['./mbase/SConscript'])
SConscript(['./message_handle/SConscript'])
SConscript(['./epoll/SConscript'])
SConscript(['./transport/SConscript'])
SConscript(['./demo/bench/SConscript'])
SConscript(['./demo/echo/SConscript'])print("\n All Done, Please Check {0}".format(env['build_dir']))

来分析一下这个文件,源文件可以直接在 我的github下载。

SConstruct 文件主要做了两件事:

  • env 环境变量的构造,主要是头文件路径,库路径,编译参数,自定义的一些变量等
  • 使用 SConscript 函数解析执行子模块的 SConscript 文件

需要注意的是 SConstruct 和 SConscript 共享变量使用的就是 env 这个变量,你可以看到上面有一句:

Export('env')

这句很重要。

SConscript

那么位于子模块或者子目录的 SConscript 文件长啥样呢?

#!/usr/bin/env python
#-*- coding:utf-8 -*-import os
import sysImport('env')
project_dir  = env['MUX_DIR']epoll_lib  = 'epoll'epoll_src_path = os.path.join(project_dir, 'epoll/src')
epoll_sources = []
for item in os.listdir(epoll_src_path):if item.endswith('.cc') or item.endswith('.cpp') or item.endswith('.cxx'):abs_item = os.path.join(epoll_src_path, item)epoll_sources.append(abs_item)print('\nbuild target:lib{0}.a'.format(epoll_lib))
print(epoll_sources)lib_dir = os.path.join(env['build_dir'], 'lib')link_libraries = ['mbase']
for lib_name in link_libraries:lib_name = "{0}{1}{2}".format(env['LIBPREFIX'], lib_name, env['LIBSUFFIX'])abs_lib_name = os.path.join(lib_dir, lib_name)epoll_sources.append(abs_lib_name)env.StaticLibrary(target = os.path.join(lib_dir, epoll_lib),source  = epoll_sources,CPPPATH = env['headers'], # includeLIBPATH = env['libs'],    # lib pathLIBS    = ['pthread'],    # link libCCFLAGS = env['ccflags'])

来分析一下这个文件,源文件可以直接在 我的github下载。

SConscript 主要做了两件事:

  • 构造一个源文件列表(用来构建 target 所需要使用的源文件)
  • 根据需要构建 static_lib/dynamic_lib/binary

当然,还有一点很重要,上面其实提到了,SConscript 和 SConstruct 用来共享变量使用的是 env 这个变量,所以你可以看到一句很重要的:

Import('env')

构造源文件列表,对于 Python 来说,简直是小菜一碟,太简单了;

然后如何生成目标文件呢?

1 生成二进制文件

env.Program(target = os.path.join(bin_dir, echo_server_bin),source  = echo_server_sources,CPPPATH = env['headers'],LIBPATH = env['libs'],LIBS    = ['transport','msghandler','epoll', 'mbase', 'pthread'],CCFLAGS = env['ccflags'])

2 生成静态库

env.StaticLibrary(target = os.path.join(lib_dir, epoll_lib),source  = epoll_sources,CPPPATH = env['headers'], # includeLIBPATH = env['libs'],    # lib pathLIBS    = ['pthread'],    # link libCCFLAGS = env['ccflags'])

3 生成动态库

env.SharedLibrary(target = os.path.join(lib_dir, epoll_lib),source  = epoll_sources,CPPPATH = env['headers'], # includeLIBPATH = env['libs'],    # lib pathLIBS    = ['pthread'],    # link libCCFLAGS = env['ccflags'])

上面 3 个函数的参数都是类似的:

  • target: 指定需要生成的目标文件,通常我自己会写一个绝对路径;对于 lib 来说只需要写名字就行,前缀和后缀不需要写。(eg. target = ‘/root/scons_repo/sbuild/lib/test’ ,会生成 /root/scons_repo/sbuild/lib/libtest.a)
  • source: 编译目标文件需要的源文件列表
  • CPPPATH: 通常就是需要 Include 的头文件路径
  • LIBPATH: 通常就是需要链接的库路径
  • LIBS: 需要链接的库列表
  • CCFLAGS: 编译参数

attention:

上面有一个坑我自己碰到的,当我构建目标生成一个静态库的时候,需要链接其他的静态库,如果使用 $LIBPATH 和 $LIBS 指定链接库的话,scons 并没有链接这些库。尝试了很多方法,搜索了很多,也没有解决这个问题

最后是这样解决的。把需要链接的静态库添加到 source 参数中,和其他 cc/cpp 源文件一样放在一起,并且这些库需要使用绝对路径

通常为了跨平台的方便,需要考虑lib 的前后缀,可以这样写:

link_libraries = ['test1', 'test2']
for lib_name in link_libraries:lib_name = "{0}{1}{2}".format(env['LIBPREFIX'], lib_name, env['LIBSUFFIX'])abs_lib_name = os.path.join(lib_dir, lib_name)sources.append(abs_lib_name)

scons 命令

上面详细讲解了如何使用 python 编写构建脚本,那么写好之后怎么用呢?

常用的几个命令:

编译

scons

如果需要并行编译:

scons -j4

清理

scons -c

然后就会按照你脚本里写的方式去构建目标了。

这里贴一下 我的项目 编译的输出:

$ scons
scons: Reading SConscript files ...
workspace path:/mnt/centos-share/workspace/muxheaders path:
['/mnt/centos-share/workspace/mux/.', '/mnt/centos-share/workspace/mux/third-party/include']libs path:
['/mnt/centos-share/workspace/mux/./third-party/lib', '/mnt/centos-share/workspace/mux/sbuild/lib']begin load SConscriptbuild target:libmbase.a
['/mnt/centos-share/workspace/mux/mbase/src/packet.cc']build target:libmsghandler.a
['/mnt/centos-share/workspace/mux/message_handle/src/message_handler.cc']build target:libepoll.a
['/mnt/centos-share/workspace/mux/epoll/src/epoll_tcp_client.cc', '/mnt/centos-share/workspace/mux/epoll/src/epoll_tcp_server.cc']build target:libtransport.a
['/mnt/centos-share/workspace/mux/transport/src/tcp_transport.cc']build target:bench_server
['bench_server.cc']build target:bench_client
['client.cc']build target:echo_server
['echo_server.cc']build target:echo_client
['client.cc']All Done, Please Check /mnt/centos-share/workspace/mux/sbuild
scons: done reading SConscript files.
scons: Building targets ...
g++ -o demo/bench/bench_server.o -c -ggdb -std=c++11 -I. -Ithird-party/include demo/bench/bench_server.cc
g++ -o demo/bench/client.o -c -ggdb -std=c++11 -I. -Ithird-party/include demo/bench/client.cc
g++ -o demo/echo/client.o -c -ggdb -std=c++11 -I. -Ithird-party/include demo/echo/client.cc
g++ -o demo/echo/echo_server.o -c -ggdb -std=c++11 -I. -Ithird-party/include demo/echo/echo_server.cc
g++ -o epoll/src/epoll_tcp_client.o -c -ggdb -std=c++11 -I. -Ithird-party/include epoll/src/epoll_tcp_client.cc
g++ -o epoll/src/epoll_tcp_server.o -c -ggdb -std=c++11 -I. -Ithird-party/include epoll/src/epoll_tcp_server.cc
g++ -o mbase/src/packet.o -c -ggdb -std=c++11 -I. -Ithird-party/include mbase/src/packet.cc
g++ -o message_handle/src/message_handler.o -c -ggdb -std=c++11 -I. -Ithird-party/include message_handle/src/message_handler.cc
g++ -o transport/src/tcp_transport.o -c -ggdb -std=c++11 -I. -Ithird-party/include transport/src/tcp_transport.cc
ar rc sbuild/lib/libmbase.a mbase/src/packet.o
ranlib sbuild/lib/libmbase.a
ar rc sbuild/lib/libepoll.a epoll/src/epoll_tcp_client.o epoll/src/epoll_tcp_server.o sbuild/lib/libmbase.a
ranlib sbuild/lib/libepoll.a
ar rc sbuild/lib/libtransport.a transport/src/tcp_transport.o sbuild/lib/libepoll.a sbuild/lib/libmbase.a
ranlib sbuild/lib/libtransport.a
ar rc sbuild/lib/libmsghandler.a message_handle/src/message_handler.o sbuild/lib/libmbase.a
ranlib sbuild/lib/libmsghandler.a
g++ -o sbuild/bin/bench_client demo/bench/client.o -Lthird-party/lib -Lsbuild/lib -ltransport -lmsghandler -lepoll -lmbase -lpthread
g++ -o sbuild/bin/bench_server demo/bench/bench_server.o -Lthird-party/lib -Lsbuild/lib -ltransport -lmsghandler -lepoll -lmbase -lpthread
g++ -o sbuild/bin/echo_client demo/echo/client.o -Lthird-party/lib -Lsbuild/lib -ltransport -lmsghandler -lepoll -lmbase -lpthread
g++ -o sbuild/bin/echo_server demo/echo/echo_server.o -Lthird-party/lib -Lsbuild/lib -ltransport -lmsghandler -lepoll -lmbase -lpthread
scons: done building targets.
$ scons -c
scons: Reading SConscript files ...
workspace path:/mnt/centos-share/workspace/muxheaders path:
['/mnt/centos-share/workspace/mux/.', '/mnt/centos-share/workspace/mux/third-party/include']libs path:
['/mnt/centos-share/workspace/mux/./third-party/lib', '/mnt/centos-share/workspace/mux/sbuild/lib']begin load SConscriptbuild target:libmbase.a
['/mnt/centos-share/workspace/mux/mbase/src/packet.cc']build target:libmsghandler.a
['/mnt/centos-share/workspace/mux/message_handle/src/message_handler.cc']build target:libepoll.a
['/mnt/centos-share/workspace/mux/epoll/src/epoll_tcp_client.cc', '/mnt/centos-share/workspace/mux/epoll/src/epoll_tcp_server.cc']build target:libtransport.a
['/mnt/centos-share/workspace/mux/transport/src/tcp_transport.cc']build target:bench_server
['bench_server.cc']build target:bench_client
['client.cc']build target:echo_server
['echo_server.cc']build target:echo_client
['client.cc']All Done, Please Check /mnt/centos-share/workspace/mux/sbuild
scons: done reading SConscript files.
scons: Cleaning targets ...
Removed demo/bench/bench_server.o
Removed demo/bench/client.o
Removed demo/echo/client.o
Removed demo/echo/echo_server.o
Removed epoll/src/epoll_tcp_client.o
Removed epoll/src/epoll_tcp_server.o
Removed mbase/src/packet.o
Removed message_handle/src/message_handler.o
Removed transport/src/tcp_transport.o
Removed sbuild/lib/libmbase.a
Removed sbuild/lib/libepoll.a
Removed sbuild/lib/libtransport.a
Removed sbuild/lib/libmsghandler.a
Removed sbuild/bin/bench_client
Removed sbuild/bin/bench_server
Removed sbuild/bin/echo_client
Removed sbuild/bin/echo_server
scons: done cleaning targets.

写在最后

scons 使用 python 脚本来构建项目,如果对 python 熟悉的话,那么编写编译构建脚本将会大大提高效率,再也不用局限在 Makefile 的蛋疼语法里面了。

当然 scons 的缺点也有,据说在大型项目的时候,可能会很慢。这个我还没碰到过,因为没有用到大型项目中。

下一篇,分享下 cmake 构建 C++ 项目的一些语法和步骤。

cmake教程|cmake入门实战

另外,文中涉及到的项目可以在我的github 找到。

Blog:

  • rebootcat.com

  • email: linuxcode2niki@gmail.com

2020-08-30 于杭州
By 史矛革

Scons构建C++项目相关推荐

  1. SCons 构建工具

    SCons 简介 SCons 是一套由 Python 语言编写的开源构建系统,类似于 GNU Make.它采用不同于通常 Makefile 文件的方式,而是使用 SConstruct 和 SConsc ...

  2. 在Eclipse中使用Maven构建Spring项目

    最新版的Spring需要使用Maven构建,本文讲述怎么在Eclipse构建Maven项目,以配置Spring项目为例. maven简单介绍 maven是构建工具,也是构建管理工具.ant只是构建工具 ...

  3. vue使用命令行构建完项目后_vue-cli 构建项目在IE中无法运行解决方式(build之后可运行)...

    IE浏览器(只考虑IE11,更低版本我没考虑)运行时报 Promise未定义的错误 解决办法: 1. 安装babel-polyfill (1.)  npm install babel-polyfill ...

  4. Maven实战(三)Eclipse构建Maven项目

    2019独角兽企业重金招聘Python工程师标准>>> 1. 安装m2eclipse插件     要用Eclipse构建Maven项目,我们需要先安装meeclipse插件     ...

  5. maven(3)------maven构建web项目详细步骤

    eclipse集成工具,轻松通过maven构建web项目步骤如下: 一, 右键,new -->project, 进入下一页面 二,选择"Maven Project", 点击下 ...

  6. 在 Jenkins 中使用声明式 Pipeline 构建 Android 项目

    Blue Ocean 是 Jenkins 推出的一套新的 UI,对比经典 UI 更具有现代化气息.2017 年 4 月 James Dumay 在博客上正式推出了 Blue Ocean 1.0. 兼容 ...

  7. 转】用Maven构建Mahout项目

    原博文出自于: http://blog.fens.me/hadoop-mahout-maven-eclipse/ 感谢! 用Maven构建Mahout项目 Hadoop家族系列文章,主要介绍Hadoo ...

  8. 分布式计算Hadoop系列之如何Eclipse中构建Hadoop项目

    前言 之前根据Hadoop官方文档对HDFS.MapReduce的架构.配置管理等进行了学习,但某些地方官方文档讲解的比较模糊.做过开发的人都能够体会,官方文档有些类似业务规则或者要求,而真正的细节还 ...

  9. 用Maven构建Mahout项目

    Hadoop家族系列文章,主要介绍Hadoop家族产品,常用的项目包括Hadoop, Hive, Pig, HBase, Sqoop, Mahout, Zookeeper, Avro, Ambari, ...

最新文章

  1. Qt简介、安装及在Ubuntu14.04 32位上简单使用举例
  2. WordCount程序
  3. HDUOJ-----2175取(m堆)石子游戏
  4. Python的定时器
  5. Spring+SpringMVC+MyBatis+easyUI整合基础篇(十)SVN搭建
  6. nacos 开启权限验证后 报错状态 403
  7. 创业要有创意--应当注意的八大细节
  8. EMUI10 亮相开发者大会:分布式设计打造全场景体验
  9. 如何有效提高你的沟通技巧
  10. wordpress安装后勿忘删除install.php
  11. OD使用教程7(上)- 调试篇07|解密系列
  12. android 简介动画,android动画简介
  13. html5小说阅读器源码,文本源码阅读器(NexusTextView)
  14. 安装mp4,mp3等媒体解码器
  15. 股市中什么是多头、空头
  16. chatgpt智能提效职场办公-ppt怎么设置背景图片
  17. 求三个正整数的最大公约数和最小公倍数
  18. 【UEFI基础】UEFI网络框架之概述
  19. php utf8生僻字,支持生僻字且自动识别utf-8编码的php汉字转拼音类_PHP
  20. 电商Banner设计背后的12个人性的秘密

热门文章

  1. pygame里面物体闪烁运动_教师资格【试讲示范】高中物理试讲答辩——《自由落体运动》试讲稿答辩...
  2. 第二章:1、函数求导
  3. POJ - 3417 Network LCA+树上差分
  4. 【神经网络】(13) ShuffleNetV2 代码复现,网络解析,附Tensorflow完整代码
  5. 增加一个dbe连接_pogo pin连接器是如何解决振动的问题?
  6. 【抬杠】在某些时候不希望用户缩小浏览器的宽度,因为咳咳~会导致你的布局混乱,那么这个代码就是帮助你如何限制浏览器宽度的
  7. 【亲测可用】改变鼠标样式
  8. VSCode设置类似Webstorm那样可以用本地局域网IP地址访问自己开发的测试项目,vs code 前端如何以服务器模式打开?
  9. Maven向本地仓库导入官方仓库没有的jar包
  10. OpenResty中遇到Can't locate Time/HiRes.pm in @INC问题的解决方法