authorgravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2024-06-18 07:08:12+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-06-21 15:41:03-04:00
log9be9b8ca902bb7c250a6836c6090af8525464749
tree466679045dda3c87042f50e42e763a8cddd079f8
parent451550e86a1461e814da4eea22f78c6a77ab9308

std.Build.findProgram(): Try with and without the Windows executable extensions.

I renamed std.process.Child.CreateProcessSupportedExtension to WindowsExtension and made it public to avoid duplicating the list of extensions. While here, I also improved it to not misreport OOM from std.fs.realpathAlloc() as a generic failure to find the program, but instead panic like the rest of the build system does for OOM. Closes #20314.

2 files changed, 42 insertions(+), 20 deletions(-)

lib/std/Build.zig+36-15
...@@ -1727,20 +1727,47 @@ pub fn fmt(b: *Build, comptime format: []const u8, args: anytype) []u8 {...@@ -1727,20 +1727,47 @@ pub fn fmt(b: *Build, comptime format: []const u8, args: anytype) []u8 {
1727 return std.fmt.allocPrint(b.allocator, format, args) catch @panic("OOM");1727 return std.fmt.allocPrint(b.allocator, format, args) catch @panic("OOM");
1728}1728}
17291729
1730fn supportedWindowsProgramExtension(ext: []const u8) bool {
1731 inline for (@typeInfo(std.process.Child.WindowsExtension).Enum.fields) |field| {
1732 if (std.ascii.eqlIgnoreCase(ext, "." ++ field.name)) return true;
1733 }
1734 return false;
1735}
1736
1737fn tryFindProgram(b: *Build, full_path: []const u8) ?[]const u8 {
1738 if (fs.realpathAlloc(b.allocator, full_path)) |p| {
1739 return p;
1740 } else |err| switch (err) {
1741 error.OutOfMemory => @panic("OOM"),
1742 else => {},
1743 }
1744
1745 if (builtin.os.tag == .windows) {
1746 if (b.graph.env_map.get("PATHEXT")) |PATHEXT| {
1747 var it = mem.tokenizeScalar(u8, PATHEXT, fs.path.delimiter);
1748
1749 while (it.next()) |ext| {
1750 if (!supportedWindowsProgramExtension(ext)) continue;
1751
1752 return fs.realpathAlloc(b.allocator, b.fmt("{s}{s}", .{ full_path, ext })) catch |err| switch (err) {
1753 error.OutOfMemory => @panic("OOM"),
1754 else => continue,
1755 };
1756 }
1757 }
1758 }
1759
1760 return null;
1761}
1762
1730pub fn findProgram(b: *Build, names: []const []const u8, paths: []const []const u8) ![]const u8 {1763pub fn findProgram(b: *Build, names: []const []const u8, paths: []const []const u8) ![]const u8 {
1731 // TODO report error for ambiguous situations1764 // TODO report error for ambiguous situations
1732 const exe_extension = b.graph.host.result.exeFileExt();
1733 for (b.search_prefixes.items) |search_prefix| {1765 for (b.search_prefixes.items) |search_prefix| {
1734 for (names) |name| {1766 for (names) |name| {
1735 if (fs.path.isAbsolute(name)) {1767 if (fs.path.isAbsolute(name)) {
1736 return name;1768 return name;
1737 }1769 }
1738 const full_path = b.pathJoin(&.{1770 return tryFindProgram(b, b.pathJoin(&.{ search_prefix, "bin", name })) orelse continue;
1739 search_prefix,
1740 "bin",
1741 b.fmt("{s}{s}", .{ name, exe_extension }),
1742 });
1743 return fs.realpathAlloc(b.allocator, full_path) catch continue;
1744 }1771 }
1745 }1772 }
1746 if (b.graph.env_map.get("PATH")) |PATH| {1773 if (b.graph.env_map.get("PATH")) |PATH| {
...@@ -1750,10 +1777,7 @@ pub fn findProgram(b: *Build, names: []const []const u8, paths: []const []const...@@ -1750,10 +1777,7 @@ pub fn findProgram(b: *Build, names: []const []const u8, paths: []const []const
1750 }1777 }
1751 var it = mem.tokenizeScalar(u8, PATH, fs.path.delimiter);1778 var it = mem.tokenizeScalar(u8, PATH, fs.path.delimiter);
1752 while (it.next()) |p| {1779 while (it.next()) |p| {
1753 const full_path = b.pathJoin(&.{1780 return tryFindProgram(b, b.pathJoin(&.{ p, name })) orelse continue;
1754 p, b.fmt("{s}{s}", .{ name, exe_extension }),
1755 });
1756 return fs.realpathAlloc(b.allocator, full_path) catch continue;
1757 }1781 }
1758 }1782 }
1759 }1783 }
...@@ -1762,10 +1786,7 @@ pub fn findProgram(b: *Build, names: []const []const u8, paths: []const []const...@@ -1762,10 +1786,7 @@ pub fn findProgram(b: *Build, names: []const []const u8, paths: []const []const
1762 return name;1786 return name;
1763 }1787 }
1764 for (paths) |p| {1788 for (paths) |p| {
1765 const full_path = b.pathJoin(&.{1789 return tryFindProgram(b, b.pathJoin(&.{ p, name })) orelse continue;
1766 p, b.fmt("{s}{s}", .{ name, exe_extension }),
1767 });
1768 return fs.realpathAlloc(b.allocator, full_path) catch continue;
1769 }1790 }
1770 }1791 }
1771 return error.FileNotFound;1792 return error.FileNotFound;
lib/std/process/Child.zig+6-5
...@@ -1103,7 +1103,7 @@ fn windowsCreateProcessPathExt(...@@ -1103,7 +1103,7 @@ fn windowsCreateProcessPathExt(
1103 }1103 }
1104 var io_status: windows.IO_STATUS_BLOCK = undefined;1104 var io_status: windows.IO_STATUS_BLOCK = undefined;
11051105
1106 const num_supported_pathext = @typeInfo(CreateProcessSupportedExtension).Enum.fields.len;1106 const num_supported_pathext = @typeInfo(WindowsExtension).Enum.fields.len;
1107 var pathext_seen = [_]bool{false} ** num_supported_pathext;1107 var pathext_seen = [_]bool{false} ** num_supported_pathext;
1108 var any_pathext_seen = false;1108 var any_pathext_seen = false;
1109 var unappended_exists = false;1109 var unappended_exists = false;
...@@ -1389,8 +1389,9 @@ fn windowsMakeAsyncPipe(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *cons...@@ -1389,8 +1389,9 @@ fn windowsMakeAsyncPipe(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *cons
13891389
1390var pipe_name_counter = std.atomic.Value(u32).init(1);1390var pipe_name_counter = std.atomic.Value(u32).init(1);
13911391
1392// Should be kept in sync with `windowsCreateProcessSupportsExtension`1392/// File name extensions supported natively by `CreateProcess()` on Windows.
1393const CreateProcessSupportedExtension = enum {1393// Should be kept in sync with `windowsCreateProcessSupportsExtension`.
1394pub const WindowsExtension = enum {
1394 bat,1395 bat,
1395 cmd,1396 cmd,
1396 com,1397 com,
...@@ -1398,7 +1399,7 @@ const CreateProcessSupportedExtension = enum {...@@ -1398,7 +1399,7 @@ const CreateProcessSupportedExtension = enum {
1398};1399};
13991400
1400/// Case-insensitive WTF-16 lookup1401/// Case-insensitive WTF-16 lookup
1401fn windowsCreateProcessSupportsExtension(ext: []const u16) ?CreateProcessSupportedExtension {1402fn windowsCreateProcessSupportsExtension(ext: []const u16) ?WindowsExtension {
1402 if (ext.len != 4) return null;1403 if (ext.len != 4) return null;
1403 const State = enum {1404 const State = enum {
1404 start,1405 start,
...@@ -1457,7 +1458,7 @@ fn windowsCreateProcessSupportsExtension(ext: []const u16) ?CreateProcessSupport...@@ -1457,7 +1458,7 @@ fn windowsCreateProcessSupportsExtension(ext: []const u16) ?CreateProcessSupport
1457}1458}
14581459
1459test windowsCreateProcessSupportsExtension {1460test windowsCreateProcessSupportsExtension {
1460 try std.testing.expectEqual(CreateProcessSupportedExtension.exe, windowsCreateProcessSupportsExtension(&[_]u16{ '.', 'e', 'X', 'e' }).?);1461 try std.testing.expectEqual(WindowsExtension.exe, windowsCreateProcessSupportsExtension(&[_]u16{ '.', 'e', 'X', 'e' }).?);
1461 try std.testing.expect(windowsCreateProcessSupportsExtension(&[_]u16{ '.', 'e', 'X', 'e', 'c' }) == null);1462 try std.testing.expect(windowsCreateProcessSupportsExtension(&[_]u16{ '.', 'e', 'X', 'e', 'c' }) == null);
1462}1463}
14631464