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

אם אתם לא משתמשים ב-CMake או ב-ndk-build, אבל אתם רוצים אינטגרציה מלאה של הפלאגין Android Gradle (AGP) C/C++ build ו-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, אבל יש מחוללי פרויקטים אחרים שאין בהם תמיכה ישירה:

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

אם מוגדר כראוי פרויקט AGP עם מחולל מערכות פרויקטים משולב מסוג C/C++ מאפשר למשתמשים:

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

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

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

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

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

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

כדי להפעיל תמיכה ב-נינג'ה ב-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 הוא רשימה של יעדי הנינג'ה הספציפיים שצריך לבנות.

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

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

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

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

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

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

באופן כללי, רוב המבנה שמייצג במדויק גרסת build של 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 כללים

כשהדבר אפשרי, מומלץ שהמבנה build.ninja ישתמש בכללי phony כדי לתת לפלטים של ה-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 קיימת שלא מבוססת על נינג'ה. במקרה כזה, עדיין צריך לייצג את כל המקורות יחד עם הדגלים שלהם יחד עם ספריות הפלט, כדי שמערכת Android Studio תוכל להציג תכונות מתאימות של שירות שפה, כמו השלמה אוטומטית ו-Go-to-הגדרה. עם זאת, אתם רוצים ש-AGP ידחה את מערכת ה-build הבסיסית במהלך גרסת ה-build בפועל.

כדי לעשות זאת, אפשר להשתמש בפלט build של נינג'ה עם תוסף ספציפי .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 לא מותקנת, אפשר לדווח על באג באמצעות התבנית הזו.