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-09 17:26:58-04:00
logd128ec39df4c0a701523d7d7161df3808a6939d2
treef84736ceeae607ad26e3defe2ffbdd57a4f9455b
parent629e2e784495dd8ac91493fa7bb11e1772698e42

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 {...@@ -237,26 +237,38 @@ pub const LibCInstallation = struct {
237237
238 fn findNativeIncludeDirPosix(self: *LibCInstallation, args: FindNativeOptions) FindError!void {238 fn findNativeIncludeDirPosix(self: *LibCInstallation, args: FindNativeOptions) FindError!void {
239 const allocator = args.allocator;239 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
240 const dev_null = if (is_windows) "nul" else "/dev/null";256 const dev_null = if (is_windows) "nul" else "/dev/null";
241 const cc_exe = std.os.getenvZ("CC") orelse default_cc_exe;257
242 const argv = [_][]const u8{258 var argv = std.ArrayList([]const u8).init(allocator);
243 cc_exe,259 defer argv.deinit();
260
261 try appendCcExe(&argv, skip_cc_env_var);
262 try argv.appendSlice(&.{
244 "-E",263 "-E",
245 "-Wp,-v",264 "-Wp,-v",
246 "-xc",265 "-xc",
247 dev_null,266 dev_null,
248 };267 });
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");
256268
257 const exec_res = std.ChildProcess.exec(.{269 const exec_res = std.ChildProcess.exec(.{
258 .allocator = allocator,270 .allocator = allocator,
259 .argv = &argv,271 .argv = argv.items,
260 .max_output_bytes = 1024 * 1024,272 .max_output_bytes = 1024 * 1024,
261 .env_map = &env_map,273 .env_map = &env_map,
262 // Some C compilers, such as Clang, are known to rely on argv[0] to find the path274 // 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 {...@@ -267,7 +279,7 @@ pub const LibCInstallation = struct {
267 }) catch |err| switch (err) {279 }) catch |err| switch (err) {
268 error.OutOfMemory => return error.OutOfMemory,280 error.OutOfMemory => return error.OutOfMemory,
269 else => {281 else => {
270 printVerboseInvocation(&argv, null, args.verbose, null);282 printVerboseInvocation(argv.items, null, args.verbose, null);
271 return error.UnableToSpawnCCompiler;283 return error.UnableToSpawnCCompiler;
272 },284 },
273 };285 };
...@@ -277,11 +289,11 @@ pub const LibCInstallation = struct {...@@ -277,11 +289,11 @@ pub const LibCInstallation = struct {
277 }289 }
278 switch (exec_res.term) {290 switch (exec_res.term) {
279 .Exited => |code| if (code != 0) {291 .Exited => |code| if (code != 0) {
280 printVerboseInvocation(&argv, null, args.verbose, exec_res.stderr);292 printVerboseInvocation(argv.items, null, args.verbose, exec_res.stderr);
281 return error.CCompilerExitCode;293 return error.CCompilerExitCode;
282 },294 },
283 else => {295 else => {
284 printVerboseInvocation(&argv, null, args.verbose, exec_res.stderr);296 printVerboseInvocation(argv.items, null, args.verbose, exec_res.stderr);
285 return error.CCompilerCrashed;297 return error.CCompilerCrashed;
286 },298 },
287 }299 }
...@@ -540,8 +552,6 @@ pub const LibCInstallation = struct {...@@ -540,8 +552,6 @@ pub const LibCInstallation = struct {
540 }552 }
541};553};
542554
543const default_cc_exe = if (is_windows) "cc.exe" else "cc";
544
545pub const CCPrintFileNameOptions = struct {555pub const CCPrintFileNameOptions = struct {
546 allocator: *Allocator,556 allocator: *Allocator,
547 search_basename: []const u8,557 search_basename: []const u8,
...@@ -553,22 +563,33 @@ pub const CCPrintFileNameOptions = struct {...@@ -553,22 +563,33 @@ pub const CCPrintFileNameOptions = struct {
553fn ccPrintFileName(args: CCPrintFileNameOptions) ![:0]u8 {563fn ccPrintFileName(args: CCPrintFileNameOptions) ![:0]u8 {
554 const allocator = args.allocator;564 const allocator = args.allocator;
555565
556 const cc_exe = std.os.getenvZ("CC") orelse default_cc_exe;566 // Detect infinite loops.
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
561 var env_map = try std.process.getEnvMap(allocator);567 var env_map = try std.process.getEnvMap(allocator);
562 defer env_map.deinit();568 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.581 var argv = std.ArrayList([]const u8).init(allocator);
565 const inf_loop_env_key = "ZIG_IS_DETECTING_LIBC_PATHS";582 defer argv.deinit();
566 if (env_map.get(inf_loop_env_key) != null) return error.ZigIsTheCCompiler;583
567 try env_map.put(inf_loop_env_key, "1");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
569 const exec_res = std.ChildProcess.exec(.{590 const exec_res = std.ChildProcess.exec(.{
570 .allocator = allocator,591 .allocator = allocator,
571 .argv = &argv,592 .argv = argv.items,
572 .max_output_bytes = 1024 * 1024,593 .max_output_bytes = 1024 * 1024,
573 .env_map = &env_map,594 .env_map = &env_map,
574 // Some C compilers, such as Clang, are known to rely on argv[0] to find the path595 // 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 {...@@ -586,11 +607,11 @@ fn ccPrintFileName(args: CCPrintFileNameOptions) ![:0]u8 {
586 }607 }
587 switch (exec_res.term) {608 switch (exec_res.term) {
588 .Exited => |code| if (code != 0) {609 .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);
590 return error.CCompilerExitCode;611 return error.CCompilerExitCode;
591 },612 },
592 else => {613 else => {
593 printVerboseInvocation(&argv, args.search_basename, args.verbose, exec_res.stderr);614 printVerboseInvocation(argv.items, args.search_basename, args.verbose, exec_res.stderr);
594 return error.CCompilerCrashed;615 return error.CCompilerCrashed;
595 },616 },
596 }617 }
...@@ -659,3 +680,23 @@ fn fillSearch(search_buf: *[2]Search, sdk: *ZigWindowsSDK) []Search {...@@ -659,3 +680,23 @@ fn fillSearch(search_buf: *[2]Search, sdk: *ZigWindowsSDK) []Search {
659 }680 }
660 return search_buf[0..search_end];681 return search_buf[0..search_end];
661}682}
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}