برنامه نویسی

الحاقات Gradle قسمت 2: اکنون با شیطنت‌ها

Summarize this content to 400 words in Persian Lang
عکس از Karsten Würth در Unsplash

به جانشین معنوی پلاگین‌ها و برنامه‌های افزودنی Gradle خوش آمدید: یک پرایمر برای افراد متحیر (یکی از محبوب‌ترین پست‌های من، به گونه‌ای که برای فضا با اسناد واقعی Gradle در بالای جستجوی Google رقابت می‌کند).

به عنوان بخشی از تلاش طولانی مدت من برای نابود کردن buildSrc با Fire، اخیراً فرصتی داشتم که یاد بگیرم چگونه افزونه‌ها را به انواع دیگر، مانند وظایف، اضافه کنم. ما کدهایی مانند این داریم که در بسیاری از مخازن که تحت مراقبت ما هستند تکرار شده است:

// buildSrc/src/main/kotlin/magic/Magic.kt
package magic

object Magic {
fun shouldPracticeTheDarkArts(): Boolean {
return System.getenv(“DO_ANCIENT_MAGIC”)?.toBoolean()
?: System.getenv(“DO_SLIGHTLY_MORE_MODERN_MAGIC”)?.toBoolean()
?: false
}
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

این کد در ساخت اسکریپت های زیر استفاده می شود:

// build.gradle.kts
import magic.Magic

tasks.withType<Test>().configureEach {
if (Magic.shouldPracticeTheDarkArts()) {
logger.quiet(“👻”)
}
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

چندین چیز در این مورد وجود دارد که من می خواهم آنها را بهبود بخشم:

من این کد را در buildSrc نمی خواهم. من نسخه‌ای از آن را در build-logic می‌خواهم که تحت آزمایش باشد و به طور گسترده به اشتراک گذاشته شود (به‌جای اینکه در چندین مخزن مختلف تکراری شود).
من واردات را دوست ندارم نجس است. (اسکریپت های ساخت باید ساده، بیانی و برای تجزیه ابزارها آسان باشد.)
من زیاد طرفدار تماس نیستم System.getenv() در زمینه Gradle من ترجیح می دهم از ProviderFactory.

در حال تمدید Test وظایف

بسیاری از انواع Gradle، از جمله همه Tasks (و البته Project نوع)، هستند ExtensionAware. این بدان معنی است که همه آنها یک ExtensionContainer موجود است که می توان بر روی آن افزونه های جدید ایجاد و اضافه کرد.

package magic

abstract class TestMagicExtension @Inject constructor(
private val providers: ProviderFactory
) {

internal companion object {
const val NAME = “magic”

fun create(
testTask: Test,
providers: ProviderFactory,
) {
testTask.extensions.create(
NAME,
TestMagicExtension::class.java,
providers,
)
}
}

fun shouldPracticeTheDarkArts(): Boolean {
return providers
.environmentVariable(“DO_ANCIENT_MAGIC”)
.orElse(providers.environmentVariable(“DO_SLIGHTLY_MORE_MODERN_MAGIC”))
.map { it.isNotEmpty() }
.getOrElse(false)
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

و در افزونه ما، ما می توانیم این را به همه ما اضافه کنیم Test وظایف:

project.tasks.withType<Test>().configureEach { t ->
TestMagicExtension.create(t, project.providers)
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

و اکنون می توانیم اسکریپت های ساخت خود را به روز کنیم:

// build.gradle.kts
import magic.TestMagicExtension

tasks.withType<Test>().configureEach {
// the “extensions” call is on the Test instance,
// not the project instance
val magic = extensions.getByType(TestMagicExtension::class.java)
if (magic.shouldPracticeTheDarkArts()) {
logger.quiet(“👻”)
}
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

… که اصلا بهتر نیست!

Groovy: میان‌آهنگ

اول از همه، بیایید یک قدم به عقب برگردیم و به خود یادآوری کنیم که “ما عاشق کاتلین هستیم، ایمنی تایپ عالی است، من اهمیتی نمی دهم که عملکرد بدتر است…” می توانیم این را بارها و بارها در حین تکان دادن بگوییم. وضعیت جنین روی زمین تا زمانی که احساس بهتری داشته باشیم. اکنون، اینجا همان اسکریپت ساخت اما در Groovy است:

// build.gradle
tasks.withType(Test).configureEach {
if (magic.shouldPracticeTheDarkArts()) {
// I’m being cheeky by also omitting the
// “redundant” parentheses
logger.quiet “👻”
}
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

Groovy قرار نیست بهتر باشد! لعنتی!

روی مقداری گلاب بپاشید

چگونه Gradle Kotlin DSL این کار را انجام می دهد؟ چرا «دسترسی‌های typesafe» را برای برنامه افزودنی آزمایشی من ایجاد نمی‌کند؟ خب، سوال دوم سوال خوبی است و من جوابی ندارم. اما برای اولین… بیایید فقط “تولید” (یعنی بنویسیم) لوازم جانبی typeafe خودمان را بسازیم!

ما تعدادی کد را در یک بسته جدید (به ما) اضافه می کنیم:

package org.gradle.kotlin.dsl

import magic.TestMagicExtension

public val Test.magic: TestMagicExtension
get() = extensions.getByType(TestMagicExtension::class.java)

public fun Test.magic(configure: TestMagicExtension.() -> Unit) {
configure(TestMagicExtension.NAME, configure)
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

و اکنون می توانیم خود را به روز کنیم Kotlin DSL ساخت اسکریپت:

// build.gradle.kts
tasks.withType<Test>().configureEach {
if (magic.shouldPracticeTheDarkArts()) {
logger.quiet(“👻”)
}
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در اینجا ما از این واقعیت استفاده می کنیم که Gradle به طور خودکار همه چیز را وارد می کند org.gradle.kotlin.dsl بسته به اسکریپت های ساخت، بنابراین همه آن توابع فقط آنجا هستند (در یک فضای نام جهانی، پس مراقب باشید!).

این یک الگوی معمولی است که خود Gradle از آن در افزونه test-retry-gradle-خود استفاده می کند. همچنین یک مشکل باز (از سال 2018) در ردیاب مشکل Gradle با یک درخواست ویژگی برای اجازه دادن به افزونه های سفارشی برای اضافه کردن واردات پیش فرض خود با استفاده از بسته های تقسیم شده مانند این وجود دارد.

اکنون برو و شاد باش، زیرا آن زمان از سال است.

عکس از Karsten Würth در Unsplash

به جانشین معنوی پلاگین‌ها و برنامه‌های افزودنی Gradle خوش آمدید: یک پرایمر برای افراد متحیر (یکی از محبوب‌ترین پست‌های من، به گونه‌ای که برای فضا با اسناد واقعی Gradle در بالای جستجوی Google رقابت می‌کند).

نتایج جستجوی گوگل برای

به عنوان بخشی از تلاش طولانی مدت من برای نابود کردن buildSrc با Fire، اخیراً فرصتی داشتم که یاد بگیرم چگونه افزونه‌ها را به انواع دیگر، مانند وظایف، اضافه کنم. ما کدهایی مانند این داریم که در بسیاری از مخازن که تحت مراقبت ما هستند تکرار شده است:

// buildSrc/src/main/kotlin/magic/Magic.kt
package magic

object Magic {
  fun shouldPracticeTheDarkArts(): Boolean {
    return System.getenv("DO_ANCIENT_MAGIC")?.toBoolean()
      ?: System.getenv("DO_SLIGHTLY_MORE_MODERN_MAGIC")?.toBoolean()
      ?: false
  }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

این کد در ساخت اسکریپت های زیر استفاده می شود:

// build.gradle.kts
import magic.Magic

tasks.withType<Test>().configureEach {
  if (Magic.shouldPracticeTheDarkArts()) {
    logger.quiet("👻")
  }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

چندین چیز در این مورد وجود دارد که من می خواهم آنها را بهبود بخشم:

  1. من این کد را در buildSrc نمی خواهم. من نسخه‌ای از آن را در build-logic می‌خواهم که تحت آزمایش باشد و به طور گسترده به اشتراک گذاشته شود (به‌جای اینکه در چندین مخزن مختلف تکراری شود).
  2. من واردات را دوست ندارم نجس است. (اسکریپت های ساخت باید ساده، بیانی و برای تجزیه ابزارها آسان باشد.)
  3. من زیاد طرفدار تماس نیستم System.getenv() در زمینه Gradle من ترجیح می دهم از ProviderFactory.

در حال تمدید Test وظایف

بسیاری از انواع Gradle، از جمله همه Tasks (و البته Project نوع)، هستند ExtensionAware. این بدان معنی است که همه آنها یک ExtensionContainer موجود است که می توان بر روی آن افزونه های جدید ایجاد و اضافه کرد.

package magic

abstract class TestMagicExtension @Inject constructor(
  private val providers: ProviderFactory
) {

  internal companion object {
    const val NAME = "magic"

    fun create(
      testTask: Test,
      providers: ProviderFactory,
    ) {
      testTask.extensions.create(
        NAME, 
        TestMagicExtension::class.java, 
        providers,
      )
    }
  }

  fun shouldPracticeTheDarkArts(): Boolean {
    return providers
      .environmentVariable("DO_ANCIENT_MAGIC")
      .orElse(providers.environmentVariable("DO_SLIGHTLY_MORE_MODERN_MAGIC"))
      .map { it.isNotEmpty() }
      .getOrElse(false)
  }
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

و در افزونه ما، ما می توانیم این را به همه ما اضافه کنیم Test وظایف:

project.tasks.withType<Test>().configureEach { t ->
  TestMagicExtension.create(t, project.providers)
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

و اکنون می توانیم اسکریپت های ساخت خود را به روز کنیم:

// build.gradle.kts
import magic.TestMagicExtension

tasks.withType<Test>().configureEach {
  // the "extensions" call is on the Test instance,
  // not the project instance
  val magic = extensions.getByType(TestMagicExtension::class.java)
  if (magic.shouldPracticeTheDarkArts()) {
    logger.quiet("👻")
  }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

… که اصلا بهتر نیست!

Groovy: میان‌آهنگ

اول از همه، بیایید یک قدم به عقب برگردیم و به خود یادآوری کنیم که “ما عاشق کاتلین هستیم، ایمنی تایپ عالی است، من اهمیتی نمی دهم که عملکرد بدتر است…” می توانیم این را بارها و بارها در حین تکان دادن بگوییم. وضعیت جنین روی زمین تا زمانی که احساس بهتری داشته باشیم. اکنون، اینجا همان اسکریپت ساخت اما در Groovy است:

// build.gradle
tasks.withType(Test).configureEach {
  if (magic.shouldPracticeTheDarkArts()) {
    // I'm being cheeky by also omitting the 
    // "redundant" parentheses
    logger.quiet "👻"
  }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

Groovy قرار نیست بهتر باشد! لعنتی!

روی مقداری گلاب بپاشید

چگونه Gradle Kotlin DSL این کار را انجام می دهد؟ چرا «دسترسی‌های typesafe» را برای برنامه افزودنی آزمایشی من ایجاد نمی‌کند؟ خب، سوال دوم سوال خوبی است و من جوابی ندارم. اما برای اولین… بیایید فقط “تولید” (یعنی بنویسیم) لوازم جانبی typeafe خودمان را بسازیم!

ما تعدادی کد را در یک بسته جدید (به ما) اضافه می کنیم:

package org.gradle.kotlin.dsl

import magic.TestMagicExtension

public val Test.magic: TestMagicExtension
  get() = extensions.getByType(TestMagicExtension::class.java)

public fun Test.magic(configure: TestMagicExtension.() -> Unit) {
  configure(TestMagicExtension.NAME, configure)
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

و اکنون می توانیم خود را به روز کنیم Kotlin DSL ساخت اسکریپت:

// build.gradle.kts
tasks.withType<Test>().configureEach {
  if (magic.shouldPracticeTheDarkArts()) {
    logger.quiet("👻")
  }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در اینجا ما از این واقعیت استفاده می کنیم که Gradle به طور خودکار همه چیز را وارد می کند org.gradle.kotlin.dsl بسته به اسکریپت های ساخت، بنابراین همه آن توابع فقط آنجا هستند (در یک فضای نام جهانی، پس مراقب باشید!).

این یک الگوی معمولی است که خود Gradle از آن در افزونه test-retry-gradle-خود استفاده می کند. همچنین یک مشکل باز (از سال 2018) در ردیاب مشکل Gradle با یک درخواست ویژگی برای اجازه دادن به افزونه های سفارشی برای اضافه کردن واردات پیش فرض خود با استفاده از بسته های تقسیم شده مانند این وجود دارد.

اکنون برو و شاد باش، زیرا آن زمان از سال است.

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا