blob: 14de43765c92dbbc0f937c4d10ea0a4998b1416f [file]
"""staged_java_test — run a native-bridge JVM test via an in-process JVM host.
A JNI bridge test loads 32-bit /MD UNO DLLs that need a VC90-CRT activation
context for every load — including the ones the UNO machinery loads at runtime
via osl::Module. A stock java.exe provides no such context (R6034). So instead of
running java.exe directly, this rule runs jni_test_launcher.exe, which carries the
VC90-CRT manifest embedded and hosts the JVM in-process: its process-default
activation context covers every UNO DLL the JVM loads.
The launcher lives inside library_dir (co-located with the UNO DLLs + the CRT SxS
assembly). The emitted .bat puts the JDK dirs and library_dir on PATH (so jvm.dll
and the DLLs' transitive deps resolve) and runs the launcher with the classpath,
java.library.path and main class. Requires build --enable_runfiles.
A non-zero exit fails the test (TestJni calls System.exit(1)).
"""
load("@rules_java//java/common:java_info.bzl", "JavaInfo")
def _staged_java_test_impl(ctx):
def rf(short_path):
return "%TEST_SRCDIR%\\%TEST_WORKSPACE%\\" + short_path.replace("/", "\\")
libdir = rf(ctx.file.library_dir.short_path)
launcher = libdir + "\\" + ctx.attr.launcher_name
jars = depset(transitive = [
dep[JavaInfo].transitive_runtime_jars
for dep in ctx.attr.runtime_deps
]).to_list()
classpath = ";".join([rf(j.short_path) for j in jars])
main_slash = ctx.attr.main_class.replace(".", "/")
path_prefix = ";".join(ctx.attr.jvm_path_dirs + [libdir])
out = ctx.actions.declare_file(ctx.label.name + ".bat")
ctx.actions.write(
output = out,
content = "\r\n".join([
"@echo off",
"setlocal",
'set "PATH=' + path_prefix + ';%PATH%"',
'"' + launcher + '" "' + classpath + '" "' + libdir + '" "' +
main_slash + '" %*',
"exit /b %ERRORLEVEL%",
"",
]),
is_executable = True,
)
runfiles = ctx.runfiles(files = [ctx.file.library_dir] + jars)
return [DefaultInfo(executable = out, runfiles = runfiles)]
staged_java_test = rule(
implementation = _staged_java_test_impl,
test = True,
attrs = {
"main_class": attr.string(mandatory = True, doc = "Dotted main class (run via the launcher)."),
"runtime_deps": attr.label_list(
providers = [[JavaInfo]],
doc = "Java deps whose transitive runtime jars form the classpath.",
),
"library_dir": attr.label(
allow_single_file = True,
doc = "native_lib_dir tree artifact: UNO DLLs + CRT SxS assembly + the launcher exe.",
),
"launcher_name": attr.string(
default = "jni_test_launcher.exe",
doc = "Basename of the in-process JVM host exe inside library_dir.",
),
"jvm_path_dirs": attr.string_list(
doc = "Absolute JDK dirs to prepend to PATH so jvm.dll + its deps resolve " +
"(e.g. <jdk>\\jre\\bin\\server and <jdk>\\jre\\bin). Machine-specific.",
),
},
)