├── .github └── workflows │ ├── ci-build.yml │ └── release.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.MD ├── build.gradle.kts ├── gradle.properties ├── gradle ├── checkstyle │ ├── checkstyle.xml │ └── header.txt ├── plugins │ ├── build.gradle.kts │ ├── settings.gradle.kts │ └── src │ │ └── main │ │ └── kotlin │ │ ├── gradlexbuild.module-mappings.gradle.kts │ │ └── gradlexbuild │ │ └── UniqueModulesPropertiesUpdate.kt └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── renovate.json ├── samples ├── configuration-cache │ ├── app │ │ ├── build.gradle.kts │ │ └── src │ │ │ ├── main │ │ │ └── java │ │ │ │ ├── module-info.java │ │ │ │ └── org │ │ │ │ └── my │ │ │ │ └── app │ │ │ │ └── App.java │ │ │ └── test │ │ │ └── java │ │ │ ├── module-info.java │ │ │ └── org │ │ │ └── my │ │ │ └── app │ │ │ └── test │ │ │ └── AppTest.java │ ├── build-logic │ │ ├── build.gradle.kts │ │ ├── settings.gradle.kts │ │ └── src │ │ │ └── main │ │ │ └── kotlin │ │ │ └── org.my.gradle.java-module.gradle.kts │ ├── build.out │ ├── build.sample.conf │ ├── gradle.properties │ ├── gradle │ │ └── libs.versions.toml │ ├── lib │ │ ├── build.gradle.kts │ │ └── src │ │ │ ├── main │ │ │ └── java │ │ │ │ └── module-info.java │ │ │ └── test │ │ │ └── java │ │ │ └── module-info.java │ └── settings.gradle.kts ├── kotlin │ ├── app │ │ ├── build.gradle.kts │ │ └── src │ │ │ ├── main │ │ │ ├── java │ │ │ │ └── module-info.java │ │ │ └── kotlin │ │ │ │ └── org │ │ │ │ └── my │ │ │ │ └── app │ │ │ │ └── App.kt │ │ │ └── test │ │ │ ├── java │ │ │ └── module-info.java │ │ │ └── kotlin │ │ │ └── org │ │ │ └── my │ │ │ └── app │ │ │ └── test │ │ │ └── AppTest.kt │ ├── build-logic │ │ ├── build.gradle.kts │ │ ├── settings.gradle.kts │ │ └── src │ │ │ └── main │ │ │ └── kotlin │ │ │ └── org.my.gradle.kotlin-java-module.gradle.kts │ ├── build.out │ ├── build.sample.conf │ ├── gradle │ │ └── libs.versions.toml │ ├── lib │ │ ├── build.gradle.kts │ │ └── src │ │ │ ├── main │ │ │ ├── java │ │ │ │ └── module-info.java │ │ │ └── kotlin │ │ │ │ └── org │ │ │ │ └── my │ │ │ │ └── lib │ │ │ │ └── Util.kt │ │ │ └── test │ │ │ └── java │ │ │ └── module-info.java │ └── settings.gradle.kts ├── module-info-dsl-no-platform │ ├── app │ │ ├── build.gradle.kts │ │ └── src │ │ │ ├── main │ │ │ └── java │ │ │ │ ├── module-info.java │ │ │ │ └── org │ │ │ │ └── example │ │ │ │ └── app │ │ │ │ └── App.java │ │ │ ├── test │ │ │ ├── java │ │ │ │ └── org │ │ │ │ │ └── example │ │ │ │ │ └── app │ │ │ │ │ └── AppTest.java │ │ │ └── resources │ │ │ │ └── data.txt │ │ │ └── testFunctional │ │ │ ├── java │ │ │ ├── module-info.java │ │ │ └── org │ │ │ │ └── example │ │ │ │ └── app │ │ │ │ └── test │ │ │ │ └── AppFunctionalTest.java │ │ │ └── resources │ │ │ └── data.txt │ ├── build.gradle.kts │ ├── build.out │ ├── build.sample.conf │ ├── gradle │ │ └── plugins │ │ │ ├── build.gradle.kts │ │ │ ├── settings.gradle.kts │ │ │ └── src │ │ │ └── main │ │ │ └── kotlin │ │ │ ├── org.example.java-base.gradle.kts │ │ │ ├── org.example.java-module-app.gradle.kts │ │ │ ├── org.example.java-module.gradle.kts │ │ │ └── org.example.root.gradle.kts │ ├── lib │ │ ├── build.gradle.kts │ │ └── src │ │ │ ├── main │ │ │ └── java │ │ │ │ ├── module-info.java │ │ │ │ └── org │ │ │ │ └── example │ │ │ │ └── lib │ │ │ │ └── Lib.java │ │ │ ├── test │ │ │ ├── java │ │ │ │ └── org │ │ │ │ │ └── example │ │ │ │ │ └── lib │ │ │ │ │ └── LibTest.java │ │ │ └── resources │ │ │ │ └── data.txt │ │ │ └── testFunctional │ │ │ ├── java │ │ │ ├── module-info.java │ │ │ └── org │ │ │ │ └── example │ │ │ │ └── lib │ │ │ │ └── test │ │ │ │ └── LibFunctionalTest.java │ │ │ └── resources │ │ │ └── data.txt │ └── settings.gradle.kts ├── module-info-dsl │ ├── app │ │ ├── build.gradle.kts │ │ └── src │ │ │ ├── main │ │ │ └── java │ │ │ │ ├── module-info.java │ │ │ │ └── org │ │ │ │ └── example │ │ │ │ └── app │ │ │ │ └── App.java │ │ │ ├── test │ │ │ ├── java │ │ │ │ └── org │ │ │ │ │ └── example │ │ │ │ │ └── app │ │ │ │ │ └── AppTest.java │ │ │ └── resources │ │ │ │ └── data.txt │ │ │ └── testFunctional │ │ │ ├── java │ │ │ ├── module-info.java │ │ │ └── org │ │ │ │ └── example │ │ │ │ └── app │ │ │ │ └── test │ │ │ │ └── AppFunctionalTest.java │ │ │ └── resources │ │ │ └── data.txt │ ├── build.gradle.kts │ ├── build.out │ ├── build.sample.conf │ ├── gradle │ │ └── plugins │ │ │ ├── build.gradle.kts │ │ │ ├── settings.gradle.kts │ │ │ └── src │ │ │ └── main │ │ │ └── kotlin │ │ │ ├── org.example.java-base.gradle.kts │ │ │ ├── org.example.java-module-app.gradle.kts │ │ │ ├── org.example.java-module-versions.gradle.kts │ │ │ ├── org.example.java-module.gradle.kts │ │ │ └── org.example.root.gradle.kts │ ├── lib │ │ ├── build.gradle.kts │ │ └── src │ │ │ ├── main │ │ │ └── java │ │ │ │ ├── module-info.java │ │ │ │ └── org │ │ │ │ └── example │ │ │ │ └── lib │ │ │ │ └── Lib.java │ │ │ ├── test │ │ │ ├── java │ │ │ │ └── org │ │ │ │ │ └── example │ │ │ │ │ └── lib │ │ │ │ │ └── LibTest.java │ │ │ └── resources │ │ │ │ └── data.txt │ │ │ └── testFunctional │ │ │ ├── java │ │ │ ├── module-info.java │ │ │ └── org │ │ │ │ └── example │ │ │ │ └── lib │ │ │ │ └── test │ │ │ │ └── LibFunctionalTest.java │ │ │ └── resources │ │ │ └── data.txt │ ├── settings.gradle.kts │ └── versions │ │ └── build.gradle.kts ├── versions-in-catalog │ ├── app │ │ ├── build.gradle.kts │ │ └── src │ │ │ ├── main │ │ │ └── java │ │ │ │ ├── module-info.java │ │ │ │ └── org │ │ │ │ └── my │ │ │ │ └── app │ │ │ │ └── App.java │ │ │ └── test │ │ │ └── java │ │ │ ├── module-info.java │ │ │ └── org │ │ │ └── my │ │ │ └── app │ │ │ └── test │ │ │ └── AppTest.java │ ├── build-logic │ │ ├── build.gradle.kts │ │ ├── settings.gradle.kts │ │ └── src │ │ │ └── main │ │ │ └── kotlin │ │ │ └── org.my.gradle.java-module.gradle.kts │ ├── build.out │ ├── build.sample.conf │ ├── gradle │ │ └── libs.versions.toml │ ├── lib │ │ ├── build.gradle.kts │ │ └── src │ │ │ ├── main │ │ │ └── java │ │ │ │ └── module-info.java │ │ │ └── test │ │ │ └── java │ │ │ ├── module-info.java │ │ │ └── org │ │ │ └── my │ │ │ └── lib │ │ │ └── test │ │ │ └── MyLibTest.java │ └── settings.gradle.kts └── versions-in-platform │ ├── app │ ├── build.gradle.kts │ └── src │ │ ├── main │ │ └── java │ │ │ ├── module-info.java │ │ │ └── org │ │ │ └── my │ │ │ └── app │ │ │ └── App.java │ │ └── test │ │ └── java │ │ ├── module-info.java │ │ └── org │ │ └── my │ │ └── app │ │ └── test │ │ └── AppTest.java │ ├── build-logic │ ├── build.gradle.kts │ ├── settings.gradle.kts │ └── src │ │ └── main │ │ └── kotlin │ │ ├── org.my.gradle.java-module.gradle.kts │ │ └── org.my.gradle.platform.gradle.kts │ ├── build.out │ ├── build.sample.conf │ ├── lib │ ├── build.gradle.kts │ └── src │ │ ├── main │ │ └── java │ │ │ └── module-info.java │ │ └── test │ │ └── java │ │ ├── module-info.java │ │ └── org │ │ └── my │ │ └── lib │ │ └── test │ │ └── MyLibTest.java │ ├── platform │ └── build.gradle.kts │ └── settings.gradle.kts ├── settings.gradle.kts └── src ├── main ├── java │ └── org │ │ └── gradlex │ │ └── javamodule │ │ └── dependencies │ │ ├── GAV.java │ │ ├── JDKInfo.java │ │ ├── JavaModuleDependenciesExtension.java │ │ ├── JavaModuleDependenciesPlugin.java │ │ ├── JavaModuleVersionsPlugin.java │ │ ├── SharedMappings.java │ │ ├── dsl │ │ ├── AllDirectives.java │ │ ├── GradleOnlyDirectives.java │ │ └── ModuleVersions.java │ │ ├── initialization │ │ ├── Directory.java │ │ ├── JavaModuleDependenciesSettingsPlugin.java │ │ ├── JavaModulesExtension.java │ │ ├── Module.java │ │ └── RootPluginsExtension.java │ │ ├── internal │ │ ├── bridges │ │ │ ├── DependencyAnalysisBridge.java │ │ │ └── ExtraJavaModuleInfoBridge.java │ │ ├── diagnostics │ │ │ ├── AsciiModuleDependencyReportRenderer.java │ │ │ ├── RenderableJavaModuleResult.java │ │ │ ├── RenderableModuleDependencyResult.java │ │ │ └── StyledNodeRenderer.java │ │ ├── dsl │ │ │ ├── AllDirectivesInternal.java │ │ │ └── GradleOnlyDirectivesInternal.java │ │ └── utils │ │ │ ├── DependencyDeclarationsUtil.java │ │ │ ├── ModuleInfo.java │ │ │ ├── ModuleInfoCache.java │ │ │ ├── ModuleInfoClassCreator.java │ │ │ ├── ModuleJar.java │ │ │ ├── ModuleNamingUtil.java │ │ │ ├── TaskConfigurationUtil.java │ │ │ ├── ValueModuleDirectoryListing.java │ │ │ └── ValueSourceModuleInfo.java │ │ └── tasks │ │ ├── BuildFileDependenciesGenerate.java │ │ ├── CatalogGenerate.java │ │ ├── ModuleDependencyReport.java │ │ ├── ModuleDirectivesOrderingCheck.java │ │ ├── ModuleDirectivesScopeCheck.java │ │ ├── ModuleInfoGenerate.java │ │ ├── ModulePathAnalysis.java │ │ ├── ModuleVersionRecommendation.java │ │ └── SyntheticModuleInfoFoldersGenerate.java └── resources │ └── org │ └── gradlex │ └── javamodule │ └── dependencies │ ├── modules.properties │ └── unique_modules.properties └── test └── java └── org └── gradlex └── javamodule └── dependencies └── test ├── CustomizationTest.java ├── ModuleInfoParseTest.java ├── WarningsTest.java ├── configcache └── ConfigurationCacheTest.java ├── extension └── ExtensionTest.java ├── fixture ├── Directory.java ├── GradleBuild.java ├── Io.java └── WritableFile.java ├── groupmapping └── GroupMappingTest.java ├── initialization ├── SettingsPluginIncludeTest.java └── SettingsPluginTest.java ├── localmodules └── LocalModuleMappingsTest.java ├── runtime └── RequiresRuntimeTest.java ├── samples ├── PluginBuildLocationSampleModifier.java └── SamplesTest.java ├── tasks ├── BasicFunctionalityTest.java └── OrderingCheckTest.java └── variants └── NonMainVariantsTest.java /.github/workflows/ci-build.yml: -------------------------------------------------------------------------------- 1 | name: Build Plugin 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | gradle-build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: git clone 14 | uses: actions/checkout@v4 15 | - name: Set up JDK 16 | uses: actions/setup-java@v4 17 | with: 18 | distribution: temurin 19 | java-version: 17 20 | - name: Set up Gradle 21 | uses: gradle/actions/setup-gradle@v4 22 | - run: "./gradlew build" -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Publish Release 2 | on: 3 | push: 4 | tags: 5 | - 'v*' 6 | jobs: 7 | release-build: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: git clone 11 | uses: actions/checkout@v4 12 | - name: Set up JDK 13 | uses: actions/setup-java@v4 14 | with: 15 | distribution: temurin 16 | java-version: 17 17 | - name: Set up Gradle 18 | uses: gradle/actions/setup-gradle@v4 19 | - run: "./gradlew :publishPlugin --no-configuration-cache" 20 | env: 21 | SIGNING_KEY: ${{ secrets.SIGNING_KEY }} 22 | SIGNING_PASSPHRASE: ${{ secrets.SIGNING_PASSPHRASE }} 23 | GRADLE_PUBLISH_KEY: ${{ secrets.GRADLE_PUBLISH_KEY }} 24 | GRADLE_PUBLISH_SECRET: ${{ secrets.GRADLE_PUBLISH_SECRET }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | .idea 3 | build 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Java Module Dependencies Gradle Plugin - Changelog 2 | 3 | ## Version 1.9 4 | * [#188](https://github.com/gradlex-org/java-module-dependencies/pull/188) Add `exportsTo` and `opensTo` statements to Module Info DSL 5 | 6 | ## Version 1.8.1 7 | * Update module name mappings 8 | * Update 'org.ow2.asm:asm' to 9.8 9 | 10 | ## Version 1.8 11 | * [#136](https://github.com/gradlex-org/java-module-dependencies/pull/136) Support hierarchical project paths in Settings DSL 12 | * [#141](https://github.com/gradlex-org/java-module-dependencies/pull/141) Introduce `org.gradlex.java-module-dependencies.register-help-tasks` property 13 | * [#127](https://github.com/gradlex-org/java-module-dependencies/issues/127) Less configuration cache misses when modifying `module-info.java` (Thanks [TheGoesen](https://github.com/TheGoesen)) 14 | * [#128](https://github.com/gradlex-org/java-module-dependencies/issues/128) Less configuration cache misses when using Settings plugin (Thanks [TheGoesen](https://github.com/TheGoesen)) 15 | * [#135](https://github.com/gradlex-org/java-module-dependencies/issues/135) Improve performance of ApplyPluginsAction 16 | 17 | ## Version 1.7.1 18 | * Update module name mappings 19 | 20 | ## Version 1.7 21 | * [#112](https://github.com/gradlex-org/java-module-dependencies/issues/112) Settings plugin to configure module locations and identity 22 | 23 | ## Version 1.6.6 24 | * [#113](https://github.com/gradlex-org/java-module-dependencies/issues/113) Fix: Do not fail for duplicated project names (Thanks [TheGoesen](https://github.com/TheGoesen)) 25 | * [#111](https://github.com/gradlex-org/java-module-dependencies/issues/111) Fix: Do not use 'MapProperty.unset' (Thanks [TheGoesen](https://github.com/TheGoesen)) 26 | * [#112](https://github.com/gradlex-org/java-module-dependencies/issues/112) Improve compatibility with Project Isolation 27 | 28 | ## Version 1.6.5 29 | * [#104](https://github.com/gradlex-org/java-module-dependencies/issues/104) Fix: ModuleDependencyReport task does not correctly track inputs 30 | 31 | ## Version 1.6.4 32 | * Enhance output of 'moduleDependencies' task 33 | * Update 'org.ow2.asm:asm' to 9.7 34 | 35 | ## Version 1.6.3 36 | * Update module name mappings 37 | 38 | ## Version 1.6.2 39 | * [#90](https://github.com/gradlex-org/java-module-dependencies/issues/90) Fix: 'moduleNamePrefixToGroup' mapping uses best fit instead of first match 40 | * [#91](https://github.com/gradlex-org/java-module-dependencies/issues/91) Fix: handle duplicated module names in 'extra-module-info' bridge 41 | 42 | ## Version 1.6.1 43 | * Fix in setup of new utility tasks 44 | 45 | ## Version 1.6 46 | * Add more utility tasks to migrate from/to module-info based dependencies 47 | * Additional notation for module version DSL 48 | 49 | ## Version 1.5.2 50 | * Fix for requires /*runtime*/ support 51 | 52 | ## Version 1.5.1 53 | * Make `module-info.java` analysis tasks cacheable 54 | * Make `recommendModuleVersions` configuration cache compatible 55 | * Further tweak `requires /*runtime*/` support 56 | 57 | ## Version 1.5 58 | * [#67](https://github.com/gradlex-org/java-module-dependencies/issues/67) Support local `modules.properties` for custom mappings 59 | * [#65](https://github.com/gradlex-org/java-module-dependencies/issues/65) Error if a local Module Name does not match project name 60 | * [#24](https://github.com/gradlex-org/java-module-dependencies/issues/24) Improve support for `requires /*runtime*/` 61 | 62 | ## Version 1.4.3 63 | * Support '.' to '-' conversion in 'moduleNamePrefixToGroup' 64 | * Fix issue in integration with 'extra-module-info' 65 | * Improve support for Capability Coordinates in mappings 66 | * Remove 'version missing in catalog' warning (triggered when catalog is used for different things) 67 | 68 | ## Version 1.4.2 69 | * Fix Gradle 8.6 compatibility 70 | 71 | ## Version 1.4.1 72 | * [#47](https://github.com/gradlex-org/java-module-dependencies/issues/47) Fix Gradle 8.3 compatibility 73 | 74 | ## Version 1.4 75 | * [#31](https://github.com/gradlex-org/java-module-dependencies/issues/31) DSL for module dependencies that cannot be defined in module-info 76 | * [#45](https://github.com/gradlex-org/java-module-dependencies/issues/45) Support Capability Coordinates in mappings 77 | 78 | ## Version 1.3.1 79 | * Fix integration with analysis plugin if root projects are involved 80 | * Fix in module name calculation for additional source sets 81 | * Improve dependency analysis reporting for source sets without module-info.java 82 | 83 | ## Version 1.3 84 | * [#25](https://github.com/gradlex-org/java-module-dependencies/issues/25) Add 'moduleDependencies' help task - similar to 'dependencies' but with Module Names 85 | * [#27](https://github.com/gradlex-org/java-module-dependencies/issues/27) Add task to check scopes of requires directives (by integrating with 'dependency-analysis' plugin) 86 | * [#22](https://github.com/gradlex-org/java-module-dependencies/issues/22) Add task to check ordering (alphabetical) of requires directives 87 | * [#29](https://github.com/gradlex-org/java-module-dependencies/issues/29) Add convenience to enable consistent resolution 88 | * [#30](https://github.com/gradlex-org/java-module-dependencies/issues/30) Add an 'analyse only' mode 89 | * [#23](https://github.com/gradlex-org/java-module-dependencies/issues/23) Consider 'moduleNamePrefixToGroup' entries in: ga(), gav(), moduleName() 90 | 91 | ## Version 1.2 92 | * [#20](https://github.com/gradlex-org/java-module-dependencies/issues/20) Improve support for `requires /*runtime*/` 93 | 94 | ## Version 1.1 95 | * [#19](https://github.com/gradlex-org/java-module-dependencies/issues/19) Support for `requires /*runtime*/` 96 | 97 | ## Version 1.0 98 | * Moved project to [GradleX](https://gradlex.org) - new plugin ID: `org.gradlex.java-module-dependencies` 99 | 100 | ## Versions 0.11 101 | * [#18](https://github.com/gradlex-org/java-module-dependencies/issues/18) Fix bug with single line comments in module-info.java 102 | * More mappings for Modules on Maven Central 103 | 104 | ## Versions 0.10 105 | * `moduleNamePrefixToGroup.put("..", "..")` to register mappings for a group of modules 106 | * More mappings for Modules on Maven Central 107 | 108 | ## Versions 0.8 109 | * More mappings for Modules on Maven Central 110 | 111 | ## Versions 0.1 - 0.7 112 | * Initial features added 113 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("gradlexbuild.module-mappings") 3 | id("org.gradlex.internal.plugin-publish-conventions") version "0.6" 4 | } 5 | 6 | group = "org.gradlex" 7 | version = "1.9" 8 | 9 | java { 10 | toolchain.languageVersion = JavaLanguageVersion.of(17) 11 | } 12 | 13 | tasks.compileJava { 14 | options.release = 8 15 | } 16 | 17 | tasks.withType().configureEach { 18 | options { 19 | this as StandardJavadocDocletOptions 20 | encoding = "UTF-8" 21 | addStringOption("Xdoclint:all,-missing", "-quiet") 22 | addStringOption("Xwerror", "-quiet") 23 | } 24 | } 25 | 26 | configurations.compileClasspath { 27 | // Allow Java 11 dependencies on compile classpath 28 | attributes.attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 11) 29 | } 30 | 31 | dependencies { 32 | implementation("org.ow2.asm:asm:9.8") 33 | 34 | compileOnly("org.gradlex:extra-java-module-info:1.12") 35 | compileOnly("com.autonomousapps:dependency-analysis-gradle-plugin:2.18.0") 36 | 37 | testImplementation("org.assertj:assertj-core:3.27.3") 38 | testImplementation("org.gradle.exemplar:samples-check:1.0.3") 39 | testRuntimeOnly("org.junit.vintage:junit-vintage-engine") 40 | } 41 | 42 | pluginPublishConventions { 43 | id("${project.group}.${project.name}") 44 | implementationClass("org.gradlex.javamodule.dependencies.JavaModuleDependenciesPlugin") 45 | displayName("Java Module Dependencies Gradle Plugin") 46 | description("A plugin that makes Gradle respect the dependencies defined in 'module-info.java' files.") 47 | tags("gradlex", "java", "modularity", "jigsaw", "jpms", "dependencies", "versions") 48 | gitHub("https://github.com/gradlex-org/java-module-dependencies") 49 | developer { 50 | id.set("jjohannes") 51 | name.set("Jendrik Johannes") 52 | email.set("jendrik@gradlex.org") 53 | } 54 | } 55 | 56 | gradlePlugin.plugins.create("java-module-versions") { 57 | id = "${project.group}.${name}" 58 | implementationClass = "org.gradlex.javamodule.dependencies.JavaModuleVersionsPlugin" 59 | displayName = "Java Module Versions Gradle Plugin" 60 | description = "A plugin that makes Gradle respect the dependencies defined in 'module-info.java' files." 61 | tags = listOf("gradlex", "java", "modularity", "jigsaw", "jpms", "dependencies", "versions") 62 | } 63 | 64 | tasks.test { 65 | useJUnitPlatform() 66 | maxParallelForks = 4 67 | inputs.dir(layout.projectDirectory.dir("samples")) 68 | } 69 | 70 | testing.suites.named("test") { 71 | useJUnitJupiter() 72 | listOf("7.4", "7.6.5", "8.0.2").forEach { gradleVersionUnderTest -> 73 | targets.register("test${gradleVersionUnderTest}") { 74 | testTask { 75 | group = LifecycleBasePlugin.VERIFICATION_GROUP 76 | description = "Runs tests against Gradle $gradleVersionUnderTest" 77 | systemProperty("gradleVersionUnderTest", gradleVersionUnderTest) 78 | exclude("**/*SamplesTest.class") // Not yet cross-version ready 79 | exclude("**/initialization/**") // Settings plugin only for Gradle 8.8+ 80 | if (gradleVersionUnderTest == "7.4") { 81 | // Configuration cache only "reliable" since 7.6 (?) 82 | // https://github.com/gradlex-org/java-module-dependencies/issues/129 83 | exclude("**/configcache/**") 84 | } 85 | } 86 | } 87 | } 88 | targets.all { 89 | testTask { 90 | maxParallelForks = 4 91 | inputs.dir(layout.projectDirectory.dir("samples")) 92 | inputs.dir("samples") 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.caching=true 2 | org.gradle.configuration-cache=true 3 | systemProp.org.gradle.kotlin.dsl.skipMetadataVersionCheck=false -------------------------------------------------------------------------------- /gradle/checkstyle/checkstyle.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /gradle/checkstyle/header.txt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright the GradleX team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ -------------------------------------------------------------------------------- /gradle/plugins/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | `kotlin-dsl` 3 | } 4 | -------------------------------------------------------------------------------- /gradle/plugins/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | dependencyResolutionManagement { 2 | repositories.gradlePluginPortal() 3 | } 4 | -------------------------------------------------------------------------------- /gradle/plugins/src/main/kotlin/gradlexbuild.module-mappings.gradle.kts: -------------------------------------------------------------------------------- 1 | import gradlexbuild.UniqueModulesPropertiesUpdate 2 | import org.gradle.api.internal.project.ProjectInternal 3 | 4 | plugins { 5 | id("java") 6 | } 7 | 8 | val detachedResolver: ProjectInternal.DetachedResolver = (project as ProjectInternal).newDetachedResolver() 9 | detachedResolver.repositories.ivy { 10 | name = "Modules Properties Repository" 11 | url = project.uri("https://raw.githubusercontent.com/sormuras/modules/main/com.github.sormuras.modules") 12 | metadataSources.artifact() 13 | patternLayout { 14 | artifact("[organisation]/[module].properties") 15 | ivy("[module]/[revision]/ivy.xml") 16 | setM2compatible(true) 17 | } 18 | } 19 | val modulePropertiesScope = detachedResolver.configurations.dependencyScope("moduleProperties") 20 | val modulePropertiesPath = detachedResolver.configurations.resolvable("modulePropertiesPath") { 21 | extendsFrom(modulePropertiesScope.get()) 22 | } 23 | val dep = detachedResolver.dependencies.add(modulePropertiesScope.name, "com.github.sormuras.modules:modules:1") 24 | (dep as ExternalModuleDependency).isChanging = true 25 | 26 | val updateUniqueModulesProperties = tasks.register("updateUniqueModulesProperties") { 27 | skipUpdate.set(providers.environmentVariable("CI").getOrElse("false").toBoolean()) 28 | modulesProperties.from(modulePropertiesPath) 29 | uniqueModulesProperties.set(layout.projectDirectory.file( 30 | "src/main/resources/org/gradlex/javamodule/dependencies/unique_modules.properties") 31 | ) 32 | } 33 | 34 | sourceSets.main { 35 | resources.setSrcDirs(listOf(updateUniqueModulesProperties.map { 36 | it.uniqueModulesProperties.get().asFile.parentFile.parentFile.parentFile.parentFile.parentFile 37 | })) 38 | } 39 | 40 | -------------------------------------------------------------------------------- /gradle/plugins/src/main/kotlin/gradlexbuild/UniqueModulesPropertiesUpdate.kt: -------------------------------------------------------------------------------- 1 | package gradlexbuild 2 | 3 | import org.gradle.api.DefaultTask 4 | import org.gradle.api.file.ConfigurableFileCollection 5 | import org.gradle.api.file.ProjectLayout 6 | import org.gradle.api.file.RegularFileProperty 7 | import org.gradle.api.provider.Property 8 | import org.gradle.api.tasks.Input 9 | import org.gradle.api.tasks.InputFiles 10 | import org.gradle.api.tasks.OutputFile 11 | import org.gradle.api.tasks.TaskAction 12 | import org.gradle.util.internal.VersionNumber 13 | import java.util.Properties 14 | import javax.inject.Inject 15 | 16 | abstract class UniqueModulesPropertiesUpdate : DefaultTask() { 17 | 18 | @get:Inject 19 | abstract val layout: ProjectLayout 20 | 21 | @get:Input 22 | abstract val skipUpdate: Property 23 | 24 | @get:InputFiles 25 | abstract val modulesProperties: ConfigurableFileCollection 26 | 27 | @get:OutputFile 28 | abstract val uniqueModulesProperties: RegularFileProperty 29 | 30 | @TaskAction 31 | fun convert() { 32 | if (skipUpdate.get()) { 33 | return 34 | } 35 | 36 | val modulesToRepoLocation = Properties() 37 | modulesToRepoLocation.load(modulesProperties.singleFile.inputStream()) 38 | val modules = modulesToRepoLocation.toSortedMap { e1, e2 -> e1.toString().compareTo(e2.toString()) }.map { entry -> 39 | val split = entry.value.toString().split("/") 40 | val group = split.subList(4, split.size - 3).joinToString(".") 41 | val name = split[split.size - 3] 42 | // Special handling of "wrong" entries 43 | .replace("-debug-jdk18on", "-jdk18on") 44 | val version = split[split.size - 2] 45 | Module(entry.key.toString(), "$group:$name", version) 46 | }.groupBy { it.ga }.values.map { moduleList -> 47 | moduleList.maxBy { VersionNumber.parse(it.version) } 48 | }.sortedBy { it.name } 49 | 50 | val modulesToCoordinates = modules.map { "${it.name}=${it.ga}\n" } 51 | uniqueModulesProperties.get().asFile.writeText(modulesToCoordinates.joinToString("").trim()) 52 | } 53 | 54 | data class Module(val name: String, val ga: String, val version: String) 55 | } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gradlex-org/java-module-dependencies/050a033b81bfccfb553873cf13e0c352a1f640e5/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionSha256Sum=845952a9d6afa783db70bb3b0effaae45ae5542ca2bb7929619e8af49cb634cf 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip 5 | networkTimeout=10000 6 | validateDistributionUrl=true 7 | zipStoreBase=GRADLE_USER_HOME 8 | zipStorePath=wrapper/dists 9 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH= 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:recommended" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /samples/configuration-cache/app/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.my.gradle.java-module") 3 | id("application") 4 | } 5 | 6 | application { 7 | applicationDefaultJvmArgs = listOf("-ea") 8 | mainClass.set("org.my.app.App") 9 | mainModule.set("org.my.app") 10 | } 11 | 12 | dependencies { 13 | runtimeOnly(javaModuleDependencies.gav("org.slf4j.simple")) 14 | } 15 | -------------------------------------------------------------------------------- /samples/configuration-cache/app/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | module org.my.app { 2 | requires org.slf4j; 3 | requires org.my.lib; 4 | requires static org.apache.xmlbeans; 5 | 6 | exports org.my.app; 7 | } -------------------------------------------------------------------------------- /samples/configuration-cache/app/src/main/java/org/my/app/App.java: -------------------------------------------------------------------------------- 1 | package org.my.app; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import org.apache.xmlbeans.impl.tool.XMLBean; 5 | import org.slf4j.spi.LoggerFactoryBinder; 6 | 7 | public class App { 8 | public static void main(String[] args) { 9 | doWork(); 10 | } 11 | 12 | public static boolean doWork() { 13 | ObjectMapper om = new ObjectMapper(); 14 | if (!om.canSerialize(LoggerFactoryBinder.class)) { 15 | throw new RuntimeException("Boom!"); 16 | } 17 | System.out.println(App.class.getModule().getName()); 18 | 19 | try { 20 | new XMLBean(); 21 | throw new RuntimeException("Boom!"); 22 | } catch (NoClassDefFoundError e) { 23 | // This is expected at runtime! 24 | } 25 | return true; 26 | } 27 | } -------------------------------------------------------------------------------- /samples/configuration-cache/app/src/test/java/module-info.java: -------------------------------------------------------------------------------- 1 | open module org.my.app.test { 2 | requires org.my.app; 3 | requires org.junit.jupiter.api; 4 | } -------------------------------------------------------------------------------- /samples/configuration-cache/app/src/test/java/org/my/app/test/AppTest.java: -------------------------------------------------------------------------------- 1 | package org.my.app.test; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.my.app.App; 5 | 6 | import static org.junit.jupiter.api.Assertions.assertTrue; 7 | 8 | public class AppTest { 9 | 10 | @Test 11 | public void appDoesNotExplode() { 12 | assertTrue(App.doWork()); 13 | } 14 | 15 | } -------------------------------------------------------------------------------- /samples/configuration-cache/build-logic/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | `kotlin-dsl` 3 | } 4 | 5 | dependencies { 6 | implementation("org.gradlex:java-module-dependencies:1.9") 7 | } -------------------------------------------------------------------------------- /samples/configuration-cache/build-logic/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | dependencyResolutionManagement { 2 | repositories.gradlePluginPortal() 3 | } 4 | 5 | // This is for testing against the latest version of the plugin, remove if you copied this for a real project 6 | includeBuild(extra.properties["pluginLocation"] ?: rootDir.parentFile.parentFile.parent) 7 | 8 | -------------------------------------------------------------------------------- /samples/configuration-cache/build-logic/src/main/kotlin/org.my.gradle.java-module.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("java") 3 | id("org.gradlex.java-module-dependencies") 4 | } 5 | 6 | group = "org.my" 7 | 8 | tasks.test { 9 | useJUnitPlatform() 10 | jvmArgs("-Dorg.slf4j.simpleLogger.defaultLogLevel=error") 11 | } 12 | 13 | dependencies { 14 | javaModuleDependencies { 15 | testRuntimeOnly(gav("org.junit.jupiter.engine")) 16 | testRuntimeOnly(gav("org.junit.platform.launcher")) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /samples/configuration-cache/build.out: -------------------------------------------------------------------------------- 1 | > Task :build-logic:jar 2 | > Task :lib:compileJava 3 | > Task :lib:processResources NO-SOURCE 4 | > Task :lib:classes 5 | > Task :lib:jar 6 | > Task :app:compileJava 7 | > Task :app:processResources NO-SOURCE 8 | > Task :app:classes 9 | > Task :app:jar 10 | > Task :app:startScripts 11 | > Task :app:distTar 12 | > Task :app:distZip 13 | > Task :app:assemble 14 | > Task :app:compileTestJava 15 | > Task :app:processTestResources NO-SOURCE 16 | > Task :app:testClasses 17 | > Task :app:test 18 | > Task :app:check 19 | > Task :app:build 20 | > Task :lib:assemble 21 | > Task :lib:compileTestJava 22 | > Task :lib:processTestResources NO-SOURCE 23 | > Task :lib:testClasses 24 | > Task :lib:test 25 | > Task :lib:check 26 | > Task :lib:build 27 | 28 | > Task :app:run 29 | org.my.app -------------------------------------------------------------------------------- /samples/configuration-cache/build.sample.conf: -------------------------------------------------------------------------------- 1 | executable: gradlew 2 | expected-output-file: build.out 3 | allow-disordered-output: true 4 | -------------------------------------------------------------------------------- /samples/configuration-cache/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.configuration-cache=true 2 | -------------------------------------------------------------------------------- /samples/configuration-cache/gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | org_apache_xmlbeans = "5.0.1" 3 | com_fasterxml_jackson_databind = "2.12.5" 4 | org_slf4j = "1.7.32" 5 | org_slf4j_simple = "1.7.32" 6 | 7 | org_junit_jupiter_api = "5.7.2" 8 | org_junit_jupiter_engine = "5.7.2" 9 | -------------------------------------------------------------------------------- /samples/configuration-cache/lib/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.my.gradle.java-module") 3 | id("java-library") 4 | } 5 | -------------------------------------------------------------------------------- /samples/configuration-cache/lib/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | module org.my.lib { 2 | requires transitive com.fasterxml.jackson.databind; 3 | } -------------------------------------------------------------------------------- /samples/configuration-cache/lib/src/test/java/module-info.java: -------------------------------------------------------------------------------- 1 | open module org.my.lib.test { 2 | requires org.my.lib; 3 | requires org.junit.jupiter.api; 4 | } 5 | -------------------------------------------------------------------------------- /samples/configuration-cache/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | includeBuild("build-logic") 3 | } 4 | dependencyResolutionManagement { 5 | repositories.mavenCentral() 6 | } 7 | 8 | include("app", "lib") 9 | 10 | -------------------------------------------------------------------------------- /samples/kotlin/app/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.my.gradle.kotlin-java-module") 3 | id("application") 4 | } 5 | 6 | application { 7 | applicationDefaultJvmArgs = listOf("-ea") 8 | mainClass.set("org.my.app.AppKt") 9 | mainModule.set("org.my.app") 10 | } 11 | 12 | dependencies { 13 | runtimeOnly(javaModuleDependencies.gav("org.slf4j.simple")) 14 | } 15 | -------------------------------------------------------------------------------- /samples/kotlin/app/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | module org.my.app { 2 | requires org.slf4j; 3 | requires org.my.lib; 4 | requires static org.apache.xmlbeans; 5 | 6 | requires kotlin.stdlib; 7 | 8 | exports org.my.app; 9 | } -------------------------------------------------------------------------------- /samples/kotlin/app/src/main/kotlin/org/my/app/App.kt: -------------------------------------------------------------------------------- 1 | package org.my.app 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper 4 | import org.apache.xmlbeans.impl.tool.XMLBean 5 | import org.slf4j.spi.LoggerFactoryBinder 6 | 7 | fun main() { 8 | App.doWork() 9 | } 10 | 11 | object App { 12 | fun doWork(): Boolean { 13 | val om = ObjectMapper() 14 | if (!om.canSerialize(LoggerFactoryBinder::class.java)) { 15 | throw RuntimeException("Boom!") 16 | } 17 | println(App::class.java.module.name) 18 | try { 19 | XMLBean() 20 | throw RuntimeException("Boom!") 21 | } catch (e: NoClassDefFoundError) { 22 | // This is expected at runtime! 23 | } 24 | return true 25 | } 26 | } -------------------------------------------------------------------------------- /samples/kotlin/app/src/test/java/module-info.java: -------------------------------------------------------------------------------- 1 | open module org.my.app.test { 2 | requires org.my.app; 3 | requires org.junit.jupiter.api; 4 | } -------------------------------------------------------------------------------- /samples/kotlin/app/src/test/kotlin/org/my/app/test/AppTest.kt: -------------------------------------------------------------------------------- 1 | package org.my.app.test 2 | 3 | import org.junit.jupiter.api.Test 4 | import org.my.app.App 5 | 6 | import org.junit.jupiter.api.Assertions.assertTrue 7 | 8 | class AppTest { 9 | 10 | @Test 11 | fun appDoesNotExplode() { 12 | assertTrue(App.doWork()); 13 | } 14 | 15 | } -------------------------------------------------------------------------------- /samples/kotlin/build-logic/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | `kotlin-dsl` 3 | } 4 | 5 | dependencies { 6 | implementation("org.gradlex:java-module-dependencies:1.9") 7 | implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:2.1.21") 8 | } -------------------------------------------------------------------------------- /samples/kotlin/build-logic/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | dependencyResolutionManagement { 2 | repositories.gradlePluginPortal() 3 | } 4 | 5 | // This is for testing against the latest version of the plugin, remove if you copied this for a real project 6 | includeBuild(extra.properties["pluginLocation"] ?: rootDir.parentFile.parentFile.parent) 7 | 8 | -------------------------------------------------------------------------------- /samples/kotlin/build-logic/src/main/kotlin/org.my.gradle.kotlin-java-module.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.gradlex.java-module-dependencies") 3 | id("org.jetbrains.kotlin.jvm") 4 | } 5 | 6 | group = "org.my" 7 | val moduleName = "${project.group}.${project.name}" 8 | val testModuleName = "${moduleName}.test" 9 | 10 | val javaLanguageVersion = JavaLanguageVersion.of(11) 11 | java { 12 | toolchain.languageVersion.set(javaLanguageVersion) 13 | } 14 | kotlin.jvmToolchain { 15 | languageVersion.set(javaLanguageVersion) 16 | } 17 | 18 | // this is needed because we have a separate compile step in this example with the 'module-info.java' is in 'main/java' and the Kotlin code is in 'main/kotlin' 19 | tasks.compileJava { 20 | // Compiling module-info in the 'main/java' folder needs to see already compiled Kotlin code 21 | options.compilerArgs = listOf("--patch-module", "$moduleName=${sourceSets.main.get().output.asPath}") 22 | } 23 | 24 | // Testing with JUnit5 (which is available in modules) 25 | tasks.compileTestKotlin { 26 | // Make sure only module Jars are on the classpath and not the classes folders of the current project 27 | libraries.setFrom(configurations.testCompileClasspath.get().filter { f -> f.isFile }) 28 | } 29 | tasks.compileTestJava { 30 | // Compiling module-info in the 'test/java' folder needs to see already compiled Kotlin code 31 | options.compilerArgs = listOf("--patch-module", "$testModuleName=${sourceSets.test.get().output.asPath}") 32 | // Make sure only module Jars are on the classpath and not the classes folders of the current project 33 | classpath = configurations.testCompileClasspath.get() 34 | } 35 | val testJar = tasks.register(sourceSets.test.get().jarTaskName) { 36 | // Package test code/resources as Jar so that they are a proper module at runtime 37 | archiveClassifier.set("tests") 38 | from(sourceSets.test.get().output) 39 | } 40 | tasks.test { 41 | classpath = configurations.testRuntimeClasspath.get().filter { f -> f.isFile } + files(testJar) 42 | useJUnitPlatform() 43 | jvmArgs("-Dorg.slf4j.simpleLogger.defaultLogLevel=error") 44 | } 45 | 46 | tasks.test { 47 | useJUnitPlatform() 48 | } 49 | 50 | dependencies { 51 | testRuntimeOnly(javaModuleDependencies.gav("org.junit.jupiter.engine")) 52 | testRuntimeOnly(javaModuleDependencies.gav("org.junit.platform.launcher")) 53 | } 54 | -------------------------------------------------------------------------------- /samples/kotlin/build.out: -------------------------------------------------------------------------------- 1 | > Task :app:run 2 | org.my.app 3 | -------------------------------------------------------------------------------- /samples/kotlin/build.sample.conf: -------------------------------------------------------------------------------- 1 | executable: gradlew 2 | expected-output-file: build.out -------------------------------------------------------------------------------- /samples/kotlin/gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | kotlin_stdlib = "1.6.10" 3 | 4 | com_fasterxml_jackson_databind = "2.12.5" 5 | org_apache_xmlbeans = "5.0.1" 6 | org_slf4j = "1.7.28" 7 | org_slf4j_simple = "1.7.28" 8 | 9 | org_junit_jupiter_api = "5.7.2" 10 | org_junit_jupiter_engine = "5.7.2" 11 | -------------------------------------------------------------------------------- /samples/kotlin/lib/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.my.gradle.kotlin-java-module") 3 | id("java-library") 4 | } -------------------------------------------------------------------------------- /samples/kotlin/lib/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | module org.my.lib { 2 | requires transitive com.fasterxml.jackson.databind; 3 | 4 | requires kotlin.stdlib; 5 | 6 | // JDK modules 7 | requires java.logging; 8 | requires jdk.charsets; 9 | } -------------------------------------------------------------------------------- /samples/kotlin/lib/src/main/kotlin/org/my/lib/Util.kt: -------------------------------------------------------------------------------- 1 | package org.my.lib 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper 4 | 5 | class Util { 6 | 7 | fun doWork() { 8 | ObjectMapper() 9 | } 10 | } -------------------------------------------------------------------------------- /samples/kotlin/lib/src/test/java/module-info.java: -------------------------------------------------------------------------------- 1 | open module org.my.lib.test { 2 | requires org.my.lib; 3 | requires org.junit.jupiter.api; 4 | } 5 | -------------------------------------------------------------------------------- /samples/kotlin/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | includeBuild("build-logic") 3 | } 4 | dependencyResolutionManagement { 5 | repositories.mavenCentral() 6 | } 7 | 8 | include("app", "lib") 9 | -------------------------------------------------------------------------------- /samples/module-info-dsl-no-platform/app/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.example.java-module-app") 3 | } 4 | 5 | application { 6 | applicationDefaultJvmArgs = listOf("-ea") 7 | mainClass.set("org.example.app.App") 8 | mainModule.set("org.example.app") 9 | } 10 | 11 | mainModuleInfo { 12 | runtimeOnly("org.slf4j.simple") 13 | } 14 | 15 | testModuleInfo { 16 | requires("org.junit.jupiter.api") 17 | } 18 | 19 | moduleInfo { 20 | version("org.slf4j", "2.0.7") 21 | version("org.slf4j.simple", "2.0.7") 22 | version("com.fasterxml.jackson.databind", "2.14.0") 23 | version("org.junit.jupiter.api", "5.9.3") 24 | } 25 | -------------------------------------------------------------------------------- /samples/module-info-dsl-no-platform/app/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | module org.example.app { 2 | requires org.example.lib; 3 | requires org.slf4j; 4 | 5 | exports org.example.app; 6 | } -------------------------------------------------------------------------------- /samples/module-info-dsl-no-platform/app/src/main/java/org/example/app/App.java: -------------------------------------------------------------------------------- 1 | package org.example.app; 2 | 3 | import org.example.lib.Lib; 4 | import org.slf4j.LoggerFactory; 5 | 6 | public class App { 7 | 8 | public static void main(String[] args) { 9 | LoggerFactory.getLogger(App.class).info("App running..."); 10 | doWork(); 11 | } 12 | 13 | public static void doWork() { 14 | new Lib(); 15 | } 16 | } -------------------------------------------------------------------------------- /samples/module-info-dsl-no-platform/app/src/test/java/org/example/app/AppTest.java: -------------------------------------------------------------------------------- 1 | package org.example.app; 2 | 3 | import org.example.lib.Lib; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | 9 | import static java.nio.charset.StandardCharsets.UTF_8; 10 | import static org.junit.jupiter.api.Assertions.assertEquals; 11 | import static org.junit.jupiter.api.Assertions.assertNotNull; 12 | 13 | public class AppTest { 14 | 15 | @Test 16 | public void testApp() throws IOException { 17 | assertEquals("org.example.lib", Lib.class.getModule().getName()); 18 | assertEquals("org.example.app", App.class.getModule().getName()); 19 | assertEquals("org.example.app", AppTest.class.getModule().getName()); 20 | try (InputStream is = AppTest.class.getResourceAsStream("/data.txt")) { 21 | assertNotNull(is); 22 | assertEquals("ABC", new String(is.readAllBytes(), UTF_8)); 23 | } 24 | } 25 | 26 | } -------------------------------------------------------------------------------- /samples/module-info-dsl-no-platform/app/src/test/resources/data.txt: -------------------------------------------------------------------------------- 1 | ABC -------------------------------------------------------------------------------- /samples/module-info-dsl-no-platform/app/src/testFunctional/java/module-info.java: -------------------------------------------------------------------------------- 1 | open module org.example.app.test.functional { 2 | requires org.example.app; 3 | requires org.junit.jupiter.api; 4 | } -------------------------------------------------------------------------------- /samples/module-info-dsl-no-platform/app/src/testFunctional/java/org/example/app/test/AppFunctionalTest.java: -------------------------------------------------------------------------------- 1 | package org.example.app.test; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.example.app.App; 5 | 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | 9 | import static java.nio.charset.StandardCharsets.UTF_8; 10 | import static org.junit.jupiter.api.Assertions.assertEquals; 11 | import static org.junit.jupiter.api.Assertions.assertNotNull; 12 | 13 | public class AppFunctionalTest { 14 | 15 | @Test 16 | void testAppFunctional() throws IOException { 17 | assertEquals("org.example.app", App.class.getModule().getName()); 18 | assertEquals("org.example.app.test.functional", AppFunctionalTest.class.getModule().getName()); 19 | try (InputStream is = AppFunctionalTest.class.getResourceAsStream("/data.txt")) { 20 | assertNotNull(is); 21 | assertEquals("DEF", new String(is.readAllBytes(), UTF_8)); 22 | } 23 | } 24 | 25 | } -------------------------------------------------------------------------------- /samples/module-info-dsl-no-platform/app/src/testFunctional/resources/data.txt: -------------------------------------------------------------------------------- 1 | DEF -------------------------------------------------------------------------------- /samples/module-info-dsl-no-platform/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.example.root") 3 | } -------------------------------------------------------------------------------- /samples/module-info-dsl-no-platform/build.out: -------------------------------------------------------------------------------- 1 | > Task :lib:compileJava 2 | > Task :lib:processResources NO-SOURCE 3 | > Task :lib:classes 4 | > Task :lib:jar 5 | > Task :app:compileJava 6 | > Task :app:processResources NO-SOURCE 7 | > Task :app:classes 8 | > Task :app:jar 9 | > Task :app:startScripts 10 | > Task :app:distTar 11 | > Task :app:distZip 12 | > Task :app:assemble 13 | > Task :app:compileTestJava 14 | > Task :app:processTestResources 15 | > Task :app:testClasses 16 | > Task :app:test 17 | > Task :app:compileTestFunctionalJava 18 | > Task :app:processTestFunctionalResources 19 | > Task :app:testFunctionalClasses 20 | > Task :app:testFunctionalJar 21 | > Task :app:testFunctional 22 | > Task :app:check 23 | > Task :app:build 24 | > Task :lib:assemble 25 | > Task :lib:compileTestJava 26 | > Task :lib:processTestResources 27 | > Task :lib:testClasses 28 | > Task :lib:test 29 | > Task :lib:compileTestFunctionalJava 30 | > Task :lib:processTestFunctionalResources 31 | > Task :lib:testFunctionalClasses 32 | > Task :lib:testFunctionalJar 33 | > Task :lib:testFunctional 34 | > Task :lib:check 35 | > Task :lib:build 36 | 37 | > Task :app:run 38 | [main] INFO org.example.app.App - App running... -------------------------------------------------------------------------------- /samples/module-info-dsl-no-platform/build.sample.conf: -------------------------------------------------------------------------------- 1 | executable: gradlew 2 | expected-output-file: build.out -------------------------------------------------------------------------------- /samples/module-info-dsl-no-platform/gradle/plugins/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | `kotlin-dsl` 3 | } 4 | 5 | dependencies { 6 | implementation("com.autonomousapps:dependency-analysis-gradle-plugin:2.18.0") 7 | implementation("org.gradlex:java-module-dependencies:1.9") 8 | implementation("org.gradlex:java-module-testing:1.7") 9 | } -------------------------------------------------------------------------------- /samples/module-info-dsl-no-platform/gradle/plugins/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | dependencyResolutionManagement { 2 | repositories.gradlePluginPortal() 3 | } 4 | 5 | // This is for testing against the latest version of the plugin, remove if you copied this for a real project 6 | includeBuild(extra.properties["pluginLocation"] ?: rootDir.parentFile.parentFile.parentFile.parent) 7 | -------------------------------------------------------------------------------- /samples/module-info-dsl-no-platform/gradle/plugins/src/main/kotlin/org.example.java-base.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("java") 3 | id("org.gradlex.java-module-dependencies") 4 | id("org.gradlex.java-module-testing") 5 | } 6 | 7 | group = "org.example" 8 | 9 | java.toolchain.languageVersion.set(JavaLanguageVersion.of(11)) 10 | testing.suites.register("testFunctional") 11 | tasks.check { dependsOn(tasks.named("testFunctional")) } 12 | 13 | javaModuleDependencies { 14 | versionsFromPlatformAndConsistentResolution(":app", ":app") 15 | } 16 | 17 | tasks.withType().configureEach { 18 | jvmArgs("-Dorg.slf4j.simpleLogger.defaultLogLevel=error") 19 | } 20 | -------------------------------------------------------------------------------- /samples/module-info-dsl-no-platform/gradle/plugins/src/main/kotlin/org.example.java-module-app.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.example.java-base") 3 | id("application") 4 | id("org.gradlex.java-module-versions") 5 | } 6 | -------------------------------------------------------------------------------- /samples/module-info-dsl-no-platform/gradle/plugins/src/main/kotlin/org.example.java-module.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.example.java-base") 3 | id("java-library") 4 | } 5 | 6 | -------------------------------------------------------------------------------- /samples/module-info-dsl-no-platform/gradle/plugins/src/main/kotlin/org.example.root.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.autonomousapps.dependency-analysis") 3 | } 4 | -------------------------------------------------------------------------------- /samples/module-info-dsl-no-platform/lib/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.example.java-module") 3 | } 4 | 5 | testModuleInfo { 6 | requires("org.junit.jupiter.api") 7 | } 8 | -------------------------------------------------------------------------------- /samples/module-info-dsl-no-platform/lib/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | module org.example.lib { 2 | requires transitive com.fasterxml.jackson.databind; 3 | 4 | requires java.logging; // JDK module 5 | 6 | exports org.example.lib; 7 | } -------------------------------------------------------------------------------- /samples/module-info-dsl-no-platform/lib/src/main/java/org/example/lib/Lib.java: -------------------------------------------------------------------------------- 1 | package org.example.lib; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | 5 | public class Lib { 6 | 7 | public void process(ObjectMapper mapper) { 8 | 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /samples/module-info-dsl-no-platform/lib/src/test/java/org/example/lib/LibTest.java: -------------------------------------------------------------------------------- 1 | package org.example.lib; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | 8 | import static java.nio.charset.StandardCharsets.UTF_8; 9 | import static org.junit.jupiter.api.Assertions.assertEquals; 10 | import static org.junit.jupiter.api.Assertions.assertNotNull; 11 | 12 | class LibTest { 13 | 14 | @Test 15 | void testLib() throws IOException { 16 | assertEquals("org.example.lib", Lib.class.getModule().getName()); 17 | assertEquals("org.example.lib", LibTest.class.getModule().getName()); 18 | try (InputStream is = LibTest.class.getResourceAsStream("/data.txt")) { 19 | assertNotNull(is); 20 | assertEquals("2*21", new String(is.readAllBytes(), UTF_8)); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /samples/module-info-dsl-no-platform/lib/src/test/resources/data.txt: -------------------------------------------------------------------------------- 1 | 2*21 -------------------------------------------------------------------------------- /samples/module-info-dsl-no-platform/lib/src/testFunctional/java/module-info.java: -------------------------------------------------------------------------------- 1 | open module org.example.lib.test.functional { 2 | requires org.example.lib; 3 | requires org.junit.jupiter.api; 4 | } 5 | -------------------------------------------------------------------------------- /samples/module-info-dsl-no-platform/lib/src/testFunctional/java/org/example/lib/test/LibFunctionalTest.java: -------------------------------------------------------------------------------- 1 | package org.example.lib.test; 2 | 3 | import org.example.lib.Lib; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | 9 | import static java.nio.charset.StandardCharsets.UTF_8; 10 | import static org.junit.jupiter.api.Assertions.assertEquals; 11 | import static org.junit.jupiter.api.Assertions.assertNotNull; 12 | 13 | class LibFunctionalTest { 14 | 15 | @Test 16 | void testLibFunctional() throws IOException { 17 | assertEquals("org.example.lib", Lib.class.getModule().getName()); 18 | assertEquals("org.example.lib.test.functional", LibFunctionalTest.class.getModule().getName()); 19 | try (InputStream is = LibFunctionalTest.class.getResourceAsStream("/data.txt")) { 20 | assertNotNull(is); 21 | assertEquals("42", new String(is.readAllBytes(), UTF_8)); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /samples/module-info-dsl-no-platform/lib/src/testFunctional/resources/data.txt: -------------------------------------------------------------------------------- 1 | 42 -------------------------------------------------------------------------------- /samples/module-info-dsl-no-platform/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | includeBuild("gradle/plugins") 3 | } 4 | dependencyResolutionManagement { 5 | repositories.mavenCentral() 6 | } 7 | 8 | include("app", "lib") 9 | -------------------------------------------------------------------------------- /samples/module-info-dsl/app/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.example.java-module-app") 3 | } 4 | 5 | application { 6 | applicationDefaultJvmArgs = listOf("-ea") 7 | mainClass.set("org.example.app.App") 8 | mainModule.set("org.example.app") 9 | } 10 | 11 | mainModuleInfo { 12 | runtimeOnly("org.slf4j.simple") 13 | } 14 | 15 | testModuleInfo { 16 | requires("org.junit.jupiter.api") 17 | } 18 | -------------------------------------------------------------------------------- /samples/module-info-dsl/app/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | module org.example.app { 2 | requires org.example.lib; 3 | requires org.slf4j; 4 | 5 | exports org.example.app; 6 | } -------------------------------------------------------------------------------- /samples/module-info-dsl/app/src/main/java/org/example/app/App.java: -------------------------------------------------------------------------------- 1 | package org.example.app; 2 | 3 | import org.example.lib.Lib; 4 | import org.slf4j.LoggerFactory; 5 | 6 | public class App { 7 | 8 | public static void main(String[] args) { 9 | LoggerFactory.getLogger(App.class).info("App running..."); 10 | doWork(); 11 | } 12 | 13 | public static void doWork() { 14 | new Lib(); 15 | } 16 | } -------------------------------------------------------------------------------- /samples/module-info-dsl/app/src/test/java/org/example/app/AppTest.java: -------------------------------------------------------------------------------- 1 | package org.example.app; 2 | 3 | import org.example.lib.Lib; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | 9 | import static java.nio.charset.StandardCharsets.UTF_8; 10 | import static org.junit.jupiter.api.Assertions.assertEquals; 11 | import static org.junit.jupiter.api.Assertions.assertNotNull; 12 | 13 | public class AppTest { 14 | 15 | @Test 16 | public void testApp() throws IOException { 17 | assertEquals("org.example.lib", Lib.class.getModule().getName()); 18 | assertEquals("org.example.app", App.class.getModule().getName()); 19 | assertEquals("org.example.app", AppTest.class.getModule().getName()); 20 | try (InputStream is = AppTest.class.getResourceAsStream("/data.txt")) { 21 | assertNotNull(is); 22 | assertEquals("ABC", new String(is.readAllBytes(), UTF_8)); 23 | } 24 | } 25 | 26 | } -------------------------------------------------------------------------------- /samples/module-info-dsl/app/src/test/resources/data.txt: -------------------------------------------------------------------------------- 1 | ABC -------------------------------------------------------------------------------- /samples/module-info-dsl/app/src/testFunctional/java/module-info.java: -------------------------------------------------------------------------------- 1 | open module org.example.app.test.functional { 2 | requires org.example.app; 3 | requires org.junit.jupiter.api; 4 | } -------------------------------------------------------------------------------- /samples/module-info-dsl/app/src/testFunctional/java/org/example/app/test/AppFunctionalTest.java: -------------------------------------------------------------------------------- 1 | package org.example.app.test; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.example.app.App; 5 | 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | 9 | import static java.nio.charset.StandardCharsets.UTF_8; 10 | import static org.junit.jupiter.api.Assertions.assertEquals; 11 | import static org.junit.jupiter.api.Assertions.assertNotNull; 12 | 13 | public class AppFunctionalTest { 14 | 15 | @Test 16 | void testAppFunctional() throws IOException { 17 | assertEquals("org.example.app", App.class.getModule().getName()); 18 | assertEquals("org.example.app.test.functional", AppFunctionalTest.class.getModule().getName()); 19 | try (InputStream is = AppFunctionalTest.class.getResourceAsStream("/data.txt")) { 20 | assertNotNull(is); 21 | assertEquals("DEF", new String(is.readAllBytes(), UTF_8)); 22 | } 23 | } 24 | 25 | } -------------------------------------------------------------------------------- /samples/module-info-dsl/app/src/testFunctional/resources/data.txt: -------------------------------------------------------------------------------- 1 | DEF -------------------------------------------------------------------------------- /samples/module-info-dsl/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.example.root") 3 | } -------------------------------------------------------------------------------- /samples/module-info-dsl/build.out: -------------------------------------------------------------------------------- 1 | > Task :lib:compileJava 2 | > Task :lib:processResources NO-SOURCE 3 | > Task :lib:classes 4 | > Task :lib:jar 5 | > Task :app:compileJava 6 | > Task :app:processResources NO-SOURCE 7 | > Task :app:classes 8 | > Task :app:jar 9 | > Task :app:startScripts 10 | > Task :app:distTar 11 | > Task :app:distZip 12 | > Task :app:assemble 13 | > Task :app:compileTestJava 14 | > Task :app:processTestResources 15 | > Task :app:testClasses 16 | > Task :app:test 17 | > Task :app:compileTestFunctionalJava 18 | > Task :app:processTestFunctionalResources 19 | > Task :app:testFunctionalClasses 20 | > Task :app:testFunctionalJar 21 | > Task :app:testFunctional 22 | > Task :app:check 23 | > Task :app:build 24 | > Task :lib:assemble 25 | > Task :lib:compileTestJava 26 | > Task :lib:processTestResources 27 | > Task :lib:testClasses 28 | > Task :lib:test 29 | > Task :lib:compileTestFunctionalJava 30 | > Task :lib:processTestFunctionalResources 31 | > Task :lib:testFunctionalClasses 32 | > Task :lib:testFunctionalJar 33 | > Task :lib:testFunctional 34 | > Task :lib:check 35 | > Task :lib:build 36 | > Task :versions:assemble UP-TO-DATE 37 | > Task :versions:check UP-TO-DATE 38 | > Task :versions:build UP-TO-DATE 39 | 40 | > Task :app:run 41 | [main] INFO org.example.app.App - App running... 42 | -------------------------------------------------------------------------------- /samples/module-info-dsl/build.sample.conf: -------------------------------------------------------------------------------- 1 | executable: gradlew 2 | expected-output-file: build.out -------------------------------------------------------------------------------- /samples/module-info-dsl/gradle/plugins/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | `kotlin-dsl` 3 | } 4 | 5 | dependencies { 6 | implementation("com.autonomousapps:dependency-analysis-gradle-plugin:2.18.0") 7 | implementation("org.gradlex:java-module-dependencies:1.9") 8 | implementation("org.gradlex:java-module-testing:1.7") 9 | } -------------------------------------------------------------------------------- /samples/module-info-dsl/gradle/plugins/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | dependencyResolutionManagement { 2 | repositories.gradlePluginPortal() 3 | } 4 | 5 | // This is for testing against the latest version of the plugin, remove if you copied this for a real project 6 | includeBuild(extra.properties["pluginLocation"] ?: rootDir.parentFile.parentFile.parentFile.parent) 7 | -------------------------------------------------------------------------------- /samples/module-info-dsl/gradle/plugins/src/main/kotlin/org.example.java-base.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("java") 3 | id("org.gradlex.java-module-dependencies") 4 | id("org.gradlex.java-module-testing") 5 | } 6 | 7 | group = "org.example" 8 | 9 | java.toolchain.languageVersion.set(JavaLanguageVersion.of(11)) 10 | testing.suites.register("testFunctional") 11 | tasks.check { dependsOn(tasks.named("testFunctional")) } 12 | 13 | javaModuleDependencies { 14 | versionsFromPlatformAndConsistentResolution(":versions", ":app") 15 | } 16 | 17 | tasks.withType().configureEach { 18 | jvmArgs("-Dorg.slf4j.simpleLogger.defaultLogLevel=error") 19 | } 20 | -------------------------------------------------------------------------------- /samples/module-info-dsl/gradle/plugins/src/main/kotlin/org.example.java-module-app.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.example.java-base") 3 | id("application") 4 | } 5 | -------------------------------------------------------------------------------- /samples/module-info-dsl/gradle/plugins/src/main/kotlin/org.example.java-module-versions.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("java-platform") 3 | id("org.gradlex.java-module-versions") 4 | } -------------------------------------------------------------------------------- /samples/module-info-dsl/gradle/plugins/src/main/kotlin/org.example.java-module.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.example.java-base") 3 | id("java-library") 4 | } 5 | 6 | -------------------------------------------------------------------------------- /samples/module-info-dsl/gradle/plugins/src/main/kotlin/org.example.root.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.autonomousapps.dependency-analysis") 3 | } 4 | -------------------------------------------------------------------------------- /samples/module-info-dsl/lib/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.example.java-module") 3 | } 4 | 5 | testModuleInfo { 6 | requires("org.junit.jupiter.api") 7 | } 8 | 9 | // println(configurations.compileClasspath.get().files) -------------------------------------------------------------------------------- /samples/module-info-dsl/lib/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | module org.example.lib { 2 | requires transitive com.fasterxml.jackson.databind; 3 | 4 | requires java.logging; // JDK module 5 | 6 | exports org.example.lib; 7 | } -------------------------------------------------------------------------------- /samples/module-info-dsl/lib/src/main/java/org/example/lib/Lib.java: -------------------------------------------------------------------------------- 1 | package org.example.lib; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | 5 | public class Lib { 6 | 7 | public void process(ObjectMapper mapper) { 8 | 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /samples/module-info-dsl/lib/src/test/java/org/example/lib/LibTest.java: -------------------------------------------------------------------------------- 1 | package org.example.lib; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | 8 | import static java.nio.charset.StandardCharsets.UTF_8; 9 | import static org.junit.jupiter.api.Assertions.assertEquals; 10 | import static org.junit.jupiter.api.Assertions.assertNotNull; 11 | 12 | class LibTest { 13 | 14 | @Test 15 | void testLib() throws IOException { 16 | assertEquals("org.example.lib", Lib.class.getModule().getName()); 17 | assertEquals("org.example.lib", LibTest.class.getModule().getName()); 18 | try (InputStream is = LibTest.class.getResourceAsStream("/data.txt")) { 19 | assertNotNull(is); 20 | assertEquals("2*21", new String(is.readAllBytes(), UTF_8)); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /samples/module-info-dsl/lib/src/test/resources/data.txt: -------------------------------------------------------------------------------- 1 | 2*21 -------------------------------------------------------------------------------- /samples/module-info-dsl/lib/src/testFunctional/java/module-info.java: -------------------------------------------------------------------------------- 1 | open module org.example.lib.test.functional { 2 | requires org.example.lib; 3 | requires org.junit.jupiter.api; 4 | } 5 | -------------------------------------------------------------------------------- /samples/module-info-dsl/lib/src/testFunctional/java/org/example/lib/test/LibFunctionalTest.java: -------------------------------------------------------------------------------- 1 | package org.example.lib.test; 2 | 3 | import org.example.lib.Lib; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | 9 | import static java.nio.charset.StandardCharsets.UTF_8; 10 | import static org.junit.jupiter.api.Assertions.assertEquals; 11 | import static org.junit.jupiter.api.Assertions.assertNotNull; 12 | 13 | class LibFunctionalTest { 14 | 15 | @Test 16 | void testLibFunctional() throws IOException { 17 | assertEquals("org.example.lib", Lib.class.getModule().getName()); 18 | assertEquals("org.example.lib.test.functional", LibFunctionalTest.class.getModule().getName()); 19 | try (InputStream is = LibFunctionalTest.class.getResourceAsStream("/data.txt")) { 20 | assertNotNull(is); 21 | assertEquals("42", new String(is.readAllBytes(), UTF_8)); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /samples/module-info-dsl/lib/src/testFunctional/resources/data.txt: -------------------------------------------------------------------------------- 1 | 42 -------------------------------------------------------------------------------- /samples/module-info-dsl/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | includeBuild("gradle/plugins") 3 | } 4 | dependencyResolutionManagement { 5 | repositories.mavenCentral() 6 | } 7 | 8 | include("app", "lib") 9 | include("versions") 10 | -------------------------------------------------------------------------------- /samples/module-info-dsl/versions/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.example.java-module-versions") 3 | } 4 | 5 | moduleInfo { 6 | version("org.slf4j", "2.0.7") 7 | version("org.slf4j.simple", "2.0.7") 8 | version("com.fasterxml.jackson.databind", "2.14.0") 9 | version("org.junit.jupiter.api", "5.9.3") 10 | } 11 | -------------------------------------------------------------------------------- /samples/versions-in-catalog/app/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.my.gradle.java-module") 3 | id("application") 4 | } 5 | 6 | application { 7 | applicationDefaultJvmArgs = listOf("-ea") 8 | mainClass.set("org.my.app.App") 9 | mainModule.set("org.my.app") 10 | } 11 | 12 | dependencies { 13 | runtimeOnly(javaModuleDependencies.gav("org.slf4j.simple")) 14 | } 15 | -------------------------------------------------------------------------------- /samples/versions-in-catalog/app/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | module org.my.app { 2 | requires org.slf4j; 3 | requires org.my.lib; 4 | requires static org.apache.xmlbeans; 5 | 6 | exports org.my.app; 7 | } -------------------------------------------------------------------------------- /samples/versions-in-catalog/app/src/main/java/org/my/app/App.java: -------------------------------------------------------------------------------- 1 | package org.my.app; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import org.apache.xmlbeans.impl.tool.XMLBean; 5 | import org.slf4j.spi.LoggerFactoryBinder; 6 | 7 | public class App { 8 | public static void main(String[] args) { 9 | doWork(); 10 | } 11 | 12 | public static boolean doWork() { 13 | ObjectMapper om = new ObjectMapper(); 14 | if (!om.canSerialize(LoggerFactoryBinder.class)) { 15 | throw new RuntimeException("Boom!"); 16 | } 17 | System.out.println(App.class.getModule().getName()); 18 | 19 | try { 20 | new XMLBean(); 21 | throw new RuntimeException("Boom!"); 22 | } catch (NoClassDefFoundError e) { 23 | // This is expected at runtime! 24 | } 25 | return true; 26 | } 27 | } -------------------------------------------------------------------------------- /samples/versions-in-catalog/app/src/test/java/module-info.java: -------------------------------------------------------------------------------- 1 | open module org.my.app.test { 2 | requires org.my.app; 3 | requires org.junit.jupiter.api; 4 | } -------------------------------------------------------------------------------- /samples/versions-in-catalog/app/src/test/java/org/my/app/test/AppTest.java: -------------------------------------------------------------------------------- 1 | package org.my.app.test; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.my.app.App; 5 | 6 | import static org.junit.jupiter.api.Assertions.assertTrue; 7 | 8 | public class AppTest { 9 | 10 | @Test 11 | public void appDoesNotExplode() { 12 | assertTrue(App.doWork()); 13 | } 14 | 15 | } -------------------------------------------------------------------------------- /samples/versions-in-catalog/build-logic/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | `kotlin-dsl` 3 | } 4 | 5 | dependencies { 6 | implementation("org.gradlex:java-module-dependencies:1.9") 7 | } -------------------------------------------------------------------------------- /samples/versions-in-catalog/build-logic/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | dependencyResolutionManagement { 2 | repositories.gradlePluginPortal() 3 | } 4 | 5 | // This is for testing against the latest version of the plugin, remove if you copied this for a real project 6 | includeBuild(extra.properties["pluginLocation"] ?: rootDir.parentFile.parentFile.parent) 7 | 8 | -------------------------------------------------------------------------------- /samples/versions-in-catalog/build-logic/src/main/kotlin/org.my.gradle.java-module.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("java") 3 | id("org.gradlex.java-module-dependencies") 4 | } 5 | 6 | group = "org.my" 7 | 8 | javaModuleDependencies.versionsFromConsistentResolution(":app") 9 | 10 | tasks.test { 11 | useJUnitPlatform() 12 | jvmArgs("-Dorg.slf4j.simpleLogger.defaultLogLevel=error") 13 | } 14 | 15 | dependencies { 16 | javaModuleDependencies { 17 | testRuntimeOnly(gav("org.junit.jupiter.engine")) 18 | testRuntimeOnly(gav("org.junit.platform.launcher")) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /samples/versions-in-catalog/build.out: -------------------------------------------------------------------------------- 1 | > Task :lib:compileJava 2 | > Task :lib:processResources NO-SOURCE 3 | > Task :lib:classes 4 | > Task :lib:jar 5 | > Task :app:compileJava 6 | > Task :app:processResources NO-SOURCE 7 | > Task :app:classes 8 | > Task :app:jar 9 | > Task :app:startScripts 10 | > Task :app:distTar 11 | > Task :app:distZip 12 | > Task :app:assemble 13 | > Task :app:compileTestJava 14 | > Task :app:processTestResources NO-SOURCE 15 | > Task :app:testClasses 16 | > Task :app:test 17 | > Task :app:check 18 | > Task :app:build 19 | > Task :lib:assemble 20 | > Task :lib:compileTestJava 21 | > Task :lib:processTestResources NO-SOURCE 22 | > Task :lib:testClasses 23 | > Task :lib:test 24 | > Task :lib:check 25 | > Task :lib:build 26 | 27 | > Task :app:run 28 | org.my.app -------------------------------------------------------------------------------- /samples/versions-in-catalog/build.sample.conf: -------------------------------------------------------------------------------- 1 | executable: gradlew 2 | expected-output-file: build.out -------------------------------------------------------------------------------- /samples/versions-in-catalog/gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | org_apache_xmlbeans = "5.0.1" 3 | com_fasterxml_jackson_databind = "2.12.5" 4 | org_slf4j = "1.7.28" 5 | org_slf4j_simple = "1.7.28" 6 | 7 | org-junit-jupiter-api = "5.7.2" 8 | -------------------------------------------------------------------------------- /samples/versions-in-catalog/lib/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.my.gradle.java-module") 3 | id("java-library") 4 | } 5 | -------------------------------------------------------------------------------- /samples/versions-in-catalog/lib/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | module org.my.lib { 2 | requires transitive com.fasterxml.jackson.databind; 3 | 4 | // JDK modules 5 | requires java.logging; 6 | requires jdk.charsets; 7 | } -------------------------------------------------------------------------------- /samples/versions-in-catalog/lib/src/test/java/module-info.java: -------------------------------------------------------------------------------- 1 | open module org.my.lib.test { 2 | requires org.my.lib; 3 | requires org.junit.jupiter.api; 4 | } 5 | -------------------------------------------------------------------------------- /samples/versions-in-catalog/lib/src/test/java/org/my/lib/test/MyLibTest.java: -------------------------------------------------------------------------------- 1 | package org.my.lib.test; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | class MyLibTest { 6 | 7 | @Test 8 | void test() { 9 | } 10 | 11 | } -------------------------------------------------------------------------------- /samples/versions-in-catalog/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | includeBuild("build-logic") 3 | } 4 | dependencyResolutionManagement { 5 | repositories.mavenCentral() 6 | } 7 | 8 | include("app", "lib") 9 | -------------------------------------------------------------------------------- /samples/versions-in-platform/app/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.my.gradle.java-module") 3 | id("application") 4 | } 5 | 6 | application { 7 | applicationDefaultJvmArgs = listOf("-ea") 8 | mainClass.set("org.my.app.App") 9 | mainModule.set("org.my.app") 10 | } 11 | -------------------------------------------------------------------------------- /samples/versions-in-platform/app/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | module org.my.app { 2 | requires org.my.lib; 3 | requires org.slf4j; 4 | 5 | requires static org.apache.xmlbeans; 6 | 7 | requires /*runtime*/ org.slf4j.simple; 8 | 9 | exports org.my.app; 10 | } -------------------------------------------------------------------------------- /samples/versions-in-platform/app/src/main/java/org/my/app/App.java: -------------------------------------------------------------------------------- 1 | package org.my.app; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import org.apache.xmlbeans.impl.tool.XMLBean; 5 | import org.slf4j.spi.LoggerFactoryBinder; 6 | 7 | public class App { 8 | public static void main(String[] args) { 9 | doWork(); 10 | } 11 | 12 | public static boolean doWork() { 13 | ObjectMapper om = new ObjectMapper(); 14 | LoggerFactoryBinder lfb; 15 | System.out.println(App.class.getModule().getName()); 16 | 17 | try { 18 | new XMLBean(); 19 | throw new RuntimeException("Boom!"); 20 | } catch (NoClassDefFoundError e) { 21 | // This is expected at runtime! 22 | } 23 | return true; 24 | } 25 | } -------------------------------------------------------------------------------- /samples/versions-in-platform/app/src/test/java/module-info.java: -------------------------------------------------------------------------------- 1 | open module org.my.app.test { 2 | requires org.my.app; 3 | requires org.junit.jupiter.api; 4 | } -------------------------------------------------------------------------------- /samples/versions-in-platform/app/src/test/java/org/my/app/test/AppTest.java: -------------------------------------------------------------------------------- 1 | package org.my.app.test; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.my.app.App; 5 | 6 | import static org.junit.jupiter.api.Assertions.assertTrue; 7 | 8 | public class AppTest { 9 | 10 | @Test 11 | public void appDoesNotExplode() { 12 | assertTrue(App.doWork()); 13 | } 14 | 15 | } -------------------------------------------------------------------------------- /samples/versions-in-platform/build-logic/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | `kotlin-dsl` 3 | } 4 | 5 | dependencies { 6 | implementation("org.gradlex:java-module-dependencies:1.9") 7 | } -------------------------------------------------------------------------------- /samples/versions-in-platform/build-logic/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | dependencyResolutionManagement { 2 | repositories.gradlePluginPortal() 3 | } 4 | 5 | // This is for testing against the latest version of the plugin, remove if you copied this for a real project 6 | includeBuild(extra.properties["pluginLocation"] ?: rootDir.parentFile.parentFile.parent) 7 | 8 | -------------------------------------------------------------------------------- /samples/versions-in-platform/build-logic/src/main/kotlin/org.my.gradle.java-module.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("java") 3 | id("org.gradlex.java-module-dependencies") 4 | } 5 | 6 | group = "org.my" 7 | 8 | javaModuleDependencies.versionsFromConsistentResolution(":app") 9 | 10 | tasks.test { 11 | useJUnitPlatform() 12 | jvmArgs("-Dorg.slf4j.simpleLogger.defaultLogLevel=error") 13 | } 14 | 15 | dependencies { 16 | implementation(platform(project(":platform"))) 17 | javaModuleDependencies { 18 | testRuntimeOnly(ga("org.junit.jupiter.engine")) 19 | testRuntimeOnly(ga("org.junit.platform.launcher")) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /samples/versions-in-platform/build-logic/src/main/kotlin/org.my.gradle.platform.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("java-platform") 3 | id("org.gradlex.java-module-dependencies") 4 | } 5 | 6 | group = "org.my" 7 | 8 | javaPlatform.allowDependencies() // Use existing Platforms/BOMs 9 | -------------------------------------------------------------------------------- /samples/versions-in-platform/build.out: -------------------------------------------------------------------------------- 1 | > Task :lib:compileJava 2 | > Task :lib:processResources NO-SOURCE 3 | > Task :lib:classes 4 | > Task :lib:jar 5 | > Task :app:compileJava 6 | > Task :app:processResources NO-SOURCE 7 | > Task :app:classes 8 | > Task :app:jar 9 | > Task :app:startScripts 10 | > Task :app:distTar 11 | > Task :app:distZip 12 | > Task :app:assemble 13 | > Task :app:compileTestJava 14 | > Task :app:processTestResources NO-SOURCE 15 | > Task :app:testClasses 16 | > Task :app:test 17 | > Task :app:check 18 | > Task :app:build 19 | > Task :lib:assemble 20 | > Task :lib:compileTestJava 21 | > Task :lib:processTestResources NO-SOURCE 22 | > Task :lib:testClasses 23 | > Task :lib:test 24 | > Task :lib:check 25 | > Task :lib:build 26 | > Task :platform:assemble UP-TO-DATE 27 | > Task :platform:check UP-TO-DATE 28 | > Task :platform:build UP-TO-DATE 29 | 30 | > Task :app:run 31 | org.my.app -------------------------------------------------------------------------------- /samples/versions-in-platform/build.sample.conf: -------------------------------------------------------------------------------- 1 | executable: gradlew 2 | expected-output-file: build.out -------------------------------------------------------------------------------- /samples/versions-in-platform/lib/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.my.gradle.java-module") 3 | id("java-library") 4 | } 5 | -------------------------------------------------------------------------------- /samples/versions-in-platform/lib/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | module org.my.lib { 2 | requires transitive com.fasterxml.jackson.databind; 3 | 4 | // JDK modules 5 | requires java.logging; 6 | requires jdk.charsets; 7 | } -------------------------------------------------------------------------------- /samples/versions-in-platform/lib/src/test/java/module-info.java: -------------------------------------------------------------------------------- 1 | open module org.my.lib.test { 2 | requires org.my.lib; 3 | requires org.junit.jupiter.api; 4 | } 5 | -------------------------------------------------------------------------------- /samples/versions-in-platform/lib/src/test/java/org/my/lib/test/MyLibTest.java: -------------------------------------------------------------------------------- 1 | package org.my.lib.test; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | class MyLibTest { 6 | 7 | @Test 8 | void test() { 9 | } 10 | 11 | } -------------------------------------------------------------------------------- /samples/versions-in-platform/platform/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("org.my.gradle.platform") 3 | } 4 | 5 | 6 | dependencies { 7 | api(platform("com.fasterxml.jackson:jackson-bom:2.19.0")) 8 | api(platform("org.junit:junit-bom:5.13.0")) 9 | } 10 | 11 | dependencies.constraints { 12 | javaModuleDependencies { 13 | api(gav("org.apache.xmlbeans", "5.0.1")) 14 | api(gav("org.slf4j", "1.7.28")) 15 | api(gav("org.slf4j.simple", "1.7.28")) 16 | } 17 | } -------------------------------------------------------------------------------- /samples/versions-in-platform/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | includeBuild("build-logic") 3 | } 4 | dependencyResolutionManagement { 5 | repositories.mavenCentral() 6 | } 7 | 8 | include("app", "lib") 9 | include("platform") 10 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | includeBuild("gradle/plugins") 3 | } 4 | plugins { 5 | id("com.gradle.develocity") version "4.0.2" 6 | } 7 | 8 | dependencyResolutionManagement { 9 | repositories { 10 | mavenCentral() 11 | gradlePluginPortal() 12 | } 13 | } 14 | 15 | rootProject.name = "java-module-dependencies" 16 | 17 | develocity { 18 | buildScan { 19 | termsOfUseUrl = "https://gradle.com/help/legal-terms-of-use" 20 | termsOfUseAgree = "yes" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/org/gradlex/javamodule/dependencies/GAV.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright the GradleX team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.gradlex.javamodule.dependencies; 18 | 19 | interface GAV { 20 | String GROUP = "group"; 21 | String ARTIFACT = "name"; 22 | String VERSION = "version"; 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/org/gradlex/javamodule/dependencies/JDKInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright the GradleX team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.gradlex.javamodule.dependencies; 18 | 19 | import java.util.Arrays; 20 | import java.util.List; 21 | 22 | interface JDKInfo { 23 | List MODULES = Arrays.asList( 24 | "java.base", 25 | "java.compiler", 26 | "java.datatransfer", 27 | "java.desktop", 28 | "java.instrument", 29 | "java.logging", 30 | "java.management", 31 | "java.management.rmi", 32 | "java.naming", 33 | "java.net.http", 34 | "java.prefs", 35 | "java.rmi", 36 | "java.scripting", 37 | "java.se", 38 | "java.security.jgss", 39 | "java.security.sasl", 40 | "java.smartcardio", 41 | "java.sql", 42 | "java.sql.rowset", 43 | "java.transaction.xa", 44 | "java.xml", 45 | "java.xml.crypto", 46 | "jdk.accessibility", 47 | "jdk.attach", 48 | "jdk.charsets", 49 | "jdk.compiler", 50 | "jdk.crypto.cryptoki", 51 | "jdk.crypto.ec", 52 | "jdk.dynalink", 53 | "jdk.editpad", 54 | "jdk.hotspot.agent", 55 | "jdk.httpserver", 56 | "jdk.incubator.foreign", 57 | "jdk.incubator.vector", 58 | "jdk.jartool", 59 | "jdk.javadoc", 60 | "jdk.jcmd", 61 | "jdk.jconsole", 62 | "jdk.jdeps", 63 | "jdk.jdi", 64 | "jdk.jdwp.agent", 65 | "jdk.jfr", 66 | "jdk.jlink", 67 | "jdk.jpackage", 68 | "jdk.jshell", 69 | "jdk.jsobject", 70 | "jdk.jstatd", 71 | "jdk.localedata", 72 | "jdk.management", 73 | "jdk.management.agent", 74 | "jdk.management.jfr", 75 | "jdk.naming.dns", 76 | "jdk.naming.rmi", 77 | "jdk.net", 78 | "jdk.nio.mapmode", 79 | "jdk.sctp", 80 | "jdk.security.auth", 81 | "jdk.security.jgss", 82 | "jdk.unsupported", 83 | "jdk.unsupported.desktop", 84 | "jdk.xml.dom", 85 | "jdk.zipfs" 86 | ); 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/org/gradlex/javamodule/dependencies/SharedMappings.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright the GradleX team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.gradlex.javamodule.dependencies; 18 | 19 | import java.io.IOException; 20 | import java.io.InputStream; 21 | import java.util.Map; 22 | import java.util.Properties; 23 | 24 | final public class SharedMappings { 25 | public static Map mappings = loadModuleNameToGAProperties(); 26 | 27 | static Map loadModuleNameToGAProperties() { 28 | Properties properties = new Properties() { 29 | @Override 30 | public synchronized Object put(Object key, Object value) { 31 | if (get(key) != null) { 32 | throw new IllegalArgumentException(key + " already present."); 33 | } 34 | return super.put(key, value); 35 | } 36 | }; 37 | @SuppressWarnings({ "unchecked", "rawtypes" }) 38 | Map propertiesAsMap = (Map) properties; 39 | 40 | try (InputStream coordinatesFile = JavaModuleDependenciesExtension.class.getResourceAsStream("unique_modules.properties")) { 41 | properties.load(coordinatesFile); 42 | } catch (IOException e) { 43 | throw new RuntimeException(e); 44 | } 45 | 46 | try (InputStream coordinatesFile = JavaModuleDependenciesExtension.class.getResourceAsStream("modules.properties")) { 47 | properties.load(coordinatesFile); 48 | } catch (IOException e) { 49 | throw new RuntimeException(e); 50 | } 51 | return propertiesAsMap; 52 | } 53 | 54 | private SharedMappings() { } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/org/gradlex/javamodule/dependencies/dsl/AllDirectives.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright the GradleX team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.gradlex.javamodule.dependencies.dsl; 18 | 19 | import org.gradle.api.tasks.SourceSet; 20 | import org.gradlex.javamodule.dependencies.JavaModuleDependenciesExtension; 21 | 22 | abstract public class AllDirectives extends GradleOnlyDirectives { 23 | 24 | public AllDirectives(SourceSet sourceSet, SourceSet mainSourceSet, JavaModuleDependenciesExtension javaModuleDependencies) { 25 | super(sourceSet, mainSourceSet, javaModuleDependencies); 26 | } 27 | 28 | public void requires(String moduleName) { 29 | runtimeClasspathModules.add(moduleName); 30 | compileClasspathModules.add(moduleName); 31 | add(sourceSet.getImplementationConfigurationName(), moduleName); 32 | } 33 | 34 | public void requiresStatic(String moduleName) { 35 | compileClasspathModules.add(moduleName); 36 | add(sourceSet.getCompileOnlyConfigurationName(), moduleName); 37 | } 38 | 39 | public void exportsTo(String moduleName) { 40 | exportsToModules.add(moduleName); 41 | } 42 | 43 | public void opensTo(String moduleName) { 44 | opensToModules.add(moduleName); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/org/gradlex/javamodule/dependencies/dsl/GradleOnlyDirectives.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright the GradleX team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.gradlex.javamodule.dependencies.dsl; 18 | 19 | import org.gradle.api.artifacts.dsl.DependencyHandler; 20 | import org.gradle.api.tasks.SourceSet; 21 | import org.gradlex.javamodule.dependencies.JavaModuleDependenciesExtension; 22 | 23 | import javax.inject.Inject; 24 | import java.util.ArrayList; 25 | import java.util.List; 26 | 27 | public abstract class GradleOnlyDirectives { 28 | 29 | protected final SourceSet sourceSet; 30 | protected final SourceSet mainSourceSet; 31 | protected final JavaModuleDependenciesExtension javaModuleDependencies; 32 | 33 | protected final List compileClasspathModules = new ArrayList<>(); 34 | protected final List runtimeClasspathModules = new ArrayList<>(); 35 | protected final List exportsToModules = new ArrayList<>(); 36 | protected final List opensToModules = new ArrayList<>(); 37 | 38 | @Inject 39 | protected abstract DependencyHandler getDependencies(); 40 | 41 | public GradleOnlyDirectives(SourceSet sourceSet, SourceSet mainSourceSet, JavaModuleDependenciesExtension javaModuleDependencies) { 42 | this.sourceSet = sourceSet; 43 | this.mainSourceSet = mainSourceSet; 44 | this.javaModuleDependencies = javaModuleDependencies; 45 | } 46 | 47 | protected void add(String scope, String moduleName) { 48 | getDependencies().addProvider(scope, javaModuleDependencies.create(moduleName, mainSourceSet)); 49 | } 50 | 51 | public void runtimeOnly(String moduleName) { 52 | runtimeClasspathModules.add(moduleName); 53 | add(sourceSet.getRuntimeOnlyConfigurationName(), moduleName); 54 | } 55 | 56 | public void annotationProcessor(String moduleName) { 57 | add(sourceSet.getAnnotationProcessorConfigurationName(), moduleName); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/org/gradlex/javamodule/dependencies/dsl/ModuleVersions.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright the GradleX team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.gradlex.javamodule.dependencies.dsl; 18 | 19 | import org.gradle.api.Action; 20 | import org.gradle.api.artifacts.Configuration; 21 | import org.gradle.api.artifacts.DependencyConstraint; 22 | import org.gradle.api.artifacts.MutableVersionConstraint; 23 | import org.gradle.api.artifacts.dsl.DependencyHandler; 24 | import org.gradlex.javamodule.dependencies.JavaModuleDependenciesExtension; 25 | 26 | import javax.inject.Inject; 27 | import java.util.LinkedHashMap; 28 | import java.util.Map; 29 | 30 | abstract public class ModuleVersions { 31 | 32 | private final Map declaredVersions = new LinkedHashMap<>(); 33 | private final Configuration configuration; 34 | protected final JavaModuleDependenciesExtension javaModuleDependencies; 35 | 36 | public Map getDeclaredVersions() { 37 | return declaredVersions; 38 | } 39 | 40 | @Inject 41 | protected abstract DependencyHandler getDependencies(); 42 | 43 | public ModuleVersions(Configuration configuration, JavaModuleDependenciesExtension javaModuleDependencies) { 44 | this.configuration = configuration; 45 | this.javaModuleDependencies = javaModuleDependencies; 46 | } 47 | 48 | public void version(String moduleName, String version) { 49 | version(moduleName, version, a -> {}); 50 | } 51 | 52 | public void version(String moduleName, Action version) { 53 | version(moduleName, "", version); 54 | } 55 | 56 | public void version(String moduleName, String requiredVersion, Action version) { 57 | getDependencies().getConstraints().add(configuration.getName(), javaModuleDependencies.ga(moduleName).map(ga -> { 58 | String mainComponentCoordinates; 59 | if (ga.contains("|")) { 60 | mainComponentCoordinates = ga.substring(0, ga.indexOf("|")) + ":" + requiredVersion; 61 | } else { 62 | mainComponentCoordinates = ga + ":" + requiredVersion; 63 | } 64 | DependencyConstraint dependencyConstraint = getDependencies().getConstraints().create(mainComponentCoordinates); 65 | dependencyConstraint.version(version); 66 | return dependencyConstraint; 67 | })); 68 | declaredVersions.put(moduleName, requiredVersion); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/org/gradlex/javamodule/dependencies/initialization/Directory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright the GradleX team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.gradlex.javamodule.dependencies.initialization; 18 | 19 | import org.gradle.api.Action; 20 | import org.gradle.api.model.ObjectFactory; 21 | import org.gradle.api.provider.ListProperty; 22 | import org.gradle.api.provider.Property; 23 | 24 | import javax.inject.Inject; 25 | import java.io.File; 26 | import java.util.Arrays; 27 | import java.util.LinkedHashMap; 28 | import java.util.Map; 29 | 30 | public abstract class Directory { 31 | 32 | private final File root; 33 | final Map customizedModules = new LinkedHashMap<>(); 34 | 35 | /** 36 | * {@link Module#getGroup()} 37 | */ 38 | public abstract Property getGroup(); 39 | 40 | /** 41 | * {@link Module#plugin(String)} 42 | */ 43 | public abstract ListProperty getPlugins(); 44 | 45 | 46 | @Inject 47 | public Directory(File root) { 48 | this.root = root; 49 | getExclusions().convention(Arrays.asList("build", "\\..*")); 50 | getRequiresBuildFile().convention(false); 51 | } 52 | 53 | @Inject 54 | protected abstract ObjectFactory getObjects(); 55 | 56 | /** 57 | * {@link Module#plugin(String)} 58 | */ 59 | public void plugin(String id) { 60 | getPlugins().add(id); 61 | } 62 | 63 | /** 64 | * {@link Directory#module(String, Action)} 65 | */ 66 | public void module(String subDirectory) { 67 | module(subDirectory, m -> {}); 68 | } 69 | 70 | /** 71 | * Configure details of a Module in a subdirectory of this directory. 72 | * Note that Modules that are located in direct children of this directory are discovered automatically and 73 | * do not need to be explicitly mentioned. 74 | */ 75 | public void module(String subDirectory, Action action) { 76 | Module module = addModule(subDirectory); 77 | action.execute(module); 78 | customizedModules.put(subDirectory, module); 79 | } 80 | 81 | Module addModule(String subDirectory) { 82 | Module module = getObjects().newInstance(Module.class, new File(root, subDirectory)); 83 | module.getGroup().convention(getGroup()); 84 | module.getPlugins().addAll(getPlugins()); 85 | return module; 86 | } 87 | 88 | /** 89 | * Configure which folders should be ignored when searching for Modules. 90 | * This can be tweaked to optimize the configuration cache hit ratio. 91 | * Defaults to: 'build', '.*' 92 | */ 93 | public abstract ListProperty getExclusions(); 94 | 95 | /** 96 | * Configure if only folders that contain a 'build.gradle' or 'build.gradle.kts' 97 | * should be considered when searching for Modules. 98 | * Setting this to true may improve configuration cache hit ratio if you know 99 | * that all modules have build files in addition to the 'module-info.java' files. 100 | */ 101 | public abstract Property getRequiresBuildFile(); 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/org/gradlex/javamodule/dependencies/initialization/JavaModuleDependenciesSettingsPlugin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright the GradleX team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.gradlex.javamodule.dependencies.initialization; 18 | 19 | import org.gradle.api.GradleException; 20 | import org.gradle.api.NonNullApi; 21 | import org.gradle.api.Plugin; 22 | import org.gradle.api.initialization.Settings; 23 | import org.gradle.util.GradleVersion; 24 | 25 | @NonNullApi 26 | public abstract class JavaModuleDependenciesSettingsPlugin implements Plugin { 27 | 28 | @Override 29 | public void apply(Settings settings) { 30 | if (GradleVersion.current().compareTo(GradleVersion.version("8.8")) < 0) { 31 | throw new GradleException("This settings plugin requires Gradle 8.8+"); 32 | } 33 | registerExtension(settings); 34 | } 35 | 36 | private void registerExtension(Settings settings) { 37 | settings.getExtensions().create("rootPlugins", RootPluginsExtension.class, settings); 38 | settings.getExtensions().create("javaModules", JavaModulesExtension.class, settings); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/org/gradlex/javamodule/dependencies/initialization/Module.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright the GradleX team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.gradlex.javamodule.dependencies.initialization; 18 | 19 | import org.gradle.api.provider.ListProperty; 20 | import org.gradle.api.provider.Property; 21 | 22 | import javax.inject.Inject; 23 | import java.io.File; 24 | import java.nio.file.Paths; 25 | import java.util.Arrays; 26 | import java.util.stream.Collectors; 27 | import java.util.stream.Stream; 28 | 29 | public abstract class Module { 30 | 31 | /** 32 | * The 'artifact' name of the Module. This corresponds to the Gradle subproject name. If the Module is published 33 | * to a Maven repository, this is the 'artifact' in the 'group:artifact' identifier to address the published Jar. 34 | */ 35 | public abstract Property getArtifact(); 36 | 37 | /** 38 | * The 'group' of the Module. This corresponds to setting the 'group' property in a build.gradle file. If the 39 | * Module is published to a Maven repository, this is the 'group' in the 'group:artifact' identifier to address 40 | * the published Jar. The group needs to be configured here (rather than in build.gradle files) for the plugin 41 | * to support additional Modules inside a subproject that other modules depend on, such as a 'testFixtures' module. 42 | */ 43 | public abstract Property getGroup(); 44 | 45 | /** 46 | * The paths of the module-info.java files inside the project directory. Usually, this does not need to be adjusted. 47 | * By default, it contains all 'src/$sourceSetName/java/module-info.java' files that exist. 48 | */ 49 | public abstract ListProperty getModuleInfoPaths(); 50 | 51 | /** 52 | * {@link Module#plugin(String)} 53 | */ 54 | public abstract ListProperty getPlugins(); 55 | 56 | File directory; 57 | 58 | @Inject 59 | public Module(File directory) { 60 | this.directory = directory; 61 | getArtifact().convention(directory.getName()); 62 | getModuleInfoPaths().convention(listSrcChildren() 63 | .map(srcDir -> new File(srcDir, "java/module-info.java")) 64 | .filter(File::exists) 65 | .map(moduleInfo -> "src/" + moduleInfo.getParentFile().getParentFile().getName() + "/java") 66 | .collect(Collectors.toList())); 67 | } 68 | 69 | /** 70 | * Apply a plugin to the Module project. This is the same as using the 'plugins { }' block in the Module's 71 | * build.gradle file. Applying plugins here allows you to omit build.gradle files completely. 72 | */ 73 | public void plugin(String id) { 74 | getPlugins().add(id); 75 | } 76 | 77 | private Stream listSrcChildren() { 78 | File[] children = new File(directory, "src").listFiles(); 79 | return children == null ? Stream.empty() : Arrays.stream(children); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/org/gradlex/javamodule/dependencies/initialization/RootPluginsExtension.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright the GradleX team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.gradlex.javamodule.dependencies.initialization; 18 | 19 | import org.gradle.api.IsolatedAction; 20 | import org.gradle.api.NonNullApi; 21 | import org.gradle.api.Project; 22 | import org.gradle.api.initialization.Settings; 23 | 24 | import javax.inject.Inject; 25 | import java.util.ArrayList; 26 | import java.util.List; 27 | 28 | public abstract class RootPluginsExtension { 29 | 30 | private final List ids = new ArrayList<>(); 31 | 32 | @Inject 33 | public RootPluginsExtension(Settings settings) { 34 | settings.getGradle().getLifecycle().beforeProject(new ApplyPluginAction(ids)); 35 | } 36 | 37 | public void id(String id) { 38 | ids.add(id); 39 | } 40 | 41 | @NonNullApi 42 | private static class ApplyPluginAction implements IsolatedAction { 43 | 44 | private final List ids; 45 | 46 | public ApplyPluginAction(List ids) { 47 | this.ids = ids; 48 | } 49 | 50 | @Override 51 | public void execute(Project project) { 52 | if (isRoot(project)) { 53 | for (String id : ids) { 54 | project.getPlugins().apply(id); 55 | } 56 | } 57 | } 58 | 59 | private boolean isRoot(Project project) { 60 | return project == project.getRootProject(); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/org/gradlex/javamodule/dependencies/internal/bridges/DependencyAnalysisBridge.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright the GradleX team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.gradlex.javamodule.dependencies.internal.bridges; 18 | 19 | import com.autonomousapps.AbstractExtension; 20 | import org.gradle.api.Project; 21 | import org.gradle.api.Task; 22 | import org.gradle.api.artifacts.Configuration; 23 | import org.gradle.api.tasks.SourceSetContainer; 24 | import org.gradle.api.tasks.TaskContainer; 25 | import org.gradle.api.tasks.TaskProvider; 26 | import org.gradlex.javamodule.dependencies.tasks.ModuleDirectivesOrderingCheck; 27 | import org.gradlex.javamodule.dependencies.tasks.ModuleDirectivesScopeCheck; 28 | 29 | import javax.annotation.Nullable; 30 | import java.io.File; 31 | 32 | public class DependencyAnalysisBridge { 33 | 34 | public static void registerDependencyAnalysisPostProcessingTask(Project project, @Nullable TaskProvider checkAllModuleInfo) { 35 | TaskContainer tasks = project.getTasks(); 36 | SourceSetContainer sourceSets = project.getExtensions().getByType(SourceSetContainer.class); 37 | 38 | TaskProvider checkModuleDirectivesScope = 39 | tasks.register("checkModuleDirectivesScope", ModuleDirectivesScopeCheck.class, 40 | t -> t.getReport().convention(project.getLayout().getBuildDirectory().file("reports/module-info-analysis/scopes.txt"))); 41 | 42 | sourceSets.all(sourceSet -> checkModuleDirectivesScope.configure(t -> { 43 | File moduleInfo = new File(sourceSet.getJava().getSrcDirs().iterator().next(), "module-info.java"); 44 | if (!moduleInfo.exists()) { 45 | moduleInfo = project.getBuildFile(); // no module-info: dependencies are declared in build file 46 | } 47 | t.getSourceSets().put(sourceSet.getName(), moduleInfo.getAbsolutePath()); 48 | 49 | Configuration cpClasspath = project.getConfigurations().getByName(sourceSet.getCompileClasspathConfigurationName()); 50 | Configuration rtClasspath = project.getConfigurations().getByName(sourceSet.getRuntimeClasspathConfigurationName()); 51 | t.getModuleArtifacts().add(project.provider(() -> cpClasspath.getIncoming().getArtifacts())); 52 | t.getModuleArtifacts().add(project.provider(() -> rtClasspath.getIncoming().getArtifacts())); 53 | })); 54 | 55 | project.getExtensions().getByType(AbstractExtension.class) 56 | .registerPostProcessingTask(checkModuleDirectivesScope); 57 | 58 | if (checkAllModuleInfo != null) { 59 | checkAllModuleInfo.configure(t -> t.dependsOn(checkModuleDirectivesScope)); 60 | } 61 | tasks.withType(ModuleDirectivesOrderingCheck.class).configureEach(t -> t.mustRunAfter(checkModuleDirectivesScope)); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/org/gradlex/javamodule/dependencies/internal/bridges/ExtraJavaModuleInfoBridge.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright the GradleX team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.gradlex.javamodule.dependencies.internal.bridges; 18 | 19 | import org.gradle.api.Project; 20 | import org.gradlex.javamodule.dependencies.JavaModuleDependenciesExtension; 21 | import org.gradlex.javamodule.moduleinfo.ExtraJavaModuleInfoPluginExtension; 22 | import org.gradlex.javamodule.moduleinfo.ModuleSpec; 23 | 24 | import java.util.Map; 25 | import java.util.stream.Collectors; 26 | 27 | public class ExtraJavaModuleInfoBridge { 28 | 29 | public static void autoRegisterPatchedModuleMappings(Project project, JavaModuleDependenciesExtension javaModuleDependencies) { 30 | ExtraJavaModuleInfoPluginExtension extraJavaModuleInfo = project.getExtensions().getByType(ExtraJavaModuleInfoPluginExtension.class); 31 | javaModuleDependencies.getModuleNameToGA().putAll(extraJavaModuleInfo.getModuleSpecs().map( 32 | moduleSpecs -> moduleSpecs.entrySet().stream().collect(Collectors.toMap(ExtraJavaModuleInfoBridge::moduleNameKey, Map.Entry::getKey, (a, b) -> b)))); 33 | } 34 | 35 | private static String moduleNameKey(Map.Entry entry) { 36 | if (!entry.getKey().contains(":")) { 37 | // Entry is not usable as mapping (e.g., because it uses file names instead of GA coordinates). 38 | // Invalidate this mapping entry by creating a key that is not a valid module name. 39 | return "__" + entry.getValue().getModuleName(); 40 | } 41 | return entry.getValue().getModuleName(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/org/gradlex/javamodule/dependencies/internal/diagnostics/AsciiModuleDependencyReportRenderer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright the GradleX team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.gradlex.javamodule.dependencies.internal.diagnostics; 18 | 19 | import org.gradle.api.NonNullApi; 20 | import org.gradle.api.artifacts.ArtifactCollection; 21 | import org.gradle.api.artifacts.result.ResolvedComponentResult; 22 | import org.gradle.api.provider.Provider; 23 | import org.gradle.api.tasks.diagnostics.internal.ConfigurationDetails; 24 | import org.gradle.api.tasks.diagnostics.internal.ProjectDetails; 25 | import org.gradle.api.tasks.diagnostics.internal.dependencies.AsciiDependencyReportRenderer; 26 | import org.gradle.api.tasks.diagnostics.internal.graph.DependencyGraphsRenderer; 27 | import org.gradle.api.tasks.diagnostics.internal.graph.NodeRenderer; 28 | import org.gradle.api.tasks.diagnostics.internal.graph.nodes.RenderableDependency; 29 | import org.gradle.api.tasks.diagnostics.internal.graph.nodes.RenderableModuleResult; 30 | import org.gradle.internal.graph.GraphRenderer; 31 | import org.gradle.internal.logging.text.StyledTextOutput; 32 | 33 | import java.util.Collections; 34 | import java.util.Map; 35 | 36 | import static java.util.Objects.requireNonNull; 37 | 38 | @NonNullApi 39 | public class AsciiModuleDependencyReportRenderer extends AsciiDependencyReportRenderer { 40 | 41 | private DependencyGraphsRenderer dependencyGraphRenderer; 42 | private final Provider> resolvedJars; 43 | 44 | public AsciiModuleDependencyReportRenderer(Provider> resolvedJars) { 45 | this.resolvedJars = resolvedJars; 46 | } 47 | 48 | @Override 49 | public void startProject(ProjectDetails project) { 50 | super.startProject(project); 51 | GraphRenderer renderer = new GraphRenderer(this.getTextOutput()); 52 | this.dependencyGraphRenderer = new DependencyGraphsRenderer(this.getTextOutput(), renderer, NodeRenderer.NO_OP, new StyledNodeRenderer()); 53 | } 54 | 55 | @Override 56 | public void render(ConfigurationDetails configuration) { 57 | if (configuration.isCanBeResolved()) { 58 | ResolvedComponentResult result = requireNonNull(configuration.getResolutionResultRoot()).get(); 59 | RenderableModuleResult root = new RenderableJavaModuleResult(result, resolvedJars.get().get(configuration.getName()).getArtifacts()); 60 | renderNow(root); 61 | } else { 62 | renderNow(requireNonNull(configuration.getUnresolvableResult())); 63 | } 64 | } 65 | 66 | private void renderNow(RenderableDependency root) { 67 | if (root.getChildren().isEmpty()) { 68 | this.getTextOutput().withStyle(StyledTextOutput.Style.Info).text("No dependencies"); 69 | this.getTextOutput().println(); 70 | } else { 71 | this.dependencyGraphRenderer.render(Collections.singletonList(root)); 72 | } 73 | } 74 | 75 | public void complete() { 76 | if (this.dependencyGraphRenderer != null) { 77 | this.dependencyGraphRenderer.complete(); 78 | } 79 | super.complete(); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/org/gradlex/javamodule/dependencies/internal/diagnostics/RenderableJavaModuleResult.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright the GradleX team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.gradlex.javamodule.dependencies.internal.diagnostics; 18 | 19 | import org.gradle.api.NonNullApi; 20 | import org.gradle.api.artifacts.result.DependencyResult; 21 | import org.gradle.api.artifacts.result.ResolvedArtifactResult; 22 | import org.gradle.api.artifacts.result.ResolvedComponentResult; 23 | import org.gradle.api.artifacts.result.ResolvedDependencyResult; 24 | import org.gradle.api.artifacts.result.UnresolvedDependencyResult; 25 | import org.gradle.api.tasks.diagnostics.internal.graph.nodes.RenderableDependency; 26 | import org.gradle.api.tasks.diagnostics.internal.graph.nodes.RenderableModuleResult; 27 | import org.gradle.api.tasks.diagnostics.internal.graph.nodes.RenderableUnresolvedDependencyResult; 28 | 29 | import java.util.LinkedHashSet; 30 | import java.util.Set; 31 | 32 | @NonNullApi 33 | public class RenderableJavaModuleResult extends RenderableModuleResult { 34 | 35 | private final Set resolvedJars; 36 | 37 | public RenderableJavaModuleResult(ResolvedComponentResult module, Set resolvedJars) { 38 | super(module); 39 | this.resolvedJars = resolvedJars; 40 | } 41 | 42 | @Override 43 | public Set getChildren() { 44 | Set out = new LinkedHashSet<>(); 45 | for (DependencyResult d : module.getDependencies()) { 46 | if (d instanceof UnresolvedDependencyResult) { 47 | out.add(new RenderableUnresolvedDependencyResult((UnresolvedDependencyResult) d)); 48 | } else { 49 | out.add(new RenderableModuleDependencyResult((ResolvedDependencyResult) d, resolvedJars)); 50 | } 51 | } 52 | return out; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/org/gradlex/javamodule/dependencies/internal/diagnostics/RenderableModuleDependencyResult.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright the GradleX team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.gradlex.javamodule.dependencies.internal.diagnostics; 18 | 19 | import org.gradle.api.NonNullApi; 20 | import org.gradle.api.artifacts.component.ComponentIdentifier; 21 | import org.gradle.api.artifacts.component.ComponentSelector; 22 | import org.gradle.api.artifacts.component.ModuleComponentIdentifier; 23 | import org.gradle.api.artifacts.component.ModuleComponentSelector; 24 | import org.gradle.api.artifacts.result.DependencyResult; 25 | import org.gradle.api.artifacts.result.ResolvedArtifactResult; 26 | import org.gradle.api.artifacts.result.ResolvedDependencyResult; 27 | import org.gradle.api.artifacts.result.UnresolvedDependencyResult; 28 | import org.gradle.api.tasks.diagnostics.internal.graph.nodes.RenderableDependency; 29 | import org.gradle.api.tasks.diagnostics.internal.graph.nodes.RenderableDependencyResult; 30 | import org.gradle.api.tasks.diagnostics.internal.graph.nodes.RenderableUnresolvedDependencyResult; 31 | 32 | import java.io.IOException; 33 | import java.util.LinkedHashSet; 34 | import java.util.Set; 35 | 36 | import static org.gradlex.javamodule.dependencies.internal.utils.ModuleJar.isRealModule; 37 | import static org.gradlex.javamodule.dependencies.internal.utils.ModuleJar.readModuleNameFromJarFile; 38 | 39 | @NonNullApi 40 | public class RenderableModuleDependencyResult extends RenderableDependencyResult { 41 | private final ResolvedDependencyResult dependency; 42 | private final Set resolvedJars; 43 | 44 | public RenderableModuleDependencyResult(ResolvedDependencyResult dependency, Set resolvedJars) { 45 | super(dependency); 46 | this.dependency = dependency; 47 | this.resolvedJars = resolvedJars; 48 | } 49 | 50 | @Override 51 | public Set getChildren() { 52 | Set out = new LinkedHashSet<>(); 53 | for (DependencyResult d : dependency.getSelected().getDependencies()) { 54 | if (d instanceof UnresolvedDependencyResult) { 55 | out.add(new RenderableUnresolvedDependencyResult((UnresolvedDependencyResult) d)); 56 | } else { 57 | ResolvedDependencyResult resolved = (ResolvedDependencyResult) d; 58 | ResolvedArtifactResult artifact = resolvedJars.stream().filter(a -> 59 | a.getId().getComponentIdentifier().equals(resolved.getSelected().getId())).findFirst().orElse(null); 60 | if (artifact != null) { 61 | out.add(new RenderableModuleDependencyResult(resolved, resolvedJars)); 62 | } 63 | } 64 | } 65 | return out; 66 | } 67 | 68 | @Override 69 | public String getName() { 70 | ComponentSelector requested = getRequested(); 71 | ComponentIdentifier selected = getActual(); 72 | ResolvedArtifactResult artifact = resolvedJars.stream().filter(a -> 73 | a.getId().getComponentIdentifier().equals(selected)).findFirst().orElse(null); 74 | 75 | try { 76 | if (artifact == null) { 77 | return "[BOM] " + selected.getDisplayName(); 78 | } else { 79 | String actualModuleName = readModuleNameFromJarFile(artifact.getFile()); 80 | if (actualModuleName == null) { 81 | return "[CLASSPATH] " + selected.getDisplayName(); 82 | } else { 83 | String version = ""; 84 | String coordinates = selected.getDisplayName(); 85 | String jarName = artifact.getFile().getName(); 86 | if (selected instanceof ModuleComponentIdentifier) { 87 | String selectedVersion = ((ModuleComponentIdentifier) selected).getVersion(); 88 | version = " (" + selectedVersion + ")"; 89 | if (requested instanceof ModuleComponentSelector) { 90 | String requestedVersion = ((ModuleComponentSelector) requested).getVersion(); 91 | if (!requestedVersion.isEmpty() && !selectedVersion.equals(requestedVersion)) { 92 | version = " (" + requestedVersion + " -> " + selectedVersion + ")"; 93 | } 94 | } 95 | coordinates = ((ModuleComponentIdentifier) selected).getModuleIdentifier().toString(); 96 | } 97 | String auto = isRealModule(artifact.getFile()) ? "" : "[AUTO] "; 98 | return auto + actualModuleName + version + " | " + coordinates + 99 | (isConstraint() ? "" : " | " + jarName); 100 | } 101 | } 102 | } catch (IOException e) { 103 | throw new RuntimeException(e); 104 | } 105 | } 106 | 107 | private boolean isConstraint() { 108 | return getResolutionState() == ResolutionState.RESOLVED_CONSTRAINT; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/main/java/org/gradlex/javamodule/dependencies/internal/diagnostics/StyledNodeRenderer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright the GradleX team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.gradlex.javamodule.dependencies.internal.diagnostics; 18 | 19 | import org.gradle.api.tasks.diagnostics.internal.graph.NodeRenderer; 20 | import org.gradle.api.tasks.diagnostics.internal.graph.nodes.RenderableDependency; 21 | import org.gradle.internal.logging.text.StyledTextOutput; 22 | 23 | import static org.gradle.internal.logging.text.StyledTextOutput.Style.Description; 24 | import static org.gradle.internal.logging.text.StyledTextOutput.Style.Failure; 25 | import static org.gradle.internal.logging.text.StyledTextOutput.Style.Info; 26 | 27 | public class StyledNodeRenderer implements NodeRenderer { 28 | 29 | @Override 30 | public void renderNode(StyledTextOutput output, RenderableDependency node, boolean alreadyRendered) { 31 | String name = node.getName(); 32 | if (name.startsWith("[BOM]")) { 33 | output.withStyle(Description).text(name); 34 | } else if (name.startsWith("[AUTO]")) { 35 | output.withStyle(Failure).text(name); 36 | } else if (name.startsWith("[CLASSPATH]")) { 37 | output.withStyle(Failure).text(name); 38 | } else { 39 | int idx = name.indexOf('|'); 40 | output.text(name.substring(0, idx)).withStyle(Description).text(name.substring(idx)); 41 | } 42 | switch (node.getResolutionState()) { 43 | case FAILED: 44 | output.withStyle(Failure).text(" FAILED"); 45 | break; 46 | case RESOLVED: 47 | if (alreadyRendered && !node.getChildren().isEmpty()) { 48 | output.withStyle(Info).text(" (*)"); 49 | } 50 | break; 51 | case RESOLVED_CONSTRAINT: 52 | output.withStyle(Info).text(" (c)"); 53 | break; 54 | case UNRESOLVED: 55 | output.withStyle(Info).text(" (n)"); 56 | break; 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/org/gradlex/javamodule/dependencies/internal/dsl/AllDirectivesInternal.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright the GradleX team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.gradlex.javamodule.dependencies.internal.dsl; 18 | 19 | import org.gradle.api.tasks.SourceSet; 20 | import org.gradlex.javamodule.dependencies.JavaModuleDependenciesExtension; 21 | import org.gradlex.javamodule.dependencies.dsl.AllDirectives; 22 | 23 | import java.util.List; 24 | 25 | /** 26 | * Note: These methods are used by the 'java-module-testing' plugin to access information 27 | * defined in the Module Info DSL. 28 | */ 29 | abstract public class AllDirectivesInternal extends AllDirectives { 30 | 31 | public AllDirectivesInternal(SourceSet sourceSet, SourceSet mainSourceSet, JavaModuleDependenciesExtension javaModuleDependencies) { 32 | super(sourceSet, mainSourceSet, javaModuleDependencies); 33 | } 34 | 35 | public List getCompileClasspathModules() { 36 | return compileClasspathModules; 37 | } 38 | 39 | public List getRuntimeClasspathModules() { 40 | return runtimeClasspathModules; 41 | } 42 | 43 | public List getExportsToModules() { 44 | return exportsToModules; 45 | } 46 | 47 | public List getOpensToModules() { 48 | return opensToModules; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/org/gradlex/javamodule/dependencies/internal/dsl/GradleOnlyDirectivesInternal.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright the GradleX team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.gradlex.javamodule.dependencies.internal.dsl; 18 | 19 | import org.gradle.api.tasks.SourceSet; 20 | import org.gradlex.javamodule.dependencies.JavaModuleDependenciesExtension; 21 | import org.gradlex.javamodule.dependencies.dsl.GradleOnlyDirectives; 22 | 23 | import java.util.List; 24 | 25 | public abstract class GradleOnlyDirectivesInternal extends GradleOnlyDirectives { 26 | 27 | public GradleOnlyDirectivesInternal(SourceSet sourceSet, SourceSet mainSourceSet, JavaModuleDependenciesExtension javaModuleDependencies) { 28 | super(sourceSet, mainSourceSet, javaModuleDependencies); 29 | } 30 | 31 | public List getCompileClasspathModules() { 32 | return compileClasspathModules; 33 | } 34 | 35 | public List getRuntimeClasspathModules() { 36 | return runtimeClasspathModules; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/org/gradlex/javamodule/dependencies/internal/utils/DependencyDeclarationsUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright the GradleX team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.gradlex.javamodule.dependencies.internal.utils; 18 | 19 | import org.gradle.api.Project; 20 | import org.gradle.api.artifacts.ConfigurationContainer; 21 | import org.gradle.api.artifacts.Dependency; 22 | import org.gradle.api.artifacts.ModuleDependency; 23 | import org.gradle.api.artifacts.ProjectDependency; 24 | import org.gradle.api.attributes.Category; 25 | import org.gradle.api.capabilities.Capability; 26 | import org.gradle.api.provider.Provider; 27 | 28 | import java.util.Collections; 29 | import java.util.List; 30 | import java.util.stream.Collectors; 31 | 32 | import static org.gradle.api.attributes.Category.CATEGORY_ATTRIBUTE; 33 | import static org.gradle.api.attributes.Category.LIBRARY; 34 | 35 | public abstract class DependencyDeclarationsUtil { 36 | 37 | public static Provider> declaredDependencies(Project project, String configuration) { 38 | ConfigurationContainer configurations = project.getConfigurations(); 39 | return project.provider(() -> configurations.getNames().contains(configuration) 40 | ? configurations.getByName(configuration).getDependencies().stream() 41 | .filter(DependencyDeclarationsUtil::isLibraryDependency) 42 | .map(DependencyDeclarationsUtil::toIdentifier).collect(Collectors.toList()) 43 | : Collections.emptyList()); 44 | } 45 | 46 | private static String toIdentifier(Dependency dependency) { 47 | if (dependency instanceof ProjectDependency) { 48 | // assume Module Name of local Module 49 | ProjectDependency projectDependency = (ProjectDependency) dependency; 50 | if (projectDependency.getRequestedCapabilities().isEmpty()) { 51 | return projectDependency.getDependencyProject().getGroup() + "." + dependency.getName(); 52 | } else { 53 | Capability capability = projectDependency.getRequestedCapabilities().get(0); 54 | return capability.getGroup() + "." + capability.getName().replace("-", "."); 55 | } 56 | } 57 | return dependency.getGroup() + ":" + dependency.getName(); 58 | } 59 | 60 | private static boolean isLibraryDependency(Dependency dependency) { 61 | if (dependency instanceof ModuleDependency) { 62 | ModuleDependency moduleDependency = (ModuleDependency) dependency; 63 | Category category = moduleDependency.getAttributes().getAttribute(CATEGORY_ATTRIBUTE); 64 | return category == null || category.getName().equals(LIBRARY); 65 | } 66 | return false; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/org/gradlex/javamodule/dependencies/internal/utils/ModuleInfoCache.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright the GradleX team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.gradlex.javamodule.dependencies.internal.utils; 18 | 19 | import org.gradle.api.logging.Logger; 20 | import org.gradle.api.provider.Provider; 21 | import org.gradle.api.provider.ProviderFactory; 22 | import org.gradle.api.tasks.SourceSet; 23 | import org.slf4j.LoggerFactory; 24 | 25 | import javax.inject.Inject; 26 | import java.io.File; 27 | import java.nio.file.Path; 28 | import java.nio.file.Paths; 29 | import java.util.HashMap; 30 | import java.util.Map; 31 | 32 | import static org.gradlex.javamodule.dependencies.internal.utils.ModuleNamingUtil.sourceSetToCapabilitySuffix; 33 | 34 | public abstract class ModuleInfoCache { 35 | private static final Logger LOGGER = (Logger) LoggerFactory.getLogger(ModuleInfoCache.class); 36 | 37 | private final boolean initializedInSettings; 38 | private final Map moduleInfo = new HashMap<>(); 39 | private final Map moduleNameToProjectPath = new HashMap<>(); 40 | private final Map moduleNameToCapability = new HashMap<>(); 41 | 42 | @Inject 43 | public ModuleInfoCache(boolean initializedInSettings) { 44 | this.initializedInSettings = initializedInSettings; 45 | } 46 | 47 | public boolean isInitializedInSettings() { 48 | return initializedInSettings; 49 | } 50 | 51 | /** 52 | * Returns the module-info.java for the given SourceSet. If the SourceSet has multiple source folders with multiple 53 | * module-info files (which is usually a broken setup) the first file found is returned. 54 | * 55 | * @param sourceSet the SourceSet representing a module 56 | * @return parsed module-info.java for the given SourceSet 57 | */ 58 | public ModuleInfo get(SourceSet sourceSet, ProviderFactory providers) { 59 | for (File folder : sourceSet.getJava().getSrcDirs()) { 60 | if (maybePutModuleInfo(folder, providers)) { 61 | return moduleInfo.get(folder); 62 | } 63 | } 64 | return ModuleInfo.EMPTY; 65 | } 66 | 67 | public File getFolder(SourceSet sourceSet, ProviderFactory providers) { 68 | for (File folder : sourceSet.getJava().getSrcDirs()) { 69 | if (maybePutModuleInfo(folder, providers)) { 70 | return folder; 71 | } 72 | } 73 | return null; 74 | } 75 | 76 | /** 77 | * @param projectRoot the project that should hold a Java module 78 | * @return parsed module-info.java for the given project assuming a standard Java project layout 79 | */ 80 | public ModuleInfo put(File projectRoot, String moduleInfoPath, String projectPath, String artifact, Provider group, ProviderFactory providers) { 81 | File folder = new File(projectRoot, moduleInfoPath); 82 | if (maybePutModuleInfo(folder, providers)) { 83 | ModuleInfo thisModuleInfo = moduleInfo.get(folder); 84 | moduleNameToProjectPath.put(thisModuleInfo.getModuleName(), projectPath); 85 | Path parentDirectory = Paths.get(moduleInfoPath).getParent(); 86 | String capabilitySuffix = parentDirectory == null ? null : sourceSetToCapabilitySuffix(parentDirectory.getFileName().toString()); 87 | if (capabilitySuffix != null) { 88 | if (group.isPresent()) { 89 | moduleNameToCapability.put(thisModuleInfo.getModuleName(), group.get() + ":" + artifact + "-" + capabilitySuffix); 90 | } else { 91 | LOGGER.lifecycle( 92 | "[WARN] [Java Module Dependencies] " + thisModuleInfo.getModuleName() + " - 'group' not defined!"); 93 | } 94 | } 95 | return thisModuleInfo; 96 | } 97 | return ModuleInfo.EMPTY; 98 | } 99 | 100 | public String getProjectPath(String moduleName) { 101 | return moduleNameToProjectPath.get(moduleName); 102 | } 103 | 104 | public String getCapability(String moduleName) { 105 | return moduleNameToCapability.get(moduleName); 106 | } 107 | 108 | private boolean maybePutModuleInfo(File folder, ProviderFactory providers) { 109 | Provider moduleInfoProvider = provideModuleInfo(folder, providers); 110 | if (moduleInfoProvider.isPresent()) { 111 | if (!moduleInfo.containsKey(folder)) { 112 | moduleInfo.put(folder, moduleInfoProvider.get() ); 113 | } 114 | return true; 115 | } 116 | return false; 117 | } 118 | 119 | private Provider provideModuleInfo(File folder, ProviderFactory providers) { 120 | return providers.of(ValueSourceModuleInfo.class, spec -> spec.parameters(param -> param.getDir().set(folder))); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/main/java/org/gradlex/javamodule/dependencies/internal/utils/ModuleInfoClassCreator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright the GradleX team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.gradlex.javamodule.dependencies.internal.utils; 18 | 19 | import org.objectweb.asm.ClassWriter; 20 | import org.objectweb.asm.ModuleVisitor; 21 | 22 | import java.io.File; 23 | import java.io.FileOutputStream; 24 | import java.io.IOException; 25 | 26 | import static org.objectweb.asm.Opcodes.ACC_MANDATED; 27 | import static org.objectweb.asm.Opcodes.ACC_MODULE; 28 | import static org.objectweb.asm.Opcodes.ACC_SYNTHETIC; 29 | 30 | public abstract class ModuleInfoClassCreator { 31 | 32 | public static void createEmpty(File targetFolder) { 33 | //noinspection ResultOfMethodCallIgnored 34 | targetFolder.mkdirs(); 35 | 36 | ClassWriter cw = new ClassWriter(0); 37 | cw.visit(53, ACC_MODULE, "module-info", null, null, null); 38 | ModuleVisitor moduleVisitor = cw.visitModule(targetFolder.getName(), ACC_SYNTHETIC, null); 39 | moduleVisitor.visitRequire("java.base", ACC_MANDATED, null); 40 | cw.visitEnd(); 41 | try (FileOutputStream s = new FileOutputStream(new File(targetFolder, "module-info.class"))) { 42 | s.write(cw.toByteArray()); 43 | } catch (IOException e) { 44 | throw new RuntimeException(e); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/org/gradlex/javamodule/dependencies/internal/utils/ModuleJar.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright the GradleX team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.gradlex.javamodule.dependencies.internal.utils; 18 | 19 | import org.objectweb.asm.ClassReader; 20 | import org.objectweb.asm.ClassVisitor; 21 | import org.objectweb.asm.ModuleVisitor; 22 | import org.objectweb.asm.Opcodes; 23 | 24 | import java.io.File; 25 | import java.io.IOException; 26 | import java.io.InputStream; 27 | import java.nio.file.Files; 28 | import java.util.jar.JarInputStream; 29 | import java.util.jar.Manifest; 30 | import java.util.regex.Pattern; 31 | import java.util.zip.ZipEntry; 32 | 33 | public class ModuleJar { 34 | private static final String AUTOMATIC_MODULE_NAME_ATTRIBUTE = "Automatic-Module-Name"; 35 | private static final String MULTI_RELEASE_ATTRIBUTE = "Multi-Release"; 36 | private static final String MODULE_INFO_CLASS_FILE = "module-info.class"; 37 | private static final Pattern MODULE_INFO_CLASS_MRJAR_PATH = Pattern.compile("META-INF/versions/\\d+/module-info.class"); 38 | 39 | public static String readModuleNameFromJarFile(File jarFileOrClassFolder) throws IOException { 40 | if (jarFileOrClassFolder.isDirectory()) { 41 | // class folder 42 | File moduleInfo = new File(jarFileOrClassFolder, MODULE_INFO_CLASS_FILE); 43 | if (!moduleInfo.exists()) { 44 | return null; 45 | } 46 | return readNameFromModuleInfoClass(Files.newInputStream(moduleInfo.toPath())); 47 | } 48 | try (JarInputStream jarStream = new JarInputStream(Files.newInputStream(jarFileOrClassFolder.toPath()))) { 49 | String moduleName = getAutomaticModuleName(jarStream.getManifest()); 50 | if (moduleName != null) { 51 | return moduleName; 52 | } 53 | boolean isMultiReleaseJar = containsMultiReleaseJarEntry(jarStream); 54 | ZipEntry next = jarStream.getNextEntry(); 55 | while (next != null) { 56 | if (MODULE_INFO_CLASS_FILE.equals(next.getName())) { 57 | return readNameFromModuleInfoClass(jarStream); 58 | } 59 | if (isMultiReleaseJar && MODULE_INFO_CLASS_MRJAR_PATH.matcher(next.getName()).matches()) { 60 | return readNameFromModuleInfoClass(jarStream); 61 | } 62 | next = jarStream.getNextEntry(); 63 | } 64 | } 65 | return null; 66 | } 67 | 68 | public static boolean isRealModule(File jarFileOrClassFolder) throws IOException { 69 | if (jarFileOrClassFolder.isDirectory()) { 70 | // class folder 71 | return new File(jarFileOrClassFolder, MODULE_INFO_CLASS_FILE).exists(); 72 | } 73 | try (JarInputStream jarStream = new JarInputStream(Files.newInputStream(jarFileOrClassFolder.toPath()))) { 74 | boolean isMultiReleaseJar = containsMultiReleaseJarEntry(jarStream); 75 | ZipEntry next = jarStream.getNextEntry(); 76 | while (next != null) { 77 | if (MODULE_INFO_CLASS_FILE.equals(next.getName())) { 78 | return true; 79 | } 80 | if (isMultiReleaseJar && MODULE_INFO_CLASS_MRJAR_PATH.matcher(next.getName()).matches()) { 81 | return true; 82 | } 83 | next = jarStream.getNextEntry(); 84 | } 85 | } 86 | return false; 87 | } 88 | 89 | private static String getAutomaticModuleName(Manifest manifest) { 90 | if (manifest == null) { 91 | return null; 92 | } 93 | return manifest.getMainAttributes().getValue(AUTOMATIC_MODULE_NAME_ATTRIBUTE); 94 | } 95 | 96 | private static boolean containsMultiReleaseJarEntry(JarInputStream jarStream) { 97 | Manifest manifest = jarStream.getManifest(); 98 | return manifest !=null && Boolean.parseBoolean(manifest.getMainAttributes().getValue(MULTI_RELEASE_ATTRIBUTE)); 99 | } 100 | 101 | private static String readNameFromModuleInfoClass(InputStream input) throws IOException { 102 | ClassReader classReader = new ClassReader(input); 103 | String[] moduleName = new String[1]; 104 | classReader.accept(new ClassVisitor(Opcodes.ASM8) { 105 | @Override 106 | public ModuleVisitor visitModule(String name, int access, String version) { 107 | moduleName[0] = name; 108 | return super.visitModule(name, access, version); 109 | } 110 | }, 0); 111 | return moduleName[0]; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/main/java/org/gradlex/javamodule/dependencies/internal/utils/ModuleNamingUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright the GradleX team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.gradlex.javamodule.dependencies.internal.utils; 18 | 19 | import org.gradle.api.tasks.SourceSet; 20 | 21 | import java.util.Arrays; 22 | import java.util.stream.Collectors; 23 | 24 | public abstract class ModuleNamingUtil { 25 | 26 | public static String sourceSetToModuleName(String projectName, String sourceSetName) { 27 | if (sourceSetName.equals(SourceSet.MAIN_SOURCE_SET_NAME)) { 28 | return toDottedCase(projectName); 29 | } 30 | return toDottedCase(projectName) + "." + toDottedCase(sourceSetName); 31 | } 32 | 33 | public static String sourceSetToCapabilitySuffix(String sourceSetName) { 34 | if (sourceSetName.equals(SourceSet.MAIN_SOURCE_SET_NAME)) { 35 | return null; 36 | } 37 | return toKebabCase(sourceSetName); 38 | } 39 | 40 | /** 41 | * Converts 'camelCase' and 'kebab-case' to 'dotted.case'. 42 | */ 43 | private static String toDottedCase(String sourceSetName) { 44 | return Arrays.stream(sourceSetName.replace("-", ".") 45 | .split("(?, ValueModuleDirectoryListing.Parameter> { 33 | 34 | public interface Parameter extends ValueSourceParameters { 35 | Property getDir(); 36 | SetProperty getExplicitlyConfiguredFolders(); 37 | SetProperty getExclusions(); 38 | Property getRequiresBuildFile(); 39 | } 40 | 41 | @Override 42 | public List obtain() { 43 | Path path = getParameters().getDir().get().toPath(); 44 | try (Stream directoryStream = Files.find(path, 1, (unused, basicFileAttributes) -> basicFileAttributes.isDirectory())) { 45 | return directoryStream 46 | .filter(x -> !getParameters().getExplicitlyConfiguredFolders().get().contains(x.getFileName().toString())) 47 | .filter(x -> getParameters().getExclusions().get().stream().noneMatch(r -> x.getFileName().toString().matches(r))) 48 | .filter(x -> checkBuildFile(x, getParameters())) 49 | .map(x -> x.getFileName().toString()) 50 | .sorted() 51 | .collect(Collectors.toList()); 52 | 53 | } catch (IOException e) { 54 | throw new RuntimeException("Failed to inspect: " + path, e); 55 | } 56 | } 57 | 58 | private boolean checkBuildFile(Path x, Parameter parameters) { 59 | if (!parameters.getRequiresBuildFile().get()) { 60 | return true; 61 | } 62 | return Files.isRegularFile(x.resolve("build.gradle.kts")) || Files.isRegularFile(x.resolve("build.gradle")); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/org/gradlex/javamodule/dependencies/internal/utils/ValueSourceModuleInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright the GradleX team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.gradlex.javamodule.dependencies.internal.utils; 18 | 19 | import org.gradle.api.file.DirectoryProperty; 20 | import org.gradle.api.provider.ValueSource; 21 | import org.gradle.api.provider.ValueSourceParameters; 22 | import org.jetbrains.annotations.Nullable; 23 | 24 | import java.io.File; 25 | import java.io.IOException; 26 | import java.util.Scanner; 27 | 28 | public abstract class ValueSourceModuleInfo implements ValueSource { 29 | 30 | interface Parameter extends ValueSourceParameters { 31 | DirectoryProperty getDir(); 32 | } 33 | 34 | @Override 35 | public @Nullable ModuleInfo obtain() { 36 | Parameter parameters = getParameters(); 37 | File file = new File(parameters.getDir().get().getAsFile(), "module-info.java"); 38 | if (file.isFile()) { 39 | try { 40 | try (Scanner scan = new Scanner(file)) { 41 | scan.useDelimiter("\\Z"); 42 | String content = scan.next(); 43 | return new ModuleInfo(content); 44 | } 45 | } catch (IOException e) { 46 | throw new RuntimeException(e); 47 | } 48 | } 49 | return null; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/org/gradlex/javamodule/dependencies/tasks/CatalogGenerate.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright the GradleX team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.gradlex.javamodule.dependencies.tasks; 18 | 19 | import org.gradle.api.DefaultTask; 20 | import org.gradle.api.file.RegularFileProperty; 21 | import org.gradle.api.provider.ListProperty; 22 | import org.gradle.api.provider.Property; 23 | import org.gradle.api.provider.Provider; 24 | import org.gradle.api.provider.SetProperty; 25 | import org.gradle.api.tasks.Internal; 26 | import org.gradle.api.tasks.TaskAction; 27 | 28 | import javax.annotation.Nullable; 29 | import java.io.File; 30 | import java.io.IOException; 31 | import java.nio.file.Files; 32 | import java.util.ArrayList; 33 | import java.util.Comparator; 34 | import java.util.List; 35 | import java.util.Objects; 36 | import java.util.stream.Collectors; 37 | 38 | public abstract class CatalogGenerate extends DefaultTask { 39 | 40 | public static class CatalogEntry implements Comparator { 41 | private final String moduleName; 42 | private final Provider fullId; 43 | private final String version; 44 | 45 | public CatalogEntry(String moduleName, Provider fullId, @Nullable String version) { 46 | this.moduleName = moduleName; 47 | this.fullId = fullId; 48 | this.version = version; 49 | } 50 | 51 | @Override 52 | public int compare(CatalogEntry e1, CatalogEntry e2) { 53 | return e1.moduleName.compareTo(e2.moduleName); 54 | } 55 | 56 | @Override 57 | public boolean equals(Object o) { 58 | if (this == o) return true; 59 | if (o == null || getClass() != o.getClass()) return false; 60 | CatalogEntry that = (CatalogEntry) o; 61 | return Objects.equals(moduleName, that.moduleName); 62 | } 63 | 64 | @Override 65 | public int hashCode() { 66 | return Objects.hash(moduleName); 67 | } 68 | } 69 | 70 | @Internal 71 | public abstract SetProperty getEntries(); 72 | 73 | @Internal 74 | public abstract Property getOwnProjectGroup(); 75 | 76 | @Internal 77 | public abstract RegularFileProperty getCatalogFile(); 78 | 79 | @TaskAction 80 | public void generate() throws IOException { 81 | File catalog = getCatalogFile().get().getAsFile(); 82 | //noinspection ResultOfMethodCallIgnored 83 | catalog.getParentFile().mkdirs(); 84 | 85 | List content = new ArrayList<>(); 86 | content.add("[libraries]"); 87 | content.addAll(getEntries().get().stream().map(this::toDeclarationString).filter(Objects::nonNull).sorted().collect(Collectors.toList())); 88 | 89 | Files.write(catalog.toPath(), content); 90 | } 91 | 92 | @Nullable 93 | private String toDeclarationString(CatalogEntry entry) { 94 | String group = entry.fullId.get().split(":")[0]; 95 | if (group.equals(getOwnProjectGroup().get())) { 96 | return null; 97 | } 98 | String notation; 99 | if (entry.version == null) { 100 | notation = "{ module = \"" + entry.fullId.get() + "\" }"; 101 | } else { 102 | notation = "{ module = \"" + entry.fullId.get() + "\", version = \"" + entry.version + "\" }"; 103 | } 104 | return entry.moduleName.replace('.', '-') + " = " + notation; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/main/java/org/gradlex/javamodule/dependencies/tasks/ModuleDependencyReport.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright the GradleX team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.gradlex.javamodule.dependencies.tasks; 18 | 19 | import org.gradle.api.NonNullApi; 20 | import org.gradle.api.artifacts.ArtifactCollection; 21 | import org.gradle.api.artifacts.Configuration; 22 | import org.gradle.api.file.ConfigurableFileCollection; 23 | import org.gradle.api.provider.MapProperty; 24 | import org.gradle.api.provider.ProviderFactory; 25 | import org.gradle.api.tasks.Classpath; 26 | import org.gradle.api.tasks.Internal; 27 | import org.gradle.api.tasks.diagnostics.DependencyReportTask; 28 | import org.gradlex.javamodule.dependencies.internal.diagnostics.AsciiModuleDependencyReportRenderer; 29 | 30 | import javax.inject.Inject; 31 | import java.util.Collections; 32 | import java.util.Set; 33 | 34 | @NonNullApi 35 | public abstract class ModuleDependencyReport extends DependencyReportTask { 36 | 37 | @Internal 38 | public abstract MapProperty getModuleArtifacts(); 39 | 40 | /** 41 | * Required to track all Jar files as input of the task. 42 | * Although they are only accessed through getModuleArtifacts(). 43 | */ 44 | @Classpath 45 | public abstract ConfigurableFileCollection getModulePath(); 46 | 47 | @Inject 48 | protected abstract ProviderFactory getProviders(); 49 | 50 | public ModuleDependencyReport() { 51 | setRenderer(new AsciiModuleDependencyReportRenderer(getModuleArtifacts())); 52 | } 53 | 54 | @Override 55 | public void setConfiguration(String configurationName) { 56 | super.setConfiguration(configurationName); 57 | configurationsChanged(); 58 | } 59 | 60 | @Override 61 | public void setConfigurations(Set configurations) { 62 | super.setConfigurations(configurations); 63 | configurationsChanged(); 64 | } 65 | 66 | private void configurationsChanged() { 67 | getModulePath().setFrom(); 68 | getModuleArtifacts().set(Collections.emptyMap()); 69 | for (Configuration conf : getConfigurations()) { 70 | getModulePath().from(conf); 71 | getModuleArtifacts().put(conf.getName(), getProviders().provider(() -> conf.getIncoming().getArtifacts())); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/org/gradlex/javamodule/dependencies/tasks/ModuleDirectivesOrderingCheck.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright the GradleX team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.gradlex.javamodule.dependencies.tasks; 18 | 19 | import org.gradle.api.DefaultTask; 20 | import org.gradle.api.file.RegularFileProperty; 21 | import org.gradle.api.provider.Property; 22 | import org.gradle.api.tasks.CacheableTask; 23 | import org.gradle.api.tasks.Input; 24 | import org.gradle.api.tasks.Optional; 25 | import org.gradle.api.tasks.OutputFile; 26 | import org.gradle.api.tasks.TaskAction; 27 | import org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo; 28 | 29 | import java.io.IOException; 30 | import java.nio.file.Files; 31 | import java.util.ArrayList; 32 | import java.util.List; 33 | import java.util.stream.Collectors; 34 | 35 | @CacheableTask 36 | public abstract class ModuleDirectivesOrderingCheck extends DefaultTask { 37 | 38 | @Input 39 | @Optional 40 | public abstract Property getModuleInfoPath(); 41 | 42 | @Input 43 | @Optional 44 | public abstract Property getModuleNamePrefix(); 45 | 46 | @Input 47 | public abstract Property getModuleInfo(); 48 | 49 | @OutputFile 50 | public abstract RegularFileProperty getReport(); 51 | 52 | @TaskAction 53 | public void checkOrder() throws IOException { 54 | StringBuilder sb = new StringBuilder(); 55 | for (ModuleInfo.Directive directive : ModuleInfo.Directive.values()) { 56 | List originalOrder = getModuleInfo().get().get(directive).stream().map(name -> name + ";").collect(Collectors.toList()); 57 | 58 | List sorted = new ArrayList<>(originalOrder); 59 | sorted.sort((m1, m2) -> { 60 | // own modules go first 61 | if (getModuleNamePrefix().isPresent()) { 62 | if (m1.startsWith(getModuleNamePrefix().get()) && !m2.startsWith(getModuleNamePrefix().get())) { 63 | return -1; 64 | } 65 | if (!m1.startsWith(getModuleNamePrefix().get()) && m2.startsWith(getModuleNamePrefix().get())) { 66 | return 1; 67 | } 68 | } 69 | return m1.compareTo(m2); 70 | }); 71 | 72 | if (!originalOrder.equals(sorted)) { 73 | p(sb, "'" + directive.literal() + "' are not declared in alphabetical order. Please use this order:"); 74 | for (String entry : sorted) { 75 | p(sb, " " + directive.literal() + " " + entry); 76 | } 77 | p(sb, ""); 78 | } 79 | } 80 | 81 | Files.write(getReport().get().getAsFile().toPath(), sb.toString().getBytes()); 82 | 83 | if (sb.length() > 0) { 84 | throw new RuntimeException(getModuleInfoPath().get() + "\n\n" + sb); 85 | } 86 | } 87 | 88 | private void p(StringBuilder sb, String toPrint) { 89 | sb.append(toPrint); 90 | sb.append("\n"); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/org/gradlex/javamodule/dependencies/tasks/ModuleInfoGenerate.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright the GradleX team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.gradlex.javamodule.dependencies.tasks; 18 | 19 | import org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo; 20 | import org.gradle.api.DefaultTask; 21 | import org.gradle.api.file.RegularFileProperty; 22 | import org.gradle.api.provider.ListProperty; 23 | import org.gradle.api.provider.MapProperty; 24 | import org.gradle.api.provider.Property; 25 | import org.gradle.api.tasks.Input; 26 | import org.gradle.api.tasks.OutputFile; 27 | import org.gradle.api.tasks.TaskAction; 28 | 29 | import java.io.File; 30 | import java.io.IOException; 31 | import java.nio.file.Files; 32 | import java.util.ArrayList; 33 | import java.util.Collection; 34 | import java.util.List; 35 | import java.util.stream.Collectors; 36 | 37 | import static org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo.Directive.*; 38 | 39 | public abstract class ModuleInfoGenerate extends DefaultTask { 40 | 41 | @Input 42 | public abstract Property getModuleName(); 43 | 44 | @Input 45 | public abstract ListProperty getApiDependencies(); 46 | 47 | @Input 48 | public abstract ListProperty getImplementationDependencies(); 49 | 50 | @Input 51 | public abstract ListProperty getCompileOnlyApiDependencies(); 52 | 53 | @Input 54 | public abstract ListProperty getCompileOnlyDependencies(); 55 | 56 | @Input 57 | public abstract ListProperty getRuntimeOnlyDependencies(); 58 | 59 | @Input 60 | public abstract MapProperty getModuleNameToGA(); 61 | 62 | @OutputFile 63 | public abstract RegularFileProperty getModuleInfoFile(); 64 | 65 | @TaskAction 66 | public void generate() throws IOException { 67 | File moduleInfo = getModuleInfoFile().get().getAsFile(); 68 | List content = new ArrayList<>(); 69 | 70 | content.add("module "+ getModuleName().get() + " {"); 71 | if (!getApiDependencies().get().isEmpty()) { 72 | content.addAll(dependenciesToModuleDirectives(getApiDependencies().get(), REQUIRES_TRANSITIVE)); 73 | content.add(""); 74 | } 75 | if (!getImplementationDependencies().get().isEmpty()) { 76 | content.addAll(dependenciesToModuleDirectives(getImplementationDependencies().get(), REQUIRES)); 77 | content.add(""); 78 | } 79 | if (!getCompileOnlyApiDependencies().get().isEmpty()) { 80 | content.addAll(dependenciesToModuleDirectives(getCompileOnlyApiDependencies().get(), REQUIRES_STATIC_TRANSITIVE)); 81 | content.add(""); 82 | } 83 | if (!getCompileOnlyDependencies().get().isEmpty()) { 84 | content.addAll(dependenciesToModuleDirectives(getCompileOnlyDependencies().get(), REQUIRES_STATIC)); 85 | content.add(""); 86 | } 87 | if (!getRuntimeOnlyDependencies().get().isEmpty()) { 88 | content.addAll(dependenciesToModuleDirectives(getRuntimeOnlyDependencies().get(), REQUIRES_RUNTIME)); 89 | content.add(""); 90 | } 91 | content.add("}"); 92 | 93 | Files.write(moduleInfo.toPath(), content); 94 | } 95 | 96 | private Collection dependenciesToModuleDirectives(List dependencies, ModuleInfo.Directive directive) { 97 | return dependencies.stream().map(gaOrProjectModuleName -> { 98 | String moduleName = moduleName(gaOrProjectModuleName); 99 | if (moduleName == null) { 100 | getLogger().lifecycle("Skipping '" + gaOrProjectModuleName + "' - no mapping - run ':analyzeModulePath' for more details"); 101 | return " // " + directive.literal() + " " + gaOrProjectModuleName + ";"; 102 | } else { 103 | return " " + directive.literal() + " " + moduleName + ";"; 104 | } 105 | }).collect(Collectors.toList()); 106 | } 107 | 108 | private String moduleName(String gaOrProjectModuleName) { 109 | if (!gaOrProjectModuleName.contains(":")) { 110 | return gaOrProjectModuleName; 111 | } 112 | return getModuleNameToGA().get().get(gaOrProjectModuleName); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/main/java/org/gradlex/javamodule/dependencies/tasks/SyntheticModuleInfoFoldersGenerate.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright the GradleX team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.gradlex.javamodule.dependencies.tasks; 18 | 19 | import org.gradle.api.DefaultTask; 20 | import org.gradle.api.NonNullApi; 21 | import org.gradle.api.file.DirectoryProperty; 22 | import org.gradle.api.provider.ListProperty; 23 | import org.gradle.api.tasks.CacheableTask; 24 | import org.gradle.api.tasks.Input; 25 | import org.gradle.api.tasks.OutputDirectory; 26 | import org.gradle.api.tasks.TaskAction; 27 | import org.gradlex.javamodule.dependencies.internal.utils.ModuleInfoClassCreator; 28 | 29 | @NonNullApi 30 | @CacheableTask 31 | public abstract class SyntheticModuleInfoFoldersGenerate extends DefaultTask { 32 | 33 | @Input 34 | public abstract ListProperty getModuleNames(); 35 | 36 | @OutputDirectory 37 | public abstract DirectoryProperty getSyntheticModuleInfoFolder(); 38 | 39 | @TaskAction 40 | public void generate() { 41 | for (String moduleName : getModuleNames().get()) { 42 | ModuleInfoClassCreator.createEmpty(getSyntheticModuleInfoFolder().get().dir(moduleName).getAsFile()); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/resources/org/gradlex/javamodule/dependencies/modules.properties: -------------------------------------------------------------------------------- 1 | com.amihaiemil.eoyaml=com.amihaiemil.web:eo-yaml 2 | com.github.spotbugs.annotations=com.github.spotbugs:spotbugs-annotations 3 | com.github.virtuald.curvesapi=com.github.virtuald:curvesapi 4 | com.google.common.util.concurrent.internal=com.google.guava:failureaccess 5 | com.google.common=com.google.guava:guava 6 | com.google.gson=com.google.code.gson:gson 7 | com.google.guice.extensions.servlet=com.google.inject.extensions:guice-servlet 8 | com.google.guice=com.google.inject:guice 9 | com.google.protobuf.util=com.google.protobuf:protobuf-java-util 10 | com.google.protobuf=com.google.protobuf:protobuf-java 11 | com.google.zxing.javase=com.google.zxing:javase 12 | com.google.zxing=com.google.zxing:core 13 | com.microsoft.sqlserver.jdbc=com.microsoft.sqlserver:mssql-jdbc 14 | com.sun.jna.platform=net.java.dev.jna:jna-platform 15 | com.sun.jna=net.java.dev.jna:jna 16 | com.sun.tools.xjc=com.sun.xml.bind:jaxb-xjc 17 | io.grpc=io.helidon.grpc:io.grpc 18 | io.netty.buffer=io.netty:netty-buffer 19 | io.netty.codec.http2=io.netty:netty-codec-http2 20 | io.netty.codec.http=io.netty:netty-codec-http 21 | io.netty.common=io.netty:netty-common 22 | io.netty.handler.proxy=io.netty:netty-handler-proxy 23 | io.netty.handler=io.netty:netty-handler 24 | io.netty.resolver.dns=io.netty:netty-resolver-dns 25 | io.netty.resolver=io.netty:netty-resolver 26 | io.netty.transport.epoll.linux.aarch_64=io.netty:netty-transport-native-epoll|linux-aarch_64 27 | io.netty.transport.epoll.linux.x86_64=io.netty:netty-transport-native-epoll|linux-x86_64 28 | io.netty.transport.epoll=io.netty:netty-transport-native-epoll 29 | io.netty.transport=io.netty:netty-transport 30 | io.swagger.v3.oas.annotations=io.swagger.core.v3:swagger-annotations 31 | io.swagger.v3.oas.models=io.swagger.core.v3:swagger-models 32 | jakarta.messaging=jakarta.jms:jakarta.jms-api 33 | java.annotation=javax.annotation:javax.annotation-api 34 | java.inject=jakarta.inject:jakarta.inject-api 35 | java.validation=jakarta.validation:jakarta.validation-api 36 | javafx.base=org.openjfx:javafx-base 37 | javafx.controls=org.openjfx:javafx-controls 38 | javafx.fxml=org.openjfx:javafx-fxml 39 | javafx.graphics=org.openjfx:javafx-graphics 40 | javafx.media=org.openjfx:javafx-media 41 | javafx.swing=org.openjfx:javafx-swing 42 | javafx.web=org.openjfx:javafx-web 43 | jetty.servlet.api=org.eclipse.jetty.toolchain:jetty-jakarta-servlet-api 44 | jetty.websocket.api=org.eclipse.jetty.toolchain:jetty-jakarta-websocket-api 45 | junit=junit:junit 46 | liquibase.core=org.liquibase:liquibase-core 47 | org.antlr.antlr4.runtime=org.antlr:antlr4-runtime 48 | org.apache.commons.codec=commons-codec:commons-codec 49 | org.apache.commons.io=commons-io:commons-io 50 | org.apache.commons.logging=commons-logging:commons-logging 51 | org.apache.httpcomponents.httpclient.fluent=org.apache.httpcomponents:fluent-hc 52 | org.apache.httpcomponents.httpclient=org.apache.httpcomponents:httpclient 53 | org.apache.httpcomponents.httpcore=org.apache.httpcomponents:httpcore 54 | org.apache.httpcomponents.httpmime=org.apache.httpcomponents:httpmime 55 | org.apache.logging.log4j.slf4j=org.apache.logging.log4j:log4j-slf4j2-impl 56 | org.apache.pdfbox.tools=org.apache.pdfbox:pdfbox-tools 57 | org.apache.pdfbox=org.apache.pdfbox:pdfbox 58 | org.checkerframework.dataflow.nullaway=org.checkerframework:dataflow-nullaway 59 | org.codehaus.stax2=org.codehaus.woodstox:stax2-api 60 | org.eclipse.jdt.annotation=org.eclipse.jdt:org.eclipse.jdt.annotation 61 | org.eclipse.jetty.websocket.javax.websocket.server=org.eclipse.jetty.websocket:javax-websocket-server-impl 62 | org.hamcrest=org.hamcrest:hamcrest 63 | org.hibernate.orm.agroal=org.hibernate.orm:hibernate-agroal 64 | org.hibernate.orm.ant=org.hibernate.orm:hibernate-ant 65 | org.hibernate.orm.c3p0=org.hibernate.orm:hibernate-c3p0 66 | org.hibernate.orm.core=org.hibernate.orm:hibernate-core 67 | org.hibernate.orm.envers=org.hibernate.orm:hibernate-envers 68 | org.hibernate.orm.graalvm=org.hibernate.orm:hibernate-graalvm 69 | org.hibernate.orm.hikaricp=org.hibernate.orm:hibernate-hikaricp 70 | org.hibernate.orm.jcache=org.hibernate.orm:hibernate-jcache 71 | org.hibernate.orm.jpamodelgen=org.hibernate.orm:hibernate-jpamodelgen 72 | org.hibernate.orm.micrometer=org.hibernate.orm:hibernate-micrometer 73 | org.hibernate.orm.proxool=org.hibernate.orm:hibernate-proxool 74 | org.hibernate.orm.spatial=org.hibernate.orm:hibernate-spatial 75 | org.hibernate.orm.testing=org.hibernate.orm:hibernate-testing 76 | org.hibernate.orm.vibur=org.hibernate.orm:hibernate-vibur 77 | org.jose4j=org.bitbucket.b_c:jose4j 78 | org.keycloak.authz.client=org.keycloak:keycloak-authz-client 79 | org.keycloak.common=org.keycloak:keycloak-common 80 | org.keycloak.core=org.keycloak:keycloak-core 81 | org.objenesis=org.objenesis:objenesis 82 | org.postgresql.jdbc=org.postgresql:postgresql 83 | org.reactivestreams=org.reactivestreams:reactive-streams 84 | -------------------------------------------------------------------------------- /src/test/java/org/gradlex/javamodule/dependencies/test/ModuleInfoParseTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright the GradleX team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.gradlex.javamodule.dependencies.test; 18 | 19 | import org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo; 20 | import org.junit.jupiter.api.Test; 21 | 22 | import static org.assertj.core.api.Assertions.assertThat; 23 | import static org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo.Directive.REQUIRES; 24 | import static org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo.Directive.REQUIRES_RUNTIME; 25 | import static org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo.Directive.REQUIRES_TRANSITIVE; 26 | import static org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo.Directive.REQUIRES_STATIC; 27 | import static org.gradlex.javamodule.dependencies.internal.utils.ModuleInfo.Directive.REQUIRES_STATIC_TRANSITIVE; 28 | 29 | class ModuleInfoParseTest { 30 | 31 | @Test 32 | void ignores_single_line_comments() { 33 | var moduleInfo = new ModuleInfo(""" 34 | module some.thing { 35 | // requires com.bla.blub; 36 | requires transitive foo.bar.la; 37 | }"""); 38 | 39 | assertThat(moduleInfo.moduleNamePrefix("thing", "main", false)).isEqualTo("some"); 40 | assertThat(moduleInfo.get(REQUIRES)).isEmpty(); 41 | assertThat(moduleInfo.get(REQUIRES_TRANSITIVE)).containsExactly("foo.bar.la"); 42 | assertThat(moduleInfo.get(REQUIRES_STATIC)).isEmpty(); 43 | assertThat(moduleInfo.get(REQUIRES_STATIC_TRANSITIVE)).isEmpty(); 44 | assertThat(moduleInfo.get(REQUIRES_RUNTIME)).isEmpty(); 45 | } 46 | 47 | @Test 48 | void ignores_single_line_comments_late_in_line() { 49 | var moduleInfo = new ModuleInfo(""" 50 | module some.thing { // module some.thing.else 51 | requires transitive foo.bar.la; 52 | }"""); 53 | 54 | assertThat(moduleInfo.moduleNamePrefix("thing", "main", false)).isEqualTo("some"); 55 | assertThat(moduleInfo.get(REQUIRES)).isEmpty(); 56 | assertThat(moduleInfo.get(REQUIRES_TRANSITIVE)).containsExactly("foo.bar.la"); 57 | assertThat(moduleInfo.get(REQUIRES_STATIC)).isEmpty(); 58 | assertThat(moduleInfo.get(REQUIRES_STATIC_TRANSITIVE)).isEmpty(); 59 | assertThat(moduleInfo.get(REQUIRES_RUNTIME)).isEmpty(); 60 | } 61 | 62 | @Test 63 | void ignores_multi_line_comments() { 64 | var moduleInfo = new ModuleInfo(""" 65 | module some.thing { 66 | /* requires com.bla.blub; 67 | requires transitive foo.bar.la; 68 | */ 69 | requires static foo.bar.la; 70 | } 71 | """); 72 | 73 | assertThat(moduleInfo.get(REQUIRES)).isEmpty(); 74 | assertThat(moduleInfo.get(REQUIRES_TRANSITIVE)).isEmpty(); 75 | assertThat(moduleInfo.get(REQUIRES_STATIC)).containsExactly("foo.bar.la"); 76 | assertThat(moduleInfo.get(REQUIRES_STATIC_TRANSITIVE)).isEmpty(); 77 | assertThat(moduleInfo.get(REQUIRES_RUNTIME)).isEmpty(); 78 | } 79 | 80 | @Test 81 | void ignores_multi_line_comments_between_keywords() { 82 | var moduleInfo = new ModuleInfo(""" 83 | module some.thing { 84 | /*odd comment*/ requires transitive foo.bar.la; 85 | requires/* weird comment*/ static foo.bar.lo; 86 | requires /*something to say*/foo.bar.li; /* 87 | requires only.a.comment 88 | */ 89 | }"""); 90 | 91 | assertThat(moduleInfo.get(REQUIRES)).containsExactly("foo.bar.li"); 92 | assertThat(moduleInfo.get(REQUIRES_TRANSITIVE)).containsExactly("foo.bar.la"); 93 | assertThat(moduleInfo.get(REQUIRES_STATIC)).containsExactly("foo.bar.lo"); 94 | assertThat(moduleInfo.get(REQUIRES_STATIC_TRANSITIVE)).isEmpty(); 95 | assertThat(moduleInfo.get(REQUIRES_RUNTIME)).isEmpty(); 96 | } 97 | 98 | @Test 99 | void supports_runtime_dependencies_through_special_keyword() { 100 | var moduleInfo = new ModuleInfo(""" 101 | module some.thing { 102 | requires /*runtime*/ foo.bar.lo; 103 | } 104 | """); 105 | 106 | assertThat(moduleInfo.get(REQUIRES)).isEmpty(); 107 | assertThat(moduleInfo.get(REQUIRES_TRANSITIVE)).isEmpty(); 108 | assertThat(moduleInfo.get(REQUIRES_STATIC)).isEmpty(); 109 | assertThat(moduleInfo.get(REQUIRES_STATIC_TRANSITIVE)).isEmpty(); 110 | assertThat(moduleInfo.get(REQUIRES_RUNTIME)).containsExactly("foo.bar.lo"); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/test/java/org/gradlex/javamodule/dependencies/test/WarningsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright the GradleX team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.gradlex.javamodule.dependencies.test; 18 | 19 | import org.gradlex.javamodule.dependencies.test.fixture.GradleBuild; 20 | import org.junit.jupiter.api.Test; 21 | 22 | import static org.assertj.core.api.Assertions.assertThat; 23 | 24 | class WarningsTest { 25 | 26 | GradleBuild build = new GradleBuild(); 27 | 28 | @Test 29 | void prints_warning_for_missing_mapping() { 30 | build.appModuleInfoFile.writeText(""" 31 | module org.my.app { 32 | requires commons.math3; 33 | }"""); 34 | 35 | var result = build.fail(); 36 | 37 | assertThat(result.getOutput()).contains( 38 | "[WARN] [Java Module Dependencies] commons.math3=group:artifact missing in"); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/test/java/org/gradlex/javamodule/dependencies/test/configcache/ConfigurationCacheTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright the GradleX team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.gradlex.javamodule.dependencies.test.configcache; 18 | 19 | import org.gradlex.javamodule.dependencies.test.fixture.GradleBuild; 20 | import org.junit.jupiter.api.Test; 21 | 22 | import static org.assertj.core.api.Assertions.assertThat; 23 | import static org.gradle.util.GradleVersion.version; 24 | import static org.gradlex.javamodule.dependencies.test.fixture.GradleBuild.GRADLE_VERSION_UNDER_TEST; 25 | 26 | class ConfigurationCacheTest { 27 | 28 | GradleBuild build = new GradleBuild(); 29 | 30 | final String noCacheMessage = GRADLE_VERSION_UNDER_TEST == null || version(GRADLE_VERSION_UNDER_TEST).compareTo(version("8.8")) >= 0 31 | ? "Calculating task graph as no cached configuration is available for tasks: :app:compileJava" 32 | : "Calculating task graph as no configuration cache is available for tasks: :app:compileJava"; 33 | 34 | @Test 35 | void configurationCacheHit() { 36 | build.libModuleInfoFile.writeText("module abc.lib { }"); 37 | 38 | build.appModuleInfoFile.writeText(""" 39 | module abc.app { 40 | requires abc.lib; 41 | }"""); 42 | 43 | var runner = build.runner("--configuration-cache",":app:compileJava"); 44 | var result = runner.build(); 45 | 46 | assertThat(result.getOutput()).contains(noCacheMessage); 47 | 48 | result = runner.build(); 49 | 50 | assertThat(result.getOutput()).contains("Reusing configuration cache."); 51 | } 52 | 53 | @Test 54 | void configurationCacheHitIrrelevantChange() { 55 | build.libModuleInfoFile.writeText("module abc.lib { }"); 56 | build.appModuleInfoFile.writeText(""" 57 | module abc.app { 58 | requires abc.lib; 59 | }"""); 60 | 61 | var runner = build.runner("--configuration-cache",":app:compileJava"); 62 | var result = runner.build(); 63 | 64 | assertThat(result.getOutput()).contains(noCacheMessage); 65 | 66 | build.appModuleInfoFile.writeText(""" 67 | module abc.app { 68 | requires abc.lib; //This is a comment and should not break the configurationCache 69 | } 70 | """); 71 | result = runner.build(); 72 | 73 | assertThat(result.getOutput()).contains("Reusing configuration cache."); 74 | } 75 | 76 | @Test 77 | void configurationCacheMissRelevantChange() { 78 | build.libModuleInfoFile.writeText("module abc.lib { }"); 79 | build.appModuleInfoFile.writeText(""" 80 | module abc.app { 81 | requires abc.lib; 82 | }"""); 83 | 84 | var runner = build.runner("--configuration-cache",":app:compileJava"); 85 | var result = runner.build(); 86 | 87 | assertThat(result.getOutput()).contains(noCacheMessage); 88 | 89 | build.appModuleInfoFile.writeText(""" 90 | module abc.app { 91 | //dependency removed; so thats indeed a configuration change 92 | }"""); 93 | result = runner.build(); 94 | 95 | assertThat(result.getOutput()).contains( 96 | "Calculating task graph as configuration cache cannot be reused because a build logic input of type 'ValueSourceModuleInfo' has changed.\n"); 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /src/test/java/org/gradlex/javamodule/dependencies/test/extension/ExtensionTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright the GradleX team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.gradlex.javamodule.dependencies.test.extension; 18 | 19 | import org.gradlex.javamodule.dependencies.test.fixture.GradleBuild; 20 | import org.junit.jupiter.api.Test; 21 | 22 | import static org.assertj.core.api.Assertions.assertThat; 23 | 24 | class ExtensionTest { 25 | 26 | GradleBuild build = new GradleBuild(); 27 | 28 | @Test 29 | void can_access_mapping_information_from_extension() { 30 | build.appBuildFile.appendText(""" 31 | javaModuleDependencies.moduleNamePrefixToGroup.put("org.example.app.", "org.example.gr") 32 | 33 | javaModuleDependencies { 34 | println(ga("com.fasterxml.jackson.core").get()) 35 | println(ga(provider { "com.fasterxml.jackson.databind" }).get()) 36 | println(gav("com.fasterxml.jackson.core", "1.0").get()) 37 | println(gav(provider { "com.fasterxml.jackson.databind" }, provider { "1.0" }).get()) 38 | println(moduleName("com.fasterxml.jackson.core:jackson-core").get()) 39 | println(moduleName(provider { "com.fasterxml.jackson.core:jackson-databind" }).get()) 40 | 41 | println(ga("org.example.app.my.mod1").get()) 42 | println(ga(provider { "org.example.app.my.mod2.impl" }).get()) 43 | println(gav("org.example.app.my.mod3.impl.int", "1.0").get()) 44 | println(gav(provider { "org.example.app.mod4" }, provider { "1.0" }).get()) 45 | println(moduleName("org.example.gr:mod8.ab").get()) 46 | println(moduleName(provider { "org.example.gr:mod.z7.i9" }).get()) 47 | }"""); 48 | 49 | var result = build.build(); 50 | 51 | assertThat(result.getOutput()).contains(""" 52 | com.fasterxml.jackson.core:jackson-core 53 | com.fasterxml.jackson.core:jackson-databind 54 | com.fasterxml.jackson.core:jackson-core:1.0 55 | com.fasterxml.jackson.core:jackson-databind:1.0 56 | com.fasterxml.jackson.core 57 | com.fasterxml.jackson.databind 58 | org.example.gr:my.mod1 59 | org.example.gr:my.mod2.impl 60 | org.example.gr:my.mod3.impl.int:1.0 61 | org.example.gr:mod4:1.0 62 | org.example.app.mod8.ab 63 | org.example.app.mod.z7.i9"""); 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/test/java/org/gradlex/javamodule/dependencies/test/fixture/Directory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright the GradleX team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.gradlex.javamodule.dependencies.test.fixture; 18 | 19 | import java.io.IOException; 20 | import java.nio.file.Files; 21 | import java.nio.file.Path; 22 | import java.util.Comparator; 23 | import java.util.stream.Stream; 24 | 25 | public class Directory { 26 | 27 | private final Path directory; 28 | 29 | Directory(Path directory) { 30 | this.directory = directory; 31 | Io.unchecked(() -> Files.createDirectories(directory)); 32 | } 33 | 34 | public WritableFile file(String path) { 35 | Path file = directory.resolve(path); 36 | Io.unchecked(() -> Files.createDirectories(file.getParent())); 37 | return new WritableFile(file); 38 | } 39 | 40 | public Directory dir(String path) { 41 | Path dir = directory.resolve(path); 42 | return new Directory(dir); 43 | } 44 | 45 | public void delete() { 46 | try (Stream walk = Files.walk(directory)) { 47 | walk.sorted(Comparator.reverseOrder()).forEach(p -> Io.unchecked(p, Files::delete)); 48 | } catch (IOException e) { 49 | throw new RuntimeException(e); 50 | } 51 | } 52 | 53 | public Path getAsPath() { 54 | return directory; 55 | } 56 | 57 | public String canonicalPath() { 58 | return Io.unchecked(() -> getAsPath().toFile().getCanonicalPath()); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/test/java/org/gradlex/javamodule/dependencies/test/fixture/Io.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright the GradleX team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.gradlex.javamodule.dependencies.test.fixture; 18 | 19 | import java.io.IOException; 20 | import java.io.UncheckedIOException; 21 | 22 | class Io { 23 | 24 | private Io() { 25 | } 26 | 27 | static T unchecked(IoSupplier supplier) { 28 | try { 29 | return supplier.get(); 30 | } catch (IOException e) { 31 | throw new UncheckedIOException(e); 32 | } 33 | } 34 | 35 | static void unchecked(T t, IoConsumer consumer) { 36 | try { 37 | consumer.accept(t); 38 | } catch (IOException e) { 39 | throw new UncheckedIOException(e); 40 | } 41 | } 42 | 43 | @FunctionalInterface 44 | interface IoSupplier { 45 | T get() throws IOException; 46 | } 47 | 48 | @FunctionalInterface 49 | interface IoConsumer { 50 | void accept(T t) throws IOException; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/test/java/org/gradlex/javamodule/dependencies/test/fixture/WritableFile.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright the GradleX team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.gradlex.javamodule.dependencies.test.fixture; 18 | 19 | import java.nio.file.Files; 20 | import java.nio.file.Path; 21 | import java.nio.file.StandardOpenOption; 22 | 23 | public class WritableFile { 24 | 25 | private final Path file; 26 | 27 | public WritableFile(Path file) { 28 | this.file = file; 29 | } 30 | 31 | public WritableFile(Directory parent, String filePath) { 32 | this.file = Io.unchecked(() -> Files.createDirectories(parent.getAsPath().resolve(filePath).getParent())) 33 | .resolve(Path.of(filePath).getFileName()); 34 | } 35 | 36 | public WritableFile writeText(String text) { 37 | Io.unchecked(() -> Files.writeString(file, text, StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING)); 38 | return this; 39 | } 40 | 41 | public WritableFile appendText(String text) { 42 | Io.unchecked(() -> Files.writeString(file, text, StandardOpenOption.CREATE, StandardOpenOption.APPEND)); 43 | return this; 44 | } 45 | 46 | public WritableFile delete() { 47 | Io.unchecked(file, Files::delete); 48 | return this; 49 | } 50 | 51 | public boolean exists() { 52 | return Files.exists(file); 53 | } 54 | 55 | public Path getAsPath() { 56 | return file; 57 | } 58 | 59 | public String text() { 60 | return Io.unchecked(() -> Files.readString(file)); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/test/java/org/gradlex/javamodule/dependencies/test/groupmapping/GroupMappingTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright the GradleX team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.gradlex.javamodule.dependencies.test.groupmapping; 18 | 19 | import org.gradlex.javamodule.dependencies.test.fixture.GradleBuild; 20 | import org.junit.jupiter.api.Test; 21 | 22 | import static org.assertj.core.api.Assertions.assertThat; 23 | import static org.gradle.testkit.runner.TaskOutcome.SUCCESS; 24 | 25 | class GroupMappingTest { 26 | 27 | GradleBuild build = new GradleBuild(); 28 | 29 | @Test 30 | void can_map_overlapping_groups() { 31 | 32 | var lib2ModuleInfoFile = build.file("lib-b/src/main/java/module-info.java"); 33 | var lib2BuildFile = build.file("lib-b/build.gradle.kts").writeText(build.libBuildFile.text()); 34 | build.settingsFile.appendText("include(\"lib-b\")"); 35 | 36 | build.libModuleInfoFile.appendText("module com.lib { }"); 37 | build.libBuildFile.appendText("group = \"com.foo\""); 38 | lib2ModuleInfoFile.appendText("module com.example.lib.b { }"); 39 | lib2BuildFile.appendText("group = \"com.example\""); 40 | build.appModuleInfoFile.appendText(""" 41 | module org.gradlex.test.app { 42 | requires com.lib; 43 | requires com.example.lib.b; 44 | }"""); 45 | build.appBuildFile.appendText(""" 46 | javaModuleDependencies { 47 | moduleNamePrefixToGroup.put("com.", "com.foo") 48 | moduleNamePrefixToGroup.put("com.example.", "com.example") 49 | }"""); 50 | 51 | var result = build.runner(false, "assemble").build().task(":app:compileJava"); 52 | 53 | assertThat(result).isNotNull(); 54 | assertThat(result.getOutcome()).isEqualTo(SUCCESS); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/test/java/org/gradlex/javamodule/dependencies/test/initialization/SettingsPluginIncludeTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright the GradleX team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.gradlex.javamodule.dependencies.test.initialization; 18 | 19 | import org.gradlex.javamodule.dependencies.test.fixture.GradleBuild; 20 | import org.junit.jupiter.api.BeforeEach; 21 | import org.junit.jupiter.api.Test; 22 | 23 | import static java.util.Objects.requireNonNull; 24 | import static org.assertj.core.api.Assertions.assertThat; 25 | import static org.gradle.testkit.runner.TaskOutcome.SUCCESS; 26 | 27 | class SettingsPluginIncludeTest { 28 | 29 | GradleBuild build = new GradleBuild(); 30 | 31 | @BeforeEach 32 | void setup() { 33 | build.settingsFile.writeText("plugins { id(\"org.gradlex.java-module-dependencies\") }"); 34 | build.appBuildFile.delete(); 35 | build.libBuildFile.delete(); 36 | } 37 | 38 | @Test 39 | void can_define_included_subprojects_as_modules() { 40 | build.settingsFile.appendText(""" 41 | include(":project:with:custom:path") 42 | javaModules { 43 | module(project(":project:with:custom:path")) { 44 | group = "org.example" 45 | plugin("java-library") 46 | } 47 | module(project(":project:with:custom")) { 48 | group = "org.example" 49 | plugin("java-library") 50 | } 51 | }"""); 52 | 53 | build.file("project/with/custom/path/src/main/java/module-info.java").writeText("module abc.liba { }"); 54 | build.file("project/with/custom/src/main/java/module-info.java").writeText(""" 55 | module abc.libb { 56 | requires abc.liba; 57 | }"""); 58 | 59 | var result = build.runner(":project:with:custom:compileJava").build(); 60 | 61 | assertThat(requireNonNull(result.task(":project:with:custom:path:compileJava")).getOutcome()).isEqualTo(SUCCESS); 62 | assertThat(requireNonNull(result.task(":project:with:custom:compileJava")).getOutcome()).isEqualTo(SUCCESS); 63 | } 64 | 65 | @Test 66 | void can_define_included_subprojects_with_custom_project_directory_as_modules() { 67 | build.settingsFile.appendText(""" 68 | include(":project:with:custom:path") 69 | project(":project:with:custom:path").projectDir = file("lib") 70 | project(":project:with:custom").projectDir = file("app") 71 | javaModules { 72 | module(project(":project:with:custom:path")) { 73 | group = "org.example" 74 | plugin("java-library") 75 | } 76 | module(project(":project:with:custom")) { 77 | group = "org.example" 78 | plugin("java-library") 79 | } 80 | }"""); 81 | 82 | build.libModuleInfoFile.writeText("module abc.lib { }"); 83 | build.appModuleInfoFile.writeText(""" 84 | module abc.app { 85 | requires abc.lib; 86 | }"""); 87 | 88 | var result = build.runner(":project:with:custom:jar").build(); 89 | 90 | assertThat(requireNonNull(result.task(":project:with:custom:path:compileJava")).getOutcome()).isEqualTo(SUCCESS); 91 | assertThat(requireNonNull(result.task(":project:with:custom:compileJava")).getOutcome()).isEqualTo(SUCCESS); 92 | assertThat(build.file("lib/build/libs/path.jar").getAsPath()).exists(); 93 | assertThat(build.file("app/build/libs/custom.jar").getAsPath()).exists(); 94 | } 95 | 96 | @Test 97 | void projects_with_same_name_but_different_paths_are_supported() { 98 | build.settingsFile.appendText(""" 99 | include(":app1:feature1:data") 100 | include(":app1:feature2:data") 101 | 102 | rootProject.children.forEach { appContainer -> 103 | appContainer.children.forEach { featureContainer -> 104 | featureContainer.children.forEach { module -> 105 | javaModules.module(module) { plugin("java-library") } 106 | } 107 | } 108 | }"""); 109 | 110 | build.file("app1/feature1/data/src/main/java/module-info.java").writeText("module f1x.data { }"); 111 | build.file("app1/feature2/data/src/main/java/module-info.java").writeText(""" 112 | module f2x.data { 113 | requires f1x.data; 114 | }"""); 115 | 116 | var result = build.runner(":app1:feature2:data:jar").build(); 117 | 118 | assertThat(requireNonNull(result.task(":app1:feature1:data:jar")).getOutcome()).isEqualTo(SUCCESS); 119 | assertThat(requireNonNull(result.task(":app1:feature2:data:jar")).getOutcome()).isEqualTo(SUCCESS); 120 | assertThat(build.file("app1/feature1/data/build/libs/data.jar").getAsPath()).exists(); 121 | assertThat(build.file("app1/feature2/data/build/libs/data.jar").getAsPath()).exists(); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/test/java/org/gradlex/javamodule/dependencies/test/localmodules/LocalModuleMappingsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright the GradleX team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.gradlex.javamodule.dependencies.test.localmodules; 18 | 19 | import org.gradlex.javamodule.dependencies.test.fixture.GradleBuild; 20 | import org.junit.jupiter.api.Test; 21 | 22 | class LocalModuleMappingsTest { 23 | 24 | GradleBuild build = new GradleBuild(); 25 | 26 | @Test 27 | void automatically_maps_local_modules_if_name_prefix_matches() { 28 | build.libModuleInfoFile.writeText("module org.gradlex.test.lib { }"); 29 | build.appModuleInfoFile.writeText(""" 30 | module org.gradlex.test.app { 31 | requires org.gradlex.test.lib; 32 | }"""); 33 | 34 | build.build(); 35 | } 36 | 37 | @Test 38 | void automatically_maps_local_modules_if_name_matches() { 39 | build.libModuleInfoFile.writeText("module lib { }"); 40 | build.appModuleInfoFile.writeText(""" 41 | module app { 42 | requires lib; 43 | }"""); 44 | 45 | build.build(); 46 | } 47 | 48 | @Test 49 | void a_prefix_to_group_mapping_can_be_used() { 50 | build.appBuildFile.appendText(""" 51 | javaModuleDependencies.moduleNamePrefixToGroup.put("abc.", "foo.gr") 52 | """); 53 | build.libBuildFile.appendText(""" 54 | group = "foo.gr" 55 | """); 56 | 57 | build.libModuleInfoFile.writeText("module abc.lib { }"); 58 | build.appModuleInfoFile.writeText(""" 59 | module org.gradlex.test.app { 60 | requires abc.lib; 61 | }"""); 62 | 63 | build.runner(false, "build").build(); 64 | } 65 | 66 | @Test 67 | void does_not_fail_if_there_are_two_project_with_same_name_but_different_path() { 68 | build.libModuleInfoFile.writeText(""" 69 | module org.gradlex.test.lib { 70 | requires org.gradlex.test.anotherlib; 71 | } 72 | """); 73 | build.settingsFile.appendText("include(\"another:lib\")"); 74 | build.file("another/lib/build.gradle.kts").writeText(""" 75 | plugins { 76 | id("org.gradlex.java-module-dependencies") 77 | id("java-library") 78 | } 79 | group = "another" 80 | """); 81 | build.file("another/lib/src/main/java/module-info.java").writeText(""" 82 | module org.gradlex.test.anotherlib { } 83 | """); 84 | build.file("gradle/modules.properties").writeText(""" 85 | org.gradlex.test.anotherlib=another:lib 86 | """); 87 | 88 | build.runner(false, "build"); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/test/java/org/gradlex/javamodule/dependencies/test/samples/PluginBuildLocationSampleModifier.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright the GradleX team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.gradlex.javamodule.dependencies.test.samples; 18 | 19 | import org.gradle.exemplar.model.Command; 20 | import org.gradle.exemplar.model.Sample; 21 | import org.gradle.exemplar.test.runner.SampleModifier; 22 | 23 | import java.io.File; 24 | import java.util.Arrays; 25 | 26 | public class PluginBuildLocationSampleModifier implements SampleModifier { 27 | @Override 28 | public Sample modify(Sample sampleIn) { 29 | Command cmd = sampleIn.getCommands().remove(0); 30 | File pluginProjectDir = new File("."); 31 | sampleIn.getCommands().add( 32 | new Command(new File(pluginProjectDir, "gradlew").getAbsolutePath(), 33 | cmd.getExecutionSubdirectory(), 34 | Arrays.asList("build", "run", "-PpluginLocation=" + pluginProjectDir.getAbsolutePath()), 35 | cmd.getFlags(), 36 | cmd.getExpectedOutput(), 37 | cmd.isExpectFailure(), 38 | true, 39 | cmd.isAllowDisorderedOutput(), 40 | cmd.getUserInputs())); 41 | return sampleIn; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/test/java/org/gradlex/javamodule/dependencies/test/samples/SamplesTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright the GradleX team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.gradlex.javamodule.dependencies.test.samples; 18 | 19 | import org.gradle.exemplar.test.runner.SampleModifiers; 20 | import org.gradle.exemplar.test.runner.SamplesRoot; 21 | import org.gradle.exemplar.test.runner.SamplesRunner; 22 | import org.junit.runner.RunWith; 23 | 24 | @RunWith(SamplesRunner.class) 25 | @SamplesRoot("samples") 26 | @SampleModifiers(PluginBuildLocationSampleModifier.class) 27 | public class SamplesTest { 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/test/java/org/gradlex/javamodule/dependencies/test/tasks/BasicFunctionalityTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright the GradleX team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.gradlex.javamodule.dependencies.test.tasks; 18 | 19 | import org.gradlex.javamodule.dependencies.test.fixture.GradleBuild; 20 | import org.junit.jupiter.api.Test; 21 | 22 | import static org.assertj.core.api.Assertions.assertThat; 23 | 24 | class BasicFunctionalityTest { 25 | 26 | GradleBuild build = new GradleBuild(true); 27 | 28 | @Test 29 | void can_configure_all_tasks_in_a_build_without_error() { 30 | build.libModuleInfoFile.writeText("module abc.lib { }"); 31 | build.appModuleInfoFile.writeText(""" 32 | module org.gradlex.test.app { 33 | requires abc.lib; 34 | } 35 | """); 36 | 37 | var result = build.runner("tasks").build(); 38 | 39 | assertThat(result.getOutput()).contains(""" 40 | Java modules tasks 41 | ------------------ 42 | checkModuleInfo - Check order of directives in 'module-info.java' in 'main' source set 43 | checkTestFixturesModuleInfo - Check order of directives in 'module-info.java' in 'testFixtures' source set 44 | checkTestModuleInfo - Check order of directives in 'module-info.java' in 'test' source set 45 | generateAllModuleInfoFiles - Generate 'module-info.java' files in all source sets 46 | generateBuildFileDependencies - Generate 'dependencies' block in 'build.gradle.kts' 47 | generateCatalog - Generate 'libs.versions.toml' file 48 | generateModuleInfoFile - Generate 'module-info.java' in 'main' source set 49 | generateTestFixturesModuleInfoFile - Generate 'module-info.java' in 'testFixtures' source set 50 | generateTestModuleInfoFile - Generate 'module-info.java' in 'test' source set""" 51 | ); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/test/java/org/gradlex/javamodule/dependencies/test/tasks/OrderingCheckTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright the GradleX team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.gradlex.javamodule.dependencies.test.tasks; 18 | 19 | import org.gradlex.javamodule.dependencies.test.fixture.GradleBuild; 20 | import org.junit.jupiter.api.Test; 21 | 22 | import java.io.IOException; 23 | 24 | import static org.assertj.core.api.Assertions.assertThat; 25 | 26 | class OrderingCheckTest { 27 | 28 | GradleBuild build = new GradleBuild(true); 29 | 30 | @Test 31 | void order_is_expected_to_be_alphabetic_for_each_scope_individually() { 32 | build.appModuleInfoFile.writeText(""" 33 | module org.example.app { 34 | requires a.b.c 35 | requires b.f.g 36 | requires b.z.u 37 | 38 | requires static c.w.q 39 | requires static c.z.u 40 | }"""); 41 | 42 | build.runner(":app:checkAllModuleInfo").build(); 43 | } 44 | 45 | @Test 46 | void if_order_is_not_alphabetic_for_a_scope_an_advice_is_given() throws IOException { 47 | build.appModuleInfoFile.writeText(""" 48 | module org.example.app { 49 | requires a.b.c 50 | requires b.z.u 51 | requires b.f.g 52 | 53 | requires static c.w.q 54 | requires static c.z.u 55 | }"""); 56 | 57 | var result = build.runner(":app:checkAllModuleInfo").buildAndFail(); 58 | assertThat(result.getOutput()).contains(""" 59 | > %s/app/src/main/java/module-info.java 60 | \s 61 | 'requires' are not declared in alphabetical order. Please use this order: 62 | requires a.b.c; 63 | requires b.f.g; 64 | requires b.z.u;""".formatted(build.projectDir.canonicalPath())); 65 | } 66 | 67 | @Test 68 | void own_modules_are_expected_to_go_first() { 69 | build.appModuleInfoFile.writeText(""" 70 | module org.example.app { 71 | requires org.example.h 72 | requires org.example.j 73 | 74 | requires a.b.c 75 | requires b.f.g 76 | requires z.z.u 77 | 78 | requires static org.example.z 79 | requires static c.w.q 80 | requires static c.z.u 81 | }"""); 82 | 83 | build.runner(":app:checkAllModuleInfo").build(); 84 | } 85 | 86 | } -------------------------------------------------------------------------------- /src/test/java/org/gradlex/javamodule/dependencies/test/variants/NonMainVariantsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright the GradleX team. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.gradlex.javamodule.dependencies.test.variants; 18 | 19 | import org.gradlex.javamodule.dependencies.test.fixture.GradleBuild; 20 | import org.junit.jupiter.api.Test; 21 | 22 | import static org.assertj.core.api.Assertions.assertThat; 23 | 24 | class NonMainVariantsTest { 25 | 26 | GradleBuild build = new GradleBuild(); 27 | 28 | @Test 29 | void finds_test_fixtures_module() { 30 | build.appModuleInfoFile.writeText(""" 31 | module org.gradlex.test.app { 32 | requires org.gradlex.test.lib.test.fixtures; 33 | }"""); 34 | build.libModuleInfoFile.writeText(""" 35 | module org.gradlex.test.lib { 36 | }"""); 37 | build.file("lib/src/testFixtures/java/module-info.java").writeText(""" 38 | module org.gradlex.test.lib.test.fixtures { 39 | }"""); 40 | 41 | var result = build.printRuntimeJars(); 42 | 43 | assertThat(result.getOutput()).contains("[lib-test-fixtures.jar, lib.jar]"); 44 | } 45 | 46 | @Test 47 | void finds_feature_variant_module() { 48 | build.libBuildFile.appendText(""" 49 | val extraFeature = sourceSets.create("extraFeature") 50 | java.registerFeature(extraFeature.name) { 51 | usingSourceSet(extraFeature) 52 | }"""); 53 | 54 | build.appModuleInfoFile.writeText(""" 55 | module org.gradlex.test.app { 56 | requires org.gradlex.test.lib.extra.feature; 57 | }"""); 58 | build.libModuleInfoFile.writeText(""" 59 | module org.gradlex.test.lib { 60 | }"""); 61 | build.file("lib/src/extraFeature/java/module-info.java").appendText(""" 62 | module org.gradlex.test.lib.extra.feature { 63 | }"""); 64 | 65 | var result = build.printRuntimeJars(); 66 | 67 | assertThat(result.getOutput()).contains("[lib-extra-feature.jar"); 68 | } 69 | 70 | @Test 71 | void finds_published_feature_variant_when_corresponding_mapping_is_defined() { 72 | // There are no modules published like this anywhere public right now. 73 | // We test that the expected Jar file would have been downloaded if "org.slf4j" would have test fixtures. 74 | build.appBuildFile.appendText(""" 75 | javaModuleDependencies { 76 | moduleNameToGA.put("org.slf4j.test.fixtures", "org.slf4j:slf4j-api|org.slf4j:slf4j-api-test-fixtures") 77 | } 78 | dependencies.constraints { 79 | javaModuleDependencies { implementation(gav("org.slf4j", "2.0.3")) } 80 | }"""); 81 | build.appModuleInfoFile.appendText(""" 82 | module org.gradlex.test.app { 83 | requires org.slf4j.test.fixtures; 84 | }"""); 85 | 86 | var result = build.fail(); 87 | 88 | assertThat(result.getOutput()).contains( 89 | "Unable to find a variant", 90 | "requested capability", 91 | "org.slf4j:slf4j-api-test-fixtures" 92 | ); 93 | } 94 | } 95 | --------------------------------------------------------------------------------