友情提示:欢迎关注本人公众号,那里有更好的阅读体验以及第一时间获取最新文章

本文目录

一、前言

本篇我们介绍Android.mk与CMakeLists.txt构建NDK的配置文件,我们知道目前NDK的开发已经基本废弃Android.mk的使用了,AS创建NDK工程默认已经使用CMakeLists.txt构建文件,那我们为什么还要介绍Android.mk呢?

因为在平时开发中我们依然有可能接触到Android.mk文件,并且很多老的开源库依然使用的是Android.mk配置方式来构建的,这就要求我们能够读懂,在我们自己开发NDK的时候使用CMakeLists.txt来构建就可以了,Android.mk我们只需要读懂核心配置就可以了,遇到的时候能够读懂大部分意思,而CMakeLists.txt构建方式我们需要能够熟练使用。

上一篇中,我们在PC上编译动态库或者静态库的时候需要给编译器传递一些参数,比如头文件,库文件的查找路径,那种编译方式需要我们手动通过命令行的方式来编译,通常编译ffmpeg等提供源码的三方库为安卓平台可用的动态库或者静态库的时候会用这种方式,工作中我们也会自己在安卓项目中编写C/C++源文件,这些源文件怎么编译为动态库或者静态库呢?编译的时候我们用到三方的动态库或者静态库怎么设置依赖呢?怎么设置头文件或者库文件的查找路径呢?以及怎么配置编译文件之间的依赖呢?这些都可以在Android.mk或者CMakeLists.txt文件中配置,

本篇我们主要详细了解一下这些配置文件怎么配置,工作中出问题我们也好自己来查找解决。

好了,进入本文的学习。

二、Android.mk与Application.mk配置的了解

Android.mk的语法支持将源文件分组为模块。 模块是静态库、共享库或独立的可执行文件。 您可在每个Android.mk文件中定义一个或多个模块,也可在多个模块中使用同一个源文件。Android提供了一系列的内置变量来提供更加方便的构建语法规则。

Application.mk文件实际上是对应用程序本身进行描述的文件,它描述了应用程序要针对哪些CPU架构打包动态so包、要构建的是release包还是debug包以及一些编译和链接参数等。

Android.mk配置文件

我们先来看几个具体的Android.mk配置文件:

 LOCAL_PATH := $(call my-dir)

 include $(CLEAR_VARS)

 LOCAL_MODULE := MyGame_shared

 LOCAL_MODULE_FILENAME := libMyGame

 #源文件
LOCAL_SRC_FILES := $(LOCAL_PATH)/A.cpp \
$(LOCAL_PATH)/../../../Classes/B.cpp \
$(LOCAL_PATH)/../../../Classes/C.cpp # 编译时查找头文件的路径 相当于:-I
LOCAL_C_INCLUDES := $(LOCAL_PATH) \
$(LOCAL_PATH)/include # 需要链接的系统默认库
LOCAL_LDLIBS := -llog \
-lz include $(BUILD_SHARED_LIBRARY) $(call import-add-path,$(LOCAL_PATH)/../../../aaa)
$(call import-add-path,$(LOCAL_PATH)/../../../aaa/bbb)
#引入其他路径下的Android.mk文件
# 相当于 #include
$(call import-module, ddd)
 LOCAL_PATH := $(call my-dir)

 include $(CLEAR_VARS)
LOCAL_MODULE := Test
LOCAL_SRC_FILES := libTest.a
include $(PREBUILT_STATIC_LIBRARY) include $(CLEAR_VARS)
LOCAL_MODULE := hello-jni
LOCAL_SRC_FILES := hello-jni.c
# 编译hello-jni模块 需要链接 Test 模块
# Test模块是一个预编译库模块
LOCAL_STATIC_LIBRARIES := Test
include $(BUILD_SHARED_LIBRARY)

没接触或者看不懂没关系,接下来会解释的。

Android提供了一系列的内置变量或者函数来供我们更加方便的构建编译脚本,接下来我们来具体了解一下怎么使用或者配置:

 LOCAL_PATH := $(call my-dir)

构建系统提供的宏函数 my-dir返回Android.mk 文件所在的目录,这里的意思就是LOCAL_PATH 指向Android.mk 文件所在的目录。

 include $(CLEAR_VARS)

