This is actually the implementation of workspaces. Thanks.
You can also think about introducing an additional environment variable, for example:
$ ZIG_FORK=[path] zig build
And do you have any ideas to generalize this, as well as the manifest format itself, to all build commands: build-exe, build-lib, build-obj, test, test-obj, run?
So that its can all use the same format to manage any type of build and compilation in a single way.
For example, instead of a long and strange one
$ zig run --dep somedep Mroot="main.zig" -Msomedep="somepath_to_somedep"
one could write
$ zig run --fork="somepath_to_build.zig.zon" main.zig
or
$ ZIG_FORK=[somepath_to_build.zig.zon] zig run main.zig
my understanding is that zig build-exe must receive a command line argument (I believe an -M flag?) for each module that it discovers as an import during compilation. so if you drop below the build system and into the quote-un-quote rustc layer, you should just override those arguments.
in other words, it is not helpful to think of zig build-exe as a “mode of the build system”. you should instead think of it as “the compiler layer”
Okay, but the underlying package management subsystem itself is still essentially the same for all commands, isn’t it?
This is how it’s done in Rust, and in Golang, it allows devs to transparently manage the build in any mode.
no, to my knowledge none of the fundamental commands use any logic from build.zig or build.zig.zon, in this way they form one group and zig build forms a completely separate group.
better than Go or Rust, perhaps the right comparison is that the “fundamental commands” are akin to executing clang or gcc on the command line, while zig build is like executing cmake, make or any number of other tools added on top of a C compiler to give C the build system that it does not have.
It’s not about how it’s implemented now, it’s about how it should ideally be implemented.
But Zig already has a package subsystem, it cannot be hidden, it can only be made unified and applicable to all modes.
Of course, it’s very strange if I come first with this obvious idea.
i think the way it works is fine. in reference to your other thread, the fundamental modes are “safe” in that in every well-formed invocation they are compiling code that is on your machine in a location that you are telling them. they should probably continue in the way that they currently are because they fulfill very useful tasks; similar to how in 2026 you can still execute clang and gcc on the command line.
i believe instead of unifying, you’re actually speaking to a desire for a tool like cargo or whatever Golang uses (I haven’t written Go). A tool like cargo would sit one layer above the Zig Build System and try to restore the trust that (understandably) is broken by zig build’s ability to: 1) fetch arbitrary tarballs and 2) execute arbitrary code, including code within those tarballs.
I think that at the moment, most active projects in Zig are not interested in such a tool—for example at the meetup here in NYC, we were talking about transitive dependencies as something that’s a pain point currently but also surprisingly uncommon in all of our uses of Zig.
I wouldn’t be surprised if the appetite for a tool similar to cargo or to one of Go’s build modes were to develop later on, though!
Yes, of course it’s, but I’m not suggesting that we implement all the functions of cargo. First, I suggest unifying all modes into a single transparent package subsystem with a unified manifest format such as build.zig.zon.
But do you realize that this is a very dangerous misconception if you’re seriously counting on the global development of the language?
In Golang, unlike Rust, they first created a very poor package system, and then they rewrote it for a long time in order to eventually turn it into its current state. In fact, there is still a very poor implementation inside, but from the outside it seems almost perfect.
So you need to immediately rely on the fact that everyone will want to use a lot of dependencies from each other, and at the same time require that the build/compilation process be completely safe.
What I was trying to suggest above, is that what you are describing exists: it is called zig build.
the “fundamental modes” zig build-exezig build-libzig build-objzig test and zig run are “dumb” in that they have no knowledge of packages. when they see an @import() statement, they resolve it either by treating the string inside as a path relative to the current source location, or by using a lookup (provided on the command line) to match the given name to a file on disk. when run in these modes, the compiler does not use the network.
in this way, these modes are like executing gcc or clang on the command line. when you run gcc or clang you have to provide a whole host of -I and -L flags to tell the compiler where to look for files that are not in the current directory; -M commands and --dep commands are the Zig flavoring of that.
I personally do not want the fundamental modes to gain knowledge of packages. that would be a regression.
I’m not counting on anything! I’m a programmer who loves Zig. I will use it as long as it works, and hopefully I’ll be part of helping “as long as it works” be “a long time”.
This is an unsecure mode, and that says it all. When a project has a huge number of transitive dependencies, and all of them can execute arbitrary code, this is a big problem. At some point, Rust’s team realized this, but it was too late.
Many modes have been prudently created here, and their meaning differs from the main zig build one, but its will still have to be unified sooner or later. I’m sure that as the popularity of the language grows, more and more devs will see these security and inconsistency issues. And this cannot be solved by containerization or by special wasm modes alone.
I absolutely don’t understand why you think that. Anyway, for any build, you need to know all the dependencies. Therefore, the sooner everything is unified, the better.
I believe that the “fundamental modes” should remain unaware of packages and the build system because they are the most common invocation resulting from usage of the zig build system.
in other words, as described at the link above, the main thing that zig build does is the following:
compiles an exe called build which runs and does the rest of the following:
in the “configure” phase, a DAG of dependencies is assembled according the logic in build.zig
fetch any missing dependencies (i haven’t looked at the implementation, but one helpful way to think of how this would be implemented would be to run zig fetch as a subprocess)
in the “make” phase, a series of invocations of zig build-exe or zig build-lib or zig test is run (again, a helpful way to think about this is that those commands are being called as subprocesses, although i do not know whether that is literally the case).
in other words, zig build itself compiles only build.zig. any further compiled code is compiled by using one of the fundamental commands.
what this means is that by the time zig build-exe is called, the concepts of packages, network connection, and everything else is not necessary to the job of the running process, and so should not be introduced.
Not quite; please read the linked documentation, which explains this: zig build run is a convenience which is by convention commonly added by authors of build.zig code.
From what I see, there are a lot of artificial restrictions.
So let’s look at this with a simple example.
Here is an example project consisting of the following parts: /somepath1/proj/ /somepath1/proj/main.zig /somepath2/vendor1/dep1/ /somepath2/vendor1/dep1/dep1.zig /somepath2/vendor2/dep2/ /somepath2/vendor2/dep2/src/dep2.zig /somepath3/build.zig /somepath4/build.zig.zon
given the current status quo this is impossible in general, because it requires specifics about the content of build.zig and build.zig.zon that you have not specified
Instead of you showing up here and saying everything should be simpler than it currently is, ignoring that it has inherent complexity that is there for a valid reasons and then expecting us to explain this to you (forcing us to have conversations that have been had over and over again), I want to turn this around.
If you want to claim that it is simpler than make your case, instead of just saying it is simpler and expecting us to disprove you, you need to make a valid argument to begin with. Beyond saying I have used a language where I can do this and ignoring the reasons why it is different in Zig (and expecting us to invest the effort to disprove your low effort claims).
No
The goal is to show exactly what they should look like. Hashes and fingerprints can be omitted.
Just here’s an example of how this launch looks like via zig run:
zig run
--dep dep1
--dep dep2
-Mroot="/somepath1/proj/main.zig"
-Mdep1="/somepath2/vendor1/dep1/dep1.zig"
-Mdep2="/somepath2/vendor2/dep2/src/dep2.zig"
Yes, it takes away many of the features available through zig build. But still, there should be a relatively simple way so that every user can easy repeat the option from zig run.