blob: a2eabbdc7aa032bd328ddd8fee48c6dc72d15051 [file] [log] [blame]
/*
* Copyright (C) 2012 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 com.android.build.gradle.internal.dsl
import com.android.build.api.dsl.ApplicationBuildType
import com.android.build.api.dsl.DynamicFeatureBuildType
import com.android.build.api.dsl.LibraryBuildType
import com.android.build.api.dsl.Ndk
import com.android.build.api.dsl.Shaders
import com.android.build.api.dsl.TestBuildType
import com.android.build.gradle.internal.errors.DeprecationReporter
import com.android.build.gradle.internal.services.DslServices
import com.android.builder.core.AbstractBuildType
import com.android.builder.core.BuilderConstants
import com.android.builder.errors.IssueReporter
import com.android.builder.internal.ClassFieldImpl
import com.android.builder.model.BaseConfig
import com.android.builder.model.CodeShrinker
import com.google.common.collect.Iterables
import org.gradle.api.Action
import org.gradle.api.Incubating
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Internal
import java.io.File
import java.io.Serializable
import javax.inject.Inject
/** DSL object to configure build types. */
abstract class BuildType @Inject constructor(
private val name: String,
private val dslServices: DslServices
) :
AbstractBuildType(), CoreBuildType, Serializable,
ApplicationBuildType<SigningConfig>,
LibraryBuildType<SigningConfig>,
DynamicFeatureBuildType,
TestBuildType<SigningConfig> {
/**
* Name of this build type.
*/
override fun getName(): String {
return name
}
override var isDebuggable: Boolean = false
get() = // Accessing coverage data requires a debuggable package.
field || isTestCoverageEnabled
override var isTestCoverageEnabled: Boolean = false
override var isPseudoLocalesEnabled: Boolean = false
override var isJniDebuggable: Boolean = false
override var isRenderscriptDebuggable: Boolean = false
override var renderscriptOptimLevel = 3
@Deprecated("This property is deprecated. Changing its value has no effect.")
override var isZipAlignEnabled: Boolean = true
/**
* Whether to enable the checks that the both the old and new way of configuring
* bytecode postProcessing are not used at the same time.
*
* The checks are disabled during [.initWith].
*/
private var dslChecksEnabled = true
/**
* Describes how code postProcessing is configured. We don't allow mixing the old and new DSLs.
*/
enum class PostProcessingConfiguration {
POSTPROCESSING_BLOCK, OLD_DSL
}
override val ndkConfig: NdkOptions = dslServices.newInstance(NdkOptions::class.java)
override val externalNativeBuild: ExternalNativeBuildOptions =
dslServices.newInstance(ExternalNativeBuildOptions::class.java, dslServices)
override val externalNativeBuildOptions: ExternalNativeBuildOptions
get() {
return this.externalNativeBuild
}
private val _postProcessing: PostProcessingBlock = dslServices.newInstance(
PostProcessingBlock::class.java,
dslServices
)
private var _postProcessingConfiguration: PostProcessingConfiguration? = null
private var postProcessingDslMethodUsed: String? = null
private var _shrinkResources = false
/*
* (Non javadoc): Whether png crunching should be enabled if not explicitly overridden.
*
* Can be removed once the AaptOptions crunch method is removed.
*/
override var isCrunchPngsDefault = true
// FIXME remove: b/149431538
@Suppress("DEPRECATION")
private val _isDefaultProperty: Property<Boolean> =
dslServices.property(Boolean::class.java).convention(false)
override val matchingFallbacks: MutableList<String> = mutableListOf()
fun setMatchingFallbacks(fallbacks: List<String>) {
val newFallbacks = ArrayList(fallbacks)
matchingFallbacks.clear()
matchingFallbacks.addAll(newFallbacks)
}
fun setMatchingFallbacks(vararg fallbacks: String) {
matchingFallbacks.clear()
for (fallback in fallbacks) {
matchingFallbacks.add(fallback)
}
}
fun setMatchingFallbacks(fallback: String) {
matchingFallbacks.clear()
matchingFallbacks.add(fallback)
}
override val javaCompileOptions: JavaCompileOptions =
dslServices.newInstance(JavaCompileOptions::class.java, dslServices)
override fun javaCompileOptions(action: com.android.build.api.dsl.JavaCompileOptions.() -> Unit) {
action.invoke(javaCompileOptions)
}
override val shaders: ShaderOptions = dslServices.newInstance(ShaderOptions::class.java)
override val aarMetadata: AarMetadata = dslServices.newInstance(AarMetadata::class.java)
override fun aarMetadata(action: com.android.build.api.dsl.AarMetadata.() -> Unit) {
action.invoke(aarMetadata)
}
/**
* Initialize the DSL object with the debug signingConfig. Not meant to be used from the build
* scripts.
*/
fun init(debugSigningConfig: SigningConfig?) {
init()
if (BuilderConstants.DEBUG == name) {
assert(debugSigningConfig != null)
setSigningConfig(debugSigningConfig)
}
}
/**
* Initialize the DSL object without the signingConfig. Not meant to be used from the build
* scripts.
*/
fun init() {
if (BuilderConstants.DEBUG == name) {
setDebuggable(true)
isEmbedMicroApp = false
isCrunchPngsDefault = false
}
}
/** The signing configuration. e.g.: `signingConfig signingConfigs.myConfig` */
override var signingConfig: SigningConfig? = null
override fun setSigningConfig(signingConfig: com.android.builder.model.SigningConfig?): com.android.builder.model.BuildType {
this.signingConfig = signingConfig as SigningConfig?
return this
}
fun setSigningConfig(signingConfig: Any?) {
this.signingConfig = signingConfig as SigningConfig?
}
override var isEmbedMicroApp: Boolean = true
override fun getIsDefault(): Property<Boolean> {
return _isDefaultProperty
}
override var isDefault: Boolean
get() = _isDefaultProperty.get()
set(value) {
_isDefaultProperty.set(value)
}
fun setIsDefault(isDefault: Boolean) {
this.isDefault = isDefault
}
override fun _initWith(that: BaseConfig) {
super._initWith(that)
val thatBuildType =
that as BuildType
ndkConfig._initWith(thatBuildType.ndkConfig)
javaCompileOptions.annotationProcessorOptions._initWith(
thatBuildType.javaCompileOptions.annotationProcessorOptions
)
_shrinkResources = thatBuildType._shrinkResources
shaders._initWith(thatBuildType.shaders)
externalNativeBuildOptions._initWith(thatBuildType.externalNativeBuildOptions)
_postProcessing.initWith(that.postprocessing)
isCrunchPngs = thatBuildType.isCrunchPngs
isCrunchPngsDefault = thatBuildType.isCrunchPngsDefault
setMatchingFallbacks(thatBuildType.matchingFallbacks)
// we don't want to dynamically link these values. We just want to copy the current value.
isDefault = thatBuildType.isDefault
aarMetadata.minCompileSdk = thatBuildType.aarMetadata.minCompileSdk
}
/** Override as DSL objects have no reason to be compared for equality. */
override fun hashCode(): Int {
return System.identityHashCode(this)
}
/** Override as DSL objects have no reason to be compared for equality. */
override fun equals(o: Any?): Boolean {
return this === o
}
// -- DSL Methods. TODO remove once the instantiator does what I expect it to do.
override fun buildConfigField(
type: String,
name: String,
value: String
) {
val alreadyPresent = buildConfigFields[name]
if (alreadyPresent != null) {
val message = String.format(
"BuildType(%s): buildConfigField '%s' value is being replaced: %s -> %s",
getName(), name, alreadyPresent.value, value
)
dslServices.issueReporter.reportWarning(
IssueReporter.Type.GENERIC,
message
)
}
addBuildConfigField(ClassFieldImpl(type, name, value))
}
override fun resValue(
type: String,
name: String,
value: String
) {
val alreadyPresent = resValues[name]
if (alreadyPresent != null) {
val message = String.format(
"BuildType(%s): resValue '%s' value is being replaced: %s -> %s",
getName(), name, alreadyPresent.value, value
)
dslServices.issueReporter.reportWarning(
IssueReporter.Type.GENERIC,
message
)
}
addResValue(ClassFieldImpl(type, name, value))
}
override var proguardFiles: MutableList<File>
get() = super.proguardFiles
set(value) {
// Override to handle the proguardFiles = ['string'] case (see PluginDslTest.testProguardFiles_*)
setProguardFiles(value)
}
override fun proguardFile(proguardFile: Any): BuildType {
checkPostProcessingConfiguration(PostProcessingConfiguration.OLD_DSL, "proguardFile")
proguardFiles.add(dslServices.file(proguardFile))
return this
}
override fun proguardFiles(vararg files: Any): BuildType {
checkPostProcessingConfiguration(PostProcessingConfiguration.OLD_DSL, "proguardFiles")
for (file in files) {
proguardFile(file)
}
return this
}
/**
* Sets the ProGuard configuration files.
*
*
* There are 2 default rules files
*
* * proguard-android.txt
* * proguard-android-optimize.txt
*
*
* They are located in the SDK. Using `getDefaultProguardFile(String filename)` will return the
* full path to the files. They are identical except for enabling optimizations.
*/
fun setProguardFiles(proguardFileIterable: Iterable<*>): BuildType {
checkPostProcessingConfiguration(PostProcessingConfiguration.OLD_DSL, "setProguardFiles")
proguardFiles.clear()
proguardFiles(
*Iterables.toArray(
proguardFileIterable,
Any::class.java
)
)
return this
}
override val testProguardFiles: MutableList<File>
get() = super.testProguardFiles
override fun testProguardFile(proguardFile: Any): BuildType {
checkPostProcessingConfiguration(PostProcessingConfiguration.OLD_DSL, "testProguardFile")
testProguardFiles.add(dslServices.file(proguardFile))
return this
}
override fun testProguardFiles(vararg proguardFiles: Any): BuildType {
checkPostProcessingConfiguration(PostProcessingConfiguration.OLD_DSL, "testProguardFiles")
for (proguardFile in proguardFiles) {
testProguardFile(proguardFile)
}
return this
}
/**
* Specifies proguard rule files to be used when processing test code.
*
*
* Test code needs to be processed to apply the same obfuscation as was done to main code.
*/
fun setTestProguardFiles(files: Iterable<*>): BuildType {
checkPostProcessingConfiguration(
PostProcessingConfiguration.OLD_DSL, "setTestProguardFiles"
)
testProguardFiles.clear()
testProguardFiles(
*Iterables.toArray(
files,
Any::class.java
)
)
return this
}
override val consumerProguardFiles: MutableList<File>
get() = super.consumerProguardFiles
override fun consumerProguardFile(proguardFile: Any): BuildType {
checkPostProcessingConfiguration(
PostProcessingConfiguration.OLD_DSL, "consumerProguardFile"
)
consumerProguardFiles.add(dslServices.file(proguardFile))
return this
}
override fun consumerProguardFiles(vararg proguardFiles: Any): BuildType {
checkPostProcessingConfiguration(
PostProcessingConfiguration.OLD_DSL, "consumerProguardFiles"
)
for (proguardFile in proguardFiles) {
consumerProguardFile(proguardFile)
}
return this
}
/**
* Specifies a proguard rule file to be included in the published AAR.
*
*
* This proguard rule file will then be used by any application project that consume the AAR
* (if proguard is enabled).
*
*
* This allows AAR to specify shrinking or obfuscation exclude rules.
*
*
* This is only valid for Library project. This is ignored in Application project.
*/
fun setConsumerProguardFiles(proguardFileIterable: Iterable<*>): BuildType {
checkPostProcessingConfiguration(
PostProcessingConfiguration.OLD_DSL, "setConsumerProguardFiles"
)
consumerProguardFiles.clear()
consumerProguardFiles(
*Iterables.toArray(
proguardFileIterable,
Any::class.java
)
)
return this
}
fun ndk(action: Action<NdkOptions>) {
action.execute(ndkConfig)
}
override val ndk: Ndk
get() = ndkConfig
override fun ndk(action: Ndk.() -> Unit) {
action.invoke(ndk)
}
/**
* Configure native build options.
*/
fun externalNativeBuild(action: Action<ExternalNativeBuildOptions>): ExternalNativeBuildOptions {
action.execute(externalNativeBuildOptions)
return externalNativeBuildOptions
}
override fun externalNativeBuild(action: com.android.build.api.dsl.ExternalNativeBuildOptions.() -> Unit) {
action.invoke(externalNativeBuild)
}
/**
* Configure shader compiler options for this build type.
*/
fun shaders(action: Action<ShaderOptions>) {
action.execute(shaders)
}
override fun shaders(action: Shaders.() -> Unit) {
action.invoke(shaders)
}
override var isMinifyEnabled: Boolean = false
get() =
// Try to return a sensible value for the model and other Gradle plugins inspecting the DSL.
if (_postProcessingConfiguration != PostProcessingConfiguration.POSTPROCESSING_BLOCK) {
field
} else {
(_postProcessing.isRemoveUnusedCode ||
_postProcessing.isObfuscate ||
_postProcessing.isOptimizeCode)
}
set(value) {
checkPostProcessingConfiguration(
PostProcessingConfiguration.OLD_DSL,
"setMinifyEnabled"
)
field = value
}
/**
* Whether shrinking of unused resources is enabled.
*
* Default is false;
*/
override var isShrinkResources: Boolean
get() =
// Try to return a sensible value for the model and other Gradle plugins inspecting the DSL.
if (_postProcessingConfiguration != PostProcessingConfiguration.POSTPROCESSING_BLOCK) {
_shrinkResources
} else {
_postProcessing.isRemoveUnusedResources
}
set(value) {
checkPostProcessingConfiguration(
PostProcessingConfiguration.OLD_DSL,
"setShrinkResources"
)
this._shrinkResources = value
}
/**
* Specifies whether to always use ProGuard for code and resource shrinking.
*
* By default, when you enable code shrinking by setting
* [`minifyEnabled`](com.android.build.gradle.internal.dsl.BuildType.html#com.android.build.gradle.internal.dsl.BuildType:minifyEnabled) to `true`, the Android plugin uses R8. If you set
* this property to `true`, the Android plugin uses ProGuard.
*
* To learn more, read
* [Shrink, obfuscate, and optimize your app](https://developer.android.com/studio/build/shrink-code.html).
*/
override var isUseProguard: Boolean?
get() = if (_postProcessingConfiguration != PostProcessingConfiguration.POSTPROCESSING_BLOCK) {
false
} else {
_postProcessing.codeShrinkerEnum == CodeShrinker.PROGUARD
}
set(_: Boolean?) {
checkPostProcessingConfiguration(PostProcessingConfiguration.OLD_DSL, "setUseProguard")
if (dslChecksEnabled) {
dslServices.deprecationReporter
.reportObsoleteUsage(
"useProguard", DeprecationReporter.DeprecationTarget.DSL_USE_PROGUARD
)
}
}
fun setUseProguard(useProguard: Boolean) {
isUseProguard = useProguard
}
override var isCrunchPngs: Boolean? = null
/** This DSL is incubating and subject to change. */
@get:Internal
@get:Incubating
val postprocessing: PostProcessingBlock
get() {
checkPostProcessingConfiguration(
PostProcessingConfiguration.POSTPROCESSING_BLOCK, "getPostProcessing"
)
return _postProcessing
}
/** This DSL is incubating and subject to change. */
@Incubating
@Internal
fun postprocessing(action: Action<PostProcessingBlock>) {
checkPostProcessingConfiguration(
PostProcessingConfiguration.POSTPROCESSING_BLOCK, "postProcessing"
)
action.execute(_postProcessing)
}
/** Describes how postProcessing was configured. Not to be used from the DSL. */
// TODO(b/140406102): Should be internal.
val postProcessingConfiguration: PostProcessingConfiguration
// If the user didn't configure anything, stick to the old DSL.
get() = _postProcessingConfiguration ?: PostProcessingConfiguration.OLD_DSL
/**
* Checks that the user is consistently using either the new or old DSL for configuring bytecode
* postProcessing.
*/
private fun checkPostProcessingConfiguration(
used: PostProcessingConfiguration,
methodName: String
) {
if (!dslChecksEnabled) {
return
}
if (_postProcessingConfiguration == null) {
_postProcessingConfiguration = used
postProcessingDslMethodUsed = methodName
} else if (_postProcessingConfiguration != used) {
assert(postProcessingDslMethodUsed != null)
val message: String
message = when (used) {
PostProcessingConfiguration.POSTPROCESSING_BLOCK -> // TODO: URL with more details.
String.format(
"The `postProcessing` block cannot be used with together with the `%s` method.",
postProcessingDslMethodUsed
)
PostProcessingConfiguration.OLD_DSL -> // TODO: URL with more details.
String.format(
"The `%s` method cannot be used with together with the `postProcessing` block.",
methodName
)
else -> throw AssertionError("Unknown value $used")
}
dslServices.issueReporter.reportError(
IssueReporter.Type.GENERIC,
message,
methodName
)
}
}
override fun initWith(that: com.android.builder.model.BuildType): BuildType {
// we need to avoid doing this because of Property objects that cannot
// be set from themselves
if (that === this) {
return this
}
dslChecksEnabled = false
if (that is BuildType) {
that.dslChecksEnabled = false
}
try {
this.signingConfig = that.signingConfig as SigningConfig?
return super.initWith(that) as BuildType
} finally {
dslChecksEnabled = true
if (that is BuildType) {
that.dslChecksEnabled = true
}
}
}
companion object {
private const val serialVersionUID = 1L
}
}