authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-01-10 20:21:58-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-01-11 15:39:49-08:00
logcfcf9771c1bde357ad64d81cda9d61ba72d80b15
tree143a7434666e186bc79989c9108fdc3ab1c16741
parenta0f2e6a29f4d5c084a248d24b25fae9f30707001

zig build: support dependencies

The `zig build` command now makes `@import("@dependencies")` available to the build runner package. It contains all the dependencies in a generated file that looks something like this: ```zig pub const imports = struct { pub const foo = @import("foo"); pub const @"bar.baz" = @import("bar.baz"); }; pub const build_root = struct { pub const foo = "<path>"; pub const @"bar.baz" = "<path>"; }; ``` The build runner exports this import so that `std.build.Builder` can access it. `std.build.Builder` uses it to implement the new `dependency` function which can be used like so: ```zig const libz_dep = b.dependency("libz", .{}); const libmp3lame_dep = b.dependency("libmp3lame", .{}); // ... lib.linkLibrary(libz_dep.artifact("z")); lib.linkLibrary(libmp3lame_dep.artifact("mp3lame")); ``` The `dependency` function calls the build.zig file of the dependency as a child Builder, and then can be ransacked for its build steps via the `artifact` function. This commit also renames `dependency.id` to `dependency.name` in the `build.zig.ini` file.

4 files changed, 297 insertions(+), 57 deletions(-)

