שילוב מערכות build מותאמות אישית של C/C++ באמצעות טכנולוגיית נינג'ה (ניסיוני)

אם אתם לא משתמשים ב-CMake או ב-ndk-build אבל רוצים שילוב מלא של Android Gradle Plugin (AGP) ל-C/C++ ו-Android Studio, אתם יכולים ליצור מערכת build מותאמת אישית ל-C/C++ על ידי יצירת סקריפט מעטפת שכותב את פרטי ה-build בפורמט קובץ ה-build של Ninja.

נוספה תמיכה ניסיונית במערכות build בהתאמה אישית של C/C++ ל-Android Studio ול-AGP. התכונה הזו זמינה החל מגרסה Android Studio Dolphin | 2021.3.1 Canary 4.

סקירה כללית

דפוס נפוץ בפרויקטים של C/C++‏, במיוחד בפרויקטים שמטרגטים כמה פלטפורמות, הוא ליצור פרויקטים לכל אחת מהפלטפורמות האלה מתוך ייצוג בסיסי כלשהו. דוגמה בולטת לתבנית הזו היא CMake. CMake יכול ליצור פרויקטים ל-Android, ל-iOS ולפלטפורמות אחרות מתוך ייצוג בסיסי יחיד שנשמר בקובץ CMakeLists.txt.

AGP תומך ישירות ב-CMake, אבל יש גנרטורים אחרים של פרויקטים שאינם נתמכים ישירות:

סוגי הגנרטורים האלה תומכים ב-Ninja כמייצג לקצה העורפי של ה-build ב-C/C++ או שניתן להתאים אותם ליצירת Ninja כמייצג לקצה העורפי.

כשפרויקט AGP מוגדר בצורה נכונה עם גנרטור משולב של מערכת פרויקטים ב-C/C++, המשתמשים יכולים:

  • פיתוח מתוך שורת הפקודה ומ-Android Studio.

  • עריכת מקורות עם תמיכה מלאה בשירותי השפה (לדוגמה, הגדרה מוגדרת מראש) ב-Android Studio.

  • שימוש בניפוי הבאגים של Android Studio כדי לנפות באגים בתהליכים מקומיים ובתהליכים מעורבים.

איך משנים את ה-build כך שישתמש בסקריפט מותאם אישית של הגדרות build ב-C/C++

בקטע הזה מוסבר איך להשתמש בסקריפט מותאם אישית של הגדרות build ב-C/C++ מ-AGP.

שלב 1: משנים את הקובץ build.gradle ברמת המודול כך שיפנה לסקריפט תצורה

כדי להפעיל תמיכה ב-Ninja ב-AGP, מגדירים את experimentalProperties בקובץ build.gradle ברמת המודול:

