当前位置: 首页 > 工具软件 > SCons > 使用案例 >

【RT-Thtread 编译入门及渐进 2-- Scons 命令介绍】

公冶鸣
2023-12-01

RT-Thread Scons 编译介绍

1.1.1 Scons 编译层级设置

  • 第一层目录下会看到类似下面内容的 SConscript 脚本,
# 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脚本

  • 在子目录的脚本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 = [‘’] 表示要编译当前路径中的内容所依赖的宏,在这个脚本中为空,也即不依赖任何宏。

1.1.2 Scons DependedSrc 的使用

在代码的编译过程中有时存在下面这种情况,也即编译某个 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')

1.1.3 Scons CCFLAGS 的使用

在代码编译过程中有时会增加自己需要的一些内容,比如定义一个宏,这个时候就可以使用到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')

1.1.4 Scons defconfig 介绍

在芯片开发的不同阶段需要建立不同的 profile(板级),比如在前期验证阶段只需要建立 fpga相关的profile,在芯片回来之前需要建立 udp 的 profile,不同的profile所使用的 config文件也不同,但是SoC级别的内容是不变的,所以SoC 相关的内容会独立出来。

比如在 rt-thread/bsp/vendor/ 建立不同的 profile 目录:

fpga  evb common
  • fpga 为 fpga 验证阶段使用;
  • evb 为回片后所使用;
  • common 为 soc 内容,fpga 和 evb 共同都可以使用。

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文件。

1.2 Scons Kconfig 的使用

通常情况都是 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,几乎所以地方都会用到。

1.2.1 Scons depend on 的使用

在编译代码的时候,有时一个模块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

上面的内容含义是:

  • 即便 TEST_DEMO1 的 默认值为 y, 但是如果没有配置 TEST_DEMO 那么 就不会出现 TEST_DEMO1;
  • 即便 TEST_DEMO 定义了,如果 TEST_DEMO2 没有定义,那么TEST_DEMO1也无法生效。
 类似资料: