@@ -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 };
@@ -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+
18201846fn 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+
18751935pub 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
18941954fn 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