Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

std.Build.findProgram(): Try with and without the executable extension. #20337

Merged
merged 1 commit into from
Jun 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading