Files
bun-src/test/internal/build-debug-info-flags.test.ts
2026-08-27 21:09:14 +00:00

130 lines
5.1 KiB
TypeScript

/**
* Pins the order of the debug-info flags in scripts/build/flags.ts.
*
* clang takes the debug-info level from the LAST -g flag on the command line,
* and -glldb (a tuning flag) counts as one: it resets the level to full and,
* for lldb tuning, to the standalone kind. The tables used to emit `-g1 ...
* -glldb` for release builds, so `clang -###` showed
* `-debug-info-kind=standalone` and every release TU (deps included) carried
* full DWARF instead of the line tables the `-g1` entry asks for. The level
* flag has to be the last -g flag the tables emit, for bun's own flags and for
* the flags forwarded to deps.
*
* Pure config evaluation: no compiler needed, runs on every host.
*/
import { describe, expect, test } from "bun:test";
import { isMacOS, tempDir } from "harness";
import { resolveConfig, type Config, type PartialConfig, type Toolchain } from "../../scripts/build/config.ts";
import { computeDepFlags, computeFlags } from "../../scripts/build/flags.ts";
/** A fully-populated fake toolchain; resolveConfig never spawns any of these. */
function mockToolchain(): Toolchain {
return {
cc: "/fake/llvm/bin/clang",
cxx: "/fake/llvm/bin/clang++",
hostCc: undefined,
hostCxx: undefined,
clangVersion: "21.1.8",
clangResourceDir: "/fake/llvm/lib/clang/21",
ar: "/fake/llvm/bin/llvm-ar",
ranlib: "/fake/llvm/bin/llvm-ranlib",
ld: "/fake/llvm/bin/ld.lld",
ld64Lld: "/fake/llvm/bin/ld64.lld",
rustLld: undefined,
rustLlvmVersion: "22.1.4",
rustSysroot: undefined,
rustHostTriple: undefined,
strip: "/fake/bin/strip",
llvmStrip: "/fake/llvm/bin/llvm-strip",
dsymutil: "/fake/llvm/bin/dsymutil",
bun: "/fake/bin/bun",
jsRuntime: "/fake/bin/bun",
esbuild: "/fake/bin/esbuild",
ccache: undefined,
cmake: "/fake/bin/cmake",
cargo: undefined,
cargoHome: undefined,
rustupHome: undefined,
msvcLinker: undefined,
rc: undefined,
mt: undefined,
nasm: undefined,
};
}
/**
* A linux-x64 target resolves on every host once it is told where its sysroot
* is (the path is only recorded, never opened).
*/
function linuxConfig(partial: PartialConfig, buildDir: string): Config {
return resolveConfig(
{ os: "linux", arch: "x64", abi: "gnu", buildDir, linuxSysroot: buildDir, ...partial },
mockToolchain(),
);
}
/**
* The -g flags that set the debug-info level or kind, in command-line order.
* `-gz=` only selects section compression and is excluded.
*/
function levelFlags(flags: string[]): string[] {
return flags.filter(f => f.startsWith("-g") && !f.startsWith("-gz"));
}
/** Every flag list a config produces that reaches a compiler: bun's C and C++ flags and the deps' C and C++ flags. */
function allCompileFlagLists(cfg: Config): string[][] {
const bun = computeFlags(cfg);
const deps = computeDepFlags(cfg);
return [bun.cflags, bun.cxxflags, deps.cflags, deps.cxxflags];
}
describe("debug-info flag order", () => {
test("release + LTO: -g1 is the last -g flag, after -glldb, for bun and for deps", () => {
using dir = tempDir("build-debug-info", {});
const cfg = linuxConfig({ buildType: "Release", lto: true }, String(dir));
for (const flags of allCompileFlagLists(cfg)) {
expect(levelFlags(flags)).toEqual(["-glldb", "-g1"]);
// Line tables carry no types, so there is nothing to home.
expect(flags).not.toContain("-fno-standalone-debug");
}
});
test("release without LTO: full, homed debug info", () => {
using dir = tempDir("build-debug-info", {});
for (const partial of [{ buildType: "Release" }, { buildType: "Release", asan: true }] as const) {
const cfg = linuxConfig(partial, String(dir));
expect(cfg.lto).toBe(false);
for (const flags of allCompileFlagLists(cfg)) {
expect(levelFlags(flags)).toEqual(["-glldb", "-g"]);
expect(flags.indexOf("-gz=zstd")).toBe(flags.indexOf("-g") + 1);
expect(flags).toContain("-fno-standalone-debug");
}
}
});
test("debug: -g3 is the last -g flag, after -glldb, homed, for bun and for deps", () => {
using dir = tempDir("build-debug-info", {});
const cfg = linuxConfig({ buildType: "Debug" }, String(dir));
for (const flags of allCompileFlagLists(cfg)) {
expect(levelFlags(flags)).toEqual(["-glldb", "-g3"]);
// The compression flag stays attached to the level flag it belongs to.
expect(flags.indexOf("-gz=zstd")).toBe(flags.indexOf("-g3") + 1);
expect(flags).toContain("-fno-standalone-debug");
}
});
// Cross-config path only: on macOS, resolveConfig({ os: "darwin" }) probes
// xcode-select for the real SDK. The flag tables are the same either way.
test.skipIf(isMacOS)("darwin release + LTO: the DWARF version flag comes first and -g1 still ends the list", () => {
using dir = tempDir("build-debug-info", {});
const cfg = resolveConfig(
{ os: "darwin", arch: "aarch64", buildType: "Release", lto: true, buildDir: String(dir) },
mockToolchain(),
);
for (const flags of allCompileFlagLists(cfg)) {
expect(levelFlags(flags)).toEqual(["-gdwarf-4", "-glldb", "-g1"]);
}
});
});