工程目錄
工程目錄如圖,build文件夾是編譯出來的
.
├── app
│ ├── imx6ul.lds
│ ├── main.c
│ ├── makefile
│ └── start.S
├── bsp
│ ├── clk
│ │ ├── bsp_clk.c
│ │ └── bsp_clk.h
│ ├── delay
│ │ ├── bsp_delay.c
│ │ └── bsp_delay.h
│ └── led
│ ├── bsp_led.c
│ └── bsp_led.h
├── build
│ ├── bsp_clk.o
│ ├── bsp_delay.o
│ ├── bsp_led.o
│ ├── led_bsp.bin
│ ├── led_bsp.elf
│ ├── led_bsp.lst
│ ├── main.o
│ └── start.o
└── imx6ul├── cc.h├── fsl_common.h├── fsl_iomuxc.h├── imx6ul.h└── MCIMX6Y2.h
Makefile源碼
# $@ 表示目標文件;
# $^ 表示所有的依賴文件;
# $< 表示第一個依賴文件;# = 延時賦值,該變量只有在調用的時候,才會被賦值;
# := 直接賦值,與延時賦值相反,使用直接賦值的話,變量的值定義時就已經確定了;
# ?= 若變量的值為空,則進行賦值,通常用于設置默認值;
# += 追加賦值,可以往變量后面增加新的內容。# 定義交叉編譯器變量
CROSS_COMPILER ?= arm-linux-gnueabihf-# 定義編譯選項
CFLAGS = -Wall -Wextra -std=c11 -O2# 定義目標文件
TARGET = led_bsp# 定義連接腳本文件路徑
LD_SCRIPT = -T./imx6ul.lds# 定義目標文件夾
BUILD_DIR := ../buildSRC_DIRS := ./
INC_DIRS := ../imx6ul
LIB_DIRS := ../lib# 擴展源文件夾列表
SUBDIRS := clk led delay
SRC_DIRS += $(addprefix ../bsp/, $(SUBDIRS))
VPATH := $(SRC_DIRS)# 擴展頭文件夾列表
INC_DIRS := $(addprefix -I, $(INC_DIRS))
INC_DIRS += $(addprefix -I, $(SRC_DIRS))# 擴展庫文件夾列表
LIB_DIRS := $(addprefix -L, $(LIB_DIRS))# 定義需要鏈接的庫
#LIBS = -lm -lpthread# 查找所有的源文件
SRCS_S := $(shell find $(SRC_DIRS) -name '*.S')
SRCS_C := $(shell find $(SRC_DIRS) -name '*.c')
# 去掉路徑 只保存文件名
NOT_DIR_SRCS_S := $(notdir $(SRCS_S))
NOT_DIR_SRCS_C := $(notdir $(SRCS_C))
# 將.c .S替換成.o
OBJS_S := $(patsubst %.S, $(BUILD_DIR)/%.o, $(NOT_DIR_SRCS_S))
OBJS_C := $(patsubst %.c, $(BUILD_DIR)/%.o, $(NOT_DIR_SRCS_C))
OBJS := $(OBJS_S) $(OBJS_C)# 默認目標 鏈接所有.o
$(BUILD_DIR)/$(TARGET): $(OBJS)$(CROSS_COMPILER)ld $(LD_SCRIPT) $(LIB_DIRS) $(LIBS) -o $@.elf $^ # 編譯成.o
$(OBJS_C) : $(BUILD_DIR)/%.o : %.c$(CROSS_COMPILER)gcc $(CFLAGS) $(INC_DIRS) -c -o $@ $<$(OBJS_S) : $(BUILD_DIR)/%.o: %.S$(CROSS_COMPILER)gcc $(CFLAGS) $(INC_DIRS) -c -o $@ $<# 生成bin文件
bin: $(BUILD_DIR)/$(TARGET)$(CROSS_COMPILER)objcopy -O binary $<.elf $<.bin# 生成反匯編文件
disassemble: $(BUILD_DIR)/$(TARGET)$(CROSS_COMPILER)objdump --source --all-headers --demangle --line-numbers --wide $<.elf > $<.lst# 生成空間信息
size: $(BUILD_DIR)/$(TARGET)$(CROSS_COMPILER)size --format=berkeley $<.elf # all目標生成目標文件、bin文件和反匯編文件
all: $(BUILD_DIR)/$(TARGET) bin disassemble size# 清理規則,用于清除生成的目標文件、可執行文件、二進制文件和反匯編文件
clean:rm -rf $(BUILD_DIR)# 確保編譯目標文件夾存在
$(BUILD_DIR):mkdir -p $(BUILD_DIR)# 編譯之前先創建目標文件夾
$(OBJS): | $(BUILD_DIR).PHONY: bin disassemble size all clean
基本編譯規則
編譯過程
在GCC中,每個編譯階段都有對應的指令來執行。以下是每個階段對應的GCC指令:
-
預處理(Preprocessing):
gcc -E
這個指令告訴GCC只進行預處理,生成預處理后的代碼,而不執行后續的編譯、匯編和鏈接步驟。生成的預處理文件通常以.i
為擴展名 -
編譯(Compiling):
gcc -S
這個指令告訴GCC只進行編譯,將源代碼轉換為匯編代碼,而不執行匯編和鏈接步驟。生成的匯編語言文件通常以.s
為擴展名 -
匯編(Assembling):
gcc -c
這個指令告訴GCC只進行匯編,將匯編語言代碼轉換為目標文件,而不執行鏈接步驟。生成的目標文件通常以.o
為擴展名 -
鏈接(Linking):
ld
這個指令執行完整的編譯鏈接過程,將多個目標文件以及可能的庫文件鏈接在一起,生成最終的可執行文件。可通過指定輸入的目標文件和庫文件來完成鏈接過程,并使用-o
選項來指定輸出文件名
Makefile基本規則
規則定義了如何根據源文件生成目標文件,規則通常由三個部分組成:目標(target)、依賴關系(dependencies)和命令(commands)
以下是規則的一般格式:
codetarget: dependenciescommand
- 依賴全部存在時,才會執行command
- 程序會從默認規則開始執行,也是為了生成第一個目標(一般都是可執行文件)
- 當依賴不存在時,就會自動去別的規則中的目標中去尋找,以此類推
.PHONY
聲明了偽目標,例如make all
,這樣做的好處是,可以避免與同名的文件或目錄發生沖突,同時確保這些目標能夠被正確地執行
常用函數和語句
sheel
result := $(shell command)
會創建一個shell
進程執行command
,并將結果返回到result
需要注意執行效率,此函數可能會被多次調用到
wildcard(匹配文件名并返回列表)
$(wildcard pattern)
其中 pattern
是一個文件名模式,可以包含通配符(例如 *
和 ?
)
wildcard
函數會返回匹配 pattern
的文件列表,如果沒有匹配的文件,則返回空字符串。
以下是一個示例:
SRCS := $(wildcard src/*.c)
在這個示例中,$(wildcard src/*.c)
將匹配 src
目錄下所有以 .c
結尾的文件,并返回這些文件的列表給 SRCS
變量
foreach (遍歷執行)
用于在每個元素上執行操作,它的語法如下:
$(foreach var, list, text)
var
是一個變量名,用于保存列表中的每個元素list
是一個空格分隔的元素列表text
是要執行的文本
foreach
函數會遍歷列表中的每個元素,并將當前元素依次賦值給 var
,然后執行 text
中的命令。在每次迭代中,text
中可以使用 var
來引用當前元素
在這個示例中, 會遍歷 FILES
列表中的每個文件名,并將其打印出來
FILES := file1.txt file2.txt file3.txt
$(foreach file, $(FILES), $(info File: $(file)))
addprefix(給列表加前綴)
用于在每個元素前添加一個前綴,它的語法如下:
makefileCopy code
$(addprefix prefix,names...)
prefix
是要添加的前綴names
是一個空格分隔的元素列表
addprefix
函數會將 prefix
添加到 names
列表中的每個元素前面,并返回修改后的列表以下是一個示例:
makefileCopy codeFILES := file1.txt file2.txt file3.txt
$(addprefix prefix_, $(FILES))
在這個示例中,$(addprefix prefix_, $(FILES))
會將 FILES
列表中的每個文件名前面都添加 prefix_
前綴,得到一個新的列表。結果將是 prefix_file1.txt prefix_file2.txt prefix_file3.txt
查找所有源文件
第一步先是通過addprefix
生成所有包含源文件的目錄,將其存在SRC_DIRS
中,并借助info
將其打印出來
SUBDIRS := clk led delay
SRC_DIRS += $(addprefix ../bsp/, $(SUBDIRS))$(info SRC_DIRS : $(SRC_DIRS))#結果如下
SRC_DIRS : ./ ../bsp/clk ../bsp/led ../bsp/delay
使用 find
? 通過如下命令即可找出SRC_DIRS
中的所有.c
文件
SRCS_C := $(shell find $(SRC_DIRS) -name '*.c')$(info SRCS_C : $(SRCS_C))#結果如下
SRCS_C : ./main.c ../bsp/clk/bsp_clk.c ../bsp/led/bsp_led.c ../bsp/delay/bsp_delay.c
?
附上find
的常見用法:
-
在當前目錄及其子目錄中搜索所有文件:
find
-
在指定目錄中搜索所有文件:
find /path/to/directory
-
在當前目錄及其子目錄中搜索所有
.txt
文件:find -name "*.txt"
-
在當前目錄及其子目錄中搜索所有大于 1MB 的文件:
find -size +1M
-
在當前目錄及其子目錄中搜索所有修改時間在一天之內的文件:
find -mtime -1
-
在當前目錄及其子目錄中搜索所有空文件:
find -type f -empty
find
命令非常強大,它支持多種匹配條件和動作,可以根據實際需求來進行靈活的文件搜索和操作
使用wildcard和foreach
通過如下命令即可找出SRC_DIRS
中的所有.c
文件
SRCS_C := $(foreach dir, $(SRC_DIRS), $(wildcard $(dir)/*.c))$(info SRCS_C : $(SRCS_C))#結果如下
SRCS_C : ./main.c ../bsp/clk/bsp_clk.c ../bsp/led/bsp_led.c ../bsp/delay/bsp_delay.c
notdir
用于從文件路徑中提取文件名部分,去除路徑。它的語法如下:
$(notdir names...)
其中 names
是一個空格分隔的文件路徑列表
notdir
函數會返回 names
中每個文件路徑的文件名部分,去除路徑信息
以下是一個示例:
SRCS := src/main.c src/foo.c src/bar.c
FILE_NAMES := $(notdir $(SRCS))
在這個示例中,$(notdir $(SRCS))
將提取 SRCS
中每個文件路徑的文件名部分,結果將是 main.c foo.c bar.c
patsubst
用于在字符串中執行模式替換操作。它的語法如下:
$(patsubst pattern, replacement, text)
其中:
pattern
是要匹配的模式replacement
是用于替換匹配模式的文本text
是要進行模式替換的原始文本
patsubst
函數會查找 text
中與 pattern
匹配的部分,并將它們替換為 replacement
以下是一個示例:
SRCS := file1.c file2.c file3.c
OBJS := $(patsubst %.c, %.o, $(SRCS))
在這個示例中,$(patsubst %.c,%.o,$(SRCS))
將把 SRCS
中的 .c
文件名替換為 .o
文件名,生成的 OBJS
變量會包含 file1.o file2.o file3.o
調用其他 Makefile
在 Makefile 中,可以通過 include
指令來引入其他的 Makefile。這樣做的主要目的是為了將大型項目拆分成更小的模塊,每個模塊對應一個獨立的 Makefile,方便管理和維護。同時,通過模塊化的方式,可以提高代碼的重用性和可維護性。
在 Makefile 中使用 include
指令的語法如下:
include other_makefile.mk
其中 other_makefile.mk
是要包含的其他 Makefile 的文件名。
通過引入其他的 Makefile,可以將項目拆分成多個模塊,每個模塊負責特定的功能或任務。這樣做有以下幾個優點:
- 模塊化管理:將項目拆分成多個模塊后,每個模塊可以獨立管理,減少了代碼的耦合度,方便團隊協作和代碼維護
- 代碼重用:通過將通用的功能和規則封裝在單獨的模塊中,可以提高代碼的重用性,避免重復編寫相同的代碼
- 可維護性:將項目拆分成多個模塊后,每個模塊的功能更加清晰明確,易于理解和維護。同時,可以針對每個模塊進行單獨的測試和調試
總之,通過引入其他的 Makefile,可以將大型項目拆分成更小的模塊,提高了代碼的組織性、可維護性和重用性,是一種良好的編程實踐。