SCons教程(8) 层级编译 Hierarchical Builds

赵才俊
2023-12-01

层级编译 Hierarchical Builds

在实际工程中,一般都是多级目录结构,不会出现对单一的目录进行构建。本章就开始介绍对于多级目录下,构建脚本的创建。

最顶层的文件为 SConstruct ,里面可以使用 SConscript 来导入其他的编译脚本。例如:

SConscript(['drivers/display/SConscript',
            'drivers/mouse/SConscript',
            'parser/SConscript',
            'utilities/SConscript'])

执行 scons 后,会对这些目录进行查找,然后将这些脚本内容导入到构建过程中,进行后期的构建工作。在导入一个 SConscript 后,还可以再次导入其他的构建脚本文件。可以修改成下面这样:

# SConstruct
SConscript(['drivers/SConscript',
            'parser/SConscript',
            'utilities/SConscript'])


# drivers/SConscript
SConscript(['drivers/display/SConscript',
            'drivers/mouse/SConscript'])

按照下面的文件创建,并填入内容:

// ./hello/hello.c
#include <stdio.h>

extern void hello2(void);
// hello
int main(void)
{
#ifdef HELLO
    printf("hello\r\n");
#endif
    // hello2();
    return 0;
} 

// hello2/hello2.c
#include <stdio.h>
#include "inc.h"


int main(void)
{
// #ifdef HELLO2
    printf("hello2 \r\n");
// #endif
    return 0;
}
# SConstruct
SConscript('./SConscript')

#SConscript
SConscript('./hello/SConscript')
SConscript('./hello2/SConscript')

# ./hello/SConscript
env = Environment()
env.Program('hello', ['hello.c'])

# ./hello2/SConscript
env = Environment()
env.Program('hello2', ['hello.c2'])

执行 scons -Q 可以看到 hellohello2 两个程序都正常生成了,所以可以认为这种层级递归脚本的方式,是可以正常执行的。

admin@DESKTOP-NQU1HUV C:\Users\admin\Desktop\scons\day8
$ scons -Q
gcc -o hello\hello.o -c hello\hello.c
gcc -o hello\hello.exe hello\hello.o
gcc -o hello2\hello2.o -c hello2\hello2.c
gcc -o hello2\hello2.exe hello2\hello2.o

顶层目录,可以在 SConscript 中使用。 指的是 SConstruct 的目录。

Import Export Return

Import : 提供导入一个外部的变量,可以在当前文件内使用
Export : 提供导出一个变量到全局工作空间,可以在其他子环境或者父环境使用
Return : 返回一个变量到调用位置。

# hello/SConscript
Import('env')
obj = env.Object('hello.c')
Return('obj')

# hello2/SConscript
Import('env')
env.Program('hello2', ['hello2.c'])

# SConscript
env = Environment()
Export('env')
hello_obj = SConscript('./hello/SConscript')
SConscript('./hello2/SConscript')
env.Program(hello_obj)

执行 scons -Q ,可以看到以下输出: 可以正常生成 hello.exehello2.exe

admin@DESKTOP-NQU1HUV C:\Users\admin\Desktop\scons\day8
$ scons -Q
gcc -o hello\hello.o -c hello\hello.c
gcc -o hello\hello.exe hello\hello.o
gcc -o hello2\hello2.o -c hello2\hello2.c
gcc -o hello2\hello2.exe hello2\hello2.o

灵活使用 Return 可以方便编写出通用的子环境构建文件,方便进行自动代码执行或者一键生成构建环境。

变体目录,分离源目录和构建目录

在之前的构建过程中,可以看到生成的过程文件以及目标文件都是和源文件在同一个文件夹内,虽然不影响正常使用,但是对于多控制器编译或者是源代码整理都不是很方便,本节提供 两个思路 来分别设置构建目录,和源码目录分离。

第一种,使用之前介绍的 Command 来由开发者动态执行命令,将中间目标文件生成到一个指定的文件夹内,这种方法尽管可以解决这种问题,但是还是不太方便。
第二种,SCons 提供了一个 VariantDir 函数来处理这种情况

