ผู้ผลิต

Android NDK รองรับการใช้ CMake เพื่อ คอมไพล์โค้ด C และ C++ สำหรับแอปพลิเคชัน หน้านี้จะอธิบายวิธีใช้ CMake กับ NDK ผ่าน ExternalNativeBuild ของปลั๊กอิน Android Gradle หรือเมื่อ เรียกใช้ CMake โดยตรง

ไฟล์ Toolchain ของ CMake

NDK รองรับ CMake ผ่านไฟล์ Toolchain ไฟล์ Toolchain คือไฟล์ CMake ที่ปรับแต่งลักษณะการทำงานของ Toolchain สำหรับการคอมไพล์ข้าม ไฟล์ Toolchain ที่ใช้สำหรับ NDK อยู่ใน NDK ที่ <NDK>/build/cmake/android.toolchain.cmake

พารามิเตอร์บิลด์ เช่น ABI, minSdkVersion ฯลฯ จะระบุในบรรทัดคำสั่งเมื่อเรียกใช้ cmake ดูรายการอาร์กิวเมนต์ที่รองรับได้ที่ส่วนอาร์กิวเมนต์ของ Toolchain

ไฟล์ Toolchain "ใหม่"

NDK เวอร์ชันก่อนๆ ได้ทดลองใช้การติดตั้งใช้งานใหม่ของไฟล์ Toolchain ซึ่งจะช่วยลดความแตกต่างของลักษณะการทำงานระหว่างการใช้ไฟล์ Toolchain ของ NDK กับการใช้การรองรับ CMake ในตัว ซึ่งต้องใช้เวลาทำงานเป็นอย่างมาก (ซึ่งยังไม่เสร็จสมบูรณ์) แต่ไม่ได้ปรับปรุงลักษณะการทำงานจริง เราจึงไม่ได้ดำเนินการต่อ

ไฟล์ Toolchain "ใหม่" มีการถดถอยของลักษณะการทำงานเมื่อเทียบกับไฟล์ Toolchain "เดิม" ลักษณะการทำงานเริ่มต้นคือเวิร์กโฟลว์ที่แนะนำ หากคุณใช้ -DANDROID_USE_LEGACY_TOOLCHAIN_FILE=OFF เราขอแนะนำให้นำค่าสถานะดังกล่าว ออกจากบิลด์ ไฟล์ Toolchain ใหม่ไม่เคยเทียบเท่ากับไฟล์ Toolchain เดิม จึงอาจมีการถดถอยของลักษณะการทำงาน

แม้ว่าเราจะไม่แนะนำให้ใช้ไฟล์ Toolchain ใหม่ แต่ปัจจุบันเรายังไม่มีแผนที่จะนำไฟล์ดังกล่าวออกจาก NDK การดำเนินการดังกล่าวจะทำให้บิลด์ที่อิงตาม ความแตกต่างของลักษณะการทำงานระหว่างไฟล์ Toolchain ใหม่และไฟล์ Toolchain เดิมใช้งานไม่ได้ และ การเปลี่ยนชื่อตัวเลือกเพื่อระบุให้ชัดเจนว่า "เดิม" เป็นตัวเลือกที่ แนะนำก็อาจทำให้ผู้ใช้ตัวเลือกนั้นใช้งานไม่ได้เช่นกัน หากคุณใช้ไฟล์ Toolchain ใหม่ได้อย่างราบรื่น ก็ไม่จำเป็นต้องย้ายข้อมูล แต่โปรดทราบว่าข้อบกพร่องที่พบในลักษณะการทำงานของไฟล์ Toolchain ใหม่อาจไม่ได้รับการแก้ไข และคุณจะต้องย้ายข้อมูลแทน

การใช้งาน

Gradle

การใช้ไฟล์ Toolchain ของ CMake จะเป็นไปโดยอัตโนมัติเมื่อใช้ externalNativeBuild ดูข้อมูลเพิ่มเติมได้ในคู่มือเพิ่มโค้ด C และ C++ ลงในโปรเจ็กต์ของ Android Studio

บรรทัดคำสั่ง

เมื่อสร้างด้วย CMake นอก Gradle คุณต้องส่งไฟล์ Toolchain และอาร์กิวเมนต์ของไฟล์ไปยัง CMake เช่น

$ cmake \
    -DCMAKE_TOOLCHAIN_FILE=$NDK/build/cmake/android.toolchain.cmake \
    -DANDROID_ABI=$ABI \
    -DANDROID_PLATFORM=android-$MINSDKVERSION \
    $OTHER_ARGS

อาร์กิวเมนต์ของ Toolchain

