يبدو لي أن معظم الناس يبدأون في التعامل مع المهد فقط عندما يحتاج الأمر إلى إضافة شيء إلى المشروع أو حدوث خلل مفاجئ - وبعد حل المشكلة "المكتسبة عن طريق العمل الشاق" ، تُنسى التجربة بأمان. علاوة على ذلك ، هناك العديد من الأمثلة على الإنترنت تشبه التعاويذ شديدة التخصص التي لا تضيف فهمًا لما يحدث:
android { compileSdkVersion 28 defaultConfig { applicationId "com.habr.hello" minSdkVersion 20 targetSdkVersion 28 } buildTypes { release { minifyEnabled false } } }
لن أصف بالتفصيل الغرض من كل سطر أعلاه - هذه هي تفاصيل خاصة عن تنفيذ المكون الإضافي android. هناك شيء أكثر قيمة - فهم كيف يتم تنظيم كل شيء. تنتشر المعلومات على مواقع مختلفة / وثائق رسمية / مصادر المهد والإضافات الخاصة به - بشكل عام ، هذه معرفة عالمية أكثر قليلاً لا أريد أن أنسىها.
يمكن اعتبار النص الإضافي بمثابة ورقة خداع لأولئك الذين يتقنون المهد أو ينسون بالفعل.
روابط مفيدة
وحدة التحكم
يقوم نظام Studio / IDEA الخاص بـ Android بإخفاء أوامر gradle من المطور ، وحتى عند تغيير ملفات build.gradle ، يبدأ في غباء أو إعادة تشغيل المشروع.
في مثل هذه الحالات ، يكون الاتصال بالكابل من وحدة التحكم أسهل بكثير وأسرع. عادةً ما يأتي مجمّع grapper مع المشروع ويعمل بشكل جيد في نظام linux / macos / windows ، إلا إذا كنت في الأخير تحتاج إلى استدعاء ملف bat بدلاً من المجمّع.
مهام التحدي
./gradlew tasks
يكتب المهام المتاحة.
./gradlew subprojectName:tasks --all
يمكنك عرض مهام مشروع فرعي منفصل ، وحتى مع الخيار - all ، سيتم عرض جميع المهام ، بما في ذلك المهام الثانوية.
يمكنك استدعاء أي مهمة ، وسيتم استدعاء جميع المهام التي تعتمد عليها.
./gradlew app:assembleDevelopDebug
إذا كنت كسولًا جدًا في كتابة الاسم بالكامل ، فيمكنك التخلص من الحروف الصغيرة:
./gradlew app:assembleDD
إذا كان البرد لا يستطيع تخمين المهمة التي تفكر بها بوضوح ، فسيعرض قائمة بالخيارات المناسبة.
تسجيل
يعتمد مقدار المعلومات المعروضة في وحدة التحكم عند بدء مهمة بشكل كبير على مستوى التسجيل.
بالإضافة إلى --quiet, --warn, --info, --debug
الافتراضي ، يوجد -q, -w, -i, -d
أو جيدًا أو - --quiet, --warn, --info, --debug
أو - --quiet, --warn, --info, --debug
أو --quiet, --warn, --info, --debug
أو - --quiet, --warn, --info, --debug
في زيادة مقدار المعلومات. في المشروعات المعقدة ، يمكن أن يستغرق الإخراج مع -d أكثر من ميغابايت ، وبالتالي من الأفضل حفظه في ملف على الفور والبحث هناك عن طريق البحث عن الكلمات الرئيسية:
./gradlew app:build -d > myLog.txt
إذا تم طرح استثناء في مكان ما ، فإن الخيار -s
for stacktrace.
يمكنك الكتابة إلى السجل بنفسك:
logger.warn('A warning log message.')
المسجل هو تنفيذ SLF4J.
رائع
ما يحدث في ملفات build.gradle
هو مجرد رمز رائع.
لسبب ما ، Groovy ، كلغة برمجة ، ليست شائعة جدًا ، رغم أنها ، كما يبدو لي ، تستحق الدراسة قليلاً على الأقل. ولدت اللغة في عام 2003 وتطورت ببطء. ميزات مثيرة للاهتمام:
- تقريبا أي رمز جافا صالح رمز رائع. يساعد كثيرا على كتابة كود العمل بشكل حدسي.
- بالتزامن مع ثابت ، يتم دعم الكتابة الديناميكية في الأخدود ، بدلاً من
String a = "a"
يمكنك كتابة def a = "a"
أو حتى def map = ['one':1, 'two':2, 'list' = [1,false]]
- هناك عمليات إغلاق يمكنك من خلالها تحديد سياق التنفيذ ديناميكيًا. نفس الكتل
android {...}
تأخذ عمليات إغلاق ثم تنفذها لبعض الكائنات. - يوجد استيفاء للسلاسل
"$a, ${b}"
، سلاسل متعددة الأسطر """yep, ${c}"""
، وتم تأطير سلاسل جافا العادية بعلامات اقتباس مفردة: 'text'
- هناك ما يشبه طرق التمديد. تحتوي مجموعة اللغات القياسية بالفعل على أساليب مثل أي ، كل ، كل ، findAll. بالنسبة لي شخصياً ، تبدو أسماء الأساليب غير عادية ، لكن الشيء الرئيسي هو أنها كذلك .
- السكر النحوي اللذيذ ، الكود أقصر بكثير وأبسط. ليس عليك أن تكتب أقواس حول وسيطات الوظيفة ، من أجل إعلان القوائم
[a,b,c], [key1: value1, key2: value2]
التجزئة [a,b,c], [key1: value1, key2: value2]
الصيغة الجميلة هي: [a,b,c], [key1: value1, key2: value2]
بشكل عام ، لماذا بلغ عدد لغات مثل Python / Javascript ارتفاعًا كبيرًا ولم يكن Groovy - إنه لغز بالنسبة لي. في الوقت المناسب ، عندما لم يكن هناك حتى lambdas في java ، وكانت بدائل مثل kotlin / scala تظهر فقط أو لم تكن موجودة بعد ، كان على Groovy أن تبدو وكأنها لغة مثيرة للاهتمام حقًا.
لقد كانت مرونة بناء الجملة المذهلة والكتابة الديناميكية هي التي سمحت لنا بإنشاء DSLs موجزة في المهد.
الآن في الوثائق الرسمية لـ Gradle ، يتم تكرار الأمثلة على Kotlin ، ويبدو أنه من المخطط التحول إليها ، ولكن الرمز لم يعد يبدو بهذه البساطة ويصبح أكثر من الشفرة العادية:
task hello { doLast { println "hello" } }
مقابل
tasks.register("hello") { doLast { println("hello") } }
ومع ذلك ، لم يتم التخطيط لإعادة التسمية في Kradle حتى الآن.
مراحل التجميع
وهي مقسمة إلى التهيئة والتكوين والتنفيذ.
والفكرة هي أن gradle تجمع رسم بياني تبعية حادة ولا تستدعي سوى الحد الأدنى المطلوب منها. إذا فهمت بشكل صحيح ، فإن مرحلة التهيئة تحدث في الوقت الذي يتم فيه تنفيذ التعليمات البرمجية من build.gradle.
على سبيل المثال ، هذا:
copy { from source to dest }
أو مثل هذا:
task epicFail { copy{ from source to dest } }
ربما هذا غير واضح ، ولكن ما سبق سوف يبطئ التهيئة. من أجل عدم التعامل مع نسخ الملفات في كل تهيئة ، تحتاج إلى استخدام doLast{...}
أو doFirst{...}
في المهمة - بعد ذلك سيتم لف الرمز في الإغلاق وسيتم استدعاءه عند اكتمال المهمة.
task properCopy { doLast { copy { from dest to source } } }
او هكذا
task properCopy(type: Copy) { from dest to source }
في الأمثلة القديمة ، doLast
رؤية عامل التشغيل <<
بدلاً من doLast
، لكنهم تخلوا عنه لاحقًا بسبب السلوك غير الواضح.
task properCopy << { println("files copied") }
tasks.all
ما هو المضحك ، مع doLast
و doFirst
يمكنك تعليق نوع من العمل في أي مهمة:
tasks.all { doFirst { println("task $name started") } }
يقترح IDE أن tasks
طريقة whenTaskAdded(Closure ...)
، لكن طريقة all(Closure ...)
تعمل أكثر إثارة للاهتمام - يتم استدعاء الإغلاق لجميع المهام الحالية ، وكذلك للمهام الجديدة عند إضافتها.
قم بإنشاء مهمة تطبع تبعيات كل المهام:
task printDependencies { doLast { tasks.all { println("$name dependsOn $dependsOn") } } }
او نحو ذلك:
task printDependencies { doLast { tasks.all { Task task -> println("${task.name} dependsOn ${task.dependsOn}") } } }
إذا تم tasks.all{}
في وقت التشغيل (في كتلة doLast
) ، doLast
كل المهام والتبعيات.
إذا كنت تفعل الشيء نفسه دون doLast
(أي أثناء التهيئة) ، فقد تفتقر المهام المطبوعة إلى تبعيات ، حيث لم تتم إضافتها بعد.
أوه نعم ، الإدمان! إذا كانت مهمة أخرى يجب أن تعتمد على نتائج تنفيذنا ، فمن الجدير إضافة تبعية:
anotherTask.dependsOn properCopy
أو حتى مثل هذا:
tasks.all{ task -> if (task.name.toLowerCase().contains("debug")) { task.dependsOn properCopy } }
سيتم استدعاء مهمة مشتركة في كل مرة. إذا حددت أن المهمة المستندة إلى الملف A تنشئ الملف B ، فسيتخطى gradle المهمة إذا لم تتغير هذه الملفات. و gradle لا يتحقق تاريخ تعديل الملف ، ولكن محتوياته.
task generateCode(type: Exec) { commandLine "generateCode.sh", "input.txt", "output.java" inputs.file "input.txt" output.file "output.java" }
وبالمثل ، يمكنك تحديد المجلدات ، وكذلك بعض القيم: inputs.property(name, value)
.
وصف المهمة
عند الاتصال ./gradlew tasks --all
المهام القياسية لها وصف جميل ويتم تجميعها بطريقة أو بأخرى. لمهامك ، يتم إضافة هذا بكل بساطة:
task hello { group "MyCustomGroup" description "Prints 'hello'" doLast{ print 'hello' } }
task.enabled
يمكنك "إيقاف" المهمة - بعد ذلك سوف يتم استدعاء تبعياتها ، لكنها نفسها لن تفعل ذلك.
taskName.enabled false
عدة مشاريع (وحدات)
مشروع متعدد يبني في الوثائق
في المشروع الرئيسي ، يمكنك وضع المزيد من الوحدات. على سبيل المثال ، يتم استخدام هذا في مشاريع android - لا يوجد أي شيء تقريبًا في مشروع الجذر ، يتم تضمين البرنامج المساعد android في المشروع الفرعي. إذا كنت ترغب في إضافة وحدة نمطية جديدة ، يمكنك إضافة وحدة أخرى ، وهناك ، على سبيل المثال ، يمكنك أيضًا توصيل المكون الإضافي android ، ولكن يمكنك استخدام إعدادات أخرى له.
مثال آخر: عند نشر مشروع باستخدام jitpack ، يصف المشروع الجذر بالإعدادات اللازمة لنشر وحدة فرعية قد لا تشك في المنشور.
يتم تحديد الوحدات التابعة في settings.gradle:
include 'name'
اقرأ المزيد عن التبعيات بين المشاريع هنا.
buildSrc
إذا كان build.gradle
الكثير من التعليمات البرمجية في build.gradle
أو تم تكرارها ، فيمكن نقلها إلى وحدة نمطية منفصلة. نحن بحاجة إلى مجلد يحمل اسم السحر buildSrc
، حيث يمكنك وضع الكود في groovy أو java. (جيدًا ، أو بالأحرى ، في buildSrc/src/main/java/com/smth/
code ، يمكن إضافة الاختبارات إلى buildSrc/src/test
). إذا كنت تريد شيئًا آخر ، على سبيل المثال ، اكتب مهمتك على scala أو استخدم بعض التبعيات ، فعندئذٍ في buildSrc
مباشرة تحتاج إلى إنشاء build.gradle
وتحديد التبعيات اللازمة في ذلك / تمكين المكونات الإضافية.
لسوء الحظ ، مع وجود مشروع في buildSrc
يمكن لـ IDE أن يكون غبيًا مع تلميحات ، حيث سيتعين عليك كتابة استيراد وفئات / مهام من هناك ، كما يجب عليك استيراده إلى build.gradle
المعتاد. import com.smth.Taskname
ليست صعبة ، تحتاج فقط إلى تذكر هذا وليس لغزًا حول سبب عدم العثور على المهمة من buildSrc
).
لهذا السبب ، من المريح أولاً كتابة شيء يعمل مباشرة في build.gradle
، وعندها فقط ينقل الكود إلى buildSrc
.
نوع المهمة الخاصة
DefaultTask
المهمة من DefaultTask
، حيث يوجد العديد من الحقول والأساليب وأشياء أخرى. رمز AbstractTask الموروثة من DefaultTask.
نقاط مفيدة:
- بدلاً من إضافة
inputs
outputs
يدويًا outputs
يمكنك استخدام الحقول والتعليقات التوضيحية لهم: @Input, @OutputFile
، إلخ. - الطريقة التي سيتم تشغيلها عند تنفيذ المهمة:
@TaskAction
. - لا يزال من الممكن استدعاء أساليب ملائمة مثل
copy{from ... , into... }
، ولكن يجب عليك الاتصال بها صراحة من أجل المشروع: project.copy{...}
عندما يكتب شخص ما في build.gradle
taskName { ...
يتم استدعاء أسلوب configure(Closure)
في المهمة.
لست متأكدًا مما إذا كان هذا هو النهج الصحيح ، ولكن إذا كان للمهمة العديد من المجالات التي يصعب السيطرة على حالتها المتبادلة مع المستوطنين ، فيبدو من المريح جدًا إعادة تحديد الطريقة على النحو التالي:
override def configure(Closure closure){ def result = super().configure(closure)
وحتى لو كنت تكتب
taskName.fieldName value
ثم لا يزال يتم استدعاء طريقة configure
.
المساعد الخاص
مثل مهمة ، يمكنك كتابة البرنامج المساعد الخاص بك ، والتي سوف تكوين شيء أو إنشاء المهام. على سبيل المثال ، ما يحدث في android{...}
هو ميزة بالكامل سحر الظلام المكون الإضافي لنظام Android ، والذي ينشئ بالإضافة إلى ذلك مجموعة كاملة من المهام مثل التطبيق: assembleDevelopDebug لجميع المجموعات الممكنة من النكهة / نوع البناء / dimenstion. لا يوجد شيء معقد في كتابة البرنامج المساعد الخاص بك ، لفهم أفضل يمكنك رؤية رمز الإضافات الأخرى.
هناك خطوة ثالثة - يمكنك وضع الكود ليس في buildSrc
، ولكن جعله مشروعًا منفصلاً. بعد ذلك ، باستخدام https://jitpack.io أو أي شيء آخر ، قم بنشر المكون الإضافي وتوصيله بالمثل مع الآخرين.
النهاية
قد تتضمن الأمثلة المذكورة أعلاه الأخطاء المطبعية وغير الدقيقة. اكتب ملاحظة أو علامة شخصية باستخدام ctrl+enter
- سأقوم بتصحيحها. من الأفضل أخذ أمثلة محددة من الوثائق وإلقاء نظرة على هذه المقالة كقائمة صغيرة من "كيفية القيام بذلك".