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
ของโปรเจ็กต์
- เรียกใช้
enable_language
โดยตั้งค่าเป็นASM_NASM
- เรียกใช้
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 แทน