【linux】一個通用驅動Makefile-V2-支持編譯多目錄



前言

該 Makefile 已經通過基於內核 Linux5.4 版本驗證通過。

因為編寫這通用驅動 Makefile 時遇到了頭文件指定路徑失敗的問題。使用過 ccflags-y 、INCDIR 、EXTRA_CFLAGS 、-L 等等參數都無效。就是因為我使用了 $(shell pwd)。導致這些參數的路徑都為內核源碼路徑下,而非模塊路徑。后面重新查看內核文檔,看內核的推薦寫法才解決了,使用 $(src) 來獲取模塊源碼路徑。正確指向自定義的頭文件路徑。所以有以下建議:

  • 建議:對於不同的 Linux 內核,應該去該內核文檔看看 makefielKbuild 的語法及特點。(Documentation/kbuild)**

參考連接:

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. 源碼

# @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_APP)



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM