Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 31 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ jobs:
echo "python=false" >> "$GITHUB_OUTPUT"
fi

if grep -Eq '^(java/|integration_tests/graalvm_tests/)' <<< "$changed_files"; then
if grep -Eq '^(\.github/workflows/ci\.yml$|ci/run_ci\.(py|sh)$|ci/tasks/(common|java)\.py$|java/|integration_tests/graalvm_tests/)' <<< "$changed_files"; then
echo "graalvm=true" >> "$GITHUB_OUTPUT"
else
echo "graalvm=false" >> "$GITHUB_OUTPUT"
Expand Down Expand Up @@ -575,7 +575,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
java-version: ["17", "21", "25"]
java-version: ["17", "25"]
steps:
- uses: actions/checkout@v5
- uses: graalvm/setup-graalvm@6f3fa030c4b8f77c1f554a860f593a654538fa38 # 1.5.6
Expand All @@ -591,13 +591,36 @@ jobs:
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-
- name: Set up Python3.8
uses: actions/setup-python@v5
- name: Build JPMS native image and run
shell: bash
run: ./ci/run_ci.sh graalvm_test

graalvm_json:
name: GraalVM JSON CI
needs: changes
if: needs.changes.outputs.graalvm == 'true'
runs-on: ubuntu-latest
strategy:
matrix:
java-version: ["17", "25"]
steps:
- uses: actions/checkout@v5
- uses: graalvm/setup-graalvm@6f3fa030c4b8f77c1f554a860f593a654538fa38 # 1.5.6
with:
python-version: 3.8
- name: Build native image and run
java-version: ${{ matrix.java-version }}
distribution: "graalvm"
github-token: ${{ secrets.GITHUB_TOKEN }}
native-image-job-reports: "true"
- name: Cache Maven local repository
uses: actions/cache@v4
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-
- name: Build JSON JPMS native image and run
shell: bash
run: python ./ci/run_ci.py java --version graalvm
run: ./ci/run_ci.sh graalvm_json_tests

kotlin:
name: Kotlin CI
Expand Down Expand Up @@ -916,7 +939,7 @@ jobs:
python-version: 3.11
cache: "pip"
- name: Set up Dart
uses: dart-lang/setup-dart@e51d8e571e22473a2ddebf0ef8a2123f0ab2c02c # v1.7.1
uses: dart-lang/setup-dart@e51d8e571e22473a2ddebf0ef8a2123f0ab2c02c # v1.7.1
with:
sdk: stable
- name: Cache Maven local repository
Expand Down
3 changes: 3 additions & 0 deletions ci/run_ci.py
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ def parse_args():
"windows_java21",
"integration_tests",
"graalvm",
"graalvm_json_tests",
],
default=None,
help="Java version to use for testing",
Expand Down Expand Up @@ -423,6 +424,8 @@ def parse_args():
run_shell_script("windows_java21")
elif version == "graalvm":
run_shell_script("graalvm_test")
elif version == "graalvm_json_tests":
run_shell_script("graalvm_json_tests")
else:
run_shell_script(f"java{version}")
elif command == "cpp":
Expand Down
41 changes: 26 additions & 15 deletions ci/run_ci.sh
Original file line number Diff line number Diff line change
Expand Up @@ -79,34 +79,45 @@ install_jdks() {
done
}

