Fix JSC build and add bytecode cache timestamp validation

- Remove -ffat-lto-objects and --whole-archive flags from build-jsc.ts
  to fix libgcc multiple definition linker errors on Linux
- Add timestamp validation in transpiler.zig to invalidate bytecode
  cache when source file is newer than cached bytecode

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Sosuke Suzuki
2025-12-05 16:43:25 +09:00
parent 8d8345a114
commit 116e2b3669
2 changed files with 48 additions and 7 deletions

View File

@@ -134,16 +134,20 @@ const getBuildFlags = (config: BuildConfig) => {
const getBuildEnv = () => {
const env = { ...process.env };
const cflags = ["-ffat-lto-objects"];
const cxxflags = ["-ffat-lto-objects"];
const cflags: string[] = [];
const cxxflags: string[] = [];
if (IS_LINUX && buildConfig !== "lto") {
cflags.push("-Wl,--whole-archive");
cxxflags.push("-Wl,--whole-archive", "-DUSE_BUN_JSC_ADDITIONS=ON", "-DUSE_BUN_EVENT_LOOP=ON");
// Note: -ffat-lto-objects and --whole-archive removed due to libgcc multiple definition errors
cxxflags.push("-DUSE_BUN_JSC_ADDITIONS=ON", "-DUSE_BUN_EVENT_LOOP=ON");
}
env.CFLAGS = (env.CFLAGS || "") + " " + cflags.join(" ");
env.CXXFLAGS = (env.CXXFLAGS || "") + " " + cxxflags.join(" ");
if (cflags.length > 0) {
env.CFLAGS = (env.CFLAGS || "") + " " + cflags.join(" ");
}
if (cxxflags.length > 0) {
env.CXXFLAGS = (env.CXXFLAGS || "") + " " + cxxflags.join(" ");
}
if (IS_MAC) {
env.ICU_INCLUDE_DIRS = `${HOMEBREW_PREFIX}opt/icu4c/include`;

View File

@@ -1162,7 +1162,44 @@ pub const Transpiler = struct {
var path_buf2: bun.PathBuffer = undefined;
@memcpy(path_buf2[0..path.text.len], path.text);
path_buf2[path.text.len..][0..bun.bytecode_extension.len].* = bun.bytecode_extension.*;
const bytecode = bun.sys.File.toSourceAt(dirname_fd.unwrapValid() orelse bun.FD.cwd(), path_buf2[0 .. path.text.len + bun.bytecode_extension.len], bun.default_allocator, .{}).asValue() orelse break :brk default_value;
const dir_fd = dirname_fd.unwrapValid() orelse bun.FD.cwd();
// Add null terminator for bytecode path
path_buf2[path.text.len + bun.bytecode_extension.len] = 0;
const bytecode_path: [:0]const u8 = path_buf2[0 .. path.text.len + bun.bytecode_extension.len :0];
// Check timestamps: if source file is newer than bytecode cache, invalidate cache
// This ensures the cache is regenerated when source changes
// Create null-terminated source path
var source_path_buf: bun.PathBuffer = undefined;
@memcpy(source_path_buf[0..path.text.len], path.text);
source_path_buf[path.text.len] = 0;
const source_path: [:0]const u8 = source_path_buf[0..path.text.len :0];
const source_stat = bun.sys.fstatat(dir_fd, source_path);
const bytecode_stat = bun.sys.fstatat(dir_fd, bytecode_path);
if (source_stat == .result and bytecode_stat == .result) {
const source_mtime = source_stat.result.mtime();
const bytecode_mtime = bytecode_stat.result.mtime();
// Compare timestamps: if source is newer than bytecode by more than 1 second, skip cache
// We use 1 second tolerance because bytecode is written just before the source file
// during Bun.build, so there can be sub-second timing differences
// (source_mtime > bytecode_mtime + 1 second)
if (source_mtime.sec > bytecode_mtime.sec + 1 or
(source_mtime.sec == bytecode_mtime.sec + 1 and source_mtime.nsec > bytecode_mtime.nsec))
{
break :brk default_value;
}
} else {
// If we can't stat one of the files, skip cache
if (bytecode_stat == .err) {
break :brk default_value;
}
}
const bytecode = bun.sys.File.toSourceAt(dir_fd, bytecode_path, bun.default_allocator, .{}).asValue() orelse break :brk default_value;
if (bytecode.contents.len == 0) {
break :brk default_value;
}