scons的使用方法和进阶

scons 简要说明

文章目录

  • scons 简要说明
    • 写在前面
      • scons和make有什么不同
      • scons 安装
    • scons 的基本使用
      • 1. 编译单个文件和常用的操作介绍
      • 2. 指明编译目标
      • 2. 编译多个文件和文件的自动搜索
      • 3. 拆分出汇编阶段和各个编译阶段的选项控制
      • 4. 分离编译文件
      • 5. 防止一些分支处理被忽略(env.Depends)(往下写不动了)
      • 6. 目标换名和闭环依赖链条env.Alias(目标名,源目标)
      • 7. command 编译
    • scons 进阶
      • 1. 防止环境变量冲突
      • 2. 导入外部环境变量
      • 3. 如多个项目编译方法一致,可使用参数文件方式带入差异
      • 3. 不在依赖链条上的项目会执行0次或者执行两次

写在前面

scons和make有什么不同

make中使用了目标和为伪目标相互依赖的方法将需要编译的项目和
依赖的目标链接起来。比如一个.bin文件依赖.o ,而.o文件依赖于
.s 或者.c。

对于编译项目,需要处理的基本问题就是如上的需求。
而scons的编译函数中都有一个编译目标 和编译来源的入参,因此scons
在编译过程中会自动推导依赖关系,而不用显示的去指明依赖链条.

scons 安装

pip install scons

scons 的基本使用

