Skip to content

Commit f3f0602

Browse files
fix(cc): move user provided -isystem paths 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: #24243
1 parent 4e6a049 commit f3f0602

File tree

1 file changed

+164
-2
lines changed

1 file changed

+164
-2
lines changed

src/Compilation.zig

Lines changed: 164 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6304,6 +6304,25 @@ 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+
63076326
// As of Clang 16.x, it will by default read extra flags from /etc/clang.
63086327
// I'm sure the person who implemented this means well, but they have a lot
63096328
// to learn about abstractions and where the appropriate boundaries between
@@ -6518,6 +6537,12 @@ pub fn addCCArgs(
65186537
}
65196538
}
65206539

6540+
// User provided isystem paths come first in clang.
6541+
for (user_isystem_paths.items) |path| {
6542+
try argv.append("-isystem");
6543+
try argv.append(path);
6544+
}
6545+
65216546
if (comp.config.link_libcpp) {
65226547
try argv.append("-isystem");
65236548
try argv.append(try fs.path.join(arena, &[_][]const u8{
@@ -6798,8 +6823,145 @@ pub fn addCCArgs(
67986823
else => {},
67996824
}
68006825

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

68056967
fn failCObj(

0 commit comments

Comments
 (0)