Makefile小记

本文介绍了Makefile的基础知识,包括它的作用、为什么要使用它以及基本的语法。通过示例解释了如何自动化编译程序,避免手动编译的繁琐。此外,还提到了Android.mk中的特定变量及其在Android开发中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

###小记

就在前几天自己找到了应该算是正式的第一份工作,其中过程很辛苦但也很幸运,自己也开始步入社会,接受社会的洗礼,总之希望自己可以走的更远,更优秀,并让优秀成为一种习惯。
由于工作业务需要,最近在学习 Makefile ,由于以前没怎么接触过,linux 也基本上没怎么用过,所以接下来会恶补一些这方面的知识,并记录在此,当然也会分享一些自己觉得好的前辈们的文章,欢迎大家一起前来交流学习,共同进步。

###概述

1. 什么是 Makefile ?

首先,它关系到了整个工程的编译规则。我们知道一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作。使用它通常可以让一个文件变成另一个文件,在大多数情况下,我们主要用它来编译源代码,生成结果代码,然后把结果代码连接起来生成可执行文件或者库文件。

举个例子:假设有一个主程序代码(main.c)、三份函数代码(a.c、b.c、c.c)以及一个头文件(d.h)。通常情况下,我们需要这样编译它:

gcc -o d main.c a.c b.c c.c 

如果没有makefile,在开发+调试程序的过程中,我们就需要不断地重复输入上面这条编译命令,要不就是通过终端的历史功能不停地按上下键来寻找最近执行过的命令。这样做两个缺陷:

  1. 一旦终端历史记录被丢失,我们就不得不从头开始;
  2. 任何时候只要我们修改了其中一个文件,上述编译命令就会重新编译所有的文件,当文件足够多时这样的编译会非常耗时。

那么Makefile又能做什么呢?我们先来看一个最简单的 Makefile 文件:

// Makefile 的规则如下:
target: prerequisites
	command//请注意tab缩进。语法规定Makefile中的任何命令之前都必须要有一个tab缩进,否则make就会报错。
           
//  Makefile 文件如下:         
d: main.c a.c b.c c.c
	gcc -o d main.c a.c b.c c.c 

现在你看到的就是一个最基本的Makefile语句,它主要分成了三个部分:

  1. 第一行冒号之前的 d,我们称之为目标(target),被认为是这条语句所要处理的对象。
  2. 冒号后面的部分(main.c a.c b.c c.c),我们称之为依赖关系表,也就是编译 d 所需要的文件,这些文件只要有一个发生了变化,就会触发该语句的第三部分,命令部分。
  3. 第三部分命令部分,其实就是一条编译命令。

现在我们只要将上面这两行语句写入一个名为Makefile或者makefile的文件,然后在终端中输入make命令,就会看到它按照我们的设定去编译程序了。

make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:
 - GNU make:它常和GNU编译系统一起被使用,是大多数GNU Linux默认安装的工具。
 - BSD make:它在编译目标的时有并发计算的能力。主要应用于FreeBSD,NetBSD和OpenBSD这些系统。
 - Microsoft nmake:主要用于微软的Windows系统中。 
2. 为什么用它?

makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,可以极大的提高了软件开发的效率。

3、详细语法
  • LOCAL_JACK_ENABLED:这个变量可以禁止使用Jack编译工具链编译该模块,个人感觉目前jack工具链不是很稳定,经常会出现一些错误,比如:
    ERROR: Security problem, see Jack server log (/tmp/jack-liujinwei/jack-8072.log)。这个时候,可以在Android.mk中禁止该工具链。命令如下:
