All Downloads are FREE. Search and download functionalities are using the official Maven repository.

foundry.gradle.FoundryProperties.kt Maven / Gradle / Ivy

/*
 * Copyright (C) 2022 Slack Technologies, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package foundry.gradle

import foundry.common.FoundryKeys
import foundry.gradle.anvil.AnvilMode
import foundry.gradle.artifacts.FoundryArtifact
import foundry.gradle.properties.PropertyResolver
import foundry.gradle.properties.getOrCreateExtra
import foundry.gradle.properties.sneakyNull
import java.io.File
import java.util.Locale
import org.gradle.api.Project
import org.gradle.api.file.RegularFile
import org.gradle.api.provider.Provider
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion

/**
 * (Mostly Gradle) properties for configuration of Foundry Gradle Plugin.
 *
 * Order attempted as described by [PropertyResolver.providerFor].
 */
// TODO allow sourcing from a custom resolver or Properties
public class FoundryProperties
internal constructor(
  private val projectName: String,
  private val resolver: PropertyResolver,
  private val regularFileProvider: (String) -> RegularFile,
  private val rootDirFileProvider: (String) -> RegularFile,
  internal val versions: FoundryVersions,
) {

  private fun presenceProperty(key: String): Boolean = optionalStringProperty(key) != null

  private fun fileProperty(key: String, useRoot: Boolean = false): File? =
    optionalStringProperty(key)
      ?.let(if (useRoot) rootDirFileProvider else regularFileProvider)
      ?.asFile

  private fun fileProvider(key: String, useRoot: Boolean = false): Provider =
    resolver
      .optionalStringProvider(key)
      .map(if (useRoot) rootDirFileProvider else regularFileProvider)

  private fun intProperty(key: String, defaultValue: Int = -1): Int =
    resolver.intValue(key, defaultValue = defaultValue)

  private fun booleanProperty(key: String, defaultValue: Boolean = false): Boolean =
    resolver.booleanValue(key, defaultValue = defaultValue)

  private fun stringProperty(key: String): String =
    optionalStringProperty(key)
      ?: error("No property for $key found and no default value was provided.")

  private fun stringProperty(key: String, defaultValue: String): String =
    optionalStringProperty(key, defaultValue)!!

  private fun optionalStringProperty(
    key: String,
    defaultValue: String? = null,
    blankIsNull: Boolean = false,
  ): String? =
    resolver.optionalStringValue(key, defaultValue = defaultValue)?.takeUnless {
      blankIsNull && it.isBlank()
    }

  /** Indicates that this android library project has variants. Flag-only, value is ignored. */
  public val libraryWithVariants: Boolean
    get() = booleanProperty("foundry.android.libraryWithVariants")

  /** Default namespace prefix for android projects if one isn't specified. */
  public val defaultNamespacePrefix: String
    get() = optionalStringProperty("foundry.android.defaultNamespacePrefix") ?: defaultPackagePrefix

  /** Default package prefix for JVM projects if one isn't specified. */
  public val defaultPackagePrefix: String
    get() = stringProperty(FoundryKeys.DEFAULT_PACKAGE_PREFIX)

  /**
   * Indicates that the gradle versions plugin should allow unstable versions. By default, unstable
   * versions are excluded due to the frequent androidx alpha/beta/rc cycle noise. Flag-only, value
   * is ignored.
   */
  public val versionsPluginAllowUnstable: Boolean
    get() = booleanProperty("foundry.versionsPlugin.allowUnstable")

  /** Opt-out flag to skip the androidx dependency check. Should only be used for debugging. */
  public val skipAndroidxCheck: Boolean
    get() = booleanProperty("foundry.android.skipAndroidXCheck")

  /** Version code used for debug APK outputs. */
  public val debugVersionCode: Int
    get() = intProperty("foundry.android.debugVersionCode", 90009999)

  /** User string used for debug APK outputs. */
  public val debugUserString: String
    get() = stringProperty("foundry.android.debugUserString", "debug")

  /** Opt-in flag to enable snapshots repos, used for the dependencies build shadow job. */
  public val enableSnapshots: Boolean
    get() = booleanProperty("foundry.gradle.enableSnapshots")

  /** Opt-in flag to enable mavenLocal repos, used for local testing. */
  public val enableMavenLocal: Boolean
    get() = booleanProperty("foundry.gradle.enableMavenLocal")

  /**
   * Flag to indicate that this project should have no api dependencies, such as if it's solely an
   * annotation processor.
   */
  public val rakeNoApi: Boolean
    get() = booleanProperty("foundry.rake.noapi")

  /**
   * Flag to enable the Gradle Dependency Analysis Plugin, which is disabled by default due to
   * https://github.com/autonomousapps/dependency-analysis-android-gradle-plugin/issues/204
   */
  public val enableAnalysisPlugin: Boolean
    get() = booleanProperty("foundry.enableAnalysisPlugin")

  /**
   * Flag to indicate this project should be exempted from platforms, usually platform projects
   * themselves.
   */
  public val noPlatform: Boolean
    get() = booleanProperty("foundry.config.noPlatform")

  /** Property corresponding to the supported languages in GA builds */
  public val supportedLanguages: String
    get() = stringProperty("foundry.android.supportedLanguages")

  /** Property corresponding to the supported languages in Internal builds */
  public val supportedLanguagesInternal: String
    get() = stringProperty("foundry.android.supportedLanguagesInternal")

  /** Property corresponding to the supported languages in Beta builds */
  public val supportedLanguagesBeta: String
    get() = stringProperty("foundry.android.supportedLanguagesBeta")

  /**
   * Property corresponding to the file path of a custom versions.json file for use with
   * dependencies shadow jobs.
   */
  public val versionsJson: File?
    get() = fileProperty("foundry.versionsJson", useRoot = true)

  /**
   * An alias name to a libs.versions.toml bundle for common Android Compose dependencies that
   * should be added to android projects with compose enabled
   */
  public val defaultComposeAndroidBundleAlias: String?
    get() = optionalStringProperty("foundry.compose.android.defaultBundleAlias")

  /**
   * Enables live literals. Note that they are disabled by default due to
   * https://issuetracker.google.com/issues/274207650 and
   * https://issuetracker.google.com/issues/274231394.
   */
  public val composeEnableLiveLiterals: Boolean
    get() = booleanProperty("foundry.compose.android.enableLiveLiterals", false)

  /**
   * Common compose compiler options.
   *
   * Format is a comma-separated list of key-value pairs, e.g. "key1=value1,key2=value2". Keys
   * should be the simple name of the compose compiler option, no prefixes needed.
   */
  public val composeCommonCompilerOptions: Provider>
    get() =
      resolver
        .providerFor("foundry.compose.commonCompilerOptions")
        .map { value -> value.split(",").map { it.trim() } }
        .orElse(emptyList())

  /** Relative path to a Compose stability configuration file from the _root_ project. */
  public val composeStabilityConfigurationPath: Provider
    get() =
      resolver.providerFor("foundry.compose.stabilityConfigurationPath").map(rootDirFileProvider)

  /**
   * Use a workaround for compose-compiler's `includeInformation` option on android projects.
   *
   * On android projects, the compose compiler gradle plugin annoyingly no-ops
   *
   * @see Upstream issue
   */
  public val composeUseIncludeInformationWorkaround: Boolean
    get() =
      resolver.booleanValue("foundry.compose.useIncludeInformationWorkaround", defaultValue = true)

  /**
   * By default, Compose on android only enables source information in debug variants. This is a bit
   * silly in large projects because we generally make all libraries single-variant as "release",
   * and can result in libraries not having source information. Instead, we rely on R8 to strip out
   * this information in release builds as needed.
   *
   * @see Upstream issue
   */
  public val composeIncludeSourceInformationEverywhereByDefault: Boolean
    get() =
      resolver.booleanValue(
        "foundry.compose.includeSourceInformationEverywhereByDefault",
        defaultValue = true,
      )

  /**
   * When this property is present, the "internalRelease" build variant will have an application id
   * of "com.Slack.prototype", instead of "com.Slack.internal".
   *
   * We build and distribute "prototype" builds that are equivalent to the "internalRelease" build
   * variants, except with a different application id so they can be installed side-by-side. To
   * avoid adding a new flavor & flavor dimension (or other somewhat hacky solutions like sharing
   * source sets), we swap the application id suffix at configuration time.
   */
  public val usePrototypeAppId: Boolean
    get() = presenceProperty("foundry.android.usePrototypeAppId")

  /**
   * Property corresponding to the SDK versions we test in Robolectric tests. Its value should be a
   * comma-separated list of SDK ints to download.
   */
  public val robolectricTestSdks: List
    get() =
      stringProperty("foundry.android.robolectric.testSdks")
        .splitToSequence(",")
        .map { it.toInt() }
        .toList()

  /** Opt out for -Werror. */
  public val allowWarnings: Provider
    get() = resolver.booleanProvider("foundry.kotlin.allowWarnings", defaultValue = false)

  /** Opt out for -Werror in tests. */
  public val allowWarningsInTests: Provider
    get() = resolver.booleanProvider("foundry.kotlin.allowWarningsInTests", defaultValue = false)

  /**
   * Anvil generator projects that should always be included when Anvil is enabled.
   *
   * This should be semicolon-delimited Gradle project paths.
   */
  public val anvilGeneratorProjects: String?
    get() = optionalStringProperty("foundry.anvil.generatorProjects")

  /**
   * Anvil runtime projects that should always be included when Anvil is enabled.
   *
   * This should be semicolon-delimited Gradle project paths.
   */
  public val anvilRuntimeProjects: String?
    get() = optionalStringProperty("foundry.anvil.runtimeProjects")

  /** Flag to enable use of the Anvil KSP fork. https://github.com/ZacSweers/anvil */
  public val anvilUseKspFork: Boolean
    get() = booleanProperty("foundry.anvil.useKspFork", defaultValue = false)

  /** Log Foundry extension configuration state verbosely. */
  public val foundryExtensionVerbose: Boolean
    get() = booleanProperty("foundry.extension.verbose")

  /**
   * Flag for Error-Prone auto-patching. Enable when running an auto-patch of EP, such as when it's
   * being introduced to a new module or upgrading EP itself.
   */
  public val errorProneAutoPatch: Boolean
    get() = booleanProperty("foundry.errorprone.autoPatch")

  /**
   * Error-Prone checks that should be considered errors.
   *
   * This should be colon-delimited string.
   *
   * Example: "AnnotationMirrorToString:AutoValueSubclassLeaked"
   */
  public val errorProneCheckNamesAsErrors: String?
    get() = optionalStringProperty("foundry.errorprone.checkNamesAsErrors")

  /**
   * Flag for Nullaway baselining. When enabled along with [errorProneAutoPatch], existing
   * nullability issues will be baselined with a `castToNonNull` call to wrap it.
   */
  public val nullawayBaseline: Boolean
    get() = booleanProperty("foundry.errorprone.nullaway.baseline")

  /**
   * Ndk version to use for android projects.
   *
   * Latest versions can be found at https://developer.android.com/ndk/downloads
   */
  public val ndkVersion: String?
    get() = optionalStringProperty("foundry.android.ndkVersion")

  /**
   * Enables verbose logging in miscellaneous places of SGP. This is intended to be a less noisy
   * alternative to running gradle with `--info` or `--debug`.
   */
  public val verboseLogging: Boolean
    get() = resolver.booleanValue("foundry.logging.verbose")

  /** Flag to enable verbose logging in unit tests. */
  public val testVerboseLogging: Boolean
    get() = booleanProperty("foundry.test.verboseLogging") || verboseLogging

  /**
   * Flag to enable kapt in tests. By default these are disabled due to this undesirable (but
   * surprisingly intented) behavior of running kapt + stub generation even if no processors are
   * present.
   *
   * See https://youtrack.jetbrains.com/issue/KT-29481#focus=Comments-27-4651462.0-0
   */
  public val enableKaptInTests: Boolean
    get() = booleanProperty("foundry.kapt.enabled-in-tests")

  /** Flag to enable errors only in lint checks. */
  public val lintErrorsOnly: Boolean
    get() = booleanProperty("foundry.lint.errors-only")

  /** File name to use for a project's lint baseline. */
  public val lintBaselineFileName: String?
    get() = optionalStringProperty("foundry.lint.baseline-file-name", blankIsNull = true)

  /** Flag to control whether or not lint checks test sources. */
  public val lintCheckTestSources: Boolean
    get() = booleanProperty("foundry.lint.checkTestSources", true)

  /** Flag to control whether or not lint checks ignores test sources. */
  public val lintIgnoreTestSources: Boolean
    get() = booleanProperty("foundry.lint.ignoreTestSources", false)

  /**
   * Flag to control which agp version should be used for lint. Optional. Value should be a version
   * key in `libs.versions.toml`,
   */
  public val lintVersionOverride: String?
    get() = optionalStringProperty("foundry.lint.agpVersion")

  /**
   * Flag to indicate whether this project is a test library (such as test utils, test fixtures,
   * etc).
   */
  public val isTestLibrary: Boolean
    get() = booleanProperty("foundry.isTestLibrary", false) || projectName == "test-fixtures"

  /**
   * At the time of writing, AGP does not support running lint on `com.android.test` projects. This
   * is a flag to eventually support this in the future.
   *
   * https://issuetracker.google.com/issues/208765813
   */
  public val enableLintInAndroidTestProjects: Boolean
    get() = booleanProperty("foundry.lint.enableOnAndroidTestProjects", false)

  /** Flag to enable/disable KSP. */
  public val enableKsp: Boolean
    get() = booleanProperty("foundry.ksp.enable")

  /** Flag to enable/disable Moshi-IR. */
  public val enableMoshiIr: Boolean
    get() = booleanProperty("foundry.moshi.ir.enable")

  /** Flag to enable/disable moshi proguard rule gen. */
  public val moshixGenerateProguardRules: Boolean
    get() = booleanProperty("moshix.generateProguardRules", defaultValue = true)

  /** Flag to connect SqlDelight sources to KSP. */
  public val kspConnectSqlDelight: Boolean
    get() = booleanProperty("foundry.ksp.connect.sqldelight")

  /** Flag to connect ViewBinding sources to KSP. */
  public val kspConnectViewBinding: Boolean
    get() = booleanProperty("foundry.ksp.connect.viewbinding")

  /** Variants that should be disabled in a given subproject. */
  public val disabledVariants: String?
    get() = optionalStringProperty("foundry.android.disabledVariants")

  /**
   * The project-specific kotlin.daemon.jvmargs computed by bootstrap.
   *
   * We don't just blanket use `kotlin.daemon.jvmargs` alone because we don't want to pollute other
   * projects.
   */
  public val kotlinDaemonArgs: List?
    get() =
      optionalStringProperty(
          KOTLIN_DAEMON_ARGS_KEY,
          defaultValue = optionalStringProperty(KOTLIN_DAEMON_ARGS_KEY_OLD, defaultValue = null),
        )
        ?.split(" ")
        ?.map(String::trim)
        ?.filterNot(String::isBlank)
        ?.takeUnless(List<*>::isEmpty)

  /**
   * Flag to enable ciUnitTest on this project. Default is true.
   *
   * When enabled, a task named "ciUnitTest" will be created in this project, which will depend on
   * the unit test task for a single build variant (e.g. "testReleaseUnitTest").
   */
  public val ciUnitTestEnabled: Boolean
    get() = booleanProperty("foundry.ci-unit-test.enable", defaultValue = true)

  /** CI unit test variant (Android only). Defaults to `release`. */
  public val ciUnitTestVariant: String
    get() = stringProperty("foundry.ci-unit-test.variant", "release")

  /** If enabled, applies the kotlinx-kover plugin to projects using ciUnitTest. */
  public val ciUnitTestEnableKover: Boolean
    get() = booleanProperty("foundry.ci-unit-test.enableKover", false)

  /**
   * Parallelism multiplier to use for unit tests. This should be a float value that is multiplied
   * by the number of cores. The value can be a fraction. Default is 0.5.
   */
  public val unitTestParallelismMultiplier: Float
    get() {
      val rawValue = stringProperty("foundry.unit-test.parallelismMultiplier", "0.5")
      val floatValue = rawValue.toFloatOrNull()
      require(floatValue != null && floatValue > 0) {
        "Invalid value for foundry.unit-test.parallelismMultiplier: '$rawValue'"
      }
      return floatValue
    }

  /** Controls how often to fork the JVM in unit tests. Default is 1000. */
  public val unitTestForkEvery: Long
    get() = intProperty("foundry.unit-test.forkEvery", 1000).toLong()

  /**
   * Flag to enable ciLint on a project. Default is true.
   *
   * When enabled, a task named "ciLint" will be created in this project, which will depend on the
   * all the lint tasks in the project.
   */
  public val ciLintEnabled: Boolean
    get() = booleanProperty("foundry.ci-lint.enable", defaultValue = true)

  /**
   * Comma-separated list of CI lint variants to run (Android only). Default when unspecified will
   * lint all variants.
   */
  public val ciLintVariants: String?
    get() = optionalStringProperty("foundry.ci-lint.variants")

  /** Flag for enabling test orchestrator. */
  public val useOrchestrator: Boolean
    get() = booleanProperty("foundry.android.test.orchestrator")

  /**
   * Location for robolectric-core to be referenced by app. Temporary till we have a better solution
   * for "always add these" type of deps.
   *
   * Should be `:path:to:robolectric-core` format
   */
  public val robolectricCoreProject: String?
    get() = optionalStringProperty("foundry.location.robolectric-core")

  /**
   * Gradle path to a platform project to be referenced by other projects.
   *
   * Should be `:path:to:foundry-platform` format
   *
   * @see Platforms
   */
  public val platformProjectPath: String?
    get() = optionalStringProperty("foundry.location.foundry-platform")

  /**
   * Optional file location for an `affected_projects.txt` file that contains a list of projects
   * affected in this build.
   */
  public val affectedProjects: File?
    get() = fileProperty("foundry.avoidance.affectedProjectsFile", useRoot = true)

  /* Controls for Java/JVM/JDK versions uses in compilations and execution of tests. */

  /** Flag to enable strict JDK mode, forcing some things like JAVA_HOME. */
  public val strictJdk: Boolean
    get() = booleanProperty("foundryToolchainsStrict", defaultValue = true)

  /** Android cache fix plugin. */
  public val enableAndroidCacheFix: Boolean = booleanProperty("foundry.plugins.android-cache-fix")

  /**
   * Optional override for buildToolsVersion in Android projects. Sometimes temporarily necessary to
   * pick up new fixes.
   */
  public val buildToolsVersionOverride: String? =
    optionalStringProperty("foundry.android.buildToolsVersionOverride")

  /**
   * Performance optimization to relocate the entire project build directory to a location outside
   * the IDE's view. This prevents the IDE from tracking these files and improves IDE performance.
   */
  public val relocateBuildDir: Boolean = betaFeature("foundry.perf.relocateBuildDir")

  /** Opt-in for beta SGP features. */
  public val enableBetaFeatures: Boolean = booleanProperty("foundry.beta", defaultValue = false)

  /**
   * Shorthand helper for checking features that are in beta or falling back to their specific flag.
   */
  private fun betaFeature(key: String): Boolean {
    return enableBetaFeatures || booleanProperty(key, defaultValue = false)
  }

  /* Controls for auto-applied plugins. */
  public val autoApplyTestRetry: Boolean
    get() = booleanProperty("foundry.auto-apply.test-retry", defaultValue = true)

  public val autoApplySpotless: Boolean
    get() = booleanProperty("foundry.auto-apply.spotless", defaultValue = true)

  public val autoApplyDetekt: Boolean
    get() = booleanProperty("foundry.auto-apply.detekt", defaultValue = true)

  public val autoApplyNullaway: Boolean
    get() = booleanProperty("foundry.auto-apply.nullaway", defaultValue = true)

  public val autoApplyCacheFix: Boolean
    get() = booleanProperty("foundry.auto-apply.cache-fix", defaultValue = true)

  public val autoApplySortDependencies: Boolean
    get() = booleanProperty("foundry.auto-apply.sort-dependencies", defaultValue = true)

  /* Test retry controls. */
  public enum class TestRetryPluginType {
    RETRY_PLUGIN,
    GE,
  }

  public val testRetryPluginType: TestRetryPluginType
    get() =
      stringProperty("foundry.test.retry.pluginType", TestRetryPluginType.RETRY_PLUGIN.name)
        .let(TestRetryPluginType::valueOf)

  public val testRetryFailOnPassedAfterRetry: Provider
    get() =
      resolver.booleanProvider("foundry.test.retry.failOnPassedAfterRetry", defaultValue = false)

  public val testRetryMaxFailures: Provider
    get() = resolver.intProvider("foundry.test.retry.maxFailures", defaultValue = 20)

  public val testRetryMaxRetries: Provider
    get() = resolver.intProvider("foundry.test.retry.maxRetries", defaultValue = 1)

  /* Detekt configs. */
  /** Detekt config files, evaluated from rootProject.file(...). */
  public val detektConfigs: List?
    get() = optionalStringProperty("foundry.detekt.configs")?.split(",")

  /** Detekt baseline file, evaluated from project.layout.projectDirectory.file(...). */
  public val detektBaselineFileName: String?
    get() = optionalStringProperty("foundry.detekt.baseline-file-name", blankIsNull = true)

  /** Enables full detekt mode (with type resolution). Off by default due to performance issues. */
  public val enableFullDetekt: Boolean
    get() = booleanProperty("foundry.detekt.full")

  /** Comma-separated set of projects to ignore in sorting dependencies. */
  public val sortDependenciesIgnore: String?
    get() = optionalStringProperty("foundry.sortDependencies.ignore")

  /** Enables verbose debug logging across the plugin. */
  public val debug: Boolean
    get() = booleanProperty("foundry.debug", defaultValue = false)

  /** A comma-separated list of configurations to use in affected project detection. */
  public val affectedProjectConfigurations: String?
    get() = optionalStringProperty("foundry.avoidance.affected-project-configurations")

  /**
   * Flag to, when true, makes [affectedProjectConfigurations] build upon the defaults rather than
   * replace them.
   */
  public val buildUponDefaultAffectedProjectConfigurations: Boolean
    get() = booleanProperty("foundry.avoidance.build-upon-default-affected-project-configurations")

  /**
   * Global control for enabling stricter validation of projects, such as ensuring Kotlin projects
   * have at least one `.kt` source file.
   *
   * Note that these are expected to be slow and not used anywhere outside of debugging or CI.
   *
   * Granular controls should depend on this check + include their own opt-out check as-needed.
   */
  public val strictMode: Boolean
    get() = booleanProperty("foundry.strict", defaultValue = false)

  /** Specific toggle for validating manifests in androidTest sources. */
  public val strictValidateAndroidTestManifest: Boolean
    get() =
      booleanProperty("foundry.android.strict.validateAndroidTestManifests", defaultValue = true)

  /**
   * Always enables resources in android unit tests. Only present for benchmarking purposes and
   * should otherwise be off.
   */
  public val alwaysEnableResourcesInTests: Boolean
    get() = booleanProperty("foundry.android.test.alwaysEnableResources", defaultValue = false)

  /** Global toggle to enable bugsnag. Note this still respects variant filters. */
  public val bugsnagEnabled: Provider
    get() = resolver.booleanProvider("foundry.android.bugsnag.enabled")

  /** Branch pattern for git branches Bugsnag should be enabled on. */
  public val bugsnagEnabledBranchPattern: Provider
    get() = resolver.optionalStringProvider("foundry.android.bugsnag.enabledBranchPattern")

  /** Global boolean that controls whether mod score is enabled on this project. */
  public val modScoreGlobalEnabled: Boolean
    get() = resolver.booleanValue("foundry.modscore.enabled")

  /**
   * Per-project boolean that allows for excluding this project from mod score.
   *
   * Note this should only be applied to projects that cannot be depended on.
   */
  public val modScoreIgnore: Boolean
    get() = resolver.booleanValue("foundry.modscore.ignore")

  /** Experimental flag to enable logging thermal throttling on macOS devices. */
  public val logThermals: Boolean
    get() = resolver.booleanValue("foundry.logging.thermals", defaultValue = false)

  /**
   * Enables eager configuration of [FoundryArtifact] publishing in subprojects. This is behind a
   * flag as a failsafe while we try different approaches to allow lenient resolution.
   *
   * @see StandardProjectConfigurations.setUpSubprojectArtifactPublishing
   */
  public val eagerlyConfigureArtifactPublishing: Boolean
    get() = resolver.booleanValue("foundry.artifacts.configure-eagerly", defaultValue = false)

  /**
   * Force-disables Anvil regardless of `FoundryExtension.dagger()` settings, useful for K2 testing
   * where Anvil is unsupported.
   */
  public val disableAnvilForK2Testing: Boolean
    get() = resolver.booleanValue("foundry.anvil.forceDisable", defaultValue = false)

  /**
   * Defines the [AnvilMode] to use with this compilation. See the docs on that class for more
   * details.
   */
  public val anvilMode: AnvilMode
    get() =
      resolver.stringValue("foundry.anvil.mode", defaultValue = AnvilMode.K1_EMBEDDED.name).let {
        AnvilMode.valueOf(it.uppercase(Locale.US))
      }

  /** Overrides the kotlin language version if present. */
  public val kaptLanguageVersion: Provider
    get() =
      resolver.optionalStringProvider("foundry.kapt.languageVersion").map {
        KotlinVersion.fromVersion(it)
      }

  /** Defines a required vendor for JDK toolchains. */
  public val jvmVendor: Provider
    get() =
      resolver.optionalStringProvider("foundry.jvm.vendor").map {
        if (jvmVendorOptOut) {
          sneakyNull()
        } else {
          it
        }
      }

  /** Flag to disable JVM vendor setting locally. */
  public val jvmVendorOptOut: Boolean
    get() = booleanProperty("foundry.jvm.vendor.optOut", defaultValue = false)

  /** Optional link to JDK configuration */
  public val jdkDocsLink: String?
    get() = optionalStringProperty("foundry.jdk.docsLink")

  /** Optional error message to show when the JDK configuration is invalid. */
  public val jdkErrorMessage: String?
    get() = optionalStringProperty("foundry.jdk.errorMessage")

  /**
   * Option to force a specific kotlin language version. By default defers to the KGP default the
   * build is running with.
   */
  public val kotlinLanguageVersionOverride: Provider
    get() = resolver.optionalStringProvider("foundry.kotlin.languageVersionOverride")

  /**
   * Free compiler arguments to pass to Kotlin's `freeCompilerArgs` property in all compilations.
   * Should not include opt-in arguments or `-progressive`.
   */
  public val kotlinFreeArgs: Provider>
    get() =
      resolver
        .optionalStringProvider("foundry.kotlin.freeArgs")
        .map { it.split(',') }
        // Super important to default if absent due to
        // https://docs.gradle.org/8.7/release-notes.html#build-authoring-improvements
        .orElse(emptyList())

  /**
   * Free compiler arguments to pass to Kotlin's `freeCompilerArgs` property in JVM compilations.
   * Should not include opt-in arguments or `-progressive`.
   */
  public val kotlinJvmFreeArgs: Provider>
    get() =
      resolver
        .optionalStringProvider("foundry.kotlin.jvmFreeArgs")
        .map { it.split(',') }
        // Super important to default if absent due to
        // https://docs.gradle.org/8.7/release-notes.html#build-authoring-improvements
        .orElse(emptyList())

  /** Opt-in annotations to pass to Kotlin's `optIn` property. */
  public val kotlinOptIn: Provider>
    get() =
      resolver
        .optionalStringProvider("foundry.kotlin.optIns")
        .map { it.split(',') }
        // Super important to default if absent due to
        // https://docs.gradle.org/8.7/release-notes.html#build-authoring-improvements
        .orElse(emptyList())

  /** Default for Kotlin's `progressive` mode. Defaults to enabled. */
  public val kotlinProgressive: Provider
    get() = resolver.booleanProvider("foundry.kotlin.progressive", defaultValue = true)

  /** Property to enable auto-fixing in topography validation. */
  public val topographyAutoFix: Provider
    get() = resolver.booleanProvider("foundry.topography.validation.autoFix", defaultValue = false)

  /**
   * Property pointing at a features config JSON file for
   * [foundry.gradle.topography.ModuleFeaturesConfig].
   */
  public val topographyFeaturesConfig: Provider
    get() = fileProvider("foundry.topography.features.config", useRoot = true)

  internal fun requireAndroidSdkProperties(): AndroidSdkProperties {
    val compileSdk = compileSdkVersion ?: error("foundry.android.compileSdkVersion not set")
    val minSdk = minSdkVersion?.toInt() ?: error("foundry.android.minSdkVersion not set")
    val targetSdk = targetSdkVersion?.toInt() ?: error("foundry.android.targetSdkVersion not set")
    return AndroidSdkProperties(compileSdk, minSdk, targetSdk)
  }

  internal data class AndroidSdkProperties(
    val compileSdk: String,
    val minSdk: Int,
    val targetSdk: Int,
  )

  public val compileSdkVersion: String?
    get() = optionalStringProperty("foundry.android.compileSdkVersion")

  public fun latestCompileSdkWithSources(defaultValue: Int): Int =
    intProperty("foundry.android.latestCompileSdkWithSources", defaultValue = defaultValue)

  private val minSdkVersion: String?
    get() = optionalStringProperty("foundry.android.minSdkVersion")

  private val targetSdkVersion: String?
    get() = optionalStringProperty("foundry.android.targetSdkVersion")

  public companion object {
    /**
     * The project-specific kotlin.daemon.jvmargs computed by bootstrap.
     *
     * We don't just blanket use `kotlin.daemon.jvmargs` alone because we don't want to pollute
     * other projects.
     */
    public const val KOTLIN_DAEMON_ARGS_KEY: String = "foundry.kotlin.daemon.jvmargs"
    public const val KOTLIN_DAEMON_ARGS_KEY_OLD: String = "slack.kotlin.daemon.jvmargs"

    /** Minimum xmx value for the Gradle daemon. Value is an integer and unit is gigabytes. */
    // Key-only because it's used in a task init without a project instance
    public const val MIN_GRADLE_XMX: String = "foundry.bootstrap.minGradleXmx"

    /** Minimum xms value for the Gradle daemon. Value is an integer and unit is gigabytes. */
    // Key-only because it's used in a task init without a project instance
    public const val MIN_GRADLE_XMS: String = "foundry.bootstrap.minGradleXms"

    internal const val CACHED_PROVIDER_EXT_NAME = "foundry.properties.provider"

    public operator fun invoke(project: Project): FoundryProperties = project.foundryProperties

    public fun getOrCreateRoot(
      project: Project,
      startParameterProperty: (String) -> Provider,
      globalLocalProperty: (String) -> Provider,
    ): FoundryProperties {
      check(project.isRootProject) { "getOrCreate can only run in the root project!" }
      return project.getOrCreateExtra(CACHED_PROVIDER_EXT_NAME) { p ->
        val resolver =
          PropertyResolver.createForRootProject(
            p,
            startParameterProperty = startParameterProperty,
            globalLocalProperty = globalLocalProperty,
          )
        val versions = FoundryVersions(p.getVersionsCatalog())
        create(p, resolver, versions)
      }
    }

    public fun getOrCreate(
      project: Project,
      foundryTools: Provider = project.foundryToolsProvider(),
    ): FoundryProperties {
      return project.getOrCreateExtra(CACHED_PROVIDER_EXT_NAME) { p ->
        val globalProperties = foundryTools.get().globalConfig.globalFoundryProperties
        val resolver = PropertyResolver(project, globalResolver = globalProperties.resolver)
        val versions = globalProperties.versions
        create(p, resolver, versions)
      }
    }

    private fun create(
      project: Project,
      resolver: PropertyResolver,
      versions: FoundryVersions,
    ): FoundryProperties {
      return FoundryProperties(
        projectName = project.name,
        resolver = resolver,
        regularFileProvider = project.layout.projectDirectory::file,
        rootDirFileProvider = project.rootProject.layout.projectDirectory::file,
        versions = versions,
      )
    }
  }
}

public val Project.foundryProperties: FoundryProperties
  get() = FoundryProperties.getOrCreate(project)




© 2015 - 2024 Weber Informatics LLC | Privacy Policy