Cmake 之Android库编译

一 检测库和执行程序能否在Android上用

1.1 我们知道Cmake不止能编译Linux库程序,也能编译出其它系统的库,如windows,ios和android等,那么上一篇生成的Linux的库程序能否直接用于Android上呢,下面先来做个测试。

1.2 我们先在Linux平台生成一个最简单的C语言可执行程序,main.c

创建文件

bash 复制代码
touch main.c

修改文件,文本编辑器(如vi/vim、nano等)或图形界面编辑器(如gedit、kate等)打开并修改文件内容,ctrl+x保存退出

bash 复制代码
nano main.c
cpp 复制代码
#include <stdio.h>
int main(){
	printf("Hello Word\n");
	return 0;
}

生成执行程序

bash 复制代码
gcc main.c -o appexe

运行程序,可以看到正常输出文字

bash 复制代码
$ ./appexe
Hello Word

1.3 看执行程序能否在Android上运行。Android程序需要用到ADB命令,所以使用ADB工具来测试,提前先在Linux或者Windows上安装好ADB工具

1.4 如果ADB安装在Windows,那么可以把库复制到windows上来测试,下面以Windows为例,我现在D盘已经有了个Linux生成的appexe执行程序

1.5 adb调试,先Andoird机打开开发者选项来连接工具,用adb devices查看是否连接成功。出现设备说明连接成功

bash 复制代码
>adb devices
List of devices attached
10AC741Q7K000NG device

1.6 Win+R打开终端,用adb push命令来把文件移到Android系统上,因为Andoird其它也是Linux内核,所以是可以直接执行该程序的

bash 复制代码
adb push D:\VSProject\cmaketest4\appexe /sdcard

1.7 用shell工具来测试,打开shell

bash 复制代码
adb shell

1.8 进入sdcard 目录

bash 复制代码
$ cd sdcard/

1.9 根linux一样执行程序方式一样

bash 复制代码
./appexe

1.10 先报没有权限访问,chmod 需改权限也是不行的

bash 复制代码
chmod 777 appexe
bash 复制代码
/sdcard $ ./appexe
/system/bin/sh: ./appexe: can't execute: Permission denied

1.11 需要修改放执行程序的目录为临时目录

bash 复制代码
 adb push D:/VSProject/cmaketest4/appexe /data/local/tmp/

1.12 再次执行上面步骤

bash 复制代码
/data/local/tmp $ chmod 777 appexe
/data/local/tmp $ ./appexe

11.12 错误一,这时候看到错误信息变了,说不能执行ELF文件(这是因为非交叉编译的执行文件,不能用在Android系统上),那么下面就看怎样交叉编译

/data/local/tmp $ ./appexe
/system/bin/sh: ./appexe: not executable: 64-bit ELF file

二 AndroidNDK生成可执行程序

2.1 上面已经测试Linux生成的库程序是不能在Android上用的,那么怎样才能让Android也能使用呢,这就需要用到CMake交叉编译,交叉编译可以理解为跨平台编译,来实现库的跨平台使用。

2.2 由于C++编译器的不同,所以生成的库不能跨平台。想要生成Andorid能用的库就需要Andorid平台的C++编译工具。这个工具就是AndroidNDK,NDK下载官网链接:

https://developer.android.google.cn/ndk/downloads?hl=zh-cn

下载得到压缩包zip或tar或tar.gz

首先进入要解压的文件夹,我这是/home

cd /home

我压缩文件放在/mnt/d 目录下,所以解压命令是

tar -xvf /mnt/d/android-ndk-r17c.tar

这时候NDK的完整安装路径就是 /home/android-ndk-r17c,下面就配置NDK环境变量。

打开vim编辑器,输入命令字母"o",切换为插入模式(--INSERT--),可以在光标末尾插入新行对文件进行修改

sudo vim /etc/profile

在末尾添加NDK的安装路径

export ANDROID_NDK=/home/android-ndk-r17c

export PATH=PATH:ANDROID_NDK

保存并退出,按 esc 键,退出插入模式即可进入命令模式。在末尾输入 :wq ,回车即可写入保存并离开 。或者按大写ZZ也可以快捷保存退出

刷新使配置生效

source /etc/profile

. /etc/profile

source ~/.bashrc

查看环境变量是否有NDK路径

echo $PATH

