Cmake 之Android库编译

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

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

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

创建文件

touch main.c

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

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

生成执行程序

gcc main.c -o appexe

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

$ ./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查看是否连接成功。出现设备说明连接成功

>adb devices
List of devices attached
10AC741Q7K000NG device

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

adb push D:\VSProject\cmaketest4\appexe /sdcard

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

adb shell

1.8 进入sdcard 目录

$ cd sdcard/

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

./appexe

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

chmod 777 appexe
/sdcard $ ./appexe
/system/bin/sh: ./appexe: can't execute: Permission denied

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

 adb push D:/VSProject/cmaketest4/appexe /data/local/tmp/

1.12 再次执行上面步骤

/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

 成功会打印如下信息:

$ 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的编译命令是:

gcc main.c -o appexe 

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

 /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环境变量设置

export ANDROID_NDK_GCC="/home/android-ndk-r17c/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-gcc"

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

$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 自动寻找头文件,库文件)。

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

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

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

export SYSROOT="--sysroot=$ANDROID_NDK/platforms/android-21/arch-arm -isystem $ANDROID_NDK/sysroot/usr/include"

 查看是否设置成功

$ echo $SYSROOT
--sysroot=/home/android-ndk-r17c/platforms/android-21/arch-arm -isystem /home/android-ndk-r17c/sysroot/usr/include

再次编译

$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的路径

$ 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执行程序

$ $ANDROID_NDK_GCC $SYSROOT main.c -o appexe
$ ls
appexe  main.c

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

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重新生成执行程序

$ $ANDROID_NDK_GCC $SYSROOT -pie main.c -o appexe

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

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++的编译至少需要配置以下环境变量

#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

#include "include/calc.h"

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

calc.cpp

#include "include/calc.h"

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

calclib.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

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

package com.bob.nativelib;


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

    public native String stringFromJNI();
}

MainActivity.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() );

    }
}

成功会输出

相加的值是:20

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

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

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

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

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 配置修改如下:

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

#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库

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

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);
    }
}

能输出信息说明就成功了

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文件路径没写对,比如我随便写个路径,找不断该路径就会报这错误

# 指定库的路径
set_target_properties(
        calclib

        PROPERTIES IMPORTED_LOCATION

        src/x86_64/libcalclib.so
)

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

# 指定库的路径
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目录下,如下

# 指定库的路径
set_target_properties(
        calclib

        PROPERTIES IMPORTED_LOCATION

        ../jniLibs/x86_64/libcalclib.so
)

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

# 指定库的路径
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

#ifndef HEAD_H
#define HEAD_H

int add(int a,int b);


#endif

test.cpp

#include "test.h"

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

main.cpp测试程序

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

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

cmaketest2$ gcc test.cpp main.cpp -o mainexe
cmaketest2$ ./mainexe
20

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

$ANDROID_NDK_GCC $SYSROOT -shared -fPIC test.cpp -o libcalc.so

4.4 用AndroidNDK编译静态库

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

查找 arm-linux-androideabi-ar 位置

配置环境变量 

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文件

$ANDROID_NDK_GCC -c test.cpp

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

$ANDROID_NDK_AR rcs -o libcalc.a test.o

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

相加的值是:20

五 CMake配置交叉编译

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

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后编译如下:

$ 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库

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 内容:

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 脚本

touch myscript.sh

第三步打开编辑器

nano myscript.sh

第四步编写脚本内容

#!/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 ..

第五步编辑好后保存退出

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

第六步查看文件权限

ls -l myscript.sh 

第七步添加脚本文件权限

chmod +x myscript.sh 

第八步执行该脚本

./myscript.sh

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

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

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

vim myscript.sh

设置格式,输入以下指令

: set fileformat=unix

再次输入保存退出指令

 : wq

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

$ ./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里面,按上面第二标题方式编译运行

可以看到也能成功输出

相加的值是: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使用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/335949.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

实验算法设计

文章目录 Unettransformer整体网络架构 Unet 可以用双线性差值替换&#xff0c;效果差不多&#xff0c;参数更少。 from typing import Dict import torch import torch.nn as nn import torch.nn.functional as F class DoubleConv(nn.Sequential):def __init__(self, in_cha…

interpret,一个超酷的 Python 库

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com 大家好&#xff0c;今天为大家分享一个超酷的 Python 库 - interpret。 Github地址&#xff1a;https://github.com/interpretml/interpret Python Interpret 是一个强大的开源工具&#xff0c;它为 Python 开发…

Crow:设置网站的index.html

对于一个网展来说,index.html是其第一个页面,也是根页面,如何通过Crow来加载index.html呢。 Crow:静态资源使用举例-CSDN博客 讲述了静态资源的使用,也就是通常存饭html,css,jpg文件的地方 当然index.html也会放在这个目录,但通常是放在static的根目录,其他资源会根据…

2.1.4-相关性分析

跳转到根目录&#xff1a;知行合一&#xff1a;投资篇 已完成&#xff1a; 1、投资&技术   1.1.1 投资-编程基础-numpy   1.1.2 投资-编程基础-pandas   1.2 金融数据处理   1.3 金融数据可视化 2、投资方法论   2.1.1 预期年化收益率   2.1.2 一个关于yaxb的…

vuex-跨模块访问

1. 场景 案例&#xff1a;跨模块访问和退出登录 假设我们有一个Vuex store&#xff0c;其中包含user模块和cart模块。当用户点击退出登录按钮时&#xff0c;我们需要调用user模块中的方法来清除用户信息&#xff0c;同时还需要清除cart模块中的购物车数据。 2. 实现-跨模块访…

air001研究笔记.基于arduino快速开发简单项目

