authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-06-08 12:48:48-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-06-10 10:20:52-07:00
logc230258d9b53e98d49017588b839de5635ab7640
treebb4f0368c6a7d86dfa7b5ec630443f8ec683a07a
parent2d0dff6498d7d4fa8b7eb229579aaf54729338e7

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 files changed, 70 insertions(+), 29 deletions(-)

src/libc_installation.zig+70-29
......@@ -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
543const default_cc_exe = if (is_windows) "cc.exe" else "cc";
544
545555pub const CCPrintFileNameOptions = struct {
546556 allocator: *Allocator,
547557 search_basename: []const u8,
......@@ -553,22 +563,33 @@ pub const CCPrintFileNameOptions = struct {
553563fn 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
684const inf_loop_env_key = "ZIG_IS_DETECTING_LIBC_PATHS";
685
686fn 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}