blob: 5bedcc1d716dfbcdcb0bfe225c447806742c7292 [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.tasks
import com.android.build.api.component.impl.ComponentPropertiesImpl
import com.android.build.gradle.internal.publishing.AndroidArtifacts.ArtifactScope.ALL
import com.android.build.gradle.internal.publishing.AndroidArtifacts.ArtifactType.AIDL
import com.android.build.gradle.internal.publishing.AndroidArtifacts.ConsumedConfigType.COMPILE_CLASSPATH
import com.android.build.gradle.internal.LoggerWrapper
import com.android.build.gradle.internal.SdkComponentsBuildService
import com.android.build.gradle.internal.process.GradleProcessExecutor
import com.android.build.gradle.internal.scope.InternalArtifactType
import com.android.build.gradle.internal.services.getBuildService
import com.android.build.gradle.internal.tasks.NonIncrementalTask
import com.android.build.gradle.internal.tasks.factory.VariantTaskCreationAction
import com.android.build.gradle.internal.utils.setDisallowChanges
import com.android.builder.compiling.DependencyFileProcessor
import com.android.builder.internal.compiler.AidlProcessor
import com.android.builder.internal.compiler.DirectoryWalker
import com.android.builder.internal.incremental.DependencyData
import com.android.ide.common.process.LoggedProcessOutputHandler
import com.android.utils.FileUtils
import com.google.common.base.Preconditions
import java.io.File
import java.io.IOException
import java.io.Serializable
import javax.inject.Inject
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.FileCollection
import org.gradle.api.file.FileTree
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider
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.OutputDirectory
import org.gradle.api.tasks.PathSensitive
import org.gradle.api.tasks.PathSensitivity
import org.gradle.api.tasks.SkipWhenEmpty
import org.gradle.api.tasks.TaskProvider
import org.gradle.api.tasks.util.PatternSet
import org.gradle.process.ExecOperations
/**
* Task to compile aidl files. Supports incremental update.
*
*
* TODO(b/124424292)
*
*
* We can not use gradle worker in this task as we use [GradleProcessExecutor] for
* compiling aidl files, which should not be serialized.
*/
@CacheableTask
abstract class AidlCompile : NonIncrementalTask() {
@get:Input
@get:Optional
var packageWhitelist: Collection<String>? = null
@get:Internal
abstract val sourceDirs: ListProperty<File>
@get:InputFiles
@get:PathSensitive(PathSensitivity.RELATIVE)
lateinit var importDirs: FileCollection
private set
@get:Inject
abstract val execOperations: ExecOperations
// Given the same version, the path or contents of the AIDL tool may change across platforms,
// but it would still produce the same output (given the same inputs)---see bug 138920846.
// Therefore, the path or contents of the tool should not be an input. Instead, we set the
// tool's version as input.
@get:Input
val aidlVersion: String
get() {
val buildToolsRevision = sdkBuildService.get().buildToolsRevisionProvider.orNull
Preconditions.checkState(buildToolsRevision != null, "Build Tools not present")
val aidlExecutable = sdkBuildService.get().aidlExecutableProvider.orNull
Preconditions.checkState(
aidlExecutable != null,
"AIDL executable not present in Build Tools $buildToolsRevision"
)
Preconditions.checkState(
aidlExecutable!!.exists(),
"AIDL executable does not exist: ${aidlExecutable.path}"
)
return buildToolsRevision.toString()
}
@InputFile
@PathSensitive(PathSensitivity.NONE)
fun getAidlFrameworkProvider(): Provider<File> = sdkBuildService.flatMap { it.aidlFrameworkProvider }
@get:InputFiles
@get:SkipWhenEmpty
@get:PathSensitive(PathSensitivity.RELATIVE)
abstract val sourceFiles: Property<FileTree>
@get:OutputDirectory
abstract val sourceOutputDir: DirectoryProperty
@get:OutputDirectory
@get:Optional
abstract val packagedDir: DirectoryProperty
@get:Internal
abstract val sdkBuildService: Property<SdkComponentsBuildService>
private class DepFileProcessor : DependencyFileProcessor {
override fun processFile(dependencyFile: File): DependencyData? {
return DependencyData.parseDependencyFile(dependencyFile)
}
}
override fun doTaskAction() {
// this is full run, clean the previous output
val destinationDir = sourceOutputDir.get().asFile
val parcelableDir = packagedDir.orNull
FileUtils.cleanOutputDir(destinationDir)
if (parcelableDir != null) {
FileUtils.cleanOutputDir(parcelableDir.asFile)
}
getWorkerFacadeWithThreads(false).use { workers ->
val sourceFolders = sourceDirs.get()
val importFolders = importDirs.files
val fullImportList = sourceFolders + importFolders
val processor = AidlProcessor(
sdkBuildService.get().aidlExecutableProvider.get().absolutePath,
getAidlFrameworkProvider().get().absolutePath,
fullImportList,
destinationDir,
parcelableDir?.asFile,
packageWhitelist,
DepFileProcessor(),
GradleProcessExecutor(execOperations::exec),
LoggedProcessOutputHandler(LoggerWrapper(logger))
)
for (dir in sourceFolders) {
workers.submit(AidlCompileRunnable::class.java, AidlCompileParams(dir, processor))
}
}
}
class CreationAction(
componentProperties: ComponentPropertiesImpl
) : VariantTaskCreationAction<AidlCompile, ComponentPropertiesImpl>(
componentProperties
) {
override val name: String = computeTaskName("compile", "Aidl")
override val type: Class<AidlCompile> = AidlCompile::class.java
override fun handleProvider(
taskProvider: TaskProvider<AidlCompile>
) {
super.handleProvider(taskProvider)
creationConfig.taskContainer.aidlCompileTask = taskProvider
creationConfig.artifacts.setInitialProvider(
taskProvider,
AidlCompile::sourceOutputDir
).withName("out").on(InternalArtifactType.AIDL_SOURCE_OUTPUT_DIR)
if (creationConfig.variantType.isAar) {
creationConfig.artifacts.setInitialProvider(
taskProvider,
AidlCompile::packagedDir
).withName("out").on(InternalArtifactType.AIDL_PARCELABLE)
}
}
override fun configure(
task: AidlCompile
) {
super.configure(task)
val globalScope = creationConfig.globalScope
val project = globalScope.project
val variantSources = creationConfig.variantSources
val sdkComponents = globalScope.sdkComponents
task
.sourceDirs
.set(project.provider { variantSources.aidlSourceList })
task.sourceDirs.disallowChanges()
// This is because aidl may be in the same folder as Java and we want to restrict to
// .aidl files and not java files.
task
.sourceFiles
.set(
project.provider {
project.layout.files(task.sourceDirs).asFileTree.matching(PATTERN_SET)
})
task.sourceFiles.disallowChanges()
task.importDirs = creationConfig.variantDependencies.getArtifactFileCollection(COMPILE_CLASSPATH, ALL, AIDL)
if (creationConfig.variantType.isAar) {
task.packageWhitelist = globalScope.extension.aidlPackageWhiteList
}
task.sdkBuildService.setDisallowChanges(
getBuildService(creationConfig.services.buildServiceRegistry)
)
}
}
internal class AidlCompileRunnable @Inject
constructor(private val params: AidlCompileParams) : Runnable {
override fun run() {
try {
DirectoryWalker.builder()
.root(params.dir.toPath())
.extensions("aidl")
.action(params.processor)
.build()
.walk()
} catch (e: IOException) {
throw RuntimeException(e)
}
}
}
internal class AidlCompileParams(val dir: File, val processor: AidlProcessor): Serializable
companion object {
private val PATTERN_SET = PatternSet().include("**/*.aidl")
}
}