blob: ce9bb76150fe11f7715c03a4c76bb67fcd8e9b30 [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.artifact.ArtifactType
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.api.variant.impl.getApiString
import com.android.build.gradle.internal.LoggerWrapper
import com.android.build.gradle.internal.component.LibraryCreationConfig
import com.android.build.gradle.internal.profile.ProfileAwareWorkAction
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.mergeManifests
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.UncheckedIOException
/** 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: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() {
workerExecutor.noIsolation().submit(ProcessLibWorkAction::class.java) {
it.initializeFromAndroidVariantTask(this)
it.variantName.set(variantName)
it.aaptFriendlyManifestOutputFile.set(aaptFriendlyManifestOutputFile)
it.namespaced.set(isNamespaced)
it.mainManifest.set(mainManifest.get())
it.manifestOverlays.set(manifestOverlays)
it.packageOverride.set(packageOverride)
it.minSdkVersion.set(minSdkVersion)
it.targetSdkVersion.set(targetSdkVersion)
it.maxSdkVersion.set(maxSdkVersion)
it.manifestOutputFile.set(manifestOutputFile)
it.manifestPlaceholders.set(manifestPlaceholders)
it.reportFile.set(reportFile)
it.mergeBlameFile.set(mergeBlameFile)
it.manifestOutputDirectory.set(packagedManifestOutputDirectory)
it.aaptFriendlyManifestOutputDirectory.set(aaptFriendlyManifestOutputDirectory)
it.mainSplit.set(mainSplit.get().toSerializedForm())
}
}
abstract class ProcessLibParams: ProfileAwareWorkAction.Parameters() {
abstract val variantName: Property<String>
abstract val aaptFriendlyManifestOutputFile: RegularFileProperty
abstract val namespaced: Property<Boolean>
abstract val mainManifest: RegularFileProperty
abstract val manifestOverlays: ListProperty<File>
abstract val packageOverride: Property<String>
abstract val minSdkVersion: Property<String>
abstract val targetSdkVersion: Property<String>
abstract val maxSdkVersion: Property<Int>
abstract val manifestOutputFile: RegularFileProperty
abstract val manifestPlaceholders: MapProperty<String, Any>
abstract val reportFile: RegularFileProperty
abstract val mergeBlameFile: RegularFileProperty
abstract val manifestOutputDirectory: DirectoryProperty
abstract val aaptFriendlyManifestOutputDirectory: DirectoryProperty
abstract val mainSplit: Property<VariantOutputImpl.SerializedForm>
}
abstract class ProcessLibWorkAction : ProfileAwareWorkAction<ProcessLibParams>() {
override fun run() {
val optionalFeatures: Collection<ManifestMerger2.Invoker.Feature> =
if (parameters.namespaced.get()) listOf(
ManifestMerger2.Invoker.Feature.FULLY_NAMESPACE_LOCAL_RESOURCES
) else emptyList()
val mergingReport = mergeManifests(
parameters.mainManifest.asFile.get(),
parameters.manifestOverlays.get(), emptyList(), emptyList(),
null,
parameters.packageOverride.get(),
null,
null,
parameters.minSdkVersion.orNull,
parameters.targetSdkVersion.orNull,
parameters.maxSdkVersion.orNull,
parameters.manifestOutputFile.asFile.get().absolutePath,
parameters.aaptFriendlyManifestOutputFile.asFile.orNull?.absolutePath,
ManifestMerger2.MergeType.LIBRARY /* outInstantRunManifestLocation */,
parameters.manifestPlaceholders.get() /* outInstantAppManifestLocation */,
optionalFeatures,
emptyList(),
parameters.reportFile.asFile.get(), LoggerWrapper.getLogger(ProcessLibraryManifest::class.java)
)
val mergedXmlDocument =
mergingReport.getMergedXmlDocument(MergingReport.MergedManifestKind.MERGED)
try {
outputMergeBlameContents(
mergingReport,
parameters.mergeBlameFile.asFile.get()
)
} catch (e: IOException) {
throw UncheckedIOException(e)
}
val properties =
if (mergedXmlDocument != null) mapOf(
"packageId" to mergedXmlDocument.packageName,
"split" to mergedXmlDocument.splitName
) else mapOf()
if (parameters.manifestOutputDirectory.isPresent) {
BuiltArtifactsImpl(
BuiltArtifacts.METADATA_FILE_VERSION,
InternalArtifactType.PACKAGED_MANIFESTS,
parameters.packageOverride.get(),
parameters.variantName.get(),
listOf(
parameters.mainSplit.get().toBuiltArtifact(
parameters.manifestOutputFile.asFile.get(), properties
)
)
)
.saveToDirectory(parameters.manifestOutputDirectory.asFile.get())
}
if (parameters.aaptFriendlyManifestOutputDirectory.isPresent) {
BuiltArtifactsImpl(
BuiltArtifacts.METADATA_FILE_VERSION,
InternalArtifactType.AAPT_FRIENDLY_MERGED_MANIFESTS,
parameters.packageOverride.get(),
parameters.variantName.get(),
listOf(
parameters.mainSplit.get().toBuiltArtifact(
parameters.aaptFriendlyManifestOutputFile.asFile.get(), properties
)
)
)
.saveToDirectory(parameters.aaptFriendlyManifestOutputDirectory.asFile.get())
}
}
}
@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 }
.on(ArtifactType.MERGED_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.getApiString() })
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.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()
}
}
}