目录

1、Makefile简介... 3

2、编写通用Makefile文件... 3

3、编译进内核或编译成模块... 6

4、Android.mk文件编写... 7

 

 

1、Makefile简介

       Makefile是完成一个程序的整个编译过程。程序的编译过程主要分为1,预处理,2,编译,3,汇编,4,链接。

       当执行make命令时,编译器会首先查找当前的目录下的Makefile,然后根据当前Makefile文件中的规则来进行编译。

规则:
目标:依赖1 依赖2
命令
说明:使用命令,将依赖生成目标

注意:目标顶格写,命令前有一个TAB键,不能使用空格代替
命令执行条件:
a. “依赖”文件 比 “目标”文件 新
b. 没有“目标”这个文件

满足上述条件之一,命名就会执行

2、编写通用Makefile文件

    当我们完成驱动文件的编写后,我们接下来的步骤就是如何把我们编写的驱动可以编译进内核还是编译成模块,通过我们的U盘挂载或者ADB模式push到我们的内核。

 

       1.编译成单独的模块:我们可以选择通过编写单独Makefile的方式使我们编写的驱动文件生成我们所需要的.ko文件。

Makefile及Android.mk编写及如何编译进内核

     上面这图就是我当时编写的Makefile文件。

首先第一步我们需要指定我们需要编译的文件吧也就是我们顶层要生成的.o文件,在这里就是其实就是我们所编写的驱动文件。

       接下来我们需要指定KERNELDIR的路径了,也就是我们当前的内核库文件的路径,原因是我们的驱动文件编写的时候可定要用到内核提供的一些库函数,而这些库函数也是有具体实现的,在连接成一个内核模块时要说明这些库文件在哪里,方便链接程序把它们连接成一个完成的模块。但是如果你把我们当前的Makefile文件与驱动文件的文件夹放入到我们的driver/目录下也是可以的,但你要在上层文件夹得Makefile加入我们自己的文件夹。原因是顶层Makefile也会对我们的KERNELDIR文件进行赋值操作的。

 

       android编译ko包实际与linux编译ko没有区别,首先编写.c .h文件等,之后编写makefile文件,makefile中KERNEL_DIR为kernel编译后产生的临时文件夹的目录,有些系统工程会重定向生成的内核临时文件目录,所以不能简单的吧KERNEL_DIR定义为kernel源文件目录。而是应该确定生成的临时文件的目录后使用临时文件目录

 

       PWD在此处的定义就是指定我们生成文件的路径。在此处我们就是指定我们当前文件夹的位置。

       ARCH=arm这个就是说我们编译出来的文件是适用于arm体系架构的。

       CROSS_COMPILE这个就是指定我们的交叉编译环境,即交叉编译器的前缀(prefix),也就是选择将代码编译成目标cpu的指令的工具。在内核中我们的编译工具都放在源码目录的prebuilts目录下。

       CC就是定义我们要是用的交叉编译工具了,$(CROSS_COMPILE)就是取出我们上一行指定的交叉编译器的前缀,后面加上gcc就是指示我们当前的交叉编译工具是arm-eabi-gcc。

       LD就是指定我们的链接工具。

       CFLAGS_MODULE 指代的是编译模块的一些选项,此处需要在后面指定为 -fno-pic,如果不指定,会当我们在内核中insmod我们生成的.ko文件时会报错,导致加载不进内核的现象。

 

       all:在此处代表的是当我们键入make命令时会做的一系列操作。

       make -C $(KERNELDIR) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) M=$(PWD) modules

       通过执行本条命令后我们就可以在我们当前的文件夹内找到生成的.ko文件。

 

       生成的.ko文件我们可以通过ADB push或者U盘挂载的方式导入到内核中,再通过insmod命令加载进内核中,就可以/dev目录下找到我们生成的设备节点。

 

3、编译进内核或编译成模块

       编译成模块或直接编译进内核

       1.将我们编写完成的.c文件放入/driver/下对应的驱动文件夹内

       2.修改当前文件夹内的Makefile。

       如图所示:进入Makefile。新增一行依赖:

