Android NDK תומך בשימוש ב-CMake כדי לקמפל קוד C ו-C++ עבור האפליקציה. בדף הזה מוסבר איך להשתמש ב-CMake עם NDK דרך התוסף Android Gradle ExternalNativeBuild או כשמפעילים את CMake ישירות.
קובץ שרשרת הכלים של CMake
NDK תומך ב-CMake באמצעות קובץ toolchain. קבצים של Toolchain הם קובצי CMake שמבצעים התאמה אישית של התנהגות ה-Toolchain עבור קומפילציה חוצה. קובץ ה-toolchain שמשמש ל-NDK נמצא ב-NDK במיקום <NDK>/build/cmake/android.toolchain.cmake.
פרמטרים של Build כמו ABI, minSdkVersion וכו' מועברים בשורת הפקודה כשמפעילים את cmake. רשימת הארגומנטים הנתמכים מופיעה בקטע ארגומנטים של Toolchain.
קובץ שרשרת הכלים 'חדש'
בגרסאות קודמות של NDK ניסו להטמיע קובץ toolchain חדש שיצמצם את ההבדלים בהתנהגות בין שימוש בקובץ toolchain של NDK לבין שימוש בתמיכה המובנית של CMake. בסופו של דבר, נדרשה כמות משמעותית של עבודה (שלא הושלמה), אבל לא חל שיפור בהתנהגות, ולכן אנחנו כבר לא ממשיכים בפתרון הזה.
קובץ ה-toolchain ה'חדש' כולל רגרסיות בהתנהגות בהשוואה לקובץ ה-toolchain 'הקודם'. התנהגות ברירת המחדל היא תהליך העבודה המומלץ. אם אתם משתמשים ב--DANDROID_USE_LEGACY_TOOLCHAIN_FILE=OFF, מומלץ להסיר את הדגל הזה מהגרסה שלכם. קובץ ה-toolchain החדש אף פעם לא הגיע לרמה של קובץ ה-toolchain מדור קודם, ולכן סביר להניח שיש רגרסיות בהתנהגות.
למרות שאנחנו לא ממליצים להשתמש בקובץ החדש של כלי הפיתוח, אין כרגע תוכניות להסיר אותו מ-NDK. פעולה כזו תשבור את הבנייה של קבצים שמסתמכים על ההבדלים בהתנהגות בין קבצי שרשרת הכלים החדשים לבין קבצי שרשרת הכלים מדור קודם. לצערי, שינוי השם של האפשרות כדי להבהיר שמומלץ להשתמש באפשרות 'דור קודם' ישבור גם את המשתמשים באפשרות הזו. אם אתם משתמשים בקובץ החדש של ערכת הכלים ואתם מרוצים ממנו, אתם לא צריכים לבצע העברה. עם זאת, חשוב לדעת שבאגים שדווחו לגבי ההתנהגות של הקובץ החדש של ערכת הכלים כנראה לא יתוקנו, ובמקרה כזה תצטרכו לבצע העברה.
שימוש
Gradle
השימוש בקובץ CMake toolchain הוא אוטומטי כשמשתמשים ב-externalNativeBuild. מידע נוסף זמין במדריך של Android Studio בנושא הוספת קוד C ו-C++ לפרויקט.
שורת פקודה
כשמבצעים build באמצעות CMake מחוץ ל-Gradle, צריך להעביר ל-CMake את קובץ ה-toolchain עצמו ואת הארגומנטים שלו. לדוגמה:
$ cmake \
-DCMAKE_TOOLCHAIN_FILE=$NDK/build/cmake/android.toolchain.cmake \
-DANDROID_ABI=$ABI \
-DANDROID_PLATFORM=android-$MINSDKVERSION \
$OTHER_ARGS
ארגומנטים של שרשרת הכלים
אפשר להעביר את הארגומנטים הבאים לקובץ CMake toolchain. אם אתם יוצרים את האפליקציה באמצעות Gradle, מוסיפים ארגומנטים ל-android.defaultConfig.externalNativeBuild.cmake.arguments כמו שמתואר במסמכי ExternalNativeBuild. אם מבצעים בנייה משורת הפקודה, מעבירים ארגומנטים ל-CMake באמצעות -D. לדוגמה, כדי למנוע מ-armeabi-v7a ליצור build עם תמיכה ב-Neon, מעבירים את -DANDROID_ARM_NEON=FALSE.
ANDROID_ABI
ממשק ה-ABI של היעד. מידע על ממשקי ABI נתמכים זמין במאמר ממשקי ABI של Android.
Gradle
הארגומנט הזה מסופק על ידי Gradle באופן אוטומטי. אל תגדירו את הארגומנט הזה במפורש בקובץ build.gradle. כדי לשלוט בממשקי ה-ABI שמטרגט Gradle, משתמשים ב-abiFilters כמו שמתואר במאמר ממשקי ABI של Android.
שורת פקודה
ב-CMake, כל בנייה מיועדת ליעד אחד. כדי לטרגט יותר מממשק ABI אחד של Android, צריך לבצע בנייה פעם אחת לכל ממשק ABI. מומלץ להשתמש בספריות build שונות לכל ABI כדי למנוע התנגשויות בין build.
| ערך | הערות |
|---|---|
armeabi-v7a |
|
armeabi-v7a with NEON |
בדיוק כמו armeabi-v7a. |
arm64-v8a |
|
x86 |
|
x86_64 |
ANDROID_ARM_MODE
ההגדרה הזו קובעת אם ליצור הוראות arm או thumb עבור armeabi-v7a. אין השפעה על ממשקי ABI אחרים. מידע נוסף זמין במאמר בנושא Android ABIs.
| ערך | הערות |
|---|---|
| קבוצה | |
| אגודל | התנהגות ברירת המחדל. |
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_LEVELandroid-$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 או מאפיינים דומים. לא מומלץ לעשות את זה. לדגלים שמועברים אל CMake מ-Gradle יש התנהגויות מפתיעות של קדימות, ובמקרים מסוימים הם מבטלים דגלים שמועברים באופן מרומז על ידי ההטמעה, שנדרשים לבניית קוד Android. עדיף תמיד לטפל בהתנהגות של CMake ישירות ב-CMake. אם אתם צריכים לשלוט בדגלי קומפיילר לכל AGP buildType, כדאי לעיין במאמר עבודה עם סוגי build של AGP ב-CMake.
עבודה עם סוגי build של AGP ב-CMake
אם אתם צריכים להתאים את אופן הפעולה של CMake ל-Gradle buildType בהתאמה אישית, אתם יכולים להשתמש בסוג build זה כדי להעביר דגל CMake נוסף (לא דגל של קומפיילר) שסקריפטים של בניית CMake יכולים לקרוא. לדוגמה, אם יש לכם וריאציות של build בשם 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_, כדי למנוע התנגשות או בלבול עם דגלים קיימים.
דוגמה אפשר לראות בדוגמה של Sanitizers NDK.
הסבר על פקודת ה-build של CMake
כשמנפים באגים בבעיות ב-CMake build, כדאי לדעת את ארגומנטי ה-build הספציפיים ש-Gradle משתמש בהם כשמבצעים קומפילציה חוצת פלטפורמות ל-Android.
הפלאגין של Android Gradle שומר את ארגומנטי ה-build שבהם הוא משתמש להפעלת build של CMake לכל ABI וסוג build לזוג build_command.txt. הקבצים האלה נמצאים בספרייה הבאה:
<project-root>/<module-root>/.cxx/cmake/<build-type>/<ABI>/
בקטע הקוד הבא מוצגים ארגומנטים של CMake ליצירת גרסת debug הניתנת לניפוי באגים של הדוגמה 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, אתם יכולים לייבא אותה ולהשתמש בה לפי ההוראות במסמכי התלות של Studio. אם אתם לא משתמשים ב-AGP, אתם יכולים לפעול לפי ההוראות שבכתובת https://google.github.io/prefab/example-workflow.html, אבל סביר להניח שהרבה יותר קל לעבור ל-AGP.
במקרה של ספריות שלא מופצות כ-AAR, הוראות לשימוש בספריות מוכנות מראש עם CMake מופיעות במסמכי add_library בנושא יעדים בIMPORTEDמדריך CMake.
יצירת קוד צד שלישי
יש כמה דרכים ליצור קוד של צד שלישי כחלק מפרויקט CMake, והאפשרות הכי טובה תלויה במצב שלכם. ברוב המקרים, האפשרות הכי טובה היא לא לעשות את זה בכלל. במקום זאת, צריך ליצור AAR לספרייה ולהשתמש בו באפליקציה. לא צריך לפרסם את ה-AAR הזה. יכול להיות שהיא פנימית לפרויקט Gradle.
אם אין אפשרות כזו:
- יוצרים עותק מקוד של צד שלישי (vendoring) מהמקור שלו במאגר שלכם ומשתמשים בפונקציה add_subdirectory כדי לבצע build. הפעולה הזו אפשרית רק אם גם הספרייה השנייה נוצרה באמצעות CMake.
- מגדירים ExternalProject.
- יוצרים את הספרייה בנפרד מהפרויקט ומייבאים אותה כספרייה מוכנה מראש לפי ההוראות שבמאמר שימוש בספריות מוכנות מראש.
תמיכה ב-YASM ב-CMake
NDK מספק תמיכה ב-CMake ליצירת קוד Assembly שנכתב ב-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 test במאגר NDK git.
דיווח על בעיות
אם נתקלים בבעיות ב-NDK או בקובץ CMake toolchain שלו, אפשר לדווח עליהן דרך כלי המעקב אחר בעיות android-ndk/ndk ב-GitHub. לדיווח על בעיות ב-Gradle או בפלאגין של Android Gradle, צריך לדווח על באג ב-Studio.