authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-12-11 19:03:32+00:00
committergravatar for bratishkaerik@landless-city.netEric Joldasov <bratishkaerik@landless-city.net> 2024-12-18 01:47:51+05:00
log0bb93ca053f9520a396a652e929d2cc6358ec5be
tree66dda984696ea9f46dee15c0be4af0cd17e70d66
parentb83b161f4b2102e8f10ab84f1c99def352554dd2
signaturelock-open Commit is signed but in an unrecognized format.

std.Build: simplify module dependency handling

At the expense of a slight special case in the build runner, we can make the handling of dependencies between modules a little shorter and much easier to follow. When module and step graphs are being constructed during the "configure" phase, we do not set up step dependencies triggered by modules. Instead, after the configure phase, the build runner traverses the whole step/module graph, starting from the root top-level steps, and configures all step dependencies implied by modules. The "make" phase then proceeds as normal. Also, the old `Module.dependencyIterator` logic is replaced by two separate iterables. `Module.getGraph` takes the root module of a compilation, and returns all modules in its graph; while `Step.Compile.getCompileDependencies` takes a `*Step.Compile` and returns all `*Step.Compile` it depends on, recursively, possibly excluding dynamic libraries. The old `Module.dependencyIterator` combined these two functions into one unintuitive iterator; they are now separated, which in particular helps readability at the usage sites which only need one or the other.

4 files changed, 407 insertions(+), 440 deletions(-)

