blob: 0f19ad6e7fd8aede92d522fdbd875c4c78b60f62 [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.internal.tasks
import com.android.SdkConstants
import com.android.build.api.component.impl.ComponentPropertiesImpl
import com.android.build.api.transform.QualifiedContent.Scope.EXTERNAL_LIBRARIES
import com.android.build.api.transform.QualifiedContent.Scope.SUB_PROJECTS
import com.android.build.api.transform.QualifiedContent.ScopeType
import com.android.build.gradle.internal.packaging.SerializablePackagingOptions
import com.android.build.gradle.internal.pipeline.ExtendedContentType.NATIVE_LIBS
import com.android.build.gradle.internal.publishing.AndroidArtifacts
import com.android.build.gradle.internal.scope.InternalArtifactType
import com.android.build.gradle.internal.scope.InternalArtifactType.MERGED_NATIVE_LIBS
import com.android.build.gradle.internal.scope.InternalArtifactType.RENDERSCRIPT_LIB
import com.android.build.gradle.internal.tasks.factory.VariantTaskCreationAction
import com.android.ide.common.resources.FileStatus
import org.gradle.api.GradleException
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.FileCollection
import org.gradle.api.model.ObjectFactory
import org.gradle.api.tasks.CacheableTask
import org.gradle.api.tasks.Classpath
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.Nested
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 java.io.File
import java.util.function.Predicate
import javax.inject.Inject
/**
* Task to merge native libs from multiple modules
*/
@CacheableTask
abstract class MergeNativeLibsTask
@Inject constructor(objects: ObjectFactory) : IncrementalTask() {
@get:InputFiles
@get:PathSensitive(PathSensitivity.RELATIVE)
@get:SkipWhenEmpty
abstract val projectNativeLibs: ConfigurableFileCollection
@get:Classpath
@get:SkipWhenEmpty
abstract val subProjectNativeLibs: ConfigurableFileCollection
@get:Classpath
@get:SkipWhenEmpty
abstract val externalLibNativeLibs: ConfigurableFileCollection
@get:Nested
lateinit var packagingOptions: SerializablePackagingOptions
private set
private lateinit var intermediateDir: File
@get:OutputDirectory
abstract val cacheDir: DirectoryProperty
private lateinit var incrementalStateFile: File
@get:OutputDirectory
val outputDir: DirectoryProperty = objects.directoryProperty()
override val incremental: Boolean
get() = true
// The runnable implementing the processing is not able to deal with fine-grained file but
// instead is expecting directories of files. Use the unfiltered collection (since the filtering
// changes the FileCollection of directories into a FileTree of files) to process, but don't
// use it as a task input, it's covered by the [projectNativeLibs] above. This is a workaround
// for the lack of gradle custom snapshotting: https://github.com/gradle/gradle/issues/8503.
@get:Internal
abstract val unfilteredProjectNativeLibs: ConfigurableFileCollection
override fun doFullTaskAction() {
getWorkerFacadeWithWorkers().use {
it.submit(
MergeJavaResRunnable::class.java,
MergeJavaResRunnable.Params(
unfilteredProjectNativeLibs.files,
subProjectNativeLibs.files,
externalLibNativeLibs.files,
null,
outputDir.get().asFile,
packagingOptions,
incrementalStateFile,
false,
cacheDir.get().asFile,
null,
NATIVE_LIBS,
listOf()
)
)
}
}
override fun doIncrementalTaskAction(changedInputs: Map<File, FileStatus>) {
if (!incrementalStateFile.isFile) {
doFullTaskAction()
return
}
getWorkerFacadeWithWorkers().use {
it.submit(
MergeJavaResRunnable::class.java,
MergeJavaResRunnable.Params(
unfilteredProjectNativeLibs.files,
subProjectNativeLibs.files,
externalLibNativeLibs.files,
null,
outputDir.get().asFile,
packagingOptions,
incrementalStateFile,
true,
cacheDir.get().asFile,
changedInputs,
NATIVE_LIBS,
listOf()
)
)
}
}
class CreationAction(
private val mergeScopes: Collection<ScopeType>,
componentProperties: ComponentPropertiesImpl
) : VariantTaskCreationAction<MergeNativeLibsTask, ComponentPropertiesImpl>(
componentProperties
) {
override val name: String
get() = computeTaskName("merge", "NativeLibs")
override val type: Class<MergeNativeLibsTask>
get() = MergeNativeLibsTask::class.java
override fun handleProvider(
taskProvider: TaskProvider<MergeNativeLibsTask>
) {
super.handleProvider(taskProvider)
creationConfig.artifacts.setInitialProvider(
taskProvider,
MergeNativeLibsTask::outputDir
).withName("out").on(MERGED_NATIVE_LIBS)
}
override fun configure(
task: MergeNativeLibsTask
) {
super.configure(task)
task.packagingOptions =
SerializablePackagingOptions(
creationConfig.globalScope.extension.packagingOptions)
task.intermediateDir =
creationConfig.paths.getIncrementalDir(
"${creationConfig.name}-mergeNativeLibs")
val project = creationConfig.globalScope.project
task.cacheDir
.fileProvider(project.provider { File(task.intermediateDir, "zip-cache") })
.disallowChanges()
task.incrementalStateFile = File(task.intermediateDir, "merge-state")
task.projectNativeLibs
.from(getProjectNativeLibs(creationConfig).asFileTree.matching(patternSet))
.disallowChanges()
if (mergeScopes.contains(SUB_PROJECTS)) {
task.subProjectNativeLibs.from(getSubProjectNativeLibs(creationConfig))
}
task.subProjectNativeLibs.disallowChanges()
if (mergeScopes.contains(EXTERNAL_LIBRARIES)) {
task.externalLibNativeLibs.from(getExternalNativeLibs(creationConfig))
}
task.externalLibNativeLibs.disallowChanges()
task.unfilteredProjectNativeLibs
.from(getProjectNativeLibs(creationConfig)).disallowChanges()
}
}
companion object {
private const val includedFileSuffix = SdkConstants.DOT_NATIVE_LIBS
private val includedFileNames = listOf(SdkConstants.FN_GDBSERVER, SdkConstants.FN_GDB_SETUP)
// predicate logic must match patternSet logic below
val predicate = Predicate<String> { fileName ->
fileName.endsWith(includedFileSuffix) || includedFileNames.any { it == fileName }
}
// patternSet logic must match predicate logic above
val patternSet: PatternSet
get() {
val patternSet = PatternSet().include("**/*$includedFileSuffix")
includedFileNames.forEach { patternSet.include("**/$it") }
return patternSet
}
}
}
fun getProjectNativeLibs(componentProperties: ComponentPropertiesImpl): FileCollection {
val globalScope = componentProperties.globalScope
val artifacts = componentProperties.artifacts
val taskContainer = componentProperties.taskContainer
val project = globalScope.project
val nativeLibs = globalScope.project.files()
// add merged project native libs
nativeLibs.from(
artifacts.get(InternalArtifactType.MERGED_JNI_LIBS)
)
// add content of the local external native build
if (taskContainer.externalNativeJsonGenerator != null) {
nativeLibs.from(
project
.files(File(taskContainer.externalNativeJsonGenerator!!.get().objFolder))
.builtBy(taskContainer.externalNativeBuildTask?.name)
)
}
// add renderscript compilation output if support mode is enabled.
if (componentProperties.variantDslInfo.renderscriptSupportModeEnabled) {
val rsFileCollection: ConfigurableFileCollection =
project.files(artifacts.get(RENDERSCRIPT_LIB))
val rsLibs = globalScope.sdkComponents.get().supportNativeLibFolderProvider.orNull
if (rsLibs?.isDirectory != null) {
rsFileCollection.from(rsLibs)
}
if (componentProperties.variantDslInfo.renderscriptSupportModeBlasEnabled) {
val rsBlasLib = globalScope.sdkComponents.get().supportBlasLibFolderProvider.orNull
if (rsBlasLib == null || !rsBlasLib.isDirectory) {
throw GradleException(
"Renderscript BLAS support mode is not supported in BuildTools $rsBlasLib"
)
} else {
rsFileCollection.from(rsBlasLib)
}
}
nativeLibs.from(rsFileCollection)
}
return nativeLibs
}
fun getSubProjectNativeLibs(componentProperties: ComponentPropertiesImpl): FileCollection {
val nativeLibs = componentProperties.globalScope.project.files()
// TODO (bug 154984238) extract native libs from java res jar before this task
nativeLibs.from(
componentProperties.variantDependencies.getArtifactFileCollection(
AndroidArtifacts.ConsumedConfigType.RUNTIME_CLASSPATH,
AndroidArtifacts.ArtifactScope.PROJECT,
AndroidArtifacts.ArtifactType.JAVA_RES
)
)
nativeLibs.from(
componentProperties.variantDependencies.getArtifactFileCollection(
AndroidArtifacts.ConsumedConfigType.RUNTIME_CLASSPATH,
AndroidArtifacts.ArtifactScope.PROJECT,
AndroidArtifacts.ArtifactType.JNI
)
)
return nativeLibs
}
fun getExternalNativeLibs(componentProperties: ComponentPropertiesImpl): FileCollection {
val nativeLibs = componentProperties.globalScope.project.files()
// TODO (bug 154984238) extract native libs from java res jar before this task
nativeLibs.from(
componentProperties.variantDependencies.getArtifactFileCollection(
AndroidArtifacts.ConsumedConfigType.RUNTIME_CLASSPATH,
AndroidArtifacts.ArtifactScope.EXTERNAL,
AndroidArtifacts.ArtifactType.JAVA_RES
)
)
nativeLibs.from(
componentProperties.variantDependencies.getArtifactFileCollection(
AndroidArtifacts.ConsumedConfigType.RUNTIME_CLASSPATH,
AndroidArtifacts.ArtifactScope.EXTERNAL,
AndroidArtifacts.ArtifactType.JNI
)
)
return nativeLibs
}