如何編寫一個工程文件夾下通用的Makefile

 

新建工程文件夾,在裏面新建 bsp、imx6ul、obj 和project 這 3 個文件夾,完成之後如圖所示:php

新建的工程根目錄文件夾html

其中 bsp 用來存放驅動文件;imx6ul 用來存放跟芯片有關的文件,好比 NXP 官方的 SDK庫文件;obj 用來存放編譯生成的.o 文件;project 存放 start.S 和 main.c 文件,也就是應用文件;將十二章實驗中的 cc.h、fsl_common.h、fsl_iomuxc.h 和 MCIMX6Y2.h 這四個文件拷貝到文件夾 imx6ul 中;將 start.S 和 main.c 這兩個文件拷貝到文件夾 project 中。咱們前面的實驗中全部的驅動相關的函數都寫到了 main.c 文件中,好比函數 clk_enable、led_init 和 delay,這三個函數能夠分爲三類:時鐘驅動、LED 驅動和延時驅動。所以咱們能夠在 bsp 文件夾下建立三個子文件夾:clk、delay 和 led,分別用來存放時鐘驅動文件、延時驅動文件和 LED 驅動文件,這樣main.c 函數就會清爽不少,程序功能模塊清晰。工程文件夾都建立好了,接下來就是編寫代碼了,其實就是將時鐘驅動、LED 驅動和延時驅動相關的函數從 main.c 中提取出來作成一個獨立的驅動文件 。linux

使用VScode 新建工程,工程名字爲「ledc_bsp」。新建文件 imx6ul.h,而後保存到文件夾 imx6ul 中,新建 bsp_led.h 和 bsp_led.c 兩個文件,將這兩個文件存放到 bsp/led 中,bsp_led.c 裏面就兩個函數 led_init 和 led_switch,led_init 函數用來初始化LED 所使用的IO,led_switch 函數是控制 LED 燈的打開和關閉,這兩個函數都很簡單。函數

新建 bsp_clk.h 和 bsp_clk.c 兩個文件,將這兩個文件存放到 bsp/clk 中,bsp_delay.c 裏面就兩個函數,delay_short 和 delay。在 main.c 中咱們僅僅留下了 main 函數,至此,本例程跟程序相關的內容就所有編寫好了。spa

在工程根目錄下新建 Makefile 和 imx6ul.lds 這兩個文件,建立完成之後的工程如圖所示:code

最終的工程目錄htm

 

在文件 Makefile 中輸入以下所示內容:blog

