diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 319faa8ea..82c827292 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -110,6 +110,10 @@ When adding new features or public APIs: - Raw multiline strings in tests should be constructed using `.trimMargin()`. - Prefer `=` property assignment over `.set(...)` in Gradle build scripts (both in documentation snippets and functional tests) unless `.set(...)` is explicitly required. +- When adding or updating code snippets in documentation (`docs/`), ensure there are corresponding functional or unit + tests covering and verifying the actual behavioral logic. Annotate the snippet group with + `` directly above the tab group so that `generateDocTests` can link and validate + the reference. ### Before Submitting a Pull Request diff --git a/build.gradle.kts b/build.gradle.kts index 2a2f1a108..ef10cb780 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -154,21 +154,6 @@ testing.suites { named("test") { dependencies { implementation(libs.xmlunit) } } - register("documentTest") { - targets.configureEach { - testTask { - testLogging.showExceptions = false - addTestListener( - object : TestListener { - override fun afterTest(testDescriptor: TestDescriptor, result: TestResult) { - // Prettify test failure output in console. - result.exception?.message?.lineSequence()?.firstOrNull()?.let(logger::error) - } - } - ) - } - } - } register("functionalTest") { targets.configureEach { testTask { @@ -188,6 +173,25 @@ testing.suites { implementation(libs.moshi.kotlin) } } + register("documentTest") { + dependencies { + implementation(sourceSets["test"].output) + implementation(sourceSets["functionalTest"].output) + } + targets.configureEach { + testTask { + testLogging.showExceptions = false + addTestListener( + object : TestListener { + override fun afterTest(testDescriptor: TestDescriptor, result: TestResult) { + // Prettify test failure output in console. + result.exception?.message?.lineSequence()?.firstOrNull()?.let(logger::error) + } + } + ) + } + } + } withType().configureEach { useJUnitJupiter(libs.junit.bom.map { checkNotNull(it.version) }) @@ -239,6 +243,10 @@ val generateDocTests = description = "Generates Kotlin test source files for code snippets in documentation." inputDirectory = layout.projectDirectory.dir("docs") outputDirectory = layout.buildDirectory.dir("generated/sources/documentTest/kotlin") + testSourceDirectories.from( + sourceSets["test"].allSource.sourceDirectories, + sourceSets["functionalTest"].allSource.sourceDirectories, + ) } kotlin.sourceSets.named("documentTest") { diff --git a/docs/application-plugin/README.md b/docs/application-plugin/README.md index adf158697..f007eb330 100644 --- a/docs/application-plugin/README.md +++ b/docs/application-plugin/README.md @@ -7,6 +7,7 @@ Just like the normal [`Jar`][Jar] task, when the [`application`][application] pl [`ShadowJar`][ShadowJar] manifest will be configured to contain the `Main-Class` attribute with the value specified in the project's `application.mainClass` attribute. + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -39,6 +40,7 @@ When applied along with the `application` plugin, the `runShadow` task will be c the shadowed JAR. The `runShadow` task is a [`JavaExec`][JavaExec] task that is configured to execute `java -jar myproject-all.jar`. It can be configured the same as any other [`JavaExec`][JavaExec] task. + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -88,6 +90,7 @@ files for a distribution to `build/install/-shadow/`. You can also add more files into the distribution like: + === ":material-language-kotlin: build.gradle.kts" ```kotlin diff --git a/docs/configuration/README.md b/docs/configuration/README.md index 07f3475eb..127642c5d 100644 --- a/docs/configuration/README.md +++ b/docs/configuration/README.md @@ -78,6 +78,7 @@ If working with a Gradle project with the name `myApp` and version `1.0`, the de output a file at: `build/libs/myApp-1.0-all.jar`. You can override the properties listed above to change the output name of the shadowed JAR file. e.g. + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -112,6 +113,7 @@ Additionally, Shadow automatically configures the manifest of the [`ShadowJar`][ `Class-Path` entry in the JAR manifest. The value of the `Class-Path` entry is the name of all dependencies resolved in the `shadow` configuration for the project. + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -143,6 +145,7 @@ Class-Path: junit-3.8.2.jar The [`ShadowJar`][ShadowJar] manifest is configured in a number of ways. First, the manifest for the `shadowJar` task is configured to __inherit__ from the manifest of the standard `jar` task. + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -172,6 +175,7 @@ Main-Class: my.Main If it is desired to merge a manifest from another [`Jar`][Jar] task, the `manifest.from` methods can be used to configure the upstream. + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -211,6 +215,7 @@ into the shadow JAR. If any dependency JAR has the `Multi-Release` manifest attr You can disable this behavior by setting `addMultiReleaseAttribute` to `false`: + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -235,6 +240,7 @@ dependencies. The [`ShadowJar`][ShadowJar] task is a subclass of the [`Jar`][Jar] task, which means that the [`Jar.from`][Jar.from] method can be used to add extra files. + === ":material-language-kotlin: build.gradle.kts" ```kotlin diff --git a/docs/configuration/dependencies/README.md b/docs/configuration/dependencies/README.md index 6ff120ecf..55c3607a5 100644 --- a/docs/configuration/dependencies/README.md +++ b/docs/configuration/dependencies/README.md @@ -5,6 +5,7 @@ Shadow configures the default [`ShadowJar`][ShadowJar] task to merge all depende merging can be configured using the [`configurations`][ShadowJar.configurations] property of the [`ShadowJar`][ShadowJar] task type. + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -41,6 +42,7 @@ be included in the final JAR. The [`ShadowJar`][ShadowJar] task is a subclass of the [`Jar`][Jar] task, which means that the [`Jar.from`][Jar.from] method can be used to add extra files. + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -75,6 +77,7 @@ method can be used to add extra files. Someone may need the unzipped `bar.jar` to be bundled, try out [`zipTree`][Project.zipTree] + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -123,6 +126,7 @@ Caused by: java.util.zip.ZipException: Archive is not a ZIP archive To embed such dependencies into your shadowed JAR, you can use the [`Jar.from`][Jar.from] method with a custom configuration. + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -174,6 +178,7 @@ configuration. If the non-JAR file is a transitive dependency (e.g., a POM-only metapackage) that you don't actually need in your shadowed JAR, you can exclude it using the `dependencies` block instead of embedding it. + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -214,6 +219,7 @@ dependency does not exclude any of its dependencies from the final JAR. The `dependency` blocks provides a number of methods for resolving dependencies using the notations familiar from Gradle's [`project.configurations`][Project.configurations] block. + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -242,6 +248,7 @@ Gradle's [`project.configurations`][Project.configurations] block. } ``` + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -280,6 +287,7 @@ Gradle's [`project.configurations`][Project.configurations] block. Dependencies can be filtered using regex patterns. Coupled with the `::` notation for dependencies, this allows for excluding/including using any of these individual fields. + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -310,6 +318,7 @@ dependencies, this allows for excluding/including using any of these individual Any of the individual fields can be safely absent and will function as though a wildcard was specified. + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -349,6 +358,7 @@ This same pattern can be used for any of the dependency notation fields. e.g. - `.*:log4j-core:.*` - ... + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -377,6 +387,7 @@ This same pattern can be used for any of the dependency notation fields. e.g. } ``` + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -409,6 +420,7 @@ This same pattern can be used for any of the dependency notation fields. e.g. You can also use type-safe project accessors or version catalog accessors to filter dependencies. + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -451,6 +463,7 @@ If more complex decisions are needed to select the dependencies to be included, [`ShadowJar.dependencies`][ShadowJar.dependencies] block provides a method that accepts a `Closure` for selecting dependencies. + === ":material-language-kotlin: build.gradle.kts" ```kotlin diff --git a/docs/configuration/filtering/README.md b/docs/configuration/filtering/README.md index d2ef7ff59..8a3182ee7 100644 --- a/docs/configuration/filtering/README.md +++ b/docs/configuration/filtering/README.md @@ -7,6 +7,7 @@ When using `exclude`/`include` with a [`ShadowJar`][ShadowJar] task, the resulti JAR contents. This means that, the configuration is applied to the individual files from both the project source set or _any_ of the dependencies to be merged. + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -26,6 +27,7 @@ _any_ of the dependencies to be merged. Excludes and includes can be combined just like a normal [`Jar`][Jar] task, with `excludes` taking precedence over `includes`. Additionally, ANT style patterns can be used to match multiple files. + === ":material-language-kotlin: build.gradle.kts" ```kotlin diff --git a/docs/configuration/merging/README.md b/docs/configuration/merging/README.md index a1ed7891f..d55729f41 100644 --- a/docs/configuration/merging/README.md +++ b/docs/configuration/merging/README.md @@ -34,6 +34,7 @@ see more details about them in [`DuplicatesStrategy`][DuplicatesStrategy]. `ShadowJar` recognizes `EXCLUDE` as the default, if you want to change the strategy, you can override it like: + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -72,6 +73,7 @@ Different strategies will lead to different results for `foo/bar` files in the J If you mix the usages of `duplicatesStrategy = DuplicatesStrategy.EXCLUDE` and [`ResourceTransformer`][ResourceTransformer] like below: + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -136,6 +138,7 @@ Optional steps: Here are some examples: + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -222,6 +225,7 @@ Here are some examples: For simpler use cases, you can create a basic transformer: + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -337,6 +341,7 @@ single file in the output JAR. You can use either the short syntax method [`mergeServiceFiles()`][ShadowJar.mergeServiceFiles] or the full syntax method [`transform`][ShadowJar.transform] to add the [`ServiceFileTransformer`][ServiceFileTransformer]: + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -376,6 +381,7 @@ the [`ServiceFileTransformer`][ServiceFileTransformer]: By default, the [`ServiceFileTransformer`][ServiceFileTransformer] is configured to merge files in `META-INF/services`. This directory can be overridden to merge descriptor files in a different location. + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -413,6 +419,7 @@ This directory can be overridden to merge descriptor files in a different locati The [`ServiceFileTransformer`][ServiceFileTransformer] class supports specifying specific files to include or exclude from merging. + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -451,6 +458,7 @@ Shadow provides a specific transformer for dealing with Groovy extension module syntax and how they need to be merged together. The [`GroovyExtensionModuleTransformer`][GroovyExtensionModuleTransformer] will handle these files. + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -475,6 +483,7 @@ syntax and how they need to be merged together. The Log4j 2.x Core components. It's a Gradle equivalent of [Log4j Plugin Descriptor Transformer][log4j-plugin-descriptor-transformer]. + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -496,6 +505,7 @@ Log4j 2.x Core components. It's a Gradle equivalent of Generic text files can be appended together using the [`AppendingTransformer`][AppendingTransformer]. Each file is appended using separators (defaults to `\n`) to separate content. + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -516,6 +526,7 @@ appended using separators (defaults to `\n`) to separate content. } ``` + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -545,6 +556,7 @@ XML document and merges each root element into a single document. There is no sh [`XmlAppendingTransformer`][XmlAppendingTransformer]. It must be added using the [`transform`][ShadowJar.transform] methods. + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -574,6 +586,7 @@ relocates matched class names and package patterns within the rules according to You can add this transformer using [`transform`][ShadowJar.transform]: + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -607,6 +620,7 @@ There are lots of built-in [`ResourceTransformer`][ResourceTransformer]s provide You can use `include`/`exclude` and more methods to configure the patterns for those [`ResourceTransformer`][ResourceTransformer]s that support it. For example: + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -646,6 +660,7 @@ You can also specify specific file paths or regular expressions to match using ` strategies using `mappings`, rewrite property keys using `keyTransformer`, or change file encoding using `charsetName` (defaults to `ISO-8859-1`). + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -690,6 +705,7 @@ You can configure: - `firstSeparator`: Separator between the project's license and dependency licenses. - `separator`: Separator between individual dependency licenses. + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -715,6 +731,7 @@ You can configure: If you instead want to discard all license files from the output JAR, you can simply use [`ShadowJar.exclude`][ShadowJar.exclude]: + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -740,6 +757,7 @@ formatting requirements. You can configure properties such as `projectName`, `copyright`, `organizationName`, `organizationURL`, `inceptionYear`, `outputPath` (defaults to `META-INF/NOTICE`), `addHeader`, and `charsetName` (defaults to `UTF-8`). + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -773,6 +791,7 @@ Maven plugins and components using the Plexus IoC container provide component de aggregates these component definitions into a single file and relocates the `role` and `implementation` class names matching the configured relocators. + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -799,6 +818,7 @@ relocating class and package names within configured manifest attributes (such a To remove a specific attribute from the manifest, map its name to [`ManifestResourceTransformer.NULL`][ManifestResourceTransformer.NULL]. + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -837,6 +857,7 @@ the specified patterns and discards any subsequent duplicates found with the sam This transformer is useful when `duplicatesStrategy` is set to `INCLUDE` or `WARN`, ensuring that project resources take precedence and duplicate dependency resources at the same path are omitted. + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -876,6 +897,7 @@ or `pom.xml` files from different dependency versions), you can exclude those pa > [`DeduplicatingResourceTransformer`][DeduplicatingResourceTransformer], as they handle duplicates differently and > combining them leads to redundant or unexpected behavior. + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -904,6 +926,7 @@ resources. Shadow provides a [`FindResourceInClasspath`][FindResourceInClasspath To scan for resources, register a [`FindResourceInClasspath`][FindResourceInClasspath] task in your build script and configure its `classpath` and the resource patterns to look for: + === ":material-language-kotlin: build.gradle.kts" ```kotlin diff --git a/docs/configuration/minimizing/README.md b/docs/configuration/minimizing/README.md index 09f8f1351..ad7690f14 100644 --- a/docs/configuration/minimizing/README.md +++ b/docs/configuration/minimizing/README.md @@ -27,6 +27,7 @@ Shadow provides two minimization backends: By default, calling `minimize()` enables Shadow's built-in dependency analyzer: + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -48,6 +49,7 @@ useful when the dependency analyzer cannot find the usage of a class programmati loaded dynamically via `Class.forName(String)` or loaded via Java SPI (`ServiceLoader`). Each of the `group`, `name` and `version` fields separated by `:` of a `dependency` is interpreted as a regular expression. + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -74,6 +76,7 @@ loaded dynamically via `Class.forName(String)` or loaded via Java SPI (`ServiceL Similar to [`ShadowJar.dependencies`][ShadowJar.dependencies], projects can also be excluded. + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -131,6 +134,7 @@ rules published in dependency JARs, for example under `META-INF/proguard`. > Alternatively, if you use [R8 Repackaging][r8-repackaging] (e.g. `-repackageclasses`), R8 applies embedded rules > natively without needing rule rewriting. + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -184,6 +188,7 @@ R8 also supports ProGuard reporting options such as Add them as `proguardRules` when you want to retain name mappings, matched keep rules, or removed code: + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -241,6 +246,7 @@ When your classes reference types that are available on the compile classpath bu (such as `compileOnly` dependencies or `gradleApi()`), supply them to R8 via `classpath` so R8 can analyze the complete class hierarchy without bundling those dependencies into the output archive: + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -302,6 +308,7 @@ are independent and can be used together. For example, to downgrade R8 warnings to info: + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -336,6 +343,7 @@ For example, to downgrade R8 warnings to info: To enable name obfuscation: + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -370,6 +378,7 @@ To enable name obfuscation: To enable optimization: + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -404,6 +413,7 @@ To enable optimization: To enable both: + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -448,6 +458,7 @@ enabled (the default): - **`include(...)` (Shrink only specific dependencies)**: Applies R8 shrinking *only* to matching dependencies. All other dependencies are automatically kept in full. + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -500,6 +511,7 @@ and generates `-dontoptimize` to disable optimization unless explicitly enabled. To take full control over Shadow-generated rules and maximize R8 optimizations (such as shrinking unused project classes or methods and running optimizations), disable `useDefaultRules`: + === ":material-language-kotlin: build.gradle.kts" ```kotlin diff --git a/docs/configuration/relocation/README.md b/docs/configuration/relocation/README.md index 8358ca62a..5887d2bd7 100644 --- a/docs/configuration/relocation/README.md +++ b/docs/configuration/relocation/README.md @@ -10,6 +10,7 @@ downstream project. Shadow uses the ASM library to modify class byte code to replace the package name and any import statements for a class. Any non-class files that are stored within a package structure are also relocated to the new location. + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -45,6 +46,7 @@ class `junit.framework.TestCase` becomes `shadow.junit.TestCase`. In the resulti Specific classes or files can be `included`/`excluded` from the relocation operation if necessary. Use [Ant Path Matcher][ant-path-matcher] syntax to specify matching path for your files and directories. + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -74,6 +76,7 @@ Specific classes or files can be `included`/`excluded` from the relocation opera For a more advanced path matching you might want to use [Regular Expressions][regular-expressions] instead. Wrap the expression in `%regex[]` before passing it to `include`/`exclude`. + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -98,6 +101,7 @@ It may be desirable to relocate all packages in a Shadow JAR except for a select specifying a relocation with an empty string `''` as the pattern to match on all packages. An `exclude` filter can then be used to prevent relocation of specific packages. + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -138,6 +142,7 @@ public class Bar { in your project, and you configure the relocation like: + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -157,6 +162,7 @@ in your project, and you configure the relocation like: the string constant `"foo.Bar"` will be relocated to `"my.foo.Bar"` by default. This may not be what you want, you can skip relocating string constants in the classes like: + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -188,6 +194,7 @@ removed for clarity reasons in version 4.0.0. To configure automatic dependency relocation, set `enableAutoRelocation = true` and optionally specify a custom `relocationPrefix` to override the default value of `"shadow"`. + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -227,6 +234,7 @@ runtime. See more details and discussion in [#1622][#1622]. If you want to relocate the resources of the project only and exclude all dependencies (related to a normal JAR but with relocating), you can try out the trick like: + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -266,6 +274,7 @@ and [-keeppackagenames][keeppackagenames]. To use R8 for package relocation, enable R8 under `minimize` and provide ProGuard repackaging directives via `proguardRules` or an external rule file: + === ":material-language-kotlin: build.gradle.kts" ```kotlin diff --git a/docs/configuration/reproducible-builds/README.md b/docs/configuration/reproducible-builds/README.md index b2d9d9828..4c44ab6d9 100644 --- a/docs/configuration/reproducible-builds/README.md +++ b/docs/configuration/reproducible-builds/README.md @@ -10,6 +10,7 @@ same permissions, irrespective of the locally configured umask. More information about reproducible builds can be found at [reproducible-builds.org][reproducible-builds.org]. + === ":material-language-kotlin: build.gradle.kts" ```kotlin diff --git a/docs/custom-tasks/README.md b/docs/custom-tasks/README.md index cf625d1d5..f4e74fac9 100644 --- a/docs/custom-tasks/README.md +++ b/docs/custom-tasks/README.md @@ -5,6 +5,7 @@ possible to add arbitrary [`ShadowJar`][ShadowJar] tasks to a project. When doin [`configurations`][ShadowJar.configurations] property is specified to inform Shadow which dependencies to merge into the output. + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -61,6 +62,7 @@ It is also possible to create a shadow JAR that contains *only* the dependencies code. This is accomplished by creating a custom [`ShadowJar`][ShadowJar] task and configuring the [`configurations`][ShadowJar.configurations] property, but **not** adding any project sources with `from(...)`. + === ":material-language-kotlin: build.gradle.kts" ```kotlin diff --git a/docs/getting-started/README.md b/docs/getting-started/README.md index f75096494..2418f2223 100644 --- a/docs/getting-started/README.md +++ b/docs/getting-started/README.md @@ -1,5 +1,6 @@ # Getting Started + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -20,6 +21,7 @@ Alternatively, the plugin can be added to the buildscript classpath and applied: + === ":material-language-kotlin: build.gradle.kts" ```kotlin diff --git a/docs/kotlin-plugins/README.md b/docs/kotlin-plugins/README.md index e97c39ceb..dd33c24f0 100644 --- a/docs/kotlin-plugins/README.md +++ b/docs/kotlin-plugins/README.md @@ -11,6 +11,7 @@ kotlin.stdlib.default.dependency=false Kotlin compilations may still require the standard libraries, you can add them into `compileOnly` (`compileClasspath`) to make sure compilations success and avoid shadowing as follows: + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -33,6 +34,7 @@ See more information about [Dependency on the standard library][dependency-on-th Shadow works well for Kotlin JVM projects like Java projects. Here is an example: + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -67,6 +69,7 @@ build logic for [Publishing Libraries][publishing-libraries], [Running Applicati Shadow honors Kotlin's [`org.jetbrains.kotlin.multiplatform`][org.jetbrains.kotlin.multiplatform] plugin and will automatically configure additional tasks for bundling the shadowed JAR for its `jvm` target. + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -148,6 +151,7 @@ is enabled by default via the deprecated `enableKotlinModuleRemapping` property. To explicitly apply this remapping (recommended for future compatibility), add [`KotlinModuleMetadataTransformer`][KotlinModuleMetadataTransformer] to your task configuration: + === ":material-language-kotlin: build.gradle.kts" ```kotlin diff --git a/docs/multi-project/README.md b/docs/multi-project/README.md index 58c921c38..b99952ce4 100644 --- a/docs/multi-project/README.md +++ b/docs/multi-project/README.md @@ -10,6 +10,7 @@ In a multi-project build there may be one project that applies Shadow and anothe dependency. In this case, use Gradle's normal dependency declaration mechanism to depend on the `shadow` configuration of the shadowed project. + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -40,6 +41,7 @@ the shadowed JAR by default, preventing accidental consumption of the unshadowed **In the shadowed project (`:api`):** + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -90,6 +92,7 @@ the shadowed JAR by default, preventing accidental consumption of the unshadowed **Consuming projects can then depend on `:api` without specifying the `shadow` configuration:** + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -111,6 +114,7 @@ the shadowed JAR by default, preventing accidental consumption of the unshadowed If you want to exclude transitive dependencies that were bundled into the shadow JAR, you can add `exclude` rules to the configurations as well: + === ":material-language-kotlin: build.gradle.kts" ```kotlin diff --git a/docs/publishing/README.md b/docs/publishing/README.md index 2ec088dfd..15de707a8 100644 --- a/docs/publishing/README.md +++ b/docs/publishing/README.md @@ -6,6 +6,7 @@ The Shadow plugin will automatically configure the necessary tasks in the presen [`maven-publish`][maven-publish] plugin. The plugin provides the `shadow` component to configure the publication with the necessary artifact and dependencies in the POM file. + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -56,6 +57,7 @@ JAR. This allows consumers of the published library to choose between the standa This feature is enabled by default. It can be disabled by setting the `addShadowVariantIntoJavaComponent` property in the `shadow` extension to `false`. If you want to publish the standard JAR only, disable this feature like: + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -110,6 +112,7 @@ The target JVM version attribute (`org.gradle.jvm.version`) of the shadowed vari for consumers to select the correct variant based on their target JVM version. But it may cause issues in some cases, you can disable this by setting the `addTargetJvmVersionAttribute` property in the `shadow` extension to `false`: + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -130,6 +133,7 @@ The BUNDLING attribute (`org.gradle.dependency.bundling`) of the shadowed varian useful for consumers to distinguish between normal and shadowed dependencies. You can override this attribute by setting the `bundlingAttribute` property in the `shadow` extension: + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -161,6 +165,7 @@ No other dependencies are automatically configured for inclusion in the POM file This automatic configuration occurs _only_ when using the above methods for configuring publishing. If this behavior is not desirable, then publishing **must** be manually configured. + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -248,6 +253,7 @@ not desirable, then publishing **must** be manually configured. You may want to publish the shadowed JAR instead of the original JAR. This can be done by trimming the `archiveClassifier` of the shadowed JAR like the following: + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -298,6 +304,7 @@ Because the default `archiveClassifier` of [`Jar`][Jar] is `""` (empty), setting [`ShadowJar`][ShadowJar] to `""` (empty) will make collisions between the outputs of these two tasks in some cases. If you don't need the standard JAR, you can disable the `jar` task like: + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -316,6 +323,7 @@ you don't need the standard JAR, you can disable the `jar` task like: Or set a different `archiveClassifier` for the standard [`Jar`][Jar] like: + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -340,6 +348,7 @@ publish the output of the [`ShadowJar`][ShadowJar] tasks as the consumable artif the [Gradle Plugin Publish docs][gradle-plugin-publish-docs] for details. The only thing you need to do from the Shadow side is to empty the `archiveClassifier` like: + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -379,6 +388,7 @@ side is to empty the `archiveClassifier` like: It is possible to publish a custom [`ShadowJar`][ShadowJar] task's output via the [`MavenPublication.artifact()`][MavenPublication.artifact] method. + === ":material-language-kotlin: build.gradle.kts" ```kotlin @@ -448,6 +458,7 @@ It is possible to publish a custom [`ShadowJar`][ShadowJar] task's output via th It is possible to configure the artifact name of the shadowed JAR via properties like `archiveBaseName`, see more customizable properties listed in [Configuring Output Name][configuring-output-name]. e.g. + === ":material-language-kotlin: build.gradle.kts" ```kotlin diff --git a/gradle/build-logic/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/buildlogic/GenerateDocTests.kt b/gradle/build-logic/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/buildlogic/GenerateDocTests.kt index bd8e4b8e4..3c46afe79 100644 --- a/gradle/build-logic/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/buildlogic/GenerateDocTests.kt +++ b/gradle/build-logic/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/buildlogic/GenerateDocTests.kt @@ -2,8 +2,10 @@ package com.github.jengelman.gradle.plugins.shadow.buildlogic import kotlin.io.path.isSymbolicLink import org.gradle.api.DefaultTask +import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.file.DirectoryProperty import org.gradle.api.tasks.InputDirectory +import org.gradle.api.tasks.InputFiles import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.PathSensitive import org.gradle.api.tasks.PathSensitivity @@ -18,12 +20,30 @@ abstract class GenerateDocTests : DefaultTask() { @get:PathSensitive(PathSensitivity.RELATIVE) abstract val inputDirectory: DirectoryProperty + @get:InputFiles + @get:PathSensitive(PathSensitivity.RELATIVE) + abstract val testSourceDirectories: ConfigurableFileCollection + @get:OutputDirectory abstract val outputDirectory: DirectoryProperty @TaskAction fun generate() { val docRoot = inputDirectory.get().asFile + val classMethods = mutableMapOf>() + val methodPattern = Regex("""\bfun\s+`?([A-Za-z0-9_]+)`?\s*[(<]""") + val javaMethodPattern = Regex("""(?:public|protected|private|void)\s+([A-Za-z0-9_]+)\s*\(""") + + testSourceDirectories.asFileTree.forEach { file -> + if (file.isFile && (file.extension == "kt" || file.extension == "java")) { + val className = file.nameWithoutExtension + val text = file.readText() + val methods = classMethods.getOrPut(className) { mutableSetOf() } + methodPattern.findAll(text).forEach { methods += it.groupValues[1] } + javaMethodPattern.findAll(text).forEach { methods += it.groupValues[1] } + } + } + val snippets = docRoot .walk() @@ -34,8 +54,20 @@ abstract class GenerateDocTests : DefaultTask() { val relativePath = file.relativeTo(docRoot).invariantSeparatorsPath val parentDir = file.parentFile.relativeTo(docRoot).invariantSeparatorsPath val matcher = pattern.matcher(source) + var lastEnd = 0 + var currentTestRef: String? = null sequence { while (matcher.find()) { + val textBefore = source.substring(lastEnd, matcher.start()) + val testCommentMatcher = testAnnotationPattern.matcher(textBefore) + if (testCommentMatcher.find()) { + currentTestRef = testCommentMatcher.group(1).trim() + } else if (matcher.group(1) == "kotlin") { + // Reset for a new snippet group if no test annotation was found before the kotlin + // block + currentTestRef = null + } + var line = 1 for (i in 0 until matcher.start().coerceAtMost(source.length)) { if (source[i] == '\n') line++ @@ -48,8 +80,10 @@ abstract class GenerateDocTests : DefaultTask() { lineNumber = line, snippet = matcher.group(2), sourceLocation = "${file.toURI()}:$line", + testRef = currentTestRef, ) ) + lastEnd = matcher.end() } } } @@ -63,6 +97,23 @@ abstract class GenerateDocTests : DefaultTask() { "All languages must have the same number of code snippets: groovy=${groovySnippets.size}, kotlin=${kotlinSnippets.size}" } + snippets.forEach { snippet -> + val testRef = snippet.testRef ?: return@forEach + val parts = testRef.split('#', limit = 2) + val className = parts[0] + val methodName = parts.getOrNull(1) + + val methods = classMethods[className] + check(methods != null) { + "Referenced test class '$className' not found in test sources (referenced from ${snippet.sourceLocation})." + } + if (methodName != null) { + check(methodName in methods) { + "Referenced test method '$methodName' not found in test class '$className' (referenced from ${snippet.sourceLocation})." + } + } + } + val outputDir = outputDirectory.get().asFile snippets .groupBy { it.parentDir } @@ -76,11 +127,19 @@ abstract class GenerateDocTests : DefaultTask() { } val functionsCode = fileSnippets.joinToString("\n\n") { - (lang, relativePath, _, lineNumber, snippet, sourceLocation) -> + (lang, relativePath, _, lineNumber, snippet, sourceLocation, testRef) -> val functionName = "line_${lineNumber}_$lang" + val displayName = "$relativePath:$lineNumber ($lang)" + val kdoc = + if (testRef != null) { + val kdocRef = testRef.replace('#', '.') + " /** @see [$kdocRef] */\n" + } else { + "" + } """ - | @Test - | @DisplayName("$relativePath:$lineNumber ($lang)") + |$kdoc @Test + | @DisplayName("$displayName") | fun `$functionName`(@TempDir tempDir: Path) { | SnippetExecutable( | lang = "$lang", @@ -101,7 +160,9 @@ abstract class GenerateDocTests : DefaultTask() { """ |package com.github.jengelman.gradle.plugins.shadow.docs | + |import com.github.jengelman.gradle.plugins.shadow.* |import com.github.jengelman.gradle.plugins.shadow.SnippetExecutable + |import com.github.jengelman.gradle.plugins.shadow.transformers.* |import java.nio.file.Path |import org.junit.jupiter.api.DisplayName |import org.junit.jupiter.api.Test @@ -125,6 +186,8 @@ private data class Snippet( val lineNumber: Int, val snippet: String, val sourceLocation: String, + val testRef: String?, ) private val pattern = "(?ims) {4}```(groovy|kotlin)\n(.*?)\n {4}```".toPattern() +private val testAnnotationPattern = "".toPattern()