日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

SCons 构建工具

發(fā)布時(shí)間:2023/12/20 编程问答 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 SCons 构建工具 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

SCons 簡(jiǎn)介

SCons 是一套由 Python 語言編寫的開源構(gòu)建系統(tǒng),類似于 GNU Make。它采用不同于通常 Makefile 文件的方式,而是使用 SConstruct 和 SConscript 文件來替代。這些文件也是 Python 腳本,能夠使用標(biāo)準(zhǔn)的 Python 語法來編寫。所以在 SConstruct、SConscript 文件中可以調(diào)用 Python 標(biāo)準(zhǔn)庫進(jìn)行各類復(fù)雜的處理,而不局限于 Makefile 設(shè)定的規(guī)則。

在?SCons?的網(wǎng)站上可以找到詳細(xì)的 SCons 用戶手冊(cè),本章節(jié)講述 SCons 的基本用法,以及如何在 RT-Thread 中用好 SCons 工具。

什么是構(gòu)建工具

構(gòu)建工具 (software construction tool) 是一種軟件,它可以根據(jù)一定的規(guī)則或指令,將源代碼編譯成可執(zhí)行的二進(jìn)制程序。這是構(gòu)建工具最基本也是最重要的功能。實(shí)際上構(gòu)建工具的功能不止于此,通常這些規(guī)則有一定的語法,并組織成文件。這些文件用來控制構(gòu)建工具的行為,在完成軟件構(gòu)建之外,也可以做其他事情。

目前最流行的構(gòu)建工具是 GNU Make。很多知名開源軟件,如 Linux 內(nèi)核就采用 Make 構(gòu)建。Make 通過讀取 Makefile 文件來檢測(cè)文件的組織結(jié)構(gòu)和依賴關(guān)系,并完成 Makefile 中所指定的命令。

由于歷史原因,Makefile 的語法比較混亂,不利于初學(xué)者學(xué)習(xí)。此外在 Windows 平臺(tái)上使用 Make 也不方便,需要安裝 Cygwin 環(huán)境。為了克服 Make 的種種缺點(diǎn),人們開發(fā)了其他構(gòu)建工具,如 CMake 和 SCons 等。

RT-Thread 構(gòu)建工具

RT-Thread 早期使用 Make/Makefile 構(gòu)建。從 0.3.x 開始,RT-Thread 開發(fā)團(tuán)隊(duì)逐漸引入了 SCons 構(gòu)建系統(tǒng),引入 SCons 唯一的目是:使大家從復(fù)雜的 Makefile 配置、IDE 配置中脫離出來,把精力集中在 RT-Thread 功能開發(fā)上。

有些讀者可能會(huì)有些疑惑,這里介紹的構(gòu)建工具與 IDE 有什么不同呢?IDE 通過圖形化界面的操作來完成構(gòu)建。大部分 IDE 會(huì)根據(jù)用戶所添加的源碼生成類似 Makefile 或 SConscript 的腳本文件,在底層調(diào)用類似 Make 或 SCons 的工具來構(gòu)建源碼。

安裝 SCons

在使用 SCons 系統(tǒng)前需要在 PC 主機(jī)中安裝它,因?yàn)樗?Python 語言編寫的,所以在使用 SCons 之前需要安裝 Python 運(yùn)行環(huán)境。

RT-Thread 提供的 Env 配置工具帶有 SCons 和 Python,因此在 windows 平臺(tái)使用 SCons 則不需要安裝這兩個(gè)軟件。

在 Linux、BSD 環(huán)境中 Python 應(yīng)該已經(jīng)默認(rèn)安裝了,一般也是 2.x 版本系列的 Python 環(huán)境。這時(shí)只需要安裝 SCons 即可,例如在 Ubuntu 中可以使用如下命令安裝 SCons:

sudo apt-get install scons

SCons 基本功能

RT-Thread 構(gòu)建系統(tǒng)支持多種編譯器。目前支持的編譯器包括 ARM GCC、MDK、IAR、VisualStudio、Visual DSP。主流的 ARM Cortex M0、M3、M4 平臺(tái),基本上 ARM GCC、MDK、IAR 都是支持的。有一些 BSP 可能僅支持一種,讀者可以閱讀該 BSP 目錄下的 rtconfig.py 里的 CROSS_TOOL 選項(xiàng)查看當(dāng)前支持的編譯器。

如果是 ARM 平臺(tái)的芯片,則可以使用 Env 工具,輸入 scons 命令直接編譯 BSP,這時(shí)候默認(rèn)使用的是 ARM GCC 編譯器,因?yàn)?Env 工具帶有 ARM GCC 編譯器。 如下圖所示使用?scons?命令編譯 stm32f10x-HAL BSP,后文講解 SCons 也將基于這個(gè) BSP。

