Skip to content

Commit

Permalink
Use an artifact transform instead of doing an embedded jar (#26)
Browse files Browse the repository at this point in the history
* Use an artifact transform instead of doing an embedded jar

* update apiDump
  • Loading branch information
martinbonnin authored Nov 25, 2024
1 parent 3f49bff commit 419611a
Show file tree
Hide file tree
Showing 8 changed files with 110 additions and 122 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,10 @@ gr8 {

// Use a version from https://storage.googleapis.com/r8-releases/raw
// Requires a maven("https://storage.googleapis.com/r8-releases/raw") repository
r8Version = "8.8.19"
r8Version("8.8.19")
// Or use a commit
// The jar is downloaded on demand
r8Version = "887704078a06fc0090e7772c921a30602bf1a49f"
r8Version("887704078a06fc0090e7772c921a30602bf1a49f")
// Or leave it to the default version
}

Expand Down
4 changes: 2 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import com.gradleup.librarian.gradle.librarianRoot
import com.gradleup.librarian.gradle.Librarian

buildscript {
dependencies {
Expand All @@ -13,4 +13,4 @@ buildscript {
}
}

librarianRoot()
Librarian.root(project)
21 changes: 18 additions & 3 deletions gr8-plugin-common/api/gr8-plugin-common.api
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,30 @@ public abstract class com/gradleup/gr8/DownloadR8Task : org/gradle/api/DefaultTa
public final fun taskAction ()V
}

public abstract class com/gradleup/gr8/FilterTransform : org/gradle/api/artifacts/transform/TransformAction {
public static final field Companion Lcom/gradleup/gr8/FilterTransform$Companion;
public fun <init> ()V
public abstract fun getInputArtifact ()Lorg/gradle/api/provider/Provider;
public fun transform (Lorg/gradle/api/artifacts/transform/TransformOutputs;)V
}

public final class com/gradleup/gr8/FilterTransform$Companion {
public final fun getArtifactType ()Ljava/lang/String;
}

public abstract interface class com/gradleup/gr8/FilterTransform$Parameters : org/gradle/api/artifacts/transform/TransformParameters {
public abstract fun getExcludes ()Ljava/util/List;
public abstract fun setExcludes (Ljava/util/List;)V
}

public class com/gradleup/gr8/Gr8Configurator {
public fun <init> (Ljava/lang/String;Lorg/gradle/api/Project;Lorg/gradle/jvm/toolchain/JavaToolchainService;)V
public final fun addClassPathJarsFrom (Ljava/lang/Object;)V
public final fun addProgramJarsFrom (Ljava/lang/Object;)V
public final fun classPathConfiguration (Ljava/lang/String;)V
public final fun configuration (Ljava/lang/String;)V
public final fun exclude (Ljava/lang/String;)V
public final fun getDefaultR8Version ()Ljava/lang/String;
public final fun programJar (Ljava/lang/Object;)V
public final fun programJar (Lorg/gradle/api/provider/Provider;)V
public final fun programJar (Lorg/gradle/api/tasks/TaskProvider;)V
public final fun proguardFile (Ljava/lang/Object;)V
public final fun r8Version (Ljava/lang/String;)V
public final fun systemClassesToolchain (Lorg/gradle/api/Action;)V
Expand Down
4 changes: 2 additions & 2 deletions gr8-plugin-common/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import com.gradleup.librarian.gradle.librarianModule
import com.gradleup.librarian.gradle.Librarian

plugins {
id("org.jetbrains.kotlin.jvm")
Expand All @@ -9,5 +9,5 @@ dependencies {
implementation(libs.r8)
}

librarianModule()
Librarian.module(project)

Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.gradleup.gr8

import org.gradle.api.artifacts.transform.InputArtifact
import org.gradle.api.artifacts.transform.TransformAction
import org.gradle.api.artifacts.transform.TransformOutputs
import org.gradle.api.artifacts.transform.TransformParameters
import org.gradle.api.attributes.Attribute
import org.gradle.api.file.FileSystemLocation
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.PathSensitive
import org.gradle.api.tasks.PathSensitivity
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
import java.util.zip.ZipOutputStream

abstract class FilterTransform: TransformAction<FilterTransform.Parameters> {
@get:PathSensitive(PathSensitivity.NAME_ONLY)
@get:InputArtifact
abstract val inputArtifact: Provider<FileSystemLocation>

override fun transform(outputs: TransformOutputs) {
val file = inputArtifact.get().asFile
val regexes = parameters.excludes.map { Regex(it) }
ZipInputStream(file.inputStream()).use { inputStream ->
ZipOutputStream(outputs.file("${file.nameWithoutExtension}-excluded.${file.extension}").outputStream()).use { outputStream ->
var entry: ZipEntry? = inputStream.nextEntry
while (entry != null) {
if (regexes.none { it.matches(entry!!.name) }) {
outputStream.putNextEntry(entry)
inputStream.copyTo(outputStream)
}
entry = inputStream.nextEntry
}
}
}
}

interface Parameters : TransformParameters {
/**
* A list of Regex patterns
*/
@get:Input
var excludes: List<String>
}

companion object {
val artifactType = "filtered-jar"
}
}
138 changes: 27 additions & 111 deletions gr8-plugin-common/src/main/kotlin/com/gradleup/gr8/Gr8Configurator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,49 +2,40 @@ package com.gradleup.gr8

import org.gradle.api.Action
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.file.ArchiveOperations
import org.gradle.api.file.DuplicatesStrategy
import org.gradle.api.file.RegularFile
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.plugins.JavaPluginExtension
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.TaskProvider
import org.gradle.api.tasks.bundling.AbstractArchiveTask
import org.gradle.api.tasks.bundling.Jar
import org.gradle.api.tasks.bundling.Zip
import org.gradle.jvm.toolchain.JavaCompiler
import org.gradle.jvm.toolchain.JavaToolchainService
import org.gradle.jvm.toolchain.JavaToolchainSpec
import javax.inject.Inject

open class Gr8Configurator(
private val name: String,
private val project: Project,
private val javaToolchainService: JavaToolchainService,
private val name: String,
private val project: Project,
private val javaToolchainService: JavaToolchainService,
) {
private val programJar: RegularFileProperty = project.objects.fileProperty()
private val programJars = project.objects.fileCollection()

private val excludes: ListProperty<String> = project.objects.listProperty(String::class.java)
private val classPathExcludes: ListProperty<String> = project.objects.listProperty(String::class.java)
private val javaCompiler: Property<JavaCompiler> = project.objects.property(JavaCompiler::class.java)
private val proguardFiles = project.objects.fileCollection()
private var classPathJars = project.objects.fileCollection()

val defaultR8Version = "8.5.35"

private var r8Version_: String = defaultR8Version
private var configuration: String? = null
private var classPathConfiguration: String? = null

private val buildDir = project.layout.buildDirectory.dir("gr8/$name").get().asFile


init {
val javaExtension = project.extensions.findByType(JavaPluginExtension::class.java)
if (javaExtension != null) {
javaCompiler.convention(javaToolchainService.compilerFor(javaExtension.toolchain))
}
excludes.convention(null as List<String>?)
classPathExcludes.convention(null as List<String>?)
}

fun r8Version(r8Version: String) {
Expand All @@ -54,15 +45,8 @@ open class Gr8Configurator(
/**
* The configuration to shadow in the resulting output jar.
*/
fun configuration(name: String) {
configuration = name
}


@Deprecated("Use `programJar(Provider<RegularFile>)` or `programJar(TaskProvider<Task>)`", level = DeprecationLevel.ERROR)
fun programJar(file: Any) {
TODO()
}
@Deprecated("use addProgramJarsFrom(configurations.getByName(\"name\") instead", replaceWith = ReplaceWith("addProgramJarsFrom(configurations.getByName(\"name\")"), level = DeprecationLevel.ERROR)
fun configuration(name: String): Unit = TODO()

/**
* The jar file to include in the resulting output jar.
Expand All @@ -72,35 +56,23 @@ open class Gr8Configurator(
* @param file a file that will be evaluated like [Project.file]. If this file is created by another task, use a provider
* so that the dependency between the task can be set up
*/
fun programJar(file: Provider<RegularFile>) {
this.programJar.set(file)
fun addProgramJarsFrom(file: Any) {
programJars.from(file)
}

/**
* The jar file to include in the resulting output jar.
*
* Default: the jar produced by the "jar" task
*
* @param taskProvider a file that will be evaluated like [Project.file]. If this file is created by another task, use a provider
* so that the dependency between the task can be set up
* Adds additional jars on the classpath (but not in the output jar).
*/
fun programJar(taskProvider: TaskProvider<Task>) {
programJar(
taskProvider.flatMap { task ->
check(task is AbstractArchiveTask) {
"only AbstractArchiveTasks like Jar or Zip are supported"
}
task.archiveFile
}
)
}

@Deprecated("use addClassPathJarsFrom(configurations.getByName(\"name\") instead", replaceWith = ReplaceWith("addClassPathJarsFrom(configurations.getByName(\"name\")"), level = DeprecationLevel.ERROR)
fun classPathConfiguration(name: String): Unit = TODO()

/**
* Adds additional jars on the classpath (but not in the output jar).
*
* @param files files to add, evaluated as in [Project.file].
*/
fun classPathConfiguration(name: String) {
classPathConfiguration = name
fun addClassPathJarsFrom(files: Any) {
classPathJars.from(files)
}

/**
Expand All @@ -112,11 +84,7 @@ open class Gr8Configurator(
proguardFiles.from(file)
}

/**
* Adds the given ANT pattern as an exclusion in the resulting jar.
*
* By default, MANIFEST.MF, proguard files and module-info.class are excluded
*/
@Deprecated("exclude is not supported anymore, use addProgramJarsFrom(fileCollection) and filter your fileCollection. See also FilterTransform")
fun exclude(exclude: String) {
this.excludes.add(exclude)
}
Expand Down Expand Up @@ -158,63 +126,20 @@ open class Gr8Configurator(
this.javaCompiler.set(javaToolchainService.compilerFor(spec))
}

private fun defaultProgramJar(): Provider<RegularFile> {
return project.tasks.named("jar").flatMap {
(it as Jar).archiveFile
}
}

internal fun registerTasks(): TaskProvider<Gr8Task> {
/**
* The pipeline is:
* - Patch the Kotlin stdlib to make DefaultConstructorMarker not-public again. This is to prevent R8 to rewrite
* Class.forName("kotlin.jvm.internal.DefaultConstructorMarker") to a constant pool reference that will make a runtime
* exception if used with Kotlin 1.4 at runtime
* - Take all jars and build a big embedded Jar, keeping all META-INF files and only the MANIFEST from the main jar
* - Strip some Java9 files from gradle-api because it triggers
* com.android.tools.r8.errors.CompilationError: Class content provided for type descriptor org.gradle.internal.impldep.META-INF.versions.9.org.junit.platform.commons.util.ModuleUtils actually defines class org.gradle.internal.impldep.org.junit.platform.commons.util.ModuleUtils
* - Call R8 to generate the final jar
*/

val configuration = project.configurations.getByName(configuration ?: error("Calling gr8 { configuration() } is required"))

val embeddedJarProvider = project.tasks.register("embedJar", Zip::class.java) {
val archiveOperations = project.archiveOperations()
val objects = project.objects

it.from(configuration.elements.map { fileSystemLocations ->
objects.fileCollection().apply {
fileSystemLocations.forEach {
from(archiveOperations.zipTree(it.asFile))
}
}
})

// The jar is mostly empty but this is needed for the plugin descriptor + module-info
it.from(programJar.orElse(defaultProgramJar()).map { archiveOperations.zipTree(it.asFile) })

/*
* Exclude libraries R8 rules, we'll add them ourselves
*/
it.exclude(excludes.getOrElse(listOf("META-INF/MANIFEST.MF", "META-INF/**/*.pro", "module-info.class", "META-INF/versions/*/module-info.class")))

it.duplicatesStrategy = DuplicatesStrategy.WARN

it.destinationDirectory.set(buildDir)
it.archiveFileName.set("embedded.jar")
}
val upperCaseName = name.replaceFirstChar { it.uppercase() }

val gr8Configuration = project.configurations.create("gr8") {
val gr8Configuration = project.configurations.create("gr8$upperCaseName") {
it.isCanBeResolved = true
}
gr8Configuration.dependencies.add(project.dependencies.create("com.android.tools:r8:$defaultR8Version"))

val downloadR8 = project.tasks.register("downloadR8", DownloadR8Task::class.java) {
val downloadR8 = project.tasks.register("gr8${upperCaseName}Download", DownloadR8Task::class.java) {
it.sha1.set(r8Version_)
it.outputFile.set(buildDir.resolve("r8/$r8Version_.jar"))
}

val r8TaskProvider = project.tasks.register("${name}R8Jar", Gr8Task::class.java) { task ->
val r8TaskProvider = project.tasks.register("gr8${upperCaseName}ShadowedJar", Gr8Task::class.java) { task ->
if (r8Version_.contains('.')) {
task.classpath(gr8Configuration)
} else {
Expand All @@ -223,25 +148,16 @@ open class Gr8Configurator(

task.mainClass.set("com.android.tools.r8.R8")

task.programFiles.from(embeddedJarProvider)

task.programFiles.from(programJars)
task.mapping.set(buildDir.resolve("mapping.txt"))
if (classPathConfiguration != null) {
task.classPathFiles.from(project.configurations.getByName(classPathConfiguration!!))
}
task.classPathFiles.from(classPathJars)
task.proguardConfigurationFiles.from(proguardFiles)

task.outputJar.set(buildDir.resolve("${project.name}-${project.version}-shadowed.jar"))
task.proguardConfigurationFiles.from(proguardFiles)

task.javaCompiler.set(javaCompiler)
}

return r8TaskProvider
}
}

internal abstract class Holder2 @Inject constructor(val operations: ArchiveOperations)

private fun Project.archiveOperations(): ArchiveOperations {
return objects.newInstance(Holder2::class.java).operations
}
7 changes: 7 additions & 0 deletions gr8-plugin-common/src/main/kotlin/com/gradleup/gr8/Gr8Task.kt
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,17 @@ abstract class Gr8Task : JavaExec() {
args(outputJar.get().asFile.absolutePath)
args("--pg-map-output")
args(mapping.get().asFile.absolutePath)

classPathFiles.forEach { file ->
args("--classpath")
args(file.absolutePath)
}

proguardConfigurationFiles.forEach { file ->
args("--pg-conf")
args(file.absolutePath)
}

args("--lib")
args(javaHome)

Expand Down
4 changes: 2 additions & 2 deletions gr8-plugin-external/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import com.gradleup.librarian.gradle.librarianModule
import com.gradleup.librarian.gradle.Librarian

plugins {
id("org.jetbrains.kotlin.jvm")
Expand All @@ -17,7 +17,7 @@ fun Dependency?.excludeKotlinStdlib() {
}
}

librarianModule()
Librarian.module(project)

gradlePlugin {
plugins {
Expand Down

0 comments on commit 419611a

Please sign in to comment.