【Android开发】之编译脚本Makefile编写

摘抄自: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)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值