如果用戶要使用其他的 BSP 已經(jīng)支持的編譯器編譯工程,或者 BSP 為非 ARM 平臺(tái)的芯片,那么不能直接使用 scons 命令編譯工程,需要自己安裝對(duì)應(yīng)的編譯器,并且指定使用的編譯器路徑。在編譯工程前,可以在 Env 命令行界面使用下面的 2 個(gè)命令指定編譯器為 MDK 和編譯器路徑為 MDK 的安裝路徑。

set RTT_CC=keil set RTT_EXEC_PATH=C:/Keilv5

SCons 基本命令

本節(jié)介紹 RT-Thread 中常用的 SCons 命令。SCons 不僅完成基本的編譯,還可以生成 MDK/IAR/VS 工程。

scons

在 Env 命令行窗口進(jìn)入要編譯的 BSP 工程目錄,然后使用此命令可以直接編譯工程。如果執(zhí)行過?scons?命令后修改了一些源文件,再次執(zhí)行 scons 命令時(shí),則 SCons 會(huì)進(jìn)行增量編譯,僅編譯修改過的源文件并鏈接。

如果在 Windows 上執(zhí)行?scons?輸出以下的警告信息:

scons: warning: No version of Visual Studio compiler found - C/C++ compilers most likely not set correctly.

說明 scons 并沒在你的機(jī)器上找到 Visual Studio 編譯器,但實(shí)際上我們主要是針對(duì)設(shè)備開發(fā),和 Windows 本地沒什么關(guān)系,請(qǐng)直接忽略掉它。

scons?命令后面還可以增加一個(gè) - s 參數(shù),即命令?scons -s,和 scons 命令不同的是此命令不會(huì)打印具體的內(nèi)部命令。

scons -c

清除編譯目標(biāo)。這個(gè)命令會(huì)清除執(zhí)行 scons 時(shí)生成的臨時(shí)文件和目標(biāo)文件。

scons --target=XXX

如果使用 mdk/iar 來進(jìn)行項(xiàng)目開發(fā),當(dāng)修改了 rtconfig.h 打開或者關(guān)閉某些組件時(shí),需要使用以下命令中的其中一種重新生成對(duì)應(yīng)的定制化的工程,然后在 mdk/iar 進(jìn)行編譯下載。

scons --target=iar scons --target=mdk4 scons --target=mdk5

在命令行窗口進(jìn)入要編譯的 BSP 工程目錄,使用?scons --target=mdk5?命令后會(huì)在 BSP 目錄生成一個(gè)新的 MDK 工程文件名為 project.uvprojx。雙擊它打開,就可以使用 MDK 來編譯、調(diào)試。使用?scons --target=iar?命令后則會(huì)生成一個(gè)新的 IAR 工程文件名為 project.eww。不習(xí)慣 SCons 的用戶可以使用這種方式。如果打開 project.uvproj 失敗,請(qǐng)刪除 project.uvopt 后,重新生成工程。

在 bsp/simulator 下,可以使用下面的命令生成 vs2012 的工程或 vs2005 的工程。

scons --target=vs2012 Scons --target=vs2005

如果 BSP 目錄下提供其他 IDE 工程的模板文件也可以使用此命令生成對(duì)應(yīng)的新工程,比如 ua、vs、cb、cdk。

這個(gè)命令后面同樣可以增加一個(gè) -s 參數(shù),如命令?scons –target=mdk5 -s,執(zhí)行此命令時(shí)不會(huì)打印具體的內(nèi)部命令。

注意事項(xiàng)

要生成 MDK 或者 IAR 的工程文件,前提條件是 BSP 目錄存在一個(gè)工程模版文件,然后 scons 才會(huì)根據(jù)這份模版文件加入相關(guān)的源碼,頭文件搜索路徑,編譯參數(shù),鏈接參數(shù)等。而至于這個(gè)工程是針對(duì)哪顆芯片的,則直接由這份工程模版文件指定。所以大多數(shù)情況下,這個(gè)模版文件是一份空的工程文件,用于輔助 SCons 生成 project.uvprojx 或者 project.eww。

scons -jN

多線程編譯目標(biāo),在多核計(jì)算機(jī)上可以使用此命令加快編譯速度。一般來說一顆 cpu 核心可以支持 2 個(gè)線程。雙核機(jī)器上使用?scons -j4?命令即可。

注意事項(xiàng)

如果你只是想看看編譯錯(cuò)誤或警告,最好是不使用 - j 參數(shù),這樣錯(cuò)誤信息不會(huì)因?yàn)槎鄠€(gè)文件并行編譯而導(dǎo)致出錯(cuò)信息夾雜在一起。

scons --dist

