1. 程式人生 > >[linux]一個通用驅動Makefile-V2-支援編譯多目錄

[linux]一個通用驅動Makefile-V2-支援編譯多目錄

[toc] --- ## 前言 該 Makefile 已經通過基於核心 **Linux5.4** 版本驗證通過。 因為編寫這通用驅動 Makefile 時遇到了標頭檔案指定路徑失敗的問題。使用過 **ccflags-y 、INCDIR 、EXTRA_CFLAGS 、-L** 等等引數都無效。就是因為我使用了 **`$(shell pwd)`**。導致這些引數的路徑都為核心原始碼路徑下,而非模組路徑。後面重新檢視核心文件,看核心的推薦寫法才解決了,使用 $(src) 來獲取模組原始碼路徑。正確指向自定義的標頭檔案路徑。所以有以下建議: * **建議**:對於不同的 **Linux** 核心,應該去該核心文件看看 **makefiel** 和 **Kbuild** 的語法及特點。(***Documentation/kbuild***)** 參考連線: [李柱明部落格園](https://www.cnblogs.com/lizhuming/p/14539434.html) [核心文件html](https://www.kernel.org/doc/html/latest) ## 1. 特點 1. 支援編譯多目錄Linux核心驅動 2. 支援多目錄原始檔及標頭檔案編譯 3. 相容性高,修改介面巨集即可 ## 2. 分析 ### 2.1 簡要原理 由於驅動程式中包含了很多來自核心的標頭檔案,編譯驅動程式時需要指定板子所用的同一版本的核心原始碼(*編譯後,以下無特別說明也是編譯後*)路徑。 簡要原理其實是主要由核心原始碼中的 **Makefile** 來進行編譯並生成驅動檔案。所以當前 Makefile 只需要**提供引數和跳轉到核心原始碼路徑執行其頂層 Makefile** 即可。 ### 2.2 具體分析 **核心路徑**: **`KERNEL_DIR = /home/lss/work/kernel/imx6/ebf-buster-linux/build_image/build`**:指出編譯後的核心原始碼路徑。 **架構及編譯器**: **`ARCH = arm`**:提供架構名稱。 **`CROSS_COMPILE = arm-linux-gnueabihf-`**:提供交叉編譯器名稱。 **`CC = $(CROSS_COMPILE)gcc`**:用於測試用例APP使用的編譯器(***無測試用例可遮蔽***)。 **`export ARCH CROSS_COMPILE`**:共享架構名稱及交叉編譯器名稱到 sub-Makefile,這裡即是核心原始碼中的頂層Makefile及其sub-Makefile。 **路徑變數**: **`PWD := $(shell pwd)`**:執行時的 make 路徑。**並不是當前檔案路徑**。 **`MODDIR := $(src)`**:當前模組的頂層路徑。 * 核心原始碼原話:$(src) provides the absolute path by pointing to the directory where the currently executing kbuild file is located. * Kbuild可以看作Makefile。(*雖然不嚴謹*) * $(src) 是由核心 Makefile 提供的。為當前被執行的 子Makefile 的絕對路徑。在這裡也可以看出 M= 的路徑。 * 因為當核心頂層 Makefile 使用 -C 跳到核心頂層 Makefile ,如果使用 **`$(shell pwd)`** 的話,該命令的值為 核心頂層 Makefile 的絕對路徑,而不是核心頂層 Makefile 的路徑。( ***$(MAKE) -C $(KERNEL_DIR) M=$(CURDIR)  modules 中的 M=$(CURDIR) 例外,因為它在當前 Makefile 生效後再跳轉到 -C 的*** ) **目標**: **`TARGET_DRV := led_device_driver`**:模組目標名稱。 **`TARGET_APP := led_app`**:測試用例目標名稱。 **資原始檔和模組目標定義** **`$(TARGET_DRV)-y += led_module.o`**:目標模組所需原始檔。 **`$(TARGET_DRV)-y += ./device/led_dev_a.o`**:目標模組所需原始檔。 **`$(TARGET_DRV)-y += ./driver/led_drv.o`**:目標模組所需原始檔。 **`obj-m := $(TARGET_DRV).o`**:目標。告訴核心要編譯成模組。 * **obj-y**:編譯驅動到核心。 **obj-m**:編譯驅動為模組。 **obj-n**:不編譯。 * **驅動模組的多原始檔編譯**:obj-m := $(TARGET).o 是告訴 makefile 最總的編譯目標。而 $(TARGET)-y 則是告訴 makefile 該總目標依賴哪些目標檔案。(為固定格式,如總目標為 **`xxx.o`**,那麼它依賴的原始檔應該這樣指定 **`xxx-y += `**)(也可以使用 xxx-objs) **編譯引數**: **`ccflags-y := -I$(MODDIR)/include`**:指定自定義標頭檔案路徑。這裡只能使用 $(src) 來獲取模組檔案路徑。 * External modules tend to place header files in a separate include/ directory where their source is located, although this is not the usual kernel style. To inform kbuild of the directory, use either **ccflags-y** or **CFLAGS_.o**. **目標 all**: **`$(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) modules`**:原型是 **`make modules`**,即是編譯模組目標,其它都是**引數**。 * **-C $(KERNEL_DIR)**:把工作目錄跳轉到核心原始碼中。 * **M=$(CURDIR)**:表示編譯模組時,可以到該路徑尋找模組原始碼進行編譯。 * **推薦:觀看核心文件 *Documentation/kbuild/modules.rst*** ## 3. 原始碼 ```makefile # @file         Makefile # @brief        驅動。 # @details      led 驅動模組 Makefile 例程。 # @author       lzm # @date         2021-03-14 10:23:03 # @version      v1.1 # @copyright    Copyright By lizhuming, All Rights Reserved # # ******************************************************** # @LOG 修改日誌: # ******************************************************** # 編譯後核心路徑 KERNEL_DIR = /home/lss/work/kernel/imx6/ebf-buster-linux/build_image/build # 定義框架 # ARCH 為 x86 時,編譯鏈頭為  # ARCH 為 arm 時,編譯鏈頭為 arm-linux-gnueabihf- ARCH = arm ifeq ($(ARCH),x86) CROSS_COMPILE = # else CROSS_COMPILE = arm-linux-gnueabihf-# endif CC      = $(CROSS_COMPILE)gcc # # 共享到sub-Makefile export  ARCH  CROSS_COMPILE # 路徑 PWD := $(shell pwd) MODDIR := $(src) # 注意:驅動目標不要和檔名相同 TARGET_DRV := led_device_driver TARGET_APP := led_app # 本次整個編譯需要源 檔案 和 目錄 $(TARGET_DRV)-y += led_module.o $(TARGET_DRV)-y += ./device/led_dev_a.o $(TARGET_DRV)-y += ./driver/led_drv.o obj-m := $(TARGET_DRV).o # obj-m += $(patsubst %.c,%.o,$(shell ls *.c)) # 編譯條件處理 ccflags-y := -I$(MODDIR)/include ccflags-y += -I$(MODDIR)/device ccflags-y += -I$(MODDIR)/driver # 第一個目標 all :     @$(MAKE) -C $(KERNEL_DIR) M=$(CURDIR)  modules #   $(CROSS_COMPILE)gcc -o $(TARGET_APP) $(TARGET_APP).c      # 清理 .PHONY:clean clean:     $(MAKE)  -C $(KERNEL_DIR) M=$(CURDIR) clean #   rm $(TARGET_AP