คุณส่งอาร์กิวเมนต์ต่อไปนี้ไปยังไฟล์ Toolchain ของ CMake ได้ หากสร้างด้วย Gradle ให้เพิ่มอาร์กิวเมนต์ลงใน android.defaultConfig.externalNativeBuild.cmake.arguments ตามที่อธิบายไว้ใน เอกสารประกอบ ExternalNativeBuild หากสร้างจากบรรทัดคำสั่ง ให้ส่งอาร์กิวเมนต์ไปยัง CMake ด้วย -D ตัวอย่างเช่น หากต้องการบังคับไม่ให้ armeabi-v7a สร้างด้วยการรองรับนีออน ให้ส่ง -DANDROID_ARM_NEON=FALSE

ANDROID_ABI

ABI เป้าหมาย ดูข้อมูลเกี่ยวกับ ABI ที่รองรับได้ที่ABI ของ Android

Gradle

Gradle จะระบุอาร์กิวเมนต์นี้โดยอัตโนมัติ อย่าตั้งค่าอาร์กิวเมนต์นี้อย่างชัดเจนในไฟล์ build.gradle หากต้องการควบคุม ABI ที่ Gradle กำหนดเป้าหมาย ให้ใช้ abiFilters ตามที่อธิบายไว้ใน ABI ของ Android

บรรทัดคำสั่ง

CMake จะสร้างสำหรับเป้าหมายเดียวต่อการบิลด์ หากต้องการกำหนดเป้าหมาย ABI ของ Android มากกว่า 1 รายการ คุณต้องสร้าง 1 ครั้งต่อ ABI ขอแนะนำให้ใช้ไดเรกทอรีบิลด์ที่แตกต่างกันสำหรับแต่ละ ABI เพื่อหลีกเลี่ยงการชนกันระหว่างบิลด์

ค่า หมายเหตุ
armeabi-v7a
armeabi-v7a with NEON เหมือนกับ armeabi-v7a
arm64-v8a
x86
x86_64

ANDROID_ARM_MODE

ระบุว่าจะสร้างคำสั่ง arm หรือ thumb สำหรับ armeabi-v7a หรือไม่ ไม่มีผลกับ ABI อื่นๆ ดูข้อมูลเพิ่มเติมได้ที่เอกสารประกอบเกี่ยวกับ ABI ของ Android

ค่า หมายเหตุ
กลุ่ม
นิ้วหัวแม่มือ ลักษณะการทำงานเริ่มต้น

ANDROID_NATIVE_API_LEVEL

นามแฝงสำหรับ ANDROID_PLATFORM

ANDROID_PLATFORM

ระบุระดับ API ขั้นต่ำที่แอปพลิเคชันหรือไลบรารีรองรับ ค่านี้สอดคล้องกับ minSdkVersion ของแอปพลิเคชัน

Gradle

เมื่อใช้ปลั๊กอิน Android Gradle ระบบจะตั้งค่านี้เป็น ให้ตรงกับ minSdkVersion ของแอปพลิเคชันโดยอัตโนมัติ และไม่ควรตั้งค่าด้วยตนเอง

บรรทัดคำสั่ง

เมื่อเรียกใช้ CMake โดยตรง ค่านี้จะตั้งค่าเริ่มต้นเป็นระดับ API ต่ำสุด ที่ NDK ที่ใช้อยู่รองรับ เช่น เมื่อใช้ NDK r20 ค่านี้จะเป็นค่าเริ่มต้น ที่ระดับ API 16

ระบบรองรับพารามิเตอร์นี้ในหลายรูปแบบ ดังนี้

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

รูปแบบ $API_LETTER ช่วยให้คุณระบุ android-N ได้โดยไม่ต้อง กำหนดหมายเลขที่เชื่อมโยงกับผลงานนั้น โปรดทราบว่าผลงานบางรายการได้รับการเพิ่ม API โดยไม่ได้รับการเพิ่มจดหมาย คุณระบุ API เหล่านี้ได้ โดยต่อท้ายด้วยคำต่อท้าย -MR1 เช่น ระดับ API 25 คือ android-N-MR1

ANDROID_STL

ระบุ STL ที่จะใช้สำหรับแอปพลิเคชันนี้ ดูข้อมูลเพิ่มเติมได้ที่การรองรับไลบรารี C++ โดยค่าเริ่มต้น ระบบจะใช้ c++_static

ค่า หมายเหตุ
c++_shared libc++ เวอร์ชันไลบรารีที่ใช้ร่วมกัน
c++_static ตัวแปรไลบรารีแบบคงที่ของ libc++
ไม่มี ไม่รองรับไลบรารีมาตรฐาน C++
ระบบ STL ของระบบ

จัดการแฟล็กคอมไพเลอร์