搭建項(xiàng)目框架,使用此命令會(huì)在 BSP 目錄下生成 dist 目錄,這便是開發(fā)項(xiàng)目的目錄結(jié)構(gòu),包含了RT-Thread源碼及BSP相關(guān)工程,不相關(guān)的BSP文件夾及l(fā)ibcpu都會(huì)被移除,并且可以隨意拷貝此工程到任何目錄下使用。

scons --verbose

默認(rèn)情況下,使用 scons 命令編譯的輸出不會(huì)顯示編譯參數(shù),如下所示:

D:\repository\rt-thread\bsp\stm32f10x>scons scons: Reading SConscript files ... scons: done reading SConscript files. scons: Building targets ... scons: building associated VariantDir targets: build CC build\applications\application.o CC build\applications\startup.o CC build\components\drivers\serial\serial.o ...

使用 scons –verbose 命令的效果如下:

armcc -o build\src\mempool.o -c --device DARMSTM --apcs=interwork -ID:/Keil/ARM/ RV31/INC -g -O0 -DUSE_STDPERIPH_DRIVER -DSTM32F10X_HD -Iapplications -IF:\Projec t\git\rt-thread\applications -I. -IF:\Project\git\rt-thread -Idrivers -IF:\Proje ct\git\rt-thread\drivers -ILibraries\STM32F10x_StdPeriph_Driver\inc -IF:\Project \git\rt-thread\Libraries\STM32F10x_StdPeriph_Driver\inc -ILibraries\STM32_USB-FS -Device_Driver\inc -IF:\Project\git\rt-thread\Libraries\STM32_USB-FS-Device_Driv er\inc -ILibraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x -IF:\Project\git\rt-thre ...

SCons 進(jìn)階

SCons 使用 SConscript 和 SConstruct 文件來組織源碼結(jié)構(gòu),通常來說一個(gè)項(xiàng)目只有一 SConstruct,但是會(huì)有多個(gè) SConscript。一般情況下,每個(gè)存放有源代碼的子目錄下都會(huì)放置一個(gè) SConscript。

為了使 RT-Thread 更好的支持多種編譯器,以及方便的調(diào)整編譯參數(shù),RT-Thread 為每個(gè) BSP 單獨(dú)創(chuàng)建了一個(gè)名為 rtconfig.py 的文件。因此每一個(gè) RT-Thread BSP 目錄下都會(huì)存在下面三個(gè)文件:rtconfig.py、SConstruct 和 SConscript,它們控制 BSP 的編譯。一個(gè) BSP 中只有一個(gè) SConstruct 文件,但是卻會(huì)有多個(gè) SConscript 文件,可以說 SConscript 文件是組織源碼的主力軍。

RT-Thread 大部分源碼文件夾下也存在 SConscript 文件,這些文件會(huì)被 BSP 目錄下的 SConscript 文件 “找到” 從而將 rtconfig.h 中定義的宏對(duì)應(yīng)的源代碼加入到編譯器中來。后文將以 stm32f10x-HAL BSP 為例,講解 SCons 是如何構(gòu)建工程。

SCons 內(nèi)置函數(shù)

如果想要將自己的一些源代碼加入到 SCons 編譯環(huán)境中,一般可以創(chuàng)建或修改已有 SConscript 文件。SConscript 文件可以控制源碼文件的加入,并且可以指定文件的 Group(與 MDK/IAR 等 IDE 中的 Group 的概念類似)。

SCons 提供了很多內(nèi)置函數(shù)可以幫助我們快速添加源碼程序,利用這些函數(shù),再配合一些簡(jiǎn)單的 Python 語句我們就能隨心所欲向項(xiàng)目中添加或者刪除源碼。下面將簡(jiǎn)單介紹一些常用函數(shù)。

GetCurrentDir()

獲取當(dāng)前路徑。

Glob('*.c')

獲取當(dāng)前目錄下的所有 C 文件。修改參數(shù)的值為其他后綴就可以匹配當(dāng)前目錄下的所有某類型的文件。

GetDepend(macro)

該函數(shù)定義在 tools 目錄下的腳本文件中,它會(huì)從 rtconfig.h 文件讀取配置信息,其參數(shù)為 rtconfig.h 中的宏名。如果 rtconfig.h 打開了某個(gè)宏,則這個(gè)方法(函數(shù))返回真,否則返回假。

Split(str)

將字符串 str 分割成一個(gè)列表 list。

DefineGroup(name, src, depend,**parameters)

這是 RT-Thread 基于 SCons 擴(kuò)展的一個(gè)方法(函數(shù))。DefineGroup 用于定義一個(gè)組件。組件可以是一個(gè)目錄(下的文件或子目錄),也是后續(xù)一些 IDE 工程文件中的一個(gè) Group 或文件夾。

DefineGroup()?函數(shù)的參數(shù)描述:

