Toolchain Mandiri

Anda bisa menggunakan toolchain yang disediakan bersama Android NDK secara independen, atau sebagai plug-in bersama ID yang sudah ada. Fleksibilitas ini bisa berguna jika Anda sudah memiliki sistem pembangunan sendiri, dan hanya perlu kemampuan memanggil compiler-silang agar bisa menambahkan padanya dukungan untuk Android.

Kasus penggunaan yang umum adalah memanggil skrip konfigurasi dari pustaka sumber terbuka yang mengharapkan compiler-silang dalam variabel lingkungan CC.

Catatan: Laman ini beranggapan Anda sudah memiliki pemahaman yang signifikan tentang mengompilasi, menautkan, dan arsitektur tingkat-rendah. Sebagai tambahan, teknik-teknik yang dijelaskan tidak diperlukan untuk sebagian besar kasus penggunaan. Dalam kebanyakan kasus, kami menyarankan agar Anda meninggalkan penggunaan toolchain mandiri, dan sebagai gantinya tetaplah menggunakan sistem pembangunan NDK.

Memilih Toolchain Anda

Pertama-tama, Anda perlu memutuskan arsitektur pemrosesan manakah yang akan ditargetkan oleh toolchain mandiri Anda. Setiap arsitektur menyangkut nama toolchain berbeda, seperti yang ditampilkan oleh Tabel 1.

Tabel 1. Setelan APP_ABI untuk set petunjuk berbeda.

Arsitektur Nama toolchain
Berbasis ARM arm-linux-androideabi-<gcc-version>
Berbasis x86 x86-<gcc-version>
Berbasis MIPS mipsel-linux-android-<gcc-version>
Berbasis ARM64 aarch64-linux-android-<gcc-version>
Berbasis X86-64 x86_64-<gcc-version>
Berbasis MIPS64 mips64el-linux-android--<gcc-version>

Memilih Sysroot Anda

Hal berikutnya yang perlu Anda lakukan adalah mendefinisikan sysroot (Sysroot adalah sebuah direktori berisi header sistem dan pustaka untuk target Anda). Untuk mendefinisikan sysroot, Anda harus mengetahui Android API level yang ingin ditargetkan untuk dukungan asli; API asli yang tersedia bervariasi menurut Android API level.

API asli untuk masing-masing Android API level berada pada $NDK/platforms/; setiap direktori API-level, berisi subdirektori bagi beragam CPU dan arsitektur. Contoh berikut menampilkan cara mendefinisikan sysroot untuk pembangunan yang menargetkan Android 5.0 (API level 21), untuk arsitektur ARM:

SYSROOT=$NDK/platforms/android-21/arch-arm
Untuk detail selengkapnya tentang Android API level dan masing-masing API asli yang didukungnya, lihat Android NDK Native API.

Memanggil Compiler

Ada dua cara untuk memanggil compiler. Metode yang satu sederhana, dan menyerahkan sebagian besar pekerjaan ke sistem pembangunan. Metode yang satu lagi lebih canggih, namun memberikan fleksibilitas yang lebih besar.

Metode sederhana

Cara paling sederhana untuk membangun adalah dengan memanggil compiler yang sesuai secara langsung dari baris perintah, dengan menggunakan opsi --sysroot untuk menunjukkan lokasi file sistem bagi platform yang Anda targetkan. Misalnya:

export CC="$NDK/toolchains/arm-linux-androideabi-4.8/prebuilt/ \
linux-x86/bin/arm-linux-androideabi-gcc-4.8 --sysroot=$SYSROOT"
$CC -o foo.o -c foo.c

Walaupun sederhana, fleksibilitas metode ini memiliki kekurangan: Metode ini tidak memungkinkan Anda menggunakan C++ STL (STLport, libc++, atau GNU libstdc++) bersamanya. Juga tidak mendukung pengecualian atau RTTI.

Untuk Clang, Anda perlu melakukan dua langkah tambahan:

    1. Tambahkan -target yang sesuai untuk arsitektur target, seperti yang ditampilkan oleh Tabel 2.
    2. Tabel 2. Arsitektur dan nilai-nilainya untuk -target.

      Arsitektur Nilai
      armeabi -target armv5te-none-linux-androideabi
      armeabi-v7a -target armv7-none-linux-androideabi
      arm64-v8a -target aarch64-none-linux-android
      x86 -target i686-none-linux-android
      x86_64 -target x86_64-none-linux-android
      mips -target mipsel-none-linux-android
    3. Tambahkan dukungan assembler dan linker dengan menambahkan opsi -gcc-toolchain, seperti dalam contoh berikut:
    4. -gcc-toolchain $NDK/toolchains/arm-linux-androideabi-4.8/prebuilt/linux-x86_64
      
    Terakhir, perintah untuk mengompilasi dengan menggunakan Clang mungkin tampak seperti ini:
    export CC="$NDK/toolchains/arm-linux-androideabi-4.8/prebuilt/ \
    linux-x86/bin/arm-linux-androideabi-gcc-4.8 --sysroot=$SYSROOT" -target \
    armv7-none-linux-androideabi \
    -gcc-toolchain $NDK/toolchains/arm-linux-androideabi-4.8/prebuilt/linux-x86_64"
    $CC -o foo.o -c foo.c
    

