blob: c22fe9270f6a6e37ebaa4d7875f26ca9b602621f [file] [log] [blame]
/*
* Copyright (C) 2018 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.build.api.component.impl.ComponentPropertiesImpl
import com.android.build.api.variant.impl.BuiltArtifactImpl
import com.android.build.api.variant.impl.BuiltArtifactsImpl
import com.android.build.gradle.internal.component.ApkCreationConfig
import com.android.build.gradle.internal.scope.InternalArtifactType
import com.android.build.gradle.internal.tasks.factory.VariantTaskCreationAction
import com.android.build.gradle.internal.utils.setDisallowChanges
import com.android.build.gradle.internal.utils.toImmutableSet
import com.android.build.gradle.options.BooleanOption
import com.android.build.gradle.options.StringOption
import com.android.bundle.Devices.DeviceSpec
import com.android.tools.build.bundletool.commands.ExtractApksCommand
import com.android.utils.FileUtils
import com.google.protobuf.util.JsonFormat
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFile
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.Serializable
import java.nio.file.Files
import javax.inject.Inject
/**
* Task that extract APKs from the apk zip (created with [BundleToApkTask] into a folder. a Device
* info file indicate which APKs to extract. Only APKs for that particular device are extracted.
*/
abstract class ExtractApksTask : NonIncrementalTask() {
companion object {
const val namePrefix = "extractApksFor"
fun getTaskName(componentProperties: ComponentPropertiesImpl): String {
return componentProperties.computeTaskName(namePrefix)
}
}
@get:InputFile
@get:PathSensitive(PathSensitivity.NONE)
abstract val apkSetArchive: RegularFileProperty
@get:InputFile
@get:Optional
@get:PathSensitive(PathSensitivity.NONE)
var deviceConfig: File? = null
private set
@get:OutputDirectory
abstract val outputDir: DirectoryProperty
@get:OutputFile
abstract val apksFromBundleIdeModel: RegularFileProperty
@get:Input
abstract val applicationId: Property<String>
@get:Input
@get:Optional
abstract val dynamicModulesToInstall: ListProperty<String>
@get:Input
var extractInstant = false
private set
override fun doTaskAction() {
getWorkerFacadeWithWorkers().use {
it.submit(
BundleToolRunnable::class.java,
Params(
apkSetArchive.get().asFile,
deviceConfig
?: throw RuntimeException("Calling ExtractApk with no device config"),
outputDir.get().asFile,
extractInstant,
apksFromBundleIdeModel.get().asFile,
applicationId.get(),
variantName,
dynamicModulesToInstall.getOrElse(listOf())
)
)
}
}
private data class Params(
val apkSetArchive: File,
val deviceConfig: File,
val outputDir: File,
val extractInstant: Boolean,
val apksFromBundleIdeModel: File,
val applicationId: String,
val variantName: String,
val optionalListOfDynamicModulesToInstall: List<String>
) : Serializable
private class BundleToolRunnable @Inject constructor(private val params: Params): Runnable {
override fun run() {
FileUtils.cleanOutputDir(params.outputDir)
val builder: DeviceSpec.Builder = DeviceSpec.newBuilder()
Files.newBufferedReader(params.deviceConfig.toPath(), Charsets.UTF_8).use {
JsonFormat.parser().merge(it, builder)
}
val command = ExtractApksCommand
.builder()
.setApksArchivePath(params.apkSetArchive.toPath())
.setDeviceSpec(builder.build())
.setOutputDirectory(params.outputDir.toPath())
.setInstant(params.extractInstant)
.also {
if (params.optionalListOfDynamicModulesToInstall.isNotEmpty())
it.setModules(
params.optionalListOfDynamicModulesToInstall.toImmutableSet())
}
command.build().execute()
BuiltArtifactsImpl(
artifactType = InternalArtifactType.EXTRACTED_APKS,
applicationId = params.applicationId,
variantName = params.variantName,
elements = listOf(
BuiltArtifactImpl.make(outputFile = params.outputDir.absolutePath)
)
).saveToFile(params.apksFromBundleIdeModel)
}
}
class CreationAction(creationConfig: ApkCreationConfig) :
VariantTaskCreationAction<ExtractApksTask, ApkCreationConfig>(
creationConfig
) {
override val name: String
get() = computeTaskName(namePrefix)
override val type: Class<ExtractApksTask>
get() = ExtractApksTask::class.java
override fun handleProvider(
taskProvider: TaskProvider<ExtractApksTask>
) {
super.handleProvider(taskProvider)
creationConfig.artifacts.setInitialProvider(
taskProvider,
ExtractApksTask::outputDir)
.on(InternalArtifactType.EXTRACTED_APKS)
creationConfig.artifacts.setInitialProvider(
taskProvider,
ExtractApksTask::apksFromBundleIdeModel)
.withName(BuiltArtifactsImpl.METADATA_FILE_NAME)
.on(InternalArtifactType.APK_FROM_BUNDLE_IDE_MODEL)
}
override fun configure(
task: ExtractApksTask
) {
super.configure(task)
creationConfig.artifacts.setTaskInputToFinalProduct(
InternalArtifactType.APKS_FROM_BUNDLE,
task.apkSetArchive)
val devicePath = creationConfig.services.projectOptions.get(StringOption.IDE_APK_SELECT_CONFIG)
if (devicePath != null) {
task.deviceConfig = File(devicePath)
}
task.extractInstant = creationConfig.services.projectOptions.get(BooleanOption.IDE_EXTRACT_INSTANT)
task.applicationId.setDisallowChanges(creationConfig.applicationId)
val optionalListOfDynamicModulesToInstall = creationConfig.services.projectOptions.get(
StringOption.IDE_INSTALL_DYNAMIC_MODULES_LIST
)
if (!optionalListOfDynamicModulesToInstall.isNullOrEmpty()) {
task.dynamicModulesToInstall.addAll(
optionalListOfDynamicModulesToInstall.split(','))
}
task.dynamicModulesToInstall.disallowChanges()
}
}
}