lib/build_runner.zig+4-10
......@@ -9,6 +9,8 @@ const process = std.process;
99const ArrayList = std.ArrayList;
1010const File = std.fs.File;
1111
12pub const dependencies = @import("@dependencies");
13
1214pub fn main() !void {
1315 // Here we use an ArenaAllocator backed by a DirectAllocator because a build is a short-lived,
1416 // one shot program. We don't need to waste time freeing memory and finding places to squish
......@@ -207,7 +209,7 @@ pub fn main() !void {
207209
208210 builder.debug_log_scopes = debug_log_scopes.items;
209211 builder.resolveInstallPrefix(install_prefix, dir_list);
210 try runBuild(builder);
212 try builder.runBuild(root);
211213
212214 if (builder.validateUserInputDidItFail())
213215 return usageAndErr(builder, true, stderr_stream);
......@@ -223,19 +225,11 @@ pub fn main() !void {
223225 };
224226}
225227
226fn runBuild(builder: *Builder) anyerror!void {
227 switch (@typeInfo(@typeInfo(@TypeOf(root.build)).Fn.return_type.?)) {
228 .Void => root.build(builder),
229 .ErrorUnion => try root.build(builder),
230 else => @compileError("expected return type of build to be 'void' or '!void'"),
231 }
232}
233
234228fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void {
235229 // run the build script to collect the options
236230 if (!already_ran_build) {
237231 builder.resolveInstallPrefix(null, .{});
238 try runBuild(builder);
232 try builder.runBuild(root);
239233 }
240234
241235 try out_stream.print(
lib/std/build.zig+152-2
......@@ -69,13 +69,15 @@ pub const Builder = struct {
6969 search_prefixes: ArrayList([]const u8),
7070 libc_file: ?[]const u8 = null,
7171 installed_files: ArrayList(InstalledFile),
72 /// Path to the directory containing build.zig.
7273 build_root: []const u8,
7374 cache_root: []const u8,
7475 global_cache_root: []const u8,
7576 release_mode: ?std.builtin.Mode,
7677 is_release: bool,
78 /// zig lib dir
7779 override_lib_dir: ?[]const u8,
78 vcpkg_root: VcpkgRoot,
80 vcpkg_root: VcpkgRoot = .unattempted,
7981 pkg_config_pkg_list: ?(PkgConfigError![]const PkgConfigPkg) = null,
8082 args: ?[][]const u8 = null,
8183 debug_log_scopes: []const []const u8 = &.{},
......@@ -100,6 +102,8 @@ pub const Builder = struct {
100102 /// Information about the native target. Computed before build() is invoked.
101103 host: NativeTargetInfo,
102104
105 dep_prefix: []const u8 = "",
106
103107 pub const ExecError = error{
104108 ReadFailure,
105109 ExitCodeFailure,
......@@ -223,7 +227,6 @@ pub const Builder = struct {
223227 .is_release = false,
224228 .override_lib_dir = null,
225229 .install_path = undefined,
226 .vcpkg_root = VcpkgRoot{ .unattempted = {} },
227230 .args = null,
228231 .host = host,
229232 };
......@@ -233,6 +236,89 @@ pub const Builder = struct {
233236 return self;
234237 }
235238
239 fn createChild(
240 parent: *Builder,
241 dep_name: []const u8,
242 build_root: []const u8,
243 args: anytype,
244 ) !*Builder {
245 const child = try createChildOnly(parent, dep_name, build_root);
246 try applyArgs(child, args);
247 return child;
248 }
249
250 fn createChildOnly(parent: *Builder, dep_name: []const u8, build_root: []const u8) !*Builder {
251 const allocator = parent.allocator;
252 const child = try allocator.create(Builder);
253 child.* = .{
254 .allocator = allocator,
255 .install_tls = .{
256 .step = Step.initNoOp(.top_level, "install", allocator),
257 .description = "Copy build artifacts to prefix path",
258 },
259 .uninstall_tls = .{
260 .step = Step.init(.top_level, "uninstall", allocator, makeUninstall),
261 .description = "Remove build artifacts from prefix path",
262 },
263 .user_input_options = UserInputOptionsMap.init(allocator),
264 .available_options_map = AvailableOptionsMap.init(allocator),
265 .available_options_list = ArrayList(AvailableOption).init(allocator),
266 .verbose = parent.verbose,
267 .verbose_link = parent.verbose_link,
268 .verbose_cc = parent.verbose_cc,
269 .verbose_air = parent.verbose_air,
270 .verbose_llvm_ir = parent.verbose_llvm_ir,
271 .verbose_cimport = parent.verbose_cimport,
272 .verbose_llvm_cpu_features = parent.verbose_llvm_cpu_features,
273 .prominent_compile_errors = parent.prominent_compile_errors,
274 .color = parent.color,
275 .reference_trace = parent.reference_trace,
276 .invalid_user_input = false,
277 .zig_exe = parent.zig_exe,
278 .default_step = undefined,
279 .env_map = parent.env_map,
280 .top_level_steps = ArrayList(*TopLevelStep).init(allocator),
281 .install_prefix = undefined,
282 .dest_dir = parent.dest_dir,
283 .lib_dir = parent.lib_dir,
284 .exe_dir = parent.exe_dir,
285 .h_dir = parent.h_dir,
286 .install_path = parent.install_path,
287 .sysroot = parent.sysroot,
288 .search_prefixes = ArrayList([]const u8).init(allocator),
289 .libc_file = parent.libc_file,
290 .installed_files = ArrayList(InstalledFile).init(allocator),
291 .build_root = build_root,
292 .cache_root = parent.cache_root,
293 .global_cache_root = parent.global_cache_root,
294 .release_mode = parent.release_mode,
295 .is_release = parent.is_release,
296 .override_lib_dir = parent.override_lib_dir,
297 .debug_log_scopes = parent.debug_log_scopes,
298 .debug_compile_errors = parent.debug_compile_errors,
299 .enable_darling = parent.enable_darling,
300 .enable_qemu = parent.enable_qemu,
301 .enable_rosetta = parent.enable_rosetta,
302 .enable_wasmtime = parent.enable_wasmtime,
303 .enable_wine = parent.enable_wine,
304 .glibc_runtimes_dir = parent.glibc_runtimes_dir,
305 .host = parent.host,
306 .dep_prefix = parent.fmt("{s}{s}.", .{ parent.dep_prefix, dep_name }),
307 };
308 try child.top_level_steps.append(&child.install_tls);
309 try child.top_level_steps.append(&child.uninstall_tls);
310 child.default_step = &child.install_tls.step;
311 return child;
312 }
313
314 pub fn applyArgs(b: *Builder, args: anytype) !void {
315 // TODO this function is the way that a build.zig file communicates
316 // options to its dependencies. It is the programmatic way to give
317 // command line arguments to a build.zig script.
318 _ = b;
319 _ = args;
320 }
321
236322 pub fn destroy(self: *Builder) void {
237323 self.env_map.deinit();
238324 self.top_level_steps.deinit();
......@@ -1300,6 +1386,70 @@ pub const Builder = struct {
13001386 &[_][]const u8{ base_dir, dest_rel_path },
13011387 ) catch unreachable;
13021388 }
1389
1390 pub const Dependency = struct {
1391 builder: *Builder,
1392
1393 pub fn artifact(d: *Dependency, name: []const u8) *LibExeObjStep {
1394 var found: ?*LibExeObjStep = null;
1395 for (d.builder.install_tls.step.dependencies.items) |dep_step| {
1396 const inst = dep_step.cast(InstallArtifactStep) orelse continue;
1397 if (mem.eql(u8, inst.artifact.name, name)) {
1398 if (found != null) panic("artifact name '{s}' is ambiguous", .{name});
1399 found = inst.artifact;
1400 }
1401 }
1402 return found orelse {
1403 for (d.builder.install_tls.step.dependencies.items) |dep_step| {
1404 const inst = dep_step.cast(InstallArtifactStep) orelse continue;
1405 log.info("available artifact: '{s}'", .{inst.artifact.name});
1406 }
1407 panic("unable to find artifact '{s}'", .{name});
1408 };
1409 }
1410 };
1411
1412 pub fn dependency(b: *Builder, name: []const u8, args: anytype) *Dependency {
1413 const build_runner = @import("root");
1414 const deps = build_runner.dependencies;
1415
1416 inline for (@typeInfo(deps.imports).Struct.decls) |decl| {
1417 if (mem.startsWith(u8, decl.name, b.dep_prefix) and
1418 mem.endsWith(u8, decl.name, name) and
1419 decl.name.len == b.dep_prefix.len + name.len)
1420 {
1421 const build_zig = @field(deps.imports, decl.name);
1422 const build_root = @field(deps.build_root, decl.name);
1423 return dependencyInner(b, name, build_root, build_zig, args);
1424 }
1425 }
1426
1427 const full_path = b.pathFromRoot("build.zig.ini");
1428 std.debug.print("no dependency named '{s}' in '{s}'\n", .{ name, full_path });
1429 std.process.exit(1);
1430 }
1431
1432 fn dependencyInner(
1433 b: *Builder,
1434 name: []const u8,
1435 build_root: []const u8,
1436 comptime build_zig: type,
1437 args: anytype,
1438 ) *Dependency {
1439 const sub_builder = b.createChild(name, build_root, args) catch unreachable;
1440 sub_builder.runBuild(build_zig) catch unreachable;
1441 const dep = b.allocator.create(Dependency) catch unreachable;
1442 dep.* = .{ .builder = sub_builder };
1443 return dep;
1444 }
1445
1446 pub fn runBuild(b: *Builder, build_zig: anytype) anyerror!void {
1447 switch (@typeInfo(@typeInfo(@TypeOf(build_zig.build)).Fn.return_type.?)) {
1448 .Void => build_zig.build(b),
1449 .ErrorUnion => try build_zig.build(b),
1450 else => @compileError("expected return type of build to be 'void' or '!void'"),
1451 }
1452 }
13031453};
13041454
13051455test "builder.findProgram compiles" {
src/Package.zig+99-34
......@@ -12,6 +12,8 @@ const Compilation = @import("Compilation.zig");
1212const Module = @import("Module.zig");
1313const ThreadPool = @import("ThreadPool.zig");
1414const WaitGroup = @import("WaitGroup.zig");
15const Cache = @import("Cache.zig");
16const build_options = @import("build_options");
1517
1618pub const Table = std.StringHashMapUnmanaged(*Package);
1719
......@@ -139,6 +141,9 @@ pub fn fetchAndAddDependencies(
139141 directory: Compilation.Directory,
140142 global_cache_directory: Compilation.Directory,
141143 local_cache_directory: Compilation.Directory,
144 dependencies_source: *std.ArrayList(u8),
145 build_roots_source: *std.ArrayList(u8),
146 name_prefix: []const u8,
142147) !void {
143148 const max_bytes = 10 * 1024 * 1024;
144149 const gpa = thread_pool.allocator;
......@@ -156,15 +161,15 @@ pub fn fetchAndAddDependencies(
156161 var it = ini.iterateSection("\n[dependency]\n");
157162 while (it.next()) |dep| {
158163 var line_it = mem.split(u8, dep, "\n");
159 var opt_id: ?[]const u8 = null;
164 var opt_name: ?[]const u8 = null;
160165 var opt_url: ?[]const u8 = null;
161166 var expected_hash: ?[]const u8 = null;
162167 while (line_it.next()) |kv| {
163168 const eq_pos = mem.indexOfScalar(u8, kv, '=') orelse continue;
164169 const key = kv[0..eq_pos];
165170 const value = kv[eq_pos + 1 ..];
166 if (mem.eql(u8, key, "id")) {
167 opt_id = value;
171 if (mem.eql(u8, key, "name")) {
172 opt_name = value;
168173 } else if (mem.eql(u8, key, "url")) {
169174 opt_url = value;
170175 } else if (mem.eql(u8, key, "hash")) {
......@@ -181,9 +186,9 @@ pub fn fetchAndAddDependencies(
181186 }
182187 }
183188
184 const id = opt_id orelse {
189 const name = opt_name orelse {
185190 const loc = std.zig.findLineColumn(ini.bytes, @ptrToInt(dep.ptr) - @ptrToInt(ini.bytes.ptr));
186 std.log.err("{s}/{s}:{d}:{d} missing key: 'id'", .{
191 std.log.err("{s}/{s}:{d}:{d} missing key: 'name'", .{
187192 directory.path orelse ".",
188193 "build.zig.ini",
189194 loc.line,
......@@ -195,7 +200,7 @@ pub fn fetchAndAddDependencies(
195200
196201 const url = opt_url orelse {
197202 const loc = std.zig.findLineColumn(ini.bytes, @ptrToInt(dep.ptr) - @ptrToInt(ini.bytes.ptr));
198 std.log.err("{s}/{s}:{d}:{d} missing key: 'id'", .{
203 std.log.err("{s}/{s}:{d}:{d} missing key: 'name'", .{
199204 directory.path orelse ".",
200205 "build.zig.ini",
201206 loc.line,
......@@ -205,6 +210,10 @@ pub fn fetchAndAddDependencies(
205210 continue;
206211 };
207212
213 const sub_prefix = try std.fmt.allocPrint(gpa, "{s}{s}.", .{ name_prefix, name });
214 defer gpa.free(sub_prefix);
215 const fqn = sub_prefix[0 .. sub_prefix.len - 1];
216
208217 const sub_pkg = try fetchAndUnpack(
209218 thread_pool,
210219 http_client,
......@@ -213,22 +222,56 @@ pub fn fetchAndAddDependencies(
213222 expected_hash,
214223 ini,
215224 directory,
225 build_roots_source,
226 fqn,
216227 );
217228
218 try sub_pkg.fetchAndAddDependencies(
229 try pkg.fetchAndAddDependencies(
219230 thread_pool,
220231 http_client,
221232 sub_pkg.root_src_directory,
222233 global_cache_directory,
223234 local_cache_directory,
235 dependencies_source,
236 build_roots_source,
237 sub_prefix,
224238 );
225239
226 try addAndAdopt(pkg, gpa, id, sub_pkg);
240 try addAndAdopt(pkg, gpa, fqn, sub_pkg);
241
242 try dependencies_source.writer().print(" pub const {s} = @import(\"{}\");\n", .{
243 std.zig.fmtId(fqn), std.zig.fmtEscapes(fqn),
244 });
227245 }
228246
229247 if (any_error) return error.InvalidBuildZigIniFile;
230248}
231249
250pub fn createFilePkg(
251 gpa: Allocator,
252 global_cache_directory: Compilation.Directory,
253 basename: []const u8,
254 contents: []const u8,
255) !*Package {
256 const rand_int = std.crypto.random.int(u64);
257 const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ hex64(rand_int);
258 {
259 var tmp_dir = try global_cache_directory.handle.makeOpenPath(tmp_dir_sub_path, .{});
260 defer tmp_dir.close();
261 try tmp_dir.writeFile(basename, contents);
262 }
263
264 var hh: Cache.HashHelper = .{};
265 hh.addBytes(build_options.version);
266 hh.addBytes(contents);
267 const hex_digest = hh.final();
268
269 const o_dir_sub_path = "o" ++ fs.path.sep_str ++ hex_digest;
270 try renameTmpIntoCache(global_cache_directory.handle, tmp_dir_sub_path, o_dir_sub_path);
271
272 return createWithDir(gpa, global_cache_directory, o_dir_sub_path, basename);
273}
274
232275fn fetchAndUnpack(
233276 thread_pool: *ThreadPool,
234277 http_client: *std.http.Client,
......@@ -237,6 +280,8 @@ fn fetchAndUnpack(
237280 expected_hash: ?[]const u8,
238281 ini: std.Ini,
239282 comp_directory: Compilation.Directory,
283 build_roots_source: *std.ArrayList(u8),
284 fqn: []const u8,
240285) !*Package {
241286 const gpa = http_client.allocator;
242287 const s = fs.path.sep_str;
......@@ -267,14 +312,22 @@ fn fetchAndUnpack(
267312 const owned_src_path = try gpa.dupe(u8, build_zig_basename);
268313 errdefer gpa.free(owned_src_path);
269314
315 const build_root = try global_cache_directory.join(gpa, &.{pkg_dir_sub_path});
316 errdefer gpa.free(build_root);
317
318 try build_roots_source.writer().print(" pub const {s} = \"{}\";\n", .{
319 std.zig.fmtId(fqn), std.zig.fmtEscapes(build_root),
320 });
321
270322 ptr.* = .{
271323 .root_src_directory = .{
272 .path = try global_cache_directory.join(gpa, &.{pkg_dir_sub_path}),
324 .path = build_root,
273325 .handle = pkg_dir,
274326 },
275327 .root_src_directory_owned = true,
276328 .root_src_path = owned_src_path,
277329 };
330
278331 return ptr;
279332 }
280333
......@@ -331,31 +384,7 @@ fn fetchAndUnpack(
331384 };
332385
333386 const pkg_dir_sub_path = "p" ++ s ++ hexDigest(actual_hash);
334
335 {
336 // Rename the temporary directory into the global package cache.
337 var handled_missing_dir = false;
338 while (true) {
339 global_cache_directory.handle.rename(tmp_dir_sub_path, pkg_dir_sub_path) catch |err| switch (err) {
340 error.FileNotFound => {
341 if (handled_missing_dir) return err;
342 global_cache_directory.handle.makeDir("p") catch |mkd_err| switch (mkd_err) {
343 error.PathAlreadyExists => handled_missing_dir = true,
344 else => |e| return e,
345 };
346 continue;
347 },
348 error.PathAlreadyExists => {
349 // Package has been already downloaded and may already be in use on the system.
350 global_cache_directory.handle.deleteTree(tmp_dir_sub_path) catch |del_err| {
351 std.log.warn("unable to delete temp directory: {s}", .{@errorName(del_err)});
352 };
353 },
354 else => |e| return e,
355 };
356 break;
357 }
358 }
387 try renameTmpIntoCache(global_cache_directory.handle, tmp_dir_sub_path, pkg_dir_sub_path);
359388
360389 if (expected_hash) |h| {
361390 const actual_hex = hexDigest(actual_hash);
......@@ -378,6 +407,13 @@ fn fetchAndUnpack(
378407 );
379408 }
380409
410 const build_root = try global_cache_directory.join(gpa, &.{pkg_dir_sub_path});
411 defer gpa.free(build_root);
412
413 try build_roots_source.writer().print(" pub const {s} = \"{}\";\n", .{
414 std.zig.fmtId(fqn), std.zig.fmtEscapes(build_root),
415 });
416
381417 return createWithDir(gpa, global_cache_directory, pkg_dir_sub_path, build_zig_basename);
382418}
383419
......@@ -516,3 +552,32 @@ fn hexDigest(digest: [Hash.digest_length]u8) [Hash.digest_length * 2]u8 {
516552 }
517553 return result;
518554}
555
556fn renameTmpIntoCache(
557 cache_dir: fs.Dir,
558 tmp_dir_sub_path: []const u8,
559 dest_dir_sub_path: []const u8,
560) !void {
561 assert(dest_dir_sub_path[1] == '/');
562 var handled_missing_dir = false;
563 while (true) {
564 cache_dir.rename(tmp_dir_sub_path, dest_dir_sub_path) catch |err| switch (err) {
565 error.FileNotFound => {
566 if (handled_missing_dir) return err;
567 cache_dir.makeDir(dest_dir_sub_path[0..1]) catch |mkd_err| switch (mkd_err) {
568 error.PathAlreadyExists => handled_missing_dir = true,
569 else => |e| return e,
570 };
571 continue;
572 },
573 error.PathAlreadyExists => {
574 // Package has been already downloaded and may already be in use on the system.
575 cache_dir.deleteTree(tmp_dir_sub_path) catch |del_err| {
576 std.log.warn("unable to delete temp directory: {s}", .{@errorName(del_err)});
577 };
578 },
579 else => |e| return e,
580 };
581 break;
582 }
583}
src/main.zig+42-11
......@@ -3983,11 +3983,6 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
39833983 };
39843984 defer zig_lib_directory.handle.close();
39853985
3986 var main_pkg: Package = .{
3987 .root_src_directory = zig_lib_directory,
3988 .root_src_path = "build_runner.zig",
3989 };
3990
39913986 var cleanup_build_dir: ?fs.Dir = null;
39923987 defer if (cleanup_build_dir) |*dir| dir.close();
39933988
......@@ -4031,12 +4026,6 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
40314026 };
40324027 child_argv.items[argv_index_build_file] = build_directory.path orelse cwd_path;
40334028
4034 var build_pkg: Package = .{
4035 .root_src_directory = build_directory,
4036 .root_src_path = build_zig_basename,
4037 };
4038 try main_pkg.addAndAdopt(arena, "@build", &build_pkg);
4039
40404029 var global_cache_directory: Compilation.Directory = l: {
40414030 const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena);
40424031 break :l .{
......@@ -4083,23 +4072,65 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
40834072 try thread_pool.init(gpa);
40844073 defer thread_pool.deinit();
40854074
4075 var main_pkg: Package = .{
4076 .root_src_directory = zig_lib_directory,
4077 .root_src_path = "build_runner.zig",
4078 };
4079
40864080 if (!build_options.omit_pkg_fetching_code) {
40874081 var http_client: std.http.Client = .{ .allocator = gpa };
40884082 defer http_client.deinit();
40894083 try http_client.rescanRootCertificates();
40904084
4085 // Here we provide an import to the build runner that allows using reflection to find
4086 // all of the dependencies. Without this, there would be no way to use `@import` to
4087 // access dependencies by name, since `@import` requires string literals.
4088 var dependencies_source = std.ArrayList(u8).init(gpa);
4089 defer dependencies_source.deinit();
4090 try dependencies_source.appendSlice("pub const imports = struct {\n");
4091
4092 // This will go into the same package. It contains the file system paths
4093 // to all the build.zig files.
4094 var build_roots_source = std.ArrayList(u8).init(gpa);
4095 defer build_roots_source.deinit();
4096
4097 // Here we borrow main package's table and will replace it with a fresh
4098 // one after this process completes.
40914099 main_pkg.fetchAndAddDependencies(
40924100 &thread_pool,
40934101 &http_client,
40944102 build_directory,
40954103 global_cache_directory,
40964104 local_cache_directory,
4105 &dependencies_source,
4106 &build_roots_source,
4107 "",
40974108 ) catch |err| switch (err) {
40984109 error.PackageFetchFailed => process.exit(1),
40994110 else => |e| return e,
41004111 };
4112
4113 try dependencies_source.appendSlice("};\npub const build_root = struct {\n");
4114 try dependencies_source.appendSlice(build_roots_source.items);
4115 try dependencies_source.appendSlice("};\n");
4116
4117 const deps_pkg = try Package.createFilePkg(
4118 gpa,
4119 global_cache_directory,
4120 "dependencies.zig",
4121 dependencies_source.items,
4122 );
4123
4124 mem.swap(Package.Table, &main_pkg.table, &deps_pkg.table);
4125 try main_pkg.addAndAdopt(gpa, "@dependencies", deps_pkg);
41014126 }
41024127
4128 var build_pkg: Package = .{
4129 .root_src_directory = build_directory,
4130 .root_src_path = build_zig_basename,
4131 };
4132 try main_pkg.addAndAdopt(gpa, "@build", &build_pkg);
4133
41034134 const comp = Compilation.create(gpa, .{
41044135 .zig_lib_directory = zig_lib_directory,
41054136 .local_cache_directory = local_cache_directory,