一、air001芯片简介 air001是厂商合宙推出的一款tssop封装的mcu芯片。支持swd与串口烧录&#xff0c;多面向简单的功能简单类别的电子产品&#xff0c;因为官方文档齐全上手简易&#xff0c;所以也特别适合非专业爱好者乃至于幼儿编程。芯片内置资源&#xff1a;AIR001芯片数据…

国产AI新篇章:书生·浦语2.0带来200K超长上下文解决方案

总览&#xff1a;大模型技术的快速演进 自2023年7月6日“书生浦语”&#xff08;InternLM&#xff09;在世界人工智能大会上正式开源以来&#xff0c;其在社区和业界的影响力日益扩大。在过去半年中&#xff0c;大模型技术体系经历了快速的演进&#xff0c;特别是100K级别的长…

用LED数码显示器循环显示数字0~9

#include<reg51.h> // 包含51单片机寄存器定义的头文件 /************************************************** 函数功能&#xff1a;延时函数&#xff0c;延时一段时间 ***************************************************/ void delay(void) { unsigned …

Docker项目部署()

1.创建文件夹tools mkdir tools 配置阿里云 Docker Yum 源 : yum install - y yum - utils device - mapper - persistent - data lvm2 yum - config - manager -- add - repo http://mirrors.aliyun.com/docker- ce/linux/centos/docker - ce.repo 更新 yum 缓存 yum makec…

视频剪辑技巧:一键批量制作画中画视频的方法,高效提升剪辑任务

在数字媒体时代&#xff0c;视频剪辑已成为一项重要的技能。无论是专业的影视制作&#xff0c;还是日常的社交媒体分享&#xff0c;掌握视频剪辑技巧都能为内容增色不少。下面来看云炫AI智剪如何高效的剪辑视频技巧&#xff1a;一键批量制作画中画视频的方法&#xff0c;帮助您…

Vue3前端开发,provide和enject的基础练习,跨层级传递数据

Vue3前端开发,provide和enject的基础练习,跨层级传递数据&#xff01; 声明:provide虽然可以跨层级传递&#xff0c;但是依旧是需要由上向下的方向传递。根传子的方向。 <script setup> import {onMounted, ref} from vue import Base from ./components/Base.vue impor…

ssrf漏洞代码审计之douphp解析(超详细)

1.进入douphp的安装界面 www.douphp.com/install/ 由此可知安装界面已经被锁定了&#xff0c;但是由于install.lock是可控的&#xff0c;删除了install.lock后即可进行安装&#xff0c;所以我们现在的目的就是找到怎么去删除install.lock的方法。 要删除目标网站的任意文件&a…

人工智能-机器学习-深度学习-分类与算法梳理

人工智能-机器学习-深度学习-分类与算法梳理 目前人工智能的概念层出不穷&#xff0c;容易搞混&#xff0c;理清脉络&#xff0c;有益新知识入脑。 为便于梳理&#xff0c;本文只有提纲&#xff0c;且笔者准备仓促&#xff0c;敬请勘误&#xff0c;不甚感激。 请看右边目录索引…

动态规划Day14(子序列第二天)

目录 1143.最长公共子序列 看到题目的第一想法 看到代码随想录之后的想法 自己实现过程中遇到的困难 1035.不相交的线 看到题目的第一想法 看到代码随想录之后的想法 自己实现过程中遇到的困难 53. 最大子序和 看到题目的第一想法 …

网络编程01 常见名词的一些解释

本文将讲解网络编程的一些常见名词以及含义 在这之前让我们先唠一唠网络的产生吧,其实网络的产生也拯救了全世界 网络发展史 网络的产生是在美苏争霸的期间,实际上双方都持有核武器,希望把对方搞垮的同时不希望自己和对方两败俱伤. 希望破坏对方的核武器发射,这就涉及到三个方面…

【Vue】vue项目中Uncaught runtime errors:怎样关闭

vue项目中Uncaught runtime errors:怎样关闭 一、背景描述二、报错原因三、解决方案3.1 只显示错误信息不全屏覆盖3.2 取消全屏覆盖 四、参考资料 一、背景描述 项目本来运行的好好&#xff0c;换了个新的浏览器&#xff0c;新的Chrome浏览器版本号是116.0.5845.97&#xff08…

【Linux】Linux进程间通信(四)

​ ​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;Linux &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 上一篇博客&#xff1a;【Linux】…

flask分页宏增加更多参数

背景&#xff1a;我正在开发一个博客&#xff0c;核心的两个model是文章和文章类别。 现在想要实现的功能是&#xff1a;点击一个文章类别&#xff0c;以分页的形式显示这个文章类别下的所有文章&#xff0c;类似这种效果。 参考的书中分页宏只接受页数这一个参数&#xff0c;…

NLP论文阅读记录 - 2021 | WOS MAPGN:用于序列到序列预训练的掩码指针生成器网络

文章目录 前言0、论文摘要一、Introduction1.1目标问题1.2相关的尝试1.3本文贡献 二.前提三.本文方法四 实验效果4.1数据集4.2 对比模型4.3实施细节4.4评估指标4.5 实验结果4.6 细粒度分析 五 总结思考 前言 MAPGN: MASKED POINTER-GENERATOR NETWORK FOR SEQUENCE-TO-SEQUENCE…

python常用库

常见模块解析 1. math库 数学函数 函数返回值 ( 描述 )abs(x)返回数字的绝对值&#xff0c;如abs(-10) 返回 10ceil(x)返回数字的上入整数&#xff0c;如math.ceil(4.1) 返回 5cmp(x, y)如果 x < y 返回 -1, 如果 x y 返回 0, 如果 x > y 返回 1。 **Python 3 已废弃…