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;