验证配置是否成功

ndk-build -v

成功会打印如下信息:

bash 复制代码
$ ndk-build -v
GNU Make 3.81
Copyright (C) 2006  Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.

This program built for x86_64-pc-linux-gnu

2.3 我们知道CMake只是构建工具,真正执行编译的是C++编译器,Linux平台的C++编译器是gcc,所以Linux的编译命令是:

bash 复制代码
gcc main.c -o appexe 

那么Android平台的编译工具在哪呢,就在NDK的toochins目录下

cpp 复制代码
 /home/android-ndk-r17c/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-gcc main.c -o appexe

这个路径从哪来的呢,那就从NDK里面找支持的C++编译器,我的NDK安装在/home/android-ndk-r17c目录下,那么就一级一级找,如下所示

上面路径太长了,每次输入容易出错,那么就可以定义一个环境变量,怎样定义参考NDK环境变量设置

cpp 复制代码
export ANDROID_NDK_GCC="/home/android-ndk-r17c/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-gcc"

简化后的指令就变为,后面CMake也要用到了

bash 复制代码
$ANDROID_NDK_GCC main.c -o appexe

2.4 错误二,提示找不到文件头

ANDROID_NDK_GCC main.c -o appexe
main.c:1:19: fatal error: stdio.h: No such file or directory
#include <stdio.h>
^
compilation terminated.

配置 --sysroot 环境变量(--sysroot 自动寻找头文件,库文件)。

寻找库文件位置 ,可以看到里面全部的库

寻找头文件位置 ,可以看到里面全部的头文件

那配置库和头文件环境变量的路径就是

bash 复制代码
export SYSROOT="--sysroot=$ANDROID_NDK/platforms/android-21/arch-arm -isystem $ANDROID_NDK/sysroot/usr/include"

查看是否设置成功

bash 复制代码
$ echo $SYSROOT
--sysroot=/home/android-ndk-r17c/platforms/android-21/arch-arm -isystem /home/android-ndk-r17c/sysroot/usr/include

再次编译

bash 复制代码
$ANDROID_NDK_GCC $SYSROOT main.c -o appexe

2.5 出现第三个错误,意思执行过程中找不到 ams(汇编)支持

ANDROID_NDK_GCC $SYSROOT main.c -o appexe
In file included from /home/android-ndk-r17c/sysroot/usr/include/sys/types.h:36:0,
from /home/android-ndk-r17c/sysroot/usr/include/stdio.h:42,
from main.c:1:
/home/android-ndk-r17c/sysroot/usr/include/linux/types.h:21:23: fatal error: asm/types.h: No such file or directory
#include <asm/types.h>
^
compilation terminated.

寻找asm路径

再次配置$SYSROOT的路径

bash 复制代码
$ export SYSROOT="--sysroot=$ANDROID_NDK/platforms/android-21/arch-arm -isystem $ANDROID_NDK/sysroot/usr/include -isystem $ANDROID_NDK/sysroot/usr/include/arm-linux-androideabi"

再次执行编译,不再报错,可以成功生成appex执行程序

bash 复制代码
$ $ANDROID_NDK_GCC $SYSROOT main.c -o appexe
$ ls
appexe  main.c

2.6 把文件按开头方法导入Android手机运行测试

bash 复制代码
C:\Users\dong>adb push D:/VSProject/cmaketest4/appexe /data/local/tmp/
D:/VSProject/cmaketest4/appexe: 1 file pushed, 0 skipped. 5.8 MB/s (6436 bytes in 0.001s)

C:\Users\dong>adb shell
PD2219:/ $ cd /data/local/tmp/
PD2219:/data/local/tmp $ ls
appexe                         device-explorer                                    sky.com.example.flutter_app.sha1
com.wnxds.tataim-build-id.txt  lldb-server                                        start_lldb_server.sh
databasesfinance.db            perfd
databasesfinance.db-wal        sky.com.dongfangdashu.tangshu.flutterproject.sha1
PD2219:/data/local/tmp $ chmod 777 appexe
PD2219:/data/local/tmp $ ./appexe
"./appexe": error: Android 5.0 and later only support position-independent executables (-fPIE).

2.7 adb运行最后一个错误,需要pie文件

/data/local/tmp $ ./appexe
"./appexe": error: Android 5.0 and later only support position-independent executables (-fPIE).

