Mengintegrasikan sistem build C/C++ kustom menggunakan Ninja (eksperimental)

Jika Anda tidak menggunakan CMake atau ndk-build tetapi menginginkan integrasi penuh dari build C/C++ plugin Android Gradle (AGP) dan Android Studio, Anda dapat membuat sistem build C/C++ kustom dengan membuat skrip shell yang menulis informasi build di format file build Ninja.

Dukungan eksperimental untuk sistem build C/C++ kustom telah ditambahkan ke Android Studio dan AGP. Fitur ini tersedia mulai dari Android Studio Dolphin | 2021.3.1 Canary 4.

Ringkasan

Pola umum untuk project C/C++, terutama yang menargetkan beberapa platform, adalah membuat project untuk setiap platform tersebut dari beberapa representasi yang mendasari. Contoh jelas dari pola ini adalah CMake. CMake dapat membuat project untuk Android, iOS, dan platform lainnya dari satu representasi yang mendasari, yang disimpan dalam file CMakeLists.txt.

Meskipun CMake didukung langsung oleh AGP, ada generator project lain yang tersedia yang tidak didukung secara langsung:

Jenis generator project ini mendukung Ninja sebagai representasi backend dari build C/C++ atau dapat disesuaikan untuk menghasilkan Ninja sebagai representasi backend.

Jika dikonfigurasi dengan benar, project AGP dengan generator sistem project C/C++ terintegrasi memungkinkan pengguna untuk:

  • Mem-build dari command line dan Android Studio.

  • Mengedit sumber dengan dukungan layanan bahasa lengkap (misalnya, go-to definition) di Android Studio.

  • Menggunakan debugger Android Studio untuk men-debug proses native dan campuran.

Cara memodifikasi build untuk menggunakan skrip konfigurasi build C/C++ kustom

Bagian ini membahas langkah-langkah untuk menggunakan skrip konfigurasi build C/C++ kustom dari AGP.

Langkah 1: Ubah file build.gradle level modul untuk mereferensikan skrip konfigurasi

Untuk mengaktifkan dukungan Ninja di AGP, konfigurasikan experimentalProperties di file build.gradle level modul:

