Makefile的使用

## Makefile > android的Android.mk就是一段段Makefile单元,很多第三方库直接提供makefile,需要能够大致的读懂makefile文件,如增量更新的bspath库提供的makefile就有错误,需要修改。另外虽说现在google推荐使用cmake,但是如果遇见Android.mk还是需要能够读懂。 ### 什么是Makefile ​ 无论是c、c++首先要把源文件编译成中间代码文件,在Windows下也就是 .obj 文件,UNIX下是 .o 文件,即 Object File,这个动作叫做``编译(compile)``,然后再把大量的Object File合成执行文件或者静动态库,这个动作叫作``链接(link)``。 一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,如何进行链接等等操作。 makefile 就是“自动化编译”,告诉make命令如何编译和链接,即make工具的配置脚本。 默认的情况下,gnu make命令会在当前目录下按顺序找寻文件 `"GNUmakefile”、“makefile”、“Makefile”` 最好不要用“GNUmakefile”,这个文件是GNU的make识别的(Windows Nmake就不识别) ​ ​ 当然,也可以使用别的文件名来书写Makefile,比如:“Make.Linux”,“Make.android”。这样在使用时候就需要 `` make -f XX 或者 make --file XX。`` ### Makefile规则 ```makefile 在Makefile中的命令,必须要以[Tab]键开始。 target : prerequisites ...(预备知识,先决条件) command(指令) ----------------------------------------------------------------------------------------- target也就是一个目标文件,可以是Object File,也可以是执行文件。还可以是一个标签。 prerequisites 要生成那个target所需要的文件或是其他target。 command也就是make需要执行的命令。(任意的Shell命令) ``` ```makefile # g++ -o 指定生成可执行文件的名称 # g++ -c 只编译不链接 test:main.o T1.o g++ -o test main.o T1.o #============================================= #make会进行自动推导生成 main.o T1.o 可以不需要写这一部分 main.o:main.cpp T1.h g++ -c main.cpp T1.o:T1.cpp T1.h g++ -c T1.cpp #============================================= #当然也可以一步到位 \ 是换行连接符 便于Makefile的易读,不用都挤在一行 test2: g++ -o test2 main.cpp \ T1.cpp #============================================= clean: rm test main.o T1.o say: echo "112232" # 读取到第一个targer test,则test是终极目标 # 生成目标test ,test需要main.o T1.0 # 查找到存在目标main.o T1.o 先生成 最后生成test #clean和say是标签,并不生成“clean”这个文件,这样的target称之为 “伪目标” #伪目标的名字不能和文件名重复 #如: 当前目录下有一个文件/文件夹 名字为clean,运行make clean则会: # make: `clean' is up to date. #为了避免这种情况,可以使用一个特殊的标记“.PHONY”来显示地指明一个目标是“伪目标” .PHONY: clean clean: rm test main.o T1.o ``` ### 变量 ```makefile #如果比较复杂的情况,比如文件很多,target目标比较多,那么我们如果来修改,比如增加一个.cpp文件, #那可能需要在很多地方都写一下,也容易出错。为了易于维护,可以在makefile中使用变量。 #相当于c中的宏 #声明变量 objects=main.o T1.o #mac上自动编译 main.o test:${objects} g++ -o test ${objects} clean: rm test ${objects} #============================================= # *.c 表示所有后缀为c的文件。 # 让通配符在变量中(当前目录下所有 .c 文件) objects = $(wildcard *.c) test : ${objects} gcc -o test ${objects} clean : rm test ${obects} ``` ### include ```makefile test:main.o T1.o g++ -o test main.o T1.o #include make.clean #或者 mk=make.clean include ${mk} #如果没有指定相对或者绝对路径,make会先在当前目录查找。 #然后查找 -I 或者 --include-dir 参数 {make -I include/ clean} #如果存在 /usr/local/bin或/usr/include 目录 也会去这个目录找 #如果想让make不理那些无法读取的文件,而继续执行,你可以在include前加一个减号“-” # -include ${mk} #类似的 rm g++ 等命令前都可以加-,表示不会因为这里命令执行错误而中断make。 ``` ### 文件搜索 在一些大的工程中,有大量的源文件存放在不同的目录中,最好的方法是把一个路径告诉make,让make在自动去找。 Makefile文件中的特殊变量``VPATH``就是完成这个功能的 ```makefile #默认先查找当前目录再查找当前目录下的a、b、c目录 VPATH = a:b:c OBJ=a.o b.o c.o main.o test : $(OBJ) gcc -o test $(OBJ) clean : rm test $(OBJ) #============================================= #小写的vpath是关键字 %.c表示所有.c文件 vpath %.c a vpath %.c b:c OBJ=a.o b.o c.o main.o test : $(OBJ) gcc -o test $(OBJ) clean : rm test $(OBJ) ``` ### 其他 #### 预定义变量 | 命令变量 | 含义 | | -------- | --------------------------------- | | AR | 函数库的打包程序,默认为"ar" | | AS | 汇编语言编译程序,默认为"as" | | CC | C语言编译程序,默认命令是"cc" | | CXX | C++语言编译程序,默认命令是"g++" | | RM | 文件删除程序的名称,默认值为 rm –f | | ARFLAGS | 库文件维护程序的选项,无默认值 | | ASFLAGS | 汇编程序的选项,无默认值 | | CFLAGS | C 编译器的选项,无默认值 | | CPPFLAGS | C 预编译的选项,无默认值 | | CXXFLAGS | C++编译器的选项,无默认值 | #### 自动变量 > $@ ``target的名字`` ```makefile main.o:main.c gcc -c main.c -o main.o #使用 $@ 代替 main.o main.o:main.c gcc -c main.c -o $@ ``` > $< ``target依赖的第一个依赖文件名`` ```makefile main.o:main.c a.h b.h gcc -c main.c -o main.o #使用 $< 代替 main.c main.o:main.c a.h b.h gcc -c $< -o main.o ``` | 变量 | 说明 | | ---- | ------------------------------------------------------------ | | $* | 不包含扩展名的target文件名称 | | $+ | 所有的依赖文件,以空格分开,并以出现的先后为序,可能包含 重复的依赖文件 | | $? | 所有时间戳比target文件晚的依赖文件,并以空格分开 | | $^ | 所有不重复的依赖文件,以空格分开 | #### 条件语句 ```makefile #ifneq ifeq ($(CC),gcc) $(CC) -o foo $(objects) $(libs_for_gcc) else $(CC) -o foo $(objects) $(normal_libs) endif ``` #### 输出信息 ```makefile AAA=111 #输出变量AAA $(warning $(AAA)) $(info $(AAA)) ``` ### Android.mk 微小 GNU makefile 片段。 将源文件分组为*模块*。 模块是静态库、共享库或独立可执行文件。 可在每个 `Android.mk` 文件中定义一个或多个模块,也可在多个模块中使用同一个源文件。  ```makefile #源文件在的位置。宏函数 my-dir 返回当前目录(包含 Android.mk 文件本身的目录)的路径。 LOCAL_PATH := $(call my-dir) #引入其他makefile文件。CLEAR_VARS 变量指向特殊 GNU Makefile,可为您清除许多 LOCAL_XXX 变量 #不会清理 LOCAL_PATH 变量 include $(CLEAR_VARS) #存储您要构建的模块的名称 每个模块名称必须唯一,且不含任何空格 #如果模块名称的开头已是 lib,则构建系统不会附加额外的前缀 lib;而是按原样采用模块名称,并添加 .so 扩展名。 LOCAL_MODULE := hello-jni #包含要构建到模块中的 C 和/或 C++ 源文件列表 以空格分开 LOCAL_SRC_FILES := hello-jni.c #构建动态库 include $(BUILD_SHARED_LIBRARY) ``` **变量和宏** 定义自己的任意变量。在定义变量时请注意,NDK 构建系统会预留以下变量名称: - 以 `LOCAL_` 开头的名称,例如 `LOCAL_MODULE`。 - 以 `PRIVATE_`、`NDK_` 或 `APP` 开头的名称。构建系统在内部使用这些变量。 - 小写名称,例如 `my-dir`。构建系统也是在内部使用这些变量。 如果为了方便而需要在 `Android.mk` 文件中定义自己的变量,建议在名称前附加 `MY_`。 **常用内置变量** | 变量名 | 含义 | 示例 | | ----------------------- | -------------------------- | ---------------------------------- | | BUILD_STATIC_LIBRARY | 构建静态库的Makefile脚本 | include $(BUILD_STATIC_LIBRARY) | | PREBUILT_SHARED_LIBRARY | 预编译共享库的Makeifle脚本 | include $(PREBUILT_SHARED_LIBRARY) | | PREBUILT_STATIC_LIBRARY | 预编译静态库的Makeifle脚本 | include $(PREBUILT_STATIC_LIBRARY) | | TARGET_PLATFORM | Android API 级别号 | TARGET_PLATFORM := android-22 | | TARGET_ARCH | CUP架构 | arm arm64 x86 x86_64 | | TARGET_ARCH_ABI | CPU架构 | armeabi armeabi-v7a arm64-v8a | **模块描述变量** | 变量名 | 描述 | 例 | | ---------------------------- | -------------------------------------- | ------------------------------------------------------ | | LOCAL_MODULE_FILENAME | 覆盖构建系统默认用于其生成的文件的名称 | LOCAL_MODULE := foo LOCAL_MODULE_FILENAME := libnewfoo | | LOCAL_CPP_FEATURES | 特定 C++ 功能 | 支持异常:LOCAL_CPP_FEATURES := exceptions | | LOCAL_C_INCLUDES | 头文件目录查找路径 | LOCAL_C_INCLUDES := $(LOCAL_PATH)/include | | LOCAL_CFLAGS | 构建 C *和* C++ 的编译参数 | | | LOCAL_CPPFLAGS | c++ | | | LOCAL_STATIC_LIBRARIES | 当前模块依赖的静态库模块列表 | | | LOCAL_SHARED_LIBRARIES | | | | LOCAL_WHOLE_STATIC_LIBRARIES | --whole-archive | 将未使用的函数符号也加入编译进入这个模块 | | LOCAL_LDLIBS | 依赖 系统库 | LOCAL_LDLIBS := -lz | 导出给引入模块的模块使用: LOCAL_EXPORT_CFLAGS LOCAL_EXPORT_CPPFLAGS LOCAL_EXPORT_C_INCLUDES LOCAL_EXPORT_LDLIBS ------ **引入其他模块** ```makefile #将一个新的路径加入NDK_MODULE_PATH变量 #NDK_MODULE_PATH 变量是系统环境变量 $(call import-add-path,$(LOCAL_PATH)/platform/third_party/android/prebuilt) #包含CocosDenshion/android目录下的mk文件 $(call import-module,CocosDenshion/android) #这里即为 我需要引入 CocosDenshion/android 下面的Android.mk #CocosDenshion/android 的路径会从 $(LOCAL_PATH)/platform/third_party/android/prebuilt 去查找 ``` ### Application.mk 同样是GNU Makefile 片段,在Application.mk中定义一些全局(整个项目)的配置 **APP_OPTIM** 将此可选变量定义为 `release` 或 `debug`。在构建应用的模块时可使用它来更改优化级别。发行模式是默认模式,可生成高度优化的二进制文件。调试模式会生成未优化的二进制文件,更容易调试。 **APP_CFLAGS** 为任何模块编译任何 C 或 C++ 源代码时传递到编译器的一组 C 编译器标志 **APP_CPPFLAGS** 构建 C++ 源文件时传递到编译器的一组 C++ 编译器标志。 **APP_ABI** 需要生成的cpu架构(ndk r17 只支持:armeabi-v7a, arm64-v8a, x86, x86_64) | 指令集 | 值 | | ---------------------------------- | ------------------------ | | 基于 ARMv7 的设备上的硬件 FPU 指令 | `APP_ABI := armeabi-v7a` | | ARMv8 AArch64 | `APP_ABI := arm64-v8a` | | IA-32 | `APP_ABI := x86` | | Intel64 | `APP_ABI := x86_64` | | MIPS32 | `APP_ABI := mips` | | MIPS64 (r6) | `APP_ABI := mips64` | | 所有支持的指令集 | `APP_ABI := all` | 不同 Android 手机使用不同的 CPU,因此支持不同的指令集。 **armeabi** 此 ABI 适用于基于 ARM、至少支持 ARMv5TE 指令集的 CPU。此 ABI 不支持硬件辅助的浮点计算。 相反,所有浮点运算都使用编译器 `libgcc.a` 静态库中的软件帮助程序函数。 **armeabi-v7a** `armeabi-v7a` ABI 使用 `-mfloat-abi=softfp` 开关强制实施规则,要求编译器在函数调用时必须传递核心寄存器对中的所有双精度值,而不是专用浮点值。 系统可以使用 FP 寄存器执行所有内部计算。 这样可极大地加速计算。 如果要以 armeabi-v7a ABI 为目标,则必须设置下列标志: ``` CFLAGS= -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 ``` **arm64-v8a** 此 ABI 适用于基于 ARMv8、支持 AArch64 的 CPU。它还包含 NEON 和 VFPv4 指令集。 **x86** 此 ABI 适用于支持通常称为“x86”或“IA-32”的指令集的 CPU。设置的标志如: ``` -march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32 ``` **x86_64** ``` -march=x86-64 -msse4.2 -mpopcnt -m64 -mtune=intel ``` 现在手机主要是armeabi-v7a。查看手机cpu: ``` adb shell cat /proc/cpuinfo adb shell getprop ro.product.cpu.abi ``` apk在安装的时候,如果手机是armeabi-v7a的,则会首先查看apk中是否存在armeabi-v7a目录,如果没有就会查找armeabi。 **保证cpu目录下so数量一致。** ​ 如果目标是armeabi-v7a,但是拥有一个armeabi的,也可以把它放到armeabi-v7a目录下。但是反过来不行 | ABI(横 so)/CPU(竖 手机) | armeabi | armeabi-v7a | arm64-v8a | x86 | x86_64 | | ----------------------- | ------- | ----------- | --------- | ---- | ------ | | ARMV5 | 支持 | | | | | | ARMV7 | 支持 | 支持 | | | | | ARMV8 | 支持 | 支持 | 支持 | | | | X86 | | | | 支持 | | | X86_64 | | | | 支持 | 支持 | **APP_PLATFORM** 此变量包含目标 Android 平台的名称。例如,`android-3` 指定 Android 1.5 系统映像 **APP_STL** 默认情况下,NDK 构建系统为 Android 系统提供的最小 C++ 运行时库 (`system/lib/libstdc++.so`) 提供 C++ 功能。 | 名称 | 说明> | 功能 | | ----------------- | ---------------------------- | ----------------------- | | libstdc++(默认) | 默认最小系统 C++ 运行时库。 | 不适用 | | gabi++_static | GAbi++ 运行时(静态)。 | C++ 异常和 RTTI | | gabi++_shared | GAbi++ 运行时(共享)。 | C++ 异常和 RTTI | | stlport_static | STLport 运行时(静态)。 | C++ 异常和 RTTI;标准库 | | stlport_shared | STLport 运行时(共享)。 | C++ 异常和 RTTI;标准库 | | gnustl_static | GNU STL(静态)。 | C++ 异常和 RTTI;标准库 | | gnustl_shared | GNU STL(共享)。 | C++ 异常和 RTTI;标准库 | | c++_static | LLVM libc++ 运行时(静态)。 | C++ 异常和 RTTI;标准库 | | c++_shared | LLVM libc++ 运行时(共享)。 | C++ 异常和 RTTI;标准库 |

你可能感兴趣的:(Makefile的使用)