blob: eee904ae37c7d2822fd74bf9d855d2b67b0ffcf8 [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.LoggerWrapper
import com.android.build.gradle.internal.SdkComponentsBuildService
import com.android.build.gradle.internal.process.GradleProcessExecutor
import com.android.build.gradle.internal.publishing.AndroidArtifacts.ArtifactScope.ALL
import com.android.build.gradle.internal.publishing.AndroidArtifacts.ArtifactType.RENDERSCRIPT
import com.android.build.gradle.internal.publishing.AndroidArtifacts.ConsumedConfigType.COMPILE_CLASSPATH
import com.android.build.gradle.internal.scope.InternalArtifactType.RENDERSCRIPT_LIB
import com.android.build.gradle.internal.scope.InternalArtifactType.RENDERSCRIPT_SOURCE_OUTPUT_DIR
import com.android.build.gradle.internal.services.getBuildService
import com.android.build.gradle.internal.tasks.NdkTask
import com.android.build.gradle.internal.tasks.factory.VariantTaskCreationAction
import com.android.build.gradle.internal.utils.setDisallowChanges
import com.android.build.gradle.options.BooleanOption
import com.android.builder.internal.compiler.DirectoryWalker
import com.android.builder.internal.compiler.RenderScriptProcessor
import com.android.ide.common.process.LoggedProcessOutputHandler
import com.android.ide.common.process.ProcessOutputHandler
import com.android.repository.Revision
import com.android.sdklib.BuildToolInfo
import com.android.utils.FileUtils
import com.google.common.base.Preconditions.checkNotNull
import com.google.common.collect.Lists
import com.google.common.collect.Sets
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.FileCollection
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.SkipWhenEmpty
import org.gradle.api.tasks.TaskProvider
import org.gradle.process.ExecOperations
import java.io.File
import java.io.IOException
import java.util.concurrent.Callable
import javax.inject.Inject
/** Task to compile Renderscript files. Supports incremental update. */
@CacheableTask
abstract class RenderscriptCompile : NdkTask() {
// ----- PUBLIC TASK API -----
@get:OutputDirectory
lateinit var resOutputDir: File
@get:OutputDirectory
lateinit var objOutputDir: File
// ----- PRIVATE TASK API -----
@get:InputFiles
@get:PathSensitive(PathSensitivity.RELATIVE)
lateinit var importDirs: FileCollection
@get:Input
abstract val targetApi: Property<Int>
@get:Input
var isSupportMode: Boolean = false
@get:Input
var useAndroidX: Boolean = false
private set
@get:Input
var optimLevel: Int = 0
@get:Input
var isNdkMode: Boolean = false
@Input
fun getBuildToolsVersion(): String =
sdkBuildService.get().buildToolInfoProvider.get().revision.toString()
@get:Internal
abstract val sdkBuildService: Property<SdkComponentsBuildService>
@get:OutputDirectory
abstract val sourceOutputDir: DirectoryProperty
@get:OutputDirectory
abstract val libOutputDir: DirectoryProperty
@get:Inject
abstract val execOperations: ExecOperations
// get the import folders. If the .rsh files are not directly under the import folders,
// we need to get the leaf folders, as this is what llvm-rs-cc expects.
// TODO: should "rsh" be a constant somewhere?
private val importFolders: Collection<File>
get() {
val results = Sets.newHashSet<File>()
val dirs = Lists.newArrayList<File>()
dirs.addAll(importDirs.files)
dirs.addAll(sourceDirs.files)
for (dir in dirs) {
DirectoryWalker.builder()
.root(dir.toPath())
.extensions("rsh")
.action { _, path -> results.add(path.parent.toFile()) }
.build()
.walk()
}
return results
}
private lateinit var sourceDirs: FileCollection
@InputFiles
@PathSensitive(PathSensitivity.RELATIVE)
@SkipWhenEmpty
fun getSourceDirs(): FileCollection {
return sourceDirs.asFileTree
}
override fun doTaskAction() {
// this is full run (always), clean the previous outputs
val sourceDestDir = sourceOutputDir.get().asFile
FileUtils.cleanOutputDir(sourceDestDir)
val resDestDir = resOutputDir
FileUtils.cleanOutputDir(resDestDir)
val objDestDir = objOutputDir
FileUtils.cleanOutputDir(objDestDir)
val libDestDir = libOutputDir.get().asFile
FileUtils.cleanOutputDir(libDestDir)
val sourceDirectories = sourceDirs.files
val buildToolsInfo = sdkBuildService.get().buildToolInfoProvider.get()
compileAllRenderscriptFiles(
sourceDirectories,
importFolders,
sourceDestDir,
resDestDir,
objDestDir,
libDestDir,
targetApi.get(),
buildToolsInfo.revision,
optimLevel,
isNdkMode,
isSupportMode,
useAndroidX,
ndkConfig?.abiFilters ?: setOf(),
LoggedProcessOutputHandler(LoggerWrapper(logger)),
buildToolsInfo
)
}
/**
* Compiles all the renderscript files found in the given source folders.
*
*
* Right now this is the only way to compile them as the renderscript compiler requires all
* renderscript files to be passed for all compilation.
*
*
* Therefore whenever a renderscript file or header changes, all must be recompiled.
*
* @param sourceFolders all the source folders to find files to compile
* @param importFolders all the import folders.
* @param sourceOutputDir the output dir in which to generate the source code
* @param resOutputDir the output dir in which to generate the bitcode file
* @param targetApi the target api
* @param buildToolsRevision the build tools version used
* @param optimLevel the optimization level
* @param ndkMode whether the renderscript code should be compiled to generate C/C++ bindings
* @param supportMode support mode flag to generate .so files.
* @param useAndroidX whether to use AndroidX dependencies
* @param abiFilters ABI filters in case of support mode
* @throws IOException failed
* @throws InterruptedException failed
*/
private fun compileAllRenderscriptFiles(
sourceFolders: Collection<File>,
importFolders: Collection<File>,
sourceOutputDir: File,
resOutputDir: File,
objOutputDir: File,
libOutputDir: File,
targetApi: Int,
buildToolsRevision: Revision,
optimLevel: Int,
ndkMode: Boolean,
supportMode: Boolean,
useAndroidX: Boolean,
abiFilters: Set<String>,
processOutputHandler: ProcessOutputHandler,
buildToolInfo: BuildToolInfo
) {
checkNotNull(sourceFolders, "sourceFolders cannot be null.")
checkNotNull(importFolders, "importFolders cannot be null.")
checkNotNull(sourceOutputDir, "sourceOutputDir cannot be null.")
checkNotNull(resOutputDir, "resOutputDir cannot be null.")
val renderscript = buildToolInfo.getPath(BuildToolInfo.PathId.LLVM_RS_CC)
if (renderscript == null || !File(renderscript).isFile) {
throw IllegalStateException("llvm-rs-cc is missing")
}
val processor = RenderScriptProcessor(
sourceFolders,
importFolders,
sourceOutputDir,
resOutputDir,
objOutputDir,
libOutputDir,
buildToolInfo,
targetApi,
buildToolsRevision,
optimLevel,
ndkMode,
supportMode,
useAndroidX,
abiFilters,
LoggerWrapper(logger)
)
processor.build(GradleProcessExecutor(execOperations::exec), processOutputHandler)
}
// ----- CreationAction -----
class CreationAction(
componentProperties: ComponentPropertiesImpl
) : VariantTaskCreationAction<RenderscriptCompile, ComponentPropertiesImpl>(
componentProperties
) {
override val name: String
get() = computeTaskName("compile", "Renderscript")
override val type: Class<RenderscriptCompile>
get() = RenderscriptCompile::class.java
override fun handleProvider(
taskProvider: TaskProvider<RenderscriptCompile>
) {
super.handleProvider(taskProvider)
creationConfig.taskContainer.renderscriptCompileTask = taskProvider
creationConfig.artifacts.setInitialProvider(
taskProvider,
RenderscriptCompile::sourceOutputDir
).withName("out").on(RENDERSCRIPT_SOURCE_OUTPUT_DIR)
creationConfig.artifacts.setInitialProvider(
taskProvider,
RenderscriptCompile::libOutputDir
).withName("lib").on(RENDERSCRIPT_LIB)
}
override fun configure(
task: RenderscriptCompile
) {
super.configure(task)
val globalScope = creationConfig.globalScope
val variantDslInfo = creationConfig.variantDslInfo
val variantSources = creationConfig.variantSources
val ndkMode = variantDslInfo.renderscriptNdkModeEnabled
task.targetApi.set(globalScope.project.provider {
variantDslInfo.renderscriptTarget
})
task.targetApi.disallowChanges()
task.isSupportMode = variantDslInfo.renderscriptSupportModeEnabled
task.useAndroidX = creationConfig.services.projectOptions.get(BooleanOption.USE_ANDROID_X)
task.isNdkMode = ndkMode
task.optimLevel = variantDslInfo.renderscriptOptimLevel
task.sourceDirs = globalScope
.project
.files(Callable { variantSources.renderscriptSourceList })
task.importDirs = creationConfig.variantDependencies.getArtifactFileCollection(
COMPILE_CLASSPATH, ALL, RENDERSCRIPT
)
task.resOutputDir = creationConfig.paths.renderscriptResOutputDir
task.objOutputDir = creationConfig.paths.renderscriptObjOutputDir
task.ndkConfig = variantDslInfo.ndkConfig
task.sdkBuildService.setDisallowChanges(
getBuildService(creationConfig.services.buildServiceRegistry)
)
if (creationConfig.variantType.isTestComponent) {
task.dependsOn(creationConfig.taskContainer.processManifestTask!!)
}
}
}
}