參數(shù)描述
nameGroup 的名字
srcGroup 中包含的文件,一般指的是 C/C++ 源文件。方便起見,也能夠通過 Glob 函數(shù)采用通配符的方式列出 SConscript 文件所在目錄中匹配的文件
dependGroup 編譯時(shí)所依賴的選項(xiàng)(例如 FinSH 組件依賴于 RT_USING_FINSH 宏定義)。編譯選項(xiàng)一般指 rtconfig.h 中定義的 RT_USING_xxx 宏。當(dāng)在 rtconfig.h 配置文件中定義了相應(yīng)宏時(shí),那么這個(gè) Group 才會(huì)被加入到編譯環(huán)境中進(jìn)行編譯。如果依賴的宏并沒在 rtconfig.h 中被定義,那么這個(gè) Group 將不會(huì)被加入編譯。相類似的,在使用 scons 生成為 IDE 工程文件時(shí),如果依賴的宏未被定義,相應(yīng)的 Group 也不會(huì)在工程文件中出現(xiàn)
parameters配置其他參數(shù),可取值見下表,實(shí)際使用時(shí)不需要配置所有參數(shù)

parameters 可加入的參數(shù):

參數(shù)描述
CCFLAGSC 源文件編譯參數(shù)
CPPPATH頭文件路徑
CPPDEFINES鏈接時(shí)參數(shù)
LIBRARY包含此參數(shù),則會(huì)將組件生成的目標(biāo)文件打包成庫文件

SConscript(dirs,variant_dir,duplicate)

讀取新的 SConscript 文件,SConscript() 函數(shù)的參數(shù)描述如下所示:

參數(shù)描述
dirsSConscript 文件路徑
variant_dir指定生成的目標(biāo)文件的存放路徑
duiplicate設(shè)定是否拷貝或鏈接源文件到 variant_dir

SConscript 示例

下面我們將以幾個(gè) SConscript 為例講解 scons 構(gòu)建工具的使用方法。

SConscript 示例 1

我們先從 stm32f10x-HAL BSP 目錄下的 SConcript 文件開始講解,這個(gè)文件管理 BSP 下面的所有其他 SConscript 文件,內(nèi)容如下所示。

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:?導(dǎo)入 Python 系統(tǒng)編程 os 模塊,可以調(diào)用 os 模塊提供的函數(shù)用于處理文件和目錄。

  • cwd = str(Dir('#')):?獲取工程的頂級(jí)目錄并賦值給字符串變量 cwd,也就是工程的 SConstruct 所在的目錄,在這里它的效果與?cwd = GetCurrentDir()?相同。

  • objs = []:?定義了一個(gè)空的 list 型變量 objs。

  • list = os.listdir(cwd):?得到當(dāng)前目錄下的所有子目錄,并保存到變量 list 中。

  • 隨后是一個(gè) python 的 for 循環(huán),這個(gè) for 循環(huán)會(huì)遍歷一遍 BSP 的所有子目錄并運(yùn)行這些子目錄的 SConscript 文件。具體操作是取出一個(gè)當(dāng)前目錄的子目錄,利用?os.path.join(cwd,d)?拼接成一個(gè)完整路徑,然后判斷這個(gè)子目錄是否存在一個(gè)名為 SConscript 的文件,若存在則執(zhí)行?objs = objs + SConscript(os.path.join(d,'SConscript'))。?這一句中使用了 SCons 提供的一個(gè)內(nèi)置函數(shù)?SConscript(),它可以讀入一個(gè)新的 SConscript 文件,并將 SConscript 文件中所指明的源碼加入到了源碼編譯列表 objs 中來。

通過這個(gè) SConscript 文件,BSP 工程所需要的源代碼就被加入了編譯列表中。

SConscript 示例 2

那么 stm32f10x-HAL BSP 其他的 SConcript 文件又是怎樣的呢?我們?cè)倏匆幌?drivers 目錄下 SConcript 文件,這個(gè)文件將管理 drivers 目錄下面的源代碼。drivers 目錄用于存放根據(jù) RT-Thread 提供的驅(qū)動(dòng)框架實(shí)現(xiàn)的底層驅(qū)動(dòng)代碼。

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'):?導(dǎo)入 rtconfig 對(duì)象,后面用到的 rtconfig.CROSS_TOOL 定義在這個(gè) rtconfig 模塊。

  • from building import *:?把 building 模塊的所有內(nèi)容全都導(dǎo)入到當(dāng)前模塊,后面用到的 DefineGroup 定義在這個(gè)模塊。

  • cwd = GetCurrentDir():?獲得當(dāng)前路徑并保存到字符串變量 cwd 中。

后面一行使用?Split()?函數(shù)來將一個(gè)文件字符串分割成一個(gè)列表,其效果等價(jià)于

src = ['board.c','stm32f1xx_it.c']

