ENV工具的使用声明

    env和scons工具在我第一次接触时候,就深深喜欢这样的配置方式。在env工具的加持下,配置工程变得轻而易举;学会使用scons工具不能局限于使用,制作出package才能活学活用,进一步改善开发环境。
    按照我现在对ENV工具的简单理解,在RT-Thread下,env工具的menuconfig模式可以依照Kconfig文件来显示图形界面,配置生成rtconfig.h文件;而后scons工具依赖rtconfig.h文件和 SConstruct,SConscript文件来生成IDE工程。

目录

  • ENV工具的使用声明
  • Kconfig文件
  • Kconfig与menuconfig的关系
  • Kconfig语法
  • config条目
  • menu条目
  • choices条目
  • source条目
  • Kconfig在RT-Thread工程中的位置
  • SCons工具文件
  • SConscript文件
  • Sconscript文件的仿写
  • 编写自动配置工程
  • 例程:(音乐播放器)


Kconfig文件

Kconfig与menuconfig的关系

    Kconfig按照一定的格式来书写,menuconfig程序可以识别这种格式,然后从中提取
出有效信息组成menuconfig中的配置项。用户配置相关配置项后会在rtconfig.h中生
成对应的宏。在menuconfig界面下的界面,取决于Kconfig文件的内容。Kconfig语言内容将决定menuconfig界面。在RT-Thread下,现在主要讲config条目,menu条目,choice条目,source条目。

Kconfig语法

config条目

config条目属性:
1. 类型属性:“bool”/“tristate”/“string”/“hex”/"int“
2. 输入提示:"prompt "  prompt  [“if” expr ]
3. 依赖关系:“depends on”/“requires”  expr 
4. 选择关系:“select”  symbol  [“if”  expr ]
5. 帮助信息:“help”  or  “—help—”
6. 默认配置:“default”  expr  [“if”  expr ]

    config条目的类型多为bool,string,int,hex;而且用法常为基础配置,常嵌入到menu条目,choices条目里。除此之外,还有一种和menu结合的简易写法menuconfig,在menuconfig界面下显示为

menuconfig XIANGXISTUTEST
bool "xiangxistu menuconfig test"
select XIANGXISTU
default n

if XIANGXISTUTEST
    config BSP_USING_CH1
        bool "Enable Channel 1"
        default n
    config BSP_USING_CH2
        bool "Enable Channel 2"
        default n
    config BSP_USING_CH3
        bool "Enable Channel 3"
        default n
    config BSP_USING_CH4
        bool "Enable Channel 4"
        default n
endif

容器中的env文件在哪里_SConscript

容器中的env文件在哪里_Kconfig_02


容器中的env文件在哪里_容器中的env文件在哪里_03


    depend on的使用方法就是下面代码所示,具体现象是下面图片显示的。

config RT_USING_XIANGXISTU
    bool "Test Depends on "
    default n
	help
        This is help interface.

config RT_XIANGXISTU
    int "int default is 2"
	default 2
	depends on RT_USING_XIANGXISTU
    help
		If RT_USING_XIANGXISTU is empty,you cann't see this interface.

容器中的env文件在哪里_ENV_04


容器中的env文件在哪里_Kconfig_05


容器中的env文件在哪里_Scons_06


容器中的env文件在哪里_SConscript_07


    select项更像是直接确定这个宏定义不需要配置,直接添加到列表里。下面是RT-Thread在配置Signal时的配置方式,一旦开启signal,就必定打开内存池。

config RT_USING_SIGNALS
    bool "Enable signals"
    select RT_USING_MEMPOOL
    default n
    help
        A signal is an asynchronous notification sent to a specific thread
        in order to notify it of an event that occurred.
config RT_USING_SIGNALS
    config RT_USING_MEMPOOL
        bool "Using memory pool"
        default y
        help
            Using static memory fixed partition

容器中的env文件在哪里_Scons_08


容器中的env文件在哪里_Scons_09


    一旦打开Signal,就可以发现这个的配置。

容器中的env文件在哪里_ENV_10


