Moving from b.dependency to b.lazyDependency

this is a follow-up from an earlier post…

i use the following to download some third-party binary assets, which i install within zig-out/tools sub-folders…

   const targres = target.result;
   for (TOOLS) |name| {
        const dep = b.dependency(b.fmt("{s}-{s}-{s}", .{ name, @tagName(targres.os.tag), targres.osArchName() }), .{});
        const install_step = b.addInstallDirectory(.{
            .source_dir = dep.path("."),
            .install_dir = std.Build.InstallDir{ .custom = "tools" },
            .install_subdir = name,
        });
        exe.step.dependOn(&install_step.step);
    }

when my user invokes zig build, a set of OS- and architecture-specific set of artifact bundles would be installed…

but clearly, my build.zig.zon lists artifact bundles for all OS+arch combinations; and i’d really like to only fetch those actually needed for my user’s host PC…

i’ve heard more and more about lazy dependencies… but perhaps someone could show me a quick rework of my snippet using b.lazyDependency instead, which would be very helpful…

in reality, installation of these artifacts are NOT required to compile exe itself; i just need to ensure that the zig-out/tools folder is populated concurrently with zig-out/bin… said another way, zig-out/tools must be populated before actually executing the program in zig-out/bin

still learning :wink:

The thing that made it click for me is that you want logic in build.zig that only runs b.lazyDependency for the dependencies you want to be downloaded. If b.lazyDependency is run, the build runner will return null, exit the build process, download that dependency, then rerun.

So looking at your code, I think you are doing the right thing by generating the name given to lazyDependency.

Also it took me a while to figure out that if you are doing a @import from a dependency, when you switch to b.lazyDependency, you need to switch to using b.lazyImport.

Does that help?

1 Like

the part that’s still not clear to me is the return value from b.lazyDependency – specifically if it is null…

is it simply a matter of doing something like:

if (b.lazyDependency(...)) |dep| {everything as before}

from what i’m reading, the build.zig script will essentially execute this loop twice: the first time the results from b.lazyDependency would be null (although internally noted by the build system); before running the second time, the build system would actually fetch these dependencies; and in the second run through this loop, the results would now be non-null and usable not unlike b.dependency

is my understanding correct???

Yes that’s correct. The null return is simply to allow the build script to continue to the end before rerunning.

In an alternate universe, lazyDependency could have aborted the build process, downloaded that dependency, and then restarted. I don’t know for sure, but I suspect the intention was to have the first run of build.zig be able to signal all of the needed lazy dependencies, instead of having to rerun build.zig an arbitrary number of times.

it worked… i can certainly see the extra steps being performed…

my only “complaint” is that once zig build finishes, subsequent calls seem to output some info-garbage to my terminal:

$ zig build
←[?2026h←[J[3/6] steps
←(0mq←(B zig build-exe zig-em Debug native
←M←M←[?2026l←[J

perhaps i need to anchor my lazy dependencies on something other than exe.step???

we’re real close… but my “naive” end-user will still panic :wink:

From my experience, this is only part 1. Part 2 is protecting the b.lazyDependency behind an option. If the option is not set, the dependency is not loaded. However if you don’t use the “protect-by-option”, the dependency is fetched in either case, i.e. if it is used or not, doesn’t matter. As soon as the build.zig script reaches a b.lazyDependency call, it fetches that dependency. Maybe I missed something, or this behavior has been changed by now. Here’s a MRE, which is simple enough to illustrate my point I think.

Right - we agree. Your build.zig has to conditionally run b.lazyDependency - either you can do that behind an option, or (in this case) have the arguments be constructed to target only a specific dependency from a list.

@biosbob I have also (once) gotten into a state where I was seeing weird garbage like that during zig build (with zig 0.13) in place of the normal ascii art tree - that state persisted in that terminal until I rebooted (unsure if simply restarting the terminal would have worked). I’ll try to reproduce.