Metode tingkat lanjut

NDK menyediakan skrip shell make-standalone-toolchain.sh agar Anda dapat melakukan pemasangan toolchain yang disesuaikan dari baris perintah. Pendekatan ini menawarkan fleksibilitas lebih besar daripada prosedur yang dijelaskan dalam Metode sederhana.

Skrip ini berada di direktori $NDK/build/tools/, dalam hal ini $NDK adalah akar pemasangan untuk NDK. Contoh penggunaan skrip ini tampak seperti di bawah:

$NDK/build/tools/make-standalone-toolchain.sh \
--arch=arm --platform=android-21 --install-dir=/tmp/my-android-toolchain

Perintah ini membuat direktori bernama /tmp/my-android-toolchain/, yang berisi salinan sysroot android-21/arch-arm, dan biner toolchain untuk arsitektur ARM 32-bit.

Perhatikan, biner toolchain tidak bergantung pada atau berisi jalur spesifik host, dengan kata lain, Anda bisa memasangnya di lokasi apa saja, atau bahkan memindahnya jika perlu.

Secara default, sistem pembangunan menggunakan toolchain GCC 4.8 berbasis ARM 32-bit. Anda bisa menetapkan nilai berbeda, akan tetapi, dengan menetapkan --arch=<toolchain> sebagai opsi. Tabel 3 menampilkan nilai-nilai yang akan digunakan untuk toolchain lainnya:

Tabel 3. Toolchain dan nilai-nilai yang bersangkutan, dengan menggunakan --arch.

Toolchain Nilai
Compiler mips64 --arch=mips64
Compiler mips GCC 4.8 --arch=mips
Compiler x86 GCC 4.8 --arch=x86
Compiler x86_64 GCC 4.8 --arch=x86_64
Compiler mips GCC 4.8 --arch=mips

Atau, Anda bisa menggunakan opsi --toolchain=<toolchain>. Tabel 4 menampilkan nilai yang bisa Anda tetapkan untuk <toolchain>:

Tabel 4. Toolchain dan nilai-nilai yang bersangkutan, dengan menggunakan --toolchain.

