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

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

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

نظرة عامة

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

في حين أن CMake متوفر بشكل مباشر من AGP، هناك أدوات إنشاء مشاريع أخرى متاحة لا يتم دعمها بشكل مباشر:

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

عند تهيئة مشروع AGP بشكل صحيح مع منشئ نظام مشروع C/C++ متكامل، يمكّن المستخدمين من:

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

  • يمكنك تعديل المصادر باستخدام خدمات اللغة الكاملة (على سبيل المثال، الانتقال إلى التعريف) في "استوديو Android".

  • استخدِم برامج تصحيح الأخطاء في "استوديو 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".

  • ninja.configure هو مسار إلى ملف نص برمجي سيتم تنفيذه بواسطة Gradle عندما يكون من الضروري إعداد مشروع C/C++. يتم ضبط المشروع في الإصدار الأول، أو أثناء مزامنة Gradle في "استوديو Android"، أو عند تغيير أحد إدخالات النص البرمجي الذي تم إعداده.

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

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

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

    • ${ndk.abi} هو اسم واجهة 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

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

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