那就在末尾添加pie重新生成执行程序

bash 复制代码
$ $ANDROID_NDK_GCC $SYSROOT -pie main.c -o appexe

再次用adb测试,终于成功了,记录这一重要时刻

bash 复制代码
PD2219:/data/local/tmp $ rm -f appexe
PD2219:/data/local/tmp $ exit;

C:\Users\dong>adb push D:/VSProject/cmaketest4/appexe /data/local/tmp/
D:/VSProject/cmaketest4/appexe: 1 file pushed, 0 skipped. 14.1 MB/s (6488 bytes in 0.000s)

C:\Users\dong>adb shell
PD2219:/ $ cd /data/local/tmp/
PD2219:/data/local/tmp $  chmod 777 appexe
PD2219:/data/local/tmp $ ./appexe
Hello Word

2.8 NDK环境变量配置总结,要完成NDK对C/C++的编译至少需要配置以下环境变量

bash 复制代码
#NDK环境变量
export ANDROID_NDK=/home/android-ndk-r17c
#GCC环境变量
export ANDROID_NDK_GCC="/home/android-ndk-r17c/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-gcc"
#头文件,库文件环境变量
export SYSROOT="--sysroot=$ANDROID_NDK/platforms/android-21/arch-arm -isystem $ANDROID_NDK/sysroot/usr/include -isystem $ANDROID_NDK/sysroot/usr/include/arm-linux-androideabi"
#so库架构环境变量
export ANDROID_NDK_AR="/home/android-ndk-r17c/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-ar"

三 Android 三方so库的导入

3.1 先用androidStuiod的Native项目生成一个so库

创建所需文件

calc.h

cpp 复制代码
#include "include/calc.h"

int Calc::add(int a, int b) {
    return a+b;
}

calc.cpp

cpp 复制代码
#include "include/calc.h"

int Calc::add(int a, int b) {
    return a+b;
}

calclib.cpp

cpp 复制代码
#include <jni.h>
#include <string>
#include <filesystem>
#include "calc.h"

extern "C" JNIEXPORT jstring JNICALL
Java_com_bob_nativelib_CalcTools_stringFromJNI(
        JNIEnv* env,
        jobject) {
    Calc calcclass;
    int value = (calcclass.add(10, 10));
    std::string valueString = std::to_string(value);
    std::string hello = "相加的值是:" + valueString;
//    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

CMakeLists.txt

cpp 复制代码
cmake_minimum_required(VERSION 3.10.2)
project("calclib")


#导入头文件
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include/)
add_library(
        calclib
        SHARED
        
        #所有源文件
        calc.cpp
        calcjni.cpp)
target_link_libraries(
        calclib)

CalcTools.java

java 复制代码
package com.bob.nativelib;


public class CalcTools {
    static {
        System.loadLibrary("calclib");
    }

    public native String stringFromJNI();
}

MainActivity.java

java 复制代码
public class MainActivity extends AppCompatActivity {
    private TextView tvText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //调用函数库
        CalcTools calcTools=new CalcTools();
        Log.e("EEE", calcTools.stringFromJNI() );

    }
}

成功会输出

java 复制代码
相加的值是:20

此时在build - cmake - debug - obj 目录下会生成对应架构的so库文件

3.2 上面我们已经生成了so库,那么我们怎样引用这些so库,来进行JNI交互呢。

先新建一个module,导入该so库和头文件到我们java项目里面

3.2 在build.gradle里面增加相关配置

java 复制代码
plugins {
    id 'com.android.library'
}