1 CROSS_COMPILE ?= arm-linux-gnueabihf- 2 TARGET ?= bsp 3 4 CC := $(CROSS_COMPILE)gcc 5 LD := $(CROSS_COMPILE)ld 6 OBJCOPY := $(CROSS_COMPILE)objcopy 7 OBJDUMP := $(CROSS_COMPILE)objdump 8 9 INCDIRS := imx6ul \ 10 bsp/clk \ 11 bsp/led \ 12 bsp/delay 13 14 SRCDIRS := project \ 15 bsp/clk \ 16 bsp/led \ 17 bsp/delay 18 19 INCLUDE := $(patsubst %, -I %, $(INCDIRS)) 20 21 SFILES := $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.S)) 22 CFILES := $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.c)) 23 24 SFILENDIR := $(notdir $(SFILES)) 25 CFILENDIR := $(notdir $(CFILES)) 26 27 SOBJS := $(patsubst %, obj/%, $(SFILENDIR:.S=.o)) 28 COBJS := $(patsubst %, obj/%, $(CFILENDIR:.c=.o)) 29 OBJS := $(SOBJS) $(COBJS) 30 31 VPATH := $(SRCDIRS) 32 33 .PHONY: clean 34 35 $(TARGET).bin : $(OBJS) 36 $(LD) -Timx6ul.lds -o $(TARGET).elf $^ 37 $(OBJCOPY) -O binary -S $(TARGET).elf $@ 38 $(OBJDUMP) -D -m arm $(TARGET).elf > $(TARGET).dis 39 40 $(SOBJS) : obj/%.o : %.S 41 $(CC) -Wall -nostdlib -c -O2 $(INCLUDE) -o $@ {#content}lt; 42 43 $(COBJS) : obj/%.o : %.c 44 $(CC) -Wall -nostdlib -c -O2 $(INCLUDE) -o $@ {#content}lt; 45 46 clean: 47 rm -rf $(TARGET).elf $(TARGET).dis $(TARGET).bin $(COBJS) $(SOBJS)1 CROSS_COMPILE ?= arm-linux-gnueabihf- 2 TARGET ?= bsp 3 4 CC := $(CROSS_COMPILE)gcc 5 LD := $(CROSS_COMPILE)ld 6 OBJCOPY := $(CROSS_COMPILE)objcopy 7 OBJDUMP := $(CROSS_COMPILE)objdump 8 9 INCDIRS := imx6ul \ 10 bsp/clk \ 11 bsp/led \ 12 bsp/delay 13 14 SRCDIRS := project \ 15 bsp/clk \ 16 bsp/led \ 17 bsp/delay 18 19 INCLUDE := $(patsubst %, -I %, $(INCDIRS)) 20 21 SFILES := $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.S)) 22 CFILES := $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.c)) 23 24 SFILENDIR := $(notdir $(SFILES)) 25 CFILENDIR := $(notdir $(CFILES)) 26 27 SOBJS := $(patsubst %, obj/%, $(SFILENDIR:.S=.o)) 28 COBJS := $(patsubst %, obj/%, $(CFILENDIR:.c=.o)) 29 OBJS := $(SOBJS) $(COBJS) 30 31 VPATH := $(SRCDIRS) 32 33 .PHONY: clean 34 35 $(TARGET).bin : $(OBJS) 36 $(LD) -Timx6ul.lds -o $(TARGET).elf $^ 37 $(OBJCOPY) -O binary -S $(TARGET).elf $@ 38 $(OBJDUMP) -D -m arm $(TARGET).elf > $(TARGET).dis 39 40 $(SOBJS) : obj/%.o : %.S 41 $(CC) -Wall -nostdlib -c -O2 $(INCLUDE) -o $@ {#content}lt; 42 43 $(COBJS) : obj/%.o : %.c 44 $(CC) -Wall -nostdlib -c -O2 $(INCLUDE) -o $@ {#content}lt; 45 46 clean: 47 rm -rf $(TARGET).elf $(TARGET).dis $(TARGET).bin $(COBJS) $(SOBJS)

能夠看出本驗的 Makefile 文件要比前面的實驗複雜不少,由於代碼 中的 Makefile 代碼是一個通用 Makefile,咱們之後全部的裸機例程都使用這個 Makefile。使用時候只要將所須要編譯的源文件所在的目錄添加到Makefile 中便可,咱們接下來詳細分析一下 Makefile 源碼:get

第 1~7 行定義了一些變量,除了第 2 行之外其它的都是跟編譯器有關的,若是使用其它編譯器的話只須要修改第 1 行便可。第 2 行的變量 TARGET 目標名字,不一樣的例程確定名字不一同樣。編譯器

第 9 行的變量 INCDIRS 包含整個工程的.h 頭文件目錄,文件中的全部頭文件目錄都要添加到變量INCDIRS 中。好比本例程中包含.h 頭文件的目錄有imx6ul、bsp/clk、bsp/delay 和bsp/led,因此就須要在變量INCDIRS 中添加這些目錄,即:

INCDIRS := imx6ul bsp/clk bsp/led bsp/delay

仔細觀察的話會發現第 9~11 行後面都會有一個符號「\」,這個至關於「換行符」,表示本行和下一行屬於同一行,通常一行寫不下的時候就用符號「\」來換行。在後面的裸機例程中咱們會根據實際狀況來在變量 INCDIRS 中添加頭文件目錄。

第 14 行是變量 SRCDIRS,和變量 INCDIRS 同樣,只是 SRCDIRS 包含的是整個工程的全部.c 和.S 文件目錄。好比本例程包含有.c 和.S 的目錄有 bsp/clk、bsp/delay、bsp/led 和 project,即:

SRCDIRS := project bsp/clk bsp/led bsp/delay

一樣的,後面的裸機例程中咱們也要根據實際狀況在變量 SRCDIRS 中添加相應的文件目錄。

第 19 行的變量 INCLUDE 是用到了函數 patsubst,經過函數 patsubst 給變量 INCDIRS 添加一個「-I」,即:

INCLUDE := -I imx6ul -I bsp/clk -I bsp/led -I bsp/delay

加「-I」的目的是由於 Makefile 語法要求指明頭文件目錄的時候須要加上「-I」。

第 21 行變量 SFILES 保存工程中全部的.s 彙編文件(包含絕對路徑),變量 SRCDIRS 已經存放了工程中全部的.c 和.S 文件,因此咱們只須要從裏面挑出全部的.S 彙編文件便可,這裏藉助了函數 foreach 和函數 wildcard,最終 SFILES 以下:

SFILES := project/start.S

第 22 行變量 CFILES 和變量 SFILES 同樣,只是 CFILES 保存工程中全部的.c 文件(包含絕對路徑),最終CFILES 以下:

CFILES = project/main.c bsp/clk/bsp_clk.c bsp/led/bsp_led.c bsp/delay/bsp_delay.c

第 24 和 25 行的變量 SFILENDIR 和CFILENDIR 包含全部的.S 彙編文件和.c 文件,相比變量 SFILES 和 CFILES,SFILENDIR 和 CFILNDIR 只是文件名,不包含文件的絕對路徑。使用函數 notdir 將 SFILES 和 CFILES 中的路徑去掉便可,SFILENDIR 和CFILENDIR 以下:

SFILENDIR = start.SCFILENDIR = main.c bsp_clk.c bsp_led.c bsp_delay.c

第 27 和 28 行的變量 SOBJS 和 COBJS 是.S 和.c 文件編譯之後對應的.o 文件目錄,默認全部的文件編譯出來的.o 文件和源文件在同一個目錄中,這裏咱們將全部的.o 文件都放到 obj 文件夾下,SOBJS 和 COBJS 內容以下:

SOBJS = obj/start.o

COBJS = obj/main.o obj/bsp_clk.o obj/bsp_led.o obj/bsp_delay.o

第 29 行變量OBJS 是變量SOBJS 和 COBJS 的集合,以下:

OBJS = obj/start.o obj/main.o obj/bsp_clk.o obj/bsp_led.o obj/bsp_delay.o

編譯完成之後全部的.o 文件就所有存放到了 obj 目錄下,如圖所示:

編譯完成後的obj 文件夾

第 31 行的 VPATH 是指定搜索目錄的,這裏指定的搜素目錄就是變量 SRCDIRS 所保存的目錄,這樣當編譯的時候所需的.S 和.c 文件就會在SRCDIRS 中指定的目錄中查找。

第 34 行指定了一個僞目標 clean,僞目標前面文章 Makefile 的時候已經講解過了。

Makefile 文件內容重點工做是找到要編譯哪些文件?編譯的.o文件存放到哪裏?使用到的編譯命令和前面實驗使用的同樣,其實 Makefile 的重點工做就是解決「從哪裏來到哪裏去的」問題,也就是找到要編譯的源文件、編譯結果存放到哪裏?真正的編譯命令很簡潔。

連接腳本 imx6ul.lds 的內容和上一篇文章《經過結構體的方式來定義和使用寄存器地址》同樣,能夠直接使用上一的連接腳本文件。

本文轉自小平頭電子技術社區:https://www.xiaopingtou.cn/article-104184.html

相關文章
相關標籤/搜索