Lazier would be fine with me!
One of the sneaky reasons I branched this thread off was to lure someone in who thoroughly understands this corner of the build system. Documentation is lacking, understandable, things are in flux, but I’ve been cargo-culting like almost anyone else here. Thanks for helping us understand this stuff.
My ideal is optimal laziness. It costs at most one cache miss when code which needs a dependency is reached, and… I’ll cover why I think this is good in a second.
I think this can be late-bound at the cost of starting over sometimes. I don’t know if that’s actually a good idea or not, I think it is though.
I’ve seen transitive dependency become a problem over and over. I can’t actually think of a software ecosystem where it doesn’t rear its ugly head. There’s always a dance to try and get optional enhancements which work with a specific package, but only if you’re using that package, and those systems generally work poorly.
But all of those languages, so far as I know, work on a principle of eager compilation, using a later tree-shaking stage or link-based elimination to winnow the code down. The Zig thing where it refuses to even look at code except to make sure it parses, until something calls it, is borderline unique, and it really could tame all that complexity.
My test case here is I’m working on a library, and I’m going to add a few functions which take an mvzr
regex. But the library itself doesn’t need a regex at all, unless you call one of those two or three functions. Ideally, until the instant the build system tries to compile those functions, it doesn’t fetch that dependency, unless the user does something like zig fetch --all
(which does not exist).
That seems achievable if we’re willing to have a build fail because a dependency has to be fetched, and then started over. I think the effort expended would pay off, though I could be wrong. Something like: the build system optimistically (or pessimistically if you prefer) stubs any dependency which isn’t hashed into the cache as a trap, and if code hits that trap, then compilation stops, it signals back to the build system, which reconstructs everything using that dependency.
Maybe it should finish Sema and then fail, it would be tedious if there were fifteen dependencies and the code actually uses all of them, and the build took sixteen tries to succeed. Even for a first time build, that isn’t ideal.
It would be a good outcome though, I’m fairly convinced of that. If someone offers a module a PR which integrates some other beast of a codebase with some optional features, it’s easier to say yes to that if you know that users who don’t employ those features are never going to download them to begin with.