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 {...@@ -337,6 +337,7 @@ pub fn main() !void {
337 var prog_node = main_progress_node.start("Configure", 0);337 var prog_node = main_progress_node.start("Configure", 0);
338 defer prog_node.end();338 defer prog_node.end();
339 try builder.runBuild(root);339 try builder.runBuild(root);
340 createModuleDependencies(builder) catch @panic("OOM");
340 }341 }
341342
342 if (graph.needed_lazy_dependencies.entries.len != 0) {343 if (graph.needed_lazy_dependencies.entries.len != 0) {
...@@ -1440,3 +1441,80 @@ fn validateSystemLibraryOptions(b: *std.Build) void {...@@ -1440,3 +1441,80 @@ fn validateSystemLibraryOptions(b: *std.Build) void {
1440 process.exit(1);1441 process.exit(1);
1441 }1442 }
1442}1443}
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 @@...@@ -1,9 +1,5 @@
1/// The one responsible for creating this module.1/// The one responsible for creating this module.
2owner: *std.Build,2owner: *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),
7root_source_file: ?LazyPath,3root_source_file: ?LazyPath,
8/// The modules that are mapped into this module's import table.4/// The modules that are mapped into this module's import table.
9/// Use `addImport` rather than modifying this field directly in order to5/// Use `addImport` rather than modifying this field directly in order to
...@@ -41,6 +37,10 @@ link_libcpp: ?bool,...@@ -41,6 +37,10 @@ link_libcpp: ?bool,
41/// Symbols to be exported when compiling to WebAssembly.37/// Symbols to be exported when compiling to WebAssembly.
42export_symbol_names: []const []const u8 = &.{},38export_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
44pub const RPath = union(enum) {44pub const RPath = union(enum) {
45 lazy_path: LazyPath,45 lazy_path: LazyPath,
46 special: []const u8,46 special: []const u8,
...@@ -246,7 +246,6 @@ pub fn init(...@@ -246,7 +246,6 @@ pub fn init(
246 m: *Module,246 m: *Module,
247 owner: *std.Build,247 owner: *std.Build,
248 value: union(enum) { options: CreateOptions, existing: *const Module },248 value: union(enum) { options: CreateOptions, existing: *const Module },
249 compile: ?*Step.Compile,
250) void {249) void {
251 const allocator = owner.allocator;250 const allocator = owner.allocator;
252251
...@@ -254,7 +253,6 @@ pub fn init(...@@ -254,7 +253,6 @@ pub fn init(
254 .options => |options| {253 .options => |options| {
255 m.* = .{254 m.* = .{
256 .owner = owner,255 .owner = owner,
257 .depending_steps = .{},
258 .root_source_file = if (options.root_source_file) |lp| lp.dupe(owner) else null,256 .root_source_file = if (options.root_source_file) |lp| lp.dupe(owner) else null,
259 .import_table = .{},257 .import_table = .{},
260 .resolved_target = options.target,258 .resolved_target = options.target,
...@@ -294,19 +292,11 @@ pub fn init(...@@ -294,19 +292,11 @@ pub fn init(
294 m.* = existing.*;292 m.* = existing.*;
295 },293 },
296 }294 }
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);
305}295}
306296
307pub fn create(owner: *std.Build, options: CreateOptions) *Module {297pub fn create(owner: *std.Build, options: CreateOptions) *Module {
308 const m = owner.allocator.create(Module) catch @panic("OOM");298 const m = owner.allocator.create(Module) catch @panic("OOM");
309 m.init(owner, .{ .options = options }, null);299 m.init(owner, .{ .options = options });
310 return m;300 return m;
311}301}
312302
...@@ -314,69 +304,6 @@ pub fn create(owner: *std.Build, options: CreateOptions) *Module {...@@ -314,69 +304,6 @@ pub fn create(owner: *std.Build, options: CreateOptions) *Module {
314pub fn addImport(m: *Module, name: []const u8, module: *Module) void {304pub fn addImport(m: *Module, name: []const u8, module: *Module) void {
315 const b = m.owner;305 const b = m.owner;
316 m.import_table.put(b.allocator, b.dupe(name), module) catch @panic("OOM");306 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 }
380}307}
381308
382/// Creates a new module and adds it to be used with `@import`.309/// 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...@@ -392,91 +319,6 @@ pub fn addOptions(m: *Module, module_name: []const u8, options: *Step.Options) v
392 addImport(m, module_name, options.createModule());319 addImport(m, module_name, options.createModule());
393}320}
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
480pub const LinkSystemLibraryOptions = struct {322pub const LinkSystemLibraryOptions = struct {
481 /// Causes dynamic libraries to be linked regardless of whether they are323 /// Causes dynamic libraries to be linked regardless of whether they are
482 /// actually depended on. When false, dynamic libraries with no referenced324 /// actually depended on. When false, dynamic libraries with no referenced
...@@ -559,7 +401,6 @@ pub fn addCSourceFiles(m: *Module, options: AddCSourceFilesOptions) void {...@@ -559,7 +401,6 @@ pub fn addCSourceFiles(m: *Module, options: AddCSourceFilesOptions) void {
559 .flags = b.dupeStrings(options.flags),401 .flags = b.dupeStrings(options.flags),
560 };402 };
561 m.link_objects.append(allocator, .{ .c_source_files = c_source_files }) catch @panic("OOM");403 m.link_objects.append(allocator, .{ .c_source_files = c_source_files }) catch @panic("OOM");
562 addLazyPathDependenciesOnly(m, c_source_files.root);
563}404}
564405
565pub fn addCSourceFile(m: *Module, source: CSourceFile) void {406pub fn addCSourceFile(m: *Module, source: CSourceFile) void {
...@@ -568,7 +409,6 @@ pub fn addCSourceFile(m: *Module, source: CSourceFile) void {...@@ -568,7 +409,6 @@ pub fn addCSourceFile(m: *Module, source: CSourceFile) void {
568 const c_source_file = allocator.create(CSourceFile) catch @panic("OOM");409 const c_source_file = allocator.create(CSourceFile) catch @panic("OOM");
569 c_source_file.* = source.dupe(b);410 c_source_file.* = source.dupe(b);
570 m.link_objects.append(allocator, .{ .c_source_file = c_source_file }) catch @panic("OOM");411 m.link_objects.append(allocator, .{ .c_source_file = c_source_file }) catch @panic("OOM");
571 addLazyPathDependenciesOnly(m, source.file);
572}412}
573413
574/// Resource files must have the extension `.rc`.414/// Resource files must have the extension `.rc`.
...@@ -585,22 +425,16 @@ pub fn addWin32ResourceFile(m: *Module, source: RcSourceFile) void {...@@ -585,22 +425,16 @@ pub fn addWin32ResourceFile(m: *Module, source: RcSourceFile) void {
585 const rc_source_file = allocator.create(RcSourceFile) catch @panic("OOM");425 const rc_source_file = allocator.create(RcSourceFile) catch @panic("OOM");
586 rc_source_file.* = source.dupe(b);426 rc_source_file.* = source.dupe(b);
587 m.link_objects.append(allocator, .{ .win32_resource_file = rc_source_file }) catch @panic("OOM");427 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 }
592}428}
593429
594pub fn addAssemblyFile(m: *Module, source: LazyPath) void {430pub fn addAssemblyFile(m: *Module, source: LazyPath) void {
595 const b = m.owner;431 const b = m.owner;
596 m.link_objects.append(b.allocator, .{ .assembly_file = source.dupe(b) }) catch @panic("OOM");432 m.link_objects.append(b.allocator, .{ .assembly_file = source.dupe(b) }) catch @panic("OOM");
597 addLazyPathDependenciesOnly(m, source);
598}433}
599434
600pub fn addObjectFile(m: *Module, object: LazyPath) void {435pub fn addObjectFile(m: *Module, object: LazyPath) void {
601 const b = m.owner;436 const b = m.owner;
602 m.link_objects.append(b.allocator, .{ .static_path = object.dupe(b) }) catch @panic("OOM");437 m.link_objects.append(b.allocator, .{ .static_path = object.dupe(b) }) catch @panic("OOM");
603 addLazyPathDependenciesOnly(m, object);
604}438}
605439
606pub fn addObject(m: *Module, object: *Step.Compile) void {440pub fn addObject(m: *Module, object: *Step.Compile) void {
...@@ -616,51 +450,43 @@ pub fn linkLibrary(m: *Module, library: *Step.Compile) void {...@@ -616,51 +450,43 @@ pub fn linkLibrary(m: *Module, library: *Step.Compile) void {
616pub fn addAfterIncludePath(m: *Module, lazy_path: LazyPath) void {450pub fn addAfterIncludePath(m: *Module, lazy_path: LazyPath) void {
617 const b = m.owner;451 const b = m.owner;
618 m.include_dirs.append(b.allocator, .{ .path_after = lazy_path.dupe(b) }) catch @panic("OOM");452 m.include_dirs.append(b.allocator, .{ .path_after = lazy_path.dupe(b) }) catch @panic("OOM");
619 addLazyPathDependenciesOnly(m, lazy_path);
620}453}
621454
622pub fn addSystemIncludePath(m: *Module, lazy_path: LazyPath) void {455pub fn addSystemIncludePath(m: *Module, lazy_path: LazyPath) void {
623 const b = m.owner;456 const b = m.owner;
624 m.include_dirs.append(b.allocator, .{ .path_system = lazy_path.dupe(b) }) catch @panic("OOM");457 m.include_dirs.append(b.allocator, .{ .path_system = lazy_path.dupe(b) }) catch @panic("OOM");
625 addLazyPathDependenciesOnly(m, lazy_path);
626}458}
627459
628pub fn addIncludePath(m: *Module, lazy_path: LazyPath) void {460pub fn addIncludePath(m: *Module, lazy_path: LazyPath) void {
629 const b = m.owner;461 const b = m.owner;
630 m.include_dirs.append(b.allocator, .{ .path = lazy_path.dupe(b) }) catch @panic("OOM");462 m.include_dirs.append(b.allocator, .{ .path = lazy_path.dupe(b) }) catch @panic("OOM");
631 addLazyPathDependenciesOnly(m, lazy_path);
632}463}
633464
634pub fn addConfigHeader(m: *Module, config_header: *Step.ConfigHeader) void {465pub fn addConfigHeader(m: *Module, config_header: *Step.ConfigHeader) void {
635 const allocator = m.owner.allocator;466 const allocator = m.owner.allocator;
636 m.include_dirs.append(allocator, .{ .config_header_step = config_header }) catch @panic("OOM");467 m.include_dirs.append(allocator, .{ .config_header_step = config_header }) catch @panic("OOM");
637 addStepDependenciesOnly(m, &config_header.step);
638}468}
639469
640pub fn addSystemFrameworkPath(m: *Module, directory_path: LazyPath) void {470pub fn addSystemFrameworkPath(m: *Module, directory_path: LazyPath) void {
641 const b = m.owner;471 const b = m.owner;
642 m.include_dirs.append(b.allocator, .{ .framework_path_system = directory_path.dupe(b) }) catch472 m.include_dirs.append(b.allocator, .{ .framework_path_system = directory_path.dupe(b) }) catch
643 @panic("OOM");473 @panic("OOM");
644 addLazyPathDependenciesOnly(m, directory_path);
645}474}
646475
647pub fn addFrameworkPath(m: *Module, directory_path: LazyPath) void {476pub fn addFrameworkPath(m: *Module, directory_path: LazyPath) void {
648 const b = m.owner;477 const b = m.owner;
649 m.include_dirs.append(b.allocator, .{ .framework_path = directory_path.dupe(b) }) catch478 m.include_dirs.append(b.allocator, .{ .framework_path = directory_path.dupe(b) }) catch
650 @panic("OOM");479 @panic("OOM");
651 addLazyPathDependenciesOnly(m, directory_path);
652}480}
653481
654pub fn addLibraryPath(m: *Module, directory_path: LazyPath) void {482pub fn addLibraryPath(m: *Module, directory_path: LazyPath) void {
655 const b = m.owner;483 const b = m.owner;
656 m.lib_paths.append(b.allocator, directory_path.dupe(b)) catch @panic("OOM");484 m.lib_paths.append(b.allocator, directory_path.dupe(b)) catch @panic("OOM");
657 addLazyPathDependenciesOnly(m, directory_path);
658}485}
659486
660pub fn addRPath(m: *Module, directory_path: LazyPath) void {487pub fn addRPath(m: *Module, directory_path: LazyPath) void {
661 const b = m.owner;488 const b = m.owner;
662 m.rpaths.append(b.allocator, .{ .lazy_path = directory_path.dupe(b) }) catch @panic("OOM");489 m.rpaths.append(b.allocator, .{ .lazy_path = directory_path.dupe(b) }) catch @panic("OOM");
663 addLazyPathDependenciesOnly(m, directory_path);
664}490}
665491
666pub fn addRPathSpecial(m: *Module, bytes: []const u8) void {492pub fn addRPathSpecial(m: *Module, bytes: []const u8) void {
...@@ -784,7 +610,6 @@ fn addFlag(...@@ -784,7 +610,6 @@ fn addFlag(
784fn linkLibraryOrObject(m: *Module, other: *Step.Compile) void {610fn linkLibraryOrObject(m: *Module, other: *Step.Compile) void {
785 const allocator = m.owner.allocator;611 const allocator = m.owner.allocator;
786 _ = other.getEmittedBin(); // Indicate there is a dependency on the outputted binary.612 _ = other.getEmittedBin(); // Indicate there is a dependency on the outputted binary.
787 addStepDependenciesOnly(m, &other.step);
788613
789 if (other.rootModuleTarget().os.tag == .windows and other.isDynamicLibrary()) {614 if (other.rootModuleTarget().os.tag == .windows and other.isDynamicLibrary()) {
790 _ = other.getEmittedImplib(); // Indicate dependency on the outputted implib.615 _ = other.getEmittedImplib(); // Indicate dependency on the outputted implib.
...@@ -792,8 +617,6 @@ fn linkLibraryOrObject(m: *Module, other: *Step.Compile) void {...@@ -792,8 +617,6 @@ fn linkLibraryOrObject(m: *Module, other: *Step.Compile) void {
792617
793 m.link_objects.append(allocator, .{ .other_step = other }) catch @panic("OOM");618 m.link_objects.append(allocator, .{ .other_step = other }) catch @panic("OOM");
794 m.include_dirs.append(allocator, .{ .other_step = other }) catch @panic("OOM");619 m.include_dirs.append(allocator, .{ .other_step = other }) catch @panic("OOM");
795
796 addLazyPathDependenciesOnly(m, other.getEmittedIncludeTree());
797}620}
798621
799fn requireKnownTarget(m: *Module) std.Target {622fn requireKnownTarget(m: *Module) std.Target {
...@@ -802,6 +625,46 @@ fn requireKnownTarget(m: *Module) std.Target {...@@ -802,6 +625,46 @@ fn requireKnownTarget(m: *Module) std.Target {
802 return resolved_target.result;625 return resolved_target.result;
803}626}
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
805const Module = @This();668const Module = @This();
806const std = @import("std");669const std = @import("std");
807const assert = std.debug.assert;670const 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 {...@@ -389,7 +389,7 @@ pub fn create(owner: *std.Build, options: Options) *Compile {
389389
390 const compile = owner.allocator.create(Compile) catch @panic("OOM");390 const compile = owner.allocator.create(Compile) catch @panic("OOM");
391 compile.* = .{391 compile.* = .{
392 .root_module = undefined,392 .root_module = options.root_module,
393 .verbose_link = false,393 .verbose_link = false,
394 .verbose_cc = false,394 .verbose_cc = false,
395 .linkage = options.linkage,395 .linkage = options.linkage,
...@@ -432,8 +432,6 @@ pub fn create(owner: *std.Build, options: Options) *Compile {...@@ -432,8 +432,6 @@ pub fn create(owner: *std.Build, options: Options) *Compile {
432432
433 .zig_process = null,433 .zig_process = null,
434 };434 };
435 options.root_module.init(owner, .{ .existing = options.root_module }, compile);
436 compile.root_module = options.root_module;
437435
438 if (options.zig_lib_dir) |lp| {436 if (options.zig_lib_dir) |lp| {
439 compile.zig_lib_dir = lp.dupe(compile.step.owner);437 compile.zig_lib_dir = lp.dupe(compile.step.owner);
...@@ -607,16 +605,17 @@ pub fn dependsOnSystemLibrary(compile: *const Compile, name: []const u8) bool {...@@ -607,16 +605,17 @@ pub fn dependsOnSystemLibrary(compile: *const Compile, name: []const u8) bool {
607 var is_linking_libc = false;605 var is_linking_libc = false;
608 var is_linking_libcpp = false;606 var is_linking_libcpp = false;
609607
610 var dep_it = compile.root_module.iterateDependencies(compile, true);608 for (compile.getCompileDependencies(true)) |some_compile| {
611 while (dep_it.next()) |module| {609 for (some_compile.root_module.getGraph().modules) |mod| {
612 for (module.link_objects.items) |link_object| {610 for (mod.link_objects.items) |lo| {
613 switch (link_object) {611 switch (lo) {
614 .system_lib => |lib| if (mem.eql(u8, lib.name, name)) return true,612 .system_lib => |lib| if (mem.eql(u8, lib.name, name)) return true,
615 else => continue,613 else => {},
614 }
616 }615 }
616 if (mod.link_libc) is_linking_libc = true;
617 if (mod.link_libcpp) is_linking_libcpp = true;
617 }618 }
618 is_linking_libc = is_linking_libc or module.link_libc == true;
619 is_linking_libcpp = is_linking_libcpp or module.link_libcpp == true;
620 }619 }
621620
622 const target = compile.rootModuleTarget();621 const target = compile.rootModuleTarget();
...@@ -961,23 +960,22 @@ const CliNamedModules = struct {...@@ -961,23 +960,22 @@ const CliNamedModules = struct {
961 .modules = .{},960 .modules = .{},
962 .names = .{},961 .names = .{},
963 };962 };
964 var dep_it = root_module.iterateDependencies(null, false);963 const graph = root_module.getGraph();
965 {964 {
966 const item = dep_it.next().?;965 assert(graph.modules[0] == root_module);
967 assert(root_module == item.module);
968 try compile.modules.put(arena, root_module, {});966 try compile.modules.put(arena, root_module, {});
969 try compile.names.put(arena, "root", {});967 try compile.names.put(arena, "root", {});
970 }968 }
971 while (dep_it.next()) |item| {969 for (graph.modules[1..], graph.names[1..]) |mod, orig_name| {
972 var name = item.name;970 var name = orig_name;
973 var n: usize = 0;971 var n: usize = 0;
974 while (true) {972 while (true) {
975 const gop = try compile.names.getOrPut(arena, name);973 const gop = try compile.names.getOrPut(arena, name);
976 if (!gop.found_existing) {974 if (!gop.found_existing) {
977 try compile.modules.putNoClobber(arena, item.module, {});975 try compile.modules.putNoClobber(arena, mod, {});
978 break;976 break;
979 }977 }
980 name = try std.fmt.allocPrint(arena, "{s}{d}", .{ item.name, n });978 name = try std.fmt.allocPrint(arena, "{s}{d}", .{ orig_name, n });
981 n += 1;979 n += 1;
982 }980 }
983 }981 }
...@@ -1080,13 +1078,12 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {...@@ -1080,13 +1078,12 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
1080 // emitted if there is nothing to link.1078 // emitted if there is nothing to link.
1081 var total_linker_objects: usize = @intFromBool(compile.root_module.root_source_file != null);1079 var total_linker_objects: usize = @intFromBool(compile.root_module.root_source_file != null);
10821080
1083 {1081 // Fully recursive iteration including dynamic libraries to detect
1084 // Fully recursive iteration including dynamic libraries to detect1082 // libc and libc++ linkage.
1085 // libc and libc++ linkage.1083 for (compile.getCompileDependencies(true)) |some_compile| {
1086 var dep_it = compile.root_module.iterateDependencies(compile, true);1084 for (some_compile.root_module.getGraph().modules) |mod| {
1087 while (dep_it.next()) |key| {1085 if (mod.link_libc == true) compile.is_linking_libc = true;
1088 if (key.module.link_libc == true) compile.is_linking_libc = true;1086 if (mod.link_libcpp == true) compile.is_linking_libcpp = true;
1089 if (key.module.link_libcpp == true) compile.is_linking_libcpp = true;
1090 }1087 }
1091 }1088 }
10921089
...@@ -1094,265 +1091,265 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {...@@ -1094,265 +1091,265 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
10941091
1095 // For this loop, don't chase dynamic libraries because their link1092 // For this loop, don't chase dynamic libraries because their link
1096 // objects are already linked.1093 // objects are already linked.
1097 var dep_it = compile.root_module.iterateDependencies(compile, false);1094 for (compile.getCompileDependencies(false)) |dep_compile| {
10981095 for (dep_compile.root_module.getGraph().modules) |mod| {
1099 while (dep_it.next()) |dep| {1096 // While walking transitive dependencies, if a given link object is
1100 // While walking transitive dependencies, if a given link object is1097 // already included in a library, it should not redundantly be
1101 // already included in a library, it should not redundantly be1098 // placed on the linker line of the dependee.
1102 // placed on the linker line of the dependee.1099 const my_responsibility = dep_compile == compile;
1103 const my_responsibility = dep.compile.? == compile;1100 const already_linked = !my_responsibility and dep_compile.isDynamicLibrary();
1104 const already_linked = !my_responsibility and dep.compile.?.isDynamicLibrary();1101
11051102 // Inherit dependencies on darwin frameworks.
1106 // Inherit dependencies on darwin frameworks.1103 if (!already_linked) {
1107 if (!already_linked) {1104 for (mod.frameworks.keys(), mod.frameworks.values()) |name, info| {
1108 for (dep.module.frameworks.keys(), dep.module.frameworks.values()) |name, info| {1105 try frameworks.put(arena, name, info);
1109 try frameworks.put(arena, name, info);1106 }
1110 }1107 }
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)1109 // Inherit dependencies on system libraries and static libraries.
1132 continue;1110 for (mod.link_objects.items) |link_object| {
11331111 switch (link_object) {
1134 if ((system_lib.search_strategy != prev_search_strategy or1112 .static_path => |static_path| {
1135 system_lib.preferred_link_mode != prev_preferred_link_mode) and1113 if (my_responsibility) {
1136 compile.linkage != .static)1114 try zig_args.append(static_path.getPath2(mod.owner, step));
1137 {1115 total_linker_objects += 1;
1138 switch (system_lib.search_strategy) {1116 }
1139 .no_fallback => switch (system_lib.preferred_link_mode) {1117 },
1140 .dynamic => try zig_args.append("-search_dylibs_only"),1118 .system_lib => |system_lib| {
1141 .static => try zig_args.append("-search_static_only"),1119 const system_lib_gop = try seen_system_libs.getOrPut(arena, system_lib.name);
1142 },1120 if (system_lib_gop.found_existing) {
1143 .paths_first => switch (system_lib.preferred_link_mode) {1121 try zig_args.appendSlice(system_lib_gop.value_ptr.*);
1144 .dynamic => try zig_args.append("-search_paths_first"),1122 continue;
1145 .static => try zig_args.append("-search_paths_first_static"),1123 } else {
1146 },1124 system_lib_gop.value_ptr.* = &.{};
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 },
1151 }1125 }
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: {1127 if (already_linked)
1157 if (system_lib.needed) break :prefix "-needed-l";1128 continue;
1158 if (system_lib.weak) break :prefix "-weak-l";1129
1159 break :prefix "-l";1130 if ((system_lib.search_strategy != prev_search_strategy or
1160 };1131 system_lib.preferred_link_mode != prev_preferred_link_mode) and
1161 switch (system_lib.use_pkg_config) {1132 compile.linkage != .static)
1162 .no => try zig_args.append(b.fmt("{s}{s}", .{ prefix, system_lib.name })),1133 {
1163 .yes, .force => {1134 switch (system_lib.search_strategy) {
1164 if (compile.runPkgConfig(system_lib.name)) |result| {1135 .no_fallback => switch (system_lib.preferred_link_mode) {
1165 try zig_args.appendSlice(result.cflags);1136 .dynamic => try zig_args.append("-search_dylibs_only"),
1166 try zig_args.appendSlice(result.libs);1137 .static => try zig_args.append("-search_static_only"),
1167 try seen_system_libs.put(arena, system_lib.name, result.cflags);1138 },
1168 } else |err| switch (err) {1139 .paths_first => switch (system_lib.preferred_link_mode) {
1169 error.PkgConfigInvalidOutput,1140 .dynamic => try zig_args.append("-search_paths_first"),
1170 error.PkgConfigCrashed,1141 .static => try zig_args.append("-search_paths_first_static"),
1171 error.PkgConfigFailed,1142 },
1172 error.PkgConfigNotInstalled,1143 .mode_first => switch (system_lib.preferred_link_mode) {
1173 error.PackageNotFound,1144 .dynamic => try zig_args.append("-search_dylibs_first"),
1174 => switch (system_lib.use_pkg_config) {1145 .static => try zig_args.append("-search_static_first"),
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,
1187 },1146 },
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;
1213 }1147 }
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.1152 const prefix: []const u8 = prefix: {
1216 // For everything else, we directly link1153 if (system_lib.needed) break :prefix "-needed-l";
1217 // against the library file.1154 if (system_lib.weak) break :prefix "-weak-l";
1218 const full_path_lib = if (other_produces_implib)1155 break :prefix "-l";
1219 other.getGeneratedFilePath("generated_implib", &compile.step)1156 };
1220 else1157 switch (system_lib.use_pkg_config) {
1221 other.getGeneratedFilePath("generated_bin", &compile.step);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);1185 else => |e| return e,
1224 total_linker_objects += 1;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 and1206 if (compile.isStaticLibrary() and other_is_static) {
1227 compile.rootModuleTarget().os.tag != .windows)1207 // Avoid putting a static library inside a static library.
1228 {1208 break :l;
1229 if (fs.path.dirname(full_path_lib)) |dirname| {
1230 try zig_args.append("-rpath");
1231 try zig_args.append(dirname);
1232 }1209 }
1233 }
1234 },
1235 }
1236 },
1237 .assembly_file => |asm_file| l: {
1238 if (!my_responsibility) break :l;
12391210
1240 if (prev_has_cflags) {1211 // For DLLs, we must link against the implib.
1241 try zig_args.append("-cflags");1212 // For everything else, we directly link
1242 try zig_args.append("--");1213 // against the library file.
1243 prev_has_cflags = false;1214 const full_path_lib = if (other_produces_implib)
1244 }1215 other.getGeneratedFilePath("generated_implib", &compile.step)
1245 try zig_args.append(asm_file.getPath2(dep.module.owner, step));1216 else
1246 total_linker_objects += 1;1217 other.getGeneratedFilePath("generated_bin", &compile.step);
1247 },
12481218
1249 .c_source_file => |c_source_file| l: {1219 try zig_args.append(full_path_lib);
1250 if (!my_responsibility) break :l;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) {
1253 if (prev_has_cflags) {1236 if (prev_has_cflags) {
1254 try zig_args.append("-cflags");1237 try zig_args.append("-cflags");
1255 try zig_args.append("--");1238 try zig_args.append("--");
1256 prev_has_cflags = false;1239 prev_has_cflags = false;
1257 }1240 }
1258 } else {1241 try zig_args.append(asm_file.getPath2(mod.owner, step));
1259 try zig_args.append("-cflags");1242 total_linker_objects += 1;
1260 for (c_source_file.flags) |arg| {1243 },
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 },
12691244
1270 .c_source_files => |c_source_files| l: {1245 .c_source_file => |c_source_file| l: {
1271 if (!my_responsibility) break :l;1246 if (!my_responsibility) break :l;
12721247
1273 if (c_source_files.flags.len == 0) {1248 if (c_source_file.flags.len == 0) {
1274 if (prev_has_cflags) {1249 if (prev_has_cflags) {
1250 try zig_args.append("-cflags");
1251 try zig_args.append("--");
1252 prev_has_cflags = false;
1253 }
1254 } else {
1275 try zig_args.append("-cflags");1255 try zig_args.append("-cflags");
1256 for (c_source_file.flags) |arg| {
1257 try zig_args.append(arg);
1258 }
1276 try zig_args.append("--");1259 try zig_args.append("--");
1277 prev_has_cflags = false;1260 prev_has_cflags = true;
1278 }1261 }
1279 } else {1262 try zig_args.append(c_source_file.file.getPath2(mod.owner, step));
1280 try zig_args.append("-cflags");1263 total_linker_objects += 1;
1281 for (c_source_files.flags) |flag| {1264 },
1282 try zig_args.append(flag);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;
1283 }1282 }
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);1284 const root_path = c_source_files.root.getPath2(mod.owner, step);
1289 for (c_source_files.files) |file| {1285 for (c_source_files.files) |file| {
1290 try zig_args.append(b.pathJoin(&.{ root_path, file }));1286 try zig_args.append(b.pathJoin(&.{ root_path, file }));
1291 }1287 }
12921288
1293 total_linker_objects += c_source_files.files.len;1289 total_linker_objects += c_source_files.files.len;
1294 },1290 },
12951291
1296 .win32_resource_file => |rc_source_file| l: {1292 .win32_resource_file => |rc_source_file| l: {
1297 if (!my_responsibility) break :l;1293 if (!my_responsibility) break :l;
12981294
1299 if (rc_source_file.flags.len == 0 and rc_source_file.include_paths.len == 0) {1295 if (rc_source_file.flags.len == 0 and rc_source_file.include_paths.len == 0) {
1300 if (prev_has_rcflags) {1296 if (prev_has_rcflags) {
1297 try zig_args.append("-rcflags");
1298 try zig_args.append("--");
1299 prev_has_rcflags = false;
1300 }
1301 } else {
1301 try zig_args.append("-rcflags");1302 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 }
1302 try zig_args.append("--");1310 try zig_args.append("--");
1303 prev_has_rcflags = false;1311 prev_has_rcflags = true;
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));
1313 }1312 }
1314 try zig_args.append("--");1313 try zig_args.append(rc_source_file.file.getPath2(mod.owner, step));
1315 prev_has_rcflags = true;1314 total_linker_objects += 1;
1316 }1315 },
1317 try zig_args.append(rc_source_file.file.getPath2(dep.module.owner, step));1316 }
1318 total_linker_objects += 1;
1319 },
1320 }1317 }
1321 }
13221318
1323 // We need to emit the --mod argument here so that the above link objects1319 // 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 of1320 // have the correct parent module, but only if the module is part of
1325 // this compilation.1321 // this compilation.
1326 if (!my_responsibility) continue;1322 if (!my_responsibility) continue;
1327 if (cli_named_modules.modules.getIndex(dep.module)) |module_cli_index| {1323 if (cli_named_modules.modules.getIndex(mod)) |module_cli_index| {
1328 const module_cli_name = cli_named_modules.names.keys()[module_cli_index];1324 const module_cli_name = cli_named_modules.names.keys()[module_cli_index];
1329 try dep.module.appendZigProcessFlags(&zig_args, step);1325 try mod.appendZigProcessFlags(&zig_args, step);
13301326
1331 // --dep arguments1327 // --dep arguments
1332 try zig_args.ensureUnusedCapacity(dep.module.import_table.count() * 2);1328 try zig_args.ensureUnusedCapacity(mod.import_table.count() * 2);
1333 for (dep.module.import_table.keys(), dep.module.import_table.values()) |name, import| {1329 for (mod.import_table.keys(), mod.import_table.values()) |name, import| {
1334 const import_index = cli_named_modules.modules.getIndex(import).?;1330 const import_index = cli_named_modules.modules.getIndex(import).?;
1335 const import_cli_name = cli_named_modules.names.keys()[import_index];1331 const import_cli_name = cli_named_modules.names.keys()[import_index];
1336 zig_args.appendAssumeCapacity("--dep");1332 zig_args.appendAssumeCapacity("--dep");
1337 if (std.mem.eql(u8, import_cli_name, name)) {1333 if (std.mem.eql(u8, import_cli_name, name)) {
1338 zig_args.appendAssumeCapacity(import_cli_name);1334 zig_args.appendAssumeCapacity(import_cli_name);
1339 } else {1335 } else {
1340 zig_args.appendAssumeCapacity(b.fmt("{s}={s}", .{ name, import_cli_name }));1336 zig_args.appendAssumeCapacity(b.fmt("{s}={s}", .{ name, import_cli_name }));
1337 }
1341 }1338 }
1342 }
13431339
1344 // When the CLI sees a -M argument, it determines whether it1340 // When the CLI sees a -M argument, it determines whether it
1345 // implies the existence of a Zig compilation unit based on1341 // implies the existence of a Zig compilation unit based on
1346 // whether there is a root source file. If there is no root1342 // whether there is a root source file. If there is no root
1347 // source file, then this is not a zig compilation unit - it is1343 // source file, then this is not a zig compilation unit - it is
1348 // perhaps a set of linker objects, or C source files instead.1344 // perhaps a set of linker objects, or C source files instead.
1349 // Linker objects are added to the CLI globally, while C source1345 // Linker objects are added to the CLI globally, while C source
1350 // files must have a module parent.1346 // files must have a module parent.
1351 if (dep.module.root_source_file) |lp| {1347 if (mod.root_source_file) |lp| {
1352 const src = lp.getPath2(dep.module.owner, step);1348 const src = lp.getPath2(mod.owner, step);
1353 try zig_args.append(b.fmt("-M{s}={s}", .{ module_cli_name, src }));1349 try zig_args.append(b.fmt("-M{s}={s}", .{ module_cli_name, src }));
1354 } else if (moduleNeedsCliArg(dep.module)) {1350 } else if (moduleNeedsCliArg(mod)) {
1355 try zig_args.append(b.fmt("-M{s}", .{module_cli_name}));1351 try zig_args.append(b.fmt("-M{s}", .{module_cli_name}));
1352 }
1356 }1353 }
1357 }1354 }
1358 }1355 }
...@@ -2064,3 +2061,35 @@ fn moduleNeedsCliArg(mod: *const Module) bool {...@@ -2064,3 +2061,35 @@ fn moduleNeedsCliArg(mod: *const Module) bool {
2064 else => continue,2061 else => continue,
2065 } else false;2062 } else false;
2066}2063}
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 {...@@ -1719,15 +1719,12 @@ fn evalGeneric(run: *Run, child: *std.process.Child) !StdIoResult {
17191719
1720fn addPathForDynLibs(run: *Run, artifact: *Step.Compile) void {1720fn addPathForDynLibs(run: *Run, artifact: *Step.Compile) void {
1721 const b = run.step.owner;1721 const b = run.step.owner;
1722 var it = artifact.root_module.iterateDependencies(artifact, true);1722 const compiles = artifact.getCompileDependencies(true);
1723 while (it.next()) |item| {1723 for (compiles) |compile| {
1724 const other = item.compile.?;1724 if (compile.root_module.resolved_target.?.result.os.tag == .windows and
1725 if (item.module == other.root_module) {1725 compile.isDynamicLibrary())
1726 if (item.module.resolved_target.?.result.os.tag == .windows and1726 {
1727 other.isDynamicLibrary())1727 addPathDir(run, fs.path.dirname(compile.getEmittedBin().getPath2(b, &run.step)).?);
1728 {
1729 addPathDir(run, fs.path.dirname(other.getEmittedBin().getPath2(b, &run.step)).?);
1730 }
1731 }1728 }
1732 }1729 }
1733}1730}