前言

该 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. 源码

  1. # @file         Makefile
  2. # @brief        驱动。
  3. # @details      led 驱动模块 Makefile 例程。
  4. # @author       lzm
  5. # @date         2021-03-14 10:23:03
  6. # @version      v1.1
  7. # @copyright    Copyright By lizhuming, All Rights Reserved
  8. #
  9. # ********************************************************
  10. # @LOG 修改日志:
  11. # ********************************************************
  12. # 编译后内核路径
  13. KERNEL_DIR = /home/lss/work/kernel/imx6/ebf-buster-linux/build_image/build
  14. # 定义框架
  15. # ARCH 为 x86 时,编译链头为 
  16. # ARCH 为 arm 时,编译链头为 arm-linux-gnueabihf-
  17. ARCH = arm
  18. ifeq ($(ARCH),x86)
  19. CROSS_COMPILE = # 交叉编译工具头,如:
  20. else
  21. CROSS_COMPILE = arm-linux-gnueabihf-# 交叉编译工具头,如:arm-linux-gnueabihf-
  22. endif
  23. CC      = $(CROSS_COMPILE)gcc # 编译器,对 C 源文件进行编译处理,生成汇编文件
  24. # 共享到sub-Makefile
  25. export  ARCH  CROSS_COMPILE
  26. # 路径
  27. PWD := $(shell pwd)
  28. # 当前模块路径
  29. # $(src) 是内和文件定义并传过来的当前模块 M= 的路径。
  30. MODDIR := $(src)
  31. # 注意:驱动目标不要和文件名相同
  32. TARGET_DRV := led_device_driver
  33. TARGET_APP := led_app
  34. # 本次整个编译需要源 文件 和 目录
  35. # 这里的“obj-m” 表示该模块不会编译到zImage ,但会生成一个独立的xxx.ko 静态编译(由内核源码顶层Makefile识别)
  36. # 模块的多文件编译:obj-m 是告诉 makefile 最总的编译目标。而 $(TARGET)-y 则是告诉 makefile 该总目标依赖哪些目标文件。(也可以使用 xxx-objs)
  37. $(TARGET_DRV)-+= led_module.o
  38. $(TARGET_DRV)-+= ./device/led_dev_a.o
  39. $(TARGET_DRV)-+= ./driver/led_drv.o
  40. obj-:= $(TARGET_DRV).o
  41. # obj-m += $(patsubst %.c,%.o,$(shell ls *.c))
  42. # 编译条件处理
  43. # 指定头文件 由于该文件是 -C 后再被调用的,所以部分参数不能使用 $(shell pwd)
  44. # $(src) 是内和文件定义并传过来的当前模块 M= 的路径。
  45. ccflags-:= -I$(MODDIR)/include
  46. ccflags-+= -I$(MODDIR)/device
  47. ccflags-+= -I$(MODDIR)/driver
  48. # 第一个目标 CURDIR 是该makefile内嵌变量,自动设置为当前目录
  49. all :
  50.     @$(MAKE) -C $(KERNEL_DIR) M=$(CURDIR)  modules
  51. #   make mobailes 就是是编译模块,上面是其添加参数的指令
  52. #   $(CROSS_COMPILE)gcc -o $(TARGET_APP) $(TARGET_APP).c
  53.     
  54. # 清理
  55. .PHONY:clean
  56. clean:
  57.     $(MAKE)  -C $(KERNEL_DIR) M=$(CURDIR) clean
  58. #   rm $(TARGET_APP)