Makefile及Android.mk编写及如何编译进内核

       obj-$(CONFIG_MY_GPIO) += (驱动文件).O

       进入同级目录Kconfig,在最下面新增

       config MY_GPIO

     tristate "注释,相当于make menuconfig是选中本行时的备注信息" 

     tristate 三态,及本文件既可以编译进内核,也可以编译进内核,或者不编译。主要看defconfig的选择。

     depends on 代表本文件是否依赖于其他驱动。本次五依赖关系,所以未添加依赖。

        default  默认选项,及代表驱动tristate为选择的情况下,驱动的默认选择

     help  帮助信息

Makefile及Android.mk编写及如何编译进内核

 

       Kconfig文件编写结束后我们就需要到arch/arm/configs找到当前系统使用的defconfig文件进行对驱动的选择。本次进入的是imx_v7_android_defconfig,在最后一行加上我们的驱动信息CONFIG_MY_GPIO=y,代表编译进内核,=m代表编译为模块,=n代表不编译

Makefile及Android.mk编写及如何编译进内核

       再次编译bootimage,将boot.image烧录到内核即可在/dev下找到我们的设备节点。

       驱动文件编写完成后我们需要进行测试程序进行验证我们的驱动是否符合我们的预期目标。

       所以驱动挂载成功后我们需要编写我们的测试程序,测试程序编译完成后呢么如何将它生成可执行程序呢,我们同样可以新编一个Makefile来对我们的程序进行编译,但Android提供个我们一种Android.mk的编译方式来对我们的程序编译成可执行程序。下面是Android.mk的介绍。

4、Android.mk文件编写

       Android.mk是Android提供的一种makefile文件,用来指定诸如编译生成so库名、引用的头文件目录、需要编译的.c/.cpp文件和.a静态库文件。Android.mk其实是把真正的Makefile包装起来,做成了一个对使用者来说很简单的东西。

下面是网上找的一些Android.mk文件的语法介绍:

LOCAL_PATH := $(call my-dir) 
       每个Android.mk文件必须以定义LOCAL_PATH为开始。它用于在开发tree中查找源文件。宏my-dir 则由Build System提供。返回包含Android.mk的目录路径。

include $(CLEAR_VARS) 
       CLEAR_VARS 变量由Build System提供。并指向一个指定的GNU Makefile,由它负责清理很多LOCAL_xxx.
       例如:LOCAL_MODULE, LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES等等。但不清理LOCAL_PATH.
       这个清理动作是必须的,因为所有的编译控制文件由同一个GNU Make解析和执行,其变量是全局的。所以清理后才能避免相互影响。

LOCAL_MODULE模块必须定义,以表示Android.mk中的每一个模块。名字必须唯 一且不包含空格。Build System会自动添加适当的前缀和后缀。例如,foo,要产生动态库,则生成libfoo.so. 但请注意:如果模块名被定为:libfoo.则生成libfoo.so. 不再加前缀

LOCAL_MODULE_PATH :=$(TARGET_ROOT_OUT) 指定最后生成的模块的目标地址

TARGET_ROOT_OUT:根文件系统,路径为out/target/product/generic/root

TARGET_OUT:system文件系统,路径为out/target/product/generic/system

TARGET_OUT_DATA:data文件系统,路径为out/target/product/generic/data

       除了上面的这些,NDK还提供了很多其他的TARGET_XXX_XXX变量,用于将生成的模块拷贝到输出目录的不同路径

       默认是TARGET_OUT

       LOCAL_SRC_FILES := hello-jni.c 

       LOCAL_SRC_FILES变量必须包含将要打包如模块的C/C++ 源码。不必列出头文件,build System 会自动帮我们找出依赖文件。缺省的C++源码的扩展名为.cpp. 也可以修改,通过LOCAL_CPP_EXTENSION

       include $(BUILD_SHARED_LIBRARY) 
       BUILD_SHARED_LIBRARY:是Build System提供的一个变量,指向一个GNU Makefile Script。
       它负责收集自从上次调用 include $(CLEAR_VARS)  后的所有LOCAL_XXX信息。并决定编译为什么。

BUILD_STATIC_LIBRARY :编译为静态库。 
BUILD_SHARED_LIBRARY :编译为动态库 
BUILD_EXECUTABLE    :编译为Native C可执行程序  

