blob: 9eaf601d9ac05517b461b85b532dd37407759c6d [file] [log] [blame]
// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import java.net.URI
import java.nio.file.Path
import java.nio.file.Paths
import kotlin.io.path.exists
import java.nio.file.Files.readString
import net.ltgt.gradle.errorprone.errorprone
import org.gradle.api.artifacts.ModuleVersionIdentifier
import org.gradle.api.artifacts.component.ModuleComponentIdentifier
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.spdx.sbom.gradle.SpdxSbomTask
import org.spdx.sbom.gradle.extensions.DefaultSpdxSbomTaskExtension
import com.google.gson.Gson
import java.util.UUID
plugins {
`kotlin-dsl`
id("dependencies-plugin")
id("net.ltgt.errorprone") version "3.0.1"
id("org.spdx.sbom") version "0.4.0"
}
java {
sourceSets.main.configure {
java.srcDir(getRoot().resolveAll("src", "main", "java"))
resources.srcDirs(getRoot().resolveAll("third_party", "api_database", "api_database"))
}
sourceCompatibility = JvmCompatibility.sourceCompatibility
targetCompatibility = JvmCompatibility.targetCompatibility
toolchain {
languageVersion = JavaLanguageVersion.of(JvmCompatibility.release)
}
withSourcesJar()
}
dependencies {
implementation(":keepanno")
implementation(":resourceshrinker")
compileOnly(Deps.asm)
compileOnly(Deps.asmCommons)
compileOnly(Deps.asmUtil)
compileOnly(Deps.fastUtil)
compileOnly(Deps.gson)
compileOnly(Deps.guava)
compileOnly(Deps.kotlinMetadata)
errorprone(Deps.errorprone)
}
if (project.hasProperty("spdxVersion")) {
project.version = project.property("spdxVersion")!!
}
spdxSbom {
targets {
create("r8") {
// Use of both compileClasspath and runtimeClasspath due to how the
// dependencies jar is built and dependencies above therefore use
// compileOnly for actual runtime dependencies.
configurations.set(listOf("compileClasspath", "runtimeClasspath"))
scm {
uri.set("https://r8.googlesource.com/r8/")
if (project.hasProperty("spdxRevision")) {
revision.set(project.property("spdxRevision").toString())
}
}
document {
name.set("R8 Compiler Suite")
// Generate version 5 UUID from fixed namespace UUID and name generated from revision
// (git hash) and artifact name.
if (project.hasProperty("spdxRevision")) {
namespace.set(
"https://spdx.google/"
+ uuid5(
UUID.fromString("df17ea25-709b-4edc-8dc1-d3ca82c74e8e"),
project.property("spdxRevision").toString() + "-r8"
)
)
}
creator.set("Organization: Google LLC")
packageSupplier.set("Organization: Google LLC")
}
}
}
}
val keepAnnoJarTask = projectTask("keepanno", "jar")
val resourceShrinkerJarTask = projectTask("resourceshrinker", "jar")
val resourceShrinkerDepsTask = projectTask("resourceshrinker", "depsJar")
fun mainJarDependencies() : FileCollection {
return sourceSets
.main
.get()
.compileClasspath
.filter({ "$it".contains("third_party")
&& "$it".contains("dependencies")
&& !"$it".contains("errorprone")
})
}
tasks {
withType<Exec> {
doFirst {
println("Executing command: ${commandLine.joinToString(" ")}")
}
}
withType<ProcessResources> {
dependsOn(gradle.includedBuild("shared").task(":downloadDeps"))
}
withType<SpdxSbomTask> {
taskExtension.set(object : DefaultSpdxSbomTaskExtension() {
override fun mapRepoUri(input: URI?, moduleId: ModuleVersionIdentifier): URI? {
// Locate the file origin.json with URL for download location.
fun getOriginJson() : java.nio.file.Path {
var repositoryDir =
moduleId.group.replace('.', '/') + "/" + moduleId.name + "/" + moduleId.version
return Paths.get("third_party", "dependencies", repositoryDir, "origin.json");
}
// Simple data model of the content of origin.json generated by the tool to download
// and create a local repository. E.g.:
/*
{
"artifacts": [
{
"file": "org/ow2/asm/asm/9.5/asm-9.5.pom",
"repo": "https://repo1.maven.org/maven2/",
"artifact": "org.ow2.asm:asm:pom:9.5"
},
{
"file": "org/ow2/asm/asm/9.5/asm-9.5.jar",
"repo": "https://repo1.maven.org/maven2/",
"artifact": "org.ow2.asm:asm:jar:9.5"
}
]
}
*/
data class Artifact(val file: String, val repo: String, val artifact: String)
data class Artifacts(val artifacts: List<Artifact>)
// Read origin.json.
val json = readString(getOriginJson());
val artifacts = Gson().fromJson(json, Artifacts::class.java);
return URI.create(artifacts.artifacts.get(0).repo)
}
})
}
val consolidatedLicense by registering {
dependsOn(gradle.includedBuild("shared").task(":downloadDeps"))
val root = getRoot()
val r8License = root.resolve("LICENSE")
val libraryLicense = root.resolve("LIBRARY-LICENSE")
val libraryLicenseFiles = fileTree(root.resolve("library-licensing"))
inputs.files(
listOf(r8License, libraryLicense),
libraryLicenseFiles,
mainJarDependencies().map(::zipTree))
val license = getRoot().resolveAll("build", "generatedLicense", "LICENSE")
outputs.files(license)
val dependencies = mutableListOf<String>()
configurations
.findByName("runtimeClasspath")!!
.resolvedConfiguration
.resolvedArtifacts
.forEach {
val identifier = it.id.componentIdentifier
if (identifier is ModuleComponentIdentifier) {
dependencies.add("${identifier.group}:${identifier.module}")
}
}
doLast {
val libraryLicenses = libraryLicense.readText()
dependencies.forEach {
if (!libraryLicenses.contains("- artifact: $it")) {
throw GradleException("No license for $it in LIBRARY_LICENSE")
}
}
license.getParentFile().mkdirs()
license.createNewFile()
license.writeText(buildString {
append("This file lists all licenses for code distributed.\n")
.append("All non-library code has the following 3-Clause BSD license.\n")
.append("\n")
.append("\n")
.append(r8License.readText())
.append("\n")
.append("\n")
.append("Summary of distributed libraries:\n")
.append("\n")
.append(libraryLicenses)
.append("\n")
.append("\n")
.append("Licenses details:\n")
libraryLicenseFiles.sorted().forEach { file ->
append("\n").append("\n").append(file.readText())
}
})
}
}
val swissArmyKnife by registering(Jar::class) {
dependsOn(keepAnnoJarTask)
dependsOn(resourceShrinkerJarTask)
dependsOn(gradle.includedBuild("shared").task(":downloadDeps"))
from(sourceSets.main.get().output)
exclude("com/android/tools/r8/threading/providers/**")
from(keepAnnoJarTask.outputs.files.map(::zipTree))
from(resourceShrinkerJarTask.outputs.files.map(::zipTree))
from(getRoot().resolve("LICENSE"))
entryCompression = ZipEntryCompression.STORED
manifest {
attributes["Main-Class"] = "com.android.tools.r8.SwissArmyKnife"
}
exclude("META-INF/*.kotlin_module")
exclude("**/*.kotlin_metadata")
destinationDirectory.set(getRoot().resolveAll("build", "libs"))
archiveFileName.set("r8-full-exclude-deps.jar")
}
val threadingModuleBlockingJar by registering(Zip::class) {
from(sourceSets.main.get().output)
include("com/android/tools/r8/threading/providers/blocking/**")
destinationDirectory.set(getRoot().resolveAll("build", "libs"))
archiveFileName.set("threading-module-blocking.jar")
}
val threadingModuleSingleThreadedJar by registering(Zip::class) {
from(sourceSets.main.get().output)
include("com/android/tools/r8/threading/providers/singlethreaded/**")
destinationDirectory.set(getRoot().resolveAll("build", "libs"))
archiveFileName.set("threading-module-single-threaded.jar")
}
val depsJar by registering(Zip::class) {
dependsOn(gradle.includedBuild("shared").task(":downloadDeps"))
dependsOn(resourceShrinkerDepsTask)
dependsOn(threadingModuleBlockingJar)
dependsOn(threadingModuleSingleThreadedJar)
from(threadingModuleBlockingJar.get().outputs.getFiles().map(::zipTree))
from(threadingModuleSingleThreadedJar.get().outputs.getFiles().map(::zipTree))
from(mainJarDependencies().map(::zipTree))
from(resourceShrinkerDepsTask.outputs.files.map(::zipTree))
from(consolidatedLicense)
exclude("**/module-info.class")
exclude("**/*.kotlin_metadata")
exclude("META-INF/*.kotlin_module")
exclude("META-INF/com.android.tools/**")
exclude("META-INF/LICENSE*")
exclude("META-INF/MANIFEST.MF")
exclude("META-INF/maven/**")
exclude("META-INF/proguard/**")
exclude("META-INF/versions/**")
exclude("META-INF/services/kotlin.reflect.**")
exclude("**/*.xml")
exclude("com/android/version.properties")
exclude("NOTICE")
exclude("README.md")
exclude("javax/annotation/**")
exclude("wireless/**")
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
archiveFileName.set("deps.jar")
}
val swissArmyKnifeWithoutLicense by registering(Zip::class) {
dependsOn(swissArmyKnife)
from(swissArmyKnife.get().outputs.files.map(::zipTree))
exclude("LICENSE")
archiveFileName.set("swiss-army-no-license.jar")
}
val r8WithRelocatedDeps by registering(Exec::class) {
dependsOn(depsJar)
dependsOn(swissArmyKnifeWithoutLicense)
val swissArmy = swissArmyKnifeWithoutLicense.get().outputs.getFiles().getSingleFile()
val deps = depsJar.get().outputs.files.getSingleFile()
inputs.files(listOf(swissArmy, deps))
val output = getRoot().resolveAll("build", "libs", "r8.jar")
outputs.file(output)
commandLine = baseCompilerCommandLine(
swissArmy,
deps,
"relocator",
listOf("--input",
"$swissArmy",
"--input",
"$deps",
"--output",
"$output",
// Add identity mapping to enforce no relocation of things already in package
// com.android.tools.r8.
"--map",
"com.android.tools.r8.**->com.android.tools.r8",
// Add identify for the public annotation surface of keepanno
"--map",
"com.android.tools.r8.keepanno.annotations.**->com.android.tools.r8.keepanno.annotations",
// Explicitly move all other keepanno utilities.
"--map",
"com.android.tools.r8.keepanno.**->com.android.tools.r8.relocated.keepanno",
"--map",
"com.android.**->com.android.tools.r8.com.android",
"--map",
"com.android.build.shrinker.**->com.android.tools.r8.resourceshrinker",
"--map",
"com.google.common.**->com.android.tools.r8.com.google.common",
"--map",
"com.google.gson.**->com.android.tools.r8.com.google.gson",
"--map",
"com.google.thirdparty.**->com.android.tools.r8.com.google.thirdparty",
"--map",
"org.objectweb.asm.**->com.android.tools.r8.org.objectweb.asm",
"--map",
"it.unimi.dsi.fastutil.**->com.android.tools.r8.it.unimi.dsi.fastutil",
"--map",
"kotlin.**->com.android.tools.r8.jetbrains.kotlin",
"--map",
"kotlinx.**->com.android.tools.r8.jetbrains.kotlinx",
"--map",
"org.jetbrains.**->com.android.tools.r8.org.jetbrains",
"--map",
"org.intellij.**->com.android.tools.r8.org.intellij",
"--map",
"org.checkerframework.**->com.android.tools.r8.org.checkerframework",
"--map",
"com.google.j2objc.**->com.android.tools.r8.com.google.j2objc",
"--map",
"com.google.protobuf.**->com.android.tools.r8.com.google.protobuf",
"--map",
"android.aapt.**->com.android.tools.r8.android.aapt"
))
}
}
tasks.withType<KotlinCompile> {
enabled = false
}
tasks.withType<JavaCompile> {
dependsOn(gradle.includedBuild("shared").task(":downloadDeps"))
println("NOTE: Running with JDK: " + org.gradle.internal.jvm.Jvm.current().javaHome)
// Enable error prone for D8/R8 main sources.
options.errorprone.isEnabled.set(!project.hasProperty("disable_errorprone"))
// Make all warnings errors. Warnings that we have chosen not to fix (or suppress) are disabled
// outright below.
options.compilerArgs.add("-Werror")
// Increase number of reported errors to 1000 (default is 100).
options.compilerArgs.add("-Xmaxerrs")
options.compilerArgs.add("1000")
// Non-default / Experimental checks - explicitly enforced.
options.errorprone.error("RemoveUnusedImports")
options.errorprone.error("InconsistentOverloads")
options.errorprone.error("MissingDefault")
options.errorprone.error("MultipleTopLevelClasses")
options.errorprone.error("NarrowingCompoundAssignment")
// Warnings that cause unwanted edits (e.g., inability to write informative asserts).
options.errorprone.disable("AlreadyChecked")
// JavaDoc related warnings. Would be nice to resolve but of no real consequence.
options.errorprone.disable("InvalidLink")
options.errorprone.disable("InvalidBlockTag")
options.errorprone.disable("InvalidInlineTag")
options.errorprone.disable("EmptyBlockTag")
options.errorprone.disable("MissingSummary")
options.errorprone.disable("UnrecognisedJavadocTag")
options.errorprone.disable("AlmostJavadoc")
// Moving away from identity and canonical items is not planned.
options.errorprone.disable("IdentityHashMapUsage")
}