This is an automated email from the ASF dual-hosted git repository. leginee pushed a commit to branch bazel-migration in repository https://gitbox.apache.org/repos/asf/openoffice.git
commit 6553ab8a6e1c43a65a125f83644fa423591c6b9d Author: Peter Kovacs <[email protected]> AuthorDate: Wed Aug 5 20:48:24 2026 +0200 build(java): stage the Java2 loader runtime — classes/, jvmfwk JRE config The Java2 loader had its two DLLs (javaloader.uno, javavm.uno) but nothing they need at runtime. This wires the whole bootstrap chain. The open question was how jurt/ridl/unoil reach the class loader. There are TWO channels, and conflating them is what made URE_INTERNAL_JAVA_CLASSPATH look optional: * FIVE jars arrive by HARDCODED NAME. javavm.cxx opens the literal "$URE_INTERNAL_JAVA_DIR/unoloader.jar", and UnoClassLoader.createUrls() then appends java_uno.jar/juh.jar/jurt.jar/ridl.jar. No manifest, no classpath variable, no UNO-Type-Path: the filename IS the lookup key. * unoil.jar is NOT one of the five. It reaches the loader only as the classPath ctor argument, i.e. $URE_INTERNAL_JAVA_CLASSPATH -> URE_MORE_JAVA_TYPES. An empty value does not just lose extensions, it loses every com.sun.star.* office API type. UNO-Type-Path bites elsewhere: getClassLoader() reads it when javaloader opens a COMPONENT jar. juh's empty value suppresses the hoist; an absent one falls back to "<>" = re-add the jar itself, already present via createUrls(), so a harmless duplicate. Hence only juh's RegistrationClassName is reproduced — juh.jar is a registered component in services.rdb. New uno_jar rule (build/rules/java_pipeline.bzl, singlejar) solves the three build-side problems at once: exact `out =` filename at the producing target rather than a staging rename table; N-jar merge (ridl.jar = :ridl + :udkapi_java_jar — `exports` is compile-time only, so libridl.jar alone has no com.sun.star.uno.*); and manifest main attributes, which neither java_library nor jar_from_directory writes. DIVERGENCE: Sealed: is omitted. --deploy_manifest_lines writes the main section only, so jurt's per-package un-sealing of com/sun/star/uno/ and com/sun/star/lib/util/ (packages SPLIT across jurt.jar and ridl.jar) is inexpressible, and a blanket Sealed:true would be STRICTER than upstream -> SecurityException on exactly that split. Sealing only restricts, never enables, so omitting is the safe direction. Staged flat to program/classes/ via a new `flatten` option on tree_install (six jars from five packages, so no single strip_prefix applies). fundamental.ini gains URE_INTERNAL_JAVA_DIR, URE_MORE_JAVA_TYPES, URE_INTERNAL_JAVA_CLASSPATH and URE_OVERRIDE_JAVA_JFW_{SHARED,USER}_DATA — it is the URE_BOOTSTRAP file theMacroExpander resolves against; uno.ini is not. Two more stubs found, same family as the javaloader/javavm one (the DLL exists but the feature is compiled out): * jvmfwk.dll was built WITHOUT SOLAR_JAVA, under which framework.cxx compiles jfw_startVM() to a bare `return JFW_E_ERROR;`. No configuration could ever have made Java work. The exported ABI is unaffected — the JNI types appear only behind pointers — so consumers that include jvmfwk/framework.h without it still link. * sunjavaplugin.dll was never built. jvmfwk holds no JRE knowledge at all; javavendors.xml maps every vendor to vnd.sun.star.expand:$URE_INTERNAL_LIB_DIR/sunjavaplugin.dll, which it osl_loadModule()s. Built here (8 TUs, 4 exports from sunjavaplugin.map -> util/sunjavaplugin.def) — a PRIVATE jvmfwk plugin interface, not a UNO component, so no component_getFactory and nothing in services.rdb. Nothing links it, so it is listed explicitly in staging. javavendors.xml (from javavendors_wnt.xml via copy_file) and jvmfwk3.ini are staged into program/. The ini NAME is fixed by fwkutil.hxx's SAL_CONFIGFILE("/jvmfwk3") relative to the library's own directory, so it stays jvmfwk3 even though ours is jvmfwk.dll. Also fixes a pre-existing break that stopped EVERY java_library in the tree: //build/toolchain/java:host_jdk_runtime_toolchain does not only supply jar.exe, it also picks the JVM Bazel starts JavaBuilder on. Having no target_settings it wins resolution unconditionally, so with JAVA_HOME a 32-bit JDK 8 every Java compile died with "Unrecognized option: --add-exports=jdk.compiler/... Could not create the Java Virtual Machine". Repointed at @remotejdk21_win: hermetic, and independent of JAVA_HOME. Java 8 bytecode is unaffected (--java_language_version=8 is a compiler flag, not a property of the JVM javac runs on), as is the 32-bit JVM the java_uno JNI tests host — java_run_test.bzl takes explicit jvm_path_dirs and never consults a java toolchain. Not yet exercised: nothing has loaded a real Java UNO component. The next probe is cppuhelper/qa/propertysetmixin's three red testJava* cases. See main/stoc/readme.md and main/jvmfwk/readme.md. Co-Authored-By: Claude Opus 5 <[email protected]> --- CLAUDE.md | 73 ++++++++++---- MODULE.bazel | 12 ++- build/rules/java_pipeline.bzl | 90 +++++++++++++++++ build/toolchain/java/BUILD.bazel | 26 +++-- main/bridges/BUILD.bazel | 13 ++- main/javaunohelper/BUILD.bazel | 21 ++++ main/jurt/BUILD.bazel | 18 ++++ main/jvmfwk/BUILD.bazel | 95 ++++++++++++++++++ main/jvmfwk/readme.md | 82 +++++++++++++++- main/jvmfwk/util/sunjavaplugin.def | 6 ++ main/ridljar/BUILD.bazel | 27 +++++- main/staging/BUILD.bazel | 54 +++++++++++ main/staging/collect_files_aspect.bzl | 17 +++- main/staging/fundamental.ini | 26 +++++ main/staging/jvmfwk3.ini | 20 ++++ main/staging/uno.ini | 9 +- main/stoc/readme.md | 178 +++++++++++++++++++++------------- main/unoil/BUILD.bazel | 18 +++- 18 files changed, 682 insertions(+), 103 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 3a6d4486c2..f51b42838f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -266,27 +266,58 @@ C2027 even in javaloader.cxx which #includes "jni.h" ITSELF (the stub is already in scope); (2) javavm needs /Zc:wchar_t- (passes sal_Unicode ptr into NewString/ GetStringRegion; on Windows sal_Unicode IS wchar_t, distinct from jchar unless wchar_t is unsigned short), javaloader does NOT (only NewStringUTF). -NEXT STEP IS STAGING, NOT COMPILATION — and it is not just a classes/ dest: -(a) Bazel jar names ≠ runtime names (java_library "x" → libx.jar, runtime wants -bare unoloader.jar/jurt.jar/juh.jar/unoil.jar/java_uno.jar by EXACT string) so -each needs an explicit destname (stage_install's manifest already has that 3rd -column); (b) ridl.jar must be MERGED not copied — upstream's holds generated UDK -types + 27 hand-written sources, but here exports=[":udkapi_java"] is -COMPILE-time only, so libridl.jar has no com.sun.star.uno.* (fix = the -java_binary(create_executable=False) deploy-jar trick from propertysetmixin); -(c) the jars need MANIFESTS nothing currently emits (checked-in at -jurt/util/manifest, javaunohelper/util/manifest, ridljar/.../unoloader/manifest -— Sealed:, RegistrationClassName:, UNO-Type-Path:), and UnoClassLoader reads -UNO-Type-Path off the main attributes, so they are load-bearing; neither -jar_from_directory (bare `jar cf`) nor java_library sets one. OPEN QUESTION to -resolve FIRST: how jurt/ridl/unoil actually reach the class loader given -URE_INTERNAL_JAVA_CLASSPATH expands empty and juh's UNO-Type-Path is empty — -read UnoClassLoader.getClassLoader() + javavm.cxx's UnoClassLoader construction -before wiring. Also still needed: URE_INTERNAL_JAVA_DIR in -program/fundamental.ini (NOT uno.ini — theMacroExpander resolves against the -URE_BOOTSTRAP file; uno.ini already has it, fundamental.ini does not), and a -jvmfwk JRE config (javavendors.xml + jvmfwk3) or jvmfwk finds no JRE. -See main/stoc/readme.md. +STAGING DONE 2026-08-05 — the whole bootstrap chain is now wired; what is left is +to EXERCISE it (nothing has yet loaded a real Java UNO component). +OPEN QUESTION ANSWERED — there are TWO channels, and conflating them is what made +URE_INTERNAL_JAVA_CLASSPATH look optional: (1) FIVE jars arrive by HARDCODED NAME +— javavm.cxx opens the literal "$URE_INTERNAL_JAVA_DIR/unoloader.jar", and +UnoClassLoader.createUrls() then appends java_uno.jar/juh.jar/jurt.jar/ridl.jar. +No manifest, no classpath var, no UNO-Type-Path involved: the FILENAME IS THE +LOOKUP KEY. (2) unoil.jar is NOT one of the five — it reaches the loader only as +the classPath ctor arg, i.e. $URE_INTERNAL_JAVA_CLASSPATH → URE_MORE_JAVA_TYPES +(upstream $ORIGIN/classes/unoil.jar + ScriptFramework.jar + each extension's +UNO_JAVA_CLASSPATH). So an empty value does not just lose extensions, it loses +EVERY com.sun.star.* office API type. UNO-Type-Path bites somewhere else +entirely: getClassLoader() reads it when javaloader opens a COMPONENT jar; juh's +empty value suppresses the hoist, an ABSENT one falls back to "<>" = re-add the +jar itself, already there via createUrls() ⇒ harmless duplicate. Hence only +juh's RegistrationClassName was reproduced (juh.jar IS a registered component). +NEW RULE uno_jar (build/rules/java_pipeline.bzl, singlejar) solves all three +build-side problems at once — exact `out =` filename at the PRODUCING target +(not a staging rename table), N-jar merge (ridl.jar = :ridl + :udkapi_java_jar, +verified 436 classes incl. XInterface/TypeClass/UnoRuntime), and manifest main +attributes. DIVERGENCE: Sealed: is OMITTED. --deploy_manifest_lines writes the +MAIN section only, so jurt's per-package un-sealing of com/sun/star/uno/ + +com/sun/star/lib/util/ (those packages are SPLIT across jurt.jar and ridl.jar) is +inexpressible, and a blanket Sealed:true would be STRICTER than upstream → +SecurityException on exactly that split. Sealing only ever restricts, never +enables ⇒ omitting is the safe direction. +Staged flat to program/classes/ via //main/staging:_install_classes, using a new +`flatten` opt on tree_install (six jars, five packages, no single strip_prefix). +fundamental.ini got URE_INTERNAL_JAVA_DIR + URE_MORE_JAVA_TYPES + +URE_INTERNAL_JAVA_CLASSPATH + URE_OVERRIDE_JAVA_JFW_{SHARED,USER}_DATA — it is +the URE_BOOTSTRAP file theMacroExpander resolves against, uno.ini is NOT. +TWO MORE STUBS FOUND, same family as the javaloader/javavm one (a DLL exists but +the feature is compiled out): (a) jvmfwk.dll was built WITHOUT SOLAR_JAVA, under +which framework.cxx compiles jfw_startVM() to a bare `return JFW_E_ERROR;` — no +configuration could ever have made Java work; ABI is unaffected (JNI types appear +only behind pointers) so consumers including framework.h without it still link; +(b) sunjavaplugin.dll was NEVER BUILT — jvmfwk holds no JRE knowledge at all, +javavendors.xml maps every vendor to +vnd.sun.star.expand:$URE_INTERNAL_LIB_DIR/sunjavaplugin.dll which it +osl_loadModule()s. Now built (//main/jvmfwk, 8 TUs, 4 exports from +sunjavaplugin.map → util/sunjavaplugin.def — a PRIVATE plugin interface, NOT a +UNO component: no component_getFactory, nothing in services.rdb) and listed +explicitly in staging since NOTHING LINKS IT. javavendors.xml (from +javavendors_wnt.xml via copy_file) + jvmfwk3.ini staged into program/; the ini +NAME is fixed by fwkutil.hxx's SAL_CONFIGFILE("/jvmfwk3") relative to the +library's own dir, so it stays jvmfwk3 even though ours is jvmfwk.dll. +NOT staged: sunjavapluginrc — its one key is read via the DEFAULT rtl::Bootstrap, +not a plugin-private ini, so it looks inert in an office install. +NEXT: exercise it. Best first probe = cppuhelper/qa/propertysetmixin's 3 RED +testJava* cases, which need exactly this chain plus that suite's own Java +component jar (JavaSupplier.java + .uno.jar via javamaker). +See main/stoc/readme.md and main/jvmfwk/readme.md. NOTE: rules_java 8.11.0 IS now wired (MODULE.bazel) and the core Java UNO runtime is migrated & green — ridljar/jurt/jvmaccess/javaunohelper/jvmfwk/ bridges (incl. the java_uno JNI bridge: java_uno.dll + java_uno.jar, done diff --git a/MODULE.bazel b/MODULE.bazel index dce9a9da85..3aa2f888c7 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -52,12 +52,20 @@ bazel_dep(name = "mdds", version = "0.3.1") bazel_dep(name = "coinmp", version = "1.7.6") bazel_dep(name = "gtest", version = "1.7.0") # GoogleTest — C++ unit-test foundation (//main/test) -# Host JDK. Expose the machine's local JDK so we can register an ARCH-NEUTRAL java +# Host JDK. Expose a downloaded JDK so we can register an ARCH-NEUTRAL java # RUNTIME toolchain (see //build/toolchain/java). rules_java only auto-registers # cpu-pinned (windows + x86_64) runtime toolchains, which stop resolving the moment # the TARGET cpu isn't x86_64 — x86_32 today, arm64/riscv as the matrix grows. +# +# remotejdk21_win, NOT local_jdk: this runtime is what Bazel starts JavaBuilder on, +# and JavaBuilder passes --add-exports, which a JDK 8 JVM rejects outright +# ("Unrecognized option ... Could not create the Java Virtual Machine"). Binding +# the build to whatever JAVA_HOME happens to be is also the exact class of +# environment dependence this migration exists to remove. Java 8 BYTECODE is still +# guaranteed, by --java_language_version=8 in .bazelrc — a compiler flag, unrelated +# to the JVM the compiler itself runs on. java_toolchains = use_extension("@rules_java//java:extensions.bzl", "toolchains") -use_repo(java_toolchains, "local_jdk") +use_repo(java_toolchains, "remotejdk21_win") # --------------- build-time tools (not linked, not in registry) --------------- http_file = use_repo_rule("@bazel_tools//tools/build_defs/repo:http.bzl", "http_file") diff --git a/build/rules/java_pipeline.bzl b/build/rules/java_pipeline.bzl index 6ddbd3035e..0201090c69 100644 --- a/build/rules/java_pipeline.bzl +++ b/build/rules/java_pipeline.bzl @@ -207,3 +207,93 @@ no shell quoting or PATH issues arise. The output JAR is named <name>.jar and placed in the package output directory. """, ) + +# --------------------------------------------------------------------------- +# uno_jar +# --------------------------------------------------------------------------- +# Produce a jar under the EXACT filename the UNO runtime looks it up by. +# +# Three problems, one rule: +# +# (a) Bazel jar names are not runtime jar names. java_library(name = "x") +# emits libx.jar, but com/sun/star/lib/unoloader/UnoClassLoader.java +# hardcodes "java_uno.jar" / "juh.jar" / "jurt.jar" / "ridl.jar" and +# stoc/source/javavm/javavm.cxx hardcodes "unoloader.jar". These are +# exact-string lookups relative to $URE_INTERNAL_JAVA_DIR, so the staged +# basename is load-bearing. `out =` names the output directly rather +# than renaming at staging time, so the name lives next to the target +# that produces it. +# +# (b) Some runtime jars are the union of several Bazel targets. ridl.jar +# upstream holds the javamaker-generated UDK types AND the hand-written +# ridl sources; here those are two targets and java_library's `exports` +# is COMPILE-time only, so libridl.jar alone has no com.sun.star.uno.*. +# `jars =` takes any number of inputs and merges them. +# +# (c) The manifest main attributes are read at runtime. javaloader reads +# RegistrationClassName to register a Java component; UnoClassLoader +# .getClassLoader() reads UNO-Type-Path. Neither java_library nor +# jar_from_directory (a bare `jar cf`) writes a manifest. +# +# singlejar does all three, and is already part of the Bazel JDK tooling — +# no new external dependency. +# --------------------------------------------------------------------------- + +def _uno_jar_impl(ctx): + jar_out = ctx.actions.declare_file(ctx.attr.out) + + srcs = [] + for t in ctx.attr.jars: + srcs += [f for f in t.files.to_list() if f.extension == "jar"] + if not srcs: + fail("uno_jar '%s': jars = produced no .jar files" % ctx.label.name) + + args = ctx.actions.args() + args.add("--output", jar_out) + args.add("--sources") + args.add_all(srcs) + if ctx.attr.manifest_lines: + args.add("--deploy_manifest_lines") + args.add_all(ctx.attr.manifest_lines) + # Deterministic output: no timestamps, no build-data.properties stamp. + args.add("--normalize") + args.add("--exclude_build_data") + + ctx.actions.run( + executable = ctx.executable._singlejar, + arguments = [args], + inputs = depset(srcs), + outputs = [jar_out], + mnemonic = "UnoJar", + progress_message = "Assembling %s" % jar_out.basename, + ) + + return [DefaultInfo(files = depset([jar_out]))] + +uno_jar = rule( + implementation = _uno_jar_impl, + attrs = { + "out": attr.string( + mandatory = True, + doc = "Exact output filename, e.g. \"ridl.jar\" — the name the UNO runtime looks up.", + ), + "jars": attr.label_list( + allow_files = True, + mandatory = True, + doc = "Jars to merge, in order. A single entry simply restamps name/manifest.", + ), + "manifest_lines": attr.string_list( + doc = "Manifest MAIN-section attributes, e.g. [\"RegistrationClassName: com.sun.star.comp.Foo\"].", + ), + "_singlejar": attr.label( + default = "@bazel_tools//tools/jdk:singlejar", + executable = True, + cfg = "exec", + ), + }, + doc = """ +Assemble a runtime UNO jar: merge jars, stamp manifest main attributes, and +emit under an exact filename. See the block comment above for why each of +the three is needed. +""", +) diff --git a/build/toolchain/java/BUILD.bazel b/build/toolchain/java/BUILD.bazel index bfd627b4f3..a3ddefa208 100644 --- a/build/toolchain/java/BUILD.bazel +++ b/build/toolchain/java/BUILD.bazel @@ -17,8 +17,9 @@ # # The fix — and why it is arch-neutral # ------------------------------------ -# This runtime toolchain is only ever used as a HOST BUILD TOOL: jar.exe zipping -# arch-neutral .class files (see build/rules/java_pipeline.bzl jar_from_directory). +# This runtime toolchain is only ever used as a HOST BUILD TOOL: the JVM Bazel +# starts JavaBuilder on, jar.exe zipping arch-neutral .class files (see +# build/rules/java_pipeline.bzl jar_from_directory), and singlejar (uno_jar). # javamaker itself runs as a native exec-cfg cc_binary, NOT through this toolchain. # So a single host JDK serves EVERY target arch. We therefore register ONE runtime # toolchain with no cpu constraint; it matches any windows target platform, present @@ -26,13 +27,26 @@ # # It is registered in the ROOT module (MODULE.bazel), so it outranks rules_java's # cpu-pinned remote-JDK runtime toolchains and is selected for both x86 and x64. +# That priority is total — it has no target_settings, so --java_runtime_version and +# --tool_java_runtime_version cannot steer around it. Which makes WHICH jdk this +# points at load-bearing for every Java action in the build: +# +# NOT @local_jdk. JavaBuilder is launched with --add-exports, which a JDK 8 JVM +# rejects before main() ("Unrecognized option ... Could not create the Java +# Virtual Machine") — and this machine's JAVA_HOME is a 32-bit JDK 8, so every +# java_library in the tree failed to compile. Java 8 BYTECODE does not require a +# Java 8 javac: --java_language_version=8 in .bazelrc pins the class-file level. +# +# The 32-bit Java 8 JVM that the java_uno JNI bridge tests host is NOT affected: +# build/rules/java_run_test.bzl takes explicit jvm_path_dirs and never consults a +# java toolchain. toolchain( name = "host_jdk_runtime_toolchain", - # The machine's local JDK (JAVA_HOME), wired via the rules_java `toolchains` - # module extension in MODULE.bazel. It runs natively on the x64 build host - # regardless of the TARGET arch of the product being built. - toolchain = "@local_jdk//:jdk", + # Downloaded JDK 21, wired via the rules_java `toolchains` module extension in + # MODULE.bazel. Hermetic — independent of JAVA_HOME — and runs natively on the + # x64 build host regardless of the TARGET arch of the product being built. + toolchain = "@remotejdk21_win//:jdk", toolchain_type = "@bazel_tools//tools/jdk:runtime_toolchain_type", # No cpu constraint: arch-neutral by design (see header). os:windows only, # so it matches every windows target platform in the matrix. diff --git a/main/bridges/BUILD.bazel b/main/bridges/BUILD.bazel index 1c98408b21..fcb56343bb 100644 --- a/main/bridges/BUILD.bazel +++ b/main/bridges/BUILD.bazel @@ -4,7 +4,7 @@ load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library") load("@rules_java//java:defs.bzl", "java_library", "java_import") load("//build/rules:gtest_test.bzl", "gtest_test", "staged_run_test") load("//build/rules:idl_pipeline.bzl", "idl_library") -load("//build/rules:java_pipeline.bzl", "javamaker_classes", "jar_from_directory") +load("//build/rules:java_pipeline.bzl", "javamaker_classes", "jar_from_directory", "uno_jar") load("//build/rules:native_lib_dir.bzl", "native_lib_dir") load("//build/rules:java_run_test.bzl", "staged_java_test") @@ -287,6 +287,17 @@ java_library( visibility = ["//visibility:public"], ) +# java_uno.jar → program/classes/. First of the four names hardcoded in +# UnoClassLoader.createUrls(), so the basename is load-bearing (the comment +# above about the sealed manifest being "handled at staging" is now here: +# Sealed is omitted, for the same reason as jurt.jar — see //main/jurt). +uno_jar( + name = "java_uno_runtime_jar", + out = "java_uno.jar", + jars = [":java_uno_jar"], + visibility = ["//visibility:public"], +) + # ═══════════════════════════════════════════════════════════════════════════ # Tests # ═══════════════════════════════════════════════════════════════════════════ diff --git a/main/javaunohelper/BUILD.bazel b/main/javaunohelper/BUILD.bazel index 2281c9ec49..139e4dd5fb 100644 --- a/main/javaunohelper/BUILD.bazel +++ b/main/javaunohelper/BUILD.bazel @@ -1,4 +1,5 @@ load("@rules_cc//cc:defs.bzl", "cc_binary") +load("//build/rules:java_pipeline.bzl", "uno_jar") load("@rules_java//java:defs.bzl", "java_library") exports_files(["util/juh.component"]) @@ -33,6 +34,26 @@ java_library( visibility = ["//visibility:public"], ) +# ── juh.jar → program/classes/ ──────────────────────────────────────────────── +# Two exact-string lookups land on this file: UnoClassLoader.createUrls() names +# it "juh.jar", and services.rdb registers util/juh.component at +# vnd.sun.star.expand:$URE_INTERNAL_JAVA_DIR/juh.jar (see //main/postprocess). +# +# Because it IS a registered component, RegistrationClassName is load-bearing — +# javaloader reads it off the main attributes to find the class to register. +# Upstream's util/manifest also carries "UNO-Type-Path:" with a deliberately +# EMPTY value; that is omitted here because an absent UNO-Type-Path makes +# UnoClassLoader.getClassLoader() fall back to "<>", i.e. add juh.jar itself to +# the root loader — which createUrls() has already added, so the fallback is a +# harmless duplicate rather than a behaviour change. +uno_jar( + name = "juh_runtime_jar", + out = "juh.jar", + jars = [":juh_jar"], + manifest_lines = ["RegistrationClassName: com.sun.star.comp.JavaUNOHelperServices"], + visibility = ["//visibility:public"], +) + # ── juh.dll (preload) ────────────────────────────────────────────────────────── # Thin DLL loaded by Java via System.loadLibrary("juh"). On first JNI call it # lazily loads juhx.dll via osl_loadModuleRelative and forwards all 4 JNI diff --git a/main/jurt/BUILD.bazel b/main/jurt/BUILD.bazel index ef1af69bc2..74b15e20f1 100644 --- a/main/jurt/BUILD.bazel +++ b/main/jurt/BUILD.bazel @@ -1,3 +1,4 @@ +load("//build/rules:java_pipeline.bzl", "uno_jar") load("@rules_cc//cc:defs.bzl", "cc_binary") load("@rules_java//java:defs.bzl", "java_library") @@ -29,6 +30,23 @@ java_library( visibility = ["//visibility:public"], ) +# ── jurt.jar → program/classes/ ─────────────────────────────────────────────── +# UnoClassLoader.createUrls() looks this up as the bare string "jurt.jar". +# +# DIVERGENCE (deliberate): upstream's util/manifest seals the jar but un-seals +# com/sun/star/lib/util/ and com/sun/star/uno/, because those two packages are +# split across jurt.jar and ridl.jar. A per-package manifest section cannot be +# expressed through singlejar's --deploy_manifest_lines (main section only), and +# a blanket "Sealed: true" here would be STRICTER than upstream and throw +# SecurityException on exactly that split. Omitting Sealed is the safe +# direction — sealing only ever restricts, never enables. +uno_jar( + name = "jurt_jar", + out = "jurt.jar", + jars = [":jurt"], + visibility = ["//visibility:public"], +) + # ── jpipx.dll ───────────────────────────────────────────────────────────────── # Real JNI pipe implementation. On Windows, functions are exported with # truncated names (PipeConnection_create etc., __cdecl) so wrapper.c can call diff --git a/main/jvmfwk/BUILD.bazel b/main/jvmfwk/BUILD.bazel index 7a3f29ba91..5ff52ae393 100644 --- a/main/jvmfwk/BUILD.bazel +++ b/main/jvmfwk/BUILD.bazel @@ -1,5 +1,6 @@ package(default_visibility = ["//visibility:public"]) +load("//build/rules:copy_file.bzl", "copy_file") load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library") _COPTS = [ @@ -20,16 +21,35 @@ _DEFINES = [ "FULL_DESK", "SHAREDLIB", "_DLL_", "JVMFWK_DLLIMPLEMENTATION", + # SOLAR_JAVA is not a feature flag here, it is the difference between a + # working library and a stub. jvmfwk/source/framework.cxx compiles + # jfw_startVM() to a bare `return JFW_E_ERROR;` under #ifndef SOLAR_JAVA + # (and jvmfwk/framework.h then declares JavaVM/JNIEnv/JavaVMOption as + # incomplete types instead of including <jni.h>). Built without it, every + # caller — stoc's javavm.uno, cui's Java options page, javaldx — gets a + # generic error no matter how correct the JRE, javavendors.xml and + # jvmfwk3.ini are. Upstream drives this from configure; here it is + # unconditional, as rules_java is a hard dependency (same reasoning as + # //main/stoc's javaloader/javavm). + # + # The exported ABI does not change: the affected types appear only behind + # pointers, so consumers that include jvmfwk/framework.h WITHOUT SOLAR_JAVA + # (desktop, cui, svl…) still link and call correctly. + "SOLAR_JAVA", ] _DEPS = [ "//main/sal:sal_headers", + # salhelper/simplereferenceobject.hxx — VendorBase's base class. + "//main/salhelper:salhelper_headers", "//main/cppu:cppu_headers", "//main/cppuhelper:cppuhelper_headers", "//main/udkapi:udkapi_idl_headers", "//main/stlport:stlport", "@boost.legacy//:boost.legacy", "@libxml2//:libxml2", + # <jni.h>, reached through jvmfwk/framework.h once SOLAR_JAVA is on. + "@rules_java//toolchains:jni", ] # ── jvmfwk headers (for downstream consumers) ──────────────────── @@ -81,6 +101,65 @@ cc_binary( visibility = ["//visibility:public"], ) +# ── sunjavaplugin.dll ──────────────────────────────────────────── +# The vendor plugin: given a candidate JRE it reports vendor/version/runtime +# lib, and it is what actually calls JNI_CreateJavaVM. jvmfwk itself contains +# no JRE knowledge — javavendors.xml maps every supported vendor to +# "vnd.sun.star.expand:$URE_INTERNAL_LIB_DIR/sunjavaplugin.dll", which jvmfwk +# osl_loadModule()s. Without it jfw_findAllJREs finds nothing and the office +# reports no Java however many JDKs are installed. +# +# The four exports come from plugins/sunmajor/pluginlib/sunjavaplugin.map, +# converted to util/sunjavaplugin.def (the standard .map → .def conversion; note +# these are NOT the UNO component entry points — this is a private jvmfwk plugin +# interface, not a registered UNO component, so there is no +# component_getFactory here and it does not belong in services.rdb). +cc_binary( + name = "sunjavaplugin", + srcs = [ + "plugins/sunmajor/pluginlib/gnujre.cxx", + "plugins/sunmajor/pluginlib/gnujre.hxx", + "plugins/sunmajor/pluginlib/otherjre.cxx", + "plugins/sunmajor/pluginlib/otherjre.hxx", + "plugins/sunmajor/pluginlib/sunjavaplugin.cxx", + "plugins/sunmajor/pluginlib/sunjre.cxx", + "plugins/sunmajor/pluginlib/sunjre.hxx", + "plugins/sunmajor/pluginlib/sunversion.cxx", + "plugins/sunmajor/pluginlib/sunversion.hxx", + "plugins/sunmajor/pluginlib/util.cxx", + "plugins/sunmajor/pluginlib/util.hxx", + "plugins/sunmajor/pluginlib/vendorbase.cxx", + "plugins/sunmajor/pluginlib/vendorbase.hxx", + "plugins/sunmajor/pluginlib/vendorlist.cxx", + "plugins/sunmajor/pluginlib/vendorlist.hxx", + "plugins/sunmajor/pluginlib/diagnostics.h", + # pch, included by every .cxx as "precompiled_plugin.hxx" + "inc/pch/precompiled_plugin.hxx", + ], + copts = _COPTS, + defines = _DEFINES, + deps = _DEPS, + additional_linker_inputs = [ + "//main/sal:sal_implib", + "//main/salhelper:salhelper_implib", + "//main/cppu:cppu3_implib", + "//main/cppuhelper:cppuhelper_implib", + ], + linkshared = True, + win_def_file = "util/sunjavaplugin.def", + linkopts = [ + "$(execpath //main/sal:sal_implib)", + "$(execpath //main/salhelper:salhelper_implib)", + "$(execpath //main/cppu:cppu3_implib)", + "$(execpath //main/cppuhelper:cppuhelper_implib)", + # util.cxx reads HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft to enumerate + # installed JREs. + "advapi32.lib", + "/MANIFEST:NO", + ], + visibility = ["//visibility:public"], +) + # ── import library for downstream DLLs ─────────────────────────── filegroup( name = "jvmfwk_implib", @@ -91,3 +170,19 @@ filegroup( # Alias so existing references still resolve during transition. alias(name = "jvmfwk3_implib", actual = ":jvmfwk_implib", visibility = ["//visibility:public"]) + +# ── javavendors.xml → program/ ─────────────────────────────────────────────── +# The JRE-selection policy: which vendors are acceptable, at which minimum +# version, and which plugin library can inspect a candidate JRE for each. +# jvmfwk finds it through UNO_JAVA_JFW_VENDOR_SETTINGS in program/jvmfwk3.ini; +# VendorSettings' constructor throws JFW_E_CONFIGURATION if it is absent, so no +# JVM is located at all without this file. +# +# Renamed, not just staged: upstream picks one of the per-platform variants +# (scp2 ure.scp gid_File_Misc_JavavendorsXml installs it under the bare name) +# and the bootstrap value names javavendors.xml exactly. +copy_file( + name = "javavendors_xml", + src = "distributions/OpenOfficeorg/javavendors_wnt.xml", + out = "javavendors.xml", +) diff --git a/main/jvmfwk/readme.md b/main/jvmfwk/readme.md index cce108c4b7..4baf6c284c 100644 --- a/main/jvmfwk/readme.md +++ b/main/jvmfwk/readme.md @@ -1,10 +1,88 @@ +<!-- + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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. +--> + # Notes for jvmfwk (done) -- Single DLL: `jvmfwk.dll` -- Deps: cppu, cppuhelper, sal, udkapi_idl_headers, stlport, boost.legacy, @libxml2//:libxml2, advapi32 +- Two DLLs: `jvmfwk.dll` and `sunjavaplugin.dll` +- Deps: cppu, cppuhelper, sal, salhelper, udkapi_idl_headers, stlport, boost.legacy, @libxml2//:libxml2, @rules_java//toolchains:jni, advapi32 - Exports: 21 C-linkage `jfw_*` functions from `inc/jvmfwk/` via `jvmfwk.def` +## `SOLAR_JAVA` is the difference between a library and a stub + +`jvmfwk.dll` was originally built without it. That is not a feature switch: +`source/framework.cxx` compiles `jfw_startVM()` to a bare +`return JFW_E_ERROR;` under `#ifndef SOLAR_JAVA`, and `inc/jvmfwk/framework.h` +then declares `JavaVM` / `JNIEnv` / `JavaVMOption` as incomplete types instead of +including `<jni.h>`. Built that way, no configuration can make Java work — +stoc's `javavm.uno`, cui's Java options page and `javaldx` all get a generic +error however correct the JRE, `javavendors.xml` and `jvmfwk3.ini` are. + +Upstream drives it from `configure`; here it is unconditional, since `rules_java` +is a hard dependency. It brings in `@rules_java//toolchains:jni` for the real +`<jni.h>`. The exported ABI is unaffected — those types appear only behind +pointers, so consumers that include `jvmfwk/framework.h` *without* `SOLAR_JAVA` +(desktop, cui, svl, svtools) still compile and link against the same entry +points. + +Same landmine, third occurrence: see `//main/bridges` (java_uno JNI bridge) and +`//main/stoc` (javaloader/javavm). + +## `sunjavaplugin.dll` — the half that knows about JREs + +`jvmfwk` itself contains no JRE knowledge at all. `javavendors.xml` maps each +supported vendor to +`vnd.sun.star.expand:$URE_INTERNAL_LIB_DIR/sunjavaplugin.dll`, which jvmfwk +`osl_loadModule()`s; the plugin enumerates candidate JREs (registry +`HKLM\SOFTWARE\JavaSoft` — hence `advapi32`), reports vendor/version/runtime lib, +and is what actually calls `JNI_CreateJavaVM`. + +- Sources: the 8 TUs of `plugins/sunmajor/pluginlib` (pch + `precompiled_plugin.hxx`, not `precompiled_jvmfwk.hxx`). +- Exports: 4 functions converted from `plugins/sunmajor/pluginlib/sunjavaplugin.map` + to `util/sunjavaplugin.def`. These are a **private jvmfwk plugin interface, not + UNO component entry points** — no `component_getFactory`, and nothing in + `services.rdb`. +- **Nothing links it**, so it must be listed explicitly in + `//main/staging` (same class of runtime-only dependency as the C++/UNO bridge + DLL). Missing, the office reports no Java installed however many JDKs are present. +- No `/Zc:wchar_t-`: unlike `javavm`, this code never hands `sal_Unicode*` to a + JNI string call. + +## Runtime configuration staged into `program/` + +- `javavendors.xml` — from `distributions/OpenOfficeorg/javavendors_wnt.xml`, via + `copy_file` because the bootstrap value names the bare filename. +- `jvmfwk3.ini` — in `main/staging/`. The name is fixed by + `source/fwkutil.hxx`, which hardcodes `SAL_CONFIGFILE("/jvmfwk3")` relative to + its own library directory, so it stays `jvmfwk3` even though the library here + is `jvmfwk.dll` rather than upstream's `jvmfwk3.dll`. + +Not staged: `plugins/sunmajor/pluginlib/sunjavapluginrc`. Its one key +(`JFW_PLUGIN_DO_NOT_CHECK_ACCESSIBILITY`) is read through the *default* +`rtl::Bootstrap`, not a plugin-private ini, so the file looks inert in an office +install; without it jvmfwk falls back to probing +`HKCU\Software\OpenOffice\Accessibility\AtToolSupport`. + +See `main/stoc/readme.md` for the full Java2-loader bootstrap chain. + ## libxml2 overlay fixes for VS2008 + - `config.h` overlay: win32/VC10/config.h as base, HAVE_STDINT_H guarded to VS2010+, SEND_ARG2_CAST defined empty - `libxml2-configure.patch` disables FTP, HTTP, iconv, debug, zlib, lzma in xmlversion.h - nanoftp.c and nanohttp.c excluded from LIBXML2_SRCS (networking not needed) diff --git a/main/jvmfwk/util/sunjavaplugin.def b/main/jvmfwk/util/sunjavaplugin.def new file mode 100644 index 0000000000..aafc97c4ba --- /dev/null +++ b/main/jvmfwk/util/sunjavaplugin.def @@ -0,0 +1,6 @@ +LIBRARY sunjavaplugin +EXPORTS + jfw_plugin_getAllJavaInfos + jfw_plugin_getJavaInfoByPath + jfw_plugin_startJavaVirtualMachine + jfw_plugin_existJRE diff --git a/main/ridljar/BUILD.bazel b/main/ridljar/BUILD.bazel index ead3ce93fd..ccc89cac57 100644 --- a/main/ridljar/BUILD.bazel +++ b/main/ridljar/BUILD.bazel @@ -1,4 +1,4 @@ -load("//build/rules:java_pipeline.bzl", "jar_from_directory", "javamaker_classes") +load("//build/rules:java_pipeline.bzl", "jar_from_directory", "javamaker_classes", "uno_jar") load("@rules_java//java:defs.bzl", "java_import", "java_library") # ── Step 1: Generate Java .class files from udkapi.rdb ─────────────────── @@ -46,3 +46,28 @@ java_library( srcs = glob(["java/unoloader/src/main/java/**/*.java"]), visibility = ["//visibility:public"], ) + +# ── Runtime jars → program/classes/ ────────────────────────────────────── +# Staged by //main/staging:install. The names are exact-string lookups, not +# conventions: javavm.cxx::setUpUnoVirtualMachine builds a URLClassLoader over +# "$URE_INTERNAL_JAVA_DIR/unoloader.jar" to reach UnoClassLoader, whose +# createUrls() then hardcodes java_uno.jar / juh.jar / jurt.jar / ridl.jar +# relative to the same base. See main/stoc/readme.md. + +uno_jar( + name = "unoloader_jar", + out = "unoloader.jar", + jars = [":unoloader"], + visibility = ["//visibility:public"], +) + +# ridl.jar is a MERGE, not a rename. Upstream's single jar holds both the +# javamaker-generated UDK types and the 27 hand-written sources; here they are +# two targets and `exports` above only wires them together at COMPILE time, so +# libridl.jar on its own contains no com.sun.star.uno.*. +uno_jar( + name = "ridl_jar", + out = "ridl.jar", + jars = [":ridl", ":udkapi_java_jar"], + visibility = ["//visibility:public"], +) diff --git a/main/staging/BUILD.bazel b/main/staging/BUILD.bazel index 02fb4fa870..949733cc3a 100644 --- a/main/staging/BUILD.bazel +++ b/main/staging/BUILD.bazel @@ -11,6 +11,9 @@ filegroup( "uno.ini", # PYUNO_LOADER_PYTHONHOME / PYTHONPATH for pythonloader.dll. "pythonloader.uno.ini", + # Vendor settings + JRE-selection data paths for jvmfwk.dll. Name fixed + # by fwkutil.hxx, not by us — see the file's own header. + "jvmfwk3.ini", ], ) @@ -66,6 +69,12 @@ collect_outputs( # ── JVM framework (required by cui, svl, svtools, desktop at load time) ─ "//main/jvmfwk:jvmfwk", + # Vendor plugin — the half that actually knows how to find and start a + # JRE. jvmfwk osl_loadModule()s it by the path in javavendors.xml + # ("vnd.sun.star.expand:$URE_INTERNAL_LIB_DIR/sunjavaplugin.dll"), so + # nothing links it and it must be listed here explicitly. Missing, the + # office reports no Java installed however many JDKs are present. + "//main/jvmfwk:sunjavaplugin", # ── UNO runtime infrastructure ──────────────────────────────────────── "//main/stoc:bootstrap.uno", @@ -620,6 +629,49 @@ tree_install( dst = "share/config", ) +# ── program/classes/ — Java UNO runtime jars ($URE_INTERNAL_JAVA_DIR) ──────── +# com.sun.star.loader.Java2 bootstraps from here. javavm.cxx builds a +# URLClassLoader over "$URE_INTERNAL_JAVA_DIR/unoloader.jar" to reach +# com.sun.star.lib.unoloader.UnoClassLoader, whose createUrls() then hardcodes +# java_uno.jar / juh.jar / jurt.jar / ridl.jar relative to the same base. All +# five are exact-string lookups, which is why the uno_jar rule fixes each output +# filename at the producing target rather than renaming during staging. +# +# unoil.jar is the exception: it is NOT one of the hardcoded names and arrives +# through the second channel — $URE_INTERNAL_JAVA_CLASSPATH → URE_MORE_JAVA_TYPES +# in program/fundamental.ini, passed to UnoClassLoader as its classPath argument. +# +# flatten: the six jars come from five different packages, so no single +# strip_prefix applies. +filegroup( + name = "java_runtime_jars", + srcs = [ + "//main/ridljar:unoloader_jar", + "//main/ridljar:ridl_jar", + "//main/jurt:jurt_jar", + "//main/javaunohelper:juh_runtime_jar", + "//main/bridges:java_uno_runtime_jar", + "//main/unoil:unoil_jar", + ], +) + +tree_install( + name = "_install_classes", + src = ":java_runtime_jars", + flatten = True, + dst = "program/classes", +) + +# ── program/javavendors.xml — jvmfwk JRE-selection policy ──────────────────── +# Not staged by _install_program: flat_install filters on _INSTALL_EXTS, and +# adding "xml" there would sweep in every .xml in the transitive closure. +tree_install( + name = "_install_java_config", + src = "//main/jvmfwk:javavendors_xml", + flatten = True, + dst = "program", +) + # ── share/config/soffice.cfg — per-module UI configuration tree ─────────────── # The framework PresetHandler opens share/config/soffice.cfg as a *folder* and # reads modules/<ModuleShortName>/{menubar,toolbar,statusbar}/*.xml at startup @@ -657,6 +709,8 @@ filegroup( ":_install_images_zip", ":_install_images_industrial_zip", ":_install_uiconfig", + ":_install_classes", + ":_install_java_config", ":_install_python_scripts", # The Python stdlib tree is NOT optional: pythonloader.dll calls # Py_Initialize(), and Python 2.7 exit()s the process if it cannot find diff --git a/main/staging/collect_files_aspect.bzl b/main/staging/collect_files_aspect.bzl index 0164b5bb9a..23f222c10b 100644 --- a/main/staging/collect_files_aspect.bzl +++ b/main/staging/collect_files_aspect.bzl @@ -173,7 +173,11 @@ flat_install = rule( # strip_prefix: path component within the repo to strip. # For "@python//:python_lib" (glob(["Lib/**"])), set strip_prefix = "Lib" # so "external/python+/Lib/abc.py" → "abc.py" → installed at dst/abc.py. -# Set "" to strip nothing (files land at dst/<basename>). +# Set "" to strip nothing (files keep their full in-repo path under dst). +# +# flatten: discard the tree entirely — every file lands at dst/<basename>. +# Use when the sources come from several packages, so no single strip_prefix +# applies, and the tree carries no meaning at the destination. # # dst: destination subdirectory inside the rule's output dir. # e.g. "python-core-2.7.18/lib" @@ -213,7 +217,15 @@ def _tree_install_impl(ctx): in_repo = f.short_path # Strip the known prefix (e.g. "Lib/") to get the bare relative path. - if strip: + if ctx.attr.flatten: + # No tree to preserve: every file lands directly in dst under its own + # basename. For inputs that come from SEVERAL packages there is no + # single strip_prefix that works (each would need its own), and the + # destination name is already fixed by the producing rule — as for the + # Java UNO runtime jars, whose basenames are exact-string lookups in + # javavm.cxx / UnoClassLoader (see build/rules/java_pipeline.bzl). + rel = f.basename + elif strip: marker = strip + "/" idx = in_repo.find(marker) if idx >= 0: @@ -265,6 +277,7 @@ tree_install = rule( attrs = { "src": attr.label(allow_files = True), "strip_prefix": attr.string(default = "", doc = "In-repo path prefix to strip (e.g. 'Lib')"), + "flatten": attr.bool(default = False, doc = "Ignore strip_prefix and install every file flat into dst under its basename"), "dst": attr.string(mandatory = True, doc = "Destination subdir in output (e.g. 'python-core-2.7.18/lib')"), }, ) diff --git a/main/staging/fundamental.ini b/main/staging/fundamental.ini index 95e80d6f43..cb077861eb 100644 --- a/main/staging/fundamental.ini +++ b/main/staging/fundamental.ini @@ -10,6 +10,32 @@ UNO_SERVICES=${ORIGIN}/services.rdb URE_MORE_TYPES=${ORIGIN}/oovbaapi.rdb URE_MORE_SERVICES= URE_MORE_JAVA_CLASSPATH_URLS= +# ── Java (com.sun.star.loader.Java2) ───────────────────────────────────────── +# These MUST live here and not only in uno.ini. stoc/source/javavm/javavm.cxx +# resolves them through com.sun.star.util.theMacroExpander, which expands against +# the URE_BOOTSTRAP file — i.e. THIS one. Same trap as the extension path macros +# below. getJavaVM() raises a RuntimeException if URE_INTERNAL_JAVA_DIR is not +# expandable; URE_INTERNAL_JAVA_CLASSPATH is explicitly allowed not to be. +# +# URE_INTERNAL_JAVA_DIR is the base for the FIVE jars looked up by hardcoded +# name: javavm.cxx itself opens unoloader.jar, and the UnoClassLoader it then +# instantiates appends java_uno.jar / juh.jar / jurt.jar / ridl.jar in +# createUrls(). Staged by //main/staging:_install_classes. +URE_INTERNAL_JAVA_DIR=${ORIGIN}/classes +# unoil.jar (the office API types) is NOT one of those five. It reaches the +# class loader only as the classPath argument, i.e. via this variable — which is +# why leaving it empty silently costs every com.sun.star.* API type. Upstream +# also lists ScriptFramework.jar plus each extension's UNO_JAVA_CLASSPATH here +# (scp2 profileitem_ooo.scp gid_Profileitem_Fundamentalbasis_Ure_More_Java_Types); +# neither is built yet. +URE_MORE_JAVA_TYPES=${ORIGIN}/classes/unoil.jar +URE_INTERNAL_JAVA_CLASSPATH=${URE_MORE_JAVA_TYPES} +# Where jvmfwk records which JRE was selected. Read by program/jvmfwk3.ini, +# which is the bootstrap file jvmfwk loads from its OWN directory +# (fwkutil.hxx hardcodes SAL_CONFIGFILE("/jvmfwk3"), so the name stays jvmfwk3 +# even though the library is jvmfwk.dll here). +URE_OVERRIDE_JAVA_JFW_SHARED_DATA=${OOO_BASE_DIR}/share/config/javasettings_${_OS}_${_ARCH}.xml +URE_OVERRIDE_JAVA_JFW_USER_DATA=${${OOO_BASE_DIR}/program/bootstrap.ini:UserInstallation}/user/config/javasettings_${_OS}_${_ARCH}.xml # Extension / deployment path macros. These are also defined in uno.ini, but # the UNO component-context bootstrap (which reads unorc/uno.ini) is NOT the # context that rtl::Bootstrap::expandMacros() resolves against -- that uses the diff --git a/main/staging/jvmfwk3.ini b/main/staging/jvmfwk3.ini new file mode 100644 index 0000000000..b532302952 --- /dev/null +++ b/main/staging/jvmfwk3.ini @@ -0,0 +1,20 @@ +[Bootstrap] +# jvmfwk's own bootstrap file. The NAME is not ours to choose: jvmfwk loads it +# from the directory of its own library via SAL_CONFIGFILE("/jvmfwk3") hardcoded +# in jvmfwk/source/fwkutil.hxx, so it stays jvmfwk3.ini even though the library +# is built as jvmfwk.dll here (see //main/jvmfwk). +# +# ${ORIGIN} is program/, where //main/staging:_install_java_config puts +# javavendors.xml. Without a readable vendor settings file, VendorSettings' +# constructor throws JFW_E_CONFIGURATION and no JVM is ever located. +UNO_JAVA_JFW_VENDOR_SETTINGS=${ORIGIN}/javavendors.xml +# The URE_OVERRIDE_* macros are defined in fundamental.ini, not here; a plain +# ${NAME} falls back to the base bootstrap ini, which is how upstream's URE +# jvmfwk3rc reaches the same values in fundamentalbasisrc. Each is a +# space-separated list and jvmfwk takes the FIRST URL (getParamFirstUrl), so the +# per-user path is a fallback for when the install dir is not writable. +UNO_JAVA_JFW_SHARED_DATA=${URE_OVERRIDE_JAVA_JFW_SHARED_DATA} ${SYSUSERCONFIG}/URE/javasettings_${_OS}_${_ARCH}.xml +UNO_JAVA_JFW_USER_DATA=${URE_OVERRIDE_JAVA_JFW_USER_DATA} ${SYSUSERCONFIG}/URE/javasettings_${_OS}_${_ARCH}.xml +# Third-party jars added to the JVM classpath (bsh/hsqldb/saxon when built) — +# NOT the office API types, which travel via URE_MORE_JAVA_TYPES instead. +UNO_JAVA_JFW_CLASSPATH_URLS=${URE_MORE_JAVA_CLASSPATH_URLS} diff --git a/main/staging/uno.ini b/main/staging/uno.ini index cf0d67052d..06157bbf22 100644 --- a/main/staging/uno.ini +++ b/main/staging/uno.ini @@ -14,6 +14,13 @@ UNO_SHARED_PACKAGES=$OOO_BASE_DIR/share/uno_packages UNO_SHARED_PACKAGES_CACHE=$UNO_SHARED_PACKAGES/cache UNO_USER_PACKAGES=${$OOO_BASE_DIR/program/bootstrap.ini:UserInstallation}/user/uno_packages UNO_USER_PACKAGES_CACHE=$UNO_USER_PACKAGES/cache -URE_INTERNAL_JAVA_CLASSPATH=${URE_MORE_JAVA_CLASSPATH_URLS} +# Upstream (scp2 profileitem_ooo.scp) chains this to URE_MORE_JAVA_TYPES — the +# office API/extension jars — not to URE_MORE_JAVA_CLASSPATH_URLS, which is the +# separate bsh/hsqldb/saxon list. Cross-referenced into fundamental.ini the same +# way fundamental.ini cross-references this file, since a bare ${...} here would +# resolve only against uno.ini itself. Note the value that actually reaches +# javavm.cxx is fundamental.ini's (theMacroExpander uses URE_BOOTSTRAP); this +# line exists so the two files do not disagree. +URE_INTERNAL_JAVA_CLASSPATH=${${ORIGIN}/fundamental.ini:URE_MORE_JAVA_TYPES} URE_INTERNAL_JAVA_DIR=${ORIGIN}/classes URE_INTERNAL_LIB_DIR=${ORIGIN} diff --git a/main/stoc/readme.md b/main/stoc/readme.md index ff6dfce734..2a44881410 100644 --- a/main/stoc/readme.md +++ b/main/stoc/readme.md @@ -80,69 +80,115 @@ that is a distinct type from `jchar` (`unsigned short`) — so every such call i `wchar_t == unsigned short == jchar`. `javaloader` escapes this because it only ever hands JNI plain UTF-8 (`NewStringUTF`). -### NOT yet sufficient to run a Java component - -Having the DLLs is necessary but not enough. The remaining chain, in the order -`javavm.cxx::getJavaVM` walks it: - -1. **`URE_INTERNAL_JAVA_DIR` must be expandable** by - `com.sun.star.util.theMacroExpander`, which resolves against the - `URE_BOOTSTRAP` file — i.e. `program/fundamental.ini`, **not** `uno.ini`. - `main/staging/uno.ini` already sets `URE_INTERNAL_JAVA_DIR=${ORIGIN}/classes` - and `URE_INTERNAL_JAVA_CLASSPATH=${URE_MORE_JAVA_CLASSPATH_URLS}`, but - `fundamental.ini` does not — and that is the one that counts. Same trap the - extension-path macros already hit; see the long comment in - `main/staging/fundamental.ini`. `URE_INTERNAL_JAVA_CLASSPATH` is allowed to - be unexpandable, `URE_INTERNAL_JAVA_DIR` is not. -2. **`program/classes/` staging does not exist.** `getJavaVM` bootstraps from - `$URE_INTERNAL_JAVA_DIR/unoloader.jar` → `URLClassLoader` → - `com.sun.star.lib.unoloader.UnoClassLoader`, which then builds the real - classpath. Every jar target already exists, but this is **not** just a new - `classes/` destination in `stage_install.bzl` — three things bite: - - **(a) Bazel jar names are not runtime jar names.** `java_library(name = "x")` - emits `libx.jar`, and the runtime looks up bare `unoloader.jar` etc. by exact - name (`javavm.cxx` passes the literal string). So every entry needs an - explicit destination name — `stage_install`'s manifest already supports a - third `<destname>` column, as the `.rdb` entries use: - - | target | Bazel output | must stage as | - | --- | --- | --- | - | `//main/ridljar:unoloader` | `libunoloader.jar` | `unoloader.jar` | - | `//main/ridljar:ridl` | `libridl.jar` | `ridl.jar` — **but see (b)** | - | `//main/jurt:jurt` | `libjurt.jar` | `jurt.jar` | - | `//main/javaunohelper:juh_jar` | `libjuh_jar.jar` | `juh.jar` | - | `//main/unoil:offapi_java_jar` | `offapi_java_jar.jar` | `unoil.jar` | - | `//main/bridges:java_uno_jar` | `libjava_uno_jar.jar` | `java_uno.jar` | - - **(b) `ridl.jar` has to be MERGED, not copied.** Upstream's `ridl.jar` - contains the generated UDK types *and* the 27 hand-written sources; here - those are two targets, and `java_library(name = "ridl", exports = - [":udkapi_java"])` exports the generated types only at COMPILE time — the - jar itself holds just the hand-written classes. Staging `libridl.jar` - as-is would ship a `ridl.jar` with no `com.sun.star.uno.*` in it. Fix is the - same `java_binary(create_executable = False)` deploy-jar trick used for - `//main/cppuhelper:cppuhelper_qa_propertysetmixin`. - - **(c) The jars need MANIFESTS, which nothing currently produces.** Upstream - keeps them as checked-in files and `jar cfm`s them in: - - `main/ridljar/java/unoloader/.../manifest` — `Sealed: true` - - `main/jurt/util/manifest` — `Sealed: true`, un-sealing - `com/sun/star/lib/util/` and `com/sun/star/uno/` - - `main/javaunohelper/util/manifest` — `Sealed: true`, - `RegistrationClassName: com.sun.star.comp.JavaUNOHelperServices`, - `UNO-Type-Path:` with a deliberately empty value - - `UnoClassLoader.getClassLoader()` reads `UNO-Type-Path` off the jar's main - attributes, so these are load-bearing, not cosmetic. Neither - `jar_from_directory` nor `java_library` currently sets a manifest — - `jar_from_directory` runs a bare `jar cf`. **Open question for the next - session:** exactly how `jurt.jar` / `ridl.jar` / `unoil.jar` reach the class - loader, given `URE_INTERNAL_JAVA_CLASSPATH` expands empty and juh's - `UNO-Type-Path` is empty — read `UnoClassLoader.getClassLoader()` and - `javavm.cxx`'s `UnoClassLoader` construction before wiring anything. -3. **`jvmfwk` needs a JRE to find.** `javavendors.xml` plus the `jvmfwk3` - config decide which JVM is selected; without them `jvmfwk` reports no JRE - and `javavm` throws. Not yet staged. - -So the next Java-bucket step is **staging, not compilation**. +### The runtime chain — now wired + +Having the DLLs is necessary but not enough. The rest of the chain, in the order +`javavm.cxx::getJavaVM` walks it, is now in place: + +#### 1. Bootstrap variables — `program/fundamental.ini` + +`URE_INTERNAL_JAVA_DIR` must be expandable by +`com.sun.star.util.theMacroExpander`, which resolves against the `URE_BOOTSTRAP` +file — i.e. `program/fundamental.ini`, **not** `uno.ini`. `uno.ini` had the +variables and `fundamental.ini` did not, which is the one that counts (same trap +the extension-path macros already hit). Added there, with +`URE_MORE_JAVA_TYPES` and the two `URE_OVERRIDE_JAVA_JFW_*_DATA` paths. +`URE_INTERNAL_JAVA_CLASSPATH` is explicitly allowed to be unexpandable; +`URE_INTERNAL_JAVA_DIR` is not. + +#### 2. `program/classes/` — how the jars actually reach the class loader + +This was the open question. The answer is that there are **two** channels, and +conflating them is what made `URE_INTERNAL_JAVA_CLASSPATH` look optional: + +- **Hardcoded names.** `javavm.cxx` builds a plain `URLClassLoader` over the + single literal `"$URE_INTERNAL_JAVA_DIR/unoloader.jar"`, loads + `com.sun.star.lib.unoloader.UnoClassLoader` from it, and instantiates that with + `base` = the same directory. `UnoClassLoader.createUrls()` then appends four + more literals — `java_uno.jar`, `juh.jar`, `jurt.jar`, `ridl.jar`. **No + manifest, no classpath variable and no `UNO-Type-Path` is involved** for these + five; the filename *is* the lookup key. +- **The classPath argument.** `unoil.jar` is *not* one of the five. It reaches + the loader only as the `classPath` constructor argument, which is + `$URE_INTERNAL_JAVA_CLASSPATH` → `URE_MORE_JAVA_TYPES` (upstream: + `$ORIGIN/classes/unoil.jar $ORIGIN/classes/ScriptFramework.jar` + each + extension's `UNO_JAVA_CLASSPATH`, from `scp2` `profileitem_ooo.scp`). So an + empty `URE_INTERNAL_JAVA_CLASSPATH` does not merely lose extensions — it loses + every `com.sun.star.*` office API type. + +`UNO-Type-Path` matters at a different point entirely: +`UnoClassLoader.getClassLoader()` reads it when *javaloader* opens a **component** +jar, to decide which extra type jars to hoist into the root loader. juh's +deliberately empty value suppresses that; an absent one falls back to `"<>"`, +which re-adds the jar itself — already present via `createUrls()`, so a harmless +duplicate. That is why the juh manifest is reproduced only for +`RegistrationClassName` (load-bearing: `juh.jar` is a registered component in +`services.rdb`) and not for the rest. + +The three build-side problems are solved by one new rule, +`uno_jar` in `build/rules/java_pipeline.bzl` (singlejar): + +| target | produces | note | +| --- | --- | --- | +| `//main/ridljar:unoloader_jar` | `unoloader.jar` | | +| `//main/ridljar:ridl_jar` | `ridl.jar` | **merge** of `:ridl` + `:udkapi_java_jar` | +| `//main/jurt:jurt_jar` | `jurt.jar` | | +| `//main/javaunohelper:juh_runtime_jar` | `juh.jar` | + `RegistrationClassName` | +| `//main/bridges:java_uno_runtime_jar` | `java_uno.jar` | | +| `//main/unoil:unoil_jar` | `unoil.jar` | reaches the loader via the classPath channel | + +- **Exact names**: `out =` names the output at the producing target, so the name + lives next to the code that justifies it instead of in a staging rename table. +- **`ridl.jar` is a MERGE, not a rename.** Upstream's single jar holds the + javamaker-generated UDK types *and* the 27 hand-written sources; here they are + two targets and `java_library(exports = [":udkapi_java"])` wires them together + only at COMPILE time, so `libridl.jar` alone has no `com.sun.star.uno.*`. + (Verified: the merged jar has 436 classes including `XInterface`, `TypeClass` + and `UnoRuntime`.) +- **Manifests**: neither `java_library` nor `jar_from_directory` (a bare + `jar cf`) writes one. + +Staged flat into `program/classes/` by `//main/staging:_install_classes`, using a +new `flatten` option on `tree_install` (the six jars come from five packages, so +no single `strip_prefix` applies). + +**DIVERGENCE — `Sealed:` is omitted.** Upstream seals these jars, with jurt's +manifest un-sealing `com/sun/star/lib/util/` and `com/sun/star/uno/` because +those packages are split across `jurt.jar` and `ridl.jar`. A per-package manifest +section cannot be expressed through singlejar's `--deploy_manifest_lines`, which +writes the main section only; a blanket `Sealed: true` would therefore be +*stricter* than upstream and throw `SecurityException` on exactly that split. +Sealing only ever restricts and never enables, so omitting it is the safe +direction. Reversible if the jars ever need integrity sealing. + +#### 3. `jvmfwk` needs a JRE to find — and had two of its own gaps + +- **`jvmfwk.dll` was built without `SOLAR_JAVA`**, which is not a feature switch + here either: `framework.cxx` compiles `jfw_startVM()` down to a bare + `return JFW_E_ERROR;` under `#ifndef SOLAR_JAVA`. Every caller would have got a + generic error no matter how correct the configuration was. Now defined (the + exported ABI is unchanged — the JNI types appear only behind pointers, so + consumers that include `jvmfwk/framework.h` without it still link). +- **`sunjavaplugin.dll` was never built.** `jvmfwk` itself contains no JRE + knowledge; `javavendors.xml` maps every supported vendor to + `vnd.sun.star.expand:$URE_INTERNAL_LIB_DIR/sunjavaplugin.dll`, which jvmfwk + `osl_loadModule()`s. It is a private plugin interface (4 exports from + `sunjavaplugin.map`), *not* a UNO component — no `component_getFactory`, and + nothing in `services.rdb`. Nothing links it, so it must be listed explicitly in + staging. See `main/jvmfwk/readme.md`. +- **`javavendors.xml` and `jvmfwk3.ini`** are now staged into `program/`. The ini + name is not ours to choose: `jvmfwk/source/fwkutil.hxx` hardcodes + `SAL_CONFIGFILE("/jvmfwk3")` relative to its own library directory, so it stays + `jvmfwk3.ini` even though the library is built as `jvmfwk.dll` here. + +#### Still open + +- **No `Package_jvmfwk` equivalent for `sunjavapluginrc`.** Upstream ships it + next to the plugin with `JFW_PLUGIN_DO_NOT_CHECK_ACCESSIBILITY=1`; jvmfwk reads + that key through the *default* `rtl::Bootstrap`, not a plugin-private ini, so + the file appears to be inert in an office install. Left unstaged; the fallback + is a registry probe of `HKCU\Software\OpenOffice\Accessibility\AtToolSupport`. +- **End-to-end verification.** The chain builds and stages; it has not yet been + exercised by loading an actual Java UNO component. The natural first probe is + `cppuhelper/qa/propertysetmixin`'s three red `testJava*` cases, which need + exactly this (plus that suite's own Java component jar). diff --git a/main/unoil/BUILD.bazel b/main/unoil/BUILD.bazel index ec128c42fe..40b69a0f50 100644 --- a/main/unoil/BUILD.bazel +++ b/main/unoil/BUILD.bazel @@ -1,4 +1,4 @@ -load("//build/rules:java_pipeline.bzl", "jar_from_directory", "javamaker_classes") +load("//build/rules:java_pipeline.bzl", "jar_from_directory", "javamaker_classes", "uno_jar") load("@rules_java//java:defs.bzl", "java_import") # ── Step 1: Generate Java .class files from offapi.rdb ─────────────────────── @@ -26,3 +26,19 @@ java_import( exports = ["//main/ridljar:ridl"], visibility = ["//visibility:public"], ) + +# ── Step 4: unoil.jar → program/classes/ ───────────────────────────────────── +# Unlike the other five runtime jars, unoil.jar is NOT one of the names +# UnoClassLoader.createUrls() hardcodes. It reaches the class loader through +# the OTHER channel: javavm.cxx expands $URE_INTERNAL_JAVA_CLASSPATH and passes +# the result as the UnoClassLoader classPath argument. Upstream chains that to +# URE_MORE_JAVA_TYPES = "$ORIGIN/classes/unoil.jar $ORIGIN/classes/ +# ScriptFramework.jar ..." in fundamentalbasisrc; main/staging/fundamental.ini +# does the same (minus ScriptFramework, not built yet). So an empty +# URE_INTERNAL_JAVA_CLASSPATH is precisely what loses the office API types. +uno_jar( + name = "unoil_jar", + out = "unoil.jar", + jars = [":offapi_java_jar"], + visibility = ["//visibility:public"], +)