1. 编译单个文件和常用的操作介绍

  1. 如何编译一个单文件
    1.1 比如现在需要编译一个main.cpp,在main.cpp所在目录执行下边命令
    ggg@ggg-X550JX ~/test_demo/test_demo/pro1 $ echo Program\(\"main.cpp\"\) > Sconstruct
    ggg@ggg-X550JX ~/test_demo/test_demo/pro1 $ cat Sconstruct 
    Program("main.cpp")
    ggg@ggg-X550JX ~/test_demo/test_demo/pro1 $ scons
    scons: Reading SConscript files ...
    scons: done reading SConscript files.
    scons: Building targets ...
    g++ -o main.o -c main.cpp
    g++ -o main main.o
    scons: done building targets.
    ggg@ggg-X550JX ~/test_demo/test_demo/pro1 $ ls
    main  main.cpp  main.o  Sconstruct
    
    1.2 分析上边的过程Program函数是用来生成最终的执行目标,可以自动推导默认的编译方法编译出最终目标
  2. 清理项目
    2.1 清理命令是 scons -c
    ggg@ggg-X550JX ~/test_demo/test_demo/pro1 $ ls
    main  main.cpp  main.o  Sconstruct
    ggg@ggg-X550JX ~/test_demo/test_demo/pro1 $ 
    ggg@ggg-X550JX ~/test_demo/test_demo/pro1 $ scons -c
    scons: Reading SConscript files ...
    scons: done reading SConscript files.
    scons: Cleaning targets ...
    Removed main.o
    Removed main
    scons: done cleaning targets.
    ggg@ggg-X550JX ~/test_demo/test_demo/pro1 $ ls
    main.cpp  Sconstruct
    
    2.2 scons 上不用去专门编写项目的清理方法,scons会自动由最终结果往前的这个链条上所有中间文件,并删除掉
  3. 打印依赖树
    ggg@ggg-X550JX ~/test_demo/test_demo/pro1 $ scons --tree=all
    scons: Reading SConscript files ...
    scons: done reading SConscript files.
    scons: Building targets ...
    g++ -o main.o -c main.cpp
    g++ -o main main.o
    +-.
      +-Sconstruct
      +-main
      | +-main.o
      | | +-main.cpp
      | | +-/usr/bin/g++
      | +-/usr/bin/g++
      +-main.cpp
      +-main.o
        +-main.cpp
        +-/usr/bin/g++
    scons: done building targets.
    
  4. 静默编译
    ggg@ggg-X550JX ~/test_demo/test_demo/pro1 $ scons -Q
    g++ -o main.o -c main.cpp
    g++ -o main main.o
    ggg@ggg-X550JX ~/test_demo/test_demo/pro1 $ scons -c -Q
    Removed main.o
    Removed main
    
  5. 为输出目标命名
    ggg@ggg-X550JX ~/test_demo/test_demo/pro1 $ echo Program\(\"nice\",\"main.cpp\"\) >      Sconstruct
    ggg@ggg-X550JX ~/test_demo/test_demo/pro1 $ cat Sconstruct
    Program("nice","main.cpp")
    ggg@ggg-X550JX ~/test_demo/test_demo/pro1 $ scons -Q
    g++ -o main.o -c main.cpp
    g++ -o nice main.o
    ggg@ggg-X550JX ~/test_demo/test_demo/pro1 $ ls
    main.cpp  main.o  nice Sconstruct
    

2. 指明编译目标

  1. 项目中常遇到一个工程下有多个编译目标,但有时只想生成其中一个,全量编译会消耗很多时间
    ggg@ggg-X550JX ~/test_demo/test_demo/pro1 $ cp main.cpp main2.cpp
    ggg@ggg-X550JX ~/test_demo/test_demo/pro1 $ echo Program\(\"main2.cpp\"\) >> Sconstruct
    ggg@ggg-X550JX ~/test_demo/test_demo/pro1 $ scons -Q
    g++ -o main.o -c main.cpp
    g++ -o main main.o
    g++ -o main2.o -c main2.cpp
    g++ -o main2 main2.o
    ggg@ggg-X550JX ~/test_demo/test_demo/pro1 $ scons -c -Q
    Removed main.o
    Removed main
    Removed main2.o
    Removed main2
    ggg@ggg-X550JX ~/test_demo/test_demo/pro1 $ scons -Q main2
    g++ -o main2.o -c main2.cpp
    g++ -o main2 main2.o
    
    
  2. 前三个命令可以看出如果有多个目标在Sconstruct 文件内时不指定目标时会生成全部目标,指明目标时只生成目标

2. 编译多个文件和文件的自动搜索

  1. 参数2位置可以是一个列表
    ggg@ggg-X550JX ~/test_demo/test_demo/pro1 $ cat Sconstruct 
    Program("nice",["main.cpp","res/help.cpp"])
    ggg@ggg-X550JX ~/test_demo/test_demo/pro1 $ scons -Q
    g++ -o main.o -c main.cpp
    g++ -o res/help.o -c res/help.cpp
    g++ -o nice main.o res/help.o
    
  2. 使用Glob函数搜索源文件
    ggg@ggg-X550JX ~/test_demo/test_demo/pro1 $ cat Sconstruct 
    file=Glob("*.cpp") + Glob("*/*.cpp")
    print(file)
    Program("nice",file)
    ggg@ggg-X550JX ~/test_demo/test_demo/pro1 $ scons -Q
    [<SCons.Node.FS.File object at 0x56510efe8698>, <SCons.Node.FS.File object at 0x56510efe9b08>]
    g++ -o main.o -c main.cpp
    g++ -o res/help.o -c res/help.cpp
    g++ -o nice main.o res/help.o
    

    这里看到Glob函数能做模糊匹配的方式匹配到源文件,并且搜到的源文件可以参与到编译中

  3. Glob 的结果可以转化成字符串再进行编译,这个方法主要是针对比如并非所有源文件可以使用统一参数编译,或者要分步编译时.cpp替换成.o 或者.s
    ggg@ggg-X550JX ~/test_demo/test_demo/pro1 $ cat Sconstruct 
    file=Glob("*.cpp") + Glob("*/*.cpp")
    tmp_file = []
    for item in file:
        tmp_file.append(str(item))
    print(tmp_file)
    Program("nice",file)
    ggg@ggg-X550JX ~/test_demo/test_demo/pro1 $ scons -Q
    ['main.cpp', 'res/help.cpp']
    g++ -o main.o -c main.cpp
    g++ -o res/help.o -c res/help.cpp
    g++ -o nice main.o res/help.o
    
    

3. 拆分出汇编阶段和各个编译阶段的选项控制

工程应用中经常会遇到各个阶段编译参数不一致的情况,需要拆分出单个步骤进行编译

ggg@ggg-X550JX ~/test_demo/test_demo/pro1 $ cat Sconstruct 
env = Environment()
env2 = env.Clone()
env3 = env.Clone()
#1. 设置汇编阶段
env["CC"] = "g++"
env["CCFLAGS"] = ["-DCCFLAG","-S"]
env["OBJSUFFIX"] = [".s"]
#2. 设置编译阶段
env2["AS"] = "g++"
env2["ASFLAGS"] = ["-DASFLAGS","-c"]
env2["OBJSUFFIX"] = [".o"]
#3. 设置链接阶段
env3["LINK"] = "g++"
env3["LINKFLAGS"] = ["-DLINKFALGS",]

outfile1 = env.Object("main.s","main.cpp")
outfile2 = env2.Object("main.o","main.s")
env3.Program("aaa", "main.o")


ggg@ggg-X550JX ~/test_demo/test_demo/pro1 $ scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
g++ -o main.s -c -DCCFLAG -S main.cpp
g++ -DASFLAGS -c -o main.o main.s
g++ -o aaa -DLINKFALGS main.o
scons: done building targets.

4. 分离编译文件

Sconstruct 文件是一个工程的编译入口,一般在大型工程中不能将所有编译过程全部放到入口文件中,这样会导致工程可读性变差。


ggg@ggg-X550JX ~/test_demo/test_demo/pro1 $ tree
.
├── build
│   └── file1
├── Sconstruct
└── source
    └── pro1
        └── main.cpp

4 directories, 6 files
ggg@ggg-X550JX ~/test_demo/test_demo/pro1 $ cat Sconstruct 
file1 = "build/file1"
SConscript(file1)
ggg@ggg-X550JX ~/test_demo/test_demo/pro1 $ cat build/file1 
import os
env = Environment()
#这里路径最多退到Sconstruct 这一层
soruce_dir = "../source/pro1"
srcfile = ["main.cpp",]
usefile = [os.path.join(soruce_dir,f) for f in srcfile]
env.Program("aaa", usefile)


ggg@ggg-X550JX ~/test_demo/test_demo/pro1 $ scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
g++ -o source/pro1/main.o -c source/pro1/main.cpp
g++ -o build/aaa source/pro1/main.o
scons: done building targets.

根据命令输入目标缩小搜查范围,并只做部分编译(待完善)

5. 防止一些分支处理被忽略(env.Depends)(往下写不动了)

6. 目标换名和闭环依赖链条env.Alias(目标名,源目标)

7. command 编译

comd="md5sum $SOURCE > $TAEGET"

target2 =env.Command("output.md5",srcfile,comd)

scons 进阶

1. 防止环境变量冲突

env.Clone()
#单个环境下如果重复给同一个环境进行环境变量替换,那么最终编译时候都会按最后一次赋值进行编译

2. 导入外部环境变量

pipe=os.open("%s 2>&1;echo ---;env" %cmd,"r")

text=pip.read()

sts=pipe.close()

最终解析出需要的环境变量添加到

os.environ[k] =v

3. 如多个项目编译方法一致,可使用参数文件方式带入差异

# 在编译文件内进行区别的进行import 导入参数文件(python 格式的变量)

3. 不在依赖链条上的项目会执行0次或者执行两次

# 部分打印信息令人迷惑,实际上可能会被执行两次以上

你可能感兴趣的:(c++)