這裏所說的編譯系統是一種籠統的說法,大體上包含構建系統和編譯工具集合。編譯工具集合就是大家熟悉的編譯器、彙編器、連接器等,該平臺使用的是GCC,具體路徑位於prebuilt/win32/gcc-arm-none-eabi,這裏就不多說了。下面我們主要講講該平臺的構建系統(build system)。
講到構建系統,大家比較熟悉就是makefile了,它通過Makefile語言編寫的腳本,組織代碼、資源,調用編譯工具集合及其它工具,共同完成最終目標的實現。這個最終目標可以是庫文件、可執行的應用軟件、不同格式的可燒寫的嵌入式固件等等。實際上,每個IDE裏都有一套自己的構建工具,比如VS的nmake,注重效率的Ninja,Mac系統的xcode等等。有些軟件工程直接通過一個單一的構建工具完成系統構建,優點是簡單,缺點是缺乏靈活、跨平臺移值工作量大。Linux內核是通過Kconfig和makefile組成構建系統的,kconfig用於配置、make主導生成最終的內核鏡像。對於那些希望實現跨平臺的軟件工程而言,直接使用makefile明顯帶來移值工作的大量增加,爲了解決這個問題,出現如cmake、autotool這樣的工具,這些工具的語言語法相對簡單,通過它們自動生成makefile、nmake、Ninja、xcode所對應的腳本,完成最終目標的構建。也就說,cmake、autotool這樣的工具是位於make、nmake、Ninja、xcode等工具的上層。
UIS8910DM平臺爲了實現固件功能的可靈活配置、同時支持linux和Windows的編譯環境、又能提高編譯效率,採用 KConfig + cmake + Ninja組成它的構建系統。
KConfig
KConfig最大的作用就是實現軟件項目的配置。在linux內核中,KConfig在編譯前會生成2個文件:一個是autoconf.h,用於C代碼的宏定義;一個是???,是makefile配置項。通過這兩個文件,實現對軟件項目的配置。有人會說。通過簡單自己手寫Makefile和.h文件,也可以實現這些功能,這是沒錯的,但這樣會帶來有幾個問題:
- 沒有可視化的工具進行清晰可見的配置。
- 在配置時需要人爲考慮依賴關係,比如需要支持USB storage功能,前提是USB功能,通過KConfig可以清晰得知這種依賴關係,甚至在配置時自動依據這個關係完成相關配置。
- 自動同步Makefile和.h文件關聯性。
當然,KConfig不單單可以導出makefile腳本,還可以導出cmake腳本,本平臺就是通過kconfiglib.py
這個腳本內的功能導出相應的cmake腳本:target.cmake
。
本平臺通過運行menuconfig.bat來對項目進行可視化的配置(命令是 menuconfig.bat <target_name>
),配置完成後會生成或者更新target/<target_name>/
目錄下target.config
文件,通過minconfig.py
腳本可以將target.config
中與默認配置相同的選項清除掉,僅保留與默認配置不一致的項。
在SDK根目錄下的CMakeLists.txt
中,通過以下腳本將KConfig完成的配置轉化爲cmake可以識別的配置:target.cmake
,這一步在cmake ../.. -G Ninja
這一操作中完成的。
# Process and include target config
set(TARGET_CONFIG ${SOURCE_TOP_DIR}/target/${BUILD_TARGET}/target.config)
set(TARGET_CMAKE ${BINARY_TOP_DIR}/target.cmake)
execute_process(
COMMAND python3 ${tools_dir}/cmakeconfig.py ${TARGET_CONFIG} ${TARGET_CMAKE}
WORKING_DIRECTORY ${SOURCE_TOP_DIR}
)
include(${TARGET_CMAKE})
cmake
cmake是一種更高層次的構建工具,也是更爲通用的構建工具,基於它可以構建跨平臺的編譯環境。
本平臺通過cmake/extension.cmake
這個文件擴展很多cmake的功能函數。比如relative_glob、beautify_c_code等等。基於cmake的這些功能,將每個軟件功能模塊各自生成爲一個靜態庫,最後鏈接這些靜態庫生成最終的目標固件。基本上components下的每個子目錄都會生成一個或者多個靜態庫,生成的靜態庫位於out/<project_target>/lib
目錄下。
最終固件的生成腳本位於SDK主目錄的CMakeLists.txt
中,從這段腳本上看,我們可以爲一個項目配置多個不同的NV,以適配不同的RF硬件,該編譯系統能把適用於不同RF硬件的固件都生成出來。
# Create pac for all variants
foreach(nvmvariant ${CONFIG_NVM_VARIANTS}) build_modem_image(${nvmvariant})
set(nvname ${NVM_VARIANT_${nvmvariant}_NVMITEM})
set(pac_config ${out_hex_dir}/${nvmvariant}.json)
set(pac_file ${out_hex_dir}/${BUILD_TARGET}-${nvmvariant}-${nvname}_${BUILD_RELEASE_TYPE}.pac) pac_init_fdl(init_fdl ${pac_config})
pac_nvitem_8910(nvitem_8910 ${pac_config})
if(DEFINED package_file_depends)
set(pac_package_file cfg-pack-cpio -i PACKAGE_FILE -p ${out_hex_dir}/${package_file_cpio} ${pac_config})
endif()
execute_process(
COMMAND python3 ${pacgen_py} ${init_fdl} ${nvitem_8910}
cfg-image -i BOOTLOADER -a ${CONFIG_BOOT_FLASH_ADDRESS} -s ${CONFIG_BOOT_FLASH_SIZE}
-p ${out_hex_dir}/boot.sign.img ${pac_config}
cfg-image -i AP -a ${CONFIG_APP_FLASH_ADDRESS} -s ${CONFIG_APP_FLASH_SIZE}
-p ${out_hex_dir}/${BUILD_TARGET}.sign.img ${pac_config}
cfg-image -i PS -a ${CONFIG_FS_MODEM_FLASH_ADDRESS} -s ${CONFIG_FS_MODEM_FLASH_SIZE}
-p ${out_hex_dir}/${nvmvariant}.img ${pac_config}
${pac_package_file}
cfg-clear-nv ${pac_config}
cfg-phase-check ${pac_config}
cfg-nv -s ${CONFIG_NVBIN_FIXED_SIZE} -p ${out_hex_dir}/${nvmvariant}_nvitem.bin ${pac_config}
dep-gen --base ${SOURCE_TOP_DIR} ${pac_config}
OUTPUT_VARIABLE pac_dep
OUTPUT_STRIP_TRAILING_WHITESPACE
WORKING_DIRECTORY ${SOURCE_TOP_DIR}
)
add_custom_command(OUTPUT ${pac_file}
COMMAND python3 ${pacgen_py} pac-gen ${pac_config} ${pac_file}
DEPENDS ${pacgen_py} ${pac_config} ${pac_dep}
WORKING_DIRECTORY ${SOURCE_TOP_DIR}
)
add_custom_target(${nvmvariant}_pacgen ALL DEPENDS ${pac_file})
endforeach()
爲了在C/C++代碼中引入開關配置,本平臺採用了cmake的configure_file
機制。通過這個機制,可以將KConfig中定義的配置信息(通過target.cmake
)引入到C/C++代碼中。
比如hal_config.h.in中的 #cmakedefine CONFIG_APPIMG_LOAD_FLASH
,如果KConfig中CONFIG_APPIMG_LOAD_FLASH
開啓,則C/C++代碼中CONFIG_APPIMG_LOAD_FLASH
宏就會被定義。
又比如atr_config.h.in中的#cmakedefine CONFIG_ATR_URC_BUFF_SIZE @CONFIG_ATR_URC_BUFF_SIZE@
,在C/C++代碼中的CONFIG_ATR_URC_BUFF_SIZE宏就引用了KConfig中定義的值。
這種做法有個不好地方就是:C/C++代碼中引用這些宏的地方需要手動 include 相應的.h文件(通過.h.in生成的,位於out/<project_target>/include
目錄)。
Ninja
Ninja是一種類似於 make 的構建工具,它的主要特點是通過編譯任務並行組織,大大提高了構建速度。關於 Ninja 的詳細知識請查看相關網頁,這裏就不做描述。因爲在本平臺中,所有需要手動編寫的編譯腳本都是 cmake 腳本,通過 cmake 的 -G 命令將相關的cmake腳本轉化爲 Ninja 腳本,然後通過 Ninja 構建最終的目標固件。
以上是本人關於UIS8910DM平臺編譯系統粗淺描述,不足和有誤的地方,望不吝指教。