CMake konfigurieren

Ein CMake-Build-Script ist eine Nur-Text-Datei, die CMakeLists.txt genannt werden muss und Befehle enthält, mit denen CMake Ihre C/C++-Bibliotheken erstellt. Wenn Ihre nativen Quellen noch kein CMake-Build-Skript haben, müssen Sie selbst eines erstellen und die entsprechenden CMake-Befehle einfügen. Informationen zum Installieren von CMake finden Sie unter NDK und CMake installieren und konfigurieren.

In diesem Abschnitt werden einige grundlegende Befehle beschrieben, die Sie in Ihr Build-Skript aufnehmen sollten, damit CMake festlegen kann, welche Quellen beim Erstellen Ihrer nativen Bibliothek verwendet werden sollen. Weitere Informationen finden Sie in der offiziellen Dokumentation zu CMake-Befehlen.

Nachdem Sie ein neues CMake-Build-Skript konfiguriert haben, müssen Sie Gradle so konfigurieren, dass Ihr CMake-Projekt als Build-Abhängigkeit angegeben wird, damit Gradle Ihre native Bibliothek mit dem APK Ihrer App erstellt und verpackt.

Hinweis: Wenn Ihr Projekt ndk-build verwendet, müssen Sie kein CMake-Build-Skript erstellen. Sie können Gradle einfach so konfigurieren, dass Ihr vorhandenes natives Bibliotheksprojekt einbezogen wird. Geben Sie dazu einen Pfad zur Datei Android.mk an.

CMake-Build-Script erstellen

So erstellen Sie eine Nur-Text-Datei, die Sie als Build-Script in CMake verwenden können:

  1. Öffnen Sie links in der IDE den Bereich Projekt und wählen Sie im Drop-down-Menü die Ansicht Projekt aus.
  2. Klicken Sie mit der rechten Maustaste auf das Stammverzeichnis von your-module und wählen Sie Neu > Datei aus.

    Hinweis: Sie können das Build-Skript an einem beliebigen Speicherort erstellen. Beim Konfigurieren des Build-Skripts beziehen sich die Pfade zu den nativen Quelldateien und Bibliotheken jedoch auf den Speicherort des Build-Skripts.

  3. Geben Sie „CMakeLists.txt“ als Dateinamen ein und klicken Sie auf OK.

Sie können Ihr Build-Script jetzt konfigurieren, indem Sie CMake-Befehle hinzufügen. Um CMake anzuweisen, eine native Bibliothek aus nativem Quellcode zu erstellen, fügen Sie Ihrem Build-Skript die Befehle cmake_minimum_required() und add_library() hinzu:

# Sets the minimum version of CMake required to build your native library.
# This ensures that a certain set of CMake features is available to
# your build.

cmake_minimum_required(VERSION 3.4.1)

# Specifies a library name, specifies whether the library is STATIC or
# SHARED, and provides relative paths to the source code. You can
# define multiple libraries by adding multiple add_library() commands,
# and CMake builds them for you. When you build your app, Gradle
# automatically packages shared libraries with your APK.

add_library( # Specifies the name of the library.
             native-lib

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             src/main/cpp/native-lib.cpp )

Tipp: Ähnlich wie bei der Anweisung, dass CMake eine native Bibliothek aus Quelldateien erstellt, können Sie CMake mit dem Befehl add_executable() anweisen, stattdessen eine ausführbare Datei aus diesen Quelldateien zu erstellen. Das Erstellen ausführbarer Dateien aus Ihren nativen Quellen ist jedoch optional. Das Erstellen nativer Bibliotheken für die Verpackung in Ihr APK erfüllt die meisten Projektanforderungen.

Wenn Sie Ihrem CMake-Build-Skript eine Quelldatei oder Bibliothek mit add_library() hinzufügen, zeigt Android Studio nach der Synchronisierung Ihres Projekts in der Projektansicht auch zugehörige Headerdateien an. Damit CMake jedoch die Headerdateien während der Kompilierungszeit finden kann, müssen Sie dem CMake-Build-Skript den Befehl include_directories() hinzufügen und den Pfad zu Ihren Headern angeben:

add_library(...)

# Specifies a path to native header files.
include_directories(src/main/cpp/include/)

CMake verwendet folgende Konvention, um die Datei Ihrer Bibliothek zu benennen:

liblibrary-name.so

Wenn Sie beispielsweise im Build-Skript "native-lib" als Namen Ihrer gemeinsam genutzten Bibliothek angeben, erstellt CMake eine Datei mit dem Namen libnative-lib.so. Wenn Sie diese Bibliothek jedoch in Ihren Java- oder Kotlin-Code laden, verwenden Sie den Namen, den Sie im Build-Script von CMake angegeben haben:

Kotlin

companion object {
    init {
        System.loadLibrary("native-lib");
    }
}

Java

static {
    System.loadLibrary("native-lib");
}

