很多時候,對於C++軟件工程師來說,可能需要編譯能夠在Android版本上直接運行的可執行程序、或者是編譯so庫文件,這個時候就需要完成交叉編譯。在命令行下執行交叉編譯有兩種方式:
- 一是用NDK自帶的工具鏈
- 二是使用獨立工具鏈
一般來說,只要使用NDK自帶的工具鏈即可滿足日常的需求。本文就這種交叉編譯方式進行講解。
本文實例源碼github地址:https://github.com/yngzMiao/yngzmiao-blogs/tree/master/2019Q4/20191219。
前期準備
對於C++軟件工程師而言,如果需要進行交叉編譯,一般需要準備如下內容:
- 下載好NDK、CMake
- 已完成的C/C++文件
- 已完成的CMakeList.txt文件
準備好這些就可以進行Android的交叉編譯了。
如果對CMakeList.txt不太熟悉,可以參考博文:【CMake】CMakeLists.txt的超傻瓜手把手教程(附實例源碼)。
其實交叉編譯的步驟,和一般情況下CMake的編譯的步驟很類似:
mkdir build && cd build
cmake ..
make
make install
兩者之間的不同完全在於:編譯工具鏈的不同。而這個不同,體現在cmake ..
的時候需要添加-D
的編譯變量和參數。
編譯變量與參數
官方文檔地址:CMake | Android NDK | Android Developers。
下表介紹在將CMake和NDK搭配使用時,可以配置的部分變量:
編譯參數 | 說明 |
---|---|
ANDROID_PLATFORM | 指定目標Android平臺的名稱,如android-18指定Android 4.3(API級別18) |
ANDROID_STL | 指定CMake應使用的STL,默認c++_static |
ANDROID_PIE | 指定是否使用位置獨立的可執行文件(PIE)。Android動態鏈接器在Android 4.1(API級別16)及更高級別上支持PIE,可設置爲On、OFF |
ANDROID_CPP_FEATURES | 指定CMake編譯原生庫時需使用的特定C++功能,可設置爲rtti(運行時類型信息)、exceptions(C++異常) |
ANDROID_ALLOW_UNDEFINED_SYMBOLS | 指定CMake在構建原生庫時,如果遇到未定義的引用,是否會引發未定義的符號錯誤。默認FALSE |
ANDROID_ARM_NEON | 指定CMake是否應構建支持NEON的原生庫。API級別爲23或更高級別時,默認值爲true,否則爲false |
ANDROID_DISABLE_FORMAT_STRING_CHECKS | 指定是否在編譯源代碼時保護格式字符串。啓用保護後,如果在printf樣式函數中使用非常量格式字符串,則編譯器會引發錯誤。默認false |
下表介紹在Android進行交叉編譯時,可以使用的具體構建參數,將有助於調試CMake構建問題:
編譯參數 | 說明 |
---|---|
ANDROID_ABI | 目標ABI,可設置爲armeabi-v7a、arm64-v8a、x86、x86_64,默認armeabi |
ANDROID_NDK | 安裝的NDK根目錄的絕對路徑 |
CMAKE_TOOLCHAIN_FILE | 進行交叉編譯的android.toolchain.cmake文件的路徑,默認在$NDK/build/cmake/目錄 |
ANDROID_TOOLCHAIN | CMake使用的編譯器工具鏈,默認爲clang |
CMAKE_BUILD_TYPE | 配置構建類型,可設置爲Release、Debug |
ANDROID_NATIVE_API_LEVEL | CMake進行編譯的Android API級別 |
CMAKE_LIBRARY_OUTPUT_DIRECTORY | 構建LIBRARY目標文件之後,CMake存放這些文件的位置 |
看着編譯的變量和參數都不少,那如何來抉擇呢?
其實,一般情況下,只需要配置ANDROID_ABI
、ANDROID_NDK
、CMAKE_TOOLCHAIN_FILE
、ANDROID_PLATFORM
四個變量即可。
ANDROID_ABI是CPU架構,ANDROID_NDK是NDK的根目錄,CMAKE_TOOLCHAIN_FILE是工具鏈文件,ANDROID_PLATFORM是支持的最低Android平臺。
爲什麼指定ANDROID_PLATFORM?如果不指定,Android平臺版本較低,此時ANDROID_PIE默認爲OFF,可執行程序無法執行。
實例
實例內容:編譯C++程序爲可執行文件,程序內容實現打印語句Hello World。
主程序內容特別簡單:
# include <iostream>
int main(int argc, char const *argv[])
{
for(int i = 0; i < 5; ++i)
std::cout << "Hello World" << std::endl;
return 0;
}
編寫CMakeLists.txt:
cmake_minimum_required(VERSION 3.0)
project(main)
add_definitions("-Wall -g")
add_executable(${PROJECT_NAME} main.cpp )
install(TARGETS ${PROJECT_NAME}
RUNTIME DESTINATION ${PROJECT_SOURCE_DIR})
編寫編譯腳本:
#/bin/bash
export ANDROID_NDK=/opt/env/android-ndk-r14b
rm -r build
mkdir build && cd build
cmake -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake \
-DANDROID_ABI="armeabi-v7a" \
-DANDROID_NDK=$ANDROID_NDK \
-DANDROID_PLATFORM=android-22 \
..
make && make install
cd ..
此時會生成可執行文件main,push到Android設備上運行,可以看到Hello World的打印信息。