CLEAR_VARS 变量指向一个特殊的 GNU Makefile,后者会清除许多 LOCAL_XXX 变量,例如 LOCAL_MODULE、LOCAL_SRC_FILES 和 LOCAL_STATIC_LIBRARIES。每个模块编译描述之前都会调用一下include $(CLEAR_VARS),意思就是本模块不用你们之前模块定义的那些信息,我要自己定义,也就是将之前定义的变量值全部清空自己重新定义一下。

 LOCAL_MODULE := hello-jni

LOCAL_MODULE 变量存储您要构建的模块的名称,且必须唯一不含空格,会对分配给 LOCAL_MODULE 的名称自动添加正确的前缀和后缀。 例如,上述示例会生成名为libhello-jni.so的库。如果模块名称的开头已经是 lib,则构建系统不会附加额外的 lib前缀;而是按原样采用模块名称,并添加 .so 扩展名。

 LOCAL_MODULE_FILENAME := libnewfoo

LOCAL_MODULE_FILENAME 是一个可选变量,我们可以不配置,如果我们给这个变量配置了名称,则会强制系统将其生成的文件命名为LOCAL_MODULE_FILENAME 所配置的名称,也就是LOCAL_MODULE_FILENAME 的优先级高于LOCAL_MODULE ,比如,我们配置如下:

 LOCAL_MODULE := foo
LOCAL_MODULE_FILENAME := libnewfoo

对于生成动态库,生成的名称是libnewfoo.so而不是libfoo.so。

 LOCAL_SRC_FILES := hello-jni.c

这里配置源文件的列表,多个文件以空格隔开,也可以 \ 来换行配置

 include $(BUILD_SHARED_LIBRARY)

指导生成动态库还是静态库,或者是预编译库,预编译库就是已经生成的动态或者静态库,可选配置如下:

名称 说明
BUILD_SHARED_LIBRARY 生成动态库
BUILD_STATIC_LIBRARY 生成静态库
PREBUILT_SHARED_LIBRARY 预编译的库是动态库
PREBUILT_STATIC_LIBRARY 预编译的库是静态库
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/include

指定C/C++编译额外头文件的目录

 LOCAL_CFLAGS += -I<path>

此可选变量用于设置在构建 C 和 C++ 源文件时构建系统要传递的编译器标记,可使用此功能指定额外的宏定义或编译选项。

 LOCAL_CPPFLAGS += -I<path>

只构建 C++ 源文件时将传递的一组可选编译器标记, 这些标记会出现在编译器的命令行中,跟在 LOCAL_CFLAGS 之后。

LOCAL_CFLAGS 与LOCAL_CPPFLAGS 需要额外说明一下:这里传递的参数会直接传给编译器的命令行,上一篇我们讲解了给编译传递头文件,库文件查找路径的方式这里都可以配置。

 LOCAL_STATIC_LIBRARIES := Test

此变量用于存储当前模块所依赖的静态库模块列表。比如编译此模块需要依赖libTest.a静态库,则上面配置即可。

 LOCAL_SHARED_LIBRARIES

此变量用于存储当前模块所依赖的动态库模块列表。

 LOCAL_LDLIBS := -llog \
-lz

此变量包含额外的链接程序标记列表,供构建共享库或可执行文件时使用。 利用此变量,您可使用 -l 前缀传递特定系统库的名称,如果为静态库定义此变量,构建系统会将其忽略。 上面的指定表示编译此模块需要依赖系统的liblog.so与libz.so动态库。

 $(call import-add-path,$(LOCAL_PATH)/../../../aaa)
$(call import-add-path,$(LOCAL_PATH)/../../../aaa/bbb)
$(call import-module, ddd)

import-module函数用于按模块名称来查找和包含模块的Android.mk文件, import-add-path用来添加查找的路径,二者常常配合使用,上面的意思就是查找ddd模块,并将此模块下的Android.mk文件配置引入引入进来,查找值前调用import-add-path方法来添加查找的路径。

好了以上就是Android.mk文件的一些核心配置,我们回过头来在看下上面提到的具体配置:

 LOCAL_PATH := $(call my-dir)

 #预编译库的引入
include $(CLEAR_VARS)
LOCAL_MODULE := Test
LOCAL_SRC_FILES := libTest.a
include $(PREBUILT_STATIC_LIBRARY) include $(CLEAR_VARS)
LOCAL_MODULE := hello-jni
LOCAL_SRC_FILES := hello-jni.c
# 编译hello-jni模块 需要链接 Test 模块
# Test模块是一个预编译库模块
LOCAL_STATIC_LIBRARIES := Test
include $(BUILD_SHARED_LIBRARY)

