| author | |
| committer | |
| log | 1edc5d7d67f084941c2162dc71bd8a417189f265 |
| tree | e94f181fa0d9d0ee6fa8d901d698981c4130639b |
| parent | 54bb8d2dd9369f5e5b43b4773878edc32fd3851e |
8 files changed, 175 insertions(+), 12 deletions(-)
lib/compiler/Maker/Step.zig+2-1| ... | ... | @@ -19,6 +19,7 @@ const WebServer = @import("WebServer.zig"); |
| 19 | 19 | const Maker = @import("../Maker.zig"); |
| 20 | 20 | |
| 21 | 21 | pub const Compile = @import("Step/Compile.zig"); |
| 22 | pub const FindProgram = @import("Step/FindProgram.zig"); | |
| 22 | 23 | pub const Fmt = @import("Step/Fmt.zig"); |
| 23 | 24 | pub const InstallArtifact = @import("Step/InstallArtifact.zig"); |
| 24 | 25 | pub const InstallDir = @import("Step/InstallDir.zig"); |
| ... | ... | @@ -78,7 +79,7 @@ pub const Extended = union(enum) { |
| 78 | 79 | compile: Compile, |
| 79 | 80 | config_header: Todo, |
| 80 | 81 | fail: Fail, |
| 81 | find_program: Todo, | |
| 82 | find_program: FindProgram, | |
| 82 | 83 | fmt: Fmt, |
| 83 | 84 | install_artifact: InstallArtifact, |
| 84 | 85 | install_dir: InstallDir, |
lib/compiler/Maker/Step/FindProgram.zig created+120| ... | ... | @@ -0,0 +1,120 @@ |
| 1 | const FindProgram = @This(); | |
| 2 | const builtin = @import("builtin"); | |
| 3 | ||
| 4 | const std = @import("std"); | |
| 5 | const Io = std.Io; | |
| 6 | const Configuration = std.Build.Configuration; | |
| 7 | const assert = std.debug.assert; | |
| 8 | ||
| 9 | const Step = @import("../Step.zig"); | |
| 10 | const Maker = @import("../../Maker.zig"); | |
| 11 | ||
| 12 | pub fn make( | |
| 13 | find_program: *FindProgram, | |
| 14 | step_index: Configuration.Step.Index, | |
| 15 | maker: *Maker, | |
| 16 | progress_node: std.Progress.Node, | |
| 17 | ) Step.ExtendedMakeError!void { | |
| 18 | _ = find_program; | |
| 19 | _ = progress_node; | |
| 20 | const graph = maker.graph; | |
| 21 | const step = maker.stepByIndex(step_index); | |
| 22 | const arena = graph.arena; // TODO don't leak into the process arena | |
| 23 | const conf = &maker.scanned_config.configuration; | |
| 24 | const conf_step = step_index.ptr(conf); | |
| 25 | const conf_fp = conf_step.extended.get(conf.extra).find_program; | |
| 26 | const found_path = conf_fp.found_path; | |
| 27 | const names = conf_fp.names.slice(conf); | |
| 28 | ||
| 29 | // In case we fail at the end. | |
| 30 | var err_msg: std.ArrayList(u8) = .empty; | |
| 31 | try err_msg.appendSlice(arena, "program not found. searched paths:\n"); | |
| 32 | ||
| 33 | for (names) |name_index| { | |
| 34 | const name = name_index.slice(conf); | |
| 35 | ||
| 36 | if (Io.Dir.path.isAbsolute(name)) { | |
| 37 | if (try checkCandidate(maker, step, found_path, &err_msg, name)) return; | |
| 38 | ||
| 39 | continue; | |
| 40 | } | |
| 41 | ||
| 42 | for (graph.search_prefixes.items) |search_prefix| { | |
| 43 | const full_path = try Io.Dir.path.join(arena, &.{ search_prefix, "bin", name }); | |
| 44 | ||
| 45 | if (try checkCandidate(maker, step, found_path, &err_msg, full_path)) return; | |
| 46 | } | |
| 47 | } | |
| 48 | ||
| 49 | if (graph.environ_map.get("PATH")) |PATH| { | |
| 50 | for (names) |name_index| { | |
| 51 | const name = name_index.slice(conf); | |
| 52 | ||
| 53 | var it = std.mem.tokenizeScalar(u8, PATH, Io.Dir.path.delimiter); | |
| 54 | while (it.next()) |p| { | |
| 55 | const full_path = try Io.Dir.path.join(arena, &.{ p, name }); | |
| 56 | ||
| 57 | if (try checkCandidate(maker, step, found_path, &err_msg, full_path)) return; | |
| 58 | } | |
| 59 | } | |
| 60 | } | |
| 61 | ||
| 62 | assert(err_msg.items[err_msg.items.len - 1] == '\n'); | |
| 63 | const chopped = err_msg.items[0 .. err_msg.items.len - 1]; | |
| 64 | try step.result_error_msgs.append(arena, chopped); | |
| 65 | return error.MakeFailed; | |
| 66 | } | |
| 67 | ||
| 68 | fn checkCandidate( | |
| 69 | maker: *Maker, | |
| 70 | step: *Step, | |
| 71 | found_path: Configuration.GeneratedFileIndex, | |
| 72 | err_msg: *std.ArrayList(u8), | |
| 73 | full_path: []const u8, | |
| 74 | ) !bool { | |
| 75 | const graph = maker.graph; | |
| 76 | const arena = graph.arena; // TODO don't leak into process arena | |
| 77 | const io = graph.io; | |
| 78 | ||
| 79 | if (Io.Dir.cwd().access(io, full_path, .{ .execute = true })) |_| { | |
| 80 | maker.generatedPath(found_path).* = .initCwd(full_path); | |
| 81 | return true; | |
| 82 | } else |err| switch (err) { | |
| 83 | error.Canceled => |e| return e, | |
| 84 | error.FileNotFound, error.AccessDenied, error.PermissionDenied => |e| { | |
| 85 | try err_msg.print(arena, "{t} {s}\n", .{ e, full_path }); | |
| 86 | }, | |
| 87 | else => |e| return step.fail(maker, "failed accessing {s}: {t}", .{ full_path, e }), | |
| 88 | } | |
| 89 | ||
| 90 | if (builtin.os.tag == .windows) { | |
| 91 | if (graph.environ_map.get("PATHEXT")) |PATHEXT| { | |
| 92 | var it = std.mem.tokenizeScalar(u8, PATHEXT, Io.Dir.path.delimiter); | |
| 93 | while (it.next()) |ext| { | |
| 94 | if (!supportedWindowsProgramExtension(ext)) continue; | |
| 95 | ||
| 96 | const extended_path = try std.mem.concat(arena, &.{ full_path, ext }); | |
| 97 | ||
| 98 | if (Io.Dir.cwd().access(io, extended_path, .{ .execute = true })) |_| { | |
| 99 | maker.generatedPath(found_path).* = .initCwd(extended_path); | |
| 100 | return true; | |
| 101 | } else |err| switch (err) { | |
| 102 | error.Canceled => |e| return e, | |
| 103 | error.FileNotFound, error.AccessDenied, error.PermissionDenied => |e| { | |
| 104 | try err_msg.print(arena, "{s} {t}\n", .{ extended_path, e }); | |
| 105 | }, | |
| 106 | else => |e| return step.fail(maker, "failed accessing {s}: {t}", .{ extended_path, e }), | |
| 107 | } | |
| 108 | } | |
| 109 | } | |
| 110 | } | |
| 111 | ||
| 112 | return false; | |
| 113 | } | |
| 114 | ||
| 115 | fn supportedWindowsProgramExtension(ext: []const u8) bool { | |
| 116 | inline for (@typeInfo(std.process.WindowsExtension).@"enum".fields) |field| { | |
| 117 | if (std.ascii.eqlIgnoreCase(ext, "." ++ field.name)) return true; | |
| 118 | } | |
| 119 | return false; | |
| 120 | } |
lib/compiler/configurer.zig+7-1| ... | ... | @@ -906,7 +906,13 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void { |
| 906 | 906 | .msg = sf.error_msg, |
| 907 | 907 | }); |
| 908 | 908 | }, |
| 909 | .find_program => @panic("TODO"), | |
| 909 | .find_program => e: { | |
| 910 | const fp: *Step.FindProgram = @fieldParentPtr("step", step); | |
| 911 | break :e try wc.addExtraErased(Configuration.Step.FindProgram, .{ | |
| 912 | .names = fp.names, | |
| 913 | .found_path = fp.found_path, | |
| 914 | }); | |
| 915 | }, | |
| 910 | 916 | .fmt => e: { |
| 911 | 917 | const sf: *Step.Fmt = @fieldParentPtr("step", step); |
| 912 | 918 | break :e try wc.addExtraErased(Configuration.Step.Fmt, .{ |
lib/std/Build.zig+7-6| ... | ... | @@ -1719,14 +1719,15 @@ pub fn fmt(b: *Build, comptime format: []const u8, args: anytype) []u8 { |
| 1719 | 1719 | /// globally installed and will therefore be possibly found in one of the |
| 1720 | 1720 | /// search prefix paths. |
| 1721 | 1721 | /// |
| 1722 | /// Windows file name extensions are searched automatically, respecting the | |
| 1723 | /// PATHEXT environment variable, so they need not be included in this list. | |
| 1724 | /// However, even on Windows, the names will be checked without appending | |
| 1725 | /// extensions first, so that can be used as a priority system. | |
| 1726 | /// | |
| 1722 | 1727 | /// See also: |
| 1723 | 1728 | /// * `findProgram` |
| 1724 | pub fn findProgramLazy(b: *Build, names: []const []const u8) LazyPath { | |
| 1725 | const graph = b.graph; | |
| 1726 | const wc = &graph.wip_configuration; | |
| 1727 | const string_list = wc.addStringList(names) catch @panic("OOM"); | |
| 1728 | _ = string_list; | |
| 1729 | @panic("TODO"); | |
| 1729 | pub fn findProgramLazy(b: *Build, options: Step.FindProgram.Options) LazyPath { | |
| 1730 | return .{ .generated = .{ .index = Step.FindProgram.create(b, options).found_path } }; | |
| 1730 | 1731 | } |
| 1731 | 1732 | |
| 1732 | 1733 | /// Immediately (in the configure phase), searches for an executable on the host |
lib/std/Build/Configuration.zig+2-2| ... | ... | @@ -1192,9 +1192,9 @@ pub const Step = extern struct { |
| 1192 | 1192 | }; |
| 1193 | 1193 | |
| 1194 | 1194 | pub const FindProgram = struct { |
| 1195 | flags: @This().Flags, | |
| 1195 | flags: @This().Flags = .{}, | |
| 1196 | 1196 | names: StringList, |
| 1197 | generated_file: GeneratedFileIndex, | |
| 1197 | found_path: GeneratedFileIndex, | |
| 1198 | 1198 | |
| 1199 | 1199 | pub const Flags = packed struct(u32) { |
| 1200 | 1200 | tag: Tag = .find_program, |
lib/std/Build/Step.zig+3-2| ... | ... | @@ -78,19 +78,20 @@ pub fn Type(comptime tag: Tag) type { |
| 78 | 78 | } |
| 79 | 79 | |
| 80 | 80 | pub const CheckFile = @import("Step/CheckFile.zig"); |
| 81 | pub const Compile = @import("Step/Compile.zig"); | |
| 81 | 82 | pub const ConfigHeader = @import("Step/ConfigHeader.zig"); |
| 82 | 83 | pub const Fail = @import("Step/Fail.zig"); |
| 84 | pub const FindProgram = @import("Step/FindProgram.zig"); | |
| 83 | 85 | pub const Fmt = @import("Step/Fmt.zig"); |
| 84 | 86 | pub const InstallArtifact = @import("Step/InstallArtifact.zig"); |
| 85 | 87 | pub const InstallDir = @import("Step/InstallDir.zig"); |
| 86 | 88 | pub const InstallFile = @import("Step/InstallFile.zig"); |
| 87 | 89 | pub const ObjCopy = @import("Step/ObjCopy.zig"); |
| 88 | pub const Compile = @import("Step/Compile.zig"); | |
| 89 | 90 | pub const Options = @import("Step/Options.zig"); |
| 90 | 91 | pub const Run = @import("Step/Run.zig"); |
| 91 | 92 | pub const TranslateC = @import("Step/TranslateC.zig"); |
| 92 | pub const WriteFile = @import("Step/WriteFile.zig"); | |
| 93 | 93 | pub const UpdateSourceFiles = @import("Step/UpdateSourceFiles.zig"); |
| 94 | pub const WriteFile = @import("Step/WriteFile.zig"); | |
| 94 | 95 | |
| 95 | 96 | pub const TopLevel = struct { |
| 96 | 97 | pub const base_tag: Step.Tag = .top_level; |
lib/std/Build/Step/FindProgram.zig created+31| ... | ... | @@ -0,0 +1,31 @@ |
| 1 | const FindProgram = @This(); | |
| 2 | ||
| 3 | const std = @import("std"); | |
| 4 | const Step = std.Build.Step; | |
| 5 | const Configuration = std.Build.Configuration; | |
| 6 | ||
| 7 | step: Step, | |
| 8 | found_path: Configuration.GeneratedFileIndex, | |
| 9 | names: Configuration.StringList, | |
| 10 | ||
| 11 | pub const base_tag: Step.Tag = .find_program; | |
| 12 | ||
| 13 | pub const Options = struct { | |
| 14 | names: []const []const u8, | |
| 15 | }; | |
| 16 | ||
| 17 | pub fn create(owner: *std.Build, options: Options) *FindProgram { | |
| 18 | const graph = owner.graph; | |
| 19 | const wc = &graph.wip_configuration; | |
| 20 | const fp = graph.create(FindProgram); | |
| 21 | fp.* = .{ | |
| 22 | .step = .init(.{ | |
| 23 | .tag = base_tag, | |
| 24 | .name = owner.fmt("find program {s} ({d} candidates)", .{ options.names[0], options.names.len }), | |
| 25 | .owner = owner, | |
| 26 | }), | |
| 27 | .found_path = graph.addGeneratedFile(&fp.step), | |
| 28 | .names = wc.addStringList(options.names) catch @panic("OOM"), | |
| 29 | }; | |
| 30 | return fp; | |
| 31 | } |
lib/std/Io/Dir.zig+3| ... | ... | @@ -409,7 +409,10 @@ pub const PathNameError = error{ |
| 409 | 409 | }; |
| 410 | 410 | |
| 411 | 411 | pub const AccessError = error{ |
| 412 | /// The requested `AccessOptions` would be denied to the file, or search | |
| 413 | /// permission is denied for one of the directories in the path prefix. | |
| 412 | 414 | AccessDenied, |
| 415 | /// Write permission was requested but the file is immutable. | |
| 413 | 416 | PermissionDenied, |
| 414 | 417 | FileNotFound, |
| 415 | 418 | InputOutput, |