NDK - gcc与makefile

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在预处理结束后停止编译。预处理阶段主要处理includedefine等。它把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

你可能感兴趣的:(NDK - gcc与makefile)