From 60b9326b13370754b77195b8c92eee04e06a7c54 Mon Sep 17 00:00:00 2001 From: Andreas Stefl Date: Thu, 20 Aug 2026 10:47:15 +0200 Subject: [PATCH 1/3] fix(jni): require a jdk for the jar rather than shipping without it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `find_package(Java)` was optional on android so the AAR build, which compiles `jni/java/` with the android toolchain, would not need a JDK. That made a missing JDK silently drop `odr-core-java.jar` from the android odrcore package too — where OpenDocument.droid takes the jar out of the package and fails much later, looking like a broken deployer. The jar is not an android-vs-not question, so ask it directly: `ODR_JNI_JAR` (default `ON`) says whether the java half is wanted, and with it on a JDK is `REQUIRED` everywhere. `android/build_native.py` turns it off, which is the one build that genuinely wants the native half alone. `find_package(JNI)` stays behind `if (NOT ANDROID)` — the NDK sysroot ships `jni.h` and the symbols come from the runtime. Closes #637 Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01XDs5aK3ZGSZsEvqUUwBBXU --- CHANGELOG.md | 3 +++ android/build_native.py | 3 +++ jni/AGENTS.md | 2 +- jni/CMakeLists.txt | 24 +++++++++++++----------- jni/README.md | 6 ++++++ 5 files changed, 26 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0294b38e8..434e283c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,9 @@ The release run heads these entries with the version and opens a fresh - A pdf that nests parentheses inside a string opens, and keeps its document metadata — `cairo` and `pdfTeX` write their `/Producer` that way. +- A jni build that cannot find a JDK fails instead of shipping a package without + `odr-core-java.jar` in it. The new `ODR_JNI_JAR` (default `ON`) is what says + whether the jar is wanted; the AAR build turns it off and needs no JDK. ## v6.9.0 - 2026-08-18 diff --git a/android/build_native.py b/android/build_native.py index 2df19b239..fc7a418f0 100644 --- a/android/build_native.py +++ b/android/build_native.py @@ -93,6 +93,9 @@ def build(architecture: str, conan: str, build_profile: str, output: Path) -> No # rather than shipped as a second .so the app would have to load "-DBUILD_SHARED_LIBS=OFF", "-DODR_JNI=ON", + # the AAR compiles `jni/java/` itself, so the native half is all we + # want out of cmake — and no JDK is needed to produce it + "-DODR_JNI_JAR=OFF", "-DODR_CLI=OFF", "-DODR_TEST=OFF", "-DODR_WITH_HTTP_SERVER=ON"]) diff --git a/jni/AGENTS.md b/jni/AGENTS.md index de5111f46..260ed7f3a 100644 --- a/jni/AGENTS.md +++ b/jni/AGENTS.md @@ -8,7 +8,7 @@ package `app.opendocument.core`. Mirrors the surface of the python bindings | Path | What | |------|------| -| `CMakeLists.txt` | Builds `libodr_jni` + `odr-core-java.jar`; included from the root build via `ODR_JNI`, or standalone against an installed `odrcore`. | +| `CMakeLists.txt` | Builds `libodr_jni` + `odr-core-java.jar`; included from the root build via `ODR_JNI`, or standalone against an installed `odrcore`. `ODR_JNI_JAR=OFF` builds the native half alone — what `../android` does; on it a JDK is `REQUIRED`, on android too. | | `pom.xml` | Maven distribution of the Java classes only (`app.opendocument:odr-core-java`); published to Maven Central (the `central` profile) and GitHub Packages on release via `.github/workflows/maven.yml`. Keep `--release`/`-Xlint` in sync with `CMAKE_JAVA_COMPILE_FLAGS`. | | `src/` | JNI sources, one `jni_*` unit per public-API area; `odr_jni.hpp` (strings, exceptions, handles) and `jni_convert.hpp` (struct/POJO marshalling) are the helpers. | | `java/app/opendocument/core/` | Java API: enums, POJOs (styles, metas, `HtmlConfig`), and handle-backed wrappers extending `NativeResource`. Also compiled as-is into the AAR (`../android`) — which is kotlin, but this stays java: `add_jar` below has no kotlin toolchain. | diff --git a/jni/CMakeLists.txt b/jni/CMakeLists.txt index e5ced466a..e3912e1f8 100644 --- a/jni/CMakeLists.txt +++ b/jni/CMakeLists.txt @@ -21,18 +21,20 @@ else () endif () # On android the NDK sysroot ships `jni.h` and the JNI symbols come from the -# runtime, so there is nothing to find or link against. A JDK stays optional -# there rather than unused: the android odrcore package ships -# `odr-core-java.jar` next to `libodr_jni.so` and OpenDocument.droid takes both -# out of it, while `android/` (the AAR) builds the native half alone, with the -# android toolchain compiling `java/` itself. -if (ANDROID) - find_package(Java 11 COMPONENTS Development) -else () - find_package(Java 11 REQUIRED COMPONENTS Development) +# runtime, so there is nothing to find or link against. +if (NOT ANDROID) find_package(JNI REQUIRED) endif () -if (Java_FOUND) + +# The jar is not an android-vs-not question: the android odrcore package ships +# `odr-core-java.jar` next to `libodr_jni.so` and OpenDocument.droid takes both +# out of it. Only `android/` (the AAR) wants the native half alone, with the +# android toolchain compiling `java/` itself, and says so — so a missing JDK +# fails the build that asked for a jar instead of quietly shipping half a +# package. +option(ODR_JNI_JAR "Build the java half, `odr-core-java.jar`" ON) +if (ODR_JNI_JAR) + find_package(Java 11 REQUIRED COMPONENTS Development) include(UseJava) endif () @@ -56,7 +58,7 @@ endif () install(TARGETS odr_jni LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT jni) -if (NOT Java_FOUND) +if (NOT ODR_JNI_JAR) return() endif () diff --git a/jni/README.md b/jni/README.md index 431291716..a3a8f2b5a 100644 --- a/jni/README.md +++ b/jni/README.md @@ -71,6 +71,12 @@ This produces `build/jni/libodr_jni.dylib` (or `.so`) and `build/jni/odr-core-java.jar`. `jni/CMakeLists.txt` can also be configured standalone against an installed `odrcore` package. +`ODR_JNI_JAR=OFF` builds the native library alone and needs no JDK. That is what +the AAR build (`android/build_native.py`) asks for, since it compiles +`jni/java/` with the android toolchain instead. With it on — the default — a +missing JDK fails the configure step rather than producing a package without the +jar in it. + ## Runtime data There is none. The renderer's CSS/JS are part of the library and detection needs From 658319d86bb1ead7cfe07ebc772e664a493e0242 Mon Sep 17 00:00:00 2001 From: Andreas Stefl Date: Thu, 20 Aug 2026 11:55:32 +0200 Subject: [PATCH 2/3] docs(jni): say where ODR_JNI_JAR=OFF actually removes the JDK requirement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only on android. Everywhere else `find_package(JNI REQUIRED)` runs regardless, and CMake's `FindJNI` wants a JDK for the headers — so a host build with the jar off and no JDK still fails to configure, as it should. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01XDs5aK3ZGSZsEvqUUwBBXU --- jni/AGENTS.md | 2 +- jni/README.md | 14 +++++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/jni/AGENTS.md b/jni/AGENTS.md index 260ed7f3a..12d2a6bc5 100644 --- a/jni/AGENTS.md +++ b/jni/AGENTS.md @@ -8,7 +8,7 @@ package `app.opendocument.core`. Mirrors the surface of the python bindings | Path | What | |------|------| -| `CMakeLists.txt` | Builds `libodr_jni` + `odr-core-java.jar`; included from the root build via `ODR_JNI`, or standalone against an installed `odrcore`. `ODR_JNI_JAR=OFF` builds the native half alone — what `../android` does; on it a JDK is `REQUIRED`, on android too. | +| `CMakeLists.txt` | Builds `libodr_jni` + `odr-core-java.jar`; included from the root build via `ODR_JNI`, or standalone against an installed `odrcore`. `ODR_JNI_JAR=OFF` builds the native half alone — what `../android` does, and the only build that needs no JDK, since `find_package(JNI)` is skipped on android too. With the jar on, a JDK is `REQUIRED` on every platform. | | `pom.xml` | Maven distribution of the Java classes only (`app.opendocument:odr-core-java`); published to Maven Central (the `central` profile) and GitHub Packages on release via `.github/workflows/maven.yml`. Keep `--release`/`-Xlint` in sync with `CMAKE_JAVA_COMPILE_FLAGS`. | | `src/` | JNI sources, one `jni_*` unit per public-API area; `odr_jni.hpp` (strings, exceptions, handles) and `jni_convert.hpp` (struct/POJO marshalling) are the helpers. | | `java/app/opendocument/core/` | Java API: enums, POJOs (styles, metas, `HtmlConfig`), and handle-backed wrappers extending `NativeResource`. Also compiled as-is into the AAR (`../android`) — which is kotlin, but this stays java: `add_jar` below has no kotlin toolchain. | diff --git a/jni/README.md b/jni/README.md index a3a8f2b5a..6b43cfa8f 100644 --- a/jni/README.md +++ b/jni/README.md @@ -71,11 +71,15 @@ This produces `build/jni/libodr_jni.dylib` (or `.so`) and `build/jni/odr-core-java.jar`. `jni/CMakeLists.txt` can also be configured standalone against an installed `odrcore` package. -`ODR_JNI_JAR=OFF` builds the native library alone and needs no JDK. That is what -the AAR build (`android/build_native.py`) asks for, since it compiles -`jni/java/` with the android toolchain instead. With it on — the default — a -missing JDK fails the configure step rather than producing a package without the -jar in it. +`ODR_JNI_JAR=OFF` builds the native library alone, without the java half. That +is what the AAR build (`android/build_native.py`) asks for, since it compiles +`jni/java/` with the android toolchain instead — and on android that build needs +no JDK at all, because the NDK sysroot ships `jni.h`. Everywhere else +`find_package(JNI REQUIRED)` still runs and still wants a JDK for the headers, +whatever `ODR_JNI_JAR` says. + +With the jar on — the default — a missing JDK fails the configure step rather +than producing a package without `odr-core-java.jar` in it. ## Runtime data From 91ef4e03185a36c554ee348f1efc66560bb588df Mon Sep 17 00:00:00 2001 From: Andreas Stefl Date: Thu, 20 Aug 2026 14:09:39 +0200 Subject: [PATCH 3/3] docs(jni): trim the ODR_JNI_JAR prose Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_015d5RcmsA777vwXiuafjx6k --- CHANGELOG.md | 6 +++--- android/build_native.py | 3 +-- jni/AGENTS.md | 2 +- jni/CMakeLists.txt | 10 ++++------ jni/README.md | 14 +++++--------- 5 files changed, 14 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 434e283c4..7f71ef078 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,9 +18,9 @@ The release run heads these entries with the version and opens a fresh - A pdf that nests parentheses inside a string opens, and keeps its document metadata — `cairo` and `pdfTeX` write their `/Producer` that way. -- A jni build that cannot find a JDK fails instead of shipping a package without - `odr-core-java.jar` in it. The new `ODR_JNI_JAR` (default `ON`) is what says - whether the jar is wanted; the AAR build turns it off and needs no JDK. +- A jni build without a JDK fails instead of shipping a package missing + `odr-core-java.jar`. `ODR_JNI_JAR=OFF` is how the AAR build asks for the + native half alone. ## v6.9.0 - 2026-08-18 diff --git a/android/build_native.py b/android/build_native.py index fc7a418f0..e77bb0e82 100644 --- a/android/build_native.py +++ b/android/build_native.py @@ -93,8 +93,7 @@ def build(architecture: str, conan: str, build_profile: str, output: Path) -> No # rather than shipped as a second .so the app would have to load "-DBUILD_SHARED_LIBS=OFF", "-DODR_JNI=ON", - # the AAR compiles `jni/java/` itself, so the native half is all we - # want out of cmake — and no JDK is needed to produce it + # the AAR compiles `jni/java/` itself, so no jar and no JDK here "-DODR_JNI_JAR=OFF", "-DODR_CLI=OFF", "-DODR_TEST=OFF", diff --git a/jni/AGENTS.md b/jni/AGENTS.md index 12d2a6bc5..b86f92dec 100644 --- a/jni/AGENTS.md +++ b/jni/AGENTS.md @@ -8,7 +8,7 @@ package `app.opendocument.core`. Mirrors the surface of the python bindings | Path | What | |------|------| -| `CMakeLists.txt` | Builds `libodr_jni` + `odr-core-java.jar`; included from the root build via `ODR_JNI`, or standalone against an installed `odrcore`. `ODR_JNI_JAR=OFF` builds the native half alone — what `../android` does, and the only build that needs no JDK, since `find_package(JNI)` is skipped on android too. With the jar on, a JDK is `REQUIRED` on every platform. | +| `CMakeLists.txt` | Builds `libodr_jni` + `odr-core-java.jar`; included from the root build via `ODR_JNI`, or standalone against an installed `odrcore`. `ODR_JNI_JAR=OFF` builds the native half alone — what `../android` does, and the only build needing no JDK. | | `pom.xml` | Maven distribution of the Java classes only (`app.opendocument:odr-core-java`); published to Maven Central (the `central` profile) and GitHub Packages on release via `.github/workflows/maven.yml`. Keep `--release`/`-Xlint` in sync with `CMAKE_JAVA_COMPILE_FLAGS`. | | `src/` | JNI sources, one `jni_*` unit per public-API area; `odr_jni.hpp` (strings, exceptions, handles) and `jni_convert.hpp` (struct/POJO marshalling) are the helpers. | | `java/app/opendocument/core/` | Java API: enums, POJOs (styles, metas, `HtmlConfig`), and handle-backed wrappers extending `NativeResource`. Also compiled as-is into the AAR (`../android`) — which is kotlin, but this stays java: `add_jar` below has no kotlin toolchain. | diff --git a/jni/CMakeLists.txt b/jni/CMakeLists.txt index e3912e1f8..7a4474a1d 100644 --- a/jni/CMakeLists.txt +++ b/jni/CMakeLists.txt @@ -26,12 +26,10 @@ if (NOT ANDROID) find_package(JNI REQUIRED) endif () -# The jar is not an android-vs-not question: the android odrcore package ships -# `odr-core-java.jar` next to `libodr_jni.so` and OpenDocument.droid takes both -# out of it. Only `android/` (the AAR) wants the native half alone, with the -# android toolchain compiling `java/` itself, and says so — so a missing JDK -# fails the build that asked for a jar instead of quietly shipping half a -# package. +# Android is no reason to skip the jar — the android odrcore package ships it +# next to `libodr_jni.so` for OpenDocument.droid. Only the AAR build wants the +# native half alone, and asks for it, so a missing JDK fails rather than +# quietly shipping half a package. option(ODR_JNI_JAR "Build the java half, `odr-core-java.jar`" ON) if (ODR_JNI_JAR) find_package(Java 11 REQUIRED COMPONENTS Development) diff --git a/jni/README.md b/jni/README.md index 6b43cfa8f..0ec0c6092 100644 --- a/jni/README.md +++ b/jni/README.md @@ -71,15 +71,11 @@ This produces `build/jni/libodr_jni.dylib` (or `.so`) and `build/jni/odr-core-java.jar`. `jni/CMakeLists.txt` can also be configured standalone against an installed `odrcore` package. -`ODR_JNI_JAR=OFF` builds the native library alone, without the java half. That -is what the AAR build (`android/build_native.py`) asks for, since it compiles -`jni/java/` with the android toolchain instead — and on android that build needs -no JDK at all, because the NDK sysroot ships `jni.h`. Everywhere else -`find_package(JNI REQUIRED)` still runs and still wants a JDK for the headers, -whatever `ODR_JNI_JAR` says. - -With the jar on — the default — a missing JDK fails the configure step rather -than producing a package without `odr-core-java.jar` in it. +`ODR_JNI_JAR=OFF` builds the native library alone. That is what the AAR build +(`android/build_native.py`) asks for, since it compiles `jni/java/` with the +android toolchain instead, and it is the only build needing no JDK — on android +the headers come from the NDK sysroot. Otherwise a missing JDK fails the +configure step rather than producing a package without the jar in it. ## Runtime data