The build system generates a file, dependencies.zig, which contains information about all fetched packages (more details in this recent answer of mine). This file is exposed by the build runner and can be accessed from your build script:
const build_runner = @import("root");
const packages = build_runner.dependencies.packages;
inline for (@typeInfo(packages).@"struct".decl_names |hash| {
const package = @field(packages, hash);
// Check for a 'const available = false;' decl;
// a package that is a lazy dependency might not have been fetched yet.
const available = !@hasDecl(package, "available") or package.available;
if (available) {
const build_zig_zon_path = b.pathJoin(&.{ package.build_root, "build.zig.zon" });
// ...Do something with 'build_zig_zon_path'...
}
}
This way you can programmatically iterate over all potential build.zig.zon paths for all packages.
Do with this whatever you want; for example, you could compile a utility Zig program as part of your build for processing information in build.zig.zon files, run it with b.runArtifact and pass all paths as arguments to the run step.
Note that the above loop won’t cover the root build.zig zon, which you will need to include in your processing logic manually. Also note that build.zig.zon is optional and may not exist for all packages, so your processing logic will need to account for that possibility.