@@ -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.
97+ pkg_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
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+
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,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+
18881948pub 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
19071967fn 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