@@ -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 };
@@ -1831,6 +1837,26 @@ fn findPkgHashOrFatal(b: *Build, name: []const u8) []const u8 {
18311837 std .debug .panic ("no dependency named '{s}' in '{s}'. All packages used in build.zig must be declared in this file" , .{ name , full_path });
18321838}
18331839
1840+ inline fn findImportPkgHashOrFatal (b : * Build , comptime asking_build_zig : type , comptime dep_name : []const u8 ) []const u8 {
1841+ const build_runner = @import ("root" );
1842+ const deps = build_runner .dependencies ;
1843+
1844+ const b_pkg_hash , const b_pkg_deps = comptime for (@typeInfo (deps .packages ).Struct .decls ) | decl | {
1845+ const pkg_hash = decl .name ;
1846+ const pkg = @field (deps .packages , pkg_hash );
1847+ if (@hasDecl (pkg , "build_zig" ) and pkg .build_zig == asking_build_zig ) break .{ pkg_hash , pkg .deps };
1848+ } else .{ "" , deps .root_deps };
1849+ if (! std .mem .eql (u8 , b_pkg_hash , b .pkg_hash )) {
1850+ std .debug .panic ("'{}' is not the struct that corresponds to '{s}'" , .{ asking_build_zig , b .pathFromRoot ("build.zig" ) });
1851+ }
1852+ comptime for (b_pkg_deps ) | dep | {
1853+ if (std .mem .eql (u8 , dep [0 ], dep_name )) return dep [1 ];
1854+ };
1855+
1856+ const full_path = b .pathFromRoot ("build.zig.zon" );
1857+ 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 });
1858+ }
1859+
18341860fn markNeededLazyDep (b : * Build , pkg_hash : []const u8 ) void {
18351861 b .graph .needed_lazy_dependencies .put (b .graph .arena , pkg_hash , {}) catch @panic ("OOM" );
18361862}
@@ -1861,7 +1887,7 @@ pub fn lazyDependency(b: *Build, name: []const u8, args: anytype) ?*Dependency {
18611887 markNeededLazyDep (b , pkg_hash );
18621888 return null ;
18631889 }
1864- return dependencyInner (b , name , pkg .build_root , if (@hasDecl (pkg , "build_zig" )) pkg .build_zig else null , pkg .deps , args );
1890+ return dependencyInner (b , name , pkg .build_root , if (@hasDecl (pkg , "build_zig" )) pkg .build_zig else null , pkg_hash , pkg .deps , args );
18651891 }
18661892 }
18671893
@@ -1879,13 +1905,48 @@ pub fn dependency(b: *Build, name: []const u8, args: anytype) *Dependency {
18791905 if (@hasDecl (pkg , "available" )) {
18801906 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 });
18811907 }
1882- return dependencyInner (b , name , pkg .build_root , if (@hasDecl (pkg , "build_zig" )) pkg .build_zig else null , pkg .deps , args );
1908+ return dependencyInner (b , name , pkg .build_root , if (@hasDecl (pkg , "build_zig" )) pkg .build_zig else null , pkg_hash , pkg .deps , args );
18831909 }
18841910 }
18851911
18861912 unreachable ; // Bad @dependencies source
18871913}
18881914
1915+ /// In a build.zig file, this function is to `@import` what `lazyDependency` is to `dependency`.
1916+ /// If the dependency is lazy and has not yet been fetched, it instructs the parent process to fetch
1917+ /// that dependency after the build script has finished running, then returns `null`.
1918+ /// If the dependency is lazy but has already been fetched, or if it is eager, it returns
1919+ /// the build.zig struct of that dependency, just like a regular `@import`.
1920+ pub inline fn lazyImport (
1921+ b : * Build ,
1922+ /// The build.zig struct of the package importing the dependency.
1923+ /// When calling this function from the `build` function of a build.zig file's, you normally
1924+ /// pass `@This()`.
1925+ comptime asking_build_zig : type ,
1926+ comptime dep_name : []const u8 ,
1927+ ) ? type {
1928+ const build_runner = @import ("root" );
1929+ const deps = build_runner .dependencies ;
1930+ const pkg_hash = findImportPkgHashOrFatal (b , asking_build_zig , dep_name );
1931+
1932+ inline for (@typeInfo (deps .packages ).Struct .decls ) | decl | {
1933+ if (comptime mem .eql (u8 , decl .name , pkg_hash )) {
1934+ const pkg = @field (deps .packages , decl .name );
1935+ const available = ! @hasDecl (pkg , "available" ) or pkg .available ;
1936+ if (! available ) {
1937+ markNeededLazyDep (b , pkg_hash );
1938+ return null ;
1939+ }
1940+ return if (@hasDecl (pkg , "build_zig" ))
1941+ pkg .build_zig
1942+ else
1943+ @compileError ("dependency '" ++ dep_name ++ "' does not have a build.zig" );
1944+ }
1945+ }
1946+
1947+ comptime unreachable ; // Bad @dependencies source
1948+ }
1949+
18891950pub fn anonymousDependency (
18901951 b : * Build ,
18911952 /// The path to the directory containing the dependency's build.zig file,
@@ -1902,7 +1963,7 @@ pub fn anonymousDependency(
19021963 '/' , '\\ ' = > byte .* = '.' ,
19031964 else = > continue ,
19041965 };
1905- return dependencyInner (b , name , build_root , build_zig , &.{}, args );
1966+ return dependencyInner (b , name , build_root , build_zig , "anonymous" , &.{}, args );
19061967}
19071968
19081969fn userValuesAreSame (lhs : UserValue , rhs : UserValue ) bool {
@@ -1957,6 +2018,7 @@ pub fn dependencyInner(
19572018 name : []const u8 ,
19582019 build_root_string : []const u8 ,
19592020 comptime build_zig : ? type ,
2021+ pkg_hash : []const u8 ,
19602022 pkg_deps : AvailableDeps ,
19612023 args : anytype ,
19622024) * Dependency {
@@ -1977,7 +2039,7 @@ pub fn dependencyInner(
19772039 },
19782040 };
19792041
1980- const sub_builder = b .createChild (name , build_root , pkg_deps , user_input_options ) catch @panic ("unhandled error" );
2042+ const sub_builder = b .createChild (name , build_root , pkg_hash , pkg_deps , user_input_options ) catch @panic ("unhandled error" );
19812043 if (build_zig ) | bz | {
19822044 sub_builder .runBuild (bz ) catch @panic ("unhandled error" );
19832045
0 commit comments