NuttX 編譯系統
轉載請註明出處: http://blog.csdn.net/zhumaill/article/details/244004411 簡介
NuttX 是通過 Makefile 文件組織編譯的。Makefile 文件描述了整個 NuttX 工程的編譯、鏈接等規則,告訴 make 要編譯哪些文件、怎樣編譯以及在什麼條件下編譯。NuttX 沒有使用 Autoconf、 Automake、 CMake、 SCons 等自動化編譯工具,它的 Makefile 文件完全由手工編寫,比那些自動化編譯工具所使用的編譯文件更容易閱讀,更容易理解軟件的編譯過程。正應了那句話:試圖用一種方法使事情變得簡單,結果卻使事情變得更復雜。
2 Makefile 文件組織結構
- nuttx/Makefile: 頂層 Makefile 文件,內容很簡單,根據主機環境,條件包含 Makefile.unix 或 Makefile.win。
- nuttx/Makefile.unix:Linux 環境下的 Makefile 文件。
- nuttx/Makefile.win:Windows 環境下的 Makefile 文件。
- nuttx/Make.defs:從 nuttx/config/<板卡>/<目標配置>/Make.defs 複製而來。
- 各級子目錄下的 Makefile、 Make.defs 和 Make.dep。
3 Makefile 文件包含樹
(頂層目錄是 nuttx)
- ` |<--.config
- |
- | |<--.config
- | |<--tools/Config.mk
- |<--Makefile.unix-|
- Makefile-| | |<--.config
- | |<--Make.defs-|<--tools/Config.mk
- | |<--arch/arm/src/armv7-m/Toolchain.defs
- |
- |<--Makefile.win-(略)
各級子目錄下的 Makefile、 Make.defs 和 Make.dep 並不是通過 include 包含的,而是在執行 Makefile 文件中的 make 命令時調用的。
4 構建目標和選項
4.1 構建目標:
以下是在頂層 Makefile 文件中可用的構建目標的概述:
all
默認目標,按已選擇的輸出格式構建 NuttX 可執行文件。
clean
移除派生對象文件、靜態庫文件、可執行文件和臨時文件,但保留配置和上下文的文件和目錄。還保留 Make.dep 文件。
distclean
除了完成 “clean” 的工作之外,還包括移除所有配置和上下文的文件。本質是將目錄結構還原爲其原始的、未配置的狀態。
4.2 應用程序內務處理目標
APPDIR 變量引用用戶應用程序目錄。如 NuttX 包含的 app/ 目錄,然而,這不被看作是 NuttX 的一部分,可以被一個不同的應用程序目錄替代。在大多數情況下,應用程序目錄在 Makefile 腳本中被看作像任何其它構建目錄。但是,爲方便起見,包含了以下目標,以支持從 NuttX 構建目錄處理用戶應用程序目錄中的內務處理功能。
apps_clean
僅對用戶應用程序目錄執行 clean 操作。
apps_distclean
僅對用戶應用程序目錄執行 distclean 操作。 apps/.config 文件被保留,所以這不是一個完全的 distclean,但多於配置復位。
export
export 目標將打包 NuttX 庫和頭文件到可輸出包。注意事項:(1) 需要對 KERNEL 構建做一些擴展。 (2) tools/mkexport.sh 中的邏輯只支持 GCC,例如,顯式地假定靜態庫生成器爲“ar”。
download
這是一個助手目標,它將重建 NuttX 並將其下載到目標系統,只需一步。此目標的操作完全依賴於用戶 Make.defs 文件中的 DOWNLOAD 命令的實現。如果未定義 DOWNLOAD 命令,它將產生一個錯誤。
4.3 內部目標
以下目標是由 make 邏輯內部使用的,但如果有必要,可以在某些條件下從命令行調用。
depend
創建構建依賴關係,生成 Make.dep 文件和 .depend 文件。(注意:當前在使用 Windows 本地工具鏈的 Cygwin 下不支持構建依賴關係)
context
在每次目標構建時調用 context 目標以確保正確配置了 NuttX。基本配置步驟包括在 include/nuttx 目錄中創建 config.h 和 version.h 頭文件,建立鏈接到配置目錄的符號鏈接。
clean_context
這是 distclean 目標的一部分。它移除由 context 目標創建的所有頭文件和符號鏈接。
4.4 構建選項
當然,任何 make 變量的值都可以從命令行覆蓋。然而,有一個特殊的變量賦值選項可能對你非常有用:
V=1
這是生成詳細級別標誌。如果你在命令行上指定 V=1,你將在構建時看到所使用的確切命令。當添加新的板卡或追蹤編譯時錯誤和警告時可能非常有用。
5 all 目標依賴樹
- ` |<--pass1dep-|<----------------------------------------╮
- |<--pass1deps-| |<--tools/mkdeps$(HOSTEXEEXT) |
- | | | |
- | ╭--╯ |<--$(USERLIBS)(平面構建時爲空) |
- | | |
- | | |<--pass2dep-|<---------------------------------------|
- |<---pass2deps-| |<--tools/mkdeps$(HOSTEXEEXT) |
- | | | | |
- | | | |<--$(NUTTXLIBS)(與配置有關)<--╮ |
- | | | | |
- | | | ╭-----------------------------------╯ |
- | | | | |
- | | | |<--lib/libsched$(LIBEXT)<--sched/libsched$(LIBEXT)<--------|
- | | | |<--lib/libc$(LIBEXT)<--libc/libc$(LIBEXT)<-----------------|
- | | | |<--lib/libmm$(LIBEXT)<--mm/libmm$(LIBEXT)<-----------------|
- all<--$(BIN)-| | | |<--lib/libarch$(LIBEXT)<--$(ARCH_SRC)/libarch$(LIBEXT)<----|
- | | | |<--lib/libcxx$(LIBEXT)<--libxx/libcxx$(LIBEXT)<------------|
- | | | |<--lib/libapps$(LIBEXT)<--$(APPDIR)/libapps$(LIBEXT)<------|
- | | | |<--lib/libnet$(LIBEXT)<--net/libnet$(LIBEXT)<--------------|
- | | | |<--lib/libfs$(LIBEXT)<--fs/libfs$(LIBEXT)<-----------------|
- | | | |<--lib/libdrivers$(LIBEXT)<--drivers/libdrivers$(LIBEXT)<--|
- | | | |<--lib/libbinfmt$(LIBEXT)<--binfmt/libbinfmt$(LIBEXT)<-----|
- | | | |
- | | ╰---------╮ |
- | | | |
- | ╰--------╮ | |
- | | | |
- |<--pass1<--╯ | |
- | | |
- |<--pass2<------╯ |
- |
- ╭---------------------------------------------------------------------------╯
- |
- | |<--check_context
- | |
- | |<--include/nuttx/config.h-|<--$(TOPDIR)/.config
- | | |<--tools/mkconfig$(HOSTEXEEXT)
- | |
- | |<--include/nuttx/version.h-|<--$(TOPDIR)/.version
- | | |<--tools/mkversion$(HOSTEXEEXT)
- | |
- |<--context-|<--include/math.h<--include/nuttx/math.h
- |<--include/float.h<--include/nuttx/float.h
- |<--include/stdarg.h<--include/nuttx/stdarg.h
- |
- | |<--include/arch<-Make.defs
- | |
- | |<--include/arch/board-|<--include/arch<--Make.defs
- | | |<--Make.defs
- | |
- |<--dirlinks-|<--include/arch/chip-|<--include/arch<--Make.defs
- | |<--Make.defs
- |
- |<--$(ARCH_SRC)/board<--Make.defs
- |<--$(ARCH_SRC)/chip<--Make.defs
- |<--include/apps<--Make.defs
6 all 目標編譯過程
6.1 context 目標
因爲 all 目標是默認目標,所以執行無參數的 make 命令即爲編譯 all 目標,首先會嘗試編譯 context 目標,其中,根據 .config 文件生成 config.h,有許多 C 文件包含了 config.h,以獲得用戶配置。注意其中的2個目錄軟鏈接,下文會用到:
$(ARCH_SRC)/board:
將 nuttx/configs/shenzhou/src 目錄鏈接到 nuttx/arch/arm/src/board 目錄。
$(ARCH_SRC)/chip:
將 nuttx/arch/arm/src/stm32 目錄鏈接到 nuttx/arch/arm/src/chip 目錄。
但是因爲在安裝 buildroot 時已經執行過一次 make context,而且執行 make clean 也不會刪除 context 目標所生成的文件,所以這一步沒做任何事。
6.2 pass1dep 目標
- pass1dep: context tools/mkdeps$(HOSTEXEEXT)
- $(Q) for dir in $(USERDEPDIRS) ; do \
- $(MAKE) -C $$dir TOPDIR="$(TOPDIR)" depend ; \
- done
6.3 $(USERLIBS) 目標
平面構建時 $(USERLIBS) 爲空,它也沒做任何事。這樣整個 pass1deps 目標就完成了。
6.4 pass2dep 目標
- pass2dep: context tools/mkdeps$(HOSTEXEEXT)
- $(Q) for dir in $(KERNDEPDIRS) ; do \
- $(MAKE) -C $$dir TOPDIR="$(TOPDIR)" EXTRADEFINES=$(KDEFINE) depend; \
- done
nuttx 目錄下生成 Make.dep 和 .depend 文件的子目錄有:
- nuttx/arch/arm/src
- nuttx/binfmt
- nuttx/configs/shenzhou/src
- nuttx/drivers
- nuttx/fs
- nuttx/libc
- nuttx/libxx
- nuttx/mm
- nuttx/net
- nuttx/sched
6.5 $(NUTTXLIBS) 目標
接下來就編譯 pass2deps 目標中的 $(NUTTXLIBS),這是一個多目標,根據配置的不同,具體的目標也不同。它的工作是生成多個靜態庫文件。這裏生成了10個靜態庫文件:
- nuttx/lib/libapps.a
- nuttx/lib/libarch.a
- nuttx/lib/libbinfmt.a
- nuttx/lib/libc.a
- nuttx/lib/libcxx.a
- nuttx/lib/libdrivers.a
- nuttx/lib/libfs.a
- nuttx/lib/libmm.a
- nuttx/lib/libnet.a
- nuttx/lib/libsched.a
- $(ARCH_SRC)/libarch$(LIBEXT): context
- $(Q) $(MAKE) -C $(ARCH_SRC) TOPDIR="$(TOPDIR)" libarch$(LIBEXT)
- $(BIN) $(KBIN): $(OBJS)
- $(call ARCHIVE, $@, $(OBJS))
AOBJS = $(ASRCS:.S=$(OBJEXT))
ASRCS = $(CHIP_ASRCS) $(CMN_ASRCS)
COBJS = $(CSRCS:.c=$(OBJEXT))
CSRCS = $(CHIP_CSRCS) $(CMN_CSRCS)
nuttx/arch/arm/src/Makefile 包含了 nuttx/arch/arm/src/chip/Make.defs,4個變量 CHIP_ASRCS、 CMN_ASRCS、 CHIP_CSRCS、 CMN_CSRCS 都是在 nuttx/arch/arm/src/chip/Make.defs 中定義的,多次賦值後的變量值如下:
CHIP_ASRCS = (空)
CMN_ASRCS = up_saveusercontext.S up_fullcontextrestore.S up_switchcontext.S vfork.S
CHIP_CSRCS = stm32_allocateheap.c stm32_start.c stm32_rcc.c stm32_lse.c stm32_lsi.c stm32_gpio.c stm32_exti_gpio.c stm32_flash.c stm32_irq.c stm32_timerisr.c stm32_dma.c stm32_lowputc.c stm32_serial.c stm32_spi.c stm32_sdio.c stm32_tim.c stm32_waste.c stm32_ccm.c stm32_i2c.c stm32_idle.c stm32_pmstop.c stm32_pmstandby.c stm32_pmsleep.c stm32_pminitialize.c stm32_eth.c stm32_pwr.c stm32_rtc.c
CMN_CSRCS = up_assert.c up_blocktask.c up_copyfullstate.c up_createstack.c up_mdelay.c up_udelay.c up_exit.c up_initialize.c up_initialstate.c up_interruptcontext.c up_memfault.c up_modifyreg8.c up_modifyreg16.c up_modifyreg32.c up_releasepending.c up_releasestack.c up_reprioritizertr.c up_schedulesigaction.c up_sigdeliver.c up_systemreset.c up_unblocktask.c up_usestack.c up_doirq.c up_hardfault.c up_svcall.c up_vfork.c
從這裏可以查到哪些源文件被編譯,哪些源文件沒有被編譯。
命令 $(call ARCHIVE, $@, $(OBJS)) 打包靜態庫 libarch.a。
現在回到nuttx/Makefile.unix:
- lib/libarch$(LIBEXT): $(ARCH_SRC)/libarch$(LIBEXT)
- $(Q) install $(ARCH_SRC)/libarch$(LIBEXT) lib/libarch$(LIBEXT)
6.6 pass1 目標
- pass1: pass1deps
- ifeq ($(CONFIG_BUILD_2PASS),y)
- $(Q) if [ -z "$(CONFIG_PASS1_BUILDIR)" ]; then \
- echo "ERROR: CONFIG_PASS1_BUILDIR not defined"; \
- exit 1; \
- fi
- $(Q) if [ ! -d "$(CONFIG_PASS1_BUILDIR)" ]; then \
- echo "ERROR: CONFIG_PASS1_BUILDIR does not exist"; \
- exit 1; \
- fi
- $(Q) if [ ! -f "$(CONFIG_PASS1_BUILDIR)/Makefile" ]; then \
- echo "ERROR: No Makefile in CONFIG_PASS1_BUILDIR"; \
- exit 1; \
- fi
- $(Q) $(MAKE) -C $(CONFIG_PASS1_BUILDIR) TOPDIR="$(TOPDIR)" LINKLIBS="$(LINKLIBS)" USERLIBS="$(USERLIBS)" "$(CONFIG_PASS1_TARGET)"
- endif
6.7 pass2 目標
最後編譯pass2
- pass2: pass2deps
- $(Q) $(MAKE) -C $(ARCH_SRC) TOPDIR="$(TOPDIR)" EXTRA_OBJS="$(EXTRA_OBJS)" LINKLIBS="$(LINKLIBS)" EXTRADEFINES=$(KDEFINE) $(BIN)
- $(Q) if [ -w /tftpboot ] ; then \
- cp -f $(BIN) /tftpboot/$(BIN).${CONFIG_ARCH}; \
- fi
- ifeq ($(CONFIG_RRLOAD_BINARY),y)
- @echo "MK: $(BIN).rr"
- $(Q) $(TOPDIR)/tools/mkimage.sh --Prefix $(CROSSDEV) $(BIN) $(BIN).rr
- $(Q) if [ -w /tftpboot ] ; then \
- cp -f $(BIN).rr /tftpboot/$(BIN).rr.$(CONFIG_ARCH); \
- fi
- endif
- ifeq ($(CONFIG_INTELHEX_BINARY),y)
- @echo "CP: $(BIN).hex"
- $(Q) $(OBJCOPY) $(OBJCOPYARGS) -O ihex $(BIN) $(BIN).hex
- endif
- ifeq ($(CONFIG_MOTOROLA_SREC),y)
- @echo "CP: $(BIN).srec"
- $(Q) $(OBJCOPY) $(OBJCOPYARGS) -O srec $(BIN) $(BIN).srec
- endif
- ifeq ($(CONFIG_RAW_BINARY),y)
- @echo "CP: $(BIN).bin"
- $(Q) $(OBJCOPY) $(OBJCOPYARGS) -O binary $(BIN) $(BIN).bin
- endif
- @echo "DUMP: $(BIN).out"
- $(Q) $(OBJDUMP) -x $(BIN) > $(HOME)/$(BIN).out
- @echo "DUMP: $(BIN).S"
- $(Q) $(OBJDUMP) -d -j .text -j .init_section -j .ARM.exidx -j .data -j .bss $(BIN) > $(HOME)/$(BIN).S
- cp -f $(BIN) $(BIN).hex $(BIN).bin $(HOME)
- if !(grep -q ' @echo "DUMP: $(BIN).out"' Makefile.unix); then
- sed -i '/pass2:/,/^$/{
- /^$/i\ @echo "DUMP: $(BIN).out"
- /^$/i\ $(Q) $(OBJDUMP) -x $(BIN) > $(HOME)/$(BIN).out
- /^$/i\ @echo "DUMP: $(BIN).S"
- /^$/i\ $(Q) $(OBJDUMP) -d -j .text -j .init_section -j .ARM.exidx -j .data -j .bss $(BIN) > $(HOME)/$(BIN).S
- /^$/i\ cp -f $(BIN) $(BIN).hex $(BIN).bin $(HOME)
- }' Makefile.unix
- fi
7 nuttx/Make.defs 文件
nuttx/Make.defs 文件是 Makefile 片斷,從 nuttx/config/<板卡>/<目標配置>/Make.defs 複製而來。由《NuttX 安裝腳本》中的以下幾行實現:
- echo "nuttx配置"
- cd $BASEDIR/$TOPDIR/nuttx/tools
- ./configure.sh $TARGETCONFIG
- 工具: CC、 LD、 AR、 NM、 OBJCOPY、 OBJDUMP
- 工具選項: CFLAGS、 LDFLAGS
$(TOPDIR)/.config: Nuttx 配置
$(TOPDIR)/tools/Config.mk: 一般定義
nuttx/Make.defs 文件中的定義可能依賴於 .config 文件中的一些設置。例如,如果 CONFIG_DEBUG=y, CFLAGS將最有可能不同。
tools/Config.mk 文件包含額外的定義,這些定義可能在必要時被架構特定的 Make.defs 文件覆蓋:
COMPILE、 ASSEMBLE、 ARCHIVE、 CLEAN 和 MK