后面使用了 if 判斷和?GetDepend()?檢查 rtconfig.h 中的某個(gè)宏是否打開,如果打開,則使用?src += [src_name]?來往列表變量 src 中追加源代碼文件。

  • CPPPATH = [cwd]:?將當(dāng)前路徑保存到一個(gè)列表變量 CPPPATH 中。

最后一行使用 DefineGroup 創(chuàng)建一個(gè)名為 Drivers 的組,這個(gè)組也就對(duì)應(yīng) MDK 或者 IAR 中的分組。這個(gè)組的源代碼文件為 src 指定的文件,depend 為空表示該組不依賴任何 rtconfig.h 的宏。

CPPPATH =CPPPATH?表示將當(dāng)前路徑添加到系統(tǒng)的頭文件路徑中。左邊的 CPPPATH 是 DefineGroup 中內(nèi)置參數(shù),表示頭文件路徑。右邊的 CPPPATH 是本文件上面一行定義的。這樣我們就可以在其他源碼中引用 drivers 目錄下的頭文件了。

SConscript 示例 3

我們?cè)倏匆幌?applications 目錄下的 SConcript 文件,這個(gè)文件將管理 applications 目錄下面的源代碼,用于存放用戶自己的應(yīng)用代碼。

from building import *cwd = GetCurrentDir() src = Glob('*.c') CPPPATH = [cwd, str(Dir('#'))]group = DefineGroup('Applications', src, depend = [''], CPPPATH = CPPPATH)Return('group')

src = Glob('*.c'):?得到當(dāng)前目錄下所有的 C 文件。

CPPPATH = [cwd, str(Dir('#'))]:?將當(dāng)前路徑和工程的 SConstruct 所在的路徑保存到列表變量 CPPPATH 中。

最后一行使用 DefineGroup 創(chuàng)建一個(gè)名為 Applications 的組。這個(gè)組的源代碼文件為 src 指定的文件,depend 為空表示該組不依賴任何 rtconfig.h 的宏,并將 CPPPATH 保存的路徑添加到了系統(tǒng)頭文件搜索路徑中。這樣 applications 目錄和 stm32f10x-HAL BSP 目錄里面的頭文件在源代碼的其他地方就可以引用了。

總結(jié):這個(gè)源程序會(huì)將當(dāng)前目錄下的所有 c 程序加入到組 Applications 中,因此如果在這個(gè)目錄下增加或者刪除文件,就可以將文件加入工程或者從工程中刪除。它適用于批量添加源碼文件。

SConscript 示例 4

下面是 RT-Thread 源代碼 component/finsh/SConscript 文件的內(nèi)容,這個(gè)文件將管理 finsh 目錄下面的源代碼。

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] 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_srcgroup = DefineGroup('finsh', src, depend = ['RT_USING_FINSH'], CPPPATH = CPPPATH, LINKFLAGS = LINKFLAGS)Return('group')

我們來看一下文件中第一個(gè) Python 條件判斷語句的內(nèi)容,如果編譯工具是 keil,則變量?LINKFLAGS = '--keep *.o(FSymTab)'?否則置空。

DefinGroup 同樣將 finsh 目錄下的 src 指定的文件創(chuàng)建為 finsh 組。depend = ['RT_USING_FINSH']?表示這個(gè)組依賴 rtconfig.h 中的宏 RT_USING_FINSH。當(dāng) rtconfig.h 中打開宏 RT_USING_FINSH 時(shí),finsh 組內(nèi)的源碼才會(huì)被實(shí)際編譯,否則 SCons 不會(huì)編譯。

然后將 finsh 目錄加入到系統(tǒng)頭文件目錄中,這樣我們就可以在其他源碼中引用 finsh 目錄下的頭文件。

LINKFLAGS = LINKFLAGS?的含義與?CPPPATH = CPPPATH?類似。左邊的 LINKFLAGS 表示鏈接參數(shù),右邊的 LINKFLAGS 則是前面 if else 語句所定義的值。也就是給工程指定鏈接參數(shù)。

使用 SCons 管理工程

前面小節(jié)對(duì) RT-Thread 源代碼的相關(guān) SConscript 做了詳細(xì)講解,大家也應(yīng)該知道了 SConscript 文件的一些常見寫法,本小節(jié)將指導(dǎo)大家如何使用 SCons 管理自己的工程。

添加應(yīng)用代碼

前文提到過 BSP 下的 Applications 文件夾用于存放用戶自己的應(yīng)用代碼,目前只有一個(gè) main.c 文件。如果用戶的應(yīng)用代碼不是很多,建議相關(guān)源文件都放在這個(gè)文件夾下面。在 Applications 文件夾下新增了 2 個(gè)簡(jiǎn)單的文件 hello.c 和 hello.h,內(nèi)容如下所示。

