├── .gitattributes ├── .github ├── dependabot.yml └── workflows │ ├── codeql.yml │ ├── dependency-review-action.yml │ ├── dependency-submission.yml │ ├── gradle.yml │ └── submit-dependency-graph.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── docs └── plugin-release-process.md ├── foojay-resolver ├── build.gradle.kts ├── gradle.properties ├── release-notes-1.0.0.txt └── src │ ├── functionalTest │ └── kotlin │ │ └── org │ │ └── gradle │ │ └── toolchains │ │ └── foojay │ │ ├── AbstractFoojayToolchainsPluginFunctionalTest.kt │ │ ├── FoojayToolchainsConventionPluginFunctionalTest.kt │ │ ├── FoojayToolchainsPluginFunctionalTest.kt │ │ └── gradleVersions.kt │ ├── main │ └── kotlin │ │ └── org │ │ └── gradle │ │ └── toolchains │ │ └── foojay │ │ ├── AbstractFoojayToolchainPlugin.kt │ │ ├── FoojayApi.kt │ │ ├── FoojayToolchainResolver.kt │ │ ├── FoojayToolchainsConventionPlugin.kt │ │ ├── FoojayToolchainsPlugin.kt │ │ ├── distributions.kt │ │ └── packages.kt │ └── test │ └── kotlin │ └── org │ └── gradle │ └── toolchains │ └── foojay │ └── FoojayApiTest.kt ├── gradle.properties ├── gradle ├── detekt.yml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle.kts /.gitattributes: -------------------------------------------------------------------------------- 1 | # 2 | # https://help.github.com/articles/dealing-with-line-endings/ 3 | # 4 | # Linux start script should use lf 5 | /gradlew text eol=lf 6 | 7 | # These are Windows script files and should use crlf 8 | *.bat text eol=crlf 9 | 10 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "gradle" 9 | directory: "/" 10 | schedule: 11 | interval: "daily" 12 | - package-ecosystem: "github-actions" 13 | directory: "/" 14 | schedule: 15 | interval: "daily" 16 | labels: 17 | - "@dev-productivity" 18 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL Advanced" 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | schedule: 9 | - cron: '17 18 * * 2' 10 | 11 | jobs: 12 | analyze: 13 | name: Analyze (${{ matrix.language }}) 14 | # Runner size impacts CodeQL analysis time. To learn more, please see: 15 | # - https://gh.io/recommended-hardware-resources-for-running-codeql 16 | # - https://gh.io/supported-runners-and-hardware-resources 17 | # - https://gh.io/using-larger-runners (GitHub.com only) 18 | # Consider using larger runners or machines with greater resources for possible analysis time improvements. 19 | runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} 20 | permissions: 21 | # required for all workflows 22 | security-events: write 23 | # required to fetch internal or private CodeQL packs 24 | packages: read 25 | strategy: 26 | fail-fast: false 27 | matrix: 28 | include: 29 | - language: actions 30 | build-mode: none 31 | - language: java-kotlin 32 | build-mode: autobuild 33 | # CodeQL supports the following values keywords for 'language': 'actions', 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' 34 | # Use `c-cpp` to analyze code written in C, C++ or both 35 | # Use 'java-kotlin' to analyze code written in Java, Kotlin or both 36 | # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both 37 | # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, 38 | # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. 39 | # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how 40 | # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages 41 | steps: 42 | - name: Checkout repository 43 | uses: actions/checkout@v4 44 | - name: Setup Java 45 | uses: actions/setup-java@v4 46 | with: 47 | java-version: '17' 48 | distribution: 'temurin' 49 | - name: Setup Gradle 50 | uses: gradle/actions/setup-gradle@v4 51 | # Initializes the CodeQL tools for scanning. 52 | - name: Initialize CodeQL 53 | uses: github/codeql-action/init@v3 54 | with: 55 | languages: ${{ matrix.language }} 56 | build-mode: ${{ matrix.build-mode }} 57 | # If you wish to specify custom queries, you can do so here or in a config file. 58 | # By default, queries listed here will override any specified in a config file. 59 | # Prefix the list here with "+" to use these queries and those in the config file. 60 | 61 | # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 62 | # queries: security-extended,security-and-quality 63 | 64 | - name: Perform CodeQL Analysis 65 | uses: github/codeql-action/analyze@v3 66 | with: 67 | category: "/language:${{matrix.language}}" 68 | -------------------------------------------------------------------------------- /.github/workflows/dependency-review-action.yml: -------------------------------------------------------------------------------- 1 | name: Dependency review for pull requests 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | 7 | permissions: 8 | contents: write 9 | id-token: write 10 | 11 | jobs: 12 | dependency-submission: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Configure AWS credentials 16 | uses: aws-actions/configure-aws-credentials@v4 17 | with: 18 | role-to-assume: arn:aws:iam::992382829881:role/GHASecrets_foojay-toolchains_all 19 | aws-region: "eu-central-1" 20 | - name: Get secrets 21 | uses: aws-actions/aws-secretsmanager-get-secrets@v2 22 | with: 23 | secret-ids: | 24 | DEVELOCITY_ACCESS_KEY, gha/foojay-toolchains/_all/DEVELOCITY_ACCESS_KEY 25 | - uses: actions/checkout@v4 26 | - uses: actions/setup-java@v4 27 | with: 28 | distribution: temurin 29 | java-version: 21 30 | 31 | - name: Generate and submit dependency graph 32 | uses: gradle/actions/dependency-submission@v4 33 | 34 | - name: Perform dependency review 35 | uses: actions/dependency-review-action@v4 36 | -------------------------------------------------------------------------------- /.github/workflows/dependency-submission.yml: -------------------------------------------------------------------------------- 1 | name: Generate and save dependency graph 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - main 8 | pull_request: 9 | 10 | permissions: 11 | contents: read 12 | id-token: write 13 | 14 | jobs: 15 | dependency-submission: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Configure AWS credentials 19 | uses: aws-actions/configure-aws-credentials@v4 20 | with: 21 | role-to-assume: arn:aws:iam::992382829881:role/GHASecrets_foojay-toolchains_all 22 | aws-region: "eu-central-1" 23 | - name: Get secrets 24 | uses: aws-actions/aws-secretsmanager-get-secrets@v2 25 | with: 26 | secret-ids: | 27 | DEVELOCITY_ACCESS_KEY, gha/foojay-toolchains/_all/DEVELOCITY_ACCESS_KEY 28 | - name: Checkout sources 29 | uses: actions/checkout@v4 30 | - name: Setup Java 31 | uses: actions/setup-java@v4 32 | with: 33 | distribution: 'temurin' 34 | java-version: 21 35 | - name: Generate and save dependency graph 36 | uses: gradle/actions/dependency-submission@v4 37 | with: 38 | dependency-graph: generate-and-upload 39 | -------------------------------------------------------------------------------- /.github/workflows/gradle.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. 2 | # They are provided by a third-party and are governed by 3 | # separate terms of service, privacy policy, and support 4 | # documentation. 5 | # This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time 6 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle 7 | 8 | name: Build with Gradle 9 | 10 | on: 11 | push: 12 | branches: 13 | - "main" 14 | pull_request: 15 | branches: 16 | - "main" 17 | workflow_dispatch: 18 | 19 | permissions: 20 | contents: read 21 | id-token: write 22 | 23 | jobs: 24 | build: 25 | runs-on: ubuntu-latest 26 | steps: 27 | - name: Configure AWS credentials 28 | uses: aws-actions/configure-aws-credentials@v4 29 | with: 30 | role-to-assume: arn:aws:iam::992382829881:role/GHASecrets_foojay-toolchains_all 31 | aws-region: "eu-central-1" 32 | - name: Get secrets 33 | uses: aws-actions/aws-secretsmanager-get-secrets@v2 34 | with: 35 | secret-ids: | 36 | DEVELOCITY_ACCESS_KEY, gha/foojay-toolchains/_all/DEVELOCITY_ACCESS_KEY 37 | - id: determine-sys-prop-args 38 | uses: actions/github-script@v7 39 | with: 40 | script: | 41 | if (context.payload.pull_request && context.payload.pull_request.head.repo.fork) { 42 | core.setOutput('sys-prop-args', '-DagreePublicBuildScanTermOfService=yes -DcacheNode=us --scan') 43 | } else { 44 | core.setOutput('sys-prop-args', '-DcacheNode=us') 45 | } 46 | - uses: actions/checkout@v4 47 | - name: Setup Java 48 | uses: actions/setup-java@v4 49 | with: 50 | java-version: '17' 51 | distribution: 'temurin' 52 | - name: Setup Gradle 53 | uses: gradle/actions/setup-gradle@v4 54 | - run: ./gradlew build ${{ steps.determine-sys-prop-args.outputs.sys-prop-args }} 55 | -------------------------------------------------------------------------------- /.github/workflows/submit-dependency-graph.yml: -------------------------------------------------------------------------------- 1 | name: Download and submit dependency graph 2 | 3 | on: 4 | workflow_run: 5 | workflows: ['Generate and save dependency graph'] 6 | types: [completed] 7 | 8 | permissions: 9 | actions: read 10 | contents: write 11 | 12 | jobs: 13 | submit-dependency-graph: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Download and submit dependency graph 17 | uses: gradle/actions/dependency-submission@v4 18 | with: 19 | dependency-graph: download-and-submit 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.nar 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | 22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 23 | hs_err_pid* 24 | 25 | # Ignore Gradle project-specific cache directory 26 | .gradle 27 | 28 | # Ignore Gradle build output directory 29 | build 30 | 31 | # Ignore IntelliJ IDEA folder 32 | .idea 33 | 34 | # Ignore local publishing repo for testing plugin 35 | foojay-resolver/repo/ 36 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ### Added 6 | 7 | ### Changed 8 | 9 | ### Deprecated 10 | 11 | ### Removed 12 | 13 | ### Fixed 14 | 15 | ### Security 16 | 17 | ## [1.0.0] - 2025-05-19 18 | 19 | ### Changed 20 | 21 | - Plugin now compiled with Java 17, meaning it requires that version or higher to run 22 | - Removed direct references to deprecated JvmVendorSpec.IBM_SEMERU, to prepare for Gradle 9 compatibility. 23 | - Implementation dependencies are now shaded to reduce potential conflicts with other plugins 24 | 25 | ## [0.10.0] - 2025-04-08 26 | 27 | ### Added 28 | 29 | - Support the nativeImageCapable criteria from JavaToolchainSpec in Gradle 8.14+ 30 | 31 | ## [0.9.0] - 2024-11-26 32 | 33 | ### Added 34 | 35 | - Provide an x86-64 based Java version when ARM64 is requested from macOS and none is available 36 | 37 | ## [0.8.0] - 2024-01-12 38 | 39 | ### Changed 40 | 41 | - Bump Gson dependency version 42 | 43 | ### Fixed 44 | 45 | - Fix issues related to Java version parameter 46 | 47 | ## [0.7.0] - 2023-08-17 48 | 49 | ### Added 50 | 51 | - Provide meaningful error message when plugin is applied in a build script (instead of the settings script) 52 | 53 | ## [0.6.0] - 2023-07-10 54 | 55 | ### Added 56 | 57 | - Add support for new GraalVM Community distributions 58 | 59 | ## [0.5.0] - 2023-04-24 60 | 61 | ### Added 62 | 63 | - Generate useful error message if used with unsupported Gradle versions 64 | 65 | ## [0.4.0] - 2022-12-22 66 | 67 | ### Added 68 | 69 | - Try distributions one-by-one if no vendor is specified 70 | 71 | ## [0.3.0] - 2022-12-22 72 | 73 | ### Fixed 74 | 75 | - Make sure vendors IBM and IBM_SEMERU are handled identically 76 | 77 | ## [0.2] - 2022-11-29 78 | 79 | ### Added 80 | 81 | - Make the plugin compatible with Java 8 82 | 83 | ## [0.1] - 2022-11-28 84 | 85 | Toolchains resolver using the Foojay Disco API for resolving Java runtimes. Automatically configures toolchain management. 86 | 87 | 88 | 89 | [Unreleased]: https://github.com/gradle/foojay-toolchains/compare/foojay-toolchains-plugin-1.0.0...HEAD 90 | [1.0.0]: https://github.com/gradle/foojay-toolchains/releases/tag/foojay-toolchains-plugin-1.0.0 91 | [0.10.0]: https://github.com/gradle/foojay-toolchains/releases/tag/foojay-toolchains-plugin-0.10.0 92 | [0.9.0]: https://github.com/gradle/foojay-toolchains/releases/tag/foojay-toolchains-plugin-0.9.0 93 | [0.8.0]: https://github.com/gradle/foojay-toolchains/releases/tag/foojay-toolchains-plugin-0.8.0 94 | [0.7.0]: https://github.com/gradle/foojay-toolchains/releases/tag/foojay-toolchains-plugin-0.7.0 95 | [0.6.0]: https://github.com/gradle/foojay-toolchains/releases/tag/foojay-toolchains-plugin-0.6.0 96 | [0.5.0]: https://github.com/gradle/foojay-toolchains/releases/tag/foojay-toolchains-plugin-0.5.0 97 | [0.4.0]: https://github.com/gradle/foojay-toolchains/releases/tag/foojay-toolchains-plugin-0.4.0 98 | [0.3.0]: https://github.com/gradle/foojay-toolchains/releases/tag/foojay-toolchains-plugin-0.3.0 99 | [0.2]: https://github.com/gradle/foojay-toolchains/releases/tag/foojay-toolchain-plugin-0.2 100 | [0.1]: https://github.com/gradle/foojay-toolchains/releases/tag/foojay-toolchain-plugin-0.1 101 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Foojay Toolchains Plugin 2 | 3 | The `org.gradle.toolchains.foojay-resolver` plugin provides a [repository for downloading JVMs](https://docs.gradle.org/current/userguide/toolchains.html#sub:download_repositories). 4 | 5 | It is based on the [foojay DiscoAPI](https://github.com/foojayio/discoapi), therefore, the [foojay API's Swagger UI](https://api.foojay.io/swagger-ui) can be used to explore what distributions are available. 6 | See the [Matching Toolchain Specifications](#matching-toolchain-specifications) section below for how to select specific distributions. 7 | 8 | > **TAKE HEED!** 9 | > 10 | > Requires Gradle **7.6 or later** to work. 11 | > 12 | > As opposed to most of the Gradle plugins, which are Project plugins 13 | > and must be applied in `build.gradle[.kts]` files, this is a **SETTINGS PLUGIN** and 14 | > must be applied in `settings.gradle[.kts]` files. 15 | 16 | > [!NOTE] 17 | > Versions prior to 1.0.0 require Java 8 or later and Gradle 7.6 or later. 18 | > Versions 1.0.0 and after require Java 17 or later and Gradle 7.6 or later. 19 | 20 | # Usage 21 | 22 | To make use of the plugin add following to your `settings.gradle[.kts]` file. 23 | 24 |
25 | 26 | Kotlin DSL 27 | 28 | ```kotlin 29 | // settings.gradle.kts 30 | plugins { 31 | id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" 32 | } 33 | ``` 34 | 35 |
36 | 37 |
38 | 39 | Groovy DSL 40 | 41 | ```groovy 42 | // settings.gradle 43 | plugins { 44 | id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" 45 | } 46 | ``` 47 | 48 |
49 | 50 | This is a convention plugin meant to simplify configuration. 51 | What it does is equivalent to applying the base plugin and some extra configuration: 52 | 53 |
54 | 55 | Kotlin DSL 56 | 57 | ```kotlin 58 | // settings.gradle.kts 59 | plugins { 60 | id("org.gradle.toolchains.foojay-resolver") version "1.0.0" 61 | } 62 | 63 | toolchainManagement { 64 | jvm { 65 | javaRepositories { 66 | repository("foojay") { 67 | resolverClass.set(org.gradle.toolchains.foojay.FoojayToolchainResolver::class.java) 68 | } 69 | } 70 | } 71 | } 72 | ``` 73 | 74 |
75 | 76 |
77 | 78 | Groovy DSL 79 | 80 | ```groovy 81 | // settings.gradle 82 | plugins { 83 | id("org.gradle.toolchains.foojay-resolver") version "1.0.0" 84 | } 85 | 86 | toolchainManagement { 87 | jvm { 88 | javaRepositories { 89 | repository("foojay") { 90 | resolverClass = org.gradle.toolchains.foojay.FoojayToolchainResolver 91 | } 92 | } 93 | } 94 | } 95 | ``` 96 | 97 |
98 | 99 | Feel free to use either approach. 100 | 101 | For further information about using Toolchain Download Repositories consult the [Gradle Manual](https://docs.gradle.org/current/userguide/toolchains.html#sub:download_repositories). 102 | 103 | # Matching Toolchain Specifications 104 | 105 | The main thing the plugin does is to match [Gradle's toolchain specifications](https://docs.gradle.org/current/javadoc/org/gradle/jvm/toolchain/JavaToolchainSpec.html) to foojay DiscoAPI distributions and packages. 106 | 107 | ## `nativeImageCapable` criteria 108 | 109 | When set, it is used to filter out distributions that are not capable of creating native images with GraalVM. 110 | 111 | ## Vendors 112 | 113 | There is mostly a 1-to-1 relationship between the DiscoAPI's distributions and [Gradle vendors](https://docs.gradle.org/current/userguide/toolchains.html#sec:vendors). 114 | The plugin works with the following mapping: 115 | 116 | | Gradle JVM Vendor | Foojay Distribution | 117 | |-------------------|---------------------------| 118 | | ADOPTIUM | Temurin | 119 | | ADOPTOPENJDK | AOJ | 120 | | AMAZON | Corretto | 121 | | APPLE | - | 122 | | AZUL | Zulu | 123 | | BELLSOFT | Liberica | 124 | | GRAAL_VM | Graal VM CE 8/11/16/17/19 | 125 | | HEWLETT_PACKARD | - | 126 | | IBM | Semeru | 127 | | IBM_SEMERU* | Semeru | 128 | | JETBRAINS | JetBrains | 129 | | MICROSOFT | Microsoft | 130 | | ORACLE | Oracle OpenJDK | 131 | | SAP | SAP Machine | 132 | 133 | **To note:** 134 | Not all Gradle vendors have an equivalent DiscoAPI distribution, empty cells indicate that no toolchain will be provisioned. 135 | If no vendor is specified, distributions are iterated in the order they are provided by the DiscoAPI, and the first one that has a compatible installation package available is selected. 136 | The exception to the Foojay ordering of distributions is that "Temurin" (ADOPTIUM) and then "AOJ" (ADOPTOPENJDK) come first, due to the history of the auto-provisioning feature in Gradle, specifically that AdoptOpenJDK/Adoptium have been the default sources for downloading JVMs. 137 | 138 | > [!WARNING] 139 | > `IBM_SEMERU` is deprecated in Gradle for a while and removed in Gradle 9+. 140 | 141 | ## Implementations 142 | 143 | When specifying toolchains Gradle distinguishes between `J9` JVMs and `VENDOR_SPECIFIC` ones (ie. any other). 144 | What this criteria does in the plugin is to influence the Vendor-to-Distribution matching table. 145 | `VENDOR_SPECIFICATION` doesn't change it at all, while `J9` alter it like this: 146 | 147 | | Gradle JVM Vendor | Foojay Distribution | 148 | |-------------------------|---------------------| 149 | | \ | Semeru | 150 | | ADOPTIUM | - | 151 | | ADOPTOPENJDK | AOJ OpenJ9 | 152 | | AMAZON | - | 153 | | APPLE | - | 154 | | AZUL | - | 155 | | BELLSOFT | - | 156 | | GRAAL_VM | - | 157 | | HEWLETT_PACKARD | - | 158 | | IBM | Semeru | 159 | | IBM_SEMERU* | Semeru | 160 | | JETBRAINS | - | 161 | | MICROSOFT | - | 162 | | ORACLE | - | 163 | | SAP | - | 164 | 165 | Empty cells indicate that no toolchain will be provisioned 166 | 167 | > [!WARNING] 168 | > `IBM_SEMERU` is deprecated in Gradle for a while and removed in Gradle 9+. 169 | 170 | ## Versions 171 | 172 | Once the vendor and the implementation values of the toolchain spec have been used to select a DiscoAPI distribution, a specific package of that distribution needs to be picked by the plugin, in order for it to obtain a download link. 173 | The inputs it uses to do this are: 174 | * the major **Java version** number for the spec 175 | * the **operating system** running the build that made the request 176 | * the **CPU architecture** of the system running the build that made the request 177 | 178 | Additional criteria used for selection: 179 | * for each major version number only packages having the latest minor version will be considered 180 | * only packages containing an archive of a format known to Gradle will be considered (zip, tar, tgz) 181 | * JDKs have priority over JREs 182 | -------------------------------------------------------------------------------- /docs/plugin-release-process.md: -------------------------------------------------------------------------------- 1 | # Foojay Toolchains Plugin (FTP) Release Process 2 | 3 | ## 1. Bump the plugin version number 4 | 5 | This should be the next version. 6 | Verify that it's higher than the version reported [in the portal](https://plugins.gradle.org/plugin/org.gradle.toolchains.foojay-resolver). 7 | 8 | This lives in [`foojay-resolver/gradle.properties`](https://github.com/gradle/foojay-toolchains/blob/main/foojay-resolver/gradle.properties). 9 | 10 | 11 | ## 2. Add some release notes 12 | 13 | You'll need to add a plain text file with name [`foojay-resolver/release-notes-${plugin-version}.txt`](https://github.com/gradle/foojay-toolchains/tree/main/foojay-resolver). 14 | Whatever you put here is what will appear in the portal for that version. 15 | E.g. for [0.2](https://plugins.gradle.org/plugin/org.gradle.toolchains.foojay-resolver/0.2) it was "- Make the plugin compatible with Java 8". 16 | 17 | *NOTE:* Only include new changes relative to the previously published version. 18 | 19 | Remove any old release notes files. 20 | 21 | 22 | ## 3. Update the CHANGELOG.md file 23 | 24 | On one hand it needs some content describing what is in the new release, can be the same as the release notes above. 25 | On the other hand it needs an entry towards the end of the file to specify which tag belongs to this new release. 26 | See previous content for reference. 27 | 28 | 29 | ## 4. Deploy to Dev Portal 30 | 31 | Use [this job](https://builds.gradle.org/buildConfiguration/Dotcom_PluginsPortal_DeployFoojayToolchainsPluginDevelopment?branch=%3Cdefault%3E&buildTypeTab=overview&mode=builds#all-projects) to deploy the plugin to the dev portal. 32 | 33 | Make sure it [looks ok](https://plugins.grdev.net/plugin/org.gradle.toolchains.foojay-resolver) in the portal and the release notes are formatted as you want them to be. 34 | Do the same check for the [convention version](https://plugins.grdev.net/plugin/org.gradle.toolchains.foojay-resolver-convention) too. 35 | 36 | If you need to make changes, you'll need to delete the published version from the portal before you try again. 37 | Just use the big red "Delete version X" button of the [portal page](https://plugins.grdev.net/plugin/org.gradle.toolchains.foojay-resolver). 38 | Don't forget about the [convention version](https://plugins.grdev.net/plugin/org.gradle.toolchains.foojay-resolver-convention) either. 39 | 40 | 41 | ## 5. Deploy to Prod Portal 42 | 43 | Use [this job](https://builds.gradle.org/buildConfiguration/Dotcom_PluginsPortal_DeployFoojayToolchainsPluginProduction?branch=%3Cdefault%3E&buildTypeTab=overview&mode=builds#all-projects) to deploy the plugin to the production portal. 44 | 45 | Make sure it [looks ok](https://plugins.gradle.org/plugin/org.gradle.toolchains.foojay-resolver) in the portal and the release notes are formatted as you want them to be. 46 | Check the [convention version](https://plugins.gradle.org/plugin/org.gradle.toolchains.foojay-resolver-convention) too. 47 | 48 | 49 | ## 6. Tag the release 50 | 51 | We don't have any automated tagging, but please manually tag the current master and push it in, e.g.: 52 | 53 | git tag foojay-toolchains-plugin-1.0 54 | git push origin main --tags 55 | 56 | 57 | ## 7. Notify anyone waiting for a fix 58 | 59 | 60 | ## 8. Bump the development version of the plugin 61 | 62 | - Increment the version in [`foojay-resolver/gradle.properties`](https://github.com/gradle/foojay-toolchains/blob/main/foojay-resolver/gradle.properties). 63 | - Update all usages of the previous version in [`the project`](https://github.com/gradle/foojay-toolchains/) 64 | -------------------------------------------------------------------------------- /foojay-resolver/build.gradle.kts: -------------------------------------------------------------------------------- 1 | @file:Suppress("UnusedPrivateProperty", "UnstableApiUsage") 2 | 3 | import java.io.FileNotFoundException 4 | 5 | plugins { 6 | `kotlin-dsl` 7 | signing 8 | id("com.gradle.plugin-publish") version "1.3.1" 9 | id("io.gitlab.arturbosch.detekt") version "1.23.8" 10 | id("com.gradleup.shadow") version "8.3.6" 11 | } 12 | 13 | group = "org.gradle.toolchains" 14 | val pluginVersion = property("pluginVersion") ?: throw GradleException("`pluginVersion` missing in gradle.properties!") 15 | version = pluginVersion 16 | 17 | // This value is used both for toolchain and for configuration attributes 18 | val jvmVersion = 17 19 | 20 | java { 21 | toolchain { 22 | languageVersion = JavaLanguageVersion.of(jvmVersion) 23 | } 24 | } 25 | 26 | tasks.shadowJar { 27 | isEnableRelocation = true 28 | archiveClassifier = "" 29 | minimize() 30 | 31 | // Clean up some of the unwanted files from gson shading 32 | includeEmptyDirs = false 33 | exclude("**/module-info.class") 34 | exclude("META-INF/maven/**") 35 | } 36 | 37 | detekt { 38 | buildUponDefaultConfig = true // preconfigure defaults 39 | config.setFrom(project.rootProject.file("gradle/detekt.yml")) 40 | 41 | // also check the project build file 42 | source.from(project.buildFile) 43 | } 44 | 45 | dependencies { 46 | implementation("com.google.code.gson:gson:2.13.1") 47 | } 48 | 49 | gradlePlugin { 50 | vcsUrl = "https://github.com/gradle/foojay-toolchains" 51 | website = "https://github.com/gradle/foojay-toolchains" 52 | 53 | val discoToolchains by plugins.creating { 54 | id = "org.gradle.toolchains.foojay-resolver" 55 | implementationClass = "org.gradle.toolchains.foojay.FoojayToolchainsPlugin" 56 | displayName = "Foojay Disco API Toolchains Resolver" 57 | description = "Toolchains resolver using the Foojay Disco API for resolving Java runtimes." 58 | tags = listOf("gradle", "toolchains") 59 | } 60 | 61 | val discoToolchainsConvenience by plugins.creating { 62 | id = "org.gradle.toolchains.foojay-resolver-convention" 63 | implementationClass = "org.gradle.toolchains.foojay.FoojayToolchainsConventionPlugin" 64 | displayName = "Foojay Disco API Toolchains Resolver Convention" 65 | description = "Toolchains resolver using the Foojay Disco API for resolving Java runtimes. Automatically configures toolchain management." 66 | tags = listOf("gradle", "toolchains") 67 | } 68 | 69 | } 70 | 71 | publishing { 72 | repositories { 73 | maven { 74 | url = uri(layout.projectDirectory.dir("repo")) 75 | } 76 | } 77 | } 78 | 79 | signing { 80 | useInMemoryPgpKeys( 81 | project.providers.environmentVariable("PGP_SIGNING_KEY").orNull, 82 | project.providers.environmentVariable("PGP_SIGNING_KEY_PASSPHRASE").orNull 83 | ) 84 | 85 | setRequired({ 86 | providers.environmentVariable("CI").isPresent 87 | }) 88 | } 89 | 90 | testing { 91 | suites { 92 | val functionalTest by registering(JvmTestSuite::class) { 93 | dependencies { 94 | implementation("org.jetbrains.kotlin:kotlin-test-junit5") 95 | } 96 | } 97 | val test by getting(JvmTestSuite::class) { 98 | useJUnitJupiter() 99 | dependencies { 100 | implementation("org.jetbrains.kotlin:kotlin-test-junit5") 101 | } 102 | } 103 | } 104 | } 105 | 106 | gradlePlugin.testSourceSets(sourceSets.getAt("functionalTest")) 107 | 108 | tasks.check { 109 | // Run the functional tests as part of `check` 110 | dependsOn(testing.suites.named("functionalTest")) 111 | } 112 | 113 | val readReleaseNotes by tasks.registering { 114 | notCompatibleWithConfigurationCache("This task modifies other tasks") 115 | description = "Ensure we've got some release notes handy" 116 | doLast { 117 | val releaseNotesFile = file("release-notes-$version.txt") 118 | if (!releaseNotesFile.exists()) { 119 | throw FileNotFoundException("Couldn't find release notes file ${releaseNotesFile.absolutePath}") 120 | } 121 | val releaseNotes = releaseNotesFile.readText().trim() 122 | require(!releaseNotes.isBlank()) { "Release notes file ${releaseNotesFile.absolutePath} is empty" } 123 | gradlePlugin.plugins["discoToolchains"].description = releaseNotes 124 | gradlePlugin.plugins["discoToolchainsConvenience"].description = releaseNotes 125 | } 126 | } 127 | 128 | tasks.publishPlugins { 129 | notCompatibleWithConfigurationCache("This task still has a project reference") 130 | dependsOn(readReleaseNotes) 131 | } 132 | 133 | tasks.check { 134 | dependsOn(tasks.named("detektMain"), tasks.named("detektTest"), tasks.named("detektFunctionalTest")) 135 | } 136 | 137 | configurations.named("shadowRuntimeElements") { 138 | attributes { 139 | attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, jvmVersion) 140 | attribute(TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE, objects.named(TargetJvmEnvironment.STANDARD_JVM)) 141 | attribute(GradlePluginApiVersion.GRADLE_PLUGIN_API_VERSION_ATTRIBUTE, objects.named("7.6")) 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /foojay-resolver/gradle.properties: -------------------------------------------------------------------------------- 1 | pluginVersion=1.1.0 -------------------------------------------------------------------------------- /foojay-resolver/release-notes-1.0.0.txt: -------------------------------------------------------------------------------- 1 | - Plugin now compiled with Java 17, meaning it requires that version or higher to run 2 | - Removed direct references to deprecated JvmVendorSpec.IBM_SEMERU, to prepare for Gradle 9 compatibility. 3 | - Implementation dependencies are now shaded to reduce potential conflicts with other plugins -------------------------------------------------------------------------------- /foojay-resolver/src/functionalTest/kotlin/org/gradle/toolchains/foojay/AbstractFoojayToolchainsPluginFunctionalTest.kt: -------------------------------------------------------------------------------- 1 | package org.gradle.toolchains.foojay 2 | 3 | import org.gradle.testkit.runner.BuildResult 4 | import org.gradle.testkit.runner.GradleRunner 5 | import org.gradle.testkit.runner.TaskOutcome 6 | import org.junit.jupiter.api.BeforeEach 7 | import org.junit.jupiter.api.io.TempDir 8 | import java.io.File 9 | import kotlin.test.assertTrue 10 | 11 | abstract class AbstractFoojayToolchainsPluginFunctionalTest { 12 | 13 | @field:TempDir 14 | protected lateinit var projectDir: File 15 | 16 | @field:TempDir 17 | protected lateinit var homeDir: File 18 | 19 | private val settingsFile by lazy { projectDir.resolve("settings.gradle.kts") } 20 | private val propertiesFile by lazy { projectDir.resolve("gradle.properties") } 21 | private val buildFile by lazy { projectDir.resolve("build.gradle.kts") } 22 | private val sourceFolder by lazy { projectDir.resolve("src/main/java/") } 23 | 24 | @BeforeEach 25 | internal fun setUp() { 26 | propertiesFile.writeText(""" 27 | org.gradle.java.installations.auto-detect=false 28 | org.gradle.java.installations.auto-download=true 29 | """.trimIndent()) 30 | } 31 | 32 | protected fun runner(settings: String, buildScript: String): GradleRunner { 33 | settingsFile.writeText(settings) 34 | buildFile.writeText(buildScript.trimIndent()) 35 | 36 | sourceFolder.mkdirs() 37 | val sourceFile = File(sourceFolder, "Java.java") 38 | sourceFile.writeText(""" 39 | public class Java { 40 | public static void main(String[] args) { 41 | System.out.println(); 42 | } 43 | } 44 | """.trimIndent()) 45 | 46 | return GradleRunner.create() 47 | .forwardOutput() 48 | .withPluginClasspath() 49 | .withArguments(listOf("--info", "-g", homeDir.absolutePath, "compileJava")) 50 | .withProjectDir(projectDir) 51 | } 52 | 53 | protected fun getDifferentJavaVersion() = when { 54 | System.getProperty("java.version").startsWith("11.") -> "16" 55 | else -> "11" 56 | } 57 | 58 | protected fun assertProvisioningSuccessful(buildResult: BuildResult) { 59 | val successfulTasks = buildResult.tasks(TaskOutcome.SUCCESS) 60 | assertTrue(":compileJava" in successfulTasks.map { it.path }) 61 | } 62 | 63 | protected companion object { 64 | @JvmStatic 65 | @Suppress("MagicNumber") 66 | fun getGradleTestVersions(): List { 67 | val versions = GradleTestVersions.getVersions() 68 | val latestVersions = versions.take(3) 69 | val oldestVersion = versions.takeLast(1) 70 | return latestVersions + oldestVersion // compromise, testing takes too long with all versions 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /foojay-resolver/src/functionalTest/kotlin/org/gradle/toolchains/foojay/FoojayToolchainsConventionPluginFunctionalTest.kt: -------------------------------------------------------------------------------- 1 | package org.gradle.toolchains.foojay 2 | 3 | import org.junit.jupiter.params.ParameterizedTest 4 | import org.junit.jupiter.params.provider.MethodSource 5 | import kotlin.test.Test 6 | import kotlin.test.assertTrue 7 | 8 | class FoojayToolchainsConventionPluginFunctionalTest: AbstractFoojayToolchainsPluginFunctionalTest() { 9 | 10 | @ParameterizedTest(name = "gradle version: {0}") 11 | @MethodSource("getGradleTestVersions") 12 | fun `can use convention plugin`(gradleVersion: String) { 13 | val settings = """ 14 | plugins { 15 | id("org.gradle.toolchains.foojay-resolver-convention") 16 | } 17 | """.trimIndent() 18 | 19 | val buildScript = """ 20 | plugins { 21 | java 22 | } 23 | 24 | java { 25 | toolchain { 26 | languageVersion.set(JavaLanguageVersion.of(${getDifferentJavaVersion()})) 27 | } 28 | } 29 | """ 30 | val result = runner(settings, buildScript) 31 | .withGradleVersion(gradleVersion) 32 | .build() 33 | assertProvisioningSuccessful(result) 34 | } 35 | 36 | @Test 37 | fun `generates useful error for unsupported Gradle versions`() { 38 | val settings = """ 39 | plugins { 40 | id("org.gradle.toolchains.foojay-resolver-convention") 41 | } 42 | """.trimIndent() 43 | 44 | val buildScript = """ 45 | plugins { 46 | java 47 | } 48 | 49 | java { 50 | toolchain { 51 | languageVersion.set(JavaLanguageVersion.of(${getDifferentJavaVersion()})) 52 | } 53 | } 54 | """ 55 | val result = runner(settings, buildScript) 56 | .withGradleVersion("7.5") 57 | .buildAndFail() 58 | 59 | assertTrue("FoojayToolchainsPlugin needs Gradle version 7.6 or higher" in result.output) 60 | } 61 | 62 | @ParameterizedTest(name = "gradle version: {0}") 63 | @MethodSource("getGradleTestVersions") 64 | fun `provides meaningful error when applied as a project plugin`(gradleVersion: String) { 65 | val settings = "" 66 | 67 | val buildScript = """ 68 | plugins { 69 | id("org.gradle.toolchains.foojay-resolver-convention") 70 | java 71 | } 72 | 73 | java { 74 | toolchain { 75 | languageVersion.set(JavaLanguageVersion.of(${getDifferentJavaVersion()})) 76 | } 77 | } 78 | """ 79 | val result = runner(settings, buildScript) 80 | .withGradleVersion(gradleVersion) 81 | .buildAndFail() 82 | 83 | assertTrue( 84 | "> Failed to apply plugin 'org.gradle.toolchains.foojay-resolver-convention'.\n" + 85 | " > Settings plugins must be applied in the settings script." in result.output 86 | ) 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /foojay-resolver/src/functionalTest/kotlin/org/gradle/toolchains/foojay/FoojayToolchainsPluginFunctionalTest.kt: -------------------------------------------------------------------------------- 1 | package org.gradle.toolchains.foojay 2 | 3 | import org.junit.jupiter.params.ParameterizedTest 4 | import org.junit.jupiter.params.provider.MethodSource 5 | import kotlin.test.Test 6 | import kotlin.test.assertTrue 7 | 8 | class FoojayToolchainsPluginFunctionalTest: AbstractFoojayToolchainsPluginFunctionalTest() { 9 | 10 | @ParameterizedTest(name = "gradle version: {0}") 11 | @MethodSource("getGradleTestVersions") 12 | fun `can use base plugin`(gradleVersion: String) { 13 | val settings = """ 14 | plugins { 15 | id("org.gradle.toolchains.foojay-resolver") 16 | } 17 | 18 | toolchainManagement { 19 | jvm { 20 | javaRepositories { 21 | repository("foojay") { 22 | resolverClass.set(org.gradle.toolchains.foojay.FoojayToolchainResolver::class.java) 23 | } 24 | } 25 | } 26 | } 27 | """.trimIndent() 28 | 29 | val buildScript = """ 30 | plugins { 31 | java 32 | } 33 | 34 | java { 35 | toolchain { 36 | languageVersion.set(JavaLanguageVersion.of(${getDifferentJavaVersion()})) 37 | } 38 | } 39 | """ 40 | val result = runner(settings, buildScript) 41 | .withGradleVersion(gradleVersion) 42 | .build() 43 | assertProvisioningSuccessful(result) 44 | } 45 | 46 | @Test 47 | fun `generates useful error for unsupported Gradle versions`() { 48 | val settings = """ 49 | plugins { 50 | id("org.gradle.toolchains.foojay-resolver") 51 | } 52 | 53 | toolchainManagement { 54 | jvm { 55 | javaRepositories { 56 | repository("foojay") { 57 | resolverClass.set(org.gradle.toolchains.foojay.FoojayToolchainResolver::class.java) 58 | } 59 | } 60 | } 61 | } 62 | """.trimIndent() 63 | 64 | val buildScript = """ 65 | plugins { 66 | java 67 | } 68 | 69 | java { 70 | toolchain { 71 | languageVersion.set(JavaLanguageVersion.of(${getDifferentJavaVersion()})) 72 | } 73 | } 74 | """ 75 | val result = runner(settings, buildScript) 76 | .withGradleVersion("7.5") 77 | .buildAndFail() 78 | 79 | assertTrue("FoojayToolchainsPlugin needs Gradle version 7.6 or higher" in result.output) 80 | } 81 | 82 | @ParameterizedTest(name = "gradle version: {0}") 83 | @MethodSource("getGradleTestVersions") 84 | fun `provides meaningful error when applied as a project plugin`(gradleVersion: String) { 85 | val settings = "" 86 | 87 | val buildScript = """ 88 | plugins { 89 | java 90 | id("org.gradle.toolchains.foojay-resolver") 91 | } 92 | 93 | toolchainManagement { 94 | jvm { 95 | javaRepositories { 96 | repository("foojay") { 97 | resolverClass.set(org.gradle.toolchains.foojay.FoojayToolchainResolver::class.java) 98 | } 99 | } 100 | } 101 | } 102 | 103 | java { 104 | toolchain { 105 | languageVersion.set(JavaLanguageVersion.of(${getDifferentJavaVersion()})) 106 | } 107 | } 108 | """ 109 | val result = runner(settings, buildScript) 110 | .withGradleVersion(gradleVersion) 111 | .buildAndFail() 112 | 113 | assertTrue("> Failed to apply plugin 'org.gradle.toolchains.foojay-resolver'.\n" + 114 | " > Settings plugins must be applied in the settings script." in result.output) 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /foojay-resolver/src/functionalTest/kotlin/org/gradle/toolchains/foojay/gradleVersions.kt: -------------------------------------------------------------------------------- 1 | package org.gradle.toolchains.foojay 2 | 3 | import org.gradle.internal.impldep.com.google.gson.Gson 4 | import org.gradle.util.GradleVersion 5 | import java.net.URL 6 | 7 | 8 | internal 9 | object GradleTestVersions { 10 | 11 | private val smallestVersionOfInterest = GradleVersion.version("7.6") 12 | 13 | internal 14 | fun getVersions(): List { 15 | val releasedVersions = getReleasedVersions() 16 | 17 | val testVersions = keepOnlyLatestMinor(releasedVersions) 18 | .toMutableList() 19 | testVersions.add(getLatestNightlyVersion()) 20 | 21 | return testVersions 22 | .sortedByDescending { it.majorVersion } 23 | .map { it.gradleVersion.version } 24 | .toList() 25 | } 26 | 27 | private fun getLatestNightlyVersion(): VersionInfo { 28 | val jsonText = URL("https://services.gradle.org/versions/nightly").readText() 29 | return VersionInfo(Gson().fromJson(jsonText, VersionJsonBlock::class.java).version) 30 | } 31 | 32 | private fun getReleasedVersions(): List { 33 | val jsonText = URL("https://services.gradle.org/versions/all").readText() 34 | val allVersions = Gson().fromJson(jsonText, Array::class.java) 35 | .asSequence() 36 | .filter { !it.snapshot } 37 | .filter { !it.nightly } 38 | .filter { it.rcFor.isBlank() } 39 | .filter { it.milestoneFor.isBlank() } 40 | .map { VersionInfo(it.version) } 41 | .filter { it.gradleVersion >= smallestVersionOfInterest } 42 | .toList() 43 | return allVersions 44 | } 45 | 46 | private fun keepOnlyLatestMinor(versions: List): MutableList { 47 | val filteredVersions = versions 48 | .sortedWith(compareBy { it.majorVersion }.thenBy { it.gradleVersion }) 49 | .toMutableList() 50 | var i = 0 51 | while (i < filteredVersions.size - 1) { 52 | if (filteredVersions[i].majorVersion == filteredVersions[i + 1].majorVersion) { 53 | filteredVersions.removeAt(i) 54 | } else { 55 | i++ 56 | } 57 | } 58 | return filteredVersions 59 | } 60 | } 61 | 62 | fun main() { 63 | val versions = GradleTestVersions.getVersions() 64 | println("versions = $versions") 65 | } 66 | 67 | @Suppress("MagicNumber") 68 | private class VersionInfo(val version: String) { 69 | val gradleVersion: GradleVersion = GradleVersion.version(version) 70 | val majorVersion: GradleVersion = GradleVersion.version(getMajorVersion(version)) 71 | private fun getMajorVersion(version: String): String { 72 | val majorVersionParts = version.split("\\.".toRegex()) 73 | return when (majorVersionParts.size) { 74 | 2 -> version 75 | 3 -> "${majorVersionParts[0]}.${majorVersionParts[1]}" 76 | else -> error("Unexpected version number: $version") 77 | } 78 | } 79 | 80 | override fun toString(): String = version 81 | 82 | } 83 | 84 | data class VersionJsonBlock( 85 | val version: String, 86 | val snapshot: Boolean, 87 | val nightly: Boolean, 88 | val rcFor: String, 89 | val milestoneFor: String, 90 | ) 91 | -------------------------------------------------------------------------------- /foojay-resolver/src/main/kotlin/org/gradle/toolchains/foojay/AbstractFoojayToolchainPlugin.kt: -------------------------------------------------------------------------------- 1 | package org.gradle.toolchains.foojay 2 | 3 | import org.gradle.api.GradleException 4 | import org.gradle.api.Plugin 5 | import org.gradle.api.initialization.Settings 6 | 7 | abstract class AbstractFoojayToolchainPlugin: Plugin { 8 | 9 | override fun apply(target: Any) { 10 | if (target is Settings) { 11 | apply(target) 12 | } else { 13 | throw GradleException("Settings plugins must be applied in the settings script.") 14 | } 15 | } 16 | 17 | abstract fun apply(settings: Settings) 18 | 19 | } 20 | -------------------------------------------------------------------------------- /foojay-resolver/src/main/kotlin/org/gradle/toolchains/foojay/FoojayApi.kt: -------------------------------------------------------------------------------- 1 | package org.gradle.toolchains.foojay 2 | 3 | import org.gradle.api.GradleException 4 | import org.gradle.jvm.toolchain.JavaLanguageVersion 5 | import org.gradle.jvm.toolchain.JvmImplementation 6 | import org.gradle.jvm.toolchain.JvmVendorSpec 7 | import org.gradle.platform.Architecture 8 | import org.gradle.platform.OperatingSystem 9 | import java.io.BufferedReader 10 | import java.io.InputStream 11 | import java.net.HttpURLConnection 12 | import java.net.URI 13 | import java.net.URL 14 | import java.net.URLEncoder 15 | import java.nio.charset.StandardCharsets.UTF_8 16 | import java.util.concurrent.TimeUnit.SECONDS 17 | 18 | 19 | @Suppress("UnstableApiUsage") 20 | class FoojayApi { 21 | 22 | companion object { 23 | val CONNECT_TIMEOUT = SECONDS.toMillis(10).toInt() 24 | val READ_TIMEOUT = SECONDS.toMillis(20).toInt() 25 | 26 | const val SCHEMA = "https" 27 | 28 | private const val ENDPOINT_ROOT = "api.foojay.io/disco/v3.0" 29 | const val DISTRIBUTIONS_ENDPOINT = "$ENDPOINT_ROOT/distributions" 30 | const val PACKAGES_ENDPOINT = "$ENDPOINT_ROOT/packages" 31 | } 32 | 33 | private val distributions = mutableListOf() 34 | 35 | fun toUri(links: Links?): URI? = links?.pkg_download_redirect 36 | 37 | @Suppress("LongParameterList") 38 | fun toPackage( 39 | version: JavaLanguageVersion, 40 | vendor: JvmVendorSpec, 41 | implementation: JvmImplementation, 42 | nativeImageCapable: Boolean, 43 | operatingSystem: OperatingSystem, 44 | architecture: Architecture 45 | ): Package? { 46 | val distributions = match(vendor, implementation, version, nativeImageCapable) 47 | return distributions.asSequence().mapNotNull { distribution -> 48 | match(distribution.api_parameter, version, operatingSystem, architecture) 49 | }.firstOrNull() 50 | } 51 | 52 | internal fun match(vendor: JvmVendorSpec, implementation: JvmImplementation, version: JavaLanguageVersion, nativeImageCapable: Boolean): List { 53 | fetchDistributionsIfMissing() 54 | return match(distributions, vendor, implementation, version, nativeImageCapable) 55 | } 56 | 57 | private fun fetchDistributionsIfMissing() { 58 | if (distributions.isEmpty()) { 59 | val con = createConnection( 60 | DISTRIBUTIONS_ENDPOINT, 61 | mapOf("include_versions" to "true", "include_synonyms" to "true") 62 | ) 63 | val json = readResponse(con) 64 | con.disconnect() 65 | 66 | distributions.addAll(parseDistributions(json)) 67 | } 68 | } 69 | 70 | internal fun match(distributionName: String, version: JavaLanguageVersion, operatingSystem: OperatingSystem, architecture: Architecture): Package? { 71 | val versionApiKey = when { 72 | distributionName.startsWith("graalvm_community") -> "version" 73 | distributionName == "graalvm" -> "version" 74 | else -> "jdk_version" 75 | } 76 | 77 | val con = createConnection( 78 | PACKAGES_ENDPOINT, 79 | mapOf( 80 | versionApiKey to "$version", 81 | "distro" to distributionName, 82 | "operating_system" to operatingSystem.toApiValue(), 83 | "latest" to "available", 84 | "directly_downloadable" to "true" 85 | ) 86 | ) 87 | val json = readResponse(con) 88 | con.disconnect() 89 | 90 | val packages = parsePackages(json) 91 | return match(packages, architecture) 92 | } 93 | 94 | private fun createConnection(endpoint: String, parameters: Map): HttpURLConnection { 95 | val url = URL("$SCHEMA://$endpoint?${toParameterString(parameters)}") 96 | val con = url.openConnection() as HttpURLConnection 97 | con.setRequestProperty("Content-Type", "application/json") 98 | con.requestMethod = "GET" 99 | con.connectTimeout = CONNECT_TIMEOUT 100 | con.readTimeout = READ_TIMEOUT 101 | return con 102 | } 103 | 104 | private fun toParameterString(params: Map): String { 105 | return params.entries.joinToString("&") { 106 | "${URLEncoder.encode(it.key, UTF_8.name())}=${URLEncoder.encode(it.value, UTF_8.name())}" 107 | } 108 | } 109 | 110 | private fun readResponse(con: HttpURLConnection): String { 111 | val status = con.responseCode 112 | if (status != HttpURLConnection.HTTP_OK) { 113 | throw GradleException("Requesting vendor list failed: ${readContent(con.errorStream)}") 114 | } 115 | return readContent(con.inputStream) 116 | } 117 | 118 | private fun readContent(stream: InputStream) = stream.bufferedReader().use(BufferedReader::readText) 119 | } 120 | -------------------------------------------------------------------------------- /foojay-resolver/src/main/kotlin/org/gradle/toolchains/foojay/FoojayToolchainResolver.kt: -------------------------------------------------------------------------------- 1 | package org.gradle.toolchains.foojay 2 | 3 | import org.gradle.api.provider.Property 4 | import org.gradle.jvm.toolchain.JavaToolchainDownload 5 | import org.gradle.jvm.toolchain.JavaToolchainRequest 6 | import org.gradle.jvm.toolchain.JavaToolchainResolver 7 | import org.gradle.jvm.toolchain.JavaToolchainSpec 8 | import org.gradle.util.GradleVersion 9 | import java.util.* 10 | 11 | abstract class FoojayToolchainResolver : JavaToolchainResolver { 12 | 13 | private val api: FoojayApi = FoojayApi() 14 | 15 | override fun resolve(request: JavaToolchainRequest): Optional { 16 | val spec = request.javaToolchainSpec 17 | val nativeImageCapable = if (GradleVersion.current().baseVersion >= GradleVersion.version("8.14")) { 18 | extractNativeImageCapability(spec) 19 | } else { 20 | false 21 | } 22 | val platform = request.buildPlatform 23 | val links = api.toPackage( 24 | spec.languageVersion.get(), 25 | spec.vendor.get(), 26 | spec.implementation.get(), 27 | nativeImageCapable, 28 | platform.operatingSystem, 29 | platform.architecture 30 | )?.links 31 | val uri = api.toUri(links) 32 | return Optional.ofNullable(uri).map(JavaToolchainDownload::fromUri) 33 | } 34 | 35 | @Suppress("UNCHECKED_CAST") 36 | private fun extractNativeImageCapability(spec: JavaToolchainSpec): Boolean { 37 | val result = spec.javaClass.getMethod("getNativeImageCapable").invoke(spec) as Property 38 | return result.getOrElse(false) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /foojay-resolver/src/main/kotlin/org/gradle/toolchains/foojay/FoojayToolchainsConventionPlugin.kt: -------------------------------------------------------------------------------- 1 | package org.gradle.toolchains.foojay 2 | 3 | import org.gradle.api.initialization.Settings 4 | import org.gradle.kotlin.dsl.jvm 5 | 6 | @Suppress("unused") 7 | abstract class FoojayToolchainsConventionPlugin: AbstractFoojayToolchainPlugin() { 8 | 9 | override fun apply(settings: Settings) { 10 | settings.plugins.apply(FoojayToolchainsPlugin::class.java) 11 | 12 | settings.toolchainManagement { 13 | jvm { 14 | javaRepositories { 15 | repository("foojay") { 16 | resolverClass.set(FoojayToolchainResolver::class.java) 17 | } 18 | } 19 | } 20 | } 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /foojay-resolver/src/main/kotlin/org/gradle/toolchains/foojay/FoojayToolchainsPlugin.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * This Kotlin source file was generated by the Gradle 'init' task. 3 | */ 4 | package org.gradle.toolchains.foojay 5 | 6 | import org.gradle.api.initialization.Settings 7 | import org.gradle.api.internal.SettingsInternal 8 | import org.gradle.jvm.toolchain.JavaToolchainResolverRegistry 9 | import org.gradle.util.GradleVersion 10 | 11 | @Suppress("unused") 12 | abstract class FoojayToolchainsPlugin: AbstractFoojayToolchainPlugin() { 13 | 14 | @Suppress("TooGenericExceptionThrown") 15 | override fun apply(settings: Settings) { 16 | if (GradleVersion.current().baseVersion < GradleVersion.version("7.6")) { 17 | throw RuntimeException("${FoojayToolchainsPlugin::class.simpleName} needs Gradle version 7.6 or higher") 18 | } 19 | 20 | settings.plugins.apply("jvm-toolchain-management") 21 | 22 | val registry = (settings as SettingsInternal).services.get(JavaToolchainResolverRegistry::class.java) 23 | registry.register(FoojayToolchainResolver::class.java) 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /foojay-resolver/src/main/kotlin/org/gradle/toolchains/foojay/distributions.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("UnstableApiUsage") 2 | 3 | package org.gradle.toolchains.foojay 4 | 5 | import com.google.gson.Gson 6 | import org.gradle.jvm.toolchain.JavaLanguageVersion 7 | import org.gradle.jvm.toolchain.JvmImplementation 8 | import org.gradle.jvm.toolchain.JvmVendorSpec 9 | import org.gradle.jvm.toolchain.JvmVendorSpec.ADOPTIUM 10 | import org.gradle.jvm.toolchain.JvmVendorSpec.ADOPTOPENJDK 11 | import org.gradle.jvm.toolchain.JvmVendorSpec.AMAZON 12 | import org.gradle.jvm.toolchain.JvmVendorSpec.AZUL 13 | import org.gradle.jvm.toolchain.JvmVendorSpec.BELLSOFT 14 | import org.gradle.jvm.toolchain.JvmVendorSpec.IBM 15 | import org.gradle.jvm.toolchain.JvmVendorSpec.ORACLE 16 | import org.gradle.jvm.toolchain.JvmVendorSpec.SAP 17 | import org.gradle.jvm.toolchain.internal.DefaultJvmVendorSpec.any 18 | import java.net.URI 19 | 20 | val vendorAliases: Map 21 | get() { 22 | val tmpMap = mutableMapOf( 23 | ADOPTIUM to "Temurin", 24 | ADOPTOPENJDK to "AOJ", 25 | AMAZON to "Corretto", 26 | AZUL to "Zulu", 27 | BELLSOFT to "Liberica", 28 | IBM to "Semeru", 29 | ORACLE to "Oracle OpenJDK", 30 | SAP to "SAP Machine" 31 | ) 32 | addPossiblyUndefinedVendor("IBM_SEMERU", "Semeru", tmpMap) 33 | return tmpMap.toMap() 34 | } 35 | 36 | val distributionOrderOfPreference = listOf("Temurin", "AOJ") 37 | 38 | val j9Aliases: Map 39 | get() { 40 | val tmpMap = mutableMapOf( 41 | IBM to "Semeru", 42 | ADOPTOPENJDK to "AOJ OpenJ9" 43 | ) 44 | addPossiblyUndefinedVendor("IBM_SEMERU", "Semeru", tmpMap) 45 | return tmpMap.toMap() 46 | } 47 | 48 | /** 49 | * Given a list of [distributions], return those that match the provided [vendor] and JVM [implementation]. The Java 50 | * language [version] is only used to remove wrong GraalVM distributions; no general version filtering is done here. 51 | */ 52 | @Suppress("ReturnCount") 53 | fun match( 54 | distributions: List, 55 | vendor: JvmVendorSpec, 56 | implementation: JvmImplementation, 57 | version: JavaLanguageVersion, 58 | nativeImageCapable: Boolean 59 | ): List { 60 | // Start by filtering based on the native image criteria. 61 | // If it is defined, we only keep the distributions that have `build_of_graalvm` set to true. 62 | val filteredDistributions = distributions.filter { !nativeImageCapable || it.build_of_graalvm } 63 | 64 | // Specific filter when J9 is requested 65 | if (implementation == JvmImplementation.J9) return matchForJ9(filteredDistributions, vendor) 66 | 67 | // Return early if an explicit non-GraalVM distribution is requested. 68 | if (vendor != JvmVendorSpec.GRAAL_VM && vendor != any()) return match(filteredDistributions, vendor) 69 | 70 | // Remove GraalVM distributions that target the wrong Java language version. 71 | val graalVmCeVendor = JvmVendorSpec.matching("GraalVM CE $version") 72 | val distributionsWithoutWrongGraalVm = filteredDistributions.filter { (name) -> 73 | when { 74 | // Naming scheme for old GraalVM community releases: The Java language version is part of the name. 75 | name.startsWith("GraalVM CE") -> graalVmCeVendor.matches(name) 76 | 77 | else -> true 78 | } 79 | } 80 | 81 | if (vendor == any()) return allDistributionsPrecededByWellKnownOnes(distributionsWithoutWrongGraalVm) 82 | 83 | // As Gradle has no means to distinguish between Community and Oracle distributions of GraalVM (see 84 | // https://github.com/gradle/gradle/issues/25521), disregard Oracle GraalVM distributions for now by only matching 85 | // "GraalVM Community" and "GraalVM CE". 86 | val graalVmVendor = JvmVendorSpec.matching("GraalVM C") 87 | 88 | return match(distributionsWithoutWrongGraalVm, graalVmVendor) 89 | } 90 | 91 | private fun matchForJ9(distributions: List, vendor: JvmVendorSpec) = 92 | if (vendor == any()) { 93 | distributions 94 | .filter { it.name in j9Aliases.values } 95 | .sortedBy { j9Aliases.values.indexOf(it.name) } 96 | } else { 97 | distributions.filter { it.name == j9Aliases[vendor] } 98 | } 99 | 100 | private fun match(distributions: List, vendor: JvmVendorSpec): List = 101 | findByMatchingAliases(distributions, vendor) ?: findByMatchingNamesAndSynonyms(distributions, vendor) 102 | 103 | private fun allDistributionsPrecededByWellKnownOnes(distributions: List): List = 104 | distributions.sortedBy { 105 | // Put our preferences first, preserve Foojay order otherwise. 106 | val indexOf = distributionOrderOfPreference.indexOf(it.name) 107 | when { 108 | indexOf < 0 -> distributionOrderOfPreference.size 109 | else -> indexOf 110 | } 111 | } 112 | 113 | private fun findByMatchingAliases(distributions: List, vendor: JvmVendorSpec): List? = 114 | distributions.find { it.name == vendorAliases[vendor] }?.let { 115 | listOf(it) 116 | } 117 | 118 | private fun findByMatchingNamesAndSynonyms(distributions: List, vendor: JvmVendorSpec) = 119 | distributions.filter { distribution -> 120 | vendor.matches(distribution.name) || distribution.synonyms.any { vendor.matches(it) } 121 | } 122 | 123 | fun parseDistributions(json: String): List { 124 | return Gson().fromJson(json, DistributionsResult::class.java).result 125 | } 126 | 127 | 128 | private fun addPossiblyUndefinedVendor( 129 | vendorFieldName: String, 130 | vendorAlias: String, 131 | map: MutableMap 132 | ) { 133 | try { 134 | val vendorField = JvmVendorSpec::class.java.getDeclaredField(vendorFieldName) 135 | map.put(vendorField.get(null) as JvmVendorSpec, vendorAlias) 136 | } catch (_: Exception) { 137 | // Ignore - removed in the Gradle version currently running the build where the plugin is applied. 138 | } 139 | } 140 | 141 | /** 142 | * The data class for the result objects as returned by [FoojayApi.DISTRIBUTIONS_ENDPOINT]. 143 | */ 144 | @Suppress("ConstructorParameterNaming") 145 | data class Distribution( 146 | /** 147 | * The distribution (vendor) name, e.g. "Temurin", "Oracle OpenJDK", "JetBrains", "GraalVM", ... 148 | */ 149 | val name: String, 150 | 151 | /** 152 | * The name to use as part of the path when requesting distribution-specific details, see 153 | * https://github.com/foojayio/discoapi#endpoint-distributions 154 | */ 155 | val api_parameter: String, 156 | 157 | /** 158 | * A flag to indicate whether the distribution is still maintained or not. 159 | */ 160 | val maintained: Boolean, 161 | 162 | /** 163 | * A flag to indicate whether this is an OpenJDK (re-)distribution. 164 | */ 165 | val build_of_openjdk: Boolean, 166 | 167 | /** 168 | * A flag to indicate whether this is a GraalVM (re-)distribution. 169 | */ 170 | val build_of_graalvm: Boolean, 171 | 172 | /** 173 | * The URI of the offical homepage of the ditribution. 174 | */ 175 | val official_uri: URI, 176 | 177 | /** 178 | * A list of alterative names / spellings for the distribution. 179 | */ 180 | val synonyms: List, 181 | 182 | /** 183 | * The version strings available for this distribution. 184 | */ 185 | val versions: List 186 | ) 187 | 188 | private data class DistributionsResult( 189 | val result: List 190 | ) 191 | -------------------------------------------------------------------------------- /foojay-resolver/src/main/kotlin/org/gradle/toolchains/foojay/packages.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("ConstructorParameterNaming", "UnstableApiUsage") 2 | 3 | package org.gradle.toolchains.foojay 4 | 5 | import com.google.gson.Gson 6 | import org.gradle.platform.Architecture 7 | import org.gradle.platform.OperatingSystem 8 | import java.net.URI 9 | 10 | val architectures32Bit = setOf("x32", "i386", "x86") 11 | val architectures64Bit = setOf("x64", "x86_64", "amd64", "ia64") 12 | val architecturesArm64Bit = setOf("aarch64", "arm64") 13 | 14 | val handledArchiveTypes = setOf("tar", "tar.gz", "tgz", "zip") 15 | 16 | fun parsePackages(json: String): List { 17 | return Gson().fromJson(json, PackagesResult::class.java).result 18 | } 19 | 20 | fun match(packages: List, architecture: Architecture): Package? { 21 | val candidates = packages 22 | .filter { p -> matches(p, architecture) } // we filter out packages not matching the architecture the build is running on 23 | .filter { p -> hasHandledArchiveType(p) } // Gradle can handle only certain archive types 24 | .sortedWith(compareBy(Package::package_type, Package::lib_c_type, Package::architecture)) // prefer JDKs over JREs & prefer "glibc" over "musl" 25 | return candidates.firstOrNull() 26 | } 27 | 28 | fun OperatingSystem.toApiValue(): String = 29 | when (this) { 30 | OperatingSystem.LINUX -> "linux" 31 | OperatingSystem.UNIX -> "linux" 32 | OperatingSystem.WINDOWS -> "windows" 33 | OperatingSystem.MAC_OS -> "macos" 34 | OperatingSystem.SOLARIS -> "solaris" 35 | OperatingSystem.FREE_BSD -> "linux" 36 | } 37 | 38 | private fun matches(p: Package, architecture: Architecture): Boolean = 39 | when (architecture) { 40 | Architecture.X86 -> p.architecture in architectures32Bit 41 | Architecture.X86_64 -> p.architecture in architectures64Bit 42 | Architecture.AARCH64 -> p.architecture in architecturesArm64Bit 43 | || (p.operating_system == OperatingSystem.MAC_OS.toApiValue() && p.architecture in architectures64Bit) 44 | } 45 | 46 | private fun hasHandledArchiveType(p: Package): Boolean { 47 | return p.archive_type in handledArchiveTypes 48 | } 49 | 50 | data class Package( 51 | val archive_type: String, 52 | val distribution: String, 53 | val jdk_version: Int, 54 | val distribution_version: String, 55 | val operating_system: String, 56 | val architecture: String, 57 | val package_type: String, 58 | val lib_c_type: String, 59 | val links: Links 60 | ) 61 | 62 | data class Links( 63 | val pkg_download_redirect: URI, 64 | val pkg_info_uri: URI? 65 | ) 66 | 67 | private data class PackagesResult( 68 | val result: List 69 | ) 70 | -------------------------------------------------------------------------------- /foojay-resolver/src/test/kotlin/org/gradle/toolchains/foojay/FoojayApiTest.kt: -------------------------------------------------------------------------------- 1 | package org.gradle.toolchains.foojay 2 | 3 | import org.gradle.jvm.toolchain.JavaLanguageVersion.of 4 | import org.gradle.jvm.toolchain.JvmImplementation 5 | import org.gradle.jvm.toolchain.JvmImplementation.J9 6 | import org.gradle.jvm.toolchain.JvmImplementation.VENDOR_SPECIFIC 7 | import org.gradle.jvm.toolchain.JvmVendorSpec 8 | import org.gradle.jvm.toolchain.JvmVendorSpec.* 9 | import org.gradle.jvm.toolchain.internal.DefaultJvmVendorSpec.any 10 | import org.gradle.platform.Architecture 11 | import org.gradle.platform.OperatingSystem 12 | import org.junit.jupiter.params.ParameterizedTest 13 | import org.junit.jupiter.params.provider.Arguments 14 | import org.junit.jupiter.params.provider.MethodSource 15 | import org.junit.jupiter.params.provider.ValueSource 16 | import kotlin.test.Test 17 | import kotlin.test.assertEquals 18 | import kotlin.test.assertNotNull 19 | import kotlin.test.assertTrue 20 | 21 | @Suppress("UnstableApiUsage") 22 | class FoojayApiTest { 23 | 24 | private val api = FoojayApi() 25 | 26 | @ParameterizedTest(name = "javaVersion: {0}, vendor: {1}, isJ9: {2}, os: {3}, arch: {4}") 27 | @MethodSource("getData") 28 | fun `download URI provided correctly`( 29 | javaVersion: Int, 30 | vendor: JvmVendorSpec, 31 | isJ9: Boolean, 32 | os: OperatingSystem, 33 | arch: Architecture 34 | ) = assertDownloadUri(javaVersion, vendor, isJ9, false, os, arch) 35 | 36 | companion object { 37 | 38 | private 39 | val IBM_SEMERU_VENDOR = try { 40 | JvmVendorSpec::class.java.getDeclaredField("IBM_SEMERU").get(null) as JvmVendorSpec 41 | } catch (_: Exception) { 42 | null 43 | } 44 | 45 | @Suppress("DEPRECATION") 46 | @JvmStatic 47 | fun getData(): List { 48 | val tmpList = mutableListOf( 49 | Arguments.of(8, any(), false, OperatingSystem.MAC_OS, Architecture.X86_64), 50 | Arguments.of(8, any(), false, OperatingSystem.MAC_OS, Architecture.AARCH64), 51 | Arguments.of(17, any(), false, OperatingSystem.MAC_OS, Architecture.X86_64), 52 | Arguments.of(17, any(), false, OperatingSystem.MAC_OS, Architecture.AARCH64), 53 | Arguments.of(17, ADOPTIUM, false, OperatingSystem.MAC_OS, Architecture.AARCH64), 54 | Arguments.of(17, GRAAL_VM, false, OperatingSystem.MAC_OS, Architecture.AARCH64), 55 | Arguments.of(21, any(), true, OperatingSystem.MAC_OS, Architecture.X86_64), 56 | Arguments.of(21, IBM, true, OperatingSystem.MAC_OS, Architecture.X86_64), 57 | 58 | Arguments.of(17, GRAAL_VM, false, OperatingSystem.LINUX, Architecture.X86_64), 59 | Arguments.of(17, any(), false, OperatingSystem.LINUX, Architecture.X86_64), 60 | Arguments.of(17, any(), true, OperatingSystem.LINUX, Architecture.X86_64), 61 | Arguments.of(21, GRAAL_VM, false, OperatingSystem.LINUX, Architecture.X86_64), 62 | 63 | Arguments.of(8, any(), false, OperatingSystem.WINDOWS, Architecture.X86_64), 64 | Arguments.of(8, GRAAL_VM, false, OperatingSystem.WINDOWS, Architecture.X86_64), 65 | Arguments.of(17, any(), false, OperatingSystem.WINDOWS, Architecture.X86_64), 66 | Arguments.of(17, GRAAL_VM, false, OperatingSystem.WINDOWS, Architecture.X86_64), 67 | ) 68 | IBM_SEMERU_VENDOR?.let { tmpList.add(Arguments.of(21, it, true, OperatingSystem.MAC_OS, Architecture.X86_64)) } 69 | return tmpList.toList() 70 | } 71 | } 72 | 73 | @ParameterizedTest(name = "J9 implementation influences vendor resolution (Java {0})") 74 | @ValueSource(ints = [8, 11, 16]) 75 | fun `J9 implementation influences vendor resolution`(version: Int) { 76 | assertMatchedDistributions(any(), J9, version, "Semeru", "AOJ OpenJ9") 77 | 78 | assertMatchedDistributions(ADOPTOPENJDK, J9, version, "AOJ OpenJ9") 79 | assertMatchedDistributions(IBM, J9, version, "Semeru") 80 | IBM_SEMERU_VENDOR?.let { assertMatchedDistributions(it, J9, version, "Semeru") } 81 | 82 | assertMatchedDistributions(ADOPTIUM, J9, version) 83 | assertMatchedDistributions(AZUL, J9, version) 84 | assertMatchedDistributions(AMAZON, J9, version) 85 | assertMatchedDistributions(BELLSOFT, J9, version) 86 | assertMatchedDistributions(MICROSOFT, J9, version) 87 | assertMatchedDistributions(ORACLE, J9, version) 88 | assertMatchedDistributions(SAP, J9, version) 89 | assertMatchedDistributions(APPLE, J9, version) 90 | assertMatchedDistributions(GRAAL_VM, J9, version) 91 | assertMatchedDistributions(HEWLETT_PACKARD, J9, version) 92 | } 93 | 94 | @ParameterizedTest(name = "vendor specific implementation does not influence vendor resolution (Java {0})") 95 | @ValueSource(ints = [8, 11, 16]) 96 | fun `vendor specific implementation does not influence vendor resolution`(version: Int) { 97 | assertMatchedDistributions(any(), VENDOR_SPECIFIC, version, 98 | "Temurin", "AOJ", 99 | "ZuluPrime", "Zulu", "Trava", "Semeru certified", "Semeru", "SAP Machine", "Red Hat", "Oracle OpenJDK", 100 | "Oracle", "OpenLogic", "OJDKBuild", "Microsoft", "Mandrel", "Liberica Native", "Liberica", "Kona", 101 | "JetBrains", "GraalVM Community", "GraalVM CE $version", "GraalVM", "Gluon GraalVM", "Dragonwell", 102 | "Debian", "Corretto", "Bi Sheng", "AOJ OpenJ9" 103 | ) 104 | 105 | assertMatchedDistributions(ADOPTOPENJDK, VENDOR_SPECIFIC, version, "AOJ") 106 | assertMatchedDistributions(IBM, VENDOR_SPECIFIC, version, "Semeru") 107 | IBM_SEMERU_VENDOR?.let { assertMatchedDistributions(it, VENDOR_SPECIFIC, version, "Semeru") } 108 | 109 | assertMatchedDistributions(ADOPTIUM, VENDOR_SPECIFIC, version, "Temurin") 110 | assertMatchedDistributions(AZUL, VENDOR_SPECIFIC, version, "Zulu") 111 | assertMatchedDistributions(AMAZON, VENDOR_SPECIFIC, version, "Corretto") 112 | assertMatchedDistributions(BELLSOFT, VENDOR_SPECIFIC, version, "Liberica") 113 | assertMatchedDistributions(MICROSOFT, VENDOR_SPECIFIC, version, "Microsoft") 114 | assertMatchedDistributions(ORACLE, VENDOR_SPECIFIC, version, "Oracle OpenJDK") 115 | assertMatchedDistributions(SAP, VENDOR_SPECIFIC, version, "SAP Machine") 116 | 117 | assertMatchedDistributions(GRAAL_VM, VENDOR_SPECIFIC, version, "GraalVM Community", "GraalVM CE $version") 118 | 119 | assertMatchedDistributions(APPLE, VENDOR_SPECIFIC, version) 120 | assertMatchedDistributions(HEWLETT_PACKARD, VENDOR_SPECIFIC, version) 121 | } 122 | 123 | private fun assertMatchedDistributions( 124 | vendor: JvmVendorSpec, 125 | implementation: JvmImplementation, 126 | version: Int, 127 | vararg expectedDistributions: String 128 | ) { 129 | assertEquals( 130 | listOf(*expectedDistributions), 131 | api.match(vendor, implementation, of(version), false).map { it.name }, 132 | "Mismatch in matching distributions for vendor: $vendor, implementation: $implementation, version: $version" 133 | ) 134 | } 135 | 136 | @ParameterizedTest(name = "can resolve arbitrary vendors (Java {0})") 137 | @ValueSource(ints = [8, 11, 16]) 138 | fun `can resolve arbitrary vendors`(version: Int) { 139 | assertEquals("ZuluPrime", api.match(vendorSpec("zuluprime"), VENDOR_SPECIFIC, of(version), false).firstOrNull()?.name) 140 | assertEquals("ZuluPrime", api.match(vendorSpec("zUluprIme"), VENDOR_SPECIFIC, of(version), false).firstOrNull()?.name) 141 | assertEquals("JetBrains", api.match(vendorSpec("JetBrains"), VENDOR_SPECIFIC, of(version), false).firstOrNull()?.name) 142 | } 143 | 144 | @Test 145 | fun `can pick the right package`() { 146 | val p = api.match("temurin", of(11), OperatingSystem.LINUX, Architecture.X86_64) 147 | assertNotNull(p) 148 | assertEquals("tar.gz", p.archive_type) 149 | assertEquals("temurin", p.distribution) 150 | assertEquals(11, p.jdk_version) 151 | assertTrue(Regex("11.\\d+.\\d+").matches(p.distribution_version)) 152 | assertEquals("linux", p.operating_system) 153 | assertEquals("x64", p.architecture) 154 | assertEquals("jdk", p.package_type) 155 | } 156 | 157 | @Test 158 | fun `macos arm is mapped to x64 when arm isn't available`() { 159 | // RISC architecture is preferred when available 160 | val p1 = assertDownloadUri(17, AZUL, false, false,OperatingSystem.MAC_OS, Architecture.AARCH64) 161 | assertEquals("aarch64", p1.architecture) 162 | 163 | // X86 architecture is provided when RISC is not available 164 | val p2 = assertDownloadUri(7, AZUL, false, false, OperatingSystem.MAC_OS, Architecture.AARCH64) 165 | assertEquals("x64", p2.architecture) 166 | } 167 | 168 | @Test 169 | fun `can pick a native image capable package`() { 170 | assertDownloadUri(21, any(), false, true, OperatingSystem.MAC_OS, Architecture.AARCH64) 171 | } 172 | 173 | @Test 174 | fun `can pick graalvm package`() { 175 | assertDownloadUri(21, matching("GraalVM"), false, true, OperatingSystem.MAC_OS, Architecture.AARCH64) 176 | } 177 | 178 | @Suppress("LongParameterList") 179 | private fun assertDownloadUri( 180 | javaVersion: Int, 181 | vendor: JvmVendorSpec, 182 | isJ9: Boolean, 183 | nativeImageCapable: Boolean, 184 | os: OperatingSystem, 185 | arch: Architecture 186 | ): Package { 187 | val actual = api.toPackage( 188 | of(javaVersion), 189 | vendor, 190 | if (isJ9) J9 else VENDOR_SPECIFIC, 191 | nativeImageCapable, 192 | os, 193 | arch 194 | ) 195 | assertNotNull(actual) 196 | assertNotNull(actual.links.pkg_download_redirect) 197 | assertJavaVersion(javaVersion, actual) 198 | assertDistribution(vendor, actual) 199 | assertNativeImageCapable(nativeImageCapable, vendor, actual) 200 | assertOperatingSystem(os, actual) 201 | assertArchitecture(os, arch, actual) 202 | return actual 203 | } 204 | 205 | private fun assertJavaVersion(javaVersion: Int, actual: Package) { 206 | val actualValue = actual.jdk_version 207 | assertEquals(javaVersion, actualValue, 208 | "Expected Java version ($javaVersion) doesn't match actual one ($actualValue), ${moreDetailsAt(actual)}" 209 | ) 210 | } 211 | 212 | private fun assertDistribution(vendor: JvmVendorSpec, actual: Package) { 213 | var expectedValue = vendor.toString().replace("_", "").lowercase() 214 | expectedValue = when (expectedValue) { 215 | "ibm" -> "semeru" 216 | "azul zulu" -> "zulu" 217 | else -> expectedValue 218 | } 219 | 220 | val actualValue = actual.distribution 221 | 222 | assertTrue(vendor.matches(actualValue) || actualValue.startsWith(expectedValue), 223 | "Expected vendor spec ($expectedValue) doesn't match actual distribution (${actualValue}), ${moreDetailsAt(actual)}" 224 | ) 225 | } 226 | 227 | private fun assertNativeImageCapable(nativeImageCapable: Boolean, vendor: JvmVendorSpec, actual: Package) { 228 | if (nativeImageCapable) { 229 | // TODO this is not a great test, but the package does not carry the native-image / Graal capability information anymore 230 | if (vendor == any()) { 231 | assertTrue(actual.distribution.contains("mandrel"), 232 | "Expected vendor to contain 'mandrel' when native image capable, got ${actual.distribution}") 233 | } else { 234 | assertDistribution(vendor, actual) 235 | } 236 | } 237 | } 238 | 239 | private fun assertOperatingSystem(os: OperatingSystem, actual: Package) { 240 | val expectedValue = os.toString().replace("_", "").lowercase() 241 | val actualValue = actual.operating_system 242 | assertEquals(expectedValue, actualValue, 243 | "Expected operating system ($expectedValue) doesn't match actual one ($actualValue), ${moreDetailsAt(actual)}" 244 | ) 245 | } 246 | 247 | private fun assertArchitecture(os: OperatingSystem, arch: Architecture, actual: Package) { 248 | val expectedValues = when (arch) { 249 | Architecture.X86 -> architectures32Bit 250 | Architecture.X86_64 -> architectures64Bit 251 | Architecture.AARCH64 -> 252 | if (os == OperatingSystem.MAC_OS) architecturesArm64Bit + architectures64Bit 253 | else architecturesArm64Bit 254 | } 255 | val actualValue = actual.architecture 256 | assertTrue(expectedValues.contains(actualValue), 257 | "Expected architecture (${arch}) doesn't match actual one ($actualValue), ${moreDetailsAt(actual)}" 258 | ) 259 | } 260 | 261 | private fun moreDetailsAt(actual: Package?) = "for more details see ${actual?.links?.pkg_info_uri}" 262 | 263 | private fun vendorSpec(vendorName: String): JvmVendorSpec = matching(vendorName) 264 | 265 | } 266 | 267 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.parallel=true 2 | org.gradle.caching=true 3 | org.gradle.configuration-cache=true 4 | org.gradle.configuration-cache.parallel=true -------------------------------------------------------------------------------- /gradle/detekt.yml: -------------------------------------------------------------------------------- 1 | style: 2 | MaxLineLength: 3 | maxLineLength: 180 # default is 120 4 | WildcardImport: 5 | active: false 6 | UnnecessaryAbstractClass: 7 | active: false 8 | 9 | naming: 10 | FunctionNaming: 11 | functionPattern: '[a-z][a-zA-Z0-9]*|`.*`' -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gradle/foojay-toolchains/4b5f91e9acfe51b2c7fe8f56da5932d660d69506/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionSha256Sum=61ad310d3c7d3e5da131b76bbf22b5a4c0786e9d892dae8c1658d4b484de3caa 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip 5 | networkTimeout=10000 6 | validateDistributionUrl=true 7 | zipStoreBase=GRADLE_USER_HOME 8 | zipStorePath=wrapper/dists 9 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # SPDX-License-Identifier: Apache-2.0 19 | # 20 | 21 | ############################################################################## 22 | # 23 | # Gradle start up script for POSIX generated by Gradle. 24 | # 25 | # Important for running: 26 | # 27 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 28 | # noncompliant, but you have some other compliant shell such as ksh or 29 | # bash, then to run this script, type that shell name before the whole 30 | # command line, like: 31 | # 32 | # ksh Gradle 33 | # 34 | # Busybox and similar reduced shells will NOT work, because this script 35 | # requires all of these POSIX shell features: 36 | # * functions; 37 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 38 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 39 | # * compound commands having a testable exit status, especially «case»; 40 | # * various built-in commands including «command», «set», and «ulimit». 41 | # 42 | # Important for patching: 43 | # 44 | # (2) This script targets any POSIX shell, so it avoids extensions provided 45 | # by Bash, Ksh, etc; in particular arrays are avoided. 46 | # 47 | # The "traditional" practice of packing multiple parameters into a 48 | # space-separated string is a well documented source of bugs and security 49 | # problems, so this is (mostly) avoided, by progressively accumulating 50 | # options in "$@", and eventually passing that to Java. 51 | # 52 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 53 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 54 | # see the in-line comments for details. 55 | # 56 | # There are tweaks for specific operating systems such as AIX, CygWin, 57 | # Darwin, MinGW, and NonStop. 58 | # 59 | # (3) This script is generated from the Groovy template 60 | # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 61 | # within the Gradle project. 62 | # 63 | # You can find Gradle at https://github.com/gradle/gradle/. 64 | # 65 | ############################################################################## 66 | 67 | # Attempt to set APP_HOME 68 | 69 | # Resolve links: $0 may be a link 70 | app_path=$0 71 | 72 | # Need this for daisy-chained symlinks. 73 | while 74 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 75 | [ -h "$app_path" ] 76 | do 77 | ls=$( ls -ld "$app_path" ) 78 | link=${ls#*' -> '} 79 | case $link in #( 80 | /*) app_path=$link ;; #( 81 | *) app_path=$APP_HOME$link ;; 82 | esac 83 | done 84 | 85 | # This is normally unused 86 | # shellcheck disable=SC2034 87 | APP_BASE_NAME=${0##*/} 88 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 89 | APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH="\\\"\\\"" 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | if ! command -v java >/dev/null 2>&1 137 | then 138 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 139 | 140 | Please set the JAVA_HOME variable in your environment to match the 141 | location of your Java installation." 142 | fi 143 | fi 144 | 145 | # Increase the maximum file descriptors if we can. 146 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 147 | case $MAX_FD in #( 148 | max*) 149 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 150 | # shellcheck disable=SC2039,SC3045 151 | MAX_FD=$( ulimit -H -n ) || 152 | warn "Could not query maximum file descriptor limit" 153 | esac 154 | case $MAX_FD in #( 155 | '' | soft) :;; #( 156 | *) 157 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 158 | # shellcheck disable=SC2039,SC3045 159 | ulimit -n "$MAX_FD" || 160 | warn "Could not set maximum file descriptor limit to $MAX_FD" 161 | esac 162 | fi 163 | 164 | # Collect all arguments for the java command, stacking in reverse order: 165 | # * args from the command line 166 | # * the main class name 167 | # * -classpath 168 | # * -D...appname settings 169 | # * --module-path (only if needed) 170 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 171 | 172 | # For Cygwin or MSYS, switch paths to Windows format before running java 173 | if "$cygwin" || "$msys" ; then 174 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 175 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 176 | 177 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 178 | 179 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 180 | for arg do 181 | if 182 | case $arg in #( 183 | -*) false ;; # don't mess with options #( 184 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 185 | [ -e "$t" ] ;; #( 186 | *) false ;; 187 | esac 188 | then 189 | arg=$( cygpath --path --ignore --mixed "$arg" ) 190 | fi 191 | # Roll the args list around exactly as many times as the number of 192 | # args, so each arg winds up back in the position where it started, but 193 | # possibly modified. 194 | # 195 | # NB: a `for` loop captures its iteration list before it begins, so 196 | # changing the positional parameters here affects neither the number of 197 | # iterations, nor the values presented in `arg`. 198 | shift # remove old arg 199 | set -- "$@" "$arg" # push replacement arg 200 | done 201 | fi 202 | 203 | 204 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 205 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 206 | 207 | # Collect all arguments for the java command: 208 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, 209 | # and any embedded shellness will be escaped. 210 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be 211 | # treated as '${Hostname}' itself on the command line. 212 | 213 | set -- \ 214 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 215 | -classpath "$CLASSPATH" \ 216 | -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ 217 | "$@" 218 | 219 | # Stop when "xargs" is not available. 220 | if ! command -v xargs >/dev/null 2>&1 221 | then 222 | die "xargs is not available" 223 | fi 224 | 225 | # Use "xargs" to parse quoted args. 226 | # 227 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 228 | # 229 | # In Bash we could simply go: 230 | # 231 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 232 | # set -- "${ARGS[@]}" "$@" 233 | # 234 | # but POSIX shell has neither arrays nor command substitution, so instead we 235 | # post-process each arg (as a line of input to sed) to backslash-escape any 236 | # character that might be a shell metacharacter, then use eval to reverse 237 | # that process (while maintaining the separation between arguments), and wrap 238 | # the whole thing up as a single "set" statement. 239 | # 240 | # This will of course break if any of these variables contains a newline or 241 | # an unmatched quote. 242 | # 243 | 244 | eval "set -- $( 245 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 246 | xargs -n1 | 247 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 248 | tr '\n' ' ' 249 | )" '"$@"' 250 | 251 | exec "$JAVACMD" "$@" 252 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.gradle.develocity") version "4.0.1" 3 | id("io.github.gradle.gradle-enterprise-conventions-plugin").version("0.10.3") 4 | id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" 5 | } 6 | 7 | dependencyResolutionManagement { 8 | repositories { 9 | mavenCentral() 10 | } 11 | } 12 | 13 | rootProject.name = "foojay-toolchains" 14 | include("foojay-resolver") 15 | --------------------------------------------------------------------------------