| ... | ... | @@ -1717,7 +1717,7 @@ fn runCommand( |
| 1717 | 1717 | has_side_effects: bool, |
| 1718 | 1718 | output_dir_path: []const u8, |
| 1719 | 1719 | fuzz_context: ?FuzzContext, |
| 1720 | | ) !void { |
| 1720 | ) Step.ExtendedMakeError!void { |
| 1721 | 1721 | const graph = maker.graph; |
| 1722 | 1722 | const arena = graph.arena; // TODO don't leak into process arena |
| 1723 | 1723 | const gpa = maker.gpa; |
| ... | ... | @@ -1757,8 +1757,6 @@ fn runCommand( |
| 1757 | 1757 | } |
| 1758 | 1758 | try graph.handleVerbose(cwd, environ_map, argv); |
| 1759 | 1759 | |
| 1760 | | if (true) @panic("TODO"); |
| 1761 | | |
| 1762 | 1760 | const opt_generic_result = spawnChildAndCollect( |
| 1763 | 1761 | run_index, |
| 1764 | 1762 | run, |
| ... | ... | @@ -1777,22 +1775,33 @@ fn runCommand( |
| 1777 | 1775 | // relying on it being a Compile step. This will make this logic |
| 1778 | 1776 | // work even for the edge case that the binary was produced by a |
| 1779 | 1777 | // third party. |
| 1780 | | const exe = switch (run.argv.items[0]) { |
| 1781 | | .artifact => |exe| exe.artifact, |
| 1782 | | else => break :interpret, |
| 1783 | | }; |
| 1784 | | switch (exe.kind) { |
| 1778 | const arg0 = conf_run.args.slice[0].get(conf); |
| 1779 | const producer_index = arg0.producer.value orelse break :interpret; |
| 1780 | const producer_step = producer_index.ptr(conf); |
| 1781 | const producer = producer_step.extended.get(conf.extra).compile; |
| 1782 | switch (producer.flags3.kind) { |
| 1785 | 1783 | .exe, .@"test" => {}, |
| 1786 | 1784 | else => break :interpret, |
| 1787 | 1785 | } |
| 1786 | const root_module = producer.root_module.get(conf); |
| 1787 | const root_module_target = root_module.resolved_target.get(conf).?.result.get(conf); |
| 1788 | const other_target_query = root_module_target.unwrap(conf); |
| 1789 | const root_target = std.zig.system.resolveTargetQuery(io, other_target_query) catch unreachable; |
| 1790 | const link_libc = maker.stepByIndex(producer_index).extended.compile.is_linking_libc; |
| 1791 | |
| 1792 | // TODO get this from the parent process instead |
| 1793 | const host: std.Target = std.zig.system.resolveTargetQuery(io, .{}) catch |he| switch (he) { |
| 1794 | error.Canceled => |e| return e, |
| 1795 | else => builtin.target, |
| 1796 | }; |
| 1788 | 1797 | |
| 1789 | | const root_target = exe.rootModuleTarget(); |
| 1790 | | const need_cross_libc = exe.is_linking_libc and |
| 1791 | | (root_target.isGnuLibC() or (root_target.isMuslLibC() and exe.linkage == .dynamic)); |
| 1792 | | const other_target = exe.root_module.resolved_target.?.result; |
| 1793 | | switch (std.zig.system.getExternalExecutor(io, &graph.host.result, &other_target, .{ |
| 1798 | const need_cross_libc = link_libc and root_target.os.tag == .linux and |
| 1799 | producer.flags2.linkage == .dynamic; |
| 1800 | switch (std.zig.system.getExternalExecutor(io, &root_target, .{ |
| 1801 | .host_cpu_arch = host.cpu.arch, |
| 1802 | .host_os_tag = host.os.tag, |
| 1794 | 1803 | .qemu_fixes_dl = need_cross_libc and graph.libc_runtimes_dir != null, |
| 1795 | | .link_libc = exe.is_linking_libc, |
| 1804 | .link_libc = link_libc, |
| 1796 | 1805 | })) { |
| 1797 | 1806 | .native, .rosetta => { |
| 1798 | 1807 | if (allow_skip) return error.MakeSkipped; |
| ... | ... | @@ -1800,8 +1809,9 @@ fn runCommand( |
| 1800 | 1809 | }, |
| 1801 | 1810 | .wine => |bin_name| { |
| 1802 | 1811 | if (graph.enable_wine) { |
| 1803 | | try interp_argv.append(bin_name); |
| 1804 | | try interp_argv.appendSlice(argv); |
| 1812 | try interp_argv.ensureUnusedCapacity(arena, 1 + argv.len); |
| 1813 | interp_argv.appendAssumeCapacity(bin_name); |
| 1814 | interp_argv.appendSliceAssumeCapacity(argv); |
| 1805 | 1815 | |
| 1806 | 1816 | // Wine's excessive stderr logging is only situationally helpful. Disable it by default, but |
| 1807 | 1817 | // allow the user to override it (e.g. with `WINEDEBUG=err+all`) if desired. |
| ... | ... | @@ -1809,17 +1819,18 @@ fn runCommand( |
| 1809 | 1819 | try environ_map.put("WINEDEBUG", "-all"); |
| 1810 | 1820 | } |
| 1811 | 1821 | } else { |
| 1812 | | return failForeign(conf_run, maker, run_index, "-fwine", argv[0], exe); |
| 1822 | return failForeign(&conf_run, maker, run_index, "-fwine", argv[0], &root_target, &host); |
| 1813 | 1823 | } |
| 1814 | 1824 | }, |
| 1815 | 1825 | .qemu => |bin_name| { |
| 1816 | 1826 | if (graph.enable_qemu) { |
| 1817 | | try interp_argv.append(bin_name); |
| 1827 | try interp_argv.ensureUnusedCapacity(arena, 3 + argv.len); |
| 1828 | interp_argv.appendAssumeCapacity(bin_name); |
| 1818 | 1829 | |
| 1819 | 1830 | if (need_cross_libc) { |
| 1820 | 1831 | if (graph.libc_runtimes_dir) |dir| { |
| 1821 | | try interp_argv.append("-L"); |
| 1822 | | try interp_argv.append(try Dir.path.join(arena, &.{ |
| 1832 | interp_argv.appendAssumeCapacity("-L"); |
| 1833 | interp_argv.appendAssumeCapacity(try Dir.path.join(arena, &.{ |
| 1823 | 1834 | dir, |
| 1824 | 1835 | try if (root_target.isGnuLibC()) std.zig.target.glibcRuntimeTriple( |
| 1825 | 1836 | arena, |
| ... | ... | @@ -1832,37 +1843,38 @@ fn runCommand( |
| 1832 | 1843 | root_target.abi, |
| 1833 | 1844 | ) else unreachable, |
| 1834 | 1845 | })); |
| 1835 | | } else return failForeign(conf_run, maker, run_index, "--libc-runtimes", argv[0], exe); |
| 1846 | } else return failForeign(&conf_run, maker, run_index, "--libc-runtimes", argv[0], &root_target, &host); |
| 1836 | 1847 | } |
| 1837 | 1848 | |
| 1838 | | try interp_argv.appendSlice(argv); |
| 1839 | | } else return failForeign(conf_run, maker, run_index, "-fqemu", argv[0], exe); |
| 1849 | interp_argv.appendSliceAssumeCapacity(argv); |
| 1850 | } else return failForeign(&conf_run, maker, run_index, "-fqemu", argv[0], &root_target, &host); |
| 1840 | 1851 | }, |
| 1841 | 1852 | .darling => |bin_name| { |
| 1842 | 1853 | if (graph.enable_darling) { |
| 1843 | | try interp_argv.append(bin_name); |
| 1844 | | try interp_argv.appendSlice(argv); |
| 1854 | try interp_argv.ensureUnusedCapacity(arena, 1 + argv.len); |
| 1855 | interp_argv.appendAssumeCapacity(bin_name); |
| 1856 | interp_argv.appendSliceAssumeCapacity(argv); |
| 1845 | 1857 | } else { |
| 1846 | | return failForeign(conf_run, maker, run_index, "-fdarling", argv[0], exe); |
| 1858 | return failForeign(&conf_run, maker, run_index, "-fdarling", argv[0], &root_target, &host); |
| 1847 | 1859 | } |
| 1848 | 1860 | }, |
| 1849 | 1861 | .wasmtime => |bin_name| { |
| 1850 | 1862 | if (graph.enable_wasmtime) { |
| 1851 | | try interp_argv.append(bin_name); |
| 1852 | | try interp_argv.append("--dir=."); |
| 1863 | try interp_argv.ensureUnusedCapacity(arena, 3 + argv.len); |
| 1864 | interp_argv.appendAssumeCapacity(bin_name); |
| 1865 | interp_argv.appendAssumeCapacity("--dir=."); |
| 1853 | 1866 | // Wasmtime doeesn't inherit environment variables from the parent process |
| 1854 | 1867 | // by default. '-S inherit-env' was added in Wasmtime version 20. |
| 1855 | | try interp_argv.append("-Sinherit-env"); |
| 1856 | | try interp_argv.append(argv[0]); |
| 1857 | | try interp_argv.appendSlice(argv[1..]); |
| 1868 | interp_argv.appendAssumeCapacity("-Sinherit-env"); |
| 1869 | interp_argv.appendSliceAssumeCapacity(argv); |
| 1858 | 1870 | } else { |
| 1859 | | return failForeign(conf_run, maker, run_index, "-fwasmtime", argv[0], exe); |
| 1871 | return failForeign(&conf_run, maker, run_index, "-fwasmtime", argv[0], &root_target, &host); |
| 1860 | 1872 | } |
| 1861 | 1873 | }, |
| 1862 | 1874 | .bad_dl => |foreign_dl| { |
| 1863 | 1875 | if (allow_skip) return error.MakeSkipped; |
| 1864 | 1876 | |
| 1865 | | const host_dl = graph.host.result.dynamic_linker.get() orelse "(none)"; |
| 1877 | const host_dl = host.dynamic_linker.get() orelse "(none)"; |
| 1866 | 1878 | |
| 1867 | 1879 | return step.fail(maker, |
| 1868 | 1880 | \\the host system is unable to execute binaries from the target |
| ... | ... | @@ -1874,7 +1886,7 @@ fn runCommand( |
| 1874 | 1886 | .bad_os_or_cpu => { |
| 1875 | 1887 | if (allow_skip) return error.MakeSkipped; |
| 1876 | 1888 | |
| 1877 | | const host_name = try graph.host.result.zigTriple(arena); |
| 1889 | const host_name = try host.zigTriple(arena); |
| 1878 | 1890 | const foreign_name = try root_target.zigTriple(arena); |
| 1879 | 1891 | |
| 1880 | 1892 | return step.fail(maker, "the host system ({s}) is unable to execute binaries from the target ({s})", .{ |
| ... | ... | @@ -1885,15 +1897,24 @@ fn runCommand( |
| 1885 | 1897 | |
| 1886 | 1898 | if (root_target.os.tag == .windows) { |
| 1887 | 1899 | // On Windows we don't have rpaths so we have to add .dll search paths to PATH |
| 1888 | | addPathForDynLibs(exe); |
| 1900 | addPathForDynLibs(producer_index); |
| 1889 | 1901 | } |
| 1890 | 1902 | |
| 1891 | 1903 | gpa.free(step.result_failed_command.?); |
| 1892 | 1904 | step.result_failed_command = null; |
| 1893 | | try graph.handleVerbose(cwd, run.environ_map, interp_argv.items); |
| 1905 | try graph.handleVerbose(cwd, environ_map, interp_argv.items); |
| 1894 | 1906 | |
| 1895 | | break :term spawnChildAndCollect(run_index, run, maker, progress_node, interp_argv.items, &environ_map, has_side_effects, fuzz_context) catch |e| { |
| 1896 | | if (!run.failing_to_execute_foreign_is_an_error) return error.MakeSkipped; |
| 1907 | break :term spawnChildAndCollect( |
| 1908 | run_index, |
| 1909 | run, |
| 1910 | maker, |
| 1911 | progress_node, |
| 1912 | interp_argv.items, |
| 1913 | environ_map, |
| 1914 | has_side_effects, |
| 1915 | fuzz_context, |
| 1916 | ) catch |e| { |
| 1917 | if (!conf_run.flags.failing_to_execute_foreign_is_an_error) return error.MakeSkipped; |
| 1897 | 1918 | if (e == error.MakeFailed) return error.MakeFailed; // error already reported |
| 1898 | 1919 | return step.fail(maker, "unable to spawn interpreter {s}: {t}", .{ interp_argv.items[0], e }); |
| 1899 | 1920 | }; |
| ... | ... | @@ -1919,47 +1940,51 @@ fn runCommand( |
| 1919 | 1940 | const Stream = struct { |
| 1920 | 1941 | captured: ?Configuration.Step.Run.CapturedStream, |
| 1921 | 1942 | bytes: ?[]const u8, |
| 1943 | trim_whitespace: Configuration.Step.Run.TrimWhitespace, |
| 1922 | 1944 | }; |
| 1923 | | for ([_]Stream{ |
| 1945 | for (&[_]Stream{ |
| 1924 | 1946 | .{ |
| 1925 | 1947 | .captured = conf_run.captured_stdout.value, |
| 1926 | 1948 | .bytes = generic_result.stdout, |
| 1949 | .trim_whitespace = conf_run.flags.stdout_trim_whitespace, |
| 1927 | 1950 | }, |
| 1928 | 1951 | .{ |
| 1929 | 1952 | .captured = conf_run.captured_stderr.value, |
| 1930 | 1953 | .bytes = generic_result.stderr, |
| 1954 | .trim_whitespace = conf_run.flags.stderr_trim_whitespace, |
| 1931 | 1955 | }, |
| 1932 | | }) |stream| { |
| 1956 | }) |*stream| { |
| 1933 | 1957 | if (stream.captured) |captured| { |
| 1934 | | const output_components = .{ output_dir_path, captured.output.basename }; |
| 1935 | | const output_path = try cache_root.join(arena, &output_components); |
| 1936 | | captured.output.generated_file.path = output_path; |
| 1937 | | |
| 1938 | | const sub_path = try Dir.path.join(arena, &output_components); |
| 1939 | | const sub_path_dirname = Dir.path.dirname(sub_path).?; |
| 1940 | | cache_root.handle.createDirPath(io, sub_path_dirname) catch |err| { |
| 1941 | | return step.fail(maker, "unable to make path '{f}{s}': {t}", .{ |
| 1942 | | cache_root, sub_path_dirname, err, |
| 1943 | | }); |
| 1958 | const output_path: Path = .{ |
| 1959 | .root_dir = cache_root, |
| 1960 | .sub_path = try Dir.path.join(arena, &.{ |
| 1961 | output_dir_path, captured.basename.slice(conf), |
| 1962 | }), |
| 1944 | 1963 | }; |
| 1945 | | const data = switch (captured.trim_whitespace) { |
| 1964 | maker.generatedPath(captured.generated_file).* = output_path; |
| 1965 | |
| 1966 | const sub_path_parent = output_path.dirname().?; |
| 1967 | sub_path_parent.root_dir.handle.createDirPath(io, sub_path_parent.sub_path) catch |err| |
| 1968 | return step.fail(maker, "unable to make path {f}: {t}", .{ sub_path_parent, err }); |
| 1969 | |
| 1970 | const data = switch (stream.trim_whitespace) { |
| 1946 | 1971 | .none => stream.bytes.?, |
| 1947 | 1972 | .all => mem.trim(u8, stream.bytes.?, &std.ascii.whitespace), |
| 1948 | 1973 | .leading => mem.trimStart(u8, stream.bytes.?, &std.ascii.whitespace), |
| 1949 | 1974 | .trailing => mem.trimEnd(u8, stream.bytes.?, &std.ascii.whitespace), |
| 1950 | 1975 | }; |
| 1951 | | cache_root.handle.writeFile(io, .{ .sub_path = sub_path, .data = data }) catch |err| { |
| 1952 | | return step.fail(maker, "unable to write file '{f}{s}': {t}", .{ |
| 1953 | | cache_root, sub_path, err, |
| 1954 | | }); |
| 1955 | | }; |
| 1976 | output_path.root_dir.handle.writeFile(io, .{ |
| 1977 | .sub_path = output_path.sub_path, |
| 1978 | .data = data, |
| 1979 | }) catch |err| return step.fail(maker, "unable to write file {f}: {t}", .{ output_path, err }); |
| 1956 | 1980 | } |
| 1957 | 1981 | } |
| 1958 | 1982 | |
| 1959 | 1983 | switch (conf_run.flags.stdio) { |
| 1960 | 1984 | .zig_test => unreachable, |
| 1961 | | .check => |checks| for (checks.items) |check| switch (check) { |
| 1962 | | .expect_stderr_exact => |expected_bytes| { |
| 1985 | .check => { |
| 1986 | if (conf_run.expect_stderr_exact.value) |bytes| { |
| 1987 | const expected_bytes = bytes.slice(conf); |
| 1963 | 1988 | if (!mem.eql(u8, expected_bytes, generic_result.stderr.?)) { |
| 1964 | 1989 | return step.fail(maker, |
| 1965 | 1990 | \\========= expected this stderr: ========= |
| ... | ... | @@ -1971,34 +1996,37 @@ fn runCommand( |
| 1971 | 1996 | generic_result.stderr.?, |
| 1972 | 1997 | }); |
| 1973 | 1998 | } |
| 1974 | | }, |
| 1975 | | .expect_stderr_match => |match| { |
| 1976 | | if (mem.find(u8, generic_result.stderr.?, match) == null) { |
| 1999 | } |
| 2000 | if (conf_run.expect_stdout_exact.value) |bytes| { |
| 2001 | const expected_bytes = bytes.slice(conf); |
| 2002 | if (!mem.eql(u8, expected_bytes, generic_result.stdout.?)) { |
| 1977 | 2003 | return step.fail(maker, |
| 1978 | | \\========= expected to find in stderr: ========= |
| 2004 | \\========= expected this stdout: ========= |
| 1979 | 2005 | \\{s} |
| 1980 | | \\========= but stderr does not contain it: ===== |
| 2006 | \\========= but found: ==================== |
| 1981 | 2007 | \\{s} |
| 1982 | 2008 | , .{ |
| 1983 | | match, |
| 1984 | | generic_result.stderr.?, |
| 2009 | expected_bytes, |
| 2010 | generic_result.stdout.?, |
| 1985 | 2011 | }); |
| 1986 | 2012 | } |
| 1987 | | }, |
| 1988 | | .expect_stdout_exact => |expected_bytes| { |
| 1989 | | if (!mem.eql(u8, expected_bytes, generic_result.stdout.?)) { |
| 2013 | } |
| 2014 | for (conf_run.expect_stderr_match.slice) |bytes| { |
| 2015 | const match = bytes.slice(conf); |
| 2016 | if (mem.find(u8, generic_result.stderr.?, match) == null) { |
| 1990 | 2017 | return step.fail(maker, |
| 1991 | | \\========= expected this stdout: ========= |
| 2018 | \\========= expected to find in stderr: ========= |
| 1992 | 2019 | \\{s} |
| 1993 | | \\========= but found: ==================== |
| 2020 | \\========= but stderr does not contain it: ===== |
| 1994 | 2021 | \\{s} |
| 1995 | 2022 | , .{ |
| 1996 | | expected_bytes, |
| 1997 | | generic_result.stdout.?, |
| 2023 | match, |
| 2024 | generic_result.stderr.?, |
| 1998 | 2025 | }); |
| 1999 | 2026 | } |
| 2000 | | }, |
| 2001 | | .expect_stdout_match => |match| { |
| 2027 | } |
| 2028 | for (conf_run.expect_stdout_match.slice) |bytes| { |
| 2029 | const match = bytes.slice(conf); |
| 2002 | 2030 | if (mem.find(u8, generic_result.stdout.?, match) == null) { |
| 2003 | 2031 | return step.fail(maker, |
| 2004 | 2032 | \\========= expected to find in stdout: ========= |
| ... | ... | @@ -2010,15 +2038,21 @@ fn runCommand( |
| 2010 | 2038 | generic_result.stdout.?, |
| 2011 | 2039 | }); |
| 2012 | 2040 | } |
| 2013 | | }, |
| 2014 | | .expect_term => |expected_term| { |
| 2041 | } |
| 2042 | if (conf_run.expect_term_value.value) |expected_term_value| { |
| 2043 | const expected_term: process.Child.Term = switch (conf_run.flags2.expect_term_status) { |
| 2044 | .exited => .{ .exited = @intCast(expected_term_value) }, |
| 2045 | .signal => .{ .signal = @enumFromInt(expected_term_value) }, |
| 2046 | .stopped => .{ .stopped = @enumFromInt(expected_term_value) }, |
| 2047 | .unknown => .{ .unknown = expected_term_value }, |
| 2048 | }; |
| 2015 | 2049 | if (!termMatches(expected_term, generic_result.term)) { |
| 2016 | 2050 | return step.fail(maker, "process {f} (expected {f})", .{ |
| 2017 | 2051 | fmtTerm(generic_result.term), |
| 2018 | 2052 | fmtTerm(expected_term), |
| 2019 | 2053 | }); |
| 2020 | 2054 | } |
| 2021 | | }, |
| 2055 | } |
| 2022 | 2056 | }, |
| 2023 | 2057 | else => { |
| 2024 | 2058 | // On failure, report captured stderr like normal standard error output. |
| ... | ... | @@ -2032,7 +2066,7 @@ fn runCommand( |
| 2032 | 2066 | } |
| 2033 | 2067 | } |
| 2034 | 2068 | |
| 2035 | | try step.handleChildProcessTerm(generic_result.term); |
| 2069 | try step.handleChildProcessTerm(maker, generic_result.term); |
| 2036 | 2070 | }, |
| 2037 | 2071 | } |
| 2038 | 2072 | } |
| ... | ... | @@ -2256,7 +2290,8 @@ fn failForeign( |
| 2256 | 2290 | step_index: Configuration.Step.Index, |
| 2257 | 2291 | suggested_flag: []const u8, |
| 2258 | 2292 | argv0: []const u8, |
| 2259 | | exe: *Step.Compile, |
| 2293 | artifact_target: *const std.Target, |
| 2294 | host_target: *const std.Target, |
| 2260 | 2295 | ) Step.ExtendedMakeError { |
| 2261 | 2296 | const step = maker.stepByIndex(step_index); |
| 2262 | 2297 | switch (conf_run.flags.stdio) { |
| ... | ... | @@ -2265,8 +2300,8 @@ fn failForeign( |
| 2265 | 2300 | |
| 2266 | 2301 | const graph = maker.graph; |
| 2267 | 2302 | const process_arena = graph.arena; // TODO don't leak into process arena |
| 2268 | | const host_name = try graph.host.result.zigTriple(process_arena); |
| 2269 | | const foreign_name = try exe.rootModuleTarget().zigTriple(process_arena); |
| 2303 | const host_name = try host_target.zigTriple(process_arena); |
| 2304 | const foreign_name = try artifact_target.zigTriple(process_arena); |
| 2270 | 2305 | |
| 2271 | 2306 | return step.fail(maker, |
| 2272 | 2307 | \\unable to spawn foreign binary '{s}' ({s}) on host system ({s}) |