* fix(ghidra): resolve keg-only Homebrew openjdk on macOS (#4556) Fixes an issue where the `ghidra-java` check fails when OpenJDK is installed via Homebrew on macOS (where it is keg-only and not added to `PATH` or resolved by `java_home`). - **Problem:** When `options.javaHome` is undefined, the provider defaulted to a bare `"java"` command, causing valid Homebrew OpenJDK installations to be reported as missing dependencies. - **Solution:** Replaced the single fallback with an absolute candidate list (`javaCandidates`) that probes standard Homebrew OpenJDK paths (`/opt/homebrew/opt/openjdk/...` and `/usr/local/opt/openjdk/...`) before falling back to `"java"`. All candidates are absolute, preserving existing constraints and current behavior. * fix(ghidra): fix javaHome type narrowing in java candidate list (#4556) Fixes a TypeScript compilation error (`TS2322` and `TS2345`) in `src/ghidra/GhidraInstallation.ts` where `options.javaHome` was inferred as `string | undefined` inside `path.join()`. - **Problem:** The ternary operator structure prevented TypeScript from narrowing `options.javaHome` to `string` in the `path.join()` call when `options.javaHome` was defined, causing CI compile checks to fail. - **Solution:** Refactored the `javaCandidates` assignment using an immediately invoked function expression (IIFE) with an explicit `if (options.javaHome === undefined)` check. This properly narrows `options.javaHome` to `string` in the non-undefined branch while keeping the same logic and Homebrew OpenJDK paths intact. * fix(ghidra): order homebrew java candidates by host architecture (#4556) Updates the Homebrew OpenJDK candidate ordering in `src/ghidra/GhidraInstallation.ts` to be architecture-aware, addressing review feedback regarding cross-architecture runs (e.g., x64 under Rosetta on Apple Silicon). - **Problem:** Previously, `/opt/homebrew` was always checked before `/usr/local`. When running REA as an x64 process under Rosetta on Apple Silicon, this could incorrectly select an ARM64 JDK over an Intel/x64 one, triggering a `nativeDecompilerCheck` failure and missing the valid fallback. - **Solution:** Dynamically orders `javaCandidates` based on the runtime `architecture`. On `arm64`, `/opt/homebrew` is prioritized; on `x64`, `/usr/local` is prioritized. Bare `"java"` remains first in both cases to preserve standard PATH resolution. --------- Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
This commit is contained in:
1 parent
360626434d
commit
8267edc0ef
2 files changed
+109
-10
No files matched your search
@@ -1410,3 +1410,80 @@ it("explains the macOS keg-only JDK case without probing an unselected installat
|
||||
"/opt/homebrew/opt/openjdk/libexec/openjdk.jdk/Contents/Home",
|
||||
);
|
||||
});
|
||||
|
||||
it.each([
|
||||
["arm64", "/opt/homebrew"],
|
||||
["x64", "/usr/local"],
|
||||
] as const)("discovers keg-only Java for macOS %s", (architecture, prefix) => {
|
||||
const probed: string[] = [];
|
||||
const checked: string[] = [];
|
||||
const inspection = inspectGhidraInstallation(
|
||||
{
|
||||
environment: { PATH: ":relative-bin:/empty-bin" },
|
||||
installDir: INSTALL,
|
||||
platform: "darwin",
|
||||
architecture,
|
||||
},
|
||||
host({
|
||||
executable: (candidate) => {
|
||||
checked.push(candidate);
|
||||
return (
|
||||
candidate.includes("/opt/openjdk/") || host().executable(candidate)
|
||||
);
|
||||
},
|
||||
probeJava: (command) => {
|
||||
probed.push(command);
|
||||
return javaProbeOk({
|
||||
...JAVA,
|
||||
home: `${prefix}/opt/openjdk/libexec/openjdk.jdk/Contents/Home`,
|
||||
osArch: architecture === "arm64" ? "aarch64" : "x86_64",
|
||||
osName: "Mac OS X",
|
||||
});
|
||||
},
|
||||
}),
|
||||
);
|
||||
expect(probed).toEqual([
|
||||
`${prefix}/opt/openjdk/libexec/openjdk.jdk/Contents/Home/bin/java`,
|
||||
]);
|
||||
expect(checked).not.toContain("java");
|
||||
expect(checked).not.toContain("relative-bin/java");
|
||||
expect(inspection.checks.find(({ name }) => name === "java")).toMatchObject({
|
||||
status: "passed",
|
||||
});
|
||||
});
|
||||
|
||||
it.each([
|
||||
["darwin", undefined, "/selected-bin/java", "java"],
|
||||
["darwin", "/selected-jdk", undefined, "/selected-jdk/bin/java"],
|
||||
["linux", undefined, undefined, "java"],
|
||||
["win32", undefined, undefined, "java.exe"],
|
||||
] as const)(
|
||||
"preserves selected Java on %s with JAVA_HOME %s",
|
||||
(platform, javaHome, pathJava, expected) => {
|
||||
const checked: string[] = [];
|
||||
const probed: string[] = [];
|
||||
inspectGhidraInstallation(
|
||||
{
|
||||
environment: { PATH: "/selected-bin" },
|
||||
installDir: INSTALL,
|
||||
platform,
|
||||
architecture: "arm64",
|
||||
...(javaHome === undefined ? {} : { javaHome }),
|
||||
},
|
||||
host({
|
||||
executable: (candidate) => {
|
||||
checked.push(candidate);
|
||||
return candidate === pathJava || host().executable(candidate);
|
||||
},
|
||||
probeJava: (command) => {
|
||||
probed.push(command);
|
||||
return missingJavaProbe;
|
||||
},
|
||||
}),
|
||||
);
|
||||
expect(probed).toEqual([expected]);
|
||||
expect(
|
||||
checked.some((candidate) => candidate.includes("/opt/openjdk/")),
|
||||
).toBe(false);
|
||||
},
|
||||
);
|
||||
@@ -446,16 +446,38 @@ const installationCoordinates = (
|
||||
applicationPropertiesPath === null
|
||||
? undefined
|
||||
: host.readText(applicationPropertiesPath);
|
||||
const javaCommand =
|
||||
options.javaHome === undefined
|
||||
? platform === "win32"
|
||||
? "java.exe"
|
||||
: "java"
|
||||
: path.join(
|
||||
options.javaHome,
|
||||
"bin",
|
||||
platform === "win32" ? "java.exe" : "java",
|
||||
);
|
||||
const javaCommand = (() => {
|
||||
if (options.javaHome !== undefined)
|
||||
return path.join(
|
||||
options.javaHome,
|
||||
"bin",
|
||||
platform === "win32" ? "java.exe" : "java",
|
||||
);
|
||||
if (platform !== "darwin")
|
||||
return platform === "win32" ? "java.exe" : "java";
|
||||
// Check the caller's PATH, never a file named java in the working directory.
|
||||
const javaOnPath = options.environment.PATH?.split(":").some(
|
||||
(directory) =>
|
||||
path.isAbsolute(directory) &&
|
||||
host.executable(path.join(directory, "java")),
|
||||
);
|
||||
if (javaOnPath) return "java";
|
||||
// Prefer the Homebrew prefix matching the process architecture.
|
||||
const prefixes =
|
||||
architecture === "arm64"
|
||||
? ["/opt/homebrew", "/usr/local"]
|
||||
: ["/usr/local", "/opt/homebrew"];
|
||||
return (
|
||||
prefixes
|
||||
.map((prefix) =>
|
||||
path.join(
|
||||
prefix,
|
||||
"opt/openjdk/libexec/openjdk.jdk/Contents/Home/bin/java",
|
||||
),
|
||||
)
|
||||
.find((candidate) => host.executable(candidate)) ?? "java"
|
||||
);
|
||||
})();
|
||||
// Relative JAVA_HOME is refused by configuration validation without execution.
|
||||
const javaProbe =
|
||||
options.javaHome === undefined || path.isAbsolute(options.javaHome)
|
||||
|
||||
Reference in new issue
Block a user