/* file: hello.h */#ifndef _HELLO_H_ #define _HELLO_H_int hello_world(void);#endif /* _HELLO_H_ *//* file: hello.c */ #include <stdio.h> #include <finsh.h> #include <rtthread.h>int hello_world(void) {rt_kprintf("Hello, world!\n");return 0; }MSH_CMD_EXPORT(hello_world, Hello world!)

applications 目錄下的 SConcript 文件會(huì)把當(dāng)前目錄下的所有源文件都添加到工程中。需要使用?scons --target=xxx?命令才會(huì)把新增的 2 個(gè)文件添加到工程項(xiàng)目中。注意每次新增文件都要重新生成工程。

添加模塊

前文提到在自己源代碼文件不多的情況下,建議所有源代碼文件都放在 applications 文件夾里面。如果用戶源代碼很多了,并且想創(chuàng)建自己的工程模塊,或者需要使用自己獲取的其他模塊,怎么做會(huì)比較合適呢?

同樣以上文提到的 hello.c 和 hello.h 為例,這兩個(gè)文件將會(huì)放到一個(gè)單獨(dú)的文件夾里管理,并且在 MDK 工程文件里有自己的分組,且可以通過 menuconfig 選擇是否使用這個(gè)模塊。在 BSP 下新增 hello 文件夾。

大家注意到文件夾里多了一個(gè) SConscript 文件,如果想要將自己的一些源代碼加入到 SCons 編譯環(huán)境中,一般可以創(chuàng)建或修改已有的 SConscript 文件。參考上文對(duì) RT-Thread 源代碼的一些對(duì) SConscript 文件的分析,這個(gè)新增的 hello 模塊 SConscript 文件內(nèi)容如下所示:

from building import *cwd = GetCurrentDir() include_path = [cwd] src = []if GetDepend(['RT_USING_HELLO']):src += ['hello.c']group = DefineGroup('hello', src, depend = [''], CPPPATH = include_path)Return('group')

通過上面幾行簡(jiǎn)單的代碼,就創(chuàng)建了一個(gè)新組 hello,并且可以通過宏定義控制要加入到組里面的源文件,還將這個(gè)組所在的目錄添加到了系統(tǒng)頭文件路徑中。那么自定義宏 RT_USING_HELLO 又是通過怎樣的方式定義呢?這里要介紹一個(gè)新的文件 Kconfig。Kconfig 用來配置內(nèi)核,使用 Env 配置系統(tǒng)時(shí)使用的 menuconfig 命令生成的配置界面就依賴 Kconfig 文件。menuconfig 命令通過讀取工程的各個(gè) Kconfig 文件,生成配置界面供用戶配置內(nèi)核,最后所有配置相關(guān)的宏定義都會(huì)自動(dòng)保存到 BSP 目錄里的 rtconfig.h 文件中,每一個(gè) BSP 都有一個(gè) rtconfig.h 文件,也就是這個(gè) BSP 的配置信息。

在 stm32f10x-HAL BSP 目錄下已經(jīng)有了關(guān)于這個(gè) BSP 的 Kconfig 文件,我們可以基于這個(gè)文件添加自己需要的配置選項(xiàng)。關(guān)于 hello 模塊添加了如下配置選項(xiàng),# 號(hào)后面為注釋。

使用 Env 工具進(jìn)入 stm32f10x-HAL BSP 目錄后,使用 menuconfig 命令在主頁面最下面就可以看到新增的 hello 模塊的配置菜單,進(jìn)入菜單后如下圖所示。

還可以修改 hello value 的值。

保存配置后退出配置界面,打開 stm32f10x-HAL BSP 目錄下的 rtconfig.h 文件可以看到 hello 模塊的配置信息已經(jīng)有了。

注意:每次 menuconfig 配置完成后都要使用 scons --target=XXX 命令生成新工程。

因?yàn)?rtconfig.h 中已經(jīng)定義了 RT_USING_HELLO 宏,所以新生成工程時(shí)就會(huì)把 hello.c 的源文件添加到新工程中。

上面只是簡(jiǎn)單列舉了在 Kconfig 文件中添加自己模塊的配置選項(xiàng),用戶還可以參考《Env 用戶手冊(cè)》,里面也有對(duì)配置選項(xiàng)修改和添加的講解,也可以自己百度查看 Kconfig 的相關(guān)文檔,實(shí)現(xiàn)其他更復(fù)雜的配置選項(xiàng)。

添加庫

如果要往工程中添加一個(gè)額外的庫,需要注意不同的工具鏈對(duì)二進(jìn)制庫的命名。

  • ARMCC 工具鏈下的庫名稱應(yīng)該是 xxx.lib,一個(gè)以 .lib 為后綴的文件。
  • IAR 工具鏈下的庫名稱應(yīng)該是 xxx.a,一個(gè)以 .a 為后綴的文件。
  • GCC 工具鏈下的庫名稱應(yīng)該是 libxxx.a,一個(gè)以 .a 為后綴的文件,并且有 lib 前綴。

