{"id":25287521,"url":"https://github.com/gradleup/gr8","last_synced_at":"2025-04-07T07:01:33.333Z","repository":{"id":40298432,"uuid":"393462224","full_name":"GradleUp/gr8","owner":"GradleUp","description":"Gr8 = Gradle + R8. It makes it easy to relocate, shrink and obfuscate your jars.","archived":false,"fork":false,"pushed_at":"2025-03-21T14:28:07.000Z","size":4983,"stargazers_count":87,"open_issues_count":1,"forks_count":7,"subscribers_count":8,"default_branch":"main","last_synced_at":"2025-03-31T06:01:05.707Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://blog.mbonnin.net/use-kotlin-15-in-your-gradle-plugins","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/GradleUp.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-08-06T18:08:31.000Z","updated_at":"2025-03-26T05:39:44.000Z","dependencies_parsed_at":"2024-12-05T18:20:31.666Z","dependency_job_id":"17a3157c-b5e8-4f65-a45d-dc63a818a61e","html_url":"https://github.com/GradleUp/gr8","commit_stats":null,"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GradleUp%2Fgr8","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GradleUp%2Fgr8/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GradleUp%2Fgr8/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GradleUp%2Fgr8/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/GradleUp","download_url":"https://codeload.github.com/GradleUp/gr8/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247608150,"owners_count":20965952,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-02-12T22:39:54.724Z","updated_at":"2025-04-07T07:01:33.277Z","avatar_url":"https://github.com/GradleUp.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Gr8 [![Maven Central](https://img.shields.io/maven-central/v/com.gradleup/gr8-plugin?style=flat-square)](https://central.sonatype.com/namespace/com.gradleup)\n\nGr8 is [Gradle](https://gradle.org/) + [R8](https://r8.googlesource.com/r8). \n\nGr8 makes it easy to shadow, shrink, and minimize your jars. \n\n## Motivation\n\nGradle has a [very powerful plugin system](https://r8.googlesource.com/r8). Unfortunately, [Gradle handling of classpath/Classloaders](https://dev.to/autonomousapps/build-compile-run-a-crash-course-in-classpaths-f4g) for plugins has some serious limitations. For an example:\n\n* Gradle always [forces its bundled version of the Kotlin stdlib in the classpath](https://github.com/gradle/gradle/issues/16345). This makes it impossible to use Kotlin 1.5 APIs with Gradle 7.1 for an example because Gradle 7.1 uses Kotlin 1.4 (See [compatibility matrix](https://docs.gradle.org/current/userguide/compatibility.html) for other versions).\n* [`buildSrc` dependencies leak in the classpath](https://github.com/gradle/gradle/issues/8301). This causes [very weird bugs](https://github.com/apollographql/apollo-android/issues/2939) during execution because a conflicting dependency might be forced in the classpath. This happens especially with popular libraries such as `okio` or `antlr` that are likely to be used with conflicting versions by different plugins in your build.\n\nBy shadowing (embedding and relocating) the plugin dependencies, it is possible to ship a plugin and all its dependencies without having to worry about what other dependencies are on the classpath, including the Kotlin stdlib.\n\nTo learn more, read the [\"Use latest Kotlin in your Gradle plugins\"](https://mbonnin.net/2021-11-12_use-latest-kotlin-in-your-gradle-plugins/) blog post.\n\nGr8 is mostly focused at Gradle plugins but you can use it to relocate/shrink any library/binary. See [Shrinking a Kotlin binary by 99.2%](https://jakewharton.com/shrinking-a-kotlin-binary/ ) for a good illustration.\n\n## Usage\n\n```kotlin\nplugins {\n  id(\"org.jetbrains.kotlin.jvm\").version(\"$latestKotlinVersion\")\n  id(\"com.gradleup.gr8\").version(\"$gr8Version\")\n}\n\ndependencies {\n  implementation(\"com.squareup.okhttp3:okhttp:4.9.0\")\n  // More dependencies here\n}\n\n/**\n * Create a separate configuration to resolve compileOnly dependencies.\n * You can skip this if you have no compileOnly dependencies. \n */\nval compileOnlyDependencies: Configuration = configurations.create(\"compileOnlyDependencies\") \ncompileOnlyDependencies.extendsFrom(configurations.getByName(\"compileOnly\"))\n\ngr8 {\n  val shadowedJar = create(\"gr8\") {\n    // program jars are included in the final shadowed jar\n    addProgramJarsFrom(configurations.getByName(\"runtimeClasspath\"))\n    addProgramJarsFrom(tasks.getByName(\"jar\"))\n    // classpath jars are only used by R8 for analysis but are not included in the\n    // final shadowed jar.\n    addClassPathJarsFrom(compileOnlyDependencies)\n    proguardFile(\"rules.pro\")\n\n    // Use a version from https://storage.googleapis.com/r8-releases/raw\n    // Requires a maven(\"https://storage.googleapis.com/r8-releases/raw\") repository\n    r8Version(\"8.8.19\")\n    // Or use a commit\n    // The jar is downloaded on demand\n    r8Version(\"887704078a06fc0090e7772c921a30602bf1a49f\")\n    // Or leave it to the default version \n  }\n}\n```\n\nThen customize your proguard rules. The below is a non-exhaustive example. If you're using reflection, you might need more rules \n\n```\n# Keep your public API so that it's callable from scripts\n-keep class com.example.** { *; }\n\n# Repackage other classes\n-repackageclasses com.example.relocated\n\n# Allows more aggressive repackaging \n-allowaccessmodification\n\n# We need to keep type arguments for Gradle to be able to instantiate abstract models like `Property`\n-keepattributes Signature,Exceptions,*Annotation*,InnerClasses,PermittedSubclasses,EnclosingMethod,Deprecated,SourceFile,LineNumberTable\n```\n\n## Using Gr8 for Gradle plugins \n\nUsing Gr8 to shadow dependencies in Gradle plugin is a typical use case but requires extra care because:\n\n* The `java-gradle-plugin` automatically adds `api(gradleApi())` to your dependencies but `gradleApi()` shouldn't be shadowed.\n* `gradleApi()` is a [multi-release jar](https://docs.oracle.com/javase/10/docs/specs/jar/jar.html#multi-release-jar-files) file that [R8 doesn't support](https://issuetracker.google.com/u/1/issues/380805015).\n* Since the plugins are published, the shadowed dependencies must not be exposed in the .pom/.module files.\n\nTo work around this, you can use, `removeGradleApiFromApi()`, `registerTransform()` and custom configurations:\n\n```kotlin\nval shadowedDependencies = configurations.create(\"shadowedDependencies\")\n\nval compileOnlyDependencies: Configuration = configurations.create(\"compileOnlyDependencies\") {\n  attributes {\n    attribute(Usage.USAGE_ATTRIBUTE, project.objects.named\u003cUsage\u003e(Usage.JAVA_API))\n  }\n  // this attribute is needed to filter out some classes, see https://issuetracker.google.com/u/1/issues/380805015 \n  attributes {\n    attribute(ArtifactTypeDefinition.ARTIFACT_TYPE_ATTRIBUTE, FilterTransform.artifactType)\n  }\n}\ncompileOnlyDependencies.extendsFrom(configurations.getByName(\"compileOnly\"))\n\ndependencies {\n  add(shadowedDependencies.name, \"com.squareup.okhttp3:okhttp:4.9.0\")\n  add(\"compileOnly\", gradleApi())\n  // More dependencies here\n}\n\nif (shadow) {\n  gr8 {\n    create(\"default\") {\n      val shadowedJar = create(\"default\") {\n        addProgramJarsFrom(shadowedDependencies)\n        addProgramJarsFrom(tasks.getByName(\"jar\"))\n        // classpath jars are only used by R8 for analysis but are not included in the\n        // final shadowed jar.\n        addClassPathJarsFrom(compileOnlyDependencies)\n\n        proguardFile(\"rules.pro\")\n\n        // for more information about the different options, refer to their matching R8 documentation\n        // at https://r8.googlesource.com/r8#running-r8\n\n        // See https://issuetracker.google.com/u/1/issues/380805015 for why this is required\n        registerFilterTransform(listOf(\".*/impldep/META-INF/versions/.*\"))\n      }\n\n      removeGradleApiFromApi()\n      \n      // Optional: replace the regular jar with the shadowed one in the publication\n      replaceOutgoingJar(shadowedJar)\n\n      // Or if you prefer the shadowed jar to be a separate variant in the default publication\n      // The variant will have `org.gradle.dependency.bundling = shadowed`\n      addShadowedVariant(shadowedJar)\n\n      // Allow to compile the module without exposing the shadowedDependencies downstream\n      configurations.getByName(\"compileOnly\").extendsFrom(shadowedDependencies)\n      configurations.getByName(\"testImplementation\").extendsFrom(shadowedDependencies)\n    }\n  }\n} else {\n  configurations.getByName(\"implementation\").extendsFrom(shadowedDependencies)\n}\n```\n\n## Kotlin interop\n\nBy default, R8 removes `kotlin.Metadata` from the shadowed jar. This means the Kotlin compiler only sees plain Java classes and symbols and Kotlin-only features such as parameters default values, extension function, etc... are lost.\n\nIf you want to keep them, you need to keep `kotlin.Metadata` and `kotlin.Unit`:\n\n```\n# Keep kotlin metadata so that the Kotlin compiler knows about top level functions\n-keep class kotlin.Metadata { *; }\n# Keep Unit as it's in the signature of public methods:\n-keep class kotlin.Unit { *; }\n```\n\n\u003e [!NOTE]\n\u003e Stripping kotlin.Metadata acts as a compile-time verification that your API is usable in Groovy as it is in Kotlin and might be beneficial.\n\n## Java runtime version\n\nYou can specify the version of the java runtime to use with `systemClassesToolchain`:\n\n```\ngr8 {\n  val shadowedJar = create(\"gr8\") {\n    proguardFile(\"rules.pro\")\n    addProgramJarsFrom(configurations.getByName(\"runtimeClasspath\"))\n    systemClassesToolchain {\n      languageVersion.set(JavaLanguageVersion.of(11))\n    }\n  }\n}\n```\n\n## FAQ\n\n**Could I use the Shadow plugin instead?**\n\nThe [Gradle Shadow Plugin](https://imperceptiblethoughts.com/shadow/) has been [helping plugin authors](https://www.alecstrong.com/posts/shading/) for years and is a very stable solution. Unfortunately, it doesn't allow very granular configuration and [might relocate constant strings that shouldn't be](https://github.com/johnrengelman/shadow/issues/232). In practice, any plugin that tries to read the `\"kotlin\"` extension is subject to having its behaviour changed:\n\n```kotlin\nproject.extensions.getByName(\"kotlin\")\n```\n\nwill be transformed to:\n\n```kotlin\nproject.extensions.getByName(\"com.relocated.kotlin\")\n```\n\nFor plugins that generate source code and contain a lot of package names, this might be even more unpredictable and require weird [workarounds](https://github.com/apollographql/apollo-android/blob/f72c3afd17655591aca90a6a118dbb7be9c50920/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/codegen/kotlin/OkioJavaTypeName.kt#L19).\n\nBy using `R8` and [proguard rules](https://www.guardsquare.com/manual/configuration/usage), `Gr8` makes relocation more predictable and configurable.\n\n**Could I use the Gradle Worker API instead?** \n\nThe [Gradle Worker API](https://docs.gradle.org/current/userguide/worker_api.html) has a [classLoaderIsolation mode](https://docs.gradle.org/current/kotlin-dsl/gradle/org.gradle.workers/-worker-executor/class-loader-isolation.html) that can be used to achieve a similar result with some limitations:\n* `gradle-api` and `kotlin-stdlib` are still in the worker classpath meaning you need to make sure your Kotlin version is compatible.\n* [classLoaderIsolation leaks memory](https://github.com/gradle/gradle/issues/18313)\n* Workers require serializing parameters and writing more boilerplate code.\n\n**Are there any drawbacks?**\n\nYes. Because every plugin now relocates its own version of `kotlin-stdlib`, `okio` and other dependencies, it means more work for the Classloaders and more Metaspace being used. There's a risk that builds will use more memory, although it hasn't been a big issue so far.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgradleup%2Fgr8","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgradleup%2Fgr8","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgradleup%2Fgr8/lists"}