Pakiet Android NDK obsługuje używanie CMake do
kompilowania kodu C i C++ na potrzeby aplikacji. Z tej strony dowiesz się, jak używać CMake z NDK za pomocą wtyczki Androida do obsługi Gradle ExternalNativeBuild lub podczas bezpośredniego wywoływania CMake.
Plik łańcucha narzędzi CMake
NDK obsługuje CMake za pomocą pliku łańcucha narzędzi. Pliki łańcucha narzędzi to pliki CMake, które dostosowują działanie łańcucha narzędzi do kompilacji krzyżowej. Plik łańcucha narzędzi używany w NDK znajduje się w NDK w lokalizacji
<NDK>/build/cmake/android.toolchain.cmake.
Parametry kompilacji, takie jak ABI, minSdkVersion itp., są podawane w wierszu poleceń podczas wywoływania cmake. Listę obsługiwanych argumentów znajdziesz w sekcji
Argumenty łańcucha narzędzi.
„Nowy” plik łańcucha narzędzi
W starszych wersjach NDK eksperymentowano z nową implementacją pliku łańcucha narzędzi, która miała zmniejszyć różnice w działaniu między używaniem pliku łańcucha narzędzi NDK a używaniem wbudowanej obsługi CMake. Wymagało to znacznej ilości pracy (która nie została ukończona), ale nie poprawiło działania, więc nie kontynuujemy tego.
„Nowy” plik łańcucha narzędzi ma regresje w działaniu w porównaniu ze „starszym” plikiem łańcucha narzędzi. Domyślne działanie to zalecany przepływ pracy. Jeśli używasz -DANDROID_USE_LEGACY_TOOLCHAIN_FILE=OFF, zalecamy usunięcie tej flagi z kompilacji. Nowy plik łańcucha narzędzi nigdy nie osiągnął równości ze starszym plikiem łańcucha narzędzi, więc prawdopodobnie występują regresje w działaniu.
Chociaż nie zalecamy używania nowego pliku łańcucha narzędzi, obecnie nie planujemy jego usunięcia z NDK. Spowodowałoby to przerwanie kompilacji, które opierają się na różnicach w działaniu między nowym a starszym plikiem łańcucha narzędzi. Niestety zmiana nazwy opcji, aby było jasne, że zalecany jest „starszy” plik, również spowodowałaby problemy u użytkowników tej opcji. Jeśli używasz nowego pliku łańcucha narzędzi i wszystko działa prawidłowo, nie musisz przeprowadzać migracji. Pamiętaj jednak, że błędy zgłoszone w związku z działaniem nowego pliku łańcucha narzędzi prawdopodobnie nie zostaną naprawione i konieczna będzie migracja.
Wykorzystanie
Gradle
Używanie pliku łańcucha narzędzi CMake jest automatyczne, gdy używasz externalNativeBuild. Więcej informacji znajdziesz w przewodniku Androida Studio Dodawanie kodu C i C++ do
projektu.
Wiersz poleceń
Podczas kompilacji za pomocą CMake poza Gradle do CMake należy przekazać sam plik łańcucha narzędzi i jego argumenty. Na przykład:
$ cmake \
-DCMAKE_TOOLCHAIN_FILE=$NDK/build/cmake/android.toolchain.cmake \
-DANDROID_ABI=$ABI \
-DANDROID_PLATFORM=android-$MINSDKVERSION \
$OTHER_ARGS
Argumenty łańcucha narzędzi
Do pliku łańcucha narzędzi CMake można przekazać te argumenty. Jeśli kompilujesz
za pomocą Gradle, dodaj argumenty do
android.defaultConfig.externalNativeBuild.cmake.arguments zgodnie z opisem w
dokumentacji ExternalNativeBuild. Jeśli kompilujesz z wiersza poleceń, przekaż argumenty do CMake za pomocą -D. Aby na przykład wymusić, aby armeabi-v7a nie kompilowało się z obsługą Neon, przekaż -DANDROID_ARM_NEON=FALSE.
ANDROID_ABI
Docelowy interfejs ABI. Informacje o obsługiwanych interfejsach ABI znajdziesz w artykule Interfejsy ABI Androida.
Gradle
Gradle automatycznie udostępnia ten argument. Nie ustawiaj tego argumentu jawnie w pliku build.gradle. Aby kontrolować, które interfejsy ABI są kierowane przez Gradle,
użyj abiFilters zgodnie z opisem w Interfejsy ABI Androida.
Wiersz poleceń
CMake kompiluje tylko jeden cel na kompilację. Aby kierować na więcej niż 1 interfejs ABI Androida, musisz kompilować raz na interfejs ABI. Aby uniknąć kolizji między kompilacjami, zalecamy używanie różnych katalogów kompilacji dla każdego interfejsu ABI.
| Wartość | Uwagi |
|---|---|
armeabi-v7a |
|
armeabi-v7a with NEON |
Tak samo jak armeabi-v7a. |
arm64-v8a |
|
x86 |
|
x86_64 |
ANDROID_ARM_MODE
Określa, czy dla armeabi-v7a mają być generowane instrukcje arm czy thumb. Nie ma wpływu na inne interfejsy ABI. Więcej informacji znajdziesz w dokumentacji Interfejsy ABI Androida.
| Wartość | Uwagi |
|---|---|
| arm | |
| thumb | Domyślne działanie. |
ANDROID_NATIVE_API_LEVEL
Alias ANDROID_PLATFORM.
ANDROID_PLATFORM
Określa minimalny poziom API obsługiwany przez aplikację lub bibliotekę. Ta wartość odpowiada minSdkVersion aplikacji.
Gradle
Gdy używasz wtyczki Androida do obsługi Gradle, ta wartość jest automatycznie ustawiana tak, aby pasowała do minSdkVersion aplikacji, i nie należy jej ustawiać ręcznie.
Wiersz poleceń
Gdy wywołujesz CMake bezpośrednio, ta wartość domyślnie przyjmuje najniższy poziom API obsługiwany przez używany NDK. Na przykład w przypadku NDK r20 ta wartość domyślnie przyjmuje poziom API 16.
ANDROID_PLATFORM
Ten parametr może mieć kilka formatów:
android-$API_LEVEL$API_LEVELandroid-$API_LETTER
Format $API_LETTER umożliwia określenie android-N bez konieczności określania numeru powiązanego z tą wersją. Pamiętaj, że w przypadku niektórych wersji poziom API został zwiększony bez zwiększenia litery. Te interfejsy API można określić, dodając sufiks -MR1. Na przykład poziom API 25 to android-N-MR1.
ANDROID_STL
Określa, którego STL ma używać ta aplikacja. Więcej informacji znajdziesz w artykule C++
Obsługa biblioteki. Domyślnie używana jest biblioteka c++_static.
| Wartość | Uwagi |
|---|---|
| c++_shared | Wariant biblioteki współdzielonej libc++. |
| c++_static | Wariant biblioteki statycznej libc++. |
| none | Brak obsługi standardowej biblioteki C++. |
| system | Systemowa biblioteka system STL |
Zarządzanie flagami kompilatora
Jeśli musisz przekazać do kompilatora lub konsolidatora określone flagi kompilacji, zapoznaj się z dokumentacją CMake dotyczącą set_target_compile_options i powiązanych opcji. Przydatne wskazówki znajdziesz w sekcji „Zobacz też” na dole tej strony.
Ogólnie najlepszym rozwiązaniem jest stosowanie flag kompilatora w najwęższym dostępnym zakresie. Flagi, które chcesz zastosować do wszystkich celów (np. -Werror), są niewygodne do powtarzania w każdym module, ale nadal rzadko należy je stosować globalnie (CMAKE_CXX_FLAGS), ponieważ mogą mieć niepożądane skutki dla zależności innych firm w projekcie. W takich przypadkach flagi można stosować w zakresie katalogu (add_compile_options).
W przypadku wąskiego podzbioru flag kompilatora można je też ustawić w pliku build.gradle za pomocą cppFlags lub podobnych właściwości. Nie należy tego robić. Flagi przekazywane do CMake z Gradle będą miały zaskakujące zachowanie priorytetowe, w niektórych przypadkach zastępując flagi przekazywane niejawnie przez implementację, które są wymagane do kompilacji kodu Androida. Zawsze preferuj bezpośrednie zarządzanie działaniem CMake w CMake. Jeśli musisz kontrolować flagi kompilatora w zależności od wtyczki AGP buildType,
przeczytaj artykuł Praca z typami kompilacji AGP w CMake.
Praca z typami kompilacji AGP w CMake
Jeśli musisz dostosować działanie CMake do niestandardowego rodzaju kompilacji Gradle buildType, użyj tego rodzaju kompilacji, aby przekazać dodatkową flagę CMake (nie flagę kompilatora), którą mogą odczytać skrypty kompilacji CMake. Jeśli na przykład masz warianty kompilacji „free” i „premium”, które są kontrolowane przez build.gradle.kts, i musisz przekazać te dane do CMake:
android {
buildTypes {
free {
externalNativeBuild {
cmake {
arguments.add("-DPRODUCT_VARIANT_PREMIUM=OFF")
}
}
}
premium {
externalNativeBuild {
cmake {
arguments.add("-DPRODUCT_VARIANT_PREMIUM=ON")
}
}
}
}
}
Następnie w pliku CMakeLists.txt:
if (DPRODUCT_VARIANT_PREMIUM)
# Do stuff for the premium build.
else()
# Do stuff for the free build.
endif()
Nazwa zmiennej zależy od Ciebie, ale unikaj wszystkiego, co ma prefiks ANDROID_, APP_ lub CMAKE_, aby uniknąć kolizji lub pomyłek z istniejącymi flagami.
Przykład znajdziesz w przykładzie NDK Sanitizers.
Informacje o poleceniu kompilacji CMake
Podczas debugowania problemów z kompilacją CMake warto znać konkretne argumenty kompilacji, których Gradle używa podczas kompilacji krzyżowej na Androida.
Wtyczka Androida do obsługi Gradle zapisuje argumenty kompilacji, których używa do wykonywania kompilacji CMake dla każdej pary interfejsu ABI i rodzaju kompilacji, w pliku build_command.txt. Te pliki znajdują się w tym katalogu:
<project-root>/<module-root>/.cxx/cmake/<build-type>/<ABI>/
Ten fragment kodu pokazuje przykład argumentów CMake do kompilacji wersji z możliwością debugowania przykładu hello-jni kierowanej na architekturę armeabi-v7a.
Executable : ${HOME}/Android/Sdk/cmake/3.10.2.4988404/bin/cmake
arguments :
-H${HOME}/Dev/github-projects/googlesamples/ndk-samples/hello-jni/app/src/main/cpp
-DCMAKE_FIND_ROOT_PATH=${HOME}/Dev/github-projects/googlesamples/ndk-samples/hello-jni/app/.cxx/cmake/universalDebug/prefab/armeabi-v7a/prefab
-DCMAKE_BUILD_TYPE=Debug
-DCMAKE_TOOLCHAIN_FILE=${HOME}/Android/Sdk/ndk/22.1.7171670/build/cmake/android.toolchain.cmake
-DANDROID_ABI=armeabi-v7a
-DANDROID_NDK=${HOME}/Android/Sdk/ndk/22.1.7171670
-DANDROID_PLATFORM=android-23
-DCMAKE_ANDROID_ARCH_ABI=armeabi-v7a
-DCMAKE_ANDROID_NDK=${HOME}/Android/Sdk/ndk/22.1.7171670
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON
-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=${HOME}/Dev/github-projects/googlesamples/ndk-samples/hello-jni/app/build/intermediates/cmake/universalDebug/obj/armeabi-v7a
-DCMAKE_RUNTIME_OUTPUT_DIRECTORY=${HOME}/Dev/github-projects/googlesamples/ndk-samples/hello-jni/app/build/intermediates/cmake/universalDebug/obj/armeabi-v7a
-DCMAKE_MAKE_PROGRAM=${HOME}/Android/Sdk/cmake/3.10.2.4988404/bin/ninja
-DCMAKE_SYSTEM_NAME=Android
-DCMAKE_SYSTEM_VERSION=23
-B${HOME}/Dev/github-projects/googlesamples/ndk-samples/hello-jni/app/.cxx/cmake/universalDebug/armeabi-v7a
-GNinja
jvmArgs :
Build command args: []
Version: 1
Używanie wstępnie skompilowanych bibliotek
Jeśli wstępnie skompilowana biblioteka, którą chcesz zaimportować, jest rozpowszechniana jako AAR, zaimportuj ją i użyj zgodnie z dokumentacją zależności w Studio. Jeśli nie używasz wtyczki AGP, możesz postępować zgodnie z instrukcjami na stronie https://google.github.io/prefab/example-workflow.html, ale prawdopodobnie łatwiej będzie przejść na wtyczkę AGP.
W przypadku bibliotek, które nie są rozpowszechniane jako AAR, instrukcje dotyczące używania wstępnie skompilowanych
bibliotek z CMake znajdziesz w add_library dokumentacji dotyczącej IMPORTED
celów w podręczniku CMake.
Kompilowanie kodu innych firm
Istnieje kilka sposobów kompilowania kodu innych firm w ramach projektu CMake. Najlepsza opcja zależy od Twojej sytuacji. Najlepszym rozwiązaniem jest często w ogóle tego nie robić. Zamiast tego skompiluj bibliotekę jako AAR i użyj jej w aplikacji. Nie musisz publikować tego pliku AAR. Może on być wewnętrzny dla projektu Gradle.
Jeśli nie jest to możliwe:
- Skopiuj źródło innej firmy do repozytorium i użyj add_subdirectory, aby je skompilować. Działa to tylko wtedy, gdy druga biblioteka jest też kompilowana za pomocą CMake.
- Zdefiniuj an ExternalProject.
- Skompiluj bibliotekę oddzielnie od projektu i zaimportuj ją jako wstępnie skompilowaną zgodnie z instrukcjami w artykule Używanie wstępnie skompilowanych bibliotek.
Obsługa YASM w CMake
NDK zapewnia obsługę CMake do kompilowania kodu asemblera napisanego w YASM, który ma być uruchamiany na architekturach x86 i x86-64. YASM to asembler open source dla architektur x86 i x86-64 oparty na asemblerze NASM.
Aby skompilować kod asemblera za pomocą CMake, wprowadź te zmiany w pliku CMakeLists.txt projektu:
- Wywołaj
enable_language, ustawiając wartość naASM_NASM. - W zależności od tego, czy kompilujesz bibliotekę współdzieloną czy plik wykonywalny
, wywołaj
add_librarylubadd_executable. W argumentach przekaż listę plików źródłowych składającą się z plików.asmprogramu asemblera w YASM oraz plików.cpowiązanych bibliotek lub funkcji C.
Ten fragment kodu pokazuje, jak skonfigurować plik CMakeLists.txt, aby skompilować program YASM jako bibliotekę współdzieloną.
cmake_minimum_required(VERSION 3.6.0)
enable_language(ASM_NASM)
add_library(test-yasm SHARED jni/test-yasm.c jni/print_hello.asm)
Przykład kompilowania programu YASM jako pliku wykonywalnego znajdziesz w teście yasm w repozytorium git NDK.
Zgłaszanie problemów
Jeśli napotkasz problemy z NDK lub jego plikiem łańcucha narzędzi CMake, zgłoś je w narzędziu do śledzenia problemów android-ndk/ndk w GitHub. W przypadku problemów z Gradle lub wtyczką Androida do obsługi Gradle zgłoś błąd w Studio zamiast tego.