blob: eb3f3216baf2c1d0d08bc7ee48a307e25bf3f2cf [file] [log] [blame]
/*
* Copyright (C) 2019 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.tasks
import com.android.SdkConstants.FD_RES_VALUES
import com.android.build.api.component.impl.ComponentPropertiesImpl
import com.android.build.gradle.internal.LoggerWrapper
import com.android.build.gradle.internal.aapt.SharedExecutorResourceCompilationService
import com.android.build.gradle.internal.res.getAapt2FromMavenAndVersion
import com.android.build.gradle.internal.scope.InternalArtifactType
import com.android.build.gradle.internal.services.Aapt2DaemonBuildService
import com.android.build.gradle.internal.services.Aapt2DaemonServiceKey
import com.android.build.gradle.internal.services.Aapt2WorkersBuildService
import com.android.build.gradle.internal.services.getBuildService
import com.android.build.gradle.internal.tasks.NewIncrementalTask
import com.android.build.gradle.internal.tasks.factory.VariantTaskCreationAction
import com.android.build.gradle.internal.utils.setDisallowChanges
import com.android.build.gradle.internal.workeractions.WorkerActionServiceRegistry
import com.android.build.gradle.options.BooleanOption
import com.android.build.gradle.options.SyncOptions
import com.android.builder.internal.aapt.v2.Aapt2RenamingConventions
import com.android.ide.common.resources.CompileResourceRequest
import com.android.utils.FileUtils
import com.google.common.collect.ImmutableList
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.FileType
import org.gradle.api.provider.Property
import org.gradle.api.tasks.CacheableTask
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.PathSensitive
import org.gradle.api.tasks.PathSensitivity
import org.gradle.api.tasks.TaskProvider
import org.gradle.work.ChangeType
import org.gradle.work.FileChange
import org.gradle.work.Incremental
import org.gradle.work.InputChanges
import java.io.File
import java.io.Serializable
import javax.inject.Inject
@CacheableTask
abstract class CompileLibraryResourcesTask : NewIncrementalTask() {
private lateinit var errorFormatMode: SyncOptions.ErrorFormatMode
@get:InputFiles
@get:Incremental
@get:PathSensitive(PathSensitivity.ABSOLUTE) // TODO(b/141301405): use relative paths
abstract val mergedLibraryResourcesDir: DirectoryProperty
@get:Input
var pseudoLocalesEnabled: Boolean = false
private set
@get:Input
var crunchPng: Boolean = true
private set
@get:Input
lateinit var aapt2Version: String
private set
@get:Internal
abstract val aapt2FromMaven: ConfigurableFileCollection
@get:OutputDirectory
abstract val outputDir: DirectoryProperty
@get:Input
var useJvmResourceCompiler: Boolean = false
private set
@get:Internal
abstract val aapt2WorkersBuildService: Property<Aapt2WorkersBuildService>
@get:Internal
abstract val aapt2DaemonBuildService: Property<Aapt2DaemonBuildService>
override fun doTaskAction(inputChanges: InputChanges) {
val aapt2ServiceKey = aapt2DaemonBuildService.get().registerAaptService(
aapt2FromMaven.singleFile, LoggerWrapper(logger)
)
getWorkerFacadeWithWorkers().use { workers ->
val requests = ImmutableList.builder<CompileResourceRequest>()
if (inputChanges.isIncremental) {
doIncrementalTaskAction(
inputChanges.getFileChanges(mergedLibraryResourcesDir),
requests
)
} else {
// do full task action
FileUtils.deleteDirectoryContents(outputDir.asFile.get())
// filter out the values files as they have to go through the resources merging
// pipeline.
mergedLibraryResourcesDir.asFile.get().listFiles()!!
.filter { it.isDirectory && !it.name.startsWith(FD_RES_VALUES) }
.forEach { dir ->
dir.listFiles()!!.forEach { file ->
submitFileToBeCompiled(file, requests)
}
}
}
workers.submit(
CompileLibraryResourcesRunnable::class.java,
CompileLibraryResourcesParams(
projectName,
path,
aapt2ServiceKey,
aapt2WorkersBuildService.get().getWorkersServiceKey(),
errorFormatMode,
requests.build(),
useJvmResourceCompiler
)
)
}
}
private fun submitFileToBeCompiled(
file: File,
requests: ImmutableList.Builder<CompileResourceRequest>
) {
requests.add(
CompileResourceRequest(
file,
outputDir.asFile.get(),
isPseudoLocalize = pseudoLocalesEnabled,
isPngCrunching = crunchPng
)
)
}
private fun handleModifiedFile(
file: File,
changeType: ChangeType,
requests: ImmutableList.Builder<CompileResourceRequest>
) {
if (changeType == ChangeType.MODIFIED || changeType == ChangeType.REMOVED) {
FileUtils.deleteIfExists(
File(
outputDir.asFile.get(),
Aapt2RenamingConventions.compilationRename(file)
)
)
}
if (changeType == ChangeType.ADDED || changeType == ChangeType.MODIFIED) {
submitFileToBeCompiled(file, requests)
}
}
private fun doIncrementalTaskAction(
fileChanges: Iterable<FileChange>,
requests: ImmutableList.Builder<CompileResourceRequest>
) {
fileChanges.filter {
it.fileType == FileType.FILE &&
!it.file.parentFile.name.startsWith(FD_RES_VALUES)
}
.forEach { fileChange ->
handleModifiedFile(
fileChange.file,
fileChange.changeType,
requests
)
}
}
private data class CompileLibraryResourcesParams(
val projectName: String,
val owner: String,
val aapt2ServiceKey: Aapt2DaemonServiceKey,
val aapt2WorkersBuildServiceKey: WorkerActionServiceRegistry.ServiceKey<Aapt2WorkersBuildService>,
val errorFormatMode: SyncOptions.ErrorFormatMode,
val requests: List<CompileResourceRequest>,
val useJvmResourceCompiler: Boolean
) : Serializable
private class CompileLibraryResourcesRunnable
@Inject constructor(private val params: CompileLibraryResourcesParams) : Runnable {
override fun run() {
SharedExecutorResourceCompilationService(
params.projectName,
params.owner,
params.aapt2WorkersBuildServiceKey,
params.aapt2ServiceKey,
params.errorFormatMode,
params.useJvmResourceCompiler
).use {
it.submitCompile(params.requests)
}
}
}
class CreationAction(
componentProperties: ComponentPropertiesImpl
) : VariantTaskCreationAction<CompileLibraryResourcesTask, ComponentPropertiesImpl>(
componentProperties
) {
override val name: String
get() = computeTaskName("compile", "LibraryResources")
override val type: Class<CompileLibraryResourcesTask>
get() = CompileLibraryResourcesTask::class.java
override fun handleProvider(
taskProvider: TaskProvider<CompileLibraryResourcesTask>
) {
super.handleProvider(taskProvider)
creationConfig.artifacts.setInitialProvider(
taskProvider,
CompileLibraryResourcesTask::outputDir
).withName("out").on(InternalArtifactType.COMPILED_LOCAL_RESOURCES)
}
override fun configure(
task: CompileLibraryResourcesTask
) {
super.configure(task)
creationConfig.artifacts.setTaskInputToFinalProduct(
InternalArtifactType.PACKAGED_RES,
task.mergedLibraryResourcesDir
)
val (aapt2FromMaven, aapt2Version) = getAapt2FromMavenAndVersion(creationConfig.globalScope)
task.aapt2FromMaven.from(aapt2FromMaven)
task.aapt2Version = aapt2Version
task.pseudoLocalesEnabled = creationConfig
.variantDslInfo
.isPseudoLocalesEnabled
task.crunchPng = creationConfig.variantScope.isCrunchPngs
task.errorFormatMode =
SyncOptions.getErrorFormatMode(creationConfig.services.projectOptions)
task.useJvmResourceCompiler =
creationConfig.services.projectOptions[BooleanOption.ENABLE_JVM_RESOURCE_COMPILER]
task.aapt2WorkersBuildService.setDisallowChanges(
getBuildService(creationConfig.services.buildServiceRegistry)
)
task.aapt2DaemonBuildService.setDisallowChanges(
getBuildService(creationConfig.services.buildServiceRegistry)
)
}
}
}