diff --git a/pkg/highway/build.zig b/pkg/highway/build.zig index 64c0e8869..57ed667d7 100644 --- a/pkg/highway/build.zig +++ b/pkg/highway/build.zig @@ -3,6 +3,7 @@ const std = @import("std"); pub fn build(b: *std.Build) !void { const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); + const no_libc = b.option(bool, "no_libc", "Avoid linking libc when embedding Highway into no-libc builds") orelse false; const upstream_ = b.lazyDependency("highway", .{}); @@ -22,9 +23,9 @@ pub fn build(b: *std.Build) !void { .linkage = .static, }); - // Our highway package is free of libc at runtime (uses no symbols) - // but does require libc headers at compile time. - lib.linkLibC(); + // Our highway package is free of libc at runtime, so only libc-backed + // consumers should request it explicitly. + if (!no_libc) lib.linkLibC(); lib.addIncludePath(b.path("src/cpp")); if (upstream_) |upstream| { diff --git a/pkg/simdutf/build.zig b/pkg/simdutf/build.zig index 95dd47483..4078c78ba 100644 --- a/pkg/simdutf/build.zig +++ b/pkg/simdutf/build.zig @@ -1,5 +1,19 @@ const std = @import("std"); +const no_libc_flags = [_][]const u8{ + "-DSIMDUTF_NO_LIBC=1", + "-DSIMDUTF_LIBC_MEMCPY=simdutf_memcpy", + "-DSIMDUTF_LIBC_MEMMOVE=simdutf_memmove", + "-DSIMDUTF_LIBC_MEMSET=simdutf_memset", + "-DSIMDUTF_LIBC_MEMCMP=simdutf_memcmp", + "-DSIMDUTF_LIBC_STRLEN=simdutf_strlen", + "-DSIMDUTF_LIBC_GETENV=simdutf_getenv", +}; + +pub fn noLibcFlags() []const []const u8 { + return &no_libc_flags; +} + pub fn build(b: *std.Build) !void { const optimize = b.standardOptimizeOption(.{}); const target = b.standardTargetOptions(.{}); @@ -15,7 +29,7 @@ pub fn build(b: *std.Build) !void { .linkage = .static, }); lib.addIncludePath(b.path("vendor")); - lib.linkLibC(); + if (!no_libc) lib.linkLibC(); libcpp: { if (target.result.abi == .msvc) { // On MSVC, we must not use linkLibCpp because Zig unconditionally @@ -66,15 +80,7 @@ pub fn build(b: *std.Build) !void { } if (no_libc) { - try flags.appendSlice(b.allocator, &.{ - "-DSIMDUTF_NO_LIBC=1", - "-DSIMDUTF_LIBC_MEMCPY=simdutf_memcpy", - "-DSIMDUTF_LIBC_MEMMOVE=simdutf_memmove", - "-DSIMDUTF_LIBC_MEMSET=simdutf_memset", - "-DSIMDUTF_LIBC_MEMCMP=simdutf_memcmp", - "-DSIMDUTF_LIBC_STRLEN=simdutf_strlen", - "-DSIMDUTF_LIBC_GETENV=simdutf_getenv", - }); + try flags.appendSlice(b.allocator, noLibcFlags()); lib.root_module.addCMacro("SIMDUTF_NO_LIBC", "1"); diff --git a/src/build/GhosttyZig.zig b/src/build/GhosttyZig.zig index b2d6b7d93..5a15775c1 100644 --- a/src/build/GhosttyZig.zig +++ b/src/build/GhosttyZig.zig @@ -113,6 +113,7 @@ fn initVt( // General build options const general_options = b.addOptions(); try cfg.addOptions(general_options); + const system_simdutf = cfg.simd and b.systemIntegrationOption("simdutf", .{}); const vt = b.addModule(name, .{ .root_source_file = b.path("src/lib_vt.zig"), @@ -122,11 +123,8 @@ fn initVt( // Vendored C++ dependencies are built with no-libcxx and // no-libc modes so we don't need libc or libcpp. System-provided // simdutf requires both libc and libcpp at runtime. - .link_libc = if (cfg.simd and - b.systemIntegrationOption("simdutf", .{})) true else null, - .link_libcpp = if (cfg.simd and - b.systemIntegrationOption("simdutf", .{}) and - cfg.target.result.abi != .msvc) true else null, + .link_libc = if (system_simdutf) true else if (cfg.emit_lib_vt) false else null, + .link_libcpp = if (system_simdutf and cfg.target.result.abi != .msvc) true else if (cfg.emit_lib_vt and cfg.target.result.abi != .msvc) false else null, }); vt.addOptions("build_options", general_options); vt_options.add(b, vt); diff --git a/src/build/SharedDeps.zig b/src/build/SharedDeps.zig index b35a55ced..4771417fb 100644 --- a/src/build/SharedDeps.zig +++ b/src/build/SharedDeps.zig @@ -766,45 +766,84 @@ pub fn addSimd( ) !void { const target = m.resolved_target.?; const optimize = m.optimize.?; + const system_simdutf = b.systemIntegrationOption("simdutf", .{}); const system_highway = b.systemIntegrationOption("highway", .{ .default = false }); // Simdutf - if (b.systemIntegrationOption("simdutf", .{})) { + const simdutf_lib: ?*std.Build.Step.Compile = if (system_simdutf) lib: { m.linkSystemLibrary("simdutf", dynamic_link_opts); - } else { - if (b.lazyDependency("simdutf", .{ - .target = target, - .optimize = optimize, - .no_libcxx = true, - .no_libc = simd_libc == .no_libc, - })) |simdutf_dep| { - m.linkLibrary(simdutf_dep.artifact("simdutf")); - if (static_libs) |v| try v.append( - b.allocator, - simdutf_dep.artifact("simdutf").getEmittedBin(), - ); + break :lib null; + } else if (b.lazyDependency("simdutf", .{ + .target = target, + .optimize = optimize, + .no_libcxx = true, + .no_libc = simd_libc == .no_libc, + })) |simdutf_dep| lib: { + const lib = simdutf_dep.artifact("simdutf"); + if (simd_libc == .no_libc) { + // Use the archive as a plain linker input so Zig doesn't carry + // simdutf's libc/libcpp linkage metadata into libghostty-vt. + m.addObjectFile(lib.getEmittedBin()); + } else { + m.linkLibrary(lib); } - } + if (static_libs) |v| try v.append( + b.allocator, + lib.getEmittedBin(), + ); + + break :lib lib; + } else null; // Highway - if (system_highway) { + const highway_lib: ?*std.Build.Step.Compile = if (system_highway) lib: { m.linkSystemLibrary("libhwy", dynamic_link_opts); - } else { - if (b.lazyDependency("highway", .{ - .target = target, - .optimize = optimize, - })) |highway_dep| { - m.linkLibrary(highway_dep.artifact("highway")); - if (static_libs) |v| try v.append( - b.allocator, - highway_dep.artifact("highway").getEmittedBin(), - ); + break :lib null; + } else if (b.lazyDependency("highway", .{ + .target = target, + .optimize = optimize, + .no_libc = simd_libc == .no_libc, + })) |highway_dep| lib: { + const lib = highway_dep.artifact("highway"); + if (simd_libc == .no_libc) { + // Same as simdutf above: keep Highway as a plain archive input for + // the no-libc VT build instead of propagating compile-step linkage. + m.addObjectFile(lib.getEmittedBin()); + } else { + m.linkLibrary(lib); } - } + if (static_libs) |v| try v.append( + b.allocator, + lib.getEmittedBin(), + ); + + break :lib lib; + } else null; // SIMD C++ files - m.addIncludePath(b.path("src")); { + const simd_cpp = b.addObject(.{ + .name = "ghostty-simd", + .root_module = b.createModule(.{ + .target = target, + .optimize = optimize, + .link_libc = simd_libc == .libc, + .link_libcpp = target.result.abi != .msvc, + .pic = true, + }), + }); + if (system_simdutf) { + simd_cpp.root_module.linkSystemLibrary("simdutf", dynamic_link_opts); + } else if (simdutf_lib) |lib| { + simd_cpp.root_module.linkLibrary(lib); + } + if (system_highway) { + simd_cpp.root_module.linkSystemLibrary("libhwy", dynamic_link_opts); + } else if (highway_lib) |lib| { + simd_cpp.root_module.linkLibrary(lib); + } + simd_cpp.root_module.addIncludePath(b.path("src")); + // From hwy/detect_targets.h const HWY_AVX10_2: c_int = 1 << 3; const HWY_AVX3_SPR: c_int = 1 << 4; @@ -850,6 +889,11 @@ pub fn addSimd( "-DSIMDUTF_NO_LIBCXX", ); + if (simd_libc == .no_libc) try flags.appendSlice( + b.allocator, + (b.lazyImport(@import("../../build.zig"), "simdutf") orelse unreachable).noLibcFlags(), + ); + // Disable ubsan for Windows C/C++ objects to avoid undefined // __ubsan_handle_* references. The Zig libraries on Windows don't // currently bundle a matching UBSan runtime for these objects in @@ -859,7 +903,7 @@ pub fn addSimd( "-fno-sanitize-trap=undefined", }); - m.addCSourceFiles(.{ + simd_cpp.root_module.addCSourceFiles(.{ .files = &.{ "src/simd/base64.cpp", "src/simd/codepoint_width.cpp", @@ -868,6 +912,11 @@ pub fn addSimd( }, .flags = flags.items, }); + + // Treat Ghostty's own SIMD object the same way as the vendored + // archives above so the no-libc VT link stays free of transitive C++ + // runtime metadata. + m.addObjectFile(simd_cpp.getEmittedBin()); } } diff --git a/src/simd/base64.zig b/src/simd/base64.zig index 81feeb723..aaa007981 100644 --- a/src/simd/base64.zig +++ b/src/simd/base64.zig @@ -59,11 +59,11 @@ fn scalarInput(input: []const u8) []const u8 { } // base64.cpp -extern "c" fn ghostty_simd_base64_max_length( +extern fn ghostty_simd_base64_max_length( input: [*]const u8, len: usize, ) usize; -extern "c" fn ghostty_simd_base64_decode( +extern fn ghostty_simd_base64_decode( input: [*]const u8, len: usize, output: [*]u8, diff --git a/src/simd/codepoint_width.zig b/src/simd/codepoint_width.zig index c1767bea4..e94696c43 100644 --- a/src/simd/codepoint_width.zig +++ b/src/simd/codepoint_width.zig @@ -2,7 +2,7 @@ const std = @import("std"); const options = @import("build_options"); // vt.cpp -extern "c" fn ghostty_simd_codepoint_width(u32) i8; +extern fn ghostty_simd_codepoint_width(u32) i8; pub fn codepointWidth(cp: u32) i8 { if (comptime options.simd) return ghostty_simd_codepoint_width(cp); diff --git a/src/simd/index_of.zig b/src/simd/index_of.zig index 7bf053b0d..406c599f1 100644 --- a/src/simd/index_of.zig +++ b/src/simd/index_of.zig @@ -1,7 +1,7 @@ const std = @import("std"); const options = @import("build_options"); -extern "c" fn ghostty_simd_index_of( +extern fn ghostty_simd_index_of( needle: u8, input: [*]const u8, count: usize, diff --git a/src/simd/vt.zig b/src/simd/vt.zig index 4230665f4..eb6b4eccf 100644 --- a/src/simd/vt.zig +++ b/src/simd/vt.zig @@ -4,7 +4,7 @@ const assert = @import("../quirks.zig").inlineAssert; const indexOf = @import("index_of.zig").indexOf; // vt.cpp -extern "c" fn ghostty_simd_decode_utf8_until_control_seq( +extern fn ghostty_simd_decode_utf8_until_control_seq( input: [*]const u8, count: usize, output: [*]u32,