Skip to content

Commit c13b603

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

File tree

2 files changed

+67
-6
lines changed

2 files changed

+67
-6
lines changed

lib/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
};
@@ -1817,6 +1823,26 @@ fn findPkgHashOrFatal(b: *Build, name: []const u8) []const u8 {
18171823
std.debug.panic("no dependency named '{s}' in '{s}'. All packages used in build.zig must be declared in this file", .{ name, full_path });
18181824
}
18191825

1826+
inline fn findImportPkgHashOrFatal(b: *Build, comptime b_build_zig: type, comptime dep_name: []const u8) []const u8 {
1827+
const build_runner = @import("root");
1828+
const deps = build_runner.dependencies;
1829+
1830+
const b_pkg_hash, const b_pkg_deps = comptime for (@typeInfo(deps.packages).Struct.decls) |decl| {
1831+
const pkg_hash = decl.name;
1832+
const pkg = @field(deps.packages, pkg_hash);
1833+
if (@hasDecl(pkg, "build_zig") and pkg.build_zig == b_build_zig) break .{ pkg_hash, pkg.deps };
1834+
} else .{ "", deps.root_deps };
1835+
if (!std.mem.eql(u8, b_pkg_hash, b.pkg_hash)) {
1836+
std.debug.panic("'{}' is not the struct that corresponds to '{s}'", .{ b_build_zig, b.pathFromRoot("build.zig") });
1837+
}
1838+
comptime for (b_pkg_deps) |dep| {
1839+
if (std.mem.eql(u8, dep[0], dep_name)) return dep[1];
1840+
};
1841+
1842+
const full_path = b.pathFromRoot("build.zig.zon");
1843+
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 });
1844+
}
1845+
18201846
fn markNeededLazyDep(b: *Build, pkg_hash: []const u8) void {
18211847
b.graph.needed_lazy_dependencies.put(b.graph.arena, pkg_hash, {}) catch @panic("OOM");
18221848
}
@@ -1847,7 +1873,7 @@ pub fn lazyDependency(b: *Build, name: []const u8, args: anytype) ?*Dependency {
18471873
markNeededLazyDep(b, pkg_hash);
18481874
return null;
18491875
}
1850-
return dependencyInner(b, name, pkg.build_root, if (@hasDecl(pkg, "build_zig")) pkg.build_zig else null, pkg.deps, args);
1876+
return dependencyInner(b, name, pkg.build_root, if (@hasDecl(pkg, "build_zig")) pkg.build_zig else null, pkg_hash, pkg.deps, args);
18511877
}
18521878
}
18531879

@@ -1865,13 +1891,47 @@ pub fn dependency(b: *Build, name: []const u8, args: anytype) *Dependency {
18651891
if (@hasDecl(pkg, "available")) {
18661892
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 });
18671893
}
1868-
return dependencyInner(b, name, pkg.build_root, if (@hasDecl(pkg, "build_zig")) pkg.build_zig else null, pkg.deps, args);
1894+
return dependencyInner(b, name, pkg.build_root, if (@hasDecl(pkg, "build_zig")) pkg.build_zig else null, pkg_hash, pkg.deps, args);
18691895
}
18701896
}
18711897

18721898
unreachable; // Bad @dependencies source
18731899
}
18741900

1901+
/// In a build.zig, this function is to `@import` what `lazyDependency` is to `dependency`.
1902+
/// If the dependency is lazy and has not yet been fetched, it instructs the parent process to fetch
1903+
/// that dependency after the build script has finished running, then returns `null`.
1904+
/// If the dependency is lazy but has already been fetched, or if it is non-lazy, it returns the
1905+
/// struct that corresponds to the build.zig of that dependency, just like `@import`.
1906+
pub inline fn lazyImport(
1907+
b: *Build,
1908+
/// The struct that corresponds to the build.zig of the package `b` was created for.
1909+
/// When calling this function from a build.zig's `build` function, you usually pass `@This()`.
1910+
comptime b_build_zig: type,
1911+
comptime dep_name: []const u8,
1912+
) ?type {
1913+
const build_runner = @import("root");
1914+
const deps = build_runner.dependencies;
1915+
const pkg_hash = findImportPkgHashOrFatal(b, b_build_zig, dep_name);
1916+
1917+
inline for (@typeInfo(deps.packages).Struct.decls) |decl| {
1918+
if (comptime mem.eql(u8, decl.name, pkg_hash)) {
1919+
const pkg = @field(deps.packages, decl.name);
1920+
const available = !@hasDecl(pkg, "available") or pkg.available;
1921+
if (!available) {
1922+
markNeededLazyDep(b, pkg_hash);
1923+
return null;
1924+
}
1925+
return if (@hasDecl(pkg, "build_zig"))
1926+
pkg.build_zig
1927+
else
1928+
@compileError("dependency '" ++ dep_name ++ "' does not have a build.zig");
1929+
}
1930+
}
1931+
1932+
comptime unreachable; // Bad @dependencies source
1933+
}
1934+
18751935
pub fn anonymousDependency(
18761936
b: *Build,
18771937
/// The path to the directory containing the dependency's build.zig file,
@@ -1888,7 +1948,7 @@ pub fn anonymousDependency(
18881948
'/', '\\' => byte.* = '.',
18891949
else => continue,
18901950
};
1891-
return dependencyInner(b, name, build_root, build_zig, &.{}, args);
1951+
return dependencyInner(b, name, build_root, build_zig, "anonymous", &.{}, args);
18921952
}
18931953

18941954
fn userValuesAreSame(lhs: UserValue, rhs: UserValue) bool {
@@ -1943,6 +2003,7 @@ pub fn dependencyInner(
19432003
name: []const u8,
19442004
build_root_string: []const u8,
19452005
comptime build_zig: ?type,
2006+
pkg_hash: []const u8,
19462007
pkg_deps: AvailableDeps,
19472008
args: anytype,
19482009
) *Dependency {
@@ -1963,7 +2024,7 @@ pub fn dependencyInner(
19632024
},
19642025
};
19652026

1966-
const sub_builder = b.createChild(name, build_root, pkg_deps, user_input_options) catch @panic("unhandled error");
2027+
const sub_builder = b.createChild(name, build_root, pkg_hash, pkg_deps, user_input_options) catch @panic("unhandled error");
19672028
if (build_zig) |bz| {
19682029
sub_builder.runBuild(bz) catch @panic("unhandled error");
19692030

0 commit comments

Comments
 (0)