Commit 0cd8710222

Andrew Kelley <andrew@ziglang.org>
2021-12-03 00:28:10
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.
1 parent b24cbec
Changed files (2)
lib
src
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("-L");
-                    try zig_args.append("--test-cmd");
-                    try zig_args.append(full_dir);
+                    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-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");
             },
         }
 
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;
+        };
+    }
+}