Skip to content
/ zig Public
forked from ziglang/zig

Commit

Permalink
std.Build.findProgram(): Try with and without the Windows executable …
Browse files Browse the repository at this point in the history
…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 ziglang#20314.
  • Loading branch information
alexrp authored and andrewrk committed Jun 21, 2024
1 parent 451550e commit 9be9b8c
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 20 deletions.
51 changes: 36 additions & 15 deletions lib/std/Build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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| {
Expand All @@ -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;
}
}
}
Expand All @@ -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;
Expand Down
11 changes: 6 additions & 5 deletions lib/std/process/Child.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -1389,16 +1389,17 @@ 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,
exe,
};

/// 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,
Expand Down Expand Up @@ -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);
}

Expand Down

0 comments on commit 9be9b8c

Please sign in to comment.