ARMCC / IAR 工具鏈下,若添加庫名為 libabc.lib / libabc_iar.a 時(shí),在指定庫時(shí)指定全名 libabc。

GCC 工具鏈比較特殊,它識(shí)別的是 libxxx.a 這樣的庫名稱,若添加庫名為 libabc.a 時(shí),在指定庫時(shí)是指定 abc,而不是 libabc。

例如,/libs?下有以下庫文件需要添加:

libabc_keil.lib libabc_iar.a libabc_gcc.a

則對(duì)應(yīng)的 SConscript 如下:

Import('rtconfig') from building import *cwd = GetCurrentDir() src = Split(''' ''')LIBPATH = [cwd + '/libs'] # LIBPATH 指定庫的路徑,表示庫的搜索路徑是當(dāng)前目錄下的'libs'目錄if rtconfig.CROSS_TOOL == 'gcc':LIBS = ['abc_gcc'] # GCC 下 LIBS 指定庫的名稱 elif rtconfig.CROSS_TOOL == 'keil':LIBS = ['libabc_keil'] # ARMCC 下 LIBS 指定庫的名稱 else:LIBS = ['libabc_iar'] # IAR 下 LIBS 指定庫的名稱group = DefineGroup('ABC', src, depend = [''], LIBS = LIBS, LIBPATH=LIBPATH)Return('group')

編譯器選項(xiàng)

rtconfig.py 是一個(gè) RT-Thread 標(biāo)準(zhǔn)的編譯器配置文件,控制了大部分編譯選項(xiàng),是一個(gè)使用 python 語言編寫的腳本文件,主要用于完成以下工作:

  • 指定編譯器(從支持的多個(gè)編譯器中選擇一個(gè)你現(xiàn)在使用的編譯器)。

  • 指定編譯器參數(shù),如編譯選項(xiàng)、鏈接選項(xiàng)等。

當(dāng)我們使用 scons 命令編譯工程時(shí),就會(huì)按照 rtconfig.py 的編譯器配置選項(xiàng)編譯工程。下面的代碼為 stm32f10x-HAL BSP 目錄下 rtconfig.py 的部分代碼。

import os# toolchains options ARCH='arm' CPU='cortex-m3' CROSS_TOOL='gcc'if os.getenv('RTT_CC'):CROSS_TOOL = os.getenv('RTT_CC')# cross_tool provides the cross compiler # EXEC_PATH is the compiler execute path, for example, CodeSourcery, Keil MDK, IARif CROSS_TOOL == 'gcc':PLATFORM = 'gcc'EXEC_PATH = '/usr/local/gcc-arm-none-eabi-5_4-2016q3/bin/' elif CROSS_TOOL == 'keil':PLATFORM = 'armcc'EXEC_PATH = 'C:/Keilv5' elif CROSS_TOOL == 'iar':PLATFORM = 'iar'EXEC_PATH = 'C:/Program Files/IAR Systems/Embedded Workbench 6.0 Evaluation'if os.getenv('RTT_EXEC_PATH'):EXEC_PATH = os.getenv('RTT_EXEC_PATH')BUILD = 'debug'if PLATFORM == 'gcc':# toolchainsPREFIX = 'arm-none-eabi-'CC = PREFIX + 'gcc'AS = PREFIX + 'gcc'AR = PREFIX + 'ar'LINK = PREFIX + 'gcc'TARGET_EXT = 'elf'SIZE = PREFIX + 'size'OBJDUMP = PREFIX + 'objdump'OBJCPY = PREFIX + 'objcopy'DEVICE = '-mcpu=cortex-m3 -mthumb -ffunction-sections -fdata-sections'CFLAGS = DEVICEAFLAGS = '-c' + DEVICE + '-x assembler-with-cpp'LFLAGS = DEVICE + '-Wl,--gc-sections,-Map=rtthread-stm32.map,-cref,-u,Reset_Handler -T stm32_rom.ld'

其中 CFLAGS 是 C 文件的編譯選項(xiàng),AFLAGS 則是匯編文件的編譯選項(xiàng),LFLAGS 是鏈接選項(xiàng)。BUILD 變量控制代碼優(yōu)化的級(jí)別。默認(rèn) BUILD 變量取值為'debug',即使用 debug 方式編譯,優(yōu)化級(jí)別 0。如果將這個(gè)變量修改為其他值,就會(huì)使用優(yōu)化級(jí)別 2 編譯。下面幾種都是可行的寫法(總之只要不是'debug'就可以了)。

BUILD = '' BUILD = 'release' BUILD = 'hello, world'

建議在開發(fā)階段都使用 debug 方式編譯,不開優(yōu)化,等產(chǎn)品穩(wěn)定之后再考慮優(yōu)化。

