From 0cd87102221233c2885faccfcaaac297b8d3b656 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 2 Dec 2021 16:28:10 -0700 Subject: [PATCH] CLI: always try to exec binaries Previously when using `zig run` or `zig test`, zig would try to guess whether the host system was capable of running the target binaries. Now, it will always try. If it fails, then Zig emits a helpful warning to explain the probable cause. --- lib/std/build.zig | 71 ++++++++++++++++++++++++++++------------------- src/main.zig | 66 ++++++++++++++++++++++++++++++------------- 2 files changed, 90 insertions(+), 47 deletions(-) diff --git a/lib/std/build.zig b/lib/std/build.zig index 041510bb02..75e6db301e 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -2527,45 +2527,56 @@ pub const LibExeObjStep = struct { } } } else switch (self.target.getExternalExecutor()) { - .native, .unavailable => {}, + .native => {}, + .unavailable => { + try zig_args.append("--test-no-exec"); + }, .rosetta => if (builder.enable_rosetta) { try zig_args.append("--test-cmd-bin"); + } else { + try zig_args.append("--test-no-exec"); }, - .qemu => |bin_name| if (builder.enable_qemu) qemu: { - const need_cross_glibc = self.target.isGnuLibC() and self.is_linking_libc; - const glibc_dir_arg = if (need_cross_glibc) - builder.glibc_runtimes_dir orelse break :qemu - else - null; - try zig_args.append("--test-cmd"); - try zig_args.append(bin_name); - if (glibc_dir_arg) |dir| { - // TODO look into making this a call to `linuxTriple`. This - // needs the directory to be called "i686" rather than - // "i386" which is why we do it manually here. - const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}"; - const cpu_arch = self.target.getCpuArch(); - const os_tag = self.target.getOsTag(); - const abi = self.target.getAbi(); - const cpu_arch_name: []const u8 = if (cpu_arch == .i386) - "i686" + .qemu => |bin_name| ok: { + if (builder.enable_qemu) qemu: { + const need_cross_glibc = self.target.isGnuLibC() and self.is_linking_libc; + const glibc_dir_arg = if (need_cross_glibc) + builder.glibc_runtimes_dir orelse break :qemu else - @tagName(cpu_arch); - const full_dir = try std.fmt.allocPrint(builder.allocator, fmt_str, .{ - dir, cpu_arch_name, @tagName(os_tag), @tagName(abi), - }); + null; + try zig_args.append("--test-cmd"); + try zig_args.append(bin_name); + if (glibc_dir_arg) |dir| { + // TODO look into making this a call to `linuxTriple`. This + // needs the directory to be called "i686" rather than + // "i386" which is why we do it manually here. + const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}"; + const cpu_arch = self.target.getCpuArch(); + const os_tag = self.target.getOsTag(); + const abi = self.target.getAbi(); + const cpu_arch_name: []const u8 = if (cpu_arch == .i386) + "i686" + else + @tagName(cpu_arch); + const full_dir = try std.fmt.allocPrint(builder.allocator, fmt_str, .{ + dir, cpu_arch_name, @tagName(os_tag), @tagName(abi), + }); - try zig_args.append("--test-cmd"); - try zig_args.append("-L"); - try zig_args.append("--test-cmd"); - try zig_args.append(full_dir); + try zig_args.append("--test-cmd"); + try zig_args.append("-L"); + try zig_args.append("--test-cmd"); + try zig_args.append(full_dir); + } + try zig_args.append("--test-cmd-bin"); + break :ok; } - try zig_args.append("--test-cmd-bin"); + try zig_args.append("--test-no-exec"); }, .wine => |bin_name| if (builder.enable_wine) { try zig_args.append("--test-cmd"); try zig_args.append(bin_name); try zig_args.append("--test-cmd-bin"); + } else { + try zig_args.append("--test-no-exec"); }, .wasmtime => |bin_name| if (builder.enable_wasmtime) { try zig_args.append("--test-cmd"); @@ -2573,11 +2584,15 @@ pub const LibExeObjStep = struct { try zig_args.append("--test-cmd"); try zig_args.append("--dir=."); try zig_args.append("--test-cmd-bin"); + } else { + try zig_args.append("--test-no-exec"); }, .darling => |bin_name| if (builder.enable_darling) { try zig_args.append("--test-cmd"); try zig_args.append(bin_name); try zig_args.append("--test-cmd-bin"); + } else { + try zig_args.append("--test-no-exec"); }, } diff --git a/src/main.zig b/src/main.zig index 5fd87b8703..3b5f10685f 100644 --- a/src/main.zig +++ b/src/main.zig @@ -2534,7 +2534,7 @@ fn buildOutputType( test_exec_args.items, self_exe_path, arg_mode, - target_info.target, + target_info, watch, &comp_destroyed, all_args, @@ -2606,7 +2606,7 @@ fn buildOutputType( test_exec_args.items, self_exe_path, arg_mode, - target_info.target, + target_info, watch, &comp_destroyed, all_args, @@ -2631,7 +2631,7 @@ fn buildOutputType( test_exec_args.items, self_exe_path, arg_mode, - target_info.target, + target_info, watch, &comp_destroyed, all_args, @@ -2695,7 +2695,7 @@ fn runOrTest( test_exec_args: []const ?[]const u8, self_exe_path: []const u8, arg_mode: ArgMode, - target: std.Target, + target_info: std.zig.system.NativeTargetInfo, watch: bool, comp_destroyed: *bool, all_args: []const []const u8, @@ -2711,20 +2711,6 @@ fn runOrTest( defer argv.deinit(); if (test_exec_args.len == 0) { - if (!builtin.target.canExecBinariesOf(target)) { - switch (arg_mode) { - .zig_test => { - warn("created {s} but skipping execution because it is non-native", .{exe_path}); - if (!watch) return cleanExit(); - return; - }, - else => { - std.log.err("unable to execute {s}: non-native", .{exe_path}); - if (!watch) process.exit(1); - return; - }, - } - } // when testing pass the zig_exe_path to argv if (arg_mode == .zig_test) try argv.appendSlice(&[_][]const u8{ @@ -2754,6 +2740,7 @@ fn runOrTest( if (std.process.can_execv and arg_mode == .run and !watch) { // execv releases the locks; no need to destroy the Compilation here. const err = std.process.execv(gpa, argv.items); + try warnAboutForeignBinaries(gpa, arena, arg_mode, target_info); const cmd = try argvCmd(arena, argv.items); fatal("the following command failed to execve with '{s}':\n{s}", .{ @errorName(err), cmd }); } else { @@ -2771,7 +2758,11 @@ fn runOrTest( comp_destroyed.* = true; } - const term = try child.spawnAndWait(); + const term = child.spawnAndWait() catch |err| { + try warnAboutForeignBinaries(gpa, arena, arg_mode, target_info); + const cmd = try argvCmd(arena, argv.items); + fatal("the following command failed with '{s}':\n{s}", .{ @errorName(err), cmd }); + }; switch (arg_mode) { .run, .build => { switch (term) { @@ -4665,3 +4656,40 @@ fn parseIntSuffix(arg: []const u8, prefix_len: usize) u64 { fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); }; } + +fn warnAboutForeignBinaries( + gpa: Allocator, + arena: Allocator, + arg_mode: ArgMode, + target_info: std.zig.system.NativeTargetInfo, +) !void { + const host_cross_target: std.zig.CrossTarget = .{}; + const host_target_info = try detectNativeTargetInfo(gpa, host_cross_target); + + if (!host_target_info.target.canExecBinariesOf(target_info.target)) { + const host_name = try host_target_info.target.zigTriple(arena); + const foreign_name = try target_info.target.zigTriple(arena); + const tip_suffix = switch (arg_mode) { + .zig_test => ". Consider using --test-no-exec or --test-cmd", + else => "", + }; + warn("the host system ({s}) does not appear to be capable of executing binaries from the target ({s}){s}", .{ + host_name, foreign_name, tip_suffix, + }); + return; + } + + if (target_info.dynamic_linker.get()) |foreign_dl| { + std.fs.cwd().access(foreign_dl, .{}) catch { + const host_dl = host_target_info.dynamic_linker.get() orelse "(none)"; + const tip_suffix = switch (arg_mode) { + .zig_test => ", --test-no-exec, or --test-cmd", + else => "", + }; + warn("the host system does not appear to be capable of executing binaries from the target because the host dynamic linker is located at '{s}', while the target dynamic linker path is '{s}'. Consider using --dynamic-linker{s}", .{ + host_dl, foreign_dl, tip_suffix, + }); + return; + }; + } +}