authorgravatar for carl@astholm.seCarl Åstholm <carl@astholm.se> 2024-02-18 19:56:34+01:00
committergravatar for carl@astholm.seCarl Åstholm <carl@astholm.se> 2024-04-07 15:30:19+02:00
logb381fb805cfe382a6786f2f1ae670d440d4184bf
tree11c34efc7c65e90bce365aad2ba9dc8fc2ed0a7b
parent9181ecd95133b42da75c6c5e8456830d93a9f7a9

std.Build: add `lazyImport` (`@import` for lazy dependencies)


2 files changed, 68 insertions(+), 6 deletions(-)

lib/compiler/build_runner.zig+1-1
......@@ -1,4 +1,3 @@
1const root = @import("@build");
21const std = @import("std");
32const builtin = @import("builtin");
43const assert = std.debug.assert;
......@@ -10,6 +9,7 @@ const ArrayList = std.ArrayList;
109const File = std.fs.File;
1110const Step = std.Build.Step;
1211
12pub const root = @import("@build");
1313pub const dependencies = @import("@dependencies");
1414
1515pub fn main() !void {
lib/std/Build.zig+67-5
......@@ -93,6 +93,8 @@ named_writefiles: std.StringArrayHashMap(*Step.WriteFile),
9393/// A map from build root dirs to the corresponding `*Dependency`. This is shared with all child
9494/// `Build`s.
9595initialized_deps: *InitializedDepMap,
96/// The hash of this instance's package. `""` means that this is the root package.
97pkg_hash: []const u8,
9698/// A mapping from dependency names to package hashes.
9799available_deps: AvailableDeps,
98100
......@@ -305,6 +307,7 @@ pub fn create(
305307 .modules = std.StringArrayHashMap(*Module).init(arena),
306308 .named_writefiles = std.StringArrayHashMap(*Step.WriteFile).init(arena),
307309 .initialized_deps = initialized_deps,
310 .pkg_hash = "",
308311 .available_deps = available_deps,
309312 .release_mode = .off,
310313 };
......@@ -318,10 +321,11 @@ fn createChild(
318321 parent: *Build,
319322 dep_name: []const u8,
320323 build_root: Cache.Directory,
324 pkg_hash: []const u8,
321325 pkg_deps: AvailableDeps,
322326 user_input_options: UserInputOptionsMap,
323327) !*Build {
324 const child = try createChildOnly(parent, dep_name, build_root, pkg_deps, user_input_options);
328 const child = try createChildOnly(parent, dep_name, build_root, pkg_hash, pkg_deps, user_input_options);
325329 try determineAndApplyInstallPrefix(child);
326330 return child;
327331}
......@@ -330,6 +334,7 @@ fn createChildOnly(
330334 parent: *Build,
331335 dep_name: []const u8,
332336 build_root: Cache.Directory,
337 pkg_hash: []const u8,
333338 pkg_deps: AvailableDeps,
334339 user_input_options: UserInputOptionsMap,
335340) !*Build {
......@@ -397,6 +402,7 @@ fn createChildOnly(
397402 .modules = std.StringArrayHashMap(*Module).init(allocator),
398403 .named_writefiles = std.StringArrayHashMap(*Step.WriteFile).init(allocator),
399404 .initialized_deps = parent.initialized_deps,
405 .pkg_hash = pkg_hash,
400406 .available_deps = pkg_deps,
401407 .release_mode = parent.release_mode,
402408 };
......@@ -1830,6 +1836,26 @@ fn findPkgHashOrFatal(b: *Build, name: []const u8) []const u8 {
18301836 std.debug.panic("no dependency named '{s}' in '{s}'. All packages used in build.zig must be declared in this file", .{ name, full_path });
18311837}
18321838
1839inline fn findImportPkgHashOrFatal(b: *Build, comptime asking_build_zig: type, comptime dep_name: []const u8) []const u8 {
1840 const build_runner = @import("root");
1841 const deps = build_runner.dependencies;
1842
1843 const b_pkg_hash, const b_pkg_deps = comptime for (@typeInfo(deps.packages).Struct.decls) |decl| {
1844 const pkg_hash = decl.name;
1845 const pkg = @field(deps.packages, pkg_hash);
1846 if (@hasDecl(pkg, "build_zig") and pkg.build_zig == asking_build_zig) break .{ pkg_hash, pkg.deps };
1847 } else .{ "", deps.root_deps };
1848 if (!std.mem.eql(u8, b_pkg_hash, b.pkg_hash)) {
1849 std.debug.panic("'{}' is not the struct that corresponds to '{s}'", .{ asking_build_zig, b.pathFromRoot("build.zig") });
1850 }
1851 comptime for (b_pkg_deps) |dep| {
1852 if (std.mem.eql(u8, dep[0], dep_name)) return dep[1];
1853 };
1854
1855 const full_path = b.pathFromRoot("build.zig.zon");
1856 std.debug.panic("no dependency named '{s}' in '{s}'. All packages used in build.zig must be declared in this file", .{ dep_name, full_path });
1857}
1858
18331859fn markNeededLazyDep(b: *Build, pkg_hash: []const u8) void {
18341860 b.graph.needed_lazy_dependencies.put(b.graph.arena, pkg_hash, {}) catch @panic("OOM");
18351861}
......@@ -1860,7 +1886,7 @@ pub fn lazyDependency(b: *Build, name: []const u8, args: anytype) ?*Dependency {
18601886 markNeededLazyDep(b, pkg_hash);
18611887 return null;
18621888 }
1863 return dependencyInner(b, name, pkg.build_root, if (@hasDecl(pkg, "build_zig")) pkg.build_zig else null, pkg.deps, args);
1889 return dependencyInner(b, name, pkg.build_root, if (@hasDecl(pkg, "build_zig")) pkg.build_zig else null, pkg_hash, pkg.deps, args);
18641890 }
18651891 }
18661892
......@@ -1878,13 +1904,48 @@ pub fn dependency(b: *Build, name: []const u8, args: anytype) *Dependency {
18781904 if (@hasDecl(pkg, "available")) {
18791905 std.debug.panic("dependency '{s}{s}' is marked as lazy in build.zig.zon which means it must use the lazyDependency function instead", .{ b.dep_prefix, name });
18801906 }
1881 return dependencyInner(b, name, pkg.build_root, if (@hasDecl(pkg, "build_zig")) pkg.build_zig else null, pkg.deps, args);
1907 return dependencyInner(b, name, pkg.build_root, if (@hasDecl(pkg, "build_zig")) pkg.build_zig else null, pkg_hash, pkg.deps, args);
18821908 }
18831909 }
18841910
18851911 unreachable; // Bad @dependencies source
18861912}
18871913
1914/// In a build.zig file, this function is to `@import` what `lazyDependency` is to `dependency`.
1915/// If the dependency is lazy and has not yet been fetched, it instructs the parent process to fetch
1916/// that dependency after the build script has finished running, then returns `null`.
1917/// If the dependency is lazy but has already been fetched, or if it is eager, it returns
1918/// the build.zig struct of that dependency, just like a regular `@import`.
1919pub inline fn lazyImport(
1920 b: *Build,
1921 /// The build.zig struct of the package importing the dependency.
1922 /// When calling this function from the `build` function of a build.zig file's, you normally
1923 /// pass `@This()`.
1924 comptime asking_build_zig: type,
1925 comptime dep_name: []const u8,
1926) ?type {
1927 const build_runner = @import("root");
1928 const deps = build_runner.dependencies;
1929 const pkg_hash = findImportPkgHashOrFatal(b, asking_build_zig, dep_name);
1930
1931 inline for (@typeInfo(deps.packages).Struct.decls) |decl| {
1932 if (comptime mem.eql(u8, decl.name, pkg_hash)) {
1933 const pkg = @field(deps.packages, decl.name);
1934 const available = !@hasDecl(pkg, "available") or pkg.available;
1935 if (!available) {
1936 markNeededLazyDep(b, pkg_hash);
1937 return null;
1938 }
1939 return if (@hasDecl(pkg, "build_zig"))
1940 pkg.build_zig
1941 else
1942 @compileError("dependency '" ++ dep_name ++ "' does not have a build.zig");
1943 }
1944 }
1945
1946 comptime unreachable; // Bad @dependencies source
1947}
1948
18881949pub fn anonymousDependency(
18891950 b: *Build,
18901951 /// The path to the directory containing the dependency's build.zig file,
......@@ -1901,7 +1962,7 @@ pub fn anonymousDependency(
19011962 '/', '\\' => byte.* = '.',
19021963 else => continue,
19031964 };
1904 return dependencyInner(b, name, build_root, build_zig, &.{}, args);
1965 return dependencyInner(b, name, build_root, build_zig, "anonymous", &.{}, args);
19051966}
19061967
19071968fn userValuesAreSame(lhs: UserValue, rhs: UserValue) bool {
......@@ -1956,6 +2017,7 @@ pub fn dependencyInner(
19562017 name: []const u8,
19572018 build_root_string: []const u8,
19582019 comptime build_zig: ?type,
2020 pkg_hash: []const u8,
19592021 pkg_deps: AvailableDeps,
19602022 args: anytype,
19612023) *Dependency {
......@@ -1976,7 +2038,7 @@ pub fn dependencyInner(
19762038 },
19772039 };
19782040
1979 const sub_builder = b.createChild(name, build_root, pkg_deps, user_input_options) catch @panic("unhandled error");
2041 const sub_builder = b.createChild(name, build_root, pkg_hash, pkg_deps, user_input_options) catch @panic("unhandled error");
19802042 if (build_zig) |bz| {
19812043 sub_builder.runBuild(bz) catch @panic("unhandled error");
19822044