Android NDK obsługuje CMake do kompilowania kodu C i C++ na potrzeby aplikacji. Na tej stronie dowiesz się, jak używać CMake z NDK za pomocą ExternalNativeBuild wtyczki Androida do obsługi Gradle lub bezpośrednio wywołując CMake.
Plik łańcucha narzędzi CMake
NDK obsługuje CMake za pomocą pliku toolchain. Pliki toolchain to pliki CMake, które dostosowują działanie zestawu narzędzi do kompilacji skrośnej. Plik łańcucha narzędzi używany w NDK znajduje się w NDK w <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 zestawu narzędzi.
„Nowy” plik zestawu narzędzi
We wcześniejszych wersjach NDK eksperymentowaliśmy z nową implementacją pliku łańcucha narzędzi, która miała zmniejszyć różnice w zachowaniu między używaniem pliku łańcucha narzędzi NDK a używaniem wbudowanej obsługi CMake. Wymagało to znacznego nakładu pracy (która nie została ukończona), ale nie poprawiło działania, więc nie będziemy już tego kontynuować.
„Nowy” plik toolchain ma regresje zachowania w porównaniu ze „starszym” plikiem toolchain. Działanie domyślne 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ównoważności ze starszym plikiem łańcucha narzędzi, więc prawdopodobnie wystąpią regresje zachowania.
Chociaż nie zalecamy używania nowego pliku łańcucha narzędzi, obecnie nie planujemy usuwać go z NDK. Spowodowałoby to błędy kompilacji, które zależą od różnic w zachowaniu między nowymi i starszymi plikami łańcucha narzędzi. Niestety zmiana nazwy opcji, aby było jasne, że „starsza” jest w rzeczywistości zalecana, również spowodowałaby błędy u użytkowników tej opcji. Jeśli korzystasz z nowego pliku zestawu narzędzi i nie chcesz go zmieniać, nie musisz przeprowadzać migracji. Pamiętaj jednak, że błędy zgłoszone w związku z działaniem nowego pliku zestawu narzędzi prawdopodobnie nie zostaną naprawione i w takim przypadku konieczna będzie migracja.
Wykorzystanie
Gradle
Plik łańcucha narzędzi CMake jest używany automatycznie, gdy korzystasz z externalNativeBuild. Więcej informacji znajdziesz w przewodniku po Android Studio Dodawanie kodu C i C++ do projektu.
Wiersz poleceń
Podczas kompilowania 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 zestawu 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 poziomu wiersza poleceń, przekaż argumenty do CMake za pomocą -D. Aby na przykład wymusić, żeby armeabi-v7a nie kompilował 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 udostępnia ten argument automatycznie. Nie ustawiaj tego argumentu bezpośrednio w pliku build.gradle. Aby kontrolować, na które interfejsy ABI kieruje Gradle, użyj abiFilters zgodnie z opisem w artykule Interfejsy ABI Androida.
Wiersz poleceń
CMake kompiluje dla jednego celu na kompilację. Aby kierować reklamy na więcej niż 1 interfejs ABI Androida, musisz utworzyć po 1 kompilacji na każdy interfejs ABI. Zalecamy używanie różnych katalogów kompilacji dla każdego interfejsu ABI, aby uniknąć kolizji między kompilacjami.
| Wartość | Uwagi |
|---|---|
armeabi-v7a |
|
armeabi-v7a with NEON |
Takie same jak armeabi-v7a. |
arm64-v8a |
|
x86 |
|
x86_64 |
ANDROID_ARM_MODE
Określa, czy generować instrukcje ARM czy Thumb dla armeabi-v7a. Nie ma wpływu na inne interfejsy ABI. Więcej informacji znajdziesz w dokumentacji ABI Androida.
| Wartość | Uwagi |
|---|---|
| ramię | |
| thumb | Działanie domyślne. |
ANDROID_NATIVE_API_LEVEL
Alias dla ANDROID_PLATFORM.
ANDROID_PLATFORM
Określa minimalny poziom interfejsu API obsługiwany przez aplikację lub bibliotekę. Ta wartość odpowiada minSdkVersion aplikacji.
Gradle
Jeśli używasz wtyczki Androida do obsługi Gradle, ta wartość jest automatycznie ustawiana tak, aby odpowiadała wartości minSdkVersion aplikacji, i nie należy jej ustawiać ręcznie.
Wiersz poleceń
Jeśli CMake jest wywoływany bezpośrednio, ta wartość domyślnie odpowiada najniższemu poziomowi interfejsu API obsługiwanemu przez używany pakiet NDK. Na przykład w przypadku NDK r20 ta wartość domyślnie odpowiada poziomowi API 16.
Ten parametr akceptuje wiele 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 zwiększono poziom interfejsu API, ale nie zmieniono 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órej biblioteki STL używać w tej aplikacji. Więcej informacji znajdziesz w sekcji Obsługa biblioteki C++. Domyślnie używany będzie model c++_static.
| Wartość | Uwagi |
|---|---|
| c++_shared | Wariant biblioteki udostępnionej libc++. |
| c++_static | Wariant biblioteki statycznej libc++. |
| brak | Brak obsługi standardowej biblioteki C++. |
| system | systemowa biblioteka STL, |
Zarządzanie flagami kompilatora
Jeśli musisz przekazać do kompilatora lub linkera określone flagi kompilacji, zapoznaj się z dokumentacją CMake dotyczącą set_target_compile_options i powiązanych opcji. Sekcja „Zobacz też” na dole tej strony zawiera przydatne wskazówki.
Ogólnie rzecz biorąc, sprawdzoną metodą 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 poszczególnych modułach, ale rzadko powinny być stosowane globalnie (CMAKE_CXX_FLAGS), ponieważ mogą mieć niepożądany wpływ na zależności innych firm w projekcie. W takich przypadkach flagi można stosować na poziomie katalogu (add_compile_options).
W przypadku wąskiego podzbioru flag kompilatora można je też ustawić w pliku build.gradle za pomocą właściwości cppFlags lub podobnych. Nie należy tego robić. Flagi przekazywane do CMake z Gradle będą miały zaskakujące zachowania związane z priorytetem, w niektórych przypadkach zastępując flagi przekazywane niejawnie przez implementację, które są wymagane do kompilowania kodu Androida. Zawsze preferuj obsługę zachowania CMake bezpośrednio w CMake. Jeśli musisz kontrolować flagi kompilatora na poziomie wtyczki Androida do obsługi Gradle buildType, zapoznaj się z artykułem Praca z typami kompilacji wtyczki Androida do obsługi Gradle w CMake.
Praca z typami kompilacji AGP w CMake
Jeśli musisz dostosować działanie CMake do niestandardowego Gradle buildType, użyj tego typu kompilacji, aby przekazać dodatkową flagę CMake (nie flagę kompilatora), którą mogą odczytać skrypty kompilacji CMake. Jeśli na przykład masz warianty kompilacji „bezpłatny” i „premium”, które są kontrolowane przez plik 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 prefiksów ANDROID_, APP_ i CMAKE_, aby uniknąć kolizji lub pomyłek z istniejącymi flagami.
Przykład znajdziesz w przykładzie NDK z użyciem sanitizerów.
Polecenie kompilacji CMake
Podczas debugowania problemów z kompilacją CMake warto znać konkretne argumenty kompilacji, których Gradle używa podczas kompilacji skrośnej na Androida.
Wtyczka Androida do obsługi Gradle zapisuje argumenty kompilacji, których używa do wykonywania kompilacji CMake dla każdej pary ABI i typu kompilacji, w pliku build_command.txt. Te pliki znajdują się w tym katalogu:
<project-root>/<module-root>/.cxx/cmake/<build-type>/<ABI>/
Poniższy fragment kodu pokazuje przykładowe argumenty CMake do utworzenia wersji próbki hello-jni z możliwością debugowania, która jest przeznaczona 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
Korzystanie z gotowych bibliotek
Jeśli gotowa biblioteka, którą chcesz zaimportować, jest rozpowszechniana jako plik AAR, postępuj zgodnie z dokumentacją zależności Studio, aby ją zaimportować i używać. Jeśli nie używasz AGP, możesz postępować zgodnie z instrukcjami podanymi na stronie https://google.github.io/prefab/example-workflow.html, ale prawdopodobnie znacznie łatwiej będzie przejść na AGP.
W przypadku bibliotek, które nie są rozpowszechniane jako pliki AAR, instrukcje korzystania z gotowych bibliotek z CMake znajdziesz w dokumentacji add_library dotyczącej obiektów docelowych IMPORTED w instrukcji CMake.
Tworzenie kodu zewnętrznego
Istnieje kilka sposobów na skompilowanie kodu zewnętrznego w ramach projektu CMake. To, która opcja sprawdzi się najlepiej, zależy od sytuacji. Najlepszym rozwiązaniem jest często niepodejmowanie żadnych działań. Zamiast tego utwórz plik AAR dla biblioteki i użyj go w aplikacji. Nie musisz publikować tego raportu. Może być wewnętrzny dla projektu Gradle.
Jeśli nie jest to możliwe:
- Skopiuj źródło zewnętrzne do repozytorium i użyj add_subdirectory, aby je skompilować. Działa to tylko wtedy, gdy druga biblioteka również została utworzona za pomocą CMake.
- Zdefiniuj ExternalProject.
- Skompiluj bibliotekę oddzielnie od projektu i postępuj zgodnie z instrukcjami podanymi w sekcji Używanie gotowych bibliotek, aby zaimportować ją jako gotową.
Obsługa YASM w CMake
NDK zapewnia obsługę CMake do tworzenia kodu asemblera napisanego w YASM, który można uruchamiać na architekturach x86 i x86-64. YASM to oparty na NASM asembler typu open source dla architektur x86 i x86-64.
Aby skompilować kod asemblera za pomocą CMake, wprowadź te zmiany w pliku CMakeLists.txt projektu:
- Wywołaj
enable_languagez wartością ustawioną naASM_NASM. - W zależności od tego, czy tworzysz bibliotekę udostępnioną, czy plik wykonywalny, wywołaj
add_librarylubadd_executable. W argumentach przekaż listę plików źródłowych składającą się z plików.asmdla programu asemblera w YASM i plików.cdla powiązanych bibliotek lub funkcji C.
Poniższy fragment kodu pokazuje, jak skonfigurować plik CMakeLists.txt, aby skompilować program YASM jako bibliotekę udostępnioną.
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 tworzenia 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 plikiem CMake toolchain, zgłoś je w narzędziu do śledzenia problemów android-ndk/ndk na GitHubie. Jeśli masz problemy z Gradle lub wtyczką Androida do obsługi Gradle, zgłoś błąd w Studio.