blob: 461c8ac078b24caa69bdb7686f3e2e05a151f025 [file] [log] [blame]
/*
* Copyright (C) 2017 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.res
import com.android.SdkConstants
import com.android.build.api.component.impl.ComponentPropertiesImpl
import com.android.build.api.variant.FilterConfiguration
import com.android.build.api.variant.impl.BuiltArtifactImpl
import com.android.build.api.variant.impl.BuiltArtifactsImpl
import com.android.build.api.variant.impl.BuiltArtifactsLoaderImpl
import com.android.build.gradle.internal.profile.ProfileAwareWorkAction
import com.android.build.gradle.internal.publishing.AndroidArtifacts
import com.android.build.gradle.internal.publishing.AndroidArtifacts.ArtifactScope.ALL
import com.android.build.gradle.internal.publishing.AndroidArtifacts.ConsumedConfigType.COMPILE_CLASSPATH
import com.android.build.gradle.internal.publishing.AndroidArtifacts.ConsumedConfigType.RUNTIME_CLASSPATH
import com.android.build.gradle.internal.scope.InternalArtifactType
import com.android.build.gradle.internal.tasks.factory.VariantTaskCreationAction
import com.android.build.gradle.internal.utils.fromDisallowChanges
import com.android.build.gradle.internal.utils.setDisallowChanges
import com.android.build.gradle.options.BooleanOption
import com.android.build.gradle.tasks.ProcessAndroidResources
import com.android.builder.symbols.processLibraryMainSymbolTable
import com.android.ide.common.symbols.IdProvider
import com.android.ide.common.symbols.SymbolIo
import com.android.ide.common.symbols.SymbolTable
import java.io.File
import java.io.IOException
import javax.inject.Inject
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.CacheableTask
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.PathSensitive
import org.gradle.api.tasks.PathSensitivity
import org.gradle.api.tasks.TaskProvider
@CacheableTask
abstract class GenerateLibraryRFileTask : ProcessAndroidResources() {
@get:OutputFile
abstract val rClassOutputJar: RegularFileProperty
@Internal // rClassOutputJar is already marked as @OutputFile
override fun getSourceOutputDir(): File? = rClassOutputJar.get().asFile
// used by Butterknife
@Suppress("unused")
@Internal
fun getTextSymbolOutputFile(): File {
return textSymbolOutputFileProperty.get().asFile
}
@get:OutputFile
@get:Optional
abstract val textSymbolOutputFileProperty: RegularFileProperty
@get:OutputFile
@get:Optional
abstract val symbolsWithPackageNameOutputFile: RegularFileProperty
@get:InputFiles
@get:PathSensitive(PathSensitivity.NONE) abstract val dependencies: ConfigurableFileCollection
@get:Input
abstract val packageForR: Property<String>
@get:InputFiles
@get:PathSensitive(PathSensitivity.NAME_ONLY)
abstract val platformAttrRTxt: ConfigurableFileCollection
@get:InputFile
@get:PathSensitive(PathSensitivity.RELATIVE)
abstract val localResourcesFile: RegularFileProperty
@get:Input
abstract val nonTransitiveRClass: Property<Boolean>
@get:Input
abstract val compileClasspathLibraryRClasses: Property<Boolean>
@get:Input
abstract val useConstantIds: Property<Boolean>
@Throws(IOException::class)
override fun doFullTaskAction() {
val manifestBuiltArtifacts = BuiltArtifactsLoaderImpl().load(manifestFiles)
?: throw RuntimeException("Cannot load generated manifests, file a bug")
val manifest = File(chooseOutput(manifestBuiltArtifacts).outputFile)
workerExecutor.noIsolation().submit(GenerateLibRFileRunnable::class.java) {
it.initializeFromAndroidVariantTask(this)
it.localResourcesFile.set(localResourcesFile)
it.manifest.set(manifest)
it.androidJar.from(platformAttrRTxt)
it.dependencies.from(dependencies)
it.packageForR.set(packageForR.get())
it.rClassOutputJar.set(rClassOutputJar)
it.textSymbolOutputFile.set(textSymbolOutputFileProperty)
it.nonTransitiveRClass.set(nonTransitiveRClass.get())
it.compileClasspathLibraryRClasses.set(compileClasspathLibraryRClasses.get())
it.symbolsWithPackageNameOutputFile.set(symbolsWithPackageNameOutputFile)
it.useConstantIds.set(useConstantIds.get())
}
}
private fun chooseOutput(manifestBuiltArtifacts: BuiltArtifactsImpl): BuiltArtifactImpl =
manifestBuiltArtifacts.elements
.firstOrNull() { output -> output.getFilter(FilterConfiguration.FilterType.DENSITY) == null }
?: throw RuntimeException("No non-density apk found")
abstract class GenerateLibRFileParams : ProfileAwareWorkAction.Parameters() {
abstract val localResourcesFile: RegularFileProperty
abstract val manifest: RegularFileProperty
abstract val androidJar: ConfigurableFileCollection
abstract val dependencies: ConfigurableFileCollection
abstract val packageForR: Property<String>
abstract val rClassOutputJar: RegularFileProperty
abstract val textSymbolOutputFile: RegularFileProperty
abstract val nonTransitiveRClass: Property<Boolean>
abstract val compileClasspathLibraryRClasses: Property<Boolean>
abstract val symbolsWithPackageNameOutputFile: RegularFileProperty
abstract val useConstantIds: Property<Boolean>
}
abstract class GenerateLibRFileRunnable @Inject constructor() :
ProfileAwareWorkAction<GenerateLibRFileParams>() {
override fun run() {
val androidAttrSymbol = getAndroidAttrSymbols()
val symbolTable = SymbolIo.readRDef(parameters.localResourcesFile.asFile.get().toPath())
val idProvider =
if (parameters.useConstantIds.get()) {
IdProvider.constant()
} else {
IdProvider.sequential()
}
processLibraryMainSymbolTable(
librarySymbols = symbolTable,
libraries = parameters.dependencies.files,
mainPackageName = parameters.packageForR.get(),
manifestFile = parameters.manifest.asFile.get(),
rClassOutputJar = parameters.rClassOutputJar.asFile.orNull,
symbolFileOut = parameters.textSymbolOutputFile.asFile.orNull,
platformSymbols = androidAttrSymbol,
nonTransitiveRClass = parameters.nonTransitiveRClass.get(),
generateDependencyRClasses = !parameters.compileClasspathLibraryRClasses.get(),
idProvider = idProvider
)
parameters.symbolsWithPackageNameOutputFile.asFile.orNull?.let {
SymbolIo.writeSymbolListWithPackageName(
parameters.textSymbolOutputFile.get().asFile.toPath(),
parameters.manifest.get().asFile.toPath(),
it.toPath()
)
}
}
private fun getAndroidAttrSymbols() =
if (parameters.androidJar.singleFile.exists())
SymbolIo.readFromAapt(parameters.androidJar.singleFile, "android")
else
SymbolTable.builder().tablePackage("android").build()
}
internal class CreationAction(
componentProperties: ComponentPropertiesImpl,
val isLibrary: Boolean)
: VariantTaskCreationAction<GenerateLibraryRFileTask, ComponentPropertiesImpl>(componentProperties) {
override val name: String
get() = computeTaskName("generate", "RFile")
override val type: Class<GenerateLibraryRFileTask>
get() = GenerateLibraryRFileTask::class.java
override fun handleProvider(
taskProvider: TaskProvider<GenerateLibraryRFileTask>
) {
super.handleProvider(taskProvider)
creationConfig.taskContainer.processAndroidResTask = taskProvider
creationConfig.artifacts.setInitialProvider(
taskProvider,
GenerateLibraryRFileTask::rClassOutputJar
).withName("R.jar").on(InternalArtifactType.COMPILE_R_CLASS_JAR)
creationConfig.artifacts.setInitialProvider(
taskProvider,
GenerateLibraryRFileTask::textSymbolOutputFileProperty
).withName(SdkConstants.FN_RESOURCE_TEXT).on(InternalArtifactType.COMPILE_SYMBOL_LIST)
// Synthetic output for AARs (see SymbolTableWithPackageNameTransform), and created in
// process resources for local subprojects.
creationConfig.artifacts.setInitialProvider(
taskProvider,
GenerateLibraryRFileTask::symbolsWithPackageNameOutputFile
).withName("package-aware-r.txt").on(InternalArtifactType.SYMBOL_LIST_WITH_PACKAGE_NAME)
}
override fun configure(
task: GenerateLibraryRFileTask
) {
super.configure(task)
val projectOptions = creationConfig.services.projectOptions
task.platformAttrRTxt.fromDisallowChanges(creationConfig.globalScope.platformAttrs)
val nonTransitiveRClass = projectOptions[BooleanOption.NON_TRANSITIVE_R_CLASS]
val compileClasspathLibraryRClasses = projectOptions[BooleanOption.COMPILE_CLASSPATH_LIBRARY_R_CLASSES]
if (!nonTransitiveRClass || !compileClasspathLibraryRClasses) {
// We need the dependencies for generating our own R class or for generating R
// classes of the dependencies:
// * If we're creating a transitive (non-namespaced) R class, then we need the
// dependencies to include them in the local R class.
// * If we're using the runtime classpath (not compile classpath) then we need the
// dependencies for generating the R classes for each of them.
// * If both above are true then we use the dependencies for generating both the
// local R class and the dependencies' R classes.
// * The only case when we don't need the dependencies is if we are generating a
// namespaced (non-transitive) local R class AND we're using the compile
// classpath R class flow.
val consumedConfigType =
if (compileClasspathLibraryRClasses) {
COMPILE_CLASSPATH
} else {
RUNTIME_CLASSPATH
}
task.dependencies.from(
creationConfig.variantDependencies.getArtifactFileCollection(
consumedConfigType,
ALL,
AndroidArtifacts.ArtifactType.SYMBOL_LIST_WITH_PACKAGE_NAME
))
}
task.nonTransitiveRClass.set(nonTransitiveRClass)
task.compileClasspathLibraryRClasses.setDisallowChanges(compileClasspathLibraryRClasses)
task.packageForR.setDisallowChanges(creationConfig.packageName)
creationConfig.artifacts.setTaskInputToFinalProduct(
InternalArtifactType.PACKAGED_MANIFESTS, task.manifestFiles)
creationConfig.artifacts.setTaskInputToFinalProduct(
InternalArtifactType.MERGED_MANIFESTS, task.mergedManifestFiles)
task.mainSplit = creationConfig.outputs.getMainSplit()
// This task can produce R classes with either constant IDs ("0") or sequential IDs
// mimicking the way AAPT2 numbers IDs. If we're generating a compile time only R class
// (either for the small merge in app or when using compile classpath resources in libs)
// we want to use the constant IDs; otherwise, we will use sequential IDs.
// In either case, the IDs are fake, and therefore are non-final.
task.useConstantIds.set(
(projectOptions[BooleanOption.ENABLE_APP_COMPILE_TIME_R_CLASS] && !isLibrary)
|| projectOptions[BooleanOption.COMPILE_CLASSPATH_LIBRARY_R_CLASSES])
creationConfig.artifacts.setTaskInputToFinalProduct(
InternalArtifactType.LOCAL_ONLY_SYMBOL_LIST,
task.localResourcesFile)
}
}
internal class TestRuntimeStubRClassCreationAction(componentProperties: ComponentPropertiesImpl) :
VariantTaskCreationAction<GenerateLibraryRFileTask, ComponentPropertiesImpl>(
componentProperties
) {
override val name: String = computeTaskName("generate", "StubRFile")
override val type: Class<GenerateLibraryRFileTask> = GenerateLibraryRFileTask::class.java
override fun handleProvider(
taskProvider: TaskProvider<GenerateLibraryRFileTask>
) {
super.handleProvider(taskProvider)
creationConfig.artifacts.setInitialProvider(
taskProvider,
GenerateLibraryRFileTask::rClassOutputJar
).withName("R.jar").on(InternalArtifactType.COMPILE_AND_RUNTIME_NOT_NAMESPACED_R_CLASS_JAR)
}
override fun configure(
task: GenerateLibraryRFileTask
) {
super.configure(task)
val projectOptions = creationConfig.services.projectOptions
task.platformAttrRTxt.fromDisallowChanges(creationConfig.globalScope.platformAttrs)
// We need the runtime dependencies for generating a set of consistent runtime R classes
// for android test, and in the case of transitive R classes, we also need them
// to include them in the local R class.
task.dependencies.fromDisallowChanges(
creationConfig.variantDependencies.getArtifactFileCollection(
RUNTIME_CLASSPATH,
ALL,
AndroidArtifacts.ArtifactType.SYMBOL_LIST_WITH_PACKAGE_NAME
)
)
task.nonTransitiveRClass.setDisallowChanges(projectOptions[BooleanOption.NON_TRANSITIVE_R_CLASS])
task.compileClasspathLibraryRClasses.setDisallowChanges(false)
task.packageForR.setDisallowChanges(creationConfig.packageName)
task.mainSplit = creationConfig.outputs.getMainSplit()
task.useConstantIds.setDisallowChanges(false)
creationConfig.onTestedConfig {
it.artifacts.setTaskInputToFinalProduct(
InternalArtifactType.PACKAGED_MANIFESTS, task.manifestFiles
)
it.artifacts.setTaskInputToFinalProduct(
InternalArtifactType.LOCAL_ONLY_SYMBOL_LIST,
task.localResourcesFile
)
}
}
}
}