blob 1bdd97b2 (118131B) - Raw
1 // SPDX-License-Identifier: MIT 2 // Copyright (c) 2015-2021 Zig Contributors 3 // This file is part of [zig](https://ziglang.org/), which is MIT licensed. 4 // The MIT license requires this copyright notice to be included in all copies 5 // and substantial portions of the software. 6 const std = @import("std.zig"); 7 const builtin = std.builtin; 8 const io = std.io; 9 const fs = std.fs; 10 const mem = std.mem; 11 const debug = std.debug; 12 const panic = std.debug.panic; 13 const assert = debug.assert; 14 const warn = std.debug.warn; 15 const ArrayList = std.ArrayList; 16 const StringHashMap = std.StringHashMap; 17 const Allocator = mem.Allocator; 18 const process = std.process; 19 const BufSet = std.BufSet; 20 const BufMap = std.BufMap; 21 const fmt_lib = std.fmt; 22 const File = std.fs.File; 23 const CrossTarget = std.zig.CrossTarget; 24 25 pub const FmtStep = @import("build/fmt.zig").FmtStep; 26 pub const TranslateCStep = @import("build/translate_c.zig").TranslateCStep; 27 pub const WriteFileStep = @import("build/write_file.zig").WriteFileStep; 28 pub const RunStep = @import("build/run.zig").RunStep; 29 pub const CheckFileStep = @import("build/check_file.zig").CheckFileStep; 30 pub const InstallRawStep = @import("build/emit_raw.zig").InstallRawStep; 31 32 pub const Builder = struct { 33 install_tls: TopLevelStep, 34 uninstall_tls: TopLevelStep, 35 allocator: *Allocator, 36 user_input_options: UserInputOptionsMap, 37 available_options_map: AvailableOptionsMap, 38 available_options_list: ArrayList(AvailableOption), 39 verbose: bool, 40 verbose_tokenize: bool, 41 verbose_ast: bool, 42 verbose_link: bool, 43 verbose_cc: bool, 44 verbose_ir: bool, 45 verbose_llvm_ir: bool, 46 verbose_cimport: bool, 47 verbose_llvm_cpu_features: bool, 48 color: enum { auto, on, off } = .auto, 49 invalid_user_input: bool, 50 zig_exe: []const u8, 51 default_step: *Step, 52 env_map: *BufMap, 53 top_level_steps: ArrayList(*TopLevelStep), 54 install_prefix: []const u8, 55 dest_dir: ?[]const u8, 56 lib_dir: []const u8, 57 exe_dir: []const u8, 58 h_dir: []const u8, 59 install_path: []const u8, 60 search_prefixes: ArrayList([]const u8), 61 installed_files: ArrayList(InstalledFile), 62 build_root: []const u8, 63 cache_root: []const u8, 64 global_cache_root: []const u8, 65 release_mode: ?builtin.Mode, 66 is_release: bool, 67 override_lib_dir: ?[]const u8, 68 vcpkg_root: VcpkgRoot, 69 pkg_config_pkg_list: ?(PkgConfigError![]const PkgConfigPkg) = null, 70 args: ?[][]const u8 = null, 71 72 const PkgConfigError = error{ 73 PkgConfigCrashed, 74 PkgConfigFailed, 75 PkgConfigNotInstalled, 76 PkgConfigInvalidOutput, 77 }; 78 79 pub const PkgConfigPkg = struct { 80 name: []const u8, 81 desc: []const u8, 82 }; 83 84 pub const CStd = enum { 85 C89, 86 C99, 87 C11, 88 }; 89 90 const UserInputOptionsMap = StringHashMap(UserInputOption); 91 const AvailableOptionsMap = StringHashMap(AvailableOption); 92 93 const AvailableOption = struct { 94 name: []const u8, 95 type_id: TypeId, 96 description: []const u8, 97 }; 98 99 const UserInputOption = struct { 100 name: []const u8, 101 value: UserValue, 102 used: bool, 103 }; 104 105 const UserValue = union(enum) { 106 Flag: void, 107 Scalar: []const u8, 108 List: ArrayList([]const u8), 109 }; 110 111 const TypeId = enum { 112 Bool, 113 Int, 114 Float, 115 Enum, 116 String, 117 List, 118 }; 119 120 const TopLevelStep = struct { 121 step: Step, 122 description: []const u8, 123 }; 124 125 pub fn create( 126 allocator: *Allocator, 127 zig_exe: []const u8, 128 build_root: []const u8, 129 cache_root: []const u8, 130 global_cache_root: []const u8, 131 ) !*Builder { 132 const env_map = try allocator.create(BufMap); 133 env_map.* = try process.getEnvMap(allocator); 134 135 const self = try allocator.create(Builder); 136 self.* = Builder{ 137 .zig_exe = zig_exe, 138 .build_root = build_root, 139 .cache_root = try fs.path.relative(allocator, build_root, cache_root), 140 .global_cache_root = global_cache_root, 141 .verbose = false, 142 .verbose_tokenize = false, 143 .verbose_ast = false, 144 .verbose_link = false, 145 .verbose_cc = false, 146 .verbose_ir = false, 147 .verbose_llvm_ir = false, 148 .verbose_cimport = false, 149 .verbose_llvm_cpu_features = false, 150 .invalid_user_input = false, 151 .allocator = allocator, 152 .user_input_options = UserInputOptionsMap.init(allocator), 153 .available_options_map = AvailableOptionsMap.init(allocator), 154 .available_options_list = ArrayList(AvailableOption).init(allocator), 155 .top_level_steps = ArrayList(*TopLevelStep).init(allocator), 156 .default_step = undefined, 157 .env_map = env_map, 158 .search_prefixes = ArrayList([]const u8).init(allocator), 159 .install_prefix = undefined, 160 .lib_dir = undefined, 161 .exe_dir = undefined, 162 .h_dir = undefined, 163 .dest_dir = env_map.get("DESTDIR"), 164 .installed_files = ArrayList(InstalledFile).init(allocator), 165 .install_tls = TopLevelStep{ 166 .step = Step.initNoOp(.TopLevel, "install", allocator), 167 .description = "Copy build artifacts to prefix path", 168 }, 169 .uninstall_tls = TopLevelStep{ 170 .step = Step.init(.TopLevel, "uninstall", allocator, makeUninstall), 171 .description = "Remove build artifacts from prefix path", 172 }, 173 .release_mode = null, 174 .is_release = false, 175 .override_lib_dir = null, 176 .install_path = undefined, 177 .vcpkg_root = VcpkgRoot{ .Unattempted = {} }, 178 .args = null, 179 }; 180 try self.top_level_steps.append(&self.install_tls); 181 try self.top_level_steps.append(&self.uninstall_tls); 182 self.default_step = &self.install_tls.step; 183 return self; 184 } 185 186 pub fn destroy(self: *Builder) void { 187 self.env_map.deinit(); 188 self.top_level_steps.deinit(); 189 self.allocator.destroy(self); 190 } 191 192 /// This function is intended to be called by std/special/build_runner.zig, not a build.zig file. 193 pub fn resolveInstallPrefix(self: *Builder, install_prefix: ?[]const u8) void { 194 if (self.dest_dir) |dest_dir| { 195 self.install_prefix = install_prefix orelse "/usr"; 196 self.install_path = fs.path.join(self.allocator, &[_][]const u8{ dest_dir, self.install_prefix }) catch unreachable; 197 } else { 198 self.install_prefix = install_prefix orelse 199 (fs.path.join(self.allocator, &[_][]const u8{ self.build_root, "zig-out" }) catch unreachable); 200 self.install_path = self.install_prefix; 201 } 202 self.lib_dir = fs.path.join(self.allocator, &[_][]const u8{ self.install_path, "lib" }) catch unreachable; 203 self.exe_dir = fs.path.join(self.allocator, &[_][]const u8{ self.install_path, "bin" }) catch unreachable; 204 self.h_dir = fs.path.join(self.allocator, &[_][]const u8{ self.install_path, "include" }) catch unreachable; 205 } 206 207 pub fn addExecutable(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { 208 return LibExeObjStep.createExecutable( 209 self, 210 name, 211 if (root_src) |p| FileSource{ .path = p } else null, 212 false, 213 ); 214 } 215 216 pub fn addExecutableFromWriteFileStep( 217 self: *Builder, 218 name: []const u8, 219 wfs: *WriteFileStep, 220 basename: []const u8, 221 ) *LibExeObjStep { 222 return LibExeObjStep.createExecutable(self, name, @as(FileSource, .{ 223 .write_file = .{ 224 .step = wfs, 225 .basename = basename, 226 }, 227 }), false); 228 } 229 230 pub fn addExecutableSource( 231 self: *Builder, 232 name: []const u8, 233 root_src: ?FileSource, 234 ) *LibExeObjStep { 235 return LibExeObjStep.createExecutable(self, name, root_src, false); 236 } 237 238 pub fn addObject(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { 239 const root_src_param = if (root_src) |p| @as(FileSource, .{ .path = p }) else null; 240 return LibExeObjStep.createObject(self, name, root_src_param); 241 } 242 243 pub fn addObjectFromWriteFileStep( 244 self: *Builder, 245 name: []const u8, 246 wfs: *WriteFileStep, 247 basename: []const u8, 248 ) *LibExeObjStep { 249 return LibExeObjStep.createObject(self, name, @as(FileSource, .{ 250 .write_file = .{ 251 .step = wfs, 252 .basename = basename, 253 }, 254 })); 255 } 256 257 pub fn addSharedLibrary( 258 self: *Builder, 259 name: []const u8, 260 root_src: ?[]const u8, 261 kind: LibExeObjStep.SharedLibKind, 262 ) *LibExeObjStep { 263 const root_src_param = if (root_src) |p| @as(FileSource, .{ .path = p }) else null; 264 return LibExeObjStep.createSharedLibrary(self, name, root_src_param, kind); 265 } 266 267 pub fn addSharedLibraryFromWriteFileStep( 268 self: *Builder, 269 name: []const u8, 270 wfs: *WriteFileStep, 271 basename: []const u8, 272 kind: LibExeObjStep.SharedLibKind, 273 ) *LibExeObjStep { 274 return LibExeObjStep.createSharedLibrary(self, name, @as(FileSource, .{ 275 .write_file = .{ 276 .step = wfs, 277 .basename = basename, 278 }, 279 }), kind); 280 } 281 282 pub fn addStaticLibrary(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { 283 const root_src_param = if (root_src) |p| @as(FileSource, .{ .path = p }) else null; 284 return LibExeObjStep.createStaticLibrary(self, name, root_src_param); 285 } 286 287 pub fn addStaticLibraryFromWriteFileStep( 288 self: *Builder, 289 name: []const u8, 290 wfs: *WriteFileStep, 291 basename: []const u8, 292 ) *LibExeObjStep { 293 return LibExeObjStep.createStaticLibrary(self, name, @as(FileSource, .{ 294 .write_file = .{ 295 .step = wfs, 296 .basename = basename, 297 }, 298 })); 299 } 300 301 pub fn addTest(self: *Builder, root_src: []const u8) *LibExeObjStep { 302 return LibExeObjStep.createTest(self, "test", .{ .path = root_src }); 303 } 304 305 pub fn addTestFromWriteFileStep( 306 self: *Builder, 307 wfs: *WriteFileStep, 308 basename: []const u8, 309 ) *LibExeObjStep { 310 return LibExeObjStep.createTest(self, "test", @as(FileSource, .{ 311 .write_file = .{ 312 .step = wfs, 313 .basename = basename, 314 }, 315 })); 316 } 317 318 pub fn addAssemble(self: *Builder, name: []const u8, src: []const u8) *LibExeObjStep { 319 const obj_step = LibExeObjStep.createObject(self, name, null); 320 obj_step.addAssemblyFile(src); 321 return obj_step; 322 } 323 324 /// Initializes a RunStep with argv, which must at least have the path to the 325 /// executable. More command line arguments can be added with `addArg`, 326 /// `addArgs`, and `addArtifactArg`. 327 /// Be careful using this function, as it introduces a system dependency. 328 /// To run an executable built with zig build, see `LibExeObjStep.run`. 329 pub fn addSystemCommand(self: *Builder, argv: []const []const u8) *RunStep { 330 assert(argv.len >= 1); 331 const run_step = RunStep.create(self, self.fmt("run {s}", .{argv[0]})); 332 run_step.addArgs(argv); 333 return run_step; 334 } 335 336 pub fn dupe(self: *Builder, bytes: []const u8) []u8 { 337 return self.allocator.dupe(u8, bytes) catch unreachable; 338 } 339 340 pub fn dupeStrings(self: *Builder, strings: []const []const u8) [][]u8 { 341 const array = self.allocator.alloc([]u8, strings.len) catch unreachable; 342 for (strings) |s, i| { 343 array[i] = self.dupe(s); 344 } 345 return array; 346 } 347 348 pub fn dupePath(self: *Builder, bytes: []const u8) []u8 { 349 const the_copy = self.dupe(bytes); 350 for (the_copy) |*byte| { 351 switch (byte.*) { 352 '/', '\\' => byte.* = fs.path.sep, 353 else => {}, 354 } 355 } 356 return the_copy; 357 } 358 359 pub fn dupePkg(self: *Builder, package: Pkg) Pkg { 360 var the_copy = Pkg{ 361 .name = self.dupe(package.name), 362 .path = self.dupePath(package.path), 363 }; 364 365 if (package.dependencies) |dependencies| { 366 const new_dependencies = self.allocator.alloc(Pkg, dependencies.len) catch unreachable; 367 the_copy.dependencies = new_dependencies; 368 369 for (dependencies) |dep_package, i| { 370 new_dependencies[i] = self.dupePkg(dep_package); 371 } 372 } 373 return the_copy; 374 } 375 376 pub fn addWriteFile(self: *Builder, file_path: []const u8, data: []const u8) *WriteFileStep { 377 const write_file_step = self.addWriteFiles(); 378 write_file_step.add(file_path, data); 379 return write_file_step; 380 } 381 382 pub fn addWriteFiles(self: *Builder) *WriteFileStep { 383 const write_file_step = self.allocator.create(WriteFileStep) catch unreachable; 384 write_file_step.* = WriteFileStep.init(self); 385 return write_file_step; 386 } 387 388 pub fn addLog(self: *Builder, comptime format: []const u8, args: anytype) *LogStep { 389 const data = self.fmt(format, args); 390 const log_step = self.allocator.create(LogStep) catch unreachable; 391 log_step.* = LogStep.init(self, data); 392 return log_step; 393 } 394 395 pub fn addRemoveDirTree(self: *Builder, dir_path: []const u8) *RemoveDirStep { 396 const remove_dir_step = self.allocator.create(RemoveDirStep) catch unreachable; 397 remove_dir_step.* = RemoveDirStep.init(self, dir_path); 398 return remove_dir_step; 399 } 400 401 pub fn addFmt(self: *Builder, paths: []const []const u8) *FmtStep { 402 return FmtStep.create(self, paths); 403 } 404 405 pub fn addTranslateC(self: *Builder, source: FileSource) *TranslateCStep { 406 return TranslateCStep.create(self, source); 407 } 408 409 pub fn version(self: *const Builder, major: u32, minor: u32, patch: u32) LibExeObjStep.SharedLibKind { 410 return .{ 411 .versioned = .{ 412 .major = major, 413 .minor = minor, 414 .patch = patch, 415 }, 416 }; 417 } 418 419 pub fn make(self: *Builder, step_names: []const []const u8) !void { 420 try self.makePath(self.cache_root); 421 422 var wanted_steps = ArrayList(*Step).init(self.allocator); 423 defer wanted_steps.deinit(); 424 425 if (step_names.len == 0) { 426 try wanted_steps.append(self.default_step); 427 } else { 428 for (step_names) |step_name| { 429 const s = try self.getTopLevelStepByName(step_name); 430 try wanted_steps.append(s); 431 } 432 } 433 434 for (wanted_steps.items) |s| { 435 try self.makeOneStep(s); 436 } 437 } 438 439 pub fn getInstallStep(self: *Builder) *Step { 440 return &self.install_tls.step; 441 } 442 443 pub fn getUninstallStep(self: *Builder) *Step { 444 return &self.uninstall_tls.step; 445 } 446 447 fn makeUninstall(uninstall_step: *Step) anyerror!void { 448 const uninstall_tls = @fieldParentPtr(TopLevelStep, "step", uninstall_step); 449 const self = @fieldParentPtr(Builder, "uninstall_tls", uninstall_tls); 450 451 for (self.installed_files.items) |installed_file| { 452 const full_path = self.getInstallPath(installed_file.dir, installed_file.path); 453 if (self.verbose) { 454 warn("rm {s}\n", .{full_path}); 455 } 456 fs.cwd().deleteTree(full_path) catch {}; 457 } 458 459 // TODO remove empty directories 460 } 461 462 fn makeOneStep(self: *Builder, s: *Step) anyerror!void { 463 if (s.loop_flag) { 464 warn("Dependency loop detected:\n {s}\n", .{s.name}); 465 return error.DependencyLoopDetected; 466 } 467 s.loop_flag = true; 468 469 for (s.dependencies.items) |dep| { 470 self.makeOneStep(dep) catch |err| { 471 if (err == error.DependencyLoopDetected) { 472 warn(" {s}\n", .{s.name}); 473 } 474 return err; 475 }; 476 } 477 478 s.loop_flag = false; 479 480 try s.make(); 481 } 482 483 fn getTopLevelStepByName(self: *Builder, name: []const u8) !*Step { 484 for (self.top_level_steps.items) |top_level_step| { 485 if (mem.eql(u8, top_level_step.step.name, name)) { 486 return &top_level_step.step; 487 } 488 } 489 warn("Cannot run step '{s}' because it does not exist\n", .{name}); 490 return error.InvalidStepName; 491 } 492 493 pub fn option(self: *Builder, comptime T: type, name_raw: []const u8, description_raw: []const u8) ?T { 494 const name = self.dupe(name_raw); 495 const description = self.dupe(description_raw); 496 const type_id = comptime typeToEnum(T); 497 const available_option = AvailableOption{ 498 .name = name, 499 .type_id = type_id, 500 .description = description, 501 }; 502 if ((self.available_options_map.fetchPut(name, available_option) catch unreachable) != null) { 503 panic("Option '{s}' declared twice", .{name}); 504 } 505 self.available_options_list.append(available_option) catch unreachable; 506 507 const entry = self.user_input_options.getEntry(name) orelse return null; 508 entry.value.used = true; 509 switch (type_id) { 510 .Bool => switch (entry.value.value) { 511 .Flag => return true, 512 .Scalar => |s| { 513 if (mem.eql(u8, s, "true")) { 514 return true; 515 } else if (mem.eql(u8, s, "false")) { 516 return false; 517 } else { 518 warn("Expected -D{s} to be a boolean, but received '{s}'\n\n", .{ name, s }); 519 self.markInvalidUserInput(); 520 return null; 521 } 522 }, 523 .List => { 524 warn("Expected -D{s} to be a boolean, but received a list.\n\n", .{name}); 525 self.markInvalidUserInput(); 526 return null; 527 }, 528 }, 529 .Int => switch (entry.value.value) { 530 .Flag => { 531 warn("Expected -D{s} to be an integer, but received a boolean.\n\n", .{name}); 532 self.markInvalidUserInput(); 533 return null; 534 }, 535 .Scalar => |s| { 536 const n = std.fmt.parseInt(T, s, 10) catch |err| switch (err) { 537 error.Overflow => { 538 warn("-D{s} value {s} cannot fit into type {s}.\n\n", .{ name, s, @typeName(T) }); 539 self.markInvalidUserInput(); 540 return null; 541 }, 542 else => { 543 warn("Expected -D{s} to be an integer of type {s}.\n\n", .{ name, @typeName(T) }); 544 self.markInvalidUserInput(); 545 return null; 546 }, 547 }; 548 return n; 549 }, 550 .List => { 551 warn("Expected -D{s} to be an integer, but received a list.\n\n", .{name}); 552 self.markInvalidUserInput(); 553 return null; 554 }, 555 }, 556 .Float => switch (entry.value.value) { 557 .Flag => { 558 warn("Expected -D{s} to be a float, but received a boolean.\n\n", .{name}); 559 self.markInvalidUserInput(); 560 return null; 561 }, 562 .Scalar => |s| { 563 const n = std.fmt.parseFloat(T, s) catch |err| { 564 warn("Expected -D{s} to be a float of type {s}.\n\n", .{ name, @typeName(T) }); 565 self.markInvalidUserInput(); 566 return null; 567 }; 568 return n; 569 }, 570 .List => { 571 warn("Expected -D{s} to be a float, but received a list.\n\n", .{name}); 572 self.markInvalidUserInput(); 573 return null; 574 }, 575 }, 576 .Enum => switch (entry.value.value) { 577 .Flag => { 578 warn("Expected -D{s} to be a string, but received a boolean.\n\n", .{name}); 579 self.markInvalidUserInput(); 580 return null; 581 }, 582 .Scalar => |s| { 583 if (std.meta.stringToEnum(T, s)) |enum_lit| { 584 return enum_lit; 585 } else { 586 warn("Expected -D{s} to be of type {s}.\n\n", .{ name, @typeName(T) }); 587 self.markInvalidUserInput(); 588 return null; 589 } 590 }, 591 .List => { 592 warn("Expected -D{s} to be a string, but received a list.\n\n", .{name}); 593 self.markInvalidUserInput(); 594 return null; 595 }, 596 }, 597 .String => switch (entry.value.value) { 598 .Flag => { 599 warn("Expected -D{s} to be a string, but received a boolean.\n\n", .{name}); 600 self.markInvalidUserInput(); 601 return null; 602 }, 603 .List => { 604 warn("Expected -D{s} to be a string, but received a list.\n\n", .{name}); 605 self.markInvalidUserInput(); 606 return null; 607 }, 608 .Scalar => |s| return s, 609 }, 610 .List => switch (entry.value.value) { 611 .Flag => { 612 warn("Expected -D{s} to be a list, but received a boolean.\n\n", .{name}); 613 self.markInvalidUserInput(); 614 return null; 615 }, 616 .Scalar => |s| { 617 return self.allocator.dupe([]const u8, &[_][]const u8{s}) catch unreachable; 618 }, 619 .List => |lst| return lst.items, 620 }, 621 } 622 } 623 624 pub fn step(self: *Builder, name: []const u8, description: []const u8) *Step { 625 const step_info = self.allocator.create(TopLevelStep) catch unreachable; 626 step_info.* = TopLevelStep{ 627 .step = Step.initNoOp(.TopLevel, name, self.allocator), 628 .description = self.dupe(description), 629 }; 630 self.top_level_steps.append(step_info) catch unreachable; 631 return &step_info.step; 632 } 633 634 /// This provides the -Drelease option to the build user and does not give them the choice. 635 pub fn setPreferredReleaseMode(self: *Builder, mode: builtin.Mode) void { 636 if (self.release_mode != null) { 637 @panic("setPreferredReleaseMode must be called before standardReleaseOptions and may not be called twice"); 638 } 639 const description = self.fmt("Create a release build ({s})", .{@tagName(mode)}); 640 self.is_release = self.option(bool, "release", description) orelse false; 641 self.release_mode = if (self.is_release) mode else builtin.Mode.Debug; 642 } 643 644 /// If you call this without first calling `setPreferredReleaseMode` then it gives the build user 645 /// the choice of what kind of release. 646 pub fn standardReleaseOptions(self: *Builder) builtin.Mode { 647 if (self.release_mode) |mode| return mode; 648 649 const release_safe = self.option(bool, "release-safe", "Optimizations on and safety on") orelse false; 650 const release_fast = self.option(bool, "release-fast", "Optimizations on and safety off") orelse false; 651 const release_small = self.option(bool, "release-small", "Size optimizations on and safety off") orelse false; 652 653 const mode = if (release_safe and !release_fast and !release_small) 654 builtin.Mode.ReleaseSafe 655 else if (release_fast and !release_safe and !release_small) 656 builtin.Mode.ReleaseFast 657 else if (release_small and !release_fast and !release_safe) 658 builtin.Mode.ReleaseSmall 659 else if (!release_fast and !release_safe and !release_small) 660 builtin.Mode.Debug 661 else x: { 662 warn("Multiple release modes (of -Drelease-safe, -Drelease-fast and -Drelease-small)\n\n", .{}); 663 self.markInvalidUserInput(); 664 break :x builtin.Mode.Debug; 665 }; 666 self.is_release = mode != .Debug; 667 self.release_mode = mode; 668 return mode; 669 } 670 671 pub const StandardTargetOptionsArgs = struct { 672 whitelist: ?[]const CrossTarget = null, 673 674 default_target: CrossTarget = CrossTarget{}, 675 }; 676 677 /// Exposes standard `zig build` options for choosing a target. 678 pub fn standardTargetOptions(self: *Builder, args: StandardTargetOptionsArgs) CrossTarget { 679 const maybe_triple = self.option( 680 []const u8, 681 "target", 682 "The CPU architecture, OS, and ABI to build for", 683 ); 684 const mcpu = self.option([]const u8, "cpu", "Target CPU"); 685 686 const triple = maybe_triple orelse return args.default_target; 687 688 var diags: CrossTarget.ParseOptions.Diagnostics = .{}; 689 const selected_target = CrossTarget.parse(.{ 690 .arch_os_abi = triple, 691 .cpu_features = mcpu, 692 .diagnostics = &diags, 693 }) catch |err| switch (err) { 694 error.UnknownCpuModel => { 695 warn("Unknown CPU: '{s}'\nAvailable CPUs for architecture '{s}':\n", .{ 696 diags.cpu_name.?, 697 @tagName(diags.arch.?), 698 }); 699 for (diags.arch.?.allCpuModels()) |cpu| { 700 warn(" {s}\n", .{cpu.name}); 701 } 702 warn("\n", .{}); 703 self.markInvalidUserInput(); 704 return args.default_target; 705 }, 706 error.UnknownCpuFeature => { 707 warn( 708 \\Unknown CPU feature: '{s}' 709 \\Available CPU features for architecture '{s}': 710 \\ 711 , .{ 712 diags.unknown_feature_name, 713 @tagName(diags.arch.?), 714 }); 715 for (diags.arch.?.allFeaturesList()) |feature| { 716 warn(" {s}: {s}\n", .{ feature.name, feature.description }); 717 } 718 warn("\n", .{}); 719 self.markInvalidUserInput(); 720 return args.default_target; 721 }, 722 error.UnknownOperatingSystem => { 723 warn( 724 \\Unknown OS: '{s}' 725 \\Available operating systems: 726 \\ 727 , .{diags.os_name}); 728 inline for (std.meta.fields(std.Target.Os.Tag)) |field| { 729 warn(" {s}\n", .{field.name}); 730 } 731 warn("\n", .{}); 732 self.markInvalidUserInput(); 733 return args.default_target; 734 }, 735 else => |e| { 736 warn("Unable to parse target '{s}': {s}\n\n", .{ triple, @errorName(e) }); 737 self.markInvalidUserInput(); 738 return args.default_target; 739 }, 740 }; 741 742 const selected_canonicalized_triple = selected_target.zigTriple(self.allocator) catch unreachable; 743 744 if (args.whitelist) |list| whitelist_check: { 745 // Make sure it's a match of one of the list. 746 for (list) |t| { 747 const t_triple = t.zigTriple(self.allocator) catch unreachable; 748 if (mem.eql(u8, t_triple, selected_canonicalized_triple)) { 749 break :whitelist_check; 750 } 751 } 752 warn("Chosen target '{s}' does not match one of the supported targets:\n", .{ 753 selected_canonicalized_triple, 754 }); 755 for (list) |t| { 756 const t_triple = t.zigTriple(self.allocator) catch unreachable; 757 warn(" {s}\n", .{t_triple}); 758 } 759 warn("\n", .{}); 760 self.markInvalidUserInput(); 761 return args.default_target; 762 } 763 764 return selected_target; 765 } 766 767 pub fn addUserInputOption(self: *Builder, name_raw: []const u8, value_raw: []const u8) !bool { 768 const name = self.dupe(name_raw); 769 const value = self.dupe(value_raw); 770 const gop = try self.user_input_options.getOrPut(name); 771 if (!gop.found_existing) { 772 gop.entry.value = UserInputOption{ 773 .name = name, 774 .value = UserValue{ .Scalar = value }, 775 .used = false, 776 }; 777 return false; 778 } 779 780 // option already exists 781 switch (gop.entry.value.value) { 782 UserValue.Scalar => |s| { 783 // turn it into a list 784 var list = ArrayList([]const u8).init(self.allocator); 785 list.append(s) catch unreachable; 786 list.append(value) catch unreachable; 787 self.user_input_options.put(name, UserInputOption{ 788 .name = name, 789 .value = UserValue{ .List = list }, 790 .used = false, 791 }) catch unreachable; 792 }, 793 UserValue.List => |*list| { 794 // append to the list 795 list.append(value) catch unreachable; 796 self.user_input_options.put(name, UserInputOption{ 797 .name = name, 798 .value = UserValue{ .List = list.* }, 799 .used = false, 800 }) catch unreachable; 801 }, 802 UserValue.Flag => { 803 warn("Option '-D{s}={s}' conflicts with flag '-D{s}'.\n", .{ name, value, name }); 804 return true; 805 }, 806 } 807 return false; 808 } 809 810 pub fn addUserInputFlag(self: *Builder, name_raw: []const u8) !bool { 811 const name = self.dupe(name_raw); 812 const gop = try self.user_input_options.getOrPut(name); 813 if (!gop.found_existing) { 814 gop.entry.value = UserInputOption{ 815 .name = name, 816 .value = UserValue{ .Flag = {} }, 817 .used = false, 818 }; 819 return false; 820 } 821 822 // option already exists 823 switch (gop.entry.value.value) { 824 UserValue.Scalar => |s| { 825 warn("Flag '-D{s}' conflicts with option '-D{s}={s}'.\n", .{ name, name, s }); 826 return true; 827 }, 828 UserValue.List => { 829 warn("Flag '-D{s}' conflicts with multiple options of the same name.\n", .{name}); 830 return true; 831 }, 832 UserValue.Flag => {}, 833 } 834 return false; 835 } 836 837 fn typeToEnum(comptime T: type) TypeId { 838 return switch (@typeInfo(T)) { 839 .Int => .Int, 840 .Float => .Float, 841 .Bool => .Bool, 842 .Enum => .Enum, 843 else => switch (T) { 844 []const u8 => .String, 845 []const []const u8 => .List, 846 else => @compileError("Unsupported type: " ++ @typeName(T)), 847 }, 848 }; 849 } 850 851 fn markInvalidUserInput(self: *Builder) void { 852 self.invalid_user_input = true; 853 } 854 855 pub fn typeIdName(id: TypeId) []const u8 { 856 return switch (id) { 857 .Bool => "bool", 858 .Int => "int", 859 .Float => "float", 860 .Enum => "enum", 861 .String => "string", 862 .List => "list", 863 }; 864 } 865 866 pub fn validateUserInputDidItFail(self: *Builder) bool { 867 // make sure all args are used 868 var it = self.user_input_options.iterator(); 869 while (true) { 870 const entry = it.next() orelse break; 871 if (!entry.value.used) { 872 warn("Invalid option: -D{s}\n\n", .{entry.key}); 873 self.markInvalidUserInput(); 874 } 875 } 876 877 return self.invalid_user_input; 878 } 879 880 pub fn spawnChild(self: *Builder, argv: []const []const u8) !void { 881 return self.spawnChildEnvMap(null, self.env_map, argv); 882 } 883 884 fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void { 885 if (cwd) |yes_cwd| warn("cd {s} && ", .{yes_cwd}); 886 for (argv) |arg| { 887 warn("{s} ", .{arg}); 888 } 889 warn("\n", .{}); 890 } 891 892 fn spawnChildEnvMap(self: *Builder, cwd: ?[]const u8, env_map: *const BufMap, argv: []const []const u8) !void { 893 if (self.verbose) { 894 printCmd(cwd, argv); 895 } 896 897 const child = std.ChildProcess.init(argv, self.allocator) catch unreachable; 898 defer child.deinit(); 899 900 child.cwd = cwd; 901 child.env_map = env_map; 902 903 const term = child.spawnAndWait() catch |err| { 904 warn("Unable to spawn {s}: {s}\n", .{ argv[0], @errorName(err) }); 905 return err; 906 }; 907 908 switch (term) { 909 .Exited => |code| { 910 if (code != 0) { 911 warn("The following command exited with error code {}:\n", .{code}); 912 printCmd(cwd, argv); 913 return error.UncleanExit; 914 } 915 }, 916 else => { 917 warn("The following command terminated unexpectedly:\n", .{}); 918 printCmd(cwd, argv); 919 920 return error.UncleanExit; 921 }, 922 } 923 } 924 925 pub fn makePath(self: *Builder, path: []const u8) !void { 926 fs.cwd().makePath(self.pathFromRoot(path)) catch |err| { 927 warn("Unable to create path {s}: {s}\n", .{ path, @errorName(err) }); 928 return err; 929 }; 930 } 931 932 pub fn installArtifact(self: *Builder, artifact: *LibExeObjStep) void { 933 self.getInstallStep().dependOn(&self.addInstallArtifact(artifact).step); 934 } 935 936 pub fn addInstallArtifact(self: *Builder, artifact: *LibExeObjStep) *InstallArtifactStep { 937 return InstallArtifactStep.create(self, artifact); 938 } 939 940 ///`dest_rel_path` is relative to prefix path 941 pub fn installFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) void { 942 self.getInstallStep().dependOn(&self.addInstallFileWithDir(src_path, .Prefix, dest_rel_path).step); 943 } 944 945 pub fn installDirectory(self: *Builder, options: InstallDirectoryOptions) void { 946 self.getInstallStep().dependOn(&self.addInstallDirectory(options).step); 947 } 948 949 ///`dest_rel_path` is relative to bin path 950 pub fn installBinFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) void { 951 self.getInstallStep().dependOn(&self.addInstallFileWithDir(src_path, .Bin, dest_rel_path).step); 952 } 953 954 ///`dest_rel_path` is relative to lib path 955 pub fn installLibFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) void { 956 self.getInstallStep().dependOn(&self.addInstallFileWithDir(src_path, .Lib, dest_rel_path).step); 957 } 958 959 pub fn installRaw(self: *Builder, artifact: *LibExeObjStep, dest_filename: []const u8) void { 960 self.getInstallStep().dependOn(&self.addInstallRaw(artifact, dest_filename).step); 961 } 962 963 ///`dest_rel_path` is relative to install prefix path 964 pub fn addInstallFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) *InstallFileStep { 965 return self.addInstallFileWithDir(src_path, .Prefix, dest_rel_path); 966 } 967 968 ///`dest_rel_path` is relative to bin path 969 pub fn addInstallBinFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) *InstallFileStep { 970 return self.addInstallFileWithDir(src_path, .Bin, dest_rel_path); 971 } 972 973 ///`dest_rel_path` is relative to lib path 974 pub fn addInstallLibFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) *InstallFileStep { 975 return self.addInstallFileWithDir(src_path, .Lib, dest_rel_path); 976 } 977 978 pub fn addInstallRaw(self: *Builder, artifact: *LibExeObjStep, dest_filename: []const u8) *InstallRawStep { 979 return InstallRawStep.create(self, artifact, dest_filename); 980 } 981 982 pub fn addInstallFileWithDir( 983 self: *Builder, 984 src_path: []const u8, 985 install_dir: InstallDir, 986 dest_rel_path: []const u8, 987 ) *InstallFileStep { 988 if (dest_rel_path.len == 0) { 989 panic("dest_rel_path must be non-empty", .{}); 990 } 991 const install_step = self.allocator.create(InstallFileStep) catch unreachable; 992 install_step.* = InstallFileStep.init(self, src_path, install_dir, dest_rel_path); 993 return install_step; 994 } 995 996 pub fn addInstallDirectory(self: *Builder, options: InstallDirectoryOptions) *InstallDirStep { 997 const install_step = self.allocator.create(InstallDirStep) catch unreachable; 998 install_step.* = InstallDirStep.init(self, options); 999 return install_step; 1000 } 1001 1002 pub fn pushInstalledFile(self: *Builder, dir: InstallDir, dest_rel_path: []const u8) void { 1003 const file = InstalledFile{ 1004 .dir = dir, 1005 .path = dest_rel_path, 1006 }; 1007 self.installed_files.append(file.dupe(self)) catch unreachable; 1008 } 1009 1010 pub fn updateFile(self: *Builder, source_path: []const u8, dest_path: []const u8) !void { 1011 if (self.verbose) { 1012 warn("cp {s} {s} ", .{ source_path, dest_path }); 1013 } 1014 const cwd = fs.cwd(); 1015 const prev_status = try fs.Dir.updateFile(cwd, source_path, cwd, dest_path, .{}); 1016 if (self.verbose) switch (prev_status) { 1017 .stale => warn("# installed\n", .{}), 1018 .fresh => warn("# up-to-date\n", .{}), 1019 }; 1020 } 1021 1022 pub fn pathFromRoot(self: *Builder, rel_path: []const u8) []u8 { 1023 return fs.path.resolve(self.allocator, &[_][]const u8{ self.build_root, rel_path }) catch unreachable; 1024 } 1025 1026 pub fn fmt(self: *Builder, comptime format: []const u8, args: anytype) []u8 { 1027 return fmt_lib.allocPrint(self.allocator, format, args) catch unreachable; 1028 } 1029 1030 pub fn findProgram(self: *Builder, names: []const []const u8, paths: []const []const u8) ![]const u8 { 1031 // TODO report error for ambiguous situations 1032 const exe_extension = @as(CrossTarget, .{}).exeFileExt(); 1033 for (self.search_prefixes.items) |search_prefix| { 1034 for (names) |name| { 1035 if (fs.path.isAbsolute(name)) { 1036 return name; 1037 } 1038 const full_path = try fs.path.join(self.allocator, &[_][]const u8{ 1039 search_prefix, 1040 "bin", 1041 self.fmt("{s}{s}", .{ name, exe_extension }), 1042 }); 1043 return fs.realpathAlloc(self.allocator, full_path) catch continue; 1044 } 1045 } 1046 if (self.env_map.get("PATH")) |PATH| { 1047 for (names) |name| { 1048 if (fs.path.isAbsolute(name)) { 1049 return name; 1050 } 1051 var it = mem.tokenize(PATH, &[_]u8{fs.path.delimiter}); 1052 while (it.next()) |path| { 1053 const full_path = try fs.path.join(self.allocator, &[_][]const u8{ 1054 path, 1055 self.fmt("{s}{s}", .{ name, exe_extension }), 1056 }); 1057 return fs.realpathAlloc(self.allocator, full_path) catch continue; 1058 } 1059 } 1060 } 1061 for (names) |name| { 1062 if (fs.path.isAbsolute(name)) { 1063 return name; 1064 } 1065 for (paths) |path| { 1066 const full_path = try fs.path.join(self.allocator, &[_][]const u8{ 1067 path, 1068 self.fmt("{s}{s}", .{ name, exe_extension }), 1069 }); 1070 return fs.realpathAlloc(self.allocator, full_path) catch continue; 1071 } 1072 } 1073 return error.FileNotFound; 1074 } 1075 1076 pub fn execAllowFail( 1077 self: *Builder, 1078 argv: []const []const u8, 1079 out_code: *u8, 1080 stderr_behavior: std.ChildProcess.StdIo, 1081 ) ![]u8 { 1082 assert(argv.len != 0); 1083 1084 const max_output_size = 400 * 1024; 1085 const child = try std.ChildProcess.init(argv, self.allocator); 1086 defer child.deinit(); 1087 1088 child.stdin_behavior = .Ignore; 1089 child.stdout_behavior = .Pipe; 1090 child.stderr_behavior = stderr_behavior; 1091 child.env_map = self.env_map; 1092 1093 try child.spawn(); 1094 1095 const stdout = try child.stdout.?.reader().readAllAlloc(self.allocator, max_output_size); 1096 errdefer self.allocator.free(stdout); 1097 1098 const term = try child.wait(); 1099 switch (term) { 1100 .Exited => |code| { 1101 if (code != 0) { 1102 out_code.* = @truncate(u8, code); 1103 return error.ExitCodeFailure; 1104 } 1105 return stdout; 1106 }, 1107 .Signal, .Stopped, .Unknown => |code| { 1108 out_code.* = @truncate(u8, code); 1109 return error.ProcessTerminated; 1110 }, 1111 } 1112 } 1113 1114 pub fn execFromStep(self: *Builder, argv: []const []const u8, src_step: ?*Step) ![]u8 { 1115 assert(argv.len != 0); 1116 1117 if (self.verbose) { 1118 printCmd(null, argv); 1119 } 1120 1121 var code: u8 = undefined; 1122 return self.execAllowFail(argv, &code, .Inherit) catch |err| switch (err) { 1123 error.FileNotFound => { 1124 if (src_step) |s| warn("{s}...", .{s.name}); 1125 warn("Unable to spawn the following command: file not found\n", .{}); 1126 printCmd(null, argv); 1127 std.os.exit(@truncate(u8, code)); 1128 }, 1129 error.ExitCodeFailure => { 1130 if (src_step) |s| warn("{s}...", .{s.name}); 1131 warn("The following command exited with error code {d}:\n", .{code}); 1132 printCmd(null, argv); 1133 std.os.exit(@truncate(u8, code)); 1134 }, 1135 error.ProcessTerminated => { 1136 if (src_step) |s| warn("{s}...", .{s.name}); 1137 warn("The following command terminated unexpectedly:\n", .{}); 1138 printCmd(null, argv); 1139 std.os.exit(@truncate(u8, code)); 1140 }, 1141 else => |e| return e, 1142 }; 1143 } 1144 1145 pub fn exec(self: *Builder, argv: []const []const u8) ![]u8 { 1146 return self.execFromStep(argv, null); 1147 } 1148 1149 pub fn addSearchPrefix(self: *Builder, search_prefix: []const u8) void { 1150 self.search_prefixes.append(self.dupePath(search_prefix)) catch unreachable; 1151 } 1152 1153 pub fn getInstallPath(self: *Builder, dir: InstallDir, dest_rel_path: []const u8) []const u8 { 1154 assert(!fs.path.isAbsolute(dest_rel_path)); // Install paths must be relative to the prefix 1155 const base_dir = switch (dir) { 1156 .Prefix => self.install_path, 1157 .Bin => self.exe_dir, 1158 .Lib => self.lib_dir, 1159 .Header => self.h_dir, 1160 .Custom => |path| fs.path.join(self.allocator, &[_][]const u8{ self.install_path, path }) catch unreachable, 1161 }; 1162 return fs.path.resolve( 1163 self.allocator, 1164 &[_][]const u8{ base_dir, dest_rel_path }, 1165 ) catch unreachable; 1166 } 1167 1168 fn execPkgConfigList(self: *Builder, out_code: *u8) ![]const PkgConfigPkg { 1169 const stdout = try self.execAllowFail(&[_][]const u8{ "pkg-config", "--list-all" }, out_code, .Ignore); 1170 var list = ArrayList(PkgConfigPkg).init(self.allocator); 1171 errdefer list.deinit(); 1172 var line_it = mem.tokenize(stdout, "\r\n"); 1173 while (line_it.next()) |line| { 1174 if (mem.trim(u8, line, " \t").len == 0) continue; 1175 var tok_it = mem.tokenize(line, " \t"); 1176 try list.append(PkgConfigPkg{ 1177 .name = tok_it.next() orelse return error.PkgConfigInvalidOutput, 1178 .desc = tok_it.rest(), 1179 }); 1180 } 1181 return list.toOwnedSlice(); 1182 } 1183 1184 fn getPkgConfigList(self: *Builder) ![]const PkgConfigPkg { 1185 if (self.pkg_config_pkg_list) |res| { 1186 return res; 1187 } 1188 var code: u8 = undefined; 1189 if (self.execPkgConfigList(&code)) |list| { 1190 self.pkg_config_pkg_list = list; 1191 return list; 1192 } else |err| { 1193 const result = switch (err) { 1194 error.ProcessTerminated => error.PkgConfigCrashed, 1195 error.ExitCodeFailure => error.PkgConfigFailed, 1196 error.FileNotFound => error.PkgConfigNotInstalled, 1197 error.InvalidName => error.PkgConfigNotInstalled, 1198 error.PkgConfigInvalidOutput => error.PkgConfigInvalidOutput, 1199 else => return err, 1200 }; 1201 self.pkg_config_pkg_list = result; 1202 return result; 1203 } 1204 } 1205 }; 1206 1207 test "builder.findProgram compiles" { 1208 if (builtin.os.tag == .wasi) return error.SkipZigTest; 1209 1210 var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); 1211 defer arena.deinit(); 1212 1213 const builder = try Builder.create( 1214 &arena.allocator, 1215 "zig", 1216 "zig-cache", 1217 "zig-cache", 1218 "zig-cache", 1219 ); 1220 defer builder.destroy(); 1221 _ = builder.findProgram(&[_][]const u8{}, &[_][]const u8{}) catch null; 1222 } 1223 1224 /// Deprecated. Use `std.builtin.Version`. 1225 pub const Version = builtin.Version; 1226 1227 /// Deprecated. Use `std.zig.CrossTarget`. 1228 pub const Target = std.zig.CrossTarget; 1229 1230 pub const Pkg = struct { 1231 name: []const u8, 1232 path: []const u8, 1233 dependencies: ?[]const Pkg = null, 1234 }; 1235 1236 pub const CSourceFile = struct { 1237 source: FileSource, 1238 args: []const []const u8, 1239 1240 fn dupe(self: CSourceFile, b: *Builder) CSourceFile { 1241 return .{ 1242 .source = self.source.dupe(b), 1243 .args = b.dupeStrings(self.args), 1244 }; 1245 } 1246 }; 1247 1248 const CSourceFiles = struct { 1249 files: []const []const u8, 1250 flags: []const []const u8, 1251 }; 1252 1253 fn isLibCLibrary(name: []const u8) bool { 1254 const libc_libraries = [_][]const u8{ "c", "m", "dl", "rt", "pthread" }; 1255 for (libc_libraries) |libc_lib_name| { 1256 if (mem.eql(u8, name, libc_lib_name)) 1257 return true; 1258 } 1259 return false; 1260 } 1261 1262 pub const FileSource = union(enum) { 1263 /// Relative to build root 1264 path: []const u8, 1265 write_file: struct { 1266 step: *WriteFileStep, 1267 basename: []const u8, 1268 }, 1269 translate_c: *TranslateCStep, 1270 1271 pub fn addStepDependencies(self: FileSource, step: *Step) void { 1272 switch (self) { 1273 .path => {}, 1274 .write_file => |wf| step.dependOn(&wf.step.step), 1275 .translate_c => |tc| step.dependOn(&tc.step), 1276 } 1277 } 1278 1279 /// Should only be called during make() 1280 pub fn getPath(self: FileSource, builder: *Builder) []const u8 { 1281 return switch (self) { 1282 .path => |p| builder.pathFromRoot(p), 1283 .write_file => |wf| wf.step.getOutputPath(wf.basename), 1284 .translate_c => |tc| tc.getOutputPath(), 1285 }; 1286 } 1287 1288 pub fn dupe(self: FileSource, b: *Builder) FileSource { 1289 return switch (self) { 1290 .path => |p| .{ .path = b.dupe(p) }, 1291 .write_file => |wf| .{ .write_file = .{ 1292 .step = wf.step, 1293 .basename = b.dupe(wf.basename), 1294 } }, 1295 .translate_c => |tc| .{ .translate_c = tc }, 1296 }; 1297 } 1298 }; 1299 1300 const BuildOptionArtifactArg = struct { 1301 name: []const u8, 1302 artifact: *LibExeObjStep, 1303 }; 1304 1305 const BuildOptionWriteFileArg = struct { 1306 name: []const u8, 1307 write_file: *WriteFileStep, 1308 basename: []const u8, 1309 }; 1310 1311 pub const LibExeObjStep = struct { 1312 step: Step, 1313 builder: *Builder, 1314 name: []const u8, 1315 target: CrossTarget = CrossTarget{}, 1316 linker_script: ?[]const u8 = null, 1317 version_script: ?[]const u8 = null, 1318 out_filename: []const u8, 1319 is_dynamic: bool, 1320 version: ?Version, 1321 build_mode: builtin.Mode, 1322 kind: Kind, 1323 major_only_filename: []const u8, 1324 name_only_filename: []const u8, 1325 strip: bool, 1326 lib_paths: ArrayList([]const u8), 1327 framework_dirs: ArrayList([]const u8), 1328 frameworks: BufSet, 1329 verbose_link: bool, 1330 verbose_cc: bool, 1331 emit_llvm_ir: bool = false, 1332 emit_asm: bool = false, 1333 emit_bin: bool = true, 1334 emit_docs: bool = false, 1335 emit_h: bool = false, 1336 bundle_compiler_rt: ?bool = null, 1337 disable_stack_probing: bool, 1338 disable_sanitize_c: bool, 1339 sanitize_thread: bool, 1340 rdynamic: bool, 1341 c_std: Builder.CStd, 1342 override_lib_dir: ?[]const u8, 1343 main_pkg_path: ?[]const u8, 1344 exec_cmd_args: ?[]const ?[]const u8, 1345 name_prefix: []const u8, 1346 filter: ?[]const u8, 1347 single_threaded: bool, 1348 test_evented_io: bool = false, 1349 code_model: builtin.CodeModel = .default, 1350 1351 root_src: ?FileSource, 1352 out_h_filename: []const u8, 1353 out_lib_filename: []const u8, 1354 out_pdb_filename: []const u8, 1355 packages: ArrayList(Pkg), 1356 build_options_contents: std.ArrayList(u8), 1357 build_options_artifact_args: std.ArrayList(BuildOptionArtifactArg), 1358 build_options_write_file_args: std.ArrayList(BuildOptionWriteFileArg), 1359 1360 object_src: []const u8, 1361 1362 link_objects: ArrayList(LinkObject), 1363 include_dirs: ArrayList(IncludeDir), 1364 c_macros: ArrayList([]const u8), 1365 output_dir: ?[]const u8, 1366 is_linking_libc: bool = false, 1367 vcpkg_bin_path: ?[]const u8 = null, 1368 1369 /// This may be set in order to override the default install directory 1370 override_dest_dir: ?InstallDir, 1371 installed_path: ?[]const u8, 1372 install_step: ?*InstallArtifactStep, 1373 1374 /// Base address for an executable image. 1375 image_base: ?u64 = null, 1376 1377 libc_file: ?[]const u8 = null, 1378 1379 valgrind_support: ?bool = null, 1380 1381 /// Create a .eh_frame_hdr section and a PT_GNU_EH_FRAME segment in the ELF 1382 /// file. 1383 link_eh_frame_hdr: bool = false, 1384 link_emit_relocs: bool = false, 1385 1386 /// Place every function in its own section so that unused ones may be 1387 /// safely garbage-collected during the linking phase. 1388 link_function_sections: bool = false, 1389 1390 linker_allow_shlib_undefined: ?bool = null, 1391 1392 /// Uses system Wine installation to run cross compiled Windows build artifacts. 1393 enable_wine: bool = false, 1394 1395 /// Uses system QEMU installation to run cross compiled foreign architecture build artifacts. 1396 enable_qemu: bool = false, 1397 1398 /// Uses system Wasmtime installation to run cross compiled wasm/wasi build artifacts. 1399 enable_wasmtime: bool = false, 1400 1401 /// After following the steps in https://github.com/ziglang/zig/wiki/Updating-libc#glibc, 1402 /// this will be the directory $glibc-build-dir/install/glibcs 1403 /// Given the example of the aarch64 target, this is the directory 1404 /// that contains the path `aarch64-linux-gnu/lib/ld-linux-aarch64.so.1`. 1405 glibc_multi_install_dir: ?[]const u8 = null, 1406 1407 /// Position Independent Code 1408 force_pic: ?bool = null, 1409 1410 /// Position Independent Executable 1411 pie: ?bool = null, 1412 1413 red_zone: ?bool = null, 1414 1415 subsystem: ?builtin.SubSystem = null, 1416 1417 /// Overrides the default stack size 1418 stack_size: ?u64 = null, 1419 1420 want_lto: ?bool = null, 1421 1422 const LinkObject = union(enum) { 1423 StaticPath: []const u8, 1424 OtherStep: *LibExeObjStep, 1425 SystemLib: []const u8, 1426 AssemblyFile: FileSource, 1427 CSourceFile: *CSourceFile, 1428 CSourceFiles: *CSourceFiles, 1429 }; 1430 1431 const IncludeDir = union(enum) { 1432 RawPath: []const u8, 1433 RawPathSystem: []const u8, 1434 OtherStep: *LibExeObjStep, 1435 }; 1436 1437 const Kind = enum { 1438 Exe, 1439 Lib, 1440 Obj, 1441 Test, 1442 }; 1443 1444 const SharedLibKind = union(enum) { 1445 versioned: Version, 1446 unversioned: void, 1447 }; 1448 1449 pub fn createSharedLibrary(builder: *Builder, name: []const u8, root_src: ?FileSource, kind: SharedLibKind) *LibExeObjStep { 1450 const self = builder.allocator.create(LibExeObjStep) catch unreachable; 1451 self.* = initExtraArgs(builder, name, root_src, Kind.Lib, true, switch (kind) { 1452 .versioned => |ver| ver, 1453 .unversioned => null, 1454 }); 1455 return self; 1456 } 1457 1458 pub fn createStaticLibrary(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep { 1459 const self = builder.allocator.create(LibExeObjStep) catch unreachable; 1460 self.* = initExtraArgs(builder, name, root_src, Kind.Lib, false, null); 1461 return self; 1462 } 1463 1464 pub fn createObject(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep { 1465 const self = builder.allocator.create(LibExeObjStep) catch unreachable; 1466 self.* = initExtraArgs(builder, name, root_src, Kind.Obj, false, null); 1467 return self; 1468 } 1469 1470 pub fn createExecutable(builder: *Builder, name: []const u8, root_src: ?FileSource, is_dynamic: bool) *LibExeObjStep { 1471 const self = builder.allocator.create(LibExeObjStep) catch unreachable; 1472 self.* = initExtraArgs(builder, name, root_src, Kind.Exe, is_dynamic, null); 1473 return self; 1474 } 1475 1476 pub fn createTest(builder: *Builder, name: []const u8, root_src: FileSource) *LibExeObjStep { 1477 const self = builder.allocator.create(LibExeObjStep) catch unreachable; 1478 self.* = initExtraArgs(builder, name, root_src, Kind.Test, false, null); 1479 return self; 1480 } 1481 1482 fn initExtraArgs( 1483 builder: *Builder, 1484 name_raw: []const u8, 1485 root_src_raw: ?FileSource, 1486 kind: Kind, 1487 is_dynamic: bool, 1488 ver: ?Version, 1489 ) LibExeObjStep { 1490 const name = builder.dupe(name_raw); 1491 const root_src: ?FileSource = if (root_src_raw) |rsrc| rsrc.dupe(builder) else null; 1492 if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) { 1493 panic("invalid name: '{s}'. It looks like a file path, but it is supposed to be the library or application name.", .{name}); 1494 } 1495 var self = LibExeObjStep{ 1496 .strip = false, 1497 .builder = builder, 1498 .verbose_link = false, 1499 .verbose_cc = false, 1500 .build_mode = builtin.Mode.Debug, 1501 .is_dynamic = is_dynamic, 1502 .kind = kind, 1503 .root_src = root_src, 1504 .name = name, 1505 .frameworks = BufSet.init(builder.allocator), 1506 .step = Step.init(.LibExeObj, name, builder.allocator, make), 1507 .version = ver, 1508 .out_filename = undefined, 1509 .out_h_filename = builder.fmt("{s}.h", .{name}), 1510 .out_lib_filename = undefined, 1511 .out_pdb_filename = builder.fmt("{s}.pdb", .{name}), 1512 .major_only_filename = undefined, 1513 .name_only_filename = undefined, 1514 .packages = ArrayList(Pkg).init(builder.allocator), 1515 .include_dirs = ArrayList(IncludeDir).init(builder.allocator), 1516 .link_objects = ArrayList(LinkObject).init(builder.allocator), 1517 .c_macros = ArrayList([]const u8).init(builder.allocator), 1518 .lib_paths = ArrayList([]const u8).init(builder.allocator), 1519 .framework_dirs = ArrayList([]const u8).init(builder.allocator), 1520 .object_src = undefined, 1521 .build_options_contents = std.ArrayList(u8).init(builder.allocator), 1522 .build_options_artifact_args = std.ArrayList(BuildOptionArtifactArg).init(builder.allocator), 1523 .build_options_write_file_args = std.ArrayList(BuildOptionWriteFileArg).init(builder.allocator), 1524 .c_std = Builder.CStd.C99, 1525 .override_lib_dir = null, 1526 .main_pkg_path = null, 1527 .exec_cmd_args = null, 1528 .name_prefix = "", 1529 .filter = null, 1530 .disable_stack_probing = false, 1531 .disable_sanitize_c = false, 1532 .sanitize_thread = false, 1533 .rdynamic = false, 1534 .output_dir = null, 1535 .single_threaded = false, 1536 .override_dest_dir = null, 1537 .installed_path = null, 1538 .install_step = null, 1539 }; 1540 self.computeOutFileNames(); 1541 if (root_src) |rs| rs.addStepDependencies(&self.step); 1542 return self; 1543 } 1544 1545 fn computeOutFileNames(self: *LibExeObjStep) void { 1546 const target_info = std.zig.system.NativeTargetInfo.detect( 1547 self.builder.allocator, 1548 self.target, 1549 ) catch unreachable; 1550 const target = target_info.target; 1551 self.out_filename = std.zig.binNameAlloc(self.builder.allocator, .{ 1552 .root_name = self.name, 1553 .target = target, 1554 .output_mode = switch (self.kind) { 1555 .Lib => .Lib, 1556 .Obj => .Obj, 1557 .Exe, .Test => .Exe, 1558 }, 1559 .link_mode = if (self.is_dynamic) .Dynamic else .Static, 1560 .version = self.version, 1561 }) catch unreachable; 1562 1563 if (self.kind == .Lib) { 1564 if (!self.is_dynamic) { 1565 self.out_lib_filename = self.out_filename; 1566 } else if (self.version) |version| { 1567 if (target.isDarwin()) { 1568 self.major_only_filename = self.builder.fmt("lib{s}.{d}.dylib", .{ 1569 self.name, 1570 version.major, 1571 }); 1572 self.name_only_filename = self.builder.fmt("lib{s}.dylib", .{self.name}); 1573 self.out_lib_filename = self.out_filename; 1574 } else if (target.os.tag == .windows) { 1575 self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name}); 1576 } else { 1577 self.major_only_filename = self.builder.fmt("lib{s}.so.{d}", .{ self.name, version.major }); 1578 self.name_only_filename = self.builder.fmt("lib{s}.so", .{self.name}); 1579 self.out_lib_filename = self.out_filename; 1580 } 1581 } else { 1582 if (target.isDarwin()) { 1583 self.out_lib_filename = self.out_filename; 1584 } else if (target.os.tag == .windows) { 1585 self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name}); 1586 } else { 1587 self.out_lib_filename = self.out_filename; 1588 } 1589 } 1590 } 1591 } 1592 1593 pub fn setTarget(self: *LibExeObjStep, target: CrossTarget) void { 1594 self.target = target; 1595 self.computeOutFileNames(); 1596 } 1597 1598 pub fn setOutputDir(self: *LibExeObjStep, dir: []const u8) void { 1599 self.output_dir = self.builder.dupePath(dir); 1600 } 1601 1602 pub fn install(self: *LibExeObjStep) void { 1603 self.builder.installArtifact(self); 1604 } 1605 1606 pub fn installRaw(self: *LibExeObjStep, dest_filename: []const u8) void { 1607 self.builder.installRaw(self, dest_filename); 1608 } 1609 1610 /// Creates a `RunStep` with an executable built with `addExecutable`. 1611 /// Add command line arguments with `addArg`. 1612 pub fn run(exe: *LibExeObjStep) *RunStep { 1613 assert(exe.kind == Kind.Exe); 1614 1615 // It doesn't have to be native. We catch that if you actually try to run it. 1616 // Consider that this is declarative; the run step may not be run unless a user 1617 // option is supplied. 1618 const run_step = RunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name})); 1619 run_step.addArtifactArg(exe); 1620 1621 if (exe.vcpkg_bin_path) |path| { 1622 run_step.addPathDir(path); 1623 } 1624 1625 return run_step; 1626 } 1627 1628 pub fn setLinkerScriptPath(self: *LibExeObjStep, path: []const u8) void { 1629 self.linker_script = self.builder.dupePath(path); 1630 } 1631 1632 pub fn linkFramework(self: *LibExeObjStep, framework_name: []const u8) void { 1633 assert(self.target.isDarwin()); 1634 self.frameworks.put(self.builder.dupe(framework_name)) catch unreachable; 1635 } 1636 1637 /// Returns whether the library, executable, or object depends on a particular system library. 1638 pub fn dependsOnSystemLibrary(self: LibExeObjStep, name: []const u8) bool { 1639 if (isLibCLibrary(name)) { 1640 return self.is_linking_libc; 1641 } 1642 for (self.link_objects.items) |link_object| { 1643 switch (link_object) { 1644 LinkObject.SystemLib => |n| if (mem.eql(u8, n, name)) return true, 1645 else => continue, 1646 } 1647 } 1648 return false; 1649 } 1650 1651 pub fn linkLibrary(self: *LibExeObjStep, lib: *LibExeObjStep) void { 1652 assert(lib.kind == Kind.Lib); 1653 self.linkLibraryOrObject(lib); 1654 } 1655 1656 pub fn isDynamicLibrary(self: *LibExeObjStep) bool { 1657 return self.kind == Kind.Lib and self.is_dynamic; 1658 } 1659 1660 pub fn producesPdbFile(self: *LibExeObjStep) bool { 1661 if (!self.target.isWindows() and !self.target.isUefi()) return false; 1662 if (self.strip) return false; 1663 return self.isDynamicLibrary() or self.kind == .Exe; 1664 } 1665 1666 pub fn linkLibC(self: *LibExeObjStep) void { 1667 if (!self.is_linking_libc) { 1668 self.is_linking_libc = true; 1669 self.link_objects.append(LinkObject{ .SystemLib = "c" }) catch unreachable; 1670 } 1671 } 1672 1673 /// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1. 1674 pub fn defineCMacro(self: *LibExeObjStep, name_and_value: []const u8) void { 1675 self.c_macros.append(self.builder.dupe(name_and_value)) catch unreachable; 1676 } 1677 1678 /// This one has no integration with anything, it just puts -lname on the command line. 1679 /// Prefer to use `linkSystemLibrary` instead. 1680 pub fn linkSystemLibraryName(self: *LibExeObjStep, name: []const u8) void { 1681 self.link_objects.append(LinkObject{ .SystemLib = self.builder.dupe(name) }) catch unreachable; 1682 } 1683 1684 /// This links against a system library, exclusively using pkg-config to find the library. 1685 /// Prefer to use `linkSystemLibrary` instead. 1686 pub fn linkSystemLibraryPkgConfigOnly(self: *LibExeObjStep, lib_name: []const u8) !void { 1687 const pkg_name = match: { 1688 // First we have to map the library name to pkg config name. Unfortunately, 1689 // there are several examples where this is not straightforward: 1690 // -lSDL2 -> pkg-config sdl2 1691 // -lgdk-3 -> pkg-config gdk-3.0 1692 // -latk-1.0 -> pkg-config atk 1693 const pkgs = try self.builder.getPkgConfigList(); 1694 1695 // Exact match means instant winner. 1696 for (pkgs) |pkg| { 1697 if (mem.eql(u8, pkg.name, lib_name)) { 1698 break :match pkg.name; 1699 } 1700 } 1701 1702 // Next we'll try ignoring case. 1703 for (pkgs) |pkg| { 1704 if (std.ascii.eqlIgnoreCase(pkg.name, lib_name)) { 1705 break :match pkg.name; 1706 } 1707 } 1708 1709 // Now try appending ".0". 1710 for (pkgs) |pkg| { 1711 if (std.ascii.indexOfIgnoreCase(pkg.name, lib_name)) |pos| { 1712 if (pos != 0) continue; 1713 if (mem.eql(u8, pkg.name[lib_name.len..], ".0")) { 1714 break :match pkg.name; 1715 } 1716 } 1717 } 1718 1719 // Trimming "-1.0". 1720 if (mem.endsWith(u8, lib_name, "-1.0")) { 1721 const trimmed_lib_name = lib_name[0 .. lib_name.len - "-1.0".len]; 1722 for (pkgs) |pkg| { 1723 if (std.ascii.eqlIgnoreCase(pkg.name, trimmed_lib_name)) { 1724 break :match pkg.name; 1725 } 1726 } 1727 } 1728 1729 return error.PackageNotFound; 1730 }; 1731 1732 var code: u8 = undefined; 1733 const stdout = if (self.builder.execAllowFail(&[_][]const u8{ 1734 "pkg-config", 1735 pkg_name, 1736 "--cflags", 1737 "--libs", 1738 }, &code, .Ignore)) |stdout| stdout else |err| switch (err) { 1739 error.ProcessTerminated => return error.PkgConfigCrashed, 1740 error.ExitCodeFailure => return error.PkgConfigFailed, 1741 error.FileNotFound => return error.PkgConfigNotInstalled, 1742 else => return err, 1743 }; 1744 var it = mem.tokenize(stdout, " \r\n\t"); 1745 while (it.next()) |tok| { 1746 if (mem.eql(u8, tok, "-I")) { 1747 const dir = it.next() orelse return error.PkgConfigInvalidOutput; 1748 self.addIncludeDir(dir); 1749 } else if (mem.startsWith(u8, tok, "-I")) { 1750 self.addIncludeDir(tok["-I".len..]); 1751 } else if (mem.eql(u8, tok, "-L")) { 1752 const dir = it.next() orelse return error.PkgConfigInvalidOutput; 1753 self.addLibPath(dir); 1754 } else if (mem.startsWith(u8, tok, "-L")) { 1755 self.addLibPath(tok["-L".len..]); 1756 } else if (mem.eql(u8, tok, "-l")) { 1757 const lib = it.next() orelse return error.PkgConfigInvalidOutput; 1758 self.linkSystemLibraryName(lib); 1759 } else if (mem.startsWith(u8, tok, "-l")) { 1760 self.linkSystemLibraryName(tok["-l".len..]); 1761 } else if (mem.eql(u8, tok, "-D")) { 1762 const macro = it.next() orelse return error.PkgConfigInvalidOutput; 1763 self.defineCMacro(macro); 1764 } else if (mem.startsWith(u8, tok, "-D")) { 1765 self.defineCMacro(tok["-D".len..]); 1766 } else if (mem.eql(u8, tok, "-pthread")) { 1767 self.linkLibC(); 1768 } else if (self.builder.verbose) { 1769 warn("Ignoring pkg-config flag '{s}'\n", .{tok}); 1770 } 1771 } 1772 } 1773 1774 pub fn linkSystemLibrary(self: *LibExeObjStep, name: []const u8) void { 1775 if (isLibCLibrary(name)) { 1776 self.linkLibC(); 1777 return; 1778 } 1779 if (self.linkSystemLibraryPkgConfigOnly(name)) |_| { 1780 // pkg-config worked, so nothing further needed to do. 1781 return; 1782 } else |err| switch (err) { 1783 error.PkgConfigInvalidOutput, 1784 error.PkgConfigCrashed, 1785 error.PkgConfigFailed, 1786 error.PkgConfigNotInstalled, 1787 error.PackageNotFound, 1788 => {}, 1789 1790 else => unreachable, 1791 } 1792 1793 self.linkSystemLibraryName(name); 1794 } 1795 1796 pub fn setNamePrefix(self: *LibExeObjStep, text: []const u8) void { 1797 assert(self.kind == Kind.Test); 1798 self.name_prefix = self.builder.dupe(text); 1799 } 1800 1801 pub fn setFilter(self: *LibExeObjStep, text: ?[]const u8) void { 1802 assert(self.kind == Kind.Test); 1803 self.filter = if (text) |t| self.builder.dupe(t) else null; 1804 } 1805 1806 /// Handy when you have many C/C++ source files and want them all to have the same flags. 1807 pub fn addCSourceFiles(self: *LibExeObjStep, files: []const []const u8, flags: []const []const u8) void { 1808 const c_source_files = self.builder.allocator.create(CSourceFiles) catch unreachable; 1809 1810 const files_copy = self.builder.dupeStrings(files); 1811 const flags_copy = self.builder.dupeStrings(flags); 1812 1813 c_source_files.* = .{ 1814 .files = files_copy, 1815 .flags = flags_copy, 1816 }; 1817 self.link_objects.append(LinkObject{ .CSourceFiles = c_source_files }) catch unreachable; 1818 } 1819 1820 pub fn addCSourceFile(self: *LibExeObjStep, file: []const u8, flags: []const []const u8) void { 1821 self.addCSourceFileSource(.{ 1822 .args = flags, 1823 .source = .{ .path = file }, 1824 }); 1825 } 1826 1827 pub fn addCSourceFileSource(self: *LibExeObjStep, source: CSourceFile) void { 1828 const c_source_file = self.builder.allocator.create(CSourceFile) catch unreachable; 1829 c_source_file.* = source.dupe(self.builder); 1830 self.link_objects.append(LinkObject{ .CSourceFile = c_source_file }) catch unreachable; 1831 } 1832 1833 pub fn setVerboseLink(self: *LibExeObjStep, value: bool) void { 1834 self.verbose_link = value; 1835 } 1836 1837 pub fn setVerboseCC(self: *LibExeObjStep, value: bool) void { 1838 self.verbose_cc = value; 1839 } 1840 1841 pub fn setBuildMode(self: *LibExeObjStep, mode: builtin.Mode) void { 1842 self.build_mode = mode; 1843 } 1844 1845 pub fn overrideZigLibDir(self: *LibExeObjStep, dir_path: []const u8) void { 1846 self.override_lib_dir = self.builder.dupePath(dir_path); 1847 } 1848 1849 pub fn setMainPkgPath(self: *LibExeObjStep, dir_path: []const u8) void { 1850 self.main_pkg_path = self.builder.dupePath(dir_path); 1851 } 1852 1853 pub fn setLibCFile(self: *LibExeObjStep, libc_file: ?[]const u8) void { 1854 self.libc_file = if (libc_file) |f| self.builder.dupe(f) else null; 1855 } 1856 1857 /// Unless setOutputDir was called, this function must be called only in 1858 /// the make step, from a step that has declared a dependency on this one. 1859 /// To run an executable built with zig build, use `run`, or create an install step and invoke it. 1860 pub fn getOutputPath(self: *LibExeObjStep) []const u8 { 1861 return fs.path.join( 1862 self.builder.allocator, 1863 &[_][]const u8{ self.output_dir.?, self.out_filename }, 1864 ) catch unreachable; 1865 } 1866 1867 /// Unless setOutputDir was called, this function must be called only in 1868 /// the make step, from a step that has declared a dependency on this one. 1869 pub fn getOutputLibPath(self: *LibExeObjStep) []const u8 { 1870 assert(self.kind == Kind.Lib); 1871 return fs.path.join( 1872 self.builder.allocator, 1873 &[_][]const u8{ self.output_dir.?, self.out_lib_filename }, 1874 ) catch unreachable; 1875 } 1876 1877 /// Unless setOutputDir was called, this function must be called only in 1878 /// the make step, from a step that has declared a dependency on this one. 1879 pub fn getOutputHPath(self: *LibExeObjStep) []const u8 { 1880 assert(self.kind != Kind.Exe); 1881 assert(self.emit_h); 1882 return fs.path.join( 1883 self.builder.allocator, 1884 &[_][]const u8{ self.output_dir.?, self.out_h_filename }, 1885 ) catch unreachable; 1886 } 1887 1888 /// Unless setOutputDir was called, this function must be called only in 1889 /// the make step, from a step that has declared a dependency on this one. 1890 pub fn getOutputPdbPath(self: *LibExeObjStep) []const u8 { 1891 assert(self.target.isWindows() or self.target.isUefi()); 1892 return fs.path.join( 1893 self.builder.allocator, 1894 &[_][]const u8{ self.output_dir.?, self.out_pdb_filename }, 1895 ) catch unreachable; 1896 } 1897 1898 pub fn addAssemblyFile(self: *LibExeObjStep, path: []const u8) void { 1899 self.link_objects.append(LinkObject{ 1900 .AssemblyFile = .{ .path = self.builder.dupe(path) }, 1901 }) catch unreachable; 1902 } 1903 1904 pub fn addAssemblyFileFromWriteFileStep(self: *LibExeObjStep, wfs: *WriteFileStep, basename: []const u8) void { 1905 self.addAssemblyFileSource(.{ 1906 .write_file = .{ 1907 .step = wfs, 1908 .basename = self.builder.dupe(basename), 1909 }, 1910 }); 1911 } 1912 1913 pub fn addAssemblyFileSource(self: *LibExeObjStep, source: FileSource) void { 1914 const source_duped = source.dupe(self.builder); 1915 self.link_objects.append(LinkObject{ .AssemblyFile = source_duped }) catch unreachable; 1916 source_duped.addStepDependencies(&self.step); 1917 } 1918 1919 pub fn addObjectFile(self: *LibExeObjStep, path: []const u8) void { 1920 self.link_objects.append(LinkObject{ .StaticPath = self.builder.dupe(path) }) catch unreachable; 1921 } 1922 1923 pub fn addObject(self: *LibExeObjStep, obj: *LibExeObjStep) void { 1924 assert(obj.kind == Kind.Obj); 1925 self.linkLibraryOrObject(obj); 1926 } 1927 1928 pub fn addBuildOption(self: *LibExeObjStep, comptime T: type, name: []const u8, value: T) void { 1929 const out = self.build_options_contents.writer(); 1930 switch (T) { 1931 []const []const u8 => { 1932 out.print("pub const {}: []const []const u8 = &[_][]const u8{{\n", .{std.zig.fmtId(name)}) catch unreachable; 1933 for (value) |slice| { 1934 out.print(" \"{}\",\n", .{std.zig.fmtEscapes(slice)}) catch unreachable; 1935 } 1936 out.writeAll("};\n") catch unreachable; 1937 return; 1938 }, 1939 [:0]const u8 => { 1940 out.print("pub const {}: [:0]const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) }) catch unreachable; 1941 return; 1942 }, 1943 []const u8 => { 1944 out.print("pub const {}: []const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) }) catch unreachable; 1945 return; 1946 }, 1947 ?[:0]const u8 => { 1948 out.print("pub const {}: ?[:0]const u8 = ", .{std.zig.fmtId(name)}) catch unreachable; 1949 if (value) |payload| { 1950 out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)}) catch unreachable; 1951 } else { 1952 out.writeAll("null;\n") catch unreachable; 1953 } 1954 return; 1955 }, 1956 ?[]const u8 => { 1957 out.print("pub const {}: ?[]const u8 = ", .{std.zig.fmtId(name)}) catch unreachable; 1958 if (value) |payload| { 1959 out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)}) catch unreachable; 1960 } else { 1961 out.writeAll("null;\n") catch unreachable; 1962 } 1963 return; 1964 }, 1965 std.builtin.Version => { 1966 out.print( 1967 \\pub const {}: @import("builtin").Version = .{{ 1968 \\ .major = {d}, 1969 \\ .minor = {d}, 1970 \\ .patch = {d}, 1971 \\}}; 1972 \\ 1973 , .{ 1974 std.zig.fmtId(name), 1975 1976 value.major, 1977 value.minor, 1978 value.patch, 1979 }) catch unreachable; 1980 }, 1981 std.SemanticVersion => { 1982 out.print( 1983 \\pub const {}: @import("std").SemanticVersion = .{{ 1984 \\ .major = {d}, 1985 \\ .minor = {d}, 1986 \\ .patch = {d}, 1987 \\ 1988 , .{ 1989 std.zig.fmtId(name), 1990 1991 value.major, 1992 value.minor, 1993 value.patch, 1994 }) catch unreachable; 1995 if (value.pre) |some| { 1996 out.print(" .pre = \"{}\",\n", .{std.zig.fmtEscapes(some)}) catch unreachable; 1997 } 1998 if (value.build) |some| { 1999 out.print(" .build = \"{}\",\n", .{std.zig.fmtEscapes(some)}) catch unreachable; 2000 } 2001 out.writeAll("};\n") catch unreachable; 2002 return; 2003 }, 2004 else => {}, 2005 } 2006 switch (@typeInfo(T)) { 2007 .Enum => |enum_info| { 2008 out.print("pub const {} = enum {{\n", .{std.zig.fmtId(@typeName(T))}) catch unreachable; 2009 inline for (enum_info.fields) |field| { 2010 out.print(" {},\n", .{std.zig.fmtId(field.name)}) catch unreachable; 2011 } 2012 out.writeAll("};\n") catch unreachable; 2013 }, 2014 else => {}, 2015 } 2016 out.print("pub const {}: {s} = {};\n", .{ std.zig.fmtId(name), @typeName(T), value }) catch unreachable; 2017 } 2018 2019 /// The value is the path in the cache dir. 2020 /// Adds a dependency automatically. 2021 pub fn addBuildOptionArtifact(self: *LibExeObjStep, name: []const u8, artifact: *LibExeObjStep) void { 2022 self.build_options_artifact_args.append(.{ .name = self.builder.dupe(name), .artifact = artifact }) catch unreachable; 2023 self.step.dependOn(&artifact.step); 2024 } 2025 2026 /// The value is the path in the cache dir. 2027 /// Adds a dependency automatically. 2028 /// basename refers to the basename of the WriteFileStep 2029 pub fn addBuildOptionWriteFile( 2030 self: *LibExeObjStep, 2031 name: []const u8, 2032 write_file: *WriteFileStep, 2033 basename: []const u8, 2034 ) void { 2035 self.build_options_write_file_args.append(.{ 2036 .name = name, 2037 .write_file = write_file, 2038 .basename = basename, 2039 }) catch unreachable; 2040 self.step.dependOn(&write_file.step); 2041 } 2042 2043 pub fn addSystemIncludeDir(self: *LibExeObjStep, path: []const u8) void { 2044 self.include_dirs.append(IncludeDir{ .RawPathSystem = self.builder.dupe(path) }) catch unreachable; 2045 } 2046 2047 pub fn addIncludeDir(self: *LibExeObjStep, path: []const u8) void { 2048 self.include_dirs.append(IncludeDir{ .RawPath = self.builder.dupe(path) }) catch unreachable; 2049 } 2050 2051 pub fn addLibPath(self: *LibExeObjStep, path: []const u8) void { 2052 self.lib_paths.append(self.builder.dupe(path)) catch unreachable; 2053 } 2054 2055 pub fn addFrameworkDir(self: *LibExeObjStep, dir_path: []const u8) void { 2056 self.framework_dirs.append(self.builder.dupe(dir_path)) catch unreachable; 2057 } 2058 2059 pub fn addPackage(self: *LibExeObjStep, package: Pkg) void { 2060 self.packages.append(self.builder.dupePkg(package)) catch unreachable; 2061 } 2062 2063 pub fn addPackagePath(self: *LibExeObjStep, name: []const u8, pkg_index_path: []const u8) void { 2064 self.packages.append(Pkg{ 2065 .name = self.builder.dupe(name), 2066 .path = self.builder.dupe(pkg_index_path), 2067 }) catch unreachable; 2068 } 2069 2070 /// If Vcpkg was found on the system, it will be added to include and lib 2071 /// paths for the specified target. 2072 pub fn addVcpkgPaths(self: *LibExeObjStep, linkage: VcpkgLinkage) !void { 2073 // Ideally in the Unattempted case we would call the function recursively 2074 // after findVcpkgRoot and have only one switch statement, but the compiler 2075 // cannot resolve the error set. 2076 switch (self.builder.vcpkg_root) { 2077 .Unattempted => { 2078 self.builder.vcpkg_root = if (try findVcpkgRoot(self.builder.allocator)) |root| 2079 VcpkgRoot{ .Found = root } 2080 else 2081 .NotFound; 2082 }, 2083 .NotFound => return error.VcpkgNotFound, 2084 .Found => {}, 2085 } 2086 2087 switch (self.builder.vcpkg_root) { 2088 .Unattempted => unreachable, 2089 .NotFound => return error.VcpkgNotFound, 2090 .Found => |root| { 2091 const allocator = self.builder.allocator; 2092 const triplet = try self.target.vcpkgTriplet(allocator, linkage); 2093 defer self.builder.allocator.free(triplet); 2094 2095 const include_path = try fs.path.join(allocator, &[_][]const u8{ root, "installed", triplet, "include" }); 2096 errdefer allocator.free(include_path); 2097 try self.include_dirs.append(IncludeDir{ .RawPath = include_path }); 2098 2099 const lib_path = try fs.path.join(allocator, &[_][]const u8{ root, "installed", triplet, "lib" }); 2100 try self.lib_paths.append(lib_path); 2101 2102 self.vcpkg_bin_path = try fs.path.join(allocator, &[_][]const u8{ root, "installed", triplet, "bin" }); 2103 }, 2104 } 2105 } 2106 2107 pub fn setExecCmd(self: *LibExeObjStep, args: []const ?[]const u8) void { 2108 assert(self.kind == Kind.Test); 2109 const duped_args = self.builder.allocator.alloc(?[]u8, args.len) catch unreachable; 2110 for (args) |arg, i| { 2111 duped_args[i] = if (arg) |a| self.builder.dupe(a) else null; 2112 } 2113 self.exec_cmd_args = duped_args; 2114 } 2115 2116 fn linkLibraryOrObject(self: *LibExeObjStep, other: *LibExeObjStep) void { 2117 self.step.dependOn(&other.step); 2118 self.link_objects.append(LinkObject{ .OtherStep = other }) catch unreachable; 2119 self.include_dirs.append(IncludeDir{ .OtherStep = other }) catch unreachable; 2120 2121 // Inherit dependency on system libraries 2122 for (other.link_objects.items) |link_object| { 2123 switch (link_object) { 2124 .SystemLib => |name| self.linkSystemLibrary(name), 2125 else => continue, 2126 } 2127 } 2128 2129 // Inherit dependencies on darwin frameworks 2130 if (self.target.isDarwin() and !other.isDynamicLibrary()) { 2131 var it = other.frameworks.iterator(); 2132 while (it.next()) |entry| { 2133 self.frameworks.put(entry.key) catch unreachable; 2134 } 2135 } 2136 } 2137 2138 fn makePackageCmd(self: *LibExeObjStep, pkg: Pkg, zig_args: *ArrayList([]const u8)) error{OutOfMemory}!void { 2139 const builder = self.builder; 2140 2141 try zig_args.append("--pkg-begin"); 2142 try zig_args.append(pkg.name); 2143 try zig_args.append(builder.pathFromRoot(pkg.path)); 2144 2145 if (pkg.dependencies) |dependencies| { 2146 for (dependencies) |sub_pkg| { 2147 try self.makePackageCmd(sub_pkg, zig_args); 2148 } 2149 } 2150 2151 try zig_args.append("--pkg-end"); 2152 } 2153 2154 fn make(step: *Step) !void { 2155 const self = @fieldParentPtr(LibExeObjStep, "step", step); 2156 const builder = self.builder; 2157 2158 if (self.root_src == null and self.link_objects.items.len == 0) { 2159 warn("{s}: linker needs 1 or more objects to link\n", .{self.step.name}); 2160 return error.NeedAnObject; 2161 } 2162 2163 var zig_args = ArrayList([]const u8).init(builder.allocator); 2164 defer zig_args.deinit(); 2165 2166 zig_args.append(builder.zig_exe) catch unreachable; 2167 2168 const cmd = switch (self.kind) { 2169 .Lib => "build-lib", 2170 .Exe => "build-exe", 2171 .Obj => "build-obj", 2172 .Test => "test", 2173 }; 2174 zig_args.append(cmd) catch unreachable; 2175 2176 if (builder.color != .auto) { 2177 try zig_args.append("--color"); 2178 try zig_args.append(@tagName(builder.color)); 2179 } 2180 2181 if (self.stack_size) |stack_size| { 2182 try zig_args.append("--stack"); 2183 try zig_args.append(try std.fmt.allocPrint(builder.allocator, "{}", .{stack_size})); 2184 } 2185 2186 if (self.root_src) |root_src| try zig_args.append(root_src.getPath(builder)); 2187 2188 var prev_has_extra_flags = false; 2189 for (self.link_objects.items) |link_object| { 2190 switch (link_object) { 2191 .StaticPath => |static_path| { 2192 try zig_args.append(builder.pathFromRoot(static_path)); 2193 }, 2194 2195 .OtherStep => |other| switch (other.kind) { 2196 .Exe => unreachable, 2197 .Test => unreachable, 2198 .Obj => { 2199 try zig_args.append(other.getOutputPath()); 2200 }, 2201 .Lib => { 2202 const full_path_lib = other.getOutputLibPath(); 2203 try zig_args.append(full_path_lib); 2204 2205 if (other.is_dynamic and !self.target.isWindows()) { 2206 if (fs.path.dirname(full_path_lib)) |dirname| { 2207 try zig_args.append("-rpath"); 2208 try zig_args.append(dirname); 2209 } 2210 } 2211 }, 2212 }, 2213 .SystemLib => |name| { 2214 try zig_args.append(builder.fmt("-l{s}", .{name})); 2215 }, 2216 .AssemblyFile => |asm_file| { 2217 if (prev_has_extra_flags) { 2218 try zig_args.append("-extra-cflags"); 2219 try zig_args.append("--"); 2220 prev_has_extra_flags = false; 2221 } 2222 try zig_args.append(asm_file.getPath(builder)); 2223 }, 2224 2225 .CSourceFile => |c_source_file| { 2226 if (c_source_file.args.len == 0) { 2227 if (prev_has_extra_flags) { 2228 try zig_args.append("-cflags"); 2229 try zig_args.append("--"); 2230 prev_has_extra_flags = false; 2231 } 2232 } else { 2233 try zig_args.append("-cflags"); 2234 for (c_source_file.args) |arg| { 2235 try zig_args.append(arg); 2236 } 2237 try zig_args.append("--"); 2238 } 2239 try zig_args.append(c_source_file.source.getPath(builder)); 2240 }, 2241 2242 .CSourceFiles => |c_source_files| { 2243 if (c_source_files.flags.len == 0) { 2244 if (prev_has_extra_flags) { 2245 try zig_args.append("-cflags"); 2246 try zig_args.append("--"); 2247 prev_has_extra_flags = false; 2248 } 2249 } else { 2250 try zig_args.append("-cflags"); 2251 for (c_source_files.flags) |flag| { 2252 try zig_args.append(flag); 2253 } 2254 try zig_args.append("--"); 2255 } 2256 for (c_source_files.files) |file| { 2257 try zig_args.append(builder.pathFromRoot(file)); 2258 } 2259 }, 2260 } 2261 } 2262 2263 if (self.build_options_contents.items.len > 0 or 2264 self.build_options_artifact_args.items.len > 0 or 2265 self.build_options_write_file_args.items.len > 0) 2266 { 2267 // Render build artifact and write file options at the last minute, now that the path is known. 2268 // 2269 // Note that pathFromRoot uses resolve path, so this will have 2270 // correct behavior even if getOutputPath is already absolute. 2271 for (self.build_options_artifact_args.items) |item| { 2272 self.addBuildOption( 2273 []const u8, 2274 item.name, 2275 self.builder.pathFromRoot(item.artifact.getOutputPath()), 2276 ); 2277 } 2278 for (self.build_options_write_file_args.items) |item| { 2279 self.addBuildOption( 2280 []const u8, 2281 item.name, 2282 self.builder.pathFromRoot(item.write_file.getOutputPath(item.basename)), 2283 ); 2284 } 2285 2286 const build_options_file = try fs.path.join( 2287 builder.allocator, 2288 &[_][]const u8{ builder.cache_root, builder.fmt("{s}_build_options.zig", .{self.name}) }, 2289 ); 2290 const path_from_root = builder.pathFromRoot(build_options_file); 2291 try fs.cwd().writeFile(path_from_root, self.build_options_contents.items); 2292 try zig_args.append("--pkg-begin"); 2293 try zig_args.append("build_options"); 2294 try zig_args.append(path_from_root); 2295 try zig_args.append("--pkg-end"); 2296 } 2297 2298 if (self.image_base) |image_base| { 2299 try zig_args.append("--image-base"); 2300 try zig_args.append(builder.fmt("0x{x}", .{image_base})); 2301 } 2302 2303 if (self.filter) |filter| { 2304 try zig_args.append("--test-filter"); 2305 try zig_args.append(filter); 2306 } 2307 2308 if (self.test_evented_io) { 2309 try zig_args.append("--test-evented-io"); 2310 } 2311 2312 if (self.name_prefix.len != 0) { 2313 try zig_args.append("--test-name-prefix"); 2314 try zig_args.append(self.name_prefix); 2315 } 2316 2317 if (builder.verbose_tokenize) zig_args.append("--verbose-tokenize") catch unreachable; 2318 if (builder.verbose_ast) zig_args.append("--verbose-ast") catch unreachable; 2319 if (builder.verbose_cimport) zig_args.append("--verbose-cimport") catch unreachable; 2320 if (builder.verbose_ir) zig_args.append("--verbose-ir") catch unreachable; 2321 if (builder.verbose_llvm_ir) zig_args.append("--verbose-llvm-ir") catch unreachable; 2322 if (builder.verbose_link or self.verbose_link) zig_args.append("--verbose-link") catch unreachable; 2323 if (builder.verbose_cc or self.verbose_cc) zig_args.append("--verbose-cc") catch unreachable; 2324 if (builder.verbose_llvm_cpu_features) zig_args.append("--verbose-llvm-cpu-features") catch unreachable; 2325 2326 if (self.emit_llvm_ir) try zig_args.append("-femit-llvm-ir"); 2327 if (self.emit_asm) try zig_args.append("-femit-asm"); 2328 if (!self.emit_bin) try zig_args.append("-fno-emit-bin"); 2329 if (self.emit_docs) try zig_args.append("-femit-docs"); 2330 if (self.emit_h) try zig_args.append("-femit-h"); 2331 2332 if (self.strip) { 2333 try zig_args.append("--strip"); 2334 } 2335 if (self.link_eh_frame_hdr) { 2336 try zig_args.append("--eh-frame-hdr"); 2337 } 2338 if (self.link_emit_relocs) { 2339 try zig_args.append("--emit-relocs"); 2340 } 2341 if (self.link_function_sections) { 2342 try zig_args.append("-ffunction-sections"); 2343 } 2344 if (self.linker_allow_shlib_undefined) |x| { 2345 try zig_args.append(if (x) "-fallow-shlib-undefined" else "-fno-allow-shlib-undefined"); 2346 } 2347 if (self.single_threaded) { 2348 try zig_args.append("--single-threaded"); 2349 } 2350 2351 if (self.libc_file) |libc_file| { 2352 try zig_args.append("--libc"); 2353 try zig_args.append(builder.pathFromRoot(libc_file)); 2354 } 2355 2356 switch (self.build_mode) { 2357 .Debug => {}, // Skip since it's the default. 2358 else => zig_args.append(builder.fmt("-O{s}", .{@tagName(self.build_mode)})) catch unreachable, 2359 } 2360 2361 try zig_args.append("--cache-dir"); 2362 try zig_args.append(builder.pathFromRoot(builder.cache_root)); 2363 2364 try zig_args.append("--global-cache-dir"); 2365 try zig_args.append(builder.pathFromRoot(builder.global_cache_root)); 2366 2367 zig_args.append("--name") catch unreachable; 2368 zig_args.append(self.name) catch unreachable; 2369 2370 if (self.kind == Kind.Lib and self.is_dynamic) { 2371 if (self.version) |version| { 2372 zig_args.append("--version") catch unreachable; 2373 zig_args.append(builder.fmt("{}", .{version})) catch unreachable; 2374 } 2375 } 2376 if (self.is_dynamic) { 2377 try zig_args.append("-dynamic"); 2378 } 2379 if (self.bundle_compiler_rt) |x| { 2380 if (x) { 2381 try zig_args.append("-fcompiler-rt"); 2382 } else { 2383 try zig_args.append("-fno-compiler-rt"); 2384 } 2385 } 2386 if (self.disable_stack_probing) { 2387 try zig_args.append("-fno-stack-check"); 2388 } 2389 if (self.red_zone) |red_zone| { 2390 if (red_zone) { 2391 try zig_args.append("-mred-zone"); 2392 } else { 2393 try zig_args.append("-mno-red-zone"); 2394 } 2395 } 2396 if (self.disable_sanitize_c) { 2397 try zig_args.append("-fno-sanitize-c"); 2398 } 2399 if (self.sanitize_thread) { 2400 try zig_args.append("-fsanitize-thread"); 2401 } 2402 if (self.rdynamic) { 2403 try zig_args.append("-rdynamic"); 2404 } 2405 2406 if (self.code_model != .default) { 2407 try zig_args.append("-mcmodel"); 2408 try zig_args.append(@tagName(self.code_model)); 2409 } 2410 2411 if (!self.target.isNative()) { 2412 try zig_args.append("-target"); 2413 try zig_args.append(try self.target.zigTriple(builder.allocator)); 2414 2415 // TODO this logic can disappear if cpu model + features becomes part of the target triple 2416 const cross = self.target.toTarget(); 2417 const all_features = cross.cpu.arch.allFeaturesList(); 2418 var populated_cpu_features = cross.cpu.model.features; 2419 populated_cpu_features.populateDependencies(all_features); 2420 2421 if (populated_cpu_features.eql(cross.cpu.features)) { 2422 // The CPU name alone is sufficient. 2423 // If it is the baseline CPU, no command line args are required. 2424 if (cross.cpu.model != std.Target.Cpu.baseline(cross.cpu.arch).model) { 2425 try zig_args.append("-mcpu"); 2426 try zig_args.append(cross.cpu.model.name); 2427 } 2428 } else { 2429 var mcpu_buffer = std.ArrayList(u8).init(builder.allocator); 2430 2431 try mcpu_buffer.writer().print("-mcpu={s}", .{cross.cpu.model.name}); 2432 2433 for (all_features) |feature, i_usize| { 2434 const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize); 2435 const in_cpu_set = populated_cpu_features.isEnabled(i); 2436 const in_actual_set = cross.cpu.features.isEnabled(i); 2437 if (in_cpu_set and !in_actual_set) { 2438 try mcpu_buffer.writer().print("-{s}", .{feature.name}); 2439 } else if (!in_cpu_set and in_actual_set) { 2440 try mcpu_buffer.writer().print("+{s}", .{feature.name}); 2441 } 2442 } 2443 2444 try zig_args.append(mcpu_buffer.toOwnedSlice()); 2445 } 2446 2447 if (self.target.dynamic_linker.get()) |dynamic_linker| { 2448 try zig_args.append("--dynamic-linker"); 2449 try zig_args.append(dynamic_linker); 2450 } 2451 } 2452 2453 if (self.linker_script) |linker_script| { 2454 try zig_args.append("--script"); 2455 try zig_args.append(builder.pathFromRoot(linker_script)); 2456 } 2457 2458 if (self.version_script) |version_script| { 2459 try zig_args.append("--version-script"); 2460 try zig_args.append(builder.pathFromRoot(version_script)); 2461 } 2462 2463 if (self.exec_cmd_args) |exec_cmd_args| { 2464 for (exec_cmd_args) |cmd_arg| { 2465 if (cmd_arg) |arg| { 2466 try zig_args.append("--test-cmd"); 2467 try zig_args.append(arg); 2468 } else { 2469 try zig_args.append("--test-cmd-bin"); 2470 } 2471 } 2472 } else switch (self.target.getExternalExecutor()) { 2473 .native, .unavailable => {}, 2474 .qemu => |bin_name| if (self.enable_qemu) qemu: { 2475 const need_cross_glibc = self.target.isGnuLibC() and self.is_linking_libc; 2476 const glibc_dir_arg = if (need_cross_glibc) 2477 self.glibc_multi_install_dir orelse break :qemu 2478 else 2479 null; 2480 try zig_args.append("--test-cmd"); 2481 try zig_args.append(bin_name); 2482 if (glibc_dir_arg) |dir| { 2483 const full_dir = try fs.path.join(builder.allocator, &[_][]const u8{ 2484 dir, 2485 try self.target.linuxTriple(builder.allocator), 2486 }); 2487 2488 try zig_args.append("--test-cmd"); 2489 try zig_args.append("-L"); 2490 try zig_args.append("--test-cmd"); 2491 try zig_args.append(full_dir); 2492 } 2493 try zig_args.append("--test-cmd-bin"); 2494 }, 2495 .wine => |bin_name| if (self.enable_wine) { 2496 try zig_args.append("--test-cmd"); 2497 try zig_args.append(bin_name); 2498 try zig_args.append("--test-cmd-bin"); 2499 }, 2500 .wasmtime => |bin_name| if (self.enable_wasmtime) { 2501 try zig_args.append("--test-cmd"); 2502 try zig_args.append(bin_name); 2503 try zig_args.append("--test-cmd"); 2504 try zig_args.append("--dir=."); 2505 try zig_args.append("--test-cmd-bin"); 2506 }, 2507 } 2508 2509 for (self.packages.items) |pkg| { 2510 try self.makePackageCmd(pkg, &zig_args); 2511 } 2512 2513 for (self.include_dirs.items) |include_dir| { 2514 switch (include_dir) { 2515 .RawPath => |include_path| { 2516 try zig_args.append("-I"); 2517 try zig_args.append(self.builder.pathFromRoot(include_path)); 2518 }, 2519 .RawPathSystem => |include_path| { 2520 try zig_args.append("-isystem"); 2521 try zig_args.append(self.builder.pathFromRoot(include_path)); 2522 }, 2523 .OtherStep => |other| if (other.emit_h) { 2524 const h_path = other.getOutputHPath(); 2525 try zig_args.append("-isystem"); 2526 try zig_args.append(fs.path.dirname(h_path).?); 2527 }, 2528 } 2529 } 2530 2531 for (self.lib_paths.items) |lib_path| { 2532 try zig_args.append("-L"); 2533 try zig_args.append(lib_path); 2534 } 2535 2536 for (self.c_macros.items) |c_macro| { 2537 try zig_args.append("-D"); 2538 try zig_args.append(c_macro); 2539 } 2540 2541 if (self.target.isDarwin()) { 2542 for (self.framework_dirs.items) |dir| { 2543 try zig_args.append("-F"); 2544 try zig_args.append(dir); 2545 } 2546 2547 var it = self.frameworks.iterator(); 2548 while (it.next()) |entry| { 2549 zig_args.append("-framework") catch unreachable; 2550 zig_args.append(entry.key) catch unreachable; 2551 } 2552 } 2553 2554 for (builder.search_prefixes.items) |search_prefix| { 2555 try zig_args.append("-L"); 2556 try zig_args.append(try fs.path.join(builder.allocator, &[_][]const u8{ 2557 search_prefix, "lib", 2558 })); 2559 try zig_args.append("-isystem"); 2560 try zig_args.append(try fs.path.join(builder.allocator, &[_][]const u8{ 2561 search_prefix, "include", 2562 })); 2563 } 2564 2565 if (self.valgrind_support) |valgrind_support| { 2566 if (valgrind_support) { 2567 try zig_args.append("-fvalgrind"); 2568 } else { 2569 try zig_args.append("-fno-valgrind"); 2570 } 2571 } 2572 2573 if (self.override_lib_dir) |dir| { 2574 try zig_args.append("--override-lib-dir"); 2575 try zig_args.append(builder.pathFromRoot(dir)); 2576 } else if (self.builder.override_lib_dir) |dir| { 2577 try zig_args.append("--override-lib-dir"); 2578 try zig_args.append(builder.pathFromRoot(dir)); 2579 } 2580 2581 if (self.main_pkg_path) |dir| { 2582 try zig_args.append("--main-pkg-path"); 2583 try zig_args.append(builder.pathFromRoot(dir)); 2584 } 2585 2586 if (self.force_pic) |pic| { 2587 if (pic) { 2588 try zig_args.append("-fPIC"); 2589 } else { 2590 try zig_args.append("-fno-PIC"); 2591 } 2592 } 2593 2594 if (self.pie) |pie| { 2595 if (pie) { 2596 try zig_args.append("-fPIE"); 2597 } else { 2598 try zig_args.append("-fno-PIE"); 2599 } 2600 } 2601 2602 if (self.want_lto) |lto| { 2603 if (lto) { 2604 try zig_args.append("-flto"); 2605 } else { 2606 try zig_args.append("-fno-lto"); 2607 } 2608 } 2609 2610 if (self.subsystem) |subsystem| { 2611 try zig_args.append("--subsystem"); 2612 try zig_args.append(switch (subsystem) { 2613 .Console => "console", 2614 .Windows => "windows", 2615 .Posix => "posix", 2616 .Native => "native", 2617 .EfiApplication => "efi_application", 2618 .EfiBootServiceDriver => "efi_boot_service_driver", 2619 .EfiRom => "efi_rom", 2620 .EfiRuntimeDriver => "efi_runtime_driver", 2621 }); 2622 } 2623 2624 if (self.kind == Kind.Test) { 2625 try builder.spawnChild(zig_args.items); 2626 } else { 2627 try zig_args.append("--enable-cache"); 2628 2629 const output_dir_nl = try builder.execFromStep(zig_args.items, &self.step); 2630 const build_output_dir = mem.trimRight(u8, output_dir_nl, "\r\n"); 2631 2632 if (self.output_dir) |output_dir| { 2633 var src_dir = try std.fs.cwd().openDir(build_output_dir, .{ .iterate = true }); 2634 defer src_dir.close(); 2635 2636 // Create the output directory if it doesn't exist. 2637 try std.fs.cwd().makePath(output_dir); 2638 2639 var dest_dir = try std.fs.cwd().openDir(output_dir, .{}); 2640 defer dest_dir.close(); 2641 2642 var it = src_dir.iterate(); 2643 while (try it.next()) |entry| { 2644 // The compiler can put these files into the same directory, but we don't 2645 // want to copy them over. 2646 if (mem.eql(u8, entry.name, "stage1.id") or 2647 mem.eql(u8, entry.name, "llvm-ar.id") or 2648 mem.eql(u8, entry.name, "libs.txt") or 2649 mem.eql(u8, entry.name, "builtin.zig") or 2650 mem.eql(u8, entry.name, "lld.id")) continue; 2651 2652 _ = try src_dir.updateFile(entry.name, dest_dir, entry.name, .{}); 2653 } 2654 } else { 2655 self.output_dir = build_output_dir; 2656 } 2657 } 2658 2659 if (self.kind == Kind.Lib and self.is_dynamic and self.version != null and self.target.wantSharedLibSymLinks()) { 2660 try doAtomicSymLinks(builder.allocator, self.getOutputPath(), self.major_only_filename, self.name_only_filename); 2661 } 2662 } 2663 }; 2664 2665 pub const InstallArtifactStep = struct { 2666 step: Step, 2667 builder: *Builder, 2668 artifact: *LibExeObjStep, 2669 dest_dir: InstallDir, 2670 pdb_dir: ?InstallDir, 2671 h_dir: ?InstallDir, 2672 2673 const Self = @This(); 2674 2675 pub fn create(builder: *Builder, artifact: *LibExeObjStep) *Self { 2676 if (artifact.install_step) |s| return s; 2677 2678 const self = builder.allocator.create(Self) catch unreachable; 2679 self.* = Self{ 2680 .builder = builder, 2681 .step = Step.init(.InstallArtifact, builder.fmt("install {s}", .{artifact.step.name}), builder.allocator, make), 2682 .artifact = artifact, 2683 .dest_dir = artifact.override_dest_dir orelse switch (artifact.kind) { 2684 .Obj => unreachable, 2685 .Test => unreachable, 2686 .Exe => InstallDir{ .Bin = {} }, 2687 .Lib => InstallDir{ .Lib = {} }, 2688 }, 2689 .pdb_dir = if (artifact.producesPdbFile()) blk: { 2690 if (artifact.kind == .Exe) { 2691 break :blk InstallDir{ .Bin = {} }; 2692 } else { 2693 break :blk InstallDir{ .Lib = {} }; 2694 } 2695 } else null, 2696 .h_dir = if (artifact.kind == .Lib and artifact.emit_h) .Header else null, 2697 }; 2698 self.step.dependOn(&artifact.step); 2699 artifact.install_step = self; 2700 2701 builder.pushInstalledFile(self.dest_dir, artifact.out_filename); 2702 if (self.artifact.isDynamicLibrary()) { 2703 if (self.artifact.version != null) { 2704 builder.pushInstalledFile(.Lib, artifact.major_only_filename); 2705 builder.pushInstalledFile(.Lib, artifact.name_only_filename); 2706 } 2707 if (self.artifact.target.isWindows()) { 2708 builder.pushInstalledFile(.Lib, artifact.out_lib_filename); 2709 } 2710 } 2711 if (self.pdb_dir) |pdb_dir| { 2712 builder.pushInstalledFile(pdb_dir, artifact.out_pdb_filename); 2713 } 2714 if (self.h_dir) |h_dir| { 2715 builder.pushInstalledFile(h_dir, artifact.out_h_filename); 2716 } 2717 return self; 2718 } 2719 2720 fn make(step: *Step) !void { 2721 const self = @fieldParentPtr(Self, "step", step); 2722 const builder = self.builder; 2723 2724 const full_dest_path = builder.getInstallPath(self.dest_dir, self.artifact.out_filename); 2725 try builder.updateFile(self.artifact.getOutputPath(), full_dest_path); 2726 if (self.artifact.isDynamicLibrary() and self.artifact.version != null and self.artifact.target.wantSharedLibSymLinks()) { 2727 try doAtomicSymLinks(builder.allocator, full_dest_path, self.artifact.major_only_filename, self.artifact.name_only_filename); 2728 } 2729 if (self.pdb_dir) |pdb_dir| { 2730 const full_pdb_path = builder.getInstallPath(pdb_dir, self.artifact.out_pdb_filename); 2731 try builder.updateFile(self.artifact.getOutputPdbPath(), full_pdb_path); 2732 } 2733 if (self.h_dir) |h_dir| { 2734 const full_pdb_path = builder.getInstallPath(h_dir, self.artifact.out_h_filename); 2735 try builder.updateFile(self.artifact.getOutputHPath(), full_pdb_path); 2736 } 2737 self.artifact.installed_path = full_dest_path; 2738 } 2739 }; 2740 2741 pub const InstallFileStep = struct { 2742 step: Step, 2743 builder: *Builder, 2744 src_path: []const u8, 2745 dir: InstallDir, 2746 dest_rel_path: []const u8, 2747 2748 pub fn init( 2749 builder: *Builder, 2750 src_path: []const u8, 2751 dir: InstallDir, 2752 dest_rel_path: []const u8, 2753 ) InstallFileStep { 2754 builder.pushInstalledFile(dir, dest_rel_path); 2755 return InstallFileStep{ 2756 .builder = builder, 2757 .step = Step.init(.InstallFile, builder.fmt("install {s}", .{src_path}), builder.allocator, make), 2758 .src_path = builder.dupePath(src_path), 2759 .dir = dir.dupe(builder), 2760 .dest_rel_path = builder.dupePath(dest_rel_path), 2761 }; 2762 } 2763 2764 fn make(step: *Step) !void { 2765 const self = @fieldParentPtr(InstallFileStep, "step", step); 2766 const full_dest_path = self.builder.getInstallPath(self.dir, self.dest_rel_path); 2767 const full_src_path = self.builder.pathFromRoot(self.src_path); 2768 try self.builder.updateFile(full_src_path, full_dest_path); 2769 } 2770 }; 2771 2772 pub const InstallDirectoryOptions = struct { 2773 source_dir: []const u8, 2774 install_dir: InstallDir, 2775 install_subdir: []const u8, 2776 exclude_extensions: ?[]const []const u8 = null, 2777 2778 fn dupe(self: InstallDirectoryOptions, b: *Builder) InstallDirectoryOptions { 2779 return .{ 2780 .source_dir = b.dupe(self.source_dir), 2781 .install_dir = self.install_dir.dupe(b), 2782 .install_subdir = b.dupe(self.install_subdir), 2783 .exclude_extensions = if (self.exclude_extensions) |extensions| 2784 b.dupeStrings(extensions) 2785 else 2786 null, 2787 }; 2788 } 2789 }; 2790 2791 pub const InstallDirStep = struct { 2792 step: Step, 2793 builder: *Builder, 2794 options: InstallDirectoryOptions, 2795 2796 pub fn init( 2797 builder: *Builder, 2798 options: InstallDirectoryOptions, 2799 ) InstallDirStep { 2800 builder.pushInstalledFile(options.install_dir, options.install_subdir); 2801 return InstallDirStep{ 2802 .builder = builder, 2803 .step = Step.init(.InstallDir, builder.fmt("install {s}/", .{options.source_dir}), builder.allocator, make), 2804 .options = options.dupe(builder), 2805 }; 2806 } 2807 2808 fn make(step: *Step) !void { 2809 const self = @fieldParentPtr(InstallDirStep, "step", step); 2810 const dest_prefix = self.builder.getInstallPath(self.options.install_dir, self.options.install_subdir); 2811 const full_src_dir = self.builder.pathFromRoot(self.options.source_dir); 2812 var it = try fs.walkPath(self.builder.allocator, full_src_dir); 2813 next_entry: while (try it.next()) |entry| { 2814 if (self.options.exclude_extensions) |ext_list| for (ext_list) |ext| { 2815 if (mem.endsWith(u8, entry.path, ext)) { 2816 continue :next_entry; 2817 } 2818 }; 2819 2820 const rel_path = entry.path[full_src_dir.len + 1 ..]; 2821 const dest_path = try fs.path.join(self.builder.allocator, &[_][]const u8{ dest_prefix, rel_path }); 2822 switch (entry.kind) { 2823 .Directory => try fs.cwd().makePath(dest_path), 2824 .File => try self.builder.updateFile(entry.path, dest_path), 2825 else => continue, 2826 } 2827 } 2828 } 2829 }; 2830 2831 pub const LogStep = struct { 2832 step: Step, 2833 builder: *Builder, 2834 data: []const u8, 2835 2836 pub fn init(builder: *Builder, data: []const u8) LogStep { 2837 return LogStep{ 2838 .builder = builder, 2839 .step = Step.init(.Log, builder.fmt("log {s}", .{data}), builder.allocator, make), 2840 .data = builder.dupe(data), 2841 }; 2842 } 2843 2844 fn make(step: *Step) anyerror!void { 2845 const self = @fieldParentPtr(LogStep, "step", step); 2846 warn("{s}", .{self.data}); 2847 } 2848 }; 2849 2850 pub const RemoveDirStep = struct { 2851 step: Step, 2852 builder: *Builder, 2853 dir_path: []const u8, 2854 2855 pub fn init(builder: *Builder, dir_path: []const u8) RemoveDirStep { 2856 return RemoveDirStep{ 2857 .builder = builder, 2858 .step = Step.init(.RemoveDir, builder.fmt("RemoveDir {s}", .{dir_path}), builder.allocator, make), 2859 .dir_path = builder.dupePath(dir_path), 2860 }; 2861 } 2862 2863 fn make(step: *Step) !void { 2864 const self = @fieldParentPtr(RemoveDirStep, "step", step); 2865 2866 const full_path = self.builder.pathFromRoot(self.dir_path); 2867 fs.cwd().deleteTree(full_path) catch |err| { 2868 warn("Unable to remove {s}: {s}\n", .{ full_path, @errorName(err) }); 2869 return err; 2870 }; 2871 } 2872 }; 2873 2874 const ThisModule = @This(); 2875 pub const Step = struct { 2876 id: Id, 2877 name: []const u8, 2878 makeFn: fn (self: *Step) anyerror!void, 2879 dependencies: ArrayList(*Step), 2880 loop_flag: bool, 2881 done_flag: bool, 2882 2883 pub const Id = enum { 2884 TopLevel, 2885 LibExeObj, 2886 InstallArtifact, 2887 InstallFile, 2888 InstallDir, 2889 Log, 2890 RemoveDir, 2891 Fmt, 2892 TranslateC, 2893 WriteFile, 2894 Run, 2895 CheckFile, 2896 InstallRaw, 2897 Custom, 2898 }; 2899 2900 pub fn init(id: Id, name: []const u8, allocator: *Allocator, makeFn: fn (*Step) anyerror!void) Step { 2901 return Step{ 2902 .id = id, 2903 .name = allocator.dupe(u8, name) catch unreachable, 2904 .makeFn = makeFn, 2905 .dependencies = ArrayList(*Step).init(allocator), 2906 .loop_flag = false, 2907 .done_flag = false, 2908 }; 2909 } 2910 pub fn initNoOp(id: Id, name: []const u8, allocator: *Allocator) Step { 2911 return init(id, name, allocator, makeNoOp); 2912 } 2913 2914 pub fn make(self: *Step) !void { 2915 if (self.done_flag) return; 2916 2917 try self.makeFn(self); 2918 self.done_flag = true; 2919 } 2920 2921 pub fn dependOn(self: *Step, other: *Step) void { 2922 self.dependencies.append(other) catch unreachable; 2923 } 2924 2925 fn makeNoOp(self: *Step) anyerror!void {} 2926 2927 pub fn cast(step: *Step, comptime T: type) ?*T { 2928 if (step.id == comptime typeToId(T)) { 2929 return @fieldParentPtr(T, "step", step); 2930 } 2931 return null; 2932 } 2933 2934 fn typeToId(comptime T: type) Id { 2935 inline for (@typeInfo(Id).Enum.fields) |f| { 2936 if (std.mem.eql(u8, f.name, "TopLevel") or 2937 std.mem.eql(u8, f.name, "Custom")) continue; 2938 2939 if (T == @field(ThisModule, f.name ++ "Step")) { 2940 return @field(Id, f.name); 2941 } 2942 } 2943 unreachable; 2944 } 2945 }; 2946 2947 fn doAtomicSymLinks(allocator: *Allocator, output_path: []const u8, filename_major_only: []const u8, filename_name_only: []const u8) !void { 2948 const out_dir = fs.path.dirname(output_path) orelse "."; 2949 const out_basename = fs.path.basename(output_path); 2950 // sym link for libfoo.so.1 to libfoo.so.1.2.3 2951 const major_only_path = fs.path.join( 2952 allocator, 2953 &[_][]const u8{ out_dir, filename_major_only }, 2954 ) catch unreachable; 2955 fs.atomicSymLink(allocator, out_basename, major_only_path) catch |err| { 2956 warn("Unable to symlink {s} -> {s}\n", .{ major_only_path, out_basename }); 2957 return err; 2958 }; 2959 // sym link for libfoo.so to libfoo.so.1 2960 const name_only_path = fs.path.join( 2961 allocator, 2962 &[_][]const u8{ out_dir, filename_name_only }, 2963 ) catch unreachable; 2964 fs.atomicSymLink(allocator, filename_major_only, name_only_path) catch |err| { 2965 warn("Unable to symlink {s} -> {s}\n", .{ name_only_path, filename_major_only }); 2966 return err; 2967 }; 2968 } 2969 2970 /// Returned slice must be freed by the caller. 2971 fn findVcpkgRoot(allocator: *Allocator) !?[]const u8 { 2972 const appdata_path = try fs.getAppDataDir(allocator, "vcpkg"); 2973 defer allocator.free(appdata_path); 2974 2975 const path_file = try fs.path.join(allocator, &[_][]const u8{ appdata_path, "vcpkg.path.txt" }); 2976 defer allocator.free(path_file); 2977 2978 const file = fs.cwd().openFile(path_file, .{}) catch return null; 2979 defer file.close(); 2980 2981 const size = @intCast(usize, try file.getEndPos()); 2982 const vcpkg_path = try allocator.alloc(u8, size); 2983 const size_read = try file.read(vcpkg_path); 2984 std.debug.assert(size == size_read); 2985 2986 return vcpkg_path; 2987 } 2988 2989 const VcpkgRoot = union(VcpkgRootStatus) { 2990 Unattempted: void, 2991 NotFound: void, 2992 Found: []const u8, 2993 }; 2994 2995 const VcpkgRootStatus = enum { 2996 Unattempted, 2997 NotFound, 2998 Found, 2999 }; 3000 3001 pub const VcpkgLinkage = std.builtin.LinkMode; 3002 3003 pub const InstallDir = union(enum) { 3004 Prefix: void, 3005 Lib: void, 3006 Bin: void, 3007 Header: void, 3008 /// A path relative to the prefix 3009 Custom: []const u8, 3010 3011 fn dupe(self: InstallDir, builder: *Builder) InstallDir { 3012 if (self == .Custom) { 3013 // Written with this temporary to avoid RLS problems 3014 const duped_path = builder.dupe(self.Custom); 3015 return .{ .Custom = duped_path }; 3016 } else { 3017 return self; 3018 } 3019 } 3020 }; 3021 3022 pub const InstalledFile = struct { 3023 dir: InstallDir, 3024 path: []const u8, 3025 3026 pub fn dupe(self: InstalledFile, builder: *Builder) InstalledFile { 3027 return .{ 3028 .dir = self.dir.dupe(builder), 3029 .path = builder.dupe(self.path), 3030 }; 3031 } 3032 }; 3033 3034 test "Builder.dupePkg()" { 3035 if (builtin.os.tag == .wasi) return error.SkipZigTest; 3036 3037 var arena = std.heap.ArenaAllocator.init(std.testing.allocator); 3038 defer arena.deinit(); 3039 var builder = try Builder.create( 3040 &arena.allocator, 3041 "test", 3042 "test", 3043 "test", 3044 "test", 3045 ); 3046 defer builder.destroy(); 3047 3048 var pkg_dep = Pkg{ 3049 .name = "pkg_dep", 3050 .path = "/not/a/pkg_dep.zig", 3051 }; 3052 var pkg_top = Pkg{ 3053 .name = "pkg_top", 3054 .path = "/not/a/pkg_top.zig", 3055 .dependencies = &[_]Pkg{pkg_dep}, 3056 }; 3057 const dupe = builder.dupePkg(pkg_top); 3058 3059 const original_deps = pkg_top.dependencies.?; 3060 const dupe_deps = dupe.dependencies.?; 3061 3062 // probably the same top level package details 3063 try std.testing.expectEqualStrings(pkg_top.name, dupe.name); 3064 3065 // probably the same dependencies 3066 try std.testing.expectEqual(original_deps.len, dupe_deps.len); 3067 try std.testing.expectEqual(original_deps[0].name, pkg_dep.name); 3068 3069 // could segfault otherwise if pointers in duplicated package's fields are 3070 // the same as those in stack allocated package's fields 3071 try std.testing.expect(dupe_deps.ptr != original_deps.ptr); 3072 try std.testing.expect(dupe.name.ptr != pkg_top.name.ptr); 3073 try std.testing.expect(dupe.path.ptr != pkg_top.path.ptr); 3074 try std.testing.expect(dupe_deps[0].name.ptr != pkg_dep.name.ptr); 3075 try std.testing.expect(dupe_deps[0].path.ptr != pkg_dep.path.ptr); 3076 } 3077 3078 test "LibExeObjStep.addBuildOption" { 3079 if (builtin.os.tag == .wasi) return error.SkipZigTest; 3080 3081 var arena = std.heap.ArenaAllocator.init(std.testing.allocator); 3082 defer arena.deinit(); 3083 var builder = try Builder.create( 3084 &arena.allocator, 3085 "test", 3086 "test", 3087 "test", 3088 "test", 3089 ); 3090 defer builder.destroy(); 3091 3092 var exe = builder.addExecutable("not_an_executable", "/not/an/executable.zig"); 3093 exe.addBuildOption(usize, "option1", 1); 3094 exe.addBuildOption(?usize, "option2", null); 3095 exe.addBuildOption([]const u8, "string", "zigisthebest"); 3096 exe.addBuildOption(?[]const u8, "optional_string", null); 3097 exe.addBuildOption(std.SemanticVersion, "semantic_version", try std.SemanticVersion.parse("0.1.2-foo+bar")); 3098 3099 try std.testing.expectEqualStrings( 3100 \\pub const option1: usize = 1; 3101 \\pub const option2: ?usize = null; 3102 \\pub const string: []const u8 = "zigisthebest"; 3103 \\pub const optional_string: ?[]const u8 = null; 3104 \\pub const semantic_version: @import("std").SemanticVersion = .{ 3105 \\ .major = 0, 3106 \\ .minor = 1, 3107 \\ .patch = 2, 3108 \\ .pre = "foo", 3109 \\ .build = "bar", 3110 \\}; 3111 \\ 3112 , exe.build_options_contents.items); 3113 } 3114 3115 test "LibExeObjStep.addPackage" { 3116 if (builtin.os.tag == .wasi) return error.SkipZigTest; 3117 3118 var arena = std.heap.ArenaAllocator.init(std.testing.allocator); 3119 defer arena.deinit(); 3120 3121 var builder = try Builder.create( 3122 &arena.allocator, 3123 "test", 3124 "test", 3125 "test", 3126 "test", 3127 ); 3128 defer builder.destroy(); 3129 3130 const pkg_dep = Pkg{ 3131 .name = "pkg_dep", 3132 .path = "/not/a/pkg_dep.zig", 3133 }; 3134 const pkg_top = Pkg{ 3135 .name = "pkg_dep", 3136 .path = "/not/a/pkg_top.zig", 3137 .dependencies = &[_]Pkg{pkg_dep}, 3138 }; 3139 3140 var exe = builder.addExecutable("not_an_executable", "/not/an/executable.zig"); 3141 exe.addPackage(pkg_top); 3142 3143 try std.testing.expectEqual(@as(usize, 1), exe.packages.items.len); 3144 3145 const dupe = exe.packages.items[0]; 3146 try std.testing.expectEqualStrings(pkg_top.name, dupe.name); 3147 } 3148 3149 test { 3150 // The only purpose of this test is to get all these untested functions 3151 // to be referenced to avoid regression so it is okay to skip some targets. 3152 if (comptime std.Target.current.cpu.arch.ptrBitWidth() == 64) { 3153 std.testing.refAllDecls(@This()); 3154 std.testing.refAllDecls(Builder); 3155 3156 inline for (std.meta.declarations(@This())) |decl| 3157 if (comptime mem.endsWith(u8, decl.name, "Step")) 3158 std.testing.refAllDecls(decl.data.Type); 3159 } 3160 }