Hinweis:Wenn Sie eine Bibliothek in Ihrem CMake-Build-Skript umbenennen oder entfernen, müssen Sie Ihr Projekt bereinigen, bevor Gradle die Änderungen anwendet oder die ältere Version der Bibliothek aus Ihrem APK entfernt. Wählen Sie zum Bereinigen Ihres Projekts in der Menüleiste Erstellen > Projekt bereinigen aus.

Android Studio fügt die Quelldateien und Header automatisch der Gruppe cpp im Bereich Projekt hinzu. Mit mehreren add_library()-Befehlen können Sie zusätzliche Bibliotheken definieren, die CMake für die Erstellung aus anderen Quelldateien verwenden soll.

NDK APIs hinzufügen

Das Android NDK bietet eine Reihe nativer APIs und Bibliotheken, die für Sie nützlich sein können. Du kannst jede dieser APIs verwenden, indem du die NDK-Bibliotheken in die Skriptdatei CMakeLists.txt deines Projekts einfügst.

Vordefinierte NDK-Bibliotheken sind bereits auf der Android-Plattform vorhanden, sodass du sie nicht selbst erstellen oder in dein APK verpacken musst. Da die NDK-Bibliotheken bereits Teil des Suchpfads von CMake sind, müssen Sie nicht einmal den Speicherort der Bibliothek in Ihrer lokalen NDK-Installation angeben. Sie müssen CMake nur den Namen der Bibliothek angeben, die Sie verwenden möchten, und sie mit Ihrer eigenen nativen Bibliothek verknüpfen.

Fügen Sie Ihrem CMake-Build-Skript den Befehl find_library() hinzu, um eine NDK-Bibliothek zu finden und ihren Pfad als Variable zu speichern. Sie verwenden diese Variable, um in anderen Teilen des Build-Skripts auf die NDK-Bibliothek zu verweisen. Im folgenden Beispiel wird die Android-spezifische Logunterstützungsbibliothek ermittelt und ihr Pfad in log-lib gespeichert:

find_library( # Defines the name of the path variable that stores the
              # location of the NDK library.
              log-lib

              # Specifies the name of the NDK library that
              # CMake needs to locate.
              log )

Damit Ihre native Bibliothek Funktionen in der Bibliothek log aufrufen kann, müssen Sie die Bibliotheken mithilfe des Befehls target_link_libraries() in Ihrem CMake-Build-Skript verknüpfen:

find_library(...)

# Links your native library against one or more other native libraries.
target_link_libraries( # Specifies the target library.
                       native-lib

                       # Links the log library to the target library.
                       ${log-lib} )

Der NDK enthält auch einige Bibliotheken als Quellcode, die Sie erstellen und mit Ihrer nativen Bibliothek verknüpfen müssen. Sie können den Quellcode in eine native Bibliothek kompilieren, indem Sie den add_library()-Befehl in Ihrem CMake-Build-Skript verwenden. Um einen Pfad zu deiner lokalen NDK-Bibliothek anzugeben, kannst du die Pfadvariable ANDROID_NDK verwenden, die von Android Studio automatisch für dich definiert wird.

Mit dem folgenden Befehl wird CMake angewiesen, android_native_app_glue.c zu erstellen. Damit werden NativeActivity-Lebenszyklusereignisse und die Eingabe per Berührung in einer statischen Bibliothek verwaltet und mit native-lib verknüpft:

add_library( app-glue
             STATIC
             ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c )

# You need to link static libraries against your shared native library.
target_link_libraries( native-lib app-glue ${log-lib} )

Weitere vordefinierte Bibliotheken hinzufügen

Das Hinzufügen einer vordefinierten Bibliothek ähnelt der Angabe einer weiteren nativen Bibliothek, die CMake erstellen soll. Da die Bibliothek jedoch bereits erstellt wurde, müssen Sie CMake mit dem Flag IMPORTED mitteilen, dass Sie die Bibliothek nur in Ihr Projekt importieren möchten:

add_library( imported-lib
             SHARED
             IMPORTED )

Anschließend müssen Sie den Pfad zur Bibliothek mit dem Befehl set_target_properties() wie unten gezeigt angeben.

Einige Bibliotheken bieten separate Pakete für bestimmte CPU-Architekturen oder Application Binary Interfaces (ABI), die in separaten Verzeichnissen organisiert sind. So können Bibliotheken bestimmte CPU-Architekturen nutzen und gleichzeitig nur die gewünschten Versionen der Bibliothek verwenden. Wenn Sie Ihrem CMake-Build-Script mehrere ABI-Versionen einer Bibliothek hinzufügen möchten, ohne für jede Version der Bibliothek mehrere Befehle schreiben zu müssen, können Sie die Pfadvariable ANDROID_ABI verwenden. Diese Variable verwendet eine Liste der Standard-ABIs, die vom NDK unterstützt werden, oder eine gefilterte Liste von ABIs, für die Sie Gradle manuell konfigurieren. Beispiele:

add_library(...)
set_target_properties( # Specifies the target library.
                       imported-lib

                       # Specifies the parameter you want to define.
                       PROPERTIES IMPORTED_LOCATION

                       # Provides the path to the library you want to import.
                       imported-lib/src/${ANDROID_ABI}/libimported-lib.so )

Damit CMake Ihre Headerdateien während der Kompilierungszeit finden kann, müssen Sie den Befehl include_directories() verwenden und den Pfad zu Ihren Headerdateien angeben:

include_directories( imported-lib/include/ )

Hinweis: Wenn Sie ein Paket für eine vordefinierte Bibliothek erstellen möchten, die keine Build-Zeitabhängigkeit ist, z. B. beim Hinzufügen einer vordefinierten Bibliothek, die eine Abhängigkeit von imported-lib ist, müssen Sie die folgende Anleitung zum Verknüpfen der Bibliothek nicht ausführen.

Wenn Sie die vordefinierte Bibliothek mit Ihrer eigenen nativen Bibliothek verknüpfen möchten, fügen Sie sie dem Befehl target_link_libraries() in Ihrem CMake-Build-Skript hinzu:

target_link_libraries( native-lib imported-lib app-glue ${log-lib} )

Wenn du die vordefinierte Bibliothek in dein APK verpacken möchtest, musst du Gradle manuell mit dem Block sourceSets konfigurieren, um den Pfad zu deiner .so-Datei anzugeben. Nachdem du dein APK erstellt hast, kannst du mit dem APK Analyzer prüfen, welche Gradle-Bibliotheken deiner APK in dein APK integriert sind.

Andere CMake-Projekte einbeziehen

Wenn Sie mehrere CMake-Projekte erstellen und deren Ausgaben in Ihr Android-Projekt aufnehmen möchten, können Sie eine CMakeLists.txt-Datei als CMake-Build-Skript der obersten Ebene verwenden (das Sie mit Gradle verknüpfen) und weitere CMake-Projekte als Abhängigkeiten dieses Build-Skripts hinzufügen. Im folgenden CMake-Build-Skript der obersten Ebene wird mit dem Befehl add_subdirectory() eine weitere CMakeLists.txt-Datei als Build-Abhängigkeit angegeben. Anschließend wird wie bei jeder anderen vordefinierten Bibliothek eine Verknüpfung mit der Ausgabe hergestellt.

# Sets lib_src_DIR to the path of the target CMake project.
set( lib_src_DIR ../gmath )

# Sets lib_build_DIR to the path of the desired output directory.
set( lib_build_DIR ../gmath/outputs )
file(MAKE_DIRECTORY ${lib_build_DIR})

# Adds the CMakeLists.txt file located in the specified directory
# as a build dependency.
add_subdirectory( # Specifies the directory of the CMakeLists.txt file.
                  ${lib_src_DIR}

                  # Specifies the directory for the build outputs.
                  ${lib_build_DIR} )

# Adds the output of the additional CMake build as a prebuilt static
# library and names it lib_gmath.
add_library( lib_gmath STATIC IMPORTED )
set_target_properties( lib_gmath PROPERTIES IMPORTED_LOCATION
                       ${lib_build_DIR}/${ANDROID_ABI}/lib_gmath.a )
include_directories( ${lib_src_DIR}/include )

# Links the top-level CMake build output against lib_gmath.
target_link_libraries( native-lib ... lib_gmath )

CMake über die Befehlszeile aufrufen

Verwenden Sie den folgenden Befehl, um CMake aufzurufen und ein Ninja-Projekt außerhalb von Android Studio zu generieren:

cmake
-Hpath/to/cmakelists/folder
-Bpath/to/generated/ninja/project/debug/ABI
-DANDROID_ABI=ABI                               // For example, arm64-v8a
-DANDROID_PLATFORM=platform-version-string      // For example, android-16
-DANDROID_NDK=android-sdk/ndk/ndk-version
-DCMAKE_TOOLCHAIN_FILE=android-sdk/ndk/ndk-version/build/cmake/android.toolchain.cmake
-G Ninja

Dieser Befehl generiert das Ninja-Projekt, das ausgeführt werden kann, um ausführbare Android-Bibliotheken (.so-Dateien) zu erstellen. Das CMAKE_TOOLCHAIN_FILE ist erforderlich, um die CMake-Unterstützung von NDK zu verwenden. Ab CMake 3.21 kann stattdessen die integrierte NDK-Unterstützung verwendet werden. Es muss jedoch eine andere Gruppe von Variablen verwendet werden, wie in der CMake-Dokumentation Cross-Compiling für Android beschrieben.

die ausführbare Ninja-Datei nicht finden konnte.-DCMAKE_MAKE_PROGRAM=path/to/ninja/ninja.exe