關(guān)于這些選項(xiàng)的具體含義需要參考編譯器手冊(cè),如上面使用的 armcc 是 MDK 的底層編譯器。其編譯選項(xiàng)的含義在 MDK help 中有詳細(xì)說明。

前文提到過如果用戶執(zhí)行 scons 命令時(shí)希望使用其他編譯器編譯工程,可以在 Env 的命令行端使用相關(guān)命令指定編譯器和編譯器路徑。但是這樣修改只對(duì)當(dāng)前的 Env 進(jìn)程有效,再次打開時(shí)又需要重新使用命令設(shè)置,我們可以直接修改 rtconfig.py 文件達(dá)到永久配置編譯器的目的。一般來說,我們只需要修改 CROSS_TOOL 和下面的 EXEC_PATH 兩個(gè)選項(xiàng)。

  • CROSS_TOOL:指定編譯器。可選的值有 keil、gcc、iar,瀏覽 rtconfig.py 可以查看當(dāng)前 BSP 所支持的編譯器。如果您的機(jī)器上安裝了 MDK,那么可以將 CROSS_TOOL 修改為 keil,則使用 MDK 編譯工程。

  • EXEC_PATH:編譯器的安裝路徑。這里有兩點(diǎn)需要注意:

安裝編譯器時(shí)(如 MDK、GNU GCC、IAR 等),不要安裝到帶有中文或者空格的路徑中。否則,某些解析路徑時(shí)會(huì)出現(xiàn)錯(cuò)誤。有些程序默認(rèn)會(huì)安裝到?C:\Program Files?目錄下,中間帶有空格。建議安裝時(shí)選擇其他路徑,養(yǎng)成良好的開發(fā)習(xí)慣。

修改 EXEC_PATH 時(shí),需要注意路徑的格式。在 windows 平臺(tái)上,默認(rèn)的路徑分割符號(hào)是反斜杠?“\”,而這個(gè)符號(hào)在 C 語言以及 Python 中都是用于轉(zhuǎn)義字符的。所以修改路徑時(shí),可以將“\”?改為?“/”,或者在前面加 r(python 特有的語法,表示原始數(shù)據(jù))。

假如某編譯器安裝位置為?D:\Dir1\Dir2?下。下面幾種是正確的寫法:

  • EXEC_PATH =?r'D:\Dir1\Dir2'?注意,字符串前帶有 r,則可正常使用?“\”。

  • EXEC_PATH =?'D:/Dir1/Dir2'?注意,改用?“/”,前面沒有 r。

  • EXEC_PATH =?'D:\\Dir1\\Dir2'?注意,這里使用?“\”?的轉(zhuǎn)義性來轉(zhuǎn)義?“\”?自己。

  • 這是錯(cuò)誤的寫法:EXEC_PATH =?'D:\Dir1\Dir2'。

如果 rtconfig.py 文件有以下代碼,在配置自己的編譯器時(shí)請(qǐng)將下列代碼注釋掉。

if os.getenv('RTT_CC'):CROSS_TOOL = os.getenv('RTT_CC') ... ... if os.getenv('RTT_EXEC_PATH'):EXEC_PATH = os.getenv('RTT_EXEC_PATH')

上面 2 個(gè) if 判斷會(huì)設(shè)置 CROSS_TOOL 和 EXEC_PATH 為 Env 的默認(rèn)值。

編譯器配置完成之后,我們就可以使用 SCons 來編譯 RT-Thread 的 BSP 了。在 BSP 目錄打開命令行窗口,執(zhí)行?scons?命令就會(huì)啟動(dòng)編譯過程。

RT-Thread 輔助編譯腳本

在 RT-Thread 源代碼的 tools 目錄下存放有 RT-Thread 自己定義的一些輔助編譯的腳本,例如用于自動(dòng)生成 RT-Thread 針對(duì)一些 IDE 集成開發(fā)環(huán)境的工程文件。其中最主要的是 building.py 腳本。

SCons 更多使用

對(duì)于復(fù)雜、大型的系統(tǒng),顯然不僅僅是一個(gè)目錄下的幾個(gè)文件就可以搞定的,很可能是由數(shù)個(gè)文件夾一級(jí)一級(jí)組合而成。

在 SCons 中,可以編寫 SConscript 腳本文件來編譯這些相對(duì)獨(dú)立目錄中的文件,同時(shí)也可以使用 SCons 中的 Export 和 Import 函數(shù)在 SConstruct 與 SConscript 文件之間共享數(shù)據(jù)(也就是 Python 中的一個(gè)對(duì)象數(shù)據(jù))。更多 SCons 的使用方法請(qǐng)參考?SCons 官方文檔。

總結(jié)

以上是生活随笔為你收集整理的SCons 构建工具的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。