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.
This commit is contained in:
Alex Rønne Petersen 2024-06-18 07:08:12 +02:00 committed by Andrew Kelley
parent 451550e86a
commit 9be9b8ca90
2 changed files with 42 additions and 20 deletions

View File

@ -1727,20 +1727,47 @@ pub fn fmt(b: *Build, comptime format: []const u8, args: anytype) []u8 {
return std.fmt.allocPrint(b.allocator, format, args) catch @panic("OOM");
}
fn supportedWindowsProgramExtension(ext: []const u8) bool {
inline for (@typeInfo(std.process.Child.WindowsExtension).Enum.fields) |field| {
if (std.ascii.eqlIgnoreCase(ext, "." ++ field.name)) return true;
}
return false;
}
fn tryFindProgram(b: *Build, full_path: []const u8) ?[]const u8 {
if (fs.realpathAlloc(b.allocator, full_path)) |p| {
return p;
} else |err| switch (err) {
error.OutOfMemory => @panic("OOM"),
else => {},
}
if (builtin.os.tag == .windows) {
if (b.graph.env_map.get("PATHEXT")) |PATHEXT| {
var it = mem.tokenizeScalar(u8, PATHEXT, fs.path.delimiter);
while (it.next()) |ext| {
if (!supportedWindowsProgramExtension(ext)) continue;
return fs.realpathAlloc(b.allocator, b.fmt("{s}{s}", .{ full_path, ext })) catch |err| switch (err) {
error.OutOfMemory => @panic("OOM"),
else => continue,
};
}
}
}
return null;
}
pub fn findProgram(b: *Build, names: []const []const u8, paths: []const []const u8) ![]const u8 {
// TODO report error for ambiguous situations
const exe_extension = b.graph.host.result.exeFileExt();
for (b.search_prefixes.items) |search_prefix| {
for (names) |name| {
if (fs.path.isAbsolute(name)) {
return name;
}
const full_path = b.pathJoin(&.{
search_prefix,
"bin",
b.fmt("{s}{s}", .{ name, exe_extension }),
});
return fs.realpathAlloc(b.allocator, full_path) catch continue;
return tryFindProgram(b, b.pathJoin(&.{ search_prefix, "bin", name })) orelse continue;
}
}
if (b.graph.env_map.get("PATH")) |PATH| {
@ -1750,10 +1777,7 @@ pub fn findProgram(b: *Build, names: []const []const u8, paths: []const []const
}
var it = mem.tokenizeScalar(u8, PATH, fs.path.delimiter);
while (it.next()) |p| {
const full_path = b.pathJoin(&.{
p, b.fmt("{s}{s}", .{ name, exe_extension }),
});
return fs.realpathAlloc(b.allocator, full_path) catch continue;
return tryFindProgram(b, b.pathJoin(&.{ p, name })) orelse continue;
}
}
}
@ -1762,10 +1786,7 @@ pub fn findProgram(b: *Build, names: []const []const u8, paths: []const []const
return name;
}
for (paths) |p| {
const full_path = b.pathJoin(&.{
p, b.fmt("{s}{s}", .{ name, exe_extension }),
});
return fs.realpathAlloc(b.allocator, full_path) catch continue;
return tryFindProgram(b, b.pathJoin(&.{ p, name })) orelse continue;
}
}
return error.FileNotFound;

View File

@ -1103,7 +1103,7 @@ fn windowsCreateProcessPathExt(
}
var io_status: windows.IO_STATUS_BLOCK = undefined;
const num_supported_pathext = @typeInfo(CreateProcessSupportedExtension).Enum.fields.len;
const num_supported_pathext = @typeInfo(WindowsExtension).Enum.fields.len;
var pathext_seen = [_]bool{false} ** num_supported_pathext;
var any_pathext_seen = false;
var unappended_exists = false;
@ -1389,8 +1389,9 @@ fn windowsMakeAsyncPipe(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *cons
var pipe_name_counter = std.atomic.Value(u32).init(1);
// Should be kept in sync with `windowsCreateProcessSupportsExtension`
const CreateProcessSupportedExtension = enum {
/// File name extensions supported natively by `CreateProcess()` on Windows.
// Should be kept in sync with `windowsCreateProcessSupportsExtension`.
pub const WindowsExtension = enum {
bat,
cmd,
com,
@ -1398,7 +1399,7 @@ const CreateProcessSupportedExtension = enum {
};
/// Case-insensitive WTF-16 lookup
fn windowsCreateProcessSupportsExtension(ext: []const u16) ?CreateProcessSupportedExtension {
fn windowsCreateProcessSupportsExtension(ext: []const u16) ?WindowsExtension {
if (ext.len != 4) return null;
const State = enum {
start,
@ -1457,7 +1458,7 @@ fn windowsCreateProcessSupportsExtension(ext: []const u16) ?CreateProcessSupport
}
test windowsCreateProcessSupportsExtension {
try std.testing.expectEqual(CreateProcessSupportedExtension.exe, windowsCreateProcessSupportsExtension(&[_]u16{ '.', 'e', 'X', 'e' }).?);
try std.testing.expectEqual(WindowsExtension.exe, windowsCreateProcessSupportsExtension(&[_]u16{ '.', 'e', 'X', 'e' }).?);
try std.testing.expect(windowsCreateProcessSupportsExtension(&[_]u16{ '.', 'e', 'X', 'e', 'c' }) == null);
}