blob: 6ba6a032762f6c4acc75c7220c570e4d9e961eed [file] [log] [blame]
/*
* Copyright (C) 2014 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
import com.android.build.api.variant.BuiltArtifacts
import com.android.build.api.variant.impl.BuiltArtifactsImpl
import com.android.build.api.variant.impl.VariantOutputImpl
import com.android.build.api.variant.impl.dirName
import com.android.build.gradle.internal.LoggerWrapper
import com.android.build.gradle.internal.component.LibraryCreationConfig
import com.android.build.gradle.internal.scope.InternalArtifactType
import com.android.build.gradle.internal.scope.InternalArtifactType.MANIFEST_MERGE_REPORT
import com.android.build.gradle.internal.tasks.factory.VariantTaskCreationAction
import com.android.build.gradle.internal.tasks.manifest.mergeManifestsForApplication
import com.android.build.gradle.internal.utils.setDisallowChanges
import com.android.manifmerger.ManifestMerger2
import com.android.manifmerger.MergingReport
import com.android.utils.FileUtils
import com.google.common.annotations.VisibleForTesting
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.MapProperty
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.Nested
import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.PathSensitive
import org.gradle.api.tasks.PathSensitivity
import org.gradle.api.tasks.TaskProvider
import java.io.File
import java.io.IOException
import java.io.Serializable
import java.io.UncheckedIOException
import javax.inject.Inject
/** a Task that only merge a single manifest with its overlays. */
@CacheableTask
abstract class ProcessLibraryManifest : ManifestProcessorTask() {
@get:OutputFile
abstract val manifestOutputFile: RegularFileProperty
@get:Optional
@get:Input
abstract val packageOverride: Property<String>
@get:Input
@get:Optional
abstract val versionCode: Property<Int?>
@get:Optional
@get:Input
abstract val versionName: Property<String?>
@get:PathSensitive(PathSensitivity.RELATIVE)
@get:InputFiles
abstract val manifestOverlays: ListProperty<File>
@get:Optional
@get:Input
abstract val manifestPlaceholders: MapProperty<String, Any>
/** The processed Manifests files folder. */
@get:OutputDirectory
abstract val packagedManifestOutputDirectory: DirectoryProperty
/**
* The aapt friendly processed Manifest. In case we are processing a library manifest, some
* placeholders may not have been resolved (and will be when the library is merged into the
* importing application). However, such placeholders keys are not friendly to aapt which flags
* some illegal characters. Such characters are replaced/encoded in this version.
*/
@get:Optional
@get:OutputDirectory
abstract val aaptFriendlyManifestOutputDirectory: DirectoryProperty
/**
* The aapt friendly processed Manifest. In case we are processing a library manifest, some
* placeholders may not have been resolved (and will be when the library is merged into the
* importing application). However, such placeholders keys are not friendly to aapt which flags
* some illegal characters. Such characters are replaced/encoded in this version.
*/
@get:Internal
val aaptFriendlyManifestOutputFile: File?
get() = if (aaptFriendlyManifestOutputDirectory.isPresent) FileUtils.join(
aaptFriendlyManifestOutputDirectory.get().asFile,
mainSplit.get().dirName(),
SdkConstants.ANDROID_MANIFEST_XML
) else null
@VisibleForTesting
@get:Nested
abstract val mainSplit: Property<VariantOutputImpl>
private var isNamespaced = false
override fun doFullTaskAction() {
getWorkerFacadeWithWorkers().use { workers ->
val manifestOutputDirectory = packagedManifestOutputDirectory
val aaptFriendlyManifestOutputDirectory =
aaptFriendlyManifestOutputDirectory
workers.submit(
ProcessLibRunnable::class.java,
ProcessLibParams(
variantName,
aaptFriendlyManifestOutputFile,
isNamespaced,
mainManifest.get(),
manifestOverlays.get(),
packageOverride.get(),
versionCode.orNull,
versionName.orNull,
minSdkVersion.orNull,
targetSdkVersion.orNull,
maxSdkVersion.orNull,
manifestOutputFile.get().asFile,
manifestPlaceholders.get(),
reportFile.get().asFile,
mergeBlameFile.get().asFile,
if (manifestOutputDirectory.isPresent) manifestOutputDirectory.get().asFile else null,
if (aaptFriendlyManifestOutputDirectory.isPresent) aaptFriendlyManifestOutputDirectory.get().asFile else null,
mainSplit.get().toSerializedForm()
)
)
}
}
class ProcessLibParams(
val variantName: String,
val aaptFriendlyManifestOutputFile: File?,
val isNamespaced: Boolean,
val mainManifest: File,
val manifestOverlays: List<File>,
val packageOverride: String,
val versionCode: Int?,
val versionName: String?,
val minSdkVersion: String?,
val targetSdkVersion: String?,
val maxSdkVersion: Int?,
val manifestOutputFile: File,
val manifestPlaceholders: Map<String, Any>,
val reportFile: File,
val mergeBlameFile: File,
val manifestOutputDirectory: File?,
val aaptFriendlyManifestOutputDirectory: File?,
val mainSplit: VariantOutputImpl.SerializedForm
) : Serializable
class ProcessLibRunnable @Inject constructor(private val params: ProcessLibParams) :
Runnable {
override fun run() {
val optionalFeatures: Collection<ManifestMerger2.Invoker.Feature> =
if (params.isNamespaced) listOf(
ManifestMerger2.Invoker.Feature.FULLY_NAMESPACE_LOCAL_RESOURCES
) else emptyList()
val mergingReport = mergeManifestsForApplication(
params.mainManifest,
params.manifestOverlays, emptyList(), emptyList(),
null,
params.packageOverride,
params.versionCode,
params.versionName,
params.minSdkVersion,
params.targetSdkVersion,
params.maxSdkVersion,
params.manifestOutputFile.absolutePath,
if (params.aaptFriendlyManifestOutputFile != null) params.aaptFriendlyManifestOutputFile.absolutePath else null,
ManifestMerger2.MergeType.LIBRARY /* outInstantRunManifestLocation */,
params.manifestPlaceholders /* outInstantAppManifestLocation */,
optionalFeatures,
emptyList(),
params.reportFile, LoggerWrapper.getLogger(ProcessLibraryManifest::class.java)
)
val mergedXmlDocument =
mergingReport.getMergedXmlDocument(MergingReport.MergedManifestKind.MERGED)
try {
outputMergeBlameContents(
mergingReport,
params.mergeBlameFile
)
} catch (e: IOException) {
throw UncheckedIOException(e)
}
val properties =
if (mergedXmlDocument != null) mapOf(
"packageId" to mergedXmlDocument.packageName,
"split" to mergedXmlDocument.splitName
) else mapOf()
if (params.manifestOutputDirectory != null) {
BuiltArtifactsImpl(
BuiltArtifacts.METADATA_FILE_VERSION,
InternalArtifactType.PACKAGED_MANIFESTS,
params.packageOverride,
params.variantName,
listOf(
params.mainSplit.toBuiltArtifact(
params.manifestOutputFile, properties
)
)
)
.saveToDirectory(params.manifestOutputDirectory)
}
if (params.aaptFriendlyManifestOutputDirectory != null) {
BuiltArtifactsImpl(
BuiltArtifacts.METADATA_FILE_VERSION,
InternalArtifactType.AAPT_FRIENDLY_MERGED_MANIFESTS,
params.packageOverride,
params.variantName,
listOf(
params.mainSplit.toBuiltArtifact(
params.aaptFriendlyManifestOutputFile!!, properties
)
)
)
.saveToDirectory(params.aaptFriendlyManifestOutputDirectory)
}
}
}
@get:Optional
@get:Input
abstract val minSdkVersion: Property<String?>
@get:Optional
@get:Input
abstract val targetSdkVersion: Property<String?>
@get:Optional
@get:Input
abstract val maxSdkVersion: Property<Int?>
@get:PathSensitive(PathSensitivity.RELATIVE)
@get:InputFile
abstract val mainManifest: Property<File>
class CreationAction
/** `EagerTaskCreationAction` for the library process manifest task. */(creationConfig: LibraryCreationConfig) :
VariantTaskCreationAction<ProcessLibraryManifest, LibraryCreationConfig>(creationConfig) {
override val name: String
get() = computeTaskName("process", "Manifest")
override val type: Class<ProcessLibraryManifest>
get() = ProcessLibraryManifest::class.java
override fun handleProvider(
taskProvider: TaskProvider<ProcessLibraryManifest>
) {
super.handleProvider(taskProvider)
creationConfig.taskContainer.processManifestTask = taskProvider
val artifacts = creationConfig.artifacts
artifacts.setInitialProvider(
taskProvider,
ProcessLibraryManifest::aaptFriendlyManifestOutputDirectory
).withName("aapt").on(InternalArtifactType.AAPT_FRIENDLY_MERGED_MANIFESTS)
artifacts.setInitialProvider(
taskProvider,
ProcessLibraryManifest::packagedManifestOutputDirectory
).on(InternalArtifactType.PACKAGED_MANIFESTS)
artifacts.setInitialProvider(
taskProvider
) { task: ProcessLibraryManifest -> task.manifestOutputFile }
.withName(SdkConstants.ANDROID_MANIFEST_XML)
.on(InternalArtifactType.LIBRARY_MANIFEST)
artifacts.setInitialProvider(
taskProvider,
ProcessLibraryManifest::mergeBlameFile
).withName("manifest-merger-blame-" + creationConfig.baseName + "-report.txt")
.on(InternalArtifactType.MANIFEST_MERGE_BLAME_FILE)
artifacts.setInitialProvider(
taskProvider,
ProcessLibraryManifest::reportFile
)
.atLocation(
FileUtils.join(creationConfig.globalScope.outputsDir, "logs")
.absolutePath
)
.withName("manifest-merger-" + creationConfig.baseName + "-report.txt")
.on(MANIFEST_MERGE_REPORT)
}
override fun configure(
task: ProcessLibraryManifest
) {
super.configure(task)
val variantDslInfo = creationConfig.variantDslInfo
val variantSources = creationConfig.variantSources
val project = creationConfig.globalScope.project
task.minSdkVersion
.set(project.provider { creationConfig.minSdkVersion.apiString })
task.minSdkVersion.disallowChanges()
task.targetSdkVersion
.set(
project.provider {
val targetSdkVersion =
creationConfig.targetSdkVersion
if (targetSdkVersion.apiLevel < 0) {
return@provider null
}
targetSdkVersion.apiString
}
)
task.targetSdkVersion.disallowChanges()
task.maxSdkVersion
.set(project.provider(creationConfig::maxSdkVersion))
task.maxSdkVersion.disallowChanges()
task.mainSplit.set(project.provider { creationConfig.outputs.getMainSplit() })
task.mainSplit.disallowChanges()
task.isNamespaced =
creationConfig.globalScope.extension.aaptOptions.namespaced
task.versionName.setDisallowChanges(variantDslInfo.versionName)
task.versionCode.setDisallowChanges(variantDslInfo.versionCode)
task.packageOverride.set(creationConfig.applicationId)
task.packageOverride.disallowChanges()
task.manifestPlaceholders.set(
task.project.provider(
variantDslInfo::manifestPlaceholders
)
)
task.manifestPlaceholders.disallowChanges()
task.mainManifest
.set(project.provider(variantSources::mainManifestFilePath))
task.mainManifest.disallowChanges()
task.manifestOverlays.set(
task.project.provider(variantSources::manifestOverlays)
)
task.manifestOverlays.disallowChanges()
}
}
}