From 962bd890de679f7c75b32a93600d6a01af5e7bea Mon Sep 17 00:00:00 2001 From: Jendrik Johannes Date: Thu, 30 Oct 2025 16:20:39 +0100 Subject: [PATCH 1/4] Migrate build to shared convention plugins setup --- build.gradle.kts | 159 ++++++++++-------- gradle/checkstyle/checkstyle.xml | 8 - gradle/checkstyle/header.txt | 15 -- gradle/gradle-daemon-jvm.properties | 1 + gradle/plugins/build.gradle.kts | 3 - gradle/plugins/settings.gradle.kts | 3 - .../gradlexbuild.module-mappings.gradle.kts | 39 ----- .../UniqueModulesPropertiesUpdate.kt | 55 ------ settings.gradle.kts | 21 +-- .../SettingsPluginIncludeTest.java | 2 + .../initialization/SettingsPluginTest.java | 2 + .../SettingsPluginVersionManagementTest.java | 2 + .../test/samples/SamplesTest.java | 29 ---- .../PluginBuildLocationSampleModifier.java | 27 +-- .../test/samples/SamplesTest.java | 12 ++ 15 files changed, 119 insertions(+), 259 deletions(-) delete mode 100644 gradle/checkstyle/checkstyle.xml delete mode 100644 gradle/checkstyle/header.txt create mode 100644 gradle/gradle-daemon-jvm.properties delete mode 100644 gradle/plugins/build.gradle.kts delete mode 100644 gradle/plugins/settings.gradle.kts delete mode 100644 gradle/plugins/src/main/kotlin/gradlexbuild.module-mappings.gradle.kts delete mode 100644 gradle/plugins/src/main/kotlin/gradlexbuild/UniqueModulesPropertiesUpdate.kt delete mode 100644 src/test/java/org/gradlex/javamodule/dependencies/test/samples/SamplesTest.java rename src/{test => testSamples}/java/org/gradlex/javamodule/dependencies/test/samples/PluginBuildLocationSampleModifier.java (56%) create mode 100644 src/testSamples/java/org/gradlex/javamodule/dependencies/test/samples/SamplesTest.java diff --git a/build.gradle.kts b/build.gradle.kts index 5a26a309..be96ed57 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,28 +1,9 @@ -plugins { - id("gradlexbuild.module-mappings") - id("org.gradlex.internal.plugin-publish-conventions") version "0.6" -} +import java.util.Properties +import org.gradle.api.internal.project.ProjectInternal +import org.gradle.util.internal.VersionNumber -group = "org.gradlex" version = "1.11" -java { - toolchain.languageVersion = JavaLanguageVersion.of(17) -} - -tasks.compileJava { - options.release = 8 -} - -tasks.withType().configureEach { - options { - this as StandardJavadocDocletOptions - encoding = "UTF-8" - addStringOption("Xdoclint:all,-missing", "-quiet") - addStringOption("Xwerror", "-quiet") - } -} - configurations.compileClasspath { // Allow Java 11 dependencies on compile classpath attributes.attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 11) @@ -30,21 +11,22 @@ configurations.compileClasspath { dependencies { implementation("org.ow2.asm:asm:9.9") - compileOnly("org.gradlex:extra-java-module-info:1.13.1") compileOnly("com.autonomousapps:dependency-analysis-gradle-plugin:3.4.0") - - testImplementation("org.assertj:assertj-core:3.27.6") - testImplementation("org.gradle.exemplar:samples-check:1.0.3") - testRuntimeOnly("org.junit.vintage:junit-vintage-engine") } -pluginPublishConventions { - id("${project.group}.${project.name}") - implementationClass("org.gradlex.javamodule.dependencies.JavaModuleDependenciesPlugin") - displayName("Java Module Dependencies Gradle Plugin") - description("A plugin that makes Gradle respect the dependencies defined in 'module-info.java' files.") - tags("gradlex", "java", "modularity", "jigsaw", "jpms", "dependencies", "versions") +publishingConventions { + pluginPortal("${project.group}.${project.name}") { + implementationClass("org.gradlex.javamodule.dependencies.JavaModuleDependenciesPlugin") + displayName("Java Module Dependencies Gradle Plugin") + description("A plugin that makes Gradle respect the dependencies defined in 'module-info.java' files.") + tags("gradlex", "java", "modularity", "jigsaw", "jpms", "dependencies", "versions") + } + pluginPortal("${project.group}.java-module-versions") { + implementationClass("org.gradlex.javamodule.dependencies.JavaModuleVersionsPlugin") + displayName("Java Module Versions Gradle Plugin") + description("A plugin that makes Gradle respect the dependencies defined in 'module-info.java' files.") + } gitHub("https://github.com/gradlex-org/java-module-dependencies") developer { id.set("jjohannes") @@ -53,43 +35,88 @@ pluginPublishConventions { } } -gradlePlugin.plugins.register("java-module-versions") { - id = "${project.group}.${name}" - implementationClass = "org.gradlex.javamodule.dependencies.JavaModuleVersionsPlugin" - displayName = "Java Module Versions Gradle Plugin" - description = "A plugin that makes Gradle respect the dependencies defined in 'module-info.java' files." - tags = listOf("gradlex", "java", "modularity", "jigsaw", "jpms", "dependencies", "versions") -} +testingConventions { testGradleVersions("7.4", "7.6.5", "8.0.2", "8.14.3") } -tasks.test { - useJUnitPlatform() - maxParallelForks = 4 - inputs.dir(layout.projectDirectory.dir("samples")) +val detachedResolver: ProjectInternal.DetachedResolver = (project as ProjectInternal).newDetachedResolver() + +detachedResolver.repositories.ivy { + name = "Modules Properties Repository" + url = project.uri("https://raw.githubusercontent.com/sormuras/modules/main/com.github.sormuras.modules") + metadataSources.artifact() + patternLayout { + artifact("[organisation]/[module].properties") + ivy("[module]/[revision]/ivy.xml") + setM2compatible(true) + } } -testing.suites.named("test") { - useJUnitJupiter() - listOf("7.4", "7.6.5", "8.0.2", "8.14.3").forEach { gradleVersionUnderTest -> - targets.register("test${gradleVersionUnderTest}") { - testTask { - group = LifecycleBasePlugin.VERIFICATION_GROUP - description = "Runs tests against Gradle $gradleVersionUnderTest" - systemProperty("gradleVersionUnderTest", gradleVersionUnderTest) - exclude("**/*SamplesTest.class") // Not yet cross-version ready - exclude("**/initialization/**") // Settings plugin only for Gradle 8.8+ - if (gradleVersionUnderTest == "7.4") { - // Configuration cache only "reliable" since 7.6 (?) - // https://github.com/gradlex-org/java-module-dependencies/issues/129 - exclude("**/configcache/**") - } - } - } +val modulePropertiesScope = detachedResolver.configurations.dependencyScope("moduleProperties") +val modulePropertiesPath = + detachedResolver.configurations.resolvable("modulePropertiesPath") { extendsFrom(modulePropertiesScope.get()) } +val dep = detachedResolver.dependencies.add(modulePropertiesScope.name, "com.github.sormuras.modules:modules:1") + +(dep as ExternalModuleDependency).isChanging = true + +val updateUniqueModulesProperties = + tasks.register("updateUniqueModulesProperties") { + skipUpdate.set(providers.environmentVariable("CI").getOrElse("false").toBoolean()) + modulesProperties.from(modulePropertiesPath) + uniqueModulesProperties.set( + layout.projectDirectory.file( + "src/main/resources/org/gradlex/javamodule/dependencies/unique_modules.properties" + ) + ) } - targets.all { - testTask { - maxParallelForks = 4 - inputs.dir(layout.projectDirectory.dir("samples")) - inputs.dir("samples") + +sourceSets.main { + resources.setSrcDirs( + listOf( + updateUniqueModulesProperties.map { + it.uniqueModulesProperties.get().asFile.parentFile.parentFile.parentFile.parentFile.parentFile + } + ) + ) +} + +abstract class UniqueModulesPropertiesUpdate : DefaultTask() { + + @get:Inject abstract val layout: ProjectLayout + + @get:Input abstract val skipUpdate: Property + + @get:InputFiles abstract val modulesProperties: ConfigurableFileCollection + + @get:OutputFile abstract val uniqueModulesProperties: RegularFileProperty + + @TaskAction + fun convert() { + if (skipUpdate.get()) { + return } + + val modulesToRepoLocation = Properties() + modulesToRepoLocation.load(modulesProperties.singleFile.inputStream()) + val modules = + modulesToRepoLocation + .toSortedMap { e1, e2 -> e1.toString().compareTo(e2.toString()) } + .map { entry -> + val split = entry.value.toString().split("/") + val group = split.subList(4, split.size - 3).joinToString(".") + val name = + split[split.size - 3] + // Special handling of "wrong" entries + .replace("-debug-jdk18on", "-jdk18on") + val version = split[split.size - 2] + Module(entry.key.toString(), "$group:$name", version) + } + .groupBy { it.ga } + .values + .map { moduleList -> moduleList.maxBy { VersionNumber.parse(it.version) } } + .sortedBy { it.name } + + val modulesToCoordinates = modules.map { "${it.name}=${it.ga}\n" } + uniqueModulesProperties.get().asFile.writeText(modulesToCoordinates.joinToString("").trim()) } + + data class Module(val name: String, val ga: String, val version: String) } diff --git a/gradle/checkstyle/checkstyle.xml b/gradle/checkstyle/checkstyle.xml deleted file mode 100644 index 987e06d8..00000000 --- a/gradle/checkstyle/checkstyle.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - diff --git a/gradle/checkstyle/header.txt b/gradle/checkstyle/header.txt deleted file mode 100644 index 4fe8a001..00000000 --- a/gradle/checkstyle/header.txt +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ \ No newline at end of file diff --git a/gradle/gradle-daemon-jvm.properties b/gradle/gradle-daemon-jvm.properties new file mode 100644 index 00000000..20fe5710 --- /dev/null +++ b/gradle/gradle-daemon-jvm.properties @@ -0,0 +1 @@ +toolchainVersion=17 \ No newline at end of file diff --git a/gradle/plugins/build.gradle.kts b/gradle/plugins/build.gradle.kts deleted file mode 100644 index bc0172f0..00000000 --- a/gradle/plugins/build.gradle.kts +++ /dev/null @@ -1,3 +0,0 @@ -plugins { - `kotlin-dsl` -} diff --git a/gradle/plugins/settings.gradle.kts b/gradle/plugins/settings.gradle.kts deleted file mode 100644 index a43c8694..00000000 --- a/gradle/plugins/settings.gradle.kts +++ /dev/null @@ -1,3 +0,0 @@ -dependencyResolutionManagement { - repositories.gradlePluginPortal() -} diff --git a/gradle/plugins/src/main/kotlin/gradlexbuild.module-mappings.gradle.kts b/gradle/plugins/src/main/kotlin/gradlexbuild.module-mappings.gradle.kts deleted file mode 100644 index e3b634b9..00000000 --- a/gradle/plugins/src/main/kotlin/gradlexbuild.module-mappings.gradle.kts +++ /dev/null @@ -1,39 +0,0 @@ -import gradlexbuild.UniqueModulesPropertiesUpdate -import org.gradle.api.internal.project.ProjectInternal - -plugins { - id("java") -} - -val detachedResolver: ProjectInternal.DetachedResolver = (project as ProjectInternal).newDetachedResolver() -detachedResolver.repositories.ivy { - name = "Modules Properties Repository" - url = project.uri("https://raw.githubusercontent.com/sormuras/modules/main/com.github.sormuras.modules") - metadataSources.artifact() - patternLayout { - artifact("[organisation]/[module].properties") - ivy("[module]/[revision]/ivy.xml") - setM2compatible(true) - } -} -val modulePropertiesScope = detachedResolver.configurations.dependencyScope("moduleProperties") -val modulePropertiesPath = detachedResolver.configurations.resolvable("modulePropertiesPath") { - extendsFrom(modulePropertiesScope.get()) -} -val dep = detachedResolver.dependencies.add(modulePropertiesScope.name, "com.github.sormuras.modules:modules:1") -(dep as ExternalModuleDependency).isChanging = true - -val updateUniqueModulesProperties = tasks.register("updateUniqueModulesProperties") { - skipUpdate.set(providers.environmentVariable("CI").getOrElse("false").toBoolean()) - modulesProperties.from(modulePropertiesPath) - uniqueModulesProperties.set(layout.projectDirectory.file( - "src/main/resources/org/gradlex/javamodule/dependencies/unique_modules.properties") - ) -} - -sourceSets.main { - resources.setSrcDirs(listOf(updateUniqueModulesProperties.map { - it.uniqueModulesProperties.get().asFile.parentFile.parentFile.parentFile.parentFile.parentFile - })) -} - diff --git a/gradle/plugins/src/main/kotlin/gradlexbuild/UniqueModulesPropertiesUpdate.kt b/gradle/plugins/src/main/kotlin/gradlexbuild/UniqueModulesPropertiesUpdate.kt deleted file mode 100644 index b169f201..00000000 --- a/gradle/plugins/src/main/kotlin/gradlexbuild/UniqueModulesPropertiesUpdate.kt +++ /dev/null @@ -1,55 +0,0 @@ -package gradlexbuild - -import org.gradle.api.DefaultTask -import org.gradle.api.file.ConfigurableFileCollection -import org.gradle.api.file.ProjectLayout -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.provider.Property -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.InputFiles -import org.gradle.api.tasks.OutputFile -import org.gradle.api.tasks.TaskAction -import org.gradle.util.internal.VersionNumber -import java.util.Properties -import javax.inject.Inject - -abstract class UniqueModulesPropertiesUpdate : DefaultTask() { - - @get:Inject - abstract val layout: ProjectLayout - - @get:Input - abstract val skipUpdate: Property - - @get:InputFiles - abstract val modulesProperties: ConfigurableFileCollection - - @get:OutputFile - abstract val uniqueModulesProperties: RegularFileProperty - - @TaskAction - fun convert() { - if (skipUpdate.get()) { - return - } - - val modulesToRepoLocation = Properties() - modulesToRepoLocation.load(modulesProperties.singleFile.inputStream()) - val modules = modulesToRepoLocation.toSortedMap { e1, e2 -> e1.toString().compareTo(e2.toString()) }.map { entry -> - val split = entry.value.toString().split("/") - val group = split.subList(4, split.size - 3).joinToString(".") - val name = split[split.size - 3] - // Special handling of "wrong" entries - .replace("-debug-jdk18on", "-jdk18on") - val version = split[split.size - 2] - Module(entry.key.toString(), "$group:$name", version) - }.groupBy { it.ga }.values.map { moduleList -> - moduleList.maxBy { VersionNumber.parse(it.version) } - }.sortedBy { it.name } - - val modulesToCoordinates = modules.map { "${it.name}=${it.ga}\n" } - uniqueModulesProperties.get().asFile.writeText(modulesToCoordinates.joinToString("").trim()) - } - - data class Module(val name: String, val ga: String, val version: String) -} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 6bb45b59..d1cf9b5e 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,22 +1,3 @@ -pluginManagement { - includeBuild("gradle/plugins") -} -plugins { - id("com.gradle.develocity") version "4.2.2" -} - -dependencyResolutionManagement { - repositories { - mavenCentral() - gradlePluginPortal() - } -} +plugins { id("org.gradlex.internal-build-conventions") version "0.8" } rootProject.name = "java-module-dependencies" - -develocity { - buildScan { - termsOfUseUrl = "https://gradle.com/help/legal-terms-of-use" - termsOfUseAgree = "yes" - } -} diff --git a/src/test/java/org/gradlex/javamodule/dependencies/test/initialization/SettingsPluginIncludeTest.java b/src/test/java/org/gradlex/javamodule/dependencies/test/initialization/SettingsPluginIncludeTest.java index d53fd0fe..8753e68a 100644 --- a/src/test/java/org/gradlex/javamodule/dependencies/test/initialization/SettingsPluginIncludeTest.java +++ b/src/test/java/org/gradlex/javamodule/dependencies/test/initialization/SettingsPluginIncludeTest.java @@ -18,12 +18,14 @@ import org.gradlex.javamodule.dependencies.test.fixture.GradleBuild; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import static java.util.Objects.requireNonNull; import static org.assertj.core.api.Assertions.assertThat; import static org.gradle.testkit.runner.TaskOutcome.SUCCESS; +@Tag("no-cross-version") class SettingsPluginIncludeTest { GradleBuild build = new GradleBuild(); diff --git a/src/test/java/org/gradlex/javamodule/dependencies/test/initialization/SettingsPluginTest.java b/src/test/java/org/gradlex/javamodule/dependencies/test/initialization/SettingsPluginTest.java index c6bc0cd9..55e2a035 100644 --- a/src/test/java/org/gradlex/javamodule/dependencies/test/initialization/SettingsPluginTest.java +++ b/src/test/java/org/gradlex/javamodule/dependencies/test/initialization/SettingsPluginTest.java @@ -18,6 +18,7 @@ import org.gradlex.javamodule.dependencies.test.fixture.GradleBuild; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import static java.util.Objects.requireNonNull; @@ -25,6 +26,7 @@ import static org.gradle.testkit.runner.TaskOutcome.NO_SOURCE; import static org.gradle.testkit.runner.TaskOutcome.SUCCESS; +@Tag("no-cross-version") class SettingsPluginTest { GradleBuild build = new GradleBuild(); diff --git a/src/test/java/org/gradlex/javamodule/dependencies/test/initialization/SettingsPluginVersionManagementTest.java b/src/test/java/org/gradlex/javamodule/dependencies/test/initialization/SettingsPluginVersionManagementTest.java index 1fc194a9..e50e6aea 100644 --- a/src/test/java/org/gradlex/javamodule/dependencies/test/initialization/SettingsPluginVersionManagementTest.java +++ b/src/test/java/org/gradlex/javamodule/dependencies/test/initialization/SettingsPluginVersionManagementTest.java @@ -18,6 +18,7 @@ import org.gradlex.javamodule.dependencies.test.fixture.GradleBuild; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import static java.util.Objects.requireNonNull; @@ -25,6 +26,7 @@ import static org.gradle.testkit.runner.TaskOutcome.SUCCESS; import static org.gradle.testkit.runner.TaskOutcome.UP_TO_DATE; +@Tag("no-cross-version") class SettingsPluginVersionManagementTest { GradleBuild build = new GradleBuild(); diff --git a/src/test/java/org/gradlex/javamodule/dependencies/test/samples/SamplesTest.java b/src/test/java/org/gradlex/javamodule/dependencies/test/samples/SamplesTest.java deleted file mode 100644 index 566a2a76..00000000 --- a/src/test/java/org/gradlex/javamodule/dependencies/test/samples/SamplesTest.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright the GradleX team. - * - * 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 org.gradlex.javamodule.dependencies.test.samples; - -import org.gradle.exemplar.test.runner.SampleModifiers; -import org.gradle.exemplar.test.runner.SamplesRoot; -import org.gradle.exemplar.test.runner.SamplesRunner; -import org.junit.runner.RunWith; - -@RunWith(SamplesRunner.class) -@SamplesRoot("samples") -@SampleModifiers(PluginBuildLocationSampleModifier.class) -public class SamplesTest { - -} diff --git a/src/test/java/org/gradlex/javamodule/dependencies/test/samples/PluginBuildLocationSampleModifier.java b/src/testSamples/java/org/gradlex/javamodule/dependencies/test/samples/PluginBuildLocationSampleModifier.java similarity index 56% rename from src/test/java/org/gradlex/javamodule/dependencies/test/samples/PluginBuildLocationSampleModifier.java rename to src/testSamples/java/org/gradlex/javamodule/dependencies/test/samples/PluginBuildLocationSampleModifier.java index d647a0b8..ad825b50 100644 --- a/src/test/java/org/gradlex/javamodule/dependencies/test/samples/PluginBuildLocationSampleModifier.java +++ b/src/testSamples/java/org/gradlex/javamodule/dependencies/test/samples/PluginBuildLocationSampleModifier.java @@ -1,35 +1,20 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies.test.samples; +import java.io.File; +import java.util.Arrays; import org.gradle.exemplar.model.Command; import org.gradle.exemplar.model.Sample; import org.gradle.exemplar.test.runner.SampleModifier; -import java.io.File; -import java.util.Arrays; - public class PluginBuildLocationSampleModifier implements SampleModifier { @Override public Sample modify(Sample sampleIn) { Command cmd = sampleIn.getCommands().remove(0); File pluginProjectDir = new File("."); - sampleIn.getCommands().add( - new Command(new File(pluginProjectDir, "gradlew").getAbsolutePath(), + sampleIn.getCommands() + .add(new Command( + new File(pluginProjectDir, "gradlew").getAbsolutePath(), cmd.getExecutionSubdirectory(), Arrays.asList("build", "run", "-PpluginLocation=" + pluginProjectDir.getAbsolutePath()), cmd.getFlags(), diff --git a/src/testSamples/java/org/gradlex/javamodule/dependencies/test/samples/SamplesTest.java b/src/testSamples/java/org/gradlex/javamodule/dependencies/test/samples/SamplesTest.java new file mode 100644 index 00000000..ee788117 --- /dev/null +++ b/src/testSamples/java/org/gradlex/javamodule/dependencies/test/samples/SamplesTest.java @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.gradlex.javamodule.dependencies.test.samples; + +import org.gradle.exemplar.test.runner.SampleModifiers; +import org.gradle.exemplar.test.runner.SamplesRoot; +import org.gradle.exemplar.test.runner.SamplesRunner; +import org.junit.runner.RunWith; + +@RunWith(SamplesRunner.class) +@SamplesRoot("samples") +@SampleModifiers(PluginBuildLocationSampleModifier.class) +public class SamplesTest {} From 828846ffac451e84d001f1bacff2189a3e44cac8 Mon Sep 17 00:00:00 2001 From: Jendrik Johannes Date: Thu, 30 Oct 2025 16:21:38 +0100 Subject: [PATCH 2/4] Format code --- .../javamodule/dependencies/JDKInfo.java | 20 +- .../JavaModuleDependenciesExtension.java | 230 +++++++----- .../JavaModuleDependenciesPlugin.java | 346 ++++++++++++------ .../JavaModuleVersionsPlugin.java | 116 +++--- .../javamodule/dependencies/LocalModule.java | 30 +- .../dependencies/SharedMappings.java | 32 +- .../dependencies/dsl/AllDirectives.java | 22 +- .../dsl/GradleOnlyDirectives.java | 27 +- .../dependencies/dsl/ModuleVersions.java | 53 ++- .../dependencies/dsl/package-info.java | 19 +- .../initialization/Directory.java | 29 +- .../JavaModuleDependenciesSettingsPlugin.java | 17 +- .../initialization/JavaModulesExtension.java | 57 +-- .../dependencies/initialization/Module.java | 36 +- .../initialization/RootPluginsExtension.java | 24 +- .../initialization/package-info.java | 19 +- .../bridges/DependencyAnalysisBridge.java | 50 ++- .../bridges/ExtraJavaModuleInfoBridge.java | 35 +- .../internal/bridges/package-info.java | 19 +- .../AsciiModuleDependencyReportRenderer.java | 37 +- .../RenderableJavaModuleResult.java | 22 +- .../RenderableModuleDependencyResult.java | 52 ++- .../diagnostics/StyledNodeRenderer.java | 25 +- .../internal/diagnostics/package-info.java | 19 +- .../internal/dsl/AllDirectivesInternal.java | 25 +- .../dsl/GradleOnlyDirectivesInternal.java | 23 +- .../internal/dsl/package-info.java | 19 +- .../utils/DependencyDeclarationsUtil.java | 40 +- .../internal/utils/ModuleInfo.java | 42 +-- .../internal/utils/ModuleInfoCache.java | 55 ++- .../utils/ModuleInfoClassCreator.java | 28 +- .../internal/utils/ModuleJar.java | 60 ++- .../internal/utils/ModuleNamingUtil.java | 34 +- .../internal/utils/TaskConfigurationUtil.java | 17 +- .../utils/ValueModuleDirectoryListing.java | 43 +-- .../internal/utils/ValueSourceModuleInfo.java | 24 +- .../internal/utils/package-info.java | 19 +- .../javamodule/dependencies/package-info.java | 19 +- .../tasks/BuildFileDependenciesGenerate.java | 139 +++---- .../dependencies/tasks/CatalogGenerate.java | 44 +-- .../tasks/ModuleDependencyReport.java | 27 +- .../tasks/ModuleDirectivesOrderingCheck.java | 38 +- .../tasks/ModuleDirectivesScopeCheck.java | 102 +++--- .../tasks/ModuleInfoGenerate.java | 77 ++-- .../tasks/ModulePathAnalysis.java | 78 ++-- .../tasks/ModuleVersionRecommendation.java | 91 +++-- .../SyntheticModuleInfoFoldersGenerate.java | 20 +- .../dependencies/tasks/package-info.java | 19 +- .../dependencies/test/CustomizationTest.java | 71 ++-- .../test/ModuleInfoParseTest.java | 40 +- .../dependencies/test/WarningsTest.java | 28 +- .../configcache/ConfigurationCacheTest.java | 55 ++- .../test/extension/ExtensionTest.java | 33 +- .../dependencies/test/fixture/Directory.java | 17 +- .../test/fixture/GradleBuild.java | 59 ++- .../dependencies/test/fixture/Io.java | 20 +- .../test/fixture/WritableFile.java | 23 +- .../test/groupmapping/GroupMappingTest.java | 30 +- .../SettingsPluginIncludeTest.java | 73 ++-- .../initialization/SettingsPluginTest.java | 143 +++++--- .../SettingsPluginVersionManagementTest.java | 57 ++- .../localmodules/LocalModuleMappingsTest.java | 39 +- .../test/runtime/RequiresRuntimeTest.java | 99 ++--- .../test/tasks/BasicFunctionalityTest.java | 32 +- .../test/tasks/OrderingCheckTest.java | 50 +-- .../test/variants/NonMainVariantsTest.java | 51 +-- 66 files changed, 1423 insertions(+), 1916 deletions(-) diff --git a/src/main/java/org/gradlex/javamodule/dependencies/JDKInfo.java b/src/main/java/org/gradlex/javamodule/dependencies/JDKInfo.java index 98c521a7..3bf346ac 100644 --- a/src/main/java/org/gradlex/javamodule/dependencies/JDKInfo.java +++ b/src/main/java/org/gradlex/javamodule/dependencies/JDKInfo.java @@ -1,19 +1,4 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies; import java.util.Arrays; @@ -82,6 +67,5 @@ interface JDKInfo { "jdk.unsupported", "jdk.unsupported.desktop", "jdk.xml.dom", - "jdk.zipfs" - ); + "jdk.zipfs"); } diff --git a/src/main/java/org/gradlex/javamodule/dependencies/JavaModuleDependenciesExtension.java b/src/main/java/org/gradlex/javamodule/dependencies/JavaModuleDependenciesExtension.java index da895d27..4c2360ae 100644 --- a/src/main/java/org/gradlex/javamodule/dependencies/JavaModuleDependenciesExtension.java +++ b/src/main/java/org/gradlex/javamodule/dependencies/JavaModuleDependenciesExtension.java @@ -1,21 +1,25 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies; +import static java.util.Optional.empty; +import static org.gradlex.javamodule.dependencies.internal.utils.DependencyDeclarationsUtil.copyVersionConstraint; +import static org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo.Directive.REQUIRES_RUNTIME; + +import java.io.CharArrayReader; +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Properties; +import java.util.Set; +import java.util.TreeSet; +import java.util.stream.Collectors; +import javax.inject.Inject; import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.ConfigurationContainer; @@ -48,26 +52,6 @@ import org.gradlex.javamodule.dependencies.tasks.SyntheticModuleInfoFoldersGenerate; import org.jspecify.annotations.Nullable; -import javax.inject.Inject; -import java.io.CharArrayReader; -import java.io.File; -import java.io.IOException; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Properties; -import java.util.Set; -import java.util.TreeSet; -import java.util.stream.Collectors; - -import static java.util.Optional.empty; -import static org.gradlex.javamodule.dependencies.internal.utils.DependencyDeclarationsUtil.copyVersionConstraint; -import static org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo.Directive.REQUIRES_RUNTIME; - /** * - Configure behavior of the 'java-module-dependencies' plugin * - Add additional mappings using {@link #getModuleNameToGA()} @@ -136,7 +120,8 @@ public abstract class JavaModuleDependenciesExtension { public JavaModuleDependenciesExtension(@Nullable VersionCatalogsExtension versionCatalogs, File rootDir) { this.versionCatalogs = versionCatalogs; - getModuleInfoCache().convention(getProviders().provider(() -> getObjects().newInstance(ModuleInfoCache.class, false))); + getModuleInfoCache() + .convention(getProviders().provider(() -> getObjects().newInstance(ModuleInfoCache.class, false))); getModulesProperties().set(new File(rootDir, "gradle/modules.properties")); getVersionCatalogName().convention("libs"); getModuleNameCheck().convention(true); @@ -167,7 +152,10 @@ private Provider> parsedModulesProperties() { * @return Dependency notation */ public Provider ga(String moduleName) { - return getModuleNameToGA().getting(moduleName).orElse(mapByPrefix(getProviders().provider(() -> moduleName))).orElse(errorIfNotFound(moduleName)); + return getModuleNameToGA() + .getting(moduleName) + .orElse(mapByPrefix(getProviders().provider(() -> moduleName))) + .orElse(errorIfNotFound(moduleName)); } /** @@ -178,36 +166,47 @@ public Provider ga(String moduleName) { * @return Dependency notation */ public Provider ga(Provider moduleName) { - return moduleName.flatMap(n -> getModuleNameToGA().getting(n)).orElse(mapByPrefix(moduleName)).orElse(errorIfNotFound(moduleName)); + return moduleName + .flatMap(n -> getModuleNameToGA().getting(n)) + .orElse(mapByPrefix(moduleName)) + .orElse(errorIfNotFound(moduleName)); } private Provider mapByPrefix(Provider moduleName) { - return getModuleNamePrefixToGroup().map( - m -> { - Optional> prefixToGroup = m.entrySet().stream() - .filter(e -> moduleName.get().startsWith(e.getKey())).max(Comparator.comparingInt(e -> e.getKey().length())); - if (prefixToGroup.isPresent()) { - String group = prefixToGroup.get().getValue(); - String artifact = toProjectName(moduleName.get().substring(prefixToGroup.get().getKey().length())); - return group + ":" + artifact; - } - return null; - } - ); + return getModuleNamePrefixToGroup().map(m -> { + Optional> prefixToGroup = m.entrySet().stream() + .filter(e -> moduleName.get().startsWith(e.getKey())) + .max(Comparator.comparingInt(e -> e.getKey().length())); + if (prefixToGroup.isPresent()) { + String group = prefixToGroup.get().getValue(); + String artifact = toProjectName( + moduleName.get().substring(prefixToGroup.get().getKey().length())); + return group + ":" + artifact; + } + return null; + }); } private String toProjectName(String moduleNameSuffix) { - List allProjectNames = getProject().getRootProject().getSubprojects().stream().map(Project::getName).collect(Collectors.toList()); - - Optional perfectMatch = allProjectNames.stream().filter(p -> p.replace("-", ".").equals(moduleNameSuffix)).findFirst(); - Optional existingProjectName = allProjectNames.stream().filter(p -> moduleNameSuffix.startsWith(p.replace("-", ".") + ".")) + List allProjectNames = getProject().getRootProject().getSubprojects().stream() + .map(Project::getName) + .collect(Collectors.toList()); + + Optional perfectMatch = allProjectNames.stream() + .filter(p -> p.replace("-", ".").equals(moduleNameSuffix)) + .findFirst(); + Optional existingProjectName = allProjectNames.stream() + .filter(p -> moduleNameSuffix.startsWith(p.replace("-", ".") + ".")) .max(Comparator.comparingInt(String::length)); if (perfectMatch.isPresent()) { return perfectMatch.get(); } else if (existingProjectName.isPresent()) { - String capabilityClassifier = moduleNameSuffix.substring(existingProjectName.get().length() + 1).replace(".", "-"); - return existingProjectName.get() + "|" + capabilityClassifier; // no exact match (assume last segment is capability) + String capabilityClassifier = moduleNameSuffix + .substring(existingProjectName.get().length() + 1) + .replace(".", "-"); + return existingProjectName.get() + "|" + + capabilityClassifier; // no exact match (assume last segment is capability) } return moduleNameSuffix; @@ -231,7 +230,8 @@ private Provider createPrecise(String moduleName) { if (localModule != null) { // local project - ProjectDependency projectDependency = (ProjectDependency) getDependencies().create(getProject().project(localModule.getProjectPath())); + ProjectDependency projectDependency = (ProjectDependency) + getDependencies().create(getProject().project(localModule.getProjectPath())); projectDependency.because(moduleName); if (localModule.getCapability() != null) { projectDependency.capabilities(c -> c.requireCapabilities(localModule.getCapability())); @@ -245,32 +245,45 @@ private Provider createPrecise(String moduleName) { private Provider createWithGuessing(String moduleName, SourceSet sourceSetWithModuleInfo) { return getProviders().provider(() -> { - Map allProjectNamesAndGroups = getProject().getRootProject().getSubprojects().stream().collect( - Collectors.toMap(Project::getName, p -> (String) p.getGroup(), (a, b) -> a)); + Map allProjectNamesAndGroups = getProject().getRootProject().getSubprojects().stream() + .collect(Collectors.toMap(Project::getName, p -> (String) p.getGroup(), (a, b) -> a)); ModuleInfo moduleInfo = getModuleInfoCache().get().get(sourceSetWithModuleInfo, getProviders()); - String ownModuleNamesPrefix = moduleInfo.moduleNamePrefix(getProject().getName(), sourceSetWithModuleInfo.getName(), getModuleNameCheck().get()); - - String moduleNameSuffix = ownModuleNamesPrefix == null ? null : - moduleName.startsWith(ownModuleNamesPrefix + ".") ? moduleName.substring(ownModuleNamesPrefix.length() + 1) : - ownModuleNamesPrefix.isEmpty() ? moduleName : null; - - String parentPath = getProject().getParent() == null ? "" : getProject().getParent().getPath(); - Optional perfectMatch = allProjectNamesAndGroups.keySet().stream().filter(p -> p.replace("-", ".").equals(moduleNameSuffix)).findFirst(); - Optional existingProjectName = allProjectNamesAndGroups.keySet().stream().filter(p -> moduleNameSuffix != null && moduleNameSuffix.startsWith(p.replace("-", ".") + ".")) + String ownModuleNamesPrefix = moduleInfo.moduleNamePrefix( + getProject().getName(), + sourceSetWithModuleInfo.getName(), + getModuleNameCheck().get()); + + String moduleNameSuffix = ownModuleNamesPrefix == null + ? null + : moduleName.startsWith(ownModuleNamesPrefix + ".") + ? moduleName.substring(ownModuleNamesPrefix.length() + 1) + : ownModuleNamesPrefix.isEmpty() ? moduleName : null; + + String parentPath = getProject().getParent() == null + ? "" + : getProject().getParent().getPath(); + Optional perfectMatch = allProjectNamesAndGroups.keySet().stream() + .filter(p -> p.replace("-", ".").equals(moduleNameSuffix)) + .findFirst(); + Optional existingProjectName = allProjectNamesAndGroups.keySet().stream() + .filter(p -> moduleNameSuffix != null && moduleNameSuffix.startsWith(p.replace("-", ".") + ".")) .max(Comparator.comparingInt(String::length)); if (perfectMatch.isPresent()) { - Dependency projectDependency = getDependencies().create(getProject().project(parentPath + ":" + perfectMatch.get())); + Dependency projectDependency = + getDependencies().create(getProject().project(parentPath + ":" + perfectMatch.get())); projectDependency.because(moduleName); return projectDependency; } else if (existingProjectName.isPresent()) { // no exact match -> add capability to point at Module in other source set String projectName = existingProjectName.get(); - ProjectDependency projectDependency = (ProjectDependency) getDependencies().create(getProject().project(parentPath + ":" + projectName)); - String capabilityName = projectName + moduleNameSuffix.substring(projectName.length()).replace(".", "-"); - projectDependency.capabilities(c -> c.requireCapabilities( - allProjectNamesAndGroups.get(projectName) + ":" + capabilityName)); + ProjectDependency projectDependency = (ProjectDependency) + getDependencies().create(getProject().project(parentPath + ":" + projectName)); + String capabilityName = projectName + + moduleNameSuffix.substring(projectName.length()).replace(".", "-"); + projectDependency.capabilities( + c -> c.requireCapabilities(allProjectNamesAndGroups.get(projectName) + ":" + capabilityName)); projectDependency.because(moduleName); return projectDependency; } @@ -280,7 +293,9 @@ private Provider createWithGuessing(String moduleName, SourceSet sou } private @Nullable ModuleDependency createExternalDependency(String moduleName) { - Provider coordinates = getModuleNameToGA().getting(moduleName).orElse(mapByPrefix(getProviders().provider(() -> moduleName))); + Provider coordinates = getModuleNameToGA() + .getting(moduleName) + .orElse(mapByPrefix(getProviders().provider(() -> moduleName))); if (coordinates.isPresent()) { ExternalDependency component; String capability; @@ -304,8 +319,10 @@ private Provider createWithGuessing(String moduleName, SourceSet sou } return dependency; } else { - getProject().getLogger().lifecycle( - "[WARN] [Java Module Dependencies] " + moduleName + "=group:artifact missing in " + getModulesProperties().get().getAsFile()); + getProject() + .getLogger() + .lifecycle("[WARN] [Java Module Dependencies] " + moduleName + "=group:artifact missing in " + + getModulesProperties().get().getAsFile()); return null; } } @@ -365,14 +382,17 @@ public Provider gav(Provider moduleName) { } private ExternalDependency findGav(String ga, String moduleName) { - Optional catalog = versionCatalogs == null ? empty() : versionCatalogs.find(getVersionCatalogName().get()); - Optional version = catalog.flatMap(versionCatalog -> versionCatalog.findVersion(moduleName.replace('_', '.'))); + Optional catalog = versionCatalogs == null + ? empty() + : versionCatalogs.find(getVersionCatalogName().get()); + Optional version = + catalog.flatMap(versionCatalog -> versionCatalog.findVersion(moduleName.replace('_', '.'))); ExternalDependency dependency = (ExternalDependency) getDependencies().create(ga); - version.ifPresent(versionConstraint -> dependency.version(copy -> copyVersionConstraint(versionConstraint, copy))); + version.ifPresent( + versionConstraint -> dependency.version(copy -> copyVersionConstraint(versionConstraint, copy))); return dependency; } - /** * Finds the Module Name for given coordinates * @@ -391,16 +411,20 @@ public Provider moduleName(String ga) { */ public Provider moduleName(Provider ga) { return ga.map(groupArtifact -> { - Optional found = getModuleNameToGA().get().entrySet().stream().filter( - e -> e.getValue().equals(groupArtifact)).map(Map.Entry::getKey).findFirst(); + Optional found = getModuleNameToGA().get().entrySet().stream() + .filter(e -> e.getValue().equals(groupArtifact)) + .map(Map.Entry::getKey) + .findFirst(); if (found.isPresent()) { return found.get(); } else { String[] split = groupArtifact.split(":"); String group = split[0]; String artifact = split[1]; - Optional modulePrefix = getModuleNamePrefixToGroup().get().entrySet().stream().filter( - e -> e.getValue().equals(group)).map(Map.Entry::getKey).findFirst(); + Optional modulePrefix = getModuleNamePrefixToGroup().get().entrySet().stream() + .filter(e -> e.getValue().equals(group)) + .map(Map.Entry::getKey) + .findFirst(); return modulePrefix.map(s -> s + artifact).orElse(null); } }); @@ -435,8 +459,14 @@ public Configuration versionsFromConsistentResolution(Collection version c.setCanBeConsumed(false); c.getAttributes().attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.class, Usage.JAVA_RUNTIME)); c.getAttributes().attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.class, Category.LIBRARY)); - c.getAttributes().attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.class, LibraryElements.JAR)); - c.getAttributes().attribute(TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE, objects.named(TargetJvmEnvironment.class, TargetJvmEnvironment.STANDARD_JVM)); + c.getAttributes() + .attribute( + LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, + objects.named(LibraryElements.class, LibraryElements.JAR)); + c.getAttributes() + .attribute( + TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE, + objects.named(TargetJvmEnvironment.class, TargetJvmEnvironment.STANDARD_JVM)); c.getAttributes().attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling.class, Bundling.EXTERNAL)); }); getConfigurations().configureEach(c -> { @@ -453,9 +483,11 @@ public Configuration versionsFromConsistentResolution(Collection version private Dependency createDependency(String project) { boolean isProjectInBuild = project.startsWith(":"); - return getDependencies().create(isProjectInBuild - ? getDependencies().project(Collections.singletonMap("path", project)) - : project); + return getDependencies() + .create( + isProjectInBuild + ? getDependencies().project(Collections.singletonMap("path", project)) + : project); } /** @@ -471,7 +503,10 @@ public void addRequiresRuntimeSupport(SourceSet sourceSetForModuleInfo, SourceSe } void doAddRequiresRuntimeSupport(SourceSet sourceSetForModuleInfo, SourceSet sourceSetForClasspath) { - List requiresRuntime = getModuleInfoCache().get().get(sourceSetForModuleInfo, getProviders()).get(REQUIRES_RUNTIME); + List requiresRuntime = getModuleInfoCache() + .get() + .get(sourceSetForModuleInfo, getProviders()) + .get(REQUIRES_RUNTIME); String generatorTaskName = sourceSetForClasspath.getTaskName("generate", "syntheticModuleInfoFolders"); if (requiresRuntime.isEmpty() || getProject().getTasks().getNames().contains(generatorTaskName)) { // Already active or not needed for this source set @@ -479,15 +514,18 @@ void doAddRequiresRuntimeSupport(SourceSet sourceSetForModuleInfo, SourceSet sou } ConfigurableFileCollection syntheticModuleInfoFolders = getObjects().fileCollection(); - Provider moduleInfoFoldersBase = getLayout().getBuildDirectory().dir("tmp/java-module-dependencies/" + sourceSetForClasspath.getName()); - TaskProvider generatorTask = getProject().getTasks().register( - generatorTaskName, - SyntheticModuleInfoFoldersGenerate.class, t -> { + Provider moduleInfoFoldersBase = + getLayout().getBuildDirectory().dir("tmp/java-module-dependencies/" + sourceSetForClasspath.getName()); + TaskProvider generatorTask = getProject() + .getTasks() + .register(generatorTaskName, SyntheticModuleInfoFoldersGenerate.class, t -> { t.getModuleNames().set(requiresRuntime); t.getSyntheticModuleInfoFolder().set(moduleInfoFoldersBase); }); - List> moduleInfoFolders = requiresRuntime.stream().map(moduleName -> moduleInfoFoldersBase.map(b -> b.dir(moduleName))).collect(Collectors.toList()); + List> moduleInfoFolders = requiresRuntime.stream() + .map(moduleName -> moduleInfoFoldersBase.map(b -> b.dir(moduleName))) + .collect(Collectors.toList()); for (Provider syntheticModuleInfoFolder : moduleInfoFolders) { syntheticModuleInfoFolders.from(syntheticModuleInfoFolder); } diff --git a/src/main/java/org/gradlex/javamodule/dependencies/JavaModuleDependenciesPlugin.java b/src/main/java/org/gradlex/javamodule/dependencies/JavaModuleDependenciesPlugin.java index c2157cd0..20921055 100644 --- a/src/main/java/org/gradlex/javamodule/dependencies/JavaModuleDependenciesPlugin.java +++ b/src/main/java/org/gradlex/javamodule/dependencies/JavaModuleDependenciesPlugin.java @@ -1,21 +1,21 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies; +import static org.gradle.api.plugins.HelpTasksPlugin.HELP_GROUP; +import static org.gradle.language.base.plugins.LifecycleBasePlugin.VERIFICATION_GROUP; +import static org.gradlex.javamodule.dependencies.JavaModuleDependenciesExtension.JAVA_MODULE_DEPENDENCIES; +import static org.gradlex.javamodule.dependencies.internal.utils.DependencyDeclarationsUtil.declaredDependencies; +import static org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo.Directive.REQUIRES; +import static org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo.Directive.REQUIRES_RUNTIME; +import static org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo.Directive.REQUIRES_STATIC; +import static org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo.Directive.REQUIRES_STATIC_TRANSITIVE; +import static org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo.Directive.REQUIRES_TRANSITIVE; +import static org.gradlex.javamodule.dependencies.internal.utils.ModuleNamingUtil.sourceSetToModuleName; + +import java.io.File; +import java.util.HashSet; +import java.util.List; +import java.util.stream.Collectors; import org.gradle.api.GradleException; import org.gradle.api.Plugin; import org.gradle.api.Project; @@ -46,27 +46,12 @@ import org.gradlex.javamodule.dependencies.tasks.ModulePathAnalysis; import org.gradlex.javamodule.dependencies.tasks.ModuleVersionRecommendation; -import java.io.File; -import java.util.HashSet; -import java.util.List; -import java.util.stream.Collectors; - -import static org.gradle.api.plugins.HelpTasksPlugin.HELP_GROUP; -import static org.gradle.language.base.plugins.LifecycleBasePlugin.VERIFICATION_GROUP; -import static org.gradlex.javamodule.dependencies.JavaModuleDependenciesExtension.JAVA_MODULE_DEPENDENCIES; -import static org.gradlex.javamodule.dependencies.internal.utils.DependencyDeclarationsUtil.declaredDependencies; -import static org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo.Directive.REQUIRES; -import static org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo.Directive.REQUIRES_RUNTIME; -import static org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo.Directive.REQUIRES_STATIC; -import static org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo.Directive.REQUIRES_STATIC_TRANSITIVE; -import static org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo.Directive.REQUIRES_TRANSITIVE; -import static org.gradlex.javamodule.dependencies.internal.utils.ModuleNamingUtil.sourceSetToModuleName; - @SuppressWarnings("unused") public abstract class JavaModuleDependenciesPlugin implements Plugin { private static final String EXTRA_JAVA_MODULE_INFO_PLUGIN_ID = "org.gradlex.extra-java-module-info"; - private static final String REGISTER_HELP_TASKS_PROPERTY = "org.gradlex.java-module-dependencies.register-help-tasks"; + private static final String REGISTER_HELP_TASKS_PROPERTY = + "org.gradlex.java-module-dependencies.register-help-tasks"; @Override public void apply(ExtensionAware projectOrSettings) { @@ -83,12 +68,17 @@ public void apply(ExtensionAware projectOrSettings) { private void applyProject(Project project) { VersionCatalogsExtension versionCatalogs = project.getExtensions().findByType(VersionCatalogsExtension.class); @SuppressWarnings("DataFlowIssue") - JavaModuleDependenciesExtension javaModuleDependencies = project.getExtensions().create( - JAVA_MODULE_DEPENDENCIES, JavaModuleDependenciesExtension.class, versionCatalogs, project.getRootDir()); + JavaModuleDependenciesExtension javaModuleDependencies = project.getExtensions() + .create( + JAVA_MODULE_DEPENDENCIES, + JavaModuleDependenciesExtension.class, + versionCatalogs, + project.getRootDir()); setupExtraJavaModulePluginBridge(project, javaModuleDependencies); - project.getPlugins().withType(JavaPlugin.class, javaPlugin -> setupForJavaProject(project, javaModuleDependencies)); + project.getPlugins() + .withType(JavaPlugin.class, javaPlugin -> setupForJavaProject(project, javaModuleDependencies)); } private void setupForJavaProject(Project project, JavaModuleDependenciesExtension javaModuleDependencies) { @@ -98,21 +88,48 @@ private void setupForJavaProject(Project project, JavaModuleDependenciesExtensio SourceSetContainer sourceSets = project.getExtensions().getByType(SourceSetContainer.class); sourceSets.all(sourceSet -> { - process(REQUIRES, sourceSet.getImplementationConfigurationName(), sourceSet, project, javaModuleDependencies); - process(REQUIRES_STATIC, sourceSet.getCompileOnlyConfigurationName(), sourceSet, project, javaModuleDependencies); - process(REQUIRES_TRANSITIVE, sourceSet.getApiConfigurationName(), sourceSet, project, javaModuleDependencies); - process(REQUIRES_STATIC_TRANSITIVE, sourceSet.getCompileOnlyApiConfigurationName(), sourceSet, project, javaModuleDependencies); - process(REQUIRES_RUNTIME, sourceSet.getRuntimeOnlyConfigurationName(), sourceSet, project, javaModuleDependencies); + process( + REQUIRES, + sourceSet.getImplementationConfigurationName(), + sourceSet, + project, + javaModuleDependencies); + process( + REQUIRES_STATIC, + sourceSet.getCompileOnlyConfigurationName(), + sourceSet, + project, + javaModuleDependencies); + process( + REQUIRES_TRANSITIVE, + sourceSet.getApiConfigurationName(), + sourceSet, + project, + javaModuleDependencies); + process( + REQUIRES_STATIC_TRANSITIVE, + sourceSet.getCompileOnlyApiConfigurationName(), + sourceSet, + project, + javaModuleDependencies); + process( + REQUIRES_RUNTIME, + sourceSet.getRuntimeOnlyConfigurationName(), + sourceSet, + project, + javaModuleDependencies); javaModuleDependencies.doAddRequiresRuntimeSupport(sourceSet, sourceSet); }); setupDirectivesDSL(project, javaModuleDependencies); - TaskProvider checkAllModuleInfo = registerHelpTasks ? project.getTasks().register("checkAllModuleInfo", t -> { - t.setGroup(VERIFICATION_GROUP); - t.setDescription("Check scope and order of directives in 'module-info.java' files"); - }) : null; + TaskProvider checkAllModuleInfo = registerHelpTasks + ? project.getTasks().register("checkAllModuleInfo", t -> { + t.setGroup(VERIFICATION_GROUP); + t.setDescription("Check scope and order of directives in 'module-info.java' files"); + }) + : null; if (registerHelpTasks) { setupOrderingCheckTasks(project, checkAllModuleInfo, javaModuleDependencies); @@ -121,13 +138,20 @@ private void setupForJavaProject(Project project, JavaModuleDependenciesExtensio setupMigrationTasks(project, javaModuleDependencies); } - project.getPlugins().withId("com.autonomousapps.dependency-analysis", analysisPlugin -> - DependencyAnalysisBridge.registerDependencyAnalysisPostProcessingTask(project, checkAllModuleInfo)); + project.getPlugins() + .withId( + "com.autonomousapps.dependency-analysis", + analysisPlugin -> DependencyAnalysisBridge.registerDependencyAnalysisPostProcessingTask( + project, checkAllModuleInfo)); } - private void setupExtraJavaModulePluginBridge(Project project, JavaModuleDependenciesExtension javaModuleDependencies) { - project.getPlugins().withId(EXTRA_JAVA_MODULE_INFO_PLUGIN_ID, - e -> ExtraJavaModuleInfoBridge.autoRegisterPatchedModuleMappings(project, javaModuleDependencies)); + private void setupExtraJavaModulePluginBridge( + Project project, JavaModuleDependenciesExtension javaModuleDependencies) { + project.getPlugins() + .withId( + EXTRA_JAVA_MODULE_INFO_PLUGIN_ID, + e -> ExtraJavaModuleInfoBridge.autoRegisterPatchedModuleMappings( + project, javaModuleDependencies)); } private void setupDirectivesDSL(Project project, JavaModuleDependenciesExtension javaModuleDependencies) { @@ -135,19 +159,33 @@ private void setupDirectivesDSL(Project project, JavaModuleDependenciesExtension SourceSetContainer sourceSets = extensions.getByType(SourceSetContainer.class); SourceSet mainSourceSet = sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME); sourceSets.all(sourceSet -> { - File moduleInfoFile = new File(sourceSet.getJava().getSrcDirs().iterator().next(), "module-info.java"); + File moduleInfoFile = + new File(sourceSet.getJava().getSrcDirs().iterator().next(), "module-info.java"); String extensionName = sourceSet.getName() + "ModuleInfo"; if (moduleInfoFile.exists()) { - extensions.create(GradleOnlyDirectives.class, extensionName, GradleOnlyDirectivesInternal.class, sourceSet, mainSourceSet, javaModuleDependencies); + extensions.create( + GradleOnlyDirectives.class, + extensionName, + GradleOnlyDirectivesInternal.class, + sourceSet, + mainSourceSet, + javaModuleDependencies); } else { - extensions.create(AllDirectives.class, extensionName, AllDirectivesInternal.class, sourceSet, mainSourceSet, javaModuleDependencies); + extensions.create( + AllDirectives.class, + extensionName, + AllDirectivesInternal.class, + sourceSet, + mainSourceSet, + javaModuleDependencies); } }); } private void setupModuleDependenciesTask(Project project) { SourceSetContainer sourceSets = project.getExtensions().getByType(SourceSetContainer.class); - TaskProvider moduleDependencies = project.getTasks().register("moduleDependencies", ModuleDependencyReport.class, t -> t.setGroup(HELP_GROUP)); + TaskProvider moduleDependencies = project.getTasks() + .register("moduleDependencies", ModuleDependencyReport.class, t -> t.setGroup(HELP_GROUP)); sourceSets.all(sourceSet -> moduleDependencies.configure(t -> { HashSet joined = new HashSet<>(); //noinspection ConstantValue @@ -167,8 +205,10 @@ private void setupReportTasks(Project project, JavaModuleDependenciesExtension j t.setDescription("Check consistency of the Module Path"); for (SourceSet sourceSet : sourceSets) { - t.getClasspathConfigurations().add(project.getConfigurations().getByName(sourceSet.getCompileClasspathConfigurationName())); - t.getClasspathConfigurations().add(project.getConfigurations().getByName(sourceSet.getRuntimeClasspathConfigurationName())); + t.getClasspathConfigurations() + .add(project.getConfigurations().getByName(sourceSet.getCompileClasspathConfigurationName())); + t.getClasspathConfigurations() + .add(project.getConfigurations().getByName(sourceSet.getRuntimeClasspathConfigurationName())); } }); project.getTasks().register("recommendModuleVersions", ModuleVersionRecommendation.class, t -> { @@ -190,23 +230,40 @@ private void setupMigrationTasks(Project project, JavaModuleDependenciesExtensio }); sourceSets.all(sourceSet -> { - TaskProvider generateModuleInfo = project.getTasks().register(sourceSet.getTaskName("generate", "ModuleInfoFile"), ModuleInfoGenerate.class, t -> { - t.setGroup("java modules"); - t.setDescription("Generate 'module-info.java' in '" + sourceSet.getName() + "' source set"); - - t.getModuleNameToGA().putAll(javaModuleDependencies.getModuleNameToGA()); - - t.getModuleName().convention(project.provider(() -> project.getGroup() + "." + sourceSetToModuleName(project.getName(), sourceSet.getName()))); - - t.getApiDependencies().convention(declaredDependencies(project, sourceSet.getApiConfigurationName())); - t.getImplementationDependencies().convention(declaredDependencies(project, sourceSet.getImplementationConfigurationName())); - t.getCompileOnlyApiDependencies().convention(declaredDependencies(project, sourceSet.getCompileOnlyApiConfigurationName())); - t.getCompileOnlyDependencies().convention(declaredDependencies(project, sourceSet.getCompileOnlyConfigurationName())); - t.getRuntimeOnlyDependencies().convention(declaredDependencies(project, sourceSet.getRuntimeOnlyConfigurationName())); - - t.getModuleInfoFile().convention(project.getLayout().file(project.provider(() -> - new File(sourceSet.getJava().getSrcDirs().iterator().next(), "module-info.java")))); - }); + TaskProvider generateModuleInfo = project.getTasks() + .register(sourceSet.getTaskName("generate", "ModuleInfoFile"), ModuleInfoGenerate.class, t -> { + t.setGroup("java modules"); + t.setDescription("Generate 'module-info.java' in '" + sourceSet.getName() + "' source set"); + + t.getModuleNameToGA().putAll(javaModuleDependencies.getModuleNameToGA()); + + t.getModuleName() + .convention(project.provider(() -> project.getGroup() + "." + + sourceSetToModuleName(project.getName(), sourceSet.getName()))); + + t.getApiDependencies() + .convention(declaredDependencies(project, sourceSet.getApiConfigurationName())); + t.getImplementationDependencies() + .convention( + declaredDependencies(project, sourceSet.getImplementationConfigurationName())); + t.getCompileOnlyApiDependencies() + .convention( + declaredDependencies(project, sourceSet.getCompileOnlyApiConfigurationName())); + t.getCompileOnlyDependencies() + .convention(declaredDependencies(project, sourceSet.getCompileOnlyConfigurationName())); + t.getRuntimeOnlyDependencies() + .convention(declaredDependencies(project, sourceSet.getRuntimeOnlyConfigurationName())); + + t.getModuleInfoFile() + .convention(project.getLayout() + .file(project.provider(() -> new File( + sourceSet + .getJava() + .getSrcDirs() + .iterator() + .next(), + "module-info.java")))); + }); generateAllModuleInfoFiles.configure(t -> t.dependsOn(generateModuleInfo)); }); @@ -217,82 +274,153 @@ private void setupMigrationTasks(Project project, JavaModuleDependenciesExtensio t.getOwnProjectGroup().set(project.provider(() -> project.getGroup().toString())); t.getWithCatalog().set(true); - sourceSets.all(sourceSet -> t.addDependencies(sourceSet.getName(), - collectDependencies(project, javaModuleDependencies, sourceSet, REQUIRES_TRANSITIVE, sourceSet.getApiConfigurationName()), - collectDependencies(project, javaModuleDependencies, sourceSet, REQUIRES, sourceSet.getImplementationConfigurationName()), - collectDependencies(project, javaModuleDependencies, sourceSet, REQUIRES_STATIC_TRANSITIVE, sourceSet.getCompileOnlyApiConfigurationName()), - collectDependencies(project, javaModuleDependencies, sourceSet, REQUIRES_STATIC, sourceSet.getCompileOnlyConfigurationName()), - collectDependencies(project, javaModuleDependencies, sourceSet, REQUIRES_RUNTIME, sourceSet.getRuntimeOnlyConfigurationName()) - )); + sourceSets.all(sourceSet -> t.addDependencies( + sourceSet.getName(), + collectDependencies( + project, + javaModuleDependencies, + sourceSet, + REQUIRES_TRANSITIVE, + sourceSet.getApiConfigurationName()), + collectDependencies( + project, + javaModuleDependencies, + sourceSet, + REQUIRES, + sourceSet.getImplementationConfigurationName()), + collectDependencies( + project, + javaModuleDependencies, + sourceSet, + REQUIRES_STATIC_TRANSITIVE, + sourceSet.getCompileOnlyApiConfigurationName()), + collectDependencies( + project, + javaModuleDependencies, + sourceSet, + REQUIRES_STATIC, + sourceSet.getCompileOnlyConfigurationName()), + collectDependencies( + project, + javaModuleDependencies, + sourceSet, + REQUIRES_RUNTIME, + sourceSet.getRuntimeOnlyConfigurationName()))); t.getBuildFile().set(project.getLayout().getProjectDirectory().file("build.gradle.kts")); }); } - private void setupOrderingCheckTasks(Project project, TaskProvider checkAllModuleInfo, JavaModuleDependenciesExtension javaModuleDependencies) { + private void setupOrderingCheckTasks( + Project project, + TaskProvider checkAllModuleInfo, + JavaModuleDependenciesExtension javaModuleDependencies) { SourceSetContainer sourceSets = project.getExtensions().getByType(SourceSetContainer.class); ConfigurationContainer configurations = project.getConfigurations(); sourceSets.configureEach(sourceSet -> { - TaskProvider checkModuleInfo = project.getTasks().register(sourceSet.getTaskName("check", "ModuleInfo"), ModuleDirectivesOrderingCheck.class, t -> { - t.setGroup("java modules"); - t.setDescription("Check order of directives in 'module-info.java' in '" + sourceSet.getName() + "' source set"); - - ModuleInfo moduleInfo = javaModuleDependencies.getModuleInfoCache().get().get(sourceSet, project.getProviders()); - File folder = javaModuleDependencies.getModuleInfoCache().get().getFolder(sourceSet, project.getProviders()); - if (folder != null) { - t.getModuleInfoPath().convention(new File(folder, "module-info.java").getAbsolutePath()); - } - t.getModuleNamePrefix().convention(moduleInfo.moduleNamePrefix(project.getName(), sourceSet.getName(), false)); - t.getModuleInfo().convention(moduleInfo); - - t.getReport().convention(project.getLayout().getBuildDirectory().file("reports/module-info-analysis/" + sourceSet.getName() + "-ordering.txt")); - }); + TaskProvider checkModuleInfo = project.getTasks() + .register(sourceSet.getTaskName("check", "ModuleInfo"), ModuleDirectivesOrderingCheck.class, t -> { + t.setGroup("java modules"); + t.setDescription("Check order of directives in 'module-info.java' in '" + sourceSet.getName() + + "' source set"); + + ModuleInfo moduleInfo = javaModuleDependencies + .getModuleInfoCache() + .get() + .get(sourceSet, project.getProviders()); + File folder = javaModuleDependencies + .getModuleInfoCache() + .get() + .getFolder(sourceSet, project.getProviders()); + if (folder != null) { + t.getModuleInfoPath().convention(new File(folder, "module-info.java").getAbsolutePath()); + } + t.getModuleNamePrefix() + .convention(moduleInfo.moduleNamePrefix(project.getName(), sourceSet.getName(), false)); + t.getModuleInfo().convention(moduleInfo); + + t.getReport() + .convention(project.getLayout() + .getBuildDirectory() + .file("reports/module-info-analysis/" + sourceSet.getName() + "-ordering.txt")); + }); checkAllModuleInfo.configure(t -> t.dependsOn(checkModuleInfo)); }); } - private void process(ModuleInfo.Directive moduleDirective, String gradleConfiguration, SourceSet sourceSet, Project project, JavaModuleDependenciesExtension javaModuleDependenciesExtension) { + private void process( + ModuleInfo.Directive moduleDirective, + String gradleConfiguration, + SourceSet sourceSet, + Project project, + JavaModuleDependenciesExtension javaModuleDependenciesExtension) { Configuration conf = project.getConfigurations().findByName(gradleConfiguration); if (conf != null) { - conf.withDependencies(d -> readModuleInfo(moduleDirective, sourceSet, project, conf, javaModuleDependenciesExtension)); + conf.withDependencies( + d -> readModuleInfo(moduleDirective, sourceSet, project, conf, javaModuleDependenciesExtension)); } else { project.getConfigurations().whenObjectAdded(lateAddedConf -> { if (gradleConfiguration.equals(lateAddedConf.getName())) { - lateAddedConf.withDependencies(d -> readModuleInfo(moduleDirective, sourceSet, project, lateAddedConf, javaModuleDependenciesExtension)); + lateAddedConf.withDependencies(d -> readModuleInfo( + moduleDirective, sourceSet, project, lateAddedConf, javaModuleDependenciesExtension)); } }); } } - private void readModuleInfo(ModuleInfo.Directive moduleDirective, SourceSet sourceSet, Project project, Configuration configuration, JavaModuleDependenciesExtension javaModuleDependenciesExtension) { + private void readModuleInfo( + ModuleInfo.Directive moduleDirective, + SourceSet sourceSet, + Project project, + Configuration configuration, + JavaModuleDependenciesExtension javaModuleDependenciesExtension) { if (javaModuleDependenciesExtension.getAnalyseOnly().get()) { return; } - ModuleInfo moduleInfo = javaModuleDependenciesExtension.getModuleInfoCache().get().get(sourceSet, project.getProviders()); + ModuleInfo moduleInfo = + javaModuleDependenciesExtension.getModuleInfoCache().get().get(sourceSet, project.getProviders()); for (String moduleName : moduleInfo.get(moduleDirective)) { - declareDependency(moduleName, project, sourceSet, configuration, javaModuleDependenciesExtension); + declareDependency(moduleName, project, sourceSet, configuration, javaModuleDependenciesExtension); } } - private void declareDependency(String moduleName, Project project, SourceSet sourceSet, Configuration configuration, JavaModuleDependenciesExtension javaModuleDependencies) { - project.getDependencies().addProvider(configuration.getName(), javaModuleDependencies.create(moduleName, sourceSet)); + private void declareDependency( + String moduleName, + Project project, + SourceSet sourceSet, + Configuration configuration, + JavaModuleDependenciesExtension javaModuleDependencies) { + project.getDependencies() + .addProvider(configuration.getName(), javaModuleDependencies.create(moduleName, sourceSet)); } - private List collectDependencies(Project project, JavaModuleDependenciesExtension javaModuleDependencies, SourceSet sourceSet, ModuleInfo.Directive directive, String scope) { - ModuleInfo moduleInfo = javaModuleDependencies.getModuleInfoCache().get().get(sourceSet, project.getProviders()); + private List collectDependencies( + Project project, + JavaModuleDependenciesExtension javaModuleDependencies, + SourceSet sourceSet, + ModuleInfo.Directive directive, + String scope) { + ModuleInfo moduleInfo = + javaModuleDependencies.getModuleInfoCache().get().get(sourceSet, project.getProviders()); if (moduleInfo == ModuleInfo.EMPTY) { // check if there is a whiltebox module-info we can use isntead - File sourceSetDir = sourceSet.getJava().getSrcDirs().iterator().next().getParentFile(); + File sourceSetDir = + sourceSet.getJava().getSrcDirs().iterator().next().getParentFile(); File whiteboxModuleInfoFile = new File(sourceSetDir, "java9/module-info.java"); if (whiteboxModuleInfoFile.exists()) { - moduleInfo = new ModuleInfo(project.getProviders().fileContents(project.getLayout().getProjectDirectory().file(whiteboxModuleInfoFile.getAbsolutePath())).getAsText().get()); + moduleInfo = new ModuleInfo(project.getProviders() + .fileContents(project.getLayout() + .getProjectDirectory() + .file(whiteboxModuleInfoFile.getAbsolutePath())) + .getAsText() + .get()); } } return moduleInfo.get(directive).stream() - .map(moduleName -> new BuildFileDependenciesGenerate.DependencyDeclaration(scope, moduleName, javaModuleDependencies.ga(moduleName))) + .map(moduleName -> new BuildFileDependenciesGenerate.DependencyDeclaration( + scope, moduleName, javaModuleDependencies.ga(moduleName))) .collect(Collectors.toList()); } - } diff --git a/src/main/java/org/gradlex/javamodule/dependencies/JavaModuleVersionsPlugin.java b/src/main/java/org/gradlex/javamodule/dependencies/JavaModuleVersionsPlugin.java index bd7a99db..610a8c34 100644 --- a/src/main/java/org/gradlex/javamodule/dependencies/JavaModuleVersionsPlugin.java +++ b/src/main/java/org/gradlex/javamodule/dependencies/JavaModuleVersionsPlugin.java @@ -1,21 +1,19 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies; +import static org.gradle.api.attributes.Usage.JAVA_RUNTIME; +import static org.gradle.api.plugins.JavaPlatformPlugin.API_CONFIGURATION_NAME; +import static org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo.Directive.REQUIRES; +import static org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo.Directive.REQUIRES_RUNTIME; +import static org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo.Directive.REQUIRES_STATIC; +import static org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo.Directive.REQUIRES_STATIC_TRANSITIVE; +import static org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo.Directive.REQUIRES_TRANSITIVE; + +import java.io.File; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; @@ -31,23 +29,10 @@ import org.gradlex.javamodule.dependencies.tasks.CatalogGenerate; import org.jspecify.annotations.Nullable; -import java.io.File; -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static org.gradle.api.attributes.Usage.JAVA_RUNTIME; -import static org.gradle.api.plugins.JavaPlatformPlugin.API_CONFIGURATION_NAME; -import static org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo.Directive.REQUIRES; -import static org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo.Directive.REQUIRES_RUNTIME; -import static org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo.Directive.REQUIRES_STATIC; -import static org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo.Directive.REQUIRES_STATIC_TRANSITIVE; -import static org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo.Directive.REQUIRES_TRANSITIVE; - @SuppressWarnings("unused") public abstract class JavaModuleVersionsPlugin implements Plugin { - private static final boolean MIN_GRADLE_9_0 = GradleVersion.current().compareTo(GradleVersion.version("9.0.0")) >= 0; + private static final boolean MIN_GRADLE_9_0 = + GradleVersion.current().compareTo(GradleVersion.version("9.0.0")) >= 0; @Override public void apply(Project project) { @@ -81,9 +66,14 @@ private void setupForJavaProject(Project project) { if (GradleVersion.current().compareTo(GradleVersion.version("8.6")) < 0) { // https://github.com/gradle/gradle/issues/26163 - project.afterEvaluate(p -> platformElements.getOutgoing().capability(project.getGroup() + ":" + project.getName() + "-platform:" + project.getVersion())); + project.afterEvaluate(p -> platformElements + .getOutgoing() + .capability(project.getGroup() + ":" + project.getName() + "-platform:" + project.getVersion())); } else { - platformElements.getOutgoing().capability(project.provider(() -> project.getGroup() + ":" + project.getName() + "-platform:" + project.getVersion())); + platformElements + .getOutgoing() + .capability(project.provider( + () -> project.getGroup() + ":" + project.getName() + "-platform:" + project.getVersion())); } setupVersionsDSL(project, versions); @@ -93,18 +83,24 @@ private void setupForJavaProject(Project project) { private void setupVersionsDSL(Project project, Configuration configuration) { project.getPlugins().apply(JavaModuleDependenciesPlugin.class); - JavaModuleDependenciesExtension javaModuleDependencies = project.getExtensions().getByType(JavaModuleDependenciesExtension.class); + JavaModuleDependenciesExtension javaModuleDependencies = + project.getExtensions().getByType(JavaModuleDependenciesExtension.class); project.getExtensions().create("moduleInfo", ModuleVersions.class, configuration, javaModuleDependencies); } private void setupConstraintsValidation(Project project, Configuration configuration) { configuration.getDependencyConstraints().configureEach(d -> { - JavaModuleDependenciesExtension javaModuleDependencies = project.getExtensions().getByType(JavaModuleDependenciesExtension.class); + JavaModuleDependenciesExtension javaModuleDependencies = + project.getExtensions().getByType(JavaModuleDependenciesExtension.class); String userDefinedReason = d.getReason(); String ga = d.getModule().toString(); Provider moduleName = javaModuleDependencies.moduleName(ga); - if (moduleName.isPresent() && isModuleName(userDefinedReason) && !moduleName.get().equals(userDefinedReason)) { - project.getLogger().lifecycle("WARN: Expected module name for '" + ga + "' is '" + moduleName.get() + "' (not '" + userDefinedReason + "')"); + if (moduleName.isPresent() + && isModuleName(userDefinedReason) + && !moduleName.get().equals(userDefinedReason)) { + project.getLogger() + .lifecycle("WARN: Expected module name for '" + ga + "' is '" + moduleName.get() + "' (not '" + + userDefinedReason + "')"); } }); } @@ -114,7 +110,8 @@ private boolean isModuleName(@Nullable String s) { } private void registerCatalogTask(Project project) { - JavaModuleDependenciesExtension javaModuleDependencies = project.getExtensions().getByType(JavaModuleDependenciesExtension.class); + JavaModuleDependenciesExtension javaModuleDependencies = + project.getExtensions().getByType(JavaModuleDependenciesExtension.class); ModuleVersions moduleVersions = project.getExtensions().getByType(ModuleVersions.class); project.getTasks().register("generateCatalog", CatalogGenerate.class, t -> { t.setGroup("java modules"); @@ -131,12 +128,27 @@ private void registerCatalogTask(Project project) { moduleInfoFile = new File(srcDirSet, "java9/module-info.java"); } if (moduleInfoFile.exists()) { - ModuleInfo moduleInfo = new ModuleInfo(project.getProviders().fileContents(project.getLayout().getProjectDirectory().file(moduleInfoFile.getAbsolutePath())).getAsText().get()); - t.getEntries().addAll(collectCatalogEntriesFromModuleInfos(javaModuleDependencies, moduleInfo.get(REQUIRES_TRANSITIVE))); - t.getEntries().addAll(collectCatalogEntriesFromModuleInfos(javaModuleDependencies, moduleInfo.get(REQUIRES))); - t.getEntries().addAll(collectCatalogEntriesFromModuleInfos(javaModuleDependencies, moduleInfo.get(REQUIRES_STATIC_TRANSITIVE))); - t.getEntries().addAll(collectCatalogEntriesFromModuleInfos(javaModuleDependencies, moduleInfo.get(REQUIRES_STATIC))); - t.getEntries().addAll(collectCatalogEntriesFromModuleInfos(javaModuleDependencies, moduleInfo.get(REQUIRES_RUNTIME))); + ModuleInfo moduleInfo = new ModuleInfo(project.getProviders() + .fileContents(project.getLayout() + .getProjectDirectory() + .file(moduleInfoFile.getAbsolutePath())) + .getAsText() + .get()); + t.getEntries() + .addAll(collectCatalogEntriesFromModuleInfos( + javaModuleDependencies, moduleInfo.get(REQUIRES_TRANSITIVE))); + t.getEntries() + .addAll(collectCatalogEntriesFromModuleInfos( + javaModuleDependencies, moduleInfo.get(REQUIRES))); + t.getEntries() + .addAll(collectCatalogEntriesFromModuleInfos( + javaModuleDependencies, moduleInfo.get(REQUIRES_STATIC_TRANSITIVE))); + t.getEntries() + .addAll(collectCatalogEntriesFromModuleInfos( + javaModuleDependencies, moduleInfo.get(REQUIRES_STATIC))); + t.getEntries() + .addAll(collectCatalogEntriesFromModuleInfos( + javaModuleDependencies, moduleInfo.get(REQUIRES_RUNTIME))); } }); }); @@ -147,12 +159,20 @@ private void registerCatalogTask(Project project) { }); } - private List collectCatalogEntriesFromVersions(JavaModuleDependenciesExtension javaModuleDependencies, ModuleVersions moduleVersions) { - return moduleVersions.getDeclaredVersions().entrySet().stream().map(mv -> new CatalogGenerate.CatalogEntry(mv.getKey(), javaModuleDependencies.ga(mv.getKey()), mv.getValue())).collect(Collectors.toList()); + private List collectCatalogEntriesFromVersions( + JavaModuleDependenciesExtension javaModuleDependencies, ModuleVersions moduleVersions) { + return moduleVersions.getDeclaredVersions().entrySet().stream() + .map(mv -> new CatalogGenerate.CatalogEntry( + mv.getKey(), javaModuleDependencies.ga(mv.getKey()), mv.getValue())) + .collect(Collectors.toList()); } - private List collectCatalogEntriesFromModuleInfos(JavaModuleDependenciesExtension javaModuleDependencies, List moduleNames) { - return moduleNames.stream().map(moduleName -> new CatalogGenerate.CatalogEntry(moduleName, javaModuleDependencies.ga(moduleName), null)).collect(Collectors.toList()); + private List collectCatalogEntriesFromModuleInfos( + JavaModuleDependenciesExtension javaModuleDependencies, List moduleNames) { + return moduleNames.stream() + .map(moduleName -> + new CatalogGenerate.CatalogEntry(moduleName, javaModuleDependencies.ga(moduleName), null)) + .collect(Collectors.toList()); } @SuppressWarnings("deprecation") diff --git a/src/main/java/org/gradlex/javamodule/dependencies/LocalModule.java b/src/main/java/org/gradlex/javamodule/dependencies/LocalModule.java index bebf4dc5..56f0b45a 100644 --- a/src/main/java/org/gradlex/javamodule/dependencies/LocalModule.java +++ b/src/main/java/org/gradlex/javamodule/dependencies/LocalModule.java @@ -1,29 +1,14 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies; -import org.jspecify.annotations.Nullable; - import java.io.Serializable; import java.util.Objects; +import org.jspecify.annotations.Nullable; public class LocalModule implements Comparable, Serializable { private final String moduleName; private final String projectPath; + @Nullable private final String capability; @@ -57,11 +42,10 @@ public String getCapability() { @Override public String toString() { - return "[" + - "moduleName='" + moduleName + '\'' + - ", projectPath='" + projectPath + '\'' + - ", capability='" + capability + '\'' + - ']'; + return "[" + "moduleName='" + + moduleName + '\'' + ", projectPath='" + + projectPath + '\'' + ", capability='" + + capability + '\'' + ']'; } @Override diff --git a/src/main/java/org/gradlex/javamodule/dependencies/SharedMappings.java b/src/main/java/org/gradlex/javamodule/dependencies/SharedMappings.java index 6cb37b72..4d0ef1a8 100644 --- a/src/main/java/org/gradlex/javamodule/dependencies/SharedMappings.java +++ b/src/main/java/org/gradlex/javamodule/dependencies/SharedMappings.java @@ -1,29 +1,13 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies; -import org.jspecify.annotations.Nullable; - import java.io.IOException; import java.io.InputStream; import java.util.Map; import java.util.Properties; +import org.jspecify.annotations.Nullable; -final public class SharedMappings { +public final class SharedMappings { public static Map mappings = loadModuleNameToGAProperties(); static Map loadModuleNameToGAProperties() { @@ -36,16 +20,18 @@ static Map loadModuleNameToGAProperties() { return super.put(key, value); } }; - @SuppressWarnings({ "unchecked", "rawtypes" }) + @SuppressWarnings({"unchecked", "rawtypes"}) Map propertiesAsMap = (Map) properties; - try (InputStream coordinatesFile = JavaModuleDependenciesExtension.class.getResourceAsStream("unique_modules.properties")) { + try (InputStream coordinatesFile = + JavaModuleDependenciesExtension.class.getResourceAsStream("unique_modules.properties")) { properties.load(coordinatesFile); } catch (IOException e) { throw new RuntimeException(e); } - try (InputStream coordinatesFile = JavaModuleDependenciesExtension.class.getResourceAsStream("modules.properties")) { + try (InputStream coordinatesFile = + JavaModuleDependenciesExtension.class.getResourceAsStream("modules.properties")) { properties.load(coordinatesFile); } catch (IOException e) { throw new RuntimeException(e); @@ -53,5 +39,5 @@ static Map loadModuleNameToGAProperties() { return propertiesAsMap; } - private SharedMappings() { } + private SharedMappings() {} } diff --git a/src/main/java/org/gradlex/javamodule/dependencies/dsl/AllDirectives.java b/src/main/java/org/gradlex/javamodule/dependencies/dsl/AllDirectives.java index 4178f8dc..be934da0 100644 --- a/src/main/java/org/gradlex/javamodule/dependencies/dsl/AllDirectives.java +++ b/src/main/java/org/gradlex/javamodule/dependencies/dsl/AllDirectives.java @@ -1,27 +1,13 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies.dsl; import org.gradle.api.tasks.SourceSet; import org.gradlex.javamodule.dependencies.JavaModuleDependenciesExtension; -abstract public class AllDirectives extends GradleOnlyDirectives { +public abstract class AllDirectives extends GradleOnlyDirectives { - public AllDirectives(SourceSet sourceSet, SourceSet mainSourceSet, JavaModuleDependenciesExtension javaModuleDependencies) { + public AllDirectives( + SourceSet sourceSet, SourceSet mainSourceSet, JavaModuleDependenciesExtension javaModuleDependencies) { super(sourceSet, mainSourceSet, javaModuleDependencies); } diff --git a/src/main/java/org/gradlex/javamodule/dependencies/dsl/GradleOnlyDirectives.java b/src/main/java/org/gradlex/javamodule/dependencies/dsl/GradleOnlyDirectives.java index b5b69a93..02183aa6 100644 --- a/src/main/java/org/gradlex/javamodule/dependencies/dsl/GradleOnlyDirectives.java +++ b/src/main/java/org/gradlex/javamodule/dependencies/dsl/GradleOnlyDirectives.java @@ -1,29 +1,13 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies.dsl; +import java.util.ArrayList; +import java.util.List; +import javax.inject.Inject; import org.gradle.api.artifacts.dsl.DependencyHandler; import org.gradle.api.tasks.SourceSet; import org.gradlex.javamodule.dependencies.JavaModuleDependenciesExtension; -import javax.inject.Inject; -import java.util.ArrayList; -import java.util.List; - public abstract class GradleOnlyDirectives { protected final SourceSet sourceSet; @@ -38,7 +22,8 @@ public abstract class GradleOnlyDirectives { @Inject protected abstract DependencyHandler getDependencies(); - public GradleOnlyDirectives(SourceSet sourceSet, SourceSet mainSourceSet, JavaModuleDependenciesExtension javaModuleDependencies) { + public GradleOnlyDirectives( + SourceSet sourceSet, SourceSet mainSourceSet, JavaModuleDependenciesExtension javaModuleDependencies) { this.sourceSet = sourceSet; this.mainSourceSet = mainSourceSet; this.javaModuleDependencies = javaModuleDependencies; diff --git a/src/main/java/org/gradlex/javamodule/dependencies/dsl/ModuleVersions.java b/src/main/java/org/gradlex/javamodule/dependencies/dsl/ModuleVersions.java index e2500742..fa72d6d6 100644 --- a/src/main/java/org/gradlex/javamodule/dependencies/dsl/ModuleVersions.java +++ b/src/main/java/org/gradlex/javamodule/dependencies/dsl/ModuleVersions.java @@ -1,21 +1,9 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies.dsl; +import java.util.LinkedHashMap; +import java.util.Map; +import javax.inject.Inject; import org.gradle.api.Action; import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.DependencyConstraint; @@ -23,11 +11,7 @@ import org.gradle.api.artifacts.dsl.DependencyHandler; import org.gradlex.javamodule.dependencies.JavaModuleDependenciesExtension; -import javax.inject.Inject; -import java.util.LinkedHashMap; -import java.util.Map; - -abstract public class ModuleVersions { +public abstract class ModuleVersions { private final Map declaredVersions = new LinkedHashMap<>(); private final Configuration configuration; @@ -54,17 +38,22 @@ public void version(String moduleName, Action } public void version(String moduleName, String requiredVersion, Action version) { - getDependencies().getConstraints().add(configuration.getName(), javaModuleDependencies.ga(moduleName).map(ga -> { - String mainComponentCoordinates; - if (ga.contains("|")) { - mainComponentCoordinates = ga.substring(0, ga.indexOf("|")) + ":" + requiredVersion; - } else { - mainComponentCoordinates = ga + ":" + requiredVersion; - } - DependencyConstraint dependencyConstraint = getDependencies().getConstraints().create(mainComponentCoordinates); - dependencyConstraint.version(version); - return dependencyConstraint; - })); + getDependencies() + .getConstraints() + .add( + configuration.getName(), + javaModuleDependencies.ga(moduleName).map(ga -> { + String mainComponentCoordinates; + if (ga.contains("|")) { + mainComponentCoordinates = ga.substring(0, ga.indexOf("|")) + ":" + requiredVersion; + } else { + mainComponentCoordinates = ga + ":" + requiredVersion; + } + DependencyConstraint dependencyConstraint = + getDependencies().getConstraints().create(mainComponentCoordinates); + dependencyConstraint.version(version); + return dependencyConstraint; + })); declaredVersions.put(moduleName, requiredVersion); } } diff --git a/src/main/java/org/gradlex/javamodule/dependencies/dsl/package-info.java b/src/main/java/org/gradlex/javamodule/dependencies/dsl/package-info.java index 133fb8a3..92640d31 100644 --- a/src/main/java/org/gradlex/javamodule/dependencies/dsl/package-info.java +++ b/src/main/java/org/gradlex/javamodule/dependencies/dsl/package-info.java @@ -1,20 +1,5 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 @NullMarked package org.gradlex.javamodule.dependencies.dsl; -import org.jspecify.annotations.NullMarked; \ No newline at end of file +import org.jspecify.annotations.NullMarked; diff --git a/src/main/java/org/gradlex/javamodule/dependencies/initialization/Directory.java b/src/main/java/org/gradlex/javamodule/dependencies/initialization/Directory.java index 2fdea24a..96ccc6a6 100644 --- a/src/main/java/org/gradlex/javamodule/dependencies/initialization/Directory.java +++ b/src/main/java/org/gradlex/javamodule/dependencies/initialization/Directory.java @@ -1,31 +1,15 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies.initialization; -import org.gradle.api.Action; -import org.gradle.api.model.ObjectFactory; -import org.gradle.api.provider.ListProperty; -import org.gradle.api.provider.Property; - -import javax.inject.Inject; import java.io.File; import java.util.Arrays; import java.util.LinkedHashMap; import java.util.Map; +import javax.inject.Inject; +import org.gradle.api.Action; +import org.gradle.api.model.ObjectFactory; +import org.gradle.api.provider.ListProperty; +import org.gradle.api.provider.Property; public abstract class Directory { @@ -42,7 +26,6 @@ public abstract class Directory { */ public abstract ListProperty getPlugins(); - @Inject public Directory(File root) { this.root = root; diff --git a/src/main/java/org/gradlex/javamodule/dependencies/initialization/JavaModuleDependenciesSettingsPlugin.java b/src/main/java/org/gradlex/javamodule/dependencies/initialization/JavaModuleDependenciesSettingsPlugin.java index 01a69dc6..bee0b022 100644 --- a/src/main/java/org/gradlex/javamodule/dependencies/initialization/JavaModuleDependenciesSettingsPlugin.java +++ b/src/main/java/org/gradlex/javamodule/dependencies/initialization/JavaModuleDependenciesSettingsPlugin.java @@ -1,19 +1,4 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies.initialization; import org.gradle.api.GradleException; diff --git a/src/main/java/org/gradlex/javamodule/dependencies/initialization/JavaModulesExtension.java b/src/main/java/org/gradlex/javamodule/dependencies/initialization/JavaModulesExtension.java index f18389e1..d315b834 100644 --- a/src/main/java/org/gradlex/javamodule/dependencies/initialization/JavaModulesExtension.java +++ b/src/main/java/org/gradlex/javamodule/dependencies/initialization/JavaModulesExtension.java @@ -1,21 +1,10 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies.initialization; +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import javax.inject.Inject; import org.gradle.api.Action; import org.gradle.api.IsolatedAction; import org.gradle.api.Project; @@ -36,11 +25,6 @@ import org.gradlex.javamodule.dependencies.internal.utils.ValueModuleDirectoryListing; import org.jspecify.annotations.Nullable; -import javax.inject.Inject; -import java.io.File; -import java.util.ArrayList; -import java.util.List; - public abstract class JavaModulesExtension { private final Settings settings; @@ -158,8 +142,13 @@ private void configureModule(Module module, ProjectDescriptor project, boolean d String mainModuleName = null; if (!definesVersions) { for (String moduleInfoPath : module.getModuleInfoPaths().get()) { - ModuleInfo moduleInfo = moduleInfoCache.put(project.getProjectDir(), moduleInfoPath, - project.getPath(), module.getArtifact().get(), module.getGroup(), settings.getProviders()); + ModuleInfo moduleInfo = moduleInfoCache.put( + project.getProjectDir(), + moduleInfoPath, + project.getPath(), + module.getArtifact().get(), + module.getGroup(), + settings.getProviders()); if (moduleInfoPath.contains("/main/")) { mainModuleName = moduleInfo.getModuleName(); } @@ -178,7 +167,12 @@ private static class ModuleProject { private final @Nullable String mainModuleName; private final boolean definesVersions; - public ModuleProject(String path, @Nullable String group, List plugins, @Nullable String mainModuleName, boolean definesVersions) { + public ModuleProject( + String path, + @Nullable String group, + List plugins, + @Nullable String mainModuleName, + boolean definesVersions) { this.path = path; this.group = group; this.plugins = plugins; @@ -205,15 +199,22 @@ public void execute(Project project) { if (m.definesVersions) { project.getPlugins().apply(JavaPlatformPlugin.class); project.getPlugins().apply(JavaModuleVersionsPlugin.class); - project.getExtensions().getByType(JavaPlatformExtension.class).allowDependencies(); + project.getExtensions() + .getByType(JavaPlatformExtension.class) + .allowDependencies(); } else { project.getPlugins().apply(JavaModuleDependenciesPlugin.class); - project.getExtensions().getByType(JavaModuleDependenciesExtension.class).getModuleInfoCache().set(moduleInfoCache); + project.getExtensions() + .getByType(JavaModuleDependenciesExtension.class) + .getModuleInfoCache() + .set(moduleInfoCache); } m.plugins.forEach(id -> project.getPlugins().apply(id)); if (m.mainModuleName != null) { - project.getPlugins().withType(ApplicationPlugin.class, p -> - project.getExtensions().getByType(JavaApplication.class).getMainModule().set(m.mainModuleName)); + project.getPlugins().withType(ApplicationPlugin.class, p -> project.getExtensions() + .getByType(JavaApplication.class) + .getMainModule() + .set(m.mainModuleName)); } } } diff --git a/src/main/java/org/gradlex/javamodule/dependencies/initialization/Module.java b/src/main/java/org/gradlex/javamodule/dependencies/initialization/Module.java index fdf19db1..a7e5d37d 100644 --- a/src/main/java/org/gradlex/javamodule/dependencies/initialization/Module.java +++ b/src/main/java/org/gradlex/javamodule/dependencies/initialization/Module.java @@ -1,29 +1,13 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies.initialization; -import org.gradle.api.provider.ListProperty; -import org.gradle.api.provider.Property; - -import javax.inject.Inject; import java.io.File; import java.util.Arrays; import java.util.stream.Collectors; import java.util.stream.Stream; +import javax.inject.Inject; +import org.gradle.api.provider.ListProperty; +import org.gradle.api.provider.Property; public abstract class Module { @@ -58,11 +42,13 @@ public abstract class Module { public Module(File directory) { this.directory = directory; getArtifact().convention(directory.getName()); - getModuleInfoPaths().convention(listSrcChildren() - .map(srcDir -> new File(srcDir, "java/module-info.java")) - .filter(File::exists) - .map(moduleInfo -> "src/" + moduleInfo.getParentFile().getParentFile().getName() + "/java") - .collect(Collectors.toList())); + getModuleInfoPaths() + .convention(listSrcChildren() + .map(srcDir -> new File(srcDir, "java/module-info.java")) + .filter(File::exists) + .map(moduleInfo -> "src/" + + moduleInfo.getParentFile().getParentFile().getName() + "/java") + .collect(Collectors.toList())); } /** diff --git a/src/main/java/org/gradlex/javamodule/dependencies/initialization/RootPluginsExtension.java b/src/main/java/org/gradlex/javamodule/dependencies/initialization/RootPluginsExtension.java index 1ab712be..8fadbd1e 100644 --- a/src/main/java/org/gradlex/javamodule/dependencies/initialization/RootPluginsExtension.java +++ b/src/main/java/org/gradlex/javamodule/dependencies/initialization/RootPluginsExtension.java @@ -1,29 +1,13 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies.initialization; +import java.util.ArrayList; +import java.util.List; +import javax.inject.Inject; import org.gradle.api.IsolatedAction; import org.gradle.api.Project; import org.gradle.api.initialization.Settings; -import javax.inject.Inject; -import java.util.ArrayList; -import java.util.List; - public abstract class RootPluginsExtension { private final List ids = new ArrayList<>(); diff --git a/src/main/java/org/gradlex/javamodule/dependencies/initialization/package-info.java b/src/main/java/org/gradlex/javamodule/dependencies/initialization/package-info.java index 500f227a..0e41e8cc 100644 --- a/src/main/java/org/gradlex/javamodule/dependencies/initialization/package-info.java +++ b/src/main/java/org/gradlex/javamodule/dependencies/initialization/package-info.java @@ -1,20 +1,5 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 @NullMarked package org.gradlex.javamodule.dependencies.initialization; -import org.jspecify.annotations.NullMarked; \ No newline at end of file +import org.jspecify.annotations.NullMarked; diff --git a/src/main/java/org/gradlex/javamodule/dependencies/internal/bridges/DependencyAnalysisBridge.java b/src/main/java/org/gradlex/javamodule/dependencies/internal/bridges/DependencyAnalysisBridge.java index 78f89fb8..5e23bb9b 100644 --- a/src/main/java/org/gradlex/javamodule/dependencies/internal/bridges/DependencyAnalysisBridge.java +++ b/src/main/java/org/gradlex/javamodule/dependencies/internal/bridges/DependencyAnalysisBridge.java @@ -1,22 +1,8 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies.internal.bridges; import com.autonomousapps.AbstractExtension; +import java.io.File; import org.gradle.api.Project; import org.gradle.api.Task; import org.gradle.api.artifacts.Configuration; @@ -27,37 +13,45 @@ import org.gradlex.javamodule.dependencies.tasks.ModuleDirectivesScopeCheck; import org.jspecify.annotations.Nullable; -import java.io.File; - public class DependencyAnalysisBridge { - public static void registerDependencyAnalysisPostProcessingTask(Project project, @Nullable TaskProvider checkAllModuleInfo) { + public static void registerDependencyAnalysisPostProcessingTask( + Project project, @Nullable TaskProvider checkAllModuleInfo) { TaskContainer tasks = project.getTasks(); SourceSetContainer sourceSets = project.getExtensions().getByType(SourceSetContainer.class); TaskProvider checkModuleDirectivesScope = - tasks.register("checkModuleDirectivesScope", ModuleDirectivesScopeCheck.class, - t -> t.getReport().convention(project.getLayout().getBuildDirectory().file("reports/module-info-analysis/scopes.txt"))); + tasks.register("checkModuleDirectivesScope", ModuleDirectivesScopeCheck.class, t -> t.getReport() + .convention(project.getLayout() + .getBuildDirectory() + .file("reports/module-info-analysis/scopes.txt"))); sourceSets.all(sourceSet -> checkModuleDirectivesScope.configure(t -> { - File moduleInfo = new File(sourceSet.getJava().getSrcDirs().iterator().next(), "module-info.java"); + File moduleInfo = + new File(sourceSet.getJava().getSrcDirs().iterator().next(), "module-info.java"); if (!moduleInfo.exists()) { moduleInfo = project.getBuildFile(); // no module-info: dependencies are declared in build file } t.getSourceSets().put(sourceSet.getName(), moduleInfo.getAbsolutePath()); - Configuration cpClasspath = project.getConfigurations().getByName(sourceSet.getCompileClasspathConfigurationName()); - Configuration rtClasspath = project.getConfigurations().getByName(sourceSet.getRuntimeClasspathConfigurationName()); - t.getModuleArtifacts().add(project.provider(() -> cpClasspath.getIncoming().getArtifacts())); - t.getModuleArtifacts().add(project.provider(() -> rtClasspath.getIncoming().getArtifacts())); + Configuration cpClasspath = + project.getConfigurations().getByName(sourceSet.getCompileClasspathConfigurationName()); + Configuration rtClasspath = + project.getConfigurations().getByName(sourceSet.getRuntimeClasspathConfigurationName()); + t.getModuleArtifacts() + .add(project.provider(() -> cpClasspath.getIncoming().getArtifacts())); + t.getModuleArtifacts() + .add(project.provider(() -> rtClasspath.getIncoming().getArtifacts())); })); - project.getExtensions().getByType(AbstractExtension.class) + project.getExtensions() + .getByType(AbstractExtension.class) .registerPostProcessingTask(checkModuleDirectivesScope); if (checkAllModuleInfo != null) { checkAllModuleInfo.configure(t -> t.dependsOn(checkModuleDirectivesScope)); } - tasks.withType(ModuleDirectivesOrderingCheck.class).configureEach(t -> t.mustRunAfter(checkModuleDirectivesScope)); + tasks.withType(ModuleDirectivesOrderingCheck.class) + .configureEach(t -> t.mustRunAfter(checkModuleDirectivesScope)); } } diff --git a/src/main/java/org/gradlex/javamodule/dependencies/internal/bridges/ExtraJavaModuleInfoBridge.java b/src/main/java/org/gradlex/javamodule/dependencies/internal/bridges/ExtraJavaModuleInfoBridge.java index 65749002..d7bc8043 100644 --- a/src/main/java/org/gradlex/javamodule/dependencies/internal/bridges/ExtraJavaModuleInfoBridge.java +++ b/src/main/java/org/gradlex/javamodule/dependencies/internal/bridges/ExtraJavaModuleInfoBridge.java @@ -1,35 +1,24 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies.internal.bridges; +import java.util.Map; +import java.util.stream.Collectors; import org.gradle.api.Project; import org.gradlex.javamodule.dependencies.JavaModuleDependenciesExtension; import org.gradlex.javamodule.moduleinfo.ExtraJavaModuleInfoPluginExtension; import org.gradlex.javamodule.moduleinfo.ModuleSpec; -import java.util.Map; -import java.util.stream.Collectors; - public class ExtraJavaModuleInfoBridge { - public static void autoRegisterPatchedModuleMappings(Project project, JavaModuleDependenciesExtension javaModuleDependencies) { - ExtraJavaModuleInfoPluginExtension extraJavaModuleInfo = project.getExtensions().getByType(ExtraJavaModuleInfoPluginExtension.class); - javaModuleDependencies.getModuleNameToGA().putAll(extraJavaModuleInfo.getModuleSpecs().map( - moduleSpecs -> moduleSpecs.entrySet().stream().collect(Collectors.toMap(ExtraJavaModuleInfoBridge::moduleNameKey, Map.Entry::getKey, (a, b) -> b)))); + public static void autoRegisterPatchedModuleMappings( + Project project, JavaModuleDependenciesExtension javaModuleDependencies) { + ExtraJavaModuleInfoPluginExtension extraJavaModuleInfo = + project.getExtensions().getByType(ExtraJavaModuleInfoPluginExtension.class); + javaModuleDependencies + .getModuleNameToGA() + .putAll(extraJavaModuleInfo.getModuleSpecs().map(moduleSpecs -> moduleSpecs.entrySet().stream() + .collect(Collectors.toMap( + ExtraJavaModuleInfoBridge::moduleNameKey, Map.Entry::getKey, (a, b) -> b)))); } private static String moduleNameKey(Map.Entry entry) { diff --git a/src/main/java/org/gradlex/javamodule/dependencies/internal/bridges/package-info.java b/src/main/java/org/gradlex/javamodule/dependencies/internal/bridges/package-info.java index 5938c7fd..5a93f8f2 100644 --- a/src/main/java/org/gradlex/javamodule/dependencies/internal/bridges/package-info.java +++ b/src/main/java/org/gradlex/javamodule/dependencies/internal/bridges/package-info.java @@ -1,20 +1,5 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 @NullMarked package org.gradlex.javamodule.dependencies.internal.bridges; -import org.jspecify.annotations.NullMarked; \ No newline at end of file +import org.jspecify.annotations.NullMarked; diff --git a/src/main/java/org/gradlex/javamodule/dependencies/internal/diagnostics/AsciiModuleDependencyReportRenderer.java b/src/main/java/org/gradlex/javamodule/dependencies/internal/diagnostics/AsciiModuleDependencyReportRenderer.java index 9f222851..4422b842 100644 --- a/src/main/java/org/gradlex/javamodule/dependencies/internal/diagnostics/AsciiModuleDependencyReportRenderer.java +++ b/src/main/java/org/gradlex/javamodule/dependencies/internal/diagnostics/AsciiModuleDependencyReportRenderer.java @@ -1,21 +1,10 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies.internal.diagnostics; +import static java.util.Objects.requireNonNull; + +import java.util.Collections; +import java.util.Map; import org.gradle.api.artifacts.ArtifactCollection; import org.gradle.api.artifacts.result.ResolvedComponentResult; import org.gradle.api.provider.Provider; @@ -30,11 +19,6 @@ import org.gradle.internal.logging.text.StyledTextOutput; import org.jspecify.annotations.Nullable; -import java.util.Collections; -import java.util.Map; - -import static java.util.Objects.requireNonNull; - public class AsciiModuleDependencyReportRenderer extends AsciiDependencyReportRenderer { private @Nullable DependencyGraphsRenderer dependencyGraphRenderer; @@ -48,14 +32,17 @@ public AsciiModuleDependencyReportRenderer(Provider resolvedJars; diff --git a/src/main/java/org/gradlex/javamodule/dependencies/internal/diagnostics/RenderableModuleDependencyResult.java b/src/main/java/org/gradlex/javamodule/dependencies/internal/diagnostics/RenderableModuleDependencyResult.java index fd1735ed..2700a2ec 100644 --- a/src/main/java/org/gradlex/javamodule/dependencies/internal/diagnostics/RenderableModuleDependencyResult.java +++ b/src/main/java/org/gradlex/javamodule/dependencies/internal/diagnostics/RenderableModuleDependencyResult.java @@ -1,21 +1,12 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies.internal.diagnostics; +import static org.gradlex.javamodule.dependencies.internal.utils.ModuleJar.isRealModule; +import static org.gradlex.javamodule.dependencies.internal.utils.ModuleJar.readModuleNameFromJarFile; + +import java.io.IOException; +import java.util.LinkedHashSet; +import java.util.Set; import org.gradle.api.artifacts.component.ComponentIdentifier; import org.gradle.api.artifacts.component.ComponentSelector; import org.gradle.api.artifacts.component.ModuleComponentIdentifier; @@ -28,18 +19,12 @@ import org.gradle.api.tasks.diagnostics.internal.graph.nodes.RenderableDependencyResult; import org.gradle.api.tasks.diagnostics.internal.graph.nodes.RenderableUnresolvedDependencyResult; -import java.io.IOException; -import java.util.LinkedHashSet; -import java.util.Set; - -import static org.gradlex.javamodule.dependencies.internal.utils.ModuleJar.isRealModule; -import static org.gradlex.javamodule.dependencies.internal.utils.ModuleJar.readModuleNameFromJarFile; - public class RenderableModuleDependencyResult extends RenderableDependencyResult { private final ResolvedDependencyResult dependency; private final Set resolvedJars; - public RenderableModuleDependencyResult(ResolvedDependencyResult dependency, Set resolvedJars) { + public RenderableModuleDependencyResult( + ResolvedDependencyResult dependency, Set resolvedJars) { super(dependency); this.dependency = dependency; this.resolvedJars = resolvedJars; @@ -53,7 +38,10 @@ public Set getChildren() { out.add(new RenderableUnresolvedDependencyResult((UnresolvedDependencyResult) d)); } else { ResolvedDependencyResult resolved = (ResolvedDependencyResult) d; - resolvedJars.stream().filter(a -> a.getId().getComponentIdentifier().equals(resolved.getSelected().getId())) + resolvedJars.stream() + .filter(a -> a.getId() + .getComponentIdentifier() + .equals(resolved.getSelected().getId())) .findFirst() .ifPresent(artifact -> out.add(new RenderableModuleDependencyResult(resolved, resolvedJars))); } @@ -65,8 +53,10 @@ public Set getChildren() { public String getName() { ComponentSelector requested = getRequested(); ComponentIdentifier selected = getActual(); - ResolvedArtifactResult artifact = resolvedJars.stream().filter(a -> - a.getId().getComponentIdentifier().equals(selected)).findFirst().orElse(null); + ResolvedArtifactResult artifact = resolvedJars.stream() + .filter(a -> a.getId().getComponentIdentifier().equals(selected)) + .findFirst() + .orElse(null); try { if (artifact == null) { @@ -88,11 +78,13 @@ public String getName() { version = " (" + requestedVersion + " -> " + selectedVersion + ")"; } } - coordinates = ((ModuleComponentIdentifier) selected).getModuleIdentifier().toString(); + coordinates = ((ModuleComponentIdentifier) selected) + .getModuleIdentifier() + .toString(); } String auto = isRealModule(artifact.getFile()) ? "" : "[AUTO] "; - return auto + actualModuleName + version + " | " + coordinates + - (isConstraint() ? "" : " | " + jarName); + return auto + actualModuleName + version + " | " + coordinates + + (isConstraint() ? "" : " | " + jarName); } } } catch (IOException e) { diff --git a/src/main/java/org/gradlex/javamodule/dependencies/internal/diagnostics/StyledNodeRenderer.java b/src/main/java/org/gradlex/javamodule/dependencies/internal/diagnostics/StyledNodeRenderer.java index e9a89969..b1e3fafa 100644 --- a/src/main/java/org/gradlex/javamodule/dependencies/internal/diagnostics/StyledNodeRenderer.java +++ b/src/main/java/org/gradlex/javamodule/dependencies/internal/diagnostics/StyledNodeRenderer.java @@ -1,29 +1,14 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies.internal.diagnostics; -import org.gradle.api.tasks.diagnostics.internal.graph.NodeRenderer; -import org.gradle.api.tasks.diagnostics.internal.graph.nodes.RenderableDependency; -import org.gradle.internal.logging.text.StyledTextOutput; - import static org.gradle.internal.logging.text.StyledTextOutput.Style.Description; import static org.gradle.internal.logging.text.StyledTextOutput.Style.Failure; import static org.gradle.internal.logging.text.StyledTextOutput.Style.Info; +import org.gradle.api.tasks.diagnostics.internal.graph.NodeRenderer; +import org.gradle.api.tasks.diagnostics.internal.graph.nodes.RenderableDependency; +import org.gradle.internal.logging.text.StyledTextOutput; + public class StyledNodeRenderer implements NodeRenderer { @Override diff --git a/src/main/java/org/gradlex/javamodule/dependencies/internal/diagnostics/package-info.java b/src/main/java/org/gradlex/javamodule/dependencies/internal/diagnostics/package-info.java index 665ff0ca..1e06e37f 100644 --- a/src/main/java/org/gradlex/javamodule/dependencies/internal/diagnostics/package-info.java +++ b/src/main/java/org/gradlex/javamodule/dependencies/internal/diagnostics/package-info.java @@ -1,20 +1,5 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 @NullMarked package org.gradlex.javamodule.dependencies.internal.diagnostics; -import org.jspecify.annotations.NullMarked; \ No newline at end of file +import org.jspecify.annotations.NullMarked; diff --git a/src/main/java/org/gradlex/javamodule/dependencies/internal/dsl/AllDirectivesInternal.java b/src/main/java/org/gradlex/javamodule/dependencies/internal/dsl/AllDirectivesInternal.java index 264193f3..cc017a3d 100644 --- a/src/main/java/org/gradlex/javamodule/dependencies/internal/dsl/AllDirectivesInternal.java +++ b/src/main/java/org/gradlex/javamodule/dependencies/internal/dsl/AllDirectivesInternal.java @@ -1,34 +1,19 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies.internal.dsl; +import java.util.List; import org.gradle.api.tasks.SourceSet; import org.gradlex.javamodule.dependencies.JavaModuleDependenciesExtension; import org.gradlex.javamodule.dependencies.dsl.AllDirectives; -import java.util.List; - /** * Note: These methods are used by the 'java-module-testing' plugin to access information * defined in the Module Info DSL. */ -abstract public class AllDirectivesInternal extends AllDirectives { +public abstract class AllDirectivesInternal extends AllDirectives { - public AllDirectivesInternal(SourceSet sourceSet, SourceSet mainSourceSet, JavaModuleDependenciesExtension javaModuleDependencies) { + public AllDirectivesInternal( + SourceSet sourceSet, SourceSet mainSourceSet, JavaModuleDependenciesExtension javaModuleDependencies) { super(sourceSet, mainSourceSet, javaModuleDependencies); } diff --git a/src/main/java/org/gradlex/javamodule/dependencies/internal/dsl/GradleOnlyDirectivesInternal.java b/src/main/java/org/gradlex/javamodule/dependencies/internal/dsl/GradleOnlyDirectivesInternal.java index b22a90de..8660e1ab 100644 --- a/src/main/java/org/gradlex/javamodule/dependencies/internal/dsl/GradleOnlyDirectivesInternal.java +++ b/src/main/java/org/gradlex/javamodule/dependencies/internal/dsl/GradleOnlyDirectivesInternal.java @@ -1,30 +1,15 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies.internal.dsl; +import java.util.List; import org.gradle.api.tasks.SourceSet; import org.gradlex.javamodule.dependencies.JavaModuleDependenciesExtension; import org.gradlex.javamodule.dependencies.dsl.GradleOnlyDirectives; -import java.util.List; - public abstract class GradleOnlyDirectivesInternal extends GradleOnlyDirectives { - public GradleOnlyDirectivesInternal(SourceSet sourceSet, SourceSet mainSourceSet, JavaModuleDependenciesExtension javaModuleDependencies) { + public GradleOnlyDirectivesInternal( + SourceSet sourceSet, SourceSet mainSourceSet, JavaModuleDependenciesExtension javaModuleDependencies) { super(sourceSet, mainSourceSet, javaModuleDependencies); } diff --git a/src/main/java/org/gradlex/javamodule/dependencies/internal/dsl/package-info.java b/src/main/java/org/gradlex/javamodule/dependencies/internal/dsl/package-info.java index 61bdbea9..e23e1a63 100644 --- a/src/main/java/org/gradlex/javamodule/dependencies/internal/dsl/package-info.java +++ b/src/main/java/org/gradlex/javamodule/dependencies/internal/dsl/package-info.java @@ -1,20 +1,5 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 @NullMarked package org.gradlex.javamodule.dependencies.internal.dsl; -import org.jspecify.annotations.NullMarked; \ No newline at end of file +import org.jspecify.annotations.NullMarked; diff --git a/src/main/java/org/gradlex/javamodule/dependencies/internal/utils/DependencyDeclarationsUtil.java b/src/main/java/org/gradlex/javamodule/dependencies/internal/utils/DependencyDeclarationsUtil.java index 30d5a950..7795b995 100644 --- a/src/main/java/org/gradlex/javamodule/dependencies/internal/utils/DependencyDeclarationsUtil.java +++ b/src/main/java/org/gradlex/javamodule/dependencies/internal/utils/DependencyDeclarationsUtil.java @@ -1,21 +1,12 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies.internal.utils; +import static org.gradle.api.attributes.Category.CATEGORY_ATTRIBUTE; +import static org.gradle.api.attributes.Category.LIBRARY; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; import org.gradle.api.Project; import org.gradle.api.artifacts.ConfigurationContainer; import org.gradle.api.artifacts.Dependency; @@ -27,21 +18,15 @@ import org.gradle.api.capabilities.Capability; import org.gradle.api.provider.Provider; -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; - -import static org.gradle.api.attributes.Category.CATEGORY_ATTRIBUTE; -import static org.gradle.api.attributes.Category.LIBRARY; - public abstract class DependencyDeclarationsUtil { public static Provider> declaredDependencies(Project project, String configuration) { ConfigurationContainer configurations = project.getConfigurations(); return project.provider(() -> configurations.getNames().contains(configuration) ? configurations.getByName(configuration).getDependencies().stream() - .filter(DependencyDeclarationsUtil::isLibraryDependency) - .map(d -> toIdentifier(project, d)).collect(Collectors.toList()) + .filter(DependencyDeclarationsUtil::isLibraryDependency) + .map(d -> toIdentifier(project, d)) + .collect(Collectors.toList()) : Collections.emptyList()); } @@ -50,9 +35,10 @@ private static String toIdentifier(Project project, Dependency dependency) { // assume Module Name of local Module ProjectDependency projectDependency = (ProjectDependency) dependency; if (projectDependency.getRequestedCapabilities().isEmpty()) { - return project.getGroup() + "." + dependency.getName(); + return project.getGroup() + "." + dependency.getName(); } else { - Capability capability = projectDependency.getRequestedCapabilities().get(0); + Capability capability = + projectDependency.getRequestedCapabilities().get(0); return capability.getGroup() + "." + capability.getName().replace("-", "."); } } diff --git a/src/main/java/org/gradlex/javamodule/dependencies/internal/utils/ModuleInfo.java b/src/main/java/org/gradlex/javamodule/dependencies/internal/utils/ModuleInfo.java index 143d1dec..c3fe5232 100644 --- a/src/main/java/org/gradlex/javamodule/dependencies/internal/utils/ModuleInfo.java +++ b/src/main/java/org/gradlex/javamodule/dependencies/internal/utils/ModuleInfo.java @@ -1,22 +1,7 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies.internal.utils; -import org.jspecify.annotations.Nullable; +import static org.gradlex.javamodule.dependencies.internal.utils.ModuleNamingUtil.sourceSetToModuleName; import java.io.Serializable; import java.util.ArrayList; @@ -24,8 +9,7 @@ import java.util.Collections; import java.util.List; import java.util.Objects; - -import static org.gradlex.javamodule.dependencies.internal.utils.ModuleNamingUtil.sourceSetToModuleName; +import org.jspecify.annotations.Nullable; public class ModuleInfo implements Serializable { @@ -37,8 +21,7 @@ public enum Directive { REQUIRES_RUNTIME; public String literal() { - return toString().toLowerCase().replace("_", " ") - .replace("runtime", RUNTIME_KEYWORD); + return toString().toLowerCase().replace("_", " ").replace("runtime", RUNTIME_KEYWORD); } } @@ -53,7 +36,6 @@ public String literal() { private final List requiresStaticTransitive = new ArrayList<>(); private final List requiresRuntime = new ArrayList<>(); - public ModuleInfo(String moduleInfoFileContent) { boolean insideComment = false; for (String line : moduleInfoFileContent.split("\n")) { @@ -101,8 +83,9 @@ public String moduleNamePrefix(String projectName, String sourceSetName, boolean return moduleName.substring(0, moduleName.length() - projectName.length() - 1); } if (this != EMPTY && fail) { - throw new RuntimeException("Module name '" + moduleName + "' does not fit the project and source set names; " + - "expected name '" + projectPlusSourceSetName + "'."); + throw new RuntimeException( + "Module name '" + moduleName + "' does not fit the project and source set names; " + + "expected name '" + projectPlusSourceSetName + "'."); } return null; } @@ -121,7 +104,8 @@ private boolean parse(String moduleLine, boolean insideComment) { .replace("}", "") .replace(RUNTIME_KEYWORD, "runtime") .replaceAll("/\\*.*?\\*/", " ") - .trim().split("\\s+")); + .trim() + .split("\\s+")); int singleLineCommentStartIndex = tokens.indexOf("//"); if (singleLineCommentStartIndex >= 0) { tokens = tokens.subList(0, singleLineCommentStartIndex); @@ -162,12 +146,6 @@ public boolean equals(Object o) { @Override public int hashCode() { return Objects.hash( - moduleName, - requires, - requiresTransitive, - requiresStatic, - requiresStaticTransitive, - requiresRuntime - ); + moduleName, requires, requiresTransitive, requiresStatic, requiresStaticTransitive, requiresRuntime); } } diff --git a/src/main/java/org/gradlex/javamodule/dependencies/internal/utils/ModuleInfoCache.java b/src/main/java/org/gradlex/javamodule/dependencies/internal/utils/ModuleInfoCache.java index 29e35cf2..bb17c1ed 100644 --- a/src/main/java/org/gradlex/javamodule/dependencies/internal/utils/ModuleInfoCache.java +++ b/src/main/java/org/gradlex/javamodule/dependencies/internal/utils/ModuleInfoCache.java @@ -1,38 +1,22 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies.internal.utils; -import org.gradle.api.logging.Logger; -import org.gradle.api.provider.Provider; -import org.gradle.api.provider.ProviderFactory; -import org.gradle.api.tasks.SourceSet; -import org.gradlex.javamodule.dependencies.LocalModule; -import org.jspecify.annotations.Nullable; -import org.slf4j.LoggerFactory; +import static org.gradlex.javamodule.dependencies.internal.utils.ModuleNamingUtil.sourceSetToCapabilitySuffix; -import javax.inject.Inject; import java.io.File; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Collection; import java.util.HashMap; import java.util.Map; - -import static org.gradlex.javamodule.dependencies.internal.utils.ModuleNamingUtil.sourceSetToCapabilitySuffix; +import javax.inject.Inject; +import org.gradle.api.logging.Logger; +import org.gradle.api.provider.Provider; +import org.gradle.api.provider.ProviderFactory; +import org.gradle.api.tasks.SourceSet; +import org.gradlex.javamodule.dependencies.LocalModule; +import org.jspecify.annotations.Nullable; +import org.slf4j.LoggerFactory; public abstract class ModuleInfoCache { private static final Logger LOGGER = (Logger) LoggerFactory.getLogger(ModuleInfoCache.class); @@ -79,20 +63,27 @@ public ModuleInfo get(SourceSet sourceSet, ProviderFactory providers) { * @param projectRoot the project that should hold a Java module * @return parsed module-info.java for the given project assuming a standard Java project layout */ - public ModuleInfo put(File projectRoot, String moduleInfoPath, String projectPath, String artifact, Provider group, ProviderFactory providers) { + public ModuleInfo put( + File projectRoot, + String moduleInfoPath, + String projectPath, + String artifact, + Provider group, + ProviderFactory providers) { File folder = new File(projectRoot, moduleInfoPath); if (maybePutModuleInfo(folder, providers)) { ModuleInfo thisModuleInfo = moduleInfo.get(folder); String moduleName = thisModuleInfo.getModuleName(); String capability = null; Path parentDirectory = Paths.get(moduleInfoPath).getParent(); - String capabilitySuffix = parentDirectory == null ? null : sourceSetToCapabilitySuffix(parentDirectory.getFileName().toString()); + String capabilitySuffix = parentDirectory == null + ? null + : sourceSetToCapabilitySuffix(parentDirectory.getFileName().toString()); if (capabilitySuffix != null) { if (group.isPresent()) { capability = group.get() + ":" + artifact + "-" + capabilitySuffix; } else { - LOGGER.lifecycle( - "[WARN] [Java Module Dependencies] " + moduleName + " - 'group' not defined!"); + LOGGER.lifecycle("[WARN] [Java Module Dependencies] " + moduleName + " - 'group' not defined!"); } } localModules.put(moduleName, new LocalModule(moduleName, projectPath, capability)); @@ -121,6 +112,8 @@ private boolean maybePutModuleInfo(File folder, ProviderFactory providers) { } private Provider provideModuleInfo(File folder, ProviderFactory providers) { - return providers.of(ValueSourceModuleInfo.class, spec -> spec.parameters(param -> param.getDir().set(folder))); + return providers.of( + ValueSourceModuleInfo.class, + spec -> spec.parameters(param -> param.getDir().set(folder))); } } diff --git a/src/main/java/org/gradlex/javamodule/dependencies/internal/utils/ModuleInfoClassCreator.java b/src/main/java/org/gradlex/javamodule/dependencies/internal/utils/ModuleInfoClassCreator.java index 28b50cce..cb4083a1 100644 --- a/src/main/java/org/gradlex/javamodule/dependencies/internal/utils/ModuleInfoClassCreator.java +++ b/src/main/java/org/gradlex/javamodule/dependencies/internal/utils/ModuleInfoClassCreator.java @@ -1,31 +1,15 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies.internal.utils; -import org.objectweb.asm.ClassWriter; -import org.objectweb.asm.ModuleVisitor; +import static org.objectweb.asm.Opcodes.ACC_MANDATED; +import static org.objectweb.asm.Opcodes.ACC_MODULE; +import static org.objectweb.asm.Opcodes.ACC_SYNTHETIC; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; - -import static org.objectweb.asm.Opcodes.ACC_MANDATED; -import static org.objectweb.asm.Opcodes.ACC_MODULE; -import static org.objectweb.asm.Opcodes.ACC_SYNTHETIC; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.ModuleVisitor; public abstract class ModuleInfoClassCreator { diff --git a/src/main/java/org/gradlex/javamodule/dependencies/internal/utils/ModuleJar.java b/src/main/java/org/gradlex/javamodule/dependencies/internal/utils/ModuleJar.java index 0c3e1d09..45311073 100644 --- a/src/main/java/org/gradlex/javamodule/dependencies/internal/utils/ModuleJar.java +++ b/src/main/java/org/gradlex/javamodule/dependencies/internal/utils/ModuleJar.java @@ -1,27 +1,6 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies.internal.utils; -import org.jspecify.annotations.Nullable; -import org.objectweb.asm.ClassReader; -import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.ModuleVisitor; -import org.objectweb.asm.Opcodes; - import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -30,12 +9,18 @@ import java.util.jar.Manifest; import java.util.regex.Pattern; import java.util.zip.ZipEntry; +import org.jspecify.annotations.Nullable; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.ModuleVisitor; +import org.objectweb.asm.Opcodes; public class ModuleJar { private static final String AUTOMATIC_MODULE_NAME_ATTRIBUTE = "Automatic-Module-Name"; private static final String MULTI_RELEASE_ATTRIBUTE = "Multi-Release"; private static final String MODULE_INFO_CLASS_FILE = "module-info.class"; - private static final Pattern MODULE_INFO_CLASS_MRJAR_PATH = Pattern.compile("META-INF/versions/\\d+/module-info.class"); + private static final Pattern MODULE_INFO_CLASS_MRJAR_PATH = + Pattern.compile("META-INF/versions/\\d+/module-info.class"); @Nullable public static String readModuleNameFromJarFile(File jarFileOrClassFolder) throws IOException { @@ -47,7 +32,7 @@ public static String readModuleNameFromJarFile(File jarFileOrClassFolder) throws } return readNameFromModuleInfoClass(Files.newInputStream(moduleInfo.toPath())); } - try (JarInputStream jarStream = new JarInputStream(Files.newInputStream(jarFileOrClassFolder.toPath()))) { + try (JarInputStream jarStream = new JarInputStream(Files.newInputStream(jarFileOrClassFolder.toPath()))) { String moduleName = getAutomaticModuleName(jarStream.getManifest()); if (moduleName != null) { return moduleName; @@ -58,7 +43,8 @@ public static String readModuleNameFromJarFile(File jarFileOrClassFolder) throws if (MODULE_INFO_CLASS_FILE.equals(next.getName())) { return readNameFromModuleInfoClass(jarStream); } - if (isMultiReleaseJar && MODULE_INFO_CLASS_MRJAR_PATH.matcher(next.getName()).matches()) { + if (isMultiReleaseJar + && MODULE_INFO_CLASS_MRJAR_PATH.matcher(next.getName()).matches()) { return readNameFromModuleInfoClass(jarStream); } next = jarStream.getNextEntry(); @@ -72,14 +58,15 @@ public static boolean isRealModule(File jarFileOrClassFolder) throws IOException // class folder return new File(jarFileOrClassFolder, MODULE_INFO_CLASS_FILE).exists(); } - try (JarInputStream jarStream = new JarInputStream(Files.newInputStream(jarFileOrClassFolder.toPath()))) { + try (JarInputStream jarStream = new JarInputStream(Files.newInputStream(jarFileOrClassFolder.toPath()))) { boolean isMultiReleaseJar = containsMultiReleaseJarEntry(jarStream); ZipEntry next = jarStream.getNextEntry(); while (next != null) { if (MODULE_INFO_CLASS_FILE.equals(next.getName())) { return true; } - if (isMultiReleaseJar && MODULE_INFO_CLASS_MRJAR_PATH.matcher(next.getName()).matches()) { + if (isMultiReleaseJar + && MODULE_INFO_CLASS_MRJAR_PATH.matcher(next.getName()).matches()) { return true; } next = jarStream.getNextEntry(); @@ -98,19 +85,22 @@ private static String getAutomaticModuleName(@Nullable Manifest manifest) { private static boolean containsMultiReleaseJarEntry(JarInputStream jarStream) { Manifest manifest = jarStream.getManifest(); - return manifest !=null && Boolean.parseBoolean(manifest.getMainAttributes().getValue(MULTI_RELEASE_ATTRIBUTE)); + return manifest != null + && Boolean.parseBoolean(manifest.getMainAttributes().getValue(MULTI_RELEASE_ATTRIBUTE)); } private static String readNameFromModuleInfoClass(InputStream input) throws IOException { ClassReader classReader = new ClassReader(input); String[] moduleName = new String[1]; - classReader.accept(new ClassVisitor(Opcodes.ASM8) { - @Override - public ModuleVisitor visitModule(String name, int access, String version) { - moduleName[0] = name; - return super.visitModule(name, access, version); - } - }, 0); + classReader.accept( + new ClassVisitor(Opcodes.ASM8) { + @Override + public ModuleVisitor visitModule(String name, int access, String version) { + moduleName[0] = name; + return super.visitModule(name, access, version); + } + }, + 0); return moduleName[0]; } } diff --git a/src/main/java/org/gradlex/javamodule/dependencies/internal/utils/ModuleNamingUtil.java b/src/main/java/org/gradlex/javamodule/dependencies/internal/utils/ModuleNamingUtil.java index 22eaaeae..f3d0e936 100644 --- a/src/main/java/org/gradlex/javamodule/dependencies/internal/utils/ModuleNamingUtil.java +++ b/src/main/java/org/gradlex/javamodule/dependencies/internal/utils/ModuleNamingUtil.java @@ -1,26 +1,10 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies.internal.utils; -import org.gradle.api.tasks.SourceSet; -import org.jspecify.annotations.Nullable; - import java.util.Arrays; import java.util.stream.Collectors; +import org.gradle.api.tasks.SourceSet; +import org.jspecify.annotations.Nullable; public abstract class ModuleNamingUtil { @@ -43,17 +27,17 @@ public static String sourceSetToCapabilitySuffix(String sourceSetName) { * Converts 'camelCase' and 'kebab-case' to 'dotted.case'. */ private static String toDottedCase(String sourceSetName) { - return Arrays.stream(sourceSetName.replace("-", ".") - .split("(?, ValueModuleDirectoryListing.Parameter> { +public abstract class ValueModuleDirectoryListing + implements ValueSource, ValueModuleDirectoryListing.Parameter> { public interface Parameter extends ValueSourceParameters { Property getDir(); + SetProperty getExplicitlyConfiguredFolders(); + SetProperty getExclusions(); + Property getRequiresBuildFile(); } @Override public List obtain() { Path path = getParameters().getDir().get().toPath(); - try (Stream directoryStream = Files.find(path, 1, (unused, basicFileAttributes) -> basicFileAttributes.isDirectory())) { + try (Stream directoryStream = + Files.find(path, 1, (unused, basicFileAttributes) -> basicFileAttributes.isDirectory())) { return directoryStream - .filter(x -> !getParameters().getExplicitlyConfiguredFolders().get().contains(x.getFileName().toString())) - .filter(x -> getParameters().getExclusions().get().stream().noneMatch(r -> x.getFileName().toString().matches(r))) + .filter(x -> !getParameters() + .getExplicitlyConfiguredFolders() + .get() + .contains(x.getFileName().toString())) + .filter(x -> getParameters().getExclusions().get().stream() + .noneMatch(r -> x.getFileName().toString().matches(r))) .filter(x -> checkBuildFile(x, getParameters())) .map(x -> x.getFileName().toString()) .sorted() diff --git a/src/main/java/org/gradlex/javamodule/dependencies/internal/utils/ValueSourceModuleInfo.java b/src/main/java/org/gradlex/javamodule/dependencies/internal/utils/ValueSourceModuleInfo.java index 524d6b83..4f2f0d3b 100644 --- a/src/main/java/org/gradlex/javamodule/dependencies/internal/utils/ValueSourceModuleInfo.java +++ b/src/main/java/org/gradlex/javamodule/dependencies/internal/utils/ValueSourceModuleInfo.java @@ -1,30 +1,14 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies.internal.utils; +import java.io.File; +import java.io.IOException; +import java.util.Scanner; import org.gradle.api.file.DirectoryProperty; import org.gradle.api.provider.ValueSource; import org.gradle.api.provider.ValueSourceParameters; import org.jetbrains.annotations.Nullable; -import java.io.File; -import java.io.IOException; -import java.util.Scanner; - public abstract class ValueSourceModuleInfo implements ValueSource { interface Parameter extends ValueSourceParameters { diff --git a/src/main/java/org/gradlex/javamodule/dependencies/internal/utils/package-info.java b/src/main/java/org/gradlex/javamodule/dependencies/internal/utils/package-info.java index 161f9019..20fb5d99 100644 --- a/src/main/java/org/gradlex/javamodule/dependencies/internal/utils/package-info.java +++ b/src/main/java/org/gradlex/javamodule/dependencies/internal/utils/package-info.java @@ -1,20 +1,5 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 @NullMarked package org.gradlex.javamodule.dependencies.internal.utils; -import org.jspecify.annotations.NullMarked; \ No newline at end of file +import org.jspecify.annotations.NullMarked; diff --git a/src/main/java/org/gradlex/javamodule/dependencies/package-info.java b/src/main/java/org/gradlex/javamodule/dependencies/package-info.java index d96f3d5e..ab4c5504 100644 --- a/src/main/java/org/gradlex/javamodule/dependencies/package-info.java +++ b/src/main/java/org/gradlex/javamodule/dependencies/package-info.java @@ -1,20 +1,5 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 @NullMarked package org.gradlex.javamodule.dependencies; -import org.jspecify.annotations.NullMarked; \ No newline at end of file +import org.jspecify.annotations.NullMarked; diff --git a/src/main/java/org/gradlex/javamodule/dependencies/tasks/BuildFileDependenciesGenerate.java b/src/main/java/org/gradlex/javamodule/dependencies/tasks/BuildFileDependenciesGenerate.java index 5345eb63..9aaaf0f3 100644 --- a/src/main/java/org/gradlex/javamodule/dependencies/tasks/BuildFileDependenciesGenerate.java +++ b/src/main/java/org/gradlex/javamodule/dependencies/tasks/BuildFileDependenciesGenerate.java @@ -1,21 +1,14 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies.tasks; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import javax.inject.Inject; import org.gradle.api.DefaultTask; import org.gradle.api.file.RegularFileProperty; import org.gradle.api.model.ObjectFactory; @@ -25,15 +18,6 @@ import org.gradle.api.tasks.Internal; import org.gradle.api.tasks.TaskAction; -import javax.inject.Inject; -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; - public abstract class BuildFileDependenciesGenerate extends DefaultTask { public abstract static class SourceSetDependencies { @@ -90,7 +74,13 @@ public DependencyDeclaration(String scope, String moduleName, Provider f @Inject public abstract ObjectFactory getObjects(); - public void addDependencies(String name, List api, List implementation, List compileOnlyApi, List compileOnly, List runtimeOnly) { + public void addDependencies( + String name, + List api, + List implementation, + List compileOnlyApi, + List compileOnly, + List runtimeOnly) { SourceSetDependencies dependencies = getObjects().newInstance(SourceSetDependencies.class, name); dependencies.getApiDependencies().convention(api); dependencies.getImplementationDependencies().convention(implementation); @@ -104,9 +94,12 @@ public void addDependencies(String name, List api, List fileContentToPreserve = Files.readAllLines(buildGradle.toPath()); - Optional dependenciesBlock = fileContentToPreserve.stream().filter(line -> line.contains("dependencies")).findFirst(); + Optional dependenciesBlock = fileContentToPreserve.stream() + .filter(line -> line.contains("dependencies")) + .findFirst(); if (dependenciesBlock.isPresent()) { - fileContentToPreserve = fileContentToPreserve.subList(0, fileContentToPreserve.indexOf(dependenciesBlock.get())); + fileContentToPreserve = + fileContentToPreserve.subList(0, fileContentToPreserve.indexOf(dependenciesBlock.get())); } List content = new ArrayList<>(fileContentToPreserve); @@ -116,16 +109,18 @@ public void generate() throws IOException { if (!getDependencies().get().stream().allMatch(SourceSetDependencies::isEmpty)) { content.add("dependencies {"); - getDependencies().get().stream().sorted((a, b) -> ("main".equals(a.name)) ? -1 : a.name.compareTo(b.name)).forEach(sourceSetBlock -> { - content.addAll(toDeclarationString(sourceSetBlock.getApiDependencies())); - content.addAll(toDeclarationString(sourceSetBlock.getImplementationDependencies())); - content.addAll(toDeclarationString(sourceSetBlock.getCompileOnlyApiDependencies())); - content.addAll(toDeclarationString(sourceSetBlock.getCompileOnlyDependencies())); - content.addAll(toDeclarationString(sourceSetBlock.getRuntimeOnlyDependencies())); - if (!sourceSetBlock.isEmpty()) { - content.add(""); - } - }); + getDependencies().get().stream() + .sorted((a, b) -> ("main".equals(a.name)) ? -1 : a.name.compareTo(b.name)) + .forEach(sourceSetBlock -> { + content.addAll(toDeclarationString(sourceSetBlock.getApiDependencies())); + content.addAll(toDeclarationString(sourceSetBlock.getImplementationDependencies())); + content.addAll(toDeclarationString(sourceSetBlock.getCompileOnlyApiDependencies())); + content.addAll(toDeclarationString(sourceSetBlock.getCompileOnlyDependencies())); + content.addAll(toDeclarationString(sourceSetBlock.getRuntimeOnlyDependencies())); + if (!sourceSetBlock.isEmpty()) { + content.add(""); + } + }); content.remove(content.size() - 1); content.add("}"); } @@ -134,36 +129,39 @@ public void generate() throws IOException { } private List toDeclarationString(ListProperty dependencies) { - return dependencies.get().stream().map(d -> { - String group = d.fullId.get().split(":")[0]; - String artifact = d.fullId.get().split(":")[1]; - String feature = null; - if (artifact.contains("|")) { - feature = artifact.split("\\|")[1]; - artifact = artifact.split("\\|")[0]; - } - - String identifier; - if (group.equals(getOwnProjectGroup().get())) { - if (getWithCatalog().get()) { - identifier = "projects." + toCamelCase(artifact); - } else { - identifier = "project(\":" + artifact + "\")"; - } - } else { - if (getWithCatalog().get()) { - identifier = "libs." + d.moduleName; - } else { - identifier = "\"" + group + ":" + artifact + "\""; - } - } - - if (feature == null) { - return " " + d.scope + "(" + identifier + ")"; - } else { - return " " + d.scope + "(" + identifier + ") { capabilities { requireCapabilities(\"" + group + ":" + artifact + "-" + feature + "\") } }"; - } - }).collect(Collectors.toList()); + return dependencies.get().stream() + .map(d -> { + String group = d.fullId.get().split(":")[0]; + String artifact = d.fullId.get().split(":")[1]; + String feature = null; + if (artifact.contains("|")) { + feature = artifact.split("\\|")[1]; + artifact = artifact.split("\\|")[0]; + } + + String identifier; + if (group.equals(getOwnProjectGroup().get())) { + if (getWithCatalog().get()) { + identifier = "projects." + toCamelCase(artifact); + } else { + identifier = "project(\":" + artifact + "\")"; + } + } else { + if (getWithCatalog().get()) { + identifier = "libs." + d.moduleName; + } else { + identifier = "\"" + group + ":" + artifact + "\""; + } + } + + if (feature == null) { + return " " + d.scope + "(" + identifier + ")"; + } else { + return " " + d.scope + "(" + identifier + ") { capabilities { requireCapabilities(\"" + group + + ":" + artifact + "-" + feature + "\") } }"; + } + }) + .collect(Collectors.toList()); } private String toCamelCase(String s) { @@ -174,7 +172,10 @@ private String toCamelCase(String s) { if (i == 0) { word = word.isEmpty() ? word : word.toLowerCase(); } else { - word = word.isEmpty() ? word : Character.toUpperCase(word.charAt(0)) + word.substring(1).toLowerCase(); + word = word.isEmpty() + ? word + : Character.toUpperCase(word.charAt(0)) + + word.substring(1).toLowerCase(); } builder.append(word); } diff --git a/src/main/java/org/gradlex/javamodule/dependencies/tasks/CatalogGenerate.java b/src/main/java/org/gradlex/javamodule/dependencies/tasks/CatalogGenerate.java index be260505..880163ac 100644 --- a/src/main/java/org/gradlex/javamodule/dependencies/tasks/CatalogGenerate.java +++ b/src/main/java/org/gradlex/javamodule/dependencies/tasks/CatalogGenerate.java @@ -1,30 +1,6 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies.tasks; -import org.gradle.api.DefaultTask; -import org.gradle.api.file.RegularFileProperty; -import org.gradle.api.provider.Property; -import org.gradle.api.provider.Provider; -import org.gradle.api.provider.SetProperty; -import org.gradle.api.tasks.Internal; -import org.gradle.api.tasks.TaskAction; -import org.jspecify.annotations.Nullable; - import java.io.File; import java.io.IOException; import java.nio.file.Files; @@ -33,6 +9,14 @@ import java.util.List; import java.util.Objects; import java.util.stream.Collectors; +import org.gradle.api.DefaultTask; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.provider.Property; +import org.gradle.api.provider.Provider; +import org.gradle.api.provider.SetProperty; +import org.gradle.api.tasks.Internal; +import org.gradle.api.tasks.TaskAction; +import org.jspecify.annotations.Nullable; public abstract class CatalogGenerate extends DefaultTask { @@ -83,7 +67,11 @@ public void generate() throws IOException { List content = new ArrayList<>(); content.add("[libraries]"); - content.addAll(getEntries().get().stream().map(this::toDeclarationString).filter(Objects::nonNull).sorted().collect(Collectors.toList())); + content.addAll(getEntries().get().stream() + .map(this::toDeclarationString) + .filter(Objects::nonNull) + .sorted() + .collect(Collectors.toList())); Files.write(catalog.toPath(), content); } @@ -96,9 +84,9 @@ private String toDeclarationString(CatalogEntry entry) { } String notation; if (entry.version == null) { - notation = "{ module = \"" + entry.fullId.get() + "\" }"; + notation = "{ module = \"" + entry.fullId.get() + "\" }"; } else { - notation = "{ module = \"" + entry.fullId.get() + "\", version = \"" + entry.version + "\" }"; + notation = "{ module = \"" + entry.fullId.get() + "\", version = \"" + entry.version + "\" }"; } return entry.moduleName.replace('.', '-') + " = " + notation; } diff --git a/src/main/java/org/gradlex/javamodule/dependencies/tasks/ModuleDependencyReport.java b/src/main/java/org/gradlex/javamodule/dependencies/tasks/ModuleDependencyReport.java index b48654ed..e97dd47d 100644 --- a/src/main/java/org/gradlex/javamodule/dependencies/tasks/ModuleDependencyReport.java +++ b/src/main/java/org/gradlex/javamodule/dependencies/tasks/ModuleDependencyReport.java @@ -1,21 +1,9 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies.tasks; +import java.util.Collections; +import java.util.Set; +import javax.inject.Inject; import org.gradle.api.artifacts.ArtifactCollection; import org.gradle.api.artifacts.Configuration; import org.gradle.api.file.ConfigurableFileCollection; @@ -26,10 +14,6 @@ import org.gradle.api.tasks.diagnostics.DependencyReportTask; import org.gradlex.javamodule.dependencies.internal.diagnostics.AsciiModuleDependencyReportRenderer; -import javax.inject.Inject; -import java.util.Collections; -import java.util.Set; - public abstract class ModuleDependencyReport extends DependencyReportTask { @Internal @@ -66,7 +50,8 @@ private void configurationsChanged() { getModuleArtifacts().set(Collections.emptyMap()); for (Configuration conf : getConfigurations()) { getModulePath().from(conf); - getModuleArtifacts().put(conf.getName(), getProviders().provider(() -> conf.getIncoming().getArtifacts())); + getModuleArtifacts().put(conf.getName(), getProviders().provider(() -> conf.getIncoming() + .getArtifacts())); } } } diff --git a/src/main/java/org/gradlex/javamodule/dependencies/tasks/ModuleDirectivesOrderingCheck.java b/src/main/java/org/gradlex/javamodule/dependencies/tasks/ModuleDirectivesOrderingCheck.java index 81006eeb..d389b359 100644 --- a/src/main/java/org/gradlex/javamodule/dependencies/tasks/ModuleDirectivesOrderingCheck.java +++ b/src/main/java/org/gradlex/javamodule/dependencies/tasks/ModuleDirectivesOrderingCheck.java @@ -1,21 +1,11 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies.tasks; +import java.io.IOException; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; import org.gradle.api.DefaultTask; import org.gradle.api.file.RegularFileProperty; import org.gradle.api.provider.Property; @@ -26,12 +16,6 @@ import org.gradle.api.tasks.TaskAction; import org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo; -import java.io.IOException; -import java.nio.file.Files; -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - @CacheableTask public abstract class ModuleDirectivesOrderingCheck extends DefaultTask { @@ -53,16 +37,20 @@ public abstract class ModuleDirectivesOrderingCheck extends DefaultTask { public void checkOrder() throws IOException { StringBuilder sb = new StringBuilder(); for (ModuleInfo.Directive directive : ModuleInfo.Directive.values()) { - List originalOrder = getModuleInfo().get().get(directive).stream().map(name -> name + ";").collect(Collectors.toList()); + List originalOrder = getModuleInfo().get().get(directive).stream() + .map(name -> name + ";") + .collect(Collectors.toList()); List sorted = new ArrayList<>(originalOrder); sorted.sort((m1, m2) -> { // own modules go first if (getModuleNamePrefix().isPresent()) { - if (m1.startsWith(getModuleNamePrefix().get()) && !m2.startsWith(getModuleNamePrefix().get())) { + if (m1.startsWith(getModuleNamePrefix().get()) + && !m2.startsWith(getModuleNamePrefix().get())) { return -1; } - if (!m1.startsWith(getModuleNamePrefix().get()) && m2.startsWith(getModuleNamePrefix().get())) { + if (!m1.startsWith(getModuleNamePrefix().get()) + && m2.startsWith(getModuleNamePrefix().get())) { return 1; } } diff --git a/src/main/java/org/gradlex/javamodule/dependencies/tasks/ModuleDirectivesScopeCheck.java b/src/main/java/org/gradlex/javamodule/dependencies/tasks/ModuleDirectivesScopeCheck.java index f2301ca9..3c8771d4 100644 --- a/src/main/java/org/gradlex/javamodule/dependencies/tasks/ModuleDirectivesScopeCheck.java +++ b/src/main/java/org/gradlex/javamodule/dependencies/tasks/ModuleDirectivesScopeCheck.java @@ -1,23 +1,20 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies.tasks; +import static org.gradle.api.plugins.JavaPlugin.RUNTIME_ONLY_CONFIGURATION_NAME; +import static org.gradlex.javamodule.dependencies.internal.utils.ModuleJar.readModuleNameFromJarFile; + import com.autonomousapps.AbstractPostProcessingTask; import com.autonomousapps.model.Advice; +import java.io.IOException; +import java.nio.file.Files; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; import org.gradle.api.artifacts.ArtifactCollection; import org.gradle.api.artifacts.component.ComponentIdentifier; import org.gradle.api.artifacts.component.ModuleComponentIdentifier; @@ -34,24 +31,12 @@ import org.gradle.api.tasks.TaskAction; import org.jspecify.annotations.Nullable; -import java.io.IOException; -import java.nio.file.Files; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; - -import static org.gradle.api.plugins.JavaPlugin.RUNTIME_ONLY_CONFIGURATION_NAME; -import static org.gradlex.javamodule.dependencies.internal.utils.ModuleJar.readModuleNameFromJarFile; - @CacheableTask public abstract class ModuleDirectivesScopeCheck extends AbstractPostProcessingTask { private static final Map SCOPES_TO_DIRECTIVES = new HashMap<>(); private static final Map SCOPES_TO_DIRECTIVES_BUILD_FILE_DSL = new HashMap<>(); + static { SCOPES_TO_DIRECTIVES.put("compileOnlyApi", "requires static transitive"); SCOPES_TO_DIRECTIVES.put("compileOnly", "requires static"); @@ -79,21 +64,29 @@ public void analyze() throws IOException { StringBuilder message = new StringBuilder(); for (Map.Entry sourceSet : getSourceSets().get().entrySet()) { boolean inBuildFile = !sourceSet.getValue().endsWith("module-info.java"); - List toAdd = projectAdvice.stream().filter(a -> - a.getToConfiguration() != null && !RUNTIME_ONLY_CONFIGURATION_NAME.equals(getScope(a.getToConfiguration()).orElse(null)) - ).filter(a -> - sourceSet.getKey().equals(sourceSetName(a.getToConfiguration())) - ).map(a -> - declaration(a.getToConfiguration(), a.getCoordinates().getIdentifier(), a.getCoordinates().getGradleVariantIdentification().getCapabilities(), inBuildFile) - ).sorted().collect(Collectors.toList()); - - List toRemove = projectAdvice.stream().filter(a -> - a.getFromConfiguration() != null - ).filter(a -> - sourceSet.getKey().equals(sourceSetName(a.getFromConfiguration())) - ).map(a -> - declaration(a.getFromConfiguration(), a.getCoordinates().getIdentifier(), a.getCoordinates().getGradleVariantIdentification().getCapabilities(), inBuildFile) - ).sorted().collect(Collectors.toList()); + List toAdd = projectAdvice.stream() + .filter(a -> a.getToConfiguration() != null + && !RUNTIME_ONLY_CONFIGURATION_NAME.equals( + getScope(a.getToConfiguration()).orElse(null))) + .filter(a -> sourceSet.getKey().equals(sourceSetName(a.getToConfiguration()))) + .map(a -> declaration( + a.getToConfiguration(), + a.getCoordinates().getIdentifier(), + a.getCoordinates().getGradleVariantIdentification().getCapabilities(), + inBuildFile)) + .sorted() + .collect(Collectors.toList()); + + List toRemove = projectAdvice.stream() + .filter(a -> a.getFromConfiguration() != null) + .filter(a -> sourceSet.getKey().equals(sourceSetName(a.getFromConfiguration()))) + .map(a -> declaration( + a.getFromConfiguration(), + a.getCoordinates().getIdentifier(), + a.getCoordinates().getGradleVariantIdentification().getCapabilities(), + inBuildFile)) + .sorted() + .collect(Collectors.toList()); if (!toAdd.isEmpty() || !toRemove.isEmpty()) { if (message.length() > 0) { @@ -131,9 +124,13 @@ public void analyze() throws IOException { } private String declaration(String conf, String coordinates, Set capabilities, boolean inBuildFile) { - String capability = capabilities.isEmpty() ? coordinates : capabilities.iterator().next(); - ResolvedArtifactResult moduleJar = getModuleArtifacts().get().stream().flatMap(c -> c.getArtifacts().stream()).filter(a -> - coordinatesEquals(coordinates, capability, a)).findFirst().orElse(null); + String capability = + capabilities.isEmpty() ? coordinates : capabilities.iterator().next(); + ResolvedArtifactResult moduleJar = getModuleArtifacts().get().stream() + .flatMap(c -> c.getArtifacts().stream()) + .filter(a -> coordinatesEquals(coordinates, capability, a)) + .findFirst() + .orElse(null); try { String moduleName = null; if (moduleJar != null) { @@ -159,7 +156,8 @@ private boolean coordinatesEquals(String coordinates, String capability, Resolve return false; } if (id instanceof ModuleComponentIdentifier) { - return coordinates.equals(((ModuleComponentIdentifier) id).getModuleIdentifier().toString()); + return coordinates.equals( + ((ModuleComponentIdentifier) id).getModuleIdentifier().toString()); } if (id instanceof ProjectComponentIdentifier) { return coordinates.equals(((ProjectComponentIdentifier) id).getProjectPath()); @@ -173,7 +171,8 @@ private String sourceSetName(String configurationName) { if (!scope.isPresent()) { return null; } - String sourceSet = configurationName.substring(0, configurationName.length() - scope.get().length()); + String sourceSet = configurationName.substring( + 0, configurationName.length() - scope.get().length()); return sourceSet.isEmpty() ? "main" : sourceSet; } @@ -183,7 +182,8 @@ private String directive(String configurationName, Map scopesToD } private Optional getScope(String configurationName) { - return SCOPES_TO_DIRECTIVES.keySet().stream().filter(k -> configurationName.toLowerCase(Locale.ROOT).endsWith(k.toLowerCase(Locale.ROOT))).findFirst(); + return SCOPES_TO_DIRECTIVES.keySet().stream() + .filter(k -> configurationName.toLowerCase(Locale.ROOT).endsWith(k.toLowerCase(Locale.ROOT))) + .findFirst(); } - } diff --git a/src/main/java/org/gradlex/javamodule/dependencies/tasks/ModuleInfoGenerate.java b/src/main/java/org/gradlex/javamodule/dependencies/tasks/ModuleInfoGenerate.java index f58cc0c0..b77e0bd2 100644 --- a/src/main/java/org/gradlex/javamodule/dependencies/tasks/ModuleInfoGenerate.java +++ b/src/main/java/org/gradlex/javamodule/dependencies/tasks/ModuleInfoGenerate.java @@ -1,22 +1,15 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies.tasks; -import org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo; +import static org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo.Directive.*; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; import org.gradle.api.DefaultTask; import org.gradle.api.file.RegularFileProperty; import org.gradle.api.provider.ListProperty; @@ -25,18 +18,9 @@ import org.gradle.api.tasks.Input; import org.gradle.api.tasks.OutputFile; import org.gradle.api.tasks.TaskAction; +import org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo; import org.jspecify.annotations.Nullable; -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.stream.Collectors; - -import static org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo.Directive.*; - public abstract class ModuleInfoGenerate extends DefaultTask { @Input @@ -68,25 +52,29 @@ public void generate() throws IOException { File moduleInfo = getModuleInfoFile().get().getAsFile(); List content = new ArrayList<>(); - content.add("module "+ getModuleName().get() + " {"); + content.add("module " + getModuleName().get() + " {"); if (!getApiDependencies().get().isEmpty()) { content.addAll(dependenciesToModuleDirectives(getApiDependencies().get(), REQUIRES_TRANSITIVE)); content.add(""); } if (!getImplementationDependencies().get().isEmpty()) { - content.addAll(dependenciesToModuleDirectives(getImplementationDependencies().get(), REQUIRES)); + content.addAll(dependenciesToModuleDirectives( + getImplementationDependencies().get(), REQUIRES)); content.add(""); } if (!getCompileOnlyApiDependencies().get().isEmpty()) { - content.addAll(dependenciesToModuleDirectives(getCompileOnlyApiDependencies().get(), REQUIRES_STATIC_TRANSITIVE)); + content.addAll(dependenciesToModuleDirectives( + getCompileOnlyApiDependencies().get(), REQUIRES_STATIC_TRANSITIVE)); content.add(""); } if (!getCompileOnlyDependencies().get().isEmpty()) { - content.addAll(dependenciesToModuleDirectives(getCompileOnlyDependencies().get(), REQUIRES_STATIC)); + content.addAll( + dependenciesToModuleDirectives(getCompileOnlyDependencies().get(), REQUIRES_STATIC)); content.add(""); } if (!getRuntimeOnlyDependencies().get().isEmpty()) { - content.addAll(dependenciesToModuleDirectives(getRuntimeOnlyDependencies().get(), REQUIRES_RUNTIME)); + content.addAll( + dependenciesToModuleDirectives(getRuntimeOnlyDependencies().get(), REQUIRES_RUNTIME)); content.add(""); } content.add("}"); @@ -94,16 +82,21 @@ public void generate() throws IOException { Files.write(moduleInfo.toPath(), content); } - private Collection dependenciesToModuleDirectives(List dependencies, ModuleInfo.Directive directive) { - return dependencies.stream().map(gaOrProjectModuleName -> { - String moduleName = moduleName(gaOrProjectModuleName); - if (moduleName == null) { - getLogger().lifecycle("Skipping '" + gaOrProjectModuleName + "' - no mapping - run ':analyzeModulePath' for more details"); - return " // " + directive.literal() + " " + gaOrProjectModuleName + ";"; - } else { - return " " + directive.literal() + " " + moduleName + ";"; - } - }).collect(Collectors.toList()); + private Collection dependenciesToModuleDirectives( + List dependencies, ModuleInfo.Directive directive) { + return dependencies.stream() + .map(gaOrProjectModuleName -> { + String moduleName = moduleName(gaOrProjectModuleName); + if (moduleName == null) { + getLogger() + .lifecycle("Skipping '" + gaOrProjectModuleName + + "' - no mapping - run ':analyzeModulePath' for more details"); + return " // " + directive.literal() + " " + gaOrProjectModuleName + ";"; + } else { + return " " + directive.literal() + " " + moduleName + ";"; + } + }) + .collect(Collectors.toList()); } private @Nullable String moduleName(String gaOrProjectModuleName) { diff --git a/src/main/java/org/gradlex/javamodule/dependencies/tasks/ModulePathAnalysis.java b/src/main/java/org/gradlex/javamodule/dependencies/tasks/ModulePathAnalysis.java index 72da9a7b..95019ede 100644 --- a/src/main/java/org/gradlex/javamodule/dependencies/tasks/ModulePathAnalysis.java +++ b/src/main/java/org/gradlex/javamodule/dependencies/tasks/ModulePathAnalysis.java @@ -1,21 +1,18 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies.tasks; +import static org.gradlex.javamodule.dependencies.internal.utils.ModuleJar.isRealModule; +import static org.gradlex.javamodule.dependencies.internal.utils.ModuleJar.readModuleNameFromJarFile; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import javax.inject.Inject; import org.gradle.api.DefaultTask; import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; @@ -33,26 +30,13 @@ import org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo; import org.jspecify.annotations.Nullable; -import javax.inject.Inject; -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.util.List; -import java.util.Set; -import java.util.TreeSet; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static org.gradlex.javamodule.dependencies.internal.utils.ModuleJar.isRealModule; -import static org.gradlex.javamodule.dependencies.internal.utils.ModuleJar.readModuleNameFromJarFile; - public abstract class ModulePathAnalysis extends DefaultTask { private final String projectName; private final SourceSetContainer sourceSets; private final JavaModuleDependenciesExtension javaModuleDependencies; @InputFiles - public abstract ListProperty getClasspathConfigurations() ; + public abstract ListProperty getClasspathConfigurations(); @Inject public ModulePathAnalysis(Project project) { @@ -74,15 +58,16 @@ public void report() throws IOException { for (File folder : main.getJava().getSrcDirs()) { File file = new File(folder, "module-info.java"); if (file.exists()) { - try(Stream lines = Files.lines(file.toPath())) { + try (Stream lines = Files.lines(file.toPath())) { String fileContent = lines.collect(Collectors.joining("\n")); - ownModuleNamesPrefix = new ModuleInfo(fileContent).moduleNamePrefix(projectName, main.getName(), false); + ownModuleNamesPrefix = + new ModuleInfo(fileContent).moduleNamePrefix(projectName, main.getName(), false); } break; } } - for (Configuration classPath: getClasspathConfigurations().get()) { + for (Configuration classPath : getClasspathConfigurations().get()) { collect(classPath, usedMappings, nonModules, missingMappings, wrongMappings, ownModuleNamesPrefix); } @@ -102,7 +87,8 @@ public void report() throws IOException { } p(""); p("Notes / Options:"); - p(" - This may be ok if you use the Classpath (aka ALL-UNNAMED) in addition to the Module Path (automatic modules can see ALL-UNNAMED)"); + p( + " - This may be ok if you use the Classpath (aka ALL-UNNAMED) in addition to the Module Path (automatic modules can see ALL-UNNAMED)"); p(" - Remove the dependencies or upgrade to higher versions"); p(" - Patch legacy Jars to Modules: https://github.com/gradlex-org/extra-java-module-info"); } @@ -118,8 +104,10 @@ public void report() throws IOException { p("Options to fix:"); p(" - Upgrade to newer version(s) - use ':recommendModuleVersions'"); p(" - Fix wrong mapping, via 'moduleNameToGA.put('...', '...')'"); - p(" - If it is about a legacy Jar you want to use as Module, you need to patch it: https://github.com/gradlex-org/extra-java-module-info"); - p(" - Report a wrong mapping in the plugin: https://github.com/gradlex-org/java-module-dependencies/issues/new"); + p( + " - If it is about a legacy Jar you want to use as Module, you need to patch it: https://github.com/gradlex-org/extra-java-module-info"); + p( + " - Report a wrong mapping in the plugin: https://github.com/gradlex-org/java-module-dependencies/issues/new"); } if (!missingMappings.isEmpty()) { @@ -135,12 +123,20 @@ public void report() throws IOException { p(""); p("Options to fix:"); p(" - Add mappings in your convention plugins - you may copy&paste the above output"); - p(" - Provide a PR to add missing mappings for well-known Modules to the plugin: https://github.com/gradlex-org/java-module-dependencies/pulls"); + p( + " - Provide a PR to add missing mappings for well-known Modules to the plugin: https://github.com/gradlex-org/java-module-dependencies/pulls"); } p(""); } - private void collect(Configuration configuration, Set usedMappings, Set nonModules, Set missingMappings, Set wrongMappings, @Nullable String ownModuleNamesPrefix) throws IOException { + private void collect( + Configuration configuration, + Set usedMappings, + Set nonModules, + Set missingMappings, + Set wrongMappings, + @Nullable String ownModuleNamesPrefix) + throws IOException { for (ResolvedArtifactResult result : configuration.getIncoming().getArtifacts()) { ComponentIdentifier id = result.getId().getComponentIdentifier(); File resultFile = result.getFile(); @@ -162,9 +158,10 @@ private void collect(Configuration configuration, Set usedMappings, Set< if (capabilities.isEmpty()) { moduleName = ownModuleNamesPrefix + "." + projectName; } else { - moduleName = ownModuleNamesPrefix + "." + capabilities.get(0).getName().replace("-", "."); + moduleName = ownModuleNamesPrefix + "." + + capabilities.get(0).getName().replace("-", "."); } - } else if (id instanceof ModuleComponentIdentifier){ + } else if (id instanceof ModuleComponentIdentifier) { ModuleComponentIdentifier moduleVersion = (ModuleComponentIdentifier) id; ga = moduleVersion.getGroup() + ":" + moduleVersion.getModule(); version = " (" + moduleVersion.getVersion() + ")"; @@ -200,5 +197,4 @@ private void collect(Configuration configuration, Set usedMappings, Set< private void p(String toPrint) { System.out.println(toPrint); } - } diff --git a/src/main/java/org/gradlex/javamodule/dependencies/tasks/ModuleVersionRecommendation.java b/src/main/java/org/gradlex/javamodule/dependencies/tasks/ModuleVersionRecommendation.java index 4a02d9b5..f6dc4f96 100644 --- a/src/main/java/org/gradlex/javamodule/dependencies/tasks/ModuleVersionRecommendation.java +++ b/src/main/java/org/gradlex/javamodule/dependencies/tasks/ModuleVersionRecommendation.java @@ -1,21 +1,11 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies.tasks; +import java.util.Objects; +import java.util.Set; +import java.util.TreeSet; +import java.util.stream.Collectors; +import javax.inject.Inject; import org.gradle.api.DefaultTask; import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; @@ -41,12 +31,6 @@ import org.gradle.api.tasks.TaskAction; import org.gradlex.javamodule.dependencies.JavaModuleDependenciesExtension; -import javax.inject.Inject; -import java.util.Objects; -import java.util.Set; -import java.util.TreeSet; -import java.util.stream.Collectors; - public abstract class ModuleVersionRecommendation extends DefaultTask { @Input @@ -67,18 +51,24 @@ public ModuleVersionRecommendation(Project project) { ConfigurationContainer configurations = project.getConfigurations(); ComponentMetadataHandler components = project.getDependencies().getComponents(); SourceSetContainer sourceSets = project.getExtensions().getByType(SourceSetContainer.class); - JavaModuleDependenciesExtension javaModuleDependencies = project.getExtensions().getByType(JavaModuleDependenciesExtension.class); + JavaModuleDependenciesExtension javaModuleDependencies = + project.getExtensions().getByType(JavaModuleDependenciesExtension.class); - AttributeContainer rtClasspathAttributes = configurations.getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME).getAttributes(); + AttributeContainer rtClasspathAttributes = configurations + .getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME) + .getAttributes(); Configuration latestVersionsClasspath = configurations.create("latestVersionsClasspath", c -> { c.setCanBeConsumed(false); c.setCanBeResolved(true); - c.getAttributes().attribute( - Usage.USAGE_ATTRIBUTE, - Objects.requireNonNull(rtClasspathAttributes.getAttribute(Usage.USAGE_ATTRIBUTE))); - c.getAttributes().attribute( - LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, - Objects.requireNonNull(rtClasspathAttributes.getAttribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE))); + c.getAttributes() + .attribute( + Usage.USAGE_ATTRIBUTE, + Objects.requireNonNull(rtClasspathAttributes.getAttribute(Usage.USAGE_ATTRIBUTE))); + c.getAttributes() + .attribute( + LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, + Objects.requireNonNull( + rtClasspathAttributes.getAttribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE))); c.getResolutionStrategy().getDependencySubstitution().all(m -> { ComponentSelector requested = m.getRequested(); @@ -91,8 +81,10 @@ public ModuleVersionRecommendation(Project project) { }); for (SourceSet sourceSet : sourceSets) { - latestVersionsClasspath.extendsFrom(configurations.getByName(sourceSet.getRuntimeClasspathConfigurationName())); - latestVersionsClasspath.extendsFrom(configurations.getByName(sourceSet.getCompileClasspathConfigurationName())); + latestVersionsClasspath.extendsFrom( + configurations.getByName(sourceSet.getRuntimeClasspathConfigurationName())); + latestVersionsClasspath.extendsFrom( + configurations.getByName(sourceSet.getCompileClasspathConfigurationName())); } components.all(c -> { @@ -108,19 +100,24 @@ public ModuleVersionRecommendation(Project project) { c.setStatus("integration"); } }); - getResolutionResult().set(project.provider(() -> latestVersionsClasspath.getIncoming().getResolutionResult().getAllComponents().stream().map( - result -> { - ModuleVersionIdentifier moduleVersion = result.getModuleVersion(); - if (moduleVersion != null && !(result.getId() instanceof ProjectComponentIdentifier)) { - String ga = moduleVersion.getGroup() + ":" + moduleVersion.getName(); - String version = moduleVersion.getVersion(); - Provider moduleName = javaModuleDependencies.moduleName(ga); - if (moduleName.isPresent()) { - return moduleName.get() + ":" + version; - } - } - return null; - }).filter(Objects::nonNull).collect(Collectors.toList()))); + getResolutionResult() + .set(project.provider( + () -> latestVersionsClasspath.getIncoming().getResolutionResult().getAllComponents().stream() + .map(result -> { + ModuleVersionIdentifier moduleVersion = result.getModuleVersion(); + if (moduleVersion != null + && !(result.getId() instanceof ProjectComponentIdentifier)) { + String ga = moduleVersion.getGroup() + ":" + moduleVersion.getName(); + String version = moduleVersion.getVersion(); + Provider moduleName = javaModuleDependencies.moduleName(ga); + if (moduleName.isPresent()) { + return moduleName.get() + ":" + version; + } + } + return null; + }) + .filter(Objects::nonNull) + .collect(Collectors.toList()))); } @TaskAction @@ -159,7 +156,8 @@ public void report() { if (getPrintForPropertiesFile().isPresent()) { p(""); - p("Latest Stable Versions of Java Modules - use in: " + getPrintForPropertiesFile().get().getAsFile()); + p("Latest Stable Versions of Java Modules - use in: " + + getPrintForPropertiesFile().get().getAsFile()); p("================================================================================================="); for (String entry : moduleVersionsPropertiesFile) { p(entry); @@ -172,5 +170,4 @@ public void report() { private void p(String toPrint) { System.out.println(toPrint); } - } diff --git a/src/main/java/org/gradlex/javamodule/dependencies/tasks/SyntheticModuleInfoFoldersGenerate.java b/src/main/java/org/gradlex/javamodule/dependencies/tasks/SyntheticModuleInfoFoldersGenerate.java index 45ce33d0..5df0d8cc 100644 --- a/src/main/java/org/gradlex/javamodule/dependencies/tasks/SyntheticModuleInfoFoldersGenerate.java +++ b/src/main/java/org/gradlex/javamodule/dependencies/tasks/SyntheticModuleInfoFoldersGenerate.java @@ -1,19 +1,4 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies.tasks; import org.gradle.api.DefaultTask; @@ -37,7 +22,8 @@ public abstract class SyntheticModuleInfoFoldersGenerate extends DefaultTask { @TaskAction public void generate() { for (String moduleName : getModuleNames().get()) { - ModuleInfoClassCreator.createEmpty(getSyntheticModuleInfoFolder().get().dir(moduleName).getAsFile()); + ModuleInfoClassCreator.createEmpty( + getSyntheticModuleInfoFolder().get().dir(moduleName).getAsFile()); } } } diff --git a/src/main/java/org/gradlex/javamodule/dependencies/tasks/package-info.java b/src/main/java/org/gradlex/javamodule/dependencies/tasks/package-info.java index e7ce3929..183bea45 100644 --- a/src/main/java/org/gradlex/javamodule/dependencies/tasks/package-info.java +++ b/src/main/java/org/gradlex/javamodule/dependencies/tasks/package-info.java @@ -1,20 +1,5 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 @NullMarked package org.gradlex.javamodule.dependencies.tasks; -import org.jspecify.annotations.NullMarked; \ No newline at end of file +import org.jspecify.annotations.NullMarked; diff --git a/src/test/java/org/gradlex/javamodule/dependencies/test/CustomizationTest.java b/src/test/java/org/gradlex/javamodule/dependencies/test/CustomizationTest.java index cf5cad09..ab463f7d 100644 --- a/src/test/java/org/gradlex/javamodule/dependencies/test/CustomizationTest.java +++ b/src/test/java/org/gradlex/javamodule/dependencies/test/CustomizationTest.java @@ -1,33 +1,19 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies.test; +import static org.assertj.core.api.Assertions.assertThat; + import org.gradlex.javamodule.dependencies.test.fixture.GradleBuild; import org.junit.jupiter.api.Test; -import static org.assertj.core.api.Assertions.assertThat; - class CustomizationTest { GradleBuild build = new GradleBuild(); @Test void can_add_custom_mapping() { - build.appBuildFile.appendText(""" + build.appBuildFile.appendText( + """ javaModuleDependencies { // Override because there are multiple alternatives moduleNameToGA.put("jakarta.mail", "com.sun.mail:jakarta.mail") @@ -37,7 +23,8 @@ void can_add_custom_mapping() { implementation(gav("jakarta.mail", "2.0.1")) } }"""); - build.appModuleInfoFile.appendText(""" + build.appModuleInfoFile.appendText( + """ module org.gradlex.test.app { requires jakarta.mail; }"""); @@ -54,7 +41,8 @@ void can_add_custom_mapping_via_properties_file_in_default_location() { customModulesPropertiesFile.writeText("jakarta.mail=com.sun.mail:jakarta.mail"); build.appBuildFile.appendText("moduleInfo { version(\"jakarta.mail\", \"2.0.1\") }"); - build.appModuleInfoFile.writeText(""" + build.appModuleInfoFile.writeText( + """ module org.gradlex.test.app { requires jakarta.mail; }"""); @@ -68,13 +56,15 @@ void can_add_custom_mapping_via_properties_file_in_custom_location() { var customModulesPropertiesFile = build.file(".hidden/modules.properties"); customModulesPropertiesFile.writeText("jakarta.mail=com.sun.mail:jakarta.mail"); - build.appBuildFile.appendText(""" + build.appBuildFile.appendText( + """ moduleInfo { version("jakarta.mail", "2.0.1") } javaModuleDependencies { modulesProperties.set(File(rootDir,".hidden/modules.properties")) }"""); - build.appModuleInfoFile.writeText(""" + build.appModuleInfoFile.writeText( + """ module org.gradlex.test.app { requires jakarta.mail; }"""); @@ -86,14 +76,16 @@ void can_add_custom_mapping_via_properties_file_in_custom_location() { @Test void can_use_custom_catalog() { - build.settingsFile.appendText(""" + build.settingsFile.appendText( + """ dependencyResolutionManagement.versionCatalogs.create("moduleLibs") { version("org.apache.xmlbeans", "5.0.1") version("com.fasterxml.jackson.databind", "2.12.5") }"""); build.appBuildFile.appendText(""" javaModuleDependencies.versionCatalogName.set("moduleLibs")"""); - build.appModuleInfoFile.writeText(""" + build.appModuleInfoFile.writeText( + """ module org.gradlex.test.app { requires com.fasterxml.jackson.databind; requires static org.apache.xmlbeans; @@ -101,10 +93,13 @@ void can_use_custom_catalog() { """); var runtime = build.printRuntimeJars(); - assertThat(runtime.getOutput()).contains("[jackson-annotations-2.12.5.jar, jackson-core-2.12.5.jar, jackson-databind-2.12.5.jar]"); + assertThat(runtime.getOutput()) + .contains("[jackson-annotations-2.12.5.jar, jackson-core-2.12.5.jar, jackson-databind-2.12.5.jar]"); var compile = build.printCompileJars(); - assertThat(compile.getOutput()).contains("[xmlbeans-5.0.1.jar, jackson-annotations-2.12.5.jar, jackson-core-2.12.5.jar, jackson-databind-2.12.5.jar, log4j-api-2.14.0.jar]"); + assertThat(compile.getOutput()) + .contains( + "[xmlbeans-5.0.1.jar, jackson-annotations-2.12.5.jar, jackson-core-2.12.5.jar, jackson-databind-2.12.5.jar, log4j-api-2.14.0.jar]"); } @Test @@ -112,14 +107,16 @@ void can_define_versions_in_platform_with_different_notations() { var customModulesPropertiesFile = build.file("gradle/modules.properties"); customModulesPropertiesFile.writeText("jakarta.mail=com.sun.mail:jakarta.mail"); - build.appBuildFile.appendText(""" + build.appBuildFile.appendText( + """ moduleInfo { version("jakarta.mail", "2.0.1") version("jakarta.servlet", "6.0.0") { reject("[7.0.0,)") } version("java.inject") { require("1.0.5"); reject("[2.0.0,)") } }"""); - build.appModuleInfoFile.writeText(""" + build.appModuleInfoFile.writeText( + """ module org.gradlex.test.app { requires jakarta.mail; requires jakarta.servlet; @@ -127,16 +124,20 @@ void can_define_versions_in_platform_with_different_notations() { }"""); var result = build.printRuntimeJars(); - assertThat(result.getOutput()).contains("[jakarta.mail-2.0.1.jar, jakarta.servlet-api-6.0.0.jar, jakarta.inject-api-1.0.5.jar, jakarta.activation-2.0.1.jar]"); + assertThat(result.getOutput()) + .contains( + "[jakarta.mail-2.0.1.jar, jakarta.servlet-api-6.0.0.jar, jakarta.inject-api-1.0.5.jar, jakarta.activation-2.0.1.jar]"); } @Test void can_use_toml_catalog_with_underscore_for_dot() { - build.file("gradle/libs.versions.toml").writeText(""" + build.file("gradle/libs.versions.toml") + .writeText(""" [versions] org_apache_xmlbeans = "5.0.1" """); - build.appModuleInfoFile.appendText(""" + build.appModuleInfoFile.appendText( + """ module org.gradlex.test.app { requires org.apache.xmlbeans; }"""); @@ -148,11 +149,13 @@ void can_use_toml_catalog_with_underscore_for_dot() { @Test void can_use_toml_catalog_with_dash_for_dot() { - build.file("gradle/libs.versions.toml").writeText(""" + build.file("gradle/libs.versions.toml") + .writeText(""" [versions] org-apache-xmlbeans = "5.0.1" """); - build.appModuleInfoFile.appendText(""" + build.appModuleInfoFile.appendText( + """ module org.gradlex.test.app { requires org.apache.xmlbeans; }"""); diff --git a/src/test/java/org/gradlex/javamodule/dependencies/test/ModuleInfoParseTest.java b/src/test/java/org/gradlex/javamodule/dependencies/test/ModuleInfoParseTest.java index 9dbab2d9..50c4868f 100644 --- a/src/test/java/org/gradlex/javamodule/dependencies/test/ModuleInfoParseTest.java +++ b/src/test/java/org/gradlex/javamodule/dependencies/test/ModuleInfoParseTest.java @@ -1,36 +1,22 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies.test; -import org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo; -import org.junit.jupiter.api.Test; - import static org.assertj.core.api.Assertions.assertThat; import static org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo.Directive.REQUIRES; import static org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo.Directive.REQUIRES_RUNTIME; -import static org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo.Directive.REQUIRES_TRANSITIVE; import static org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo.Directive.REQUIRES_STATIC; import static org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo.Directive.REQUIRES_STATIC_TRANSITIVE; +import static org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo.Directive.REQUIRES_TRANSITIVE; + +import org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo; +import org.junit.jupiter.api.Test; class ModuleInfoParseTest { @Test void ignores_single_line_comments() { - var moduleInfo = new ModuleInfo(""" + var moduleInfo = new ModuleInfo( + """ module some.thing { // requires com.bla.blub; requires transitive foo.bar.la; @@ -46,7 +32,8 @@ void ignores_single_line_comments() { @Test void ignores_single_line_comments_late_in_line() { - var moduleInfo = new ModuleInfo(""" + var moduleInfo = new ModuleInfo( + """ module some.thing { // module some.thing.else requires transitive foo.bar.la; }"""); @@ -61,7 +48,8 @@ void ignores_single_line_comments_late_in_line() { @Test void ignores_multi_line_comments() { - var moduleInfo = new ModuleInfo(""" + var moduleInfo = new ModuleInfo( + """ module some.thing { /* requires com.bla.blub; requires transitive foo.bar.la; @@ -79,7 +67,8 @@ void ignores_multi_line_comments() { @Test void ignores_multi_line_comments_between_keywords() { - var moduleInfo = new ModuleInfo(""" + var moduleInfo = new ModuleInfo( + """ module some.thing { /*odd comment*/ requires transitive foo.bar.la; requires/* weird comment*/ static foo.bar.lo; @@ -97,7 +86,8 @@ void ignores_multi_line_comments_between_keywords() { @Test void supports_runtime_dependencies_through_special_keyword() { - var moduleInfo = new ModuleInfo(""" + var moduleInfo = new ModuleInfo( + """ module some.thing { requires /*runtime*/ foo.bar.lo; } diff --git a/src/test/java/org/gradlex/javamodule/dependencies/test/WarningsTest.java b/src/test/java/org/gradlex/javamodule/dependencies/test/WarningsTest.java index 52188aa9..88a0a941 100644 --- a/src/test/java/org/gradlex/javamodule/dependencies/test/WarningsTest.java +++ b/src/test/java/org/gradlex/javamodule/dependencies/test/WarningsTest.java @@ -1,40 +1,26 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies.test; +import static org.assertj.core.api.Assertions.assertThat; + import org.gradlex.javamodule.dependencies.test.fixture.GradleBuild; import org.junit.jupiter.api.Test; -import static org.assertj.core.api.Assertions.assertThat; - class WarningsTest { GradleBuild build = new GradleBuild(); @Test void prints_warning_for_missing_mapping() { - build.appModuleInfoFile.writeText(""" + build.appModuleInfoFile.writeText( + """ module org.my.app { requires commons.math3; }"""); var result = build.fail(); - assertThat(result.getOutput()).contains( - "[WARN] [Java Module Dependencies] commons.math3=group:artifact missing in"); + assertThat(result.getOutput()) + .contains("[WARN] [Java Module Dependencies] commons.math3=group:artifact missing in"); } } diff --git a/src/test/java/org/gradlex/javamodule/dependencies/test/configcache/ConfigurationCacheTest.java b/src/test/java/org/gradlex/javamodule/dependencies/test/configcache/ConfigurationCacheTest.java index a0e9d842..f867e1c9 100644 --- a/src/test/java/org/gradlex/javamodule/dependencies/test/configcache/ConfigurationCacheTest.java +++ b/src/test/java/org/gradlex/javamodule/dependencies/test/configcache/ConfigurationCacheTest.java @@ -1,33 +1,21 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies.test.configcache; -import org.gradlex.javamodule.dependencies.test.fixture.GradleBuild; -import org.junit.jupiter.api.Test; - import static org.assertj.core.api.Assertions.assertThat; import static org.gradle.util.GradleVersion.version; import static org.gradlex.javamodule.dependencies.test.fixture.GradleBuild.GRADLE_VERSION_UNDER_TEST; +import org.gradlex.javamodule.dependencies.test.fixture.GradleBuild; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; + +@Tag("no-cross-version") // can be removed once the min version is increased to 7.6.5 class ConfigurationCacheTest { GradleBuild build = new GradleBuild(); - final String noCacheMessage = GRADLE_VERSION_UNDER_TEST == null || version(GRADLE_VERSION_UNDER_TEST).compareTo(version("8.8")) >= 0 + final String noCacheMessage = GRADLE_VERSION_UNDER_TEST == null + || version(GRADLE_VERSION_UNDER_TEST).compareTo(version("8.8")) >= 0 ? "Calculating task graph as no cached configuration is available for tasks: :app:compileJava" : "Calculating task graph as no configuration cache is available for tasks: :app:compileJava"; @@ -35,12 +23,13 @@ class ConfigurationCacheTest { void configurationCacheHit() { build.libModuleInfoFile.writeText("module abc.lib { }"); - build.appModuleInfoFile.writeText(""" + build.appModuleInfoFile.writeText( + """ module abc.app { requires abc.lib; }"""); - var runner = build.runner("--configuration-cache",":app:compileJava"); + var runner = build.runner("--configuration-cache", ":app:compileJava"); var result = runner.build(); assertThat(result.getOutput()).contains(noCacheMessage); @@ -53,17 +42,19 @@ void configurationCacheHit() { @Test void configurationCacheHitIrrelevantChange() { build.libModuleInfoFile.writeText("module abc.lib { }"); - build.appModuleInfoFile.writeText(""" + build.appModuleInfoFile.writeText( + """ module abc.app { requires abc.lib; }"""); - var runner = build.runner("--configuration-cache",":app:compileJava"); + var runner = build.runner("--configuration-cache", ":app:compileJava"); var result = runner.build(); assertThat(result.getOutput()).contains(noCacheMessage); - build.appModuleInfoFile.writeText(""" + build.appModuleInfoFile.writeText( + """ module abc.app { requires abc.lib; //This is a comment and should not break the configurationCache } @@ -76,24 +67,26 @@ void configurationCacheHitIrrelevantChange() { @Test void configurationCacheMissRelevantChange() { build.libModuleInfoFile.writeText("module abc.lib { }"); - build.appModuleInfoFile.writeText(""" + build.appModuleInfoFile.writeText( + """ module abc.app { requires abc.lib; }"""); - var runner = build.runner("--configuration-cache",":app:compileJava"); + var runner = build.runner("--configuration-cache", ":app:compileJava"); var result = runner.build(); assertThat(result.getOutput()).contains(noCacheMessage); - build.appModuleInfoFile.writeText(""" + build.appModuleInfoFile.writeText( + """ module abc.app { //dependency removed; so thats indeed a configuration change }"""); result = runner.build(); - assertThat(result.getOutput()).contains( - "Calculating task graph as configuration cache cannot be reused because a build logic input of type 'ValueSourceModuleInfo' has changed.\n"); + assertThat(result.getOutput()) + .contains( + "Calculating task graph as configuration cache cannot be reused because a build logic input of type 'ValueSourceModuleInfo' has changed.\n"); } - } diff --git a/src/test/java/org/gradlex/javamodule/dependencies/test/extension/ExtensionTest.java b/src/test/java/org/gradlex/javamodule/dependencies/test/extension/ExtensionTest.java index a1f612a7..a82947ca 100644 --- a/src/test/java/org/gradlex/javamodule/dependencies/test/extension/ExtensionTest.java +++ b/src/test/java/org/gradlex/javamodule/dependencies/test/extension/ExtensionTest.java @@ -1,35 +1,21 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies.test.extension; +import static org.assertj.core.api.Assertions.assertThat; + import org.gradlex.javamodule.dependencies.test.fixture.GradleBuild; import org.junit.jupiter.api.Test; -import static org.assertj.core.api.Assertions.assertThat; - class ExtensionTest { GradleBuild build = new GradleBuild(); @Test void can_access_mapping_information_from_extension() { - build.appBuildFile.appendText(""" + build.appBuildFile.appendText( + """ javaModuleDependencies.moduleNamePrefixToGroup.put("org.example.app.", "org.example.gr") - + javaModuleDependencies { println(ga("com.fasterxml.jackson.core").get()) println(ga(provider { "com.fasterxml.jackson.databind" }).get()) @@ -37,7 +23,7 @@ void can_access_mapping_information_from_extension() { println(gav(provider { "com.fasterxml.jackson.databind" }, provider { "1.0" }).get()) println(moduleName("com.fasterxml.jackson.core:jackson-core").get()) println(moduleName(provider { "com.fasterxml.jackson.core:jackson-databind" }).get()) - + println(ga("org.example.app.my.mod1").get()) println(ga(provider { "org.example.app.my.mod2.impl" }).get()) println(gav("org.example.app.my.mod3.impl.int", "1.0").get()) @@ -48,7 +34,9 @@ void can_access_mapping_information_from_extension() { var result = build.build(); - assertThat(result.getOutput()).contains(""" + assertThat(result.getOutput()) + .contains( + """ com.fasterxml.jackson.core:jackson-core com.fasterxml.jackson.core:jackson-databind com.fasterxml.jackson.core:jackson-core:1.0 @@ -62,5 +50,4 @@ void can_access_mapping_information_from_extension() { org.example.app.mod8.ab org.example.app.mod.z7.i9"""); } - } diff --git a/src/test/java/org/gradlex/javamodule/dependencies/test/fixture/Directory.java b/src/test/java/org/gradlex/javamodule/dependencies/test/fixture/Directory.java index d779a3cb..1894c82d 100644 --- a/src/test/java/org/gradlex/javamodule/dependencies/test/fixture/Directory.java +++ b/src/test/java/org/gradlex/javamodule/dependencies/test/fixture/Directory.java @@ -1,19 +1,4 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies.test.fixture; import java.io.IOException; diff --git a/src/test/java/org/gradlex/javamodule/dependencies/test/fixture/GradleBuild.java b/src/test/java/org/gradlex/javamodule/dependencies/test/fixture/GradleBuild.java index ffd1ac4c..81837b3a 100644 --- a/src/test/java/org/gradlex/javamodule/dependencies/test/fixture/GradleBuild.java +++ b/src/test/java/org/gradlex/javamodule/dependencies/test/fixture/GradleBuild.java @@ -1,23 +1,7 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies.test.fixture; -import org.gradle.testkit.runner.BuildResult; -import org.gradle.testkit.runner.GradleRunner; +import static java.util.function.Function.identity; import java.io.IOException; import java.lang.management.ManagementFactory; @@ -27,8 +11,8 @@ import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; - -import static java.util.function.Function.identity; +import org.gradle.testkit.runner.BuildResult; +import org.gradle.testkit.runner.GradleRunner; public class GradleBuild { @@ -60,13 +44,15 @@ public GradleBuild(boolean withHelpTasks, Path dir) { this.appModuleInfoFile = new WritableFile(projectDir.dir("app/src/main/java"), "module-info.java"); this.libModuleInfoFile = new WritableFile(projectDir.dir("lib/src/main/java"), "module-info.java"); - settingsFile.writeText(""" + settingsFile.writeText( + """ dependencyResolutionManagement { repositories.mavenCentral() } rootProject.name = "test-project" include("lib", "app") includeBuild(".") """); - appBuildFile.writeText(""" + appBuildFile.writeText( + """ plugins { id("org.gradlex.java-module-dependencies") id("org.gradlex.java-module-versions") @@ -90,7 +76,8 @@ public GradleBuild(boolean withHelpTasks, Path dir) { doLast { println(inputs.files.map { it.name }) } } """); - libBuildFile.writeText(""" + libBuildFile.writeText( + """ plugins { id("org.gradlex.java-module-dependencies") id("java-library") @@ -114,6 +101,7 @@ public BuildResult run() { public BuildResult printRuntimeJars() { return runner(":app:printRuntimeJars", "-q").build(); } + public BuildResult printCompileJars() { return runner(":app:printCompileJars", "-q").build(); } @@ -127,25 +115,30 @@ public GradleRunner runner(String... args) { } public GradleRunner runner(boolean projectIsolation, String... args) { - boolean debugMode = ManagementFactory.getRuntimeMXBean().getInputArguments().toString().contains("-agentlib:jdwp"); - List latestFeaturesArgs = GRADLE_VERSION_UNDER_TEST != null || !projectIsolation ? List.of() : List.of( - "--configuration-cache", - "-Dorg.gradle.unsafe.isolated-projects=true", - // "getGroup" in "JavaModuleDependenciesExtension.create" - "--configuration-cache-problems=warn", "-Dorg.gradle.configuration-cache.max-problems=3" - ); + boolean debugMode = ManagementFactory.getRuntimeMXBean() + .getInputArguments() + .toString() + .contains("-agentlib:jdwp"); + List latestFeaturesArgs = GRADLE_VERSION_UNDER_TEST != null || !projectIsolation + ? List.of() + : List.of( + "--configuration-cache", + "-Dorg.gradle.unsafe.isolated-projects=true", + // "getGroup" in "JavaModuleDependenciesExtension.create" + "--configuration-cache-problems=warn", + "-Dorg.gradle.configuration-cache.max-problems=3"); Stream standardArgs = Stream.of( "-s", "--warning-mode=fail", - "-Porg.gradlex.java-module-dependencies.register-help-tasks=" + withHelpTasks - ); + "-Porg.gradlex.java-module-dependencies.register-help-tasks=" + withHelpTasks); GradleRunner runner = GradleRunner.create() .forwardOutput() .withPluginClasspath() .withDebug(debugMode) .withProjectDir(projectDir.getAsPath().toFile()) .withArguments(Stream.of(Arrays.stream(args), latestFeaturesArgs.stream(), standardArgs) - .flatMap(identity()).collect(Collectors.toList())); + .flatMap(identity()) + .collect(Collectors.toList())); if (GRADLE_VERSION_UNDER_TEST != null) { runner.withGradleVersion(GRADLE_VERSION_UNDER_TEST); } diff --git a/src/test/java/org/gradlex/javamodule/dependencies/test/fixture/Io.java b/src/test/java/org/gradlex/javamodule/dependencies/test/fixture/Io.java index d0b0f117..1167a1e7 100644 --- a/src/test/java/org/gradlex/javamodule/dependencies/test/fixture/Io.java +++ b/src/test/java/org/gradlex/javamodule/dependencies/test/fixture/Io.java @@ -1,19 +1,4 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies.test.fixture; import java.io.IOException; @@ -21,8 +6,7 @@ class Io { - private Io() { - } + private Io() {} static T unchecked(IoSupplier supplier) { try { diff --git a/src/test/java/org/gradlex/javamodule/dependencies/test/fixture/WritableFile.java b/src/test/java/org/gradlex/javamodule/dependencies/test/fixture/WritableFile.java index f6ac707b..7341502b 100644 --- a/src/test/java/org/gradlex/javamodule/dependencies/test/fixture/WritableFile.java +++ b/src/test/java/org/gradlex/javamodule/dependencies/test/fixture/WritableFile.java @@ -1,19 +1,4 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies.test.fixture; import java.nio.file.Files; @@ -29,12 +14,14 @@ public WritableFile(Path file) { } public WritableFile(Directory parent, String filePath) { - this.file = Io.unchecked(() -> Files.createDirectories(parent.getAsPath().resolve(filePath).getParent())) + this.file = Io.unchecked(() -> Files.createDirectories( + parent.getAsPath().resolve(filePath).getParent())) .resolve(Path.of(filePath).getFileName()); } public WritableFile writeText(String text) { - Io.unchecked(() -> Files.writeString(file, text, StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING)); + Io.unchecked(() -> Files.writeString( + file, text, StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING)); return this; } diff --git a/src/test/java/org/gradlex/javamodule/dependencies/test/groupmapping/GroupMappingTest.java b/src/test/java/org/gradlex/javamodule/dependencies/test/groupmapping/GroupMappingTest.java index fe2b68d4..7cb4b4d6 100644 --- a/src/test/java/org/gradlex/javamodule/dependencies/test/groupmapping/GroupMappingTest.java +++ b/src/test/java/org/gradlex/javamodule/dependencies/test/groupmapping/GroupMappingTest.java @@ -1,27 +1,12 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies.test.groupmapping; -import org.gradlex.javamodule.dependencies.test.fixture.GradleBuild; -import org.junit.jupiter.api.Test; - import static org.assertj.core.api.Assertions.assertThat; import static org.gradle.testkit.runner.TaskOutcome.SUCCESS; +import org.gradlex.javamodule.dependencies.test.fixture.GradleBuild; +import org.junit.jupiter.api.Test; + class GroupMappingTest { GradleBuild build = new GradleBuild(); @@ -37,12 +22,14 @@ void can_map_overlapping_groups() { build.libBuildFile.appendText("group = \"com.foo\""); lib2ModuleInfoFile.appendText("module com.example.lib.b { }"); lib2BuildFile.appendText("group = \"com.example\""); - build.appModuleInfoFile.appendText(""" + build.appModuleInfoFile.appendText( + """ module org.gradlex.test.app { requires com.lib; requires com.example.lib.b; }"""); - build.appBuildFile.appendText(""" + build.appBuildFile.appendText( + """ javaModuleDependencies { moduleNamePrefixToGroup.put("com.", "com.foo") moduleNamePrefixToGroup.put("com.example.", "com.example") @@ -53,5 +40,4 @@ void can_map_overlapping_groups() { assertThat(result).isNotNull(); assertThat(result.getOutcome()).isEqualTo(SUCCESS); } - } diff --git a/src/test/java/org/gradlex/javamodule/dependencies/test/initialization/SettingsPluginIncludeTest.java b/src/test/java/org/gradlex/javamodule/dependencies/test/initialization/SettingsPluginIncludeTest.java index 8753e68a..7e8e773d 100644 --- a/src/test/java/org/gradlex/javamodule/dependencies/test/initialization/SettingsPluginIncludeTest.java +++ b/src/test/java/org/gradlex/javamodule/dependencies/test/initialization/SettingsPluginIncludeTest.java @@ -1,30 +1,15 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies.test.initialization; +import static java.util.Objects.requireNonNull; +import static org.assertj.core.api.Assertions.assertThat; +import static org.gradle.testkit.runner.TaskOutcome.SUCCESS; + import org.gradlex.javamodule.dependencies.test.fixture.GradleBuild; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; -import static java.util.Objects.requireNonNull; -import static org.assertj.core.api.Assertions.assertThat; -import static org.gradle.testkit.runner.TaskOutcome.SUCCESS; - @Tag("no-cross-version") class SettingsPluginIncludeTest { @@ -39,7 +24,8 @@ void setup() { @Test void can_define_included_subprojects_as_modules() { - build.settingsFile.appendText(""" + build.settingsFile.appendText( + """ include(":project:with:custom:path") javaModules { module(project(":project:with:custom:path")) { @@ -53,21 +39,27 @@ void can_define_included_subprojects_as_modules() { }"""); build.file("project/with/custom/path/src/main/java/module-info.java").writeText("module abc.liba { }"); - build.file("project/with/custom/src/main/java/module-info.java").writeText(""" + build.file("project/with/custom/src/main/java/module-info.java") + .writeText(""" module abc.libb { requires abc.liba; }"""); var result = build.runner(":project:with:custom:compileJava").build(); - assertThat(requireNonNull(result.task(":project:with:custom:path:compileJava")).getOutcome()).isEqualTo(SUCCESS); - assertThat(requireNonNull(result.task(":project:with:custom:compileJava")).getOutcome()).isEqualTo(SUCCESS); + assertThat(requireNonNull(result.task(":project:with:custom:path:compileJava")) + .getOutcome()) + .isEqualTo(SUCCESS); + assertThat(requireNonNull(result.task(":project:with:custom:compileJava")) + .getOutcome()) + .isEqualTo(SUCCESS); } @Test void can_define_included_subprojects_with_custom_project_directory_as_modules() { build.projectDir.dir("project/with/custom/path"); - build.settingsFile.appendText(""" + build.settingsFile.appendText( + """ include(":project:with:custom:path") project(":project:with:custom:path").projectDir = file("lib") project(":project:with:custom").projectDir = file("app") @@ -83,25 +75,31 @@ void can_define_included_subprojects_with_custom_project_directory_as_modules() }"""); build.libModuleInfoFile.writeText("module abc.lib { }"); - build.appModuleInfoFile.writeText(""" + build.appModuleInfoFile.writeText( + """ module abc.app { requires abc.lib; }"""); var result = build.runner(":project:with:custom:jar").build(); - assertThat(requireNonNull(result.task(":project:with:custom:path:compileJava")).getOutcome()).isEqualTo(SUCCESS); - assertThat(requireNonNull(result.task(":project:with:custom:compileJava")).getOutcome()).isEqualTo(SUCCESS); + assertThat(requireNonNull(result.task(":project:with:custom:path:compileJava")) + .getOutcome()) + .isEqualTo(SUCCESS); + assertThat(requireNonNull(result.task(":project:with:custom:compileJava")) + .getOutcome()) + .isEqualTo(SUCCESS); assertThat(build.file("lib/build/libs/path.jar").getAsPath()).exists(); assertThat(build.file("app/build/libs/custom.jar").getAsPath()).exists(); } @Test void projects_with_same_name_but_different_paths_are_supported() { - build.settingsFile.appendText(""" + build.settingsFile.appendText( + """ include(":app1:feature1:data") include(":app1:feature2:data") - + rootProject.children.forEach { appContainer -> appContainer.children.forEach { featureContainer -> featureContainer.children.forEach { module -> @@ -111,16 +109,21 @@ void projects_with_same_name_but_different_paths_are_supported() { }"""); build.file("app1/feature1/data/src/main/java/module-info.java").writeText("module f1x.data { }"); - build.file("app1/feature2/data/src/main/java/module-info.java").writeText(""" + build.file("app1/feature2/data/src/main/java/module-info.java") + .writeText(""" module f2x.data { requires f1x.data; }"""); var result = build.runner(":app1:feature2:data:jar").build(); - assertThat(requireNonNull(result.task(":app1:feature1:data:jar")).getOutcome()).isEqualTo(SUCCESS); - assertThat(requireNonNull(result.task(":app1:feature2:data:jar")).getOutcome()).isEqualTo(SUCCESS); - assertThat(build.file("app1/feature1/data/build/libs/data.jar").getAsPath()).exists(); - assertThat(build.file("app1/feature2/data/build/libs/data.jar").getAsPath()).exists(); + assertThat(requireNonNull(result.task(":app1:feature1:data:jar")).getOutcome()) + .isEqualTo(SUCCESS); + assertThat(requireNonNull(result.task(":app1:feature2:data:jar")).getOutcome()) + .isEqualTo(SUCCESS); + assertThat(build.file("app1/feature1/data/build/libs/data.jar").getAsPath()) + .exists(); + assertThat(build.file("app1/feature2/data/build/libs/data.jar").getAsPath()) + .exists(); } } diff --git a/src/test/java/org/gradlex/javamodule/dependencies/test/initialization/SettingsPluginTest.java b/src/test/java/org/gradlex/javamodule/dependencies/test/initialization/SettingsPluginTest.java index 55e2a035..3db5f602 100644 --- a/src/test/java/org/gradlex/javamodule/dependencies/test/initialization/SettingsPluginTest.java +++ b/src/test/java/org/gradlex/javamodule/dependencies/test/initialization/SettingsPluginTest.java @@ -1,31 +1,16 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies.test.initialization; -import org.gradlex.javamodule.dependencies.test.fixture.GradleBuild; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Tag; -import org.junit.jupiter.api.Test; - import static java.util.Objects.requireNonNull; import static org.assertj.core.api.Assertions.assertThat; import static org.gradle.testkit.runner.TaskOutcome.NO_SOURCE; import static org.gradle.testkit.runner.TaskOutcome.SUCCESS; +import org.gradlex.javamodule.dependencies.test.fixture.GradleBuild; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; + @Tag("no-cross-version") class SettingsPluginTest { @@ -40,7 +25,8 @@ void setup() { @Test void can_define_individual_modules() { - build.settingsFile.appendText(""" + build.settingsFile.appendText( + """ javaModules { module("app") { plugin("application") } module("lib") { @@ -49,7 +35,8 @@ void can_define_individual_modules() { } }"""); build.libModuleInfoFile.writeText("module abc.lib { }"); - build.appModuleInfoFile.writeText(""" + build.appModuleInfoFile.writeText( + """ module org.gradlex.test.app { requires abc.lib; }"""); @@ -57,17 +44,20 @@ void can_define_individual_modules() { var result = build.runner(":app:compileJava").build(); assertThat(requireNonNull(result.task(":app:compileJava")).getOutcome()).isEqualTo(SUCCESS); - assertThat(requireNonNull(result.task(":lib-x:compileJava")).getOutcome()).isEqualTo(SUCCESS); + assertThat(requireNonNull(result.task(":lib-x:compileJava")).getOutcome()) + .isEqualTo(SUCCESS); } @Test void finds_all_modules_in_a_directory() { - build.settingsFile.appendText(""" + build.settingsFile.appendText( + """ javaModules { directory(".") { plugin("java-library") } }"""); build.libModuleInfoFile.writeText("module abc.lib { }"); - build.appModuleInfoFile.writeText(""" + build.appModuleInfoFile.writeText( + """ module org.gradlex.test.app { requires abc.lib; }"""); @@ -80,21 +70,23 @@ void finds_all_modules_in_a_directory() { @Test void configurationCacheHit() { - build.settingsFile.appendText(""" + build.settingsFile.appendText( + """ javaModules { directory(".") { plugin("java-library") } }"""); build.libModuleInfoFile.writeText("module abc.lib { }"); - build.appModuleInfoFile.writeText(""" + build.appModuleInfoFile.writeText( + """ module org.gradlex.test.app { requires abc.lib; }"""); - var runner = build.runner(":app:compileJava"); var result = runner.build(); - assertThat(result.getOutput()).contains("Calculating task graph as no cached configuration is available for tasks: :app:compileJava"); + assertThat(result.getOutput()) + .contains("Calculating task graph as no cached configuration is available for tasks: :app:compileJava"); result = runner.build(); @@ -103,12 +95,14 @@ void configurationCacheHit() { @Test void configurationCacheHitExtraDir() { - build.settingsFile.appendText(""" + build.settingsFile.appendText( + """ javaModules { directory(".") { plugin("java-library") } }"""); build.libModuleInfoFile.writeText("module abc.lib { }"); - build.appModuleInfoFile.writeText(""" + build.appModuleInfoFile.writeText( + """ module org.gradlex.test.app { requires abc.lib; }"""); @@ -116,7 +110,8 @@ void configurationCacheHitExtraDir() { var runner = build.runner(":app:compileJava"); var result = runner.build(); - assertThat(result.getOutput()).contains("Calculating task graph as no cached configuration is available for tasks: :app:compileJava"); + assertThat(result.getOutput()) + .contains("Calculating task graph as no cached configuration is available for tasks: :app:compileJava"); build.projectDir.dir(".thisShallBeIgnored"); result = runner.build(); @@ -126,12 +121,14 @@ void configurationCacheHitExtraDir() { @Test void configurationCacheHitExtraNotIgnored() { - build.settingsFile.appendText(""" + build.settingsFile.appendText( + """ javaModules { directory(".") { plugin("java-library") } }"""); build.libModuleInfoFile.writeText("module abc.lib { }"); - build.appModuleInfoFile.writeText(""" + build.appModuleInfoFile.writeText( + """ module org.gradlex.test.app { requires abc.lib; }"""); @@ -139,22 +136,27 @@ void configurationCacheHitExtraNotIgnored() { var runner = build.runner(":app:compileJava"); var result = runner.build(); - assertThat(result.getOutput()).contains("Calculating task graph as no cached configuration is available for tasks: :app:compileJava"); + assertThat(result.getOutput()) + .contains("Calculating task graph as no cached configuration is available for tasks: :app:compileJava"); build.projectDir.dir("thisShallNotBeIgnored"); result = runner.build(); - assertThat(result.getOutput()).contains("Calculating task graph as configuration cache cannot be reused because a build logic input of type 'ValueModuleDirectoryListing' has changed."); + assertThat(result.getOutput()) + .contains( + "Calculating task graph as configuration cache cannot be reused because a build logic input of type 'ValueModuleDirectoryListing' has changed."); } @Test void configurationCacheHitIrrelevantChange() { - build.settingsFile.appendText(""" + build.settingsFile.appendText( + """ javaModules { directory(".") { plugin("java-library") } }"""); build.libModuleInfoFile.writeText("module abc.lib { }"); - build.appModuleInfoFile.writeText(""" + build.appModuleInfoFile.writeText( + """ module org.gradlex.test.app { requires abc.lib; }"""); @@ -162,9 +164,11 @@ void configurationCacheHitIrrelevantChange() { var runner = build.runner(":app:compileJava"); var result = runner.build(); - assertThat(result.getOutput()).contains("Calculating task graph as no cached configuration is available for tasks: :app:compileJava"); + assertThat(result.getOutput()) + .contains("Calculating task graph as no cached configuration is available for tasks: :app:compileJava"); - build.appModuleInfoFile.writeText(""" + build.appModuleInfoFile.writeText( + """ module org.gradlex.test.app { requires abc.lib; //This is a comment and should not break the configurationCache }"""); @@ -175,12 +179,14 @@ void configurationCacheHitIrrelevantChange() { @Test void configurationCacheMissRelevantChange() { - build.settingsFile.appendText(""" + build.settingsFile.appendText( + """ javaModules { directory(".") { plugin("java-library") } }"""); build.libModuleInfoFile.writeText("module abc.lib { }"); - build.appModuleInfoFile.writeText(""" + build.appModuleInfoFile.writeText( + """ module org.gradlex.test.app { requires abc.lib; }"""); @@ -188,21 +194,26 @@ void configurationCacheMissRelevantChange() { var runner = build.runner(":app:compileJava"); var result = runner.build(); - assertThat(result.getOutput()).contains("Calculating task graph as no cached configuration is available for tasks: :app:compileJava"); + assertThat(result.getOutput()) + .contains("Calculating task graph as no cached configuration is available for tasks: :app:compileJava"); - build.appModuleInfoFile.writeText(""" + build.appModuleInfoFile.writeText( + """ module org.gradlex.test.app { //dependency removed; so thats indeed a configuration change } """); result = runner.build(); - assertThat(result.getOutput()).contains("Calculating task graph as configuration cache cannot be reused because a build logic input of type 'ValueSourceModuleInfo' has changed.\n"); + assertThat(result.getOutput()) + .contains( + "Calculating task graph as configuration cache cannot be reused because a build logic input of type 'ValueSourceModuleInfo' has changed.\n"); } @Test void automatically_sets_module_for_application_plugin() { - build.settingsFile.appendText(""" + build.settingsFile.appendText( + """ javaModules { directory(".") { plugin("java-library") @@ -210,12 +221,14 @@ void automatically_sets_module_for_application_plugin() { } }"""); build.libModuleInfoFile.writeText("module abc.libxyz { }"); - build.appModuleInfoFile.writeText(""" + build.appModuleInfoFile.writeText( + """ module org.gradlex.test.app { requires abc.libxyz; }"""); build.appBuildFile.appendText("application.mainClass = \"app.App\""); - build.file("app/src/main/java/app/App.java").writeText("package app; public class App { public static void main(String[] args) { } }"); + build.file("app/src/main/java/app/App.java") + .writeText("package app; public class App { public static void main(String[] args) { } }"); var result = build.runner(":app:run").build(); @@ -226,7 +239,8 @@ void automatically_sets_module_for_application_plugin() { @Test void can_depend_on_test_fixtures_module() { - build.settingsFile.appendText(""" + build.settingsFile.appendText( + """ javaModules { directory(".") { group = "bar.foo" @@ -236,7 +250,8 @@ void can_depend_on_test_fixtures_module() { }"""); build.libModuleInfoFile.writeText("module foo.bar.m { }"); build.file("lib/src/testFixtures/java/module-info.java").writeText("module abc.libxyz.dsdsds { }"); - build.appModuleInfoFile.writeText(""" + build.appModuleInfoFile.writeText( + """ module org.gradlex.test.app { requires foo.bar.m; requires abc.libxyz.dsdsds; @@ -246,7 +261,8 @@ void can_depend_on_test_fixtures_module() { assertThat(requireNonNull(result.task(":app:compileJava")).getOutcome()).isEqualTo(SUCCESS); assertThat(requireNonNull(result.task(":lib:compileJava")).getOutcome()).isEqualTo(SUCCESS); - assertThat(requireNonNull(result.task(":lib:compileTestFixturesJava")).getOutcome()).isEqualTo(SUCCESS); + assertThat(requireNonNull(result.task(":lib:compileTestFixturesJava")).getOutcome()) + .isEqualTo(SUCCESS); } @Test @@ -263,7 +279,8 @@ void can_apply_root_project_plugin_from_settings() { @Test void can_have_moduleinfo_in_custom_location() { - build.settingsFile.appendText(""" + build.settingsFile.appendText( + """ javaModules { module("app") { plugin("application") } module("lib") { @@ -273,7 +290,8 @@ void can_have_moduleinfo_in_custom_location() { }"""); build.libBuildFile.appendText("sourceSets.main { java.setSrcDirs(listOf(\"src\")) }"); build.file("lib/src/module-info.java").writeText("module abc.lib { }"); - build.appModuleInfoFile.writeText(""" + build.appModuleInfoFile.writeText( + """ module org.gradlex.test.app { requires abc.lib; }"""); @@ -286,7 +304,8 @@ void can_have_moduleinfo_in_custom_location() { @Test void can_access_local_module_information_in_project() { - build.settingsFile.appendText(""" + build.settingsFile.appendText( + """ javaModules { directory(".") { group = "org.example" @@ -295,12 +314,15 @@ void can_access_local_module_information_in_project() { }"""); build.libModuleInfoFile.writeText("module abc.lib { }"); build.file("lib/src/testFixtures/java/module-info.java").writeText("module abc.lib.test.fixtures { }"); - build.appModuleInfoFile.writeText(""" + build.appModuleInfoFile.writeText( + """ module org.gradlex.test.app { requires abc.lib; }"""); - build.file("aggregation/build.gradle.kts").writeText(""" + build.file("aggregation/build.gradle.kts") + .writeText( + """ plugins { id("org.gradlex.java-module-dependencies") } tasks.register("info") { val info = javaModuleDependencies.allLocalModules().joinToString("\\n") @@ -309,13 +331,14 @@ void can_access_local_module_information_in_project() { var result = build.runner(":aggregation:info").build(); - assertThat(result.getOutput()).contains(""" + assertThat(result.getOutput()) + .contains( + """ > Task :aggregation:info [moduleName='abc.lib', projectPath=':lib', capability='null'] [moduleName='abc.lib.test.fixtures', projectPath=':lib', capability='org.example:lib-test-fixtures'] [moduleName='org.gradlex.test.app', projectPath=':app', capability='null'] - + """); } - } diff --git a/src/test/java/org/gradlex/javamodule/dependencies/test/initialization/SettingsPluginVersionManagementTest.java b/src/test/java/org/gradlex/javamodule/dependencies/test/initialization/SettingsPluginVersionManagementTest.java index e50e6aea..52d51bcd 100644 --- a/src/test/java/org/gradlex/javamodule/dependencies/test/initialization/SettingsPluginVersionManagementTest.java +++ b/src/test/java/org/gradlex/javamodule/dependencies/test/initialization/SettingsPluginVersionManagementTest.java @@ -1,31 +1,16 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies.test.initialization; -import org.gradlex.javamodule.dependencies.test.fixture.GradleBuild; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Tag; -import org.junit.jupiter.api.Test; - import static java.util.Objects.requireNonNull; import static org.assertj.core.api.Assertions.assertThat; import static org.gradle.testkit.runner.TaskOutcome.SUCCESS; import static org.gradle.testkit.runner.TaskOutcome.UP_TO_DATE; +import org.gradlex.javamodule.dependencies.test.fixture.GradleBuild; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; + @Tag("no-cross-version") class SettingsPluginVersionManagementTest { @@ -33,10 +18,12 @@ class SettingsPluginVersionManagementTest { @BeforeEach void setup() { - var buildFile = """ + var buildFile = + """ plugins { id("java-library") } dependencies { implementation(platform(project(":versions"))) }"""; - build.settingsFile.writeText(""" + build.settingsFile.writeText( + """ plugins { id("org.gradlex.java-module-dependencies") } dependencyResolutionManagement { repositories.mavenCentral() } """); @@ -46,18 +33,22 @@ void setup() { @Test void can_define_a_version_providing_project_in_settings() { - build.settingsFile.appendText(""" + build.settingsFile.appendText( + """ javaModules { directory(".") versions("gradle/versions") }"""); build.libModuleInfoFile.writeText("module abc.lib { }"); - build.appModuleInfoFile.writeText(""" + build.appModuleInfoFile.writeText( + """ module org.gradlex.test.app { requires abc.lib; requires java.inject; }"""); - build.file("gradle/versions/build.gradle.kts").writeText(""" + build.file("gradle/versions/build.gradle.kts") + .writeText( + """ moduleInfo { version("java.inject", "1.0.5") }"""); @@ -69,25 +60,29 @@ void can_define_a_version_providing_project_in_settings() { @Test void can_define_a_version_providing_project_in_settings_with_additional_plugin() { - build.settingsFile.appendText(""" + build.settingsFile.appendText( + """ javaModules { directory(".") versions("gradle/versions") { plugin("maven-publish") } }"""); build.libModuleInfoFile.writeText("module abc.lib { }"); - build.appModuleInfoFile.writeText(""" + build.appModuleInfoFile.writeText( + """ module org.gradlex.test.app { requires abc.lib; requires java.inject; }"""); - build.file("gradle/versions/build.gradle.kts").writeText(""" + build.file("gradle/versions/build.gradle.kts") + .writeText( + """ moduleInfo { version("java.inject", "1.0.5") }"""); var result = build.runner(":versions:publish").build(); - assertThat(requireNonNull(result.task(":versions:publish")).getOutcome()).isEqualTo(UP_TO_DATE); + assertThat(requireNonNull(result.task(":versions:publish")).getOutcome()) + .isEqualTo(UP_TO_DATE); } - } diff --git a/src/test/java/org/gradlex/javamodule/dependencies/test/localmodules/LocalModuleMappingsTest.java b/src/test/java/org/gradlex/javamodule/dependencies/test/localmodules/LocalModuleMappingsTest.java index 242f4feb..7fd733a0 100644 --- a/src/test/java/org/gradlex/javamodule/dependencies/test/localmodules/LocalModuleMappingsTest.java +++ b/src/test/java/org/gradlex/javamodule/dependencies/test/localmodules/LocalModuleMappingsTest.java @@ -1,19 +1,4 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies.test.localmodules; import org.gradlex.javamodule.dependencies.test.fixture.GradleBuild; @@ -26,7 +11,8 @@ class LocalModuleMappingsTest { @Test void automatically_maps_local_modules_if_name_prefix_matches() { build.libModuleInfoFile.writeText("module org.gradlex.test.lib { }"); - build.appModuleInfoFile.writeText(""" + build.appModuleInfoFile.writeText( + """ module org.gradlex.test.app { requires org.gradlex.test.lib; }"""); @@ -47,7 +33,8 @@ void automatically_maps_local_modules_if_name_matches() { @Test void a_prefix_to_group_mapping_can_be_used() { - build.appBuildFile.appendText(""" + build.appBuildFile.appendText( + """ javaModuleDependencies.moduleNamePrefixToGroup.put("abc.", "foo.gr") """); build.libBuildFile.appendText(""" @@ -55,7 +42,8 @@ void a_prefix_to_group_mapping_can_be_used() { """); build.libModuleInfoFile.writeText("module abc.lib { }"); - build.appModuleInfoFile.writeText(""" + build.appModuleInfoFile.writeText( + """ module org.gradlex.test.app { requires abc.lib; }"""); @@ -65,23 +53,28 @@ void a_prefix_to_group_mapping_can_be_used() { @Test void does_not_fail_if_there_are_two_project_with_same_name_but_different_path() { - build.libModuleInfoFile.writeText(""" + build.libModuleInfoFile.writeText( + """ module org.gradlex.test.lib { requires org.gradlex.test.anotherlib; } """); build.settingsFile.appendText("include(\"another:lib\")"); - build.file("another/lib/build.gradle.kts").writeText(""" + build.file("another/lib/build.gradle.kts") + .writeText( + """ plugins { id("org.gradlex.java-module-dependencies") id("java-library") } group = "another" """); - build.file("another/lib/src/main/java/module-info.java").writeText(""" + build.file("another/lib/src/main/java/module-info.java") + .writeText(""" module org.gradlex.test.anotherlib { } """); - build.file("gradle/modules.properties").writeText(""" + build.file("gradle/modules.properties") + .writeText(""" org.gradlex.test.anotherlib=another:lib """); diff --git a/src/test/java/org/gradlex/javamodule/dependencies/test/runtime/RequiresRuntimeTest.java b/src/test/java/org/gradlex/javamodule/dependencies/test/runtime/RequiresRuntimeTest.java index 28178843..ef646e3b 100644 --- a/src/test/java/org/gradlex/javamodule/dependencies/test/runtime/RequiresRuntimeTest.java +++ b/src/test/java/org/gradlex/javamodule/dependencies/test/runtime/RequiresRuntimeTest.java @@ -1,41 +1,28 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies.test.runtime; -import org.gradlex.javamodule.dependencies.test.fixture.GradleBuild; -import org.junit.jupiter.api.Test; - import static org.assertj.core.api.Assertions.assertThat; import static org.gradle.testkit.runner.TaskOutcome.SUCCESS; +import org.gradlex.javamodule.dependencies.test.fixture.GradleBuild; +import org.junit.jupiter.api.Test; + class RequiresRuntimeTest { GradleBuild build = new GradleBuild(); @Test void can_define_runtime_only_dependencies_in_moduleinfo() { - build.appBuildFile.appendText(""" + build.appBuildFile.appendText( + """ dependencies.constraints { javaModuleDependencies { implementation(gav("org.slf4j", "2.0.3")) implementation(gav("org.slf4j.simple", "2.0.3")) } }"""); - build.appModuleInfoFile.appendText(""" + build.appModuleInfoFile.appendText( + """ module org.gradlex.test.app { requires org.slf4j; requires /*runtime*/ org.slf4j.simple; @@ -47,34 +34,41 @@ void can_define_runtime_only_dependencies_in_moduleinfo() { var cp = build.printCompileJars(); - assertThat(cp.getOutput()).contains("[org.slf4j.simple, slf4j-api-2.0.3.jar]"); // 'org.slf4j.simple' is the folder nam of synthetic 'module-info.class' + assertThat(cp.getOutput()) + .contains( + "[org.slf4j.simple, slf4j-api-2.0.3.jar]"); // 'org.slf4j.simple' is the folder nam of synthetic + // 'module-info.class' } @Test void compiles_with_runtime_only_dependencies_in_moduleinfo() { - build.appBuildFile.appendText(""" + build.appBuildFile.appendText( + """ dependencies.constraints { javaModuleDependencies { implementation(gav("org.slf4j", "2.0.3")) implementation(gav("org.slf4j.simple", "2.0.3")) } }"""); - build.appModuleInfoFile.writeText(""" + build.appModuleInfoFile.writeText( + """ module org.gradlex.test.app { requires org.slf4j; requires /*runtime*/ org.slf4j.simple; - + exports org.gradlex.test.app; }"""); - build.file("app/src/main/java/org/gradlex/test/app/Main.java").writeText(""" + build.file("app/src/main/java/org/gradlex/test/app/Main.java") + .writeText( + """ package org.gradlex.test.app; - + import org.slf4j.Logger; import org.slf4j.LoggerFactory; - + public class Main { private static final Logger LOGGER = LoggerFactory.getLogger(Main.class); - + public static void main(String[] args) { LOGGER.info("Running application..."); } @@ -87,28 +81,32 @@ public static void main(String[] args) { @Test void runtime_only_dependencies_are_not_visible_at_compile_time() { - build.appBuildFile.appendText(""" + build.appBuildFile.appendText( + """ dependencies.constraints { javaModuleDependencies { implementation(gav("org.slf4j", "2.0.3")) } } """); - build.appModuleInfoFile.writeText(""" + build.appModuleInfoFile.writeText( + """ module org.gradlex.test.app { requires /*runtime*/ org.slf4j; - + exports org.gradlex.test.app; }"""); - build.file("app/src/main/java/org/gradlex/test/app/Main.java").writeText(""" + build.file("app/src/main/java/org/gradlex/test/app/Main.java") + .writeText( + """ package org.gradlex.test.app; - + import org.slf4j.Logger; import org.slf4j.LoggerFactory; - + public class Main { private static final Logger LOGGER = LoggerFactory.getLogger(Main.class); - + public static void main(String[] args) { LOGGER.info("Running application..."); } @@ -121,7 +119,8 @@ public static void main(String[] args) { @Test void generates_javadoc_with_runtime_only_dependencies_in_moduleinfo() { - build.appBuildFile.appendText(""" + build.appBuildFile.appendText( + """ java.withJavadocJar() dependencies.constraints { javaModuleDependencies { @@ -129,16 +128,18 @@ void generates_javadoc_with_runtime_only_dependencies_in_moduleinfo() { implementation(gav("org.slf4j.simple", "2.0.3")) } }"""); - build.appModuleInfoFile.writeText(""" + build.appModuleInfoFile.writeText( + """ module org.gradlex.test.app { requires org.slf4j; requires /*runtime*/ org.slf4j.simple; - + exports org.gradlex.test.app; }"""); - build.file("app/src/main/java/org/gradlex/test/app/Main.java").writeText(""" + build.file("app/src/main/java/org/gradlex/test/app/Main.java") + .writeText(""" package org.gradlex.test.app; - + public class Main {}"""); var result = build.build().task(":app:javadoc"); @@ -150,11 +151,12 @@ public class Main {}"""); @Test void can_configure_additional_compile_tasks_to_work_with_runtime_only_dependencies() { // This is typically needed for whitebox testing - build.appBuildFile.appendText(""" + build.appBuildFile.appendText( + """ javaModuleDependencies.addRequiresRuntimeSupport(sourceSets.main.get(), sourceSets.test.get()) tasks.compileTestJava { classpath += sourceSets.main.get().output - + val srcDir = sourceSets.test.get().java.sourceDirectories.first() options.compilerArgumentProviders.add { listOf( @@ -170,7 +172,7 @@ void can_configure_additional_compile_tasks_to_work_with_runtime_only_dependenci } } tasks.test { useJUnitPlatform() } - + dependencies.constraints { javaModuleDependencies { implementation(gav("org.slf4j", "2.0.3")) @@ -185,14 +187,17 @@ void can_configure_additional_compile_tasks_to_work_with_runtime_only_dependenci } } """); - build.appModuleInfoFile.writeText(""" + build.appModuleInfoFile.writeText( + """ module org.gradlex.test.app { requires org.slf4j; requires /*runtime*/ org.slf4j.simple; }"""); - build.file("app/src/test/java/org/gradlex/test/app/MainTest.java").writeText(""" + build.file("app/src/test/java/org/gradlex/test/app/MainTest.java") + .writeText( + """ package org.gradlex.test.app; - + public class MainTest { @org.junit.jupiter.api.Test void test() {} diff --git a/src/test/java/org/gradlex/javamodule/dependencies/test/tasks/BasicFunctionalityTest.java b/src/test/java/org/gradlex/javamodule/dependencies/test/tasks/BasicFunctionalityTest.java index 788ded33..9ab0cd22 100644 --- a/src/test/java/org/gradlex/javamodule/dependencies/test/tasks/BasicFunctionalityTest.java +++ b/src/test/java/org/gradlex/javamodule/dependencies/test/tasks/BasicFunctionalityTest.java @@ -1,26 +1,11 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies.test.tasks; +import static org.assertj.core.api.Assertions.assertThat; + import org.gradlex.javamodule.dependencies.test.fixture.GradleBuild; import org.junit.jupiter.api.Test; -import static org.assertj.core.api.Assertions.assertThat; - class BasicFunctionalityTest { GradleBuild build = new GradleBuild(true); @@ -28,7 +13,8 @@ class BasicFunctionalityTest { @Test void can_configure_all_tasks_in_a_build_without_error() { build.libModuleInfoFile.writeText("module abc.lib { }"); - build.appModuleInfoFile.writeText(""" + build.appModuleInfoFile.writeText( + """ module org.gradlex.test.app { requires abc.lib; } @@ -36,7 +22,9 @@ void can_configure_all_tasks_in_a_build_without_error() { var result = build.runner("tasks").build(); - assertThat(result.getOutput()).contains(""" + assertThat(result.getOutput()) + .contains( + """ Java modules tasks ------------------ checkModuleInfo - Check order of directives in 'module-info.java' in 'main' source set @@ -47,8 +35,6 @@ void can_configure_all_tasks_in_a_build_without_error() { generateCatalog - Generate 'libs.versions.toml' file generateModuleInfoFile - Generate 'module-info.java' in 'main' source set generateTestFixturesModuleInfoFile - Generate 'module-info.java' in 'testFixtures' source set - generateTestModuleInfoFile - Generate 'module-info.java' in 'test' source set""" - ); + generateTestModuleInfoFile - Generate 'module-info.java' in 'test' source set"""); } - } diff --git a/src/test/java/org/gradlex/javamodule/dependencies/test/tasks/OrderingCheckTest.java b/src/test/java/org/gradlex/javamodule/dependencies/test/tasks/OrderingCheckTest.java index 63191a1c..2541f589 100644 --- a/src/test/java/org/gradlex/javamodule/dependencies/test/tasks/OrderingCheckTest.java +++ b/src/test/java/org/gradlex/javamodule/dependencies/test/tasks/OrderingCheckTest.java @@ -1,40 +1,24 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies.test.tasks; +import static org.assertj.core.api.Assertions.assertThat; + import org.gradlex.javamodule.dependencies.test.fixture.GradleBuild; import org.junit.jupiter.api.Test; -import java.io.IOException; - -import static org.assertj.core.api.Assertions.assertThat; - class OrderingCheckTest { GradleBuild build = new GradleBuild(true); @Test void order_is_expected_to_be_alphabetic_for_each_scope_individually() { - build.appModuleInfoFile.writeText(""" + build.appModuleInfoFile.writeText( + """ module org.example.app { requires a.b.c requires b.f.g requires b.z.u - + requires static c.w.q requires static c.z.u }"""); @@ -44,37 +28,42 @@ void order_is_expected_to_be_alphabetic_for_each_scope_individually() { @Test void if_order_is_not_alphabetic_for_a_scope_an_advice_is_given() { - build.appModuleInfoFile.writeText(""" + build.appModuleInfoFile.writeText( + """ module org.example.app { requires a.b.c requires b.z.u requires b.f.g - + requires static c.w.q requires static c.z.u }"""); var result = build.runner(":app:checkAllModuleInfo").buildAndFail(); - assertThat(result.getOutput()).contains(""" + assertThat(result.getOutput()) + .contains( + """ > %s/app/src/main/java/module-info.java \s 'requires' are not declared in alphabetical order. Please use this order: requires a.b.c; requires b.f.g; - requires b.z.u;""".formatted(build.projectDir.canonicalPath())); + requires b.z.u;""" + .formatted(build.projectDir.canonicalPath())); } @Test void own_modules_are_expected_to_go_first() { - build.appModuleInfoFile.writeText(""" + build.appModuleInfoFile.writeText( + """ module org.example.app { requires org.example.h requires org.example.j - + requires a.b.c requires b.f.g requires z.z.u - + requires static org.example.z requires static c.w.q requires static c.z.u @@ -82,5 +71,4 @@ void own_modules_are_expected_to_go_first() { build.runner(":app:checkAllModuleInfo").build(); } - -} \ No newline at end of file +} diff --git a/src/test/java/org/gradlex/javamodule/dependencies/test/variants/NonMainVariantsTest.java b/src/test/java/org/gradlex/javamodule/dependencies/test/variants/NonMainVariantsTest.java index 2c68715a..182f4792 100644 --- a/src/test/java/org/gradlex/javamodule/dependencies/test/variants/NonMainVariantsTest.java +++ b/src/test/java/org/gradlex/javamodule/dependencies/test/variants/NonMainVariantsTest.java @@ -1,40 +1,27 @@ -/* - * Copyright the GradleX team. - * - * 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. - */ - +// SPDX-License-Identifier: Apache-2.0 package org.gradlex.javamodule.dependencies.test.variants; +import static org.assertj.core.api.Assertions.assertThat; + import org.gradlex.javamodule.dependencies.test.fixture.GradleBuild; import org.junit.jupiter.api.Test; -import static org.assertj.core.api.Assertions.assertThat; - class NonMainVariantsTest { GradleBuild build = new GradleBuild(); @Test void finds_test_fixtures_module() { - build.appModuleInfoFile.writeText(""" + build.appModuleInfoFile.writeText( + """ module org.gradlex.test.app { requires org.gradlex.test.lib.test.fixtures; }"""); build.libModuleInfoFile.writeText(""" module org.gradlex.test.lib { }"""); - build.file("lib/src/testFixtures/java/module-info.java").writeText(""" + build.file("lib/src/testFixtures/java/module-info.java") + .writeText(""" module org.gradlex.test.lib.test.fixtures { }"""); @@ -45,20 +32,23 @@ void finds_test_fixtures_module() { @Test void finds_feature_variant_module() { - build.libBuildFile.appendText(""" + build.libBuildFile.appendText( + """ val extraFeature = sourceSets.create("extraFeature") java.registerFeature(extraFeature.name) { usingSourceSet(extraFeature) }"""); - - build.appModuleInfoFile.writeText(""" + + build.appModuleInfoFile.writeText( + """ module org.gradlex.test.app { requires org.gradlex.test.lib.extra.feature; }"""); build.libModuleInfoFile.writeText(""" module org.gradlex.test.lib { }"""); - build.file("lib/src/extraFeature/java/module-info.java").appendText(""" + build.file("lib/src/extraFeature/java/module-info.java") + .appendText(""" module org.gradlex.test.lib.extra.feature { }"""); @@ -71,24 +61,23 @@ void finds_feature_variant_module() { void finds_published_feature_variant_when_corresponding_mapping_is_defined() { // There are no modules published like this anywhere public right now. // We test that the expected Jar file would have been downloaded if "org.slf4j" would have test fixtures. - build.appBuildFile.appendText(""" + build.appBuildFile.appendText( + """ javaModuleDependencies { moduleNameToGA.put("org.slf4j.test.fixtures", "org.slf4j:slf4j-api|org.slf4j:slf4j-api-test-fixtures") } dependencies.constraints { javaModuleDependencies { implementation(gav("org.slf4j", "2.0.3")) } }"""); - build.appModuleInfoFile.appendText(""" + build.appModuleInfoFile.appendText( + """ module org.gradlex.test.app { requires org.slf4j.test.fixtures; }"""); var result = build.fail(); - assertThat(result.getOutput()).contains( - "Unable to find a variant", - "requested capability", - "org.slf4j:slf4j-api-test-fixtures" - ); + assertThat(result.getOutput()) + .contains("Unable to find a variant", "requested capability", "org.slf4j:slf4j-api-test-fixtures"); } } From 475f42e765f098ae86fce6e364ddf8a7b4e15e40 Mon Sep 17 00:00:00 2001 From: Jendrik Johannes Date: Thu, 30 Oct 2025 16:25:16 +0100 Subject: [PATCH 3/4] Add gradle/actions/dependency-submission --- .github/workflows/dependency-submission.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .github/workflows/dependency-submission.yml diff --git a/.github/workflows/dependency-submission.yml b/.github/workflows/dependency-submission.yml new file mode 100644 index 00000000..15811c53 --- /dev/null +++ b/.github/workflows/dependency-submission.yml @@ -0,0 +1,17 @@ +name: Dependency Submission + +on: [ push ] + +permissions: + contents: write + +jobs: + dependency-submission: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - uses: gradle/actions/dependency-submission@v5 + with: + build-scan-publish: true + build-scan-terms-of-use-url: "https://gradle.com/help/legal-terms-of-use" + build-scan-terms-of-use-agree: "yes" From 35bdb774c2329d91de5fc9186aca578bbce78a6d Mon Sep 17 00:00:00 2001 From: Jendrik Johannes Date: Thu, 30 Oct 2025 16:33:41 +0100 Subject: [PATCH 4/4] Add dependency verification data --- build.gradle.kts | 8 +- gradle/verification-keyring.keys | 1769 ++++++++++++++++++++++++++++++ gradle/verification-metadata.xml | 165 +++ 3 files changed, 1940 insertions(+), 2 deletions(-) create mode 100644 gradle/verification-keyring.keys create mode 100644 gradle/verification-metadata.xml diff --git a/build.gradle.kts b/build.gradle.kts index be96ed57..2ddb15a5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -12,7 +12,10 @@ configurations.compileClasspath { dependencies { implementation("org.ow2.asm:asm:9.9") compileOnly("org.gradlex:extra-java-module-info:1.13.1") - compileOnly("com.autonomousapps:dependency-analysis-gradle-plugin:3.4.0") + compileOnly("com.autonomousapps:dependency-analysis-gradle-plugin:3.4.0") { + exclude("dev.zacsweers.moshix", "moshi-sealed-runtime") + exclude("javax.inject", "javax.inject") + } } publishingConventions { @@ -53,7 +56,8 @@ detachedResolver.repositories.ivy { val modulePropertiesScope = detachedResolver.configurations.dependencyScope("moduleProperties") val modulePropertiesPath = detachedResolver.configurations.resolvable("modulePropertiesPath") { extendsFrom(modulePropertiesScope.get()) } -val dep = detachedResolver.dependencies.add(modulePropertiesScope.name, "com.github.sormuras.modules:modules:1") +val dep = + detachedResolver.dependencies.add(modulePropertiesScope.name, "com.github.sormuras.modules:modules:1@properties") (dep as ExternalModuleDependency).isChanging = true diff --git a/gradle/verification-keyring.keys b/gradle/verification-keyring.keys new file mode 100644 index 00000000..6c56a0be --- /dev/null +++ b/gradle/verification-keyring.keys @@ -0,0 +1,1769 @@ +pub FE6C7D77A1CE15A6 +uid GradleX Code Signing + +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQINBGL3vDQBEAC06DyZDdCJFMDH2/J7zrbhN5oJmYpM/mkwmOjkHbsIMJRcocz5 +7gYQC8OeMyfs+dkorsrY31UpgMW+efjEZbDKItfbcs+y1iEhQ0fkKKam/lzNyjK0 +wb1ew3QPMz6NFPcE9+ZmHpQDV3oHJSdNB2wrVPxrDgyxQE5Qufz4KBgbggY+Mn63 +yNPxeo7axeK0GgGzoDMw3Iu/pQCVz6eNMn8+0OKowNiE9U8PyYYNdghrmsFEe9sS +ShS8y6GmEYGz4qErI4TpkG5ZkXuIG1ZEgQlP/BuBrY4AIwkUJADGXowr70JMeeZB +1oFWa1aTx3iIHBtr2ksVOqOuQRmnFdGcUZnqEebQov+q9ZwAge5l1jHiZqZfxQ6m +oQnj7vK7rIA0FW+IznZ3De3M+vUnL/D5EIChySvsxjWOqaRCF8gCUA+KrR7Rqifl +lndVJc4gLFj/NhBvh0TwNYUu2wAEWVEuN40KFs4GcVymdPvy9nJwGqrPOZ3jqdqT +fOskvrgZJtj75wbSppED9Z+pdHLUc85lC5OzjzgE1qKiBh+cxLUwUgRSDFE797Vt +ucYWH2coJQRh92I40koTYgPBV4chkNMc3R8/Gy7LJNMIJcjS513tPuVawCMJRDYO +vz5BSWCgfily85jLX8eK2ceYt1m7OUA8LOElxmzysCGbARgP+lpCMfvHeQARAQAB +tCdHcmFkbGVYIENvZGUgU2lnbmluZyA8dGVhbUBncmFkbGV4Lm9yZz4= +=5N7Z +-----END PGP PUBLIC KEY BLOCK----- + +pub 893A028475557671 +uid Gradle Inc. + +sub 5E9AEEBA28836032 +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +WzZKkqBVoahM3qqyoKOy7mfpt1hB4gEq +=E5AV +-----END PGP PUBLIC KEY BLOCK----- + +pub 85911F425EC61B51 +uid Marc Philipp + +sub 8B2A34A7D4A9B8B3 +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +PXCe8La72ukmaxEGbpJ8mdzS2PJko7mm +=Xe8l +-----END PGP PUBLIC KEY BLOCK----- + +pub E2F38302C8075E3D +uid Gradle Inc. + +sub 1B80C80E07BC7190 +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +=Rl7p +-----END PGP PUBLIC KEY BLOCK----- + +pub AE5A7FB608A0221C +uid Robert Scholte + +sub 38185785755267BD +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +=Kqmv +-----END PGP PUBLIC KEY BLOCK----- + +pub 86FDC7E2A11262CB +uid Gary David Gregory (Code signing key) + +sub 59BA7BFEAD3D7F94 +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQENBE2kzuwBCACYV+G9yxNkSjAKSji0B5ipMGM74JAL1Ogtcu+993pLHHYsdXri +WWXi37x9PLjeHxw63mN26SFyrbMJ4A8erLB03PDjw0DEzAwiu9P2vSvL/RFxGBbk +cM0BTNXNR1rk8DpIzvXtejp8IHtD1qcDLTlJ8D0W3USebShDPo6NmMxTNuH0u99B +WHCMAdSa34wsg0ZpffwQmRxeA+ebrf2ydKupGkeZsKjkLlaXNkTVp1ghn5ts/lvg +KeHv1SJivWKCRmFlbPhBK4+mxSUSOPdoBNAfxA51QzZoPizSk0VbRz3YufYRVLFy +9vqPSorDmYJhCvn3f6+A38FS/j8VE+8obQ2rABEBAAG0O0dhcnkgRGF2aWQgR3Jl +Z29yeSAoQ29kZSBzaWduaW5nIGtleSkgPGdncmVnb3J5QGFwYWNoZS5vcmc+uQEN +BE2kzuwBCACzeGpkd6X/xTfKDBWvXgHOOKIJ2pht9XmtZZKiIj7LIiSwvSds/Zko +ZKxAm7AY+KPh8Xjf968FtoUBQJvHAG4rbowEqT7OOrJae2JcenH5qzaod7TpIPQV +v+Ysz8I1wLlC6LzKRj1X99Hng6X+obsEasnPbmEEkuiZ/Sgi4vVC8SHkDmYt1Dx8 +jDgm53oUeWkEJO9LSI2zcrZhSgvg1xa4Q4gY5UUK7gE4LbmGCjFlATuuW/0sryxu +8zxph15gkn4Nqgk0CPMSjesMYEGOsdDzfQXl2tXbt+Pe6mBoWh67MZ1v5zOq3EDt +oSqDpWPxponAeaCuNDDFX44vGjfxGE0tABEBAAGJAR8EGAECAAkFAk2kzuwCGwwA +CgkQhv3H4qESYsvEMAf/VGyqIEcw4T2D3gZZ3ITkeoBevQdxBT/27xNvoWOZyGSz +GYlRbRQrlo+uZsjfMc9MNvaSmxyy4gLVbcdvQr3PF//GxphJ98W8pk9l+M57jfyH +nnCumn7MO4o9ed+WuigN5oeuNJ6BIq3ff2o1DsrEvDChYOJEOeFuWxv+u7I2ABJJ +ep7NbByM2n9PE8vlGU3zUBgWUBsk6jT+klKnEyHE76WzegPLz3jtElTuyB7jRhjy +QJu1yiJEMbs2zH8aJGObi5f8Jum4tILZuEAdoI0M3c3VRq12cz/vLy+9VXa/s//8 +IsGn88kjyyYqOy8WJEjoOXFh++dpWiM7nZkgQcNi5A== +=ggBv +-----END PGP PUBLIC KEY BLOCK----- + +pub A6ADFC93EF34893E +uid Tom Denley (scarytom) + +sub 9C4C23E6FFE405BD +-----BEGIN PGP PUBLIC KEY BLOCK----- + +xsBNBE+xZxIBCACzKctn4ez8xOC0pGThhAwjYWGkzcwK4HNaC1usHThBFz3/t8JN +OqUXRixLyi5wELN6GHlsGVUQS3IfB4JtuhScsieSB8PTree68/knMq6JI08mJqZr +9nFrAB4eDW0UMbSL9kPmclUm/yN+qcCZBrsVn0q6CWb/Kcd8EEXEu6sGILzOGqGe +d433t5O+tGXWL2TjAz+Scsk2Hf4zcuDeQcxELAMnVaVgKuGuEZvibrjsdIvJDGI+ +0BzWIu8ZP8ldBl4SVtzGpEVzLvDUo3mOqBeTkj3rP7xLtFDN/3AFtowbLfL7L2Pg +SMcTnKK+jfFHRfbHP1Ih3rQ4ilLzhCnY/QIZABEBAAG0K1RvbSBEZW5sZXkgKHNj +YXJ5dG9tKSA8dC5kZW5sZXlAY2FudGFiLm5ldD7OwE0ET7FnEgEIAM3i3e1sjwrx +2PN8XYMPQWG+/YTtw1BYDl2+iYE+LaZvtq1hpbgeCLgEVwXrCJ4spLP1rFXogWqK +rkJ0LRjlpdKhKBvyH1ex4grh3cWN/bIDJcJ7JA4I/Bhqhlh8hYycS9pGFeS+MR3a +FIsii+vadrwYYvuVYGeWvdZhB7mJKYevj5Ms0OpYTfZd95Pzo4o//lNpDnrG7Xd3 +tgTNU/fkpw6rFB/2Ib1Qlk+Kz1z6JNsp+tOPGGCBrzwfwglcikTuqS+xyRgC9cHh +5eCol11uSoWPKcQR2Ar8Eo56nxv/UApdu15iJ7R8cA5guKeeS4jt0CGCPs2Phugg +DxI73Xvl4zsAEQEAAcLAXwQYAQIACQUCT7FnEgIbDAAKCRCmrfyT7zSJPuylB/9i +wtIQeexMWBmQNdDe0md8HLulDfcujPtklrvYHtXMJQFaGA0Vafq0oT9MhBfb1YCP +79uF0qgswSxINYCOJx4nTPIP9BOdTwqfGo7ul27REgNq4lIUW0GkMgZAUA2ft/vc +0u/I0PqnhKCi4Pq79hLIx7eiX2ySfXfYfLXRVzbMWKMoi7lWXseQqbM0RvCA54J1 +qAi6Ew+JyoYGQ7OvXdL5Eh5Tkm2cpIADyqCkp/aFDe5lqZiU1zS2fU6mpOf/o0co ++GoYkieIxxibDCmt3BioLgmyzpGUsMNwh4pAIQUGkcxd4spC0KIWdDEvq/QJEEIh +ZlI/ojefaZkRseFrtl3X +=L0GY +-----END PGP PUBLIC KEY BLOCK----- + +pub 54AC8E2D98CFEAC6 +uid gitlab@ysb33r.org + +sub 1309825C79629B3C +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQGNBGB1tD4BDADSzOETLscSpqNTDixnOsYLLwodQQ6pxebc4w4s2QarDHvdaFft +lZge0kTQCxpvGiENusWwt6gQngegy6qWziWSfbdHgVWBsH5u9frgJ8wCZ21UqEwV +A3d/rEbzpc3Ujo1L3yFpuZJWpBo4jTwH3GGxNXS64VwKN/mAKFZirm9DrSpdNjQU +S0mlOThn/SdJ4jqH2podnRZs3yxlr33NvVAYaACndLABCTMkHqKTRPhFYR2C2XAV +sMQ0sQaYJLmMHUTB2Cn8FZRudhuBl+O3TY58w77h+EoUrSw8T4aQRZn9Lbuq1voL +3onEnoR9tU06P576wjrJ44KGi4YJz/L5yKreKBfSe7vfGhBWDmrcjSb3thj+pP30 +Q7LQFmY5EaAz/Wpr0oDMAxtBBbii+QCllEFxu397OMrZZYqQjt9K/l4krYjDZc12 +fQZqCT889kRBA8nqhPpueY5iELmL5oGHTKmWDbKFo6fEPAq6F3e9p50s9pfhT1g2 +GUM9m+0LDs+N+38AEQEAAbQRZ2l0bGFiQHlzYjMzci5vcme5AY0EYHW0PgEMALkB +cL/X7eKvtznC1SE4dJr0skc9rGniiYwVhSNiyiYXlbb0ETdRDbajSHjv53cR5DGb +AyPgwwLNzHo1K3MrE0CvM09sCMJBOjpCu1X5sma1w76I9ScvJ+Gqr3jFu8CO9JbS +s4bj2VRr9ITOEZinvwo+Ult1vj1Vib9K4lAlpsXcd0dgFS7jlTBqeDgRR+xyRdt+ +54yrCay+IXupBF0GuYQ9lHwCVyFhwhOneoQq4xhEmwmNEQOWq28byVLUpMDzKAPt ++T5AmyxNebLRQmE7rWX2PmoCSTby8aQPlG0OSRBQJu3f6lhMZN21QbTcCBeDW5VI +sjPtZnJ7nIpOkT2UXulnlRk/QAEydJdPdSz/Idps3JcHyNK0YXKJ3qvrH1k0ur8Y +fgHuY41d4QqShOZleDcJtDXj7Gh0BtLlijspiscBNt16BpkKzEcqd/6Pt1fMIOEx +iBXrdad9mPpZT14KTXT8dXHtAEKozut37a4hLfJ7al+XBqilnYEm1OjVx/BVKwAR +AQABiQG2BBgBCAAgFiEE6gIlYKgeW9SNs9GLVKyOLZjP6sYFAmB1tD4CGwwACgkQ +VKyOLZjP6sbEogv+LX9Z8BlnClkJ3lVMZlyzbUf2lleg+QA4cWTsPE4SSi1VFHsR +CAoxesJdKqJuFcIEOFxscGfpkSbhTsTEczI9e2XsaaDAaY1GodvJfRUAu2VdvVAG +xHhoEqUUSbD4r3AW0yLViLlsQVoiuKIizm8PQZr9bfLSkXjGQz1wlPfZVC7raPJ1 +VYb3TQXmIALTc8peel2SlqFLQVZ+3Lb8JKC/9DeHAGmpBipB+VcSC5vRchqpncsH +Az0GXtmqzkgcyzq0Kyh0nIshDsGQ/vXoZQRl5hjMWBLaN/y5ik7Qde4bjL7p1ozK +Vm0S2qY0CfrGSzZnLXLOxX90fqXXN9cwmLv++bdyrXElA10u9LIqsViop3UvHitV +jkJlIhwt9/duJEhPJnT/qH2O9YAqwlEFggSNSctD3LC1uMjB8JD6XcpQgDP6BiZU +wP/yCGQGvA27Omf2WgrpGukXiZFSBkCZPL+/rcP5dnVrCwneGCXluI+sONfybagy +2nNTTJWxonBh7c5p +=ksoJ +-----END PGP PUBLIC KEY BLOCK----- + +pub 8B8E0CB0F6A7657E +uid Robert Panzer + +sub 66E5C946D7F2C395 +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mDMEYpEQgxYJKwYBBAHaRw8BAQdAAyUqDlsM76TEI9sgzz6DaXhdy7BqSrZ1C/Hi +P8QhqbK0JFJvYmVydCBQYW56ZXIgPHJvYmVydC5wYW56ZXJAbWUuY29tPrg4BGKR +EIMSCisGAQQBl1UBBQEBB0AJY1E2mT4hAuveR1BnaNwwosfF1JiwU7ihSB0aiytl +DAMBCAeIfgQYFgoAJhYhBK0pbKAUMhSF62eA/4uODLD2p2V+BQJikRCDAhsMBQkD +wmcAAAoJEIuODLD2p2V+VMEA/j1HUrIcDQ+FZtFsVQOnCjgIoeOzphExVVZmOjr2 +mAQWAPsFmTyYBbzvAHsj8KBEyO6FlSY0mKQlJxIelqBxUlxyDw== +=oQqF +-----END PGP PUBLIC KEY BLOCK----- + +pub 38EE757D69184620 +uid Lasse Collin + +sub 5923A9D358ADF744 +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +mBUN4AU6 +=kKlc +-----END PGP PUBLIC KEY BLOCK----- + +pub 1AAAA7AB37293D3E +uid DiffPlug LLC (release signing key) + +sub 2C7F998F4272C851 +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +Eg== +=PrQD +-----END PGP PUBLIC KEY BLOCK----- + +pub A1AE06236CA2BA62 +uid DiffPlug LLC + +sub 030DD9087C31C9AF +-----BEGIN PGP PUBLIC KEY BLOCK----- + +xsDNBGPAYVUBDADCs9PkY8zzhzE38bRZX+vTrr3LdChGNBmObV858NGRrXeZtyLu +U7YwYVF7w2vHUI/JQWrlPZc5tBFicefucfUtKt790WzAk3NBjGtX4IWpG57C1Z5t +QRI23HqWr1If0UanH2vUjy4fUgNZdYlH2KzookoU0950dIcqwA/HwiosO5RmQ9iY +HztkwwvFW9QxiJgL+lR84EcaIVN1ukr0ZKrG1a6wOJ6HLf9S2F3DMe7fQ+O+TpT/ +A11RewOZHwE9spH8cEsNYgutBouw/MttuYHjZKD7O4hN12MmzecWeMAPyrvYgTJp +PHcjQaVeD27OGLRpy5n5LonvkzJbhTzM+Ps1qEj/4of3EFVhxNjR0gdrkX+0Ub1A +XXiw3gjqAQuLQVKIbwvCbfj2go+YHrfcXN6kpKkYZU8ERPxI/VBTw230PPdXXwXP +Ux/JRQIEXOuMIuELm/91H8TXKutw6NdNRu5q+LPcUkU7W6jsv28dmQHdC+ebVHlA +vNcBOWVSY5e8IasAEQEAAbQjRGlmZlBsdWcgTExDIDxzc2xjZXJ0QGRpZmZwbHVn +LmNvbT7OwM0EY8BhVQEMAL4ZVuEVH9zbhY6AewA4T3u2XZ7k1KGOxoK74eygzYEp +fKMplWQtCxcxBXe2tboT7I8U3MrV6m7KDwcxLNVZM638fvfU3Px0yGs0jBzyjOcb +Vk6n18xX9UoNyoEqpxHhyPbTBr+U9OINcneXZ/iG9FfLURZjDxhNcQcnrnmvbUB6 +M+teZ6Gpb1Ye8ghVCJJNjRRQhFxXE7XnmX3C1pZoSoGcBx5zVspSuHjq7nTTw/rd +7OpC6sBK0ULk8GPAd2vJUfOtZcsLvOs2++bHxNULTXraTy/fYvXsTSe+PmbJo1Fd +5o3imI0eUy853UJmF/HbuWspFe8yONHjo0+uZITsAMq0jbzG8MTKMmgsXS/i/vaW +8BVUmLfhB7E+bUXJLJuQAan397NbYZqPF5agLZ1wHSki3iuYEttdMsy5PYCLeCqh +8Tv6VBNkEToKDAvNbaad4ZgBdwbBQaAIrWekWpiXSXizyGr/VTFE2hT5NC0d1BXy +sc9P2UCvHit6A1bCl7MSywARAQABwsD8BBgBCAAmFiEER5e09dzEbOphBZBxoa4G +I2yiumIFAmPAYVUCGwwFCQPCZwAACgkQoa4GI2yiumIHXwwAh4/tSXSQ9Btws4ZP +eLfihAb4ogHOsrJ8ZO+lZMyQOrEyzDK/y/1LpFVlHYEP51XS5h4u4XVivXGzsZ+r +tQoXaCS6n19dyyNeusehZx/BxxQrdV9OYEkgb3BC+05AWogdHXTP4prGdMtpSttd +gcxTuHwx9RUv/d6CsQ8DyKyjTv82hd3yuXQVl1829NwDbM7HJ8eq0uZPmez2ewbx +Ze9CxjKoOLfYSQ4k0DfcIFqz8CSqTVIz5aNLLXiY6NXPhS9B9/bXkRNAXzUgMrG4 +GmmP8XLYjBn9g8V+fAad67N0dUWDeAPzz3OXjp6bxyScgjT6OMlp55xXaE5HWW4a +aE9epjKjLuOD7LYdmv0GI1HhSrOnlqznB3TCwJgKMw6/37uGZnpsX0JoMs947ZIm +pcN1kNNR3e4aAFcpBwj2OSjds+G/DI3/WOXJj3aaRI4nBRr2/IB3TVhzLOizLTNQ +Q/IKL5Iy5doINK/iyjb/G/JLH1/TkhW9zEheiKUY6TiXeR3p +=fdmt +-----END PGP PUBLIC KEY BLOCK----- + +pub 379CE192D401AB61 +uid Bintray (by JFrog) + +sub 0CFE993CDBE1D0A2 +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +E/XhP4QkRJrZI1F/ +=Jm2n +-----END PGP PUBLIC KEY BLOCK----- + +pub 0729A0AFF8999A87 +uid Kotlin Release + +sub 6005789E24E5AD1E +sub 6A0975F8B1127B83 +sub 3FF44D37464BBB7E +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +BedOSN7DqsnvIUfFaQ== +=bvfg +-----END PGP PUBLIC KEY BLOCK----- + +pub 66B50994442D2D40 +uid Square Clippy + +-----BEGIN PGP PUBLIC KEY BLOCK----- + +xsFNBGDoYisBEACqUDZnT4h6ma6XIzdC6KR++uDbR2VKdhCuv0Og/sHEKkm6ZbG0 +OFB8tAaQx/WlsoQyf3DlLfUEOGDai875Aqor3fbM+E1hrZbQNfsOySKEE52k7PYe +0qGWlnAzINuQaEuZwNw+pjZqPraMlwc/hwzJB8yFNHCv25pCFohK7KXvFGr5Fc6y +NHBp6pM3pnDQ1kbkloDr32YZY2LdrfdkRqwa9STNMcZtM724aaInValFpVGEHolF +dklo9MIsMI6mVHlxi6UwFSSLltUfTXGYY+rt2Q2sLNnEKzK1GvVhK996vrNWCvpr +cdtbTzGE3WK4f2knhqzlaX99OLmkM1ah+p2EkK7HgWM9oEO7SYpNxKe/F/QfRNRS +4W0aokPsEtfKCD7vQ3cRWQXdqFwvksilv+b6pcSrwfAsaCzVuhB3lcIra4MevJcH +ZEbPrfGMi5/MIVtLayglLHSPoZtjQBhlqo8w3nuADR/aFlIUZ6NGOwaz5yXIGVEs +6E1wiuILRAd7ecJ3Zyr/URHjawfHfKMM2tNCJKl48cScBMY61FJ1EmYzwhDw+at5 +D4pCk75eM5/t6VdYQ1cDWm7J3LGXEANMU5aSZMqgVnb4SQEmRxkW7oq3Z+GIkQQf +Sj4OK6Oi4cUpM7b0m7Cbcsoqb6nD27VKD3J5KTYEq3e+78h0VRjhoi0Z+QARAQAB +tCdTcXVhcmUgQ2xpcHB5IDxvcGVuc291cmNlQHNxdWFyZXVwLmNvbT4= +=Vcde +-----END PGP PUBLIC KEY BLOCK----- + +pub B6D3AB9BCC641282 +sub 700E4F39BC05364B +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +2bX9MtjSsKhYeVDZhwUHbX5VuRtS+X0IIFhu1Q== +=xPkk +-----END PGP PUBLIC KEY BLOCK----- + +pub 72475FD306B9CAB7 +sub 1723844CF9A045EC +-----BEGIN PGP PUBLIC KEY BLOCK----- + +xsBNBE7+huMBCADW9rfqKBXOqUSLCK5Klag5WqLFxAOddqEM7wTx/42XaIKjDiAW +gmFnV4XBKm/7Z4fwWq7+ku6NDYUjBpI4vcQ2hYJJ4SRWZHT5wWzOmqgznf+/Qwug +P7Ss3EUTRGX3LnhKhKN656XQhM0PutdsHQlUKjvnl2JOaKerEhbHCRxga/U/WWOT +KdobRO+x8v1scsrnUG83J7sTSaja0McmgUhKrhJqrgSk1Tod45SxprxOyp0cgATY +xjHrf2rkafBn7K7aFDe8a73iCJPWS77gxTZCZ72xkcnMLR0m7QI8TzFa4lRjTovA +QcTpr7jwjmyjA1+68peL6VHdVr0cdXm34mTVABEBAAHOwE0ETv6G4wEIANLmAn1+ +tkX0pSiu6NJ0rkaZlMbvkxeFAstHdQ5zOoSWvJV+52DAd+h8HKZy6I89/BI1alYt +7KWALGzj1I6s1+HavaYKWftk7UvAM6hEh8PzFQD5NNziptDOjTG3oztZ0SK6Et3F +Qaczyzh9GyPC83vC8Dr48a+Bp5Ygpo/5LbuNloptVIapHftW6PUmRuxlXVXgFdc5 +vSDI2xPqz8dzLSjBa1mRUEiAFqdi6L0na6iyBEyEd4QlQ7TnDp6EKgCSvaS3OPRB +EOaWOZsaOIcSw5w7tkO7YlDIjbltVQKTbVLUIR0h9njGaJXlGPYvDFvssIMR766Z +z5bb/uPlG1HtFYUAEQEAAcLAdgQYAQIACQUCTv6G4wIbDAAhCRByR1/TBrnKtxYh +BAFUeeEFU0FDG0VFq3JHX9MGucq3IAgIAIdFSkB0cMKL+mA1dtR9P4dL7/d+G68D +VsSD8+OllWtMSPn5gNMGF4PtTk0Acu66iR1qCtCNzOqo2b72iqO+8lFLXcnteoH/ +0sSj1MFzMk4PfKIzr6u6K7wxdJLROfbLc2so1LN760e8Q4f5sacIgjetNiZNu7wg +k3YNAB27vwrCvgrta5KpamvZ0NDtDaNCIhiXQyudf2ZbhgYYQe98UPo6oAH/4Nam +mxtGcr9jfubcPgJafbKg7H8gNdfQeqt8SqGiaqPvYfs0NL6WazTLqYdBA3mxsUfV +TIjthffHWnAJv0K/EE8kY+ONuK5AkitlW2bb2pXsMpWOdeMYNpVf0oc= +=WUy8 +-----END PGP PUBLIC KEY BLOCK----- + +pub 5B05CCDE140C2876 +uid Matthias Sohn + +sub 9D29AE4A6B50E01F +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +=Ll5p +-----END PGP PUBLIC KEY BLOCK----- + +pub 7EB97D110DFADD60 +uid Niall Gallagher (www.npgall.com) + +sub DC0B7E986BD7398F +-----BEGIN PGP PUBLIC KEY BLOCK----- + +xsBNBE9/RAsBCADI/pVIFcoLmbq4LCKkqeN4i5xgGKsuQsDAf/ndFkILDUA2FaPN +7cI3EvZacWnWUA0QkkKNKpajU2OjjQlu4IyBosJht3VMtD0BJ2nL8eIDvwO6L8TS +2RRGMnMaDUc91NnoxKs/7VlQ2ySk6Cm6lH3t8KVkwaJdU59lAH1ey9UKhYyvRQuT +htenl2R63lyyDe1ZLMAlmQXi4RcCWOO+L1emChNv0q0Fsir+7go9ZNYUi6pmIEva +jKXM8bo/VtRIHrS73DsH7BVVCURYoBWexZWlRdb86KSE993dRXLvFPy5JzlRM+eu +mUY3CMKxx3nLaDN5qepf1nGzMW88xjq4z4rhABEBAAG0M05pYWxsIEdhbGxhZ2hl +ciAod3d3Lm5wZ2FsbC5jb20pIDxuaWFsbEBucGdhbGwuY29tPs7ATQRPf0QLAQgA +68HLImPvBSPnMtjUHczE+gccsVWzLEsjVYSBcOUi1j67KQHbTPcHAqzYJl19t4FA +N/yU1oOjuu/4GKVni27y8NGSavzY5elTZ22lqUqgqT6DjoOG2BTLHuOiNRIMqBmD +Gy41mEq62C9I107pqJnnbARmde4646kDiaf2vkF1BsnBx0Dp93re2eJq4rkAf803 +fDvA8iyk5uDFiGg3f70JAu7ZCAKczglD0WUjIiO5Jxncz2sWiO2OuVgdsTuZf+9T +0aODKua60Z7CLn4ZK4ZpdibbOEp66XLeaGuy5HPInTTsr4UnT2kvor/AmmPKOryp +9oBFnPvf5+wREwlQN2h/PwARAQABwsGbBBgBAgAPBQJPf0QLAhsuBQkHhh+AAUAJ +EH65fREN+t1gwF0gBBkBAgAGBQJPf0QLAAoJENwLfphr1zmPJtcH/RJ5ba5m0Obq +BGbcJpJwhEjpB6tCOufdzvvJGAMMAuH0Vs5kXrASIJPyVgJ2ab4txg6U3DKIfxnE +IGjfdH9okl/oHRYrI/EDMN0PnIkE1JidhVOEOj3UWaoLUS8vvobKq0XP8B6J+P4q +NA5L3cPlBBtH7yqzVNavi6ljJcsJH3g7L5vJDQyw+xxfOvQq66y4lcO8ptAqB+nw +oHfSsfRKQQgT+Xlp4lG+acf+Kc0bLjWWUnBRgJfkhbGPVYHQ/QfnxbuLvlqohive +HEV+d/PxCwUHq4EtLC9d8V3ADCZgb3v9YE03abItwg7tnQBd/LuJ4qdOEbjAWI4c +crfZTmD74BkWIQSpeJNC9ZitWxF17zV+uX0RDfrdYGx7CACw7LqreqNnAHAONQdz +j4yr1nBp6Fngtq70SPKs49nDWRIJZzutzGVNs9r7bFSu/fzZlzsqi8gyYSizhEnW +BoV9e+t4YrMI6uWwDiSddzCn2fLCNkfYd36nuX78QHZgD32syCVeX/k1+zmTYjVt +Op85vZ45d17mnwCSHV7G+4jImZF/+lF5ED/x1coIT+ob4pmQk6Hwf2AP/ydfyf/l +B+boV/SIUXeZ0Esz1B0Frfr+qjAFhDO7blgUfQ0qLOIXRjavSl3g1CGgCKsZ1qZU +99YrO8eFnxdaGSWvVV77df02pkg5xdDHWusPcEvovBUm0dx9DG5PchEOeOdhdkBQ +5UCV +=MD5j +-----END PGP PUBLIC KEY BLOCK----- + +pub BCF4173966770193 +uid IntelliJ IDEA Sign Key + +sub C9F04E6E2DC4F7F8 +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQENBFKneXIBCACtnX3ZQmPujf6ocvdnhsBheze71DSl34TfebyW2Qt+g9NhMxo4 +DaJy+iFNnsaMwLZRr6k/qf+ISE3A4opWAQlbk+Wb5s6DPPA2cHH6W4GdkxtuJzqt +tFn6YtkFhA15Aahr/vz31NBjUJlBmO4PwvkyxiF/MYP6TQ/AHar4xP1RxSYEPcCi +dIQczQ8nXzya4OqOyTfibeGz/eiHHuwTLHi3Rd2kihQnlRQdhE1rmm8uTyzFe1H+ +P7WW7kQgygW6yxQ3J+DXrG8kG+nbe57ZY1oyv3F/fOBxzn/kuoKHZ3JJEMJmTIrT +Lr1ngCZApgteAynRHk4t/SYZiyoyqZCuBcwHABEBAAG0RUludGVsbGlKIElERUEg +U2lnbiBLZXkgPGludGVsbGlqLWlkZWEtc2lnbi1rZXktbm9yZXBseUBqZXRicmFp +bnMuY29tPrkBDQRSp3lyAQgAvc8Q7O0gVSJsHoVgSQ5tWGwNsKcfD3I7kwC8BYHr +Q6F/UnhP1ArreNnn8KKpwOvD65pv0j5G7P9KAbIVLRRcCTB9MgJR2FPmRTNmYbKi +Pa6X6IUM/25R0SBKDJddqSvEFsE/M1ozHz4bIhdFUXJFMfv7WBaA9Cx03WwZg6Bn +5/xMzMC/qzG7QlXOMpcABtd2JlPImH13qHWNLkhyKW7y9HCfdBz9nOy0FGT54ttv +r3BL1gahSXNi8MHP7m2I3C8dSuIpzrNVPgR2eByvSYpZN28P4Cy9l99TRcr6/FuA +y5FaL/nWpv5WAraAV4Cx5Xpr4PXTn27b7k+feH8W/+9EAQARAQABiQElBBgBAgAP +BQJSp3lyAhsMBQkSzAMAAAoJELz0FzlmdwGTSqAIAJ0/yTJRlWp+dwDZGxAffw0V +iEHPkwAQ4iEKburA8LpcbTwJRl+k9d1RvFkZSWITq+F+Putlu9QooeVwcM+ht1Mm +oah/aO3Yx+yMnXwljR7FJa5VOY2aoALeCyIx8QYiqNAVaid+bQ53gC924u5zRM+T +J+vSChtqSBi+EOOTt5C+ALVB8qWTqEcD84AVbvvippCzKsA2sV69FrsIFAShvpXo +3xpXW83GCXxrp8nM9M0E46Y/SarvGTqfKRC6phNUXKp9c3SnVttPEcGhb9+92LOL +vMxKy4GRZS18bXDI3vS6gRDNJDCqBYIhp13Os9k+ZpnwK3PPIHv4l1I0i0EHZKk= +=WJEa +-----END PGP PUBLIC KEY BLOCK----- + +pub 436902AF59EDF60E +uid Sebastian Sampaoli + +sub D94994D14B55169B +-----BEGIN PGP PUBLIC KEY BLOCK----- + +xjMEY4fp+xYJKwYBBAHaRw8BAQdArb04PVwQKvEhtUEmEu7/aASZivOWgEkZBqX0 +Tovwvq+0J1NlYmFzdGlhbiBTYW1wYW9saSA8c3NhbXBhb2xpQGVxdW8uZGV2Ps44 +BGOH6fsSCisGAQQBl1UBBQEBB0CSPWzZfBjKWyPW+D6RDRLFz5xlO9/30yGD/VhA +EPXybAMBCAfCfQQYFgoAJhYhBB0sfvitoPeUtYx8Y0NpAq9Z7fYOBQJjh+n7AhsM +BQkDwmcAAAoJEENpAq9Z7fYOTMMBAKfZb2ahnfGNBt8Hrbu1j99580a2IaFQddAk +xXZy2unHAPYyfxDLPkbTR7Mm4k8Cva8PCcXotDow4bDLm9rhwVkJwn0EGBYKACYW +IQQdLH74raD3lLWMfGNDaQKvWe32DgUCY4fp+wIbDAUJA8JnAAAKCRBDaQKvWe32 +DkzDAQCn2W9moZ3xjQbfB627tY/fefNGtiGhUHXQJMV2ctrpxwD2Mn8Qyz5G00ez +JuJPAr2vDwnF6LQ6MOGwy5va4cFZCQ== +=VAP1 +-----END PGP PUBLIC KEY BLOCK----- + +pub 0555B3BA1CA4ECF9 +uid Andrea Peruffo + +-----BEGIN PGP PUBLIC KEY BLOCK----- + +xsBNBFeI2igBCADSyC1+7+GrBt5easeGer72OC8yrqjO/Wv6o7AYWbs3f6SRmzEi +myc1sh8gBt1NbpkhIDuxdlqSOiYU4uHn246FuEEOPFyp3SHnTkEcI3J/5JzRLpKB +p6qdA2uXSPFtyMZWiMJS84eEeuzgs2J22ankAIRhC4HsV2TWkKEE5r0TnKBAP1pW +p16sXlDaUNf5JXIVOjILwFx22hVc/ZEx/3i/MmY02ZbZTDYDHvDC/v8zyv8aXIft +aKJEuV3H+jHZXbum2KMVVKokptVAr1boq6vTth0MfHDMibBHkm0bYNTSV5dUEoQO +ZlDSAKhjJlZHRzqy3Yp6hZyOA0ELYSI31HJNABEBAAG0LUFuZHJlYSBQZXJ1ZmZv +IDxhbmRyZWEucGVydWZmbzE5ODJAZ21haWwuY29tPg== +=b3AU +-----END PGP PUBLIC KEY BLOCK----- + +pub 1DA784CCB5C46DD5 +uid Rafael Winterhalter + +sub 7999BEFBA1039E8B +sub A7E989B0634097AC +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +pIXjPlQ0i6kV0h8KapE1Uo005JYgeg== +=/SKc +-----END PGP PUBLIC KEY BLOCK----- + +pub 0CC0B712FEE75827 +uid AssertJ + +sub A9E4161147556D82 +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +sMY= +=vnKw +-----END PGP PUBLIC KEY BLOCK----- + +pub 3D12CA2AC19F3181 +uid Tatu Saloranta (cowtowncoder) + +sub 575D6C921D84AC76 +-----BEGIN PGP PUBLIC KEY BLOCK----- + +xsFNBGL4BxIBEAC+lX44fd/zrVQPzdKygarBd/X0bBpGakT++Kfk4UBGl3q+wd2G +R9puB9R377ds8hU7U3To8sHguUZo6DbD9Gb/is/WajSb9g92z+rMow3KbqfCYqWr +kaIj27OJgbziFcnMAtvGoFRfaPI/7TOwEw3jT7B87RXeiATX4iL8fzMUmkfZm0Hk +qjnepMQeaz3KzMY4DfBcI45kwzl3EIBFIlk428mhBU5iAAANoyPsimfqEPRCUDjx +vT8g7PvpkBdNZgRS6R9vLxyzKi/f5KswZIMvop/pRXIhAKDhCCyr2GD+T3JoIKp9 +kvS1MQucWeX8+TFWh5qEA3e06Xu0JSdPCEej0BH06EiTMsAOU5bWqgLAO9DVpS32 +I092KAuMJlEPCnz7IGXVkeNY5KYrlsmoKrBO3GF/zsCyiZDvSULkVJcrtBCYOrgq +HRIzvJWQaTJ5V15MD8CZIELyjCGZ8Jy8hdZpaTjYalw0bUq+yRAqMD5slp6A1tnv +jyqVTgU+yRGq2HB90vJ0D3P1w4xRDuNF8c02futO415Yc/qkyh3/5AjGSoocrlfX +cMreJXpQWVsvXn3NsitjsA6XOJpMOgipCDxfvn8SSLl9fWNJf55j7fCkBokF/lIi +81RVQbyjVCOV0OEqHJLP9asPHyAFvUppNWtcvViPxVmb52djnw/x/61WVQARAQAB +tDVUYXR1IFNhbG9yYW50YSAoY293dG93bmNvZGVyKSA8dGF0dS5zYWxvcmFudGFA +aWtpLmZpPs7BTQRi+AcSARAAsKXGqznhDeU87UA073pnPg12bloq5h79U8iZozoV +NIRhjMxJyilOlWZVCIOWEDWJJ1Dnzn/9OaYEJrBIY4yPDQQ9wsrOklUOsDpZAPiq +QyrP3V8MibbWBPhBvyDM48GVtg2xedB5Jk9lSv6BYUUn9D2q/nG1UP5jSwFQu7nm +VgVV5XXs6lb5N7Q2GGXn/U/EJX/ffS1VxYIjM0Ra8yy3HdihBwF+LHuuRU8SHxWG +Aq7IRSCg0YuCFjc0KrT1e5m/eMF2NFcLHuZjBII5onhj4wRmJ3tiVNMWDQcbZctc +t2ng13MTZTa3EvwJHvQKlgGFOGoLaHAnn29abeUN5YtKoNz7FSgyealg3Hm/pIHF +Lh4LcBxQlSAqEFDLL/aeRf5Fi9/PzlnE0dpUOLRnqxNnZpcqhVru5qRC3JAH10qS +aG2ZbVG6fAjuu/YNJZPjiVkpsXXZVcm3VwhWgHjikG9MKEDpEdb6NrSR8hphq9tB +HmvlF/pHS6I1UMGAqiAnb5yuGKR7oaU+XK85OpaIX2aQTzB3aUexUEGXkBFuRG3B +TX6FBMLIG9qpBvoUCC+UO8EWox5Bmht1roWNsRMqB7i0m9tIT+YSNrobcbMFJf/i +Do42bQwo8y8+fUPgA5A2WDPjzd3kdFCQ6mCpcuPSk7s9t8y5bjYzcKqPCtMtOVxg +kDMAEQEAAcLBfAQYAQgAJhYhBCgRjAcMsioBdaLo1D0SyirBnzGBBQJi+AcSAhsM +BQkJZgGAAAoJED0SyirBnzGBkG0P/28WaiFCKz2vOqFxC6tfRPjhU7wilUM4KIYm +ij0uh8dq4Lbz0tmybzvq15QL0QBciPLF+w6tHXnmT9KV3n4nY6X4ys9W4VvFn+0V +OkDinNBMpfP2KglWYoJ9Q8yZRda9pq5GWtFUTS44fOj/2NU+2YawIkdDzb/vixID +bD2y/E7ta8lpfL1hXZaLONFvMZXj9ZwVNfTloXjj1PVWDfNHgQ+Yo9gp9CwsSUHc +jTqVQ9Nz92HGrpPThzlQnflFV9gO1cHpl2+MEQy+fYAH0hsmCx2KgBdVyWzl5IXk +z0bLbcV0SJM7wP4I6ZkJoqDVN1IYjGdRCZGyeNpaBT7+2KZW5gV6DACiRdeNNvrD +lbrAtRVCzEELaWbwv24KG6hKnU84WWvx6ygOOQRaXGkzvNIybaPJImUe4p38F9YA +Rq2IMF4rMYomDyOclcAL2E3DZ1NZw/VZOYsk4MdATQRtYSz2mQbZGGqw5lKNCsmH +9GPJkGZne1NJzh6bXZEfucjQ+cjtvf8Bn7HtSnmXETRoHGEBShsO9hw4mLDhC4os +LBaslDFjyxMECWr3v7TuEmEmNcD+KwNyACFNuBjEBWeuJZYwCkAkVy8AyitrTMh8 +/CPhk/tPm26c+KI5BJsQg8V34FMtd+trRhXRG2mfPB2cU2t9Il7Tlzi71iGEafIb +96Um/Inf +=Evfn +-----END PGP PUBLIC KEY BLOCK----- + +pub 199C76080F8A18BE +uid Open Source + +sub E3461D2D16725F94 +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +=rjU5 +-----END PGP PUBLIC KEY BLOCK----- + +pub F6D4A1D411E9D1AE +uid Christopher Povirk + +sub B5CB27F94F97173B +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQENBE89LqsBCAC/C7QToaRF8eZgGOxcvp9aG+mFFCMjaRAb4Mh59OYdmUb6ZjfO +9388HPebGbPNR8SHYs0dBIuWY4ZJ7oUTYPswasL8vB0iPFdyHhvkCca+yk0b8ZBM +DmFlISm9HkYpoVjcFUp1oivyeJ5LRTJTd5JGEd/SWFRbB4TimdKXBzej9fIm2zVl +KInEMMd8HnSYE6nm3aNkbyiqhx81bFvl8x6X3ZMWcKs+TAVXdP9uLVvWowUwcApk +xpee442Ld1QfzMqdDnA6bGrp8LN8PZF9AXQ9Z6LTQL3p9PIq/6LPueQjpJWM+2j8 +BfhbW/F2kyHRwVNkjaa68A544shgxJcrxWzJABEBAAG0J0NocmlzdG9waGVyIFBv +dmlyayA8Y3Bvdmlya0Bnb29nbGUuY29tPrkBDQRPPS6rAQgAuYRnTE225fVwuw1T +POrQdXPAOLDkiq49bLfcxwRJe+RozKrJC1iKxb751jTozEEJLe5Xj7WcojqgDsuT +jzaLHDNvDCzRFvwfkJ4scMTAZd+2GYsC8N3Gg0JRgC2lU4wZxsanLnVMbdX2L0lZ +7WnH6S+GJ5f0Et8PM/g+V2Gj2UraBhGGak8OBQ6NhmCJBcyYg8Bh90cgD9V1hMRM +LSW7gB1vnpLM7C8Yymd3etdZSIltmDuVb3uG9s4Uwq51s2MEKsXsuFYCHTz0xT2u ++6e7Puaq5V0218QGR1Wupkl29iIUF57hFR7f6oYKkecvPKc4Yev6Ii0Mbvc1H19k +LOXUrwARAQABiQEfBBgBAgAJBQJPPS6rAhsMAAoJEPbUodQR6dGunSQH/A+4/Zbr +2jB46q1JEN/UV4U3MBQiNvCOSD9tOPMnBvVzJ53HutvGGkmafbtbwDZaN+YMs6fi +itBMqjF/eQ/pJ54aFguTPGMFrlFyjz2n/pffkHLpVHgs8V5M4ALITttwCOo8Vv7u +3VjO+ea5kiCm9MqJySrUP2Dv4lPVB32eoEUqWDxoyeACihW+Utdo8TBDVd+R8w36 +W3CUSvujW2z9jMNTF+VoVWDQWc3up7Nqb+ztW9wrjqs73nJCv9bLPahUPNzfh742 +v9vak3TkwMcDR1eZv+KvA8GXSZM6ACALzTmqRHXjGF3UZ4vowQDfiTzZKr87eBaE +FoHco7Lnn+W+8qk= +=9+x+ +-----END PGP PUBLIC KEY BLOCK----- + +pub DDF3944950267CD1 +uid Jean-Baptiste Giraudeau + +sub 76947357924D7C43 +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +=Q1Om +-----END PGP PUBLIC KEY BLOCK----- + +pub 78178478013521D0 +uid Ktfmt Team + +sub FA84183FDD6A6B98 +-----BEGIN PGP PUBLIC KEY BLOCK----- + +xsBNBF6RvwcBCADIVU7oxOiljoWxNTkZ00PKVwyqhahYpN/4lamULtECCS+HAF+J +DsNy/6QCl7lKAGrSyn9dvsI56KEkGvUJfpQrpRlg+uIQDMxS8JF7p9n49DNc8Q88 +XuCaiv7pv9hhCN3Mn5qcuzTR2gGP7ToZ4fQW9W/PSJBSOeO6JK6xXNyiKcyEoXp/ +jGSRqqbDkAT4OYEUy9RfG9BeJEGUbDK4RIaH5Bo0bdahq3mfdlKYXMwRWGV9D3KN +4iLE/26h3y6tQZS5zHQGjIIJedaKGWEiMsF+b7Jjf5OEC//FoH1IS7nDz2GK9BjW +iELtSnmyDIDiFAvMY0pGsn8AQFn4JxEP3TMTABEBAAG0GUt0Zm10IFRlYW0gPGt0 +Zm10QGZiLmNvbT7OwE0EXpG/BwEIAPuROTIEOMOtp07cZ0bYwuPEsX40TJNFPOxg +ak9Mx9RSfzhSNnQ0HaxaGc7O+HelsE6xeWtivbuHWjveWUvoxvkjaoYQ9WVtDN/s +Bh2kwxS26BkvlxbbA25ZDgjk+M0xCqwkB0WX2O5PoV17+KJUhrxU9ySKe4ROCTnT +zLIgfTvlaL1nzsJd/NWDN5RhzBKe9Uv9pcxZAmZySDXmn6z62B55TXyeyvzQZWaH +acHb4DYdCE7tPkfVY+7kV+LvaLOsWsBf5v+Eo4yQb1EFc4Yd/jgMTq8GUAnN8isS +HeQ14Sb1r3mHSPA+/8Q/SGZ5s+NJc13/RS2Q8OhiOE007JO2nDEAEQEAAcLAfAQY +AQgAJhYhBGSBkJluwJMKbX1JqXgXhHgBNSHQBQJekb8HAhsMBQkDwmcAAAoJEHgX +hHgBNSHQXSsH/jmg51oPLs8Qu0USx4aHVgmD0F3//9xssxx3/SW5l6iR8UPKoxUy +2uUOAZ8xSrDa8arNxfrSf1h5WRrJ0GqiKMJm8eeVDuLaFnGYX1LuMgC2qCrfNlQ0 +9DdFEHYHnxyEoyjBPWgqcZpa/bYMTiOjbHz+HEQs7gUBGEEuWh2zezp5MX6a50zB +D7whEN9qwzVz1fxdAwb5Xl+iXqQZ+mYvQo+TBqbM/KNcDb3a6hv904Fcdzy0Seb3 +aXWb3uzaQDSJEEJxxEgWjUG9uEuS+eMD4TJqrsfg6TqN6ykXvdbXTHO2cD0DFFZN +zO5quWpqxTLMSgNswFB4mjvd0mS3bUK4DhM= +=3R74 +-----END PGP PUBLIC KEY BLOCK----- + +pub D364ABAA39A47320 +uid Liam Miller-Cushon (Error Prone releases) + +sub 3F606403DCA455C8 +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +xquhnpl2HvE= +=A/Aj +-----END PGP PUBLIC KEY BLOCK----- + +pub E3822B59020A349D +uid Kotlin Libraries Release + +sub 60EB70DDAAC2EC21 +sub 3D5839A2262CBBFB +sub 9351716690874F25 +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +=9e+M +-----END PGP PUBLIC KEY BLOCK----- + +pub 4DB7BC57DFDBCEA4 +uid Timothy Wall + +sub 25EB2A6CB1459233 +-----BEGIN PGP PUBLIC KEY BLOCK----- + +xsBNBFBm+zUBCACsrBpO6mOsZ/B6PdPPV/Hj87m2GHeEYEHt2o2l8X2BdbZKbVW1 +FIKnpYe3+TsFCe/qNxlR6vk0Jpy3ChD3nW/J0rmU0ju1SZnS7rdSMj3AI5M5xxpy +Vn03vCbYyFWw4Bj9avYmiz8kC7yImfGfTLv5XPmlHhUQaxW8+s+393n1mHZsIZTt ++luPftSLCkcmKGHn+PUkFaHHD+q+TTaG98nDkkaCXS0JwXzujYDJDQgV+6HB6PO3 +gJJW4QcUHWa5uUY4CfqGcYGBvzlfII2psyKFIHqrx1uHxbhjLlIwif6I+Q9OFcdq +qq4JgVnWti2dEBgOVunb5S0KnN40VcH0v3VVABEBAAG0IVRpbW90aHkgV2FsbCA8 +dHdhbGxAdXNlcnMuc2YubmV0Ps7ATQRQZvs1AQgAumH3wbBy7RYBaRjaWjN90OKc +pXuJ0MTIzkgC/JtNexIVtzFdrfmHPOEZESYyLVk0E5YU5i3ux+K9ARi+bWrst3s1 +iiPfebeNCRU0lmxI7fih/r+PxcTTdJnHAoZUNBK1ZzjlTgXacradjS1LGFX5+52g +TgFVNlYXvI3qPKQFb+Vfz3x4LwOI10ipnqZK61D/FC96JD0osEFJkmFvI5n00Vww +XXRoeefN44Guvs58nf6MNi9ZA11iQlK2a8CswAuHJTndkQejVq5D1+ejVVmt27mH +5RhG1xoNZ6FSkha+Y3ImTpxQMJzdbkOtMzj1S2VIt8ZcrV/6dt+yCAskTFW/ewAR +AQABwsBfBBgBAgAJBQJQZvs1AhsMAAoJEE23vFff286kd/gH/2Wz5zWkcvk8ReH1 +Ke9vYH2gee0qtXIrCjtV1WBOTy0NLm0S4Bx/uFjbFUbqNK0nKjTix4mAe9KrVJwN +47mXVGxG5N79KkiS8lj+YM8WOUIe5tkDovzEfqFukzgpHBY3CXjiojugxVv6cEe1 +GLnEEOYJlezZj02HS0D1eEeIdu7aV6f6xPFAo2q94aavz8cC01IXrnccUfuDtBJE +VqdZmVoc/5tUZf/dU2m5ga62U7yaVbiimP2NXIL2tzGLgyUaT73E3e+EXzfcpJaO +rBqbcs+2HPfAjBm/km/0EC3ET0r2Vqoq40hP3fQqLSC/LYK6Vbuc/l5M0I1QxhJ1 +njCEin0= +=U1nj +-----END PGP PUBLIC KEY BLOCK----- + +pub 39B48E1BADDB933F +uid GradleUp (GradleUp sonatype key) + +sub 8F97B35AA6D60AA4 +-----BEGIN PGP PUBLIC KEY BLOCK----- + +xsBNBGE/hA0BCAC+MEPPNI9edKJ8TTplIvVa4o5Umd89fWtwhkxOaenN6y07L9iV +rI2RISRdv73Kc+K+Qb1Vlom4Fa9R4+NAibhb/QRsn1LkA02Z6JIF9w78JN9DBesS +090TiQwZMzgljJaLVIxzCZk6T9XlZQC3bXmkyZkYS/a9NMa7JnXrPua06JKpH9vZ +cM6L4TUb0p6Tw7RsdBHKBj895XqIVlqJXfNEpcOZ2LCMUcVaOJFS/dAXSrTmNdT1 +z1eoeoob/CbHIzH3STWVCXnXOw9APOry+aPpBHAD9g+1Q1BEj8pSmEnByjeO6yHN +UMbFTUtM77oVAbLx6rHL7jzfgUQdVPr8PgU3ABEBAAG0NUdyYWRsZVVwIChHcmFk +bGVVcCBzb25hdHlwZSBrZXkpIDxtYXJ0aW5AbWJvbm5pbi5uZXQ+zsBNBGE/hA0B +CADaDvPtymFgLSJLHPCRIUATJNwKu06QIE1nJ9EiCzHn6HfKm9EvY48g8ovfziQi +bleP7MKllhUXm/ZAN8/ufg3/kRD0KI4aFyToiJtvBOweSfOj/USgC2nSDh7BcZXn +dVkSafrjsrFMdk2sFDiTNi+Qv+BULC3m1ciG0AQyGYQIiGMZnKpTDP6aHZUZTFTB +h9JRXWhjTG4xO3WadRKYAUN879tp73KMACuaI0tK99fLEN/bMMmd9cxBTpZztDnd +PnVlLVyFZGTmjdJKCMcLC/gnixiRpLoy7qXzj9z/nrlZ3QcAPgGKtM+AO37Zsbbn +Qox+Yao0o+GvycRCtNT4OcaTABEBAAHCwHYEGAEIACAWIQRIV9HOBOePqyoXLo85 +tI4brduTPwUCYT+EDQIbDAAKCRA5tI4brduTP5/RB/9YiKJTWHr0PRVtV8STKvFm +gMVw+RF+eig5vZ/MKq0svyfeFLf7i/XfndEG4X85G169tvNI7MG8d362NdpSwEdJ +ekiQh4+i5oE5ZdnNDvPXr2pVAKDhyuyPuMUmA0Zf+JWDQ5vfzgpcxos3Y3i2D1D5 +92BDms675Ob18NvjjAMNR9bf0vEmjlv/k5oWUe0lYEDOBnImGgEIPqEOOvKrXZFT +klXcR/JI0sUwvNWLfAsyi3HtnR8r9n26tyo0wCBVWO+zXjvI7xhvpS6bvI1CfeQ0 +04CuCUoP619vciqXdyso5jTobWTXxKvMHgrbw0XAZVlL53qp1OUWi1pA2/eQRMdY +=Sqth +-----END PGP PUBLIC KEY BLOCK----- + +pub 6329ADE2B25B244B +uid Paul de Vrieze + +sub 3FACB47A6AE68B7C +-----BEGIN PGP PUBLIC KEY BLOCK----- + +xsBNBGB9fcABCADNbbtAiTtnUYkhQPik50gYwTuP4QIQjTVeQDBIcwRIjhGMUVov +PD+Tf41NTy9VKlbuQeFIPrx+MzT0V3B+c7KSFXEyi36LzeACDpcDjR3Dzh+cAKGg +acaxk0lsfARgcnCtayPY+rUaZyuJDd/681onRm52GqpkNbCQ4Ikij153VguPcKM3 +vPCmckEHtrcbZZbgsH14A9+AQfL599QDJ5XkQv4g3KXVgBAODJrVjjRiVbTNSqJu +gTQsEe1JDSSDv7vYiDmWBdtC1Qbw0IRpuxPLnF0CzNO4gBbWQF+1YN6p5cxpPaae +Q1qMDePS2nLJfcuCEFrIwBG3fGzvmJzh9FHpABEBAAG0KFBhdWwgZGUgVnJpZXpl +IDxwYXVsLmRldnJpZXplQGdtYWlsLmNvbT7OwE0EYH19wAEIANt8dkaPfHGJS38o +m9vEKcS9XJckGHv79xnppCfSEw+S8vjjCR6UZ59YRJRT+8fhSCIE4+yqBpKAXV+p +ElskkJlidvSA+Yu1O+VeZvXhNpGdWMse8obUULf1+JbxaNCmhCUbXoqCZ0MDJvep +ulFOmMTHs4Vh3P6DcgKNpyvOSiEfspY9GwxBbWfirYwjg+wdw/RW00F49vtSL3ax +R9p5DYDWDEhLIuC/yvbDxKeTvICzTB2uQhysONzYUeoqtucE63//gXvdftv34S8w +JgB/3a3S9NzbrAsgRi8QwEole+lKToyNh6CuN+KzYeA4XcvwGIwU5bAAKkY2CRuo +pqSEqhEAEQEAAcLBrAQYAQgAIBYhBAXxpdpXAdQVvvxn/GMpreKyWyRLBQJgfX3A +AhsuAUAJEGMpreKyWyRLwHQgBBkBCAAdFiEEbvTCO4JEqDcAI8Q/P6y0emrmi3wF +AmB9fcAACgkQP6y0emrmi3y7fAf/Zgb+4gbdTycXSG+JmUGzfapgpXtjnNKd5sWG +BW7mGkvfyKIaML3NUKUYz+BQicF9rxnPNm74PM9vl2W3ejY0LXGllk8nJHQTqbNr +YSLMGDTZk9oH3DtaFFImu/XF2etU3OFruqp9WBbr95YCz7TaqsNKfiK8U8PBAShn +8BcmN9The1XpypVxXdBAiKkX8rZQ31T0eaw/JF1esjMEa741gybgz70Y9oxI8xiO +7zIj1fr9n3aNns8jmZ3InFI1IA5FI1zg0wAXKAdFbW5crf9osD1WkwIyO21XLWkh +7goj1A/I/JQAxWZiL7dUpbmtKngVJOaJlSPfbW4zhvsneusYJkkQB/96/b/zyT9V +LXKqwatZF6JXJmG9tEZD6EOEXYV1xfdKMEV6aaiFMN75O6iTTdXs+DBsHeBGUhd7 +YvoF2OBVTNKwyAQw8Vtt3edx0LJPZZLk2JoVyQDKW3hmZS8fq+WZg6wgL3PFY227 +jzXHWuZ/FPtJRBa/QXJLV8DeQDPGM42ds5nzWk/AegT+nw8jsgznVubRkdjhBT7C +oVADwp4ucWwH/q/05xBqQnv4WdA8QZirLrRqjhQJiA+sMkIHC7b83Q7KrEpSbUGz +rXfNOAUjWjYEM11ek3EKUScDr+hyP7IW6pqf6Le1rHjDQt3PT7zL4LogfL3Rt6GP +Fu69UuSeTPOL +=LH1+ +-----END PGP PUBLIC KEY BLOCK----- + +pub BEDE11EAF1164480 +sub 4BE257B370130000 +-----BEGIN PGP PUBLIC KEY BLOCK----- + +xsDNBFv1EEwBDAC61jyEM99KH18hI3zlfuqvGoNjTLIh0wge5vXAH8VxMR0ndOID +HYSBT2+L6OeiqKlyhCgF1km48F/dMzyJdTASkNO1Ni+B2Ric1sBxjsSPufkjl4en +yMOl/FuQOB2myht1fCXhlynmOoiRia5J6xzCsCNVGOVYfSru8vpoT9QKcD1OlwoD +WhfyBx/bXsoRvD1CMjQdalcGxv1aJRWfhRumXQwhMPZlFeARAzeDmWNpglqrMnuG +/VADZXZsbLv8VWaequ4wEWiwTOeA6YYElx648OTSv7NjMM7iyPPPWbbUvkVbA3Em +lLBLlGYZTx2nI0B/322SsREcEDwaBzO53GStIzP1XvaRosM/98/Y9ITwB+Oh7ZwZ +dYmmabxN6F5O3v+TNndEW7wgP0lkbsOWZ6YNmFhvoEtd1RxZiSNov5CxokYUrug1 +cS+/vsa9oIecUwxYOG2D1v/pwYhQnr3qasYz4nEEBWHnnkhyr1BbUSuen7w2SiK+ +64cQn6V9aeZYi6cAEQEAAc7AzQRb9RBMAQwA7UCAsQ8KxX8nYO4Sy2pzlh9W5FMP +wGluuokPA2A6g2Fz3vF62RqeaE4HrRQMpijQCsN3JTJVwDid41X84XCMItkdAxMj +mn5zeF/yCcRuHe2Ci/+ae5BzrBaKE/VWRAkaZSZWJ1MoDdpSxJhLHNFnVrwTkM/S +eSNUBk9ZDEC+43b0hciefX9bFlc6XPHgV+yr5ohhwcNcrZ/gbAhhN3/xIVmvKoib +mb+ZIajhiCP1OOH+GpZAPT93w9qZWq3+2gvP4ZZ7bO+8N8Gmz24GL3/0eYI6aMUM +wWGjy5J+iRiFjb6E+Iv/zToyZFWm2VOuOUqy5t4u+Vyk5bl0hATpJICmKa5OFtQw +G5Uvfztk6rujjat90xv8yzsBvoEUqKqzIzjHdN36qop5hLMnBljdLdFY+Rk9CHdF +7MW8Nf0YWbP/3uUk19utGW686Lolt8gvBQc4B5N7VtNoXFCKM/I3ufgnHQvDlf8p +gdJOcyx/a90V/DpUI1ANlwg6IsmFZXbBQw7tABEBAAHCwPwEGAEKACYWIQTjqflQ +eehM4gH3z2C+3hHq8RZEgAUCW/UQTAIbDAUJA8JnAAAKCRC+3hHq8RZEgEy+C/4l +sgrKCmq2Nc7eTdN1AxwMkj28XQFmkqO8orfJm1hAtVK1KRizkX52RNeRN6QX3pX9 +s1e3DjJi3Hpa1UWqeicPA0kKTi2ytUlxR/iZDkaQkLyCCZtWnGHr/eRBdOjblprl +5O+v/tcyrmQGC04TqOntMumuk7JNjZ0QAVkZUxdmfi9bHaF5W5vlcaFYT5gdWpkO +Q0YaWXXw5ynh6Ookjhq0g4pZNjl2rdWWyTC59YIvC9THx0+vuyN7xnSWIb8J1IjE +EYvPqRfpd8s1Vf2AA0JRPjUG2UV8MZqu8k8x4iC2gbdji/vyg/ycdlRT/ULyNprz +1nTLMfhBT0Wmy8B5lFVme3URmld8T90RPln6Dy+c+IKb/79z3FPujuSbipXzx3Qv +GwVYyP80JFn7CJluOl/u8vxi2EVFN6aVqdzwoswFE3+0W0AfbpHUUT4oeBW5OBTJ +5i1Qb0DT6WXk3Y2j1Z08xxhY1RITnc2C33wjXAW0h+qq7/7Yq3w3/7ncv9sWIzU= +=+T4J +-----END PGP PUBLIC KEY BLOCK----- + +pub 6A65176A0FB1CD0B +uid Paul King + +sub EA8543C570FAF804 +sub CA890A5FA09CFD80 +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +=g/bF +-----END PGP PUBLIC KEY BLOCK----- + +pub 72FEFD1572EB75E1 +uid Spockframework Robot + +sub E95B8FF73F6B84E3 +-----BEGIN PGP PUBLIC KEY BLOCK----- + +xsBNBFt1xhcBCADDofPZYtlG1I5NRG67j5Fn8KArIzKK9L3bH3MfJC+XYYPY2N11 +ckiF3JQ0kXH3BPL6r0+dOQm/9SGJYsRVRYYoz2HEf/bzLSxFvKeEdEsWh66IcEzp +xlxV7rXc3oeGCZ+EvobG7JVoRyyLegx/VareBwtBhW39lLGfBjGBzFuXEMVG4H4U +v304N7D4sEIBYWT8c2z5Q5/Iviiyu/VgHxczDn0H6Sl1kXP1vVhWff+OBlbO7JAF +TcW4ET3K/ASWL/CirLbyyVkO8DO5pQyuJqci7pJFWWp/56CpJBmxuyQRc4SEpPjC +ZUOs335j1+MxAJh6z1L/xZHF9OV0GgT8cs67ABEBAAG0M1Nwb2NrZnJhbWV3b3Jr +IFJvYm90IDxkZXZAZm9ydW0uc3BvY2tmcmFtZXdvcmsub3JnPs7ATQRbdcYXAQgA +xh+deGqkK22ydYsUjGkRe8BGN9QQfdY3KfuTIugU9nQBTtXetx2wGD2T0Oz0vLnG +FEVVR3wDOWvLBJ4BD9tJX76JeHf3Eurj3g8ctBMbla7GDwfDbW0p7vT2I7Mlryvo +I7XSyP0l8KfnNaKWo5tzFw2MpyYY7BN+giWbyCJV+10w2B7XMSwlDddNm0w1I0ec +rcCn8ju0gFPH+nYB2AUo4vvejC2pAgFemWZGv0AesSLCri+zHCHfdLf0ex8TcoKZ +VYkQin0JuK00aaidv7lwW1rGEQSGeamZekBu5Bw2yMGA6JZchR5ynuJOQ8ygX98L +MmAHMdXHfghX9cHOLhm5mQARAQABwsB8BBgBCAAmFiEEdulOj/CrWvO2+DZpcv79 +FXLrdeEFAlt1xhcCGwwFCQPCZwAACgkQcv79FXLrdeGvyggAvNhz/LfzuQKaEkSc +xaJ5Ww5xhDilQuPbJNhT2lhFCbEd0EjALxmRvE+WMVm+Cs+yd1Gce5VlS7/BNJWM +7JTRr1QDueirU+nmwj7DPEpJgx3cI9rPFytA8SmeokWJUOZrU0BddNARLNImcKei +B/SeaDiy+BVs1qY5+ZqwGKvPAJuEUOgvTUbYd6EWCYnxGttZtrAa/Dx2kShNOgNz +kLBXkjgSJxFIbaRjrOFvsH/wcSG3Teyv5g/kOXb6stVtoUi9RoyQCEe9JbxeH4pA +j4u9f5+Fcg4SfYIub6ONQy61an6LPty07DADRtYREyn+rmgzopI9KSB2iSZiwhrt +1RHXCQ== +=mM4w +-----END PGP PUBLIC KEY BLOCK----- + +pub 5F69AD087600B22C +sub 0440006D577EAE4B +-----BEGIN PGP PUBLIC KEY BLOCK----- + +xsBNBE7JURcBCADO+9Dc4/JnB+wX+fq+Fr2zUGSPOT6/qjE5kXL4FEbJKsqDSAKG +VnbtRrsIUdmNIFQmz71bBDFhRBbrSrkz927k8eUPhYtxE2NmmWSuKgrjF4qviPQv +m/7SqGx378m/qw4EvpgGUB8EYif98LYdWp5vsU/zx0Ps9auqvetAzJaL9489oE0F +q8FVhve6BMfUUV7zOTCmJnf438YO68upjU0PVBdfFE6Qx4cgCeWbQGy2cooW5azN +iIenhuYU1qikmxMHq2xZzN4uSTWLGDpimPyz+Y1aTSYJ/bgn9gPStbI9sojWo9SS +5gvNK3XqJzMwxwFow86UcIE0vPD2T6ZlBAXRABEBAAHOwE0ETslRFwEIANrjVe+1 +SPgEY9Xfe2+XGN/3q9DHHPP0PXLTGPuWKUmAzkBddNEAXTkPKtyCbH4WXhf4jBZR +yFIpLkoGrW/yIeJZNfoq5xET7ldjDaAMNxb59/78JfyctGbEAgUlzueFONk3Dl0s +DS5GDiWqJg4orqOtUcY+46oo6Z8ncKBbxIvOL+qv1KLWtzIoxjG81PUMk1/CfTij +HgklKtLlbpOHs3m47/28AP2sf6kESKxBwzjgdSyeSci2nCuPoeSfLTJBJlsREL+z +DaJES4G2nj6r/rXX9YHnuH9n0hNxFyhcY0ztsIyz1wqx0kiOYHnLIpUJ+2TcR/YN +mFCYqZ/co98sDMUAEQEAAcLAdgQYAQIACQUCTslRFwIbDAAhCRBfaa0IdgCyLBYh +BKW9Ark+ekBILrHWal9prQh2ALIsrWwH/3s8uN8/gDnbcbTX+7N/ZfQBXJZ+H9GG +ikmYRJE1xoOeEt9MOqZyGDTZfGM/qNKeDGfar7pcRQlMK/A4Nts5E6d1OX8fBkUB +tYanyyjNLlT3yDjO6VaV0SCsgAzNjUZqc4lxS9atN6md5m6lWLAdHghrXuV6LsiK +OS+96htchoCvTvm7mcPI7w146yJRSyCC5+PybG3ult5Y6QASkwI3ZWB0u0PKUoqg +lwWngplu+0Fib2rxQvL32is4YrYaZ+XwoR6u/Bgv0ZvZiypk17Uk17rDb/JfeLqD +n7oW6Hlgi9KOLbRRIg7vwZVo2Ixco7aGxZp5c4zSfaPvn241v813ZcA= +=GS+Y +-----END PGP PUBLIC KEY BLOCK----- + +pub 95962C5E716C39AA +uid Tony Robalik + +sub E04D6BFB21395F43 +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +=r2vu +-----END PGP PUBLIC KEY BLOCK----- diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml new file mode 100644 index 00000000..448c729c --- /dev/null +++ b/gradle/verification-metadata.xml @@ -0,0 +1,165 @@ + + + + false + true + armored + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +