View on GitHub

富乎 · 地问


avatar
辗转探寻为富乎?《天问》无解向地问!

<<< 返回主页

懒人版Linux驱动Makefile

1、背景

就是懒。看了很多教程示例,自己也亲手写过,觉得见过的Linux驱动Makefile都大同小异, 既不想每次都要在一大片相同之中找不同而浪费精力,也不想重复这种毫无意义的复制粘贴, 于是乎就催生了这样一份通(懒)用(人)版Makefile

2、脚本内容

第一手核心内容先上,原理解析以及完整版脚本内容见后面:

STRIP ?= arm-linux-gnueabihf-strip
NDEBUG ?= y

KERNEL_ROOT ?= ${HOME}/src/linux
ifeq (${DRVNAME},)
    export DRVNAME := $(basename $(notdir $(shell find ./ -name "*.c" | grep -v '_app\.c$$' | head -n 1)))
    ifeq (${DRVNAME},)
        $(error DRVNAME not specified and not deductive)
    endif
endif
obj-m := ${DRVNAME}.o
ccflags-y += -D__VER__=\"${__VER__}\"
ifeq (${NDEBUG},)
    ccflags-y += -O0 -g
endif

APP_NAME ?= $(basename $(shell find ./ -name "*.c" | grep '_app\.c$$' | head -n 1))
APP_OBJS ?= ${APP_NAME}.o
APP_CC ?= arm-linux-gnueabihf-gcc
ifeq (${NDEBUG},)
    APP_DEBUG_FLAGS ?= -O0 -g
else
    APP_DEBUG_FLAGS ?= -O2 -DNDEBUG
endif
APP_CFLAGS ?= -D_REENTRANT -D__VER__=\"${__VER__}\" -fPIC -Wall \
    ${APP_DEBUG_FLAGS} ${APP_DEFINES} ${APP_INCLUDES} ${OTHER_APP_CFLAGS}

all: ${DRVNAME}.ko ${APP_NAME}

${DRVNAME}.ko:
	make -C ${KERNEL_ROOT} M=`pwd` modules
	[ -f $@ ] || mv $$(ls *.ko | head -n 1) $@
	[ -z "${NDEBUG}" ] || ${STRIP} -d $@

${APP_NAME}: ${APP_OBJS}
	${APP_CC} -o $@ -fPIE $^ ${APP_LDFLAGS}
	[ -z "${NDEBUG}" ] || ${STRIP} -s $@

%.o: %.c
	${APP_CC} ${APP_CFLAGS} -c -o $@ $<

debug:
	make NDEBUG=""

clean:
	rm -f ${APP_NAME} ${APP_OBJS}
	make -C ${KERNEL_ROOT} M=`pwd` clean

将以上内容保存到一个名为linux_driver.mk的文件。

3、用法

在详细讲解脚本的原理之前,先讲一下如何使用,以获得一些感性认识, 有助于后面的理解。

3.1 最简单的小测试场景

这种场景最常见,新手入门和老手日常小测试都属于这类,特点是文件少、 代码简单甚至就是玩具代码Toy Code),Hello World就是一个典型例子。 假设你有很多小测试代码,目录结构如下:

/path/to/test/root/directory
    |-- test1
    |       `-- test1.c
    |-- test2
    |       |-- test2.c
    |       `-- test2_app.c
    .   .
    .   .
    .   .
    `-- testN
            `-- testN.c

以上所示的文件其实有一定的规律和命名要求,但现在先不管, 只需知道要为符合这些规律和要求的测试代码写Makefile是很简单的事即可, 基本不必做太多的改动和定制化。如果是做ARM嵌入式Linux驱动的, 甚至不需要写Makefile,而只需要作一个软链接即可, 以test1为例(假设linux_driver.mkLinux源码均保存在~/src目录):

$ ln -s ${HOME}/src/linux_driver.mk /path/to/test1/Makefile

如果是做其他平台的驱动,例如X86,就需要稍微多一点工作。 可以创建一个${HOME}/src/x86_driver.mk,并写入以下内容:

STRIP := strip
APP_CC := gcc
include ${HOME}/src/linux_driver.mk

最后再为每个测试创建一个Makefile软链接,仍以test1为例:

$ ln -s ${HOME}/src/x86_driver.mk /path/to/test1/Makefile

3.2 用于正式项目的场景

这种场景稍微复杂一些,不能直接套用模板,要做一些定制化, 例如组织多个源文件、修改编译参数、定义应用程序或驱动的版本号等, 但也不难。假设项目目录层次如下:

/path/to/project/source/code/directory
    |-- common
    |       `-- __ver__.mk # 内含软件版本号的定义,这里不必深究
    |-- app
    |-- kernel # Linux内核源码目录
    `-- drivers
            |-- driver1
            |       |-- driver1_main.c
            |       |-- driver1_utils.c
            |       |-- driver1_app_main.c
            |       |-- driver1_app_utils.c
            |       `-- Makefile
            |-- driver2 # 内容与driver1相似
            .       .
            .       .
            .       .
            `-- driverN # 内容与driver1相似

先将linux_driver.mk放入以上common目录。

再按实际情况修改每个测试的Makefile,以driver1为例:

KERNEL_ROOT := ${PWD}/../../kernel
DRVNAME := driver1
${DRVNAME}-objs := driver1_main.o driver1_utils.o
APP_NAME := driver1_app
APP_OBJS := driver1_app_main.o driver1_app_utils.o
# 根据需要再设置其它变量:STRIP、APP_CC、APP_DEFINES、……
include ${PWD}/../../common/__ver__.mk
include ${PWD}/../../common/linux_driver.mk

4、原理解析

5、完整脚本

详见懒编程秘笈项目的makefile/linux_driver.mk, 内容与本文的脚本无多大差别,主要是参数和注释更详细,且后续若有更新,仅更新GitHub项目, 本文的脚本内容不会再同步。

此外,由于Markdown插件的影响,本文的某些水平制表符Tab)可能会转成空格, 导致直接复制本文的脚本内容来使用可能会报错,建议直接使用GitHub项目的脚本。

6、更新说明