一个通用驱动Makefile-V2-支持编译多目录的更多相关文章

  1. 编写一个通用的Makefile文件

    1.1在这之前,我们需要了解程序的编译过程 a.预处理:检查语法错误,展开宏,包含头文件等 b.编译:*.c-->*.S c.汇编:*.S-->*.o d.链接:.o +库文件=*.exe ...

  2. Linux C编程学习之开发工具3---多文件项目管理、Makefile、一个通用的Makefile

    GNU Make简介 大型项目的开发过程中,往往会划分出若干个功能模块,这样可以保证软件的易维护性. 作为项目的组成部分,各个模块不可避免的存在各种联系,如果其中某个模块发生改动,那么其他的模块需要相 ...

  3. 一个通用的Makefile(二)

    1.各级子目录的Makefile: obj-y += file.o obj-y += subdir/ “obj-y += file.o” 表示把当前目录下的file.c编进程序里. “obj-y += ...

  4. 14、编写一个通用的Makefile

    编译test_Makefile的方法:a. gcc -o test a.c b.c对于a.c: 预处理.编译(C文件转换成汇编).汇编(汇编转换成机器码)对于b.c:预处理.编译.汇编最后链接优点:命 ...

  5. 一步一步写一个简单通用的makefile(四)--写一个通用的makefile编译android可执行文件

    通常要把我们自己的的代码编译成在android里面编译的可执行文件,我们通常是建一个文件夹 . ├── Android.mk ├── Application.mk ├── convolve.cl ├─ ...

  6. 一个通用的Makefile (转)

    据http://bbs.chinaunix.net/thread-2300778-1-1.html的讨论,发现还是有很多人在问通用Makefile的问题,这里做一个总结.也作为以后的参考.       ...

  7. 我所使用的一个通用的Makefile模板

    话不多说,请看: 我的项目有的目录结构有: dirls/ ├── include │   └── apue.h ├── lib │   ├── error.c │   ├── error.o │   ...

  8. 一个简单的makefile,一次性编译本文件夹下所有的cpp文件

    代码: CXX := g++ CFLAGS := -g TARGET := xxx.exe SRCS := $(wildcard *.cpp) OBJS := $(patsubst %cpp,%o,$ ...

  9. 一个通用的makefile(一)

    最近在编写Android编译系统时,需要遍历每一个目录下每一个文件夹下的makefile,网上的方法有些繁琐 :就直接贴上自己遍历子目录深度为1:(for  temporary)(之后会继续更新) 下 ...

随机推荐

  1. MyBatis中传参时为什么要用#{}

    MyBatis中传参时为什么要用#{},这个问题和MyBatis如何防止SQL注入类似.不过在解释这个问题之前,先解释一下什么是SQL注入,还有些称作注入攻击这个问题. SQL注入就是SQL 对传入参 ...

  2. vector最最最基础用法(非原创)

    在c++中,vector是一个十分有用的容器,下面对这个容器做一下总结. 1 基本操作 (1)头文件#include<vector>. (2)创建vector对象,vector<in ...

  3. JavaScript事件绑定的三种方式

    (一)事件绑定的三种方式 (1)通过on的形式 <script type="text/javascript"> var div = document.getElemen ...

  4. Single Round Math sdut3260高精度除以低精度

    做高精度除法,从高位开始除..高位除剩下的我们就*10扔给低一位处理,最终余数是在最低位取模得到的 高精除以高精,我们可以这么做,让除数在后面补零,刚好小于被除数,作若干次减法,减的次数加到商里面 然 ...

  5. CodeForces 1047C Enlarge GCD(数论)题解

    题意:n个数的gcd是k,要你删掉最少的数使得删完后的数组的gcd > k 思路:先求出k,然后每个数除以k.然后找出出现次数最多的质因数即可. 代码: #include<cmath> ...

  6. 翻译:《实用的Python编程》01_03_Numbers

    目录 | 上一节 (1.2 第一个程序) | 下一节 (1.4 字符串) 1.3 数字 本节讨论数学计算. 数字类型 Python 有 4 种类型的数字: 布尔型 整型 浮点型 复数(虚数) 布尔型( ...

  7. VuePress 最新教程

    VuePress 最新教程 https://vuepress.vuejs.org/ https://github.com/vuejs/vuepress VuePress plugins 插件通常会为 ...

  8. MBP 2018

    MBP 2018 touch pad MacBook Pro 如何调节键盘背光 https://support.apple.com/zh-cn/HT202310 F6 & F5 如何清洁 Ma ...

  9. ORM All In One

    ORM All In One ORM Object Relational Mapping https://en.wikipedia.org/wiki/Object-relational_mapping ...

  10. subline3 如何设置es6高亮

    步骤: 1.操作Ctrl+Shift+P , 然后在弹出的框内输入Package Control: in,2.选择Package Control: install package,3.等待再次弹出输入 ...