หากต้องการส่งแฟล็กที่เฉพาะเจาะจงไปยังคอมไพเลอร์หรือลิงเกอร์สำหรับการบิลด์ โปรดดูเอกสารประกอบ CMake สำหรับ set_target_compile_options และ ตัวเลือกที่เกี่ยวข้อง ส่วน "ดูเพิ่มเติม" ที่ด้านล่างของหน้านั้นมี คำใบ้ที่เป็นประโยชน์

โดยทั่วไป แนวทางปฏิบัติแนะนำคือการใช้แฟล็กคอมไพเลอร์เป็นขอบเขตที่แคบที่สุด เท่าที่จะเป็นไปได้ การตั้งค่าสถานะที่ต้องการใช้กับเป้าหมายทั้งหมด (เช่น -Werror) จะไม่สะดวกหากต้องทำซ้ำต่อโมดูล แต่ก็ไม่ควรนำไปใช้ทั่วโลก (CMAKE_CXX_FLAGS) เนื่องจากอาจส่งผลที่ไม่พึงประสงค์ต่อ การอ้างอิงของบุคคลที่สามในโปรเจ็กต์ สำหรับกรณีดังกล่าว คุณสามารถใช้แฟล็กได้ที่ขอบเขตไดเรกทอรี (add_compile_options)

สำหรับแฟล็กคอมไพเลอร์ที่เจาะจง คุณยังตั้งค่าในไฟล์ build.gradle ได้โดยใช้ cppFlags หรือพร็อพเพอร์ตี้ที่คล้ายกัน คุณไม่ควรทำเช่นนี้ Flag ที่ส่งไปยัง CMake จาก Gradle จะมีลักษณะการทำงานที่น่าประหลาดใจในบางกรณี ซึ่งจะลบล้าง Flag ที่การติดตั้งใช้งานส่งโดยนัย ซึ่งจำเป็นสำหรับการสร้างโค้ด Android ควรจัดการลักษณะการทำงานของ CMake ใน CMake โดยตรงเสมอ หากต้องการควบคุมแฟล็กคอมไพเลอร์ต่อ AGP buildType, โปรดดูทํางานกับประเภทบิลด์ AGP ใน CMake

ทำงานกับประเภทบิลด์ AGP ใน CMake

หากต้องการปรับแต่งลักษณะการทำงานของ CMake ให้เข้ากับ Gradle ที่กำหนดเอง buildType ให้ใช้ประเภทบิลด์นั้น เพื่อส่งแฟล็ก CMake เพิ่มเติม (ไม่ใช่แฟล็กคอมไพเลอร์) ที่สคริปต์บิลด์ CMake สามารถอ่านได้ เช่น หากคุณมีตัวแปรบิลด์ "free" และ "premium" ที่ควบคุมโดย build.gradle.kts และคุณต้องส่ง ข้อมูลนั้นไปยัง CMake ให้ทำดังนี้

android {
    buildTypes {
        free {
            externalNativeBuild {
                cmake {
                    arguments.add("-DPRODUCT_VARIANT_PREMIUM=OFF")
                }
            }
        }
        premium {
            externalNativeBuild {
                cmake {
                    arguments.add("-DPRODUCT_VARIANT_PREMIUM=ON")
                }
            }
        }
    }
}

จากนั้นใน CMakeLists.txt ให้ทำดังนี้

if (DPRODUCT_VARIANT_PREMIUM)
  # Do stuff for the premium build.
else()
  # Do stuff for the free build.
endif()

คุณตั้งชื่อตัวแปรได้ตามต้องการ แต่โปรดหลีกเลี่ยงการใช้คำนำหน้า ANDROID_, APP_ หรือ CMAKE_ เพื่อไม่ให้เกิดการทับซ้อนหรือสับสนกับฟีเจอร์ค่าสถานะที่มีอยู่

ดูตัวอย่างได้ที่ตัวอย่าง NDK ของ Sanitizers

ทำความเข้าใจคำสั่งบิลด์ CMake

เมื่อแก้ไขข้อบกพร่องของปัญหาการสร้าง CMake การทราบอาร์กิวเมนต์การสร้างที่เฉพาะเจาะจงซึ่ง Gradle ใช้เมื่อคอมไพล์แบบข้ามระบบสำหรับ Android จะเป็นประโยชน์

ปลั๊กอิน Android Gradle จะบันทึกอาร์กิวเมนต์การสร้างที่ใช้ในการดำเนินการสร้าง CMake สำหรับแต่ละคู่ ABI และประเภทบิลด์ ไปยัง build_command.txt ไฟล์เหล่านี้อยู่ในไดเรกทอรีต่อไปนี้

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

ข้อมูลโค้ดต่อไปนี้แสดงตัวอย่างอาร์กิวเมนต์ CMake เพื่อสร้างรุ่นที่แก้ไขข้อบกพร่องได้ของตัวอย่าง hello-jni ซึ่งกำหนดเป้าหมายเป็นสถาปัตยกรรม 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

