diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index aaa63565b..e3224d917 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -7,11 +7,21 @@ assignees: ''
---
+**Before reporting**
+
+- This repository should be used to report **issues on the Maven or Gradle plugins for GraalVM**.
+- Please report issues which are specific to [the Spring Framework](https://spring.io/) or [the Micronaut framework](https://micronaut.io/) to their specific repositories.
+- Do not report issues with building your specific application, e.g errors which happen at image build time like classes initialized at build time, or missing classes as run time: those are not related to the plugins but problems with configuration. You can refer to the [GraalVM native image documentation](https://www.graalvm.org/latest/reference-manual/native-image/) for available options and the [plugins documentation](https://graalvm.github.io/native-build-tools) for how to use them with the plugin.
+
**Describe the bug**
A clear and concise description of what the bug is.
**Make sure that you have read [the documentation](https://graalvm.github.io/native-build-tools) and that you are using the latest plugin version.**
**To Reproduce**
+
+When possible, provide a link to a repository which reproduces the issue, with instructions on how to use.
+The reproducer **must** make use of either the Maven or Gradle plugin.
+
Steps to reproduce the behavior:
```xml
diff --git a/.github/actions/prepare-environment/action.yml b/.github/actions/prepare-environment/action.yml
index 6f1e80333..6dddfd9bb 100644
--- a/.github/actions/prepare-environment/action.yml
+++ b/.github/actions/prepare-environment/action.yml
@@ -6,14 +6,10 @@ inputs:
description: 'secrets.GITHUB_TOKEN'
required: false
default: ''
- graalvm-version:
- description: 'GraalVM version to use'
- required: false
- default: 'dev'
java-version:
description: 'Java version to use'
required: false
- default: '11'
+ default: '17'
push-access:
description: 'Does this workflow require push access?'
required: false
@@ -26,13 +22,12 @@ inputs:
runs:
using: "composite"
steps:
- - name: "🔧 Install GraalVM ${{ inputs.graalvm-version }} (JDK${{ inputs.java-version }})"
+ - name: "🔧 Install GraalVM (JDK${{ inputs.java-version }})"
uses: graalvm/setup-graalvm@main
with:
- components: 'native-image'
- github-token: ${{ inputs.github-token }}
java-version: ${{ inputs.java-version }}
- version: ${{ inputs.graalvm-version }}
+ distribution: 'graalvm'
+ github-token: ${{ inputs.github-token }}
- name: "🔒 Configure push access"
if: ${{ inputs.push-access == '1' }}
shell: "bash"
diff --git a/.github/workflows/deploy-documentation.yml b/.github/workflows/deploy-documentation.yml
index 780cf9550..0154433ec 100644
--- a/.github/workflows/deploy-documentation.yml
+++ b/.github/workflows/deploy-documentation.yml
@@ -18,19 +18,17 @@ jobs:
strategy:
fail-fast: false
matrix:
- graalvm-version: [ latest ]
- java-version: [ 11 ]
- os: [ ubuntu-20.04 ]
+ java-version: [ 17 ]
+ os: [ ubuntu-22.04 ]
steps:
- name: "☁ Checkout repository"
- uses: actions/checkout@v2
+ uses: actions/checkout@v4
with:
ssh-key: "${{ secrets.SSH_PRIVATE_KEY }}"
- name: "🔧 Prepare environment"
uses: ./.github/actions/prepare-environment
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
- graalvm-version: ${{ matrix.graalvm-version }}
java-version: ${{ matrix.java-version }}
push-access: 1
ssh-key: ${{ secrets.SSH_PRIVATE_KEY }}
diff --git a/.github/workflows/deploy-snapshots.yml b/.github/workflows/deploy-snapshots.yml
index b3f6f7707..1a528a648 100644
--- a/.github/workflows/deploy-snapshots.yml
+++ b/.github/workflows/deploy-snapshots.yml
@@ -2,7 +2,7 @@ name: "Deploy snapshots"
on:
workflow_run:
- workflows: [ "deploy-documentation", "test-native-gradle-plugin", "test-native-maven-plugin", "test-junit-platform-native" ]
+ workflows: [ "Deploy documentation to website", "Test native-gradle-plugin", "Test native-maven-plugin", "Test junit-platform-native" ]
branches: [ "master" ]
types:
- completed
@@ -20,22 +20,20 @@ jobs:
strategy:
fail-fast: false
matrix:
- graalvm-version: [ latest ]
- java-version: [ 11 ]
+ java-version: [ 17 ]
os: [ ubuntu-22.04 ]
steps:
- name: "☁ Checkout repository"
- uses: actions/checkout@v2
+ uses: actions/checkout@v4
with:
ssh-key: "${{ secrets.SSH_PRIVATE_KEY }}"
- name: "🔧 Prepare environment"
uses: ./.github/actions/prepare-environment
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
- graalvm-version: ${{ matrix.graalvm-version }}
java-version: ${{ matrix.java-version }}
push-access: 1
ssh-key: ${{ secrets.SSH_PRIVATE_KEY }}
- name: "🌍 Build and publish snapshots"
run: |
- ./gradlew publishAllPublicationsToSnapshotsRepository --no-parallel
+ ./gradlew publishToMavenLocal publishAllPublicationsToSnapshotsRepository --no-parallel
diff --git a/.github/workflows/test-graalvm-metadata.yml b/.github/workflows/test-graalvm-metadata.yml
index edb52c224..36290d411 100644
--- a/.github/workflows/test-graalvm-metadata.yml
+++ b/.github/workflows/test-graalvm-metadata.yml
@@ -4,13 +4,15 @@ on:
push:
paths:
- 'common/graalvm-reachability-metadata/**'
- - '.github/actions/prepare-environment'
+ - '.github/actions/**'
- '.github/workflows/test-graalvm-metadata.yml'
+ - 'gradle/libs.versions.toml'
pull_request:
paths:
- 'common/graalvm-reachability-metadata/**'
- - '.github/actions/prepare-environment'
+ - '.github/actions/**'
- '.github/workflows/test-graalvm-metadata.yml'
+ - 'gradle/libs.versions.toml'
workflow_dispatch:
concurrency:
@@ -24,16 +26,14 @@ jobs:
strategy:
fail-fast: false
matrix:
- graalvm-version: [ dev ]
- java-version: [ 11 ]
- os: [ ubuntu-20.04 ]
+ java-version: [ 17 ]
+ os: [ ubuntu-22.04 ]
steps:
- name: "☁️ Checkout repository"
- uses: actions/checkout@v2
+ uses: actions/checkout@v4
- name: "🔧 Prepare environment"
uses: ./.github/actions/prepare-environment
with:
- graalvm-version: ${{ matrix.graalvm-version }}
java-version: ${{ matrix.java-version }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: "❓ Checkstyle"
@@ -42,7 +42,7 @@ jobs:
run: ./gradlew :graalvm-reachability-metadata:test
- name: "📜 Upload tests results"
if: always()
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@v4
with:
- name: tests-results
+ name: tests-results-${{ strategy.job-index }}-${{ matrix.java-version }}-${{ matrix.os }}
path: common/graalvm-reachability-metadata/build/reports/tests/
diff --git a/.github/workflows/test-junit-platform-native.yml b/.github/workflows/test-junit-platform-native.yml
index 5d561d4bc..51c03960b 100644
--- a/.github/workflows/test-junit-platform-native.yml
+++ b/.github/workflows/test-junit-platform-native.yml
@@ -4,13 +4,15 @@ on:
push:
paths:
- 'common/junit-platform-native/**'
- - '.github/actions/prepare-environment'
+ - '.github/actions/**'
- '.github/workflows/test-junit-platform-native.yml'
+ - 'gradle/libs.versions.toml'
pull_request:
paths:
- 'common/junit-platform-native/**'
- - '.github/actions/prepare-environment'
+ - '.github/actions/**'
- '.github/workflows/test-junit-platform-native.yml'
+ - 'gradle/libs.versions.toml'
workflow_dispatch:
concurrency:
@@ -24,16 +26,14 @@ jobs:
strategy:
fail-fast: false
matrix:
- graalvm-version: [ dev ]
- java-version: [ 11 ]
- os: [ ubuntu-20.04 ]
+ java-version: [ 17 ]
+ os: [ ubuntu-22.04 ]
steps:
- name: "☁️ Checkout repository"
- uses: actions/checkout@v2
+ uses: actions/checkout@v4
- name: "🔧 Prepare environment"
uses: ./.github/actions/prepare-environment
with:
- graalvm-version: ${{ matrix.graalvm-version }}
java-version: ${{ matrix.java-version }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: "❓ Checkstyle"
@@ -44,7 +44,7 @@ jobs:
run: ./gradlew :junit-platform-native:nativeTest
- name: "📜 Upload tests results"
if: always()
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@v4
with:
- name: tests-results
+ name: tests-results-${{ strategy.job-index }}-${{ matrix.java-version }}-${{ matrix.os }}
path: common/junit-platform-native/build/reports/tests/
diff --git a/.github/workflows/test-native-gradle-plugin.yml b/.github/workflows/test-native-gradle-plugin.yml
index e2ddc2116..96765a0f3 100644
--- a/.github/workflows/test-native-gradle-plugin.yml
+++ b/.github/workflows/test-native-gradle-plugin.yml
@@ -6,15 +6,17 @@ on:
- 'native-gradle-plugin/**'
- 'samples/**'
- 'common/**'
- - '.github/actions/prepare-environment'
+ - '.github/actions/**'
- '.github/workflows/test-native-gradle-plugin.yml'
+ - 'gradle/libs.versions.toml'
pull_request:
paths:
- 'native-gradle-plugin/**'
- 'samples/**'
- 'common/**'
- - '.github/actions/prepare-environment'
+ - '.github/actions/**'
- '.github/workflows/test-native-gradle-plugin.yml'
+ - 'gradle/libs.versions.toml'
workflow_dispatch:
concurrency:
@@ -22,83 +24,150 @@ concurrency:
cancel-in-progress: ${{ github.event_name == 'pull_request' || github.repository != 'graalvm/native-build-tools' }}
jobs:
- test-native-gradle-plugin:
- name: "Sanity checks"
+ populate-matrix:
+ name: "Set matrix"
+ runs-on: "ubuntu-22.04"
+ timeout-minutes: 5
+ outputs:
+ matrix: ${{ steps.set-matrix.outputs.matrix }}
+ steps:
+ - name: "☁️ Checkout repository"
+ uses: actions/checkout@v3
+ - name: "🔧 Prepare environment"
+ uses: graalvm/setup-graalvm@v1
+ with:
+ java-version: '17'
+ distribution: 'graalvm'
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ - name: "🕸️ Populate matrix"
+ id: set-matrix
+ run: ./gradlew -PmatrixType=gradle :native-gradle-plugin:dumpFunctionalTestList
+
+ functional-testing-gradle-plugin:
+ name: "🧪 Gradle: ${{ matrix.test }} on ${{ matrix.os }} with gradle version: ${{ matrix.gradle-version }}"
runs-on: ${{ matrix.os }}
+ timeout-minutes: 60
+ needs: populate-matrix
strategy:
fail-fast: false
- matrix:
- graalvm-version: [ latest ] # dev
- java-version: [ 11 ]
- os: [ ubuntu-20.04 ]
+ matrix: ${{fromJson(needs.populate-matrix.outputs.matrix)}}
steps:
- name: "☁️ Checkout repository"
- uses: actions/checkout@v2
+ uses: actions/checkout@v4
- name: "🔧 Prepare environment"
uses: ./.github/actions/prepare-environment
with:
- graalvm-version: ${{ matrix.graalvm-version }}
java-version: ${{ matrix.java-version }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- - name: "❓ Unit tests and inspections"
- run: ./gradlew :native-gradle-plugin:test :native-gradle-plugin:inspections
- - name: "📜 Upload unit test results"
- uses: actions/upload-artifact@v1
+ - name: "❓ Check and test the plugin"
+ run: ./gradlew :native-gradle-plugin:functionalTest -DgradleVersion="${{ matrix.gradle-version }}" --tests ${{ matrix.test }}
+ - name: "📜 Upload functional tests results"
+ if: always()
+ uses: actions/upload-artifact@v4
with:
- name: unit-tests-results
- path: native-gradle-plugin/build/reports/tests/test/
- functional-testing-gradle-plugin:
- name: "Functional testing"
+ name: functional-tests-results-${{ strategy.job-index }}-${{ matrix.gradle-version }}
+ path: native-gradle-plugin/build/reports/tests/functionalTest/
+
+ populate-cache-matrix:
+ name: "Set cache matrix"
+ runs-on: "ubuntu-22.04"
+ timeout-minutes: 5
+ outputs:
+ matrix: ${{ steps.set-matrix.outputs.matrix }}
+ steps:
+ - name: "☁️ Checkout repository"
+ uses: actions/checkout@v3
+ - name: "🔧 Prepare environment"
+ uses: graalvm/setup-graalvm@v1
+ with:
+ java-version: '17'
+ distribution: 'graalvm'
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ - name: "🕸️ Populate matrix"
+ id: set-matrix
+ run: |
+ ./gradlew -PmatrixType=gradleCached :native-gradle-plugin:dumpFunctionalTestList
+
+ functional-testing-gradle-with-cache-version:
+ name: "🧪 Gradle: ${{ matrix.test }} on ${{ matrix.os }} with cache gradle version: ${{ matrix.gradle-config-cache-version }}"
runs-on: ${{ matrix.os }}
+ timeout-minutes: 60
+ needs: populate-cache-matrix
strategy:
fail-fast: false
- matrix:
- gradle-version: ["current", "6.7.1"]
- # Following versions are disabled temporarily in order to speed up PR testing
- # "7.3.3", "7.2", "7.1", "6.8.3"
- graalvm-version: [ latest ] # dev
- java-version: [ 11 ]
- os: [ ubuntu-20.04 ]
+ matrix: ${{fromJson(needs.populate-cache-matrix.outputs.matrix)}}
steps:
- name: "☁️ Checkout repository"
- uses: actions/checkout@v2
- - uses: ./.github/actions/prepare-environment
+ uses: actions/checkout@v4
+ - name: "🔧 Prepare environment"
+ uses: ./.github/actions/prepare-environment
with:
- graalvm-version: ${{ matrix.graalvm-version }}
java-version: ${{ matrix.java-version }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- - name: "❓ Check and test the plugin"
- run: ./gradlew :native-gradle-plugin:functionalTest -DgradleVersion=${{ matrix.gradle-version }}
+ - name: "❓ Check and test the plugin with configuration cache"
+ run: ./gradlew :native-gradle-plugin:configCacheFunctionalTest -DgradleVersion="${{ matrix.gradle-config-cache-version }}" --tests ${{ matrix.test }}
- name: "📜 Upload functional tests results"
if: always()
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@v4
with:
- name: functional-tests-results-${{ matrix.gradle-version }}
+ name: functional-tests-results-${{ strategy.job-index }}-${{ matrix.gradle-config-cache-version }}-${{ matrix.os }}
path: native-gradle-plugin/build/reports/tests/functionalTest/
- functional-testing-gradle-plugin-windows:
- name: "Windows Gradle plugin functional testing"
- if: ${{ false }}
- # Disabled due to https://github.com/gradle/native-platform/issues/274
+
+ test-native-gradle-plugin:
+ name: "Sanity checks"
+ runs-on: "ubuntu-22.04"
+ timeout-minutes: 50
+ strategy:
+ fail-fast: false
+ matrix:
+ java-version: [ 17 ]
+ os: [ ubuntu-22.04 ]
+ steps:
+ - name: "☁️ Checkout repository"
+ uses: actions/checkout@v4
+ - name: "🔧 Prepare environment"
+ uses: ./.github/actions/prepare-environment
+ with:
+ java-version: ${{ matrix.java-version }}
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ - name: "❓ Unit tests and inspections"
+ run: ./gradlew :native-gradle-plugin:test :native-gradle-plugin:inspections
+ - name: "📜 Upload unit test results"
+ uses: actions/upload-artifact@v4
+ with:
+ name: unit-tests-results-${{ strategy.job-index }}-${{ matrix.java-version }}-${{ matrix.os }}
+ path: native-gradle-plugin/build/reports/tests/test/
+
+ functional-testing-gradle-plugin-dev:
+ name: "Functional testing (GraalVM Dev Build)"
runs-on: ${{ matrix.os }}
+ env:
+ IS_GRAALVM_DEV_BUILD: 'true'
strategy:
fail-fast: false
matrix:
- graalvm-version: [ latest ]
- java-version: [ 11 ]
- os: [ windows-latest ]
+ java-version: [ 17 ]
+ os: [ ubuntu-22.04 ]
steps:
- name: "☁️ Checkout repository"
- uses: actions/checkout@v2
- - uses: ./.github/actions/prepare-environment
+ uses: actions/checkout@v4
+ - name: "🔧 Prepare environment"
+ uses: ./.github/actions/prepare-environment
with:
- graalvm-version: ${{ matrix.graalvm-version }}
java-version: ${{ matrix.java-version }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- - name: "❓ Check and test the Gradle plugin"
- run: ./gradlew :native-gradle-plugin:functionalTest --no-daemon --fail-fast
- - name: "📜 Upload tests results"
+ - name: "🔧 Install GraalVM (dev)"
+ uses: graalvm/setup-graalvm@main
+ with:
+ java-version: 'latest-ea'
+ distribution: 'graalvm'
+ github-token: ${{ inputs.github-token }}
+ set-java-home: 'false'
+ - name: "❓ Check and test the plugin"
+ run: ./gradlew :native-gradle-plugin:functionalTest
+ - name: "📜 Upload functional tests results"
if: always()
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@v4
with:
- name: windows-gradle-functional-tests-results
- path: native-gradle-plugin/build/reports/tests/
+ name: functional-tests-results-graalvm-dev-${{ strategy.job-index }}-${{ matrix.java-version }}-${{ matrix.os }}
+ path: native-gradle-plugin/build/reports/tests/functionalTest/
diff --git a/.github/workflows/test-native-maven-plugin.yml b/.github/workflows/test-native-maven-plugin.yml
index afff175c8..097c5c71c 100644
--- a/.github/workflows/test-native-maven-plugin.yml
+++ b/.github/workflows/test-native-maven-plugin.yml
@@ -6,15 +6,17 @@ on:
- 'native-maven-plugin/**'
- 'samples/**'
- 'common/**'
- - '.github/actions/prepare-environment'
+ - '.github/actions/**'
- '.github/workflows/test-native-maven-plugin.yml'
+ - 'gradle/libs.versions.toml'
pull_request:
paths:
- 'native-maven-plugin/**'
- 'samples/**'
- 'common/**'
- - '.github/actions/prepare-environment'
+ - '.github/actions/**'
- '.github/workflows/test-native-maven-plugin.yml'
+ - 'gradle/libs.versions.toml'
workflow_dispatch:
concurrency:
@@ -22,55 +24,81 @@ concurrency:
cancel-in-progress: ${{ github.event_name == 'pull_request' || github.repository != 'graalvm/native-build-tools' }}
jobs:
+ populate-matrix:
+ name: "Set matrix"
+ runs-on: "ubuntu-22.04"
+ timeout-minutes: 5
+ outputs:
+ matrix: ${{ steps.set-matrix.outputs.matrix }}
+ steps:
+ - name: "☁️ Checkout repository"
+ uses: actions/checkout@v3
+ - name: "🔧 Prepare environment"
+ uses: graalvm/setup-graalvm@v1
+ with:
+ java-version: '17'
+ distribution: 'graalvm'
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ - name: "🕸️ Populate matrix"
+ id: set-matrix
+ run: |
+ ./gradlew -PmatrixType=maven :native-maven-plugin:dumpFunctionalTestList
+
test-native-maven-plugin:
- name: "Maven plugin tests"
+ name: "🧪 Maven: ${{ matrix.test }} on ${{ matrix.os }}"
runs-on: ${{ matrix.os }}
+ timeout-minutes: 60
+ needs: populate-matrix
strategy:
fail-fast: false
- matrix:
- graalvm-version: [ latest ] # dev
- java-version: [ 11 ]
- os: [ ubuntu-20.04 ]
+ matrix: ${{fromJson(needs.populate-matrix.outputs.matrix)}}
steps:
- name: "☁️ Checkout repository"
- uses: actions/checkout@v2
- - uses: ./.github/actions/prepare-environment
+ uses: actions/checkout@v4
+ - name: "🔧 Prepare environment"
+ uses: ./.github/actions/prepare-environment
with:
- graalvm-version: ${{ matrix.graalvm-version }}
java-version: ${{ matrix.java-version }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: "❓ Check and test the plugin"
- run: ./gradlew :native-maven-plugin:check --no-daemon
+ run: ./gradlew :native-maven-plugin:functionalTest --no-daemon --fail-fast --tests ${{ matrix.test }}
- name: "📜 Upload unit test results"
if: always()
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@v4
with:
- name: maven-functional-tests-results-${{ matrix.os }}
+ name: maven-functional-tests-results-${{ strategy.job-index }}-${{ matrix.os }}-${{ matrix.test }}
path: native-maven-plugin/build/reports/tests/
- test-native-maven-plugin-windows:
- name: "Windows Minimal Tests for Maven plugin"
- if: ${{ false }}
- # Fails with "Illegal char <�> at index 3: C:\�Users\�RUNNER~1\�AppData\�Local\�Temp\�spock_supports_spaces_in__0_testDirectory18198684407774777428\�with spaces\�local-repo\�org\�apache\�commons\�commons-lang3\�3.8.1\�commons-lang3-3.8.1.jar"
+
+ functional-testing-maven-plugin-dev:
+ name: "Functional testing (GraalVM Dev Build)"
runs-on: ${{ matrix.os }}
+ env:
+ IS_GRAALVM_DEV_BUILD: 'true'
strategy:
fail-fast: false
matrix:
- graalvm-version: [ latest ] # dev
- java-version: [ 11 ]
- os: [ windows-latest ]
+ java-version: [ 17 ]
+ os: [ ubuntu-22.04 ]
steps:
- name: "☁️ Checkout repository"
- uses: actions/checkout@v2
- - uses: ./.github/actions/prepare-environment
+ uses: actions/checkout@v4
+ - name: "🔧 Prepare environment"
+ uses: ./.github/actions/prepare-environment
with:
- graalvm-version: ${{ matrix.graalvm-version }}
java-version: ${{ matrix.java-version }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- - name: "❓ Check and test the Maven plugin"
- run: ./gradlew :native-maven-plugin:functionalTest --no-daemon --fail-fast
- - name: "📜 Upload tests results"
+ - name: "🔧 Install GraalVM (dev)"
+ uses: graalvm/setup-graalvm@main
+ with:
+ java-version: 'latest-ea'
+ distribution: 'graalvm'
+ github-token: ${{ inputs.github-token }}
+ set-java-home: 'false'
+ - name: "❓ Check and test the plugin"
+ run: ./gradlew :native-maven-plugin:functionalTest
+ - name: "📜 Upload functional tests results"
if: always()
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@v4
with:
- name: windows-maven-functional-tests-results
- path: native-maven-plugin/build/reports/tests/
+ name: functional-tests-results-graalvm-dev-${{ strategy.job-index }}-${{ matrix.os }}-${{ matrix.test }}
+ path: native-maven-plugin/build/reports/tests/functionalTest/
diff --git a/.sdkmanrc b/.sdkmanrc
new file mode 100644
index 000000000..2c6bbe805
--- /dev/null
+++ b/.sdkmanrc
@@ -0,0 +1,3 @@
+# Enable auto-env through the sdkman_auto_env config
+# Add key=value pairs of SDKs to use below
+java=17.0.9-graalce
diff --git a/DEVELOPING.md b/DEVELOPING.md
new file mode 100644
index 000000000..8b46bd519
--- /dev/null
+++ b/DEVELOPING.md
@@ -0,0 +1,122 @@
+# Documentation for Developers
+
+This document describes how to set up and develop Native Build Tools on your local machine.
+
+## Environment
+
+Start by setting `JAVA_HOME` to a [Gradle-compatible JDK](https://docs.gradle.org/current/userguide/compatibility.html).
+
+Some build tasks require a GraalVM JDK (e.g., tests). You should set `GRAALVM_HOME` to an appropriate GraalVM JDK.
+
+## IDE Setup
+
+The Native Build Tools repository is structured as a Gradle multi-project, with the Maven and Gradle plugins declared as subprojects of the root project.
+To configure it in your IDE (e.g., IntelliJ IDEA), import the root project, and the IDE should automatically detect and include the subprojects.
+
+## Building and Testing
+
+You can use the various commands in the [Gradle build lifecycle](https://docs.gradle.org/current/userguide/build_lifecycle.html) to build and test the project.
+Some examples are (all executed from the root of the repository):
+
+```bash
+# Compile all projects
+./gradlew assemble
+
+# Run unit tests in all projects
+./gradlew test
+
+# Run functional tests in all projects
+./gradlew funTest
+
+# Compile only the native-gradle-plugin (for example)
+./gradlew :native-gradle-plugin:assemble
+
+# Build and run all tests, complete (and very long) build
+./gradlew build
+```
+
+## Debugging Plugin(s)
+
+It is often useful to attach a debugger to the Gradle and Maven plugins during a project build.
+
+For the Gradle plugin, this can be accomplished by passing debugger options to the Gradle daemon via `org.gradle.jvmargs`, for example:
+
+```bash
+JAVA_OPTS="-Dorg.gradle.jvmargs=-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=8000" ./gradlew assemble
+```
+
+The Gradle daemon will suspend on start-up, wait for you to attach a debugger, and then remain attached for subsequent Gradle commands.
+
+For the Maven plugin, simply use the `mvnDebug` command in place of the `mvn` command.
+
+## Testing Local Changes with an Existing Project
+
+A common development task is to modify a plugin and then test it with an existing project.
+
+To do this, first modify the project as necessary, and then build and publish the plugins:
+```bash
+./gradlew publishAllPublicationsToCommonRepository --no-parallel
+```
+The above command publishes to a "common" repository located at `build/common-repo`.
+
+Next, update the project build files:
+1. Update the version string. The version can be found manually by searching for the published artifacts in `build/common-repo`, or alternatively by checking the `nativeBuildTools` property [here](gradle/libs.versions.toml).
+2. Update the list of repositories to include and prioritize the common repo.
+
+### Gradle
+
+Make the following changes to the build files:
+```bash
+# build.gradle
+ plugins {
+ ...
+- id 'org.graalvm.buildtools.native' version '0.9.25'
++ id 'org.graalvm.buildtools.native' version '0.10.5-SNAPSHOT'
+ }
+
+ repositories {
++ maven {
++ name = "common"
++ url = "$NATIVE_BUILD_TOOLS_ROOT/build/common-repo"
++ }
+ ...
+ }
+
+# settings.gradle
+ pluginManagement {
+ repositories {
++ maven {
++ name = "common"
++ url = "$NATIVE_BUILD_TOOLS_ROOT/build/common-repo"
++ }
+ # NB: If repositories were not specified before, declaring the common
+ # repo will overwrite the default repository; you may also need to
+ # declare that repository explicitly.
+ mavenCentral()
+ ...
+ }
+ }
+```
+Then, run the Gradle command as usual.
+
+### Maven
+
+Make the following changes to _pom.xml_:
+```bash
+# pom.xml
+
+
+- 0.9.25
++ 0.10.5-SNAPSHOT
+
+ ...
++
++
++ common
++ file://$NATIVE_BUILD_TOOLS_ROOT/build/common-repo
++
++
+
+```
+
+Then, run the Maven command with the `-U` flag to force Maven to use an updated snapshot (e.g., `mvn -Pnative package -U`).
diff --git a/README.md b/README.md
index 7d5385dc8..2b97eb838 100644
--- a/README.md
+++ b/README.md
@@ -2,20 +2,29 @@


-Repository which contains build tool plugins for interoperability with [GraalVM Native Image](https://www.graalvm.org/reference-manual/native-image/)
+Repository which contains build tool plugins for interoperability with [GraalVM Native Image](https://www.graalvm.org/reference-manual/native-image/).
End-user documentation about the plugins can be found [here](https://graalvm.github.io/native-build-tools/).
## Contributing
+
+Documentation for common developer tasks can be found [here](DEVELOPING.md).
+
### Projects
* [native-maven-plugin](native-maven-plugin/README.md)
* [native-gradle-plugin](native-gradle-plugin/README.md)
### Common subprojects
* [junit-platform-native](common/junit-platform-native/README.md)
-
-_Examples can be found in the [samples subdirectory](samples/README.md)._
+### Walkthroughs
+
+* [Maven Plugin End-to-End Guide](https://graalvm.github.io/native-build-tools/latest/end-to-end-maven-guide.html)
+* [Gradle Plugin End-to-End Guide](https://graalvm.github.io/native-build-tools/latest/end-to-end-gradle-guide.html)
+
+### Samples
+
+Examples can be found in the [samples subdirectory](samples).
### Contributing Code
We welcome your code contributions. To get started, you will need to sign the [Oracle Contributor Agreement](https://oca.opensource.oracle.com) (OCA).
diff --git a/THIRD_PARTY_LICENSES.txt b/THIRD_PARTY_LICENSES.txt
index f183de310..268aa79ec 100644
--- a/THIRD_PARTY_LICENSES.txt
+++ b/THIRD_PARTY_LICENSES.txt
@@ -1,454 +1,6 @@
-TOP LEVEL COMPONENT NAMES: com.fasterxml.jackson.core:jackson-databind
-Copyright © 2008–2012 FasterXML. All rights reserved.
-----------------------------------------------------------------------
+TOP LEVEL COMPONENT NAME: com.github.openjson:openjson
-This copy of Jackson JSON processor databind module is licensed under the
-Apache (Software) License, version 2.0 ("the License").
-See the License for details about distribution rights, and the
-specific rights regarding derivate works.
-
-You may obtain a copy of the License at:
-
-http://www.apache.org/licenses/LICENSE-2.0
-
-NOTICE FILE:
-===============
-# Jackson JSON processor
-"Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- ""License"" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- ""Licensor"" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- ""Legal Entity"" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- ""control"" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- ""You"" (or ""Your"") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- ""Source"" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- ""Object"" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- ""Work"" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- ""Derivative Works"" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- ""Contribution"" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, ""submitted""
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as ""Not a Contribution.""
-
- ""Contributor"" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a ""NOTICE"" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an ""AS IS"" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets ""[]""
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same ""printed page"" as the copyright notice for easier
- identification within third-party archives.
-
-Copyright © 2008–2012 FasterXML. All rights reserved.
-
- Licensed under the Apache License, Version 2.0 (the ""License"");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an ""AS IS"" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License."
-
-jackson-core
------------------
-Fourth Party Dependecy 1: com.fasterxml.jackson.core
-
-Copyright © 2008–2021 FasterXML. All rights reserved
-
-This project contains core low-level incremental ("streaming") parser and generator abstractions used by
-[Jackson Data Processor](http://wiki.fasterxml.com/JacksonHome).
-It also includes the default implementation of handler types (parser, generator) that handle JSON format.
-The core abstractions are not JSON specific, although naming does contain 'JSON' in many places, due to historical reasons. Only packages that specifically contain word 'json' are JSON-specific.
-
-This package is the base on which [Jackson data-binding](https://github.com/FasterXML/jackson-databind) package builds on.
-It is licensed under [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0).
-
-
-"Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- ""License"" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- ""Licensor"" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- ""Legal Entity"" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- ""control"" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- ""You"" (or ""Your"") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- ""Source"" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- ""Object"" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- ""Work"" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- ""Derivative Works"" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- ""Contribution"" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, ""submitted""
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as ""Not a Contribution.""
-
- ""Contributor"" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a ""NOTICE"" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an ""AS IS"" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets ""[]""
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same ""printed page"" as the copyright notice for easier
- identification within third-party archives.
-
-Copyright © 2008–2021 FasterXML. All rights reserved
-
- Licensed under the Apache License, Version 2.0 (the ""License"");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an ""AS IS"" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License."
-
-jackson-annotation
----------------------
-Fourth Party Dependecy 1: com.fasterxml.jackson.annotation
-
-Copyright © 2008–2020 FasterXML. All rights reserved.
-
-This project contains general purpose annotations for Jackson Data Processor, used on value and handler types. The only annotations not included are ones that require dependency to the Databind package. Note that only annotations themselves (and related value classes) are included, but no functionality that uses annotations.
-
-Project contains versions 2.0 and above: source code for earlier (1.x) versions is available from Jackson-1 repository.
-
-Full Listing of Jackson Annotations details all available annotations; Project Wiki gives more details.
-
-Project is licensed under Apache License 2.0.
-
-
-"Apache License
+ Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
@@ -456,38 +8,38 @@ Project is licensed under Apache License 2.0.
1. Definitions.
- ""License"" shall mean the terms and conditions for use, reproduction,
+ "License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
- ""Licensor"" shall mean the copyright owner or entity authorized by
+ "Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
- ""Legal Entity"" shall mean the union of the acting entity and all
+ "Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
- ""control"" means (i) the power, direct or indirect, to cause the
+ "control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
- ""You"" (or ""Your"") shall mean an individual or Legal Entity
+ "You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
- ""Source"" form shall mean the preferred form for making modifications,
+ "Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
- ""Object"" form shall mean any form resulting from mechanical
+ "Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
- ""Work"" shall mean the work of authorship, whether in Source or
+ "Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
- ""Derivative Works"" shall mean any work, whether in Source or Object
+ "Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
@@ -495,21 +47,21 @@ Project is licensed under Apache License 2.0.
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
- ""Contribution"" shall mean any work of authorship, including
+ "Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, ""submitted""
+ the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as ""Not a Contribution.""
+ designated in writing by the copyright owner as "Not a Contribution."
- ""Contributor"" shall mean Licensor and any individual or Legal Entity
+ "Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
@@ -553,7 +105,7 @@ Project is licensed under Apache License 2.0.
excluding those notices that do not pertain to any part of
the Derivative Works; and
- (d) If the Work includes a ""NOTICE"" text file as part of its
+ (d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
@@ -592,7 +144,7 @@ Project is licensed under Apache License 2.0.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an ""AS IS"" BASIS,
+ Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
@@ -628,24 +180,24 @@ Project is licensed under Apache License 2.0.
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets ""[]""
+ boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
- same ""printed page"" as the copyright notice for easier
+ same "printed page" as the copyright notice for easier
identification within third-party archives.
- Copyright © 2008–2020 FasterXML. All rights reserved.
+ Copyright [yyyy] [name of copyright owner]
- Licensed under the Apache License, Version 2.0 (the ""License"");
+ Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an ""AS IS"" BASIS,
+ distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
- limitations under the License."
+ limitations under the License.
diff --git a/build-logic/aggregator/src/main/kotlin/org.graalvm.build.aggregator.gradle.kts b/build-logic/aggregator/src/main/kotlin/org.graalvm.build.aggregator.gradle.kts
index c28db8655..d15ac6360 100644
--- a/build-logic/aggregator/src/main/kotlin/org.graalvm.build.aggregator.gradle.kts
+++ b/build-logic/aggregator/src/main/kotlin/org.graalvm.build.aggregator.gradle.kts
@@ -74,11 +74,19 @@ tasks.named("check") {
}
}
+tasks.register("showPublications") {
+ gradle.includedBuilds.forEach {
+ if (it.name != "docs" && !it.projectDir.path.contains("build-logic")) {
+ dependsOn(it.task(":showPublications"))
+ }
+ }
+}
+
listOf(
- "publishTo" to "MavenLocal",
- "publishAllPublicationsTo" to "CommonRepository",
- "publishAllPublicationsTo" to "SnapshotsRepository",
- "publishAllPublicationsTo" to "NexusRepository",
+ "publishTo" to "MavenLocal",
+ "publishAllPublicationsTo" to "CommonRepository",
+ "publishAllPublicationsTo" to "SnapshotsRepository",
+ "publishAllPublicationsTo" to "NexusRepository",
).forEach { entry ->
val (taskPrefix, repo) = entry
tasks.register("$taskPrefix$repo") {
@@ -123,8 +131,8 @@ tasks.register("releaseZip") {
val updateSamples by tasks.registering
mapOf(
- "updateSamplesDir" to "samples",
- "updateMavenReprosDir" to "native-maven-plugin/reproducers"
+ "updateSamplesDir" to "samples",
+ "updateMavenReprosDir" to "native-maven-plugin/reproducers"
).forEach { (taskName, dir) ->
val t = tasks.register(taskName) {
inputDirectory.set(layout.projectDirectory.dir(dir))
diff --git a/build-logic/common-plugins/build.gradle.kts b/build-logic/common-plugins/build.gradle.kts
index e1fc239a9..424902864 100644
--- a/build-logic/common-plugins/build.gradle.kts
+++ b/build-logic/common-plugins/build.gradle.kts
@@ -41,6 +41,7 @@
plugins {
`kotlin-dsl`
+ `groovy-gradle-plugin`
}
repositories {
diff --git a/build-logic/common-plugins/src/main/groovy/org.graalvm.build.github-actions-helper.gradle b/build-logic/common-plugins/src/main/groovy/org.graalvm.build.github-actions-helper.gradle
new file mode 100644
index 000000000..e639bd997
--- /dev/null
+++ b/build-logic/common-plugins/src/main/groovy/org.graalvm.build.github-actions-helper.gradle
@@ -0,0 +1,54 @@
+import groovy.json.JsonOutput
+
+def matrixDefault = [
+ "java-version": [ "17" ],
+ "os": [ "ubuntu-22.04", "windows-latest" ]
+]
+
+// # Following versions are disabled temporarily in order to speed up PR testing "7.3.3", "7.2", "7.1", "6.8.3",
+def gradleVersions = [
+ "gradle-version": ["current", "8.4", "8.14.2"],
+]
+
+def gradleCachedVersions = [
+ "gradle-config-cache-version": ["current", "8.14.2"]
+]
+
+sourceSets.configureEach { sourceSet ->
+ if (sourceSet.name == 'functionalTest') {
+ tasks.register("dumpFunctionalTestList") {
+ doLast {
+ def matrix = [
+ test: []
+ ]
+
+ // add all defaults
+ matrix.putAll(matrixDefault)
+
+ // add gradle specific stuff
+ if (matrixType == "gradle") {
+ matrix.putAll(gradleVersions)
+ }
+
+ if (matrixType == "gradleCached") {
+ matrix.putAll(gradleCachedVersions)
+ }
+
+ // add functional tests
+ sourceSet.allSource.each {
+ matrix.test << it.name.substring(0, it.name.lastIndexOf('.'))
+ }
+
+ String githubOut = System.getenv("GITHUB_OUTPUT")
+ if (githubOut != null) {
+ new File(githubOut).withWriterAppend {
+ it.println "matrix=${JsonOutput.toJson(matrix)}"
+ }
+ } else {
+ println "Warning: GITHUB_OUTPUT environment variable not found"
+ }
+ println "Build Matrix: $matrix"
+ }
+ }
+ }
+}
diff --git a/build-logic/common-plugins/src/main/kotlin/org.graalvm.build.java.gradle.kts b/build-logic/common-plugins/src/main/kotlin/org.graalvm.build.java.gradle.kts
index f7d4ae4d3..ca29cc45c 100644
--- a/build-logic/common-plugins/src/main/kotlin/org.graalvm.build.java.gradle.kts
+++ b/build-logic/common-plugins/src/main/kotlin/org.graalvm.build.java.gradle.kts
@@ -51,7 +51,7 @@ plugins {
java {
toolchain {
- languageVersion.set(JavaLanguageVersion.of(8))
+ languageVersion.set(JavaLanguageVersion.of(17))
}
withJavadocJar()
withSourcesJar()
@@ -64,21 +64,20 @@ repositories {
group = "org.graalvm.buildtools"
extensions.findByType()?.also { catalogs ->
- val versionFromCatalog = catalogs.named("libs")
+ if (catalogs.find("libs").isPresent) {
+ val versionFromCatalog = catalogs.named("libs")
.findVersion("nativeBuildTools")
- if (versionFromCatalog.isPresent()) {
- version = versionFromCatalog.get().requiredVersion
+ if (versionFromCatalog.isPresent()) {
+ version = versionFromCatalog.get().requiredVersion
+ } else {
+ throw GradleException("Version catalog doesn't define project version 'nativeBuildTools'")
+ }
} else {
- throw GradleException("Version catalog doesn't define project version 'nativeBuildTools'")
+ version = "undefined"
}
}
tasks.javadoc {
- // Use a different toolchain version from the compilation
- // so that we can generate HTML5 docs
- javadocTool.set(javaToolchains.javadocToolFor {
- languageVersion.set(JavaLanguageVersion.of(11))
- })
(options as StandardJavadocDocletOptions).addBooleanOption("html5", true)
(options as StandardJavadocDocletOptions).noTimestamp(true)
}
diff --git a/build-logic/common-plugins/src/main/kotlin/org.graalvm.build.publishing.gradle.kts b/build-logic/common-plugins/src/main/kotlin/org.graalvm.build.publishing.gradle.kts
index 182d16891..fe3308a26 100644
--- a/build-logic/common-plugins/src/main/kotlin/org.graalvm.build.publishing.gradle.kts
+++ b/build-logic/common-plugins/src/main/kotlin/org.graalvm.build.publishing.gradle.kts
@@ -58,7 +58,7 @@ val mavenExtension = project.extensions.create("maven").also {
it.description.convention(project.description)
}
-val publishingTasks = tasks.withType()
+val publishingTasks = tasks.withType()
.matching { it.name.endsWith("ToCommonRepository") }
val repositoryElements by configurations.creating {
@@ -151,8 +151,33 @@ plugins.withId("java-test-fixtures") {
}
}
+val publicationCoordinatesCollector = gradle.sharedServices.registerIfAbsent("publicationCoordinatesCollector", PublicationCoordinatesCollector::class.java) {
+}
+
+val showPublications by tasks.registering {
+ usesService(publicationCoordinatesCollector)
+ doLast {
+ publishing.publications.all {
+ val pub = this as MavenPublication
+ publicationCoordinatesCollector.get().addPublication(pub)
+ }
+ }
+}
+
// Get a handle on the software component factory
interface Services {
@Inject
fun getSoftwareComponentFactory(): SoftwareComponentFactory
}
+
+abstract class PublicationCoordinatesCollector: BuildService, AutoCloseable {
+ val publications = mutableSetOf()
+
+ fun addPublication(publication: MavenPublication) {
+ publications.add("${publication.groupId}:${publication.artifactId}:${publication.version}")
+ }
+
+ override fun close() {
+ publications.sorted().forEach { println(it) }
+ }
+}
diff --git a/build-logic/gradle-functional-testing/src/main/groovy/org.graalvm.build.functional-testing.gradle b/build-logic/gradle-functional-testing/src/main/groovy/org.graalvm.build.functional-testing.gradle
index 459302f56..3125fc3cb 100644
--- a/build-logic/gradle-functional-testing/src/main/groovy/org.graalvm.build.functional-testing.gradle
+++ b/build-logic/gradle-functional-testing/src/main/groovy/org.graalvm.build.functional-testing.gradle
@@ -74,20 +74,24 @@ configurations {
gradlePlugin.testSourceSets(sourceSets.functionalTest)
configurations.functionalTestImplementation.extendsFrom(configurations.testImplementation)
+def graalVmHomeProvider = providers.environmentVariable("GRAALVM_HOME")
+def isGraalVmDevBuild = providers.environmentVariable("IS_GRAALVM_DEV_BUILD")
def graalVm = javaToolchains.launcherFor {
- languageVersion.set(JavaLanguageVersion.of(11))
- vendor.set(JvmVendorSpec.matching("GraalVM"))
+ languageVersion.set(JavaLanguageVersion.of(17))
+// vendor.set(JvmVendorSpec.matching("Oracle Corporation"))
}
def fullFunctionalTest = tasks.register("fullFunctionalTest")
-['functionalTest', 'configCacheFunctionalTest'].each { baseName ->
- ["current", "7.3.3", "7.2", "7.1", "6.8.3", "6.7.1"].each { gradleVersion ->
+['functionalTest': ["current", "8.4", "8.14.2"],
+ 'configCacheFunctionalTest': ['current', "8.14.2"]
+].each { baseName, gradleVersions ->
+ gradleVersions.each { gradleVersion ->
String taskName = gradleVersion == 'current' ? baseName : "gradle${gradleVersion}${baseName.capitalize()}"
// Add a task to run the functional tests
def testTask = tasks.register(taskName, Test) {
String effectiveVersion = gradleVersion
- def versionProvider = providers.systemProperty('gradleVersion').forUseAtConfigurationTime()
+ def versionProvider = providers.systemProperty('gradleVersion')
if (effectiveVersion == 'current' && versionProvider.isPresent()) {
effectiveVersion = versionProvider.get()
}
@@ -99,12 +103,30 @@ def fullFunctionalTest = tasks.register("fullFunctionalTest")
systemProperty('common.repo.url', configurations.functionalTestCommonRepository.incoming.files.files[0])
systemProperty('gradle.test.version', effectiveVersion)
systemProperty('versions.junit', libs.versions.junitJupiter.get())
- environment('GRAALVM_HOME', graalVm.forUseAtConfigurationTime().get().metadata.installationPath.asFile.absolutePath)
+ environment('GRAALVM_HOME', graalVmHomeProvider.
+ orElse(providers.provider(() -> graalVm.get().metadata.installationPath.asFile.absolutePath))
+ .map {
+ println("Test task $taskName will use GRAALVM_HOME = $it")
+ it
+ }
+ .get()
+ )
+ if (isGraalVmDevBuild.isPresent()) {
+ environment('IS_GRAALVM_DEV_BUILD', 'true')
+ }
testClassesDirs = sourceSets.functionalTest.output.classesDirs
classpath = sourceSets.functionalTest.runtimeClasspath
javaLauncher.set(graalVm)
if (baseName == 'configCacheFunctionalTest') {
systemProperty('config.cache', 'true')
+ // These args are required so that we can debug tests which
+ // run with the configuration cache, see https://github.com/gradle/gradle/issues/25898
+ jvmArgs = [
+ '--add-opens=java.base/java.lang=ALL-UNNAMED',
+ '--add-opens=java.base/java.util=ALL-UNNAMED',
+ '--add-opens=java.base/java.lang.invoke=ALL-UNNAMED',
+ '--add-opens=java.base/java.net=ALL-UNNAMED',
+ ]
}
}
fullFunctionalTest.configure {
diff --git a/build-logic/reachability-plugins/build.gradle.kts b/build-logic/reachability-plugins/build.gradle.kts
new file mode 100644
index 000000000..9b5dc2dd0
--- /dev/null
+++ b/build-logic/reachability-plugins/build.gradle.kts
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * The Universal Permissive License (UPL), Version 1.0
+ *
+ * Subject to the condition set forth below, permission is hereby granted to any
+ * person obtaining a copy of this software, associated documentation and/or
+ * data (collectively the "Software"), free of charge and under any and all
+ * copyright rights in the Software, and any and all patent rights owned or
+ * freely licensable by each licensor hereunder covering either (i) the
+ * unmodified Software as contributed to or provided by such licensor, or (ii)
+ * the Larger Works (as defined below), to deal in both
+ *
+ * (a) the Software, and
+ *
+ * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+ * one is included with the Software each a "Larger Work" to which the Software
+ * is contributed by such licensors),
+ *
+ * without restriction, including without limitation the rights to copy, create
+ * derivative works of, display, perform, and distribute the Software and make,
+ * use, sell, offer for sale, import, export, have made, and have sold the
+ * Software and the Larger Work(s), and to sublicense the foregoing rights on
+ * either these or other terms.
+ *
+ * This license is subject to the following condition:
+ *
+ * The above copyright notice and either this complete permission notice or at a
+ * minimum a reference to the UPL must be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+plugins {
+ java
+ `kotlin-dsl`
+}
+
+repositories {
+ mavenCentral()
+ gradlePluginPortal()
+}
+
+dependencies {
+ implementation(":common-plugins")
+}
diff --git a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/internal/NativeConfigurations.java b/build-logic/reachability-plugins/settings.gradle.kts
similarity index 76%
rename from native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/internal/NativeConfigurations.java
rename to build-logic/reachability-plugins/settings.gradle.kts
index c611980fb..19b872b93 100644
--- a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/internal/NativeConfigurations.java
+++ b/build-logic/reachability-plugins/settings.gradle.kts
@@ -38,25 +38,15 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-package org.graalvm.buildtools.gradle.internal;
-import org.gradle.api.artifacts.Configuration;
+rootProject.name = "reachability-plugins"
-public class NativeConfigurations {
- private final Configuration imageCompileOnly;
- private final Configuration imageClasspath;
-
- public NativeConfigurations(Configuration imageCompileOnly, Configuration imageClasspath) {
- this.imageCompileOnly = imageCompileOnly;
- this.imageClasspath = imageClasspath;
- }
-
- public Configuration getImageCompileOnlyConfiguration() {
- return imageCompileOnly;
- }
+pluginManagement {
+ includeBuild("../settings-plugins")
+}
- public Configuration getImageClasspathConfiguration() {
- return imageClasspath;
- }
+includeBuild("../common-plugins")
+plugins {
+ id("org.graalvm.build.common")
}
diff --git a/build-logic/reachability-plugins/src/main/java/org/graalvm/build/FetchRepositoryMetadata.java b/build-logic/reachability-plugins/src/main/java/org/graalvm/build/FetchRepositoryMetadata.java
new file mode 100644
index 000000000..b2ebc6866
--- /dev/null
+++ b/build-logic/reachability-plugins/src/main/java/org/graalvm/build/FetchRepositoryMetadata.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2003-2021 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.graalvm.build;
+
+import org.gradle.api.DefaultTask;
+import org.gradle.api.file.DirectoryProperty;
+import org.gradle.api.file.FileSystemOperations;
+import org.gradle.api.file.RegularFile;
+import org.gradle.api.provider.Property;
+import org.gradle.api.provider.Provider;
+import org.gradle.api.tasks.CacheableTask;
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.Internal;
+import org.gradle.api.tasks.OutputDirectory;
+import org.gradle.api.tasks.TaskAction;
+
+import javax.inject.Inject;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.net.URI;
+import java.nio.channels.Channels;
+import java.nio.channels.FileChannel;
+import java.nio.channels.ReadableByteChannel;
+
+@CacheableTask
+public abstract class FetchRepositoryMetadata extends DefaultTask {
+ @Input
+ public abstract Property getVersion();
+
+ @Input
+ public abstract Property getRepositoryUrlTemplate();
+
+ @OutputDirectory
+ public abstract DirectoryProperty getOutputDirectory();
+
+ @Inject
+ protected abstract FileSystemOperations getFileSystemOperations();
+
+ @Internal
+ public Provider getOutputFile() {
+ return getOutputDirectory().file("repository.zip");
+ }
+
+ public FetchRepositoryMetadata() {
+ getOutputs().doNotCacheIf("Snapshot version", task -> getVersion().get().endsWith("-SNAPSHOT"));
+ getRepositoryUrlTemplate().convention("https://github.com/oracle/graalvm-reachability-metadata/releases/download/%1$s/graalvm-reachability-metadata-%1$s.zip");
+ }
+
+ @TaskAction
+ public void fetchMetadataRepository() {
+ String url = String.format(getRepositoryUrlTemplate().get(), getVersion().get());
+ File tmpFile = new File(getTemporaryDir(), "repository.zip");
+ try (ReadableByteChannel channel = Channels.newChannel(new URI(url).toURL().openStream())) {
+ try (FileChannel outChannel = new FileOutputStream(tmpFile).getChannel()) {
+ outChannel.transferFrom(channel, 0, Long.MAX_VALUE);
+ }
+ } catch (Exception e) {
+ throw new RuntimeException("Could not download repository metadata from " + url, e);
+ }
+ getFileSystemOperations().copy(spec -> {
+ spec.from(tmpFile);
+ spec.into(getOutputDirectory());
+ });
+ tmpFile.delete();
+ }
+}
diff --git a/build-logic/reachability-plugins/src/main/kotlin/org.graalvm.build.reachability-module.gradle.kts b/build-logic/reachability-plugins/src/main/kotlin/org.graalvm.build.reachability-module.gradle.kts
new file mode 100644
index 000000000..b51dd4584
--- /dev/null
+++ b/build-logic/reachability-plugins/src/main/kotlin/org.graalvm.build.reachability-module.gradle.kts
@@ -0,0 +1,71 @@
+import org.graalvm.build.FetchRepositoryMetadata
+
+/*
+ * Copyright (c) 2020, 2022 Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * The Universal Permissive License (UPL), Version 1.0
+ *
+ * Subject to the condition set forth below, permission is hereby granted to any
+ * person obtaining a copy of this software, associated documentation and/or
+ * data (collectively the "Software"), free of charge and under any and all
+ * copyright rights in the Software, and any and all patent rights owned or
+ * freely licensable by each licensor hereunder covering either (i) the
+ * unmodified Software as contributed to or provided by such licensor, or (ii)
+ * the Larger Works (as defined below), to deal in both
+ *
+ * (a) the Software, and
+ *
+ * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+ * one is included with the Software each a "Larger Work" to which the Software
+ * is contributed by such licensors),
+ *
+ * without restriction, including without limitation the rights to copy, create
+ * derivative works of, display, perform, and distribute the Software and make,
+ * use, sell, offer for sale, import, export, have made, and have sold the
+ * Software and the Larger Work(s), and to sublicense the foregoing rights on
+ * either these or other terms.
+ *
+ * This license is subject to the following condition:
+ *
+ * The above copyright notice and either this complete permission notice or at a
+ * minimum a reference to the UPL must be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+plugins {
+ checkstyle
+ id("org.graalvm.build.java")
+ id("org.graalvm.build.publishing")
+}
+
+tasks.withType().configureEach {
+ useJUnitPlatform()
+}
+
+val fetchRepository = tasks.register("fetchMetadataRepository") {
+ outputDirectory.convention(layout.buildDirectory.dir("repository"))
+}
+
+publishing {
+ publications {
+ create("maven") {
+ from(components["java"])
+ artifact(fetchRepository.flatMap { it.outputFile }) {
+ classifier = "repository"
+ }
+ }
+ }
+}
+
+tasks.withType().configureEach {
+ setConfigFile(layout.projectDirectory.dir("../../config/checkstyle.xml").asFile)
+}
diff --git a/build-logic/utils-plugins/src/main/kotlin/org.graalvm.build.utils-module.gradle.kts b/build-logic/utils-plugins/src/main/kotlin/org.graalvm.build.utils-module.gradle.kts
index 1eca1254c..5a75e1a41 100644
--- a/build-logic/utils-plugins/src/main/kotlin/org.graalvm.build.utils-module.gradle.kts
+++ b/build-logic/utils-plugins/src/main/kotlin/org.graalvm.build.utils-module.gradle.kts
@@ -58,6 +58,8 @@ extensions.findByType()?.also { catalogs ->
val libs = catalogs.named("libs")
val generateVersionInfo = tasks.register("generateVersionInfo", org.graalvm.build.GenerateVersionClass::class.java) {
versions.put("junitPlatformNative", libs.findVersion("nativeBuildTools").get().requiredVersion)
+ versions.put("metadataRepo", libs.findVersion("metadataRepository").get().requiredVersion)
+ versions.put("nbt", libs.findVersion("nativeBuildTools").get().requiredVersion)
outputDirectory.set(layout.buildDirectory.dir("generated/sources/versions"))
}
diff --git a/common/graalvm-reachability-metadata/build.gradle.kts b/common/graalvm-reachability-metadata/build.gradle.kts
index 432b4210c..994d6dca6 100644
--- a/common/graalvm-reachability-metadata/build.gradle.kts
+++ b/common/graalvm-reachability-metadata/build.gradle.kts
@@ -1,3 +1,5 @@
+import org.graalvm.build.FetchRepositoryMetadata
+
/*
* Copyright (c) 2020, 2022 Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
@@ -40,9 +42,7 @@
*/
plugins {
- checkstyle
- id("org.graalvm.build.java")
- id("org.graalvm.build.publishing")
+ id("org.graalvm.build.reachability-module")
}
maven {
@@ -51,23 +51,12 @@ maven {
}
dependencies {
- implementation(libs.jackson.databind)
+ implementation(libs.openjson)
testImplementation(platform(libs.test.junit.bom))
testImplementation(libs.test.junit.jupiter.core)
+ testRuntimeOnly(libs.test.junit.platform.launcher)
}
-tasks.withType().configureEach {
- useJUnitPlatform()
-}
-
-publishing {
- publications {
- create("maven") {
- from(components["java"])
- }
- }
-}
-
-tasks.withType().configureEach {
- setConfigFile(layout.projectDirectory.dir("../../config/checkstyle.xml").asFile)
+tasks.withType().configureEach {
+ version.set(libs.versions.metadataRepository)
}
diff --git a/common/graalvm-reachability-metadata/gradle/native-image-testing.gradle b/common/graalvm-reachability-metadata/gradle/native-image-testing.gradle
index b7de78a52..40645cbd1 100644
--- a/common/graalvm-reachability-metadata/gradle/native-image-testing.gradle
+++ b/common/graalvm-reachability-metadata/gradle/native-image-testing.gradle
@@ -38,9 +38,6 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-
-import org.gradle.util.GFileUtils
-
def agentOutput = layout.buildDirectory.dir("agent")
ext {
@@ -50,12 +47,14 @@ ext {
tasks.register("testConsoleLauncher", JavaExec) {
outputs.dir(agentOutput)
classpath = test.classpath
- main = "org.junit.platform.console.ConsoleLauncher"
+ mainClass = "org.junit.platform.console.ConsoleLauncher"
args = ["--scan-class-path"]
doFirst {
def agentOutputDir = agentOutput.get().asFile
if (agentOutputDir.exists()) {
- GFileUtils.deleteDirectory(agentOutputDir)
+ delete {
+ delete(agentOutputDir)
+ }
}
if (project.hasProperty("agent")) {
mkdir "${agentOutputDir}"
@@ -99,8 +98,7 @@ abstract class NativeTestArgumentProvider implements CommandLineArgumentProvider
"-cp", classpath.asPath,
"--no-fallback",
"--features=org.graalvm.junit.platform.JUnitPlatformFeature",
- "-H:Name=native-image-tests",
- "-H:Class=org.graalvm.junit.platform.NativeImageJUnitLauncher",
+ "-o", "native-image-tests",
"-Djunit.platform.listeners.uid.tracking.output.dir=${testIdsDir.get().asFile.absolutePath}"
]
if (agentOutputDir.isPresent()) {
@@ -111,12 +109,15 @@ abstract class NativeTestArgumentProvider implements CommandLineArgumentProvider
}
args << "-H:ConfigurationFileDirectories=${outputDir.absolutePath}/agentOutput"
- args << "-H:+AllowIncompleteClasspath"
}
if (discovery.get()) {
args << "-DtestDiscovery"
}
+
+ // Main class comes last
+ args << "org.graalvm.junit.platform.NativeImageJUnitLauncher"
+
args.collect { it.toString() }
}
}
@@ -136,7 +137,7 @@ tasks.register("nativeTestCompile", Exec) {
argsProvider.classpath.from(test.classpath)
argsProvider.testIdsDir.set(testIdsDir)
argsProvider.agentOutputDir.set(agentOutput)
- argsProvider.discovery.set(providers.systemProperty("testDiscovery").forUseAtConfigurationTime().map(v -> Boolean.valueOf(v)).orElse(false))
+ argsProvider.discovery.set(providers.systemProperty("testDiscovery").map(v -> Boolean.valueOf(v)).orElse(false))
argumentProviders.add(argsProvider)
}
diff --git a/common/graalvm-reachability-metadata/settings.gradle.kts b/common/graalvm-reachability-metadata/settings.gradle.kts
index 19ce0fb57..1f8815e47 100644
--- a/common/graalvm-reachability-metadata/settings.gradle.kts
+++ b/common/graalvm-reachability-metadata/settings.gradle.kts
@@ -42,6 +42,7 @@
pluginManagement {
includeBuild("../../build-logic/settings-plugins")
includeBuild("../../build-logic/common-plugins")
+ includeBuild("../../build-logic/reachability-plugins")
}
plugins {
diff --git a/common/graalvm-reachability-metadata/src/main/java/org/graalvm/reachability/DirectoryConfiguration.java b/common/graalvm-reachability-metadata/src/main/java/org/graalvm/reachability/DirectoryConfiguration.java
index db3f824f0..3a5e94e1c 100644
--- a/common/graalvm-reachability-metadata/src/main/java/org/graalvm/reachability/DirectoryConfiguration.java
+++ b/common/graalvm-reachability-metadata/src/main/java/org/graalvm/reachability/DirectoryConfiguration.java
@@ -40,15 +40,34 @@
*/
package org.graalvm.reachability;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.Collection;
public class DirectoryConfiguration {
+ private static final String PROPERTIES = "reachability-metadata.properties";
+
+ private final String groupId;
+
+ private final String artifactId;
+
+ private final String version;
+
private final Path directory;
private final boolean override;
- public DirectoryConfiguration(Path directory, boolean override) {
+ public DirectoryConfiguration(String groupId, String artifactId, String version, Path directory, boolean override) {
+ this.groupId = groupId;
+ this.artifactId = artifactId;
+ this.version = version;
this.directory = directory;
this.override = override;
}
@@ -60,4 +79,65 @@ public Path getDirectory() {
public boolean isOverride() {
return override;
}
+
+ public String getGroupId() {
+ return groupId;
+ }
+
+ public String getArtifactId() {
+ return artifactId;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public static void copy(Collection configurations, Path destinationDirectory) throws IOException {
+ Path nativeImageDestination = destinationDirectory.resolve("META-INF").resolve("native-image");
+ for (DirectoryConfiguration configuration : configurations) {
+ Path target = nativeImageDestination
+ .resolve(configuration.groupId)
+ .resolve(configuration.artifactId)
+ .resolve((configuration.version != null) ? configuration.version :
+ configuration.getDirectory().getFileName().toString());
+ copyFileTree(configuration.directory, target);
+ writeConfigurationProperties(configuration, target);
+ }
+ }
+
+ private static void copyFileTree(Path source, Path target) throws IOException {
+ if (Files.notExists(source)) {
+ return;
+ }
+ Files.walkFileTree(source, new SimpleFileVisitor() {
+
+ @Override
+ public FileVisitResult preVisitDirectory(Path directory, BasicFileAttributes attrs) throws IOException {
+ Files.createDirectories(target.resolve(source.relativize(directory)));
+ return FileVisitResult.CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+ if (!"index.json".equalsIgnoreCase(file.getFileName().toString())) {
+ Files.copy(file, target.resolve(source.relativize(file)), StandardCopyOption.REPLACE_EXISTING);
+ }
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ }
+
+ private static void writeConfigurationProperties(DirectoryConfiguration configuration, Path target)
+ throws IOException {
+ if (Files.notExists(target)) {
+ Files.createDirectories(target);
+ }
+ StringBuilder content = new StringBuilder();
+ if (configuration.isOverride()) {
+ content.append("override=true\n");
+ }
+ if (content.length() > 0) {
+ Files.write(target.resolve(PROPERTIES), content.toString().getBytes(StandardCharsets.ISO_8859_1));
+ }
+ }
}
diff --git a/common/graalvm-reachability-metadata/src/main/java/org/graalvm/reachability/internal/FileSystemRepository.java b/common/graalvm-reachability-metadata/src/main/java/org/graalvm/reachability/internal/FileSystemRepository.java
index 51b04d051..e1d645a75 100644
--- a/common/graalvm-reachability-metadata/src/main/java/org/graalvm/reachability/internal/FileSystemRepository.java
+++ b/common/graalvm-reachability-metadata/src/main/java/org/graalvm/reachability/internal/FileSystemRepository.java
@@ -112,7 +112,7 @@ public Set findConfigurationsFor(Consumer super Query>
Optional configuration = index.findConfiguration(groupId, artifactId, version);
if (!configuration.isPresent() && artifactQuery.isUseLatestVersion()) {
logger.log(groupId, artifactId, version, "Configuration directory not found. Trying latest version.");
- configuration = index.findLatestConfigurationFor(groupId, artifactId);
+ configuration = index.findLatestConfigurationFor(groupId, artifactId, version);
if (!configuration.isPresent()) {
logger.log(groupId, artifactId, version, "Latest version not found!");
}
diff --git a/common/graalvm-reachability-metadata/src/main/java/org/graalvm/reachability/internal/index/artifacts/Artifact.java b/common/graalvm-reachability-metadata/src/main/java/org/graalvm/reachability/internal/index/artifacts/Artifact.java
index 248f11f9e..1409293e5 100644
--- a/common/graalvm-reachability-metadata/src/main/java/org/graalvm/reachability/internal/index/artifacts/Artifact.java
+++ b/common/graalvm-reachability-metadata/src/main/java/org/graalvm/reachability/internal/index/artifacts/Artifact.java
@@ -40,31 +40,25 @@
*/
package org.graalvm.reachability.internal.index.artifacts;
-import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
-import com.fasterxml.jackson.annotation.JsonProperty;
-
import java.util.Set;
+import java.util.regex.Pattern;
-@JsonIgnoreProperties(ignoreUnknown = true)
public class Artifact {
private final String module;
private final Set versions;
private final String directory;
private final boolean latest;
private final boolean override;
+ private final Pattern defaultForPattern;
- @JsonCreator
- public Artifact(@JsonProperty("module") String module,
- @JsonProperty("tested-versions") Set versions,
- @JsonProperty("metadata-version") String directory,
- @JsonProperty(value = "latest", defaultValue = "false") boolean latest,
- @JsonProperty(value = "override", defaultValue = "false") boolean override) {
+ public Artifact(String module, Set versions, String directory,
+ boolean latest, boolean override, String defaultFor) {
this.module = module;
this.versions = versions;
this.directory = directory;
this.latest = latest;
this.override = override;
+ this.defaultForPattern = (defaultFor == null ? null : Pattern.compile(defaultFor));
}
public String getModule() {
@@ -86,4 +80,8 @@ public boolean isLatest() {
public boolean isOverride() {
return override;
}
+
+ public boolean isDefaultFor(String version) {
+ return defaultForPattern != null && defaultForPattern.matcher(version).matches();
+ }
}
diff --git a/common/graalvm-reachability-metadata/src/main/java/org/graalvm/reachability/internal/index/artifacts/SingleModuleJsonVersionToConfigDirectoryIndex.java b/common/graalvm-reachability-metadata/src/main/java/org/graalvm/reachability/internal/index/artifacts/SingleModuleJsonVersionToConfigDirectoryIndex.java
index 5a27bd3d9..cd0be44d7 100644
--- a/common/graalvm-reachability-metadata/src/main/java/org/graalvm/reachability/internal/index/artifacts/SingleModuleJsonVersionToConfigDirectoryIndex.java
+++ b/common/graalvm-reachability-metadata/src/main/java/org/graalvm/reachability/internal/index/artifacts/SingleModuleJsonVersionToConfigDirectoryIndex.java
@@ -40,18 +40,20 @@
*/
package org.graalvm.reachability.internal.index.artifacts;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.type.TypeFactory;
+import com.github.openjson.JSONArray;
+import com.github.openjson.JSONObject;
import org.graalvm.reachability.DirectoryConfiguration;
import org.graalvm.reachability.internal.UncheckedIOException;
-import java.io.BufferedReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
+import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@@ -66,13 +68,13 @@ public SingleModuleJsonVersionToConfigDirectoryIndex(Path moduleRoot) {
private Map> parseIndexFile(Path rootPath) {
Path indexFile = rootPath.resolve("index.json");
- ObjectMapper objectMapper = new ObjectMapper();
- TypeFactory typeFactory = objectMapper.getTypeFactory();
- try (BufferedReader reader = Files.newBufferedReader(indexFile)) {
- List entries = objectMapper.readValue(
- reader,
- typeFactory.constructCollectionType(List.class, Artifact.class)
- );
+ try {
+ String fileContent = Files.readString(indexFile);
+ JSONArray json = new JSONArray(fileContent);
+ List entries = new ArrayList<>();
+ for (int i = 0; i < json.length(); i++) {
+ entries.add(fromJson(json.getJSONObject(i)));
+ }
return entries.stream()
.collect(Collectors.groupingBy(Artifact::getModule));
} catch (IOException e) {
@@ -91,22 +93,34 @@ private Map> parseIndexFile(Path rootPath) {
*/
@Override
public Optional findConfiguration(String groupId, String artifactId, String version) {
- return findConfigurationFor(groupId, artifactId, artifact -> artifact.getVersions().contains(version));
+ Optional exactMatch = findConfigurationFor(groupId, artifactId, version, artifact -> artifact.getVersions().contains(version));
+ return exactMatch.isPresent() ? exactMatch :
+ findConfigurationFor(groupId, artifactId, version, artifact -> artifact.isDefaultFor(version));
+ }
+
+ @Override
+ @Deprecated
+ public Optional findLatestConfigurationFor(String groupId, String artifactId) {
+ return findConfigurationFor(groupId, artifactId, null, Artifact::isLatest);
}
/**
- * Returns the latest configuration directory for the requested artifact.
+ * Returns the matching configuration directory for the requested artifact.
*
* @param groupId the group ID of the artifact
* @param artifactId the artifact ID of the artifact
+ * @param version the version of the artifact
* @return a configuration directory, or empty if no configuration directory is available
*/
@Override
- public Optional findLatestConfigurationFor(String groupId, String artifactId) {
- return findConfigurationFor(groupId, artifactId, Artifact::isLatest);
+ public Optional findLatestConfigurationFor(String groupId, String artifactId, String version) {
+ Optional defaultMatch = findConfigurationFor(groupId, artifactId, version, artifact -> artifact.isDefaultFor(version));
+ return defaultMatch.isPresent() ? defaultMatch :
+ findConfigurationFor(groupId, artifactId, version, Artifact::isLatest);
}
- private Optional findConfigurationFor(String groupId, String artifactId, Predicate super Artifact> predicate) {
+ private Optional findConfigurationFor(String groupId, String artifactId, String version,
+ Predicate super Artifact> predicate) {
String module = groupId + ":" + artifactId;
List artifacts = index.get(module);
if (artifacts == null) {
@@ -116,7 +130,28 @@ private Optional findConfigurationFor(String groupId, St
.filter(artifact -> artifact.getModule().equals(module))
.filter(predicate)
.findFirst()
- .map(artifact -> new DirectoryConfiguration(moduleRoot.resolve(artifact.getDirectory()), artifact.isOverride()));
+ .map(artifact -> new DirectoryConfiguration(groupId, artifactId, version,
+ moduleRoot.resolve(artifact.getDirectory()), artifact.isOverride()));
+ }
+
+ private Artifact fromJson(JSONObject json) {
+ String module = json.optString("module", null);
+ Set testVersions = readTestedVersions(json.optJSONArray("tested-versions"));
+ String directory = json.optString("metadata-version", null);
+ boolean latest = json.optBoolean("latest");
+ boolean override = json.optBoolean("override");
+ String defaultFor = json.optString("default-for", null);
+ return new Artifact(module, testVersions, directory, latest, override, defaultFor);
+ }
+
+ private Set readTestedVersions(JSONArray array) {
+ Set testVersions = new LinkedHashSet<>();
+ if (array != null) {
+ for (int i = 0; i < array.length(); i++) {
+ testVersions.add(array.getString(i));
+ }
+ }
+ return testVersions;
}
}
diff --git a/common/graalvm-reachability-metadata/src/main/java/org/graalvm/reachability/internal/index/artifacts/VersionToConfigDirectoryIndex.java b/common/graalvm-reachability-metadata/src/main/java/org/graalvm/reachability/internal/index/artifacts/VersionToConfigDirectoryIndex.java
index c93512d1e..6f7d96113 100644
--- a/common/graalvm-reachability-metadata/src/main/java/org/graalvm/reachability/internal/index/artifacts/VersionToConfigDirectoryIndex.java
+++ b/common/graalvm-reachability-metadata/src/main/java/org/graalvm/reachability/internal/index/artifacts/VersionToConfigDirectoryIndex.java
@@ -60,6 +60,17 @@ public interface VersionToConfigDirectoryIndex {
* @param groupId the group ID of the artifact
* @param artifactId the artifact ID of the artifact
* @return a configuration, or empty if no configuration directory is available
+ * @deprecated in favor of {@link #findLatestConfigurationFor(String, String, String)}
*/
+ @Deprecated
Optional findLatestConfigurationFor(String groupId, String artifactId);
+
+ /**
+ * Returns the latest configuration for the requested artifact.
+ * @param groupId the group ID of the artifact
+ * @param artifactId the artifact ID of the artifact
+ * @param version the version of the artifact
+ * @return a configuration, or empty if no configuration directory is available
+ */
+ Optional findLatestConfigurationFor(String groupId, String artifactId, String version);
}
diff --git a/common/graalvm-reachability-metadata/src/main/java/org/graalvm/reachability/internal/index/modules/JsonModuleToConfigDirectoryIndex.java b/common/graalvm-reachability-metadata/src/main/java/org/graalvm/reachability/internal/index/modules/JsonModuleToConfigDirectoryIndex.java
index c072de033..a58532230 100644
--- a/common/graalvm-reachability-metadata/src/main/java/org/graalvm/reachability/internal/index/modules/JsonModuleToConfigDirectoryIndex.java
+++ b/common/graalvm-reachability-metadata/src/main/java/org/graalvm/reachability/internal/index/modules/JsonModuleToConfigDirectoryIndex.java
@@ -40,14 +40,14 @@
*/
package org.graalvm.reachability.internal.index.modules;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.type.TypeFactory;
+import com.github.openjson.JSONArray;
+import com.github.openjson.JSONObject;
import org.graalvm.reachability.internal.UncheckedIOException;
-import java.io.BufferedReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -68,13 +68,13 @@ public JsonModuleToConfigDirectoryIndex(Path rootPath) {
private Map> parseIndexFile(Path rootPath) {
Path indexFile = rootPath.resolve("index.json");
- ObjectMapper objectMapper = new ObjectMapper();
- TypeFactory typeFactory = objectMapper.getTypeFactory();
- try (BufferedReader reader = Files.newBufferedReader(indexFile)) {
- List entries = objectMapper.readValue(
- reader,
- typeFactory.constructCollectionType(List.class, ModuleEntry.class)
- );
+ try {
+ String fileContent = Files.readString(indexFile);
+ JSONArray json = new JSONArray(fileContent);
+ List entries = new ArrayList<>();
+ for (int i = 0; i < json.length(); i++) {
+ entries.add(fromJson(json.getJSONObject(i)));
+ }
Map> moduleToEntries = entries.stream()
.collect(Collectors.groupingBy(ModuleEntry::getModule));
Map> index = new HashMap<>(moduleToEntries.size());
@@ -104,6 +104,23 @@ private Map> parseIndexFile(Path rootPath) {
}
+ private ModuleEntry fromJson(JSONObject json) {
+ String module = json.optString("module", null);
+ String moduleDirectory = json.optString("directory", null);
+ List requires = readRequires(json.optJSONArray("requires"));
+ return new ModuleEntry(module, moduleDirectory, requires);
+ }
+
+ private List readRequires(JSONArray array) {
+ List requires = new ArrayList<>();
+ if (array != null) {
+ for (int i = 0; i < array.length(); i++) {
+ requires.add(array.getString(i));
+ }
+ }
+ return requires;
+ }
+
/**
* Returns the directory containing the candidate configurations for the given module.
*
diff --git a/common/graalvm-reachability-metadata/src/main/java/org/graalvm/reachability/internal/index/modules/ModuleEntry.java b/common/graalvm-reachability-metadata/src/main/java/org/graalvm/reachability/internal/index/modules/ModuleEntry.java
index 7093eae97..fdcb58f75 100644
--- a/common/graalvm-reachability-metadata/src/main/java/org/graalvm/reachability/internal/index/modules/ModuleEntry.java
+++ b/common/graalvm-reachability-metadata/src/main/java/org/graalvm/reachability/internal/index/modules/ModuleEntry.java
@@ -40,23 +40,15 @@
*/
package org.graalvm.reachability.internal.index.modules;
-import com.fasterxml.jackson.annotation.JsonCreator;
-import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
-import com.fasterxml.jackson.annotation.JsonProperty;
-
import java.util.Collections;
import java.util.List;
-@JsonIgnoreProperties(ignoreUnknown = true)
public class ModuleEntry {
private final String module;
private final String moduleDirectory;
private final List requires;
- @JsonCreator
- public ModuleEntry(@JsonProperty("module") String module,
- @JsonProperty("directory") String moduleDirectory,
- @JsonProperty("requires") List requires) {
+ public ModuleEntry(String module, String moduleDirectory, List requires) {
this.module = module;
this.moduleDirectory = moduleDirectory;
this.requires = requires == null ? Collections.emptyList() : requires;
diff --git a/common/graalvm-reachability-metadata/src/test/java/org/graalvm/reachability/DirectoryConfigurationTest.java b/common/graalvm-reachability-metadata/src/test/java/org/graalvm/reachability/DirectoryConfigurationTest.java
new file mode 100644
index 000000000..2b5c4d0cc
--- /dev/null
+++ b/common/graalvm-reachability-metadata/src/test/java/org/graalvm/reachability/DirectoryConfigurationTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2020, 2022 Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * The Universal Permissive License (UPL), Version 1.0
+ *
+ * Subject to the condition set forth below, permission is hereby granted to any
+ * person obtaining a copy of this software, associated documentation and/or
+ * data (collectively the "Software"), free of charge and under any and all
+ * copyright rights in the Software, and any and all patent rights owned or
+ * freely licensable by each licensor hereunder covering either (i) the
+ * unmodified Software as contributed to or provided by such licensor, or (ii)
+ * the Larger Works (as defined below), to deal in both
+ *
+ * (a) the Software, and
+ *
+ * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+ * one is included with the Software each a "Larger Work" to which the Software
+ * is contributed by such licensors),
+ *
+ * without restriction, including without limitation the rights to copy, create
+ * derivative works of, display, perform, and distribute the Software and make,
+ * use, sell, offer for sale, import, export, have made, and have sold the
+ * Software and the Larger Work(s), and to sublicense the foregoing rights on
+ * either these or other terms.
+ *
+ * This license is subject to the following condition:
+ *
+ * The above copyright notice and either this complete permission notice or at a
+ * minimum a reference to the UPL must be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package org.graalvm.reachability;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.ByteArrayInputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.Properties;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+class DirectoryConfigurationTest {
+
+ @TempDir
+ Path temp;
+
+ @Test
+ void copyCopiesFiles() throws IOException {
+ Path directory = temp.resolve("source/com.example.group/artifact/123");
+ Path target = temp.resolve("target");
+ createJsonFiles(directory);
+ DirectoryConfiguration configuration = new DirectoryConfiguration("com.example.group", "artifact", "123", directory, false);
+ DirectoryConfiguration.copy(Arrays.asList(configuration), target);
+ assertTrue(Files.exists(target.resolve("META-INF/native-image/com.example.group/artifact/123/reflect-config.json")));
+ assertTrue(Files.exists(target.resolve("META-INF/native-image/com.example.group/artifact/123/other.json")));
+ assertFalse(Files.exists(target.resolve("META-INF/native-image/com.example.group/artifact/123/index.json")));
+ assertFalse(Files.exists(target.resolve("META-INF/native-image/com.example.group/artifact/123/reachability-metadata.properties")));
+ }
+
+ @Test
+ void copyWhenHasOverrideGeneratesMetadataRepositoryProperties() throws IOException {
+ Path directory = temp.resolve("source/com.example.group/artifact/123");
+ Path target = temp.resolve("target");
+ createJsonFiles(directory);
+ DirectoryConfiguration configuration = new DirectoryConfiguration("com.example.group", "artifact", "123", directory, true);
+ DirectoryConfiguration.copy(Arrays.asList(configuration), target);
+ Path propertiesFile = target.resolve("META-INF/native-image/com.example.group/artifact/123/reachability-metadata.properties");
+ Properties properties = new Properties();
+ properties.load(new FileInputStream(propertiesFile.toFile()));
+ assertEquals("true", properties.getProperty("override"));
+ }
+
+ private void createJsonFiles(Path directory) throws IOException {
+ Files.createDirectories(directory);
+ byte[] json = "{}".getBytes();
+ Files.copy(new ByteArrayInputStream(json), directory.resolve("index.json"));
+ Files.copy(new ByteArrayInputStream(json), directory.resolve("reflect-config.json"));
+ Files.copy(new ByteArrayInputStream(json), directory.resolve("other.json"));
+ }
+
+}
diff --git a/common/graalvm-reachability-metadata/src/test/java/org/graalvm/reachability/internal/index/artifacts/SingleModuleJsonVersionToConfigDirectoryIndexTest.java b/common/graalvm-reachability-metadata/src/test/java/org/graalvm/reachability/internal/index/artifacts/SingleModuleJsonVersionToConfigDirectoryIndexTest.java
index 9198f5c33..de42eaf2f 100644
--- a/common/graalvm-reachability-metadata/src/test/java/org/graalvm/reachability/internal/index/artifacts/SingleModuleJsonVersionToConfigDirectoryIndexTest.java
+++ b/common/graalvm-reachability-metadata/src/test/java/org/graalvm/reachability/internal/index/artifacts/SingleModuleJsonVersionToConfigDirectoryIndexTest.java
@@ -88,13 +88,54 @@ void checkIndex() throws URISyntaxException {
config = index.findConfiguration("com.foo", "nope", "1.0");
assertFalse(config.isPresent());
- Optional latest = index.findLatestConfigurationFor("com.foo", "bar");
+ Optional latest = index.findLatestConfigurationFor("com.foo", "bar", "123");
assertTrue(latest.isPresent());
assertEquals(repoPath.resolve("2.0"), latest.get().getDirectory());
assertTrue(latest.get().isOverride());
}
+ @Test
+ void checkIndexWithDefaultFor() throws URISyntaxException {
+ withIndex("artifact-2");
+
+ Optional config = index.findConfiguration("com.foo", "bar", "1.0");
+ assertTrue(config.isPresent());
+ assertEquals(repoPath.resolve("1.0"), config.get().getDirectory());
+
+ config = index.findConfiguration("com.foo", "bar", "1.1");
+ assertTrue(config.isPresent());
+ assertEquals(repoPath.resolve("1.0"), config.get().getDirectory());
+
+ config = index.findConfiguration("com.foo", "bar", "2.0");
+ assertTrue(config.isPresent());
+ assertEquals(repoPath.resolve("2.0"), config.get().getDirectory());
+
+ config = index.findConfiguration("com.foo", "bar", "2.1");
+ assertTrue(config.isPresent());
+ assertEquals(repoPath.resolve("2.0"), config.get().getDirectory());
+
+ config = index.findConfiguration("com.foo", "baz", "1.1");
+ assertTrue(config.isPresent());
+ assertEquals(repoPath.resolve("1.0"), config.get().getDirectory());
+
+ Optional latest = index.findLatestConfigurationFor("com.foo", "bar", "123");
+ assertTrue(latest.isPresent());
+ assertEquals(repoPath.resolve("2.0"), latest.get().getDirectory());
+
+ latest = index.findLatestConfigurationFor("com.foo", "bar", "123");
+ assertTrue(latest.isPresent());
+ assertEquals(repoPath.resolve("2.0"), latest.get().getDirectory());
+
+ latest = index.findLatestConfigurationFor("com.foo", "bar", "1.0");
+ assertTrue(latest.isPresent());
+ assertEquals(repoPath.resolve("1.0"), latest.get().getDirectory());
+
+ latest = index.findLatestConfigurationFor("com.foo", "bar", "2.0");
+ assertTrue(latest.isPresent());
+ assertEquals(repoPath.resolve("2.0"), latest.get().getDirectory());
+ }
+
private void withIndex(String json) throws URISyntaxException {
repoPath = new File(SingleModuleJsonVersionToConfigDirectoryIndexTest.class.getResource("/json/" + json).toURI()).toPath();
index = new SingleModuleJsonVersionToConfigDirectoryIndex(repoPath);
diff --git a/common/graalvm-reachability-metadata/src/test/resources/json/artifact-2/index.json b/common/graalvm-reachability-metadata/src/test/resources/json/artifact-2/index.json
new file mode 100644
index 000000000..53b35b5cc
--- /dev/null
+++ b/common/graalvm-reachability-metadata/src/test/resources/json/artifact-2/index.json
@@ -0,0 +1,5 @@
+[
+ { "module": "com.foo:bar", "tested-versions": ["1.0"], "metadata-version": "1.0", "default-for": "1\\..*" },
+ { "module": "com.foo:bar", "tested-versions": ["2.0", "2.1"], "metadata-version": "2.0", "latest": true },
+ { "module": "com.foo:baz", "tested-versions": ["1.0"], "metadata-version": "1.0", "default-for": "1.*" }
+]
diff --git a/common/junit-platform-native/build.gradle b/common/junit-platform-native/build.gradle
index 0c04e86fe..e1883575e 100644
--- a/common/junit-platform-native/build.gradle
+++ b/common/junit-platform-native/build.gradle
@@ -38,9 +38,6 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-
-import org.gradle.util.GFileUtils
-
plugins {
id 'java-library'
id 'checkstyle'
@@ -55,18 +52,26 @@ maven {
}
dependencies {
compileOnly libs.graalvm.svm
- implementation(platform(libs.test.junit.bom))
- implementation libs.test.junit.platform.console
- implementation libs.test.junit.platform.launcher
- implementation libs.test.junit.jupiter.core
+ compileOnly(platform(libs.test.junit.bom))
+ compileOnly libs.test.junit.platform.console
+ compileOnly libs.test.junit.platform.launcher
+ compileOnly libs.test.junit.jupiter.core
testImplementation libs.test.junit.vintage
+
+ testImplementation libs.test.junit.jupiter.core
+
+ testRuntimeOnly(platform(libs.test.junit.bom))
+ testCompileOnly(platform(libs.test.junit.bom))
+ testRuntimeOnly libs.test.junit.platform.console
+ testRuntimeOnly libs.test.junit.platform.launcher
+ testRuntimeOnly libs.test.junit.jupiter.core
}
apply from: "gradle/native-image-testing.gradle"
-test {
+tasks.named('test') {
doFirst {
- GFileUtils.deleteDirectory(testIdsDir.get().asFile)
+ delete { delete testIdsDir.get().asFile }
}
useJUnitPlatform() {
systemProperty('junit.platform.listeners.uid.tracking.enabled', true)
diff --git a/common/junit-platform-native/gradle/native-image-testing.gradle b/common/junit-platform-native/gradle/native-image-testing.gradle
index b7de78a52..814daef49 100644
--- a/common/junit-platform-native/gradle/native-image-testing.gradle
+++ b/common/junit-platform-native/gradle/native-image-testing.gradle
@@ -39,8 +39,6 @@
* SOFTWARE.
*/
-import org.gradle.util.GFileUtils
-
def agentOutput = layout.buildDirectory.dir("agent")
ext {
@@ -55,7 +53,7 @@ tasks.register("testConsoleLauncher", JavaExec) {
doFirst {
def agentOutputDir = agentOutput.get().asFile
if (agentOutputDir.exists()) {
- GFileUtils.deleteDirectory(agentOutputDir)
+ delete { delete agentOutputDir }
}
if (project.hasProperty("agent")) {
mkdir "${agentOutputDir}"
@@ -93,16 +91,29 @@ abstract class NativeTestArgumentProvider implements CommandLineArgumentProvider
@Input
abstract Property getDiscovery()
+ @InputDirectory
+ abstract DirectoryProperty getExcludeConfigDir()
+
+ private List addExcludeConfig() {
+ String[] jarPatternPair = getExcludeConfigDir().get().file("exclude-config").getAsFile().readLines().get(0).split(",")
+ var jar = jarPatternPair[0]
+ var pattern = jarPatternPair[1]
+
+ return ["--exclude-config", jar, pattern]
+ }
+
@Override
Iterable asArguments() {
def args = [
"-cp", classpath.asPath,
"--no-fallback",
"--features=org.graalvm.junit.platform.JUnitPlatformFeature",
- "-H:Name=native-image-tests",
- "-H:Class=org.graalvm.junit.platform.NativeImageJUnitLauncher",
+ "-o", "native-image-tests",
"-Djunit.platform.listeners.uid.tracking.output.dir=${testIdsDir.get().asFile.absolutePath}"
]
+
+ args.addAll(addExcludeConfig())
+
if (agentOutputDir.isPresent()) {
def outputDir = agentOutputDir.get().asFile
if (!outputDir.exists()) {
@@ -111,12 +122,15 @@ abstract class NativeTestArgumentProvider implements CommandLineArgumentProvider
}
args << "-H:ConfigurationFileDirectories=${outputDir.absolutePath}/agentOutput"
- args << "-H:+AllowIncompleteClasspath"
}
if (discovery.get()) {
args << "-DtestDiscovery"
}
+
+ // Main class comes last
+ args << "org.graalvm.junit.platform.NativeImageJUnitLauncher"
+
args.collect { it.toString() }
}
}
@@ -135,8 +149,9 @@ tasks.register("nativeTestCompile", Exec) {
def argsProvider = objects.newInstance(NativeTestArgumentProvider)
argsProvider.classpath.from(test.classpath)
argsProvider.testIdsDir.set(testIdsDir)
+ argsProvider.excludeConfigDir.set(layout.buildDirectory.dir("resources").get().dir("main").dir("extra-build-args"))
argsProvider.agentOutputDir.set(agentOutput)
- argsProvider.discovery.set(providers.systemProperty("testDiscovery").forUseAtConfigurationTime().map(v -> Boolean.valueOf(v)).orElse(false))
+ argsProvider.discovery.set(providers.systemProperty("testDiscovery").map(v -> Boolean.valueOf(v)).orElse(false))
argumentProviders.add(argsProvider)
}
@@ -144,4 +159,5 @@ tasks.register("nativeTest", Exec) {
dependsOn nativeTestCompile
workingDir = "${buildDir}"
executable = "${buildDir}/native-image-tests"
+ args = ["-Djunit.platform.listeners.uid.tracking.output.dir=${testIdsDir.get().asFile.absolutePath}"]
}
diff --git a/common/junit-platform-native/src/main/java/org/graalvm/junit/platform/JUnitPlatformFeature.java b/common/junit-platform-native/src/main/java/org/graalvm/junit/platform/JUnitPlatformFeature.java
index e9fc8c155..757790bc5 100644
--- a/common/junit-platform-native/src/main/java/org/graalvm/junit/platform/JUnitPlatformFeature.java
+++ b/common/junit-platform-native/src/main/java/org/graalvm/junit/platform/JUnitPlatformFeature.java
@@ -45,6 +45,8 @@
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.hosted.Feature;
import org.graalvm.nativeimage.hosted.RuntimeClassInitialization;
+import org.graalvm.nativeimage.hosted.RuntimeReflection;
+
import org.junit.platform.engine.DiscoverySelector;
import org.junit.platform.engine.discovery.DiscoverySelectors;
import org.junit.platform.engine.discovery.UniqueIdSelector;
@@ -57,15 +59,17 @@
import org.junit.platform.launcher.core.LauncherFactory;
import org.junit.platform.launcher.listeners.UniqueIdTrackingListener;
+import java.io.BufferedReader;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.ServiceLoader;
+import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -74,10 +78,24 @@
public final class JUnitPlatformFeature implements Feature {
public final boolean debug = System.getProperty("debug") != null;
-
private static final NativeImageConfigurationImpl nativeImageConfigImpl = new NativeImageConfigurationImpl();
private final ServiceLoader extensionConfigProviders = ServiceLoader.load(PluginConfigProvider.class);
+ public static void debug(String format, Object... args) {
+ if (debug()) {
+ System.out.printf("[Debug] " + format + "%n", args);
+ }
+ }
+
+ @Override
+ public void afterRegistration(AfterRegistrationAccess access) {
+ extensionConfigProviders.forEach(p -> p.initialize(access.getApplicationClassLoader(), nativeImageConfigImpl));
+ }
+
+ private static boolean debug() {
+ return ImageSingletons.lookup(JUnitPlatformFeature.class).debug;
+ }
+
@Override
public void duringSetup(DuringSetupAccess access) {
forEachProvider(p -> p.onLoad(nativeImageConfigImpl));
@@ -85,55 +103,66 @@ public void duringSetup(DuringSetupAccess access) {
@Override
public void beforeAnalysis(BeforeAnalysisAccess access) {
- RuntimeClassInitialization.initializeAtBuildTime(NativeImageJUnitLauncher.class);
+ /* Before GraalVM version 22 we couldn't have classes initialized at run-time
+ * that are also used at build-time but not added to the image heap */
+ if (Runtime.version().feature() <= 21) {
+ initializeClassesForJDK21OrEarlier();
+ }
- List classpathRoots = access.getApplicationClassPath();
- List extends DiscoverySelector> selectors = getSelectors(classpathRoots);
+ List extends DiscoverySelector> selectors = getSelectors();
+ registerTestClassesForReflection(selectors);
- Launcher launcher = LauncherFactory.create();
- TestPlan testplan = discoverTestsAndRegisterTestClassesForReflection(launcher, selectors);
- ImageSingletons.add(NativeImageJUnitLauncher.class, new NativeImageJUnitLauncher(launcher, testplan));
+ /* support for JUnit Vintage */
+ registerClassesForHamcrestSupport(access);
}
- private List extends DiscoverySelector> getSelectors(List classpathRoots) {
+ private List extends DiscoverySelector> getSelectors() {
try {
- Path outputDir = Paths.get(System.getProperty(UniqueIdTrackingListener.OUTPUT_DIR_PROPERTY_NAME));
- String prefix = System.getProperty(UniqueIdTrackingListener.OUTPUT_FILE_PREFIX_PROPERTY_NAME,
+ String uniqueIdDirectoryProperty = System.getProperty(UniqueIdTrackingListener.OUTPUT_DIR_PROPERTY_NAME);
+ if (uniqueIdDirectoryProperty == null) {
+ throw new IllegalStateException("Cannot determine test-ids directory because junit.platform.listeners.uid.tracking.output.dir property is null");
+ }
+
+ String uniqueIdFilePrefix = System.getProperty(UniqueIdTrackingListener.OUTPUT_FILE_PREFIX_PROPERTY_NAME,
UniqueIdTrackingListener.DEFAULT_OUTPUT_FILE_PREFIX);
- List selectors = readAllFiles(outputDir, prefix)
+ if (uniqueIdFilePrefix == null) {
+ throw new IllegalStateException("Cannot determine unique test-ids prefix because junit.platform.listeners.uid.tracking.output.file.prefix property is null");
+ }
+
+ Path uniqueIdDirectory = Path.of(uniqueIdDirectoryProperty);
+ List selectors = readAllFiles(uniqueIdDirectory, uniqueIdFilePrefix)
.map(DiscoverySelectors::selectUniqueId)
.collect(Collectors.toList());
if (!selectors.isEmpty()) {
System.out.printf(
"[junit-platform-native] Running in 'test listener' mode using files matching pattern [%s*] "
+ "found in folder [%s] and its subfolders.%n",
- prefix, outputDir.toAbsolutePath());
+ uniqueIdFilePrefix, uniqueIdDirectory.toAbsolutePath());
return selectors;
}
} catch (Exception ex) {
debug("Failed to read UIDs from UniqueIdTrackingListener output files: " + ex.getMessage());
}
- System.out.println("[junit-platform-native] Running in 'test discovery' mode. Note that this is a fallback mode.");
- if (debug) {
- classpathRoots.forEach(entry -> debug("Selecting classpath root: " + entry));
- }
- return DiscoverySelectors.selectClasspathRoots(new HashSet<>(classpathRoots));
+ throw new RuntimeException("Cannot compute test selectors from test ids.");
}
/**
- * Use the JUnit Platform Launcher to discover tests and register classes
- * for reflection.
+ * Use the JUnit Platform Launcher to register classes for reflection.
*/
- private TestPlan discoverTestsAndRegisterTestClassesForReflection(Launcher launcher,
- List extends DiscoverySelector> selectors) {
-
+ private void registerTestClassesForReflection(List extends DiscoverySelector> selectors) {
LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request()
.selectors(selectors)
.build();
- TestPlan testPlan = launcher.discover(request);
+ TestClassRegistrar testClassRegistrar = new TestClassRegistrar(clazz -> {
+ debug("Registering test class for reflection: %s", clazz.getName());
+ nativeImageConfigImpl.registerAllClassMembersForReflection(clazz);
+ forEachProvider(p -> p.onTestClassRegistered(clazz, nativeImageConfigImpl));
+ });
+ Launcher launcher = LauncherFactory.create();
+ TestPlan testPlan = launcher.discover(request);
testPlan.getRoots().stream()
.flatMap(rootIdentifier -> testPlan.getDescendants(rootIdentifier).stream())
.map(TestIdentifier::getSource)
@@ -142,19 +171,7 @@ private TestPlan discoverTestsAndRegisterTestClassesForReflection(Launcher launc
.filter(ClassSource.class::isInstance)
.map(ClassSource.class::cast)
.map(ClassSource::getJavaClass)
- .forEach(this::registerTestClassForReflection);
-
- return testPlan;
- }
-
- private void registerTestClassForReflection(Class> clazz) {
- debug("Registering test class for reflection: %s", clazz.getName());
- nativeImageConfigImpl.registerAllClassMembersForReflection(clazz);
- forEachProvider(p -> p.onTestClassRegistered(clazz, nativeImageConfigImpl));
- Class> superClass = clazz.getSuperclass();
- if (superClass != null && superClass != Object.class) {
- registerTestClassForReflection(superClass);
- }
+ .forEach(testClassRegistrar::registerTestClassForReflection);
}
private void forEachProvider(Consumer consumer) {
@@ -163,16 +180,6 @@ private void forEachProvider(Consumer consumer) {
}
}
- public static void debug(String format, Object... args) {
- if (debug()) {
- System.out.printf("[Debug] " + format + "%n", args);
- }
- }
-
- public static boolean debug() {
- return ImageSingletons.lookup(JUnitPlatformFeature.class).debug;
- }
-
private Stream readAllFiles(Path dir, String prefix) throws IOException {
return findFiles(dir, prefix).map(outputFile -> {
try {
@@ -192,4 +199,44 @@ private static Stream findFiles(Path dir, String prefix) throws IOExceptio
&& path.getFileName().toString().startsWith(prefix)));
}
+ private static void registerClassesForHamcrestSupport(BeforeAnalysisAccess access) {
+ ClassLoader applicationLoader = access.getApplicationClassLoader();
+ Class> typeSafeMatcher = findClassOrNull(applicationLoader, "org.hamcrest.TypeSafeMatcher");
+ Class> typeSafeDiagnosingMatcher = findClassOrNull(applicationLoader, "org.hamcrest.TypeSafeDiagnosingMatcher");
+ if (typeSafeMatcher != null || typeSafeDiagnosingMatcher != null) {
+ BiConsumer> registerMatcherForReflection = (a, c) -> RuntimeReflection.register(c.getDeclaredMethods());
+ if (typeSafeMatcher != null) {
+ access.registerSubtypeReachabilityHandler(registerMatcherForReflection, typeSafeMatcher);
+ }
+ if (typeSafeDiagnosingMatcher != null) {
+ access.registerSubtypeReachabilityHandler(registerMatcherForReflection, typeSafeDiagnosingMatcher);
+ }
+ }
+ }
+
+ private static Class> findClassOrNull(ClassLoader loader, String className) {
+ try {
+ return loader.loadClass(className);
+ } catch (ClassNotFoundException e) {
+ return null;
+ }
+ }
+
+ private static void initializeClassesForJDK21OrEarlier() {
+ try (InputStream is = JUnitPlatformFeature.class.getResourceAsStream("/initialize-at-buildtime")) {
+ if (is != null) {
+ try (BufferedReader br = new BufferedReader(new InputStreamReader(is))) {
+ br.lines().forEach(cls -> {
+ try {
+ RuntimeClassInitialization.initializeAtBuildTime(cls);
+ } catch (NoClassDefFoundError e) {
+ // if users use older JUnit versions some classes might not be available
+ }
+ });
+ }
+ }
+ } catch (IOException e) {
+ throw new RuntimeException("Failed to process build time initializations for JDK 21 or earlier");
+ }
+ }
}
diff --git a/common/junit-platform-native/src/main/java/org/graalvm/junit/platform/NativeImageConfigurationImpl.java b/common/junit-platform-native/src/main/java/org/graalvm/junit/platform/NativeImageConfigurationImpl.java
index 3e0a440cd..6ffb424e0 100644
--- a/common/junit-platform-native/src/main/java/org/graalvm/junit/platform/NativeImageConfigurationImpl.java
+++ b/common/junit-platform-native/src/main/java/org/graalvm/junit/platform/NativeImageConfigurationImpl.java
@@ -42,7 +42,6 @@
package org.graalvm.junit.platform;
import org.graalvm.junit.platform.config.core.NativeImageConfiguration;
-import org.graalvm.nativeimage.hosted.RuntimeClassInitialization;
import org.graalvm.nativeimage.hosted.RuntimeReflection;
import java.lang.reflect.Executable;
@@ -64,22 +63,4 @@ public void registerForReflection(Executable... methods) {
public void registerForReflection(Field... fields) {
RuntimeReflection.register(fields);
}
-
- @Override
- public void initializeAtBuildTime(String... classNames) {
- for (String className : classNames) {
- Class> clazz;
- try {
- clazz = Class.forName(className);
- initializeAtBuildTime(clazz);
- } catch (ClassNotFoundException e) {
- JUnitPlatformFeature.debug("[Native Image Configuration] Failed to register class for build time initialization: %s Reason: %s", className, e);
- }
- }
- }
-
- @Override
- public void initializeAtBuildTime(Class>... classes) {
- RuntimeClassInitialization.initializeAtBuildTime(classes);
- }
}
diff --git a/common/junit-platform-native/src/main/java/org/graalvm/junit/platform/NativeImageJUnitLauncher.java b/common/junit-platform-native/src/main/java/org/graalvm/junit/platform/NativeImageJUnitLauncher.java
index c386fa1c5..4372adc55 100644
--- a/common/junit-platform-native/src/main/java/org/graalvm/junit/platform/NativeImageJUnitLauncher.java
+++ b/common/junit-platform-native/src/main/java/org/graalvm/junit/platform/NativeImageJUnitLauncher.java
@@ -42,38 +42,35 @@
package org.graalvm.junit.platform;
import org.graalvm.nativeimage.ImageInfo;
-import org.graalvm.nativeimage.ImageSingletons;
+import org.junit.platform.engine.DiscoverySelector;
+import org.junit.platform.engine.discovery.DiscoverySelectors;
+import org.junit.platform.engine.discovery.UniqueIdSelector;
import org.junit.platform.launcher.Launcher;
-import org.junit.platform.launcher.TestExecutionListener;
+import org.junit.platform.launcher.LauncherDiscoveryRequest;
import org.junit.platform.launcher.TestPlan;
+import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;
+import org.junit.platform.launcher.core.LauncherFactory;
import org.junit.platform.launcher.listeners.SummaryGeneratingListener;
import org.junit.platform.launcher.listeners.TestExecutionSummary;
+import org.junit.platform.launcher.listeners.UniqueIdTrackingListener;
import org.junit.platform.reporting.legacy.xml.LegacyXmlReportGeneratingListener;
+import java.io.File;
+import java.io.IOException;
import java.io.PrintWriter;
+import java.io.UncheckedIOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.LinkedList;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
public class NativeImageJUnitLauncher {
static final String DEFAULT_OUTPUT_FOLDER = Paths.get("test-results-native").resolve("test").toString();
- final Launcher launcher;
- final TestPlan testPlan;
-
- public NativeImageJUnitLauncher(Launcher launcher, TestPlan testPlan) {
- this.launcher = launcher;
- this.testPlan = testPlan;
- }
-
- public void registerTestExecutionListeners(TestExecutionListener testExecutionListener) {
- launcher.registerTestExecutionListeners(testExecutionListener);
- }
-
- public void execute() {
- launcher.execute(testPlan);
- }
-
static String stringPad(String input) {
return String.format("%1$-20s", input);
}
@@ -84,6 +81,7 @@ public static void main(String... args) {
System.exit(1);
}
+ /* scan runtime arguments */
String xmlOutput = DEFAULT_OUTPUT_FOLDER;
boolean silent = false;
@@ -113,20 +111,25 @@ public static void main(String... args) {
}
}
- PrintWriter out = new PrintWriter(System.out);
- NativeImageJUnitLauncher launcher = ImageSingletons.lookup(NativeImageJUnitLauncher.class);
+ if (xmlOutput == null) {
+ throw new RuntimeException("xml-output-dir argument passed incorrectly to the launcher class.");
+ }
+
+ Launcher launcher = LauncherFactory.create();
+ TestPlan testPlan = getTestPlan(launcher);
+ PrintWriter out = new PrintWriter(System.out);
if (!silent) {
out.println("JUnit Platform on Native Image - report");
out.println("----------------------------------------\n");
out.flush();
- launcher.registerTestExecutionListeners(new PrintTestExecutionListener(out));
+ configurePrintTestExecutionListener(launcher, out);
}
SummaryGeneratingListener summaryListener = new SummaryGeneratingListener();
launcher.registerTestExecutionListeners(summaryListener);
- launcher.registerTestExecutionListeners(new LegacyXmlReportGeneratingListener(Paths.get(xmlOutput), out));
- launcher.execute();
+ configureLegacyXMLReport(launcher, xmlOutput, out);
+ launcher.execute(testPlan);
TestExecutionSummary summary = summaryListener.getSummary();
if (!silent) {
@@ -137,4 +140,152 @@ public static void main(String... args) {
long failedCount = summary.getTotalFailureCount();
System.exit(failedCount > 0 ? 1 : 0);
}
+
+ private static TestPlan getTestPlan(Launcher launcher) {
+ List extends DiscoverySelector> selectors = getSelectors();
+ LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request()
+ .selectors(selectors)
+ .build();
+
+ return launcher.discover(request);
+ }
+
+ private static List extends DiscoverySelector> getSelectors() {
+ try {
+ String systemPropertyBasedLocation = System.getProperty(UniqueIdTrackingListener.OUTPUT_DIR_PROPERTY_NAME);
+ Path uniqueIdDirectory = systemPropertyBasedLocation != null ? Path.of(systemPropertyBasedLocation) : getTestIDsFromDefaultLocations();
+
+ String uniqueIdFilePrefix = System.getProperty(UniqueIdTrackingListener.OUTPUT_FILE_PREFIX_PROPERTY_NAME,
+ UniqueIdTrackingListener.DEFAULT_OUTPUT_FILE_PREFIX);
+ if (uniqueIdFilePrefix == null) {
+ throw new RuntimeException("Test-ids unique id file prefix not provided to the NativeImageJUnitLauncher.");
+ }
+
+ List selectors = readAllFiles(uniqueIdDirectory, uniqueIdFilePrefix)
+ .map(DiscoverySelectors::selectUniqueId)
+ .collect(Collectors.toList());
+ if (!selectors.isEmpty()) {
+ System.out.printf(
+ "[junit-platform-native] Running in 'test listener' mode using files matching pattern [%s*] "
+ + "found in folder [%s] and its subfolders.%n",
+ uniqueIdFilePrefix, uniqueIdDirectory.toAbsolutePath());
+ return selectors;
+ }
+ } catch (Exception ex) {
+ throw new RuntimeException("Failed to read UIDs from UniqueIdTrackingListener output files: " + ex.getMessage());
+ }
+
+ throw new RuntimeException("Cannot compute test selectors from test ids.");
+ }
+
+ private static Stream readAllFiles(Path dir, String prefix) throws IOException {
+ return findFiles(dir, prefix).flatMap(outputFile -> {
+ try {
+ return Files.readAllLines(outputFile).stream();
+ } catch (IOException ex) {
+ throw new UncheckedIOException(ex);
+ }
+ });
+ }
+
+ private static Stream findFiles(Path dir, String prefix) throws IOException {
+ if (!Files.exists(dir)) {
+ return Stream.empty();
+ }
+ return Files.find(dir, Integer.MAX_VALUE,
+ (path, basicFileAttributes) -> (basicFileAttributes.isRegularFile()
+ && path.getFileName().toString().startsWith(prefix)));
+ }
+
+ private static Path getTestIDsFromDefaultLocations() {
+ System.out.println("[junit-platform-native] WARNING: -djunit.platform.listeners.uid.tracking.output.dir not specified, " +
+ "trying to find test-ids on default Gradle/Maven locations. " +
+ "As this is a fallback mode, it could take a while. " +
+ "This should only happen if you are running tests executable manually and you didn't pass uid output directory with -djunit.platform.listeners.uid.tracking.output.dir=.");
+ Path defaultGradleTestIDsLocation = getGradleTestIdsDefaultLocation();
+ Path defaultMavenTestIDsLocation = getMavenTestIDsDefaultLocation();
+
+ if (testIdsDirectoryExists(defaultGradleTestIDsLocation) && testIdsDirectoryExists(defaultMavenTestIDsLocation)) {
+ throw new RuntimeException("[junit-platform-native] test-ids found in both " + defaultGradleTestIDsLocation + " and " + defaultMavenTestIDsLocation +
+ ". Please specify the test-ids location by passing the '--test-ids ' argument to your tests executable.");
+ }
+
+ if (testIdsDirectoryExists(defaultGradleTestIDsLocation)) {
+ System.out.println("[junit-platform-native] WARNING: Using test-ids from default Gradle project location:" + defaultGradleTestIDsLocation);
+ return defaultGradleTestIDsLocation;
+ }
+
+ if (testIdsDirectoryExists(defaultMavenTestIDsLocation)) {
+ System.out.println("[junit-platform-native] WARNING: Using test-ids from default Maven project location:" + defaultMavenTestIDsLocation);
+ return defaultMavenTestIDsLocation;
+ }
+
+ throw new RuntimeException("[junit-platform-native] test-ids not provided to the NativeImageJUnitLauncher and cannot be found on default locations.");
+ }
+
+ private static Path getGradleTestIdsDefaultLocation() {
+ File gradleBuildDirectory = new File(getBuildDirectory(File.separator + "build" + File.separator));
+ return searchForDirectory(gradleBuildDirectory, "testlist");
+ }
+
+ private static Path getMavenTestIDsDefaultLocation() {
+ File mavenTargetDirectory = new File(getBuildDirectory(File.separator + "target" + File.separator));
+ return searchForDirectory(mavenTargetDirectory, "test-ids");
+ }
+
+ private static String getBuildDirectory(String buildDir) {
+ String executableLocation = Path.of(".").toAbsolutePath().toString();
+ int index = executableLocation.indexOf(buildDir);
+ if (index < 0) {
+ return buildDir.substring(1);
+ }
+
+ return executableLocation.substring(0, index + buildDir.length());
+ }
+
+ private static Path searchForDirectory(File root, String target) {
+ if (root == null || !root.isDirectory()) {
+ return null;
+ }
+
+ if (root.getName().equals(target)) {
+ return Path.of(root.getAbsolutePath());
+ }
+
+ File[] content = root.listFiles();
+ if (content == null) {
+ return null;
+ }
+
+ for (File file : content) {
+ Path result = searchForDirectory(file, target);
+ if (result != null) {
+ return result;
+ }
+ }
+
+ return null;
+ }
+
+ private static boolean testIdsDirectoryExists(Path directory) {
+ return directory != null && Files.exists(directory);
+ }
+
+
+ private static void configurePrintTestExecutionListener(Launcher launcher, PrintWriter out) {
+ try {
+ Class.forName("org.junit.platform.reporting.legacy.LegacyReportingUtils");
+ launcher.registerTestExecutionListeners(new PrintTestExecutionListener(out));
+ } catch (NoClassDefFoundError | ClassNotFoundException e) {
+ // intentionally ignored
+ }
+ }
+
+ private static void configureLegacyXMLReport(Launcher launcher, String xmlOutput, PrintWriter out) {
+ try {
+ launcher.registerTestExecutionListeners(new LegacyXmlReportGeneratingListener(Paths.get(xmlOutput), out));
+ } catch (NoClassDefFoundError e) {
+ // intentionally ignored
+ }
+ }
}
diff --git a/common/junit-platform-native/src/main/java/org/graalvm/junit/platform/TestClassRegistrar.java b/common/junit-platform-native/src/main/java/org/graalvm/junit/platform/TestClassRegistrar.java
new file mode 100644
index 000000000..8929513d0
--- /dev/null
+++ b/common/junit-platform-native/src/main/java/org/graalvm/junit/platform/TestClassRegistrar.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * The Universal Permissive License (UPL), Version 1.0
+ *
+ * Subject to the condition set forth below, permission is hereby granted to any
+ * person obtaining a copy of this software, associated documentation and/or
+ * data (collectively the "Software"), free of charge and under any and all
+ * copyright rights in the Software, and any and all patent rights owned or
+ * freely licensable by each licensor hereunder covering either (i) the
+ * unmodified Software as contributed to or provided by such licensor, or (ii)
+ * the Larger Works (as defined below), to deal in both
+ *
+ * (a) the Software, and
+ *
+ * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+ * one is included with the Software each a "Larger Work" to which the Software
+ * is contributed by such licensors),
+ *
+ * without restriction, including without limitation the rights to copy, create
+ * derivative works of, display, perform, and distribute the Software and make,
+ * use, sell, offer for sale, import, export, have made, and have sold the
+ * Software and the Larger Work(s), and to sublicense the foregoing rights on
+ * either these or other terms.
+ *
+ * This license is subject to the following condition:
+ *
+ * The above copyright notice and either this complete permission notice or at a
+ * minimum a reference to the UPL must be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package org.graalvm.junit.platform;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.function.Consumer;
+
+class TestClassRegistrar {
+
+ private final Set> registeredClasses = new HashSet<>();
+ private final Consumer> registrationCallback;
+
+ TestClassRegistrar(Consumer> registrationCallback) {
+ this.registrationCallback = registrationCallback;
+ }
+
+ void registerTestClassForReflection(Class> clazz) {
+ if (!shouldRegisterClass(clazz)) {
+ return;
+ }
+
+ registrationCallback.accept(clazz);
+
+ Class>[] declaredClasses = clazz.getDeclaredClasses();
+ for (Class> declaredClass : declaredClasses) {
+ registerTestClassForReflection(declaredClass);
+ }
+
+ Class>[] interfaces = clazz.getInterfaces();
+ for (Class> inter : interfaces) {
+ registerTestClassForReflection(inter);
+ }
+
+ Class> superClass = clazz.getSuperclass();
+ if (superClass != null && superClass != Object.class) {
+ registerTestClassForReflection(superClass);
+ }
+ }
+
+ private boolean shouldRegisterClass(Class> clazz) {
+ /* avoid registering java internal classes */
+ if (ModuleLayer.boot().modules().contains(clazz.getModule())) {
+ return false;
+ }
+
+ /* avoid loops (possible case: class B is inner class of A, and B extends A) */
+ if (registeredClasses.contains(clazz)) {
+ return false;
+ }
+ registeredClasses.add(clazz);
+
+ return true;
+ }
+}
diff --git a/common/junit-platform-native/src/main/java/org/graalvm/junit/platform/config/core/NativeImageConfiguration.java b/common/junit-platform-native/src/main/java/org/graalvm/junit/platform/config/core/NativeImageConfiguration.java
index de35a6cfb..96b9ec089 100644
--- a/common/junit-platform-native/src/main/java/org/graalvm/junit/platform/config/core/NativeImageConfiguration.java
+++ b/common/junit-platform-native/src/main/java/org/graalvm/junit/platform/config/core/NativeImageConfiguration.java
@@ -42,6 +42,7 @@
package org.graalvm.junit.platform.config.core;
import org.graalvm.junit.platform.JUnitPlatformFeature;
+import org.graalvm.junit.platform.config.util.Utils;
import java.lang.reflect.Executable;
import java.lang.reflect.Field;
@@ -54,6 +55,10 @@ public interface NativeImageConfiguration {
void registerForReflection(Field... fields);
+ default void registerAllClassMembersForReflection(String... classNames) {
+ registerAllClassMembersForReflection(Utils.toClasses(classNames));
+ }
+
default void registerAllClassMembersForReflection(Class>... classes) {
for (Class> clazz : classes) {
JUnitPlatformFeature.debug("[Native Image Configuration] Registering for reflection: %s", clazz.getName());
@@ -63,8 +68,4 @@ default void registerAllClassMembersForReflection(Class>... classes) {
registerForReflection(clazz.getDeclaredFields());
}
}
-
- void initializeAtBuildTime(String... classNames);
-
- void initializeAtBuildTime(Class>... classes);
}
diff --git a/common/junit-platform-native/src/main/java/org/graalvm/junit/platform/config/core/PluginConfigProvider.java b/common/junit-platform-native/src/main/java/org/graalvm/junit/platform/config/core/PluginConfigProvider.java
index 8b104c489..fe183e49a 100644
--- a/common/junit-platform-native/src/main/java/org/graalvm/junit/platform/config/core/PluginConfigProvider.java
+++ b/common/junit-platform-native/src/main/java/org/graalvm/junit/platform/config/core/PluginConfigProvider.java
@@ -41,10 +41,38 @@
package org.graalvm.junit.platform.config.core;
-public interface PluginConfigProvider {
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
- void onLoad(NativeImageConfiguration config);
+public abstract class PluginConfigProvider {
- void onTestClassRegistered(Class> testClass, NativeImageConfiguration registry);
+ protected ClassLoader applicationClassLoader;
+ protected NativeImageConfiguration nativeImageConfigImpl;
+
+ public abstract void onLoad(NativeImageConfiguration config);
+
+ public abstract void onTestClassRegistered(Class> testClass, NativeImageConfiguration registry);
+
+ public final void initialize(ClassLoader classLoader, NativeImageConfiguration nic) {
+ applicationClassLoader = classLoader;
+ nativeImageConfigImpl = nic;
+ }
+
+ @SuppressWarnings("unchecked")
+ protected final T getAnnotationElementValue(Class> annotatedClass, String annotationName, String annotationElementName) {
+ try {
+ Class annotation = (Class) applicationClassLoader.loadClass(annotationName);
+ Method classProvider = annotation.getDeclaredMethod(annotationElementName);
+
+ Annotation classAnnotation = annotatedClass.getAnnotation(annotation);
+ if (classAnnotation != null) {
+ return (T) classProvider.invoke(classAnnotation);
+ }
+ } catch (ReflectiveOperationException e) {
+ // intentionally ignored
+ }
+
+ return null;
+ }
}
diff --git a/common/junit-platform-native/src/main/java/org/graalvm/junit/platform/config/jupiter/JupiterConfigProvider.java b/common/junit-platform-native/src/main/java/org/graalvm/junit/platform/config/jupiter/JupiterConfigProvider.java
index 25762dbc9..894099f12 100644
--- a/common/junit-platform-native/src/main/java/org/graalvm/junit/platform/config/jupiter/JupiterConfigProvider.java
+++ b/common/junit-platform-native/src/main/java/org/graalvm/junit/platform/config/jupiter/JupiterConfigProvider.java
@@ -54,65 +54,57 @@
import org.junit.jupiter.params.converter.ConvertWith;
import org.junit.jupiter.params.provider.ArgumentsSource;
import org.junit.jupiter.params.provider.EnumSource;
+import org.junit.jupiter.params.provider.FieldSource;
import org.junit.jupiter.params.provider.MethodSource;
-import org.junit.platform.commons.support.AnnotationSupport;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
-import java.util.Optional;
import static org.graalvm.junit.platform.JUnitPlatformFeature.debug;
-public class JupiterConfigProvider implements PluginConfigProvider {
+public class JupiterConfigProvider extends PluginConfigProvider {
@Override
public void onLoad(NativeImageConfiguration config) {
- String[] buildTimeInitializedClasses = new String[]{
- "org.junit.jupiter.engine.config.EnumConfigurationParameterConverter",
- "org.junit.jupiter.engine.descriptor.ClassTestDescriptor",
- "org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor",
- "org.junit.jupiter.engine.descriptor.TestFactoryTestDescriptor",
- "org.junit.jupiter.engine.descriptor.JupiterTestDescriptor",
- "org.junit.jupiter.engine.descriptor.JupiterTestDescriptor$1",
- "org.junit.jupiter.engine.descriptor.MethodBasedTestDescriptor",
- "org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor",
- "org.junit.jupiter.engine.descriptor.TestTemplateTestDescriptor",
- "org.junit.jupiter.engine.execution.ConditionEvaluator",
- "org.junit.jupiter.engine.execution.ExecutableInvoker",
- "org.junit.jupiter.params.provider.EnumSource$Mode",
- };
- for (String className : buildTimeInitializedClasses) {
- config.initializeAtBuildTime(className);
- }
+ /* Provide support for Timeout annotation */
+ config.registerAllClassMembersForReflection(
+ "org.junit.jupiter.engine.extension.TimeoutExtension$ExecutorResource",
+ "org.junit.jupiter.engine.extension.TimeoutInvocationFactory$SingleThreadExecutorResource"
+ );
}
@Override
public void onTestClassRegistered(Class> testClass, NativeImageConfiguration registry) {
+ /* Provide support for various annotations */
+ AnnotationUtils.registerClassesFromAnnotationForReflection(testClass, registry, TestMethodOrder.class, TestMethodOrder::value);
AnnotationUtils.registerClassesFromAnnotationForReflection(testClass, registry, ArgumentsSource.class, ArgumentsSource::value);
AnnotationUtils.registerClassesFromAnnotationForReflection(testClass, registry, ExtendWith.class, ExtendWith::value);
- AnnotationUtils.forEachAnnotatedMethod(testClass, EnumSource.class, (m, annotation) -> handleEnumSource(m, annotation, registry));
- handleTestOrderer(testClass, registry);
AnnotationUtils.registerClassesFromAnnotationForReflection(testClass, registry, DisplayNameGeneration.class, DisplayNameGeneration::value);
AnnotationUtils.registerClassesFromAnnotationForReflection(testClass, registry, IndicativeSentencesGeneration.class, IndicativeSentencesGeneration::generator);
AnnotationUtils.forEachAnnotatedMethodParameter(testClass, ConvertWith.class, annotation -> registry.registerAllClassMembersForReflection(annotation.value()));
AnnotationUtils.forEachAnnotatedMethodParameter(testClass, AggregateWith.class, annotation -> registry.registerAllClassMembersForReflection(annotation.value()));
+ AnnotationUtils.forEachAnnotatedMethod(testClass, EnumSource.class, (m, annotation) -> handleEnumSource(m, annotation, registry));
AnnotationUtils.registerClassesFromAnnotationForReflection(testClass, registry, MethodSource.class, JupiterConfigProvider::handleMethodSource);
AnnotationUtils.registerClassesFromAnnotationForReflection(testClass, registry, EnabledIf.class, JupiterConfigProvider::handleEnabledIf);
AnnotationUtils.registerClassesFromAnnotationForReflection(testClass, registry, DisabledIf.class, JupiterConfigProvider::handleDisabledIf);
try {
- Class > executor = Class.forName("org.junit.jupiter.engine.extension.TimeoutExtension$ExecutorResource");
- registry.registerAllClassMembersForReflection(executor);
- } catch (ClassNotFoundException e) {
- debug("Failed to register class for reflection. Reason: %s", e);
+ AnnotationUtils.registerClassesFromAnnotationForReflection(testClass, registry, FieldSource.class, JupiterConfigProvider::handleFieldSource);
+ } catch (NoClassDefFoundError e) {
+ // if users use JUnit version older than 5.11, FieldSource class won't be available
}
+
}
private static Class>[] handleMethodSource(MethodSource annotation) {
return handleMethodReference(annotation.value());
}
+ private static Class>[] handleFieldSource(FieldSource annotation) {
+ return handleMethodReference(annotation.value());
+ }
+
private static Class>[] handleEnabledIf(EnabledIf annotation) {
return handleMethodReference(annotation.value());
}
@@ -161,14 +153,4 @@ public static void handleEnumSource(Method method, EnumSource source, NativeImag
debug("Method doesn't have at least 1 parameter - skipping enum registration. Method: %s", method);
}
}
-
- private static void handleTestOrderer(Class> testClass, NativeImageConfiguration registry) {
- Optional annotation = AnnotationSupport.findAnnotation(testClass, TestMethodOrder.class);
- if (annotation.isPresent()) {
- TestMethodOrder testMethodOrder = annotation.get();
- Class> clazz = testMethodOrder.value();
- registry.initializeAtBuildTime(clazz);
- }
- }
-
}
diff --git a/common/junit-platform-native/src/main/java/org/graalvm/junit/platform/config/platform/PlatformConfigProvider.java b/common/junit-platform-native/src/main/java/org/graalvm/junit/platform/config/platform/PlatformConfigProvider.java
index d67cd5d77..9125c5f48 100644
--- a/common/junit-platform-native/src/main/java/org/graalvm/junit/platform/config/platform/PlatformConfigProvider.java
+++ b/common/junit-platform-native/src/main/java/org/graalvm/junit/platform/config/platform/PlatformConfigProvider.java
@@ -43,37 +43,14 @@
import org.graalvm.junit.platform.config.core.NativeImageConfiguration;
import org.graalvm.junit.platform.config.core.PluginConfigProvider;
+import org.graalvm.nativeimage.hosted.RuntimeSerialization;
+import org.junit.platform.launcher.TestIdentifier;
-public class PlatformConfigProvider implements PluginConfigProvider {
+public class PlatformConfigProvider extends PluginConfigProvider {
@Override
public void onLoad(NativeImageConfiguration config) {
- String[] buildTimeInitializedClasses = new String[] {
- "org.junit.platform.launcher.TestIdentifier",
- "org.junit.platform.launcher.core.InternalTestPlan",
- "org.junit.platform.commons.util.StringUtils",
- "org.junit.platform.launcher.core.TestExecutionListenerRegistry",
- "org.junit.platform.commons.logging.LoggerFactory$DelegatingLogger",
- "org.junit.platform.launcher.core.EngineDiscoveryOrchestrator",
- "org.junit.platform.launcher.core.LauncherConfigurationParameters",
- "org.junit.platform.commons.logging.LoggerFactory",
- "org.junit.platform.engine.UniqueIdFormat",
- "org.junit.platform.commons.util.ReflectionUtils",
- // https://github.com/graalvm/native-build-tools/issues/300
- "org.junit.platform.reporting.open.xml.OpenTestReportGeneratingListener"
- };
- for (String className : buildTimeInitializedClasses) {
- config.initializeAtBuildTime(className);
- }
-
- try {
- /* Verify if the core JUnit Platform test class is available on the classpath */
- Class.forName("org.junit.platform.commons.annotation.Testable");
- } catch (ClassNotFoundException e) {
- throw new RuntimeException("Missing some JUnit Platform classes for runtime reflection configuration. \n" +
- "Check if JUnit Platform is on your classpath or if that version is supported. \n" +
- "Original error: " + e);
- }
+ RuntimeSerialization.register(TestIdentifier.class.getDeclaredClasses());
}
@Override
diff --git a/common/junit-platform-native/src/main/java/org/graalvm/junit/platform/config/util/Utils.java b/common/junit-platform-native/src/main/java/org/graalvm/junit/platform/config/util/Utils.java
new file mode 100644
index 000000000..f2021842a
--- /dev/null
+++ b/common/junit-platform-native/src/main/java/org/graalvm/junit/platform/config/util/Utils.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2023, 2023, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * The Universal Permissive License (UPL), Version 1.0
+ *
+ * Subject to the condition set forth below, permission is hereby granted to any
+ * person obtaining a copy of this software, associated documentation and/or
+ * data (collectively the "Software"), free of charge and under any and all
+ * copyright rights in the Software, and any and all patent rights owned or
+ * freely licensable by each licensor hereunder covering either (i) the
+ * unmodified Software as contributed to or provided by such licensor, or (ii)
+ * the Larger Works (as defined below), to deal in both
+ *
+ * (a) the Software, and
+ *
+ * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+ * one is included with the Software each a "Larger Work" to which the Software
+ * is contributed by such licensors),
+ *
+ * without restriction, including without limitation the rights to copy, create
+ * derivative works of, display, perform, and distribute the Software and make,
+ * use, sell, offer for sale, import, export, have made, and have sold the
+ * Software and the Larger Work(s), and to sublicense the foregoing rights on
+ * either these or other terms.
+ *
+ * This license is subject to the following condition:
+ *
+ * The above copyright notice and either this complete permission notice or at a
+ * minimum a reference to the UPL must be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package org.graalvm.junit.platform.config.util;
+
+import org.graalvm.junit.platform.JUnitPlatformFeature;
+
+import java.util.Arrays;
+
+public class Utils {
+ public static Class>[] toClasses(String... classNames) {
+ return Arrays.stream(classNames).map(className -> {
+ Class> clazz = null;
+ try {
+ clazz = Class.forName(className);
+ } catch (ClassNotFoundException e) {
+ JUnitPlatformFeature.debug("[Native Image Configuration] Failed to resolve: %s Reason: %s", className, e);
+ }
+ return clazz;
+ }).filter(c -> c != null).toArray(Class[]::new);
+ }
+}
diff --git a/common/junit-platform-native/src/main/java/org/graalvm/junit/platform/config/vintage/VintageConfigProvider.java b/common/junit-platform-native/src/main/java/org/graalvm/junit/platform/config/vintage/VintageConfigProvider.java
index b5a6eec98..f4399457f 100644
--- a/common/junit-platform-native/src/main/java/org/graalvm/junit/platform/config/vintage/VintageConfigProvider.java
+++ b/common/junit-platform-native/src/main/java/org/graalvm/junit/platform/config/vintage/VintageConfigProvider.java
@@ -43,27 +43,31 @@
import org.graalvm.junit.platform.config.core.NativeImageConfiguration;
import org.graalvm.junit.platform.config.core.PluginConfigProvider;
+import org.graalvm.nativeimage.hosted.RuntimeReflection;
+import org.graalvm.nativeimage.hosted.RuntimeSerialization;
-public class VintageConfigProvider implements PluginConfigProvider {
+public class VintageConfigProvider extends PluginConfigProvider {
@Override
public void onLoad(NativeImageConfiguration config) {
- String[] buildTimeInitializedClasses = new String[]{
- "org.junit.vintage.engine.descriptor.RunnerTestDescriptor",
- "org.junit.vintage.engine.support.UniqueIdReader",
- "org.junit.vintage.engine.support.UniqueIdStringifier",
- "org.junit.runner.Description",
- "org.junit.runners.BlockJUnit4ClassRunner",
- "org.junit.runners.JUnit4",
- /* Workaround until we can register serializable classes from a native-image feature */
- "org.junit.runner.Result"
- };
- for (String className : buildTimeInitializedClasses) {
- config.initializeAtBuildTime(className);
+ try {
+ RuntimeSerialization.register(Class.forName("org.junit.runner.Result").getDeclaredClasses());
+ RuntimeReflection.register(Class.forName("org.junit.runner.Description").getDeclaredFields());
+ } catch (ClassNotFoundException e) {
+ System.out.println("Cannot register declared classes of org.junit.runner.Result for serialization or fields of org.junit.runner.Description for reflection. Vintage JUnit not available.");
}
}
@Override
public void onTestClassRegistered(Class> testClass, NativeImageConfiguration registry) {
+ registerAnnotationClassesForReflection(testClass, "org.junit.runner.RunWith", "value");
+ registerAnnotationClassesForReflection(testClass, "org.junit.runners.Parameterized.UseParametersRunnerFactory", "value");
+ }
+
+ private void registerAnnotationClassesForReflection(Class> testClass, String annotationName, String annotationElementName) {
+ Class> annotationArgument = getAnnotationElementValue(testClass, annotationName, annotationElementName);
+ if (annotationArgument != null) {
+ nativeImageConfigImpl.registerAllClassMembersForReflection(annotationArgument);
+ }
}
}
diff --git a/common/junit-platform-native/src/main/resources/extra-build-args/exclude-config b/common/junit-platform-native/src/main/resources/extra-build-args/exclude-config
new file mode 100644
index 000000000..ceae5d2e3
--- /dev/null
+++ b/common/junit-platform-native/src/main/resources/extra-build-args/exclude-config
@@ -0,0 +1 @@
+.*.jar,META-INF\/native-image\/org.junit.*.properties.*
diff --git a/common/junit-platform-native/src/main/resources/initialize-at-buildtime b/common/junit-platform-native/src/main/resources/initialize-at-buildtime
new file mode 100644
index 000000000..279c3a80b
--- /dev/null
+++ b/common/junit-platform-native/src/main/resources/initialize-at-buildtime
@@ -0,0 +1,125 @@
+org.junit.internal.runners.rules.RuleMemberValidator
+org.junit.jupiter.api.condition.OS
+org.junit.jupiter.api.DisplayNameGenerator$IndicativeSentences
+org.junit.jupiter.api.DisplayNameGenerator$Standard
+org.junit.jupiter.api.extension.ConditionEvaluationResult
+org.junit.jupiter.api.MethodOrderer$DisplayName
+org.junit.jupiter.api.MethodOrderer$MethodName
+org.junit.jupiter.api.MethodOrderer$Random
+org.junit.jupiter.api.RandomOrdererUtils
+org.junit.jupiter.api.TestInstance$Lifecycle
+org.junit.jupiter.engine.config.CachingJupiterConfiguration
+org.junit.jupiter.engine.config.DefaultJupiterConfiguration
+org.junit.jupiter.engine.config.EnumConfigurationParameterConverter
+org.junit.jupiter.engine.config.InstantiatingConfigurationParameterConverter
+org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor
+org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$ClassInfo
+org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor$LifecycleMethods
+org.junit.jupiter.engine.descriptor.ClassTemplateInvocationTestDescriptor
+org.junit.jupiter.engine.descriptor.ClassTemplateTestDescriptor
+org.junit.jupiter.engine.descriptor.ClassTestDescriptor
+org.junit.jupiter.engine.descriptor.DisplayNameUtils
+org.junit.jupiter.engine.descriptor.DynamicDescendantFilter
+org.junit.jupiter.engine.descriptor.DynamicDescendantFilter$Mode
+org.junit.jupiter.engine.descriptor.ExclusiveResourceCollector$1
+org.junit.jupiter.engine.descriptor.JupiterEngineDescriptor
+org.junit.jupiter.engine.descriptor.JupiterTestDescriptor
+org.junit.jupiter.engine.descriptor.JupiterTestDescriptor$1
+org.junit.jupiter.engine.descriptor.MethodBasedTestDescriptor
+org.junit.jupiter.engine.descriptor.MethodBasedTestDescriptor$MethodInfo
+org.junit.jupiter.engine.descriptor.NestedClassTestDescriptor
+org.junit.jupiter.engine.descriptor.TestFactoryTestDescriptor
+org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor
+org.junit.jupiter.engine.descriptor.TestTemplateTestDescriptor
+org.junit.jupiter.engine.discovery.AbstractOrderingVisitor
+org.junit.jupiter.engine.discovery.AbstractOrderingVisitor$DescriptorWrapperOrderer
+org.junit.jupiter.engine.discovery.ClassOrderingVisitor
+org.junit.jupiter.engine.discovery.ClassSelectorResolver
+org.junit.jupiter.engine.discovery.ClassSelectorResolver$DummyClassTemplateInvocationContext
+org.junit.jupiter.engine.discovery.DiscoverySelectorResolver
+org.junit.jupiter.engine.discovery.MethodFinder
+org.junit.jupiter.engine.discovery.MethodSelectorResolver
+org.junit.jupiter.engine.discovery.MethodSelectorResolver$MethodType
+org.junit.jupiter.engine.discovery.MethodSelectorResolver$MethodType$1
+org.junit.jupiter.engine.discovery.MethodSelectorResolver$MethodType$2
+org.junit.jupiter.engine.discovery.MethodSelectorResolver$MethodType$3
+org.junit.jupiter.engine.discovery.predicates.IsTestClassWithTests
+org.junit.jupiter.engine.discovery.predicates.IsTestFactoryMethod
+org.junit.jupiter.engine.execution.ConditionEvaluator
+org.junit.jupiter.engine.execution.ExecutableInvoker
+org.junit.jupiter.engine.execution.InterceptingExecutableInvoker
+org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall
+org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall$VoidMethodInterceptorCall
+org.junit.jupiter.engine.execution.InvocationInterceptorChain
+org.junit.jupiter.engine.JupiterTestEngine
+org.junit.jupiter.params.provider.EnumSource$Mode
+org.junit.jupiter.params.provider.EnumSource$Mode$Validator
+org.junit.platform.commons.logging.LoggerFactory
+org.junit.platform.commons.logging.LoggerFactory$DelegatingLogger
+org.junit.platform.commons.support.scanning.DefaultClasspathScanner
+org.junit.platform.commons.util.ClasspathScanner
+org.junit.platform.commons.util.LruCache
+org.junit.platform.commons.util.ReflectionUtils
+org.junit.platform.commons.util.StringUtils
+org.junit.platform.engine.SelectorResolutionResult
+org.junit.platform.engine.support.descriptor.ClassSource
+org.junit.platform.engine.support.descriptor.MethodSource
+org.junit.platform.engine.support.discovery.SelectorResolver$Resolution
+org.junit.platform.engine.support.hierarchical.Node$ExecutionMode
+org.junit.platform.engine.support.store.NamespacedHierarchicalStore
+org.junit.platform.engine.support.store.NamespacedHierarchicalStore$EvaluatedValue
+org.junit.platform.engine.TestDescriptor$Type
+org.junit.platform.engine.UniqueId
+org.junit.platform.engine.UniqueId$Segment
+org.junit.platform.engine.UniqueIdFormat
+org.junit.platform.launcher.core.DefaultLauncher
+org.junit.platform.launcher.core.DefaultLauncherConfig
+org.junit.platform.launcher.core.DiscoveryIssueCollector
+org.junit.platform.launcher.core.DiscoveryIssueNotifier
+org.junit.platform.launcher.core.EngineDiscoveryOrchestrator
+org.junit.platform.launcher.core.EngineExecutionOrchestrator
+org.junit.platform.launcher.core.EngineFilterer
+org.junit.platform.launcher.core.EngineIdValidator
+org.junit.platform.launcher.core.HierarchicalOutputDirectoryProvider
+org.junit.platform.launcher.core.InternalTestPlan
+org.junit.platform.launcher.core.LauncherConfig
+org.junit.platform.launcher.core.LauncherConfigurationParameters
+org.junit.platform.launcher.core.LauncherConfigurationParameters$ParameterProvider$1
+org.junit.platform.launcher.core.LauncherConfigurationParameters$ParameterProvider$2
+org.junit.platform.launcher.core.LauncherConfigurationParameters$ParameterProvider$3
+org.junit.platform.launcher.core.LauncherConfigurationParameters$ParameterProvider$4
+org.junit.platform.launcher.core.LauncherDiscoveryResult
+org.junit.platform.launcher.core.LauncherDiscoveryResult$EngineResultInfo
+org.junit.platform.launcher.core.LauncherListenerRegistry
+org.junit.platform.launcher.core.LauncherPhase
+org.junit.platform.launcher.core.ListenerRegistry
+org.junit.platform.launcher.core.ServiceLoaderRegistry
+org.junit.platform.launcher.core.ServiceLoaderTestEngineRegistry
+org.junit.platform.launcher.core.SessionPerRequestLauncher
+org.junit.platform.launcher.EngineDiscoveryResult
+org.junit.platform.launcher.LauncherSessionListener$1
+org.junit.platform.launcher.listeners.discovery.LauncherDiscoveryListeners$LauncherDiscoveryListenerType
+org.junit.platform.launcher.listeners.UniqueIdTrackingListener
+org.junit.platform.launcher.TestIdentifier
+org.junit.platform.reporting.open.xml.OpenTestReportGeneratingListener
+org.junit.platform.reporting.shadow.org.opentest4j.reporting.events.api.DocumentWriter$1
+org.junit.platform.suite.engine.DiscoverySelectorResolver
+org.junit.platform.suite.engine.SuiteEngineDescriptor
+org.junit.platform.suite.engine.SuiteLauncher
+org.junit.platform.suite.engine.SuiteTestDescriptor
+org.junit.platform.suite.engine.SuiteTestDescriptor$DiscoveryIssueForwardingListener
+org.junit.platform.suite.engine.SuiteTestDescriptor$LifecycleMethods
+org.junit.platform.suite.engine.SuiteTestEngine
+org.junit.runner.Description
+org.junit.runner.Result
+org.junit.runners.BlockJUnit4ClassRunner
+org.junit.runners.JUnit4
+org.junit.validator.PublicClassValidator
+org.junit.vintage.engine.descriptor.RunnerTestDescriptor
+org.junit.vintage.engine.descriptor.VintageEngineDescriptor
+org.junit.vintage.engine.discovery.DefensiveAllDefaultPossibilitiesBuilder
+org.junit.vintage.engine.discovery.VintageDiscoverer
+org.junit.vintage.engine.JUnit4VersionCheck
+org.junit.vintage.engine.support.UniqueIdReader
+org.junit.vintage.engine.support.UniqueIdStringifier
+org.junit.vintage.engine.VintageTestEngine
diff --git a/common/junit-platform-native/src/test/java/org/graalvm/junit/jupiter/MethodSourceTests.java b/common/junit-platform-native/src/test/java/org/graalvm/junit/jupiter/MethodSourceTests.java
index 7083a3d01..626bfb617 100644
--- a/common/junit-platform-native/src/test/java/org/graalvm/junit/jupiter/MethodSourceTests.java
+++ b/common/junit-platform-native/src/test/java/org/graalvm/junit/jupiter/MethodSourceTests.java
@@ -60,6 +60,7 @@ public abstract static class ArgumentTestBase {
@BeforeAll
public static void setup() {
+ expectedArgs.clear();
actualArgs.clear();
}
diff --git a/common/junit-platform-native/src/test/java/org/graalvm/junit/jupiter/OSTest.java b/common/junit-platform-native/src/test/java/org/graalvm/junit/jupiter/OSTest.java
new file mode 100644
index 000000000..c005a3192
--- /dev/null
+++ b/common/junit-platform-native/src/test/java/org/graalvm/junit/jupiter/OSTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * The Universal Permissive License (UPL), Version 1.0
+ *
+ * Subject to the condition set forth below, permission is hereby granted to any
+ * person obtaining a copy of this software, associated documentation and/or
+ * data (collectively the "Software"), free of charge and under any and all
+ * copyright rights in the Software, and any and all patent rights owned or
+ * freely licensable by each licensor hereunder covering either (i) the
+ * unmodified Software as contributed to or provided by such licensor, or (ii)
+ * the Larger Works (as defined below), to deal in both
+ *
+ * (a) the Software, and
+ *
+ * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+ * one is included with the Software each a "Larger Work" to which the Software
+ * is contributed by such licensors),
+ *
+ * without restriction, including without limitation the rights to copy, create
+ * derivative works of, display, perform, and distribute the Software and make,
+ * use, sell, offer for sale, import, export, have made, and have sold the
+ * Software and the Larger Work(s), and to sublicense the foregoing rights on
+ * either these or other terms.
+ *
+ * This license is subject to the following condition:
+ *
+ * The above copyright notice and either this complete permission notice or at a
+ * minimum a reference to the UPL must be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package org.graalvm.junit.jupiter;
+
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.EnabledOnOs;
+import org.junit.jupiter.api.condition.OS;
+
+import java.util.Locale;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+public class OSTest {
+ @Test
+ @EnabledOnOs({OS.LINUX})
+ public void timeoutTest() {
+ assertThat(System.getProperties().getProperty("os.name").toLowerCase(Locale.ENGLISH).contains("linux"), is(true));
+ }
+}
diff --git a/common/junit-platform-native/src/test/java/org/graalvm/junit/jupiter/ParameterizedClassTests.java b/common/junit-platform-native/src/test/java/org/graalvm/junit/jupiter/ParameterizedClassTests.java
new file mode 100644
index 000000000..cc0d5cba0
--- /dev/null
+++ b/common/junit-platform-native/src/test/java/org/graalvm/junit/jupiter/ParameterizedClassTests.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * The Universal Permissive License (UPL), Version 1.0
+ *
+ * Subject to the condition set forth below, permission is hereby granted to any
+ * person obtaining a copy of this software, associated documentation and/or
+ * data (collectively the "Software"), free of charge and under any and all
+ * copyright rights in the Software, and any and all patent rights owned or
+ * freely licensable by each licensor hereunder covering either (i) the
+ * unmodified Software as contributed to or provided by such licensor, or (ii)
+ * the Larger Works (as defined below), to deal in both
+ *
+ * (a) the Software, and
+ *
+ * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+ * one is included with the Software each a "Larger Work" to which the Software
+ * is contributed by such licensors),
+ *
+ * without restriction, including without limitation the rights to copy, create
+ * derivative works of, display, perform, and distribute the Software and make,
+ * use, sell, offer for sale, import, export, have made, and have sold the
+ * Software and the Larger Work(s), and to sublicense the foregoing rights on
+ * either these or other terms.
+ *
+ * This license is subject to the following condition:
+ *
+ * The above copyright notice and either this complete permission notice or at a
+ * minimum a reference to the UPL must be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package org.graalvm.junit.jupiter;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInfo;
+import org.junit.jupiter.params.ParameterizedClass;
+import org.junit.jupiter.params.provider.ValueSource;
+
+abstract class BaseTests {
+ @Test
+ void test(TestInfo testInfo) {
+ Assertions.assertEquals(ParameterizedClassTests.InnerTests.class, testInfo.getTestClass().orElseThrow());
+ }
+}
+
+@ParameterizedClass
+@ValueSource(ints = { 1, 2 })
+class ParameterizedClassTests {
+
+ private final int value;
+
+ ParameterizedClassTests(int value) {
+ this.value = value;
+ }
+
+ @Test
+ void test() {
+ Assertions.assertTrue(value == 1 || value == 2);
+ }
+
+ @Nested
+ class InnerTests extends BaseTests {
+ }
+}
diff --git a/common/junit-platform-native/src/test/java/org/graalvm/junit/platform/TestClassRegistrarTests.java b/common/junit-platform-native/src/test/java/org/graalvm/junit/platform/TestClassRegistrarTests.java
new file mode 100644
index 000000000..b42d19ba6
--- /dev/null
+++ b/common/junit-platform-native/src/test/java/org/graalvm/junit/platform/TestClassRegistrarTests.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * The Universal Permissive License (UPL), Version 1.0
+ *
+ * Subject to the condition set forth below, permission is hereby granted to any
+ * person obtaining a copy of this software, associated documentation and/or
+ * data (collectively the "Software"), free of charge and under any and all
+ * copyright rights in the Software, and any and all patent rights owned or
+ * freely licensable by each licensor hereunder covering either (i) the
+ * unmodified Software as contributed to or provided by such licensor, or (ii)
+ * the Larger Works (as defined below), to deal in both
+ *
+ * (a) the Software, and
+ *
+ * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+ * one is included with the Software each a "Larger Work" to which the Software
+ * is contributed by such licensors),
+ *
+ * without restriction, including without limitation the rights to copy, create
+ * derivative works of, display, perform, and distribute the Software and make,
+ * use, sell, offer for sale, import, export, have made, and have sold the
+ * Software and the Larger Work(s), and to sublicense the foregoing rights on
+ * either these or other terms.
+ *
+ * This license is subject to the following condition:
+ *
+ * The above copyright notice and either this complete permission notice or at a
+ * minimum a reference to the UPL must be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package org.graalvm.junit.platform;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class TestClassRegistrarTests {
+
+ @Test
+ void avoidsInfiniteLoopsWhenClassDefinitionsAreRecursive() {
+ List> registeredClasses = new ArrayList<>();
+ var registrar = new TestClassRegistrar(registeredClasses::add);
+
+ registrar.registerTestClassForReflection(OuterClass.class);
+
+ assertEquals(List.of(OuterClass.class, OuterClass.InfiniteLoop.class), registeredClasses);
+ }
+
+ @Test
+ void avoidsRegistrationOfJavaInternalTypes() {
+ List> registeredClasses = new ArrayList<>();
+ var registrar = new TestClassRegistrar(registeredClasses::add);
+
+ registrar.registerTestClassForReflection(EnumTest.class);
+
+ assertEquals(List.of(EnumTest.class), registeredClasses);
+ }
+
+ public static class OuterClass {
+
+ /* Since TestClassRegistrar registers all declared classes and superclass of the test class, and we do so
+ * recursively, we want to avoid infinite loop.
+ * This inheritance shows that we won't call registration of these classes indefinitely (call registration of
+ * all declared classes of AbstractParentClassTests, then recursively call superclass of InfiniteLoopTest and
+ * repeat the process indefinitely) */
+ @SuppressWarnings({"unused", "InnerClassMayBeStatic"})
+ class InfiniteLoop extends OuterClass {
+ }
+ }
+
+ /* Since enum here is declared class of AbstractParentClassTests, we want to avoid registrations of
+ * enum's internal superclasses and sub-classes */
+ @SuppressWarnings("unused")
+ enum EnumTest {
+ SOME_VALUE,
+ OTHER_VALUE
+ }
+}
diff --git a/common/utils/build.gradle.kts b/common/utils/build.gradle.kts
index a35b0ff9b..4676dc2cc 100644
--- a/common/utils/build.gradle.kts
+++ b/common/utils/build.gradle.kts
@@ -49,7 +49,8 @@ maven {
}
dependencies {
- implementation(libs.jackson.databind)
+ implementation(libs.openjson)
testImplementation(platform(libs.test.junit.bom))
testImplementation(libs.test.junit.jupiter.core)
+ testRuntimeOnly(libs.test.junit.platform.launcher)
}
diff --git a/common/utils/src/main/java/org/graalvm/buildtools/agent/AgentConfiguration.java b/common/utils/src/main/java/org/graalvm/buildtools/agent/AgentConfiguration.java
index 7e3dfe404..07e070ce0 100644
--- a/common/utils/src/main/java/org/graalvm/buildtools/agent/AgentConfiguration.java
+++ b/common/utils/src/main/java/org/graalvm/buildtools/agent/AgentConfiguration.java
@@ -40,30 +40,55 @@
*/
package org.graalvm.buildtools.agent;
+import java.io.IOException;
+import java.io.InputStream;
import java.io.Serializable;
+import java.nio.file.FileAlreadyExistsException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
+import java.util.logging.Logger;
public class AgentConfiguration implements Serializable {
+ private static final String ACCESS_FILTER_PREFIX = "access-filter";
+ private static final String ACCESS_FILTER_SUFFIX = ".json";
+ private static final String DEFAULT_ACCESS_FILTER_FILE_LOCATION = "/" + ACCESS_FILTER_PREFIX + ACCESS_FILTER_SUFFIX;
+
+ private static final Logger logger = Logger.getGlobal();
+
private final Collection callerFilterFiles;
private final Collection accessFilterFiles;
- private final boolean builtinCallerFilter;
- private final boolean builtinHeuristicFilter;
- private final boolean experimentalPredefinedClasses;
- private final boolean experimentalUnsafeAllocationTracing;
- private final boolean trackReflectionMetadata;
+ private final Boolean builtinCallerFilter;
+ private final Boolean builtinHeuristicFilter;
+ private final Boolean experimentalPredefinedClasses;
+ private final Boolean experimentalUnsafeAllocationTracing;
+ private final Boolean trackReflectionMetadata;
private final AgentMode agentMode;
+ // This constructor should be used only to specify that we have instance of agent that is disabled (to avoid using null for agent enable check)
+ public AgentConfiguration(AgentMode ...modes) {
+ this.callerFilterFiles = null;
+ this.accessFilterFiles = null;
+ this.builtinCallerFilter = null;
+ this.builtinHeuristicFilter = null;
+ this.experimentalPredefinedClasses = null;
+ this.experimentalUnsafeAllocationTracing = null;
+ this.trackReflectionMetadata = null;
+ this.agentMode = modes.length == 1 ? modes[0] : new DisabledAgentMode();
+ }
+
public AgentConfiguration(Collection callerFilterFiles,
Collection accessFilterFiles,
- boolean builtinCallerFilter,
- boolean builtinHeuristicFilter,
- boolean experimentalPredefinedClasses,
- boolean experimentalUnsafeAllocationTracing,
- boolean trackReflectionMetadata,
+ Boolean builtinCallerFilter,
+ Boolean builtinHeuristicFilter,
+ Boolean experimentalPredefinedClasses,
+ Boolean experimentalUnsafeAllocationTracing,
+ Boolean trackReflectionMetadata,
AgentMode agentMode) {
this.callerFilterFiles = callerFilterFiles;
this.accessFilterFiles = accessFilterFiles;
@@ -76,14 +101,15 @@ public AgentConfiguration(Collection callerFilterFiles,
}
public List getAgentCommandLine() {
+ addDefaultAccessFilter();
List cmdLine = new ArrayList<>(agentMode.getAgentCommandLine());
appendOptionToValues("caller-filter-file=", callerFilterFiles, cmdLine);
appendOptionToValues("access-filter-file=", accessFilterFiles, cmdLine);
- cmdLine.add("builtin-caller-filter=" + builtinCallerFilter);
- cmdLine.add("builtin-heuristic-filter=" + builtinHeuristicFilter);
- cmdLine.add("experimental-class-define-support=" + experimentalPredefinedClasses);
- cmdLine.add("experimental-unsafe-allocation-support=" + experimentalUnsafeAllocationTracing);
- cmdLine.add("track-reflection-metadata=" + trackReflectionMetadata);
+ addToCmd("builtin-caller-filter=", builtinCallerFilter, cmdLine);
+ addToCmd("builtin-heuristic-filter=", builtinHeuristicFilter, cmdLine);
+ addToCmd("experimental-class-define-support=", experimentalPredefinedClasses, cmdLine);
+ addToCmd("experimental-unsafe-allocation-support=", experimentalUnsafeAllocationTracing, cmdLine);
+ addToCmd("track-reflection-metadata=", trackReflectionMetadata, cmdLine);
return cmdLine;
}
@@ -100,10 +126,62 @@ public boolean isEnabled() {
}
public static void appendOptionToValues(String option, Collection values, Collection target) {
- values.stream().map(value -> option + value).forEach(target::add);
+ if (values != null) {
+ values.stream().map(value -> option + value).forEach(target::add);
+ }
}
public AgentMode getAgentMode() {
return agentMode;
}
+
+ private void addToCmd(String option, Boolean value, List cmdLine) {
+ if (value != null) {
+ cmdLine.add(option + value);
+ }
+ }
+
+ private void addDefaultAccessFilter() {
+ if (accessFilterFiles == null) {
+ // this could only happen if we instantiated disabled agent configuration
+ return;
+ }
+
+ String tempDir = System.getProperty("java.io.tmpdir");
+ Path agentDir = Path.of(tempDir).resolve("agent-config");
+ Path accessFilterFile = agentDir.resolve(ACCESS_FILTER_PREFIX + ACCESS_FILTER_SUFFIX);
+ if (Files.exists(accessFilterFile)) {
+ accessFilterFiles.add(accessFilterFile.toString());
+ return;
+ }
+
+ try (InputStream accessFilterData = AgentConfiguration.class.getResourceAsStream(DEFAULT_ACCESS_FILTER_FILE_LOCATION)) {
+ if (accessFilterData == null) {
+ throw new IOException("Cannot access data from: " + DEFAULT_ACCESS_FILTER_FILE_LOCATION);
+ }
+
+ try {
+ Files.createDirectory(agentDir);
+ } catch (FileAlreadyExistsException e) {
+ logger.info("Skip creation of directory " + agentDir + " (already created).");
+ }
+
+ long pid = ProcessHandle.current().pid();
+ long time = System.nanoTime();
+ Path tmpAccessFilter = agentDir.resolve(ACCESS_FILTER_PREFIX + '_' + pid + '_' + time + '_' + ACCESS_FILTER_SUFFIX);
+ Files.copy(accessFilterData, tmpAccessFilter);
+
+ try {
+ Files.move(tmpAccessFilter, accessFilterFile, StandardCopyOption.ATOMIC_MOVE);
+ } catch (FileAlreadyExistsException e) {
+ Files.delete(tmpAccessFilter);
+ logger.info(accessFilterFile + " already exists. Delete " + tmpAccessFilter);
+ }
+
+ accessFilterFiles.add(accessFilterFile.toString());
+ } catch (IOException e) {
+ throw new RuntimeException("Cannot add default access-filter.json", e);
+ }
+ }
+
}
diff --git a/common/utils/src/main/java/org/graalvm/buildtools/model/resources/ClassPathDirectoryAnalyzer.java b/common/utils/src/main/java/org/graalvm/buildtools/model/resources/ClassPathDirectoryAnalyzer.java
index 39a7f7bbf..9ad774048 100644
--- a/common/utils/src/main/java/org/graalvm/buildtools/model/resources/ClassPathDirectoryAnalyzer.java
+++ b/common/utils/src/main/java/org/graalvm/buildtools/model/resources/ClassPathDirectoryAnalyzer.java
@@ -102,7 +102,7 @@ public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOEx
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
- if (inNativeImageDir && relativePathOf(file).endsWith("resource-config.json")) {
+ if (!ignoreExistingResourcesConfig && inNativeImageDir && relativePathOf(file).endsWith("resource-config.json")) {
hasNativeImageResourceFile = true;
return FileVisitResult.TERMINATE;
}
diff --git a/common/utils/src/main/java/org/graalvm/buildtools/model/resources/NativeImageFlags.java b/common/utils/src/main/java/org/graalvm/buildtools/model/resources/NativeImageFlags.java
new file mode 100644
index 000000000..ed4f5dab2
--- /dev/null
+++ b/common/utils/src/main/java/org/graalvm/buildtools/model/resources/NativeImageFlags.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2021, 2021 Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * The Universal Permissive License (UPL), Version 1.0
+ *
+ * Subject to the condition set forth below, permission is hereby granted to any
+ * person obtaining a copy of this software, associated documentation and/or
+ * data (collectively the "Software"), free of charge and under any and all
+ * copyright rights in the Software, and any and all patent rights owned or
+ * freely licensable by each licensor hereunder covering either (i) the
+ * unmodified Software as contributed to or provided by such licensor, or (ii)
+ * the Larger Works (as defined below), to deal in both
+ *
+ * (a) the Software, and
+ *
+ * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+ * one is included with the Software each a "Larger Work" to which the Software
+ * is contributed by such licensors),
+ *
+ * without restriction, including without limitation the rights to copy, create
+ * derivative works of, display, perform, and distribute the Software and make,
+ * use, sell, offer for sale, import, export, have made, and have sold the
+ * Software and the Larger Work(s), and to sublicense the foregoing rights on
+ * either these or other terms.
+ *
+ * This license is subject to the following condition:
+ *
+ * The above copyright notice and either this complete permission notice or at a
+ * minimum a reference to the UPL must be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package org.graalvm.buildtools.model.resources;
+
+public abstract class NativeImageFlags {
+ public static final String BUILD_OUTPUT_COLORFUL = "-H:+BuildOutputColorful";
+ public static final String COLOR = "--color";
+ public static final String CONFIGURATION_FILE_DIRECTORIES = "-H:ConfigurationFileDirectories";
+ public static final String LAYER_CREATE = "-H:LayerCreate";
+ public static final String LAYER_USE = "-H:LayerUse";
+ public static final String NO_FALLBACK = "--no-fallback";
+ public static final String PGO_INSTRUMENT = "--pgo-instrument";
+ public static final String QUICK_BUILD = "-Ob";
+ public static final String SHARED = "--shared";
+ public static final String UNLOCK_EXPERIMENTAL_VMOPTIONS = "-H:+UnlockExperimentalVMOptions";
+ public static final String VERBOSE = "--verbose";
+
+ private NativeImageFlags() {
+
+ }
+}
diff --git a/common/utils/src/main/java/org/graalvm/buildtools/model/resources/ResourcesConfigModelSerializer.java b/common/utils/src/main/java/org/graalvm/buildtools/model/resources/ResourcesConfigModelSerializer.java
index 7334728bd..784ff0fd1 100644
--- a/common/utils/src/main/java/org/graalvm/buildtools/model/resources/ResourcesConfigModelSerializer.java
+++ b/common/utils/src/main/java/org/graalvm/buildtools/model/resources/ResourcesConfigModelSerializer.java
@@ -40,7 +40,8 @@
*/
package org.graalvm.buildtools.model.resources;
-import com.fasterxml.jackson.databind.ObjectMapper;
+import com.github.openjson.JSONArray;
+import com.github.openjson.JSONObject;
import java.io.File;
import java.io.FileOutputStream;
@@ -50,8 +51,8 @@
public class ResourcesConfigModelSerializer {
public static void serialize(ResourcesConfigModel model, File outputFile) throws IOException {
- ObjectMapper mapper = new ObjectMapper();
- String pretty = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(model);
+ JSONObject json = toJson(model);
+ String pretty = json.toString(2);
File outputDir = outputFile.getParentFile();
if (outputDir.isDirectory() || outputDir.mkdirs()) {
try (OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(outputFile), StandardCharsets.UTF_8)) {
@@ -59,4 +60,37 @@ public static void serialize(ResourcesConfigModel model, File outputFile) throw
}
}
}
+
+ private static JSONObject toJson(ResourcesConfigModel model) {
+ JSONObject json = new JSONObject();
+ json.put("resources", toJson(model.getResources()));
+ JSONArray namedValues = new JSONArray();
+ model.getBundles().forEach(namedValue -> namedValues.put(toJson(namedValue)));
+ json.put("bundles", namedValues);
+ return json;
+ }
+
+ private static JSONObject toJson(ResourcesModel model) {
+ JSONObject json = new JSONObject();
+ JSONArray includes = new JSONArray();
+ model.getIncludes().forEach(patternValue -> includes.put(toJson(patternValue)));
+ json.put("includes", includes);
+ JSONArray excludes = new JSONArray();
+ model.getExcludes().forEach(patternValue -> excludes.put(toJson(patternValue)));
+ json.put("excludes", excludes);
+ return json;
+ }
+
+ private static JSONObject toJson(PatternValue patternValue) {
+ JSONObject json = new JSONObject();
+ json.put("pattern", patternValue.getPattern());
+ return json;
+ }
+
+ private static JSONObject toJson(NamedValue namedValue) {
+ JSONObject json = new JSONObject();
+ json.put("name", namedValue.getName());
+ return json;
+ }
+
}
diff --git a/common/utils/src/main/java/org/graalvm/buildtools/utils/ExponentialBackoff.java b/common/utils/src/main/java/org/graalvm/buildtools/utils/ExponentialBackoff.java
new file mode 100644
index 000000000..d44ab3206
--- /dev/null
+++ b/common/utils/src/main/java/org/graalvm/buildtools/utils/ExponentialBackoff.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * The Universal Permissive License (UPL), Version 1.0
+ *
+ * Subject to the condition set forth below, permission is hereby granted to any
+ * person obtaining a copy of this software, associated documentation and/or
+ * data (collectively the "Software"), free of charge and under any and all
+ * copyright rights in the Software, and any and all patent rights owned or
+ * freely licensable by each licensor hereunder covering either (i) the
+ * unmodified Software as contributed to or provided by such licensor, or (ii)
+ * the Larger Works (as defined below), to deal in both
+ *
+ * (a) the Software, and
+ *
+ * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+ * one is included with the Software each a "Larger Work" to which the Software
+ * is contributed by such licensors),
+ *
+ * without restriction, including without limitation the rights to copy, create
+ * derivative works of, display, perform, and distribute the Software and make,
+ * use, sell, offer for sale, import, export, have made, and have sold the
+ * Software and the Larger Work(s), and to sublicense the foregoing rights on
+ * either these or other terms.
+ *
+ * This license is subject to the following condition:
+ *
+ * The above copyright notice and either this complete permission notice or at a
+ * minimum a reference to the UPL must be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package org.graalvm.buildtools.utils;
+
+import java.time.Duration;
+import java.time.temporal.ChronoUnit;
+
+/**
+ * An utility class for exponential backoff of operations which
+ * can fail and can be retried.
+ */
+public class ExponentialBackoff {
+ private static final int DEFAULT_MAX_RETRIES = 3;
+ private static final Duration DEFAULT_INITIAL_WAIT_PERIOD = Duration.of(250, ChronoUnit.MILLIS);
+
+ private final int maxRetries;
+ private final Duration initialWaitPeriod;
+
+ public ExponentialBackoff() {
+ this(DEFAULT_MAX_RETRIES, DEFAULT_INITIAL_WAIT_PERIOD);
+ }
+
+ private ExponentialBackoff(int maxRetries, Duration initialWaitPeriod) {
+ if (maxRetries < 1) {
+ throw new IllegalArgumentException("Max retries must be at least 1");
+ }
+ if (initialWaitPeriod.isNegative() || initialWaitPeriod.isZero()) {
+ throw new IllegalArgumentException("Initial backoff wait delay must be strictly positive");
+ }
+ this.maxRetries = maxRetries;
+ this.initialWaitPeriod = initialWaitPeriod;
+ }
+
+ public static ExponentialBackoff get() {
+ return new ExponentialBackoff();
+ }
+
+ /**
+ * The maximum number of retries.
+ *
+ * @param maxRetries the maximum number of retries
+ * @return an exponential backoff with the specified number of retries
+ */
+ public ExponentialBackoff withMaxRetries(int maxRetries) {
+ return new ExponentialBackoff(maxRetries, initialWaitPeriod);
+ }
+
+ /**
+ * The initial backoff duration, that is to say the time we will wait
+ * before the first retry (there's no wait for the initial attempt).
+ *
+ * @param duration the duration for the first retry
+ * @return an exponential backoff with the specified initial wait period
+ */
+ public ExponentialBackoff withInitialWaitPeriod(Duration duration) {
+ return new ExponentialBackoff(maxRetries, duration);
+ }
+
+ /**
+ * Executes an operation which returns a result. Retries a maximum number of
+ * times by multiplying the delay between each attempt by 2.
+ * @param supplier the operation to execute
+ * @return the result of the operation
+ * @param the type of the result
+ */
+ public T supply(FailableSupplier supplier) {
+ int attempts = maxRetries + 1;
+ Duration waitPeriod = initialWaitPeriod;
+ Exception last = null;
+ while (attempts > 0) {
+ try {
+ return supplier.get();
+ } catch (Exception ex) {
+ last = ex;
+ attempts--;
+ try {
+ Thread.sleep(waitPeriod.toMillis());
+ waitPeriod = waitPeriod.multipliedBy(2);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new RetriableOperationFailedException("Thread was interrupted", e);
+ }
+ }
+ }
+ throw new RetriableOperationFailedException("Operation failed after " + maxRetries + " retries", last);
+ }
+
+ /**
+ * Executes an operation which doesn't return any result, until it passes,
+ * with this exponential backoff parameters.
+ * See {@link #supply(FailableSupplier)} for an operation which returns a result.
+ * @param operation the operation to execute.
+ */
+ public void execute(FailableOperation operation) {
+ supply(() -> {
+ operation.run();
+ return null;
+ });
+ }
+
+ @FunctionalInterface
+ public interface FailableOperation {
+ void run() throws Exception;
+ }
+
+ @FunctionalInterface
+ public interface FailableSupplier {
+ T get() throws Exception;
+ }
+
+ public static final class RetriableOperationFailedException extends RuntimeException {
+ public RetriableOperationFailedException(String message, Throwable cause) {
+ super(message, cause);
+ }
+ }
+}
diff --git a/common/utils/src/main/java/org/graalvm/buildtools/utils/FileUtils.java b/common/utils/src/main/java/org/graalvm/buildtools/utils/FileUtils.java
index 5c8438ecd..45cfb133b 100644
--- a/common/utils/src/main/java/org/graalvm/buildtools/utils/FileUtils.java
+++ b/common/utils/src/main/java/org/graalvm/buildtools/utils/FileUtils.java
@@ -45,6 +45,7 @@
import java.math.BigInteger;
import java.net.HttpURLConnection;
import java.net.URI;
+import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
@@ -67,6 +68,13 @@ public static String normalizePathSeparators(String path) {
}
public static Optional download(URL url, Path destination, Consumer errorLogger) {
+ if ("file".equals(url.getProtocol())) {
+ try {
+ return Optional.of(new java.io.File(url.toURI()).toPath());
+ } catch (URISyntaxException e) {
+ return Optional.empty();
+ }
+ }
try {
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
diff --git a/common/utils/src/main/java/org/graalvm/buildtools/utils/JUnitPlatformNativeDependenciesHelper.java b/common/utils/src/main/java/org/graalvm/buildtools/utils/JUnitPlatformNativeDependenciesHelper.java
new file mode 100644
index 000000000..3f41d2286
--- /dev/null
+++ b/common/utils/src/main/java/org/graalvm/buildtools/utils/JUnitPlatformNativeDependenciesHelper.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2003-2021 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.graalvm.buildtools.utils;
+
+import java.util.List;
+
+public abstract class JUnitPlatformNativeDependenciesHelper {
+
+ private static final DependencyNotation JUNIT_PLATFORM_LAUNCHER = new DependencyNotation(
+ "org.junit.platform", "junit-platform-launcher", ""
+ );
+ private static final DependencyNotation JUNIT_PLATFORM_ENGINE = new DependencyNotation(
+ "org.junit.platform", "junit-platform-engine", ""
+ );
+ private static final DependencyNotation JUNIT_PLATFORM_CONSOLE = new DependencyNotation(
+ "org.junit.platform", "junit-platform-console", ""
+ );
+ private static final DependencyNotation JUNIT_PLATFORM_REPORTING = new DependencyNotation(
+ "org.junit.platform", "junit-platform-reporting", ""
+ );
+
+ private static final List JUNIT_PLATFORM_DEPENDENCIES = List.of(
+ JUNIT_PLATFORM_LAUNCHER,
+ JUNIT_PLATFORM_CONSOLE,
+ JUNIT_PLATFORM_REPORTING
+ );
+
+ private JUnitPlatformNativeDependenciesHelper() {
+
+ }
+
+ /**
+ * Returns the list of dependencies which should be added to the
+ * native test classpath in order for tests to execute.
+ * @param input the current list of dependencies
+ * @return a list of dependencies which need to be added
+ */
+ public static List inferMissingDependenciesForTestRuntime(
+ List input
+ ) {
+ var junitPlatformVersion = input.stream()
+ .filter(d -> d.equalsIgnoreVersion(JUNIT_PLATFORM_ENGINE))
+ .findFirst()
+ .map(DependencyNotation::version)
+ .orElse("");
+ var list = JUNIT_PLATFORM_DEPENDENCIES.stream()
+ .filter(d -> input.stream().noneMatch(o -> o.equalsIgnoreVersion(d)))
+ .map(d -> d.withVersion(junitPlatformVersion))
+ .toList();
+ return list;
+ }
+
+
+ public record DependencyNotation(
+ String groupId,
+ String artifactId,
+ String version
+ ) {
+ public boolean equalsIgnoreVersion(DependencyNotation other) {
+ return other.groupId.equals(groupId) &&
+ other.artifactId.equals(artifactId);
+ }
+
+ public DependencyNotation withVersion(String version) {
+ return new DependencyNotation(groupId, artifactId, version);
+ }
+ }
+}
diff --git a/common/utils/src/main/java/org/graalvm/buildtools/utils/JUnitUtils.java b/common/utils/src/main/java/org/graalvm/buildtools/utils/JUnitUtils.java
new file mode 100644
index 000000000..e8dd7cf70
--- /dev/null
+++ b/common/utils/src/main/java/org/graalvm/buildtools/utils/JUnitUtils.java
@@ -0,0 +1,17 @@
+package org.graalvm.buildtools.utils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public final class JUnitUtils {
+
+ public static List excludeJUnitClassInitializationFiles() {
+ List args = new ArrayList<>();
+ args.add("--exclude-config");
+ args.add(".*.jar");
+ args.add("META-INF\\/native-image\\/org.junit.*.properties.*");
+
+ return args;
+ }
+
+}
diff --git a/common/utils/src/main/java/org/graalvm/buildtools/utils/JarMetadata.java b/common/utils/src/main/java/org/graalvm/buildtools/utils/JarMetadata.java
new file mode 100644
index 000000000..042b8a8be
--- /dev/null
+++ b/common/utils/src/main/java/org/graalvm/buildtools/utils/JarMetadata.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2003-2021 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.graalvm.buildtools.utils;
+
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Properties;
+
+public class JarMetadata {
+ private final List packageList;
+
+ public JarMetadata(List packageList) {
+ this.packageList = packageList;
+ }
+
+ public List getPackageList() {
+ return packageList;
+ }
+
+ public static JarMetadata readFrom(Path propertiesFile) {
+ Properties props = new Properties();
+ try (InputStream is = Files.newInputStream(propertiesFile)) {
+ props.load(is);
+ } catch (Exception e) {
+ throw new RuntimeException("Unable to read metadata from properties file " + propertiesFile, e);
+ }
+ String packages = (String) props.get("packages");
+ List packageList = packages == null ? List.of() : Arrays.asList(packages.split(","));
+ return new JarMetadata(packageList);
+ }
+}
diff --git a/common/utils/src/main/java/org/graalvm/buildtools/utils/JarScanner.java b/common/utils/src/main/java/org/graalvm/buildtools/utils/JarScanner.java
new file mode 100644
index 000000000..4a4d7e29a
--- /dev/null
+++ b/common/utils/src/main/java/org/graalvm/buildtools/utils/JarScanner.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2003-2021 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.graalvm.buildtools.utils;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.stream.Stream;
+
+/**
+ * Performs scanning of a JAR file and extracts some metadata in the
+ * form of a properties file. For now this type only extracts the list
+ * of packages from a jar.
+ */
+public class JarScanner {
+ /**
+ * Scans a jar and creates a properties file with metadata about the jar contents.
+ * @param inputJar the input jar
+ * @param outputFile the output file
+ * @throws IOException
+ */
+ public static void scanJar(Path inputJar, Path outputFile) throws IOException {
+ try (Writer fileWriter = Files.newBufferedWriter(outputFile); PrintWriter writer = new PrintWriter(fileWriter)) {
+ Set packageList = new TreeSet<>();
+ try (FileSystem jarFileSystem = FileSystems.newFileSystem(inputJar, (ClassLoader) null)) {
+ Path root = jarFileSystem.getPath("/");
+ try (Stream files = Files.walk(root)) {
+ files.forEach(path -> {
+ if (path.toString().endsWith(".class") && !path.toString().contains("META-INF")) {
+ Path relativePath = root.relativize(path);
+ String className = relativePath.toString()
+ .replace('/', '.')
+ .replace('\\', '.')
+ .replaceAll("[.]class$", "");
+ var lastDot = className.lastIndexOf(".");
+ if (lastDot > 0) {
+ var packageName = className.substring(0, lastDot);
+ packageList.add(packageName);
+ }
+ }
+ });
+ }
+ }
+ writer.println("packages=" + String.join(",", packageList));
+ } catch (IOException ex) {
+ throw new RuntimeException("Unable to write JAR analysis", ex);
+ }
+ }
+}
diff --git a/common/utils/src/main/java/org/graalvm/buildtools/utils/NativeImageUtils.java b/common/utils/src/main/java/org/graalvm/buildtools/utils/NativeImageUtils.java
index b0b3a485e..2e1949bf0 100644
--- a/common/utils/src/main/java/org/graalvm/buildtools/utils/NativeImageUtils.java
+++ b/common/utils/src/main/java/org/graalvm/buildtools/utils/NativeImageUtils.java
@@ -49,11 +49,25 @@
import java.nio.file.StandardOpenOption;
import java.util.Collections;
import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static org.graalvm.buildtools.utils.SharedConstants.GRAALVM_EXE_EXTENSION;
public class NativeImageUtils {
+ public static final String ORACLE_GRAALVM_IDENTIFIER = "Oracle GraalVM";
+
+ private static final Pattern requiredVersionPattern = Pattern.compile("^([0-9]+)(?:\\.([0-9]+)?)?(?:\\.([0-9]+)?)?$");
+
+ private static final Pattern graalvmVersionPattern = Pattern.compile("^(GraalVM|native-image) ([0-9]+)\\.([0-9]+)\\.([0-9]+).*", Pattern.DOTALL);
+
+ private static final Pattern javaVersionPattern = Pattern.compile("^native-image ([0-9]+).*", Pattern.DOTALL);
+ private static final Pattern javaVersionLegacyPattern = Pattern.compile(".* \\(Java Version ([0-9]+)\\.([0-9]+)\\.([0-9]+).*");
+
+
+ private static final Pattern SAFE_SHELL_ARG = Pattern.compile("[A-Za-z0-9@%_\\-+=:,./]+");
+
public static void maybeCreateConfigureUtilSymlink(File configureUtilFile, Path nativeImageExecutablePath) {
if (!configureUtilFile.exists()) {
// possibly the symlink is missing
@@ -95,11 +109,81 @@ public static List convertToArgsFile(List cliArgs, Path outputDi
}
}
+
public static String escapeArg(String arg) {
- arg = arg.replace("\\", "\\\\");
- if (arg.contains(" ")) {
- arg = "\"" + arg + "\"";
+ if (!(arg.startsWith("\\Q") && arg.endsWith("\\E"))) {
+ arg = arg.replace("\\", "\\\\");
+ if (arg.contains(" ")) {
+ arg = "\"" + arg + "\"";
+ }
}
return arg;
}
+
+
+ /**
+ *
+ * @param requiredVersion Required version can be {@code MAJOR}, {@code MAJOR.MINOR} or {@code MAJOR.MINOR.PATCH}
+ * @param versionToCheck The version to check, as returned by {@code native-image --version}
+ * @throws IllegalStateException when the version is not correct
+ */
+ public static void checkVersion(String requiredVersion, String versionToCheck) {
+ if (versionToCheck.contains("GraalVM Runtime Environment")) {
+ return; // later than 22.3.1 (e.g., GraalVM for JDK 17 / GraalVM for JDK 20)
+ }
+ if ((versionToCheck.startsWith("native-image") && versionToCheck.contains("dev")) ||
+ versionToCheck.startsWith("GraalVM dev")) { /* For GraalVM 22.3 and earlier */
+ return;
+ }
+ Matcher requiredMatcher = requiredVersionPattern.matcher(requiredVersion);
+ if (!requiredMatcher.matches()) {
+ throw new IllegalArgumentException("Invalid version " + requiredVersion + ", should be for example \"22\", \"22.3\" or \"22.3.0\".");
+ }
+ Matcher checkedMatcher = graalvmVersionPattern.matcher(versionToCheck.trim());
+ if (!checkedMatcher.matches()) {
+ throw new IllegalArgumentException("Version to check '" + versionToCheck + "' can't be parsed.");
+ }
+ int requiredMajor = Integer.parseInt(requiredMatcher.group(1));
+ int checkedMajor = Integer.parseInt(checkedMatcher.group(2));
+ if (checkedMajor < requiredMajor) {
+ throw new IllegalStateException("GraalVM version " + requiredMajor + " is required but " + checkedMajor +
+ " has been detected, please upgrade.");
+ }
+ if (checkedMajor > requiredMajor) {
+ return;
+ }
+ if (requiredMatcher.group(2) != null) {
+ int requiredMinor = Integer.parseInt(requiredMatcher.group(2));
+ int checkedMinor = Integer.parseInt(checkedMatcher.group(3));
+ if (checkedMinor < requiredMinor) {
+ throw new IllegalStateException("GraalVM version " + requiredMajor + "." + requiredMinor +
+ " is required but " + checkedMajor + "." + checkedMinor + " has been detected, please upgrade.");
+ }
+ if (checkedMinor > requiredMinor) {
+ return;
+ }
+ if (requiredMatcher.group(3) != null) {
+ int requiredPatch = Integer.parseInt(requiredMatcher.group(3));
+ int checkedPatch = Integer.parseInt(checkedMatcher.group(4));
+ if (checkedPatch < requiredPatch) {
+ throw new IllegalStateException("GraalVM version " + requiredMajor + "." + requiredMinor + "." +
+ requiredPatch + " is required but " + checkedMajor + "." + checkedMinor + "." + checkedPatch +
+ " has been detected, please upgrade.");
+ }
+ }
+ }
+ }
+
+ public static int getMajorJDKVersion(String versionString) {
+ String trimmedVersionString = versionString.trim();
+ Matcher matcher = javaVersionPattern.matcher(versionString.trim());
+ if (matcher.matches()) {
+ return Integer.parseInt(matcher.group(1));
+ }
+ Matcher legacyMatcher = javaVersionLegacyPattern.matcher(trimmedVersionString);
+ if (legacyMatcher.matches()) {
+ return Integer.parseInt(legacyMatcher.group(1));
+ }
+ return -1;
+ }
}
diff --git a/common/utils/src/main/java/org/graalvm/buildtools/utils/SharedConstants.java b/common/utils/src/main/java/org/graalvm/buildtools/utils/SharedConstants.java
index e67851a8f..8f5ba8efb 100644
--- a/common/utils/src/main/java/org/graalvm/buildtools/utils/SharedConstants.java
+++ b/common/utils/src/main/java/org/graalvm/buildtools/utils/SharedConstants.java
@@ -41,6 +41,8 @@
package org.graalvm.buildtools.utils;
+import org.graalvm.buildtools.VersionInfo;
+
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -76,5 +78,11 @@ public interface SharedConstants {
String AGENT_OUTPUT_DIRECTORY_MARKER = "{output_dir}";
String AGENT_OUTPUT_DIRECTORY_OPTION = "config-output-dir=";
String METADATA_REPO_URL_TEMPLATE = "https://github.com/oracle/graalvm-reachability-metadata/releases/download/%1$s/graalvm-reachability-metadata-%1$s.zip";
- String METADATA_REPO_DEFAULT_VERSION = "0.2.0";
+ /**
+ * The default metadata repository version. Maintained for backwards
+ * compatibility.
+ * @deprecated Please use {@link VersionInfo#METADATA_REPO_VERSION} instead
+ */
+ @Deprecated
+ String METADATA_REPO_DEFAULT_VERSION = VersionInfo.METADATA_REPO_VERSION;
}
diff --git a/common/utils/src/main/resources/access-filter.json b/common/utils/src/main/resources/access-filter.json
new file mode 100644
index 000000000..4781418b7
--- /dev/null
+++ b/common/utils/src/main/resources/access-filter.json
@@ -0,0 +1,13 @@
+{
+ "rules": [
+ {
+ "includeClasses": "**"
+ },
+ {
+ "excludeClasses": "org.gradle.**"
+ },
+ {
+ "excludeClasses": "org.junit.**"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/common/utils/src/test/java/org/graalvm/buildtools/utils/ExponentialBackoffTest.java b/common/utils/src/test/java/org/graalvm/buildtools/utils/ExponentialBackoffTest.java
new file mode 100644
index 000000000..c03c68d25
--- /dev/null
+++ b/common/utils/src/test/java/org/graalvm/buildtools/utils/ExponentialBackoffTest.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * The Universal Permissive License (UPL), Version 1.0
+ *
+ * Subject to the condition set forth below, permission is hereby granted to any
+ * person obtaining a copy of this software, associated documentation and/or
+ * data (collectively the "Software"), free of charge and under any and all
+ * copyright rights in the Software, and any and all patent rights owned or
+ * freely licensable by each licensor hereunder covering either (i) the
+ * unmodified Software as contributed to or provided by such licensor, or (ii)
+ * the Larger Works (as defined below), to deal in both
+ *
+ * (a) the Software, and
+ *
+ * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+ * one is included with the Software each a "Larger Work" to which the Software
+ * is contributed by such licensors),
+ *
+ * without restriction, including without limitation the rights to copy, create
+ * derivative works of, display, perform, and distribute the Software and make,
+ * use, sell, offer for sale, import, export, have made, and have sold the
+ * Software and the Larger Work(s), and to sublicense the foregoing rights on
+ * either these or other terms.
+ *
+ * This license is subject to the following condition:
+ *
+ * The above copyright notice and either this complete permission notice or at a
+ * minimum a reference to the UPL must be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package org.graalvm.buildtools.utils;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import java.time.Duration;
+import java.time.temporal.ChronoUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class ExponentialBackoffTest {
+ @Test
+ @DisplayName("executed a passing operation")
+ void simpleExecution() {
+ AtomicBoolean success = new AtomicBoolean();
+ ExponentialBackoff.get().execute(() -> success.set(true));
+ assertTrue(success.get());
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = {1, 3})
+ @DisplayName("retries expected amount of times")
+ void countRetries(int retries) {
+ AtomicInteger count = new AtomicInteger();
+ assertThrows(ExponentialBackoff.RetriableOperationFailedException.class, () -> ExponentialBackoff.get().withMaxRetries(retries)
+ .execute(() -> {
+ count.incrementAndGet();
+ throw new RuntimeException();
+ }));
+ assertEquals(retries + 1, count.get());
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = {1, 3})
+ @DisplayName("passes after one retry")
+ void passAfterRetry(int retries) {
+ AtomicInteger count = new AtomicInteger();
+ int result = ExponentialBackoff.get().withMaxRetries(retries)
+ .supply(() -> {
+ if (count.getAndIncrement() == 0) {
+ throw new RuntimeException();
+ }
+ return 200;
+ });
+ assertEquals(2, count.get());
+ assertEquals(200, result);
+ }
+
+ @Test
+ @DisplayName("can configure initial backoff time")
+ void canConfigureInitialBackoffTime() {
+ double sd = System.currentTimeMillis();
+ assertThrows(ExponentialBackoff.RetriableOperationFailedException.class, () -> ExponentialBackoff.get()
+ .withMaxRetries(4)
+ .withInitialWaitPeriod(Duration.of(1, ChronoUnit.MILLIS))
+ .execute(() -> {
+ throw new RuntimeException();
+ }));
+ double duration = System.currentTimeMillis() - sd;
+ assertTrue(duration < 100);
+ }
+
+}
diff --git a/common/utils/src/test/java/org/graalvm/buildtools/utils/FileUtilsTest.java b/common/utils/src/test/java/org/graalvm/buildtools/utils/FileUtilsTest.java
index c266c2e7a..48fdc92fc 100644
--- a/common/utils/src/test/java/org/graalvm/buildtools/utils/FileUtilsTest.java
+++ b/common/utils/src/test/java/org/graalvm/buildtools/utils/FileUtilsTest.java
@@ -80,21 +80,21 @@ void testDownloadOk(@TempDir Path tempDir) throws IOException {
@Test
@DisplayName("It doesn't blow up with a URL that isn't a file download")
void testDownloadNoFile(@TempDir Path tempDir) throws IOException {
- URL url = new URL("https://httpstat.us/200");
+ URL url = new URL("https://httpbin.org/html");
List errorLogs = new ArrayList<>();
Optional download = FileUtils.download(url, tempDir, errorLogs::add);
System.out.println("errorLogs = " + errorLogs);
assertTrue(download.isPresent());
- assertEquals("200", download.get().getFileName().toString());
+ assertEquals("html", download.get().getFileName().toString());
assertEquals(0, errorLogs.size());
}
@Test
@DisplayName("It doesn't blow up with a URL that does not exist")
void testDownloadNotFound(@TempDir Path tempDir) throws IOException {
- URL url = new URL("https://httpstat.us/404");
+ URL url = new URL("https://google.com/notfound");
List errorLogs = new ArrayList<>();
Optional download = FileUtils.download(url, tempDir, errorLogs::add);
@@ -107,7 +107,7 @@ void testDownloadNotFound(@TempDir Path tempDir) throws IOException {
@Test
@DisplayName("It doesn't blow up with connection timeouts")
void testDownloadTimeout(@TempDir Path tempDir) throws IOException {
- URL url = new URL("https://httpstat.us/200?sleep=" + (FileUtils.READ_TIMEOUT + 1000));
+ URL url = new URL("https://httpbin.org/delay/10");
List errorLogs = new ArrayList<>();
Optional download = FileUtils.download(url, tempDir, errorLogs::add);
diff --git a/common/utils/src/test/java/org/graalvm/buildtools/utils/NativeImageUtilsTest.java b/common/utils/src/test/java/org/graalvm/buildtools/utils/NativeImageUtilsTest.java
new file mode 100644
index 000000000..8ed151efd
--- /dev/null
+++ b/common/utils/src/test/java/org/graalvm/buildtools/utils/NativeImageUtilsTest.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * The Universal Permissive License (UPL), Version 1.0
+ *
+ * Subject to the condition set forth below, permission is hereby granted to any
+ * person obtaining a copy of this software, associated documentation and/or
+ * data (collectively the "Software"), free of charge and under any and all
+ * copyright rights in the Software, and any and all patent rights owned or
+ * freely licensable by each licensor hereunder covering either (i) the
+ * unmodified Software as contributed to or provided by such licensor, or (ii)
+ * the Larger Works (as defined below), to deal in both
+ *
+ * (a) the Software, and
+ *
+ * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+ * one is included with the Software each a "Larger Work" to which the Software
+ * is contributed by such licensors),
+ *
+ * without restriction, including without limitation the rights to copy, create
+ * derivative works of, display, perform, and distribute the Software and make,
+ * use, sell, offer for sale, import, export, have made, and have sold the
+ * Software and the Larger Work(s), and to sublicense the foregoing rights on
+ * either these or other terms.
+ *
+ * This license is subject to the following condition:
+ *
+ * The above copyright notice and either this complete permission notice or at a
+ * minimum a reference to the UPL must be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package org.graalvm.buildtools.utils;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.regex.Pattern;
+
+class NativeImageUtilsTest {
+
+ @Test
+ void invalidVersionToCheck() {
+ Assertions.assertThrows(IllegalArgumentException.class, () ->
+ NativeImageUtils.checkVersion("22.3", "invalid"));
+ Assertions.assertThrows(IllegalArgumentException.class, () ->
+ NativeImageUtils.checkVersion("22.3", "GraalVM"));
+ }
+
+ @Test
+ void invalidRequiredVersion() {
+ Assertions.assertThrows(IllegalArgumentException.class, () ->
+ NativeImageUtils.checkVersion("invalid", "GraalVM 22.3.0"));
+ Assertions.assertThrows(IllegalArgumentException.class, () ->
+ NativeImageUtils.checkVersion("22.3.0-dev", "GraalVM 22.3.0"));
+ }
+
+ @Test
+ void checkGraalVMCEVersion() {
+ String graalVMCE223 = "GraalVM 22.3.0 Java 17 CE (Java Version 17.0.5+8-jvmci-22.3-b08)";
+ NativeImageUtils.checkVersion("22", graalVMCE223);
+ NativeImageUtils.checkVersion("22.3", graalVMCE223);
+ NativeImageUtils.checkVersion("22.3.0", graalVMCE223);
+ Assertions.assertEquals(17, NativeImageUtils.getMajorJDKVersion(graalVMCE223));
+
+ String graalVMCEForJDK17 = "native-image 17.0.7 2023-04-18\nGraalVM Runtime Environment GraalVM CE 17.0.7+4.1 (build 17.0.7+4-jvmci-23.0-b10)\nSubstrate VM GraalVM CE 17.0.7+4.1 (build 17.0.7+4, serial gc)";
+ NativeImageUtils.checkVersion("22.3.0", graalVMCEForJDK17);
+ NativeImageUtils.checkVersion("23", graalVMCEForJDK17);
+ NativeImageUtils.checkVersion("23.0", graalVMCEForJDK17);
+ NativeImageUtils.checkVersion("23.0.0", graalVMCEForJDK17);
+ Assertions.assertEquals(17, NativeImageUtils.getMajorJDKVersion(graalVMCEForJDK17));
+
+ String graalVMCEForJDK20 = "native-image 20 2023-04-18\nGraalVM Runtime Environment GraalVM CE 20+34.1 (build 20+34-jvmci-23.0-b10)\nSubstrate VM GraalVM CE 20+34.1 (build 20+34, serial gc)";
+ NativeImageUtils.checkVersion("22.3.0", graalVMCEForJDK20);
+ NativeImageUtils.checkVersion("23", graalVMCEForJDK20);
+ NativeImageUtils.checkVersion("23.0", graalVMCEForJDK20);
+ NativeImageUtils.checkVersion("23.0.0", graalVMCEForJDK20);
+ Assertions.assertEquals(20, NativeImageUtils.getMajorJDKVersion(graalVMCEForJDK20));
+
+ String graalVMCEForJDK21 = "native-image 21 2023-09-19\nGraalVM Runtime Environment GraalVM CE 21+35.1 (build 21+35-jvmci-23.1-b15)\nSubstrate VM GraalVM CE 21+35.1 (build 21+35, serial gc)";
+ NativeImageUtils.checkVersion("22.3.0", graalVMCEForJDK20);
+ NativeImageUtils.checkVersion("23", graalVMCEForJDK20);
+ NativeImageUtils.checkVersion("23.0", graalVMCEForJDK20);
+ NativeImageUtils.checkVersion("23.1", graalVMCEForJDK20);
+ NativeImageUtils.checkVersion("23.1.0", graalVMCEForJDK20);
+ Assertions.assertEquals(21, NativeImageUtils.getMajorJDKVersion(graalVMCEForJDK21));
+ }
+
+ @Test
+ void checkGraalVMCEDevVersion() {
+ NativeImageUtils.checkVersion("22", "GraalVM 22.3.0-dev Java 17 CE (Java Version 17.0.5+8-LTS)");
+ NativeImageUtils.checkVersion("22.3", "GraalVM 22.3.0-dev Java 17 CE (Java Version 17.0.5+8-LTS)");
+ NativeImageUtils.checkVersion("22.3.0", "GraalVM 22.3.0-dev Java 17 CE (Java Version 17.0.5+8-LTS)");
+ NativeImageUtils.checkVersion("22.3.0", "GraalVM 23.0.0-dev Java 17.0.6+2-jvmci-23.0-b04 CE (Java Version 17.0.6+2-jvmci-23.0-b04)");
+ NativeImageUtils.checkVersion("22.3.0", "GraalVM dev CE (Java Version 19+36-jvmci-23.0-b01)");
+ NativeImageUtils.checkVersion("22.3.0", "native-image dev CE (Java Version 19+36-jvmci-23.0-b01)");
+ String latestGraalVMDevFormat = "native-image 21 2023-09-19\nGraalVM Runtime Environment GraalVM CE 21-dev+35.1 (build 21+35-jvmci-23.1-b15)\nSubstrate VM GraalVM CE 21-dev+35.1 (build 21+35, serial gc)";
+ NativeImageUtils.checkVersion("22.3.0", latestGraalVMDevFormat);
+ }
+
+ @Test
+ void checkGraalVMEEVersion() {
+ NativeImageUtils.checkVersion("22", "GraalVM 22.3.0 Java 17 EE (Java Version 17.0.5+9-LTS-jvmci-22.3-b07)");
+ NativeImageUtils.checkVersion("22.3", "GraalVM 22.3.0 Java 17 EE (Java Version 17.0.5+9-LTS-jvmci-22.3-b07)");
+ NativeImageUtils.checkVersion("22.3.0", "GraalVM 22.3.0 Java 17 EE (Java Version 17.0.5+9-LTS-jvmci-22.3-b07)");
+ }
+
+ @Test
+ void checkOracleGraalVMVersion() {
+ String oracleGraalVMForJDK17 = "native-image 17.0.7 2023-04-18\nGraalVM Runtime Environment Oracle GraalVM (build 17.0.7+8-jvmci-23.0-b10)\nSubstrate VM Oracle GraalVM (build 17.0.7+8, serial gc)";
+ NativeImageUtils.checkVersion("22.3.0", oracleGraalVMForJDK17);
+ NativeImageUtils.checkVersion("23", oracleGraalVMForJDK17);
+ NativeImageUtils.checkVersion("23.0", oracleGraalVMForJDK17);
+ NativeImageUtils.checkVersion("23.0.0", oracleGraalVMForJDK17);
+ Assertions.assertEquals(17, NativeImageUtils.getMajorJDKVersion(oracleGraalVMForJDK17));
+
+ String oracleGraalVMForJDK20 = "native-image 20.0.1 2023-04-18\nGraalVM Runtime Environment Oracle GraalVM 20.0.1+9.1 (build 20.0.1+9-jvmci-23.0-b10)\nSubstrate VM Oracle GraalVM 20.0.1+9.1 (build 20.0.1+9, serial gc)";
+ NativeImageUtils.checkVersion("22.3.0", oracleGraalVMForJDK20);
+ NativeImageUtils.checkVersion("23", oracleGraalVMForJDK20);
+ NativeImageUtils.checkVersion("23.0", oracleGraalVMForJDK20);
+ NativeImageUtils.checkVersion("23.0.0", oracleGraalVMForJDK20);
+ Assertions.assertEquals(20, NativeImageUtils.getMajorJDKVersion(oracleGraalVMForJDK20));
+
+ String oracleGraalVMForJDK21 = "native-image 21 2023-09-19\nGraalVM Runtime Environment Oracle GraalVM 21+35.1 (build 21+35-jvmci-23.1-b15)\nSubstrate VM Oracle GraalVM 21+35.1 (build 21+35, serial gc, compressed references)";
+ NativeImageUtils.checkVersion("22.3.0", oracleGraalVMForJDK21);
+ NativeImageUtils.checkVersion("23", oracleGraalVMForJDK21);
+ NativeImageUtils.checkVersion("23.1", oracleGraalVMForJDK21);
+ NativeImageUtils.checkVersion("23.1.0", oracleGraalVMForJDK21);
+ Assertions.assertEquals(21, NativeImageUtils.getMajorJDKVersion(oracleGraalVMForJDK21));
+ }
+
+ @Test
+ void checkOtherGraalVMVersion() {
+ String otherGraalVMForJDK23 = "native-image 23.0.1 2024-10-15\nJava(TM) SE Runtime Environment Foo Bar 23.0.1+11.1 (build 23.0.1+11-foo-bar-jvmci-b01)\n" +
+ "Java HotSpot(TM) 64-Bit Server VM Foo Bar 23.0.1+11.1 (build 23.0.1+11-foo-bar-jvmci-b01, mixed mode, sharing)";
+ NativeImageUtils.checkVersion("22.3.0", otherGraalVMForJDK23);
+ NativeImageUtils.checkVersion("23", otherGraalVMForJDK23);
+ NativeImageUtils.checkVersion("23.0", otherGraalVMForJDK23);
+ NativeImageUtils.checkVersion("23.0.1", otherGraalVMForJDK23);
+ Assertions.assertEquals(23, NativeImageUtils.getMajorJDKVersion(otherGraalVMForJDK23));
+ }
+
+ @Test
+ void checkGreaterVersion() {
+ NativeImageUtils.checkVersion("22", "GraalVM 23.2.1");
+ NativeImageUtils.checkVersion("23.1", "GraalVM 23.2.1");
+ NativeImageUtils.checkVersion("23.2.0", "GraalVM 23.2.1");
+ NativeImageUtils.checkVersion("22.3.0", "GraalVM 23.0.0");
+ NativeImageUtils.checkVersion("22.2.1", "GraalVM 22.3.0");
+ NativeImageUtils.checkVersion("22.2.1", "native-image 22.3.0");
+ }
+
+ @Test
+ void checkVersionWithTrailingNewLine() {
+ NativeImageUtils.checkVersion("22.3.0", "GraalVM 22.3.0\n");
+ }
+
+ @Test
+ void checkLowerVersion() {
+ Assertions.assertThrows(IllegalStateException.class, () ->
+ NativeImageUtils.checkVersion("23", "GraalVM 22.2.1")
+ );
+ Assertions.assertThrows(IllegalStateException.class, () ->
+ NativeImageUtils.checkVersion("22.3", "GraalVM 22.2.1")
+ );
+ Assertions.assertThrows(IllegalStateException.class, () ->
+ NativeImageUtils.checkVersion("22.2.2", "GraalVM 22.2.1")
+ );
+ }
+
+ @Test
+ void escapeArg() {
+ Assertions.assertEquals("/foo/bar", NativeImageUtils.escapeArg("/foo/bar"));
+ Assertions.assertEquals("c:\\\\foo\\\\bar", NativeImageUtils.escapeArg("c:\\foo\\bar"));
+ Assertions.assertEquals("\"c:\\\\foo\\\\bar baz\"", NativeImageUtils.escapeArg("c:\\foo\\bar baz"));
+ }
+
+ @Test
+ void doNotEscapeQuotedRegexp() {
+ Assertions.assertEquals(Pattern.quote("/foo/bar"), NativeImageUtils.escapeArg(Pattern.quote("/foo/bar")));
+ Assertions.assertEquals(Pattern.quote("c:\\foo\\bar"), NativeImageUtils.escapeArg(Pattern.quote("c:\\foo\\bar")));
+ Assertions.assertEquals(Pattern.quote("c:\\foo\\bar baz"), NativeImageUtils.escapeArg(Pattern.quote("c:\\foo\\bar baz")));
+ }
+
+}
diff --git a/docs/build.gradle.kts b/docs/build.gradle.kts
index 6e603adea..ea9980fdf 100644
--- a/docs/build.gradle.kts
+++ b/docs/build.gradle.kts
@@ -44,7 +44,6 @@ plugins {
}
version = providers.gradleProperty("publishVersion")
- .forUseAtConfigurationTime()
.orElse(libs.versions.nativeBuildTools)
.get()
@@ -70,7 +69,8 @@ asciidoctorj {
"highlightjsdir" to "highlight",
"gradle-plugin-version" to libs.versions.nativeBuildTools.get(),
"gradle-plugin-version" to libs.versions.nativeBuildTools.get(),
- "maven-plugin-version" to libs.versions.nativeBuildTools.get()
+ "maven-plugin-version" to libs.versions.nativeBuildTools.get(),
+ "metadata-repository-version" to libs.versions.metadataRepository.get(),
))
}
diff --git a/docs/src/docs/asciidoc/changelog.adoc b/docs/src/docs/asciidoc/changelog.adoc
new file mode 100644
index 000000000..abb971326
--- /dev/null
+++ b/docs/src/docs/asciidoc/changelog.adoc
@@ -0,0 +1,534 @@
+[[changelog]]
+== Changelog
+
+== Release 0.11.0
+
+- This version introduces a breaking change: the plugins now require Java 17 to run
+- Refactored JUnit support:
+-- new implementation doesn't require list of classes for build-time initialization (starting from GraalVM for JDK 22)
+-- added support for `@FieldSource` for `@ParameterizedTest` methods
+-- added support for (registering for reflection) interfaces implemented by test classes
+-- added support for `org.junit.rules.ExpectedException`
+-- removed fallback mode that used classpath to generate test plan
+
+=== Gradle plugin
+
+- This version introduces a breaking change: the plugin now requires Gradle 8.3+
+- Added experimental support for layered images
+
+=== Maven plugin
+
+- Added support for running integration tests via maven-failsafe-plugin
+- Provided scope dependencies are now included
+- Add `runtimeArgs` support to `native-maven-plugin`
+
+== Release 0.10.6
+
+=== Gradle plugin
+
+- Added options to override parameters from command line
+
+=== Maven plugin
+
+- Add support for dependency exclusions
+
+== Release 0.10.5
+
+- Add missing getters to `DirectoryConfiguration`
+- Refactor Native Build Tools documentation and add end-to-end guides for both Gradle and Maven plugins
+- Restyle Native Build Tools documentation
+- Improve GraalVM version check
+- Upgrade the use of Jackson to plain old JSONObject
+
+==== Gradle plugin
+
+- Fix sporadic failures with: access-filter.json already exists
+
+==== Maven plugin
+
+- Add maven functional testing in github actions
+- Fix Broken Maven Functional Tests when Using New Metadata Format
+- Fix SBOM integration test bug
+- Make metadataCopy task work with custom disabled stages
+
+=== Release 0.10.4
+
+- Improve Native Image SBOM Generation
+- Make accessFilter file registration thread safe
+- Fix broken links in the projects documentation
+
+=== Release 0.10.3
+
+- Remove usage of macro from merger tool initialization and throw better error if executable does not exist
+- Add support for the new reachability-metadata.json config file
+- Remove custom post-processing task for filtering config files entries and use access-filter.json instead
+
+==== Gradle plugin
+
+- Add retries when downloading the metadata repository when using a URL directly
+
+==== Maven plugin
+
+- Add retries when downloading the metadata repository when using a URL directly
+
+
+=== Release 0.10.2
+
+- Fix class path directory analyzer
+- Update Reachability Metadata repository version
+
+==== Gradle plugin
+
+- Update Default Target Directory for MetadataCopy Task
+
+==== Maven plugin
+
+- Update Getting Started with Maven Plugin doc
+- Delete old stale args file
+- Add a parameter to be able to skip build native for pom type modules, leave it as false per default for backward compat
+
+=== Release 0.10.1
+
+- Mark additional JUnit 5 types for build-time initialization for compatibility with Native Image's `--strict-image-heap` option.
+
+
+=== Release 0.10.0
+
+- Update version of GraalVM dependency to 22.3.5
+
+==== Gradle plugin
+
+- Update plugin to use metadata repository by default. Metadata repository <>
+
+==== Maven plugin
+
+- Update plugin to use metadata repository by default. Metadata repository <>
+
+
+=== Release 0.9.28
+
+* Fix path escaping problem for Windows users
+* Fix and improve major JDK version detection.
+
+==== Gradle plugin
+
+- Remove use of deprecated `getConvention` APIs
+
+=== Release 0.9.27
+
+* Update JUnit configuration for native testing on GraalVM for JDK 21 with `--strict-image-heap` mode.
+
+=== Release 0.9.26
+
+* Relax GraalVM version check for dev versions
+* Prepare plugins for release of _GraalVM for JDK 21_. They no longer deploy any experimental options.
+* Bump Java compliance of the plugins from Java 8+ to Java 11+.
+* Fix compatibility with Maven 3.9.
+
+==== Gradle plugin
+
+* Fix compatibility with Gradle 8.3
+
+=== Release 0.9.25
+
+* Upgrade metadata to 0.3.4
+
+=== Release 0.9.24
+
+* Upgrade metadata to 0.3.3
+
+==== Gradle plugin
+
+* Add support for PGO
+
+=== Release 0.9.23
+
+* Upgrade metadata to 0.3.2
+* Display the GraalVM logo on the generated NBT docs
+
+==== Gradle plugin
+
+- Remove use of GFileUtils
+- Fix "collect reachability metadata" with config cache
+
+=== Release 0.9.22
+
+* Upgrade metadata to 0.3.0
+* Add support for default-for attribute
+
+==== Gradle plugin
+
+- Fix lookup of metadata repository
+
+=== Release 0.9.21
+
+* Deprecate `requiredVersion` check without replacement.
+
+==== Gradle plugin
+
+- Bump minimal version of Gradle to 7.4
+- Fix compatibility with Gradle's https://docs.gradle.org/8.0.2/userguide/configuration_cache.html#header[configuration cache] (requires Gradle 7.5+)
+- Remove use of deprecated Gradle APIs
+- [Behavior change] Toolchain detection is now disabled by default
+
+==== Maven plugin
+
+- Add a new `native:write-args-file` goal that can be used to write the arguments passed to `native-image` to a file
+
+=== Release 0.9.20
+
+==== Gradle plugin
+
+- Fix `collectReachabilityMetadata` not being thread-safe
+- Add an option to configure the maximum number of images which can be built in parallel
+- Fix GraalVM version check being too strict
+
+==== Maven plugin
+
+- Add agent modes to Maven plugin
+
+=== Release 0.9.19
+
+==== Gradle plugin
+
+- Fix `nativeCompile` being out-of-date whenever native runtime arguments change
+- Fix GraalVM metadata repository not downloaded from project repositories by default
+
+==== Maven plugin
+
+=== Release 0.9.18
+
+* Trim GraalVM version to fix Windows support
+* Add tests for NativeImageUtils.escapeArg
+* Do not escape quoted regexp args when using argsfile
+* Upgrade to GraalVM metadata repository 0.2.5
+
+=== Release 0.9.17
+
+* Add a `requiredVersion` property to check the minimal GraalVM version
+* Make GraalVM installation check lazy
+
+=== Release 0.9.16
+
+* Fixed regression with a reachability-metadata repository
+
+=== Release 0.9.15
+
+* Upgrade to GraalVM metadata repository 0.2.3.
+* Ship the metadata repository as an artifact alongside the plugin
+* Add ability to collect GraalVM metadata of dependencies to a custom location
+
+==== Gradle plugin
+
+* Improved diagnostics to help users figure out what GraalVM toolchain was selected
+
+=== Release 0.9.14
+
+==== Gradle plugin
+* Add ability to set environment variables to the native image builder process
+* Argument files are now stored in the `build` directory (workaround for absolute path issue on Windows with older GraalVM versions)
+
+==== Maven plugin
+* Added `native:compile` forking goal that can be started from the command line as `mvn native:compile`.
+* Deprecated `build` goal in favour of `compile-no-fork` goal. This goal should now be used for attaching to the `package` phase in `pom.xml`. Attaching the `build` goal will (for now) produce a runtime warning.
+* Argument files are now stored in the `target` directory (workaround for absolute path issue on Windows with older GraalVM versions).
+* Default and test outputs are now much less noisy.
+* When running tests in JVM mode with the native-image-agent, GraalVM's `java` executable is now always used.
+* Maven plugin now shouldn't require that JVM running it must be GraalVM.
+
+=== Release 0.9.13
+
+==== Gradle plugin
+* Reverted a change in the `NativeImagePlugin` that removed publicly accessible constants. This should prevent breakage of external plugins.
+
+==== JUnit testing support
+* Adapted the JUnit automatic metadata registration to changes in annotation handling on newer native image versions.
+
+=== Release 0.9.12
+
+==== Gradle plugin
+* Completely reworked agent support - **BREAKING CHANGE**
+* The agent block is no longer tied to the target binary.
+* The agent can now instrument any task that extends `JavaForkOptions`.
+* Introduced the `metadataCopy` task.
+* Introduced the concept of agent modes.
+** Under the hood, the agent mode dictates what options are passed to the agent and how metadata produced by multiple runs get merged.
+* Added `excludeConfig` configuration option that allows skipping of configuration files that are present in dependencies.
+* `useArgFile` is now set to true by default only on Windows.
+* Added `quickBuild` configuration option.
+
+==== Maven plugin
+* Added support for GraalVM Reachability Metadata Repository.
+* Completely reworked Maven plugin (should fix many of previous issues and inconsistencies between main and test builds).
+* Added `classesDirectory`, `debug`, `fallback`, `verbose`, `sharedLibrary`, `configurationFileDirectories`, `excludeConfig`, `quickBuild`, and `jvmArgs` properties in order to match those present in the Gradle plugin.
++
+See <> for more information.
+* `useArgFile` is now set to true by default only on Windows.
+* Changed lookup order for `native-image` discovery -- `GRAALVM_HOME`, `JAVA_HOME`, `PATH`.
+
+=== Release 0.9.11
+
+==== Maven plugin
+
+* Fix long classpath issue under Windows when running native tests
+* Inherit environment variables and system properties from the surefire plugin configuration when executing tests
+* Fix invocation of `native-image` when classpath contains spaces
+
+==== Gradle plugin
+
+* Add support for environment variables in native test execution
+* Fix invocation of `native-image` when classpath contains spaces
+* Add experimental support for the JVM reachability metadata repository
+
+=== Release 0.9.10
+
+==== Maven plugin
+
+* Native testing support can now be explicitly disabled via `skipNativeTests`.
+ - See <> for details.
+* Fixed race condition which prevented the agent files to be generated properly if tests were executed concurrently
+* Documented version compatibility for the JUnit Platform and Maven Surefire plugin.
+ - See <> for details.
+* Add support for long classpath by using an argument file when invoking `native-image`
+
+==== Gradle plugin
+
+* Fixed `nativeRun` not working properly under Windows
+* Fixed race condition which prevented the agent files to be generated properly if tests were executed concurrently
+* Add support for long classpath by using an argument file when invoking `native-image`
+
+=== Release 0.9.9
+
+==== Gradle plugin
+
+* Fixed resource inference not working on custom binaries
+* Fixed `disableToolchainDetection` not working if a GraalVM installation isn't present. Please use `graalvmNative.toolchainDetection.set(false)` instead.
+
+=== Release 0.9.8
+
+==== Gradle plugin
+
+* [Breaking change] The `agent` option has been replaced with an `agent { ... }` configuration block which includes an `enabled` property.
+* Toolchain support can now be disabled altogether, which can be useful when using GraalVM Enterprise Edition.
+ - See <> for details.
+* Fixed a bug when using a _fat jar_ which assumed that all entries to be repackaged were jars.
+* Agent options are now configurable.
+ - Note that the `experimental-class-loader-support` agent option is no longer added by default.
+ - See <> for details.
+* Added an option to perform resource detection in classpath entries which contain a `native-image/resource-config.json` file.
+
+==== Maven plugin
+
+* The agent can now be enabled in the POM.
+ - See <> for details.
+* Agent options are now configurable.
+ - Note that the `experimental-class-loader-support` agent option is no longer added by default.
+ - See <> for details.
+* Added an option to perform resource detection in classpath entries which contain a `native-image/resource-config.json` file.
+
+==== JUnit Platform Native
+
+* Builds now correctly fail if a container-level extension or lifecycle method fails --
+ for example, if an `@BeforeAll` method in a JUnit Jupiter test class throws an exception.
+* Builds no longer fail when tests are aborted -- for example, via a failed assumption.
+* Improved documentation for JUnit Platform and Maven Surefire support in the plugins.
+
+=== Release 0.9.7.1
+
+==== Bugfixes
+
+- Fixed https://github.com/graalvm/native-build-tools/issues/144[Maven plugin configuration not applied if declared in a parent POM].
+
+=== Release 0.9.7
+
+Release didn't include any fixes.
+
+=== Release 0.9.6
+
+==== Upgrade to JUnit 5.8
+
+The plugins now depend on JUnit 5.8 which provides an official test listener which is used by these plugins.
+As a consequence, Maven users will have to configure their builds to enable the plugin extensions:
+
+```xml
+
+ org.graalvm.buildtools
+ native-maven-plugin
+ ${native.maven.plugin.version}
+ true
+ ...
+
+```
+
+The dependency on `junit-platform-native` which used to be required pre-0.9.6 can now safely be removed.
+
+For Gradle users, there's no impact on the configuration, however a good consequence is that the `junit-native-platform` dependency no longer leaks into your application's classpath.
+
+==== Agent support for Maven plugin
+
+The Maven plugin now supports the GraalVM agent to generate configuration files.
+Please refer to the <> for details.
+
+==== Disabling testing support
+
+The Gradle plugin now provides an option to disable testing support.
+This can be useful if the test framework you are using doesn't work with this plugin or that you simply don't want to execute tests natively.
+
+To disable tests, use the `graalvmNative` configuration block:
+
+```kotlin
+graalvmNative {
+ testSupport.set(false)
+}
+```
+
+==== Configuring additional test images
+
+The Gradle plugin now supports building multiple test images, which can be used to execute tests natively for more kinds of tests: integration tests, functional tests, ...
+
+For more information, please refer to <>
+
+=== Release 0.9.5
+
+This release contains, in preparation for supporting more images in the Gradle plugin:
+
+- The `nativeBuild` and `nativeTest` extensions are now deprecated. A top-level container for configuring native images has been introduced. Instead of:
+
+[source,groovy]
+----
+nativeBuild {
+ verbose = true
+}
+----
+
+you need to use:
+
+[source,groovy]
+----
+graalvmNative {
+ binaries {
+ main {
+ verbose = true
+ }
+ }
+}
+----
+
+and instead of:
+
+[source,groovy]
+----
+nativeTest {
+ buildArgs("...")
+}
+----
+
+you need to use:
+
+[source,groovy]
+----
+graalvmNative {
+ binaries {
+ test {
+ verbose = true
+ }
+ }
+}
+----
+
+- The `nativeBuild` task has been renamed to `nativeCompile`.
+- The `nativeTestBuild` task has been renamed to `nativeTestCompile`.
+
+Both `nativeBuild` and `nativeTestBuild` task invocations are still supported but deprecated and will be removed in a future release.
+
+=== Release 0.9.4
+
+This release works around a limitation for Windows users who encounter an issue with long classpath entries on CLI: the Gradle plugin will now automatically handle this problem by creating a fat jar instead of passing all entries on classpath (this behavior can be <>) if needed).
+Maven users will have to <> to use shading.
+
+In addition to this, we're now publishing development snapshots of this plugin. For Gradle, you will need to declare this repository in your settings.gradle(.kts) file:
+
+[source,groovy]
+----
+pluginManagement {
+ plugins {
+ id 'org.graalvm.buildtools.native' version '0.9.5-SNAPSHOT'
+ }
+ repositories {
+ maven {
+ url "https://raw.githubusercontent.com/graalvm/native-build-tools/snapshots"
+ }
+ gradlePluginPortal()
+ }
+}
+----
+
+For Maven, you need to use this repository configuration:
+
+[source,xml]
+----
+
+
+ graalvm-native-build-tools-snapshots
+ GraalVM native-build-tools Snapshots
+ https://raw.githubusercontent.com/graalvm/native-build-tools/snapshots
+
+ false
+
+
+ true
+
+
+
+----
+
+=== Release 0.9.3
+
+This release contains:
+
+- Fix for mainClass not being optional (Gradle plugin)
+- Fix for Gradle < 7 failing to determine GraalVM toolchain
+- Gradle plugin now registers proper groups
+- Automatic native-image tool fetching via gu (Gradle plugin)
+- FIxed issue where nativeTest would fail when tests are annotated with Timeout
+- Added a sharedLibrary configuration option for Gradle plugin
+- Removed broken server configuration option from Gradle plugin
+- Added a documentation website with proper CI integration
+
+In addition to those improvements, several behind-the-scenes changes were made:
+
+- Introduced "Dockerless" Maven plugin functional testing
+- Parallelized Gradle testing in CI
+- Replaced groovy-json with jackson-databind for JSON handling
+- Fixed Github Actions syntax to enable manual workflow invoking
+
+=== Release 0.9.2
+
+This release contains:
+
+- Revamped Gradle plugin that is now a lot more idiomatic.
+- Fixes for several issues regarding JUnit testing.
+- Removal of Test Discovery mode from the Maven plugin.
+- Fix for Maven creating empty test images when no tests are present.
+- Added support for Kotlin tests in Gradle.
+
+In addition to those improvements, several behind-the-scenes changes were made in order to ensure better compatibility moving forward:
+
+- Test coverage has been greatly improved for all subprojects.
+- Build tooling for this repository has been improved significantly.
+
+Note that there has been a breaking change in the Gradle plugin - `persistConfig` configuration option was removed.
+Using said option will cause existing builds to break, so users are advised to remove it from their configuration prior to upgrading.
+System property `-DpersistConfig` will have no effect going forward.
+
+=== Release 0.9.1
+
+This release contains:
+
+- Fixes for most of the known issues regarding Gradle and Maven plugins
+- Massively improved automatic JUnit support as well as initial JUnit Vintage support
+- Improved JavaDoc and tests for the Gradle plugin
+
+=== Release 0.9.0
+
+Initial release
diff --git a/docs/src/docs/asciidoc/css/docs-back.css b/docs/src/docs/asciidoc/css/docs-back.css
new file mode 100644
index 000000000..5bb2d6edd
--- /dev/null
+++ b/docs/src/docs/asciidoc/css/docs-back.css
@@ -0,0 +1,15 @@
+#header, #content {
+ max-width: 1200px;
+ padding-left: 40px;
+ padding-right: 40px;
+ background-color: #ffffff;
+ color: black;
+}
+
+h1 {
+ color: black !important;
+}
+
+p {
+ color: black !important;
+}
\ No newline at end of file
diff --git a/docs/src/docs/asciidoc/css/footer.css b/docs/src/docs/asciidoc/css/footer.css
new file mode 100644
index 000000000..69cd56ce6
--- /dev/null
+++ b/docs/src/docs/asciidoc/css/footer.css
@@ -0,0 +1,26 @@
+#footer {
+ padding: 0px;
+}
+
+.foot-container {
+ background: url('../css/images/grain.png') repeat, url('../css/images/metaimage-main-01.svg') 100% 20%/650px no-repeat, radial-gradient(79.99% 167.49% at 30.24% 129.2%, rgba(47, 79, 124, 0.8) 0%, rgba(38, 84, 107, 0) 100%), #171E25;
+ padding: 20px 40px;
+}
+
+.foot-content {
+ display: flex;
+ justify-content: center;
+ flex-direction: row;
+ flex-wrap: wrap;
+}
+
+.foot-logo {
+ display: flex;
+ max-width: 8%;
+ padding-right: 10px;
+ align-items: center;
+}
+
+.foot-text {
+ color: #ffffff;
+}
\ No newline at end of file
diff --git a/docs/src/docs/asciidoc/css/images/graalvm-logo.svg b/docs/src/docs/asciidoc/css/images/graalvm-logo.svg
new file mode 100644
index 000000000..10e2742a7
--- /dev/null
+++ b/docs/src/docs/asciidoc/css/images/graalvm-logo.svg
@@ -0,0 +1,4 @@
+
diff --git a/docs/src/docs/asciidoc/css/images/graalvm-rabbit-icon.svg b/docs/src/docs/asciidoc/css/images/graalvm-rabbit-icon.svg
new file mode 100644
index 000000000..445994f2b
--- /dev/null
+++ b/docs/src/docs/asciidoc/css/images/graalvm-rabbit-icon.svg
@@ -0,0 +1,8 @@
+
diff --git a/docs/src/docs/asciidoc/css/images/grain.png b/docs/src/docs/asciidoc/css/images/grain.png
new file mode 100644
index 000000000..2d425e747
Binary files /dev/null and b/docs/src/docs/asciidoc/css/images/grain.png differ
diff --git a/docs/src/docs/asciidoc/css/images/logo-header.svg b/docs/src/docs/asciidoc/css/images/logo-header.svg
new file mode 100644
index 000000000..1756e517a
--- /dev/null
+++ b/docs/src/docs/asciidoc/css/images/logo-header.svg
@@ -0,0 +1,4 @@
+
diff --git a/docs/src/docs/asciidoc/css/images/metaimage-main-01.svg b/docs/src/docs/asciidoc/css/images/metaimage-main-01.svg
new file mode 100644
index 000000000..b0d6ca97b
--- /dev/null
+++ b/docs/src/docs/asciidoc/css/images/metaimage-main-01.svg
@@ -0,0 +1,43 @@
+
diff --git a/docs/src/docs/asciidoc/css/multi-lang-sample.css b/docs/src/docs/asciidoc/css/multi-lang-sample.css
index df018b2b1..4b2d6af52 100644
--- a/docs/src/docs/asciidoc/css/multi-lang-sample.css
+++ b/docs/src/docs/asciidoc/css/multi-lang-sample.css
@@ -52,20 +52,20 @@
.multi-language-selector .language-option[data-lang='groovy'],
+.multi-language-selector .language-option.selected[data-lang='groovy'],
.exampleblock[data-lang=groovy] > .content .title {
background-image: url('data:image/svg+xml;base64,PHN2Zw0KICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIg0KICAgd2lkdGg9IjYxNC4wNjA4NSINCiAgIGhlaWdodD0iMzAzLjU5ODYiDQogICB2ZXJzaW9uPSIxLjEiPg0KICA8Zw0KICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMzUuMzk2NTk5LC0zMC41NTgxOTYpIj4NCiAgICA8cGF0aA0KICAgICAgIGQ9Im0gMTU0Ljk3Njg3LDMzMi42OTE2MiBjIDAsLTAuODA1ODcgMTAuODMxMjMsLTE4LjYyMTcyIDI0LjA2OTQsLTM5LjU5MDc2IDEzLjIzODE5LC0yMC45NjkwNSAyMi42NjA1NywtMzguNjIwNjYgMjAuOTM4NjcsLTM5LjIyNTc3IC0xLjcyMTkxLC0wLjYwNTEgLTcuNjc5OTIsMC40ODU2NyAtMTMuMjQwMDIsMi40MjM5MyAtMTYuNzQ1MjQsNS44Mzc0MyAtMjAuMTg2MDUsNC4yOTAxNiAtMzIuNjkzMDgsLTE0LjcwMTU1IC0xMy41MzI2NSwtMjAuNTQ5MDYgLTE0LjAxNzI0LC0yMi42Njk4MyAtNi45NjE1OCwtMzAuNDY2MjQgNC42MDg3NiwtNS4wOTI1NiA0LjMzMTU5LC02LjI3OTY3IC0zLjIzMTksLTEzLjg0MzE2IC00LjU1MDA5LC00LjU1MDEgLTguMjcyODksLTkuNjQzMDkgLTguMjcyODksLTExLjMxNzc2IDAsLTIuODE2NzkgLTMwLjk5MTQ1LC0xNS41ODkzMyAtNzkuOTg5NDk2LC0zMi45NjYxNSAtMTEuMTA5NjYsLTMuOTM5OTcgLTIwLjE5OTM3NSwtNy44MjkyOCAtMjAuMTk5Mzc1LC04LjY0MjkxIDAsLTAuODEzNjYgMjMuNDcwNzM4LC0xLjAwNDc2IDUyLjE1NzE5MSwtMC40MjQ2OSBsIDUyLjE1NzIxLDEuMDU0NjYgNS4wOTEyNywtOC4wMzAwMSBjIDIxLjUxNjQ0LC0zMy45MzU5MiA1NC4wMDk1MiwtNjQuNTg4MjEyIDY4LjQ2NzA1LC02NC41ODgyMTIgMy4wMTA2LDAgOS4xMjE0MSwyLjg2OTE4IDEzLjU3OTUzLDYuMzc1OTQ1IDcuMjU5NDYsNS43MTAyODMgOC4yNTY1LDkuMjQ4NjMyIDkuNTQ5OTQsMzMuODkxOTY3IDAuNzk0MywxNS4xMzM4MiAyLjQyNDIyLDI4LjQ5NjAxIDMuNjIxOTgsMjkuNjkzNzkgMS4xOTc3NSwxLjE5Nzc2IDQuOTk3NjksLTAuMzc0MjUgOC40NDQzMSwtMy40OTMzOSA0LjQwMTQsLTMuOTgzMjMgOS4wMzQ4NCwtNS4wNjMxMyAxNS41Njc2MSwtMy42MjgzIDYuNzY3NTYsMS40ODY0MSAxMC42MjcxLDAuNDY4OTUgMTQuMTY5MzgsLTMuNzM1NDMgNS4zMTM2LC02LjMwNjY2IDMxLjkwNTk2LC00OC45MzcyMDEgMzEuOTA1OTYsLTUxLjE0ODc0NCAwLC0wLjc1MTU4OCA1LjI2NTA5LC05Ljc1NzMwNiAxMS43MDAyMywtMjAuMDEyNzAzIDYuNDM1MDksLTEwLjI1NTM5NyAxMy44NDY2NCwtMjIuMDgwNjMxIDE2LjQ3MDA3LC0yNi4yNzgyODMgNC42NjU1OCwtNy40NjUyMDEgNS4zOTY2LC02LjU4MTczNSAzMy40MjY5Miw0MC4zOTg3MzYgMjguOTQxMjEsNDguNTA3MTY0IDQ0LjUxNzY3LDY2LjQwNjU1NCA1Mi4zMTU3LDYwLjExNzYzNCA1LjQ0MiwtNC4zODg4OSAzNC4wMDA1NSwtNC42Mzc5NCA0MS45ODI0NiwtMC4zNjYxNCA0LjUzNjY1LDIuNDI3OTMgNy40MTkxNiwyLjM5NjEgOS45MjQ4NCwtMC4xMDk2MiA1LjIwMDA3LC01LjIwMDA2IDE3LjY0OTgyLC00LjI5NDM3IDIwLjkwNDEyLDEuNTIwNzggMi41NjM5Myw0LjU4MTQ2IDMuMjE3ODQsNC41ODE0NiA3LjAyMDE0LDAgMi4zMDYwNiwtMi43Nzg2NyA5LjEzNjE5LC01LjA1MjA4IDE1LjE3ODAzLC01LjA1MjA4IDguNTM4ODQsMCAxMS43OTMzNiwxLjc3MzY3IDE0LjYxNDAzLDcuOTY0MzcgbCAzLjYyODgxLDcuOTY0MzYgNTYuMDkyMDMsLTEuOTg1OCBjIDMwLjg1MDYyLC0xLjA5MjIyIDU2LjA5MjA0LC0xLjI4Njg4IDU2LjA5MjA0LC0wLjQzMjU1IDAsMC44NTQyNyAtMjIuOTA2MDgsMTAuMDcxOTUgLTUwLjkwMjQxLDIwLjQ4MzcxIC0yNy45OTYzMiwxMC40MTE3MiAtNTEuMzY3LDE5LjI0MzA1IC01MS45MzQ4NCwxOS42MjUxMiAtMC41Njc4NSwwLjM4MjA3IDAuNjk4NDcsOS40OTI3MyAyLjgxNDA1LDIwLjI0NTg4IDUuNTA0NTcsMjcuOTc5MTcgMS45MTQ1OCw0Ni42MjU4OSAtMTEuOTI2MjksNjEuOTQ2MDQgLTYuMjA5LDYuODcyNjMgLTE2LjI4MzM4LDE0LjE0Mzk1IC0yMi4zODc1MywxNi4xNTg0OSAtNi4xMDQxNSwyLjAxNDU0IC0xMS4wOTg0NCw0LjM4NjE0IC0xMS4wOTg0NCw1LjI3MDE5IDAsMC44ODQwNiA2LjUxNTI1LDExLjY3ODkzIDE0LjQ3ODM1LDIzLjk4ODU5IDcuOTYzMDksMTIuMzA5NjkgMTMuMjgyOCwyMi4zODEyNSAxMS44MjE1MiwyMi4zODEyNSAtMi4zODUwNCwwIC02MC40Njk4NywtMjIuNDQyMzggLTE1MC43ODg5OCwtNTguMjYwNyAtMTcuODA5LC03LjA2MjU4IC0zNC4wMjY4MywtMTIuODQxMDkgLTM2LjAzOTYxLC0xMi44NDEwOSAtMy43NTM2MywwIC0zNi4xNjY5LDEyLjI4NjYyIC0xMzEuMjI0MjMsNDkuNzQyMDYgLTU1LjEyNTI1LDIxLjcyMTAzIC01Ny4yOTIxNywyMi40NzM0OCAtNTcuMjkyMTcsMTkuODk0NTEgeiBtIDExMy4xMTY0OCwtNTEuMjUyNjUgYyAyOS4zMjk0OSwtMTEuMzk1MDYgNTguNDE2NTcsLTIyLjUzMDggNjQuNjM3OTgsLTI0Ljc0NjA3IDEwLjcwMDY4LC0zLjgxMDE5IDE0LjI3OTE4LC0yLjg1NDkzIDY2LjI1Mzk0LDE3LjY4NjM0IDMwLjIxODI1LDExLjk0Mjc0IDYwLjAzMjUyLDIzLjU3NTg3IDY2LjI1MzkyLDI1Ljg1MTM0IDYuMjIxNDEsMi4yNzU1MiAxOS4wNDMxNyw3LjIyMDU5IDI4LjQ5Mjc1LDEwLjk4OTA4IDEwLjc3NTAzLDQuMjk3MDQgMTYuMjgyNTEsNS4zMzE2NiAxNC43NzEwOCwyLjc3NDg0IC0xNi44ODM3LC0yOC41NjExMyAtMTcuMzk5OCwtMjkuMDM3MTYgLTI5LjEwMDgzLC0yNi44NDIwNSAtMTQuMTEzNjEsMi42NDc3MiAtMjIuODc4OTcsLTAuMDY4NSAtMjcuMzgzNjksLTguNDg1NTQgLTIuOTIxMTIsLTUuNDU4MTYgLTIuMDI5NDQsLTguMTI3NTkgNS4yNTQ0MiwtMTUuNzMwMzEgMTAuODQ4ODcsLTExLjMyMzc3IDkuMTQ0NTcsLTIxLjc0MTQ0IC00Ljg0NDI5LC0yOS42MTA5OSAtNS4zNjc2NCwtMy4wMTk1OSAtMTIuMTQwNTksLTEwLjQ4MzcyIC0xNS4wNTEwMiwtMTYuNTg2OTcgbCAtNS4yOTE2OSwtMTEuMDk2ODIgLTExLjM0NTUxLDcuNjk5MjggYyAtMTMuOTQ0NTgsOS40NjMwNCAtMzAuOTUwNjIsOS45NTA2MyAtNDUuMTQ3NjQsMS4yOTQ0OCBsIC0xMC40NjQ4OCwtNi4zODA2NSAtMTIuMTU4NDEsOC4yNjI4NCBjIC0xMi4xNzQ2Miw4LjI3Mzg2IC0yNi4xOTEwOCw5LjM0MjggLTQwLjQzNzUyLDMuMDgzOTIgLTMuOTI0OTgsLTEuNzI0MzkgLTUuNjU1ODMsLTEuMDU0MTUgLTUuNjU1ODMsMi4xOTAwOCAwLDIuNTcxMTggLTMuNzA0MjksNi4zNjI2NSAtOC4yMzE4MSw4LjQyNTUzIC0xMy41NzQyLDYuMTg0NzkgLTI2LjgyMTA3LDQuNTQyNDcgLTM1LjgwMjI5LC00LjQzODc1IGwgLTguMTg5NDQsLTguMTg5NDQgLTYuNjA5ODIsOC40MDMwMyBjIC0zLjYzNTM5LDQuNjIxNjYgLTEyLjA0OTUyLDExLjgzNjk4IC0xOC42OTgwNSwxNi4wMzM5OCAtOC4yMjk2NSw1LjE5NTE4IC0xOC40MTc0MSwxNy44NzE4MiAtMzEuOTE2NCwzOS43MTM2MSAtMTAuOTA1NDksMTcuNjQ1NDYgLTE5LjgyODE2LDMyLjk4MDQyIC0xOS44MjgxNiwzNC4wNzc3NiAwLDEuMDk3MyA4LjM2MjU1LC0xLjUyNzM2IDE4LjU4MzQyLC01LjgzMjU5IDEwLjIyMDg5LC00LjMwNTIxIDQyLjU4MDI4LC0xNy4xNTA4NyA3MS45MDk3NywtMjguNTQ1OTMgeiBtIDI0OC43NjcyOSwtMTIuMjU4NDYgYyAyMi4yNTU1NywtMTIuNTA4MzMgMjcuNDE0MjQsLTM1LjY0ODA4IDE3LjU3Njg1LC03OC44NDMxOCAtOC4yMjE0LC0zNi4wOTk2MSAtMTIuMzI1NSwtNDcuOTEzMjcgLTE3LjE5NDk0LC00OS40OTU5NyAtMy4zNjkwMiwtMS4wOTUwMyAtMy44ODY3NCwwLjEwNjk5IC0xLjk4ODU4LDQuNjE2MzUgMTIuMzE1MTYsMjkuMjU3NzEgMTIuOTk5NjMsNDMuOTQ2NjIgMi4yNDAyMiw0OC4wNzU0MSAtNy4zOTE1MSwyLjgzNjQyIC0xNS4yMzIzOSwtNy43OTA3NSAtMjEuMzk5MDIsLTI5LjAwMzI2IC00Ljc3NTEyLC0xNi40MjU4NyAtOS43Njg2MSwtMjMuODI4MzcgLTEzLjUzMzg3LC0yMC4wNjMxMSAtMC45MDE1NywwLjkwMTYgMC42Nzg0OCw2LjEyMTE3IDMuNTExMjQsMTEuNTk5MTMgMi44MzI3Myw1LjQ3NzkgNi43MTcyMSwyMS45NzQ1NSA4LjYzMjE3LDM2LjY1OTIgMS45MTQ5NywxNC42ODQ2MiA1LjYxMzg1LDMwLjA2MDczIDguMjE5NzMsMzQuMTY5MTIgNi43NzE5OCwxMC42NzY2MSAxNy41NTEyOCw4LjEwMzYgMjQuNTIzODIsLTUuODUzODQgNi44OTM3MywtMTMuNzk5NzUgNy42NzI4NCwtNC42MDc4MSAwLjk2OTMsMTEuNDM2MDIgLTYuOTYwNzQsMTYuNjU5MzcgLTI3LjYzNDQyLDIxLjY1NDgxIC01MS41ODQxOCwxMi40NjQ0MiAtMy44MzMzLC0xLjQ3MDk2IC01LjEzMDM5LDAuMDUxNSAtNS4xMzAzOSw2LjAyMDk3IDAsNC4zOTQzMSAtMi45MzYxOCwxMS43MjI0NSAtNi41MjQ4OCwxNi4yODQ3IGwgLTYuNTI0ODgsOC4yOTUwNiA4Ljk0ODgxLDEuNTExMjMgYyAxNS40NDk0NSwyLjYwOTA5IDM2LjYzMzI4LC0wLjc3NjQyIDQ5LjI1ODYsLTcuODcyMjUgeiBtIC0zMjAuMzY2NSwtMjcuNTM3MzMgYyA0Ni40NTk2NSwtMTMuMzU0NSA1NC43NjE4OCwtMjguOTMwOTMgMzkuMzI4ODUsLTczLjc4NzgyIC0zLjU4MTg1LC0xMC40MTA3OSAtNi41MTI0MywtMjEuNjA5NzEgLTYuNTEyNDMsLTI0Ljg4NjU0IDAsLTExLjE1NzEyIC01LjYxMTUyLC02LjAwMTYxIC05LjgxNzEzLDkuMDE5NDMgLTUuNTYyMDMsMTkuODY1NjEgLTIyLjI5MTE1LDM2Ljc1MDUzIC0zNi4zNDUxNiwzNi42ODM2NCAtMTIuMjA1NDcsLTAuMDU4MiAtMTYuMTgyNTEsLTIuMjgwMDggLTIxLjIyMzc4LC0xMS44NTc2OCAtOS4wNDc0OCwtMTcuMTg4ODIgMC44MTQ2OSwtNDMuNTY3OTcgMjUuOTY5NTQsLTY5LjQ2MjcyIDE4LjQyNDMxLC0xOC45NjYzMDQgMjguNDg4OTMsLTIwLjY4Mzk2NSAyOC40ODg5MywtNC44NjIwNSAwLDI0LjY3MzIgLTE3LjAxMTM0LDYwLjk5NDcgLTI1Ljk3MzA5LDU1LjQ1NjA0IC00LjM4MjA1LC0yLjcwODI2IC0zLjY3MTM4LC0xNC42Njc1MiAxLjQxNzIyLC0yMy44NTAwOSA0LjMyNzM1LC03LjgwODgyIDMuNDQ4MTQsLTE5LjcwNzcxIC0xLjQ1NjIsLTE5LjcwNzcxIC01LjY4Njg5LDAgLTE2Ljk0NTQ3LDIwLjcxMTk2IC0xOC40MzMxOCwzMy45MTA3NCAtMS4zMTUxNSwxMS42NjgyNiAtMC40OTI4NiwxNC41MDU0NSA1LjA2NjUyLDE3LjQ4MDcyIDE5LjQ4MjQ0LDEwLjQyNjcgNDUuMDA1MzEsLTIxLjM5NTQ1IDQ2LjI5MDgzLC01Ny43MTU4NCAwLjY3NTA0LC0xOS4wNzMxODEgLTMuMDE2NzYsLTI0LjQ0MTEwNSAtMTQuNzc5MDYsLTIxLjQ4ODk1MSAtMTcuMTE4NzksNC4yOTY1NDMgLTYzLjIzNDgzLDYzLjc4NjY0MSAtNjMuMjM0ODMsODEuNTczNDYxIDAsMTMuOTEyOCA2Ljk4NzM3LDIzLjY5NzI4IDE5LjI4ODI3LDI3LjAwOTYgMjAuOTE0MTksNS42MzE2MiA0MS4xMzUzOSwtNC4zNzM4OSA1NS40NjMzLC0yNy40NDMzOCBsIDYuMDIxNzUsLTkuNjk1NzEgMC4wMTMyLDEzLjE3MjIzIGMgMC4wMTYyLDE5LjA3MzggLTEyLjQyNjE5LDI5LjEyNzQgLTUxLjY5ODE3LDQxLjc2NzkzIC05Ljc3NjQ5LDMuMTQ2OCAtMTguMTgzMzIsNi4wNTQ0MSAtMTguNjgxOCw2LjQ2MTM3IC0xLjgyMDU2LDEuNDg2MzIgMTYuNjg3MzksMjcuNDc1NzEgMTkuNTY2NDQsMjcuNDc1NzEgMS42MzI2MiwwIDExLjE5MTE1LC0yLjM2MzU5IDIxLjI0MTIzLC01LjI1MjM4IHogTSA0NzYuNDExLDIxNy45NjMwMSBjIDQuNjYzNDcsLTUuMTUzMSA1LjQ4NTUxLC0xMC43NTEwMyA0LjQ4NTc5LC0zMC41NDgyMyAtMS4wODczOSwtMjEuNTMzMzEgLTIuMjc3NDYsLTI1LjMzODUxIC0xMC42NjE0MywtMzQuMDg5NDkgLTE1LjE0ODgyLC0xNS44MTE5NCAtMzMuODYxMjMsLTExLjc3ODI4IC0yMi45MDgxOSw0LjkzODE0IDUuODIyNTYsOC44ODYzNyAxMi45MjU0LDcuMzY4NjQgMTAuNDE2NDEsLTIuMjI1ODEgLTEuOTk4NjQsLTcuNjQyNzYgMS43Mjk5OCwtNy44NDM1OCA4LjIyODM5LC0wLjQ0MzIyIDEyLjE5MzAxLDEzLjg4NTM3IDkuMTE1MzQsMzMuMTI2OTggLTUuMjk4NiwzMy4xMjY5OCAtOS44NjcxNiwwIC0xMi45NDE5NSwtNC4yMzE3NiAtMTkuMzczNTMsLTI2LjY2MzE4IC0yLjkzMDU5LC0xMC4yMjA4NyAtNi40OTk2NCwtMTguNTgzNDMgLTcuOTMxMjcsLTE4LjU4MzQzIC00Ljc2ODk5LDAgLTYuODkyODcsNC4yMDg3OCAtMy45MjcyMSw3Ljc4MjQgMS42MTA4OCwxLjk0MTEgNi4wNzIxOCwxNS44OTEyNyA5LjkxNDA0LDMxLjAwMDQxIDkuNjY5MDMsMzguMDI1OTYgMjMuMTc2NjYsNTEuMDQxNDUgMzcuMDU1NiwzNS43MDU0MyB6IG0gLTE4My43NzEyNSwwLjYwMzk1IGMgMS45ODk3OCwtMS4zMzUgMi44OTk3NSwtNy42NDg5MyAyLjA3ODY1LC0xNC40MjMgLTEuMzI0NDksLTEwLjkyNzAyIC0yLjA2NjYyLC0xMS43MjQxIC03Ljk3MjcsLTguNTYzMjcgLTguNDg3MzMsNC41NDIyNyAtMTcuNjA4MzksLTQuMDYzNTEgLTIwLjcwMjIyLC0xOS41MzI2IC0yLjM0NzA3LC0xMS43MzUzOCAtMy4wNjUyNiwtMTEuNTA0ODkgMTQuMDI1NzYsLTQuNTAxNDkgMi4xNDI4OCwwLjg3ODA4IDcuNTk2NzEsLTIuMTA0MDIgMTIuMTE5NjMsLTYuNjI2OTQgOC4yNzQwOCwtOC4yNzQwOCAxMC45OTgwMiwtMTguMjEyOTkgNC45OTE1NiwtMTguMjEyOTkgLTEuNzc3NTQsMCAtMy4yMzE5LDEuNDU0MzYgLTMuMjMxOSwzLjIzMTkgMCw0LjY0OTIzIC04Ljg0MjMxLDMuOTg5ODIgLTEwLjgzNDY1LC0wLjgwNzk3IC0xLjE2MjI0LC0yLjc5ODc5IC0zLjYzODA3LC0xLjgwNTkxIC04LjA1OTE1LDMuMjMxOSAtMy41MDk4NSwzLjk5OTQ4IC03Ljc5NjczLDcuMjcxNzggLTkuNTI2NDEsNy4yNzE3OCAtMS43Mjk2OSwwIC00LjA2NTYsLTMuMjcyMyAtNS4xOTA4OCwtNy4yNzE3OCAtMS45NzE5NSwtNy4wMDg1NiAtMi4yMTM2NiwtNy4wNTI3NyAtNi42NzczNywtMS4yMjE2OSAtMy45MTA2LDUuMTA4NTQgLTMuOTQ1NTcsNi43MzU4MyAtMC4yMjQ2MSwxMC40NTY2MSAzLjk0MDYyLDMuOTQwNjQgMTEuNDI2MTUsMzAuODcxMDIgMTEuNDI2MTUsNDEuMTA3NDQgMCwxMi43Mjg2NyAxNy41MjkzMywyMi43MzgzMiAyNy43NzgzLDE1Ljg2MjEgeiBtIDU4Ljk0MzA5LC0xNi44NzUzMSBjIDEyLjE0NTY3LC0xMi4xNDU2NyAxNS43NDQ1MiwtMzAuMzUxNDMgOS4xMjk0MywtNDYuMTgzNTcgLTUuMTcwMTYsLTEyLjM3Mzk3IC0xNC4xNDQ2NiwtMTcuMDA1MTYgLTI2Ljc5ODc4LC0xMy44MjkxOCAtOS40MDI2MywyLjM1OTkxIC0yMy43MjMxMSwyOS43NzY1OSAtMjMuNzczODcsNDUuNTE1MzYgLTAuMDc5MiwyNC41MjkwMiAyMy4yNTA1MywzMi42OTAwNSA0MS40NDMyMiwxNC40OTczOSB6IG0gLTIyLjA4MzQxLC0xOS40MzQwNyBjIC0zLjU1NTEsLTMuNTU1MSAtNi40MTc1NSwtMTAuNDYzMjggLTYuMzYxMDIsLTE1LjM1MTUzIDAuMDkzNCwtOC4wNzk2NSAwLjQ5MzE1LC04LjM3MzUgNC4zOTYxOSwtMy4yMzE5IDQuODI0NjQsNi4zNTU2OSAxMy4zOTc5Nyw3LjQ1NjY2IDE2LjY4MjQxLDIuMTQyMzIgMS4xOTQzMiwtMS45MzI0MSAwLjU1Mjk1LC01LjEzMjAyIC0xLjQyNTIsLTcuMTEwMTcgLTUuMDgxMzIsLTUuMDgxMzIgLTQuNDY1NjEsLTExLjk5OTYzIDEuMDY3OTIsLTExLjk5OTYzIDYuMTUxMzcsMCAxNC43MjY3OSwxMi41MTQ3NyAxNC43MjY3OSwyMS40OTE5MSAwLDYuNjA2NzQgLTEzLjQ0NDQsMjAuNTIyOCAtMTkuODI3MjQsMjAuNTIyOCAtMS41Mzc4NSwwIC01LjcwNDc2LC0yLjkwODcyIC05LjI1OTg1LC02LjQ2MzggeiBtIDg2LjM0ODk3LDE4LjU4MzQxIGMgMTMuNDEwNzQsLTE0LjYwMjYzIDEyLjEwMzc2LC0zNy4wOTggLTMuMDk4MjUsLTUzLjMyNjM0IC03Ljg1MDU3LC04LjM4MDYgLTI1LjI5Mjk4LC05LjgyNTUyIC0zMi40MzE4NSwtMi42ODY2NCAtNi41ODc2MSw2LjU4NzYxIC0xMy4xNjQ2NSwzMi40OTMwMyAtMTAuNzIxMjIsNDIuMjI4NDEgNS4yMTEyMiwyMC43NjMxOCAzMi40MDA0MSwyOC44NjY1MiA0Ni4yNTEzMiwxMy43ODQ1NyB6IG0gLTI3LjQ1NTY1LC0yMy42MTQ1MiBjIC01LjMwNjIzLC0zLjcxNjY0IC03LjEyOTQxLC04LjE0OTc0IC02Ljk3ODA2LC0xNi45Njc0NyAwLjE2OTM1LC05Ljg2MDMzIDAuNjg0MywtMTAuNzQ5ODEgMi45NjE0NSwtNS4xMTQxNSAzLjM0NTczLDguMjgwMzIgMTAuMzYyOTIsMTEuNDA3MzQgMTYuNTE4MTQsNy4zNjA5IDMuODE3NTMsLTIuNTA5NjMgMy42MzkyOSwtMy42OTU5NyAtMS4xMDEwNCwtNy4zMjkwNyAtNi4zMjE4OSwtNC44NDUyMyAtNy41NTUwOCwtMTEuNzAxOTEgLTIuMTA0NjUsLTExLjcwMTkxIDQuODczNjQsMCAyMi4zMDQwMiwxOS4wNjY2NiAyMi4zMDQwMiwyNC4zOTc3OCAwLDUuMjAzMTYgLTEyLjM3NzM5LDE0LjM4NTAzIC0xOS4zOTEzOSwxNC4zODUwMyAtMi43NjQwNSwwIC04LjI1NzksLTIuMjY0MDIgLTEyLjIwODQ3LC01LjAzMTExIHogbSAxODkuOTYyOTIsLTE0LjI4ODg3IDMzLjkzNDk1LC0xMi43MzY4MiAtMzYuMzU4ODgsLTAuMTMxMjUgYyAtMzIuNTM5MiwtMC4xMTczNiAtMzYuMzU0MjQsMC40NjMwNyAtMzYuMzE0NzUsNS41MjQ3NSAwLjA0MDEsNS4xMjc1NyAzLjMzMDczLDIwLjI4Mzk1IDQuMzcyNywyMC4xMzk2NyAwLjIzNzIyLC0wLjAzMjkgMTUuNzAxNzcsLTUuNzkxMjQgMzQuMzY1OTgsLTEyLjc5NjQ4IHogbSAtNDQzLjQ5MzQzLC0yLjI3OTMgMy40NjgyNSwtMTAuMjg3NjEgLTMwLjQ2Njg2LC0xLjAyNDA0IGMgLTE2Ljc1Njc0NCwtMC41NjMyMiAtMzAuNDYzNTg0LC0wLjEzMjEzIC0zMC40NTk1OCwwLjk1ODA0IDAuMDA1OCwxLjU5NTEzIDUyLjUwNjU3LDIyLjEwNzA3IDUzLjY2MTc0LDIwLjk2NTUyIDAuMTgwMzUsLTAuMTc4NCAxLjg4ODkyLC00Ljk1MzcyIDMuNzk2NDUsLTEwLjYxMTkxIHogbSAyMDguOTA5MiwtMzEuODg2OTUgYyA3LjE2NjY3LDEuMTQ2IDE1LjI0OTgxLDQuMDc5MjQgMTcuOTYyNTYsNi41MTgzNSA0LjIxMjc5LDMuNzg3ODMgNi4wNDUyOCwzLjUyOTY2IDEyLjU2MjA3LC0xLjc2OTg1IDQuMTk2NDMsLTMuNDEyNSA5LjY1MDI2LC02LjIwNDU3IDEyLjExOTYzLC02LjIwNDU3IDIuNDY5MzksMCA0LjQ4OTgyLC0wLjcxMzA4IDQuNDg5ODIsLTEuNTg0NjcgMCwtMy4wMDkxOCAtNDYuNTM5NTQsLTc5LjIxMjUyIC00OC4zMTQ0NywtNzkuMTA5NjEgLTIuMDg1LDAuMTIwODcyIC00OS43ODYwMyw4MC4wODc5OCAtNTEuMjU0NjQsODUuOTI0MzcgLTAuNjE2MzQsMi40NDk1MiAxLjMxODA0LDMuMzUzNzEgNS4wNzM2NCwyLjM3MTYgMy4zMzgzNiwtMC44NzMgNy45NTQxNiwwLjY4MzI2IDEwLjI1NzI3LDMuNDU4MzYgMy43MzU0LDQuNTAwODcgNS4yNjEwOCw0LjE0MjI5IDE0LjEzMDY4LC0zLjMyMDk3IDguNDI2NDcsLTcuMDkwNDEgMTEuOTMwOCwtOC4wNDg4IDIyLjk3MzQ0LC02LjI4MzAxIHoiDQogICAgICAgc3R5bGU9ImZpbGw6IzMzMzMzMztmaWxsLW9wYWNpdHk6MSIgLz4NCiAgICA8cGF0aA0KICAgICAgIHN0eWxlPSJmaWxsOiM2Mzk4YWE7ZmlsbC1vcGFjaXR5OjE7c3Ryb2tlOm5vbmUiDQogICAgICAgZD0ibSAzMDkuNTQ1NzksMTQwLjg3OTIyIGMgLTAuNTAwNzUsLTAuMjMzMTEgLTEuMTE4MDQsLTAuNzY3OTkgLTIuNTI3OCwtMi4xOTAzNSAtMS45MTE4OCwtMS45Mjg5NyAtMi42NzE4OCwtMi40OTAxMyAtNC4yNjEwOCwtMy4xNDYyOCAtMi4wODc5MSwtMC44NjIwNCAtNC4zNTQ5LC0xLjA5NDIzIC02LjQ4MjMxLC0wLjY2MzkxIC0xLjg5ODA4LDAuMzgzOTMgLTIuNDI0MzUsMC40MjUyNyAtMy4yODI0MiwwLjI1NzggLTEuNDMyMjQsLTAuMjc5NTMgLTEuOTQxNTcsLTEuMTQ5MjkgLTEuNTM1ODMsLTIuNjIyNzIgMC41MDU3MywtMS44MzY1NyA0LjMzNjkzLC04Ljk0MjU1IDEyLjAwODk5LC0yMi4yNzM4NyAxNS41ODM3LC0yNy4wNzg5ODggMzYuNTM2MjQsLTYxLjIyMDk2OCAzOC44NTA1NiwtNjMuMzA2NjYyIDAuMjU2MzEsLTAuMjMwOTk0IDAuMjY1LC0wLjIzMDk5NCAwLjUyMjQ4LDAgMS4zNzc2OCwxLjIzNTk3NiA5LjQ3NjIsMTMuNzI1ODg5IDIwLjU0MDM2LDMxLjY3ODI5OCAxNC45NjY5NiwyNC4yODUwMDQgMjcuNDExMDIsNDUuNjQzNzE0IDI3LjQxMTAyLDQ3LjA0NzcwNCAwLDAuODU3MjkgLTEuMzE2MDgsMS4zNzIxNSAtNC4wODU5MSwxLjU5ODQ1IC0zLjQ0MjI2LDAuMjgxMjQgLTcuNDI0NzgsMi4zMDE4OCAtMTIuODY4ODcsNi41MjkzNiAtMi41ODcxMiwyLjAwODk4IC0zLjY5MTI2LDIuNzU5MzMgLTQuOTUwNjIsMy4zNjQzOCAtMS4wNDkyOCwwLjUwNDExIC0xLjEzNjkyLDAuNTI0MzcgLTIuMjY4NTQsMC41MjQzNyAtMS4xMTM2OCwwIC0xLjIyNjYxLC0wLjAyNDkgLTIuMDk2MjMsLTAuNDYxMjggLTAuNTEyMTMsLTAuMjU3MDIgLTEuNTA0NjksLTAuOTQ5MyAtMi4yNDE1NSwtMS41NjM0MiAtMi4xNzg0OCwtMS44MTU2MSAtNC43OTkxOCwtMy4xMzYxNCAtOS4wNzE5NywtNC41NzEyNCAtNS40Mjk5NywtMS44MjM3NiAtMTEuMjQ0NTQsLTIuOTE4NCAtMTYuMTk1NTEsLTMuMDQ4OTYgLTMuOTA5MzQsLTAuMTAzMDggLTUuNzEyMDEsMC4yNDc5OCAtOC42NzYwMiwxLjY4OTYzIC0yLjQxOTg4LDEuMTc2OTggLTMuNTg2MjEsMi4wMTMxMSAtOS4xMjQ3NSw2LjU0MTQ1IC01LjYzMzc2LDQuNjA2MTcgLTcuNjM0NDIsNS41NjIwNSAtOS42NjQsNC42MTcyNSB6IiAvPg0KICAgIDxwYXRoDQogICAgICAgc3R5bGU9ImZpbGw6IzYzOThhYTtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZSINCiAgICAgICBkPSJtIDEyOC42Mzc4NiwxNzAuNzM5IGMgLTEwLjE1OSwtMi45NjA3IC00NS41MDMxNzEsLTE2LjkxOTYyIC01MC40NTA5MjQsLTE5LjkyNTIzIC0wLjY4MjkxNiwtMC40MTQ4NCAtMC43MzgwOTgsLTAuNDc3ODcgLTAuNTQzMjA3LC0wLjYyMDM4IDEuMTMxNTYzLC0wLjgyNzQxIDEyLjMzNzk4MywtMS4yMzkwMSAyNS4wMDQwMzEsLTAuOTE4MzYgNS4wNTEzMSwwLjEyNzg4IDM1LjUwMTMyLDEuMTMzMjMgMzUuNTUwOSwxLjE3Mzc3IDAuMDk5NywwLjA4MTUgLTYuNTIxNjQsMTkuMjg4OCAtNy4wMzA1MSwyMC4zOTQzMyAtMC4yMTI1MSwwLjQ2MTcgLTAuNjUwNjMsMC40NDM2NyAtMi41MzAyOSwtMC4xMDQxMyB6IiAvPg0KICAgIDxwYXRoDQogICAgICAgc3R5bGU9ImZpbGw6IzYzOThhYTtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZSINCiAgICAgICBkPSJtIDU0My42OTQxMywxNzUuMzEwNTUgYyAtMS4zODYyLC0yLjcxMjg3IC0zLjg4NzY5LC0xNC45NzA2NyAtMy45NjA3OCwtMTkuNDA4NTggLTAuMDI4NCwtMS43MjQyNCAwLjE4NjY3LC0yLjQyNzQxIDAuOTYwNiwtMy4xNDA4IDEuNzczNzgsLTEuNjM1MDEgNS4zNzkzOCwtMi4yMjMyIDE1LjI5MDE3LC0yLjQ5NDM0IDUuMDAzNDcsLTAuMTM2ODkgMjguMTAxOSwtMC4xNTc5NiA0NC40MTEyNywtMC4wNDA1IGwgMTEuNDI4NTksMC4wODIzIC0yNy41MTc3LDEwLjMxNDI3IGMgLTI1LjA5MjkyLDkuNDA1NCAtMzkuNjg0NzMsMTQuODQxMjYgLTQwLjIyNDkxLDE0Ljk4NDkgLTAuMTM1MTcsMC4wMzYgLTAuMjY5ODEsLTAuMDY3NCAtMC4zODcyNCwtMC4yOTcyMyBsIDAsMCB6IiAvPg0KICAgIDxwYXRoDQogICAgICAgc3R5bGU9ImZpbGw6IzYzOThhYTtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZSINCiAgICAgICBkPSJtIDE3OC4wODgzNSwzMTUuNTE0MzMgYyAwLC0wLjc3MDc4IDMuMjY4OSwtNi41MzMxIDExLjU1Mzk1LC0yMC4zNjY5OCAxOS44NTExOCwtMzMuMTQ2MzEgMjguOTI3MTUsLTQ1LjE2MDEyIDQwLjA3MjkyLC01My4wNDQyOSA5Ljk3MTI3LC03LjA1MzM3IDE1LjMwMjc5LC0xMS45Mjk0IDIxLjI0NTM5LC0xOS40MzAzMyBsIDMuNjY0MDksLTQuNjI0OTIgNC45Mzk5LDQuODU2NTIgYyA4LjY0Nzg0LDguNTAxODkgMTMuODYxMywxMC45NjY2OCAyMy4yOTcxNywxMS4wMTQzMiAxMS41NDA1OCwwLjA1ODMgMjQuMTc4NDksLTYuMzg4MSAyNC4xNzg0OSwtMTIuMzMyOTkgMCwtMS4zMzk4IDAuOTU0MDQsLTIuNzIyMzYgMS44Nzg1NywtMi43MjIzNiAwLjM5MTMyLDAgMi42MTY4NiwwLjY4MzY1IDQuOTQ1NjUsMS41MTkyMyA2LjA2OTkyLDIuMTc3OSA5LjYzMDI4LDIuOTY1NDggMTQuNjA5MTIsMy4yMzE2NSA0LjkzOTk4LDAuMjY0MDkgOC41NDg0OCwtMC4xNzA0IDEzLjM1ODE3LC0xLjYwODQxIDQuNzI1NjEsLTEuNDEyODggNy40NDcwOSwtMi44NTkwNSAxNS41OTgzMywtOC4yODg4MiA0LjEwNzYsLTIuNzM2MTggNy41OTk1NCwtNC45NzQ4NyA3Ljc1OTg4LC00Ljk3NDg3IDAuMTYwMzMsMCAzLjEyNTM0LDEuNzE4OTYgNi41ODg5LDMuODE5OTEgMy40NjM1NiwyLjEwMDk1IDcuNjI1NDIsNC4zODEzMyA5LjI0ODU5LDUuMDY3NTEgMTEuMDY4NjIsNC42NzkxOCAyMy4wMjQ0NSw0LjM3MzkgMzMuOTcxMjYsLTAuODY3NDEgMS45NTc5NSwtMC45Mzc0NyA2LjU2NzkzLC0zLjczMDc0IDEwLjI0NDQsLTYuMjA3MjcgMy42NzY0OCwtMi40NzY1MyA2LjczNjEzLC00LjQ0NTc2IDYuNzk5MjMsLTQuMzc2MDYgMC4wNjMxLDAuMDY5NyAxLjQ1NjA1LDIuOTIwOCAzLjA5NTQ0LDYuMzM1NzggMS42Mzk0LDMuNDE0OTggMy42OTg5NSw3LjI2NjcgNC41NzY3OSw4LjU1OTM3IDMuODAwMDEsNS41OTU3OCA4LjE3NTg4LDkuNzUxNDEgMTQuMDgzNDMsMTMuMzc0NjQgNi4wODEyMiwzLjcyOTc1IDkuODMyMDYsOC42ODY4IDEwLjIxODgyLDEzLjUwNTA3IDAuMzgzNjcsNC43Nzk2OCAtMS43MDMyOSw5LjAxOTM5IC03LjcyMTg5LDE1LjY4NzIxIC00LjY5NzQ3LDUuMjA0MTkgLTUuODczMyw3LjIzNTQgLTUuODYxMTMsMTAuMTI0OTEgMC4wMTIyLDIuODkyNjIgMS43MDEwMyw2LjIyODU0IDQuNjY5NjQsOS4yMjM3OSA0LjgwNTMzLDQuODQ4NDQgMTIuMzQ2MDUsNi4yODc3NyAyMy40MzUzNCw0LjQ3MzE5IDIuNDc0MDgsLTAuNDA0ODQgNS41OTQ3OSwtMC43Mzc1NyA2LjkzNDkxLC0wLjczOTM4IDQuMjg1NTEsLTAuMDA2IDYuOTAzMTksMi4wOTU5NSAxMS44OTI1MSw5LjU0ODU5IDMuNDQzNTEsNS4xNDM2NSAxMC45NzM5NiwxNy43NTUwOCAxMC45NzM5NiwxOC4zNzgzNiAwLDAuMzI0MDcgLTAuMzUxNDUsMC41NTIwOCAtMC44NTA5NywwLjU1MjA4IC0xLjA1MTA5LDAgLTUuNjIzMTQsLTEuMjQzOCAtOC45MzIxNCwtMi40Mjk5NCAtNC4zNDg1LC0xLjU1ODc1IC01Ny4yNzIzOCwtMjIuMDA1MjYgLTc0Ljc0Nzg2LC0yOC44Nzc5NSAtNDYuNjYzNSwtMTguMzUxNjIgLTYyLjAyODA1LC0yNC4yMTI0NiAtNjkuNTM2NTcsLTI2LjUyNDg5IC04LjExNDE3LC0yLjQ5ODk1IC0xMy42NDAyOSwtMy4wODUyMiAtMTguMTI2NjEsLTEuOTIzMDYgLTUuNTc5OTYsMS40NDU0NiAtODEuNzIwNjcsMzAuODY4ODggLTEyNi4xMzI0NSw0OC43NDE5NSAtMjEuOTMxMTksOC44MjYgLTI0LjY3NzQ5LDkuODc1NzcgLTI4Ljc4MTE2LDExLjAwMTU5IC0zLjI2MzAzLDAuODk1MjEgLTMuMTQ0MDcsMC44ODI5NCAtMy4xNDQwNywwLjMyNDI2IHoiIC8+DQogIDwvZz4NCjwvc3ZnPg==');
- background-position: 16px 80%;
background-repeat: no-repeat;
- background-size: 20px 12px;
+ background-size: 25px 14px;
padding-left: 2.5em;
}
.multi-language-selector .language-option[data-lang='kotlin'],
+.multi-language-selector .language-option.selected[data-lang='kotlin'],
.exampleblock[data-lang=kotlin] > .content .title {
background-image: url('data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeD0iMHB4IiB5PSIwcHgiDQoJIHZpZXdCb3g9IjAgMCA2MCA2MCIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgNjAgNjA7Ij4NCjxnPg0KICAgIDxsaW5lYXJHcmFkaWVudCBpZD0iWE1MSURfM18iIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiB4MT0iMTUuOTU5NCIgeTE9Ii0xMy4wMTQzIiB4Mj0iNDQuMzA2OCIgeTI9IjE1LjMzMzIiIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMSAwIDAgLTEgMCA2MSkiPg0KCQk8c3RvcCAgb2Zmc2V0PSI5LjY3NzAwMGUtMDIiIHN0eWxlPSJzdG9wLWNvbG9yOiMwMDk1RDUiLz4NCgkJPHN0b3AgIG9mZnNldD0iMC4zMDA3IiBzdHlsZT0ic3RvcC1jb2xvcjojMjM4QUQ5Ii8+DQoJCTxzdG9wICBvZmZzZXQ9IjAuNjIxMSIgc3R5bGU9InN0b3AtY29sb3I6IzU1N0JERSIvPg0KCQk8c3RvcCAgb2Zmc2V0PSIwLjg2NDMiIHN0eWxlPSJzdG9wLWNvbG9yOiM3NDcyRTIiLz4NCgkJPHN0b3AgIG9mZnNldD0iMSIgc3R5bGU9InN0b3AtY29sb3I6IzgwNkVFMyIvPg0KCTwvbGluZWFyR3JhZGllbnQ+DQoJPHBvbHlnb24gaWQ9IlhNTElEXzJfIiBzdHlsZT0iZmlsbDp1cmwoI1hNTElEXzNfKTsiIHBvaW50cz0iMCw2MCAzMC4xLDI5LjkgNjAsNjAgCSIvPg0KICAgIDxsaW5lYXJHcmFkaWVudCBpZD0iU1ZHSURfMV8iIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiB4MT0iNC4yMDkyIiB5MT0iNDguOTQwOSIgeDI9IjIwLjY3MzQiIHkyPSI2NS40MDUiIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMSAwIDAgLTEgMCA2MSkiPg0KCQk8c3RvcCAgb2Zmc2V0PSIwLjExODMiIHN0eWxlPSJzdG9wLWNvbG9yOiMwMDk1RDUiLz4NCgkJPHN0b3AgIG9mZnNldD0iMC40MTc4IiBzdHlsZT0ic3RvcC1jb2xvcjojM0M4M0RDIi8+DQoJCTxzdG9wICBvZmZzZXQ9IjAuNjk2MiIgc3R5bGU9InN0b3AtY29sb3I6IzZENzRFMSIvPg0KCQk8c3RvcCAgb2Zmc2V0PSIwLjgzMzMiIHN0eWxlPSJzdG9wLWNvbG9yOiM4MDZFRTMiLz4NCgk8L2xpbmVhckdyYWRpZW50Pg0KCTxwb2x5Z29uIHN0eWxlPSJmaWxsOnVybCgjU1ZHSURfMV8pOyIgcG9pbnRzPSIwLDAgMzAuMSwwIDAsMzIuNSAJIi8+DQogICAgPGxpbmVhckdyYWRpZW50IGlkPSJTVkdJRF8yXyIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHgxPSItMTAuMTAxNyIgeTE9IjUuODM2MiIgeDI9IjQ1LjczMTUiIHkyPSI2MS42Njk0IiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDEgMCAwIC0xIDAgNjEpIj4NCgkJPHN0b3AgIG9mZnNldD0iMC4xMDc1IiBzdHlsZT0ic3RvcC1jb2xvcjojQzc1N0JDIi8+DQoJCTxzdG9wICBvZmZzZXQ9IjAuMjEzOCIgc3R5bGU9InN0b3AtY29sb3I6I0QwNjA5QSIvPg0KCQk8c3RvcCAgb2Zmc2V0PSIwLjQyNTQiIHN0eWxlPSJzdG9wLWNvbG9yOiNFMTcyNUMiLz4NCgkJPHN0b3AgIG9mZnNldD0iMC42MDQ4IiBzdHlsZT0ic3RvcC1jb2xvcjojRUU3RTJGIi8+DQoJCTxzdG9wICBvZmZzZXQ9IjAuNzQzIiBzdHlsZT0ic3RvcC1jb2xvcjojRjU4NjEzIi8+DQoJCTxzdG9wICBvZmZzZXQ9IjAuODIzMiIgc3R5bGU9InN0b3AtY29sb3I6I0Y4ODkwOSIvPg0KCTwvbGluZWFyR3JhZGllbnQ+DQoJPHBvbHlnb24gc3R5bGU9ImZpbGw6dXJsKCNTVkdJRF8yXyk7IiBwb2ludHM9IjMwLjEsMCAwLDMxLjcgMCw2MCAzMC4xLDI5LjkgNjAsMCAJIi8+DQo8L2c+DQo8L3N2Zz4=');
- background-position: 17px 80%;
background-repeat: no-repeat;
- background-size: 11px 11px;
+ background-size: 12px 12px;
padding-left: 2.3em;
}
@@ -108,9 +108,10 @@
}
.multi-language-selector .language-option {
- background-color: white;
- border: 1px solid #f7f7f8;
- border-radius: 4px 4px 0 0;
+ color: #ffc107;
+ border: 0.4px solid #71d0ff;
+ background: none;
+ border-radius: 4px;
cursor: pointer;
display: inline-block;
font-weight: normal;
@@ -122,18 +123,20 @@
text-align: center;
filter: grayscale(1);
-webkit-filter: grayscale(1);
- opacity: 0.7;
}
.multi-language-selector .language-option.selected {
- background-color: #f7f7f8;
- color: #000000;
+ color: #f29111;
+ border: 0.4px solid #f29111;
+ box-shadow: 0px 0px 4px 0px #F29111;
+
font-weight: bold;
filter: none;
-webkit-filter: none;
- opacity: 1;
}
+
+
.multi-language-text.hidden,
.multi-language-selector + .multi-language-sample.hidden,
.multi-language-selector + .multi-language-sample + .multi-language-sample.hidden,
@@ -149,13 +152,20 @@
.copytoclipboard {
font-family: "Open Sans";
cursor: pointer;
- background-color: lightgray;
- color: #000000;
+ border: 0.4px solid #8ec9e696;
+ border-top: none;
float: right;
padding: 10px 15px;
font-size: 10px;
margin-right: 0px;
+ margin-top: -1px;
display: block;
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
}
+
+.copytoclipboard:hover {
+ background: #8EC9E6;
+ color: #171F26;
+ font-weight: 600;
+}
diff --git a/docs/src/docs/asciidoc/css/page.css b/docs/src/docs/asciidoc/css/page.css
new file mode 100644
index 000000000..41fe63895
--- /dev/null
+++ b/docs/src/docs/asciidoc/css/page.css
@@ -0,0 +1,172 @@
+body {
+ background: #161D24;
+ color: white;
+ font-family: 'OracleSans', sans-serif;
+}
+
+#content, #header {
+ max-width: 1200px;
+ padding-left: 40px;
+ padding-right: 40px;
+}
+
+.topscreen {
+ background: url('../css/images/grain.png') repeat, url('../css/images/metaimage-main-01.svg') 100% 20%/650px no-repeat, radial-gradient(142.83% 143.5% at 0.12% 128.91%, #264d6d 0%, rgba(38, 84, 107, 0) 100%), #171E25;
+}
+
+.topscreen__body {
+ padding: 0 20px;
+}
+
+.wrapper {
+ max-width: 1200px;
+ margin: 0 auto;
+ padding: 20px;
+}
+
+.topscreen__row {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: space-between;
+}
+
+.topscreen__content {
+ font-family: 'Oracle Sans', sans-serif;
+ color: #FFFFFF;
+ padding: 0px 0px 0px 0px;
+}
+
+.graalvm-logo img {
+ padding-top: 28px;
+ max-width: 55%;
+}
+
+.graalvm-rabbit {
+ padding: 10px 0px 0px 20px;
+ max-width: 8%;
+}
+@media (min-width: 320px) and (max-width: 372px) {
+ .graalvm-rabbit {
+ display: none;
+ }
+}
+@media (min-width: 373px) and (max-width: 1024px) {
+ .graalvm-rabbit {
+ max-width: 80px;
+ }
+}
+
+#toc {
+ background: none !important;
+}
+
+#toc > #toctitle {
+ color: #ffc107;
+}
+
+#header > h1:first-child {
+ color: white;
+ font-weight: 600;
+}
+
+#header>.details {
+ color: white;
+}
+
+#preamble>.sectionbody>[class="paragraph"]:first-of-type p {
+ color: white;
+ font-size: 1.0625rem;
+}
+
+p {
+ color: white;
+}
+
+a {
+ color: #f29111;
+}
+
+#toc a {
+ color: #ffffff;
+ font-size: 18px;
+ line-height: 28px;
+ font-weight: 600;
+}
+
+ul.sectlevel2 a {
+ font-size: 16px !important;
+ line-height: 20px !important;
+ font-weight: 200 !important;
+}
+
+#toc a:hover {
+ color: #8EC9E6;
+}
+
+a:hover {
+ color: #8EC9E6;
+}
+
+#toc.toc2 {
+ background: #1f2a33 !important;
+}
+
+#toctitle {
+ font-weight: 600;
+ margin-bottom: 24px !important;
+ color: #afe4ff !important;
+}
+
+.listingblock >.content>pre:not(.highlight) {
+ color: white;
+ background: none;
+ border: 1px solid #ffc107;
+ border-radius: 4px;
+}
+
+:not(pre):not([class^=L])>code {
+ background: #00758F;
+ color: white;
+}
+
+.admonitionblock td.icon .icon-tip::before,
+.admonitionblock td.icon .icon-note::before,
+.admonitionblock td.icon .icon-warning::before {
+ color: white;
+}
+
+.admonitionblock>table td.content {
+ color: white;
+}
+
+#toc > #toctitle {
+ color: #FFFFFF;
+}
+
+:not(pre):not([class^=L])>code {
+ background: #8EC9E6;
+ color: #171F26;
+}
+
+.listingblock pre.highlightjs>code {
+ background: #1f2a33;
+ border: 0.4px solid #8ec9e696;
+}
+
+.admonitionblock td.content>.title, .paragraph>.title {
+ color: #8EC9E6;
+}
+
+td.tableblock{
+ background: #171E25;
+}
+
+tr:first-child td:nth-child(1),
+tr:first-child td:nth-child(2) {
+ background: #283441;
+}
+
+p.tableblock>code:only-child {
+ background: #8EC9E6;
+ padding: 0px 4px;
+}
\ No newline at end of file
diff --git a/docs/src/docs/asciidoc/css/typography.css b/docs/src/docs/asciidoc/css/typography.css
new file mode 100644
index 000000000..b236dbccd
--- /dev/null
+++ b/docs/src/docs/asciidoc/css/typography.css
@@ -0,0 +1,72 @@
+@font-face {
+ font-family: 'OracleSans';
+ src: url('../resources/Fonts/OracleSans_Rg.ttf') format('truetype');
+ font-weight: 400;
+ font-style: normal;
+}
+@font-face {
+ font-family: 'OracleSans';
+ src: url('../resources/Fonts/OracleSans_Lt.ttf') format('truetype');
+ font-weight: 200;
+ font-style: normal;
+}
+@font-face {
+ font-family: 'OracleSans';
+ src: url('../resources/Fonts/OracleSans_ULt.ttf') format('truetype');
+ font-weight: 100;
+ font-style: normal;
+}
+@font-face {
+ font-family: 'OracleSans';
+ src: url('../resources/Fonts/OracleSans_SBd.ttf') format('truetype');
+ font-weight: 600;
+ font-style: normal;
+}
+@font-face {
+ font-family: 'OracleSans';
+ src: url('../resources/Fonts/OracleSans_Bd.ttf') format('truetype');
+ font-weight: 700;
+ font-style: normal;
+}
+
+h1 {
+ font-size: 48px;
+ line-height: 56px;
+ font-weight: 700;
+ color: #ffffff;
+}
+
+h2 {
+ font-size: 40px;
+ line-height: 48px;
+ font-weight: 600;
+ color: #ffffff;
+}
+
+h3 {
+ font-size: 34px;
+ line-height: 40px;
+ font-weight: 400;
+ color: #ffffff;
+}
+
+h4 {
+ font-size: 24px;
+ line-height: 32px;
+ font-weight: 400;
+ color: #ffffff;
+}
+
+h5 {
+ font-size: 16px;
+ line-height: 24px;
+ font-weight: 400;
+ color: #ffffff;
+}
+
+h6 {
+ font-size: 14px;
+ line-height: 24px;
+ font-weight: 600;
+ color: #ffffff;
+}
\ No newline at end of file
diff --git a/docs/src/docs/asciidoc/docinfo-footer.html b/docs/src/docs/asciidoc/docinfo-footer.html
index 6119b4f50..b66a940a0 100644
--- a/docs/src/docs/asciidoc/docinfo-footer.html
+++ b/docs/src/docs/asciidoc/docinfo-footer.html
@@ -1,7 +1,14 @@
+
+
+