NDK编译笔记

使用NDK编译大型项目

最近公司的项目需要做改造,原本是纯linux平台的项目,现在需要在Android平台上跑,之前对gcc的交叉编译了解了些,现在需要使用NDK编译,这篇文章主要记录 NDK编译 的一些学习心得。

什么是NDK编译

NDK编译,其实就是为了在Android平台上跑C、C++程序,使用ndk-build脚本来进行项目构建,ndk-build本身是基于Make的构建系统。

内部原理

运行ndk-build,其内部原理如下:

$GNUMAKE -f “ndk”/build/core/build-local.mk
“parameters”

其中 $GNUMAKE 指向 GNU Make 3.81 或更高版本,“ndk” 则指向 NDK 安装目录。

实际使用

ndk-build 脚本在 NDK 安装目录顶层。如需从命令行运行该脚本,到应用项目目录或其子目录中进行调用。例如:

$ cd “project”
$ “ndk”/ndk-build

命令中,“project” 指向项目的根目录,“ndk” 则是您安装 NDK 的目录。

怎么构建NDK编译

Application.mk

Application主要是对整个项目要的编译进行一些设置,这些设置是针对每个二进制的(动态库,静态库,执行程序)生成的。通过对一些变量的设置来确定:
APP_ABI:使用 APP_ABI 设置为特定 ABI 生成代码,常用设置如下:

APP_ABI := armeabi-v7a arm64-v8a x86

APP_BUILD_SCRIPT:默认情况下,ndk-build 假定 Android.mk 文件位于项目根目录的相对路径 jni/Android.mk 中。如需从其他位置加载 Android.mk 文件,需要对APP_BUILD_SCRIPT 进行设置,设置为 Android.mk 文件的绝对路径

APP_BUILD_SCRIPTI := C:/Users/hzg/Desktop/

APP_CFLAGS:项目中的所有需要编译的 C/C++ 源文件绝对路径

