Skip to content

Commit 3dc3308

Browse files
committed
native libc detection: respect spaces in CC env var
Zig has detection for when it is accidentally being called recursively when trying to find the native libc installation. However it was not working, resulting in a cryptic failure, because zig tried to execute a command which had spaces in it rather than tokenizing it. This improves the user experience of `zig cc` for systems that Zig does not support cross-compiling for. Closes #8960
1 parent ccfa168 commit 3dc3308

File tree

1 file changed

+70
-29
lines changed

1 file changed

+70
-29
lines changed

src/libc_installation.zig

Lines changed: 70 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -237,26 +237,38 @@ pub const LibCInstallation = struct {
237237

238238
fn findNativeIncludeDirPosix(self: *LibCInstallation, args: FindNativeOptions) FindError!void {
239239
const allocator = args.allocator;
240+
241+
// Detect infinite loops.
242+
var env_map = try std.process.getEnvMap(allocator);
243+
defer env_map.deinit();
244+
const skip_cc_env_var = if (env_map.get(inf_loop_env_key)) |phase| blk: {
245+
if (std.mem.eql(u8, phase, "1")) {
246+
try env_map.put(inf_loop_env_key, "2");
247+
break :blk true;
248+
} else {
249+
return error.ZigIsTheCCompiler;
250+
}
251+
} else blk: {
252+
try env_map.put(inf_loop_env_key, "1");
253+
break :blk false;
254+
};
255+
240256
const dev_null = if (is_windows) "nul" else "/dev/null";
241-
const cc_exe = std.os.getenvZ("CC") orelse default_cc_exe;
242-
const argv = [_][]const u8{
243-
cc_exe,
257+
258+
var argv = std.ArrayList([]const u8).init(allocator);
259+
defer argv.deinit();
260+
261+
try appendCcExe(&argv, skip_cc_env_var);
262+
try argv.appendSlice(&.{
244263
"-E",
245264
"-Wp,-v",
246265
"-xc",
247266
dev_null,
248-
};
249-
var env_map = try std.process.getEnvMap(allocator);
250-
defer env_map.deinit();
251-
252-
// Detect infinite loops.
253-
const inf_loop_env_key = "ZIG_IS_DETECTING_LIBC_PATHS";
254-
if (env_map.get(inf_loop_env_key) != null) return error.ZigIsTheCCompiler;
255-
try env_map.put(inf_loop_env_key, "1");
267+
});
256268

257269
const exec_res = std.ChildProcess.exec(.{
258270
.allocator = allocator,
259-
.argv = &argv,
271+
.argv = argv.items,
260272
.max_output_bytes = 1024 * 1024,
261273
.env_map = &env_map,
262274
// Some C compilers, such as Clang, are known to rely on argv[0] to find the path
@@ -267,7 +279,7 @@ pub const LibCInstallation = struct {
267279
}) catch |err| switch (err) {
268280
error.OutOfMemory => return error.OutOfMemory,
269281
else => {
270-
printVerboseInvocation(&argv, null, args.verbose, null);
282+
printVerboseInvocation(argv.items, null, args.verbose, null);
271283
return error.UnableToSpawnCCompiler;
272284
},
273285
};
@@ -277,11 +289,11 @@ pub const LibCInstallation = struct {
277289
}
278290
switch (exec_res.term) {
279291
.Exited => |code| if (code != 0) {
280-
printVerboseInvocation(&argv, null, args.verbose, exec_res.stderr);
292+
printVerboseInvocation(argv.items, null, args.verbose, exec_res.stderr);
281293
return error.CCompilerExitCode;
282294
},
283295
else => {
284-
printVerboseInvocation(&argv, null, args.verbose, exec_res.stderr);
296+
printVerboseInvocation(argv.items, null, args.verbose, exec_res.stderr);
285297
return error.CCompilerCrashed;
286298
},
287299
}
@@ -540,8 +552,6 @@ pub const LibCInstallation = struct {
540552
}
541553
};
542554

543-
const default_cc_exe = if (is_windows) "cc.exe" else "cc";
544-
545555
pub const CCPrintFileNameOptions = struct {
546556
allocator: *Allocator,
547557
search_basename: []const u8,
@@ -553,22 +563,33 @@ pub const CCPrintFileNameOptions = struct {
553563
fn ccPrintFileName(args: CCPrintFileNameOptions) ![:0]u8 {
554564
const allocator = args.allocator;
555565

556-
const cc_exe = std.os.getenvZ("CC") orelse default_cc_exe;
557-
const arg1 = try std.fmt.allocPrint(allocator, "-print-file-name={s}", .{args.search_basename});
558-
defer allocator.free(arg1);
559-
const argv = [_][]const u8{ cc_exe, arg1 };
560-
566+
// Detect infinite loops.
561567
var env_map = try std.process.getEnvMap(allocator);
562568
defer env_map.deinit();
569+
const skip_cc_env_var = if (env_map.get(inf_loop_env_key)) |phase| blk: {
570+
if (std.mem.eql(u8, phase, "1")) {
571+
try env_map.put(inf_loop_env_key, "2");
572+
break :blk true;
573+
} else {
574+
return error.ZigIsTheCCompiler;
575+
}
576+
} else blk: {
577+
try env_map.put(inf_loop_env_key, "1");
578+
break :blk false;
579+
};
563580

564-
// Detect infinite loops.
565-
const inf_loop_env_key = "ZIG_IS_DETECTING_LIBC_PATHS";
566-
if (env_map.get(inf_loop_env_key) != null) return error.ZigIsTheCCompiler;
567-
try env_map.put(inf_loop_env_key, "1");
581+
var argv = std.ArrayList([]const u8).init(allocator);
582+
defer argv.deinit();
583+
584+
const arg1 = try std.fmt.allocPrint(allocator, "-print-file-name={s}", .{args.search_basename});
585+
defer allocator.free(arg1);
586+
587+
try appendCcExe(&argv, skip_cc_env_var);
588+
try argv.append(arg1);
568589

569590
const exec_res = std.ChildProcess.exec(.{
570591
.allocator = allocator,
571-
.argv = &argv,
592+
.argv = argv.items,
572593
.max_output_bytes = 1024 * 1024,
573594
.env_map = &env_map,
574595
// Some C compilers, such as Clang, are known to rely on argv[0] to find the path
@@ -586,11 +607,11 @@ fn ccPrintFileName(args: CCPrintFileNameOptions) ![:0]u8 {
586607
}
587608
switch (exec_res.term) {
588609
.Exited => |code| if (code != 0) {
589-
printVerboseInvocation(&argv, args.search_basename, args.verbose, exec_res.stderr);
610+
printVerboseInvocation(argv.items, args.search_basename, args.verbose, exec_res.stderr);
590611
return error.CCompilerExitCode;
591612
},
592613
else => {
593-
printVerboseInvocation(&argv, args.search_basename, args.verbose, exec_res.stderr);
614+
printVerboseInvocation(argv.items, args.search_basename, args.verbose, exec_res.stderr);
594615
return error.CCompilerCrashed;
595616
},
596617
}
@@ -659,3 +680,23 @@ fn fillSearch(search_buf: *[2]Search, sdk: *ZigWindowsSDK) []Search {
659680
}
660681
return search_buf[0..search_end];
661682
}
683+
684+
const inf_loop_env_key = "ZIG_IS_DETECTING_LIBC_PATHS";
685+
686+
fn appendCcExe(args: *std.ArrayList([]const u8), skip_cc_env_var: bool) !void {
687+
const default_cc_exe = if (is_windows) "cc.exe" else "cc";
688+
try args.ensureUnusedCapacity(1);
689+
if (skip_cc_env_var) {
690+
args.appendAssumeCapacity(default_cc_exe);
691+
return;
692+
}
693+
const cc_env_var = std.os.getenvZ("CC") orelse {
694+
args.appendAssumeCapacity(default_cc_exe);
695+
return;
696+
};
697+
// Respect space-separated flags to the C compiler.
698+
var it = std.mem.tokenize(cc_env_var, " ");
699+
while (it.next()) |arg| {
700+
try args.append(arg);
701+
}
702+
}

0 commit comments

Comments
 (0)