摘抄自:https://blog.csdn.net/SoaringLee_fighting/article/details/84037468
略加部分修改完善。
1、NDK编译C/C++ Native代码的通用方法
1.1、 编译脚本基本知识
使用android ndk包中的ndk-build编译时,ndk-build是从jni目录下寻找Android.mk及Application.mk,根据其设定的编译规则进行编译;jni目录内容如:
- jni/Android.mk
- jni/Application.mk
- …
(1)、Android.mk
- 作用是定义模块及其名称、要编译的源文件、版本标志以及要链接的库。
- 内容大致如下,不同的工程主体差不多:
# 构建系统提供的宏函数 my-dir 将返回当前目录(包含 Android.mk 文件本身的目录)的路径,基本上是固定的,不需要去动
LOCAL_PATH := $(call my-dir)
# 会清除很多 LOCAL_XXX 变量,不会清除 LOCAL_PATH,基本上是固定的,不需要去动
include $(CLEAR_VARS)
# 需要构建模块的名称,会自动生成相应的 libNDKSample.so 文件,每个模块名称必须唯一,且不含任何空格
LOCAL_MODULE := NDKSample
# 包含要构建到模块中的 C 或 C++ 源文件列表
LOCAL_SRC_FILES := HelloCPP.cpp
# 指定这个模块里会用到哪些原生 API,详见:https://developer.android.google.cn/ndk/guides/stable_apis.html
LOCAL_LDLIBS := -llog
# 帮助系统将所有内容连接到一起,固定的,不需要去动
include $(BUILD_SHARED_LIBRARY)
(2)、Application.mk
- 用于描述应用需要的原生模块,模块可以是静态库、共享库或可执行文件。
- 内容大致如下,不同的工程主体差不多
# 选择不同的 ABI,多个使用空格作为分隔符,全部是all
APP_ABI := all
# 指定要使用的运行时
APP_STL := gnustl_static
参考网址:http://wuxiaolong.me/2017/12/27/AndroidNDK/
备注
如果不新建jni目录则需要通过anddoid ndk-build的关键字指定*.mk的路径,如:ndk-build NDK_PROJECT_PATH=. NDK_APPLICATION_MK=./Application.mk APP_BUILD_SCRIPT=./Android.mk
更多信息参考:https://blog.csdn.net/suningning/article/details/74508629
1.2、 编译后产物介绍
libs:编译后自动创建,存放各个架构平台stripped的可执行文件和库
obj:编译后自动创建,存放各个架构平台not stripped的目标文件、可执行文件和库。
out:自己创建的目录,存放各个架构平台的动静态库和可执行文件。
2、 示例编译脚本
小编喜欢在jni下建立多个mk文件,目的将编译Lib跟编译app的脚本区分开。如:
jni/Android_lib.mk
jni/Android_app.mk
jni/common.mk
jni/Application.mk
2.1、 common.mk
设置源文件目录以及头文件目录:配置LOCAL_C_INCLUDES和LOCAL_SRC_FILES。
##主要指定源文件路径和头文件路径
SRC_DIR := ../../../src
##头文件路径
LOCAL_C_INCLUDES := ./ \
$(DIR_SRC) \
$(DIR_SRC)/../include \
$(DIR_SRC)/$(INCLUDEASM)
##纯C文件路径
C_SRCS := $(DIR_SRC)/test1.c \
$(DIR_SRC)/test2.c \
$(DIR_SRC)/test3.c \
$(DIR_SRC)/test4.c \
$(DIR_SRC)/test5.c
##汇编文件路径
A_SRCS :=
ifeq($(PURE_C),0)
#x86架构
ifeq($(findstring x86, $(TARGET_ARCH_ABI)), x86)
IFLAGS += -I$(SRC_DIR)/x86
A_SRCS += $(DIR_SRC)/x86/test1.asm \
$(DIR_SRC)/x86/test2.asm
endif
#arm架构
ifeq($(findstring armeabi, $(TARGET_ARCH_ABI)), armeabi)
IFLAGS += -I$(SRC_DIR)/arm
A_SRCS += $(DIR_SRC)/arm/test1.asm \
$(DIR_SRC)/arm/test2.asm
endif
endif
##设置汇编文件
LOCAL_SRC_FILES := $(C_SRCS) $(A_SRCS)
2.2、 Android_lib.mk
设置编译参数、特定平台编译参数、编译动静态库配置:配置LOCAL_PATH,LOCAL_MODULE,LOCAL_CFLAGS,LOCAL_CPPFLAGS。
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libxxx
##参数初始化
DEBUG ?= 0
SHARED ?= 1
INCLUDEASM :=
#设置汇编优化使能
ifeq ($(PURE_C),1)
OPTIM := 1
else
OPTIM := 0
endif
#设置GDB调试使能
ifeq ($(DEBUG),1)
GDBEN := -g
else
GDBEN := -O3
endif
#设置公用编译参数
CFLAGS := -Wall
CFLAGS += -fPIC
CFLAGS += -std=c99
CFLAGS += $(GDBEN)
#特定架构编译参数
ifeq($(TARGET_ARCH_ABI),arm64-v8a)
INCLUDEASM = aarch64
CFLAGS += -march=armv8-a
CFLAGS += -D_REENTRANT
CFLAGS += 架构相关优化宏
endif
ifeq($(TARGET_ARCH_ABI),armeabi-v7a)
INCLUDEASM = arm
CFLAGS += -march=armv7-a -mfpu=neon -marm
CFLAGS += -D_REENTRANT
CFLAGS += 架构相关优化宏
endif
ifeq($(TARGET_ARCH_ABI),x86_64)
INCLUDEASM = x86
CFLAGS += -march=x86-64 -m64 -msse -msse2 -msse3 -msse4.1
CFLAGS += -D_REENTRANT
CFLAGS += 架构相关优化宏
endif
ifeq($(TARGET_ARCH_ABI),x86)
INCLUDEASM = x86
CFLAGS += -march=i686 -m32 -msse -msse2 -msse3 -msse4.1
CFLAGS += -D_REENTRANT
CFLAGS += 架构相关优化宏
endif
#设置编译参数
LOCAL_CFLAGS := $(CFLAGS)
LOCAL_CPPFLAGS := $(CFLAGS)
#include $(LOCAL_PATH)/common.mk
#配置编译静态库、动态库
ifeq ($(SHARED),1)
include $(BUILD_SHARED_LIBRARY)
else
include $(BUILD_STATIC_LIBRARY)
endif
2.3、 Android_app.mk
设置编译参数、特定平台编译参数、编译可执行文件配置:配置LOCAL_PATH,LOCAL_MODULE,LOCAL_CFLAGS,LOCAL_CPPFLAGS。
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libxxx
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../demo/ \
$(LOCAL_PATH)/../../../include/
#设置公用编译参数
CFLAGS := -Wall
CFLAGS += -fPIC
CFLAGS += -std=c99
CFLAGS += $(GDBEN)
#特定架构编译参数
ifeq($(TARGET_ARCH_ABI),arm64-v8a)
INCLUDEASM = aarch64
CFLAGS += -march=armv8-a
endif
ifeq($(TARGET_ARCH_ABI),armeabi-v7a)
INCLUDEASM = arm
CFLAGS += -march=armv7-a -mfpu=neon -marm
endif
ifeq($(TARGET_ARCH_ABI),x86_64)
INCLUDEASM = x86
CFLAGS += -march=x86-64 -m64 -msse -msse2 -msse3 -msse4.1
endif
ifeq($(TARGET_ARCH_ABI),x86)
INCLUDEASM = x86
CFLAGS += -march=i686 -m32 -msse -msse2 -msse3 -msse4.1
endif
#设置编译参数
LOCAL_CFLAGS := $(CFLAGS)
LOCAL_CPPFLAGS := $(CFLAGS)
LOCAL_LDFLAGS := $(LOCAL_PATH)/../out/$(TARGET_ARCH_ABI)/libxxx.a
LOCAL_STATIC_LIBRARIES := $(LOCAL_PATH)/../out/$(TARGET_ARCH_ABI)/libxxx.a
#设置demo源文件
SRC_DIR =$(LOCAL_PATH)/../../../demo
DEMO_SRCS := $(SRC_DIR)/demo.c
LOCAL_SRC_FILES := $(DEMO_SRCS)
include $(BUILD_EXECUTABLE)
2.4、 Application.mk
设置APP_ABI,APP_PLATFORM和NDK_TOOLCHAIN_VERSION等。
APP_ABI := x86 x86_64 armeabi armeabi-v7a arm64-v8a
APP_PLATFORM := android-9
NDK_TOOLCHAIN_VERSION := 4.9
系统宏用法:
APP_ABI
默认情况下,NDK 构建系统为 armeabi ABI 生成机器代码。 此机器代码对应于基于 ARMv5TE、采用软件浮点运算的 CPU。 您可以使用 APP_ABI 选择不同的 ABI。
APP_PLATFORM
此变量包含目标 Android 平台的名称。
APP_BUILD_SCRIPT
默认情况下,NDK 构建系统在 jni/ 下查找名称为 Android.mk 的文件。
如果要改写此行为,可以定义 APP_BUILD_SCRIPT 指向替代构建脚本。 构建系统始终将非绝对路径解释为 NDK 顶级目录的相对路径。
NDK_TOOLCHAIN_VERSION
将此变量定义为 4.9 或 4.8 以选择 GCC 编译器的版本。 64 位 ABI 默认使用版本 4.9 ,32 位 ABI 默认使用版本 4.8。要选择 Clang 的版本,请将此变量定义为 clang3.4、clang3.5 或 clang。 指定 clang 会选择 Clang 的最新版本
关于系统变量、宏和函数宏的用法更多可参考:
https://developer.android.google.cn/ndk/guides/android_mk
https://developer.android.google.cn/ndk/guides/application_mk
https://developer.android.google.cn/ndk/guides/ndk-build
https://www.ibm.com/developerworks/cn/opensource/os-cn-android-build/
3、 示例编译脚本的编译方法
3.1、 编译环境:Android NDK下载:(内含ndk-build)
https://developer.android.google.cn/ndk/downloads/
http://www.voidcn.com/article/p-fjfskgpc-bph.html
https://developer.android.google.cn/ndk/guides/stable_apis#purpose
3.2、 编译方法一(shell脚本)
#!/bin/bash
if [ $# -ne 1]; then
echo "./build.sh ndk_dir"
exit
fi
# get current dir
PJTDIR=$(pwd)
#check if we are in the ndk directory
cd $1
if [ ! -f "ndk-build"]; then
echo "Run this script under NDK directory\n"
exit
fi
echo "------------------------clear output dir-------------------------"
find $PJTDIR/obj -name "*.[od]" | xargs rm -rf
find $PJTDIR/obj -name "*.so" | xargs rm -rf
find $PJTDIR/libs -name "*.so" | xargs rm -rf
find $PJTDIR/out -name "*.so" | xargs rm -rf
find $PJTDIR/obj -name "*.[od]" | xargs rm -rf
find $PJTDIR/obj -name "*.a" | xargs rm -rf
find $PJTDIR/libs -name "*.a" | xargs rm -rf
find $PJTDIR/out -name "*.a" | xargs rm -rf
echo "------------------------build static library----------------------"
#cp $PJRDOR/jni/Android_lib.mk $PJRLIB/jni/Android.mk
./ndk-build -B -C $PJTDIR APP_BUILD_SCRIPT:=Android_lib.mk
cp $PJTDIR/obj/local/armeabi/libxxx.a $PJTDIR/out/armeabi/
cp $PJTDIR/obj/local/armeabi-v7a/libxxx.a $PJTDIR/out/armeabi-v7a/
cp $PJTDIR/obj/local/arm64-v8a/libxxx.a $PJTDIR/out/arm64-v8a/
cp $PJTDIR/obj/local/x86/libxxx.a $PJTDIR/out/x86/
cp $PJTDIR/obj/local/x86_64/libxxx.a $PJTDIR/out/x86_64/
cp $PJTDIR/obj/local/mips/libxxx.a $PJTDIR/out/mips/
cp $PJTDIR/obj/local/mips64/libxxx.a $PJTDIR/out/mips64/
echo "------------------------build dynamic library----------------------"
#cp $PJRDOR/jni/Android_lib.mk $PJRLIB/jni/Android.mk
./ndk-build -B -C $PJTDIR APP_BUILD_SCRIPT:=Android_lib.mk
cp $PJTDIR/obj/local/armeabi/libxxx.so $PJTDIR/out/armeabi/
cp $PJTDIR/obj/local/armeabi-v7a/libxxx.so $PJTDIR/out/armeabi-v7a/
cp $PJTDIR/obj/local/arm64-v8a/libxxx.so $PJTDIR/out/arm64-v8a/
cp $PJTDIR/obj/local/x86/libxxx.so $PJTDIR/out/x86/
cp $PJTDIR/obj/local/x86_64/libxxx.so $PJTDIR/out/x86_64/
cp $PJTDIR/obj/local/mips/libxxx.so $PJTDIR/out/mips/
cp $PJTDIR/obj/local/mips64/libxxx.so $PJTDIR/out/mips64/
echo "------------------------build dynamic executable----------------------"
#cp $PJRDOR/jni/Android_app.mk $PJRLIB/jni/Android.mk
./ndk-build -B -C $PJTDIR APP_BUILD_SCRIPT:=Android_app.mk
cp $PJTDIR/obj/local/armeabi/xxx $PJTDIR/out/armeabi/
cp $PJTDIR/obj/local/armeabi-v7a/xxx $PJTDIR/out/armeabi-v7a/
cp $PJTDIR/obj/local/arm64-v8a/xxx $PJTDIR/out/arm64-v8a/
cp $PJTDIR/obj/local/x86/xxx $PJTDIR/out/x86/
cp $PJTDIR/obj/local/x86_64/xxx $PJTDIR/out/x86_64/
cp $PJTDIR/obj/local/mips/xxx $PJTDIR/out/mips/
cp $PJTDIR/obj/local/mips64/xxx $PJTDIR/out/mips64/
echo "------------------------build static executable----------------------"
#cp $PJRDOR/jni/Android_app_static.mk $PJRLIB/jni/Android.mk
./ndk-build -B -C $PJTDIR APP_BUILD_SCRIPT:=Android_app_static.mk
cp $PJTDIR/obj/local/armeabi/xxx $PJTDIR/out/armeabi/
cp $PJTDIR/obj/local/armeabi-v7a/xxx $PJTDIR/out/armeabi-v7a/
cp $PJTDIR/obj/local/arm64-v8a/xxx $PJTDIR/out/arm64-v8a/
cp $PJTDIR/obj/local/x86/xxx $PJTDIR/out/x86/
cp $PJTDIR/obj/local/x86_64/xxx $PJTDIR/out/x86_64/
cp $PJTDIR/obj/local/mips/xxx $PJTDIR/out/mips/
cp $PJTDIR/obj/local/mips64/xxx $PJTDIR/out/mips64/
echo "------------------------tar---------------------"
tar -zcvf $PJTDIR/out/libxxx_android_armeabi.tar.gz -C $PJTDIR/out/armeabi/
tar -zcvf $PJTDIR/out/libxxx_android_armeabi-v7a.tar.gz -C $PJTDIR/out/armeabi-v7a/
tar -zcvf $PJTDIR/out/libxxx_android_arm64-v8a.tar.gz -C $PJTDIR/out/arm64-v8a/
tar -zcvf $PJTDIR/out/libxxx_android_x86.tar.gz -C $PJTDIR/out/x86/
tar -zcvf $PJTDIR/out/libxxx_android_x86_64.tar.gz -C $PJTDIR/out/x86_64/
tar -zcvf $PJTDIR/out/libxxx_android_mips.tar.gz -C $PJTDIR/out/mips/
tar -zcvf $PJTDIR/out/libxxx_android_mips64.tar.gz -C $PJTDIR/out/mips64/
echo "------------------------check results---------------------"
ls -l $PJTDIR/out
ls -l $PJTDIR/armeabi
ls -l $PJTDIR/armeabi-v7a
ls -l $PJTDIR/arm64-v8a
ls -l $PJTDIR/x86
ls -l $PJTDIR/x86-64
ls -l $PJTDIR/mips
ls -l $PJTDIR/mips-64
其中:核心命令为:
./ndk-build -B -C $PJTDIR APP_BUILD_SCRIPT:=Android_lib.mk
其中,-B表示强制rebuild,-C表示Android.mk和Application.mk两个文件夹所在路径,就是指定makefile的路径,与linux中make -C dir是同一个含义,APP_BUILD_SCRIPT用于指定编译的makefile,同linux中的make -f makefile,NDK_PROJECT_PATH用于指定makefile的路径。
注意:
1、无论是编译动态库、静态库还是可执行文件,都需要各自对应的Android.mk和Application.mk这两个文件。
2、Application.mk通常用于指定需要编译的平台的个数,比如
APP_ABI := x86 x86_64 armeabi armeabi-v7a arm64-v8a
APP_PLATFORM := android-9
NDK_TOOLCHAIN_VERSION := 4.9
3、Android.mk相当于被Applicatioin.mk调用的,即每个平台都会调用一次Android.mk产生一个相应平台的编译出来的模块。
3.2、 编译方法二(python)
//cmd="ndk-build -B -C ./ NDK_PROJECT_PATH=./ APP_BUILD_SCRIPT=./Android.mk"
#coding=utf8
import os
import sys
import glob
import shutil
import platform
from sys import argv
share_lib_name = 'libxxx.so'
static_lib_name = 'libxxx.a'
static_exe = 'demo'
#检查是否为windows系统
def isWindowsSys():
return 'Windows' in platform.system()
#检查是否为Linux系统
def isLinuxSys():
return 'Linux' in platform.system()
#清空目录下的内容
def delDirFile(path):
def i in os.listdir(path):
file_path = os.path.join(path, i)
if os.path.isfile(file_path):
os.remove(file_path)
else
delDirFile(file_path)
def process(ndk_dir, android_dir, pure_c, delimiter):
ndk_build = ndk_dir + delimiter + 'ndk-build'
app_mk = android_dir + delimiter + 'jni' + delimiter + 'Application.mk'
mk_dir = android_dir + delimiter + 'jni'
static_lib_dir = android_dir + delimiter + 'obj' + delimiter + 'local' + delimiter
out_sha_lib = android_dir + delimiter + 'out' + delimiter
share_lib_dir = android_dir + delimiter + 'obj' + delimiter + 'local' + delimiter
out_sta_lib = android_dir + delimiter + 'out' + delimiter
if isWindowsSys():
cpy = "copy "
else:
cpy = "cp "
# "=================CLEAR OUTPUT DIR========================="
outdir_files = glob.glob(android_dir + delimiter + 'out' + delimiter + '*')
for outdir in outdir_files:
print(outdir)
delDelFile(outdir)
isExist = os.path.exists(ourdir)
if(not isExist):
os.mkdir(outdir)
pFile = open(app_mk, 'w')
cmd = 'APP_ABI := x86 x86_64 armeabi armeabi-v7a arm64-v8a \nAPP_PLATFORM := android-9\nNDK_TOOLCHAIN_VERSION := 4.9'
pFile.writeLines(cmd)
pFile.write('\n')
pFile.close()
# "==================BUILD SHARED LIBRARY===================="
cmd = ndk_build + " -B " + "NDK_PROJECT_PATH:=" + android_dir + "APP_BUILD_SCRIPT:=" + mk_dir + delimiter + "Android_lib.mk" + " V=1 SHARED=0"
print(cmd)
os.system(cmd)
cmd = cpy + share_lib_dir + "x86" + delimiter + share_lib_name + " " + out_sha_lib + "x86" + delimiter
print(cmd)
os.system(cmd)
##其他平台依次类推
# "==================BUILD STATIC LIBRARY===================="
cmd = ndk_build + " -B " + "NDK_PROJECT_PATH:=" + android_dir + "APP_BUILD_SCRIPT:=" + mk_dir + delimiter + "Android_lib.mk"
print(cmd)
os.system(cmd)
cmd = cpy + static_lib_dir + "x86" + delimiter + static_lib_name + " " + out_sta_lib + "x86" + delimiter
print(cmd)
os.system(cmd)
##其他平台依次类推
# "==================BUILD STATIC EXE ===================="
cmd = ndk_build + " -B " + "NDK_PROJECT_PATH:=" + android_dir + "APP_BUILD_SCRIPT:=" + mk_dir + delimiter + "Android_app.mk"
print(cmd)
os.system(cmd)
cmd = cpy + static_lib_dir + "x86" + delimiter + static_lib_name + " " + out_sta_lib + "x86" + delimiter
print(cmd)
os.system(cmd)
##其他平台依次类推
##############################main函数入口######################
if __name__ == '__main__':
if(len(argv) < 3):
printf("Tips: build_android.py android_ndk_dir pure_c=0/1\n")
exit()
delimiter =os.path.sep
ndk_dir = argv[1]
pure_c = argv[2]
android_dir = os.getcwd()
process(ndkdir,android_dir,pure_c,delimiter)