android {
    namespace 'com.xixia.jincmakelib'
    compileSdk 33

    defaultConfig {
        minSdk 21
        targetSdk 33

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        consumerProguardFiles "consumer-rules.pro"
        externalNativeBuild {
            cmake {
                cppFlags ""
            }
        }
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    externalNativeBuild {
        cmake {
            path "src/main/cpp/CMakeLists.txt"
            version "3.22.1"
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

dependencies {

    implementation 'androidx.appcompat:appcompat:1.4.1'
    implementation 'com.google.android.material:material:1.5.0'
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}

4.3 CMakeLists.txt 配置修改如下:

bash 复制代码
cmake_minimum_required(VERSION 3.22.1)

project("jincmakelib")

#导入库文件开始---------------------------------------------

#导入头文件
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include/)


#导入库文件
add_library(
        calclib

        SHARED

        IMPORTED)
# 指定库的路径
set_target_properties(
        calclib

        PROPERTIES IMPORTED_LOCATION

        ${CMAKE_CURRENT_SOURCE_DIR}/${CMAKE_ANDROID_ARCH_ABI}/libcalclib.so
)
#导入库文件结束---------------------------------------------



add_library(
        jincmakelib

        SHARED

        jincmakelib.cpp)

find_library(
        log-lib

        log)

target_link_libraries(
        jincmakelib
        calclib
        ${log-lib})

3.4 编写JNI文件 jnicmaketest.cpp,调用动态库函数calc.so

cpp 复制代码
#include <jni.h>
#include <string>
#include <calc.h>

extern "C" JNIEXPORT jstring JNICALL
Java_com_xixia_jincmakelib_NativeCmakeLib_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    Calc calcclass;
    int value = (calcclass.add(10, 10));
    std::string valueString = std::to_string(value);
    std::string hello = "相加的值是:" + valueString;
    //std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

3.5 编写Java层接口NativeCmakeLib.java,加载jni库

java 复制代码
package com.xixia.jincmakelib;

public class NativeCmakeLib {

    static {
        System.loadLibrary("jincmakelib");
        System.loadLibrary("calclib");

    }

    public native String stringFromJNI();
}

至此JNI层就写好了,下面写java层来调用native层,注意so库名字一定要在前面加上lib,不然会报找不到so库错误

比如源来是这样写的

set_target_properties(

calclib

PROPERTIES IMPORTED_LOCATION

${CMAKE_CURRENT_SOURCE_DIR}/x86_64/calc.so

)

编译可以通过,但运行可能就会报错

java.lang.UnsatisfiedLinkError: dlopen failed: library "libcalclib.so" not found
at java.lang.Runtime.loadLibrary0(Runtime.java:1016)
at java.lang.System.loadLibrary(System.java:1669)
at com.xixia.jincmakelib.NativeCmakeLib.<clinit>(NativeCmakeLib.java:6)

按照提示改为libcalclib.so,当然也可能与编译有关,按提示名字改就行了

set_target_properties(

calclib

PROPERTIES IMPORTED_LOCATION

${CMAKE_CURRENT_SOURCE_DIR}/x86_64/libcalclib.so

)

3.6 java层调用JNI层,MainActivity.java

java 复制代码
public class MainActivity extends AppCompatActivity {
    private TextView tvText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tvText = (TextView) findViewById(R.id.tv_text);


        //Cmake测试so库
        NativeCmakeLib testCallBack = new NativeCmakeLib();

        String cpuArchitecture = Build.CPU_ABI; // 获取当前设备的 CPU 架构
        StringBuilder stringBuilder=new StringBuilder();
        stringBuilder.append("CPU架构:"+cpuArchitecture).append("\n");
        stringBuilder.append(testCallBack.stringFromJNI());
        tvText.setText(stringBuilder);
    }
}

能输出信息说明就成功了

java 复制代码
CPU架构:x86_64
相加的值是:20

3.7 编译过程可能遇到的错误:

错误1:

Build command failed.
Error while executing process D:\AndroidSdk\cmake\3.22.1\bin\ninja.exe with arguments {-C D:\AndroidProject\MyApplication\jincmakelib\.cxx\Debug\4r5q3z6v\x86_64 jincmakelib}
ninja: Entering directory `D:\AndroidProject\MyApplication\jincmakelib\.cxx\Debug\4r5q3z6v\x86_64'

ninja: error: 'src/x86_64/libcalclib.so', needed by 'D:/AndroidProject/MyApplication/jincmakelib/build/intermediates/cxx/Debug/4r5q3z6v/obj/x86_64/libjincmakelib.so', missing and no known rule to make it

报这样错误可能就是so文件路径没写对,比如我随便写个路径,找不断该路径就会报这错误

bash 复制代码
# 指定库的路径
set_target_properties(
        calclib

        PROPERTIES IMPORTED_LOCATION

        src/x86_64/libcalclib.so
)

这种把路径写对就可以了,相对路径,绝对路径都可以

bash 复制代码
# 指定库的路径
set_target_properties(
        calclib

        PROPERTIES IMPORTED_LOCATION

        ${CMAKE_CURRENT_SOURCE_DIR}/${CMAKE_ANDROID_ARCH_ABI}/libcalclib.so
)

错误2:

2 files found with path 'lib/x86_64/libcalclib.so' from inputs:

  • D:\AndroidProject\MyApplication\jincmakelib\build\intermediates\merged_jni_libs\debug\out\x86_64\libcalclib.so
  • D:\AndroidProject\MyApplication\jincmakelib\build\intermediates\cxx\Debug\4r5q3z6v\obj\x86_64\libcalclib.so
    If you are using jniLibs and CMake IMPORTED targets, see
    https://developer.android.com/r/tools/jniLibs-vs-imported-targets

会提示重复so库文件,这是因为我们可能放so库在jniLibs目录下,如下

bash 复制代码
# 指定库的路径
set_target_properties(
        calclib

        PROPERTIES IMPORTED_LOCATION

        ../jniLibs/x86_64/libcalclib.so
)

这个目录是自动加载so库的,我们在CMakelist.txt 里面又导入了一个,就会重复,所以我们可以放在另一个任意地方,不要放在jniLibs目录下。

bash 复制代码
# 指定库的路径
set_target_properties(
        calclib

        PROPERTIES IMPORTED_LOCATION

        ${CMAKE_CURRENT_SOURCE_DIR}/${CMAKE_ANDROID_ARCH_ABI}/libcalclib.so
)

四 AndroidNDK编译静态动态库

4.1 上面用androidStudio环境生成so库可以正常调用,那在linux环境编译出的库文件能否被android使用呢,下面来测试下

4.2 同样在cmaketest2文件夹下创建test.h和test.cpp文件

test.h

cpp 复制代码
#ifndef HEAD_H
#define HEAD_H

int add(int a,int b);


#endif

test.cpp

cpp 复制代码
#include "test.h"

int add(int a, int b) {
    return a+b;
}

main.cpp测试程序

bash 复制代码
#include <string>
#include "test.h"
int main(){
    int value = add(10, 10);
    printf("%d\n",value);
	return 0;
}

先用gcc编译器测试main.cpp是否运行正常,看到可以正常输出20

cpp 复制代码
cmaketest2$ gcc test.cpp main.cpp -o mainexe
cmaketest2$ ./mainexe
20

4.3 用AndroidNDK编译动态库,编译文成会在cmaketest2文件夹里面生成一个libcalc.so库文件

bash 复制代码
$ANDROID_NDK_GCC $SYSROOT -shared -fPIC test.cpp -o libcalc.so

4.4 用AndroidNDK编译静态库

静态库需要用到另一个编译类型,所以需要再配置一个环境变量

查找 arm-linux-androideabi-ar 位置

配置环境变量

bash 复制代码
export ANDROID_NDK_AR="/home/android-ndk-r17c/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-ar"

将这源文件编译成目标文件,生成test.o文件

bash 复制代码
$ANDROID_NDK_GCC -c test.cpp

接下来使用ar命令将目标文件打包成一个静态库文件calctlib.a

bash 复制代码
$ANDROID_NDK_AR rcs -o libcalc.a test.o

把编译出来的so库放进androdStudio里面,按上面第二标题方式编译运行,如果能成功输出信息,说明编译成功

cpp 复制代码
相加的值是:20

五 CMake配置交叉编译

5.1 上面已经用C++编译工具直接编译后的可执行程序演示,那么怎样用CMake进行交叉编译呢?来看下CMake需要增加的配置:

cpp 复制代码
cmake .. -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake \
-DANDROID_NDK=$ANDROID_NKD \
-DANDROID_ABI=arm64-v8a \
-DANDROID_TOOLCHAIN=gcc \
-DANDROID_PLATFORM=android-21 \
-DCMAKE_BUILD_TYPE=debug \
  • DCMAKE_TOOLCHAIN_FILE:指定ndk交叉编译链工具的路径,在ndk16版本以上,ndk自带cmake交叉编译工具链。
  • DANDROID_NDK:NDK的安装跟目录
  • DANDROID_ABI:各大平台:如armeabi-v7a、x86、mips等。android下基本编译一个armeabi-v7a就可以了,当然也可以加一个x86,方便在虚拟机上调试。
  • DANDROID_TOOLCHAIN:表示交叉编译链类型,取值gcc或者clang,clang++;gcc已经被废弃
  • DANDROID_PLATFORM:定义最低api版本
  • DCMAKE_BUILD_TYPE:定义构建类型,取值Debug或Release,Release

执行cmake后编译如下:

bash 复制代码
$ cmake .. -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake \
-DANDROID_NDK=$ANDROID_NKD \
-DANDROID_ABI=arm64-v8a \
-DANDROID_TOOLCHAIN=gcc \
-DANDROID_PLATFORM=android-21 \
-DCMAKE_BUILD_TYPE=debug \
>
CMake Warning at /home/android-ndk-r17c/build/cmake/android.toolchain.cmake:185 (message):
  GCC is deprecated and will be removed in the next release.  See
  https://android.googlesource.com/platform/ndk/+/master/docs/ClangMigration.md.
Call Stack (most recent call first):
  /usr/share/cmake-3.22/Modules/CMakeDetermineSystem.cmake:124 (include)
  CMakeLists.txt:2 (project)


-- The C compiler identification is GNU 4.9.0
-- The CXX compiler identification is GNU 4.9.0
-- Detecting C compiler ABI info
CMake Warning at /home/android-ndk-r17c/build/cmake/android.toolchain.cmake:185 (message):
  GCC is deprecated and will be removed in the next release.  See
  https://android.googlesource.com/platform/ndk/+/master/docs/ClangMigration.md.
Call Stack (most recent call first):
  /mnt/d/VSProject/cmaketest/build/CMakeFiles/3.22.1/CMakeSystem.cmake:6 (include)
  /mnt/d/VSProject/cmaketest/build/CMakeFiles/CMakeTmp/CMakeLists.txt:3 (project)


-- Detecting C compiler ABI info - done
-- Check for working C compiler: /home/android-ndk-r17c/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-gcc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
CMake Warning at /home/android-ndk-r17c/build/cmake/android.toolchain.cmake:185 (message):
  GCC is deprecated and will be removed in the next release.  See
  https://android.googlesource.com/platform/ndk/+/master/docs/ClangMigration.md.
Call Stack (most recent call first):
  /mnt/d/VSProject/cmaketest/build/CMakeFiles/3.22.1/CMakeSystem.cmake:6 (include)
  /mnt/d/VSProject/cmaketest/build/CMakeFiles/CMakeTmp/CMakeLists.txt:3 (project)


-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /home/android-ndk-r17c/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-g++ - skipped
-- Detecting CXX compile features
CMake Warning at /home/android-ndk-r17c/build/cmake/android.toolchain.cmake:185 (message):
  GCC is deprecated and will be removed in the next release.  See
  https://android.googlesource.com/platform/ndk/+/master/docs/ClangMigration.md.
Call Stack (most recent call first):
  /mnt/d/VSProject/cmaketest/build/CMakeFiles/3.22.1/CMakeSystem.cmake:6 (include)
  /mnt/d/VSProject/cmaketest/build/CMakeFiles/CMakeTmp/CMakeLists.txt:3 (project)


-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /mnt/d/VSProject/cmaketest/build

然后再执行make,可以成功生成so库

bash 复制代码
build$ make
[ 25%] Building C object CMakeFiles/calc.dir/src/main.c.o
[ 50%] Building C object CMakeFiles/calc.dir/src/test1.c.o
[ 75%] Building C object CMakeFiles/calc.dir/src/test2.c.o
[100%] Linking C shared library ../outpath/lib/libcalc.so
[100%] Built target calc

5.2 脚本进行cmake编译

上面步骤可以写在一个脚本里面,增加可读性,简化控制台流程

第一步放头文件,源文件,脚本文件在一个文件夹里面,如下结构

cmaketest2

├── CMakeLists.txt

├── include

│ └── test.h

└── src

├── test.cpp

└── main.cpp

CMakeList.txt 内容:

bash 复制代码
cmake_minimum_required(VERSION 3.0)
project(cmaketest2)

# 输出库文件目录
set(MYPATH /mnt/d/VSProject/cmaketest2/outpath)
set(LIBRARY_OUTPUT_PATH ${MYPATH}/lib)

# 示例:
set(SOURCE_DIR /mnt/d/VSProject/cmaketest2)
# 包含头文件
include_directories(${SOURCE_DIR}/include)
file(GLOB SRC_LIST ${SOURCE_DIR}/src/*.cpp)


# 包含库路径
# link_directories(${MYPATH}/lib)

# 链接静态库
# link_libraries(calc)

# 启动程序
# add_executable(apptest  ${SRC_LIST})
# add_library(calc STATIC ${SRC_LIST})
add_library(calc SHARED ${SRC_LIST})

# 程序启动后链接动态库
# target_link_libraries(apptest calc)


# 输出一般日志信息
# message(STATUS "STATUS Message")
# 输出警告信息
# message(WARNING "WARNING Message")
# 输出错误信息
# message(FATAL_ERROR "FATAL_ERROR Message")

第二步cmaketest项目目录,touch命令 新建 myscript.sh 脚本

bash 复制代码
touch myscript.sh

第三步打开编辑器

bash 复制代码
nano myscript.sh

第四步编写脚本内容

bash 复制代码
#!/bin/bash
rm -r build
mkdir build
cd build
cmake -DCMAKE_SYSTEM_NAME=Android \
-DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake \
-DANDROID_NDK=$ANDROID_NKD \
-DANDROID_ABI=x86_64 \
-DANDROID_TOOLCHAIN=gcc \
-DANDROID_PLATFORM=android-21 \
-DCMAKE_BUILD_TYPE=debug \
..
make
cd ..

第五步编辑好后保存退出

bash 复制代码
Ctrl + O   # 保存文件
Enter      # 确认保存
Ctrl + X   # 退出编辑器或者关闭当前的shell会话 

第六步查看文件权限

bash 复制代码
ls -l myscript.sh 

第七步添加脚本文件权限

bash 复制代码
chmod +x myscript.sh 

第八步执行该脚本

bash 复制代码
./myscript.sh

注意,可能会报换行错误,找不到文件

./myscript.sh rm: cannot remove 'build''\r': No such file or directory

这时候需要设置编码方式,打开vim文件

vim myscript.sh

设置格式,输入以下指令

: set fileformat=unix

再次输入保存退出指令

: wq

再次执行脚本就可以成功了

bash 复制代码
$ ./myscript.sh
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /mnt/d/VSProject/cmaketest/build
[ 25%] Building C object CMakeFiles/calc.dir/src/main.c.o
[ 50%] Building C object CMakeFiles/calc.dir/src/test1.c.o
[ 75%] Building C object CMakeFiles/calc.dir/src/test2.c.o
[100%] Linking C static library ../outpath/lib/libcalc.a
[100%] Built target calc

5.3 编译后会生成以下目录

cmaketest2

├── build

├── CMakeLists.txt

├── include

│ └── test.h

├── outpath

│ └── lib

│ └── libcalc.so # 动态库的名字

└── src

├── test.cpp

└── main.cpp

把编译出来的so库放进androdStudio里面,按上面第二标题方式编译运行

可以看到也能成功输出

cpp 复制代码
相加的值是:20

六 总结

编译C/C++到Android平台的so库的三种方式:

1,源码编译,把C/C++源码完成的复制到Android的项目里面,Android用jni接口来调用你C/C++代码,这种是最原始最简单的方式,但也伴随着兼容性,安全性,维护性等方面问题,所以一半不采用这种方案。

2,C/C++编译器,在Linux平台通过GCC编译器把C/C++程序编译为目标架构的so库,然后Android导入so库编译运行。

3,Cmake构建编译器脚本,通过简单配置把C/C++编译为so库,供android使用。

相关推荐
paid槮1 小时前
MySql基础:数据类型
android·mysql·adb
用户2018792831672 小时前
AMS和app通信的小秘密
android
用户2018792831672 小时前
ThreadPoolExecutor之市场雇工的故事
android
诺诺Okami2 小时前
Android Framework-Launcher-InvariantDeviceProfile
android
Antonio9153 小时前
【音视频】Android NDK 与.so库适配
android·音视频
sun00770012 小时前
android ndk编译valgrind
android
AI视觉网奇13 小时前
android studio 断点无效
android·ide·android studio
jiaxi的天空13 小时前
android studio gradle 访问不了
android·ide·android studio
No Silver Bullet14 小时前
android组包时会把从maven私服获取的包下载到本地吗
android
catchadmin14 小时前
PHP serialize 序列化完全指南
android·开发语言·php