LOCAL_JACK_ENABLED := disabled  
  • LOCAL_DEX_PREOPT := false 可以使整个系统使用提前优化的时候,某个app不使用提前优化。在Android.mk中给该变量赋值为false,则编译生成的文件没有oat文件,也就意味着没有被提前优化
  • WITH_DEXPREOPT := true 使能导致system image中的所有东西都被提前优化(pre-optimized)。这可能导致system image非常大
  • DONT_DEXPREOPT_PREBUILTS:和 WITH_DEXPREOPT 组合使用,可以使那些 prebuild的app不会被提前优化(pre-optimized),即就是那些在Android.mk中包含intclude $(BUILD_PREBUILT)的app都不会被提前优化
  • WITH_DEXPREOPT_BOOT_IMG_ONLY:和 WITH_DEXPREOPT 组合使用只会优化boot image
  • LOCAL_MODULE_TAGS:指该模块在什么版本下编译,user、 eng、 tests、 optional,其中 optional 指该模块在所有版本下都可以编译
  • LOCAL_PATH:当前目录,使用LOCAL_PATH := $(call my-dir)获取当前目录,LOCAL_PATH不会被include $(CLEAR_VARS) 清理
  • LOCAL_MODULE:模块名,在模块编译的时候,LOCAL_MODULE的值会被赋予ALL_MODULE,ALL_MODULE包含了系统所有模块,这些模块会更具系统的其他配置进一步筛选,最终筛选出来的模块会被编译
  • LOCAL_SHARED_LIBRARIES:要连接到本模块的共享库
  • LOCAL_CERTIFICATE := platform 使用平台签名文件签名
  • include $(BUILD_STATIC_JAVA_LIBRARY) 构建一个静态的jar包
  • include $(BUILD_PACKAGE) 编译生成apk
  • include $(BUILD_EXECUTABLE) 编译生成可执行文件
  • include $(BUILD_SHARED_LIBRARY) 编译生成动态共享库
  • include $(BUILD_STATIC_LIBRARY) 编译生成静态库
  • LOCAL_JAVA_LIBRARIES := hello.jar 用于指明依赖的共享Jar包
  • LOCAL_STATIC_JAVA_LIBRARIES 用于指明依赖的静态jar包
  • LOCAL_LDLIBS:链接选项,指明链接的参数,LOCAL_SHARED_LIBRARIES 会生成依赖关系,当库不存在时会去编译这个库,LOCAL_LDLIBS则不会,它只是指明链接需要的参数。如:LOCAL_LDLIBS += -lm –lz –lc -lcutils –lutils –llog …

Android.mk结构实列:

LOCAL_PATH:=$(call my-dir) 	#设置当前编译目录(包含此Android.mk的目录)
include $(CLEAR_VARS)	#清除LOCAL_XXX变量,LOCAL_PATH除外
LOCAL_MODULE:=module_name 	#模块名字,不能包含空格,唯一
LOCAL_SRC_FILES:=srcfile1.cpp srcfile2.cpp 	#模块编译需要的c或cpp文件
include $(BUILD_SHARED_LIBRARY)	#BUILD_SHARED_LIBRARY指定编译成动态库,BUILD_STATIC_LIBRARY指定生成静态库,BUILD_EXECUTEABLE指定生成可执行文件。只有动态库才能被打包到安装包中,静态库可以用来生成动态库。

包含指定的目录下的Android.mk结构示列:

include $(call all-makefiles-under,$(LOCAL_PATH))	
#即include $(call all-makefiles-under,指定目录)

include $(call all-subdir-makefiles) 		
#包含所有当前目录中子目录的Android.mk,包含语句必须放在Android.mk文件的末尾,
#不要在包含别的Android.mk后再调用call my-dir,会出错的,因此
#如果需要包含别的Android.mk,需要放到末尾

设置头文件搜索路径示列:

#当前目录会被默认搜索(不包含子目录),设置其他位置的头文件查找路径如下:
LOCAL_C_INCLUDES:= $(ANDROID_SOURCE)/frameworks/av/include
LOCAL_C_INCLUDES+= $(ANDROID_SOURCE)/frameworks/base/include

#设置需要链接的动态库
LOCAL_LDLIBS:=-Llibspath -lxxx -lyyy ... #libspath是库所在路径,xxx和yyy是库

#指定链接Android.mk中定义的模块(如果xxx没有被编译好,它会先被编译):
LOCAL_SHARED_LIBRARIES:=module_name #指定动态库
LOCAL_STATIC_LIBRARIES:=module_name #指定静态库

导出模块头文件路径示列:

LOCAL_EXPORT_C_INCLUDES:=module_include
#这个主要用来输出当前模块的头文件所在路径,其他模块如果依赖它,就不需要指定它的头文件路径了

导出模块依赖关系示列:

LOCAL_EXPORT_LDLIBS:= -lxxx -lyyy

#当用静态库生成动态库时,可以用此方法设置静态库的依赖环境,一般用法是:
LOCAL_EXPORT_LDLIBS:=$(LOCAL_LDLIBS) 
#上面提到用LOCAL_LDLIBS设置链接路径的好处就在这里,可以直接导出库和库所在的路径.

预编译示列:

include $(PREBUILT_SHARED_LIBRARY)
include $(PREBUILT_STATIC_LIBRARY)
#它主要用来包含第三方库,为第三方库在Android.mk中创建模块名字,被其他库使用.
#使用这种编译时,LOCAL_SRC_FILES中指向一个动态库,
#并且最后被打包到安装包里的库会是这个动态库.

参考资料如下:

陈皓
凌杰的技术博客
一篇文章学懂Shell脚本
Android之.mk详解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值