lib/compiler/build_runner.zig+78
......@@ -337,6 +337,7 @@ pub fn main() !void {
337337 var prog_node = main_progress_node.start("Configure", 0);
338338 defer prog_node.end();
339339 try builder.runBuild(root);
340 createModuleDependencies(builder) catch @panic("OOM");
340341 }
341342
342343 if (graph.needed_lazy_dependencies.entries.len != 0) {
......@@ -1440,3 +1441,80 @@ fn validateSystemLibraryOptions(b: *std.Build) void {
14401441 process.exit(1);
14411442 }
14421443}
1444
1445/// Starting from all top-level steps in `b`, traverses the entire step graph
1446/// and adds all step dependencies implied by module graphs.
1447fn createModuleDependencies(b: *std.Build) Allocator.Error!void {
1448 const arena = b.graph.arena;
1449
1450 var all_steps: std.AutoArrayHashMapUnmanaged(*Step, void) = .empty;
1451 var next_step_idx: usize = 0;
1452
1453 try all_steps.ensureUnusedCapacity(arena, b.top_level_steps.count());
1454 for (b.top_level_steps.values()) |tls| {
1455 all_steps.putAssumeCapacityNoClobber(&tls.step, {});
1456 }
1457
1458 while (next_step_idx < all_steps.count()) {
1459 const step = all_steps.keys()[next_step_idx];
1460 next_step_idx += 1;
1461
1462 // Set up any implied dependencies for this step. It's important that we do this first, so
1463 // that the loop below discovers steps implied by the module graph.
1464 try createModuleDependenciesForStep(step);
1465
1466 try all_steps.ensureUnusedCapacity(arena, step.dependencies.items.len);
1467 for (step.dependencies.items) |other_step| {
1468 all_steps.putAssumeCapacity(other_step, {});
1469 }
1470 }
1471}
1472
1473/// If the given `Step` is a `Step.Compile`, adds any dependencies for that step which
1474/// are implied by the module graph rooted at `step.cast(Step.Compile).?.root_module`.
1475fn createModuleDependenciesForStep(step: *Step) Allocator.Error!void {
1476 const root_module = if (step.cast(Step.Compile)) |cs| root: {
1477 break :root cs.root_module;
1478 } else return; // not a compile step so no module dependencies
1479
1480 // Starting from `root_module`, discover all modules in this graph.
1481 const modules = root_module.getGraph().modules;
1482
1483 // For each of those modules, set up the implied step dependencies.
1484 for (modules) |mod| {
1485 if (mod.root_source_file) |lp| lp.addStepDependencies(step);
1486 for (mod.include_dirs.items) |include_dir| switch (include_dir) {
1487 .path,
1488 .path_system,
1489 .path_after,
1490 .framework_path,
1491 .framework_path_system,
1492 => |lp| lp.addStepDependencies(step),
1493
1494 .other_step => |other| {
1495 other.getEmittedIncludeTree().addStepDependencies(step);
1496 step.dependOn(&other.step);
1497 },
1498
1499 .config_header_step => |other| step.dependOn(&other.step),
1500 };
1501 for (mod.lib_paths.items) |lp| lp.addStepDependencies(step);
1502 for (mod.rpaths.items) |rpath| switch (rpath) {
1503 .lazy_path => |lp| lp.addStepDependencies(step),
1504 .special => {},
1505 };
1506 for (mod.link_objects.items) |link_object| switch (link_object) {
1507 .static_path,
1508 .assembly_file,
1509 => |lp| lp.addStepDependencies(step),
1510 .other_step => |other| step.dependOn(&other.step),
1511 .system_lib => {},
1512 .c_source_file => |source| source.file.addStepDependencies(step),
1513 .c_source_files => |source_files| source_files.root.addStepDependencies(step),
1514 .win32_resource_file => |rc_source| {
1515 rc_source.file.addStepDependencies(step);
1516 for (rc_source.include_paths) |lp| lp.addStepDependencies(step);
1517 },
1518 };
1519 }
1520}
lib/std/Build/Module.zig+45-182
......@@ -1,9 +1,5 @@
11/// The one responsible for creating this module.
22owner: *std.Build,
3/// Tracks the set of steps that depend on this `Module`. This ensures that
4/// when making this `Module` depend on other `Module` objects and `Step`
5/// objects, respective `Step` dependencies can be added.
6depending_steps: std.AutoArrayHashMapUnmanaged(*Step.Compile, void),
73root_source_file: ?LazyPath,
84/// The modules that are mapped into this module's import table.
95/// Use `addImport` rather than modifying this field directly in order to
......@@ -41,6 +37,10 @@ link_libcpp: ?bool,
4137/// Symbols to be exported when compiling to WebAssembly.
4238export_symbol_names: []const []const u8 = &.{},
4339
40/// Caches the result of `getGraph` when called multiple times.
41/// Use `getGraph` instead of accessing this field directly.
42cached_graph: Graph = .{ .modules = &.{}, .names = &.{} },
43
4444pub const RPath = union(enum) {
4545 lazy_path: LazyPath,
4646 special: []const u8,
......@@ -246,7 +246,6 @@ pub fn init(
246246 m: *Module,
247247 owner: *std.Build,
248248 value: union(enum) { options: CreateOptions, existing: *const Module },
249 compile: ?*Step.Compile,
250249) void {
251250 const allocator = owner.allocator;
252251
......@@ -254,7 +253,6 @@ pub fn init(
254253 .options => |options| {
255254 m.* = .{
256255 .owner = owner,
257 .depending_steps = .{},
258256 .root_source_file = if (options.root_source_file) |lp| lp.dupe(owner) else null,
259257 .import_table = .{},
260258 .resolved_target = options.target,
......@@ -294,19 +292,11 @@ pub fn init(
294292 m.* = existing.*;
295293 },
296294 }
297
298 if (compile) |c| {
299 m.depending_steps.put(allocator, c, {}) catch @panic("OOM");
300 }
301
302 // This logic accesses `depending_steps` which was just modified above.
303 var it = m.iterateDependencies(null, false);
304 while (it.next()) |item| addShallowDependencies(m, item.module);
305295}
306296
307297pub fn create(owner: *std.Build, options: CreateOptions) *Module {
308298 const m = owner.allocator.create(Module) catch @panic("OOM");
309 m.init(owner, .{ .options = options }, null);
299 m.init(owner, .{ .options = options });
310300 return m;
311301}
312302
......@@ -314,69 +304,6 @@ pub fn create(owner: *std.Build, options: CreateOptions) *Module {
314304pub fn addImport(m: *Module, name: []const u8, module: *Module) void {
315305 const b = m.owner;
316306 m.import_table.put(b.allocator, b.dupe(name), module) catch @panic("OOM");
317
318 var it = module.iterateDependencies(null, false);
319 while (it.next()) |item| addShallowDependencies(m, item.module);
320}
321
322/// Creates step dependencies and updates `depending_steps` of `dependee` so that
323/// subsequent calls to `addImport` on `dependee` will additionally create step
324/// dependencies on `m`'s `depending_steps`.
325fn addShallowDependencies(m: *Module, dependee: *Module) void {
326 if (dependee.root_source_file) |lazy_path| addLazyPathDependencies(m, dependee, lazy_path);
327 for (dependee.lib_paths.items) |lib_path| addLazyPathDependencies(m, dependee, lib_path);
328 for (dependee.rpaths.items) |rpath| switch (rpath) {
329 .lazy_path => |lp| addLazyPathDependencies(m, dependee, lp),
330 .special => {},
331 };
332
333 for (dependee.link_objects.items) |link_object| switch (link_object) {
334 .other_step => |compile| {
335 addStepDependencies(m, dependee, &compile.step);
336 addLazyPathDependenciesOnly(m, compile.getEmittedIncludeTree());
337 },
338
339 .static_path,
340 .assembly_file,
341 => |lp| addLazyPathDependencies(m, dependee, lp),
342
343 .c_source_file => |x| addLazyPathDependencies(m, dependee, x.file),
344 .win32_resource_file => |x| addLazyPathDependencies(m, dependee, x.file),
345
346 .c_source_files,
347 .system_lib,
348 => {},
349 };
350}
351
352fn addLazyPathDependencies(m: *Module, module: *Module, lazy_path: LazyPath) void {
353 addLazyPathDependenciesOnly(m, lazy_path);
354 if (m != module) {
355 for (m.depending_steps.keys()) |compile| {
356 module.depending_steps.put(m.owner.allocator, compile, {}) catch @panic("OOM");
357 }
358 }
359}
360
361fn addLazyPathDependenciesOnly(m: *Module, lazy_path: LazyPath) void {
362 for (m.depending_steps.keys()) |compile| {
363 lazy_path.addStepDependencies(&compile.step);
364 }
365}
366
367fn addStepDependencies(m: *Module, module: *Module, dependee: *Step) void {
368 addStepDependenciesOnly(m, dependee);
369 if (m != module) {
370 for (m.depending_steps.keys()) |compile| {
371 module.depending_steps.put(m.owner.allocator, compile, {}) catch @panic("OOM");
372 }
373 }
374}
375
376fn addStepDependenciesOnly(m: *Module, dependee: *Step) void {
377 for (m.depending_steps.keys()) |compile| {
378 compile.step.dependOn(dependee);
379 }
380307}
381308
382309/// Creates a new module and adds it to be used with `@import`.
......@@ -392,91 +319,6 @@ pub fn addOptions(m: *Module, module_name: []const u8, options: *Step.Options) v
392319 addImport(m, module_name, options.createModule());
393320}
394321
395pub const DependencyIterator = struct {
396 allocator: std.mem.Allocator,
397 index: usize,
398 set: std.AutoArrayHashMapUnmanaged(Key, []const u8),
399 chase_dyn_libs: bool,
400
401 pub const Key = struct {
402 /// The compilation that contains the `Module`. Note that a `Module` might be
403 /// used by more than one compilation.
404 compile: ?*Step.Compile,
405 module: *Module,
406 };
407
408 pub const Item = struct {
409 /// The compilation that contains the `Module`. Note that a `Module` might be
410 /// used by more than one compilation.
411 compile: ?*Step.Compile,
412 module: *Module,
413 name: []const u8,
414 };
415
416 pub fn deinit(it: *DependencyIterator) void {
417 it.set.deinit(it.allocator);
418 it.* = undefined;
419 }
420
421 pub fn next(it: *DependencyIterator) ?Item {
422 if (it.index >= it.set.count()) {
423 it.set.clearAndFree(it.allocator);
424 return null;
425 }
426 const key = it.set.keys()[it.index];
427 const name = it.set.values()[it.index];
428 it.index += 1;
429 const module = key.module;
430 it.set.ensureUnusedCapacity(it.allocator, module.import_table.count()) catch
431 @panic("OOM");
432 for (module.import_table.keys(), module.import_table.values()) |dep_name, dep| {
433 it.set.putAssumeCapacity(.{
434 .module = dep,
435 .compile = key.compile,
436 }, dep_name);
437 }
438
439 if (key.compile != null) {
440 for (module.link_objects.items) |link_object| switch (link_object) {
441 .other_step => |compile| {
442 if (!it.chase_dyn_libs and compile.isDynamicLibrary()) continue;
443
444 it.set.put(it.allocator, .{
445 .module = compile.root_module,
446 .compile = compile,
447 }, "root") catch @panic("OOM");
448 },
449 else => {},
450 };
451 }
452
453 return .{
454 .compile = key.compile,
455 .module = key.module,
456 .name = name,
457 };
458 }
459};
460
461pub fn iterateDependencies(
462 m: *Module,
463 chase_steps: ?*Step.Compile,
464 chase_dyn_libs: bool,
465) DependencyIterator {
466 var it: DependencyIterator = .{
467 .allocator = m.owner.allocator,
468 .index = 0,
469 .set = .{},
470 .chase_dyn_libs = chase_dyn_libs,
471 };
472 it.set.ensureUnusedCapacity(m.owner.allocator, m.import_table.count() + 1) catch @panic("OOM");
473 it.set.putAssumeCapacity(.{
474 .module = m,
475 .compile = chase_steps,
476 }, "root");
477 return it;
478}
479
480322pub const LinkSystemLibraryOptions = struct {
481323 /// Causes dynamic libraries to be linked regardless of whether they are
482324 /// actually depended on. When false, dynamic libraries with no referenced
......@@ -559,7 +401,6 @@ pub fn addCSourceFiles(m: *Module, options: AddCSourceFilesOptions) void {
559401 .flags = b.dupeStrings(options.flags),
560402 };
561403 m.link_objects.append(allocator, .{ .c_source_files = c_source_files }) catch @panic("OOM");
562 addLazyPathDependenciesOnly(m, c_source_files.root);
563404}
564405
565406pub fn addCSourceFile(m: *Module, source: CSourceFile) void {
......@@ -568,7 +409,6 @@ pub fn addCSourceFile(m: *Module, source: CSourceFile) void {
568409 const c_source_file = allocator.create(CSourceFile) catch @panic("OOM");
569410 c_source_file.* = source.dupe(b);
570411 m.link_objects.append(allocator, .{ .c_source_file = c_source_file }) catch @panic("OOM");
571 addLazyPathDependenciesOnly(m, source.file);
572412}
573413
574414/// Resource files must have the extension `.rc`.
......@@ -585,22 +425,16 @@ pub fn addWin32ResourceFile(m: *Module, source: RcSourceFile) void {
585425 const rc_source_file = allocator.create(RcSourceFile) catch @panic("OOM");
586426 rc_source_file.* = source.dupe(b);
587427 m.link_objects.append(allocator, .{ .win32_resource_file = rc_source_file }) catch @panic("OOM");
588 addLazyPathDependenciesOnly(m, source.file);
589 for (source.include_paths) |include_path| {
590 addLazyPathDependenciesOnly(m, include_path);
591 }
592428}
593429
594430pub fn addAssemblyFile(m: *Module, source: LazyPath) void {
595431 const b = m.owner;
596432 m.link_objects.append(b.allocator, .{ .assembly_file = source.dupe(b) }) catch @panic("OOM");
597 addLazyPathDependenciesOnly(m, source);
598433}
599434
600435pub fn addObjectFile(m: *Module, object: LazyPath) void {
601436 const b = m.owner;
602437 m.link_objects.append(b.allocator, .{ .static_path = object.dupe(b) }) catch @panic("OOM");
603 addLazyPathDependenciesOnly(m, object);
604438}
605439
606440pub fn addObject(m: *Module, object: *Step.Compile) void {
......@@ -616,51 +450,43 @@ pub fn linkLibrary(m: *Module, library: *Step.Compile) void {
616450pub fn addAfterIncludePath(m: *Module, lazy_path: LazyPath) void {
617451 const b = m.owner;
618452 m.include_dirs.append(b.allocator, .{ .path_after = lazy_path.dupe(b) }) catch @panic("OOM");
619 addLazyPathDependenciesOnly(m, lazy_path);
620453}
621454
622455pub fn addSystemIncludePath(m: *Module, lazy_path: LazyPath) void {
623456 const b = m.owner;
624457 m.include_dirs.append(b.allocator, .{ .path_system = lazy_path.dupe(b) }) catch @panic("OOM");
625 addLazyPathDependenciesOnly(m, lazy_path);
626458}
627459
628460pub fn addIncludePath(m: *Module, lazy_path: LazyPath) void {
629461 const b = m.owner;
630462 m.include_dirs.append(b.allocator, .{ .path = lazy_path.dupe(b) }) catch @panic("OOM");
631 addLazyPathDependenciesOnly(m, lazy_path);
632463}
633464
634465pub fn addConfigHeader(m: *Module, config_header: *Step.ConfigHeader) void {
635466 const allocator = m.owner.allocator;
636467 m.include_dirs.append(allocator, .{ .config_header_step = config_header }) catch @panic("OOM");
637 addStepDependenciesOnly(m, &config_header.step);
638468}
639469
640470pub fn addSystemFrameworkPath(m: *Module, directory_path: LazyPath) void {
641471 const b = m.owner;
642472 m.include_dirs.append(b.allocator, .{ .framework_path_system = directory_path.dupe(b) }) catch
643473 @panic("OOM");
644 addLazyPathDependenciesOnly(m, directory_path);
645474}
646475
647476pub fn addFrameworkPath(m: *Module, directory_path: LazyPath) void {
648477 const b = m.owner;
649478 m.include_dirs.append(b.allocator, .{ .framework_path = directory_path.dupe(b) }) catch
650479 @panic("OOM");
651 addLazyPathDependenciesOnly(m, directory_path);
652480}
653481
654482pub fn addLibraryPath(m: *Module, directory_path: LazyPath) void {
655483 const b = m.owner;
656484 m.lib_paths.append(b.allocator, directory_path.dupe(b)) catch @panic("OOM");
657 addLazyPathDependenciesOnly(m, directory_path);
658485}
659486
660487pub fn addRPath(m: *Module, directory_path: LazyPath) void {
661488 const b = m.owner;
662489 m.rpaths.append(b.allocator, .{ .lazy_path = directory_path.dupe(b) }) catch @panic("OOM");
663 addLazyPathDependenciesOnly(m, directory_path);
664490}
665491
666492pub fn addRPathSpecial(m: *Module, bytes: []const u8) void {
......@@ -784,7 +610,6 @@ fn addFlag(
784610fn linkLibraryOrObject(m: *Module, other: *Step.Compile) void {
785611 const allocator = m.owner.allocator;
786612 _ = other.getEmittedBin(); // Indicate there is a dependency on the outputted binary.
787 addStepDependenciesOnly(m, &other.step);
788613
789614 if (other.rootModuleTarget().os.tag == .windows and other.isDynamicLibrary()) {
790615 _ = other.getEmittedImplib(); // Indicate dependency on the outputted implib.
......@@ -792,8 +617,6 @@ fn linkLibraryOrObject(m: *Module, other: *Step.Compile) void {
792617
793618 m.link_objects.append(allocator, .{ .other_step = other }) catch @panic("OOM");
794619 m.include_dirs.append(allocator, .{ .other_step = other }) catch @panic("OOM");
795
796 addLazyPathDependenciesOnly(m, other.getEmittedIncludeTree());
797620}
798621
799622fn requireKnownTarget(m: *Module) std.Target {
......@@ -802,6 +625,46 @@ fn requireKnownTarget(m: *Module) std.Target {
802625 return resolved_target.result;
803626}
804627
628/// Elements of `modules` and `names` are matched one-to-one.
629pub const Graph = struct {
630 modules: []const *Module,
631 names: []const []const u8,
632};
633
634/// Intended to be used during the make phase only.
635///
636/// Given that `root` is the root `Module` of a compilation, return all `Module`s
637/// in the module graph, including `root` itself. `root` is guaranteed to be the
638/// first module in the returned slice.
639pub fn getGraph(root: *Module) Graph {
640 if (root.cached_graph.modules.len != 0) {
641 return root.cached_graph;
642 }
643
644 const arena = root.owner.graph.arena;
645
646 var modules: std.AutoArrayHashMapUnmanaged(*std.Build.Module, []const u8) = .empty;
647 var next_idx: usize = 0;
648
649 modules.putNoClobber(arena, root, "root") catch @panic("OOM");
650
651 while (next_idx < modules.count()) {
652 const mod = modules.keys()[next_idx];
653 next_idx += 1;
654 modules.ensureUnusedCapacity(arena, mod.import_table.count()) catch @panic("OOM");
655 for (mod.import_table.keys(), mod.import_table.values()) |import_name, other_mod| {
656 modules.putAssumeCapacity(other_mod, import_name);
657 }
658 }
659
660 const result: Graph = .{
661 .modules = modules.keys(),
662 .names = modules.values(),
663 };
664 root.cached_graph = result;
665 return result;
666}
667
805668const Module = @This();
806669const std = @import("std");
807670const assert = std.debug.assert;
lib/std/Build/Step/Compile.zig+278-249
......@@ -389,7 +389,7 @@ pub fn create(owner: *std.Build, options: Options) *Compile {
389389
390390 const compile = owner.allocator.create(Compile) catch @panic("OOM");
391391 compile.* = .{
392 .root_module = undefined,
392 .root_module = options.root_module,
393393 .verbose_link = false,
394394 .verbose_cc = false,
395395 .linkage = options.linkage,
......@@ -432,8 +432,6 @@ pub fn create(owner: *std.Build, options: Options) *Compile {
432432
433433 .zig_process = null,
434434 };
435 options.root_module.init(owner, .{ .existing = options.root_module }, compile);
436 compile.root_module = options.root_module;
437435
438436 if (options.zig_lib_dir) |lp| {
439437 compile.zig_lib_dir = lp.dupe(compile.step.owner);
......@@ -607,16 +605,17 @@ pub fn dependsOnSystemLibrary(compile: *const Compile, name: []const u8) bool {
607605 var is_linking_libc = false;
608606 var is_linking_libcpp = false;
609607
610 var dep_it = compile.root_module.iterateDependencies(compile, true);
611 while (dep_it.next()) |module| {
612 for (module.link_objects.items) |link_object| {
613 switch (link_object) {
614 .system_lib => |lib| if (mem.eql(u8, lib.name, name)) return true,
615 else => continue,
608 for (compile.getCompileDependencies(true)) |some_compile| {
609 for (some_compile.root_module.getGraph().modules) |mod| {
610 for (mod.link_objects.items) |lo| {
611 switch (lo) {
612 .system_lib => |lib| if (mem.eql(u8, lib.name, name)) return true,
613 else => {},
614 }
616615 }
616 if (mod.link_libc) is_linking_libc = true;
617 if (mod.link_libcpp) is_linking_libcpp = true;
617618 }
618 is_linking_libc = is_linking_libc or module.link_libc == true;
619 is_linking_libcpp = is_linking_libcpp or module.link_libcpp == true;
620619 }
621620
622621 const target = compile.rootModuleTarget();
......@@ -961,23 +960,22 @@ const CliNamedModules = struct {
961960 .modules = .{},
962961 .names = .{},
963962 };
964 var dep_it = root_module.iterateDependencies(null, false);
963 const graph = root_module.getGraph();
965964 {
966 const item = dep_it.next().?;
967 assert(root_module == item.module);
965 assert(graph.modules[0] == root_module);
968966 try compile.modules.put(arena, root_module, {});
969967 try compile.names.put(arena, "root", {});
970968 }
971 while (dep_it.next()) |item| {
972 var name = item.name;
969 for (graph.modules[1..], graph.names[1..]) |mod, orig_name| {
970 var name = orig_name;
973971 var n: usize = 0;
974972 while (true) {
975973 const gop = try compile.names.getOrPut(arena, name);
976974 if (!gop.found_existing) {
977 try compile.modules.putNoClobber(arena, item.module, {});
975 try compile.modules.putNoClobber(arena, mod, {});
978976 break;
979977 }
980 name = try std.fmt.allocPrint(arena, "{s}{d}", .{ item.name, n });
978 name = try std.fmt.allocPrint(arena, "{s}{d}", .{ orig_name, n });
981979 n += 1;
982980 }
983981 }
......@@ -1080,13 +1078,12 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
10801078 // emitted if there is nothing to link.
10811079 var total_linker_objects: usize = @intFromBool(compile.root_module.root_source_file != null);
10821080
1083 {
1084 // Fully recursive iteration including dynamic libraries to detect
1085 // libc and libc++ linkage.
1086 var dep_it = compile.root_module.iterateDependencies(compile, true);
1087 while (dep_it.next()) |key| {
1088 if (key.module.link_libc == true) compile.is_linking_libc = true;
1089 if (key.module.link_libcpp == true) compile.is_linking_libcpp = true;
1081 // Fully recursive iteration including dynamic libraries to detect
1082 // libc and libc++ linkage.
1083 for (compile.getCompileDependencies(true)) |some_compile| {
1084 for (some_compile.root_module.getGraph().modules) |mod| {
1085 if (mod.link_libc == true) compile.is_linking_libc = true;
1086 if (mod.link_libcpp == true) compile.is_linking_libcpp = true;
10901087 }
10911088 }
10921089
......@@ -1094,265 +1091,265 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
10941091
10951092 // For this loop, don't chase dynamic libraries because their link
10961093 // objects are already linked.
1097 var dep_it = compile.root_module.iterateDependencies(compile, false);
1098
1099 while (dep_it.next()) |dep| {
1100 // While walking transitive dependencies, if a given link object is
1101 // already included in a library, it should not redundantly be
1102 // placed on the linker line of the dependee.
1103 const my_responsibility = dep.compile.? == compile;
1104 const already_linked = !my_responsibility and dep.compile.?.isDynamicLibrary();
1105
1106 // Inherit dependencies on darwin frameworks.
1107 if (!already_linked) {
1108 for (dep.module.frameworks.keys(), dep.module.frameworks.values()) |name, info| {
1109 try frameworks.put(arena, name, info);
1094 for (compile.getCompileDependencies(false)) |dep_compile| {
1095 for (dep_compile.root_module.getGraph().modules) |mod| {
1096 // While walking transitive dependencies, if a given link object is
1097 // already included in a library, it should not redundantly be
1098 // placed on the linker line of the dependee.
1099 const my_responsibility = dep_compile == compile;
1100 const already_linked = !my_responsibility and dep_compile.isDynamicLibrary();
1101
1102 // Inherit dependencies on darwin frameworks.
1103 if (!already_linked) {
1104 for (mod.frameworks.keys(), mod.frameworks.values()) |name, info| {
1105 try frameworks.put(arena, name, info);
1106 }
11101107 }
1111 }
1112
1113 // Inherit dependencies on system libraries and static libraries.
1114 for (dep.module.link_objects.items) |link_object| {
1115 switch (link_object) {
1116 .static_path => |static_path| {
1117 if (my_responsibility) {
1118 try zig_args.append(static_path.getPath2(dep.module.owner, step));
1119 total_linker_objects += 1;
1120 }
1121 },
1122 .system_lib => |system_lib| {
1123 const system_lib_gop = try seen_system_libs.getOrPut(arena, system_lib.name);
1124 if (system_lib_gop.found_existing) {
1125 try zig_args.appendSlice(system_lib_gop.value_ptr.*);
1126 continue;
1127 } else {
1128 system_lib_gop.value_ptr.* = &.{};
1129 }
11301108
1131 if (already_linked)
1132 continue;
1133
1134 if ((system_lib.search_strategy != prev_search_strategy or
1135 system_lib.preferred_link_mode != prev_preferred_link_mode) and
1136 compile.linkage != .static)
1137 {
1138 switch (system_lib.search_strategy) {
1139 .no_fallback => switch (system_lib.preferred_link_mode) {
1140 .dynamic => try zig_args.append("-search_dylibs_only"),
1141 .static => try zig_args.append("-search_static_only"),
1142 },
1143 .paths_first => switch (system_lib.preferred_link_mode) {
1144 .dynamic => try zig_args.append("-search_paths_first"),
1145 .static => try zig_args.append("-search_paths_first_static"),
1146 },
1147 .mode_first => switch (system_lib.preferred_link_mode) {
1148 .dynamic => try zig_args.append("-search_dylibs_first"),
1149 .static => try zig_args.append("-search_static_first"),
1150 },
1109 // Inherit dependencies on system libraries and static libraries.
1110 for (mod.link_objects.items) |link_object| {
1111 switch (link_object) {
1112 .static_path => |static_path| {
1113 if (my_responsibility) {
1114 try zig_args.append(static_path.getPath2(mod.owner, step));
1115 total_linker_objects += 1;
1116 }
1117 },
1118 .system_lib => |system_lib| {
1119 const system_lib_gop = try seen_system_libs.getOrPut(arena, system_lib.name);
1120 if (system_lib_gop.found_existing) {
1121 try zig_args.appendSlice(system_lib_gop.value_ptr.*);
1122 continue;
1123 } else {
1124 system_lib_gop.value_ptr.* = &.{};
11511125 }
1152 prev_search_strategy = system_lib.search_strategy;
1153 prev_preferred_link_mode = system_lib.preferred_link_mode;
1154 }
11551126
1156 const prefix: []const u8 = prefix: {
1157 if (system_lib.needed) break :prefix "-needed-l";
1158 if (system_lib.weak) break :prefix "-weak-l";
1159 break :prefix "-l";
1160 };
1161 switch (system_lib.use_pkg_config) {
1162 .no => try zig_args.append(b.fmt("{s}{s}", .{ prefix, system_lib.name })),
1163 .yes, .force => {
1164 if (compile.runPkgConfig(system_lib.name)) |result| {
1165 try zig_args.appendSlice(result.cflags);
1166 try zig_args.appendSlice(result.libs);
1167 try seen_system_libs.put(arena, system_lib.name, result.cflags);
1168 } else |err| switch (err) {
1169 error.PkgConfigInvalidOutput,
1170 error.PkgConfigCrashed,
1171 error.PkgConfigFailed,
1172 error.PkgConfigNotInstalled,
1173 error.PackageNotFound,
1174 => switch (system_lib.use_pkg_config) {
1175 .yes => {
1176 // pkg-config failed, so fall back to linking the library
1177 // by name directly.
1178 try zig_args.append(b.fmt("{s}{s}", .{
1179 prefix,
1180 system_lib.name,
1181 }));
1182 },
1183 .force => {
1184 panic("pkg-config failed for library {s}", .{system_lib.name});
1185 },
1186 .no => unreachable,
1127 if (already_linked)
1128 continue;
1129
1130 if ((system_lib.search_strategy != prev_search_strategy or
1131 system_lib.preferred_link_mode != prev_preferred_link_mode) and
1132 compile.linkage != .static)
1133 {
1134 switch (system_lib.search_strategy) {
1135 .no_fallback => switch (system_lib.preferred_link_mode) {
1136 .dynamic => try zig_args.append("-search_dylibs_only"),
1137 .static => try zig_args.append("-search_static_only"),
1138 },
1139 .paths_first => switch (system_lib.preferred_link_mode) {
1140 .dynamic => try zig_args.append("-search_paths_first"),
1141 .static => try zig_args.append("-search_paths_first_static"),
1142 },
1143 .mode_first => switch (system_lib.preferred_link_mode) {
1144 .dynamic => try zig_args.append("-search_dylibs_first"),
1145 .static => try zig_args.append("-search_static_first"),
11871146 },
1188
1189 else => |e| return e,
1190 }
1191 },
1192 }
1193 },
1194 .other_step => |other| {
1195 switch (other.kind) {
1196 .exe => return step.fail("cannot link with an executable build artifact", .{}),
1197 .@"test" => return step.fail("cannot link with a test", .{}),
1198 .obj => {
1199 const included_in_lib_or_obj = !my_responsibility and
1200 (dep.compile.?.kind == .lib or dep.compile.?.kind == .obj);
1201 if (!already_linked and !included_in_lib_or_obj) {
1202 try zig_args.append(other.getEmittedBin().getPath2(b, step));
1203 total_linker_objects += 1;
1204 }
1205 },
1206 .lib => l: {
1207 const other_produces_implib = other.producesImplib();
1208 const other_is_static = other_produces_implib or other.isStaticLibrary();
1209
1210 if (compile.isStaticLibrary() and other_is_static) {
1211 // Avoid putting a static library inside a static library.
1212 break :l;
12131147 }
1148 prev_search_strategy = system_lib.search_strategy;
1149 prev_preferred_link_mode = system_lib.preferred_link_mode;
1150 }
12141151
1215 // For DLLs, we must link against the implib.
1216 // For everything else, we directly link
1217 // against the library file.
1218 const full_path_lib = if (other_produces_implib)
1219 other.getGeneratedFilePath("generated_implib", &compile.step)
1220 else
1221 other.getGeneratedFilePath("generated_bin", &compile.step);
1152 const prefix: []const u8 = prefix: {
1153 if (system_lib.needed) break :prefix "-needed-l";
1154 if (system_lib.weak) break :prefix "-weak-l";
1155 break :prefix "-l";
1156 };
1157 switch (system_lib.use_pkg_config) {
1158 .no => try zig_args.append(b.fmt("{s}{s}", .{ prefix, system_lib.name })),
1159 .yes, .force => {
1160 if (compile.runPkgConfig(system_lib.name)) |result| {
1161 try zig_args.appendSlice(result.cflags);
1162 try zig_args.appendSlice(result.libs);
1163 try seen_system_libs.put(arena, system_lib.name, result.cflags);
1164 } else |err| switch (err) {
1165 error.PkgConfigInvalidOutput,
1166 error.PkgConfigCrashed,
1167 error.PkgConfigFailed,
1168 error.PkgConfigNotInstalled,
1169 error.PackageNotFound,
1170 => switch (system_lib.use_pkg_config) {
1171 .yes => {
1172 // pkg-config failed, so fall back to linking the library
1173 // by name directly.
1174 try zig_args.append(b.fmt("{s}{s}", .{
1175 prefix,
1176 system_lib.name,
1177 }));
1178 },
1179 .force => {
1180 panic("pkg-config failed for library {s}", .{system_lib.name});
1181 },
1182 .no => unreachable,
1183 },
12221184
1223 try zig_args.append(full_path_lib);
1224 total_linker_objects += 1;
1185 else => |e| return e,
1186 }
1187 },
1188 }
1189 },
1190 .other_step => |other| {
1191 switch (other.kind) {
1192 .exe => return step.fail("cannot link with an executable build artifact", .{}),
1193 .@"test" => return step.fail("cannot link with a test", .{}),
1194 .obj => {
1195 const included_in_lib_or_obj = !my_responsibility and
1196 (dep_compile.kind == .lib or dep_compile.kind == .obj);
1197 if (!already_linked and !included_in_lib_or_obj) {
1198 try zig_args.append(other.getEmittedBin().getPath2(b, step));
1199 total_linker_objects += 1;
1200 }
1201 },
1202 .lib => l: {
1203 const other_produces_implib = other.producesImplib();
1204 const other_is_static = other_produces_implib or other.isStaticLibrary();
12251205
1226 if (other.linkage == .dynamic and
1227 compile.rootModuleTarget().os.tag != .windows)
1228 {
1229 if (fs.path.dirname(full_path_lib)) |dirname| {
1230 try zig_args.append("-rpath");
1231 try zig_args.append(dirname);
1206 if (compile.isStaticLibrary() and other_is_static) {
1207 // Avoid putting a static library inside a static library.
1208 break :l;
12321209 }
1233 }
1234 },
1235 }
1236 },
1237 .assembly_file => |asm_file| l: {
1238 if (!my_responsibility) break :l;
12391210
1240 if (prev_has_cflags) {
1241 try zig_args.append("-cflags");
1242 try zig_args.append("--");
1243 prev_has_cflags = false;
1244 }
1245 try zig_args.append(asm_file.getPath2(dep.module.owner, step));
1246 total_linker_objects += 1;
1247 },
1211 // For DLLs, we must link against the implib.
1212 // For everything else, we directly link
1213 // against the library file.
1214 const full_path_lib = if (other_produces_implib)
1215 other.getGeneratedFilePath("generated_implib", &compile.step)
1216 else
1217 other.getGeneratedFilePath("generated_bin", &compile.step);
12481218
1249 .c_source_file => |c_source_file| l: {
1250 if (!my_responsibility) break :l;
1219 try zig_args.append(full_path_lib);
1220 total_linker_objects += 1;
1221
1222 if (other.linkage == .dynamic and
1223 compile.rootModuleTarget().os.tag != .windows)
1224 {
1225 if (fs.path.dirname(full_path_lib)) |dirname| {
1226 try zig_args.append("-rpath");
1227 try zig_args.append(dirname);
1228 }
1229 }
1230 },
1231 }
1232 },
1233 .assembly_file => |asm_file| l: {
1234 if (!my_responsibility) break :l;
12511235
1252 if (c_source_file.flags.len == 0) {
12531236 if (prev_has_cflags) {
12541237 try zig_args.append("-cflags");
12551238 try zig_args.append("--");
12561239 prev_has_cflags = false;
12571240 }
1258 } else {
1259 try zig_args.append("-cflags");
1260 for (c_source_file.flags) |arg| {
1261 try zig_args.append(arg);
1262 }
1263 try zig_args.append("--");
1264 prev_has_cflags = true;
1265 }
1266 try zig_args.append(c_source_file.file.getPath2(dep.module.owner, step));
1267 total_linker_objects += 1;
1268 },
1241 try zig_args.append(asm_file.getPath2(mod.owner, step));
1242 total_linker_objects += 1;
1243 },
12691244
1270 .c_source_files => |c_source_files| l: {
1271 if (!my_responsibility) break :l;
1245 .c_source_file => |c_source_file| l: {
1246 if (!my_responsibility) break :l;
12721247
1273 if (c_source_files.flags.len == 0) {
1274 if (prev_has_cflags) {
1248 if (c_source_file.flags.len == 0) {
1249 if (prev_has_cflags) {
1250 try zig_args.append("-cflags");
1251 try zig_args.append("--");
1252 prev_has_cflags = false;
1253 }
1254 } else {
12751255 try zig_args.append("-cflags");
1256 for (c_source_file.flags) |arg| {
1257 try zig_args.append(arg);
1258 }
12761259 try zig_args.append("--");
1277 prev_has_cflags = false;
1260 prev_has_cflags = true;
12781261 }
1279 } else {
1280 try zig_args.append("-cflags");
1281 for (c_source_files.flags) |flag| {
1282 try zig_args.append(flag);
1262 try zig_args.append(c_source_file.file.getPath2(mod.owner, step));
1263 total_linker_objects += 1;
1264 },
1265
1266 .c_source_files => |c_source_files| l: {
1267 if (!my_responsibility) break :l;
1268
1269 if (c_source_files.flags.len == 0) {
1270 if (prev_has_cflags) {
1271 try zig_args.append("-cflags");
1272 try zig_args.append("--");
1273 prev_has_cflags = false;
1274 }
1275 } else {
1276 try zig_args.append("-cflags");
1277 for (c_source_files.flags) |flag| {
1278 try zig_args.append(flag);
1279 }
1280 try zig_args.append("--");
1281 prev_has_cflags = true;
12831282 }
1284 try zig_args.append("--");
1285 prev_has_cflags = true;
1286 }
12871283
1288 const root_path = c_source_files.root.getPath2(dep.module.owner, step);
1289 for (c_source_files.files) |file| {
1290 try zig_args.append(b.pathJoin(&.{ root_path, file }));
1291 }
1284 const root_path = c_source_files.root.getPath2(mod.owner, step);
1285 for (c_source_files.files) |file| {
1286 try zig_args.append(b.pathJoin(&.{ root_path, file }));
1287 }
12921288
1293 total_linker_objects += c_source_files.files.len;
1294 },
1289 total_linker_objects += c_source_files.files.len;
1290 },
12951291
1296 .win32_resource_file => |rc_source_file| l: {
1297 if (!my_responsibility) break :l;
1292 .win32_resource_file => |rc_source_file| l: {
1293 if (!my_responsibility) break :l;
12981294
1299 if (rc_source_file.flags.len == 0 and rc_source_file.include_paths.len == 0) {
1300 if (prev_has_rcflags) {
1295 if (rc_source_file.flags.len == 0 and rc_source_file.include_paths.len == 0) {
1296 if (prev_has_rcflags) {
1297 try zig_args.append("-rcflags");
1298 try zig_args.append("--");
1299 prev_has_rcflags = false;
1300 }
1301 } else {
13011302 try zig_args.append("-rcflags");
1303 for (rc_source_file.flags) |arg| {
1304 try zig_args.append(arg);
1305 }
1306 for (rc_source_file.include_paths) |include_path| {
1307 try zig_args.append("/I");
1308 try zig_args.append(include_path.getPath2(mod.owner, step));
1309 }
13021310 try zig_args.append("--");
1303 prev_has_rcflags = false;
1304 }
1305 } else {
1306 try zig_args.append("-rcflags");
1307 for (rc_source_file.flags) |arg| {
1308 try zig_args.append(arg);
1309 }
1310 for (rc_source_file.include_paths) |include_path| {
1311 try zig_args.append("/I");
1312 try zig_args.append(include_path.getPath2(dep.module.owner, step));
1311 prev_has_rcflags = true;
13131312 }
1314 try zig_args.append("--");
1315 prev_has_rcflags = true;
1316 }
1317 try zig_args.append(rc_source_file.file.getPath2(dep.module.owner, step));
1318 total_linker_objects += 1;
1319 },
1313 try zig_args.append(rc_source_file.file.getPath2(mod.owner, step));
1314 total_linker_objects += 1;
1315 },
1316 }
13201317 }
1321 }
13221318
1323 // We need to emit the --mod argument here so that the above link objects
1324 // have the correct parent module, but only if the module is part of
1325 // this compilation.
1326 if (!my_responsibility) continue;
1327 if (cli_named_modules.modules.getIndex(dep.module)) |module_cli_index| {
1328 const module_cli_name = cli_named_modules.names.keys()[module_cli_index];
1329 try dep.module.appendZigProcessFlags(&zig_args, step);
1330
1331 // --dep arguments
1332 try zig_args.ensureUnusedCapacity(dep.module.import_table.count() * 2);
1333 for (dep.module.import_table.keys(), dep.module.import_table.values()) |name, import| {
1334 const import_index = cli_named_modules.modules.getIndex(import).?;
1335 const import_cli_name = cli_named_modules.names.keys()[import_index];
1336 zig_args.appendAssumeCapacity("--dep");
1337 if (std.mem.eql(u8, import_cli_name, name)) {
1338 zig_args.appendAssumeCapacity(import_cli_name);
1339 } else {
1340 zig_args.appendAssumeCapacity(b.fmt("{s}={s}", .{ name, import_cli_name }));
1319 // We need to emit the --mod argument here so that the above link objects
1320 // have the correct parent module, but only if the module is part of
1321 // this compilation.
1322 if (!my_responsibility) continue;
1323 if (cli_named_modules.modules.getIndex(mod)) |module_cli_index| {
1324 const module_cli_name = cli_named_modules.names.keys()[module_cli_index];
1325 try mod.appendZigProcessFlags(&zig_args, step);
1326
1327 // --dep arguments
1328 try zig_args.ensureUnusedCapacity(mod.import_table.count() * 2);
1329 for (mod.import_table.keys(), mod.import_table.values()) |name, import| {
1330 const import_index = cli_named_modules.modules.getIndex(import).?;
1331 const import_cli_name = cli_named_modules.names.keys()[import_index];
1332 zig_args.appendAssumeCapacity("--dep");
1333 if (std.mem.eql(u8, import_cli_name, name)) {
1334 zig_args.appendAssumeCapacity(import_cli_name);
1335 } else {
1336 zig_args.appendAssumeCapacity(b.fmt("{s}={s}", .{ name, import_cli_name }));
1337 }
13411338 }
1342 }
13431339
1344 // When the CLI sees a -M argument, it determines whether it
1345 // implies the existence of a Zig compilation unit based on
1346 // whether there is a root source file. If there is no root
1347 // source file, then this is not a zig compilation unit - it is
1348 // perhaps a set of linker objects, or C source files instead.
1349 // Linker objects are added to the CLI globally, while C source
1350 // files must have a module parent.
1351 if (dep.module.root_source_file) |lp| {
1352 const src = lp.getPath2(dep.module.owner, step);
1353 try zig_args.append(b.fmt("-M{s}={s}", .{ module_cli_name, src }));
1354 } else if (moduleNeedsCliArg(dep.module)) {
1355 try zig_args.append(b.fmt("-M{s}", .{module_cli_name}));
1340 // When the CLI sees a -M argument, it determines whether it
1341 // implies the existence of a Zig compilation unit based on
1342 // whether there is a root source file. If there is no root
1343 // source file, then this is not a zig compilation unit - it is
1344 // perhaps a set of linker objects, or C source files instead.
1345 // Linker objects are added to the CLI globally, while C source
1346 // files must have a module parent.
1347 if (mod.root_source_file) |lp| {
1348 const src = lp.getPath2(mod.owner, step);
1349 try zig_args.append(b.fmt("-M{s}={s}", .{ module_cli_name, src }));
1350 } else if (moduleNeedsCliArg(mod)) {
1351 try zig_args.append(b.fmt("-M{s}", .{module_cli_name}));
1352 }
13561353 }
13571354 }
13581355 }
......@@ -2064,3 +2061,35 @@ fn moduleNeedsCliArg(mod: *const Module) bool {
20642061 else => continue,
20652062 } else false;
20662063}
2064
2065/// Return the full set of `Step.Compile` which `start` depends on, recursively. `start` itself is
2066/// always returned as the first element. If `chase_dynamic` is `false`, then dynamic libraries are
2067/// not included, and their dependencies are not considered; if `chase_dynamic` is `true`, dynamic
2068/// libraries are treated the same as other linked `Compile`s.
2069pub fn getCompileDependencies(start: *Compile, chase_dynamic: bool) []const *Compile {
2070 const arena = start.step.owner.graph.arena;
2071
2072 var compiles: std.AutoArrayHashMapUnmanaged(*Compile, void) = .empty;
2073 var next_idx: usize = 0;
2074
2075 compiles.putNoClobber(arena, start, {}) catch @panic("OOM");
2076
2077 while (next_idx < compiles.count()) {
2078 const compile = compiles.keys()[next_idx];
2079 next_idx += 1;
2080
2081 for (compile.root_module.getGraph().modules) |mod| {
2082 for (mod.link_objects.items) |lo| {
2083 switch (lo) {
2084 .other_step => |other_compile| {
2085 if (!chase_dynamic and other_compile.isDynamicLibrary()) continue;
2086 compiles.put(arena, other_compile, {}) catch @panic("OOM");
2087 },
2088 else => {},
2089 }
2090 }
2091 }
2092 }
2093
2094 return compiles.keys();
2095}
lib/std/Build/Step/Run.zig+6-9
......@@ -1719,15 +1719,12 @@ fn evalGeneric(run: *Run, child: *std.process.Child) !StdIoResult {
17191719
17201720fn addPathForDynLibs(run: *Run, artifact: *Step.Compile) void {
17211721 const b = run.step.owner;
1722 var it = artifact.root_module.iterateDependencies(artifact, true);
1723 while (it.next()) |item| {
1724 const other = item.compile.?;
1725 if (item.module == other.root_module) {
1726 if (item.module.resolved_target.?.result.os.tag == .windows and
1727 other.isDynamicLibrary())
1728 {
1729 addPathDir(run, fs.path.dirname(other.getEmittedBin().getPath2(b, &run.step)).?);
1730 }
1722 const compiles = artifact.getCompileDependencies(true);
1723 for (compiles) |compile| {
1724 if (compile.root_module.resolved_target.?.result.os.tag == .windows and
1725 compile.isDynamicLibrary())
1726 {
1727 addPathDir(run, fs.path.dirname(compile.getEmittedBin().getPath2(b, &run.step)).?);
17311728 }
17321729 }
17331730}