android {
  defaultConfig {
    externalNativeBuild {
      experimentalProperties["ninja.abiFilters"] = [ "x86", "arm64-v8a" ]
      experimentalProperties["ninja.path"] = "source-file-list.txt"
      experimentalProperties["ninja.configure"] = "configure-ninja"
      experimentalProperties["ninja.arguments"] = [
            "\${ndk.moduleMakeFile}",
            "--variant=\${ndk.variantName}",
            "--abi=Android-\${ndk.abi}",
            "--configuration-dir=\${ndk.configurationDir}",
            "--ndk-version=\${ndk.moduleNdkVersion}",
            "--min-sdk-version=\${ndk.minSdkVersion}"
       ]
     }
   }

Properti ini ditafsirkan oleh AGP sebagai berikut:

  • ninja.abiFilters adalah daftar ABI yang akan di-build. Nilai yang valid adalah: x86, x86-64, armeabi-v7a, dan arm64-v8a.

  • ninja.path adalah jalur ke file project C/C++. Format file ini dapat berupa format apa pun yang Anda inginkan. Perubahan pada file ini akan memicu perintah untuk menyinkronkan Gradle di Android Studio.

  • ninja.configure adalah jalur ke file skrip yang akan dijalankan oleh Gradle saat diperlukan untuk mengonfigurasi project C/C++. Project dikonfigurasi pada build pertama, selama sinkronisasi Gradle di Android Studio, atau saat salah satu input skrip konfigurasi berubah.

  • ninja.arguments adalah daftar argumen yang akan diteruskan ke skrip yang ditentukan oleh ninja.configure. Elemen dalam daftar ini dapat merujuk kumpulan makro yang nilainya bergantung pada konteks konfigurasi saat ini di AGP:

    • ${ndk.moduleMakeFile} adalah jalur lengkap ke file ninja.configure. Jadi, dalam contoh ini akan menjadi C:\path\to\configure-ninja.bat.

    • ${ndk.variantName} adalah nama varian AGP saat ini yang sedang dibuat. Misalnya, debug atau rilis.

    • ${ndk.abi} adalah nama ABI AGP saat ini yang sedang dibuat. Misalnya, x86 atau arm64-v8a.

    • ${ndk.buildRoot} adalah nama folder yang dibuat oleh AGP yang menjadi tujuan penulisan skrip. Detail hal ini akan dijelaskan di Langkah 2: Buat skrip konfigurasi.

    • ${ndk.ndkVersion} adalah versi NDK yang akan digunakan. Ini biasanya adalah nilai yang diteruskan ke android.ndkVersion dalam file build.gradle atau nilai default jika tidak ada nilai.

    • ${ndk.minPlatform} adalah platform Android target minimum yang diminta oleh AGP.

  • ninja.targets adalah daftar target Ninja tertentu yang harus dibuat.

Langkah 2: Buat skrip konfigurasi

Tanggung jawab minimum skrip konfigurasi (configure-ninja.bat dalam contoh sebelumnya) adalah membuat file build.ninja yang, saat dibuat dengan Ninja, akan mengompilasi dan menautkan semua output native project. Biasanya adalah file .o (Objek), .a (Arsip), dan .so (Objek Bersama).

Skrip konfigurasi dapat menulis file build.ninja di dua tempat yang berbeda sesuai dengan kebutuhan Anda.

  • Jika AGP dapat memilih lokasi, skrip konfigurasi akan menulis build.ninja di lokasi yang ditetapkan dalam makro ${ndk.buildRoot}.

  • Jika skrip konfigurasi perlu memilih lokasi file build.ninja, skrip tersebut juga akan menulis file bernama build.ninja.txt di lokasi yang ditetapkan dalam makro ${ndk.buildRoot}. File ini berisi jalur lengkap ke file build.ninja yang ditulis oleh skrip konfigurasi.

Struktur file build.ninja

Umumnya, sebagian besar struktur yang secara akurat merepresentasikan build C/C++ Android akan berfungsi. Elemen utama yang diperlukan oleh AGP dan Android Studio adalah:

  • Daftar file sumber C/C++ beserta flag yang diperlukan oleh Clang untuk mengompilasinya.

  • Daftar library output. Daftar tersebut biasanya berupa file .so (objek bersama), tetapi juga dapat berupa .a (arsip) atau executable (tanpa ekstensi).

Jika memerlukan contoh cara membuat file build.ninja, Anda dapat melihat output CMake saat generator build.ninja digunakan.

Berikut adalah contoh template build.ninja minimal.

rule COMPILE
   command = /path/to/ndk/clang -c $in -o $out {other flags}
rule LINK
   command = /path/to/ndk/clang $in -o $out {other flags}

build source.o : COMPILE source.cpp
build lib.so : LINK source.o

Praktik terbaik

Selain persyaratan (daftar file sumber dan library output), berikut adalah beberapa praktik terbaik yang direkomendasikan.

Mendeklarasikan output bernama dengan aturan phony

Jika memungkinkan, sebaiknya struktur build.ninja menggunakan aturan phony untuk memberikan nama output build yang dapat dibaca manusia. Jadi misalnya, jika Anda memiliki output bernama c:/path/to/lib.so, Anda dapat memberinya nama yang dapat dibaca manusia seperti berikut.

build curl: phony /path/to/lib.so

Manfaatnya adalah Anda kemudian dapat menentukan nama ini sebagai target build dalam file build.gradle. Misalnya,

android {
  defaultConfig {
    externalNativeBuild {
      ...
      experimentalProperties["ninja.targets"] = [ "curl" ]

Menentukan target 'all'

Saat Anda menentukannya, target all akan menjadi kumpulan library default yang dibuat oleh AGP saat tidak ada target yang ditetapkan secara eksplisit dalam file build.gradle.

rule COMPILE
   command = /path/to/ndk/clang $in -o $out {other flags}
rule LINK
   command = /path/to/ndk/clang $in -o $out {other flags}

build foo.o : COMPILE foo.cpp
build bar.o : COMPILE bar.cpp
build libfoo.so : LINK foo.o
build libbar.so : LINK bar.o
build all: phony libfoo.so libbar.so

Menentukan metode build alternatif (opsional)

Kasus penggunaan yang lebih lanjut adalah menggabungkan sistem build yang ada yang bukan berbasis Ninja. Dalam hal ini, Anda masih perlu merepresentasikan semua sumber dengan flagnya bersama dengan library output sehingga Android Studio dapat memberikan fitur layanan bahasa yang tepat seperti pelengkapan otomatis dan go-to definition. Namun, Anda ingin AGP beralih ke sistem build yang mendasarinya selama build yang sebenarnya.

Untuk melakukannya, Anda dapat menggunakan output build Ninja dengan ekstensi .passthrough tertentu.

Sebagai contoh yang lebih konkret, misalnya Anda ingin menggabungkan MSBuild. Skrip konfigurasi akan menghasilkan build.ninja seperti biasa, tetapi juga akan menambahkan target passthrough yang menentukan cara AGP memanggil MSBuild.

rule COMPILE
   command = /path/to/ndk/clang $in -o $out {other flags}
rule LINK
   command = /path/to/ndk/clang $in -o $out {other flags}

rule MBSUILD_CURL
  command = /path/to/msbuild {flags to build curl with MSBuild}

build source.o : COMPILE source.cpp
build lib.so : LINK source.o
build curl : phony lib.so
build curl.passthrough : MBSUILD_CURL

Beri masukan

Fitur ini bersifat eksperimental, jadi kami sangat menghargai masukan Anda. Anda dapat memberikan masukan melalui saluran berikut:

  • Untuk masukan umum, tambahkan komentar ke bug ini.

  • Untuk melaporkan bug, buka Android Studio dan klik Help >Submit Feedback. Pastikan untuk merujuk "Custom C/C++ Build Systems" untuk membantu mengarahkan bug.

  • Untuk melaporkan bug jika Anda belum menginstal Android Studio, laporkan bug menggunakan template ini.