Skip to content

Commit 3746be5

Browse files
fix(cc): move user provided zig cc args to front of command
Problem: Currently if you pass a `-isystem` argument to `zig cc`, the include path will be searched after all of the libc, libcpp, and native system paths. However if you run the same command against `clang` and `gcc` you'll see that any user provided `-isystem` paths are at the very front. Solution: Push all user provided arguments to the arg list at the start of `addCCArgs()`. This makes it so that any compiler arguments that rely on the order of arguments passed will prioritize the user provided values first. Fixes: #24342 Fixes: #24243
1 parent f97baca commit 3746be5

File tree

1 file changed

+163
-2
lines changed

1 file changed

+163
-2
lines changed

src/Compilation.zig

Lines changed: 163 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6304,6 +6304,30 @@ pub fn addCCArgs(
63046304
) !void {
63056305
const target = &mod.resolved_target.result;
63066306

6307+
var user_isystem_paths: std.ArrayList([]const u8) = .init(arena);
6308+
var other_cc_argv: std.ArrayList([]const u8) = .init(arena);
6309+
6310+
var isystem_path_next = false;
6311+
for ([_][]const []const u8{ comp.global_cc_argv, mod.cc_argv }) |cc_argv| {
6312+
for (cc_argv) |arg| {
6313+
if (isystem_path_next) {
6314+
try user_isystem_paths.append(arg);
6315+
isystem_path_next = false;
6316+
continue;
6317+
}
6318+
if (mem.eql(u8, arg, "-isystem")) {
6319+
isystem_path_next = true;
6320+
continue;
6321+
}
6322+
try other_cc_argv.append(arg);
6323+
}
6324+
}
6325+
6326+
for (user_isystem_paths.items) |path| {
6327+
try argv.append("-isystem");
6328+
try argv.append(path);
6329+
}
6330+
63076331
// As of Clang 16.x, it will by default read extra flags from /etc/clang.
63086332
// I'm sure the person who implemented this means well, but they have a lot
63096333
// to learn about abstractions and where the appropriate boundaries between
@@ -6798,8 +6822,145 @@ pub fn addCCArgs(
67986822
else => {},
67996823
}
68006824

6801-
try argv.appendSlice(comp.global_cc_argv);
6802-
try argv.appendSlice(mod.cc_argv);
6825+
try argv.appendSlice(other_cc_argv.items);
6826+
}
6827+
6828+
test "addCCArgs() puts user provided `-isystem` paths before native paths" {
6829+
const expected_isys_path_order: []const []const u8 = &.{ "/user_provided/isystem/dir/", "/module/isystem/dir/", "/native/isystem/dir/" };
6830+
var expected_isys_path_queue: std.fifo.LinearFifo([]const u8, .{ .Static = expected_isys_path_order.len }) = .init();
6831+
try expected_isys_path_queue.write(expected_isys_path_order);
6832+
var alloc: std.heap.DebugAllocator(.{}) = .init;
6833+
var arena = std.heap.ArenaAllocator.init(alloc.allocator());
6834+
6835+
const resolved_target: Package.Module.ResolvedTarget = .{
6836+
.is_native_os = true,
6837+
.is_explicit_dynamic_linker = false,
6838+
.is_native_abi = true,
6839+
.result = .{
6840+
.abi = .musl,
6841+
.cpu = .{
6842+
.arch = .x86_64,
6843+
.features = .empty,
6844+
.model = Target.Cpu.Model.baseline(.x86_64, .{
6845+
.tag = .linux,
6846+
.version_range = .{ .none = {} },
6847+
}),
6848+
},
6849+
.os = .{
6850+
.tag = .linux,
6851+
.version_range = .default(.x86_64, .linux, .musl),
6852+
},
6853+
.ofmt = .c,
6854+
},
6855+
};
6856+
const conf = try Compilation.Config.resolve(.{
6857+
.emit_bin = true,
6858+
.have_zcu = false,
6859+
.is_test = true,
6860+
.output_mode = .Exe,
6861+
.resolved_target = resolved_target,
6862+
});
6863+
6864+
const cwd = try std.fs.cwd().realpathAlloc(arena.allocator(), ".");
6865+
const global_cache = try introspect.resolveGlobalCacheDir(arena.allocator());
6866+
const global_cache_handle = try std.fs.openDirAbsolute(global_cache, .{ .access_sub_paths = true });
6867+
const local_cache = (try introspect.resolveSuitableLocalCacheDir(arena.allocator(), cwd)).?;
6868+
const local_cache_handle = try std.fs.openDirAbsolute(local_cache, .{ .access_sub_paths = true });
6869+
var thread_pool: std.Thread.Pool = undefined;
6870+
try thread_pool.init(.{ .allocator = arena.allocator() });
6871+
6872+
const mod = try Package.Module.create(arena.allocator(), .{
6873+
.paths = .{
6874+
.root = .{
6875+
.root = .none,
6876+
.sub_path = cwd,
6877+
},
6878+
.root_src_path = "src",
6879+
},
6880+
.cc_argv = &.{ "-isystem", "/module/isystem/dir/" },
6881+
.fully_qualified_name = "mod",
6882+
.parent = null,
6883+
.global = conf,
6884+
.inherited = .{ .resolved_target = resolved_target },
6885+
});
6886+
6887+
const compilation = try Compilation.create(
6888+
alloc.allocator(),
6889+
arena.allocator(),
6890+
.{
6891+
.dirs = .{
6892+
.cwd = cwd,
6893+
.global_cache = .{ .path = global_cache, .handle = global_cache_handle },
6894+
.local_cache = .{ .path = local_cache, .handle = local_cache_handle },
6895+
.zig_lib = try introspect.findZigLibDir(arena.allocator()),
6896+
},
6897+
.cache_mode = .whole,
6898+
.config = conf,
6899+
.thread_pool = &thread_pool,
6900+
.root_mod = mod,
6901+
.root_name = "mod",
6902+
.emit_bin = .yes_cache,
6903+
.native_system_include_paths = &.{"/native/isystem/dir/"},
6904+
.global_cc_argv = &.{ "-isystem", "/user_provided/isystem/dir/" },
6905+
},
6906+
);
6907+
6908+
var argv: std.ArrayList([]const u8) = .init(arena.allocator());
6909+
6910+
try compilation.addCCArgs(arena.allocator(), &argv, .c, null, mod);
6911+
6912+
var isys_paths_result: std.ArrayList([]const u8) = .init(arena.allocator());
6913+
6914+
const isys_paths_in_order = check_path_order: {
6915+
var isystem_path_up_next = false;
6916+
for (argv.items) |arg| {
6917+
if (mem.eql(u8, arg, "-isystem")) {
6918+
isystem_path_up_next = true;
6919+
continue;
6920+
}
6921+
if (isystem_path_up_next) {
6922+
try isys_paths_result.append(arg);
6923+
if (mem.eql(u8, arg, expected_isys_path_queue.peekItem(0))) {
6924+
expected_isys_path_queue.discard(1);
6925+
if (expected_isys_path_queue.readableLength() == 0) {
6926+
break :check_path_order true;
6927+
}
6928+
}
6929+
}
6930+
isystem_path_up_next = false;
6931+
}
6932+
break :check_path_order false;
6933+
};
6934+
6935+
std.testing.expect(isys_paths_in_order) catch |e| {
6936+
var result_str: std.ArrayList(u8) = .init(arena.allocator());
6937+
var result_writer = result_str.writer();
6938+
var expect_str: std.ArrayList(u8) = .init(arena.allocator());
6939+
var expect_writer = expect_str.writer();
6940+
6941+
for (isys_paths_result.items, 0..) |path, index| {
6942+
_ = try result_writer.write(path);
6943+
if (index < isys_paths_result.items.len - 1)
6944+
_ = try result_writer.write(", ");
6945+
}
6946+
for (expected_isys_path_order, 0..) |path, index| {
6947+
_ = try expect_writer.write(path);
6948+
if (index < expected_isys_path_order.len - 1)
6949+
_ = try expect_writer.write(", ");
6950+
}
6951+
std.log.err(
6952+
\\Expected isystem paths were not found in the correct order.
6953+
\\
6954+
\\Expected:
6955+
\\ [{s}]
6956+
\\
6957+
\\Result:
6958+
\\ [{s}]
6959+
\\
6960+
\\Note: Expected and Result do not need to match 1:1, all items in expected just need to be found in result in the same order.
6961+
, .{ expect_str.items, result_str.items });
6962+
return e;
6963+
};
68036964
}
68046965

68056966
fn failCObj(

0 commit comments

Comments
 (0)