在工程中hello-jni.c源文件中使用到了libTest.a静态库中方法,所以在编译hello-jni.c源文件为动态库的时候需要先预编译libTest.a静态库,并通过LOCAL_STATIC_LIBRARIES 指定引用的静态库。

好了Android.mk中的一些核心配置介绍到此,对于Android.mk文件工作中遇到能大体读明白就可以了。

Application.mk配置文件

接下来我们看下Application.mk配置文件有哪些核心配置,同样先来看一下实际例子:

 APP_CPPFLAGS := -frtti
APP_LDFLAGS := -latomic
APP_ABI := armeabi-v7a
APP_PLATFORM = android-21
APP_OPTIM := debug

Application.mk中核心配置就少多了,主要是一些全局的配置,可作用于任何Android.mk中的编译单元。

我们看一下核心的配置表示的意义:

 APP_CFLAGS += -I<PATH>

APP_CFLAGS 变量用于存储一组 C 编译器标记,供构建系统在为任何模块编译任何 C 或 C++ 源代码时传递到编译器。

 APP_CPPFLAGS

此变量包含一组 C++ 编译器标记,仅供构建系统构建 C++ 源文件时传递到编译器。 使用 APP_CFLAGS 可以为 C 和 C++ 指定标记。

 APP_OPTIM := debug

这个可选变量定义为 release 或 debug。 使用此变量可以在构建应用的模块时变更优化级别。

发布模式为默认模式,可以生成高度优化的二进制文件。 调试模式会生成未经优化的二进制文件,如此更容易调试。

 APP_ABI := armeabi-v7a arm64-v8a

使用 APP_ABI 设置为特定的平台生成机器代码,多个值以空格隔开,如上面设置生成armeabi-v7a与 arm64-v8a平台架构下的动态或者静态库。

 APP_PLATFORM = android-21

指定支持的最低版本的 Android 平台

好了,以上就是Application.mk中一些核心的配置,对于mk配置文件我们大概能读懂就可以了,很多我也没介绍,现在基本没人用了,相当于上古时候的工具,现在开发NDK基本全用的CMakeLists.txt方式了,接下来我们来看看CMakelists.txt的配置方式。

三、掌握CMakeLists.txt的配置

在android studio 2.2及以上,构建原生库的默认工具是 CMake。

CMake是一个跨平台的构建工具,可以用简单的语句来描述所有平台的编译过程。Cmake 并不直接建构出最终的软件,而是产生其他工具的脚本(如Makefile ),然后再依这个工具的构建方式使用。

CMake是一个比make更高级的编译配置工具,它可以根据不同平台、不同的编译器,生成相应的Makefile或者vcproj项目。从而达到跨平台的目的。

Android Studio利用CMake生成的是ninja,ninja是一个小型的关注速度的构建系统。我们不需要关心ninja是什么鬼,知道怎么配置cmake就可以了。

以上吧啦吧啦一堆就是说CMake怎么怎么的好,CMake 的配置文件是CMakeLists.txt,我们只需要关心怎么配置就好了,接下来我们看下具体的配置以及其含义。

单文件

如果我们工程只有一个C/C++文件,我们配置设置生成动态或者静态库,如下配置即可:

 # 指定运行此配置文件所需的 CMake 的最低版本
cmake_minimum_required(VERSION 3.4.1) # 该命令表示项目的名称是NDK
project(NDK) #src/main/cpp/native-lib.cpp 会编译为libnative-lib.so动态库
#SHARED编译动态库 STATIC:静态库
add_library( native-lib
SHARED
src/main/cpp/native-lib.cpp)

上面已经给出详细解释,不再细说。

多文件

如果我们有多个C/C++源文件想生成动态或者静态库加入如下配置即可:

 # 指定运行此配置文件所需的 CMake 的最低版本
