blob: f40e0873d0823c06e424d21b5cfab7355e1aab9a [file] [log] [blame]
/*
* Copyright (C) 2017 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.transforms
import com.android.build.api.artifact.BuildableArtifact
import com.android.build.api.transform.QualifiedContent
import com.android.build.api.transform.QualifiedContent.ContentType
import com.android.build.api.transform.QualifiedContent.Scope
import com.android.build.api.transform.SecondaryFile
import com.android.build.api.transform.Transform
import com.android.build.api.transform.TransformException
import com.android.build.api.transform.TransformInvocation
import com.android.build.gradle.internal.LoggerWrapper
import com.android.build.gradle.internal.api.artifact.singleFile
import com.android.build.gradle.internal.pipeline.TransformManager
import com.android.build.gradle.internal.scope.InternalArtifactType
import com.android.build.gradle.internal.scope.VariantScope
import com.android.builder.multidex.D8MainDexList
import com.android.ide.common.blame.MessageReceiver
import com.google.common.collect.ImmutableList
import com.google.common.collect.ImmutableMap
import com.google.common.collect.ImmutableSet
import com.google.common.collect.Sets
import java.io.File
import java.nio.file.Files
import java.nio.file.Path
import java.util.function.Supplier
/**
* Calculate the main dex list using D8.
*/
class D8MainDexListTransform(
private val manifestProguardRules: BuildableArtifact,
private val userProguardRules: Path? = null,
private val userClasses: Path? = null,
private val includeDynamicFeatures: Boolean = false,
private val bootClasspath: Supplier<List<Path>>,
private val messageReceiver: MessageReceiver) : Transform(), MainDexListWriter {
private val logger = LoggerWrapper.getLogger(D8MainDexListTransform::class.java)
private lateinit var outputMainDexList: Path
@JvmOverloads
constructor(variantScope: VariantScope, includeDynamicFeatures: Boolean = false) :
this(
variantScope.artifacts.getFinalArtifactFiles(InternalArtifactType.LEGACY_MULTIDEX_AAPT_DERIVED_PROGUARD_RULES),
variantScope.variantConfiguration.multiDexKeepProguard?.toPath(),
variantScope.variantConfiguration.multiDexKeepFile?.toPath(),
includeDynamicFeatures,
Supplier {
variantScope
.globalScope
.androidBuilder
.getBootClasspath(true)
.map { it.toPath() }},
variantScope.globalScope.messageReceiver)
override fun setMainDexListOutputFile(mainDexListFile: File) {
this.outputMainDexList = mainDexListFile.toPath()
}
override fun getName(): String = if (includeDynamicFeatures) "bundleMultiDexList" else "multidexlist"
override fun getInputTypes(): ImmutableSet<out ContentType> =
Sets.immutableEnumSet(QualifiedContent.DefaultContentType.CLASSES)
override fun getScopes(): ImmutableSet<in Scope> = ImmutableSet.of()
override fun getReferencedScopes(): MutableSet<in Scope> {
val referenced = Sets.immutableEnumSet(
Scope.PROJECT,
Scope.SUB_PROJECTS,
Scope.EXTERNAL_LIBRARIES,
Scope.PROVIDED_ONLY,
Scope.TESTED_CODE
)
if (!includeDynamicFeatures) {
return referenced
}
return Sets.union(referenced, TransformManager.SCOPE_FEATURES)
}
override fun isIncremental(): Boolean = false
override fun isCacheable(): Boolean = true
override fun getSecondaryFiles(): MutableCollection<SecondaryFile> =
ImmutableList.builder<SecondaryFile>().apply {
add(SecondaryFile.nonIncremental(manifestProguardRules))
userProguardRules?.let { add(SecondaryFile.nonIncremental(it.toFile())) }
userClasses?.let { add(SecondaryFile.nonIncremental(it.toFile())) }
}.build()
override fun getSecondaryFileOutputs(): ImmutableList<File> =
ImmutableList.of(outputMainDexList.toFile())
override fun getParameterInputs(): ImmutableMap<String, Any> =
ImmutableMap.of("implementation", D8MainDexListTransform::class.java.name)
override fun transform(invocation: TransformInvocation) {
logger.verbose("Generating the main dex list using D8.")
try {
val inputs = getByInputType(invocation)
val programFiles = inputs[ProguardInput.INPUT_JAR]!!
val libraryFiles = inputs[ProguardInput.LIBRARY_JAR]!! + bootClasspath.get()
logger.verbose("Program files: %s", programFiles.joinToString())
logger.verbose("Library files: %s", libraryFiles.joinToString())
logger.verbose(
"Proguard rule files: %s",
listOfNotNull(manifestProguardRules, userProguardRules).joinToString())
val proguardRules =
listOfNotNull(manifestProguardRules.singleFile().toPath(), userProguardRules)
val mainDexClasses = mutableSetOf<String>()
mainDexClasses.addAll(
D8MainDexList.generate(
getPlatformRules(),
proguardRules,
programFiles,
libraryFiles,
messageReceiver
)
)
if (userClasses != null) {
mainDexClasses.addAll(Files.readAllLines(userClasses))
}
Files.deleteIfExists(outputMainDexList)
Files.write(outputMainDexList, mainDexClasses)
} catch (e: D8MainDexList.MainDexListException) {
throw TransformException("Error while generating the main dex list:${System.lineSeparator()}${e.message}", e)
}
}
private fun getByInputType(invocation: TransformInvocation): Map<ProguardInput, List<Path>> {
val libraryScopes = Sets.immutableEnumSet(Scope.PROVIDED_ONLY, Scope.TESTED_CODE)
val (inputs, libraries) =
invocation.referencedInputs
.flatMap { it.directoryInputs + it.jarInputs }
.asSequence().partition {
!it.scopes.minus(
libraryScopes
).isEmpty()
}
return mapOf(
ProguardInput.INPUT_JAR to inputs.map { it.file.toPath() },
ProguardInput.LIBRARY_JAR to libraries.map { it.file.toPath() })
}
}
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"
+ " Z onException(java.lang.Object, java.lang.Throwable);\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>(); }"
)
private enum class ProguardInput {
INPUT_JAR,
LIBRARY_JAR
}