android {
  defaultConfig {
    externalNativeBuild {
      experimentalProperties["ninja.abiFilters"] = [ "x86", "arm64-v8a" ]
      experimentalProperties["ninja.path"] = "source-file-list.txt"
      experimentalProperties["ninja.configure"] = "configure-ninja"
      experimentalProperties["ninja.arguments"] = [
            "\${ndk.moduleMakeFile}",
            "--variant=\${ndk.variantName}",
            "--abi=Android-\${ndk.abi}",
            "--configuration-dir=\${ndk.configurationDir}",
            "--ndk-version=\${ndk.moduleNdkVersion}",
            "--min-sdk-version=\${ndk.minSdkVersion}"
       ]
     }
   }

מערכת AGP מפרשת את המאפיינים באופן הבא:

  • ninja.abiFilters היא רשימה של ממשקי ABI ליצירה. הערכים התקפים הם: x86,‏ x86-64,‏ armeabi-v7a ו-arm64-v8a.

  • ninja.path הוא נתיב לקובץ פרויקט ב-C/C++‎. הפורמט של הקובץ הזה יכול להיות כל מה שתרצו. שינויים בקובץ הזה יגרמו להצגת הודעה על סנכרון Gradle ב-Android Studio.

  • ninja.configure הוא נתיב לקובץ סקריפט שיופעל על ידי Gradle כשצריך להגדיר את הפרויקט ב-C/C++. הפרויקט מוגדר ב-build הראשון, במהלך סנכרון Gradle ב-Android Studio או כשאחד מהקלטים של סקריפט ההגדרה משתנה.

  • ninja.arguments היא רשימת ארגומנטים שתועברו לסקריפט שמוגדר על ידי ninja.configure. רכיבים ברשימה הזו יכולים להפנות לקבוצת מאקרו שהערכים שלה תלויים בהקשר התצורה הנוכחי ב-AGP:

    • ${ndk.moduleMakeFile} הוא הנתיב המלא לקובץ ninja.configure. בדוגמה הזו, זה יהיה C:\path\to\configure-ninja.bat.

    • ${ndk.variantName} הוא השם של גרסת ה-AGP הנוכחית שנוצרת. לדוגמה, ניפוי באגים או פרסום.

    • ${ndk.abi} הוא השם של AGP ABI הנוכחי שנוצר. לדוגמה, x86 או arm64-v8a.

    • ${ndk.buildRoot} הוא שם התיקייה שנוצרה על ידי AGP, שבה הסקריפט כותב את הפלט שלו. פרטים נוספים מופיעים בקטע שלב 2: יצירת סקריפט ההגדרה.

    • ${ndk.ndkVersion} היא הגרסה של NDK שבה צריך להשתמש. בדרך כלל זהו הערך שמוענק ל-android.ndkVersion בקובץ build.gradle, או ערך ברירת מחדל אם לא קיים ערך.

    • ${ndk.minPlatform} היא פלטפורמת היעד המינימלית ל-Android שנדרשת על ידי AGP.

  • ninja.targets היא רשימה של יעדי Ninja הספציפיים שצריך ליצור.

שלב 2: יוצרים את סקריפט ההגדרה

האחריות המינימלית של סקריפט התצורה (configure-ninja.bat בדוגמה הקודמת) היא ליצור קובץ build.ninja, שבזמן ה-build עם Ninja יתבצע בו הידור וקישור של כל הפלטים המקומיים של הפרויקט. בדרך כלל אלה קבצים מסוג .o (אובייקט), .a (ארכיון) ו-.so (אובייקט משותף).

סקריפט ההגדרה יכול לכתוב את הקובץ build.ninja בשני מקומות שונים, בהתאם לצרכים שלכם.

  • אם מותר ל-AGP לבחור מיקום, סקריפט ההגדרה יכתוב את הערך build.ninja במיקום שהוגדר במאקרו ${ndk.buildRoot}.

  • אם סקריפט התצורה צריך לבחור את המיקום של קובץ build.ninja, הוא גם יכתוב קובץ בשם build.ninja.txt במיקום שהוגדר במאקרו ${ndk.buildRoot}. הקובץ הזה מכיל את הנתיב המלא לקובץ build.ninja שסקריפט התצורה כתב.

המבנה של הקובץ build.ninja

באופן כללי, רוב המבנים שמייצגים בצורה מדויקת גרסה של Android שנוצרה באמצעות C/C++ יפעלו. הרכיבים העיקריים הנדרשים ל-AGP ול-Android Studio הם:

  • רשימת קובצי המקור של C/C++ יחד עם הדגלים הנדרשים ל-Clang כדי לקמפל אותם.

  • רשימת ספריות הפלט. בדרך כלל אלה קובצי .so (אובייקט משותף), אבל הם יכולים להיות גם קובצי .a (ארכיון) או קובצי הפעלה (ללא סיומת).

אם אתם צריכים דוגמאות ליצירת קובץ build.ninja, אתם יכולים לעיין בפלט של CMake כשמשתמשים בגנרטור build.ninja.

דוגמה לתבנית build.ninja מינימלית.

rule COMPILE
   command = /path/to/ndk/clang -c $in -o $out {other flags}
rule LINK
   command = /path/to/ndk/clang $in -o $out {other flags}

build source.o : COMPILE source.cpp
build lib.so : LINK source.o

שיטות מומלצות

בנוסף לדרישות (רשימת קובצי המקור וספריות הפלט), ריכזנו כאן כמה שיטות מומלצות.

הצהרה על משתני פלט עם שמות באמצעות כללי phony

כשהדבר אפשרי, מומלץ להשתמש בכללי phony במבנה build.ninja כדי לתת לשמות של הפלט של ה-build שמות שאנשים יכולים לקרוא. לדוגמה, אם יש לכם פלט בשם c:/path/to/lib.so, אתם יכולים לתת לו שם קריא באופן הבא.

build curl: phony /path/to/lib.so

היתרון של כך הוא שתוכלו לציין את השם הזה כיעד build בקובץ build.gradle. לדוגמה,

android {
  defaultConfig {
    externalNativeBuild {
      ...
      experimentalProperties["ninja.targets"] = [ "curl" ]

ציון יעד 'הכול'

כשמציינים יעד all, זו תהיה קבוצת ברירת המחדל של הספריות שייווצרו על ידי AGP אם לא יצוינו יעדים באופן מפורש בקובץ build.gradle.

rule COMPILE
   command = /path/to/ndk/clang $in -o $out {other flags}
rule LINK
   command = /path/to/ndk/clang $in -o $out {other flags}

build foo.o : COMPILE foo.cpp
build bar.o : COMPILE bar.cpp
build libfoo.so : LINK foo.o
build libbar.so : LINK bar.o
build all: phony libfoo.so libbar.so

ציון שיטת build חלופית (אופציונלי)

תרחיש לדוגמה מתקדם יותר הוא גיבוב של מערכת build קיימת שלא מבוססת על Ninja. במקרה כזה, עדיין צריך לייצג את כל המקורות עם הדגלים שלהם יחד עם ספריות הפלט, כדי ש-Android Studio יוכל להציג את התכונות המתאימות של שירות השפה, כמו השלמה אוטומטית והעברה להגדרה. עם זאת, אתם רוצים ש-AGP יפנה למערכת ה-build הבסיסית במהלך ה-build בפועל.

כדי לעשות זאת, אפשר להשתמש בפלט של build ב-Ninja עם סיומת ספציפית .passthrough.

דוגמה קונקרטית יותר: נניח שאתם רוצים לעטוף MSBuild. סקריפט התצורה ייצור את build.ninja כרגיל, אבל הוא גם יוסיף יעד העברה שמגדיר איך AGP יפעיל את MSBuild.

rule COMPILE
   command = /path/to/ndk/clang $in -o $out {other flags}
rule LINK
   command = /path/to/ndk/clang $in -o $out {other flags}

rule MBSUILD_CURL
  command = /path/to/msbuild {flags to build curl with MSBuild}

build source.o : COMPILE source.cpp
build lib.so : LINK source.o
build curl : phony lib.so
build curl.passthrough : MBSUILD_CURL

שליחת משוב

התכונה הזו ניסיונית, ולכן נשמח לקבל ממך משוב. אתם יכולים לשלוח משוב דרך הערוצים הבאים:

  • כדי לשלוח משוב כללי, אפשר להוסיף תגובה לבאג הזה.

  • כדי לדווח על באג, פותחים את Android Studio ולוחצים על עזרה > שליחת משוב. חשוב לעיין במאמר 'מערכות build מותאמות אישית של C/C++‎' כדי לעזור לנו לכוון את הבאג.

  • אם לא התקנתם את Android Studio, תוכלו לדווח על באג באמצעות התבנית הזו.