ผู้ผลิต

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

ไฟล์ Toolchain ของ CMake

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

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

ไฟล์ Toolchain "ใหม่"

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

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

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

การใช้งาน

เกรเดิล

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

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

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

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

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

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

ANDROID_ABI

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

เกรเดิล

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

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

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

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

ANDROID_ARM_MODE

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

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

ANDROID_NATIVE_API_LEVEL

ชื่อแทนของ ANDROID_PLATFORM

ANDROID_PLATFORM

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

เกรเดิล

เมื่อใช้ปลั๊กอิน Gradle ของ Android ระบบจะตั้งค่านี้โดยอัตโนมัติให้ตรงกับ 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++_ แชร์ ตัวแปรไลบรารีที่ใช้ร่วมกันของ libc++
c++_static ตัวแปรไลบรารีแบบคงที่ของ libc++
ไม่มี ไม่รองรับไลบรารีมาตรฐาน C++
ระบบ STL ของระบบ

จัดการ Flag คอมไพเลอร์

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

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

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

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

หากต้องการปรับแต่งลักษณะการทํางานของ CMake ให้เหมาะกับ Gradle buildType ที่กําหนดเอง ให้ใช้ประเภทบิลด์นั้นเพื่อส่ง Flag CMake เพิ่มเติม (ไม่ใช่ Flag คอมไพเลอร์) ที่สคริปต์บิลด์ CMake อ่านได้ ตัวอย่างเช่น หากคุณมีเวอร์ชันบิลด์ "ฟรี" และ "พรีเมียม" ที่ควบคุมโดย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 ของโปรแกรมตรวจสอบ

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

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

ปลั๊กอิน Gradle ของ Android จะบันทึกอาร์กิวเมนต์การสร้างที่ใช้สำหรับเรียกใช้การสร้าง 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 ด้วยเท่านั้น
  • กำหนดโปรเจ็กต์ภายนอก
  • สร้างไลบรารีแยกจากโปรเจ็กต์และทำตามใช้ไลบรารีที่สร้างไว้ล่วงหน้าเพื่อนำเข้าเป็นไลบรารีที่สร้างไว้ล่วงหน้า

การรองรับ 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 ในที่เก็บ Git สำหรับ NDK

รายงานปัญหา

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