BUILD_PREBUILT      :该模块已经预先编译

NDK还定义了很多其他的BUILD_XXX_XXX变量,它们用来指定模块的生成方式。

以下是我自己写的Android.mk文件

Makefile及Android.mk编写及如何编译进内核

    LOCAL_PATH := $(call my-dir) :每个Android.mk文件必须在文件头进行定义      LOCAL_PATH为开始。它用于在开发tree中查找源文件。宏my-dir 则由Build System提供。返回包含Android.mk的目录路径。这个变量不会被$(CLEAR_VARS)清除,因此每个Android.mk只需要定义一次(即使你在一个文件中定义了几个模块的情况下)。

       include :$(CLEAR_VARS) CLEAR_VARS 变量由Build System提供。并指向一个指定的GNU Makefile,由它负责清理很多LOCAL_xxx. 这个清理动作是必须的,因为所有的编译控制文件由同一个GNU Make解析和执行,其变量是全局的。所以清理后才能避免相互影响。

       LOCAL_SRC_FILES : my_camera.c : 指代的是我们所依赖的文件,及我们编写的测试程序源码,不必跟带.h文件,build system会帮我们找出我们所依赖文件。及这是要编译的源代码文件列表。只要列出要传递给编译器的文件,因为编译系统自动为你计算依赖。

       LOCAL_MODULE_PATH :指定的是我们生成的目标及我们最后生成的可执行程序的位置,我这边指定的是TARGET_OUT_DATA ,最后执行完毕后我们的可执行程序会在out/target/product/具体cpu/data/下可以找到我们生成的可执行程序。如图:

Makefile及Android.mk编写及如何编译进内核

    TARGET_ROOT_OUT:根文件系统,路径为out/target/product/generic/root

       TARGET_OUT:system文件系统,路径为out/target/product/generic/system

       TARGET_OUT_DATA:data文件系统,路径out/target/product/generic/data

       除了上面的这些,NDK还提供了很多其他的TARGET_XXX_XXX变量,用于将生成的模块拷贝到输出目录的不同路径

       默认是TARGET_OUT

       LOCAL_CFLAGS += -DBUILD_FOR_ANDROID  相当于在所有源文件中增加一个宏定义#define, 相当于#define BUILD_FOR_ANDROID

       LOCAL_C_INCLUDE += $(LOCAL_PATH) 设置头文件的include路径

    LOCAL_SHARED_LIBRARIES := libutils libc这个模块在运行时要依赖的共享库模块列表,在链接时需要,在生成文件时嵌入的相应的信息。注意:这不会附加列出的模块到编译图,也就是,你仍然需要在Application.mk中把它们添加到程序要求的模块中。即要链接到本模块的静态库动态库。
如果要使用静态库可使用LOCAL_STATIC_LIBRARIES: 
               LOCAL_MODULE :=my_camera 将要生成的模块的名字,在include $(BUILD_XXXXX)之前,必须定义这个变量。此变量必须唯一且不能有空格。

 

       LOCAL_MODULE_TAGS := test   指该模块只在tests版本下才编译

       user: 指该模块只在user版本下才编译

       eng: 指该模块只在eng版本下才编译

       tests: 指该模块只在tests版本下才编译

       optional:指该模块在所有版本下都编译

       include $(BUILD_EXECUTABLE)  是Build System提供的一个变量,指向一个GNU Makefile Script。

       它负责收集自从上次调用 include $(CLEAR_VARS)  后的所有LOCAL_XXX信息。并决定编译为什么。

BUILD_STATIC_LIBRARY:编译为静态库。
BUILD_SHARED_LIBRARY :编译为动态库
BUILD_EXECUTABLE:编译为Native C可执行程序  

此处使用的是BUILD_EXECUTABLE,代表编译为编译为Native C可执行程序 。

 

       此时写完的Android.mk已经可以使用了,在当前目录下执行mm命令后进入out/target/product/generic/data,将我们编译生成的可执行程序通过adb push或者其他方式导入到车机内,修改权限以便于执行后直接执行我们的可执行程序即可验证我们的驱动是否功能符合我们的预期目标。

相关文章: