دمج أنظمة إنشاء C/C++ مخصصة باستخدام Ninja (تجريبي)

إذا كنت لا تستخدم CMake أو ndk-build ولكنك تريد الدمج الكامل بين أداة إنشاء C/C++ في مكوّن إضافي لـ Android Gradle (AGP) وAndroid Studio، يمكنك إنشاء نظام إنشاء مخصّص لـ C/C++ من خلال إنشاء نص برمجي لنظام التشغيل Shell يكتب معلومات الإنشاء بتنسيق ملف إنشاء Ninja.

تمت إضافة دعم تجريبي لأنظمة إنشاء C/C++ المخصّصة إلى "استوديو Android" وAGP. تتوفّر هذه الميزة بدءًا من الإصدار Android Studio Dolphin | 2021.3.1 Canary 4.

نظرة عامة

من الأنماط الشائعة لمشاريع C/C++، خاصةً تلك التي تستهدف أنظمة تشغيل متعددة، إنشاء مشاريع لكل نظام تشغيل من هذه الأنظمة من تمثيل أساسي. ومن الأمثلة البارزة على هذا النمط CMake. يمكن أن تنشئ أداة CMake مشاريع لنظامَي التشغيل Android وiOS والأنظمة الأساسية الأخرى من تمثيل أساسي واحد محفوظ في ملف CMakeLists.txt.

على الرغم من أنّ AGP متوافق مباشرةً مع CMake، تتوفّر أدوات إنشاء مشاريع أخرى غير متوافقة مباشرةً:

تتيح هذه الأنواع من أدوات إنشاء المشاريع استخدام Ninja كتمثيل لنظام التشغيل الخلفي لإنشاء C/C++ أو يمكن تعديلها لإنشاء Ninja كتمثيل لنظام التشغيل الخلفي.

عند الضبط الصحيح، يتيح مشروع AGP المزوّد بنظام مولد متكامل لمشاريع C/C++ للمستخدمين إجراء ما يلي:

  • يمكنك إنشاء التطبيق من سطر الأوامر و"استوديو Android".

  • تعديل المصادر باستخدام ميزة الدعم الكامل لخدمة اللغة (مثل التعريف المرجعي) في Android Studio

  • استخدِم برامج تصحيح الأخطاء في "استوديو Android" لتصحيح أخطاء العمليات الأصلية والمختلطة.

كيفية تعديل عملية الإنشاء لاستخدام نص برمجي مخصّص لإعداد عملية إنشاء C/C++

يشرح هذا القسم خطوات استخدام نص برمجي مخصّص لإعداد عملية إنشاء 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++. يتم ضبط المشروع في عملية التصميم الأولى، أو أثناء مزامنة Gradle في Android Studio، أو عند تغيير إحدى مدخلات نص الإعداد.

  • ninja.arguments هي قائمة بالوسيطات التي سيتم تمريرها إلى البرنامج النصي الذي يحدّده ninja.configure. يمكن أن تشير العناصر في هذه القائمة إلى مجموعة من وحدات الماكرو التي تعتمد قيمها على سياق الضبط الحالي في AGP:

    • ${ndk.moduleMakeFile} هو المسار الكامل لملف ninja.configure. في المثال، سيكون C:\path\to\configure-ninja.bat.

    • ${ndk.variantName} هو اسم الصيغة الحالية لملف AGP الذي يتم إنشاؤه. على سبيل المثال، تصحيح الأخطاء أو الإصدار.

    • ${ndk.abi} هو اسم ABI الحالي لـ AGP الذي يتم إنشاؤه. على سبيل المثال، 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 الذي سيُنشئ عند إنشائه باستخدام Ninja جميع النتائج الأصلية للمشروع ويربطها. وتكون هذه الملفات عادةً من النوع .o (كائن) و.a (أرشيف) و.so (كائن مشترَك).

يمكن للنص البرمجي لإعداد الملفات كتابة ملف build.ninja في مكانَين مختلفَين حسب احتياجاتك.

  • إذا كان من المقبول أن يختار AGP موقعًا جغرافيًا، يكتب النص البرمجي لإعداد build.ninja في الموقع الجغرافي الذي تم ضبطه في الماكرو ${ndk.buildRoot}.

  • إذا كان النص البرمجي للضبط بحاجة إلى اختيار موقع ملف build.ninja، سيكتب أيضًا ملفًا باسم build.ninja.txt في الموقع الذي تم ضبطه في الماكرو ${ndk.buildRoot}. يحتوي هذا الملف على المسار الكامل لملف build.ninja الذي كتبه النص البرمجي لإعداد الإصدار.

بنية ملف build.ninja

بشكل عام، ستعمل معظم البنى التي تمثّل بدقة إصدار C/C++ لنظام التشغيل Android. في ما يلي العناصر الرئيسية التي يحتاج إليها كلّ من AGP و"استوديو Android":

  • قائمة ملفات 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 كلما أمكن ذلك لمنح أسماء قابلة للقراءة من قِبل البشر لمخرجات الإنشاء. على سبيل المثال، إذا كان لديك إخراج باسم c:/path/to/lib.so، يمكنك منحه اسمًا يسهل على المستخدمين قراءته على النحو التالي.

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

وتتمثل فائدة إجراء ذلك في أنّه يمكنك بعد ذلك تحديد هذا الاسم كهدف إنشاء في ملف 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

تحديد طريقة إنشاء بديلة (اختياري)

وإحدى حالات الاستخدام الأكثر تقدمًا هي لف نظام إنشاء حالي غير مستند إلى Ninja. في هذه الحالة، لا يزال عليك تمثيل جميع المصادر باستخدام علاماتها مع مكتبات الإخراج حتى يتمكّن Android Studio من تقديم ميزات خدمة اللغة المناسبة، مثل الإكمال التلقائي والانتقال إلى التعريف. ومع ذلك، تريد أن يستند AGP إلى نظام الإنشاء الأساسي أثناء عملية الإنشاء الفعلية.

ولإجراء ذلك، يمكنك استخدام ناتج إنشاء 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 وانقر على المساعدة > إرسال ملاحظات. احرص على الإشارة إلى "أنظمة إنشاء C/C++ المخصّصة" للمساعدة في توجيه الخطأ.

  • للإبلاغ عن خطأ إذا لم يكن لديك "استوديو Android" مثبّتًا، يمكنك إرسال تقرير الخطأ باستخدام هذا النموذج.