blob: 30f599cff516e967d1293d55b6284e8d9ee43032 [file] [log] [blame]
/*
* Copyright 2018 The Android Open Source Project
*
* 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
*
* http://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 androidx.build
import androidx.build.checkapi.shouldConfigureApiTasks
import androidx.build.transform.configureAarAsJarForConfiguration
import com.android.build.gradle.internal.crash.afterEvaluate
import groovy.lang.Closure
import org.gradle.api.GradleException
import org.gradle.api.Project
import org.gradle.api.provider.Property
import java.io.File
/**
* Extension for [AndroidXImplPlugin] that's responsible for holding configuration options.
*/
open class AndroidXExtension(val project: Project) {
@JvmField
val LibraryVersions: Map<String, Version>
@JvmField
val LibraryGroups: Map<String, LibraryGroup>
init {
val toml = project.objects.fileProperty().fileValue(
File(project.getSupportRootFolder(), "libraryversions.toml")
)
val content = project.providers.fileContents(toml)
val composeCustomVersion = project.providers.environmentVariable("COMPOSE_CUSTOM_VERSION")
val composeCustomGroup = project.providers.environmentVariable("COMPOSE_CUSTOM_GROUP")
val serviceProvider = project.gradle.sharedServices.registerIfAbsent(
"libraryVersionsService",
LibraryVersionsService::class.java
) { spec ->
spec.parameters.tomlFile = content.asText
spec.parameters.composeCustomVersion = composeCustomVersion
spec.parameters.composeCustomGroup = composeCustomGroup
}
LibraryGroups = serviceProvider.get().libraryGroups
LibraryVersions = serviceProvider.get().libraryVersions
}
var name: Property<String?> = project.objects.property(String::class.java)
fun setName(newName: String) { name.set(newName) }
var mavenVersion: Version? = null
set(value) {
field = value
chooseProjectVersion()
}
var mavenGroup: LibraryGroup? = null
set(value) {
field = value
chooseProjectVersion()
}
private val ALLOWED_EXTRA_PREFIXES = listOf("-alpha", "-beta", "-rc", "-dev", "-SNAPSHOT")
private fun chooseProjectVersion() {
val version: Version
val group: String? = mavenGroup?.group
val groupVersion: Version? = mavenGroup?.atomicGroupVersion
val mavenVersion: Version? = mavenVersion
if (mavenVersion != null) {
if (groupVersion != null && !isGroupVersionOverrideAllowed()) {
throw GradleException(
"Cannot set mavenVersion (" + mavenVersion +
") for a project (" + project +
") whose mavenGroup already specifies forcedVersion (" + groupVersion +
")"
)
} else {
verifyVersionExtraFormat(mavenVersion)
version = mavenVersion
}
} else {
if (groupVersion != null) {
verifyVersionExtraFormat(groupVersion)
version = groupVersion
} else {
return
}
}
if (group != null) {
project.group = group
}
project.version = if (isSnapshotBuild()) version.copy(extra = "-SNAPSHOT") else version
versionIsSet = true
}
private fun verifyVersionExtraFormat(version: Version) {
val extra = version.extra
if (extra != null) {
if (!version.isSnapshot() && project.isVersionExtraCheckEnabled()) {
if (ALLOWED_EXTRA_PREFIXES.any { extra.startsWith(it) }) {
for (potentialPrefix in ALLOWED_EXTRA_PREFIXES) {
if (extra.startsWith(potentialPrefix)) {
val secondExtraPart = extra.removePrefix(
potentialPrefix
)
if (secondExtraPart.toIntOrNull() == null) {
throw IllegalArgumentException(
"Version $version is not" +
" a properly formatted version, please ensure that " +
"$potentialPrefix is followed by a number only"
)
}
}
}
} else {
throw IllegalArgumentException(
"Version $version is not a proper " +
"version, version suffixes following major.minor.patch should " +
"be one of ${ALLOWED_EXTRA_PREFIXES.joinToString(", ")}"
)
}
}
}
}
private fun isGroupVersionOverrideAllowed(): Boolean {
// Grant an exception to the same-version-group policy for artifacts that haven't shipped a
// stable API surface, e.g. 1.0.0-alphaXX, to allow for rapid early-stage development.
val version = mavenVersion
return version != null && version.major == 1 && version.minor == 0 && version.patch == 0 &&
version.isAlpha()
}
private var versionIsSet = false
fun isVersionSet(): Boolean {
return versionIsSet
}
var description: String? = null
var inceptionYear: String? = null
/**
* targetsJavaConsumers = true, if project is intended to be accessed from Java-language
* source code.
*/
var targetsJavaConsumers = true
get() {
when (project.path) {
// add per-project overrides here
// for example
// the following project is intended to be accessed from Java
// ":compose:lint:internal-lint-checks" -> return true
// the following project is not intended to be accessed from Java
// ":annotation:annotation" -> return false
}
// TODO: rework this to use LibraryType. Fork Library and KolinOnlyLibrary?
if (project.path.contains("-ktx")) return false
if (project.path.contains("compose")) return false
if (project.path.startsWith(":ui")) return false
return field
}
private var licenses: MutableCollection<License> = ArrayList()
// Should only be used to override LibraryType.publish, if a library isn't ready to publish yet
var publish: Publish = Publish.UNSET
internal fun shouldPublish(): Boolean =
if (publish != Publish.UNSET) {
publish.shouldPublish()
} else if (type != LibraryType.UNSET) {
type.publish.shouldPublish()
} else {
false
}
internal fun shouldRelease(): Boolean =
if (publish != Publish.UNSET) {
publish.shouldRelease()
} else if (type != LibraryType.UNSET) {
type.publish.shouldRelease()
} else {
false
}
internal fun ifReleasing(action: () -> Unit) {
project.afterEvaluate {
if (shouldRelease()) {
action()
}
}
}
internal fun isPublishConfigured(): Boolean = (
publish != Publish.UNSET ||
type.publish != Publish.UNSET
)
/**
* Whether to run API tasks such as tracking and linting. The default value is
* [RunApiTasks.Auto], which automatically picks based on the project's properties.
*/
// TODO: decide whether we want to support overriding runApiTasks
// @Deprecated("Replaced with AndroidXExtension.type: LibraryType.runApiTasks")
var runApiTasks: RunApiTasks = RunApiTasks.Auto
get() = if (field == RunApiTasks.Auto && type != LibraryType.UNSET) type.checkApi else field
var type: LibraryType = LibraryType.UNSET
set(value) {
// don't disable multiplatform if it's already enabled, because sometimes it's enabled
// through flags and we don't want setting `type =` to disable it accidentally.
if (value.shouldEnableMultiplatform()) {
multiplatform = true
}
field = value
}
var failOnDeprecationWarnings = true
var legacyDisableKotlinStrictApiMode = false
var benchmarkRunAlsoInterpreted = false
var bypassCoordinateValidation = false
/**
* Whether this project uses KMP.
*/
private var multiplatform: Boolean = false
set(value) {
Multiplatform.setEnabledForProject(project, value)
field = value
}
fun shouldEnforceKotlinStrictApiMode(): Boolean {
return !legacyDisableKotlinStrictApiMode &&
shouldConfigureApiTasks()
}
fun license(closure: Closure<Any>): License {
val license = project.configure(License(), closure) as License
licenses.add(license)
return license
}
fun getLicenses(): Collection<License> {
return licenses
}
fun configureAarAsJarForConfiguration(name: String) {
configureAarAsJarForConfiguration(project, name)
}
companion object {
const val DEFAULT_UNSPECIFIED_VERSION = "unspecified"
}
}
class License {
var name: String? = null
var url: String? = null
}
private fun LibraryType.shouldEnableMultiplatform() = this is LibraryType.KmpLibrary