ورقة الغش الدرج

يبدو لي أن معظم الناس يبدأون في التعامل مع المهد فقط عندما يحتاج الأمر إلى إضافة شيء إلى المشروع أو حدوث خلل مفاجئ - وبعد حل المشكلة "المكتسبة عن طريق العمل الشاق" ، تُنسى التجربة بأمان. علاوة على ذلك ، هناك العديد من الأمثلة على الإنترنت تشبه التعاويذ شديدة التخصص التي لا تضيف فهمًا لما يحدث:


android { compileSdkVersion 28 defaultConfig { applicationId "com.habr.hello" minSdkVersion 20 targetSdkVersion 28 } buildTypes { release { minifyEnabled false } } } 

لن أصف بالتفصيل الغرض من كل سطر أعلاه - هذه هي تفاصيل خاصة عن تنفيذ المكون الإضافي android. هناك شيء أكثر قيمة - فهم كيف يتم تنظيم كل شيء. تنتشر المعلومات على مواقع مختلفة / وثائق رسمية / مصادر المهد والإضافات الخاصة به - بشكل عام ، هذه معرفة عالمية أكثر قليلاً لا أريد أن أنسىها.


يمكن اعتبار النص الإضافي بمثابة ورقة خداع لأولئك الذين يتقنون المهد أو ينسون بالفعل.


روابط مفيدة


  • الوثائق الرسمية ضخمة للغاية ، ولكن في بعض الأماكن قد يكون هناك نقص في التفاصيل.
  • أكواد المصدر على github ، javadoc - بسبب الكتابة الديناميكية في groovy ، بيئة التطوير بعيدة كل البعد عن القدرة دائمًا على تقديم قائمة بالحقول / الطرق المتاحة ، ومن خلال الأسماء المختصرة للطرق وأنواع الوسيطة (إغلاق الإغلاق) ، لا يمكن دائمًا فهم سبب الحاجة إليها.
  • مقالة حول محور مع مجموعة من الأمثلة هي ترجمة للفصل الثاني من كتاب "بناء واختبار مع gradle". يمكن قراءة الكتاب أيضًا ، وهو متاح مجانًا.
  • مقال آخر - حول buildSrc

وحدة التحكم


يقوم نظام 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 { ... //some code } 

يتم استدعاء أسلوب configure(Closure) في المهمة.


لست متأكدًا مما إذا كان هذا هو النهج الصحيح ، ولكن إذا كان للمهمة العديد من المجالات التي يصعب السيطرة على حالتها المتبادلة مع المستوطنين ، فيبدو من المريح جدًا إعادة تحديد الطريقة على النحو التالي:


 override def configure(Closure closure){ def result = super().configure(closure) //    / - return result; } 

وحتى لو كنت تكتب


 taskName.fieldName value 

ثم لا يزال يتم استدعاء طريقة configure .


المساعد الخاص


مثل مهمة ، يمكنك كتابة البرنامج المساعد الخاص بك ، والتي سوف تكوين شيء أو إنشاء المهام. على سبيل المثال ، ما يحدث في android{...} هو ميزة بالكامل سحر الظلام المكون الإضافي لنظام Android ، والذي ينشئ بالإضافة إلى ذلك مجموعة كاملة من المهام مثل التطبيق: assembleDevelopDebug لجميع المجموعات الممكنة من النكهة / نوع البناء / dimenstion. لا يوجد شيء معقد في كتابة البرنامج المساعد الخاص بك ، لفهم أفضل يمكنك رؤية رمز الإضافات الأخرى.


هناك خطوة ثالثة - يمكنك وضع الكود ليس في buildSrc ، ولكن جعله مشروعًا منفصلاً. بعد ذلك ، باستخدام https://jitpack.io أو أي شيء آخر ، قم بنشر المكون الإضافي وتوصيله بالمثل مع الآخرين.


النهاية


قد تتضمن الأمثلة المذكورة أعلاه الأخطاء المطبعية وغير الدقيقة. اكتب ملاحظة أو علامة شخصية باستخدام ctrl+enter - سأقوم بتصحيحها. من الأفضل أخذ أمثلة محددة من الوثائق وإلقاء نظرة على هذه المقالة كقائمة صغيرة من "كيفية القيام بذلك".

Source: https://habr.com/ru/post/ar458046/


All Articles