CMake

Pakiet Android NDK na Androida obsługuje użycie narzędzia CMake do kompilowania kodu C i C++ aplikacji. Na tej stronie opisujemy, jak korzystać z CMake z narzędziem NDK za pomocą ExternalNativeBuild wtyczki Androida do obsługi Gradle lub podczas bezpośredniego wywoływania CMake.

plik łańcucha narzędzi CMake;

NDK obsługuje CMake za pomocą plikutoolchain. Pliki narzędzi to pliki CMake, które dostosowują działanie łańcucha narzędzi na potrzeby kompilacji krzyżowej. Plik narzędzia NDK znajduje się w folderze NDK pod adresem <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

Wcześniejsze wersje NDK eksperymentowały z nową implementacją pliku łańcucha narzędzi, która miała zmniejszyć różnice w zachowaniu między użyciem pliku łańcucha narzędzi NDK a użyciem wbudowanego wsparcia CMake. Okazało się, że wymaga to znacznej ilości pracy (która nie została ukończona), ale nie poprawia to działania, więc nie będziemy już tego robić.

W „nowym” pliku narzędzia wystąpiły regresje zachowania w porównaniu z „starym” plikiem narzędzia. Działanie domyślne to zalecany proces. Jeśli używasz flagi -DANDROID_USE_LEGACY_TOOLCHAIN_FILE=OFF, zalecamy jej usunięcie z kompilacji. Nowy plik łańcucha narzędzi nigdy nie był zgodny ze starszym plikiem, więc prawdopodobnie mogą wystąpić regresje w działaniu.

Nie zalecamy używania nowego pliku narzędzi, ale nie planujemy go obecnie usuwać z NDK. Spowoduje to przerwanie kompilacji, które opierają się na różnicach w zachowaniu między nowymi a starszymi plikami narzędzi. Niestety zmiana nazwy opcji w celu wskazania, że zalecana jest wersja starsza, również spowoduje przerwanie pracy użytkowników tej opcji. Jeśli korzystasz z nowego pliku łańcucha narzędzi, nie musisz go przenosić, ale wiesz, że wszelkie błędy wynikające z działania plików w nowym łańcuchu narzędzi prawdopodobnie nie zostaną naprawione i trzeba będzie przeprowadzić migrację.

Wykorzystanie

Gradle

Korzystanie z pliku łańcucha narzędzi CMake jest automatyczne, gdy używasz opcji externalNativeBuild. Więcej informacji znajdziesz w przewodniku Dodawanie kodu C i C++ do projektu w Android Studio.

Wiersz poleceń

Podczas kompilowania za pomocą CMake poza Gradle należy przekazać do CMake plik toolchain 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 poziomu wiersza poleceń, przekaż argumenty do CMake za pomocą -D. Aby na przykład wymusić kompilację bez obsługi Neona na platformie armeabi-v7a, prześlij wartość -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 w pliku build.gradle. Aby kontrolować, na jakie interfejsy ABI Gradle ma być kierowany, użyj abiFilters zgodnie z opisem w artykule Interfejsy ABI Androida.

Wiersz poleceń

CMake kompiluje pojedynczy cel na kompilację. Aby kierować reklamy na więcej niż 1 interfejs ABI Androida, musisz utworzyć po 1 wersji na każdy interfejs ABI. Zalecamy używanie różnych katalogów kompilacji dla każdego ABI, aby uniknąć kolizji między kompilacjami.

Wartość Uwagi
armeabi-v7a
armeabi-v7a with NEON Taka sama cena jak w usłudze armeabi-v7a.
arm64-v8a
x86
x86_64

ANDROID_ARM_MODE

Określa, czy dla armeabi-v7a należy wygenerować instrukcje arm czy thumb. Nie ma to wpływu na inne interfejsy ABI. Więcej informacji znajdziesz w dokumentacji dotyczącej interfejsów ABI Androida.

Wartość Uwagi
ręka
kciuk Domyślne zachowanie.

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 Gradle na Androida, ta wartość jest automatycznie ustawiana tak, aby pasowała do wartości minSdkVersion aplikacji. Nie należy jej ustawiać ręcznie.

Wiersz poleceń

Gdy wywołujesz CMake bezpośrednio, ta wartość domyślnie przyjmuje najniższy poziom interfejsu API obsługiwany przez używany NDK. Na przykład w NDK r20 ta wartość jest domyślnie ustawiona na poziom API 16.

W przypadku tego parametru akceptowane są różne formaty:

  • android-$API_LEVEL
  • $API_LEVEL
  • android-$API_LETTER

Format $API_LETTER umożliwia określenie android-N bez konieczności określania numeru powiązanego z danym wydaniem. Pamiętaj, że niektóre wersje otrzymały nowe interfejsy API bez zmiany numeru wersji. Te interfejsy API można określić, dodając sufiks -MR1. Na przykład poziom interfejsu API 25 to android-N-MR1.

ANDROID_STL

Określa, którego STL użyć w przypadku tej aplikacji. Więcej informacji znajdziesz w artykule na temat obsługi biblioteki C++. Domyślnie używana jest wartość c++_static.

Wartość Uwagi
c++_shared Wersja biblioteki współdzielonej libc++.
c++_static Wersja statycznej biblioteki libc++.
brak Brak obsługi standardowej biblioteki C++.
system System STL

Zarządzanie flagami kompilatora

Jeśli chcesz przekazać kompilatorowi lub linkerowi określone flagi w ramach kompilacji, zapoznaj się z dokumentacją CMake dotyczącą opcji set_target_compile_options i powiązanej grupy opcji. Sekcja „Zobacz też” na dole tej strony zawiera przydatne wskazówki.