容器中的env文件在哪里_Scons_11

menu条目

    menu条目为了展开到下一级,用法简单,不赘述。

menu "USB Host Config"
    config USBH_USING_CONTROLLABLE_POWER
        bool "using a gpio control usb power"
        default y
    if USBH_USING_CONTROLLABLE_POWER
        config USBH_POWER_PIN
            int "power control pin"
            default 15
        config USBH_CURRENT_PIN
            int "current check pin"
            default 86
    endif
    config USBH_USING_VBUS
        bool "using vbus check pin"
        default n
endmenu

容器中的env文件在哪里_SConscript_12

choices条目

    choice条目用法也比较单一,很容易理解。

choice
    prompt "xiangxistu type"
    default xiangxi_default
    config SOC_one
        bool "SOC_one"
    config SOC_two
        bool "SOC_two"
    config SOC_three
        bool "SOC_onethree"
endchoice

容器中的env文件在哪里_Kconfig_13

source条目

    source条目是为了加载其他路径下的Kconfig文件。

config $BSP_DIR
    string
    option env="BSP_ROOT"
    default "."			#当前目录

config $RTT_DIR
    string
    option env="RTT_ROOT"
    default "../.."		#上上级目录

config $PKGS_DIR
    string
    option env="PKGS_ROOT"	#env/packages/
    default "packages"

source "$RTT_DIR/Kconfig"
source "$PKGS_DIR/Kconfig"

    负责任的说,$PKGS_DIR/Kconfig的目录是env工具下的packages下的Konfig

menu "RT-Thread online packages"

source "$PKGS_DIR/packages/iot/Kconfig"
source "$PKGS_DIR/packages/security/Kconfig"
source "$PKGS_DIR/packages/language/Kconfig"
source "$PKGS_DIR/packages/multimedia/Kconfig"
source "$PKGS_DIR/packages/tools/Kconfig"
source "$PKGS_DIR/packages/system/Kconfig"
source "$PKGS_DIR/packages/peripherals/Kconfig"
source "$PKGS_DIR/packages/misc/Kconfig"

endmenu

容器中的env文件在哪里_容器中的env文件在哪里_14

Kconfig在RT-Thread工程中的位置

    Kconfig在rt-thread工程中,有四个地方可以看到

“$RTT_DIR/src/Kconfig”                         #kernel,即src路径下
“$RTT_DIR/libcpu/Kconfig”                    #芯片内核选择,我总感觉这里没有用开配置,而是Sconscript直接写死的
“$RTT_DIR/components/Kconfig”           #RT-Thread组件选择
“$PKGS_DIR/packages/Kconfig”             #这个在env/packages路径下,不在rt-thread下

SCons工具文件

    SCons 是一套由 Python 语言编写的开源构建系统,类似于 GNU Make。它采用不同于通常 Makefile 文件的方式,而是使用 SConstruct 和 SConscript 文件来替代。这些文件也是 Python 脚本,能够使用标准的 Python 语法来编写。
    SCons 使用 SConscript 和 SConstruct 文件来组织源码结构,通常来说一个项目只有一 SConstruct,但是会有多个 SConscript。一般情况下,每个存放有源代码的子目录下都会放置一个 SConscript。为了使 RT-Thread 更好的支持多种编译器,以及方便的调整编译参数,RT-Thread 为每个 BSP 单独创建了一个名为 rtconfig.py 的文件。因此每一个 RT-Thread BSP 目录下都会存在下面三个文件:rtconfig.py、SConstruct 和 SConscript,它们控制 BSP 的编译。一个 BSP 中只有一个 SConstruct 文件,但是却会有多个 SConscript 文件,可以说 SConscript 文件是组织源码的主力军。

