blob: c2061656a186736c8a7916e35750f6f6ef253771 [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.build.api.component.impl.ComponentPropertiesImpl
import com.android.build.api.transform.QualifiedContent
import com.android.build.api.transform.QualifiedContent.Scope.EXTERNAL_LIBRARIES
import com.android.build.api.transform.QualifiedContent.Scope.PROJECT
import com.android.build.api.transform.QualifiedContent.Scope.PROVIDED_ONLY
import com.android.build.api.transform.QualifiedContent.Scope.SUB_PROJECTS
import com.android.build.api.transform.QualifiedContent.Scope.TESTED_CODE
import com.android.build.gradle.internal.InternalScope.FEATURES
import com.android.build.gradle.internal.errors.MessageReceiverImpl
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.options.SyncOptions
import com.android.builder.multidex.D8MainDexList
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.FileCollection
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.logging.Logging
import org.gradle.api.provider.Property
import org.gradle.api.tasks.CacheableTask
import org.gradle.api.tasks.Classpath
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.Optional
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 javax.inject.Inject
/**
* Calculate the main dex list using D8.
*/
@CacheableTask
abstract class D8MainDexListTask : NonIncrementalTask() {
@get:Input
abstract val errorFormat: Property<SyncOptions.ErrorFormatMode>
@get:InputFile
@get:PathSensitive(PathSensitivity.NONE)
abstract val aaptGeneratedRules: RegularFileProperty
@get:Optional
@get:InputFile
@get:PathSensitive(PathSensitivity.NONE)
abstract val userMultidexProguardRules: RegularFileProperty
@get:Optional
@get:InputFile
@get:PathSensitive(PathSensitivity.NONE)
abstract val userMultidexKeepFile: RegularFileProperty
@get:Classpath
abstract val bootClasspath: ConfigurableFileCollection
@get:Classpath
abstract val inputClasses: ConfigurableFileCollection
@get:Classpath
abstract val libraryClasses: ConfigurableFileCollection
@get:OutputFile
abstract val output: RegularFileProperty
override fun doTaskAction() {
// Javac output may be missing if there are no .java sources, so filter missing b/152759930.
val programClasses = inputClasses.files.filter { it.exists() }
val libraryFilesNotInInputs =
libraryClasses.files.filter { !programClasses.contains(it) } + bootClasspath.files
getWorkerFacadeWithWorkers().use {
it.submit(
MainDexListRunnable::class.java,
MainDexListRunnable.Params(
listOfNotNull(
aaptGeneratedRules.get().asFile,
userMultidexProguardRules.orNull?.asFile),
programClasses,
libraryFilesNotInInputs,
userMultidexKeepFile.orNull?.asFile,
output.get().asFile,
errorFormat.get()
)
)
}
}
class MainDexListRunnable @Inject constructor(val params: Params) : Runnable {
class Params(
val proguardRules: Collection<File>,
val programFiles: Collection<File>,
val libraryFiles: Collection<File>,
val userMultidexKeepFile: File?,
val output: File,
val errorFormat: SyncOptions.ErrorFormatMode
) : Serializable
override fun run() {
val logger = Logging.getLogger(D8MainDexListTask::class.java)
logger.debug("Generating the main dex list using D8.")
logger.debug("Program files: %s", params.programFiles.joinToString())
logger.debug("Library files: %s", params.libraryFiles.joinToString())
logger.debug("Proguard rule files: %s", params.proguardRules.joinToString())
val mainDexClasses = mutableSetOf<String>()
mainDexClasses.addAll(
D8MainDexList.generate(
getPlatformRules(),
params.proguardRules.map { it.toPath() },
params.programFiles.map { it.toPath() },
params.libraryFiles.map { it.toPath() },
MessageReceiverImpl(params.errorFormat, logger)
)
)
params.userMultidexKeepFile?.let {
mainDexClasses.addAll(it.readLines())
}
params.output.writeText(mainDexClasses.joinToString(separator = System.lineSeparator()))
}
}
class CreationAction(
componentProperties: ComponentPropertiesImpl,
private val includeDynamicFeatures: Boolean
) : VariantTaskCreationAction<D8MainDexListTask, ComponentPropertiesImpl>(
componentProperties
) {
private val inputClasses: FileCollection
private val libraryClasses: FileCollection
init {
val inputScopes: Set<QualifiedContent.ScopeType> = setOf(
PROJECT,
SUB_PROJECTS,
EXTERNAL_LIBRARIES
) + (if (includeDynamicFeatures) setOf(FEATURES) else emptySet())
val libraryScopes = setOf(PROVIDED_ONLY, TESTED_CODE)
// It is ok to get streams that have more types/scopes than we are asking for, so just
// check if intersection is not empty. This is what TransformManager does.
inputClasses = componentProperties.transformManager
.getPipelineOutputAsFileCollection { contentTypes, scopes ->
contentTypes.contains(
QualifiedContent.DefaultContentType.CLASSES
) && inputScopes.intersect(scopes).isNotEmpty()
}
libraryClasses = componentProperties.transformManager
.getPipelineOutputAsFileCollection { contentTypes, scopes ->
contentTypes.contains(
QualifiedContent.DefaultContentType.CLASSES
) && libraryScopes.intersect(scopes).isNotEmpty()
}
}
override val name: String =
componentProperties.computeTaskName(if (includeDynamicFeatures) "bundleMultiDexList" else "multiDexList")
override val type: Class<D8MainDexListTask> = D8MainDexListTask::class.java
override fun handleProvider(
taskProvider: TaskProvider<D8MainDexListTask>
) {
super.handleProvider(taskProvider)
val request = creationConfig.artifacts.setInitialProvider(
taskProvider, D8MainDexListTask::output
).withName("mainDexList.txt")
if (includeDynamicFeatures) {
request.on(InternalArtifactType.MAIN_DEX_LIST_FOR_BUNDLE)
} else {
request.on(InternalArtifactType.LEGACY_MULTIDEX_MAIN_DEX_LIST)
}
}
override fun configure(
task: D8MainDexListTask
) {
super.configure(task)
creationConfig.artifacts.setTaskInputToFinalProduct(
InternalArtifactType.LEGACY_MULTIDEX_AAPT_DERIVED_PROGUARD_RULES,
task.aaptGeneratedRules
)
val variantDslInfo = creationConfig.variantDslInfo
val project = creationConfig.globalScope.project
if (variantDslInfo.multiDexKeepProguard != null) {
task.userMultidexProguardRules.fileProvider(
project.provider { variantDslInfo.multiDexKeepProguard }
)
}
task.userMultidexProguardRules.disallowChanges()
if (variantDslInfo.multiDexKeepFile != null) {
task.userMultidexKeepFile.fileProvider(
project.provider { variantDslInfo.multiDexKeepFile }
)
}
task.userMultidexKeepFile.disallowChanges()
task.inputClasses.from(inputClasses).disallowChanges()
task.libraryClasses.from(libraryClasses).disallowChanges()
task.bootClasspath.from(creationConfig.variantScope.bootClasspath).disallowChanges()
task.errorFormat
.setDisallowChanges(
SyncOptions.getErrorFormatMode(creationConfig.services.projectOptions))
}
}
}
internal fun getPlatformRules(): List<String> = listOf(
"-keep public class * extends android.app.Instrumentation {\n"
+ " <init>(); \n"
+ " void onCreate(...);\n"
+ " android.app.Application newApplication(...);\n"
+ " void callApplicationOnCreate(android.app.Application);\n"
+ "}",
"-keep public class * extends android.app.Application { "
+ " <init>();\n"
+ " void attachBaseContext(android.content.Context);\n"
+ "}",
"-keep public class * extends android.app.backup.BackupAgent { <init>(); }",
"-keep public class * implements java.lang.annotation.Annotation { *;}",
"-keep public class * extends android.test.InstrumentationTestCase { <init>(); }"
)