| ... | @@ -99,6 +99,8 @@ pub const Graph = struct { | ... | @@ -99,6 +99,8 @@ pub const Graph = struct { |
| 99 | wip_configuration: Configuration.Wip, | 99 | wip_configuration: Configuration.Wip, |
| 100 | | 100 | |
| 101 | cache_poison: CachePoison = .pure, | 101 | cache_poison: CachePoison = .pure, |
| | 102 | /// Observing this data causes cache poisoning. See `CachePoison`. |
| | 103 | search_prefixes: std.ArrayList([]const u8) = .empty, |
| 102 | | 104 | |
| 103 | /// If the cache is poisoned means that the **configure logic** had side | 105 | /// If the cache is poisoned means that the **configure logic** had side |
| 104 | /// effects, or otherwise did something that could not be tracked by the | 106 | /// effects, or otherwise did something that could not be tracked by the |
| ... | @@ -1706,9 +1708,6 @@ pub fn fmt(b: *Build, comptime format: []const u8, args: anytype) []u8 { | ... | @@ -1706,9 +1708,6 @@ pub fn fmt(b: *Build, comptime format: []const u8, args: anytype) []u8 { |
| 1706 | /// Creates an anonymous `Step` that searches for an executable on the host that | 1708 | /// Creates an anonymous `Step` that searches for an executable on the host that |
| 1707 | /// has more than one possible name. | 1709 | /// has more than one possible name. |
| 1708 | /// | 1710 | /// |
| 1709 | /// Names are searched in order, observing search prefixes first and then PATH | | |
| 1710 | /// environment variable. | | |
| 1711 | /// | | |
| 1712 | /// Returns the `LazyPath` of the found executable. The search only takes place | 1711 | /// Returns the `LazyPath` of the found executable. The search only takes place |
| 1713 | /// if the `LazyPath` will be used by a depending `Step`. | 1712 | /// if the `LazyPath` will be used by a depending `Step`. |
| 1714 | /// | 1713 | /// |
| ... | @@ -1719,6 +1718,9 @@ pub fn fmt(b: *Build, comptime format: []const u8, args: anytype) []u8 { | ... | @@ -1719,6 +1718,9 @@ pub fn fmt(b: *Build, comptime format: []const u8, args: anytype) []u8 { |
| 1719 | /// globally installed and will therefore be possibly found in one of the | 1718 | /// globally installed and will therefore be possibly found in one of the |
| 1720 | /// search prefix paths. | 1719 | /// search prefix paths. |
| 1721 | /// | 1720 | /// |
| | 1721 | /// Names are searched in order, observing search prefixes first and then PATH |
| | 1722 | /// environment variable. |
| | 1723 | /// |
| 1722 | /// Windows file name extensions are searched automatically, respecting the | 1724 | /// Windows file name extensions are searched automatically, respecting the |
| 1723 | /// PATHEXT environment variable, so they need not be included in this list. | 1725 | /// PATHEXT environment variable, so they need not be included in this list. |
| 1724 | /// However, even on Windows, the names will be checked without appending | 1726 | /// However, even on Windows, the names will be checked without appending |
| ... | @@ -1730,24 +1732,98 @@ pub fn findProgramLazy(b: *Build, options: Step.FindProgram.Options) LazyPath { | ... | @@ -1730,24 +1732,98 @@ pub fn findProgramLazy(b: *Build, options: Step.FindProgram.Options) LazyPath { |
| 1730 | return .{ .generated = .{ .index = Step.FindProgram.create(b, options).found_path } }; | 1732 | return .{ .generated = .{ .index = Step.FindProgram.create(b, options).found_path } }; |
| 1731 | } | 1733 | } |
| 1732 | | 1734 | |
| | 1735 | pub const FindProgramOptions = Step.FindProgram.Options; |
| | 1736 | |
| 1733 | /// Immediately (in the configure phase), searches for an executable on the host | 1737 | /// Immediately (in the configure phase), searches for an executable on the host |
| 1734 | /// that has more than one possible name. | 1738 | /// that has more than one possible name. |
| 1735 | /// | 1739 | /// |
| | 1740 | /// Calling this function poisons the configuration cache, so it is only |
| | 1741 | /// appropriate when the existence of the program or its output needs to be |
| | 1742 | /// observed by configuration logic. For more information, see |
| | 1743 | /// `Graph.CachePoison` documentation. |
| | 1744 | /// |
| 1736 | /// Names are searched in order, observing search prefixes first and then PATH | 1745 | /// Names are searched in order, observing search prefixes first and then PATH |
| 1737 | /// environment variable. | 1746 | /// environment variable. |
| 1738 | /// | 1747 | /// |
| 1739 | /// Calling this function poisons the configuration cache. For more | 1748 | /// Windows file name extensions are searched automatically, respecting the |
| 1740 | /// information, see `Graph.CachePoison` documentation. | 1749 | /// PATHEXT environment variable, so they need not be included in this list. |
| | 1750 | /// However, even on Windows, the names will be checked without appending |
| | 1751 | /// extensions first, so that can be used as a priority system. |
| 1741 | /// | 1752 | /// |
| 1742 | /// See also: | 1753 | /// See also: |
| 1743 | /// * `findProgramLazy` | 1754 | /// * `findProgramLazy` |
| 1744 | pub fn findProgram(b: *Build, names: []const []const u8) ?[]const u8 { | 1755 | pub fn findProgram(b: *Build, options: FindProgramOptions) ?[]const u8 { |
| 1745 | const graph = b.graph; | 1756 | const graph = b.graph; |
| 1746 | const wc = &graph.wip_configuration; | 1757 | |
| 1747 | const string_list = wc.addStringList(names) catch @panic("OOM"); | 1758 | // Because it observes search prefixes and contents of directories in PATH. |
| 1748 | _ = string_list; | | |
| 1749 | graph.poisonCache(); | 1759 | graph.poisonCache(); |
| 1750 | @panic("TODO"); | 1760 | |
| | 1761 | for (options.names) |name| { |
| | 1762 | if (Io.Dir.path.isAbsolute(name)) { |
| | 1763 | if (tryFindProgram(b, name)) |found| return found; |
| | 1764 | } |
| | 1765 | for (graph.search_prefixes.items) |search_prefix| { |
| | 1766 | const full_path = b.pathJoin(&.{ search_prefix, "bin", name }); |
| | 1767 | if (tryFindProgram(b, full_path)) |found| return found; |
| | 1768 | } |
| | 1769 | } |
| | 1770 | |
| | 1771 | if (b.graph.environ_map.get("PATH")) |PATH| { |
| | 1772 | for (options.names) |name| { |
| | 1773 | var it = mem.tokenizeScalar(u8, PATH, Io.Dir.path.delimiter); |
| | 1774 | while (it.next()) |p| { |
| | 1775 | const full_path = b.pathJoin(&.{ p, name }); |
| | 1776 | if (tryFindProgram(b, full_path)) |found| return found; |
| | 1777 | } |
| | 1778 | } |
| | 1779 | } |
| | 1780 | |
| | 1781 | return null; |
| | 1782 | } |
| | 1783 | |
| | 1784 | fn supportedWindowsProgramExtension(ext: []const u8) bool { |
| | 1785 | inline for (@typeInfo(std.process.WindowsExtension).@"enum".fields) |field| { |
| | 1786 | if (std.ascii.eqlIgnoreCase(ext, "." ++ field.name)) return true; |
| | 1787 | } |
| | 1788 | return false; |
| | 1789 | } |
| | 1790 | |
| | 1791 | fn tryFindProgram(b: *Build, full_path: []const u8) ?[]const u8 { |
| | 1792 | const graph = b.graph; |
| | 1793 | const io = graph.io; |
| | 1794 | const arena = graph.arena; |
| | 1795 | |
| | 1796 | if (Io.Dir.cwd().access(io, full_path, .{ .execute = true })) |_| { |
| | 1797 | return full_path; |
| | 1798 | } else |err| switch (err) { |
| | 1799 | error.FileNotFound, error.AccessDenied, error.PermissionDenied => |e| { |
| | 1800 | if (graph.verbose) log.info("searched: {t} {s}", .{ e, full_path }); |
| | 1801 | }, |
| | 1802 | else => |e| return panic("failed accessing {s}: {t}", .{ full_path, e }), |
| | 1803 | } |
| | 1804 | |
| | 1805 | if (builtin.os.tag == .windows) { |
| | 1806 | if (b.graph.environ_map.get("PATHEXT")) |PATHEXT| { |
| | 1807 | var it = mem.tokenizeScalar(u8, PATHEXT, fs.path.delimiter); |
| | 1808 | |
| | 1809 | while (it.next()) |ext| { |
| | 1810 | if (!supportedWindowsProgramExtension(ext)) continue; |
| | 1811 | |
| | 1812 | const extended_path = try mem.concat(arena, &.{ full_path, ext }); |
| | 1813 | |
| | 1814 | if (Io.Dir.cwd().access(io, extended_path, .{ .execute = true })) |_| { |
| | 1815 | return extended_path; |
| | 1816 | } else |err| switch (err) { |
| | 1817 | error.FileNotFound, error.AccessDenied, error.PermissionDenied => |e| { |
| | 1818 | if (graph.verbose) log.info("searched: {t} {s}", .{ e, extended_path }); |
| | 1819 | }, |
| | 1820 | else => |e| return panic("failed accessing {s}: {t}", .{ extended_path, e }), |
| | 1821 | } |
| | 1822 | } |
| | 1823 | } |
| | 1824 | } |
| | 1825 | |
| | 1826 | return null; |
| 1751 | } | 1827 | } |
| 1752 | | 1828 | |
| 1753 | /// Deprecated; use `runFallible`. | 1829 | /// Deprecated; use `runFallible`. |