Skip to content

Commit b690924

Browse files
committed
std.Build: add lazyImport (@import for lazy dependencies)
1 parent f06725f commit b690924

File tree

2 files changed

+67
-6
lines changed

2 files changed

+67
-6
lines changed

lib/compiler/build_runner.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
const root = @import("@build");
21
const std = @import("std");
32
const builtin = @import("builtin");
43
const assert = std.debug.assert;
@@ -10,6 +9,7 @@ const ArrayList = std.ArrayList;
109
const File = std.fs.File;
1110
const Step = std.Build.Step;
1211

12+
pub const root = @import("@build");
1313
pub const dependencies = @import("@dependencies");
1414

1515
pub fn main() !void {

lib/std/Build.zig

Lines changed: 66 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -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.
9595
initialized_deps: *InitializedDepMap,
96+
/// The hash of this instance's package. `""` means that this is the root package.
97+
pkg_hash: []const u8,
9698
/// A mapping from dependency names to package hashes.
9799
available_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

1839+
inline fn findImportPkgHashOrFatal(b: *Build, comptime b_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 == b_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}'", .{ b_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+
18331859
fn 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,47 @@ 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, 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 non-lazy, it returns the
1918+
/// struct that corresponds to the build.zig of that dependency, just like `@import`.
1919+
pub inline fn lazyImport(
1920+
b: *Build,
1921+
/// The struct that corresponds to the build.zig of the package `b` was created for.
1922+
/// When calling this function from a build.zig's `build` function, you usually pass `@This()`.
1923+
comptime b_build_zig: type,
1924+
comptime dep_name: []const u8,
1925+
) ?type {
1926+
const build_runner = @import("root");
1927+
const deps = build_runner.dependencies;
1928+
const pkg_hash = findImportPkgHashOrFatal(b, b_build_zig, dep_name);
1929+
1930+
inline for (@typeInfo(deps.packages).Struct.decls) |decl| {
1931+
if (comptime mem.eql(u8, decl.name, pkg_hash)) {
1932+
const pkg = @field(deps.packages, decl.name);
1933+
const available = !@hasDecl(pkg, "available") or pkg.available;
1934+
if (!available) {
1935+
markNeededLazyDep(b, pkg_hash);
1936+
return null;
1937+
}
1938+
return if (@hasDecl(pkg, "build_zig"))
1939+
pkg.build_zig
1940+
else
1941+
@compileError("dependency '" ++ dep_name ++ "' does not have a build.zig");
1942+
}
1943+
}
1944+
1945+
comptime unreachable; // Bad @dependencies source
1946+
}
1947+
18881948
pub fn anonymousDependency(
18891949
b: *Build,
18901950
/// The path to the directory containing the dependency's build.zig file,
@@ -1901,7 +1961,7 @@ pub fn anonymousDependency(
19011961
'/', '\\' => byte.* = '.',
19021962
else => continue,
19031963
};
1904-
return dependencyInner(b, name, build_root, build_zig, &.{}, args);
1964+
return dependencyInner(b, name, build_root, build_zig, "anonymous", &.{}, args);
19051965
}
19061966

19071967
fn userValuesAreSame(lhs: UserValue, rhs: UserValue) bool {
@@ -1956,6 +2016,7 @@ pub fn dependencyInner(
19562016
name: []const u8,
19572017
build_root_string: []const u8,
19582018
comptime build_zig: ?type,
2019+
pkg_hash: []const u8,
19592020
pkg_deps: AvailableDeps,
19602021
args: anytype,
19612022
) *Dependency {
@@ -1976,7 +2037,7 @@ pub fn dependencyInner(
19762037
},
19772038
};
19782039

1979-
const sub_builder = b.createChild(name, build_root, pkg_deps, user_input_options) catch @panic("unhandled error");
2040+
const sub_builder = b.createChild(name, build_root, pkg_hash, pkg_deps, user_input_options) catch @panic("unhandled error");
19802041
if (build_zig) |bz| {
19812042
sub_builder.runBuild(bz) catch @panic("unhandled error");
19822043

0 commit comments

Comments
 (0)