cmake_minimum_required(VERSION 3.4.1)
# 该命令表示项目的名称是NDK
project(NDK) # 多文件
file(GLOB DIR_SRCS src/main/cpp/*.c src/main/cpp/*.cpp)
# SHARED: 动态库 STATIC:静态库、
add_library(hello-jni SHARED ${DIR_SRCS})

经过上面配置就可以将src/main/cpp目录下的所有C/C++文件编译为一个so动态库。

预编译静态库的引入

如果我们编译自己源文件的时候用到了已经编译好的静态库中的方法等,就需要进行额外配置让编译器编译的时候能链接到对应静态库,比如我们将静态库放入如下目录:

配置如下:

 # 指定运行此配置文件所需的 CMake 的最低版本
cmake_minimum_required(VERSION 3.4.1)
# 该命令表示项目的名称是NDK
project(NDK)
#src/main/cpp/native-lib.cpp 会编译为libnative-lib.so动态库
#SHARED编译动态库 STATIC:静态库
add_library( native-lib
SHARED
src/main/cpp/native-lib.cpp) #引入预编译好的静态库
# IMPORTED: 表示静态库是以导入的形式添加进来(预编译静态库)
# StaticTest 可以随便起名字
add_library(StaticTest STATIC IMPORTED) #设置静态库的导入路径
set_target_properties(StaticTest PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/src/main/static/armeabi-v7a/libStaticTest.a) #生成native-lib动态库需要用到StaticTest log动态或者静态库
#链接的配置
target_link_libraries( # Specifies the target library.
native-lib
# libTest.so 可以去掉lib与.so
Test
log )

CMAKE_SOURCE_DIR 是系统预定义好的变量,代表当前CMakeLists.txt所在的目录。

上面我们进行编译的时候除了设置预编译的libStaticTest.a静态库,还设置了log库用于打印一些信息,但是log库我们并没有配置其路径,这是因为构建系统为我们已经编译好一些常用的库供我们使用,其查找路径与头文件路径已经预定义好了,编译器会自己去查找的,不用我们额外配置查找路径,但是我们使用了额外的预编译库,那就需要进行额外配置了。

动态库的引入

上面的方式其实同样可以引入动态库,如:

 add_library(Test SHARED IMPORTED)
set_target_properties(Test PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi-v7a/libTest.so)

只需将STATIC改为SHARED即可,但是这样的配置方式在6.0及以上系统会报找不到库路径的错误,CMake提供了另一种配置方式:

 #动态库这样引入没有版本差异,如果像上面那样引入会有版本问题
# CMAKE_CXX_FLAGS 会传给c++编译器
# CMAKE_C_FLAGS 会传给c编译器
# CMAKE_SOURCE_DIR 的值是当前CMakelist.txt所在目录
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi-v7a")

还记得-L参数的意义吗?上一篇讲过就是给编译器传递库文件查找路径的,别忘了链接的时候添加动态库名字:

 target_link_libraries( # Specifies the target library.
native-lib
Test
StaticTest
log )
额外头文件路径查找配置以及其余CMakeLists.txt配置文件的引入
 #额外头文件查找路径设置 相当于-I
include_directories(src/main/include) #引入src/main/subcmakelist目录的cmakelist
add_subdirectory(src/main/subcmakelist)
build.gradle中有关NDK编译的配置

主要如下:

 android {
compileSdkVersion 26
defaultConfig {
applicationId "com.wanglei55.ndk"
minSdkVersion 18
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
//指导我们自己编写的c/c++生成动态或者静态库时生成几种架构的库
abiFilters "armeabi-v7a"
}
}
//应该打包几种cpu
//比如:三方库中提供了arm的提供了x86的,可以在此处指导只打包arm,生成出来的apk就只会包含 arm的
ndk{
abiFilters "armeabi-v7a"
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
externalNativeBuild {
cmake {//设置CMakeLists.txt所在目录,从build.gradle所在目录开始查找
path "CMakeLists.txt"
}
}
}

好了,以上就是关于CMakeLists.txt核心配置的一些讲解,此部分都是NDK开发中的一些基础知识,学起来可能比较枯燥,但是却是很重要的,否则后面的三方库的编译配置就直接无从下手,磨刀不误砍柴工,先把基础的学好吧。

CMake还有一些其余配置,可参考官方说明:官方说明

四、总结

本篇,我们讲解了一下mk与CMakeLists.txt的一些核心配置,主要为了以后我们看别人写的或者自己需要配置的时候能知道怎么下手,可能比较枯燥,但是这是NDK部分必须要搞懂的。

本篇到此为止。

Android:JNI与NDK(三)NDK构建的脚本文件配置的更多相关文章

  1. Android JNI入门第三篇——jni头文件分析

    一. 首先写了java文件: public class HeaderFile { private native void  doVoid(); native int doShort(); native ...

  2. Android JNI编程(三)——C语言指针的初步认识、指针变量、互换两个数、函数返回多个值

    版权声明:本文出自阿钟的博客,转载请注明出处:http://blog.csdn.net/a_zhon/. 目录(?)[+] 一.什么是指针? 简单来说: 指针就是内存地址      内存地址就是指针. ...

  3. TestNG系列(三)TestNG之XML文件配置

    前言 上一篇博客说了TestNG的注解,这篇博客来介绍Test.xml文件. Test.xml文件可以更方便的管理和执行测试用例 一.Test.xml-suite: suite为Test.xml的根节 ...

  4. Android Studio 减小项目文件夹的大小和.gitignore文件配置

    Build --> Clean Project 可以清理出很大一部分的空间 手动删除以下文件或者目录 Dir : ProjectFolder/buildDir : ProjectFolder/a ...

  5. 【Android】Eclipse自动编译NDK/JNI的三种方法

    [Android]Eclipse自动编译NDK/JNI的三种方法 SkySeraph Sep. 18th  2014 Email:skyseraph00@163.com 更多精彩请直接访问SkySer ...

  6. 【Android 系统开发】Android JNI/NDK (三) 之 JNIEnv 解析

    jni.h文件 : 了解 JNI 需要配合 jni.h 文件, jni.h 是 Google NDK 中的一个文件, 位置是 $/Android-ndk-r9d/platforms/android-1 ...

  7. Android NDK开发 Android Studio使用新的Gradle构建工具配置NDK环境(一)

    本文主要讲述了如何如何在Android Studio使用新的Gradle构建工具配置NDK环境,现在把相关的步骤整理出来分享给Android程序员兄弟们,希望给他们在配置NDK环境时带来帮助. 从An ...

  8. [转]Android通过NDK调用JNI,使用opencv做本地c++代码开发配置方法

    原文地址:http://blog.csdn.net/watkinsong/article/details/9849973 有一种方式不需要自己配置所有的Sun JDK, Android SDK以及ND ...

  9. Android JNI 和 NDK

    1.Android NDK 一.NDK产生的背景 Android平台从诞生起,就已经支持C.C++开发.众所周知,Android的SDK基于Java实现,这意味着基于Android SDK进行开发的第 ...

随机推荐

  1. V语言横空出世,C/C++/Java/Python/Go地位不保

    V语言已在github正式开源,目前已收获近9000星,引发开发者的强烈关注. V语言到底是怎样一门语言?已经有了C/C++/Java/Python/Go..., 我们还需要另外一门语言吗? 先看看V ...

  2. JVM中ClassLoader的学习

    JVM中class loaderの学习 一..class文件和jvm的关系 类的加载 所有的编译生成的.class文件都会被直接加载到JVM里面来吗(并不 首先我们明确一个概念,.class文件加载到 ...

  3. asyncio系列之sleep()实现

    先来看个例子,自己实现的模拟耗时操作 例1 import types import select import time import socket import functools class Fu ...

  4. 用kubeadm创建高可用kubernetes集群后,如何重新添加控制平面

    集群信息 集群版本:1.13.1 3个控制平面,2个worker节点 k8s-001:10.0.3.4 k8s-002:10.0.3.5 k8s-003:10.0.3.6 k8s-004:10.0.3 ...

  5. Codeforces Gym101505G:Orchard Division(扫描线+线段树第k大)

    题目链接 题意 给出一个m*m的地图,上面有n个点,现在需要用一个自定义面积的矩形笼罩住恰好n/2个点,并且这个矩形需要有一个点在至少一个角落上,问这个矩形最小的面积是多少. 思路 有点类似于扫描线. ...

  6. echo-nginx-module的安装、配置、使用

    一.下载压缩包 [root@www nginx-1.16.0]# wget https://github.com/openresty/echo-nginx-module/archive/v0.61.t ...

  7. ElasticStack学习(八):ElasticSearch索引模板与聚合分析初探

    一.Index Template与Dynamic Template的概念 1.Index Template:它是用来根据提前设定的Mappings和Settings,并按照一定的规则,自动匹配到新创建 ...

  8. [记录]Nginx配置实现&&和||的方法实例

    Nginx配置文件中if的&&和||的实现(nginx不支持&&和||的写法) 1.与(&&)的写法: set $condiction '';if ($ ...

  9. Lock和synchronized比较详解(转)

    从Java5之后,在java.util.concurrent.locks包下提供了另外一种方式来实现同步访问,那就是Lock. 也许有朋友会问,既然都可以通过synchronized来实现同步访问了, ...

  10. SpringBoot快速入门01--环境搭建

    SpringBoot快速入门--环境搭建 1.创建web工程 1.1 创建新的工程. 1.2  选择maven工程,点击下一步. 1.3 填写groupid(maven的项目名称)和artifacti ...