variant_dir

SConscript 提供一个参数,可以指定编译目录,在顶级 SConstruct 文件内,添加 variant_dir 变量。

# SConstruct

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

执行 scons -Q 可以看到当前目录下,是可以看到多了一个 build 新的文件夹。所有的中间文件和目标文件,都会出现在这个 build 下,之前的源码路径是干净的。
同时我们也可以看到一个问题,就是 build 中也会多一份源码和编译脚本,且执行 scons -Q -c 也只是会删除生成过程文件和目标文件,对于复制的源文件和脚本,是不做任何修改的。

admin@DESKTOP-NQU1HUV C:\Users\admin\Desktop\scons\day8
$ scons -Q
scons: building associated VariantDir targets: build
gcc -o build\hello\hello.o -c build\hello\hello.c
gcc -o build\hello\hello.exe build\hello\hello.o
gcc -o build\hello2\hello2.o -c build\hello2\hello2.c
gcc -o build\hello2\hello2.exe build\hello2\hello2.o

admin@DESKTOP-NQU1HUV C:\Users\admin\Desktop\scons\day8
$ scons -Q -c
Removed build\hello\hello.o
Removed build\hello\hello.exe
Removed build\hello2\hello2.o
Removed build\hello2\hello2.exe

SConscript 中提供了一个变量 duplicate ,显式设置为 false 后,就可以不进行复制源码和脚本,而使用源码目录中的源文件和构建脚本。

# SConstruct

SConscript('./SConscript', variant_dir='build', duplicate=False)

执行 scons -Q ,可以看到参与编译的源文件都是源码目录下的,且在 build 中就只保留了过程文件。如果看到 build 没有发生变化,可以尝试删除 build ,重新执行一次构建过程。

admin@DESKTOP-NQU1HUV C:\Users\admin\Desktop\scons\day8
$ scons -Q
scons: building associated VariantDir targets: build
gcc -o build\hello\hello.o -c hello\hello.c
gcc -o build\hello\hello.exe build\hello\hello.o
gcc -o build\hello2\hello2.o -c hello2\hello2.c
gcc -o build\hello2\hello2.exe build\hello2\hello2.o

VariantDir

可以使用 VariantDir 来复制一个编译环境,但是我没有测试出来。

VariantDir('build', 'src', duplicate=False)
env = Environment()
env.Program('build/hello.c')

结果

% ls src
hello.c
% scons -Q
cc -o build/hello.o -c src/hello.c
cc -o build/hello build/hello.o
% ls build
hello hello.o

跨平台示例

下面我们使用一个跨平台示例,来综合使用前面介绍的各个函数,来充分说明使用 SCons 来处理构建活动是多么 “有效率” .

# SConstruct

platform = ARGUMENTS.get('OS', Platform())
include = "#hello2"
# lib = "#export/$PLATFORM/lib"
# bin = "#export/$PLATFORM/bin"

env = Environment(
    PLATFORM=platform,
    BINDIR=bin,
    INCDIR=include,
#    LIBDIR=lib,
    CPPPATH=[include],
#    LIBPATH=[lib],
#    LIBS='world',
)
Export('env')
print(platform)

SConscript('./SConscript', variant_dir='build/'+platform, duplicate=False)

执行 scons -Q OS=win32 可以看到如下打印:可以看到的是,可以根据输入的 OS 版本,创建不同的 build/$os 进行不同平台的中间文件构建存储和目标文件存放。

admin@DESKTOP-NQU1HUV C:\Users\admin\Desktop\scons\day8
$ scons -Q OS=win32
win32
scons: building associated VariantDir targets: build\win32
gcc -o build\win32\hello\hello.o -c -Ihello2 hello\hello.c
gcc -o build\win32\hello\hello.exe build\win32\hello\hello.o
gcc -o build\win32\hello2\hello2.o -c -Ihello2 hello2\hello2.c
gcc -o build\win32\hello2\hello2.exe build\win32\hello2\hello2.o
 类似资料: