# for module compiling
import os
from building import *
cwd = GetCurrentDir()
objs = []
list = os.listdir(cwd)
for d in list:
path = os.path.join(cwd, d)
if os.path.isfile(os.path.join(path, 'SConscript')):
objs = objs + SConscript(os.path.join(d, 'SConscript'))
Return('objs')
上面脚本的内容主要有以下几个方面:
1)导入 python 相关的模块
2)获取当前目录下子目录中的SConscript脚本
from building import *
cwd = GetCurrentDir()
src = Glob('*.c')
CPPPATH = [cwd] //将当前目录累加到总的头文件路径
group = DefineGroup('DeviceDrivers', src, depend=[''], CPPPATH=CPPPATH)
Return('group')
上面内容主要有以下几方面内容:
1)获取当前目录路径;
2)使用Glob(*.c)包含要编译的所有 c 文件;
3)将当前路径赋值给 CPPPATH,用于将当前路径配置为全局include路径;
4)当前路径编译配置,其中 depend = [‘’] 表示要编译当前路径中的内容所依赖的宏,在这个脚本中为空,也即不依赖任何宏。
在代码的编译过程中有时存在下面这种情况,也即编译某个 C 文件时需要依赖某一个宏,但是同一级目录下其他的C文件并不依赖这个宏,这个时候就可以用到 DependeSrc 这个函数了,如下脚本中的内容,表示编译 test.cc 时需要依赖宏 ”ARM_DEMO“,如果 ARM_DEMO 没有定义,那么 test.cc 不会加入编译中,而其他文件不受影响。
from building import *
cwd = GetCurrentDir()
src = []
src += DependedSrc('ARM_DEMO', ['test.cc'])
group = DefineGroup('ARM_DEMO', src, depend = ['DEMO'])
Return('group')
在代码编译过程中有时会增加自己需要的一些内容,比如定义一个宏,这个时候就可以使用到Scons SConscript中的
CCFLGAS 功能,如下内容为定义一个 DEMO_TEST 宏:
import rtconfig
from building import *
cwd = GetCurrentDir()
group = []
src = Glob('test1.c')
src += Glob('test2.c')
group += DefineGroup('DEMO', src, depend = [''], CCFLAGS = ' -DDEMO_TEST')
# add all SConscripts in sudir
if GetDepend(['DEMO_FOO']): #如果配置了DEMO_FOO 宏下面的内容才会走到
list = os.listdir(cwd)
for d in list:
sub_sconscript = os.path.join(cwd, d, 'SConscript')
if os.path.isfile(sub_sconscript):
vdir = BOARD_ROOT + "/build/" + os.path.relpath(cwd, start = TOP_ROOT) + "/" + d
group += SConscript(sub_sconscript, variant_dir = vdir, duplicate = 0)
Return('group')
在芯片开发的不同阶段需要建立不同的 profile(板级),比如在前期验证阶段只需要建立 fpga相关的profile,在芯片回来之前需要建立 udp 的 profile,不同的profile所使用的 config文件也不同,但是SoC级别的内容是不变的,所以SoC 相关的内容会独立出来。
比如在 rt-thread/bsp/vendor/ 建立不同的 profile 目录:
fpga evb common
common 目录中通常会进行一些默认配置,在 rtthread 中,这些默认配置使用的 DEVICE_CONFIG_DEF
:
/**
* DEVICE_CONFIG_DEF - define some default devices in a platform
* @_compat: compatible flag, used to bind the device and driver
* @_type: device config structure (device descriptor)
* @...: body of device config array
*
* The parameter @_compat must be the same with it in macro DEVICE_INIT_CALL
* or DEVICE_XXX_INIT.
*
* This macro helps to define some default devices in a platform.
*
* A sample usage:
* DEVICE_CONFIG_DEF(sample, sample_type_t,
* { ... },
* { ... },
* ...
* );
*/
#define DEVICE_CONFIG_DEF(_compat, _type, ...) \
static _type _##_compat##_config_arr[] = { __VA_ARGS__ }; \
RT_WEAK void *_##_compat##_config_ptr = _##_compat##_config_arr;\
RT_WEAK int _##_compat##_config_step = sizeof(_type); \
RT_WEAK int _##_compat##_config_cnt = ARRAY_SIZE(_##_compat##_config_arr)
而在不同的 profile目录下的 device_config.c中会使用 DEVICE_CONFIG
宏来进行配置:
/**
* DEVICE_CONFIG - define some real used devices in a BSP project
* @_compat: compatible flag, used to bind the device and driver
* @_type: device config structure (device descriptor)
* @...: body of device config array
*
* The parameter @_compat must be the same with it in macro DEVICE_INIT_CALL
* or DEVICE_XXX_INIT.
*
* This macro helps to define some real used devices in a BSP project, and
* will override the definitions generated by DEVICE_CONFIG_DEF (if existed)
* with the same @_compat.
*
* A sample usage:
* DEVICE_CONFIG(sample, sample_type_t,
* { ... },
* { ... },
* );
*/
#define DEVICE_CONFIG(_compat, _type, ...) \
static _type _##_compat##_config_arr[] = { __VA_ARGS__ }; \
void *_##_compat##_config_ptr = _##_compat##_config_arr; \
int _##_compat##_config_step = sizeof(_type); \
int _##_compat##_config_cnt = ARRAY_SIZE(_##_compat##_config_arr)
从上面的定义可以看出,DEVICE_CONFIG
宏会覆盖 DEVICE_CONFIG_DEF
所实现的内容,这个是因为不同 board 会使用不同的配置。
不同的场景需要使用不同的 .config 文件,我们知道.config的生成是通过menuconfig配置后生成,我们可以通过生成自己所需要的.config,然后再将其命名为 xxx_defconfig 文件,在编译的时候可以直接使用下面命令即可完成自己所需配置:
$ scons --defconfig="xxx_defconfig"
再看下 scons --defconfig 的使用:
--stackanalysis thread stack static analysis
--genconfig Generate .config from rtconfig.h
--useconfig=USECONFIG make rtconfig.h from config file.
--defconfig=DEFCONFIG build with the .config from
'configs/xxx_defconfig'
--verbose print verbose information during build
--menuconfig make menuconfig for RT-Thread BSP
从上面的说明可以看到,这条命令会从 configs 目录下找 xxx_defconfig, 然后根据其再生成 .config。
我们可以在rtthread 代码中可以看到类似下面的代码:
demo/board/SConscript
from building import *
if GetDepend('DEVICE_CONFIG_NAME'):
device_config_file = GetConfigValue('DEVICE_CONFIG_NAME').strip('"') + '.c'
else:
device_config_file = 'device_config.c'
其中GetDepnd 函数中的参数就是“xxx”, 在使用scons命令是如果后面跟有参数 “xxx” 就会使用xxx_defconfig的配置文件,如果没有参数,那么就使用默认的.config文件。
通常情况都是 SConscript 配合 Kconfig 一块使用,如下:
$: ~/demo/rt-thread/bsp/demo/libraries$ ls
drivers include kernel Kconfig SConscript
其中 libraries 目录下的 Kconfig 中会包含 其子目录下的 Kconfig 文件:
source "$LIB_DIR/drivers/Kconfig"
source "$LIB_DIR/kernel/Kconfig"
在执行 scons --menuconfig 的时候会遍历所有的 Kconfig 文件,并根据 Kconfig 文件中的默认配置来生成 .config,
在执行 scons 编译命令的时候就会使用 .config中配置的宏,通常在生成 .config 的时候会自动生成一个 .h文件,我们当前项目中自动生成的.h文件叫做 rtconfig.h,其内容如下:
#ifndef RT_CONFIG_H__
#define RT_CONFIG_H__
/* Automatically generated file; DO NOT EDIT. */
/* RT-Thread Configuration */
/* RT-Thread Kernel */
#define RT_NAME_MAX 24
#define RT_ALIGN_SIZE 8
#define RT_THREAD_PRIORITY_32
#define RT_THREAD_PRIORITY_MAX 32
#define RT_TICK_PER_SECOND 1000
#define RT_USING_OVERFLOW_CHECK
#define RT_USING_HOOK
#define RT_USING_IDLE_HOOK
#define RT_IDLE_HOOK_LIST_SIZE 4
#define IDLE_THREAD_STACK_SIZE 256
#define RT_USING_TIMER_SOFT
#define RT_TIMER_THREAD_PRIO 4
#define RT_TIMER_THREAD_STACK_SIZE 1024
/* kservice optimization */
#define RT_DEBUG
/* Inter-Thread communication */
#define RT_USING_SEMAPHORE
#define RT_USING_MUTEX
#define RT_USING_EVENT
#define RT_USING_MAILBOX
#define RT_USING_MESSAGEQUEUE
#define RT_USING_SIGNALS
...
.config 中所以配置生效的宏(配置為 “y” 或者配置為某個常数值)都会生成到 rtconfig.h中,所以这个.h会动态生成,会跟着scons --menuconfig的不同配置来生成的不同的rtconfig.h文件,如果继续跟踪就会发现 在rt-thread/include/rtthread.h 中会包含 rtconfig.h 这个头文件,对于 rtthread.h 这个头文件我们都知道他的功能像linux中的 linux/module.h,几乎所以地方都会用到。
在编译代码的时候,有时一个模块A依赖另外一个模块B,如果B不编译的时候A也不能用,这个时候就需要一个依赖宏用来检查B模块是否参与了编译,如下:
menuconfig TEST_DEMO
bool " DEMO TEST"
defalult y
if TEST_DEMO
config TEST_DEMO1
bool "Enable test demo1"
depends on TEST_DEMO2
default y
endif
上面的内容含义是: