commit 380ea6fb5eea659200d496540efea1fa4e39dcaf (tree)
parent 377bb8f23744001562cda4e15ab1f041bc3c6686
Author: Andrew Kelley <andrew@ziglang.org>
Date: Fri, 30 Jan 2026 08:16:39 +0100
Merge pull request 'Compilation: make libzigc share zcu if possible' (#31037) from GasInfinity/zig:libc-share-zcu into master
Reviewed-on: https://codeberg.org/ziglang/zig/pulls/31037
Diffstat:
4 files changed, 69 insertions(+), 16 deletions(-)
diff --git a/lib/c/common.zig b/lib/c/common.zig
@@ -1,18 +1,14 @@
const builtin = @import("builtin");
const std = @import("std");
-pub const linkage: std.builtin.GlobalLinkage = if (builtin.is_test)
- .internal
-else
- .strong;
+/// It is incorrect to make this conditional on `builtin.is_test`, because it is possible that
+/// libzigc is being linked into a different test compilation, as opposed to being tested itself.
+pub const linkage: std.builtin.GlobalLinkage = .strong;
/// Determines the symbol's visibility to other objects.
/// For WebAssembly this allows the symbol to be resolved to other modules, but will not
/// export it to the host runtime.
-pub const visibility: std.builtin.SymbolVisibility = if (linkage != .internal)
- .hidden
-else
- .default;
+pub const visibility: std.builtin.SymbolVisibility = .hidden;
/// Given a low-level syscall return value, sets errno and returns `-1`, or on
/// success returns the result.
diff --git a/src/Compilation.zig b/src/Compilation.zig
@@ -80,6 +80,7 @@ sysroot: ?[]const u8,
root_name: [:0]const u8,
compiler_rt_strat: RtStrat,
ubsan_rt_strat: RtStrat,
+zigc_strat: RtStrat,
/// Resolved into known paths, any GNU ld scripts already resolved.
link_inputs: []const link.Input,
/// Needed only for passing -F args to clang.
@@ -2101,6 +2102,47 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic,
try options.root_mod.deps.putNoClobber(arena, "ubsan_rt", ubsan_rt_mod);
}
+ // Like with ubsan_rt we want to go through the `_ = @import("zigc")`
+ // approach if possible since it uses even more of the standard library
+ // and can thus reduce further unnecesary bloat.
+ const zigc_strat: RtStrat = s: {
+ if (options.skip_linker_dependencies) break :s .none;
+ if (target.ofmt == .c) break :s .none;
+ if (!link_libc or !is_exe_or_dyn_lib) break :s .none;
+ if (!target_util.wantsZigC(target, options.config.link_mode)) break :s .none;
+ if (have_zcu) break :s .zcu;
+ break :s .lib;
+ };
+
+ if (zigc_strat == .zcu) {
+ const zigc_mod = Package.Module.create(arena, .{
+ .paths = .{
+ .root = .zig_lib_root,
+ .root_src_path = "c.zig",
+ },
+ .fully_qualified_name = "zigc",
+ .cc_argv = &.{},
+ .inherited = .{},
+ .global = options.config,
+ .parent = options.root_mod,
+ }) catch |err| switch (err) {
+ error.OutOfMemory => |e| return e,
+ // None of these are possible because the configuration matches the root module
+ // which already passed these checks.
+ error.ValgrindUnsupportedOnTarget => unreachable,
+ error.TargetRequiresSingleThreaded => unreachable,
+ error.BackendRequiresSingleThreaded => unreachable,
+ error.TargetRequiresPic => unreachable,
+ error.PieRequiresPic => unreachable,
+ error.DynamicLinkingRequiresPic => unreachable,
+ error.TargetHasNoRedZone => unreachable,
+ error.StackCheckUnsupportedByTarget => unreachable,
+ error.StackProtectorUnsupportedByTarget => unreachable,
+ error.StackProtectorUnavailableWithoutLibC => unreachable,
+ };
+ try options.root_mod.deps.putNoClobber(arena, "zigc", zigc_mod);
+ }
+
if (options.verbose_llvm_cpu_features) {
if (options.root_mod.resolved_target.llvm_cpu_features) |cf| {
const stderr = try io.lockStderr(&.{}, null);
@@ -2296,6 +2338,7 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic,
.libc_installation = libc_dirs.libc_installation,
.compiler_rt_strat = compiler_rt_strat,
.ubsan_rt_strat = ubsan_rt_strat,
+ .zigc_strat = zigc_strat,
.link_inputs = options.link_inputs,
.framework_dirs = options.framework_dirs,
.llvm_opt_bisect_limit = options.llvm_opt_bisect_limit,
@@ -2651,13 +2694,6 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic,
} else {
return diag.fail(.cross_libc_unavailable);
}
-
- if ((target.isMuslLibC() and comp.config.link_mode == .static) or
- target.isWasiLibC() or
- target.isMinGW())
- {
- comp.queued_jobs.zigc_lib = true;
- }
}
// Generate Windows import libs.
@@ -2711,6 +2747,15 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic,
.dyn_lib => unreachable, // hack for compiler_rt only
}
+ switch (comp.zigc_strat) {
+ .none, .zcu => {},
+ .lib => {
+ log.debug("queuing a job to build libzigc", .{});
+ comp.queued_jobs.zigc_lib = true;
+ },
+ .obj, .dyn_lib => unreachable, // only available as a static library or inside an existing ZCU
+ }
+
if (is_exe_or_dyn_lib and comp.config.any_fuzz) {
log.debug("queuing a job to build libfuzzer", .{});
comp.queued_jobs.fuzzer_lib = true;
@@ -3100,6 +3145,11 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateE
zcu.analysis_roots_buffer[zcu.analysis_roots_len] = ubsan_rt_mod;
zcu.analysis_roots_len += 1;
}
+
+ if (zcu.root_mod.deps.get("zigc")) |zigc_mod| {
+ zcu.analysis_roots_buffer[zcu.analysis_roots_len] = zigc_mod;
+ zcu.analysis_roots_len += 1;
+ }
}
// The linker progress node is set up here instead of in `performAllTheWork`, because
@@ -3531,6 +3581,7 @@ fn addNonIncrementalStuffToCacheManifest(
man.hash.add(comp.skip_linker_dependencies);
man.hash.add(comp.compiler_rt_strat);
man.hash.add(comp.ubsan_rt_strat);
+ man.hash.add(comp.zigc_strat);
man.hash.add(comp.rc_includes);
man.hash.addListOfBytes(comp.force_undefined_symbols.keys());
man.hash.addListOfBytes(comp.framework_dirs);
diff --git a/src/Zcu.zig b/src/Zcu.zig
@@ -269,7 +269,7 @@ nav_val_analysis_queued: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, voi
/// These are the modules which we initially queue for analysis in `Compilation.update`.
/// `resolveReferences` will use these as the root of its reachability traversal.
-analysis_roots_buffer: [4]*Package.Module,
+analysis_roots_buffer: [5]*Package.Module,
analysis_roots_len: usize = 0,
/// This is the cached result of `Zcu.resolveReferences`. It is computed on-demand, and
/// reset to `null` when any semantic analysis occurs (since this invalidates the data).
diff --git a/src/target.zig b/src/target.zig
@@ -423,6 +423,12 @@ pub fn canBuildLibUbsanRt(target: *const std.Target) enum { no, yes, llvm_only,
};
}
+/// Whether libzigc can fill-in the gaps of an existing libc
+/// or *is* the libc of the target.
+pub fn wantsZigC(target: *const std.Target, link_mode: std.builtin.LinkMode) bool {
+ return (target.isMuslLibC() and link_mode == .static) or target.isWasiLibC() or target.isMinGW();
+}
+
pub fn hasRedZone(target: *const std.Target) bool {
return switch (target.cpu.arch) {
.aarch64,