Toolchain Nilai
arm
  • --toolchain=arm-linux-androideabi-4.8
  • --toolchain=arm-linux-androideabi-4.9
  • --toolchain=arm-linux-android-clang3.5
  • --toolchain=arm-linux-android-clang3.6
  • x86
  • --toolchain=x86-linux-android-4.8
  • --toolchain=x86-linux-android-4.9
  • --toolchain=x86-linux-android-clang3.5
  • --toolchain=x86-linux-android-clang3.6
  • mips
  • --toolchain=mips-linux-android-4.8
  • --toolchain=mips-linux-android-4.9
  • --toolchain=mips-linux-android-clang3.5
  • --toolchain=mips-linux-android-clang3.6
  • arm64
  • --toolchain=aarch64-linux-android-4.9
  • --toolchain=aarch64-linux-android-clang3.5
  • --toolchain=aarch64-linux-android-clang3.6
  • x86_64
  • --toolchain=x86_64-linux-android-4.9
  • --toolchain=x86_64-linux-android-clang3.5
  • --toolchain=x86_64-linux-android-clang3.6
  • mips64
  • --toolchain=mips64el-linux-android-4.9
  • --toolchain=mips64el-linux-android-clang3.5
  • --toolchain=mips64el-linux-android-clang3.6
  • Catatan: Tabel 4 bukan daftar yang lengkap. Kombinasi lainnya juga mungkin valid, namun belum diverifikasi.

    Anda juga bisa menyalin Clang/LLVM 3.6, dengan menggunakan salah satu dari dua metode ini: Anda bisa menambahkan -clang3.6 ke opsi --toolchain, sehingga opsi --toolchain tampak seperti contoh berikut:

    --toolchain=arm-linux-androideabi-clang3.6
    

    Anda juga bisa menambahkan -llvm-version=3.6 sebagai opsi terpisah pada baris perintah.

    Catatan: Sebagai ganti menetapkan versi yang spesifik, Anda juga bisa menggunakan <version>, yang merupakan default untuk versi Clang tertinggi yang tersedia.

    Secara default, sistem pembangunan akan membangun untuk toolchain host 32-bit. Anda bisa menetapkan toolchain host 64-bit sebagai gantinya. Tabel 5 menampilkan nilai yang akan digunakan bersama -system untuk platform yang berbeda.

    Tabel 5. Toolchain host dan nilai-nilai yang bersangkutan, dengan menggunakan -system.

    Toolchain host Nilai
    Linux 64-bit -system=linux-x86_64
    MacOSX 64-bit -system=darwin-x86_64
    Windows 64-bit -system=windows-x86_64
    Untuk informasi selengkapnya mengenai menetapkan toolchain host instruksi 64- atau 32-bit, lihat Toolchain 64-Bit dan 32-Bit.

    Anda dapat menetapkan --stl=stlport untuk menyalin libstlport sebagai ganti libgnustl default. Jika ini dilakukan, dan Anda ingin menautkan ke pustaka bersama, Anda harus secara eksplisit menggunakan -lstlport_shared. Persyaratan ini serupa dengan keharusan menggunakan -lgnustl_shared untuk GNU libstdc++.

    Demikian pula, Anda bisa menetapkan --stl=libc++ untuk menyalin pustaka dan header LLVM libc++. Untuk menautkan ke pustaka bersama, Anda harus secara eksplisit menggunakan -lc++_shared.

    Anda bisa membuat setelan ini secara langsung, seperti dalam contoh berikut:

    export PATH=/tmp/my-android-toolchain/bin:$PATH
    export CC=arm-linux-androideabi-gcc   # or export CC=clang
    export CXX=arm-linux-androideabi-g++  # or export CXX=clang++
    

    Perhatikan, jika Anda meninggalkan opsi -install-dir, skrip shell make-standalone-toolchain.sh akan membuat sebuah tarball dalam tmp/ndk/<toolchain-name>.tar.bz2. Tarball ini akan memudahkan pengarsipannya, serta untuk mendistribusikan ulang biner tersebut.

    Toolchain mandiri ini menyediakan manfaat tambahan, serta, karena ia berisi salinan fungsional dari pustaka C++ STL, dengan pengecualian fungsional dan dukungan RTTI.

    Untuk opsi selengkapnya dan detail, gunakan --help.

    Menangani Clang

    Anda bisa memasang biner Clang dalam pemasangan mandiri dengan menggunakan opsi --llvm-version=<version>. <version> adalah nomor versi LLVM/Clang, misalnya 3.5 atau 3.6. Misalnya:

    build/tools/make-standalone-toolchain.sh \
    --install-dir=/tmp/mydir \
    --toolchain=arm-linux-androideabi-4.8 \
    --llvm-version=3.6
    

    Perhatikan, biner Clang disalin bersama biner GCC, karena keduanya bergantung pada assembler, linker, header, pustaka, dan implementasi C++ STL yang sama.

    Operasi ini juga memasang dua skrip, bernama clang dan clang++, pada <install-dir>/bin/@. Skrip ini memanggil biner clang sesungguhnya bersama flag arsitektur target default. Dengan kata lain, semuanya bekerja tanpa modifikasi apa pun, dan Anda akan dapat menggunakannya dalam versi sendiri cukup dengan menyetel variabel lingkungan CC dan CXX untuk menunjuk ke sana.

    Memanggil Clang

    Dalam pemasangan mandiri ARM yang dibangun dengan llvm-version=3.6, pemanggilan Clang pada sistem Unix menggunakan format baris tunggal. Sebagai contoh:

    `dirname $0`/clang36 -target armv5te-none-linux-androideabi "$@"
    

    clang++ memanggil clang++31 dalam cara yang sama.

    Target Clang pada ARM

    Saat membangun untuk ARM, Clang mengubah target berdasarkan adanya opsi -march=armv7-a dan/atau -mthumb:

    Tabel 5. Nilai-nilai -march yang dapat ditetapkan dan target yang dihasilkannya.

    Nilai -march Target yang dihasilkan
    -march=armv7-a armv7-none-linux-androideabi
    -mthumb thumb-none-linux-androideabi
    Baik -march=armv7-a maupun -mthumb thumbv7-none-linux-androideabi

    Anda juga dapat menggantinya dengan -target milik sendiri jika diinginkan.

    Opsi -gcc-toolchain tidak diperlukan karena dalam paket mandiri, Clang akan mencari as dan ld dalam lokasi relatif yang telah didefinisikan sebelumnya.

    clang dan clang++ akan menjadi pengganti drop-in yang mudah bagi gcc dan g++ dalam makefile. Bila ragu, tambahkan opsi berikut untuk memverifikasi apakah semua itu bekerja dengan benar:

    • -v untuk menimbun perintah-perintah yang menyangkut masalah driver compiler
    • -### untuk menimbun berbagai opsi baris perintah, termasuk opsi yang telah didefinisikan sebelumnya secara implisit.
    • -x c < /dev/null -dM -E untuk menimbun definisi praprosesor yang telah didefinisikan sebelumnya
    • -save-temps untuk membandingkan file *.i atau *.ii yang telah diproses sebelumnya.

    Untuk informasi selengkapnya tentang Clang, lihat http://clang.llvm.org/, khususnya di bagian kompatibilitas GCC .

    Kompatibilitas ABI

    Kode mesin yang dihasilkan toolchain ARM seharusnya kompatibel dengan armeabi ABI resmi Android secara default.

    Kami menyarankan penggunaan flag compiler -mthumb untuk memaksa pembuatan petunjuk Thumb-1 16-bit (default-nya adalah petunjuk ARM 32-bit).

    Jika Anda ingin menargetkan armeabi-v7a ABI, flag berikut harus disetel:

    CFLAGS= -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16
    

    Flag pertama akan mengaktifkan petunjuk Thumb-2. Flag kedua akan mengaktifkan petunjuk FPU perangkat keras sekaligus memastikan bahwa sistem meneruskan parameter titik-mengambang dalam register inti, yang sangat penting bagi kompatibilitas ABI.

    Catatan: Dalam versi NDK sebelum r9b, jangan gunakan flag ini secara terpisah. Anda harus menyetel semuanya atau tidak satu pun. Jika tidak maka bisa terjadi perilaku yang tidak dapat diprediksi dan mogok.

    Untuk menggunakan petunjuk NEON, Anda harus mengubah flag compiler -mfpu:

    CFLAGS= -march=armv7-a -mfloat-abi=softfp -mfpu=neon
    

    Perhatikan, setelan ini akan memaksa penggunaan VFPv3-D32, sesuai spesifikasi ARM.

    Pastikan juga menyediakan dua flag berikut pada linker:

    LDFLAGS= -march=armv7-a -Wl,--fix-cortex-a8
    

    Flag pertama memerintahkan linker memilih libgcc.a, libgcov.a, dan crt*.o, yang telah disesuaikan untuk armv7-a. Flag ke-2 diperlukan sebagai solusi bagi bug CPU di beberapa implementasi Cortex-A8.

    Sejak NDK versi r9b, semua API asli Android yang mengambil atau mengembalikan nilai ganda atau nilai mengambang memiliki attribute((pcs("aapcs"))) untuk ARM. Hal ini memungkinkan kode pengguna dikompilasi dalam -mhard-float (yang menyiratkan -mfloat-abi=hard), dan tetap menautkan dengan API asli Android yang mengikuti softfp ABI. Untuk informasi selengkapnya mengenai hal ini, lihat komentar di $NDK/tests/device/hard-float/jni/Android.mk.

    Jika Anda ingin menggunakan intrinsik NEON di x86, sistem pembangunan bisa menerjemahkannya ke intrinsik SSE x86 asli menggunakan header bahasa C/C++ khusus dengan nama yang sama, arm_neon.h, sebagai header intrinsik standar ARM NEON.

    Secara default, x86 ABI mendukung SIMD hingga SSSE3, dan header mencakup ~93% dari (1869 pada 2009) fungsi NEON.

    Anda tidak perlu menggunakan flag compiler tertentu saat menargetkan MIPS ABI.

    Untuk mengetahui selengkapnya tentang dukungan ABI, lihat Dukungan x86.

    Peringatan dan Batasan

    Dukungan Windows

    Biner Windows tidak bergantung pada Cygwin. Tidak adanya dependensi ini membuatnya jadi lebih cepat. Akan tetapi konsekuensinya adalah tidak memahami spesifikasi jalur Cygwin seperti cygdrive/c/foo/bar, yang kebalikan dari C:/foo/bar.

    Sistem pembangunan NDK memastikan semua jalur yang diteruskan ke compiler dari Cygwin secara otomatis diterjemahkan, dan mengelola kompleksitas lainnya. Jika memiliki sistem pembangunan khusus, Anda mungkin perlu mengatasi sendiri kompleksitas ini.

    Untuk informasi mengenai cara berkontribusi pada dukungan untuk Cygwin/MSys, kunjungi forum android-ndk.

    wchar_t support

    Platform Android tidak begitu mendukung wchar_t hingga Android 2.3 (API level 9). Fakta ini memiliki sejumlah ramifikasi:

    • Jika Anda menargetkan platform Android 2.3 atau yang lebih tinggi, ukuran wchar_t adalah 4 byte, dan hampir semua fungsi wide-char tersedia di pustaka C (dengan pengecualian fungsi enkode/dekode multi-byte dan wsprintf/wsscanf).
    • Jika Anda menargetkan API level yang lebih rendah, ukuran wchar_t adalah 1 byte, dan tidak satu pun dari fungsi wide-char yang bekerja.

    Kami menyarankan agar Anda menghilangkan dependensi pada tipe wchar_t, dan beralih ke representasi yang lebih baik. Dukungan yang disediakan di Android hanya untuk membantu Anda memindahkan kode yang ada.

    Pengecualian, RTTI, dan STL

    Biner toolchain mendukung pengecualian C++ dan RTTI secara default. Untuk menonaktifkan pengecualian C++ dan RTTI saat membangun sumber (untuk menghasilkan kode mesin kelas ringan, misalnya), gunakan -fno-exceptions dan -fno-rtti.

    Untuk menggunakan semua fitur ini bersama GNU libstdc++, Anda harus secara eksplisit menautkan dengan libsupc++. Caranya, gunakan -lsupc++ saat menautkan biner. Misalnya:

    arm-linux-androideabi-g++ .... -lsupc++
    

    Anda tidak perlu melakukannya saat menggunakan pustaka STLport atau libc++.

    Dukungan C++ STL

    Toolchain mandiri menyertakan salinan implementasi C++ Standard Template Library. Implementasi ini adalah untuk GNU libstdc++, STLport, atau libc++, bergantung pada apa yang Anda tetapkan untuk opsi --stl=<name> yang telah dijelaskan sebelumnya. Untuk menggunakan implementasi STL, Anda perlu menautkan proyek dengan pustaka yang sesuai:

    • Gunakan -lstdc++ untuk menautkan ke versi pustaka statis dari suatu implementasi. Hal itu akan memastikan semua kode C++ STL yang diperlukan akan disertakan ke dalam biner akhir. Metode ini ideal jika Anda hanya menghasilkan satu pustaka bersama atau file yang dapat dieksekusi.

      Inilah metode yang kami sarankan.

    • Atau, gunakan -lgnustl_shared untuk menautkan ke versi pustaka bersama dari GNU libstdc++. Jika Anda menggunakan opsi ini, Anda juga harus memastikan menyalin libgnustl_shared.so ke perangkat agar kode Anda dapat dimuat dengan benar. Tabel 6 menampilkan lokasi file ini untuk setiap tipe toolchain.
    • Catatan: GNU libstdc++ berlisensi GPLv3, dengan pengecualian penautan. Jika tidak bisa memenuhi persyaratannya, Anda tidak bisa mendistribusikan ulang pustaka bersama dalam proyek Anda.

    • Gunakan -lstlport_shared untuk menautkan ke versi pustaka bersama dari STLport. Bila melakukannya, Anda perlu memastikan bahwa Anda juga menyalin libstlport_shared.so ke perangkat agar kode Anda dapat dimuat dengan benar. Tabel 6 menampilkan lokasi file ini untuk setiap toolchain:
    • Tabel 6. Nilai-nilai -march yang dapat ditetapkan dan target yang dihasilkannya.

      Toolchain Lokasi
      arm $TOOLCHAIN/arm-linux-androideabi/lib/
      arm64 $TOOLCHAIN/aarch64-linux-android/lib/
      x86 $TOOLCHAIN/i686-linux-android/lib/
      x86_64 $TOOLCHAIN/x86_64-linux-android/lib/
      mips $TOOLCHAIN/mipsel-linux-android/lib/
      mips64 $TOOLCHAIN/mips64el-linux-android/lib/

      Catatan: Jika proyek Anda berisi beberapa pustaka bersama atau file yang dapat dieksekusi, Anda harus menautkannya ke implementasi STL pustaka bersama. Jika tidak maka sistem pembangunan tidak akan mendefinisikan global tertentu secara unik, sehingga bisa mengakibatkan perilaku waktu proses yang tidak dapat diprediksi. Perilaku ini mungkin meliputi mogok dan kegagalan menangkap pengecualian dengan benar.

      Alasan versi pustaka bersama itu tidak cuma disebut libstdc++.so adalah karena nama ini akan konflik pada waktu proses dengan waktu proses C++ minimal milik sistem. Karena alasan ini, sistem pembangunan akan memberlakukan nama baru untuk pustaka GNU ELF. Pustaka statis tidak mengalami masalah ini.