mirror of
https://codeberg.org/ziglang/zig.git
synced 2026-05-30 04:45:22 +03:00
121 lines
4.2 KiB
Zig
121 lines
4.2 KiB
Zig
const FindProgram = @This();
|
|
const builtin = @import("builtin");
|
|
|
|
const std = @import("std");
|
|
const Io = std.Io;
|
|
const Configuration = std.Build.Configuration;
|
|
const assert = std.debug.assert;
|
|
|
|
const Step = @import("../Step.zig");
|
|
const Maker = @import("../../Maker.zig");
|
|
|
|
pub fn make(
|
|
find_program: *FindProgram,
|
|
step_index: Configuration.Step.Index,
|
|
maker: *Maker,
|
|
progress_node: std.Progress.Node,
|
|
) Step.ExtendedMakeError!void {
|
|
_ = find_program;
|
|
_ = progress_node;
|
|
const graph = maker.graph;
|
|
const step = maker.stepByIndex(step_index);
|
|
const arena = graph.arena; // TODO don't leak into the process arena
|
|
const conf = &maker.scanned_config.configuration;
|
|
const conf_step = step_index.ptr(conf);
|
|
const conf_fp = conf_step.extended.get(conf.extra).find_program;
|
|
const found_path = conf_fp.found_path;
|
|
const names = conf_fp.names.slice(conf);
|
|
|
|
// In case we fail at the end.
|
|
var err_msg: std.ArrayList(u8) = .empty;
|
|
try err_msg.appendSlice(arena, "program not found. searched paths:\n");
|
|
|
|
for (names) |name_index| {
|
|
const name = name_index.slice(conf);
|
|
|
|
if (Io.Dir.path.isAbsolute(name)) {
|
|
if (try checkCandidate(maker, step, found_path, &err_msg, name)) return;
|
|
|
|
continue;
|
|
}
|
|
|
|
for (graph.search_prefixes.items) |search_prefix| {
|
|
const full_path = try Io.Dir.path.join(arena, &.{ search_prefix, "bin", name });
|
|
|
|
if (try checkCandidate(maker, step, found_path, &err_msg, full_path)) return;
|
|
}
|
|
}
|
|
|
|
if (graph.environ_map.get("PATH")) |PATH| {
|
|
for (names) |name_index| {
|
|
const name = name_index.slice(conf);
|
|
|
|
var it = std.mem.tokenizeScalar(u8, PATH, Io.Dir.path.delimiter);
|
|
while (it.next()) |p| {
|
|
const full_path = try Io.Dir.path.join(arena, &.{ p, name });
|
|
|
|
if (try checkCandidate(maker, step, found_path, &err_msg, full_path)) return;
|
|
}
|
|
}
|
|
}
|
|
|
|
assert(err_msg.items[err_msg.items.len - 1] == '\n');
|
|
const chopped = err_msg.items[0 .. err_msg.items.len - 1];
|
|
try step.result_error_msgs.append(arena, chopped);
|
|
return error.MakeFailed;
|
|
}
|
|
|
|
fn checkCandidate(
|
|
maker: *Maker,
|
|
step: *Step,
|
|
found_path: Configuration.GeneratedFileIndex,
|
|
err_msg: *std.ArrayList(u8),
|
|
full_path: []const u8,
|
|
) !bool {
|
|
const graph = maker.graph;
|
|
const arena = graph.arena; // TODO don't leak into process arena
|
|
const io = graph.io;
|
|
|
|
if (Io.Dir.cwd().access(io, full_path, .{ .execute = true })) |_| {
|
|
maker.generatedPath(found_path).* = .initCwd(full_path);
|
|
return true;
|
|
} else |err| switch (err) {
|
|
error.Canceled => |e| return e,
|
|
error.FileNotFound, error.AccessDenied, error.PermissionDenied => |e| {
|
|
try err_msg.print(arena, "{t} {s}\n", .{ e, full_path });
|
|
},
|
|
else => |e| return step.fail(maker, "failed accessing {s}: {t}", .{ full_path, e }),
|
|
}
|
|
|
|
if (builtin.os.tag == .windows) {
|
|
if (graph.environ_map.get("PATHEXT")) |PATHEXT| {
|
|
var it = std.mem.tokenizeScalar(u8, PATHEXT, Io.Dir.path.delimiter);
|
|
while (it.next()) |ext| {
|
|
if (!supportedWindowsProgramExtension(ext)) continue;
|
|
|
|
const extended_path = try std.mem.concat(arena, u8, &.{ full_path, ext });
|
|
|
|
if (Io.Dir.cwd().access(io, extended_path, .{ .execute = true })) |_| {
|
|
maker.generatedPath(found_path).* = .initCwd(extended_path);
|
|
return true;
|
|
} else |err| switch (err) {
|
|
error.Canceled => |e| return e,
|
|
error.FileNotFound, error.AccessDenied, error.PermissionDenied => |e| {
|
|
try err_msg.print(arena, "{t} {s}\n", .{ e, extended_path });
|
|
},
|
|
else => |e| return step.fail(maker, "failed accessing {s}: {t}", .{ extended_path, e }),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
fn supportedWindowsProgramExtension(ext: []const u8) bool {
|
|
inline for (@typeInfo(std.process.WindowsExtension).@"enum".field_names) |field_name| {
|
|
if (std.ascii.eqlIgnoreCase(ext, "." ++ field_name)) return true;
|
|
}
|
|
return false;
|
|
}
|