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