Android
开发时,有时候需要移植第三方库进项目,需要对第三方库进行交叉编译生成Android
可用的动态库或者静态库。因此需要了解c/c++的编译的基本使用,了解相应的参数设置。
Android的
Android.mk
就是Makefile
单元,很多第三方库直接提供makefile,虽然Google推荐使用CMake
,但是一些老项目还是使用的mk。因此还是需要能读懂Android.mk文件。
如果出去面试,说会
NDK
开发,但是不了解makefile,别人是不会信服的。好了好了,不扯皮子了,进入今天的主题。
gcc / g++ / clang
clang
clang
是一个C、C++、Object-C
的轻量级编译器。基于LLVM
(LLVM是以c++编写而成的架构编译器的框架系统,可以说是一个用于开发编译器相关的库)
gcc
GNU C
编译器,原本只能处理C
,后来扩展也可以出了C++
。(GNU计划,又称格奴计划,目标是创建一套完全自由的操作系统)
g++
GNU C++
编译器
- gcc和g++都能够编译c/c++,但是编译时候行为不同。
- clang也是一个编译器,对比gcc,它具有编译速度更快、编译产出更小等优点,但是某些软件在使用clang编译时候因为源码中内容的问题会出现错误。
- clang++与clang就相当于gcc与g++。
gcc与g++的对比:
- 后缀为
.c
的源文件,gcc把它当作是C程序,而g++当作是C++程序;后缀为.cpp
的,两者都会认为是c++程序 - g++会自动链接c++
标准库stl
,gcc不会 - gcc不会定义
__cplusplus宏
,而g++会
编译器过程
一个C/C++文件要经过:
预处理(preprocessing)
、编译(compilation)
、汇编(assembly)
、链接(linking)
才能变成可执行文件。
1、预处理
如gcc -E test.c -o test.i
;-E
的作用是让gcc在预处理结束后停止编译。预处理阶段主要处理include
和define
等。它把include倒入的h
文件插入到include所在的位置,把源程序中使用到的define定义的宏用实际的字符串代替。
2、编译阶段
如gcc -S test.i -o test.s
;-S
的作用是编译结束后,生成汇编文件。gcc首先检查代码规范性、是否有语法错误等,以确定代码的实际工作内容,检查无误后,gcc把代码翻译成汇编语言。
3、汇编阶段
如gcc -c test.s -o test.o
; 将.s文件翻译成二进制机器指令.o文件
4、链接阶段
gcc -o test test.s
; 链接阶段链接的是函数库
。在test.c中并没有定义某些函数,且在预编译中包含进的stdio.h
等库中也有此函数的申明。系统将这些函数的实现都被做到名为libc.so
的动态库。
Makefile
Makefile:自动化编译,告诉make命令如何编译和链接,即make工具的配置脚本。定义一系列的规则来指定哪些文件需要先编译后编译,如何进行链接等等操作。
Makefile规则:
在Makefile中的命令,必须要以[Tab
]键开始,不要使用空格space
键。
target : prerequisites ...
command
---------------------------------------------------------------------------------------------------------------------------
target:是目标文件,可以是object file 也可以是执行文件,也可以是一个标签
prerequisites:生成target文件需要的的文件或者其他target
command: 就是make需要执行的命令(任意的shell命令)
#gcc -o
#gcc -c
a.o:a.cpp c.h
gcc -c a.cpp
test:a.o
gcc -o test a.o
----------------------------------------------------------------------------------------------------------------------------
#一步到位,make会自动推到生成a.o,不需要上面的复杂步骤
test2:
gcc -o test2 a.cpp \
b.cpp
clean:
rm test test2 a.o b.o
say:
echo 'test'
#clean 和say 是标签,并不生成 clean这个文件,这样的target称之为‘伪目标’
#伪目标的名字不能和文件名重复
#如果当前目录下有文件/文件夹 名字为clean,运行make clean 会报错:
# make: 'clean' is up to date
#如何避免伪目标和标签冲突呢,可以使用特殊标记
.PHONY: clean
clean:
rm test a.o
#变量,相当于c中的宏
#申明变量
objects = a.o b.o
test:${objects}
gcc -o test ${objects}
clean:
rm test ${objects}
#include
#include make.clean
mk=make.clean
include ${make}
#条件语句
ifeq ($(CC),gcc)
$(CC) -o foo $(objects) $(libs_for_gcc)
else
$(CC) -o foo $(objects) $(normal_libs)
endif
#输出信息
output=2333
${warning ${output}}
${info ${output}}
Android.mk
微小的GNU make片段
将源文件分组为模块。模块是静态库、共享库或者独立可执行文件。mk中可以定义一个或多个模块,也可以在多个模块中使用同一个源文件
#源文件在的位置。宏函数 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 := caterpillar
#包含要构建到模块中的 C 和/或 C++ 源文件列表 以空格分开
LOCAL_SRC_FILES := caterpillar.cpp
#构建动态库
include $(BUILD_SHARED_LIBRARY)
定义自己的任意变量。在定义变量时请注意,NDK 构建系统会预留以下变量名称:
- 以 LOCAL_ 开头的名称,例如 LOCAL_MODULE。
- 以 PRIVATE_、NDK_ 或 APP 开头的名称。构建系统在内部使用这些变量。
- 小写名称,例如 my-dir。构建系统也是在内部使用这些变量。
- 如果为了方便而需要在 Android.mk 文件中定义自己的变量,建议在名称前附加 MY_。
常用内置变量
变量名 | 含义 | 示例 |
---|---|---|
BUILD_STATIC_LIBRARY | 构建静态库 | include $(BUILD_STATIC_LIBRARY) |
PREBUILT_SHARED_LIBRARY | 预编译共享库 | include $(PREBUILT_SHARED_LIBRARY) |
PREBUILT_STATIC_LIBRARY | 预编译静态库 | 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 的编译参数 | |
LOCAL_CPPFLAGS | 构建c++编译参数 | |
LOCAL_STATIC_LIBRARIES | 依赖的静态库模块列表 | |
LOCAL_SHARED_LIBRARIES | 依赖的共享库模块列表 | |
LOCAL_WHOLE_STATIC_LIBRARIES | --whole-archive | 将未使用的函数符号也加入编译进入这个模块 |
LOCAL_LDLIBS | 依赖 系统库 | LOCAL_LDLIBS := -l |
引入其他模块
#将一个新的路径添加到NDK_MODULE_PATH变量
#NDK_MODULE_PATH 变量是系统的环境变量
$(call imort-add-path,$(LOCAL_PATH)/platform/third_party/android/prebuilt)
#包含cocosdenshion/android目录下的mk文件
$(call import-module,CocosDenshion/android)
Application.mk
不同于Android.mk,定义的都是一些全局(项目)的配置
常用内置变量
变量名 | 含义 | 参数 |
---|---|---|
APP_OPTIM | release、debug | |
APP_CFLAGS | 构建c编译参数 | |
APP_CPPFLAGS | 构建cpp编译参数 | |
APP_ABI | 生成的cpu架构 | armeabi、armeabi-v7a、armeabl-v8a、x86、x86_64、mips、mips64、all |
APP_PLATFORM | platform版本 | android-8、android-9等等 |
APP_STL | 构建时最小c++运行是库 | libstdc++、gabi++_static、gabi++_shared、stlport_static、gnustl_static、gnustl_shared、c++_static、c++_shared |