APP_BUILD_SCRIPTI := C:/Users/hzg/Desktop/*.CPP/*.C

APP_CPPFLAGS:项目中的所有需要编译的 C/C++ 源文件绝对路径,不包含C源文件

APP_BUILD_SCRIPTI := C:/Users/hzg/Desktop/*.CPP

APP_DEBUG:如果要构建可调试的应用,把这个标记设置为“True”。

APP_DEBUG:= True

APP_LDFLAGS:关联可执行文件和共享库的标记。

APP_PLATFORM:APP_PLATFORM 会声明构建应用所面向的 Android API 级别。如果默认,ndk-build 将以 NDK 支持的最低 API 级别为目标。

APP_DEBUG:= True

APP_PROJECT_PATH:项目根目录的绝对路径。

实例:

APP_PLATFORM := android-22
#APP_ABI := armeabi-v7a, arm64-v8a
APP_ABI := armeabi-v7a

#APP_STL := c++_static
APP_STL := c++_shared
APP_OPTIM := debug
APP_CFLAGS := -D__NDK_BUILD__
APP_CFLAGS += -Wno-invalid-source-encoding
APP_CFLAGS += -Xlinker --disable-new-dtags
APP_CFLAGS += -g -O2
APP_CPPFLAGS += -g -O2
#APP_LDFLAGS := --disable-new-dtags
APP_LDFLAGS := --coverage
#APP_LDFLAGS += --coverage -L$(PWD)/prebuild/ -l:libssl.a
#APP_LDFLAGS += --coverage -L$(PWD)/prebuild/ -l:libcrypto.a

ifeq ($(SUPPORT_FT_TEST), Y)
PWD=$(shell pwd)
#APP_PLATFORM := android-22
APP_CFLAGS += --coverage
APP_CPPFLAGS += --coverage
APP_CFLAGS += -DVHAL_UT_TEST
APP_CPPFLAGS += -DVHAL_UT_TEST
APP_LDFLAGS += --coverage -L$(PWD)/prebuild/$(APP_ABI)/$(APP_PLATFORM) -l:libgtest.a
endif

APP_BUILD_SCRIPT := Android.mk

Android.mk

Android.mk 文件位于项目 jni/ 目录的子目录中,用于向构建系统描述源文件和共享库。它实际上是一个微小的 GNU makefile 片段,构建系统会将其解析一次或多次。Android.mk 文件用于定义 Application.mk、构建系统和环境变量所未定义的项目级设置。它还可替换特定模块的项目级设置。Android.mk 的语法支持将源文件分组为“模块”。模块是静态库、共享库或独立的可执行文件。您可在每个 Android.mk 文件中定义一个或多个模块,也可在多个模块中使用同一个源文件。

LOCAL_PATH :变量表示源文件在开发树中的位置。在下命令中,构建系统提供的宏函数 my-dir 将返回当前目录(Android.mk 文件本身所在的目录)的路径。

LOCAL_PATH := $(call my-dir)

CLEAR_VARS:CLEAR_VARS 变量指向一个特殊的 GNU Makefile,后者会为您清除许多LOCAL_XXX 变量,例如LOCAL_MODULELOCAL_SRC_FILESLOCAL_STATIC_LIBRARIES 。请注意,GNU Makefile 不会清除 LOCAL_PATH。此变量必须保留其值,因为系统在单一 GNU Make 执行上下文(其中的所有变量都是全局变量)中解析所有构建控制文件。在描述每个模块之前,必须声明(重新声明)这个变量。

include $(CLEAR_VARS)

LOCAL_MODULE:每个模块名称必须唯一,且不含任何空格。构建系统在生成最终共享库文件时,会对您分配给 LOCAL_MODULE 的名称自动添加正确的前缀和后缀。例如,下面示例会生成名为 libhello-jni.so 的库。

LOCAL_MODULE := hello-jni:= hello-jni

LOCAL_SRC_FILES :列举源文件,以空格分隔多个文件

LOCAL_SRC_FILES := hello-jni.c

BUILD_SHARED_LIBRARY:BUILD_SHARED_LIBRARY 变量指向一个 GNU Makefile 脚本,该脚本会收集您自最近 include 以来在 LOCAL_XXX 变量中定义的所有信息,并生成一个动态库。同理,BUILD_EXECUTABLEBUILD_STATIC_LIBRARY分别对应的是执行程序和静态库。

include $(BUILD_SHARED_LIBRARY)

TARGET_ARCH_ABI:构建系统解析此 Android.mk 文件时指向的 ABI

ifeq ($(TARGET_ARCH_ABI),arm64-v8a)
  ... do something ...
endif

TARGET_ABI:目标 Android API 级别与 ABI 的串联,特别适用于要针对实际设备测试特定目标系统映像的情况。例如,要检查在 Android API 级别 22 上运行的 64 位 ARM 设备

ifeq ($(TARGET_ABI),android-22-arm64-v8a)
  ... do something ...
endif

LOCAL_PATH:变量用于指定当前文件的路径。必须在 Android.mk 文件开头定义此变量。以下示例演示了如何定义此变量:

LOCAL_PATH := $(call my-dir)

CLEAR_VARS 所指向的脚本不会清除此变量。因此,即使 Android.mk 文件描述了多个模块,也只需定义此变量一次。

LOCAL_MODULE:此变量用于存储模块名称。指定的名称在所有模块名称中必须唯一,并且不得包含任何空格。您必须先定义该名称,然后才能添加任何脚本(CLEAR_VARS 的脚本除外)。无需添加 lib 前缀或 .so 或 .a 文件扩展名;构建系统会自动执行这些修改。在整个 Android.mk 和 Application.mk 文件中,请用未经修改的名称引用模块。例如,以下行会导致生成名为 libfoo.so 的共享库模块:

LOCAL_MODULE := "foo"

LOCAL_SRC_FILES :列举源文件,以空格分隔多个文件

LOCAL_SRC_FILES := hello-jni.c

LOCAL_C_INCLUDES:使用此可选变量指定相对于 NDK root 目录的路径列表,以便在编译所有源文件(C、C++ 和 Assembly)时添加到 include 搜索路径中。例如:

LOCAL_C_INCLUDES := sources/foo

或者

LOCAL_C_INCLUDES := $(LOCAL_PATH)/"subdirectory"/foo

LOCAL_CFLAGS:变量用于设置在构建 C 和 C++ 源文件时构建系统要传递的编译器标记。这样,您就可以指定额外的宏定义或编译选项。可以使用 LOCAL_CPPFLAGS 仅为 C++ 指定标记。

请勿尝试在 Android.mk 文件中更改优化/调试级别。构建系统可以使用 Application.mk 文件中的相关信息自动处理此设置。这样,构建系统就可以生成供调试期间使用的有用数据文件。

LOCAL_LDLIBS:此变量列出了在构建共享库或可执行文件时使用的额外链接器标记。利用此变量,您可使用 -l 前缀传递特定系统库的名称。例如,以下示例指示链接器生成在加载时链接到 /system/lib/libz.so 的模块:

LOCAL_LDLIBS := -lz

LOCAL_LDFLAGS:列出了构建系统在构建共享库或可执行文件时使用的其他链接器标记

LOCAL_LDFLAGS += -fuse-ld=bfd

实例:

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_SRC_FILES:= \
       addr2linetest.c \

LOCAL_C_INCLUDES := \

LOCAL_CFLAGS :=
LOCAL_CFLAGS += -fPIC -Wformat-security
LOCAL_CFLAGS += $(CFLAGS)

LOCAL_STATIC_LIBRARIES :=

LOCAL_SHARED_LIBRARIES :=

LOCAL_MODULE:= addr2linetest

include $(BUILD_EXECUTABLE)

常用的编译指令

#获得当前文件路径

export PWD_PATH := $(shell pwd)

#打印语句一般形式

$(warning $(PWD_PATH))

#获得项目根路径(方便路径添加,非必要)

export VCT_ROOT := $(PWD_PATH)/../../../..
$(warning $(VCT_ROOT))

#获得ndk-build服务的目录

export NDK_PATH := /opt/android_build/android-ndk-r11/
$(warning $(NDK_PATH))

#获得ndk-build完整目录

export PATH := $(NDK_PATH):$(PATH)
$(warning $(PATH))

#伪目标

.PHONY:ndk

#要执行的目标

ndk:

#具体执行命令,包括项目目录,编译具体实现文件,编译范围描述文件,输出目录

ndk-build NDK_PROJECT_PATH=$(PWD_PATH) APP_BUILD_SCRIPT=./Android-platform.mk NDK_APPLICATION_MK=./Application.mk NDK_OUT=./mydir

#清除命令

clean:
	ndk-build clean

#NDK编译系统会为该版本所支持的所有ABI都生成代码,如果指定特定的ABI,就只生成该ABI适配的代码

APP-ABI := armeabi-v7a 

#还有其他一些参数,目前没有用到

#该变量表示源文件在项目中的位置,该行代码将编译系统提供的宏函数my-dir,将当前.mk文件的路径返回给系统变量

LOCAL_PATH := $(call my-dir)

#打印语句一般形式

$(warning "LOCAL_PATH"-----------------> in dir $(LOCAL_PATH))

#将除了LOCAL_PATH的其他LOCAL开头的变量都清除

include $(CLEAR_VARS)

#添加源文件

LOCAL_SRC_FILES := xxxx.c

#添加头文件路径,路径到头文件的父目录

LOCAL_C_INCLUDES += $(LOCAL_PATH)/include

#保存生成的模块的名称,编译器会自动添加lib前缀和.so/.a后缀,该名称配合最后生成动态库语句最后生成动态库文件liblosys.so

LOCAL_MODULE:= losys

#添加配置文件

include $(LOCAL_ROOT_PATH)/CONFIG/VCT_MD_PRODUCT_CONFIG
include $(LOCAL_PATH)/../../../../CONFIG/VCT_GENERAL_PRODUCT_CONFIG
include $(LOCAL_PATH)/../../../../CONFIG/VCT_NET_PRODUCT_CONFIG
include $(LOCAL_PATH)/../../../../CONFIG/VCT_FLASH_PRODUCT_CONFIG
include $(LOCAL_PATH)/../../../../CONFIG/VCT_VC_PRODUCT_CONFIG
include $(LOCAL_PATH)/../../../../CONFIG/VCT_MP_PRODUCT_CONFIG
include $(LOCAL_PATH)/../../../../CONFIG/VCT_MD_PRODUCT_CONFIG
include $(LOCAL_ROOT_PATH)/CONFIG/module.mk

#这个变量保存编译源文件需要传递的编译器标记,例如宏定义或者编译选项

LOCAL_CFLAGS += 

#这个变量保存编译共享库或者可执行文件时的连接器标记,例如添加库的目录

LOCAL_LDFLAGS += 

#添加需要依赖的动态库

LOCAL_SHARED_LIBRARIES := 

#添加需要依赖的静态库

LOCAL_STATIC_LIBRARIES :=

#生成目标,以下两个目标选择其中一个
#生成动态库

include $(BUILD_SHARED_LIBRARY)

#生成静态库

include $(BUILD_STATIC_LIBRARY)

猜你喜欢

转载自blog.csdn.net/qq_38750519/article/details/100144344