rt_thread
---bsp
------stm32f4xx-hal
---------applications
------------SConscript
---------drivers
------------SConscript
---------Libraries
------------SConscript
---------packages
------------SConscript
---------rtconfig.py
---------SConstruct
---------SConscript
---components
------SConscript
---libcpu
------SConscript
---src
------SConscript
---examples
------SConscript

        我们通常都是在bsp下选定芯片位置来开启env工具,配置menuconfig,以用来修改rtconfig.h文件。配置menuconfig后保存退出,使用scons --target=mdk5 命令,用来生成可执行的工程。
        不过,我现在对python这种语言在RT-Thread里的作用理解还很基础,而且,我对这门语言的水平还是菜鸡阶段,不能分析python.py和SConsstruct文件的每条语句的意思,对/tool目录下的*py脚本文件不熟悉。不过忽略掉这也很重要的细节,仅仅使用SConscript文件也可以配置好工程。
        对rtconfig.py和SConstruct文件而言,这里是配置编译工具链,并且调用tools下的keil.py文件来生成工程。为了将SConscript文件收集的c文件和h文件的目录交给最后的工程生成工作。

SConscript文件

import os
cwd = str(Dir('#'))
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')
  • import os: 导入 Python 系统编程 os 模块,可以调用 os 模块提供的函数用于处理文件和目录。
  • cwd = str(Dir(’#’)): 获取工程的顶级目录并赋值给字符串变量 cwd,也就是工程的 SConstruct 所在的目录,在这里它的效果与 cwd = GetCurrentDir() 相同。
  • objs = []: 定义了一个空的 list 型变量 objs。
  • list = os.listdir(cwd): 得到当前目录下的所有子目录,并保存到变量 list 中。
  • 随后是一个 python 的 for 循环,这个 for 循环会遍历一遍 BSP 的所有子目录并运行这些子目录的 SConscript 文件。具体操作是取出一个当前目录的子目录,利用 os.path.join(cwd,d) 拼接成一个完整路径,然后判断这个子目录是否存在一个名为 SConscript 的文件,若存在则执行 objs = objs + SConscript(os.path.join(d,‘SConscript’))。 这一句中使用了 SCons 提供的一个内置函数 SConscript(),它可以读入一个新的 SConscript 文件,并将 SConscript 文件中所指明的源码加入到了源码编译列表 objs 中来。通过这个 SConscript 文件,BSP 工程所需要的源代码就被加入了编译列表中。
Import('rtconfig')
from building import *

cwd = GetCurrentDir()

# add the general drivers.
src = Split("""
board.c
stm32f1xx_it.c
""")

if GetDepend(['RT_USING_PIN']):
    src += ['drv_gpio.c']
if GetDepend(['RT_USING_SERIAL']):
    src += ['drv_usart.c']
if GetDepend(['RT_USING_SPI']):
    src += ['drv_spi.c']
if GetDepend(['RT_USING_USB_DEVICE']):
    src += ['drv_usb.c']
if GetDepend(['RT_USING_SDCARD']):
    src += ['drv_sdcard.c']

if rtconfig.CROSS_TOOL == 'gcc':
    src += ['gcc_startup.s']

CPPPATH = [cwd]

group = DefineGroup('Drivers', src, depend = [''], CPPPATH = CPPPATH)

Return('group')
  • Import(‘rtconfig’): 导入 rtconfig 对象,后面用到的 rtconfig.CROSS_TOOL 定义在这个 rtconfig 模块。
    from building import *: 把 building 模块的所有内容全都导入到当前模块,后面用到的 DefineGroup 定义在这个模块。
    cwd = GetCurrentDir(): 获得当前路径并保存到字符串变量 cwd 中。
    后面一行使用 Split() 函数来将一个文件字符串分割成一个列表,其效果等价于
  • src = [‘board.c’,‘stm32f1xx_it.c’]
  • 后面使用了 if 判断和 GetDepend() 检查 rtconfig.h 中的某个宏是否打开,如果打开,则使用 src += [src_name] 来往列表变量 src 中追加源代码文件。
  • CPPPATH = [cwd]: 将当前路径保存到一个列表变量 CPPPATH 中。
    最后一行使用 DefineGroup 创建一个名为 Drivers 的组,这个组也就对应 MDK 或者 IAR 中的分组。这个组的源代码文件为 src 指定的文件,depend 为空表示该组不依赖任何 rtconfig.h 的宏。
  • CPPPATH =CPPPATH 表示将当前路径添加到系统的头文件路径中。左边的 CPPPATH 是 DefineGroup 中内置参数,表示头文件路径。右边的 CPPPATH 是本文件上面一行定义的。这样我们就可以在其他源码中引用 drivers 目录下的头文件了。
from building import *

cwd = GetCurrentDir()
src = Glob('*.c')
CPPPATH = [cwd, str(Dir('#'))]

group = DefineGroup('Applications', src, depend = [''], CPPPATH = CPPPATH)

Return('group')
  • src = Glob(’*.c’): 得到当前目录下所有的 C 文件。
  • CPPPATH = [cwd, str(Dir(’#’))]: 将当前路径和工程的 SConstruct 所在的路径保存到列表变量 CPPPATH 中。
  • 最后一行使用 DefineGroup 创建一个名为 Applications 的组。这个组的源代码文件为 src 指定的文件,depend 为空表示该组不依赖任何 rtconfig.h 的宏,并将 CPPPATH 保存的路径添加到了系统头文件搜索路径中。这样 applications 目录和 stm32f10x-HAL BSP 目录里面的头文件在源代码的其他地方就可以引用了。
Import('rtconfig')
from building import *

cwd     = GetCurrentDir()
src     = Split('''
shell.c
symbol.c
cmd.c
''')

fsh_src = Split('''
finsh_compiler.c
finsh_error.c
finsh_heap.c
finsh_init.c
finsh_node.c
finsh_ops.c
finsh_parser.c
finsh_var.c
finsh_vm.c
finsh_token.c
''')

msh_src = Split('''
msh.c
msh_cmd.c
msh_file.c
''')

CPPPATH = [cwd]		#如果编译工具是 keil,则变量 LINKFLAGS = '--keep *.o(FSymTab)' 否则置空
if rtconfig.CROSS_TOOL == 'keil':
    LINKFLAGS = '--keep *.o(FSymTab)'

    if not GetDepend('FINSH_USING_MSH_ONLY'):
        LINKFLAGS = LINKFLAGS + '--keep *.o(VSymTab)'
else:
    LINKFLAGS = ''

if GetDepend('FINSH_USING_MSH'):
    src = src + msh_src
if not GetDepend('FINSH_USING_MSH_ONLY'):
    src = src + fsh_src

group = DefineGroup('finsh', src, depend = ['RT_USING_FINSH'], CPPPATH = CPPPATH, LINKFLAGS = LINKFLAGS)

Return('group')
  • 将 finsh 目录加入到系统头文件目录中,这样我们就可以在其他源码中引用 finsh 目录下的头文件。
    LINKFLAGS = LINKFLAGS 的含义与 CPPPATH = CPPPATH 类似。左边的 LINKFLAGS 表示链接参数,右边的 LINKFLAGS 则是前面 if else 语句所定义的值。也就是给工程指定链接参数。

Sconscript文件的仿写

from building import * 

# get current dir path
cwd = GetCurrentDir()

# init src and inc vars
src = []
inc = []

inc = inc + [cwd]

src = src + ['./player.c']


# add group to IDE project
group = DefineGroup('Player', src, depend = ['MUSIC_PLAY'], CPPPATH = inc)

Return('group')

        Python语法很有规律,至少在RT-Thread工程下,配置有章有法;而且语句不唯一,实现方式多种多样。这是自己改写的一个SConscript文件,用来自动配置工程。依赖项为MUSIC_PLAY,自己在rtconfig里添加,或者用menuconfig配置。

编写自动配置工程

容器中的env文件在哪里_SConscript_15

//Kconfig下的增加内容
config MUSIC_PLAY
    bool "Music_player"
    default n

        选中退出后,可以查看到rtconfig.h里的配置内容

容器中的env文件在哪里_ENV_16


        使用 scons --target=mdk5 -j4 命令

容器中的env文件在哪里_ENV_17

例程:(音乐播放器)

容器中的env文件在哪里_容器中的env文件在哪里_18

        例程是RT-Thread提供的,不包含自动配置部分。