ใช้ไลบรารีที่สร้างไว้ล่วงหน้า

หากไลบรารีที่สร้างไว้ล่วงหน้าซึ่งคุณต้องการนำเข้ามีการเผยแพร่เป็น AAR ให้ทำตามเอกสารทรัพยากร Dependency ของ Studio เพื่อนำเข้าและใช้ไลบรารีเหล่านั้น หากไม่ได้ใช้ AGP คุณสามารถทำตาม https://google.github.io/prefab/example-workflow.html แต่การย้ายข้อมูลไปยัง AGP น่าจะง่ายกว่ามาก

สำหรับไลบรารีที่ไม่ได้เผยแพร่เป็น AAR โปรดดูวิธีการใช้ไลบรารีที่สร้างไว้ล่วงหน้ากับ CMake ในadd_libraryเอกสารประกอบเกี่ยวกับIMPORTED เป้าหมายในคู่มือ CMake

การสร้างโค้ดของบุคคลที่สาม

คุณสามารถสร้างโค้ดของบุคคลที่สามเป็นส่วนหนึ่งของโปรเจ็กต์ CMake ได้หลายวิธี และตัวเลือกที่เหมาะสมที่สุดจะขึ้นอยู่กับสถานการณ์ของคุณ ตัวเลือกที่ดีที่สุดมักจะเป็นการไม่ทำเช่นนี้เลย แต่ให้สร้าง AAR สำหรับไลบรารีและใช้ในแอปพลิเคชันแทน คุณไม่จำเป็นต้องเผยแพร่ AAR นั้น ซึ่งอาจเป็นภายในโปรเจ็กต์ Gradle ของคุณ

หากไม่มีตัวเลือกดังกล่าว ให้ทำดังนี้

  • ผู้ให้บริการ (เช่น คัดลอก) แหล่งที่มาของบุคคลที่สามลงในที่เก็บและใช้ add_subdirectory เพื่อสร้าง ซึ่งจะใช้ได้ก็ต่อเมื่อไลบรารีอื่นสร้างด้วย CMake ด้วยเช่นกัน
  • กำหนด ExternalProject
  • สร้างไลบรารีแยกจากโปรเจ็กต์ แล้วทำตามใช้ไลบรารีที่สร้างไว้ล่วงหน้าเพื่อนำเข้าเป็นไลบรารีที่สร้างไว้ล่วงหน้า

การรองรับ YASM ใน CMake

NDK รองรับ CMake สำหรับการสร้างโค้ดแอสเซมบลีที่เขียนใน YASM เพื่อเรียกใช้ในสถาปัตยกรรม x86 และ x86-64 YASM เป็นแอสเซมเบลอร์โอเพนซอร์สสำหรับสถาปัตยกรรม x86 และ x86-64 ซึ่งอิงตามแอสเซมเบลอร์ NASM

หากต้องการสร้างโค้ดแอสเซมบลีด้วย CMake ให้ทำการเปลี่ยนแปลงต่อไปนี้ใน CMakeLists.txtของโปรเจ็กต์

  1. เรียกใช้ enable_language โดยตั้งค่าเป็น ASM_NASM
  2. เรียกใช้ add_library หรือ add_executable ทั้งนี้ขึ้นอยู่กับว่าคุณกำลังสร้างไลบรารีที่ใช้ร่วมกันหรือไบนารีที่เรียกใช้งานได้ ใน อาร์กิวเมนต์ ให้ส่งรายการไฟล์แหล่งข้อมูลที่ประกอบด้วยไฟล์ .asm สำหรับโปรแกรมแอสเซมบลีใน YASM และไฟล์ .c สำหรับไลบรารีหรือฟังก์ชัน C ที่เกี่ยวข้อง

ข้อมูลโค้ดต่อไปนี้แสดงวิธีที่คุณอาจกำหนดค่า CMakeLists.txt เพื่อสร้างโปรแกรม YASM เป็นไลบรารีที่ใช้ร่วมกัน

cmake_minimum_required(VERSION 3.6.0)

enable_language(ASM_NASM)

add_library(test-yasm SHARED jni/test-yasm.c jni/print_hello.asm)

ดูตัวอย่างวิธีสร้างโปรแกรม YASM เป็นไฟล์ที่เรียกใช้งานได้ที่ yasm test ในที่เก็บ git ของ NDK

รายงานปัญหา

หากพบปัญหาเกี่ยวกับ NDK หรือไฟล์ Toolchain ของ CMake โปรดรายงานปัญหาดังกล่าว ผ่านเครื่องมือติดตามปัญหา android-ndk/ndk บน GitHub สำหรับปัญหาเกี่ยวกับ Gradle หรือ ปลั๊กอิน Android Gradle โปรดรายงานข้อบกพร่องของ Studio แทน