graalvm_test() {
run_graalvm_tests() {
local main_class="$1"
local java_version
local java_major
java_version=$(java -version 2>&1 | awk -F '"' '/version/ {print $2; exit}')
if [[ "$java_version" == 1.* ]]; then
java_major=$(echo "$java_version" | cut -d. -f2)
else
java_major=$(echo "$java_version" | cut -d. -f1)
fi
if [[ "$java_major" -ge 25 ]]; then
export JDK_JAVA_OPTIONS="$(jdk25_runtime_options "ALL-UNNAMED") $(jdk25_javac_options)"
export JDK_JAVA_OPTIONS="$(jdk25_javac_options)"
else
unset JDK_JAVA_OPTIONS
fi
cd "$ROOT"/java
mvn -T10 -B --no-transfer-progress clean install -DskipTests -pl '!:fory-testsuite'
echo "Start to build graalvm native image"
# GraalVM jobs consume production jars only; Java CI owns test/source jar verification.
# Run the install goal directly after package so verify is not repeated in every native job.
mvn -T10 -B --no-transfer-progress clean package install:install \
-pl .,fory-test-core,fory-core,fory-json \
-Dmaven.test.skip=true \
-Dmaven.source.skip=true \
-Dmaven.javadoc.skip=true
echo "Start to build GraalVM JPMS native image for $main_class"
cd "$ROOT"/integration_tests/graalvm_tests
mvn -DskipTests=true --no-transfer-progress -Pnative clean package
echo "Built GraalVM classpath native image"
echo "Start to run GraalVM classpath native image"
./target/main
if [[ "$java_major" -ge 25 ]]; then
export JDK_JAVA_OPTIONS="$(jdk25_javac_options)"
fi
mvn -DskipTests=true --no-transfer-progress -Pnative-module clean package
echo "Built GraalVM module-path native image"
echo "Start to run GraalVM module-path native image"
mvn -DmainClass="$main_class" -DskipTests=true -Dassembly.skipAssembly=true \
--no-transfer-progress -Pnative-module clean package
echo "Built GraalVM JPMS native image"
echo "Start to run GraalVM JPMS native image"
./target/main-module
echo "Execute graalvm tests succeed!"
echo "Execute GraalVM tests for $main_class succeed!"
}

graalvm_test() {
run_graalvm_tests org.apache.fory.graalvm.Main
}

graalvm_json_tests() {
run_graalvm_tests org.apache.fory.graalvm.ForyJsonExample
}

jdk25_access_options() {
Expand Down
37 changes: 27 additions & 10 deletions ci/tasks/java.py
Original file line number Diff line number Diff line change
Expand Up @@ -352,29 +352,44 @@ def run_integration_tests():
logging.info("Executing fory integration tests succeeds")


def run_graalvm_test():
"""Run GraalVM tests."""
logging.info("Start GraalVM tests")
def run_graalvm_tests(main_class):
"""Build and run JPMS GraalVM tests."""
logging.info(f"Start GraalVM tests for {main_class}")
java_major = get_jdk_major_version()
if java_major is not None and java_major >= 25:
os.environ["JDK_JAVA_OPTIONS"] = " ".join(jdk25_javac_options())
else:
os.environ.pop("JDK_JAVA_OPTIONS", None)

common.cd_project_subdir("java")
# Java CI owns test/source jar verification. GraalVM jobs install only the
# production multi-release jars consumed by native-image.
common.exec_cmd(
"mvn -T10 -B --no-transfer-progress clean install -DskipTests -pl '!:fory-testsuite'"
"mvn -T10 -B --no-transfer-progress clean package install:install "
"-pl .,fory-test-core,fory-core,fory-json -Dmaven.test.skip=true "
"-Dmaven.source.skip=true -Dmaven.javadoc.skip=true"
)

logging.info("Start to build graalvm native image")
logging.info(f"Start to build GraalVM JPMS native image for {main_class}")
common.cd_project_subdir("integration_tests/graalvm_tests")
common.exec_cmd("mvn -DskipTests=true --no-transfer-progress -Pnative package")
common.exec_cmd(
f"mvn -DmainClass={main_class} -DskipTests=true "
"-Dassembly.skipAssembly=true --no-transfer-progress "
"-Pnative-module clean package"
)

logging.info("Built GraalVM JPMS native image")
logging.info("Start to run GraalVM JPMS native image")
common.exec_cmd("./target/main-module")
logging.info(f"Execute GraalVM tests for {main_class} succeed!")


def run_graalvm_test():
run_graalvm_tests("org.apache.fory.graalvm.Main")

logging.info("Built graalvm native image")
logging.info("Start to run graalvm native image")
common.exec_cmd("./target/main")

logging.info("Execute graalvm tests succeed!")
def run_graalvm_json_tests():
run_graalvm_tests("org.apache.fory.graalvm.ForyJsonExample")


def run_release():
Expand Down Expand Up @@ -435,3 +450,5 @@ def run(version=None, release=False, install_jdks=False, install_fory=False):
run_integration_tests()
elif version == "graalvm":
run_graalvm_test()
elif version == "graalvm_json_tests":
run_graalvm_json_tests()
2 changes: 2 additions & 0 deletions integration_tests/graalvm_tests/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,8 @@
<executable>native-image</executable>
<arguments>
<argument>--no-fallback</argument>
<!-- These images verify native-image compatibility, not runtime performance. -->
<argument>-O1</argument>
<argument>-H:+UnlockExperimentalVMOptions</argument>
<argument>-o</argument>
<argument>${project.build.directory}/${moduleImageName}</argument>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,5 @@ public static void main(String[] args) throws Throwable {
ExceptionExample.main(args);
AbstractClassExample.main(args);
FeatureTestExample.main(args);
ForyJsonExample.main(args);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ public final class MemoryBuffer {
private static final int LONG_ARRAY_OFFSET;
private static final int FLOAT_ARRAY_OFFSET;
private static final int DOUBLE_ARRAY_OFFSET;
private static final long BUFFER_ADDRESS_FIELD_OFFSET;

// GraalVM native-image recognizes arrayBaseOffset only when the call stores directly into the
// target static field. Keep these assignments in this shape so native images recompute heap array
Expand All @@ -91,6 +92,7 @@ public final class MemoryBuffer {
LONG_ARRAY_OFFSET = 0;
FLOAT_ARRAY_OFFSET = 0;
DOUBLE_ARRAY_OFFSET = 0;
BUFFER_ADDRESS_FIELD_OFFSET = -1;
} else {
BOOLEAN_ARRAY_OFFSET = UNSAFE.arrayBaseOffset(boolean[].class);
BYTE_ARRAY_OFFSET = UNSAFE.arrayBaseOffset(byte[].class);
Expand All @@ -100,29 +102,24 @@ public final class MemoryBuffer {
LONG_ARRAY_OFFSET = UNSAFE.arrayBaseOffset(long[].class);
FLOAT_ARRAY_OFFSET = UNSAFE.arrayBaseOffset(float[].class);
DOUBLE_ARRAY_OFFSET = UNSAFE.arrayBaseOffset(double[].class);
try {
Field addressField = Buffer.class.getDeclaredField("address");
// GraalVM native-image only recomputes a hosted object field offset when this call stores
// directly into its final static owner. Do not route it through a helper or local offset.
BUFFER_ADDRESS_FIELD_OFFSET = UNSAFE.objectFieldOffset(addressField);
checkArgument(BUFFER_ADDRESS_FIELD_OFFSET != 0);
} catch (NoSuchFieldException e) {
throw new IllegalStateException(e);
}
}
}

/** Limits each raw Unsafe copy to let large copies hit safepoint polls between chunks. */
private static final long UNSAFE_COPY_THRESHOLD = 1024L * 1024L;

private static final long BUFFER_ADDRESS_FIELD_OFFSET =
AndroidSupport.IS_ANDROID ? -1 : bufferAddressFieldOffset();

// Global allocator instance that can be customized
private static volatile MemoryAllocator globalAllocator = new DefaultMemoryAllocator();

private static long bufferAddressFieldOffset() {
try {
Field addressField = Buffer.class.getDeclaredField("address");
long offset = UNSAFE.objectFieldOffset(addressField);
checkArgument(offset != 0);
return offset;
} catch (NoSuchFieldException e) {
throw new IllegalStateException(e);
}
}

private static boolean unaligned() {
String arch = System.getProperty("os.arch", "");
if ("ppc64le".equals(arch) || "ppc64".equals(arch) || "s390x".equals(arch)) {
Expand Down
Loading
Loading