Skip to content

Commit d9e277c

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 d9e277c

File tree

1 file changed

+169
-2
lines changed

1 file changed

+169
-2
lines changed

src/Compilation.zig

Lines changed: 169 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+
const isystem_flag = "-isystem";
6319+
if (mem.eql(u8, arg, isystem_flag)) {
6320+
isystem_path_next = true;
6321+
continue;
6322+
}
6323+
if (mem.startsWith(u8, arg, isystem_flag)) {
6324+
try user_isystem_paths.append(arg[isystem_flag.len..]);
6325+
continue;
6326+
}
6327+
try other_cc_argv.append(arg);
6328+
}
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
@@ -6518,6 +6542,12 @@ pub fn addCCArgs(
65186542
}
65196543
}
65206544

6545+
// User provided isystem paths come first in clang.
6546+
for (user_isystem_paths.items) |path| {
6547+
try argv.append("-isystem");
6548+
try argv.append(path);
6549+
}
6550+
65216551
if (comp.config.link_libcpp) {
65226552
try argv.append("-isystem");
65236553
try argv.append(try fs.path.join(arena, &[_][]const u8{
@@ -6798,8 +6828,145 @@ pub fn addCCArgs(
67986828
else => {},
67996829
}
68006830

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

68056972
fn failCObj(

0 commit comments

Comments
 (0)