| 1 | const Compile = @This(); |
| 2 | |
| 3 | const builtin = @import("builtin"); |
| 4 | |
| 5 | const std = @import("std"); |
| 6 | const Io = std.Io; |
| 7 | const mem = std.mem; |
| 8 | const fs = std.fs; |
| 9 | const assert = std.debug.assert; |
| 10 | const panic = std.debug.panic; |
| 11 | const StringHashMap = std.StringHashMap; |
| 12 | const Allocator = std.mem.Allocator; |
| 13 | const Step = std.Build.Step; |
| 14 | const LazyPath = std.Build.LazyPath; |
| 15 | const Module = std.Build.Module; |
| 16 | const InstallDir = std.Build.InstallDir; |
| 17 | const Path = std.Build.Cache.Path; |
| 18 | const Configuration = std.Build.Configuration; |
| 19 | |
| 20 | pub const base_tag: Step.Tag = .compile; |
| 21 | |
| 22 | step: Step, |
| 23 | root_module: *Module, |
| 24 | |
| 25 | name: []const u8, |
| 26 | linker_script: ?LazyPath = null, |
| 27 | version_script: ?LazyPath = null, |
| 28 | /// Deprecated. |
| 29 | out_filename: []const u8, |
| 30 | linkage: ?std.builtin.LinkMode = null, |
| 31 | version: ?std.SemanticVersion, |
| 32 | kind: Kind, |
| 33 | formatted_panics: ?bool = null, |
| 34 | compress_debug_sections: std.zig.CompressDebugSections = .none, |
| 35 | verbose_link: bool, |
| 36 | verbose_cc: bool, |
| 37 | bundle_compiler_rt: ?bool = null, |
| 38 | bundle_ubsan_rt: ?bool = null, |
| 39 | rdynamic: bool, |
| 40 | import_memory: bool = false, |
| 41 | export_memory: bool = false, |
| 42 | /// For WebAssembly targets, this will allow for undefined symbols to |
| 43 | /// be imported from the host environment. |
| 44 | import_symbols: bool = false, |
| 45 | /// (WebAssembly) import function table from the host environment |
| 46 | import_table: bool = false, |
| 47 | export_table: bool = false, |
| 48 | growable_table: bool = false, |
| 49 | initial_memory: ?u64 = null, |
| 50 | max_memory: ?u64 = null, |
| 51 | shared_memory: bool = false, |
| 52 | global_base: ?u64 = null, |
| 53 | /// Set via options; intended to be read-only after that. |
| 54 | zig_lib_dir: ?LazyPath, |
| 55 | filters: []const []const u8, |
| 56 | test_runner: ?TestRunner, |
| 57 | wasi_exec_model: ?std.builtin.WasiExecModel = null, |
| 58 | |
| 59 | installed_headers: std.ArrayList(HeaderInstallation), |
| 60 | |
| 61 | /// This step is used to create an include tree that dependent modules can add to their include |
| 62 | /// search paths. Installed headers are copied to this step. |
| 63 | /// This step is created the first time a module links with this artifact and is not |
| 64 | /// created otherwise. |
| 65 | installed_headers_include_tree: ?*Step.WriteFile = null, |
| 66 | |
| 67 | /// Deprecated. This functionality will be moved to an external package: |
| 68 | /// https://codeberg.org/ziglang/rc |
| 69 | /// |
| 70 | /// Behavior of automatic detection of include directories when compiling .rc files. |
| 71 | /// any: Use MSVC if available, fall back to MinGW. |
| 72 | /// msvc: Use MSVC include paths (must be present on the system). |
| 73 | /// gnu: Use MinGW include paths (distributed with Zig). |
| 74 | /// none: Do not use any autodetected include paths. |
| 75 | rc_includes: std.zig.RcIncludes = .any, |
| 76 | |
| 77 | /// Deprecated. This functionality will be moved to an external package: |
| 78 | /// https://codeberg.org/ziglang/rc |
| 79 | /// |
| 80 | /// (Windows) .manifest file to embed in the compilation |
| 81 | /// Set via options; intended to be read-only after that. |
| 82 | win32_manifest: ?LazyPath = null, |
| 83 | |
| 84 | /// (Windows) .def file to embed in the compilation (dll) |
| 85 | /// Set via options; intended to be read-only after that. |
| 86 | win32_module_definition: ?LazyPath = null, |
| 87 | |
| 88 | /// Base address for an executable image. |
| 89 | image_base: ?u64 = null, |
| 90 | |
| 91 | libc_file: ?LazyPath = null, |
| 92 | |
| 93 | each_lib_rpath: ?bool = null, |
| 94 | /// On ELF targets, this will emit a link section called ".note.gnu.build-id" |
| 95 | /// which can be used to coordinate a stripped binary with its debug symbols. |
| 96 | /// |
| 97 | /// As an example, the bloaty project refuses to work unless its inputs have |
| 98 | /// build ids, in order to prevent accidental mismatches. |
| 99 | /// |
| 100 | /// The default is to not include this section because it slows down linking. |
| 101 | /// |
| 102 | /// This option overrides the CLI argument passed to `zig build`. |
| 103 | build_id: ?std.zig.BuildId = null, |
| 104 | |
| 105 | /// Create a .eh_frame_hdr section and a PT.GNU_EH_FRAME segment in the ELF |
| 106 | /// file. |
| 107 | link_eh_frame_hdr: bool = false, |
| 108 | link_emit_relocs: bool = false, |
| 109 | |
| 110 | /// Place every function in its own section so that unused ones may be |
| 111 | /// safely garbage-collected during the linking phase. |
| 112 | link_function_sections: bool = false, |
| 113 | |
| 114 | /// Place every data in its own section so that unused ones may be |
| 115 | /// safely garbage-collected during the linking phase. |
| 116 | link_data_sections: bool = false, |
| 117 | |
| 118 | /// Remove functions and data that are unreachable by the entry point or |
| 119 | /// exported symbols. |
| 120 | link_gc_sections: ?bool = null, |
| 121 | |
| 122 | /// (Windows) Whether or not to enable ASLR. Maps to the /DYNAMICBASE[:NO] linker argument. |
| 123 | linker_dynamicbase: bool = true, |
| 124 | |
| 125 | linker_allow_shlib_undefined: ?bool = null, |
| 126 | |
| 127 | /// Allow version scripts to refer to undefined symbols. |
| 128 | linker_allow_undefined_version: ?bool = null, |
| 129 | |
| 130 | // Enable (or disable) the new DT_RUNPATH tag in the dynamic section. |
| 131 | linker_enable_new_dtags: ?bool = null, |
| 132 | |
| 133 | /// Permit read-only relocations in read-only segments. Disallowed by default. |
| 134 | link_z_notext: bool = false, |
| 135 | |
| 136 | /// Force all relocations to be read-only after processing. |
| 137 | link_z_relro: bool = true, |
| 138 | |
| 139 | /// Allow relocations to be lazily processed after load. |
| 140 | link_z_lazy: bool = false, |
| 141 | |
| 142 | /// Common page size |
| 143 | link_z_common_page_size: ?u64 = null, |
| 144 | |
| 145 | /// Maximum page size |
| 146 | link_z_max_page_size: ?u64 = null, |
| 147 | |
| 148 | /// Force a fatal error if any undefined symbols remain. |
| 149 | link_z_defs: bool = false, |
| 150 | |
| 151 | /// (Darwin) Install name for the dylib |
| 152 | install_name: ?[]const u8 = null, |
| 153 | |
| 154 | /// Must be passed in via `Options`. |
| 155 | entitlements: ?LazyPath = null, |
| 156 | |
| 157 | /// (Darwin) Size of the pagezero segment. |
| 158 | pagezero_size: ?u64 = null, |
| 159 | |
| 160 | /// (Darwin) Set size of the padding between the end of load commands |
| 161 | /// and start of `__TEXT,__text` section. |
| 162 | headerpad_size: ?u32 = null, |
| 163 | |
| 164 | /// (Darwin) Automatically Set size of the padding between the end of load commands |
| 165 | /// and start of `__TEXT,__text` section to a value fitting all paths expanded to MAXPATHLEN. |
| 166 | headerpad_max_install_names: bool = false, |
| 167 | |
| 168 | /// (Darwin) Remove dylibs that are unreachable by the entry point or exported symbols. |
| 169 | dead_strip_dylibs: bool = false, |
| 170 | |
| 171 | /// (Darwin) Force load all members of static archives that implement an Objective-C class or category |
| 172 | force_load_objc: bool = false, |
| 173 | |
| 174 | /// Whether local symbols should be discarded from the symbol table. |
| 175 | discard_local_symbols: bool = false, |
| 176 | |
| 177 | /// Position Independent Executable |
| 178 | pie: ?bool = null, |
| 179 | |
| 180 | /// Link Time Optimization mode |
| 181 | lto: ?std.zig.LtoMode = null, |
| 182 | |
| 183 | dll_export_fns: ?bool = null, |
| 184 | |
| 185 | subsystem: ?std.zig.Subsystem = null, |
| 186 | |
| 187 | /// (Windows) When targeting the MinGW ABI, use the unicode entry point (wmain/wWinMain) |
| 188 | mingw_unicode_entry_point: bool = false, |
| 189 | |
| 190 | /// How the linker must handle the entry point of the executable. |
| 191 | entry: Entry = .default, |
| 192 | |
| 193 | /// List of symbols forced as undefined in the symbol table |
| 194 | /// thus forcing their resolution by the linker. |
| 195 | /// Corresponds to `-u <symbol>` for ELF/MachO and `/include:<symbol>` for COFF/PE. |
| 196 | force_undefined_symbols: std.array_hash_map.String(void), |
| 197 | |
| 198 | /// Overrides the default stack size |
| 199 | stack_size: ?u64 = null, |
| 200 | |
| 201 | use_llvm: ?bool, |
| 202 | use_lld: ?bool, |
| 203 | use_new_linker: ?bool, |
| 204 | |
| 205 | /// Corresponds to the `-fallow-so-scripts` / `-fno-allow-so-scripts` CLI |
| 206 | /// flags, overriding the global user setting provided to the `zig build` |
| 207 | /// command. |
| 208 | /// |
| 209 | /// The compiler defaults this value to off so that users whose system shared |
| 210 | /// libraries are all ELF files don't have to pay the cost of checking every |
| 211 | /// file to find out if it is a text file instead. |
| 212 | allow_so_scripts: ?bool = null, |
| 213 | |
| 214 | /// This is an advanced setting that can change the intent of this Compile step. |
| 215 | /// If this value is non-null, it means that this Compile step exists to |
| 216 | /// check for compile errors and return *success* if they match, and failure |
| 217 | /// otherwise. |
| 218 | expect_errors: ?ExpectedCompileErrors = null, |
| 219 | |
| 220 | /// The maximum number of distinct errors within a compilation step Defaults to |
| 221 | /// `std.math.maxInt(u16)`. Overrides the argument passed to `zig build`. |
| 222 | error_limit: ?u32 = null, |
| 223 | |
| 224 | /// Enables coverage instrumentation that is only useful if you are using third |
| 225 | /// party fuzzers that depend on it. Otherwise, slows down the instrumented |
| 226 | /// binary with unnecessary function calls. |
| 227 | /// |
| 228 | /// This kind of coverage instrumentation is used by AFLplusplus v4.21c, |
| 229 | /// however, modern fuzzers - including Zig - have switched to using "inline |
| 230 | /// 8-bit counters" or "inline bool flag" which incurs only a single |
| 231 | /// instruction for coverage, along with "trace cmp" which instruments |
| 232 | /// comparisons and reports the operands. |
| 233 | /// |
| 234 | /// To instead enable fuzz testing instrumentation on a compilation using Zig's |
| 235 | /// builtin fuzzer, see the `fuzz` flag in `Module`. |
| 236 | sanitize_coverage_trace_pc_guard: ?bool = null, |
| 237 | |
| 238 | /// Enable or disable incremental compilation. |
| 239 | /// |
| 240 | /// Incremental compilation reduces compile time by mutating an existing build artifact. Non- |
| 241 | /// incremental compilation is slower but preserves previous build artifacts. |
| 242 | incremental: ?bool = null, |
| 243 | |
| 244 | emit_directory: Configuration.OptionalGeneratedFileIndex = .none, |
| 245 | generated_docs: Configuration.OptionalGeneratedFileIndex = .none, |
| 246 | generated_asm: Configuration.OptionalGeneratedFileIndex = .none, |
| 247 | generated_bin: Configuration.OptionalGeneratedFileIndex = .none, |
| 248 | generated_pdb: Configuration.OptionalGeneratedFileIndex = .none, |
| 249 | generated_implib: Configuration.OptionalGeneratedFileIndex = .none, |
| 250 | generated_llvm_bc: Configuration.OptionalGeneratedFileIndex = .none, |
| 251 | generated_llvm_ir: Configuration.OptionalGeneratedFileIndex = .none, |
| 252 | generated_h: Configuration.OptionalGeneratedFileIndex = .none, |
| 253 | |
| 254 | pub const ExpectedCompileErrors = union(enum) { |
| 255 | contains: []const u8, |
| 256 | exact: []const []const u8, |
| 257 | starts_with: []const u8, |
| 258 | stderr_contains: []const u8, |
| 259 | }; |
| 260 | |
| 261 | pub const Entry = union(enum) { |
| 262 | /// Let the compiler decide whether to make an entry point and what to name |
| 263 | /// it. |
| 264 | default, |
| 265 | /// The executable will have no entry point. |
| 266 | disabled, |
| 267 | /// The executable will have an entry point with the default symbol name. |
| 268 | enabled, |
| 269 | /// The executable will have an entry point with the specified symbol name. |
| 270 | symbol_name: []const u8, |
| 271 | }; |
| 272 | |
| 273 | pub const Options = struct { |
| 274 | name: []const u8, |
| 275 | root_module: *Module, |
| 276 | kind: Kind, |
| 277 | linkage: ?std.builtin.LinkMode = null, |
| 278 | version: ?std.SemanticVersion = null, |
| 279 | max_rss: u64 = 0, |
| 280 | filters: []const []const u8 = &.{}, |
| 281 | test_runner: ?TestRunner = null, |
| 282 | use_llvm: ?bool = null, |
| 283 | use_lld: ?bool = null, |
| 284 | zig_lib_dir: ?LazyPath = null, |
| 285 | /// Deprecated. This functionality will be moved to an external package: |
| 286 | /// https://codeberg.org/ziglang/rc |
| 287 | /// |
| 288 | /// Embed a `.manifest` file in the compilation if the object format supports it. |
| 289 | /// https://learn.microsoft.com/en-us/windows/win32/sbscs/manifest-files-reference |
| 290 | /// Manifest files must have the extension `.manifest`. |
| 291 | /// Can be set regardless of target. The `.manifest` file will be ignored |
| 292 | /// if the target object format does not support embedded manifests. |
| 293 | win32_manifest: ?LazyPath = null, |
| 294 | /// Win32 module definition file. |
| 295 | win32_module_definition: ?LazyPath = null, |
| 296 | /// (Darwin) Path to entitlements file |
| 297 | entitlements: ?LazyPath = null, |
| 298 | }; |
| 299 | |
| 300 | pub const Kind = Configuration.Step.Compile.Kind; |
| 301 | |
| 302 | pub const HeaderInstallation = union(enum) { |
| 303 | file: File, |
| 304 | directory: Directory, |
| 305 | |
| 306 | pub const File = struct { |
| 307 | source: LazyPath, |
| 308 | dest_rel_path: []const u8, |
| 309 | |
| 310 | pub fn dupe(file: File, graph: *const std.Build.Graph) File { |
| 311 | return .{ |
| 312 | .source = file.source.dupe(graph), |
| 313 | .dest_rel_path = graph.dupePath(file.dest_rel_path), |
| 314 | }; |
| 315 | } |
| 316 | }; |
| 317 | |
| 318 | pub const Directory = struct { |
| 319 | source: LazyPath, |
| 320 | dest_rel_path: []const u8, |
| 321 | options: Directory.Options, |
| 322 | |
| 323 | pub const Options = struct { |
| 324 | /// File paths that end in any of these suffixes will be excluded from installation. |
| 325 | exclude_extensions: []const []const u8 = &.{}, |
| 326 | /// Only file paths that end in any of these suffixes will be included in installation. |
| 327 | /// `null` means that all suffixes will be included. |
| 328 | /// `exclude_extensions` takes precedence over `include_extensions`. |
| 329 | include_extensions: ?[]const []const u8 = &.{".h"}, |
| 330 | |
| 331 | pub fn dupe(opts: Directory.Options, graph: *const std.Build.Graph) Directory.Options { |
| 332 | return .{ |
| 333 | .exclude_extensions = graph.dupeStrings(opts.exclude_extensions), |
| 334 | .include_extensions = if (opts.include_extensions) |incs| graph.dupeStrings(incs) else null, |
| 335 | }; |
| 336 | } |
| 337 | }; |
| 338 | |
| 339 | pub fn dupe(dir: Directory, graph: *const std.Build.Graph) Directory { |
| 340 | return .{ |
| 341 | .source = dir.source.dupe(graph), |
| 342 | .dest_rel_path = graph.dupePath(dir.dest_rel_path), |
| 343 | .options = dir.options.dupe(graph), |
| 344 | }; |
| 345 | } |
| 346 | }; |
| 347 | |
| 348 | pub fn getSource(installation: HeaderInstallation) LazyPath { |
| 349 | return switch (installation) { |
| 350 | inline .file, .directory => |x| x.source, |
| 351 | }; |
| 352 | } |
| 353 | |
| 354 | pub fn dupe(installation: HeaderInstallation, graph: *const std.Build.Graph) HeaderInstallation { |
| 355 | return switch (installation) { |
| 356 | .file => |f| .{ .file = f.dupe(graph) }, |
| 357 | .directory => |d| .{ .directory = d.dupe(graph) }, |
| 358 | }; |
| 359 | } |
| 360 | }; |
| 361 | |
| 362 | pub const TestRunner = struct { |
| 363 | path: LazyPath, |
| 364 | /// Test runners can either be "simple", running tests when spawned and terminating when the |
| 365 | /// tests are complete, or they can use `std.zig.Server` over stdio to interact more closely |
| 366 | /// with the build system. |
| 367 | mode: enum { simple, server }, |
| 368 | }; |
| 369 | |
| 370 | pub fn create(owner: *std.Build, options: Options) *Compile { |
| 371 | const graph = owner.graph; |
| 372 | const arena = graph.arena; |
| 373 | |
| 374 | const name = owner.graph.dupeString(options.name); |
| 375 | if (mem.find(u8, name, "/") != null or mem.find(u8, name, "\\") != null) { |
| 376 | panic("invalid name: '{s}'. It looks like a file path, but it is supposed to be the library or application name.", .{name}); |
| 377 | } |
| 378 | |
| 379 | const resolved_target = options.root_module.resolved_target orelse |
| 380 | @panic("the root Module of a Compile step must be created with a known 'target' field"); |
| 381 | const target = &resolved_target.result; |
| 382 | |
| 383 | const step_name = owner.fmt("compile {s} {s} {s}", .{ |
| 384 | // Avoid the common case of the step name looking like "compile test test". |
| 385 | if (options.kind.isTest() and mem.eql(u8, name, "test")) |
| 386 | @tagName(options.kind) |
| 387 | else |
| 388 | owner.fmt("{t} {s}", .{ options.kind, name }), |
| 389 | @tagName(options.root_module.optimize orelse .debug), |
| 390 | resolved_target.query.zigTriple(arena) catch @panic("OOM"), |
| 391 | }); |
| 392 | |
| 393 | const out_filename = std.zig.binNameAlloc(arena, .{ |
| 394 | .root_name = name, |
| 395 | .cpu_arch = target.cpu.arch, |
| 396 | .os_tag = target.os.tag, |
| 397 | .ofmt = target.ofmt, |
| 398 | .abi = target.abi, |
| 399 | .output_mode = switch (options.kind) { |
| 400 | .lib => .Lib, |
| 401 | .obj, .test_obj => .Obj, |
| 402 | .exe, .@"test" => .Exe, |
| 403 | }, |
| 404 | .link_mode = options.linkage, |
| 405 | .version = options.version, |
| 406 | }) catch @panic("OOM"); |
| 407 | |
| 408 | const compile = arena.create(Compile) catch @panic("OOM"); |
| 409 | compile.* = .{ |
| 410 | .root_module = options.root_module, |
| 411 | .verbose_link = false, |
| 412 | .verbose_cc = false, |
| 413 | .linkage = options.linkage, |
| 414 | .kind = options.kind, |
| 415 | .name = name, |
| 416 | .step = .init(.{ |
| 417 | .tag = base_tag, |
| 418 | .name = step_name, |
| 419 | .owner = owner, |
| 420 | .max_rss = options.max_rss, |
| 421 | }), |
| 422 | .version = options.version, |
| 423 | .out_filename = out_filename, |
| 424 | .installed_headers = .empty, |
| 425 | .zig_lib_dir = null, |
| 426 | .filters = options.filters, |
| 427 | .test_runner = null, // set below |
| 428 | .rdynamic = false, |
| 429 | .force_undefined_symbols = .empty, |
| 430 | |
| 431 | .use_llvm = options.use_llvm, |
| 432 | .use_lld = options.use_lld, |
| 433 | .use_new_linker = null, |
| 434 | }; |
| 435 | |
| 436 | if (options.zig_lib_dir) |lp| { |
| 437 | compile.zig_lib_dir = lp.dupe(graph); |
| 438 | lp.addStepDependencies(&compile.step); |
| 439 | } |
| 440 | |
| 441 | if (options.test_runner) |runner| { |
| 442 | compile.test_runner = .{ |
| 443 | .path = runner.path.dupe(graph), |
| 444 | .mode = runner.mode, |
| 445 | }; |
| 446 | runner.path.addStepDependencies(&compile.step); |
| 447 | } |
| 448 | |
| 449 | // Only the PE/COFF format has a Resource Table which is where the manifest |
| 450 | // gets embedded, so for any other target the manifest file is just ignored. |
| 451 | if (target.ofmt == .coff) { |
| 452 | if (options.win32_manifest) |lp| { |
| 453 | compile.win32_manifest = lp.dupe(graph); |
| 454 | lp.addStepDependencies(&compile.step); |
| 455 | } |
| 456 | if (compile.kind == .lib and compile.linkage != null and compile.linkage.? == .dynamic) { |
| 457 | // Building a Win32 DLL, check for win32 .def file. |
| 458 | if (options.win32_module_definition) |lp| { |
| 459 | compile.win32_module_definition = lp.dupe(graph); |
| 460 | lp.addStepDependencies(&compile.step); |
| 461 | } |
| 462 | } |
| 463 | } |
| 464 | |
| 465 | if (options.entitlements) |lp| { |
| 466 | compile.entitlements = lp.dupe(graph); |
| 467 | lp.addStepDependencies(&compile.step); |
| 468 | } |
| 469 | |
| 470 | return compile; |
| 471 | } |
| 472 | |
| 473 | /// Marks the specified header for installation alongside this artifact. |
| 474 | /// When a module links with this artifact, all headers marked for installation are added to that |
| 475 | /// module's include search path. |
| 476 | pub fn installHeader(cs: *Compile, source: LazyPath, dest_rel_path: []const u8) void { |
| 477 | const graph = cs.step.owner.graph; |
| 478 | const arena = graph.arena; |
| 479 | const installation: HeaderInstallation = .{ .file = .{ |
| 480 | .source = source.dupe(graph), |
| 481 | .dest_rel_path = graph.dupePath(dest_rel_path), |
| 482 | } }; |
| 483 | cs.installed_headers.append(arena, installation) catch @panic("OOM"); |
| 484 | cs.addHeaderInstallationToIncludeTree(installation); |
| 485 | installation.getSource().addStepDependencies(&cs.step); |
| 486 | } |
| 487 | |
| 488 | /// Marks headers from the specified directory for installation alongside this artifact. |
| 489 | /// When a module links with this artifact, all headers marked for installation are added to that |
| 490 | /// module's include search path. |
| 491 | pub fn installHeadersDirectory( |
| 492 | cs: *Compile, |
| 493 | source: LazyPath, |
| 494 | dest_rel_path: []const u8, |
| 495 | options: HeaderInstallation.Directory.Options, |
| 496 | ) void { |
| 497 | const graph = cs.step.owner.graph; |
| 498 | const arena = graph.arena; |
| 499 | const installation: HeaderInstallation = .{ .directory = .{ |
| 500 | .source = source.dupe(graph), |
| 501 | .dest_rel_path = graph.dupePath(dest_rel_path), |
| 502 | .options = options.dupe(graph), |
| 503 | } }; |
| 504 | cs.installed_headers.append(arena, installation) catch @panic("OOM"); |
| 505 | cs.addHeaderInstallationToIncludeTree(installation); |
| 506 | installation.getSource().addStepDependencies(&cs.step); |
| 507 | } |
| 508 | |
| 509 | /// Marks the specified config header for installation alongside this artifact. |
| 510 | /// When a module links with this artifact, all headers marked for installation are added to that |
| 511 | /// module's include search path. |
| 512 | pub fn installConfigHeader(cs: *Compile, config_header: *Step.ConfigHeader) void { |
| 513 | cs.installHeader(config_header.getOutputFile(), config_header.include_path); |
| 514 | } |
| 515 | |
| 516 | /// Forwards all headers marked for installation from `lib` to this artifact. |
| 517 | /// When a module links with this artifact, all headers marked for installation are added to that |
| 518 | /// module's include search path. |
| 519 | pub fn installLibraryHeaders(cs: *Compile, lib: *Compile) void { |
| 520 | assert(lib.kind == .lib); |
| 521 | const graph = cs.step.owner.graph; |
| 522 | const arena = graph.arena; |
| 523 | for (lib.installed_headers.items) |installation| { |
| 524 | const installation_copy = installation.dupe(graph); |
| 525 | cs.installed_headers.append(arena, installation_copy) catch @panic("OOM"); |
| 526 | cs.addHeaderInstallationToIncludeTree(installation_copy); |
| 527 | installation_copy.getSource().addStepDependencies(&cs.step); |
| 528 | } |
| 529 | } |
| 530 | |
| 531 | fn addHeaderInstallationToIncludeTree(cs: *Compile, installation: HeaderInstallation) void { |
| 532 | if (cs.installed_headers_include_tree) |wf| switch (installation) { |
| 533 | .file => |file| { |
| 534 | _ = wf.addCopyFile(file.source, file.dest_rel_path); |
| 535 | }, |
| 536 | .directory => |dir| { |
| 537 | _ = wf.addCopyDirectory(dir.source, dir.dest_rel_path, .{ |
| 538 | .exclude_extensions = dir.options.exclude_extensions, |
| 539 | .include_extensions = dir.options.include_extensions, |
| 540 | }); |
| 541 | }, |
| 542 | }; |
| 543 | } |
| 544 | |
| 545 | pub fn getEmittedIncludeTree(cs: *Compile) LazyPath { |
| 546 | if (cs.installed_headers_include_tree) |wf| return wf.getDirectory(); |
| 547 | const b = cs.step.owner; |
| 548 | const wf = b.addWriteFiles(); |
| 549 | cs.installed_headers_include_tree = wf; |
| 550 | for (cs.installed_headers.items) |installation| { |
| 551 | cs.addHeaderInstallationToIncludeTree(installation); |
| 552 | } |
| 553 | // The compile step itself does not need to depend on the write files step, |
| 554 | // only dependent modules do. |
| 555 | return wf.getDirectory(); |
| 556 | } |
| 557 | |
| 558 | pub fn addObjCopy(cs: *Compile, options: Step.ObjCopy.Options) *Step.ObjCopy { |
| 559 | const b = cs.step.owner; |
| 560 | var copy = options; |
| 561 | if (copy.basename == null) { |
| 562 | if (options.format) |f| { |
| 563 | copy.basename = b.fmt("{s}.{s}", .{ cs.name, @tagName(f) }); |
| 564 | } else { |
| 565 | copy.basename = cs.name; |
| 566 | } |
| 567 | } |
| 568 | return b.addObjCopy(cs.getEmittedBin(), copy); |
| 569 | } |
| 570 | |
| 571 | pub fn setLinkerScript(compile: *Compile, source: LazyPath) void { |
| 572 | const graph = compile.step.owner.graph; |
| 573 | compile.linker_script = source.dupe(graph); |
| 574 | source.addStepDependencies(&compile.step); |
| 575 | } |
| 576 | |
| 577 | pub fn setVersionScript(compile: *Compile, source: LazyPath) void { |
| 578 | const graph = compile.step.owner.graph; |
| 579 | compile.version_script = source.dupe(graph); |
| 580 | source.addStepDependencies(&compile.step); |
| 581 | } |
| 582 | |
| 583 | pub fn forceUndefinedSymbol(compile: *Compile, symbol_name: []const u8) void { |
| 584 | const graph = compile.step.owner.graph; |
| 585 | const arena = graph.arena; |
| 586 | compile.force_undefined_symbols.put(arena, graph.dupeString(symbol_name), {}) catch @panic("OOM"); |
| 587 | } |
| 588 | |
| 589 | /// Returns whether the library, executable, or object depends on a particular system library. |
| 590 | /// Includes transitive dependencies. |
| 591 | pub fn dependsOnSystemLibrary(compile: *Compile, name: []const u8) bool { |
| 592 | var is_linking_libc = false; |
| 593 | var is_linking_libcpp = false; |
| 594 | |
| 595 | for (compile.getCompileDependencies(true)) |some_compile| { |
| 596 | for (some_compile.root_module.getGraph().modules) |mod| { |
| 597 | for (mod.link_objects.items) |lo| { |
| 598 | switch (lo) { |
| 599 | .system_lib => |lib| if (mem.eql(u8, lib.name, name)) return true, |
| 600 | else => {}, |
| 601 | } |
| 602 | } |
| 603 | if (mod.link_libc orelse false) is_linking_libc = true; |
| 604 | if (mod.link_libcpp orelse false) is_linking_libcpp = true; |
| 605 | } |
| 606 | } |
| 607 | |
| 608 | const target = compile.rootModuleTarget(); |
| 609 | |
| 610 | if (std.zig.target.isLibCLibName(&target, name)) { |
| 611 | return is_linking_libc; |
| 612 | } |
| 613 | |
| 614 | if (std.zig.target.isLibCxxLibName(&target, name)) { |
| 615 | return is_linking_libcpp; |
| 616 | } |
| 617 | |
| 618 | return false; |
| 619 | } |
| 620 | |
| 621 | pub fn isDynamicLibrary(compile: *const Compile) bool { |
| 622 | return compile.kind == .lib and compile.linkage == .dynamic; |
| 623 | } |
| 624 | |
| 625 | pub fn isStaticLibrary(compile: *const Compile) bool { |
| 626 | return compile.kind == .lib and compile.linkage != .dynamic; |
| 627 | } |
| 628 | |
| 629 | pub fn isDll(compile: *Compile) bool { |
| 630 | return compile.isDynamicLibrary() and compile.rootModuleTarget().os.tag == .windows; |
| 631 | } |
| 632 | |
| 633 | pub fn producesPdbFile(compile: *Compile) bool { |
| 634 | const target = compile.rootModuleTarget(); |
| 635 | // TODO: Is this right? Isn't PDB for *any* PE/COFF file? |
| 636 | // TODO: just share this logic with the compiler, silly! |
| 637 | switch (target.os.tag) { |
| 638 | .windows, .uefi => {}, |
| 639 | else => return false, |
| 640 | } |
| 641 | if (target.ofmt == .c) return false; |
| 642 | if (compile.use_llvm == false) return false; |
| 643 | if (compile.root_module.strip == true or |
| 644 | (compile.root_module.strip == null and compile.root_module.optimize == .small)) |
| 645 | { |
| 646 | return false; |
| 647 | } |
| 648 | return compile.isDynamicLibrary() or compile.kind == .exe or compile.kind == .@"test"; |
| 649 | } |
| 650 | |
| 651 | pub fn producesCompilerRtDynLib(compile: *Compile) bool { |
| 652 | if (compile.rootModuleTarget().ofmt != .coff) return false; |
| 653 | if (compile.bundle_compiler_rt orelse (compile.kind == .exe or compile.isDynamicLibrary())) |
| 654 | return compile.use_llvm == false; |
| 655 | return false; |
| 656 | } |
| 657 | |
| 658 | pub fn producesImplib(compile: *Compile) bool { |
| 659 | return compile.isDll(); |
| 660 | } |
| 661 | |
| 662 | pub fn setVerboseLink(compile: *Compile, value: bool) void { |
| 663 | compile.verbose_link = value; |
| 664 | } |
| 665 | |
| 666 | pub fn setVerboseCC(compile: *Compile, value: bool) void { |
| 667 | compile.verbose_cc = value; |
| 668 | } |
| 669 | |
| 670 | pub fn setLibCFile(compile: *Compile, libc_file: ?LazyPath) void { |
| 671 | const graph = compile.step.owner.graph; |
| 672 | if (libc_file) |f| { |
| 673 | compile.libc_file = f.dupe(graph); |
| 674 | f.addStepDependencies(&compile.step); |
| 675 | } else { |
| 676 | compile.libc_file = null; |
| 677 | } |
| 678 | } |
| 679 | |
| 680 | fn getEmittedFileGeneric(compile: *Compile, output_file: *Configuration.OptionalGeneratedFileIndex) LazyPath { |
| 681 | if (output_file.unwrap()) |index| return .{ .generated = .{ .index = index } }; |
| 682 | const graph = compile.step.owner.graph; |
| 683 | const index = graph.addGeneratedFile(&compile.step); |
| 684 | output_file.* = .init(index); |
| 685 | return .{ .generated = .{ .index = index } }; |
| 686 | } |
| 687 | |
| 688 | /// Returns the path to the directory that contains the emitted binary file. |
| 689 | pub fn getEmittedBinDirectory(compile: *Compile) LazyPath { |
| 690 | _ = compile.getEmittedBin(); |
| 691 | return compile.getEmittedFileGeneric(&compile.emit_directory); |
| 692 | } |
| 693 | |
| 694 | /// Returns the path to the generated executable, library or object file. |
| 695 | /// To run an executable built with zig build, use `run`, or create an install step and invoke it. |
| 696 | pub fn getEmittedBin(compile: *Compile) LazyPath { |
| 697 | return compile.getEmittedFileGeneric(&compile.generated_bin); |
| 698 | } |
| 699 | |
| 700 | /// Returns the path to the generated import library. |
| 701 | /// This function can only be called for libraries. |
| 702 | pub fn getEmittedImplib(compile: *Compile) LazyPath { |
| 703 | assert(compile.kind == .lib); |
| 704 | return compile.getEmittedFileGeneric(&compile.generated_implib); |
| 705 | } |
| 706 | |
| 707 | /// Returns the path to the generated header file. |
| 708 | /// This function can only be called for libraries or objects. |
| 709 | pub fn getEmittedH(compile: *Compile) LazyPath { |
| 710 | assert(compile.kind != .exe and compile.kind != .@"test"); |
| 711 | return compile.getEmittedFileGeneric(&compile.generated_h); |
| 712 | } |
| 713 | |
| 714 | /// Returns the generated PDB file. |
| 715 | /// If the compilation does not produce a PDB file, this causes a FileNotFound error |
| 716 | /// at build time. |
| 717 | pub fn getEmittedPdb(compile: *Compile) LazyPath { |
| 718 | _ = compile.getEmittedBin(); |
| 719 | return compile.getEmittedFileGeneric(&compile.generated_pdb); |
| 720 | } |
| 721 | |
| 722 | /// Returns the path to the generated documentation directory. |
| 723 | pub fn getEmittedDocs(compile: *Compile) LazyPath { |
| 724 | return compile.getEmittedFileGeneric(&compile.generated_docs); |
| 725 | } |
| 726 | |
| 727 | /// Returns the path to the generated assembly code. |
| 728 | pub fn getEmittedAsm(compile: *Compile) LazyPath { |
| 729 | return compile.getEmittedFileGeneric(&compile.generated_asm); |
| 730 | } |
| 731 | |
| 732 | /// Returns the path to the generated LLVM IR. |
| 733 | pub fn getEmittedLlvmIr(compile: *Compile) LazyPath { |
| 734 | return compile.getEmittedFileGeneric(&compile.generated_llvm_ir); |
| 735 | } |
| 736 | |
| 737 | /// Returns the path to the generated LLVM BC. |
| 738 | pub fn getEmittedLlvmBc(compile: *Compile) LazyPath { |
| 739 | return compile.getEmittedFileGeneric(&compile.generated_llvm_bc); |
| 740 | } |
| 741 | |
| 742 | pub fn rootModuleTarget(c: *Compile) std.Target { |
| 743 | // The root module is always given a target, so we know this to be non-null. |
| 744 | return c.root_module.resolved_target.?.result; |
| 745 | } |
| 746 | |
| 747 | /// Return the full set of `Step.Compile` which `start` depends on, recursively. `start` itself is |
| 748 | /// always returned as the first element. If `chase_dynamic` is `false`, then dynamic libraries are |
| 749 | /// not included, and their dependencies are not considered; if `chase_dynamic` is `true`, dynamic |
| 750 | /// libraries are treated the same as other linked `Compile`s. |
| 751 | pub fn getCompileDependencies(start: *Compile, chase_dynamic: bool) []const *Compile { |
| 752 | const arena = start.step.owner.graph.arena; |
| 753 | |
| 754 | var compiles: std.array_hash_map.Auto(*Compile, void) = .empty; |
| 755 | var next_idx: usize = 0; |
| 756 | |
| 757 | compiles.putNoClobber(arena, start, {}) catch @panic("OOM"); |
| 758 | |
| 759 | while (next_idx < compiles.count()) { |
| 760 | const compile = compiles.keys()[next_idx]; |
| 761 | next_idx += 1; |
| 762 | |
| 763 | for (compile.root_module.getGraph().modules) |mod| { |
| 764 | for (mod.link_objects.items) |lo| { |
| 765 | switch (lo) { |
| 766 | .other_step => |other_compile| { |
| 767 | if (!chase_dynamic and other_compile.isDynamicLibrary()) continue; |
| 768 | compiles.put(arena, other_compile, {}) catch @panic("OOM"); |
| 769 | }, |
| 770 | else => {}, |
| 771 | } |
| 772 | } |
| 773 | } |
| 774 | } |
| 775 | |
| 776 | return compiles.keys(); |
| 777 | } |