mirror of
https://github.com/morluto/rea
synced 2026-10-28 06:46:47 -04:00
fix(native): share otool build tool parsing
This commit is contained in:
+18
-13
@@ -308,7 +308,7 @@ const parseLoadCommand = (block: string, state: ParsedLoadCommands): void => {
|
||||
fields,
|
||||
});
|
||||
collectEntrypoint(kind, fields, state);
|
||||
collectBuild(kind, block, fields, state);
|
||||
collectBuild(kind, parsed, state);
|
||||
collectDependency(kind, fields, state);
|
||||
if (PAYLOAD_DEPENDENCY_COMMANDS.has(kind))
|
||||
state.omittedDependencyCommands.push(kind);
|
||||
@@ -387,12 +387,12 @@ const collectEntrypoint = (
|
||||
|
||||
const collectBuild = (
|
||||
kind: string,
|
||||
block: string,
|
||||
fields: Readonly<Record<string, string | number | null>>,
|
||||
parsed: ParsedFields,
|
||||
state: ParsedLoadCommands,
|
||||
): void => {
|
||||
const fields = parsed.values;
|
||||
if (kind === "LC_BUILD_VERSION") {
|
||||
const tools = parseBuildTools(block);
|
||||
const tools = parseBuildTools(parsed.rows);
|
||||
if (numberField(fields, "ntools") !== tools.length)
|
||||
throw new TypeError("otool build-tool count does not match ntools");
|
||||
state.builds.push({
|
||||
@@ -517,8 +517,6 @@ const parseField = (rawLine: string): readonly [string, string] | undefined => {
|
||||
// exactly, including surrounding spaces, under otool's ordinal label.
|
||||
const option = /^\s*(string #[1-9]\d*)(?: (.*))?$/u.exec(rawLine);
|
||||
if (option?.[1] !== undefined) return [option[1], option[2] ?? ""];
|
||||
const fixedName = /^\s*(segname|sectname) (.*)$/u.exec(rawLine);
|
||||
if (fixedName?.[1] !== undefined) return [fixedName[1], fixedName[2] ?? ""];
|
||||
const line = rawLine.trim();
|
||||
// These are the compound field labels used by otool. Keeping this list
|
||||
// explicit prevents a one-word field value such as `sectname version` from
|
||||
@@ -586,16 +584,23 @@ const parseSections = (block: string): ParsedSection[] =>
|
||||
};
|
||||
});
|
||||
|
||||
/** Project tool/version pairs from the same ordered facts as command fields. */
|
||||
const parseBuildTools = (
|
||||
block: string,
|
||||
rows: ParsedFields["rows"],
|
||||
): Array<{ name: string; version: string }> => {
|
||||
const tools: Array<{ name: string; version: string }> = [];
|
||||
const lines = block.split(/\r?\n/u).map((line) => line.trim());
|
||||
for (let index = 0; index < lines.length - 1; index += 1) {
|
||||
const tool = /^tool\s+(.+)$/u.exec(lines[index] ?? "")?.[1];
|
||||
const version = /^version\s+(.+)$/u.exec(lines[index + 1] ?? "")?.[1];
|
||||
if (tool !== undefined && version !== undefined)
|
||||
tools.push({ name: BUILD_TOOL_NAMES.get(tool) ?? tool, version });
|
||||
for (let index = 0; index < rows.length; index++) {
|
||||
const field = rows[index];
|
||||
if (field?.[0] === "version")
|
||||
throw new TypeError("otool build-tool version has no preceding tool");
|
||||
if (field?.[0] !== "tool") continue;
|
||||
const version = rows[++index];
|
||||
if (field[1] === "" || version?.[0] !== "version" || version[1] === "")
|
||||
throw new TypeError("otool build tool is missing its name or version");
|
||||
tools.push({
|
||||
name: BUILD_TOOL_NAMES.get(field[1]) ?? field[1],
|
||||
version: version[1],
|
||||
});
|
||||
}
|
||||
return tools;
|
||||
};
|
||||
|
||||
@@ -595,3 +595,61 @@ it.each([
|
||||
);
|
||||
expect(load.builds[0]?.tools).toEqual([{ name, version: "1.0" }]);
|
||||
});
|
||||
|
||||
it("projects build tools from the retained ordered field facts", () => {
|
||||
const load = parseOtoolLoadCommands(
|
||||
withOtoolMachHeader(
|
||||
[
|
||||
"Load command 0",
|
||||
"cmd LC_BUILD_VERSION",
|
||||
"cmdsize 40",
|
||||
"platform 1",
|
||||
"minos 1.0",
|
||||
"sdk 1.0",
|
||||
"ntools 2",
|
||||
"tool 1",
|
||||
"",
|
||||
"version 001",
|
||||
"tool 99",
|
||||
"version 0x10",
|
||||
"",
|
||||
].join("\n"),
|
||||
),
|
||||
);
|
||||
expect(load.builds[0]?.tools).toEqual([
|
||||
{ name: "CLANG", version: "001" },
|
||||
{ name: "99", version: "0x10" },
|
||||
]);
|
||||
expect(load.commands[0]?.fields).toMatchObject({
|
||||
"tool #1": 1,
|
||||
"tool #2": 99,
|
||||
"version #1": 1,
|
||||
"version #2": 16,
|
||||
});
|
||||
});
|
||||
|
||||
it.each([
|
||||
["version 9.0", "tool 1", "version 1.0"],
|
||||
["tool 1", "version 1.0", "tool 2"],
|
||||
["tool 1", "version 1.0", "version 2.0"],
|
||||
["tool 1", "unexpected 2", "version 1.0"],
|
||||
["tool 1", "version"],
|
||||
])("rejects unpaired build tool/version facts: %j", (...toolRows) => {
|
||||
expect(() =>
|
||||
parseOtoolLoadCommands(
|
||||
withOtoolMachHeader(
|
||||
[
|
||||
"Load command 0",
|
||||
"cmd LC_BUILD_VERSION",
|
||||
"cmdsize 32",
|
||||
"platform 1",
|
||||
"minos 1.0",
|
||||
"sdk 1.0",
|
||||
"ntools 1",
|
||||
...toolRows,
|
||||
"",
|
||||
].join("\n"),
|
||||
),
|
||||
),
|
||||
).toThrow(/otool build.tool/u);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user