Zwykle sprawdzoną metodą jest stosowanie flag kompilatora w najwęźszym dostępnym zakresie. Flagi, które chcesz zastosować do wszystkich docelowych wartości (np. -Werror), trudno powtarzać w każdym module, ale i tak nie należy ich stosować globalnie (CMAKE_CXX_FLAGS), ponieważ może to mieć niepożądany wpływ na zależności zewnętrzne w Twoim projekcie. W takich przypadkach flagi można zastosować w zakresie katalogu (add_compile_options).

W przypadku wąskiego podzbioru flag kompilatora można również ustawić w pliku build.gradle za pomocą właściwości cppFlags lub podobnych. Nie rób tego. Flagi przekazywane do CMake z Gradle będą miały zaskakujące zachowania priorytetów, w niektórych przypadkach zastępując flagi przekazywane domyślnie przez implementację, które są wymagane do kompilowania kodu na Androida. Zawsze preferuj obsługę zachowania CMake bezpośrednio w CMake. Jeśli chcesz kontrolować flagi kompilatora w ramach AGP buildType, zapoznaj się z artykułem Praca z typami kompilacji AGP w CMake.

Praca z typami kompilacji AGP w CMake

Jeśli chcesz dostosować działanie CMake do niestandardowego Gradle buildType, użyj tego typu kompilacji, aby przekazać dodatkowy parametr CMake (a nie parametr kompilatora), który mogą odczytać skrypty kompilacji CMake. Jeśli na przykład masz warianty kompilacji „free” 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 pamiętaj, aby nie używać prefiksów ANDROID_, APP_ ani CMAKE_, aby uniknąć konfliktów lub pomyłek z dotychczasowymi flagami.

Przykładem może być plikacja Sanitizers NDK.

Poznawanie polecenia kompilacji CMake

Przy debugowaniu problemów z kompilacją CMake warto znać konkretne argumenty kompilacji, które Gradle używa do kompilacji w przypadku Androida.

Wtyczka Android Gradle zapisuje w build_command.txt argumenty kompilacji, których używa do wykonywania kompilacji CMake dla każdej pary ABI i typu kompilacji. Znajdziesz je w tym katalogu:

<project-root>/<module-root>/.cxx/cmake/<build-type>/<ABI>/

Ten fragment kodu zawiera przykładowe argumenty CMake pozwalające utworzyć możliwą do debugowania wersję przykładowej wersji 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

Korzystanie z gotowych bibliotek

Jeśli gotowa biblioteka, którą chcesz zaimportować, jest rozpowszechniana jako plik AAR, zaimportuj ją i użyj, postępując zgodnie z dokumentacją Studio na temat zależności. Jeśli nie używasz AGP, możesz śledzić stronę https://google.github.io/prefab/example-workflow.html, ale przejście na AGP jest prawdopodobnie znacznie łatwiejsze.

Instrukcje dotyczące korzystania z wstępnie skompilowanych bibliotek za pomocą CMake znajdziesz w dokumentacji add_library dotyczącej celów IMPORTEDpodręczniku CMake.

Tworzenie kodu zewnętrznego

Istnieją różne sposoby kompilowania kodu zewnętrznego w ramach projektu CMake. Wybór najlepszej opcji zależy od sytuacji. Często najlepszym rozwiązaniem jest unikanie tego. Zamiast tego utwórz AAR dla biblioteki i użyj go w swojej aplikacji. Nie musisz publikować tego pliku AAR. Może być wewnętrzny w Twoim projekcie Gradle.

Jeśli nie ma takiej możliwości:

  • Dostawca kopiuje źródło zewnętrzne do repozytorium i używa polecenia add_subdirectory, aby je skompilować. Działa to tylko wtedy, gdy druga biblioteka jest utworzona w CMake.
  • Zdefiniuj ExternalProject.
  • Utwórz bibliotekę osobno od projektu i zaimportuj ją jako gotową, postępując zgodnie z instrukcjami podanymi w artykule Używanie gotowych bibliotek.

Obsługa YASM w CMake

NDK zapewnia obsługę CMake do kompilowania kodu w języku asemblistycznym napisanego w YASM, aby można było go uruchomić na architekturach x86 i x86-64. YASM to kompilator typu open source dla architektur x86 i x86-64 oparty na kompilatorze NASM.

Aby skompilować kod asemblera za pomocą CMake, wprowadź następujące zmiany w pliku CMakeLists.txt projektu:

  1. Wywołaj metodę enable_language z wartością ASM_NASM.
  2. W zależności od tego, czy kompilujesz bibliotekę współdzieloną, czy plik wykonywalny (binarny), wywołaj add_library lub add_executable. W argumentach podaj listę plików źródłowych zawierających pliki .asm dla programu asemblera w YASM oraz pliki .c dla powiązanych bibliotek lub funkcji C.

Ten fragment kodu pokazuje, jak skonfigurować CMakeLists.txt, aby tworzyć program YASM jako udostępnioną bibliotekę.

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ładowy sposób kompilowania programu YASM jako pliku wykonywalnego znajdziesz w pliku yasmtest w repozytorium git NDK.

Zgłaszanie problemów

Jeśli napotkasz jakiekolwiek problemy z NDK lub jego narzędziem łańcucha narzędzi CMake, zgłoś je za pomocą narzędzia do śledzenia błędów android-ndk/ndk na GitHub. W przypadku problemów z Gradle lub wtyczką Androida do obsługi Gradle zgłoś błąd w Studio.