Scons环境搭建和编译原理概述及嵌入式开发常用模板
Scons環境搭建和編譯原理概述及嵌入式開發常用模板
????????Scons是用python實現的一個類似makefile的軟件構建工具。其官網是SCons: A software construction tool - SCons,其具有詳細的文檔來對其使用進行說明SCons Documentation。
????????Scons是一個軟件構建工具,除了能對c/c++/asm進行構建外,也能實現對java等語言進行構建。因為其是用python來實現的,所以其具備跨平臺的特性。
????????本例程所有相關代碼在GitHub上,請自行下載。https://github.com/bobwenstudy/scons_demo
為什么用Scons
????????第一次接觸Scons是在RTThread中,進一步了解它的特性,漸漸的就喜歡上了,平常自己寫一些代碼,用Scons可以方便不少。對于我個人來說,主要的好處有:
????????實際項目中編譯工程時,除了要對代碼進行編譯生成固件外,還需要整合一些其他資源(如編譯耳機項目時,需要講提示音等wav資源打包進代碼中;多固件合并打包之類。)之前完成這些動作是用python寫的,當然能用makefile調用,但是相比Scons本身就是python,同一套環境調試起來會方便不少。
????????易讀性,相比于makefile這么久遠的構建語言,Scons是python寫的,其語法和調試起來更方便,函數調用和可讀性都比makefile好。
????????文件依賴,使用makefile時,由于經常編譯的時候只將c文件作為依賴,而沒有將h文件作為依賴,或其他資源,導致部分h文件更新時,經常要考慮先make clean,再make all。而Scons可以自動將關聯的文件作為依賴,并構建依賴樹,不用考慮太多就可以自動將相關依賴關系給維護好,當文件更新再次編譯時,只編譯特定文件。同時可以通過Depends指定依賴關系。
????????缺點也有:
????????集成度太強:Scons內部做了太多事情,對于我這種掌控欲比較強的程序猿來說,有點脫離掌控了,雖然方便,但是當構建大型項目時,內部一些行為可能導致debug很長時間,需要我們對其原理進行深入分析。
????????資料較少:畢竟是新語言,現在很多新語言很快的死在沙灘上了,對于新語言,大家或多或少保持觀望態度,所以目前資料還是比較少的。遇到bug得自己看官方手冊,還是有點麻煩的。
環境搭建
????????從上面描述可知,Scons類似于makefile,但并不是替代了GCC,只是實現了對GCC的使用。按照makefile的邏輯,需要準備如下環境:
GCC環境:筆者是Windows環境,所以需要msys64+mingw工具鏈。
Scons環境:python環境安裝后,再安裝scons就行。
GCC環境
PC下的GCC環境安裝
????????參考這個文章安裝即可。Win7下msys64安裝mingw工具鏈 - Milton - 博客園
安裝完以后,鍵入gcc -v,得到如下提示說明環境安裝好了,目前筆者用的是mingw32的gcc。
ARM下的GCC環境安裝
????????從各個芯片產商都可以獲取到對應的交叉工具鏈,筆者的交叉工具鏈解壓縮后,就有gcc環境,一般交叉工具鏈會有前綴。
????????安裝完以后,鍵入arm-none-eabi-gcc -v,出現如下信息就代表安裝成功了。
?
Scons環境
Python安裝
????????Scons是運行在python環境下的,目前新版本的都運行在Python3+的版本上,就不建議安裝Python2的環境了(當然也可以用Python2就是了,不過最新的pip已經不支持Python2了,希望大家早點切換)。
????????網上有很多教程了,Python安裝教程-史上最全_壬杰的博客-CSDN博客_python安裝教程可以參考這個,當然也可以直接到Welcome to Python.org下載。
????????最終安裝結束后,鍵入python -V,能看到python版本,代表環境安裝好了。
?
Scons安裝
????????使用python -m pip install scons即可。當然可以到官網看看SCons DownloadsSCons DownloadsSCons Downloads。
????????最終安裝結束后,鍵入scons -v,能看到如下信息,代表環境安裝好了。
初識Scons
基本概述
????????要講清楚Scons,需要理解gcc是如何使用的,已經相比于make,scons的行為差異。首先對gcc、make和scons基本概念進行說明。
gcc
????????實際編譯c文件的動作都是由gcc來完成的,scons/make等構建語言只是完成了對多個編譯文件和編譯目標的調度管理,本身不完成編譯文件的動作。
????????c編譯原理的概念很多大佬已經講得比較多了,可以參考:C編譯原理_daixiao3636的博客-CSDN博客_c編譯原理。當然現在要編譯文件也很簡單了,一般我們只用2步,一個是通過gcc -c -o生成object文件,然后再將object文件gcc -o生成目標文件。
make
????????使用的換需要安裝make環境,一般安裝好mingw的時候,就自動裝好了make+gcc環境,需要注意的是,新版裝好后沒有make.exe,而是:mingw32-make.exe,可以復制一份,并修改文件名為make.exe,以方便后面使用。
?
make使用可以直接在終端上使用,鍵入make -v,看到如下信息,說明make環境安裝成功了。?
執行make,會去尋找文件:GNUmakefile、makefile 和 Makefile。詳細可以見:認識Makefile文件_天糊土的博客-CSDN博客_makefile文件在哪
Scons
????????之前已經安裝好了scons環境,要使用scons,和make類似,也需要一個腳本文件,配置要執行的操作。文件名為:SConstruct。
?
單文件構建
gcc編譯
????????單個文件編譯,gcc直接命令行操作就行,兩行命令就可以搞定(當然1條命令也可以,這里為了統一,都要求先生成object文件,再生成目標文件)。
?
main.c的源碼如下:?
#include <stdio.h> int main() {printf("hello, world\n");return 0; }make編譯
????????如圖所示,第一次編譯的時候,編譯目標all依賴main.o,由于沒有main.o,先對main.c進行編譯,生成main.o,最后生成最終目標。第二次編譯時,由于main.c沒改變,自然main.o不需要改變,所以只需要執行all所對應的指令,生成目標main。
????????clean清除完環境后,再次make all,所有的操作都重新來過。
Makefile的源碼如下:?
all: main.ogcc main.o -o main main.o: main.cgcc main.c -c -o main.o clean:rm main.o main.exeScons編譯
????????如下圖所示是一個簡易的scons編譯行為,可以看到,腳本寫的時候只需配置好源文件和目標文件source=main.c target=main,直接執行scons就可以,第二次編譯的時候,由于什么都沒變化scons不執行任何操作。
????????相比于make,清空環境只需要執行scons -c就可以自動清除編譯生成的文件,無需像make要指定所有目標。而后再次編譯又會重新開始了。
?scons源碼如下:
obj = Object('main.c') Program('main', obj)總結
????????gcc直接操作,每次編譯都需要輸入所有command,不能自動識別文件哪些有改動,只編譯所需的文件,對于大型工程來說,編譯時間還是很長的,只編譯有修改的文件還是很方便的。
????????make解決了gcc的缺點,每次編譯只需要執行make all,刪除執行make clean就可以,但是寫makefile的人需要知曉所有的中間文件,并了解文件的依賴關系。
????????scons保留了make的優點,每次編譯只需要執行scons,刪除執行scons -c就可以。同時寫Sconstruct也更為輕松,不需要了解中間文件是什么,也不用專門寫一個clean操作。所有的事情交給scons就行。
帶.h文件的構建
gcc編譯
????????多個文件編譯的時候,先將2個文件生成object文件,再將2個object文件編譯成目標main.exe。
main.c的源碼如下:?
#include <stdio.h> #include "test.h" int main() {printf("hello, world\n");test_work();return 0; }test.c的源碼如下:
#include <stdio.h> #include "test.h" void test_work(void) {printf(TEST_PRINT_STRING); }test.h的源碼如下:
#ifndef _TEST_H_ #define _TEST_H_#define TEST_PRINT_STRING "Test_Origin_0" void test_work(void);#endif //_TEST_H_make編譯
????????按照流程寫好,這里需要注意的是,修改完test.h后,再次編譯的時候make只再次生成了main.exe,并沒有重新編譯test.o和main.o,并沒有達到我們的目標。
????????這是因為我們的makefile沒寫好,沒把test.h依賴關系給寫好。
????????將makefile修改一下,這時候再次修改test.h文件,make就知道需要重新編譯文件了(大型項目通常通過-MMD -MP來生成.h的依賴樹)。
????????注意,這個問題是我們寫makefile經常遇到的問題,這個還存在更換之類的場景下,依賴關系沒寫好,導致再次編譯的時候沒將所有改動給編譯進去,通常要先make clean再make all,完全喪失了make只編譯改動的特點,導致編譯效率大大降低。
?
Makefile的源碼如下:?
all: main.o test.ogcc main.o test.o -o main main.o: main.c test.hgcc main.c -c -o main.o test.o: test.c test.hgcc test.c -c -o test.o clean:rm main.o test.o main.exeScons編譯
????????scons的腳本依然很簡單,配置好objects所需的源文件,在配置好目標,執行scons就可以了。需要注意的是,我們并沒有顯示的聲明test.h的依賴關系,當修改test.h后再次編譯,scons就能知道文件間的依賴關系,將整個工程正確的重新編譯了,這對于寫Sconstruct的人要求大大降低。
?
SConstruct源碼如下:
objs = Object('main.c') objs += Object('test.c') Program('main', objs)總結
????????在包含.h文件等多種隱示依賴的情況下,scons無需開發人員了解編譯的底層原理,只需要將所需編譯的主文件配置好,并配置好目標,剩下scons都可以自動搞定,大大節約了工程人員的開發和維護成本。
Scons原理分析
????????從上一章的分析可以看出,scons保留了make的所有優點,并簡化了腳本編譯,同時很好的維護了各個文件的隱示依賴關系,避免大家每次都要make clean,再make all了。
????????這一章我們簡單分析下Scons是如何工作的。
腳本如何運行
????????沒時間細看代碼,自己也寫過一些測試環境之類,大體猜測Scons的運行機制就是將Sconstruct文件用exec函數來執行,這樣才能只寫部分代碼就可以完成編譯工作。
????????簡單搜索了下源碼Sconstruct,可以看到_SConstruct_exists函數會去用這個文件。
?之后有用python的exec函數來執行這個文件。
?
如何指導編譯
????????如果學習過make的語法,其核心語法就是目標和依賴,利用好其基本語法,再借助其強大的工具函數,就可以完成項目的編譯管理工作。
????????scons和make一樣,本質并不會去做gcc的工作,更多是管理工程文件如何依次調用gcc進行編譯。
????????要用scons實現大型項目的管理,也必須理解其底層運行機制,這樣才能實現各種復雜的行為。直接學習其源碼是可以的,但是太累了,直接看其文檔也能看到一些,但是也不是特別清楚,所以這里筆者以自己淺顯的理解來分析其行為。
????????從上面的說明例子中,簡單的c項目編譯,只需要指定Object所需的source文件,然后再指定Program所需的objs和target對象,剩下都是scons來完成的。那么我們依次拆分其操作行為。
打印環境信息env.Dump()
????????scons所有的配置參數和信息存儲在Environment對象中,可以通過Dump方法可以打印當前scons的所有配置參數,而后用python的print方法就可以打印出來了。
????????官方手冊上也有說明:
env = Environment() print(env.Dump())?
????????打印出來的信息如下所示,參數有很多,看不懂咋辦。
????????目前只需要完成嵌入式C開發需要,只要認下面幾個關鍵字:
-
C編譯:CC和CCCOM
-
ASM編譯:AS和ASCOM
-
鏈接:LINK和LINKCOM
C文件編譯行為分析
????????首先我們調整下編譯行為,注釋Program,只執行Object,可以看到其最終執行了gcc -o main.o -c main.c,那為什么其知道要使用這個命令呢?如果我需要加一些編譯參數,應該如何配置呢?如果要進行嵌入式開發,需要替換gcc為aram-xx-gcc如何處理?
?????????從上文可知,scons是通過CCCOM來指導c文件編譯的。從官網上也可以看出是通過這個來完成對c的源文件編譯,生成object文件。
?????????通過dump可知,默認配置下,各個參數如下:
- TARGET:就是目標文件
- SOURCES:就是源文件列表
- CFLAGS:通用C配置參數
- CCFLAGS:通用C和C++公用的配置參數,因為scons支持多個語言,配置了這個參數,其同時會在C++編譯時使用。詳細可以參考:CXXCOM
- _CCCOMCOM: 更多參數配置,如-D或者-I等配置。
- CPPFLAGS:用于預處理相關的配置,既可以用于C編譯,同時也被用于ASM編譯。
- _CPPDEFFLAGS:用于-D的預編譯配置。
- _CPPINCFLAGS:用于-I的include路徑配置。
?
????????其實熟悉GCC的基本已經看明白了,其實把gcc編譯object的參數全部配置好了,需要的話直接改env對應的參數即可。
????????在示例中,obj = Object('main.c')中SOURCES = [main.c],TARGET = main.o,其他參數由于都沒開啟,所以沒顯示。最終就是我們看到的gcc操作。
gcc -o main.o -c main.c obj = Object('main.c')????????假設調整CCFLAGS,加入-g參數,編譯結果如下圖所示,可以看出確實是跟著變的,之后我們只要對應改相關的參數,就可以做到修改gcc的目的。
?
ASM文件編譯行為分析
????????在PC環境下,基本很少寫ASM相關的程序,而在嵌入式環境下,不管是startup文件還是一些特殊用途的行為,都需要使用ASM語句進行操作。
????????類似于C文件的編譯過程,由于PC環境所需的匯編指令各不相同,在嵌入式系統下針對對應的芯片,也有不同匯編指令,本節只對相關配置進行說明。
????????從上文可知,scons是通過ASCOM來指導asm文件編譯的。從官網上也可以看出是通過這個來完成對c的源文件編譯,生成object文件。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-gKSS5iUs-1663252258508)(C:/Users/wenbo/AppData/Roaming/Typora/typora-user-images/image-20220915091107811.png)]
通過dump可知,默認配置下,各個參數如下:
- TARGET:就是目標文件
- SOURCES:就是源文件列表
- ASFLAGS:通用ASM配置參數
????????和gcc類似,實際是使用as來對ASM程序進行編譯,生成object文件。
Link編譯行為分析
????????在完成Object的文件編譯后,生成了一個或多個的Object文件。編譯的最后一步是將這些Object文件Link為Program程序。
????????我們保留生成的Object,只執行Program,
????????首先我們調整下編譯行為,注釋Program,只執行Object,可以看到其最終執行了gcc -o main.exe main.o,那為什么其知道要使用這個命令呢?如果我需要加一些編譯參數,應該如何配置呢?如果要進行嵌入式開發,需要替換gcc為aram-xx-ld如何處理?
?????????從上文可知,scons是通過LINKCOM來指導LINK的。從官網上也可以看出是通過這個來完成對Object文件Link流程的。
?
通過dump可知,默認配置下,各個參數如下,由于LINK實際環境使用的程序各不一樣,所以實際是一個python對象:
- TARGET:就是目標文件
- SOURCES:就是源文件列表
- LINKFLAGS:通用ld配置參數
- _LIBDIRFLAGS:用于-L的lib路徑配置,LIBPATH為具體路徑
- _LIBFLAGS: 用于-l的lib文件配置,LIBS為具體lib名稱。
?
????????和GCC一樣,Program最終調用的就是這個配置。
????????假設調整LINKFLAGS,加入-g參數,編譯結果如下圖所示,可以看出確實是跟著變的,之后我們只要對應改相關的參數,就可以做到修改link的目的。
嵌入式開發和Scons模板
????????默認情況下,c編譯是根據具體編譯環境來的,scons會自己選定gcc程序、asm程序和Link程序,但是在嵌入式開發時,需要指定交叉編譯器,按照之前的所描述的,其實大家已經清楚了,只需要調整相應的配置參數即可。
????????此外嵌入式開發也有一些特殊的配置,這都需要我們理解并調整配置參數才行。
????????如下所示,每行都有注釋,下面對一些關鍵參數/函數進行說明:
- get_path_files:工具函數,獲取路徑下所有特定后綴的文件,當然可以用其他方式實現(實現這個函數主要為了寫path的時候方便)
- GCC_ARM_PATH:交叉工具鏈路徑,scons直接改工作路徑不怎么會,直接指定路徑
- PLF:編譯平臺,默認使用PC編譯,當然嵌入式開發可以選'arm',之后會自動去配置相關參數
- PREFIX:嵌入式開發需要,交叉工具鏈前綴
- SPEC_LD_FLAGS:arm交叉工具鏈開發的時候,LD使用有一些特殊的要求,scons原生的參數不怎么好用,需要在_LIBFLAGS參數尾巴拼接上Map信息,其他工具鏈有特殊要求也可以根據需要調整。
?
?
import osdef get_path_files(dirs, file_ext):path_files = []# print('dir: ' + str(dirs))for dir in dirs:# print('dir: ' + dir)path_files.append(Glob(dir + '/' + file_ext))return path_filesGCC_ARM_PATH = 'D:/env/gcc-arm-none-eabi-4_8-2014q3-20140805-win32'#PLF = 'arm' PLF = ''# toolchains if PLF == 'arm':PREFIX = GCC_ARM_PATH + '/bin/arm-none-eabi-' else:PREFIX = ''CC = PREFIX + 'gcc' AS = PREFIX + 'as' AR = PREFIX + 'ar' CXX = PREFIX + 'g++' LINK = PREFIX + 'ld'SIZE = PREFIX + 'size' OBJDUMP = PREFIX + 'objdump' OBJCPY = PREFIX + 'objcopy'TARGET_PATH = 'build/' TARGET_NAME = 'main' TARGET_WITHOUT_SUFFIX = TARGET_PATH + TARGET_NAME################################################################################### # C source dirs config C_DIRS = [] #C_DIRS.append('src')# C source files config C_FILES = [] #C_FILES.append('main.c')# Create c sources list C_SRC_LIST = get_path_files(C_DIRS, '*.c') + C_FILES################################################################################### # ASM source dirs config AS_DIRS = [] #AS_DIRS.append('src')# ASM source files config AS_FILES = [] #AS_FILES.append('startup.s')AS_SRC_LIST = get_path_files(AS_DIRS, '*.s') + AS_FILES################################################################################### # -I, Include path config CPP_PATH = [] #CPP_PATH.append('inc')# -D, Preprocess Define CPP_DEFINES = [] #CPP_DEFINES.append('CFG_TEST')# C generate define C_FLAGS = [] C_FLAGS.append('-O1') C_FLAGS.append('-g') C_FLAGS.append('-std=c99')# C and C++ generate define CC_FLAGS = [] CC_FLAGS.append('-Wall')# ASM generate define AS_FLAGS = [] AS_FLAGS.append('-g')# Link Config LINK_FLAGS = [] #LINK_FLAGS.append('-Wl,–gc-sections')# lib path. LIB_PATH = [] #LIB_PATH.append('lib')# .lib, .a file LIBS_FILES = [] #LIBS_FILES.append('test')# spec ld flag. Arm spec. SPEC_LD_FLAGS = [] if PLF == 'arm':SPEC_LD_FLAGS.append('-Map')SPEC_LD_FLAGS.append(TARGET_WITHOUT_SUFFIX + '.map')SPEC_LD_FLAGS.append('-T' + 'src/map_ram.txt')env = Environment()################################################################################### # Step0: toolchains setting. if PLF == 'arm':env['CC'] = CCenv['AS'] = ASenv['AR'] = ARenv['CXX'] = CXXenv['LINK'] = LINKenv['OBJSUFFIX'] = '.o'env['LIBPREFIX'] = 'lib'env['LIBSUFFIX'] = '.a'env['PROGSUFFIX'] = '.elf'################################################################################### # Step1: C compile setting. use <print(env.Dump())> for details. # 'CCCOM': '$CC -o $TARGET -c $CFLAGS $CCFLAGS $_CCCOMCOM $SOURCES'# Step1.0: General options, like: optim/debug setting. $CFLAGS. env.Append(CFLAGS=C_FLAGS)# Step1.1: General options, other setting. $CCFLAGS. env.Append(CCFLAGS=CC_FLAGS)# Step1.2: -D, -I, setting. $_CCCOMCOM # '_CCCOMCOM': '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS' # StepX.2.0: CPPFLAGS setting. --- do nothing. # StepX.2.1: -D setting. # 'CPPDEFPREFIX': '-D' # '_CPPDEFFLAGS': '${_defines(CPPDEFPREFIX, CPPDEFINES, CPPDEFSUFFIX, __env__, ' # 'TARGET, SOURCE)}', env.Append(CPPDEFINES=CPP_DEFINES) # Step1.2.2: -I setting. # 'INCPREFIX': '-I' # '_CPPINCFLAGS': '${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs, ' # 'TARGET, SOURCE, affect_signature=False)}', env.Append(CPPPATH=CPP_PATH)################################################################################### # Step2: ASM compile setting. use <print(env.Dump())> for details. # 'ASCOM': '$AS $ASFLAGS -o $TARGET $SOURCES'# Step2.0: General options. $ASFLAGS. env.Append(ASFLAGS=AS_FLAGS)################################################################################### # Step3: LINK setting. use <print(env.Dump())> for details. # 'LINKCOM': '$LINK -o $TARGET $LINKFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS ' # '$_LIBFLAGS',# Step3.0: General options. $LINKFLAGS. env.Append(LINKFLAGS=LINK_FLAGS)# Step3.1: Link path setting. $_LIBDIRFLAGS. # '_LIBDIRFLAGS': '${_concat(LIBDIRPREFIX, LIBPATH, LIBDIRSUFFIX, __env__, ' # 'RDirs, TARGET, SOURCE, affect_signature=False)}', env.Append(LIBPATH=LIB_PATH)# Step3.2: libs setting, like *.a, *.lib. $_LIBFLAGS. # '_LIBFLAGS': '${_stripixes(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, LIBPREFIXES, ' # 'LIBSUFFIXES, __env__)}', env.Append(LIBS=LIBS_FILES)# Step3.3: Add some spec params. must append at end. env.Append(_LIBFLAGS=SPEC_LD_FLAGS)################################################################################### # Step4: Compile Object files, use: # 1. <$CCCOM>: For c code compile # 2. <$ASCOM>: For asm code compile c_objs = env.Object(C_SRC_LIST) as_objs = env.Object(AS_SRC_LIST)################################################################################### # Step5: Compile target <.elf>, use <$LINKCOM>. target = env.Program(target = TARGET_WITHOUT_SUFFIX, source=[c_objs, as_objs])# Other compile target. env.Command(TARGET_WITHOUT_SUFFIX + '.bin', target, OBJCPY + ' -v -O binary $SOURCE $TARGET') env.Command(TARGET_WITHOUT_SUFFIX + '.lst', target, OBJDUMP + ' --source --all-headers --demangle --line-numbers --wide $SOURCE > $TARGET') env.Command(TARGET_WITHOUT_SUFFIX + '.size', target, SIZE + ' --format=berkeley $SOURCE')# Dump() env params, if need. #print(env.Dump())模板使用示例
????????下面舉一個例子,還是以PC舉例,嵌入式開發自己根據需要配置即可。
????????有如下的一個多文件結構的C代碼,目前要用scons來編譯,在根目錄下寫SConstruct配置文件。
?????????最終的SConstruct配置如下:
import osdef get_path_files(dirs, file_ext):path_files = []# print('dir: ' + str(dirs))for dir in dirs:# print('dir: ' + dir)path_files.append(Glob(dir + '/' + file_ext))return path_filesGCC_ARM_PATH = 'D:/env/gcc-arm-none-eabi-4_8-2014q3-20140805-win32'#PLF = 'arm' PLF = ''# toolchains if PLF == 'arm':PREFIX = GCC_ARM_PATH + '/bin/arm-none-eabi-' else:PREFIX = ''CC = PREFIX + 'gcc' AS = PREFIX + 'as' AR = PREFIX + 'ar' CXX = PREFIX + 'g++' LINK = PREFIX + 'ld'SIZE = PREFIX + 'size' OBJDUMP = PREFIX + 'objdump' OBJCPY = PREFIX + 'objcopy'TARGET_PATH = 'build/' TARGET_NAME = 'main' TARGET_WITHOUT_SUFFIX = TARGET_PATH + TARGET_NAME################################################################################### # C source dirs config C_DIRS = [] C_DIRS.append('app\driver\src') C_DIRS.append('module1\src') C_DIRS.append('module2\src')# C source files config C_FILES = [] C_FILES.append('app\main.c')# Create c sources list C_SRC_LIST = get_path_files(C_DIRS, '*.c') + C_FILES################################################################################### # ASM source dirs config AS_DIRS = [] #AS_DIRS.append('src')# ASM source files config AS_FILES = [] #AS_FILES.append('startup.s')AS_SRC_LIST = get_path_files(AS_DIRS, '*.s') + AS_FILES################################################################################### # -I, Include path config CPP_PATH = [] CPP_PATH.append('app\driver\inc') CPP_PATH.append('module1\inc') CPP_PATH.append('module2\inc')# -D, Preprocess Define CPP_DEFINES = [] #CPP_DEFINES.append('CFG_TEST')# C generate define C_FLAGS = [] C_FLAGS.append('-O1') C_FLAGS.append('-g') C_FLAGS.append('-std=c99')# C and C++ generate define CC_FLAGS = [] CC_FLAGS.append('-Wall')# ASM generate define AS_FLAGS = [] AS_FLAGS.append('-g')# Link Config LINK_FLAGS = [] #LINK_FLAGS.append('-Wl,–gc-sections')# lib path. LIB_PATH = [] #LIB_PATH.append('lib')# .lib, .a file LIBS_FILES = [] #LIBS_FILES.append('test')# spec ld flag. Arm spec. SPEC_LD_FLAGS = [] if PLF == 'arm':SPEC_LD_FLAGS.append('-Map')SPEC_LD_FLAGS.append(TARGET_WITHOUT_SUFFIX + '.map')SPEC_LD_FLAGS.append('-T' + 'src/map_ram.txt')env = Environment()################################################################################### # Step0: toolchains setting. if PLF == 'arm':env['CC'] = CCenv['AS'] = ASenv['AR'] = ARenv['CXX'] = CXXenv['LINK'] = LINKenv['OBJSUFFIX'] = '.o'env['LIBPREFIX'] = 'lib'env['LIBSUFFIX'] = '.a'env['PROGSUFFIX'] = '.elf'################################################################################### # Step1: C compile setting. use <print(env.Dump())> for details. # 'CCCOM': '$CC -o $TARGET -c $CFLAGS $CCFLAGS $_CCCOMCOM $SOURCES'# Step1.0: General options, like: optim/debug setting. $CFLAGS. env.Append(CFLAGS=C_FLAGS)# Step1.1: General options, other setting. $CCFLAGS. env.Append(CCFLAGS=CC_FLAGS)# Step1.2: -D, -I, setting. $_CCCOMCOM # '_CCCOMCOM': '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS' # StepX.2.0: CPPFLAGS setting. --- do nothing. # StepX.2.1: -D setting. # 'CPPDEFPREFIX': '-D' # '_CPPDEFFLAGS': '${_defines(CPPDEFPREFIX, CPPDEFINES, CPPDEFSUFFIX, __env__, ' # 'TARGET, SOURCE)}', env.Append(CPPDEFINES=CPP_DEFINES) # Step1.2.2: -I setting. # 'INCPREFIX': '-I' # '_CPPINCFLAGS': '${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs, ' # 'TARGET, SOURCE, affect_signature=False)}', env.Append(CPPPATH=CPP_PATH)################################################################################### # Step2: ASM compile setting. use <print(env.Dump())> for details. # 'ASCOM': '$AS $ASFLAGS -o $TARGET $SOURCES'# Step2.0: General options. $ASFLAGS. env.Append(ASFLAGS=AS_FLAGS)################################################################################### # Step3: LINK setting. use <print(env.Dump())> for details. # 'LINKCOM': '$LINK -o $TARGET $LINKFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS ' # '$_LIBFLAGS',# Step3.0: General options. $LINKFLAGS. env.Append(LINKFLAGS=LINK_FLAGS)# Step3.1: Link path setting. $_LIBDIRFLAGS. # '_LIBDIRFLAGS': '${_concat(LIBDIRPREFIX, LIBPATH, LIBDIRSUFFIX, __env__, ' # 'RDirs, TARGET, SOURCE, affect_signature=False)}', env.Append(LIBPATH=LIB_PATH)# Step3.2: libs setting, like *.a, *.lib. $_LIBFLAGS. # '_LIBFLAGS': '${_stripixes(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, LIBPREFIXES, ' # 'LIBSUFFIXES, __env__)}', env.Append(LIBS=LIBS_FILES)# Step3.3: Add some spec params. must append at end. env.Append(_LIBFLAGS=SPEC_LD_FLAGS)################################################################################### # Step4: Compile Object files, use: # 1. <$CCCOM>: For c code compile # 2. <$ASCOM>: For asm code compile c_objs = env.Object(C_SRC_LIST) as_objs = env.Object(AS_SRC_LIST)################################################################################### # Step5: Compile target <.elf>, use <$LINKCOM>. target = env.Program(target = TARGET_WITHOUT_SUFFIX, source=[c_objs, as_objs])# Other compile target. env.Command(TARGET_WITHOUT_SUFFIX + '.bin', target, OBJCPY + ' -v -O binary $SOURCE $TARGET') env.Command(TARGET_WITHOUT_SUFFIX + '.lst', target, OBJDUMP + ' --source --all-headers --demangle --line-numbers --wide $SOURCE > $TARGET') env.Command(TARGET_WITHOUT_SUFFIX + '.size', target, SIZE + ' --format=berkeley $SOURCE')# Dump() env params, if need. #print(env.Dump())執行scons操作,可以看到相關參數的編譯過程,最后也會生成bin、lst以及code size信息。
D:\test\sample_4>scons scons: Reading SConscript files ... scons: done reading SConscript files. scons: Building targets ... gcc -o app\driver\src\driver1.1.o -c -O1 -g -std=c99 -Wall -Iapp\driver\inc -Imodule1\inc -Imodule2\inc app\driver\src\driver1.1.c gcc -o app\driver\src\driver2.2.o -c -O1 -g -std=c99 -Wall -Iapp\driver\inc -Imodule1\inc -Imodule2\inc app\driver\src\driver2.2.c gcc -o app\main.o -c -O1 -g -std=c99 -Wall -Iapp\driver\inc -Imodule1\inc -Imodule2\inc app\main.c gcc -o module1\src\module1.1.o -c -O1 -g -std=c99 -Wall -Iapp\driver\inc -Imodule1\inc -Imodule2\inc module1\src\module1.1.c gcc -o module1\src\module1.2.o -c -O1 -g -std=c99 -Wall -Iapp\driver\inc -Imodule1\inc -Imodule2\inc module1\src\module1.2.c gcc -o module2\src\module2.1.o -c -O1 -g -std=c99 -Wall -Iapp\driver\inc -Imodule1\inc -Imodule2\inc module2\src\module2.1.c gcc -o module2\src\module2.2.o -c -O1 -g -std=c99 -Wall -Iapp\driver\inc -Imodule1\inc -Imodule2\inc module2\src\module2.2.c gcc -o build\main.exe app\driver\src\driver1.1.o app\driver\src\driver2.2.o module1\src\module1.1.o module1\src\module1.2.o module2\src\module2.1.o module2\src\module2.2.o app\main.o objcopy -v -O binary build\main.exe build\main.bin copy from `build\main.exe' [pei-i386] to `build\main.bin' [binary] objdump --source --all-headers --demangle --line-numbers --wide build\main.exe > build\main.lst size --format=berkeley build\main.exetext data bss dec hex filename39620 2944 2676 45240 b0b8 build\main.exe scons: done building targets.D:\test\sample_4>scons -c scons: Reading SConscript files ... scons: done reading SConscript files. scons: Cleaning targets ... Removed app\driver\src\driver1.1.o Removed app\driver\src\driver2.2.o Removed app\main.o Removed module1\src\module1.1.o Removed module1\src\module1.2.o Removed module2\src\module2.1.o Removed module2\src\module2.2.o Removed build\main.exe Removed build\main.bin Removed build\main.lst scons: done cleaning targets.轉自:Scons環境搭建和編譯原理概述及嵌入式開發常用模板_CoderBob的博客-CSDN博客_scons源碼解析
總結
以上是生活随笔為你收集整理的Scons环境搭建和编译原理概述及嵌入式开发常用模板的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java lcs_LCS最长公共子序列j
- 下一篇: 步进电机控制器的设计