master
1/// The one responsible for creating this module.
2owner: *std.Build,
3root_source_file: ?LazyPath,
4/// The modules that are mapped into this module's import table.
5/// Use `addImport` rather than modifying this field directly in order to
6/// maintain step dependency edges.
7import_table: std.StringArrayHashMapUnmanaged(*Module),
8
9resolved_target: ?std.Build.ResolvedTarget = null,
10optimize: ?std.builtin.OptimizeMode = null,
11dwarf_format: ?std.dwarf.Format,
12
13c_macros: ArrayList([]const u8),
14include_dirs: ArrayList(IncludeDir),
15lib_paths: ArrayList(LazyPath),
16rpaths: ArrayList(RPath),
17frameworks: std.StringArrayHashMapUnmanaged(LinkFrameworkOptions),
18link_objects: ArrayList(LinkObject),
19
20strip: ?bool,
21unwind_tables: ?std.builtin.UnwindTables,
22single_threaded: ?bool,
23stack_protector: ?bool,
24stack_check: ?bool,
25sanitize_c: ?std.zig.SanitizeC,
26sanitize_thread: ?bool,
27fuzz: ?bool,
28code_model: std.builtin.CodeModel,
29valgrind: ?bool,
30pic: ?bool,
31red_zone: ?bool,
32omit_frame_pointer: ?bool,
33error_tracing: ?bool,
34link_libc: ?bool,
35link_libcpp: ?bool,
36no_builtin: ?bool,
37
38/// Symbols to be exported when compiling to WebAssembly.
39export_symbol_names: []const []const u8 = &.{},
40
41/// Caches the result of `getGraph` when called multiple times.
42/// Use `getGraph` instead of accessing this field directly.
43cached_graph: Graph = .{ .modules = &.{}, .names = &.{} },
44
45pub const RPath = union(enum) {
46 lazy_path: LazyPath,
47 special: []const u8,
48};
49
50pub const LinkObject = union(enum) {
51 static_path: LazyPath,
52 other_step: *Step.Compile,
53 system_lib: SystemLib,
54 assembly_file: LazyPath,
55 c_source_file: *CSourceFile,
56 c_source_files: *CSourceFiles,
57 win32_resource_file: *RcSourceFile,
58};
59
60pub const SystemLib = struct {
61 name: []const u8,
62 needed: bool,
63 weak: bool,
64 use_pkg_config: UsePkgConfig,
65 preferred_link_mode: std.builtin.LinkMode,
66 search_strategy: SystemLib.SearchStrategy,
67
68 pub const UsePkgConfig = enum {
69 /// Don't use pkg-config, just pass -lfoo where foo is name.
70 no,
71 /// Try to get information on how to link the library from pkg-config.
72 /// If that fails, fall back to passing -lfoo where foo is name.
73 yes,
74 /// Try to get information on how to link the library from pkg-config.
75 /// If that fails, error out.
76 force,
77 };
78
79 pub const SearchStrategy = enum { paths_first, mode_first, no_fallback };
80};
81
82pub const CSourceLanguage = enum {
83 c,
84 cpp,
85
86 objective_c,
87 objective_cpp,
88
89 /// Standard assembly
90 assembly,
91 /// Assembly with the C preprocessor
92 assembly_with_preprocessor,
93
94 pub fn internalIdentifier(self: CSourceLanguage) []const u8 {
95 return switch (self) {
96 .c => "c",
97 .cpp => "c++",
98 .objective_c => "objective-c",
99 .objective_cpp => "objective-c++",
100 .assembly => "assembler",
101 .assembly_with_preprocessor => "assembler-with-cpp",
102 };
103 }
104};
105
106pub const CSourceFiles = struct {
107 root: LazyPath,
108 /// `files` is relative to `root`, which is
109 /// the build root by default
110 files: []const []const u8,
111 flags: []const []const u8,
112 /// By default, determines language of each file individually based on its file extension
113 language: ?CSourceLanguage,
114};
115
116pub const CSourceFile = struct {
117 file: LazyPath,
118 flags: []const []const u8 = &.{},
119 /// By default, determines language of each file individually based on its file extension
120 language: ?CSourceLanguage = null,
121
122 pub fn dupe(file: CSourceFile, b: *std.Build) CSourceFile {
123 return .{
124 .file = file.file.dupe(b),
125 .flags = b.dupeStrings(file.flags),
126 .language = file.language,
127 };
128 }
129};
130
131pub const RcSourceFile = struct {
132 file: LazyPath,
133 /// Any option that rc.exe accepts will work here, with the exception of:
134 /// - `/fo`: The output filename is set by the build system
135 /// - `/p`: Only running the preprocessor is not supported in this context
136 /// - `/:no-preprocess` (non-standard option): Not supported in this context
137 /// - Any MUI-related option
138 /// https://learn.microsoft.com/en-us/windows/win32/menurc/using-rc-the-rc-command-line-
139 ///
140 /// Implicitly defined options:
141 /// /x (ignore the INCLUDE environment variable)
142 /// /D_DEBUG or /DNDEBUG depending on the optimization mode
143 flags: []const []const u8 = &.{},
144 /// Include paths that may or may not exist yet and therefore need to be
145 /// specified as a LazyPath. Each path will be appended to the flags
146 /// as `/I <resolved path>`.
147 include_paths: []const LazyPath = &.{},
148
149 pub fn dupe(file: RcSourceFile, b: *std.Build) RcSourceFile {
150 const include_paths = b.allocator.alloc(LazyPath, file.include_paths.len) catch @panic("OOM");
151 for (include_paths, file.include_paths) |*dest, lazy_path| dest.* = lazy_path.dupe(b);
152 return .{
153 .file = file.file.dupe(b),
154 .flags = b.dupeStrings(file.flags),
155 .include_paths = include_paths,
156 };
157 }
158};
159
160pub const IncludeDir = union(enum) {
161 path: LazyPath,
162 path_system: LazyPath,
163 path_after: LazyPath,
164 framework_path: LazyPath,
165 framework_path_system: LazyPath,
166 other_step: *Step.Compile,
167 config_header_step: *Step.ConfigHeader,
168 embed_path: LazyPath,
169
170 pub fn appendZigProcessFlags(
171 include_dir: IncludeDir,
172 b: *std.Build,
173 zig_args: *std.array_list.Managed([]const u8),
174 asking_step: ?*Step,
175 ) !void {
176 const flag: []const u8, const lazy_path: LazyPath = switch (include_dir) {
177 // zig fmt: off
178 .path => |lp| .{ "-I", lp },
179 .path_system => |lp| .{ "-isystem", lp },
180 .path_after => |lp| .{ "-idirafter", lp },
181 .framework_path => |lp| .{ "-F", lp },
182 .framework_path_system => |lp| .{ "-iframework", lp },
183 .config_header_step => |ch| .{ "-I", ch.getOutputDir() },
184 .other_step => |comp| .{ "-I", comp.installed_headers_include_tree.?.getDirectory() },
185 // zig fmt: on
186 .embed_path => |lazy_path| {
187 // Special case: this is a single arg.
188 const resolved = lazy_path.getPath3(b, asking_step);
189 const arg = b.fmt("--embed-dir={f}", .{resolved});
190 return zig_args.append(arg);
191 },
192 };
193 const resolved_str = try lazy_path.getPath3(b, asking_step).toString(b.graph.arena);
194 return zig_args.appendSlice(&.{ flag, resolved_str });
195 }
196};
197
198pub const LinkFrameworkOptions = struct {
199 /// Causes dynamic libraries to be linked regardless of whether they are
200 /// actually depended on. When false, dynamic libraries with no referenced
201 /// symbols will be omitted by the linker.
202 needed: bool = false,
203 /// Marks all referenced symbols from this library as weak, meaning that if
204 /// a same-named symbol is provided by another compilation unit, instead of
205 /// emitting a "duplicate symbol" error, the linker will resolve all
206 /// references to the symbol with the strong version.
207 ///
208 /// When the linker encounters two weak symbols, the chosen one is
209 /// determined by the order compilation units are provided to the linker,
210 /// priority given to later ones.
211 weak: bool = false,
212};
213
214/// Unspecified options here will be inherited from parent `Module` when
215/// inserted into an import table.
216pub const CreateOptions = struct {
217 /// This could either be a generated file, in which case the module
218 /// contains exactly one file, or it could be a path to the root source
219 /// file of directory of files which constitute the module.
220 /// If `null`, it means this module is made up of only `link_objects`.
221 root_source_file: ?LazyPath = null,
222
223 /// The table of other modules that this module can access via `@import`.
224 /// Imports are allowed to be cyclical, so this table can be added to after
225 /// the `Module` is created via `addImport`.
226 imports: []const Import = &.{},
227
228 target: ?std.Build.ResolvedTarget = null,
229 optimize: ?std.builtin.OptimizeMode = null,
230
231 /// `true` requires a compilation that includes this Module to link libc.
232 /// `false` causes a build failure if a compilation that includes this Module would link libc.
233 /// `null` neither requires nor prevents libc from being linked.
234 link_libc: ?bool = null,
235 /// `true` requires a compilation that includes this Module to link libc++.
236 /// `false` causes a build failure if a compilation that includes this Module would link libc++.
237 /// `null` neither requires nor prevents libc++ from being linked.
238 link_libcpp: ?bool = null,
239 single_threaded: ?bool = null,
240 strip: ?bool = null,
241 unwind_tables: ?std.builtin.UnwindTables = null,
242 dwarf_format: ?std.dwarf.Format = null,
243 code_model: std.builtin.CodeModel = .default,
244 stack_protector: ?bool = null,
245 stack_check: ?bool = null,
246 sanitize_c: ?std.zig.SanitizeC = null,
247 sanitize_thread: ?bool = null,
248 fuzz: ?bool = null,
249 /// Whether to emit machine code that integrates with Valgrind.
250 valgrind: ?bool = null,
251 /// Position Independent Code
252 pic: ?bool = null,
253 red_zone: ?bool = null,
254 /// Whether to omit the stack frame pointer. Frees up a register and makes it
255 /// more difficult to obtain stack traces. Has target-dependent effects.
256 omit_frame_pointer: ?bool = null,
257 error_tracing: ?bool = null,
258 no_builtin: ?bool = null,
259};
260
261pub const Import = struct {
262 name: []const u8,
263 module: *Module,
264};
265
266pub fn init(
267 m: *Module,
268 owner: *std.Build,
269 value: union(enum) { options: CreateOptions, existing: *const Module },
270) void {
271 const allocator = owner.allocator;
272
273 switch (value) {
274 .options => |options| {
275 m.* = .{
276 .owner = owner,
277 .root_source_file = if (options.root_source_file) |lp| lp.dupe(owner) else null,
278 .import_table = .{},
279 .resolved_target = options.target,
280 .optimize = options.optimize,
281 .link_libc = options.link_libc,
282 .link_libcpp = options.link_libcpp,
283 .dwarf_format = options.dwarf_format,
284 .c_macros = .{},
285 .include_dirs = .{},
286 .lib_paths = .{},
287 .rpaths = .{},
288 .frameworks = .{},
289 .link_objects = .{},
290 .strip = options.strip,
291 .unwind_tables = options.unwind_tables,
292 .single_threaded = options.single_threaded,
293 .stack_protector = options.stack_protector,
294 .stack_check = options.stack_check,
295 .sanitize_c = options.sanitize_c,
296 .sanitize_thread = options.sanitize_thread,
297 .fuzz = options.fuzz,
298 .code_model = options.code_model,
299 .valgrind = options.valgrind,
300 .pic = options.pic,
301 .red_zone = options.red_zone,
302 .omit_frame_pointer = options.omit_frame_pointer,
303 .error_tracing = options.error_tracing,
304 .export_symbol_names = &.{},
305 .no_builtin = options.no_builtin,
306 };
307
308 m.import_table.ensureUnusedCapacity(allocator, options.imports.len) catch @panic("OOM");
309 for (options.imports) |dep| {
310 m.import_table.putAssumeCapacity(dep.name, dep.module);
311 }
312 },
313 .existing => |existing| {
314 m.* = existing.*;
315 },
316 }
317}
318
319pub fn create(owner: *std.Build, options: CreateOptions) *Module {
320 const m = owner.allocator.create(Module) catch @panic("OOM");
321 m.init(owner, .{ .options = options });
322 return m;
323}
324
325/// Adds an existing module to be used with `@import`.
326pub fn addImport(m: *Module, name: []const u8, module: *Module) void {
327 const b = m.owner;
328 m.import_table.put(b.allocator, b.dupe(name), module) catch @panic("OOM");
329}
330
331/// Creates a new module and adds it to be used with `@import`.
332pub fn addAnonymousImport(m: *Module, name: []const u8, options: CreateOptions) void {
333 const module = create(m.owner, options);
334 return addImport(m, name, module);
335}
336
337/// Converts a set of key-value pairs into a Zig source file, and then inserts it into
338/// the Module's import table with the specified name. This makes the options importable
339/// via `@import("module_name")`.
340pub fn addOptions(m: *Module, module_name: []const u8, options: *Step.Options) void {
341 addImport(m, module_name, options.createModule());
342}
343
344pub const LinkSystemLibraryOptions = struct {
345 /// Causes dynamic libraries to be linked regardless of whether they are
346 /// actually depended on. When false, dynamic libraries with no referenced
347 /// symbols will be omitted by the linker.
348 needed: bool = false,
349 /// Marks all referenced symbols from this library as weak, meaning that if
350 /// a same-named symbol is provided by another compilation unit, instead of
351 /// emitting a "duplicate symbol" error, the linker will resolve all
352 /// references to the symbol with the strong version.
353 ///
354 /// When the linker encounters two weak symbols, the chosen one is
355 /// determined by the order compilation units are provided to the linker,
356 /// priority given to later ones.
357 weak: bool = false,
358 use_pkg_config: SystemLib.UsePkgConfig = .yes,
359 preferred_link_mode: std.builtin.LinkMode = .dynamic,
360 search_strategy: SystemLib.SearchStrategy = .paths_first,
361};
362
363pub fn linkSystemLibrary(
364 m: *Module,
365 name: []const u8,
366 options: LinkSystemLibraryOptions,
367) void {
368 const b = m.owner;
369
370 const target = m.requireKnownTarget();
371 if (std.zig.target.isLibCLibName(target, name)) {
372 m.link_libc = true;
373 return;
374 }
375 if (std.zig.target.isLibCxxLibName(target, name)) {
376 m.link_libcpp = true;
377 return;
378 }
379
380 m.link_objects.append(b.allocator, .{
381 .system_lib = .{
382 .name = b.dupe(name),
383 .needed = options.needed,
384 .weak = options.weak,
385 .use_pkg_config = options.use_pkg_config,
386 .preferred_link_mode = options.preferred_link_mode,
387 .search_strategy = options.search_strategy,
388 },
389 }) catch @panic("OOM");
390}
391
392pub fn linkFramework(m: *Module, name: []const u8, options: LinkFrameworkOptions) void {
393 const b = m.owner;
394 m.frameworks.put(b.allocator, b.dupe(name), options) catch @panic("OOM");
395}
396
397pub const AddCSourceFilesOptions = struct {
398 /// When provided, `files` are relative to `root` rather than the
399 /// package that owns the `Compile` step.
400 root: ?LazyPath = null,
401 files: []const []const u8,
402 flags: []const []const u8 = &.{},
403 /// By default, determines language of each file individually based on its file extension
404 language: ?CSourceLanguage = null,
405};
406
407/// Handy when you have many non-Zig source files and want them all to have the same flags.
408pub fn addCSourceFiles(m: *Module, options: AddCSourceFilesOptions) void {
409 const b = m.owner;
410 const allocator = b.allocator;
411
412 for (options.files) |path| {
413 if (std.fs.path.isAbsolute(path)) {
414 std.debug.panic(
415 "file paths added with 'addCSourceFiles' must be relative, found absolute path '{s}'",
416 .{path},
417 );
418 }
419 }
420
421 const c_source_files = allocator.create(CSourceFiles) catch @panic("OOM");
422 c_source_files.* = .{
423 .root = options.root orelse b.path(""),
424 .files = b.dupeStrings(options.files),
425 .flags = b.dupeStrings(options.flags),
426 .language = options.language,
427 };
428 m.link_objects.append(allocator, .{ .c_source_files = c_source_files }) catch @panic("OOM");
429}
430
431pub fn addCSourceFile(m: *Module, source: CSourceFile) void {
432 const b = m.owner;
433 const allocator = b.allocator;
434 const c_source_file = allocator.create(CSourceFile) catch @panic("OOM");
435 c_source_file.* = source.dupe(b);
436 m.link_objects.append(allocator, .{ .c_source_file = c_source_file }) catch @panic("OOM");
437}
438
439/// Resource files must have the extension `.rc`.
440/// Can be called regardless of target. The .rc file will be ignored
441/// if the target object format does not support embedded resources.
442pub fn addWin32ResourceFile(m: *Module, source: RcSourceFile) void {
443 const b = m.owner;
444 const allocator = b.allocator;
445 const target = m.requireKnownTarget();
446 // Only the PE/COFF format has a Resource Table, so for any other target
447 // the resource file is ignored.
448 if (target.ofmt != .coff) return;
449
450 const rc_source_file = allocator.create(RcSourceFile) catch @panic("OOM");
451 rc_source_file.* = source.dupe(b);
452 m.link_objects.append(allocator, .{ .win32_resource_file = rc_source_file }) catch @panic("OOM");
453}
454
455pub fn addAssemblyFile(m: *Module, source: LazyPath) void {
456 const b = m.owner;
457 m.link_objects.append(b.allocator, .{ .assembly_file = source.dupe(b) }) catch @panic("OOM");
458}
459
460pub fn addObjectFile(m: *Module, object: LazyPath) void {
461 const b = m.owner;
462 m.link_objects.append(b.allocator, .{ .static_path = object.dupe(b) }) catch @panic("OOM");
463}
464
465pub fn addObject(m: *Module, object: *Step.Compile) void {
466 assert(object.kind == .obj or object.kind == .test_obj);
467 m.linkLibraryOrObject(object);
468}
469
470pub fn linkLibrary(m: *Module, library: *Step.Compile) void {
471 assert(library.kind == .lib);
472 m.linkLibraryOrObject(library);
473}
474
475pub fn addAfterIncludePath(m: *Module, lazy_path: LazyPath) void {
476 const b = m.owner;
477 m.include_dirs.append(b.allocator, .{ .path_after = lazy_path.dupe(b) }) catch @panic("OOM");
478}
479
480pub fn addSystemIncludePath(m: *Module, lazy_path: LazyPath) void {
481 const b = m.owner;
482 m.include_dirs.append(b.allocator, .{ .path_system = lazy_path.dupe(b) }) catch @panic("OOM");
483}
484
485pub fn addIncludePath(m: *Module, lazy_path: LazyPath) void {
486 const b = m.owner;
487 m.include_dirs.append(b.allocator, .{ .path = lazy_path.dupe(b) }) catch @panic("OOM");
488}
489
490pub fn addConfigHeader(m: *Module, config_header: *Step.ConfigHeader) void {
491 const allocator = m.owner.allocator;
492 m.include_dirs.append(allocator, .{ .config_header_step = config_header }) catch @panic("OOM");
493}
494
495pub fn addSystemFrameworkPath(m: *Module, directory_path: LazyPath) void {
496 const b = m.owner;
497 m.include_dirs.append(b.allocator, .{ .framework_path_system = directory_path.dupe(b) }) catch
498 @panic("OOM");
499}
500
501pub fn addFrameworkPath(m: *Module, directory_path: LazyPath) void {
502 const b = m.owner;
503 m.include_dirs.append(b.allocator, .{ .framework_path = directory_path.dupe(b) }) catch
504 @panic("OOM");
505}
506
507pub fn addEmbedPath(m: *Module, lazy_path: LazyPath) void {
508 const b = m.owner;
509 m.include_dirs.append(b.allocator, .{ .embed_path = lazy_path.dupe(b) }) catch @panic("OOM");
510}
511
512pub fn addLibraryPath(m: *Module, directory_path: LazyPath) void {
513 const b = m.owner;
514 m.lib_paths.append(b.allocator, directory_path.dupe(b)) catch @panic("OOM");
515}
516
517pub fn addRPath(m: *Module, directory_path: LazyPath) void {
518 const b = m.owner;
519 m.rpaths.append(b.allocator, .{ .lazy_path = directory_path.dupe(b) }) catch @panic("OOM");
520}
521
522pub fn addRPathSpecial(m: *Module, bytes: []const u8) void {
523 const b = m.owner;
524 m.rpaths.append(b.allocator, .{ .special = b.dupe(bytes) }) catch @panic("OOM");
525}
526
527/// Equvialent to the following C code, applied to all C source files owned by
528/// this `Module`:
529/// ```c
530/// #define name value
531/// ```
532/// `name` and `value` need not live longer than the function call.
533pub fn addCMacro(m: *Module, name: []const u8, value: []const u8) void {
534 const b = m.owner;
535 m.c_macros.append(b.allocator, b.fmt("-D{s}={s}", .{ name, value })) catch @panic("OOM");
536}
537
538pub fn appendZigProcessFlags(
539 m: *Module,
540 zig_args: *std.array_list.Managed([]const u8),
541 asking_step: ?*Step,
542) !void {
543 const b = m.owner;
544
545 try addFlag(zig_args, m.strip, "-fstrip", "-fno-strip");
546 try addFlag(zig_args, m.single_threaded, "-fsingle-threaded", "-fno-single-threaded");
547 try addFlag(zig_args, m.stack_check, "-fstack-check", "-fno-stack-check");
548 try addFlag(zig_args, m.stack_protector, "-fstack-protector", "-fno-stack-protector");
549 try addFlag(zig_args, m.omit_frame_pointer, "-fomit-frame-pointer", "-fno-omit-frame-pointer");
550 try addFlag(zig_args, m.error_tracing, "-ferror-tracing", "-fno-error-tracing");
551 try addFlag(zig_args, m.sanitize_thread, "-fsanitize-thread", "-fno-sanitize-thread");
552 try addFlag(zig_args, m.fuzz, "-ffuzz", "-fno-fuzz");
553 try addFlag(zig_args, m.valgrind, "-fvalgrind", "-fno-valgrind");
554 try addFlag(zig_args, m.pic, "-fPIC", "-fno-PIC");
555 try addFlag(zig_args, m.red_zone, "-mred-zone", "-mno-red-zone");
556 try addFlag(zig_args, m.no_builtin, "-fno-builtin", "-fbuiltin");
557
558 if (m.sanitize_c) |sc| switch (sc) {
559 .off => try zig_args.append("-fno-sanitize-c"),
560 .trap => try zig_args.append("-fsanitize-c=trap"),
561 .full => try zig_args.append("-fsanitize-c=full"),
562 };
563
564 if (m.dwarf_format) |dwarf_format| {
565 try zig_args.append(switch (dwarf_format) {
566 .@"32" => "-gdwarf32",
567 .@"64" => "-gdwarf64",
568 });
569 }
570
571 if (m.unwind_tables) |unwind_tables| {
572 try zig_args.append(switch (unwind_tables) {
573 .none => "-fno-unwind-tables",
574 .sync => "-funwind-tables",
575 .async => "-fasync-unwind-tables",
576 });
577 }
578
579 try zig_args.ensureUnusedCapacity(1);
580 if (m.optimize) |optimize| switch (optimize) {
581 .Debug => zig_args.appendAssumeCapacity("-ODebug"),
582 .ReleaseSmall => zig_args.appendAssumeCapacity("-OReleaseSmall"),
583 .ReleaseFast => zig_args.appendAssumeCapacity("-OReleaseFast"),
584 .ReleaseSafe => zig_args.appendAssumeCapacity("-OReleaseSafe"),
585 };
586
587 if (m.code_model != .default) {
588 try zig_args.append("-mcmodel");
589 try zig_args.append(@tagName(m.code_model));
590 }
591
592 if (m.resolved_target) |*target| {
593 // Communicate the query via CLI since it's more compact.
594 if (!target.query.isNative()) {
595 try zig_args.appendSlice(&.{
596 "-target", try target.query.zigTriple(b.allocator),
597 "-mcpu", try target.query.serializeCpuAlloc(b.allocator),
598 });
599 if (target.query.dynamic_linker) |dynamic_linker| {
600 if (dynamic_linker.get()) |dynamic_linker_path| {
601 try zig_args.append("--dynamic-linker");
602 try zig_args.append(dynamic_linker_path);
603 } else {
604 try zig_args.append("--no-dynamic-linker");
605 }
606 }
607 }
608 }
609
610 for (m.export_symbol_names) |symbol_name| {
611 try zig_args.append(b.fmt("--export={s}", .{symbol_name}));
612 }
613
614 for (m.include_dirs.items) |include_dir| {
615 try include_dir.appendZigProcessFlags(b, zig_args, asking_step);
616 }
617
618 try zig_args.appendSlice(m.c_macros.items);
619
620 try zig_args.ensureUnusedCapacity(2 * m.lib_paths.items.len);
621 for (m.lib_paths.items) |lib_path| {
622 zig_args.appendAssumeCapacity("-L");
623 zig_args.appendAssumeCapacity(lib_path.getPath2(b, asking_step));
624 }
625
626 try zig_args.ensureUnusedCapacity(2 * m.rpaths.items.len);
627 for (m.rpaths.items) |rpath| switch (rpath) {
628 .lazy_path => |lp| {
629 zig_args.appendAssumeCapacity("-rpath");
630 zig_args.appendAssumeCapacity(lp.getPath2(b, asking_step));
631 },
632 .special => |bytes| {
633 zig_args.appendAssumeCapacity("-rpath");
634 zig_args.appendAssumeCapacity(bytes);
635 },
636 };
637}
638
639fn addFlag(
640 args: *std.array_list.Managed([]const u8),
641 opt: ?bool,
642 then_name: []const u8,
643 else_name: []const u8,
644) !void {
645 const cond = opt orelse return;
646 return args.append(if (cond) then_name else else_name);
647}
648
649fn linkLibraryOrObject(m: *Module, other: *Step.Compile) void {
650 const allocator = m.owner.allocator;
651 _ = other.getEmittedBin(); // Indicate there is a dependency on the outputted binary.
652
653 if (other.rootModuleTarget().os.tag == .windows and other.isDynamicLibrary()) {
654 _ = other.getEmittedImplib(); // Indicate dependency on the outputted implib.
655 }
656
657 m.link_objects.append(allocator, .{ .other_step = other }) catch @panic("OOM");
658 m.include_dirs.append(allocator, .{ .other_step = other }) catch @panic("OOM");
659}
660
661fn requireKnownTarget(m: *Module) *const std.Target {
662 const resolved_target = &(m.resolved_target orelse
663 @panic("this API requires the Module to be created with a known 'target' field"));
664 return &resolved_target.result;
665}
666
667/// Elements of `modules` and `names` are matched one-to-one.
668pub const Graph = struct {
669 modules: []const *Module,
670 names: []const []const u8,
671};
672
673/// Intended to be used during the make phase only.
674///
675/// Given that `root` is the root `Module` of a compilation, return all `Module`s
676/// in the module graph, including `root` itself. `root` is guaranteed to be the
677/// first module in the returned slice.
678pub fn getGraph(root: *Module) Graph {
679 if (root.cached_graph.modules.len != 0) {
680 return root.cached_graph;
681 }
682
683 const arena = root.owner.graph.arena;
684
685 var modules: std.AutoArrayHashMapUnmanaged(*std.Build.Module, []const u8) = .empty;
686 var next_idx: usize = 0;
687
688 modules.putNoClobber(arena, root, "root") catch @panic("OOM");
689
690 while (next_idx < modules.count()) {
691 const mod = modules.keys()[next_idx];
692 next_idx += 1;
693 modules.ensureUnusedCapacity(arena, mod.import_table.count()) catch @panic("OOM");
694 for (mod.import_table.keys(), mod.import_table.values()) |import_name, other_mod| {
695 modules.putAssumeCapacity(other_mod, import_name);
696 }
697 }
698
699 const result: Graph = .{
700 .modules = modules.keys(),
701 .names = modules.values(),
702 };
703 root.cached_graph = result;
704 return result;
705}
706
707const Module = @This();
708const std = @import("std");
709const assert = std.debug.assert;
710const LazyPath = std.Build.LazyPath;
711const Step = std.Build.Step;
712const ArrayList = std.ArrayList;