blob b47f962a (148555B) - Raw
1 const std = @import("std.zig"); 2 const builtin = @import("builtin"); 3 const io = std.io; 4 const fs = std.fs; 5 const mem = std.mem; 6 const debug = std.debug; 7 const panic = std.debug.panic; 8 const assert = debug.assert; 9 const log = std.log; 10 const ArrayList = std.ArrayList; 11 const StringHashMap = std.StringHashMap; 12 const Allocator = mem.Allocator; 13 const process = std.process; 14 const EnvMap = std.process.EnvMap; 15 const fmt_lib = std.fmt; 16 const File = std.fs.File; 17 const CrossTarget = std.zig.CrossTarget; 18 const NativeTargetInfo = std.zig.system.NativeTargetInfo; 19 const Sha256 = std.crypto.hash.sha2.Sha256; 20 21 pub const FmtStep = @import("build/FmtStep.zig"); 22 pub const TranslateCStep = @import("build/TranslateCStep.zig"); 23 pub const WriteFileStep = @import("build/WriteFileStep.zig"); 24 pub const RunStep = @import("build/RunStep.zig"); 25 pub const CheckFileStep = @import("build/CheckFileStep.zig"); 26 pub const CheckObjectStep = @import("build/CheckObjectStep.zig"); 27 pub const InstallRawStep = @import("build/InstallRawStep.zig"); 28 pub const OptionsStep = @import("build/OptionsStep.zig"); 29 pub const EmulatableRunStep = @import("build/EmulatableRunStep.zig"); 30 31 pub const Builder = struct { 32 install_tls: TopLevelStep, 33 uninstall_tls: TopLevelStep, 34 allocator: Allocator, 35 user_input_options: UserInputOptionsMap, 36 available_options_map: AvailableOptionsMap, 37 available_options_list: ArrayList(AvailableOption), 38 verbose: bool, 39 verbose_link: bool, 40 verbose_cc: bool, 41 verbose_air: bool, 42 verbose_llvm_ir: bool, 43 verbose_cimport: bool, 44 verbose_llvm_cpu_features: bool, 45 /// The purpose of executing the command is for a human to read compile errors from the terminal 46 prominent_compile_errors: bool, 47 color: enum { auto, on, off } = .auto, 48 use_stage1: ?bool = null, 49 invalid_user_input: bool, 50 zig_exe: []const u8, 51 default_step: *Step, 52 env_map: *EnvMap, 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 sysroot: ?[]const u8 = null, 61 search_prefixes: ArrayList([]const u8), 62 libc_file: ?[]const u8 = null, 63 installed_files: ArrayList(InstalledFile), 64 build_root: []const u8, 65 cache_root: []const u8, 66 global_cache_root: []const u8, 67 release_mode: ?std.builtin.Mode, 68 is_release: bool, 69 override_lib_dir: ?[]const u8, 70 vcpkg_root: VcpkgRoot, 71 pkg_config_pkg_list: ?(PkgConfigError![]const PkgConfigPkg) = null, 72 args: ?[][]const u8 = null, 73 debug_log_scopes: []const []const u8 = &.{}, 74 75 /// Experimental. Use system Darling installation to run cross compiled macOS build artifacts. 76 enable_darling: bool = false, 77 /// Use system QEMU installation to run cross compiled foreign architecture build artifacts. 78 enable_qemu: bool = false, 79 /// Darwin. Use Rosetta to run x86_64 macOS build artifacts on arm64 macOS. 80 enable_rosetta: bool = false, 81 /// Use system Wasmtime installation to run cross compiled wasm/wasi build artifacts. 82 enable_wasmtime: bool = false, 83 /// Use system Wine installation to run cross compiled Windows build artifacts. 84 enable_wine: bool = false, 85 /// After following the steps in https://github.com/ziglang/zig/wiki/Updating-libc#glibc, 86 /// this will be the directory $glibc-build-dir/install/glibcs 87 /// Given the example of the aarch64 target, this is the directory 88 /// that contains the path `aarch64-linux-gnu/lib/ld-linux-aarch64.so.1`. 89 glibc_runtimes_dir: ?[]const u8 = null, 90 91 /// Information about the native target. Computed before build() is invoked. 92 host: NativeTargetInfo, 93 94 pub const ExecError = error{ 95 ReadFailure, 96 ExitCodeFailure, 97 ProcessTerminated, 98 ExecNotSupported, 99 } || std.ChildProcess.SpawnError; 100 101 pub const PkgConfigError = error{ 102 PkgConfigCrashed, 103 PkgConfigFailed, 104 PkgConfigNotInstalled, 105 PkgConfigInvalidOutput, 106 }; 107 108 pub const PkgConfigPkg = struct { 109 name: []const u8, 110 desc: []const u8, 111 }; 112 113 pub const CStd = enum { 114 C89, 115 C99, 116 C11, 117 }; 118 119 const UserInputOptionsMap = StringHashMap(UserInputOption); 120 const AvailableOptionsMap = StringHashMap(AvailableOption); 121 122 const AvailableOption = struct { 123 name: []const u8, 124 type_id: TypeId, 125 description: []const u8, 126 /// If the `type_id` is `enum` this provides the list of enum options 127 enum_options: ?[]const []const u8, 128 }; 129 130 const UserInputOption = struct { 131 name: []const u8, 132 value: UserValue, 133 used: bool, 134 }; 135 136 const UserValue = union(enum) { 137 flag: void, 138 scalar: []const u8, 139 list: ArrayList([]const u8), 140 }; 141 142 const TypeId = enum { 143 bool, 144 int, 145 float, 146 @"enum", 147 string, 148 list, 149 }; 150 151 const TopLevelStep = struct { 152 pub const base_id = .top_level; 153 154 step: Step, 155 description: []const u8, 156 }; 157 158 pub const DirList = struct { 159 lib_dir: ?[]const u8 = null, 160 exe_dir: ?[]const u8 = null, 161 include_dir: ?[]const u8 = null, 162 }; 163 164 pub fn create( 165 allocator: Allocator, 166 zig_exe: []const u8, 167 build_root: []const u8, 168 cache_root: []const u8, 169 global_cache_root: []const u8, 170 ) !*Builder { 171 const env_map = try allocator.create(EnvMap); 172 env_map.* = try process.getEnvMap(allocator); 173 174 const host = try NativeTargetInfo.detect(allocator, .{}); 175 176 const self = try allocator.create(Builder); 177 self.* = Builder{ 178 .zig_exe = zig_exe, 179 .build_root = build_root, 180 .cache_root = try fs.path.relative(allocator, build_root, cache_root), 181 .global_cache_root = global_cache_root, 182 .verbose = false, 183 .verbose_link = false, 184 .verbose_cc = false, 185 .verbose_air = false, 186 .verbose_llvm_ir = false, 187 .verbose_cimport = false, 188 .verbose_llvm_cpu_features = false, 189 .prominent_compile_errors = false, 190 .invalid_user_input = false, 191 .allocator = allocator, 192 .user_input_options = UserInputOptionsMap.init(allocator), 193 .available_options_map = AvailableOptionsMap.init(allocator), 194 .available_options_list = ArrayList(AvailableOption).init(allocator), 195 .top_level_steps = ArrayList(*TopLevelStep).init(allocator), 196 .default_step = undefined, 197 .env_map = env_map, 198 .search_prefixes = ArrayList([]const u8).init(allocator), 199 .install_prefix = undefined, 200 .lib_dir = undefined, 201 .exe_dir = undefined, 202 .h_dir = undefined, 203 .dest_dir = env_map.get("DESTDIR"), 204 .installed_files = ArrayList(InstalledFile).init(allocator), 205 .install_tls = TopLevelStep{ 206 .step = Step.initNoOp(.top_level, "install", allocator), 207 .description = "Copy build artifacts to prefix path", 208 }, 209 .uninstall_tls = TopLevelStep{ 210 .step = Step.init(.top_level, "uninstall", allocator, makeUninstall), 211 .description = "Remove build artifacts from prefix path", 212 }, 213 .release_mode = null, 214 .is_release = false, 215 .override_lib_dir = null, 216 .install_path = undefined, 217 .vcpkg_root = VcpkgRoot{ .unattempted = {} }, 218 .args = null, 219 .host = host, 220 }; 221 try self.top_level_steps.append(&self.install_tls); 222 try self.top_level_steps.append(&self.uninstall_tls); 223 self.default_step = &self.install_tls.step; 224 return self; 225 } 226 227 pub fn destroy(self: *Builder) void { 228 self.env_map.deinit(); 229 self.top_level_steps.deinit(); 230 self.allocator.destroy(self); 231 } 232 233 /// This function is intended to be called by lib/build_runner.zig, not a build.zig file. 234 pub fn resolveInstallPrefix(self: *Builder, install_prefix: ?[]const u8, dir_list: DirList) void { 235 if (self.dest_dir) |dest_dir| { 236 self.install_prefix = install_prefix orelse "/usr"; 237 self.install_path = self.pathJoin(&.{ dest_dir, self.install_prefix }); 238 } else { 239 self.install_prefix = install_prefix orelse 240 (self.pathJoin(&.{ self.build_root, "zig-out" })); 241 self.install_path = self.install_prefix; 242 } 243 244 var lib_list = [_][]const u8{ self.install_path, "lib" }; 245 var exe_list = [_][]const u8{ self.install_path, "bin" }; 246 var h_list = [_][]const u8{ self.install_path, "include" }; 247 248 if (dir_list.lib_dir) |dir| { 249 if (std.fs.path.isAbsolute(dir)) lib_list[0] = self.dest_dir orelse ""; 250 lib_list[1] = dir; 251 } 252 253 if (dir_list.exe_dir) |dir| { 254 if (std.fs.path.isAbsolute(dir)) exe_list[0] = self.dest_dir orelse ""; 255 exe_list[1] = dir; 256 } 257 258 if (dir_list.include_dir) |dir| { 259 if (std.fs.path.isAbsolute(dir)) h_list[0] = self.dest_dir orelse ""; 260 h_list[1] = dir; 261 } 262 263 self.lib_dir = self.pathJoin(&lib_list); 264 self.exe_dir = self.pathJoin(&exe_list); 265 self.h_dir = self.pathJoin(&h_list); 266 } 267 268 fn convertOptionalPathToFileSource(path: ?[]const u8) ?FileSource { 269 return if (path) |p| 270 FileSource{ .path = p } 271 else 272 null; 273 } 274 275 pub fn addExecutable(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { 276 return addExecutableSource(self, name, convertOptionalPathToFileSource(root_src)); 277 } 278 279 pub fn addExecutableSource(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep { 280 return LibExeObjStep.createExecutable(builder, name, root_src); 281 } 282 283 pub fn addOptions(self: *Builder) *OptionsStep { 284 return OptionsStep.create(self); 285 } 286 287 pub fn addObject(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { 288 return addObjectSource(self, name, convertOptionalPathToFileSource(root_src)); 289 } 290 291 pub fn addObjectSource(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep { 292 return LibExeObjStep.createObject(builder, name, root_src); 293 } 294 295 pub fn addSharedLibrary( 296 self: *Builder, 297 name: []const u8, 298 root_src: ?[]const u8, 299 kind: LibExeObjStep.SharedLibKind, 300 ) *LibExeObjStep { 301 return addSharedLibrarySource(self, name, convertOptionalPathToFileSource(root_src), kind); 302 } 303 304 pub fn addSharedLibrarySource( 305 self: *Builder, 306 name: []const u8, 307 root_src: ?FileSource, 308 kind: LibExeObjStep.SharedLibKind, 309 ) *LibExeObjStep { 310 return LibExeObjStep.createSharedLibrary(self, name, root_src, kind); 311 } 312 313 pub fn addStaticLibrary(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { 314 return addStaticLibrarySource(self, name, convertOptionalPathToFileSource(root_src)); 315 } 316 317 pub fn addStaticLibrarySource(self: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep { 318 return LibExeObjStep.createStaticLibrary(self, name, root_src); 319 } 320 321 pub fn addTest(self: *Builder, root_src: []const u8) *LibExeObjStep { 322 return LibExeObjStep.createTest(self, "test", .{ .path = root_src }); 323 } 324 325 pub fn addTestSource(self: *Builder, root_src: FileSource) *LibExeObjStep { 326 return LibExeObjStep.createTest(self, "test", root_src.dupe(self)); 327 } 328 329 pub fn addTestExe(self: *Builder, name: []const u8, root_src: []const u8) *LibExeObjStep { 330 return LibExeObjStep.createTestExe(self, name, .{ .path = root_src }); 331 } 332 333 pub fn addTestExeSource(self: *Builder, name: []const u8, root_src: FileSource) *LibExeObjStep { 334 return LibExeObjStep.createTestExe(self, name, root_src.dupe(self)); 335 } 336 337 pub fn addAssemble(self: *Builder, name: []const u8, src: []const u8) *LibExeObjStep { 338 return addAssembleSource(self, name, .{ .path = src }); 339 } 340 341 pub fn addAssembleSource(self: *Builder, name: []const u8, src: FileSource) *LibExeObjStep { 342 const obj_step = LibExeObjStep.createObject(self, name, null); 343 obj_step.addAssemblyFileSource(src.dupe(self)); 344 return obj_step; 345 } 346 347 /// Initializes a RunStep with argv, which must at least have the path to the 348 /// executable. More command line arguments can be added with `addArg`, 349 /// `addArgs`, and `addArtifactArg`. 350 /// Be careful using this function, as it introduces a system dependency. 351 /// To run an executable built with zig build, see `LibExeObjStep.run`. 352 pub fn addSystemCommand(self: *Builder, argv: []const []const u8) *RunStep { 353 assert(argv.len >= 1); 354 const run_step = RunStep.create(self, self.fmt("run {s}", .{argv[0]})); 355 run_step.addArgs(argv); 356 return run_step; 357 } 358 359 /// Allocator.dupe without the need to handle out of memory. 360 pub fn dupe(self: *Builder, bytes: []const u8) []u8 { 361 return self.allocator.dupe(u8, bytes) catch unreachable; 362 } 363 364 /// Duplicates an array of strings without the need to handle out of memory. 365 pub fn dupeStrings(self: *Builder, strings: []const []const u8) [][]u8 { 366 const array = self.allocator.alloc([]u8, strings.len) catch unreachable; 367 for (strings) |s, i| { 368 array[i] = self.dupe(s); 369 } 370 return array; 371 } 372 373 /// Duplicates a path and converts all slashes to the OS's canonical path separator. 374 pub fn dupePath(self: *Builder, bytes: []const u8) []u8 { 375 const the_copy = self.dupe(bytes); 376 for (the_copy) |*byte| { 377 switch (byte.*) { 378 '/', '\\' => byte.* = fs.path.sep, 379 else => {}, 380 } 381 } 382 return the_copy; 383 } 384 385 /// Duplicates a package recursively. 386 pub fn dupePkg(self: *Builder, package: Pkg) Pkg { 387 var the_copy = Pkg{ 388 .name = self.dupe(package.name), 389 .source = package.source.dupe(self), 390 }; 391 392 if (package.dependencies) |dependencies| { 393 const new_dependencies = self.allocator.alloc(Pkg, dependencies.len) catch unreachable; 394 the_copy.dependencies = new_dependencies; 395 396 for (dependencies) |dep_package, i| { 397 new_dependencies[i] = self.dupePkg(dep_package); 398 } 399 } 400 return the_copy; 401 } 402 403 pub fn addWriteFile(self: *Builder, file_path: []const u8, data: []const u8) *WriteFileStep { 404 const write_file_step = self.addWriteFiles(); 405 write_file_step.add(file_path, data); 406 return write_file_step; 407 } 408 409 pub fn addWriteFiles(self: *Builder) *WriteFileStep { 410 const write_file_step = self.allocator.create(WriteFileStep) catch unreachable; 411 write_file_step.* = WriteFileStep.init(self); 412 return write_file_step; 413 } 414 415 pub fn addLog(self: *Builder, comptime format: []const u8, args: anytype) *LogStep { 416 const data = self.fmt(format, args); 417 const log_step = self.allocator.create(LogStep) catch unreachable; 418 log_step.* = LogStep.init(self, data); 419 return log_step; 420 } 421 422 pub fn addRemoveDirTree(self: *Builder, dir_path: []const u8) *RemoveDirStep { 423 const remove_dir_step = self.allocator.create(RemoveDirStep) catch unreachable; 424 remove_dir_step.* = RemoveDirStep.init(self, dir_path); 425 return remove_dir_step; 426 } 427 428 pub fn addFmt(self: *Builder, paths: []const []const u8) *FmtStep { 429 return FmtStep.create(self, paths); 430 } 431 432 pub fn addTranslateC(self: *Builder, source: FileSource) *TranslateCStep { 433 return TranslateCStep.create(self, source.dupe(self)); 434 } 435 436 pub fn version(self: *const Builder, major: u32, minor: u32, patch: u32) LibExeObjStep.SharedLibKind { 437 _ = self; 438 return .{ 439 .versioned = .{ 440 .major = major, 441 .minor = minor, 442 .patch = patch, 443 }, 444 }; 445 } 446 447 pub fn make(self: *Builder, step_names: []const []const u8) !void { 448 try self.makePath(self.cache_root); 449 450 var wanted_steps = ArrayList(*Step).init(self.allocator); 451 defer wanted_steps.deinit(); 452 453 if (step_names.len == 0) { 454 try wanted_steps.append(self.default_step); 455 } else { 456 for (step_names) |step_name| { 457 const s = try self.getTopLevelStepByName(step_name); 458 try wanted_steps.append(s); 459 } 460 } 461 462 for (wanted_steps.items) |s| { 463 try self.makeOneStep(s); 464 } 465 } 466 467 pub fn getInstallStep(self: *Builder) *Step { 468 return &self.install_tls.step; 469 } 470 471 pub fn getUninstallStep(self: *Builder) *Step { 472 return &self.uninstall_tls.step; 473 } 474 475 fn makeUninstall(uninstall_step: *Step) anyerror!void { 476 const uninstall_tls = @fieldParentPtr(TopLevelStep, "step", uninstall_step); 477 const self = @fieldParentPtr(Builder, "uninstall_tls", uninstall_tls); 478 479 for (self.installed_files.items) |installed_file| { 480 const full_path = self.getInstallPath(installed_file.dir, installed_file.path); 481 if (self.verbose) { 482 log.info("rm {s}", .{full_path}); 483 } 484 fs.cwd().deleteTree(full_path) catch {}; 485 } 486 487 // TODO remove empty directories 488 } 489 490 fn makeOneStep(self: *Builder, s: *Step) anyerror!void { 491 if (s.loop_flag) { 492 log.err("Dependency loop detected:\n {s}", .{s.name}); 493 return error.DependencyLoopDetected; 494 } 495 s.loop_flag = true; 496 497 for (s.dependencies.items) |dep| { 498 self.makeOneStep(dep) catch |err| { 499 if (err == error.DependencyLoopDetected) { 500 log.err(" {s}", .{s.name}); 501 } 502 return err; 503 }; 504 } 505 506 s.loop_flag = false; 507 508 try s.make(); 509 } 510 511 fn getTopLevelStepByName(self: *Builder, name: []const u8) !*Step { 512 for (self.top_level_steps.items) |top_level_step| { 513 if (mem.eql(u8, top_level_step.step.name, name)) { 514 return &top_level_step.step; 515 } 516 } 517 log.err("Cannot run step '{s}' because it does not exist", .{name}); 518 return error.InvalidStepName; 519 } 520 521 pub fn option(self: *Builder, comptime T: type, name_raw: []const u8, description_raw: []const u8) ?T { 522 const name = self.dupe(name_raw); 523 const description = self.dupe(description_raw); 524 const type_id = comptime typeToEnum(T); 525 const enum_options = if (type_id == .@"enum") blk: { 526 const fields = comptime std.meta.fields(T); 527 var options = ArrayList([]const u8).initCapacity(self.allocator, fields.len) catch unreachable; 528 529 inline for (fields) |field| { 530 options.appendAssumeCapacity(field.name); 531 } 532 533 break :blk options.toOwnedSlice(); 534 } else null; 535 const available_option = AvailableOption{ 536 .name = name, 537 .type_id = type_id, 538 .description = description, 539 .enum_options = enum_options, 540 }; 541 if ((self.available_options_map.fetchPut(name, available_option) catch unreachable) != null) { 542 panic("Option '{s}' declared twice", .{name}); 543 } 544 self.available_options_list.append(available_option) catch unreachable; 545 546 const option_ptr = self.user_input_options.getPtr(name) orelse return null; 547 option_ptr.used = true; 548 switch (type_id) { 549 .bool => switch (option_ptr.value) { 550 .flag => return true, 551 .scalar => |s| { 552 if (mem.eql(u8, s, "true")) { 553 return true; 554 } else if (mem.eql(u8, s, "false")) { 555 return false; 556 } else { 557 log.err("Expected -D{s} to be a boolean, but received '{s}'\n", .{ name, s }); 558 self.markInvalidUserInput(); 559 return null; 560 } 561 }, 562 .list => { 563 log.err("Expected -D{s} to be a boolean, but received a list.\n", .{name}); 564 self.markInvalidUserInput(); 565 return null; 566 }, 567 }, 568 .int => switch (option_ptr.value) { 569 .flag => { 570 log.err("Expected -D{s} to be an integer, but received a boolean.\n", .{name}); 571 self.markInvalidUserInput(); 572 return null; 573 }, 574 .scalar => |s| { 575 const n = std.fmt.parseInt(T, s, 10) catch |err| switch (err) { 576 error.Overflow => { 577 log.err("-D{s} value {s} cannot fit into type {s}.\n", .{ name, s, @typeName(T) }); 578 self.markInvalidUserInput(); 579 return null; 580 }, 581 else => { 582 log.err("Expected -D{s} to be an integer of type {s}.\n", .{ name, @typeName(T) }); 583 self.markInvalidUserInput(); 584 return null; 585 }, 586 }; 587 return n; 588 }, 589 .list => { 590 log.err("Expected -D{s} to be an integer, but received a list.\n", .{name}); 591 self.markInvalidUserInput(); 592 return null; 593 }, 594 }, 595 .float => switch (option_ptr.value) { 596 .flag => { 597 log.err("Expected -D{s} to be a float, but received a boolean.\n", .{name}); 598 self.markInvalidUserInput(); 599 return null; 600 }, 601 .scalar => |s| { 602 const n = std.fmt.parseFloat(T, s) catch { 603 log.err("Expected -D{s} to be a float of type {s}.\n", .{ name, @typeName(T) }); 604 self.markInvalidUserInput(); 605 return null; 606 }; 607 return n; 608 }, 609 .list => { 610 log.err("Expected -D{s} to be a float, but received a list.\n", .{name}); 611 self.markInvalidUserInput(); 612 return null; 613 }, 614 }, 615 .@"enum" => switch (option_ptr.value) { 616 .flag => { 617 log.err("Expected -D{s} to be a string, but received a boolean.\n", .{name}); 618 self.markInvalidUserInput(); 619 return null; 620 }, 621 .scalar => |s| { 622 if (std.meta.stringToEnum(T, s)) |enum_lit| { 623 return enum_lit; 624 } else { 625 log.err("Expected -D{s} to be of type {s}.\n", .{ name, @typeName(T) }); 626 self.markInvalidUserInput(); 627 return null; 628 } 629 }, 630 .list => { 631 log.err("Expected -D{s} to be a string, but received a list.\n", .{name}); 632 self.markInvalidUserInput(); 633 return null; 634 }, 635 }, 636 .string => switch (option_ptr.value) { 637 .flag => { 638 log.err("Expected -D{s} to be a string, but received a boolean.\n", .{name}); 639 self.markInvalidUserInput(); 640 return null; 641 }, 642 .list => { 643 log.err("Expected -D{s} to be a string, but received a list.\n", .{name}); 644 self.markInvalidUserInput(); 645 return null; 646 }, 647 .scalar => |s| return s, 648 }, 649 .list => switch (option_ptr.value) { 650 .flag => { 651 log.err("Expected -D{s} to be a list, but received a boolean.\n", .{name}); 652 self.markInvalidUserInput(); 653 return null; 654 }, 655 .scalar => |s| { 656 return self.allocator.dupe([]const u8, &[_][]const u8{s}) catch unreachable; 657 }, 658 .list => |lst| return lst.items, 659 }, 660 } 661 } 662 663 pub fn step(self: *Builder, name: []const u8, description: []const u8) *Step { 664 const step_info = self.allocator.create(TopLevelStep) catch unreachable; 665 step_info.* = TopLevelStep{ 666 .step = Step.initNoOp(.top_level, name, self.allocator), 667 .description = self.dupe(description), 668 }; 669 self.top_level_steps.append(step_info) catch unreachable; 670 return &step_info.step; 671 } 672 673 /// This provides the -Drelease option to the build user and does not give them the choice. 674 pub fn setPreferredReleaseMode(self: *Builder, mode: std.builtin.Mode) void { 675 if (self.release_mode != null) { 676 @panic("setPreferredReleaseMode must be called before standardReleaseOptions and may not be called twice"); 677 } 678 const description = self.fmt("Create a release build ({s})", .{@tagName(mode)}); 679 self.is_release = self.option(bool, "release", description) orelse false; 680 self.release_mode = if (self.is_release) mode else std.builtin.Mode.Debug; 681 } 682 683 /// If you call this without first calling `setPreferredReleaseMode` then it gives the build user 684 /// the choice of what kind of release. 685 pub fn standardReleaseOptions(self: *Builder) std.builtin.Mode { 686 if (self.release_mode) |mode| return mode; 687 688 const release_safe = self.option(bool, "release-safe", "Optimizations on and safety on") orelse false; 689 const release_fast = self.option(bool, "release-fast", "Optimizations on and safety off") orelse false; 690 const release_small = self.option(bool, "release-small", "Size optimizations on and safety off") orelse false; 691 692 const mode = if (release_safe and !release_fast and !release_small) 693 std.builtin.Mode.ReleaseSafe 694 else if (release_fast and !release_safe and !release_small) 695 std.builtin.Mode.ReleaseFast 696 else if (release_small and !release_fast and !release_safe) 697 std.builtin.Mode.ReleaseSmall 698 else if (!release_fast and !release_safe and !release_small) 699 std.builtin.Mode.Debug 700 else x: { 701 log.err("Multiple release modes (of -Drelease-safe, -Drelease-fast and -Drelease-small)\n", .{}); 702 self.markInvalidUserInput(); 703 break :x std.builtin.Mode.Debug; 704 }; 705 self.is_release = mode != .Debug; 706 self.release_mode = mode; 707 return mode; 708 } 709 710 pub const StandardTargetOptionsArgs = struct { 711 whitelist: ?[]const CrossTarget = null, 712 713 default_target: CrossTarget = CrossTarget{}, 714 }; 715 716 /// Exposes standard `zig build` options for choosing a target. 717 pub fn standardTargetOptions(self: *Builder, args: StandardTargetOptionsArgs) CrossTarget { 718 const maybe_triple = self.option( 719 []const u8, 720 "target", 721 "The CPU architecture, OS, and ABI to build for", 722 ); 723 const mcpu = self.option([]const u8, "cpu", "Target CPU features to add or subtract"); 724 725 if (maybe_triple == null and mcpu == null) { 726 return args.default_target; 727 } 728 729 const triple = maybe_triple orelse "native"; 730 731 var diags: CrossTarget.ParseOptions.Diagnostics = .{}; 732 const selected_target = CrossTarget.parse(.{ 733 .arch_os_abi = triple, 734 .cpu_features = mcpu, 735 .diagnostics = &diags, 736 }) catch |err| switch (err) { 737 error.UnknownCpuModel => { 738 log.err("Unknown CPU: '{s}'\nAvailable CPUs for architecture '{s}':", .{ 739 diags.cpu_name.?, 740 @tagName(diags.arch.?), 741 }); 742 for (diags.arch.?.allCpuModels()) |cpu| { 743 log.err(" {s}", .{cpu.name}); 744 } 745 self.markInvalidUserInput(); 746 return args.default_target; 747 }, 748 error.UnknownCpuFeature => { 749 log.err( 750 \\Unknown CPU feature: '{s}' 751 \\Available CPU features for architecture '{s}': 752 \\ 753 , .{ 754 diags.unknown_feature_name.?, 755 @tagName(diags.arch.?), 756 }); 757 for (diags.arch.?.allFeaturesList()) |feature| { 758 log.err(" {s}: {s}", .{ feature.name, feature.description }); 759 } 760 self.markInvalidUserInput(); 761 return args.default_target; 762 }, 763 error.UnknownOperatingSystem => { 764 log.err( 765 \\Unknown OS: '{s}' 766 \\Available operating systems: 767 \\ 768 , .{diags.os_name.?}); 769 inline for (std.meta.fields(std.Target.Os.Tag)) |field| { 770 log.err(" {s}", .{field.name}); 771 } 772 self.markInvalidUserInput(); 773 return args.default_target; 774 }, 775 else => |e| { 776 log.err("Unable to parse target '{s}': {s}\n", .{ triple, @errorName(e) }); 777 self.markInvalidUserInput(); 778 return args.default_target; 779 }, 780 }; 781 782 const selected_canonicalized_triple = selected_target.zigTriple(self.allocator) catch unreachable; 783 784 if (args.whitelist) |list| whitelist_check: { 785 // Make sure it's a match of one of the list. 786 var mismatch_triple = true; 787 var mismatch_cpu_features = true; 788 var whitelist_item = CrossTarget{}; 789 for (list) |t| { 790 mismatch_cpu_features = true; 791 mismatch_triple = true; 792 793 const t_triple = t.zigTriple(self.allocator) catch unreachable; 794 if (mem.eql(u8, t_triple, selected_canonicalized_triple)) { 795 mismatch_triple = false; 796 whitelist_item = t; 797 if (t.getCpuFeatures().isSuperSetOf(selected_target.getCpuFeatures())) { 798 mismatch_cpu_features = false; 799 break :whitelist_check; 800 } else { 801 break; 802 } 803 } 804 } 805 if (mismatch_triple) { 806 log.err("Chosen target '{s}' does not match one of the supported targets:", .{ 807 selected_canonicalized_triple, 808 }); 809 for (list) |t| { 810 const t_triple = t.zigTriple(self.allocator) catch unreachable; 811 log.err(" {s}", .{t_triple}); 812 } 813 } else { 814 assert(mismatch_cpu_features); 815 const whitelist_cpu = whitelist_item.getCpu(); 816 const selected_cpu = selected_target.getCpu(); 817 log.err("Chosen CPU model '{s}' does not match one of the supported targets:", .{ 818 selected_cpu.model.name, 819 }); 820 log.err(" Supported feature Set: ", .{}); 821 const all_features = whitelist_cpu.arch.allFeaturesList(); 822 var populated_cpu_features = whitelist_cpu.model.features; 823 populated_cpu_features.populateDependencies(all_features); 824 for (all_features) |feature, i_usize| { 825 const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize); 826 const in_cpu_set = populated_cpu_features.isEnabled(i); 827 if (in_cpu_set) { 828 log.err("{s} ", .{feature.name}); 829 } 830 } 831 log.err(" Remove: ", .{}); 832 for (all_features) |feature, i_usize| { 833 const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize); 834 const in_cpu_set = populated_cpu_features.isEnabled(i); 835 const in_actual_set = selected_cpu.features.isEnabled(i); 836 if (in_actual_set and !in_cpu_set) { 837 log.err("{s} ", .{feature.name}); 838 } 839 } 840 } 841 self.markInvalidUserInput(); 842 return args.default_target; 843 } 844 845 return selected_target; 846 } 847 848 pub fn addUserInputOption(self: *Builder, name_raw: []const u8, value_raw: []const u8) !bool { 849 const name = self.dupe(name_raw); 850 const value = self.dupe(value_raw); 851 const gop = try self.user_input_options.getOrPut(name); 852 if (!gop.found_existing) { 853 gop.value_ptr.* = UserInputOption{ 854 .name = name, 855 .value = .{ .scalar = value }, 856 .used = false, 857 }; 858 return false; 859 } 860 861 // option already exists 862 switch (gop.value_ptr.value) { 863 .scalar => |s| { 864 // turn it into a list 865 var list = ArrayList([]const u8).init(self.allocator); 866 list.append(s) catch unreachable; 867 list.append(value) catch unreachable; 868 self.user_input_options.put(name, .{ 869 .name = name, 870 .value = .{ .list = list }, 871 .used = false, 872 }) catch unreachable; 873 }, 874 .list => |*list| { 875 // append to the list 876 list.append(value) catch unreachable; 877 self.user_input_options.put(name, .{ 878 .name = name, 879 .value = .{ .list = list.* }, 880 .used = false, 881 }) catch unreachable; 882 }, 883 .flag => { 884 log.warn("Option '-D{s}={s}' conflicts with flag '-D{s}'.", .{ name, value, name }); 885 return true; 886 }, 887 } 888 return false; 889 } 890 891 pub fn addUserInputFlag(self: *Builder, name_raw: []const u8) !bool { 892 const name = self.dupe(name_raw); 893 const gop = try self.user_input_options.getOrPut(name); 894 if (!gop.found_existing) { 895 gop.value_ptr.* = .{ 896 .name = name, 897 .value = .{ .flag = {} }, 898 .used = false, 899 }; 900 return false; 901 } 902 903 // option already exists 904 switch (gop.value_ptr.value) { 905 .scalar => |s| { 906 log.err("Flag '-D{s}' conflicts with option '-D{s}={s}'.", .{ name, name, s }); 907 return true; 908 }, 909 .list => { 910 log.err("Flag '-D{s}' conflicts with multiple options of the same name.", .{name}); 911 return true; 912 }, 913 .flag => {}, 914 } 915 return false; 916 } 917 918 fn typeToEnum(comptime T: type) TypeId { 919 return switch (@typeInfo(T)) { 920 .Int => .int, 921 .Float => .float, 922 .Bool => .bool, 923 .Enum => .@"enum", 924 else => switch (T) { 925 []const u8 => .string, 926 []const []const u8 => .list, 927 else => @compileError("Unsupported type: " ++ @typeName(T)), 928 }, 929 }; 930 } 931 932 fn markInvalidUserInput(self: *Builder) void { 933 self.invalid_user_input = true; 934 } 935 936 pub fn validateUserInputDidItFail(self: *Builder) bool { 937 // make sure all args are used 938 var it = self.user_input_options.iterator(); 939 while (it.next()) |entry| { 940 if (!entry.value_ptr.used) { 941 log.err("Invalid option: -D{s}\n", .{entry.key_ptr.*}); 942 self.markInvalidUserInput(); 943 } 944 } 945 946 return self.invalid_user_input; 947 } 948 949 pub fn spawnChild(self: *Builder, argv: []const []const u8) !void { 950 return self.spawnChildEnvMap(null, self.env_map, argv); 951 } 952 953 fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void { 954 if (cwd) |yes_cwd| std.debug.print("cd {s} && ", .{yes_cwd}); 955 for (argv) |arg| { 956 std.debug.print("{s} ", .{arg}); 957 } 958 std.debug.print("\n", .{}); 959 } 960 961 pub fn spawnChildEnvMap(self: *Builder, cwd: ?[]const u8, env_map: *const EnvMap, argv: []const []const u8) !void { 962 if (self.verbose) { 963 printCmd(cwd, argv); 964 } 965 966 if (!std.process.can_spawn) 967 return error.ExecNotSupported; 968 969 var child = std.ChildProcess.init(argv, self.allocator); 970 child.cwd = cwd; 971 child.env_map = env_map; 972 973 const term = child.spawnAndWait() catch |err| { 974 log.err("Unable to spawn {s}: {s}", .{ argv[0], @errorName(err) }); 975 return err; 976 }; 977 978 switch (term) { 979 .Exited => |code| { 980 if (code != 0) { 981 log.err("The following command exited with error code {}:", .{code}); 982 printCmd(cwd, argv); 983 return error.UncleanExit; 984 } 985 }, 986 else => { 987 log.err("The following command terminated unexpectedly:", .{}); 988 printCmd(cwd, argv); 989 990 return error.UncleanExit; 991 }, 992 } 993 } 994 995 pub fn makePath(self: *Builder, path: []const u8) !void { 996 fs.cwd().makePath(self.pathFromRoot(path)) catch |err| { 997 log.err("Unable to create path {s}: {s}", .{ path, @errorName(err) }); 998 return err; 999 }; 1000 } 1001 1002 pub fn installArtifact(self: *Builder, artifact: *LibExeObjStep) void { 1003 self.getInstallStep().dependOn(&self.addInstallArtifact(artifact).step); 1004 } 1005 1006 pub fn addInstallArtifact(self: *Builder, artifact: *LibExeObjStep) *InstallArtifactStep { 1007 return InstallArtifactStep.create(self, artifact); 1008 } 1009 1010 ///`dest_rel_path` is relative to prefix path 1011 pub fn installFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) void { 1012 self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .prefix, dest_rel_path).step); 1013 } 1014 1015 pub fn installDirectory(self: *Builder, options: InstallDirectoryOptions) void { 1016 self.getInstallStep().dependOn(&self.addInstallDirectory(options).step); 1017 } 1018 1019 ///`dest_rel_path` is relative to bin path 1020 pub fn installBinFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) void { 1021 self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .bin, dest_rel_path).step); 1022 } 1023 1024 ///`dest_rel_path` is relative to lib path 1025 pub fn installLibFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) void { 1026 self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .lib, dest_rel_path).step); 1027 } 1028 1029 /// Output format (BIN vs Intel HEX) determined by filename 1030 pub fn installRaw(self: *Builder, artifact: *LibExeObjStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep { 1031 const raw = self.addInstallRaw(artifact, dest_filename, options); 1032 self.getInstallStep().dependOn(&raw.step); 1033 return raw; 1034 } 1035 1036 ///`dest_rel_path` is relative to install prefix path 1037 pub fn addInstallFile(self: *Builder, source: FileSource, dest_rel_path: []const u8) *InstallFileStep { 1038 return self.addInstallFileWithDir(source.dupe(self), .prefix, dest_rel_path); 1039 } 1040 1041 ///`dest_rel_path` is relative to bin path 1042 pub fn addInstallBinFile(self: *Builder, source: FileSource, dest_rel_path: []const u8) *InstallFileStep { 1043 return self.addInstallFileWithDir(source.dupe(self), .bin, dest_rel_path); 1044 } 1045 1046 ///`dest_rel_path` is relative to lib path 1047 pub fn addInstallLibFile(self: *Builder, source: FileSource, dest_rel_path: []const u8) *InstallFileStep { 1048 return self.addInstallFileWithDir(source.dupe(self), .lib, dest_rel_path); 1049 } 1050 1051 pub fn addInstallRaw(self: *Builder, artifact: *LibExeObjStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep { 1052 return InstallRawStep.create(self, artifact, dest_filename, options); 1053 } 1054 1055 pub fn addInstallFileWithDir( 1056 self: *Builder, 1057 source: FileSource, 1058 install_dir: InstallDir, 1059 dest_rel_path: []const u8, 1060 ) *InstallFileStep { 1061 if (dest_rel_path.len == 0) { 1062 panic("dest_rel_path must be non-empty", .{}); 1063 } 1064 const install_step = self.allocator.create(InstallFileStep) catch unreachable; 1065 install_step.* = InstallFileStep.init(self, source.dupe(self), install_dir, dest_rel_path); 1066 return install_step; 1067 } 1068 1069 pub fn addInstallDirectory(self: *Builder, options: InstallDirectoryOptions) *InstallDirStep { 1070 const install_step = self.allocator.create(InstallDirStep) catch unreachable; 1071 install_step.* = InstallDirStep.init(self, options); 1072 return install_step; 1073 } 1074 1075 pub fn pushInstalledFile(self: *Builder, dir: InstallDir, dest_rel_path: []const u8) void { 1076 const file = InstalledFile{ 1077 .dir = dir, 1078 .path = dest_rel_path, 1079 }; 1080 self.installed_files.append(file.dupe(self)) catch unreachable; 1081 } 1082 1083 pub fn updateFile(self: *Builder, source_path: []const u8, dest_path: []const u8) !void { 1084 if (self.verbose) { 1085 log.info("cp {s} {s} ", .{ source_path, dest_path }); 1086 } 1087 const cwd = fs.cwd(); 1088 const prev_status = try fs.Dir.updateFile(cwd, source_path, cwd, dest_path, .{}); 1089 if (self.verbose) switch (prev_status) { 1090 .stale => log.info("# installed", .{}), 1091 .fresh => log.info("# up-to-date", .{}), 1092 }; 1093 } 1094 1095 pub fn truncateFile(self: *Builder, dest_path: []const u8) !void { 1096 if (self.verbose) { 1097 log.info("truncate {s}", .{dest_path}); 1098 } 1099 const cwd = fs.cwd(); 1100 var src_file = cwd.createFile(dest_path, .{}) catch |err| switch (err) { 1101 error.FileNotFound => blk: { 1102 if (fs.path.dirname(dest_path)) |dirname| { 1103 try cwd.makePath(dirname); 1104 } 1105 break :blk try cwd.createFile(dest_path, .{}); 1106 }, 1107 else => |e| return e, 1108 }; 1109 src_file.close(); 1110 } 1111 1112 pub fn pathFromRoot(self: *Builder, rel_path: []const u8) []u8 { 1113 return fs.path.resolve(self.allocator, &[_][]const u8{ self.build_root, rel_path }) catch unreachable; 1114 } 1115 1116 /// Shorthand for `std.fs.path.join(builder.allocator, paths) catch unreachable` 1117 pub fn pathJoin(self: *Builder, paths: []const []const u8) []u8 { 1118 return fs.path.join(self.allocator, paths) catch unreachable; 1119 } 1120 1121 pub fn fmt(self: *Builder, comptime format: []const u8, args: anytype) []u8 { 1122 return fmt_lib.allocPrint(self.allocator, format, args) catch unreachable; 1123 } 1124 1125 pub fn findProgram(self: *Builder, names: []const []const u8, paths: []const []const u8) ![]const u8 { 1126 // TODO report error for ambiguous situations 1127 const exe_extension = @as(CrossTarget, .{}).exeFileExt(); 1128 for (self.search_prefixes.items) |search_prefix| { 1129 for (names) |name| { 1130 if (fs.path.isAbsolute(name)) { 1131 return name; 1132 } 1133 const full_path = self.pathJoin(&.{ 1134 search_prefix, 1135 "bin", 1136 self.fmt("{s}{s}", .{ name, exe_extension }), 1137 }); 1138 return fs.realpathAlloc(self.allocator, full_path) catch continue; 1139 } 1140 } 1141 if (self.env_map.get("PATH")) |PATH| { 1142 for (names) |name| { 1143 if (fs.path.isAbsolute(name)) { 1144 return name; 1145 } 1146 var it = mem.tokenize(u8, PATH, &[_]u8{fs.path.delimiter}); 1147 while (it.next()) |path| { 1148 const full_path = self.pathJoin(&.{ 1149 path, 1150 self.fmt("{s}{s}", .{ name, exe_extension }), 1151 }); 1152 return fs.realpathAlloc(self.allocator, full_path) catch continue; 1153 } 1154 } 1155 } 1156 for (names) |name| { 1157 if (fs.path.isAbsolute(name)) { 1158 return name; 1159 } 1160 for (paths) |path| { 1161 const full_path = self.pathJoin(&.{ 1162 path, 1163 self.fmt("{s}{s}", .{ name, exe_extension }), 1164 }); 1165 return fs.realpathAlloc(self.allocator, full_path) catch continue; 1166 } 1167 } 1168 return error.FileNotFound; 1169 } 1170 1171 pub fn execAllowFail( 1172 self: *Builder, 1173 argv: []const []const u8, 1174 out_code: *u8, 1175 stderr_behavior: std.ChildProcess.StdIo, 1176 ) ExecError![]u8 { 1177 assert(argv.len != 0); 1178 1179 if (!std.process.can_spawn) 1180 return error.ExecNotSupported; 1181 1182 const max_output_size = 400 * 1024; 1183 var child = std.ChildProcess.init(argv, self.allocator); 1184 child.stdin_behavior = .Ignore; 1185 child.stdout_behavior = .Pipe; 1186 child.stderr_behavior = stderr_behavior; 1187 child.env_map = self.env_map; 1188 1189 try child.spawn(); 1190 1191 const stdout = child.stdout.?.reader().readAllAlloc(self.allocator, max_output_size) catch { 1192 return error.ReadFailure; 1193 }; 1194 errdefer self.allocator.free(stdout); 1195 1196 const term = try child.wait(); 1197 switch (term) { 1198 .Exited => |code| { 1199 if (code != 0) { 1200 out_code.* = @truncate(u8, code); 1201 return error.ExitCodeFailure; 1202 } 1203 return stdout; 1204 }, 1205 .Signal, .Stopped, .Unknown => |code| { 1206 out_code.* = @truncate(u8, code); 1207 return error.ProcessTerminated; 1208 }, 1209 } 1210 } 1211 1212 pub fn execFromStep(self: *Builder, argv: []const []const u8, src_step: ?*Step) ![]u8 { 1213 assert(argv.len != 0); 1214 1215 if (self.verbose) { 1216 printCmd(null, argv); 1217 } 1218 1219 if (!std.process.can_spawn) { 1220 if (src_step) |s| log.err("{s}...", .{s.name}); 1221 log.err("Unable to spawn the following command: cannot spawn child process", .{}); 1222 printCmd(null, argv); 1223 std.os.abort(); 1224 } 1225 1226 var code: u8 = undefined; 1227 return self.execAllowFail(argv, &code, .Inherit) catch |err| switch (err) { 1228 error.ExecNotSupported => { 1229 if (src_step) |s| log.err("{s}...", .{s.name}); 1230 log.err("Unable to spawn the following command: cannot spawn child process", .{}); 1231 printCmd(null, argv); 1232 std.os.abort(); 1233 }, 1234 error.FileNotFound => { 1235 if (src_step) |s| log.err("{s}...", .{s.name}); 1236 log.err("Unable to spawn the following command: file not found", .{}); 1237 printCmd(null, argv); 1238 std.os.exit(@truncate(u8, code)); 1239 }, 1240 error.ExitCodeFailure => { 1241 if (src_step) |s| log.err("{s}...", .{s.name}); 1242 if (self.prominent_compile_errors) { 1243 log.err("The step exited with error code {d}", .{code}); 1244 } else { 1245 log.err("The following command exited with error code {d}:", .{code}); 1246 printCmd(null, argv); 1247 } 1248 1249 std.os.exit(@truncate(u8, code)); 1250 }, 1251 error.ProcessTerminated => { 1252 if (src_step) |s| log.err("{s}...", .{s.name}); 1253 log.err("The following command terminated unexpectedly:", .{}); 1254 printCmd(null, argv); 1255 std.os.exit(@truncate(u8, code)); 1256 }, 1257 else => |e| return e, 1258 }; 1259 } 1260 1261 pub fn exec(self: *Builder, argv: []const []const u8) ![]u8 { 1262 return self.execFromStep(argv, null); 1263 } 1264 1265 pub fn addSearchPrefix(self: *Builder, search_prefix: []const u8) void { 1266 self.search_prefixes.append(self.dupePath(search_prefix)) catch unreachable; 1267 } 1268 1269 pub fn getInstallPath(self: *Builder, dir: InstallDir, dest_rel_path: []const u8) []const u8 { 1270 assert(!fs.path.isAbsolute(dest_rel_path)); // Install paths must be relative to the prefix 1271 const base_dir = switch (dir) { 1272 .prefix => self.install_path, 1273 .bin => self.exe_dir, 1274 .lib => self.lib_dir, 1275 .header => self.h_dir, 1276 .custom => |path| self.pathJoin(&.{ self.install_path, path }), 1277 }; 1278 return fs.path.resolve( 1279 self.allocator, 1280 &[_][]const u8{ base_dir, dest_rel_path }, 1281 ) catch unreachable; 1282 } 1283 1284 fn execPkgConfigList(self: *Builder, out_code: *u8) (PkgConfigError || ExecError)![]const PkgConfigPkg { 1285 const stdout = try self.execAllowFail(&[_][]const u8{ "pkg-config", "--list-all" }, out_code, .Ignore); 1286 var list = ArrayList(PkgConfigPkg).init(self.allocator); 1287 errdefer list.deinit(); 1288 var line_it = mem.tokenize(u8, stdout, "\r\n"); 1289 while (line_it.next()) |line| { 1290 if (mem.trim(u8, line, " \t").len == 0) continue; 1291 var tok_it = mem.tokenize(u8, line, " \t"); 1292 try list.append(PkgConfigPkg{ 1293 .name = tok_it.next() orelse return error.PkgConfigInvalidOutput, 1294 .desc = tok_it.rest(), 1295 }); 1296 } 1297 return list.toOwnedSlice(); 1298 } 1299 1300 fn getPkgConfigList(self: *Builder) ![]const PkgConfigPkg { 1301 if (self.pkg_config_pkg_list) |res| { 1302 return res; 1303 } 1304 var code: u8 = undefined; 1305 if (self.execPkgConfigList(&code)) |list| { 1306 self.pkg_config_pkg_list = list; 1307 return list; 1308 } else |err| { 1309 const result = switch (err) { 1310 error.ProcessTerminated => error.PkgConfigCrashed, 1311 error.ExecNotSupported => error.PkgConfigFailed, 1312 error.ExitCodeFailure => error.PkgConfigFailed, 1313 error.FileNotFound => error.PkgConfigNotInstalled, 1314 error.InvalidName => error.PkgConfigNotInstalled, 1315 error.PkgConfigInvalidOutput => error.PkgConfigInvalidOutput, 1316 error.ChildExecFailed => error.PkgConfigFailed, 1317 else => return err, 1318 }; 1319 self.pkg_config_pkg_list = result; 1320 return result; 1321 } 1322 } 1323 }; 1324 1325 test "builder.findProgram compiles" { 1326 if (builtin.os.tag == .wasi) return error.SkipZigTest; 1327 1328 var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); 1329 defer arena.deinit(); 1330 1331 const builder = try Builder.create( 1332 arena.allocator(), 1333 "zig", 1334 "zig-cache", 1335 "zig-cache", 1336 "zig-cache", 1337 ); 1338 defer builder.destroy(); 1339 _ = builder.findProgram(&[_][]const u8{}, &[_][]const u8{}) catch null; 1340 } 1341 1342 /// TODO: propose some kind of `@deprecate` builtin so that we can deprecate 1343 /// this while still having somewhat non-lazy decls. In this file we wanted to do 1344 /// refAllDecls for example which makes it trigger `@compileError` if you try 1345 /// to use that strategy. 1346 pub const Version = @compileError("deprecated; Use `std.builtin.Version`"); 1347 pub const Target = @compileError("deprecated; Use `std.zig.CrossTarget`"); 1348 1349 pub const Pkg = struct { 1350 name: []const u8, 1351 source: FileSource, 1352 dependencies: ?[]const Pkg = null, 1353 }; 1354 1355 pub const CSourceFile = struct { 1356 source: FileSource, 1357 args: []const []const u8, 1358 1359 fn dupe(self: CSourceFile, b: *Builder) CSourceFile { 1360 return .{ 1361 .source = self.source.dupe(b), 1362 .args = b.dupeStrings(self.args), 1363 }; 1364 } 1365 }; 1366 1367 const CSourceFiles = struct { 1368 files: []const []const u8, 1369 flags: []const []const u8, 1370 }; 1371 1372 fn isLibCLibrary(name: []const u8) bool { 1373 const libc_libraries = [_][]const u8{ "c", "m", "dl", "rt", "pthread" }; 1374 for (libc_libraries) |libc_lib_name| { 1375 if (mem.eql(u8, name, libc_lib_name)) 1376 return true; 1377 } 1378 return false; 1379 } 1380 1381 fn isLibCppLibrary(name: []const u8) bool { 1382 const libcpp_libraries = [_][]const u8{ "c++", "stdc++" }; 1383 for (libcpp_libraries) |libcpp_lib_name| { 1384 if (mem.eql(u8, name, libcpp_lib_name)) 1385 return true; 1386 } 1387 return false; 1388 } 1389 1390 /// A file that is generated by a build step. 1391 /// This struct is an interface that is meant to be used with `@fieldParentPtr` to implement the actual path logic. 1392 pub const GeneratedFile = struct { 1393 /// The step that generates the file 1394 step: *Step, 1395 1396 /// The path to the generated file. Must be either absolute or relative to the build root. 1397 /// This value must be set in the `fn make()` of the `step` and must not be `null` afterwards. 1398 path: ?[]const u8 = null, 1399 1400 pub fn getPath(self: GeneratedFile) []const u8 { 1401 return self.path orelse std.debug.panic( 1402 "getPath() was called on a GeneratedFile that wasn't build yet. Is there a missing Step dependency on step '{s}'?", 1403 .{self.step.name}, 1404 ); 1405 } 1406 }; 1407 1408 /// A file source is a reference to an existing or future file. 1409 /// 1410 pub const FileSource = union(enum) { 1411 /// A plain file path, relative to build root or absolute. 1412 path: []const u8, 1413 1414 /// A file that is generated by an interface. Those files usually are 1415 /// not available until built by a build step. 1416 generated: *const GeneratedFile, 1417 1418 /// Returns a new file source that will have a relative path to the build root guaranteed. 1419 /// This should be preferred over setting `.path` directly as it documents that the files are in the project directory. 1420 pub fn relative(path: []const u8) FileSource { 1421 std.debug.assert(!std.fs.path.isAbsolute(path)); 1422 return FileSource{ .path = path }; 1423 } 1424 1425 /// Returns a string that can be shown to represent the file source. 1426 /// Either returns the path or `"generated"`. 1427 pub fn getDisplayName(self: FileSource) []const u8 { 1428 return switch (self) { 1429 .path => self.path, 1430 .generated => "generated", 1431 }; 1432 } 1433 1434 /// Adds dependencies this file source implies to the given step. 1435 pub fn addStepDependencies(self: FileSource, step: *Step) void { 1436 switch (self) { 1437 .path => {}, 1438 .generated => |gen| step.dependOn(gen.step), 1439 } 1440 } 1441 1442 /// Should only be called during make(), returns a path relative to the build root or absolute. 1443 pub fn getPath(self: FileSource, builder: *Builder) []const u8 { 1444 const path = switch (self) { 1445 .path => |p| builder.pathFromRoot(p), 1446 .generated => |gen| gen.getPath(), 1447 }; 1448 return path; 1449 } 1450 1451 /// Duplicates the file source for a given builder. 1452 pub fn dupe(self: FileSource, b: *Builder) FileSource { 1453 return switch (self) { 1454 .path => |p| .{ .path = b.dupePath(p) }, 1455 .generated => |gen| .{ .generated = gen }, 1456 }; 1457 } 1458 }; 1459 1460 pub const LibExeObjStep = struct { 1461 pub const base_id = .lib_exe_obj; 1462 1463 step: Step, 1464 builder: *Builder, 1465 name: []const u8, 1466 target: CrossTarget = CrossTarget{}, 1467 target_info: NativeTargetInfo, 1468 linker_script: ?FileSource = null, 1469 version_script: ?[]const u8 = null, 1470 out_filename: []const u8, 1471 linkage: ?Linkage = null, 1472 version: ?std.builtin.Version, 1473 build_mode: std.builtin.Mode, 1474 kind: Kind, 1475 major_only_filename: ?[]const u8, 1476 name_only_filename: ?[]const u8, 1477 strip: bool, 1478 // keep in sync with src/link.zig:CompressDebugSections 1479 compress_debug_sections: enum { none, zlib } = .none, 1480 lib_paths: ArrayList([]const u8), 1481 rpaths: ArrayList([]const u8), 1482 framework_dirs: ArrayList([]const u8), 1483 frameworks: StringHashMap(FrameworkLinkInfo), 1484 verbose_link: bool, 1485 verbose_cc: bool, 1486 emit_analysis: EmitOption = .default, 1487 emit_asm: EmitOption = .default, 1488 emit_bin: EmitOption = .default, 1489 emit_docs: EmitOption = .default, 1490 emit_implib: EmitOption = .default, 1491 emit_llvm_bc: EmitOption = .default, 1492 emit_llvm_ir: EmitOption = .default, 1493 // Lots of things depend on emit_h having a consistent path, 1494 // so it is not an EmitOption for now. 1495 emit_h: bool = false, 1496 bundle_compiler_rt: ?bool = null, 1497 single_threaded: ?bool = null, 1498 disable_stack_probing: bool, 1499 disable_sanitize_c: bool, 1500 sanitize_thread: bool, 1501 rdynamic: bool, 1502 import_memory: bool = false, 1503 import_table: bool = false, 1504 export_table: bool = false, 1505 initial_memory: ?u64 = null, 1506 max_memory: ?u64 = null, 1507 shared_memory: bool = false, 1508 global_base: ?u64 = null, 1509 c_std: Builder.CStd, 1510 override_lib_dir: ?[]const u8, 1511 main_pkg_path: ?[]const u8, 1512 exec_cmd_args: ?[]const ?[]const u8, 1513 name_prefix: []const u8, 1514 filter: ?[]const u8, 1515 test_evented_io: bool = false, 1516 code_model: std.builtin.CodeModel = .default, 1517 wasi_exec_model: ?std.builtin.WasiExecModel = null, 1518 /// Symbols to be exported when compiling to wasm 1519 export_symbol_names: []const []const u8 = &.{}, 1520 1521 root_src: ?FileSource, 1522 out_h_filename: []const u8, 1523 out_lib_filename: []const u8, 1524 out_pdb_filename: []const u8, 1525 packages: ArrayList(Pkg), 1526 1527 object_src: []const u8, 1528 1529 link_objects: ArrayList(LinkObject), 1530 include_dirs: ArrayList(IncludeDir), 1531 c_macros: ArrayList([]const u8), 1532 output_dir: ?[]const u8, 1533 is_linking_libc: bool = false, 1534 is_linking_libcpp: bool = false, 1535 vcpkg_bin_path: ?[]const u8 = null, 1536 1537 /// This may be set in order to override the default install directory 1538 override_dest_dir: ?InstallDir, 1539 installed_path: ?[]const u8, 1540 install_step: ?*InstallArtifactStep, 1541 1542 /// Base address for an executable image. 1543 image_base: ?u64 = null, 1544 1545 libc_file: ?FileSource = null, 1546 1547 valgrind_support: ?bool = null, 1548 each_lib_rpath: ?bool = null, 1549 /// On ELF targets, this will emit a link section called ".note.gnu.build-id" 1550 /// which can be used to coordinate a stripped binary with its debug symbols. 1551 /// As an example, the bloaty project refuses to work unless its inputs have 1552 /// build ids, in order to prevent accidental mismatches. 1553 /// The default is to not include this section because it slows down linking. 1554 build_id: ?bool = null, 1555 1556 /// Create a .eh_frame_hdr section and a PT_GNU_EH_FRAME segment in the ELF 1557 /// file. 1558 link_eh_frame_hdr: bool = false, 1559 link_emit_relocs: bool = false, 1560 1561 /// Place every function in its own section so that unused ones may be 1562 /// safely garbage-collected during the linking phase. 1563 link_function_sections: bool = false, 1564 1565 /// Remove functions and data that are unreachable by the entry point or 1566 /// exported symbols. 1567 link_gc_sections: ?bool = null, 1568 1569 linker_allow_shlib_undefined: ?bool = null, 1570 1571 /// Permit read-only relocations in read-only segments. Disallowed by default. 1572 link_z_notext: bool = false, 1573 1574 /// Force all relocations to be read-only after processing. 1575 link_z_relro: bool = true, 1576 1577 /// Allow relocations to be lazily processed after load. 1578 link_z_lazy: bool = false, 1579 1580 /// (Darwin) Install name for the dylib 1581 install_name: ?[]const u8 = null, 1582 1583 /// (Darwin) Path to entitlements file 1584 entitlements: ?[]const u8 = null, 1585 1586 /// (Darwin) Size of the pagezero segment. 1587 pagezero_size: ?u64 = null, 1588 1589 /// (Darwin) Search strategy for searching system libraries. Either `paths_first` or `dylibs_first`. 1590 /// The former lowers to `-search_paths_first` linker option, while the latter to `-search_dylibs_first` 1591 /// option. 1592 /// By default, if no option is specified, the linker assumes `paths_first` as the default 1593 /// search strategy. 1594 search_strategy: ?enum { paths_first, dylibs_first } = null, 1595 1596 /// (Darwin) Set size of the padding between the end of load commands 1597 /// and start of `__TEXT,__text` section. 1598 headerpad_size: ?u32 = null, 1599 1600 /// (Darwin) Automatically Set size of the padding between the end of load commands 1601 /// and start of `__TEXT,__text` section to a value fitting all paths expanded to MAXPATHLEN. 1602 headerpad_max_install_names: bool = false, 1603 1604 /// (Darwin) Remove dylibs that are unreachable by the entry point or exported symbols. 1605 dead_strip_dylibs: bool = false, 1606 1607 /// Position Independent Code 1608 force_pic: ?bool = null, 1609 1610 /// Position Independent Executable 1611 pie: ?bool = null, 1612 1613 red_zone: ?bool = null, 1614 1615 omit_frame_pointer: ?bool = null, 1616 dll_export_fns: ?bool = null, 1617 1618 subsystem: ?std.Target.SubSystem = null, 1619 1620 entry_symbol_name: ?[]const u8 = null, 1621 1622 /// Overrides the default stack size 1623 stack_size: ?u64 = null, 1624 1625 want_lto: ?bool = null, 1626 use_stage1: ?bool = null, 1627 use_llvm: ?bool = null, 1628 use_lld: ?bool = null, 1629 ofmt: ?std.Target.ObjectFormat = null, 1630 1631 output_path_source: GeneratedFile, 1632 output_lib_path_source: GeneratedFile, 1633 output_h_path_source: GeneratedFile, 1634 output_pdb_path_source: GeneratedFile, 1635 1636 pub const LinkObject = union(enum) { 1637 static_path: FileSource, 1638 other_step: *LibExeObjStep, 1639 system_lib: SystemLib, 1640 assembly_file: FileSource, 1641 c_source_file: *CSourceFile, 1642 c_source_files: *CSourceFiles, 1643 }; 1644 1645 pub const SystemLib = struct { 1646 name: []const u8, 1647 needed: bool, 1648 weak: bool, 1649 use_pkg_config: enum { 1650 /// Don't use pkg-config, just pass -lfoo where foo is name. 1651 no, 1652 /// Try to get information on how to link the library from pkg-config. 1653 /// If that fails, fall back to passing -lfoo where foo is name. 1654 yes, 1655 /// Try to get information on how to link the library from pkg-config. 1656 /// If that fails, error out. 1657 force, 1658 }, 1659 }; 1660 1661 const FrameworkLinkInfo = struct { 1662 needed: bool = false, 1663 weak: bool = false, 1664 }; 1665 1666 pub const IncludeDir = union(enum) { 1667 raw_path: []const u8, 1668 raw_path_system: []const u8, 1669 other_step: *LibExeObjStep, 1670 }; 1671 1672 pub const Kind = enum { 1673 exe, 1674 lib, 1675 obj, 1676 @"test", 1677 test_exe, 1678 }; 1679 1680 pub const SharedLibKind = union(enum) { 1681 versioned: std.builtin.Version, 1682 unversioned: void, 1683 }; 1684 1685 pub const Linkage = enum { dynamic, static }; 1686 1687 pub const EmitOption = union(enum) { 1688 default: void, 1689 no_emit: void, 1690 emit: void, 1691 emit_to: []const u8, 1692 1693 fn getArg(self: @This(), b: *Builder, arg_name: []const u8) ?[]const u8 { 1694 return switch (self) { 1695 .no_emit => b.fmt("-fno-{s}", .{arg_name}), 1696 .default => null, 1697 .emit => b.fmt("-f{s}", .{arg_name}), 1698 .emit_to => |path| b.fmt("-f{s}={s}", .{ arg_name, path }), 1699 }; 1700 } 1701 }; 1702 1703 pub fn createSharedLibrary(builder: *Builder, name: []const u8, root_src: ?FileSource, kind: SharedLibKind) *LibExeObjStep { 1704 return initExtraArgs(builder, name, root_src, .lib, .dynamic, switch (kind) { 1705 .versioned => |ver| ver, 1706 .unversioned => null, 1707 }); 1708 } 1709 1710 pub fn createStaticLibrary(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep { 1711 return initExtraArgs(builder, name, root_src, .lib, .static, null); 1712 } 1713 1714 pub fn createObject(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep { 1715 return initExtraArgs(builder, name, root_src, .obj, null, null); 1716 } 1717 1718 pub fn createExecutable(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep { 1719 return initExtraArgs(builder, name, root_src, .exe, null, null); 1720 } 1721 1722 pub fn createTest(builder: *Builder, name: []const u8, root_src: FileSource) *LibExeObjStep { 1723 return initExtraArgs(builder, name, root_src, .@"test", null, null); 1724 } 1725 1726 pub fn createTestExe(builder: *Builder, name: []const u8, root_src: FileSource) *LibExeObjStep { 1727 return initExtraArgs(builder, name, root_src, .test_exe, null, null); 1728 } 1729 1730 fn initExtraArgs( 1731 builder: *Builder, 1732 name_raw: []const u8, 1733 root_src_raw: ?FileSource, 1734 kind: Kind, 1735 linkage: ?Linkage, 1736 ver: ?std.builtin.Version, 1737 ) *LibExeObjStep { 1738 const name = builder.dupe(name_raw); 1739 const root_src: ?FileSource = if (root_src_raw) |rsrc| rsrc.dupe(builder) else null; 1740 if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) { 1741 panic("invalid name: '{s}'. It looks like a file path, but it is supposed to be the library or application name.", .{name}); 1742 } 1743 1744 const self = builder.allocator.create(LibExeObjStep) catch unreachable; 1745 self.* = LibExeObjStep{ 1746 .strip = false, 1747 .builder = builder, 1748 .verbose_link = false, 1749 .verbose_cc = false, 1750 .build_mode = std.builtin.Mode.Debug, 1751 .linkage = linkage, 1752 .kind = kind, 1753 .root_src = root_src, 1754 .name = name, 1755 .frameworks = StringHashMap(FrameworkLinkInfo).init(builder.allocator), 1756 .step = Step.init(base_id, name, builder.allocator, make), 1757 .version = ver, 1758 .out_filename = undefined, 1759 .out_h_filename = builder.fmt("{s}.h", .{name}), 1760 .out_lib_filename = undefined, 1761 .out_pdb_filename = builder.fmt("{s}.pdb", .{name}), 1762 .major_only_filename = null, 1763 .name_only_filename = null, 1764 .packages = ArrayList(Pkg).init(builder.allocator), 1765 .include_dirs = ArrayList(IncludeDir).init(builder.allocator), 1766 .link_objects = ArrayList(LinkObject).init(builder.allocator), 1767 .c_macros = ArrayList([]const u8).init(builder.allocator), 1768 .lib_paths = ArrayList([]const u8).init(builder.allocator), 1769 .rpaths = ArrayList([]const u8).init(builder.allocator), 1770 .framework_dirs = ArrayList([]const u8).init(builder.allocator), 1771 .object_src = undefined, 1772 .c_std = Builder.CStd.C99, 1773 .override_lib_dir = null, 1774 .main_pkg_path = null, 1775 .exec_cmd_args = null, 1776 .name_prefix = "", 1777 .filter = null, 1778 .disable_stack_probing = false, 1779 .disable_sanitize_c = false, 1780 .sanitize_thread = false, 1781 .rdynamic = false, 1782 .output_dir = null, 1783 .override_dest_dir = null, 1784 .installed_path = null, 1785 .install_step = null, 1786 1787 .output_path_source = GeneratedFile{ .step = &self.step }, 1788 .output_lib_path_source = GeneratedFile{ .step = &self.step }, 1789 .output_h_path_source = GeneratedFile{ .step = &self.step }, 1790 .output_pdb_path_source = GeneratedFile{ .step = &self.step }, 1791 1792 .target_info = undefined, // populated in computeOutFileNames 1793 }; 1794 self.computeOutFileNames(); 1795 if (root_src) |rs| rs.addStepDependencies(&self.step); 1796 return self; 1797 } 1798 1799 fn computeOutFileNames(self: *LibExeObjStep) void { 1800 self.target_info = NativeTargetInfo.detect(self.builder.allocator, self.target) catch 1801 unreachable; 1802 1803 const target = self.target_info.target; 1804 1805 self.out_filename = std.zig.binNameAlloc(self.builder.allocator, .{ 1806 .root_name = self.name, 1807 .target = target, 1808 .output_mode = switch (self.kind) { 1809 .lib => .Lib, 1810 .obj => .Obj, 1811 .exe, .@"test", .test_exe => .Exe, 1812 }, 1813 .link_mode = if (self.linkage) |some| @as(std.builtin.LinkMode, switch (some) { 1814 .dynamic => .Dynamic, 1815 .static => .Static, 1816 }) else null, 1817 .version = self.version, 1818 }) catch unreachable; 1819 1820 if (self.kind == .lib) { 1821 if (self.linkage != null and self.linkage.? == .static) { 1822 self.out_lib_filename = self.out_filename; 1823 } else if (self.version) |version| { 1824 if (target.isDarwin()) { 1825 self.major_only_filename = self.builder.fmt("lib{s}.{d}.dylib", .{ 1826 self.name, 1827 version.major, 1828 }); 1829 self.name_only_filename = self.builder.fmt("lib{s}.dylib", .{self.name}); 1830 self.out_lib_filename = self.out_filename; 1831 } else if (target.os.tag == .windows) { 1832 self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name}); 1833 } else { 1834 self.major_only_filename = self.builder.fmt("lib{s}.so.{d}", .{ self.name, version.major }); 1835 self.name_only_filename = self.builder.fmt("lib{s}.so", .{self.name}); 1836 self.out_lib_filename = self.out_filename; 1837 } 1838 } else { 1839 if (target.isDarwin()) { 1840 self.out_lib_filename = self.out_filename; 1841 } else if (target.os.tag == .windows) { 1842 self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name}); 1843 } else { 1844 self.out_lib_filename = self.out_filename; 1845 } 1846 } 1847 if (self.output_dir != null) { 1848 self.output_lib_path_source.path = self.builder.pathJoin( 1849 &.{ self.output_dir.?, self.out_lib_filename }, 1850 ); 1851 } 1852 } 1853 } 1854 1855 pub fn setTarget(self: *LibExeObjStep, target: CrossTarget) void { 1856 self.target = target; 1857 self.computeOutFileNames(); 1858 } 1859 1860 pub fn setOutputDir(self: *LibExeObjStep, dir: []const u8) void { 1861 self.output_dir = self.builder.dupePath(dir); 1862 } 1863 1864 pub fn install(self: *LibExeObjStep) void { 1865 self.builder.installArtifact(self); 1866 } 1867 1868 pub fn installRaw(self: *LibExeObjStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep { 1869 return self.builder.installRaw(self, dest_filename, options); 1870 } 1871 1872 /// Creates a `RunStep` with an executable built with `addExecutable`. 1873 /// Add command line arguments with `addArg`. 1874 pub fn run(exe: *LibExeObjStep) *RunStep { 1875 assert(exe.kind == .exe or exe.kind == .test_exe); 1876 1877 // It doesn't have to be native. We catch that if you actually try to run it. 1878 // Consider that this is declarative; the run step may not be run unless a user 1879 // option is supplied. 1880 const run_step = RunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name})); 1881 run_step.addArtifactArg(exe); 1882 1883 if (exe.kind == .test_exe) { 1884 run_step.addArg(exe.builder.zig_exe); 1885 } 1886 1887 if (exe.vcpkg_bin_path) |path| { 1888 run_step.addPathDir(path); 1889 } 1890 1891 return run_step; 1892 } 1893 1894 /// Creates an `EmulatableRunStep` with an executable built with `addExecutable`. 1895 /// Allows running foreign binaries through emulation platforms such as Qemu or Rosetta. 1896 /// When a binary cannot be ran through emulation or the option is disabled, a warning 1897 /// will be printed and the binary will *NOT* be ran. 1898 pub fn runEmulatable(exe: *LibExeObjStep) *EmulatableRunStep { 1899 assert(exe.kind == .exe or exe.kind == .test_exe); 1900 1901 const run_step = EmulatableRunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name}), exe); 1902 if (exe.vcpkg_bin_path) |path| { 1903 RunStep.addPathDirInternal(&run_step.step, exe.builder, path); 1904 } 1905 return run_step; 1906 } 1907 1908 pub fn checkObject(self: *LibExeObjStep, obj_format: std.Target.ObjectFormat) *CheckObjectStep { 1909 return CheckObjectStep.create(self.builder, self.getOutputSource(), obj_format); 1910 } 1911 1912 pub fn setLinkerScriptPath(self: *LibExeObjStep, source: FileSource) void { 1913 self.linker_script = source.dupe(self.builder); 1914 source.addStepDependencies(&self.step); 1915 } 1916 1917 pub fn linkFramework(self: *LibExeObjStep, framework_name: []const u8) void { 1918 self.frameworks.put(self.builder.dupe(framework_name), .{}) catch unreachable; 1919 } 1920 1921 pub fn linkFrameworkNeeded(self: *LibExeObjStep, framework_name: []const u8) void { 1922 self.frameworks.put(self.builder.dupe(framework_name), .{ 1923 .needed = true, 1924 }) catch unreachable; 1925 } 1926 1927 pub fn linkFrameworkWeak(self: *LibExeObjStep, framework_name: []const u8) void { 1928 self.frameworks.put(self.builder.dupe(framework_name), .{ 1929 .weak = true, 1930 }) catch unreachable; 1931 } 1932 1933 /// Returns whether the library, executable, or object depends on a particular system library. 1934 pub fn dependsOnSystemLibrary(self: LibExeObjStep, name: []const u8) bool { 1935 if (isLibCLibrary(name)) { 1936 return self.is_linking_libc; 1937 } 1938 if (isLibCppLibrary(name)) { 1939 return self.is_linking_libcpp; 1940 } 1941 for (self.link_objects.items) |link_object| { 1942 switch (link_object) { 1943 .system_lib => |lib| if (mem.eql(u8, lib.name, name)) return true, 1944 else => continue, 1945 } 1946 } 1947 return false; 1948 } 1949 1950 pub fn linkLibrary(self: *LibExeObjStep, lib: *LibExeObjStep) void { 1951 assert(lib.kind == .lib); 1952 self.linkLibraryOrObject(lib); 1953 } 1954 1955 pub fn isDynamicLibrary(self: *LibExeObjStep) bool { 1956 return self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic; 1957 } 1958 1959 pub fn producesPdbFile(self: *LibExeObjStep) bool { 1960 if (!self.target.isWindows() and !self.target.isUefi()) return false; 1961 if (self.strip) return false; 1962 return self.isDynamicLibrary() or self.kind == .exe or self.kind == .test_exe; 1963 } 1964 1965 pub fn linkLibC(self: *LibExeObjStep) void { 1966 if (!self.is_linking_libc) { 1967 self.is_linking_libc = true; 1968 self.link_objects.append(.{ 1969 .system_lib = .{ 1970 .name = "c", 1971 .needed = false, 1972 .weak = false, 1973 .use_pkg_config = .no, 1974 }, 1975 }) catch unreachable; 1976 } 1977 } 1978 1979 pub fn linkLibCpp(self: *LibExeObjStep) void { 1980 if (!self.is_linking_libcpp) { 1981 self.is_linking_libcpp = true; 1982 self.link_objects.append(.{ 1983 .system_lib = .{ 1984 .name = "c++", 1985 .needed = false, 1986 .weak = false, 1987 .use_pkg_config = .no, 1988 }, 1989 }) catch unreachable; 1990 } 1991 } 1992 1993 /// If the value is omitted, it is set to 1. 1994 /// `name` and `value` need not live longer than the function call. 1995 pub fn defineCMacro(self: *LibExeObjStep, name: []const u8, value: ?[]const u8) void { 1996 const macro = constructCMacro(self.builder.allocator, name, value); 1997 self.c_macros.append(macro) catch unreachable; 1998 } 1999 2000 /// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1. 2001 pub fn defineCMacroRaw(self: *LibExeObjStep, name_and_value: []const u8) void { 2002 self.c_macros.append(self.builder.dupe(name_and_value)) catch unreachable; 2003 } 2004 2005 /// This one has no integration with anything, it just puts -lname on the command line. 2006 /// Prefer to use `linkSystemLibrary` instead. 2007 pub fn linkSystemLibraryName(self: *LibExeObjStep, name: []const u8) void { 2008 self.link_objects.append(.{ 2009 .system_lib = .{ 2010 .name = self.builder.dupe(name), 2011 .needed = false, 2012 .weak = false, 2013 .use_pkg_config = .no, 2014 }, 2015 }) catch unreachable; 2016 } 2017 2018 /// This one has no integration with anything, it just puts -needed-lname on the command line. 2019 /// Prefer to use `linkSystemLibraryNeeded` instead. 2020 pub fn linkSystemLibraryNeededName(self: *LibExeObjStep, name: []const u8) void { 2021 self.link_objects.append(.{ 2022 .system_lib = .{ 2023 .name = self.builder.dupe(name), 2024 .needed = true, 2025 .weak = false, 2026 .use_pkg_config = .no, 2027 }, 2028 }) catch unreachable; 2029 } 2030 2031 /// Darwin-only. This one has no integration with anything, it just puts -weak-lname on the 2032 /// command line. Prefer to use `linkSystemLibraryWeak` instead. 2033 pub fn linkSystemLibraryWeakName(self: *LibExeObjStep, name: []const u8) void { 2034 self.link_objects.append(.{ 2035 .system_lib = .{ 2036 .name = self.builder.dupe(name), 2037 .needed = false, 2038 .weak = true, 2039 .use_pkg_config = .no, 2040 }, 2041 }) catch unreachable; 2042 } 2043 2044 /// This links against a system library, exclusively using pkg-config to find the library. 2045 /// Prefer to use `linkSystemLibrary` instead. 2046 pub fn linkSystemLibraryPkgConfigOnly(self: *LibExeObjStep, lib_name: []const u8) void { 2047 self.link_objects.append(.{ 2048 .system_lib = .{ 2049 .name = self.builder.dupe(lib_name), 2050 .needed = false, 2051 .weak = false, 2052 .use_pkg_config = .force, 2053 }, 2054 }) catch unreachable; 2055 } 2056 2057 /// This links against a system library, exclusively using pkg-config to find the library. 2058 /// Prefer to use `linkSystemLibraryNeeded` instead. 2059 pub fn linkSystemLibraryNeededPkgConfigOnly(self: *LibExeObjStep, lib_name: []const u8) void { 2060 self.link_objects.append(.{ 2061 .system_lib = .{ 2062 .name = self.builder.dupe(lib_name), 2063 .needed = true, 2064 .weak = false, 2065 .use_pkg_config = .force, 2066 }, 2067 }) catch unreachable; 2068 } 2069 2070 /// Run pkg-config for the given library name and parse the output, returning the arguments 2071 /// that should be passed to zig to link the given library. 2072 fn runPkgConfig(self: *LibExeObjStep, lib_name: []const u8) ![]const []const u8 { 2073 const pkg_name = match: { 2074 // First we have to map the library name to pkg config name. Unfortunately, 2075 // there are several examples where this is not straightforward: 2076 // -lSDL2 -> pkg-config sdl2 2077 // -lgdk-3 -> pkg-config gdk-3.0 2078 // -latk-1.0 -> pkg-config atk 2079 const pkgs = try self.builder.getPkgConfigList(); 2080 2081 // Exact match means instant winner. 2082 for (pkgs) |pkg| { 2083 if (mem.eql(u8, pkg.name, lib_name)) { 2084 break :match pkg.name; 2085 } 2086 } 2087 2088 // Next we'll try ignoring case. 2089 for (pkgs) |pkg| { 2090 if (std.ascii.eqlIgnoreCase(pkg.name, lib_name)) { 2091 break :match pkg.name; 2092 } 2093 } 2094 2095 // Now try appending ".0". 2096 for (pkgs) |pkg| { 2097 if (std.ascii.indexOfIgnoreCase(pkg.name, lib_name)) |pos| { 2098 if (pos != 0) continue; 2099 if (mem.eql(u8, pkg.name[lib_name.len..], ".0")) { 2100 break :match pkg.name; 2101 } 2102 } 2103 } 2104 2105 // Trimming "-1.0". 2106 if (mem.endsWith(u8, lib_name, "-1.0")) { 2107 const trimmed_lib_name = lib_name[0 .. lib_name.len - "-1.0".len]; 2108 for (pkgs) |pkg| { 2109 if (std.ascii.eqlIgnoreCase(pkg.name, trimmed_lib_name)) { 2110 break :match pkg.name; 2111 } 2112 } 2113 } 2114 2115 return error.PackageNotFound; 2116 }; 2117 2118 var code: u8 = undefined; 2119 const stdout = if (self.builder.execAllowFail(&[_][]const u8{ 2120 "pkg-config", 2121 pkg_name, 2122 "--cflags", 2123 "--libs", 2124 }, &code, .Ignore)) |stdout| stdout else |err| switch (err) { 2125 error.ProcessTerminated => return error.PkgConfigCrashed, 2126 error.ExecNotSupported => return error.PkgConfigFailed, 2127 error.ExitCodeFailure => return error.PkgConfigFailed, 2128 error.FileNotFound => return error.PkgConfigNotInstalled, 2129 error.ChildExecFailed => return error.PkgConfigFailed, 2130 else => return err, 2131 }; 2132 2133 var zig_args = std.ArrayList([]const u8).init(self.builder.allocator); 2134 defer zig_args.deinit(); 2135 2136 var it = mem.tokenize(u8, stdout, " \r\n\t"); 2137 while (it.next()) |tok| { 2138 if (mem.eql(u8, tok, "-I")) { 2139 const dir = it.next() orelse return error.PkgConfigInvalidOutput; 2140 try zig_args.appendSlice(&[_][]const u8{ "-I", dir }); 2141 } else if (mem.startsWith(u8, tok, "-I")) { 2142 try zig_args.append(tok); 2143 } else if (mem.eql(u8, tok, "-L")) { 2144 const dir = it.next() orelse return error.PkgConfigInvalidOutput; 2145 try zig_args.appendSlice(&[_][]const u8{ "-L", dir }); 2146 } else if (mem.startsWith(u8, tok, "-L")) { 2147 try zig_args.append(tok); 2148 } else if (mem.eql(u8, tok, "-l")) { 2149 const lib = it.next() orelse return error.PkgConfigInvalidOutput; 2150 try zig_args.appendSlice(&[_][]const u8{ "-l", lib }); 2151 } else if (mem.startsWith(u8, tok, "-l")) { 2152 try zig_args.append(tok); 2153 } else if (mem.eql(u8, tok, "-D")) { 2154 const macro = it.next() orelse return error.PkgConfigInvalidOutput; 2155 try zig_args.appendSlice(&[_][]const u8{ "-D", macro }); 2156 } else if (mem.startsWith(u8, tok, "-D")) { 2157 try zig_args.append(tok); 2158 } else if (self.builder.verbose) { 2159 log.warn("Ignoring pkg-config flag '{s}'", .{tok}); 2160 } 2161 } 2162 2163 return zig_args.toOwnedSlice(); 2164 } 2165 2166 pub fn linkSystemLibrary(self: *LibExeObjStep, name: []const u8) void { 2167 self.linkSystemLibraryInner(name, .{}); 2168 } 2169 2170 pub fn linkSystemLibraryNeeded(self: *LibExeObjStep, name: []const u8) void { 2171 self.linkSystemLibraryInner(name, .{ .needed = true }); 2172 } 2173 2174 pub fn linkSystemLibraryWeak(self: *LibExeObjStep, name: []const u8) void { 2175 self.linkSystemLibraryInner(name, .{ .weak = true }); 2176 } 2177 2178 fn linkSystemLibraryInner(self: *LibExeObjStep, name: []const u8, opts: struct { 2179 needed: bool = false, 2180 weak: bool = false, 2181 }) void { 2182 if (isLibCLibrary(name)) { 2183 self.linkLibC(); 2184 return; 2185 } 2186 if (isLibCppLibrary(name)) { 2187 self.linkLibCpp(); 2188 return; 2189 } 2190 2191 self.link_objects.append(.{ 2192 .system_lib = .{ 2193 .name = self.builder.dupe(name), 2194 .needed = opts.needed, 2195 .weak = opts.weak, 2196 .use_pkg_config = .yes, 2197 }, 2198 }) catch unreachable; 2199 } 2200 2201 pub fn setNamePrefix(self: *LibExeObjStep, text: []const u8) void { 2202 assert(self.kind == .@"test" or self.kind == .test_exe); 2203 self.name_prefix = self.builder.dupe(text); 2204 } 2205 2206 pub fn setFilter(self: *LibExeObjStep, text: ?[]const u8) void { 2207 assert(self.kind == .@"test" or self.kind == .test_exe); 2208 self.filter = if (text) |t| self.builder.dupe(t) else null; 2209 } 2210 2211 /// Handy when you have many C/C++ source files and want them all to have the same flags. 2212 pub fn addCSourceFiles(self: *LibExeObjStep, files: []const []const u8, flags: []const []const u8) void { 2213 const c_source_files = self.builder.allocator.create(CSourceFiles) catch unreachable; 2214 2215 const files_copy = self.builder.dupeStrings(files); 2216 const flags_copy = self.builder.dupeStrings(flags); 2217 2218 c_source_files.* = .{ 2219 .files = files_copy, 2220 .flags = flags_copy, 2221 }; 2222 self.link_objects.append(.{ .c_source_files = c_source_files }) catch unreachable; 2223 } 2224 2225 pub fn addCSourceFile(self: *LibExeObjStep, file: []const u8, flags: []const []const u8) void { 2226 self.addCSourceFileSource(.{ 2227 .args = flags, 2228 .source = .{ .path = file }, 2229 }); 2230 } 2231 2232 pub fn addCSourceFileSource(self: *LibExeObjStep, source: CSourceFile) void { 2233 const c_source_file = self.builder.allocator.create(CSourceFile) catch unreachable; 2234 c_source_file.* = source.dupe(self.builder); 2235 self.link_objects.append(.{ .c_source_file = c_source_file }) catch unreachable; 2236 source.source.addStepDependencies(&self.step); 2237 } 2238 2239 pub fn setVerboseLink(self: *LibExeObjStep, value: bool) void { 2240 self.verbose_link = value; 2241 } 2242 2243 pub fn setVerboseCC(self: *LibExeObjStep, value: bool) void { 2244 self.verbose_cc = value; 2245 } 2246 2247 pub fn setBuildMode(self: *LibExeObjStep, mode: std.builtin.Mode) void { 2248 self.build_mode = mode; 2249 } 2250 2251 pub fn overrideZigLibDir(self: *LibExeObjStep, dir_path: []const u8) void { 2252 self.override_lib_dir = self.builder.dupePath(dir_path); 2253 } 2254 2255 pub fn setMainPkgPath(self: *LibExeObjStep, dir_path: []const u8) void { 2256 self.main_pkg_path = self.builder.dupePath(dir_path); 2257 } 2258 2259 pub fn setLibCFile(self: *LibExeObjStep, libc_file: ?FileSource) void { 2260 self.libc_file = if (libc_file) |f| f.dupe(self.builder) else null; 2261 } 2262 2263 /// Returns the generated executable, library or object file. 2264 /// To run an executable built with zig build, use `run`, or create an install step and invoke it. 2265 pub fn getOutputSource(self: *LibExeObjStep) FileSource { 2266 return FileSource{ .generated = &self.output_path_source }; 2267 } 2268 2269 /// Returns the generated import library. This function can only be called for libraries. 2270 pub fn getOutputLibSource(self: *LibExeObjStep) FileSource { 2271 assert(self.kind == .lib); 2272 return FileSource{ .generated = &self.output_lib_path_source }; 2273 } 2274 2275 /// Returns the generated header file. 2276 /// This function can only be called for libraries or object files which have `emit_h` set. 2277 pub fn getOutputHSource(self: *LibExeObjStep) FileSource { 2278 assert(self.kind != .exe and self.kind != .test_exe and self.kind != .@"test"); 2279 assert(self.emit_h); 2280 return FileSource{ .generated = &self.output_h_path_source }; 2281 } 2282 2283 /// Returns the generated PDB file. This function can only be called for Windows and UEFI. 2284 pub fn getOutputPdbSource(self: *LibExeObjStep) FileSource { 2285 // TODO: Is this right? Isn't PDB for *any* PE/COFF file? 2286 assert(self.target.isWindows() or self.target.isUefi()); 2287 return FileSource{ .generated = &self.output_pdb_path_source }; 2288 } 2289 2290 pub fn addAssemblyFile(self: *LibExeObjStep, path: []const u8) void { 2291 self.link_objects.append(.{ 2292 .assembly_file = .{ .path = self.builder.dupe(path) }, 2293 }) catch unreachable; 2294 } 2295 2296 pub fn addAssemblyFileSource(self: *LibExeObjStep, source: FileSource) void { 2297 const source_duped = source.dupe(self.builder); 2298 self.link_objects.append(.{ .assembly_file = source_duped }) catch unreachable; 2299 source_duped.addStepDependencies(&self.step); 2300 } 2301 2302 pub fn addObjectFile(self: *LibExeObjStep, source_file: []const u8) void { 2303 self.addObjectFileSource(.{ .path = source_file }); 2304 } 2305 2306 pub fn addObjectFileSource(self: *LibExeObjStep, source: FileSource) void { 2307 self.link_objects.append(.{ .static_path = source.dupe(self.builder) }) catch unreachable; 2308 source.addStepDependencies(&self.step); 2309 } 2310 2311 pub fn addObject(self: *LibExeObjStep, obj: *LibExeObjStep) void { 2312 assert(obj.kind == .obj); 2313 self.linkLibraryOrObject(obj); 2314 } 2315 2316 /// TODO deprecated, use `addSystemIncludePath`. 2317 pub fn addSystemIncludeDir(self: *LibExeObjStep, path: []const u8) void { 2318 self.addSystemIncludePath(path); 2319 } 2320 2321 pub fn addSystemIncludePath(self: *LibExeObjStep, path: []const u8) void { 2322 self.include_dirs.append(IncludeDir{ .raw_path_system = self.builder.dupe(path) }) catch unreachable; 2323 } 2324 2325 /// TODO deprecated, use `addIncludePath`. 2326 pub fn addIncludeDir(self: *LibExeObjStep, path: []const u8) void { 2327 self.addIncludePath(path); 2328 } 2329 2330 pub fn addIncludePath(self: *LibExeObjStep, path: []const u8) void { 2331 self.include_dirs.append(IncludeDir{ .raw_path = self.builder.dupe(path) }) catch unreachable; 2332 } 2333 2334 /// TODO deprecated, use `addLibraryPath`. 2335 pub fn addLibPath(self: *LibExeObjStep, path: []const u8) void { 2336 self.addLibraryPath(path); 2337 } 2338 2339 pub fn addLibraryPath(self: *LibExeObjStep, path: []const u8) void { 2340 self.lib_paths.append(self.builder.dupe(path)) catch unreachable; 2341 } 2342 2343 pub fn addRPath(self: *LibExeObjStep, path: []const u8) void { 2344 self.rpaths.append(self.builder.dupe(path)) catch unreachable; 2345 } 2346 2347 /// TODO deprecated, use `addFrameworkPath`. 2348 pub fn addFrameworkDir(self: *LibExeObjStep, dir_path: []const u8) void { 2349 self.addFrameworkPath(dir_path); 2350 } 2351 2352 pub fn addFrameworkPath(self: *LibExeObjStep, dir_path: []const u8) void { 2353 self.framework_dirs.append(self.builder.dupe(dir_path)) catch unreachable; 2354 } 2355 2356 pub fn addPackage(self: *LibExeObjStep, package: Pkg) void { 2357 self.packages.append(self.builder.dupePkg(package)) catch unreachable; 2358 self.addRecursiveBuildDeps(package); 2359 } 2360 2361 pub fn addOptions(self: *LibExeObjStep, package_name: []const u8, options: *OptionsStep) void { 2362 self.addPackage(options.getPackage(package_name)); 2363 } 2364 2365 fn addRecursiveBuildDeps(self: *LibExeObjStep, package: Pkg) void { 2366 package.source.addStepDependencies(&self.step); 2367 if (package.dependencies) |deps| { 2368 for (deps) |dep| { 2369 self.addRecursiveBuildDeps(dep); 2370 } 2371 } 2372 } 2373 2374 pub fn addPackagePath(self: *LibExeObjStep, name: []const u8, pkg_index_path: []const u8) void { 2375 self.addPackage(Pkg{ 2376 .name = self.builder.dupe(name), 2377 .source = .{ .path = self.builder.dupe(pkg_index_path) }, 2378 }); 2379 } 2380 2381 /// If Vcpkg was found on the system, it will be added to include and lib 2382 /// paths for the specified target. 2383 pub fn addVcpkgPaths(self: *LibExeObjStep, linkage: LibExeObjStep.Linkage) !void { 2384 // Ideally in the Unattempted case we would call the function recursively 2385 // after findVcpkgRoot and have only one switch statement, but the compiler 2386 // cannot resolve the error set. 2387 switch (self.builder.vcpkg_root) { 2388 .unattempted => { 2389 self.builder.vcpkg_root = if (try findVcpkgRoot(self.builder.allocator)) |root| 2390 VcpkgRoot{ .found = root } 2391 else 2392 .not_found; 2393 }, 2394 .not_found => return error.VcpkgNotFound, 2395 .found => {}, 2396 } 2397 2398 switch (self.builder.vcpkg_root) { 2399 .unattempted => unreachable, 2400 .not_found => return error.VcpkgNotFound, 2401 .found => |root| { 2402 const allocator = self.builder.allocator; 2403 const triplet = try self.target.vcpkgTriplet(allocator, if (linkage == .static) .Static else .Dynamic); 2404 defer self.builder.allocator.free(triplet); 2405 2406 const include_path = self.builder.pathJoin(&.{ root, "installed", triplet, "include" }); 2407 errdefer allocator.free(include_path); 2408 try self.include_dirs.append(IncludeDir{ .raw_path = include_path }); 2409 2410 const lib_path = self.builder.pathJoin(&.{ root, "installed", triplet, "lib" }); 2411 try self.lib_paths.append(lib_path); 2412 2413 self.vcpkg_bin_path = self.builder.pathJoin(&.{ root, "installed", triplet, "bin" }); 2414 }, 2415 } 2416 } 2417 2418 pub fn setExecCmd(self: *LibExeObjStep, args: []const ?[]const u8) void { 2419 assert(self.kind == .@"test"); 2420 const duped_args = self.builder.allocator.alloc(?[]u8, args.len) catch unreachable; 2421 for (args) |arg, i| { 2422 duped_args[i] = if (arg) |a| self.builder.dupe(a) else null; 2423 } 2424 self.exec_cmd_args = duped_args; 2425 } 2426 2427 fn linkLibraryOrObject(self: *LibExeObjStep, other: *LibExeObjStep) void { 2428 self.step.dependOn(&other.step); 2429 self.link_objects.append(.{ .other_step = other }) catch unreachable; 2430 self.include_dirs.append(.{ .other_step = other }) catch unreachable; 2431 } 2432 2433 fn makePackageCmd(self: *LibExeObjStep, pkg: Pkg, zig_args: *ArrayList([]const u8)) error{OutOfMemory}!void { 2434 const builder = self.builder; 2435 2436 try zig_args.append("--pkg-begin"); 2437 try zig_args.append(pkg.name); 2438 try zig_args.append(builder.pathFromRoot(pkg.source.getPath(self.builder))); 2439 2440 if (pkg.dependencies) |dependencies| { 2441 for (dependencies) |sub_pkg| { 2442 try self.makePackageCmd(sub_pkg, zig_args); 2443 } 2444 } 2445 2446 try zig_args.append("--pkg-end"); 2447 } 2448 2449 fn make(step: *Step) !void { 2450 const self = @fieldParentPtr(LibExeObjStep, "step", step); 2451 const builder = self.builder; 2452 2453 if (self.root_src == null and self.link_objects.items.len == 0) { 2454 log.err("{s}: linker needs 1 or more objects to link", .{self.step.name}); 2455 return error.NeedAnObject; 2456 } 2457 2458 var zig_args = ArrayList([]const u8).init(builder.allocator); 2459 defer zig_args.deinit(); 2460 2461 zig_args.append(builder.zig_exe) catch unreachable; 2462 2463 const cmd = switch (self.kind) { 2464 .lib => "build-lib", 2465 .exe => "build-exe", 2466 .obj => "build-obj", 2467 .@"test" => "test", 2468 .test_exe => "test", 2469 }; 2470 zig_args.append(cmd) catch unreachable; 2471 2472 if (builder.color != .auto) { 2473 try zig_args.append("--color"); 2474 try zig_args.append(@tagName(builder.color)); 2475 } 2476 2477 if (self.use_stage1) |stage1| { 2478 if (stage1) { 2479 try zig_args.append("-fstage1"); 2480 } else { 2481 try zig_args.append("-fno-stage1"); 2482 } 2483 } else if (builder.use_stage1) |stage1| { 2484 if (stage1) { 2485 try zig_args.append("-fstage1"); 2486 } else { 2487 try zig_args.append("-fno-stage1"); 2488 } 2489 } 2490 2491 if (self.use_llvm) |use_llvm| { 2492 if (use_llvm) { 2493 try zig_args.append("-fLLVM"); 2494 } else { 2495 try zig_args.append("-fno-LLVM"); 2496 } 2497 } 2498 2499 if (self.use_lld) |use_lld| { 2500 if (use_lld) { 2501 try zig_args.append("-fLLD"); 2502 } else { 2503 try zig_args.append("-fno-LLD"); 2504 } 2505 } 2506 2507 if (self.ofmt) |ofmt| { 2508 try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-ofmt={s}", .{@tagName(ofmt)})); 2509 } 2510 2511 if (self.entry_symbol_name) |entry| { 2512 try zig_args.append("--entry"); 2513 try zig_args.append(entry); 2514 } 2515 2516 if (self.stack_size) |stack_size| { 2517 try zig_args.append("--stack"); 2518 try zig_args.append(try std.fmt.allocPrint(builder.allocator, "{}", .{stack_size})); 2519 } 2520 2521 if (self.root_src) |root_src| try zig_args.append(root_src.getPath(builder)); 2522 2523 var prev_has_extra_flags = false; 2524 2525 // Resolve transitive dependencies 2526 { 2527 var transitive_dependencies = std.ArrayList(LinkObject).init(builder.allocator); 2528 defer transitive_dependencies.deinit(); 2529 2530 for (self.link_objects.items) |link_object| { 2531 switch (link_object) { 2532 .other_step => |other| { 2533 // Inherit dependency on system libraries 2534 for (other.link_objects.items) |other_link_object| { 2535 switch (other_link_object) { 2536 .system_lib => try transitive_dependencies.append(other_link_object), 2537 else => continue, 2538 } 2539 } 2540 2541 // Inherit dependencies on darwin frameworks 2542 if (!other.isDynamicLibrary()) { 2543 var it = other.frameworks.iterator(); 2544 while (it.next()) |framework| { 2545 self.frameworks.put(framework.key_ptr.*, framework.value_ptr.*) catch unreachable; 2546 } 2547 } 2548 }, 2549 else => continue, 2550 } 2551 } 2552 2553 try self.link_objects.appendSlice(transitive_dependencies.items); 2554 } 2555 2556 for (self.link_objects.items) |link_object| { 2557 switch (link_object) { 2558 .static_path => |static_path| try zig_args.append(static_path.getPath(builder)), 2559 2560 .other_step => |other| switch (other.kind) { 2561 .exe => @panic("Cannot link with an executable build artifact"), 2562 .test_exe => @panic("Cannot link with an executable build artifact"), 2563 .@"test" => @panic("Cannot link with a test"), 2564 .obj => { 2565 try zig_args.append(other.getOutputSource().getPath(builder)); 2566 }, 2567 .lib => { 2568 const full_path_lib = other.getOutputLibSource().getPath(builder); 2569 try zig_args.append(full_path_lib); 2570 2571 if (other.linkage != null and other.linkage.? == .dynamic and !self.target.isWindows()) { 2572 if (fs.path.dirname(full_path_lib)) |dirname| { 2573 try zig_args.append("-rpath"); 2574 try zig_args.append(dirname); 2575 } 2576 } 2577 }, 2578 }, 2579 2580 .system_lib => |system_lib| { 2581 const prefix: []const u8 = prefix: { 2582 if (system_lib.needed) break :prefix "-needed-l"; 2583 if (system_lib.weak) { 2584 if (self.target.isDarwin()) break :prefix "-weak-l"; 2585 log.warn("Weak library import used for a non-darwin target, this will be converted to normally library import `-lname`", .{}); 2586 } 2587 break :prefix "-l"; 2588 }; 2589 switch (system_lib.use_pkg_config) { 2590 .no => try zig_args.append(builder.fmt("{s}{s}", .{ prefix, system_lib.name })), 2591 .yes, .force => { 2592 if (self.runPkgConfig(system_lib.name)) |args| { 2593 try zig_args.appendSlice(args); 2594 } else |err| switch (err) { 2595 error.PkgConfigInvalidOutput, 2596 error.PkgConfigCrashed, 2597 error.PkgConfigFailed, 2598 error.PkgConfigNotInstalled, 2599 error.PackageNotFound, 2600 => switch (system_lib.use_pkg_config) { 2601 .yes => { 2602 // pkg-config failed, so fall back to linking the library 2603 // by name directly. 2604 try zig_args.append(builder.fmt("{s}{s}", .{ 2605 prefix, 2606 system_lib.name, 2607 })); 2608 }, 2609 .force => { 2610 panic("pkg-config failed for library {s}", .{system_lib.name}); 2611 }, 2612 .no => unreachable, 2613 }, 2614 2615 else => |e| return e, 2616 } 2617 }, 2618 } 2619 }, 2620 2621 .assembly_file => |asm_file| { 2622 if (prev_has_extra_flags) { 2623 try zig_args.append("-extra-cflags"); 2624 try zig_args.append("--"); 2625 prev_has_extra_flags = false; 2626 } 2627 try zig_args.append(asm_file.getPath(builder)); 2628 }, 2629 2630 .c_source_file => |c_source_file| { 2631 if (c_source_file.args.len == 0) { 2632 if (prev_has_extra_flags) { 2633 try zig_args.append("-cflags"); 2634 try zig_args.append("--"); 2635 prev_has_extra_flags = false; 2636 } 2637 } else { 2638 try zig_args.append("-cflags"); 2639 for (c_source_file.args) |arg| { 2640 try zig_args.append(arg); 2641 } 2642 try zig_args.append("--"); 2643 } 2644 try zig_args.append(c_source_file.source.getPath(builder)); 2645 }, 2646 2647 .c_source_files => |c_source_files| { 2648 if (c_source_files.flags.len == 0) { 2649 if (prev_has_extra_flags) { 2650 try zig_args.append("-cflags"); 2651 try zig_args.append("--"); 2652 prev_has_extra_flags = false; 2653 } 2654 } else { 2655 try zig_args.append("-cflags"); 2656 for (c_source_files.flags) |flag| { 2657 try zig_args.append(flag); 2658 } 2659 try zig_args.append("--"); 2660 } 2661 for (c_source_files.files) |file| { 2662 try zig_args.append(builder.pathFromRoot(file)); 2663 } 2664 }, 2665 } 2666 } 2667 2668 if (self.image_base) |image_base| { 2669 try zig_args.append("--image-base"); 2670 try zig_args.append(builder.fmt("0x{x}", .{image_base})); 2671 } 2672 2673 if (self.filter) |filter| { 2674 try zig_args.append("--test-filter"); 2675 try zig_args.append(filter); 2676 } 2677 2678 if (self.test_evented_io) { 2679 try zig_args.append("--test-evented-io"); 2680 } 2681 2682 if (self.name_prefix.len != 0) { 2683 try zig_args.append("--test-name-prefix"); 2684 try zig_args.append(self.name_prefix); 2685 } 2686 2687 for (builder.debug_log_scopes) |log_scope| { 2688 try zig_args.append("--debug-log"); 2689 try zig_args.append(log_scope); 2690 } 2691 2692 if (builder.verbose_cimport) zig_args.append("--verbose-cimport") catch unreachable; 2693 if (builder.verbose_air) zig_args.append("--verbose-air") catch unreachable; 2694 if (builder.verbose_llvm_ir) zig_args.append("--verbose-llvm-ir") catch unreachable; 2695 if (builder.verbose_link or self.verbose_link) zig_args.append("--verbose-link") catch unreachable; 2696 if (builder.verbose_cc or self.verbose_cc) zig_args.append("--verbose-cc") catch unreachable; 2697 if (builder.verbose_llvm_cpu_features) zig_args.append("--verbose-llvm-cpu-features") catch unreachable; 2698 2699 if (self.emit_analysis.getArg(builder, "emit-analysis")) |arg| try zig_args.append(arg); 2700 if (self.emit_asm.getArg(builder, "emit-asm")) |arg| try zig_args.append(arg); 2701 if (self.emit_bin.getArg(builder, "emit-bin")) |arg| try zig_args.append(arg); 2702 if (self.emit_docs.getArg(builder, "emit-docs")) |arg| try zig_args.append(arg); 2703 if (self.emit_implib.getArg(builder, "emit-implib")) |arg| try zig_args.append(arg); 2704 if (self.emit_llvm_bc.getArg(builder, "emit-llvm-bc")) |arg| try zig_args.append(arg); 2705 if (self.emit_llvm_ir.getArg(builder, "emit-llvm-ir")) |arg| try zig_args.append(arg); 2706 2707 if (self.emit_h) try zig_args.append("-femit-h"); 2708 2709 if (self.strip) { 2710 try zig_args.append("--strip"); 2711 } 2712 2713 switch (self.compress_debug_sections) { 2714 .none => {}, 2715 .zlib => try zig_args.append("--compress-debug-sections=zlib"), 2716 } 2717 2718 if (self.link_eh_frame_hdr) { 2719 try zig_args.append("--eh-frame-hdr"); 2720 } 2721 if (self.link_emit_relocs) { 2722 try zig_args.append("--emit-relocs"); 2723 } 2724 if (self.link_function_sections) { 2725 try zig_args.append("-ffunction-sections"); 2726 } 2727 if (self.link_gc_sections) |x| { 2728 try zig_args.append(if (x) "--gc-sections" else "--no-gc-sections"); 2729 } 2730 if (self.linker_allow_shlib_undefined) |x| { 2731 try zig_args.append(if (x) "-fallow-shlib-undefined" else "-fno-allow-shlib-undefined"); 2732 } 2733 if (self.link_z_notext) { 2734 try zig_args.append("-z"); 2735 try zig_args.append("notext"); 2736 } 2737 if (!self.link_z_relro) { 2738 try zig_args.append("-z"); 2739 try zig_args.append("norelro"); 2740 } 2741 if (self.link_z_lazy) { 2742 try zig_args.append("-z"); 2743 try zig_args.append("lazy"); 2744 } 2745 2746 if (self.libc_file) |libc_file| { 2747 try zig_args.append("--libc"); 2748 try zig_args.append(libc_file.getPath(self.builder)); 2749 } else if (builder.libc_file) |libc_file| { 2750 try zig_args.append("--libc"); 2751 try zig_args.append(libc_file); 2752 } 2753 2754 switch (self.build_mode) { 2755 .Debug => {}, // Skip since it's the default. 2756 else => zig_args.append(builder.fmt("-O{s}", .{@tagName(self.build_mode)})) catch unreachable, 2757 } 2758 2759 try zig_args.append("--cache-dir"); 2760 try zig_args.append(builder.pathFromRoot(builder.cache_root)); 2761 2762 try zig_args.append("--global-cache-dir"); 2763 try zig_args.append(builder.pathFromRoot(builder.global_cache_root)); 2764 2765 zig_args.append("--name") catch unreachable; 2766 zig_args.append(self.name) catch unreachable; 2767 2768 if (self.linkage) |some| switch (some) { 2769 .dynamic => try zig_args.append("-dynamic"), 2770 .static => try zig_args.append("-static"), 2771 }; 2772 if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic) { 2773 if (self.version) |version| { 2774 zig_args.append("--version") catch unreachable; 2775 zig_args.append(builder.fmt("{}", .{version})) catch unreachable; 2776 } 2777 2778 if (self.target.isDarwin()) { 2779 const install_name = self.install_name orelse builder.fmt("@rpath/{s}{s}{s}", .{ 2780 self.target.libPrefix(), 2781 self.name, 2782 self.target.dynamicLibSuffix(), 2783 }); 2784 try zig_args.append("-install_name"); 2785 try zig_args.append(install_name); 2786 } 2787 } 2788 2789 if (self.entitlements) |entitlements| { 2790 try zig_args.appendSlice(&[_][]const u8{ "--entitlements", entitlements }); 2791 } 2792 if (self.pagezero_size) |pagezero_size| { 2793 const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{pagezero_size}); 2794 try zig_args.appendSlice(&[_][]const u8{ "-pagezero_size", size }); 2795 } 2796 if (self.search_strategy) |strat| switch (strat) { 2797 .paths_first => try zig_args.append("-search_paths_first"), 2798 .dylibs_first => try zig_args.append("-search_dylibs_first"), 2799 }; 2800 if (self.headerpad_size) |headerpad_size| { 2801 const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{headerpad_size}); 2802 try zig_args.appendSlice(&[_][]const u8{ "-headerpad", size }); 2803 } 2804 if (self.headerpad_max_install_names) { 2805 try zig_args.append("-headerpad_max_install_names"); 2806 } 2807 if (self.dead_strip_dylibs) { 2808 try zig_args.append("-dead_strip_dylibs"); 2809 } 2810 2811 if (self.bundle_compiler_rt) |x| { 2812 if (x) { 2813 try zig_args.append("-fcompiler-rt"); 2814 } else { 2815 try zig_args.append("-fno-compiler-rt"); 2816 } 2817 } 2818 if (self.single_threaded) |single_threaded| { 2819 if (single_threaded) { 2820 try zig_args.append("-fsingle-threaded"); 2821 } else { 2822 try zig_args.append("-fno-single-threaded"); 2823 } 2824 } 2825 if (self.disable_stack_probing) { 2826 try zig_args.append("-fno-stack-check"); 2827 } 2828 if (self.red_zone) |red_zone| { 2829 if (red_zone) { 2830 try zig_args.append("-mred-zone"); 2831 } else { 2832 try zig_args.append("-mno-red-zone"); 2833 } 2834 } 2835 if (self.omit_frame_pointer) |omit_frame_pointer| { 2836 if (omit_frame_pointer) { 2837 try zig_args.append("-fomit-frame-pointer"); 2838 } else { 2839 try zig_args.append("-fno-omit-frame-pointer"); 2840 } 2841 } 2842 if (self.dll_export_fns) |dll_export_fns| { 2843 if (dll_export_fns) { 2844 try zig_args.append("-fdll-export-fns"); 2845 } else { 2846 try zig_args.append("-fno-dll-export-fns"); 2847 } 2848 } 2849 if (self.disable_sanitize_c) { 2850 try zig_args.append("-fno-sanitize-c"); 2851 } 2852 if (self.sanitize_thread) { 2853 try zig_args.append("-fsanitize-thread"); 2854 } 2855 if (self.rdynamic) { 2856 try zig_args.append("-rdynamic"); 2857 } 2858 if (self.import_memory) { 2859 try zig_args.append("--import-memory"); 2860 } 2861 if (self.import_table) { 2862 try zig_args.append("--import-table"); 2863 } 2864 if (self.export_table) { 2865 try zig_args.append("--export-table"); 2866 } 2867 if (self.initial_memory) |initial_memory| { 2868 try zig_args.append(builder.fmt("--initial-memory={d}", .{initial_memory})); 2869 } 2870 if (self.max_memory) |max_memory| { 2871 try zig_args.append(builder.fmt("--max-memory={d}", .{max_memory})); 2872 } 2873 if (self.shared_memory) { 2874 try zig_args.append("--shared-memory"); 2875 } 2876 if (self.global_base) |global_base| { 2877 try zig_args.append(builder.fmt("--global-base={d}", .{global_base})); 2878 } 2879 2880 if (self.code_model != .default) { 2881 try zig_args.append("-mcmodel"); 2882 try zig_args.append(@tagName(self.code_model)); 2883 } 2884 if (self.wasi_exec_model) |model| { 2885 try zig_args.append(builder.fmt("-mexec-model={s}", .{@tagName(model)})); 2886 } 2887 for (self.export_symbol_names) |symbol_name| { 2888 try zig_args.append(builder.fmt("--export={s}", .{symbol_name})); 2889 } 2890 2891 if (!self.target.isNative()) { 2892 try zig_args.append("-target"); 2893 try zig_args.append(try self.target.zigTriple(builder.allocator)); 2894 2895 // TODO this logic can disappear if cpu model + features becomes part of the target triple 2896 const cross = self.target.toTarget(); 2897 const all_features = cross.cpu.arch.allFeaturesList(); 2898 var populated_cpu_features = cross.cpu.model.features; 2899 populated_cpu_features.populateDependencies(all_features); 2900 2901 if (populated_cpu_features.eql(cross.cpu.features)) { 2902 // The CPU name alone is sufficient. 2903 try zig_args.append("-mcpu"); 2904 try zig_args.append(cross.cpu.model.name); 2905 } else { 2906 var mcpu_buffer = std.ArrayList(u8).init(builder.allocator); 2907 2908 try mcpu_buffer.writer().print("-mcpu={s}", .{cross.cpu.model.name}); 2909 2910 for (all_features) |feature, i_usize| { 2911 const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize); 2912 const in_cpu_set = populated_cpu_features.isEnabled(i); 2913 const in_actual_set = cross.cpu.features.isEnabled(i); 2914 if (in_cpu_set and !in_actual_set) { 2915 try mcpu_buffer.writer().print("-{s}", .{feature.name}); 2916 } else if (!in_cpu_set and in_actual_set) { 2917 try mcpu_buffer.writer().print("+{s}", .{feature.name}); 2918 } 2919 } 2920 2921 try zig_args.append(mcpu_buffer.toOwnedSlice()); 2922 } 2923 2924 if (self.target.dynamic_linker.get()) |dynamic_linker| { 2925 try zig_args.append("--dynamic-linker"); 2926 try zig_args.append(dynamic_linker); 2927 } 2928 } 2929 2930 if (self.linker_script) |linker_script| { 2931 try zig_args.append("--script"); 2932 try zig_args.append(linker_script.getPath(builder)); 2933 } 2934 2935 if (self.version_script) |version_script| { 2936 try zig_args.append("--version-script"); 2937 try zig_args.append(builder.pathFromRoot(version_script)); 2938 } 2939 2940 if (self.kind == .@"test") { 2941 if (self.exec_cmd_args) |exec_cmd_args| { 2942 for (exec_cmd_args) |cmd_arg| { 2943 if (cmd_arg) |arg| { 2944 try zig_args.append("--test-cmd"); 2945 try zig_args.append(arg); 2946 } else { 2947 try zig_args.append("--test-cmd-bin"); 2948 } 2949 } 2950 } else { 2951 const need_cross_glibc = self.target.isGnuLibC() and self.is_linking_libc; 2952 2953 switch (self.builder.host.getExternalExecutor(self.target_info, .{ 2954 .qemu_fixes_dl = need_cross_glibc and builder.glibc_runtimes_dir != null, 2955 .link_libc = self.is_linking_libc, 2956 })) { 2957 .native => {}, 2958 .bad_dl, .bad_os_or_cpu => { 2959 try zig_args.append("--test-no-exec"); 2960 }, 2961 .rosetta => if (builder.enable_rosetta) { 2962 try zig_args.append("--test-cmd-bin"); 2963 } else { 2964 try zig_args.append("--test-no-exec"); 2965 }, 2966 .qemu => |bin_name| ok: { 2967 if (builder.enable_qemu) qemu: { 2968 const glibc_dir_arg = if (need_cross_glibc) 2969 builder.glibc_runtimes_dir orelse break :qemu 2970 else 2971 null; 2972 try zig_args.append("--test-cmd"); 2973 try zig_args.append(bin_name); 2974 if (glibc_dir_arg) |dir| { 2975 // TODO look into making this a call to `linuxTriple`. This 2976 // needs the directory to be called "i686" rather than 2977 // "i386" which is why we do it manually here. 2978 const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}"; 2979 const cpu_arch = self.target.getCpuArch(); 2980 const os_tag = self.target.getOsTag(); 2981 const abi = self.target.getAbi(); 2982 const cpu_arch_name: []const u8 = if (cpu_arch == .i386) 2983 "i686" 2984 else 2985 @tagName(cpu_arch); 2986 const full_dir = try std.fmt.allocPrint(builder.allocator, fmt_str, .{ 2987 dir, cpu_arch_name, @tagName(os_tag), @tagName(abi), 2988 }); 2989 2990 try zig_args.append("--test-cmd"); 2991 try zig_args.append("-L"); 2992 try zig_args.append("--test-cmd"); 2993 try zig_args.append(full_dir); 2994 } 2995 try zig_args.append("--test-cmd-bin"); 2996 break :ok; 2997 } 2998 try zig_args.append("--test-no-exec"); 2999 }, 3000 .wine => |bin_name| if (builder.enable_wine) { 3001 try zig_args.append("--test-cmd"); 3002 try zig_args.append(bin_name); 3003 try zig_args.append("--test-cmd-bin"); 3004 } else { 3005 try zig_args.append("--test-no-exec"); 3006 }, 3007 .wasmtime => |bin_name| if (builder.enable_wasmtime) { 3008 try zig_args.append("--test-cmd"); 3009 try zig_args.append(bin_name); 3010 try zig_args.append("--test-cmd"); 3011 try zig_args.append("--dir=."); 3012 try zig_args.append("--test-cmd"); 3013 try zig_args.append("--allow-unknown-exports"); // TODO: Remove when stage2 is default compiler 3014 try zig_args.append("--test-cmd-bin"); 3015 } else { 3016 try zig_args.append("--test-no-exec"); 3017 }, 3018 .darling => |bin_name| if (builder.enable_darling) { 3019 try zig_args.append("--test-cmd"); 3020 try zig_args.append(bin_name); 3021 try zig_args.append("--test-cmd-bin"); 3022 } else { 3023 try zig_args.append("--test-no-exec"); 3024 }, 3025 } 3026 } 3027 } else if (self.kind == .test_exe) { 3028 try zig_args.append("--test-no-exec"); 3029 } 3030 3031 for (self.packages.items) |pkg| { 3032 try self.makePackageCmd(pkg, &zig_args); 3033 } 3034 3035 for (self.include_dirs.items) |include_dir| { 3036 switch (include_dir) { 3037 .raw_path => |include_path| { 3038 try zig_args.append("-I"); 3039 try zig_args.append(self.builder.pathFromRoot(include_path)); 3040 }, 3041 .raw_path_system => |include_path| { 3042 if (builder.sysroot != null) { 3043 try zig_args.append("-iwithsysroot"); 3044 } else { 3045 try zig_args.append("-isystem"); 3046 } 3047 3048 const resolved_include_path = self.builder.pathFromRoot(include_path); 3049 3050 const common_include_path = if (builtin.os.tag == .windows and builder.sysroot != null and fs.path.isAbsolute(resolved_include_path)) blk: { 3051 // We need to check for disk designator and strip it out from dir path so 3052 // that zig/clang can concat resolved_include_path with sysroot. 3053 const disk_designator = fs.path.diskDesignatorWindows(resolved_include_path); 3054 3055 if (mem.indexOf(u8, resolved_include_path, disk_designator)) |where| { 3056 break :blk resolved_include_path[where + disk_designator.len ..]; 3057 } 3058 3059 break :blk resolved_include_path; 3060 } else resolved_include_path; 3061 3062 try zig_args.append(common_include_path); 3063 }, 3064 .other_step => |other| if (other.emit_h) { 3065 const h_path = other.getOutputHSource().getPath(self.builder); 3066 try zig_args.append("-isystem"); 3067 try zig_args.append(fs.path.dirname(h_path).?); 3068 }, 3069 } 3070 } 3071 3072 for (self.lib_paths.items) |lib_path| { 3073 try zig_args.append("-L"); 3074 try zig_args.append(lib_path); 3075 } 3076 3077 for (self.rpaths.items) |rpath| { 3078 try zig_args.append("-rpath"); 3079 try zig_args.append(rpath); 3080 } 3081 3082 for (self.c_macros.items) |c_macro| { 3083 try zig_args.append("-D"); 3084 try zig_args.append(c_macro); 3085 } 3086 3087 if (self.target.isDarwin()) { 3088 for (self.framework_dirs.items) |dir| { 3089 if (builder.sysroot != null) { 3090 try zig_args.append("-iframeworkwithsysroot"); 3091 } else { 3092 try zig_args.append("-iframework"); 3093 } 3094 try zig_args.append(dir); 3095 try zig_args.append("-F"); 3096 try zig_args.append(dir); 3097 } 3098 3099 var it = self.frameworks.iterator(); 3100 while (it.next()) |entry| { 3101 const name = entry.key_ptr.*; 3102 const info = entry.value_ptr.*; 3103 if (info.needed) { 3104 zig_args.append("-needed_framework") catch unreachable; 3105 } else if (info.weak) { 3106 zig_args.append("-weak_framework") catch unreachable; 3107 } else { 3108 zig_args.append("-framework") catch unreachable; 3109 } 3110 zig_args.append(name) catch unreachable; 3111 } 3112 } else { 3113 if (self.framework_dirs.items.len > 0) { 3114 log.info("Framework directories have been added for a non-darwin target, this will have no affect on the build", .{}); 3115 } 3116 3117 if (self.frameworks.count() > 0) { 3118 log.info("Frameworks have been added for a non-darwin target, this will have no affect on the build", .{}); 3119 } 3120 } 3121 3122 if (builder.sysroot) |sysroot| { 3123 try zig_args.appendSlice(&[_][]const u8{ "--sysroot", sysroot }); 3124 } 3125 3126 for (builder.search_prefixes.items) |search_prefix| { 3127 try zig_args.append("-L"); 3128 try zig_args.append(builder.pathJoin(&.{ 3129 search_prefix, "lib", 3130 })); 3131 try zig_args.append("-isystem"); 3132 try zig_args.append(builder.pathJoin(&.{ 3133 search_prefix, "include", 3134 })); 3135 } 3136 3137 if (self.valgrind_support) |valgrind_support| { 3138 if (valgrind_support) { 3139 try zig_args.append("-fvalgrind"); 3140 } else { 3141 try zig_args.append("-fno-valgrind"); 3142 } 3143 } 3144 3145 if (self.each_lib_rpath) |each_lib_rpath| { 3146 if (each_lib_rpath) { 3147 try zig_args.append("-feach-lib-rpath"); 3148 } else { 3149 try zig_args.append("-fno-each-lib-rpath"); 3150 } 3151 } 3152 3153 if (self.build_id) |build_id| { 3154 if (build_id) { 3155 try zig_args.append("-fbuild-id"); 3156 } else { 3157 try zig_args.append("-fno-build-id"); 3158 } 3159 } 3160 3161 if (self.override_lib_dir) |dir| { 3162 try zig_args.append("--zig-lib-dir"); 3163 try zig_args.append(builder.pathFromRoot(dir)); 3164 } else if (self.builder.override_lib_dir) |dir| { 3165 try zig_args.append("--zig-lib-dir"); 3166 try zig_args.append(builder.pathFromRoot(dir)); 3167 } 3168 3169 if (self.main_pkg_path) |dir| { 3170 try zig_args.append("--main-pkg-path"); 3171 try zig_args.append(builder.pathFromRoot(dir)); 3172 } 3173 3174 if (self.force_pic) |pic| { 3175 if (pic) { 3176 try zig_args.append("-fPIC"); 3177 } else { 3178 try zig_args.append("-fno-PIC"); 3179 } 3180 } 3181 3182 if (self.pie) |pie| { 3183 if (pie) { 3184 try zig_args.append("-fPIE"); 3185 } else { 3186 try zig_args.append("-fno-PIE"); 3187 } 3188 } 3189 3190 if (self.want_lto) |lto| { 3191 if (lto) { 3192 try zig_args.append("-flto"); 3193 } else { 3194 try zig_args.append("-fno-lto"); 3195 } 3196 } 3197 3198 if (self.subsystem) |subsystem| { 3199 try zig_args.append("--subsystem"); 3200 try zig_args.append(switch (subsystem) { 3201 .Console => "console", 3202 .Windows => "windows", 3203 .Posix => "posix", 3204 .Native => "native", 3205 .EfiApplication => "efi_application", 3206 .EfiBootServiceDriver => "efi_boot_service_driver", 3207 .EfiRom => "efi_rom", 3208 .EfiRuntimeDriver => "efi_runtime_driver", 3209 }); 3210 } 3211 3212 try zig_args.append("--enable-cache"); 3213 3214 // Windows has an argument length limit of 32,766 characters, macOS 262,144 and Linux 3215 // 2,097,152. If our args exceed 30 KiB, we instead write them to a "response file" and 3216 // pass that to zig, e.g. via 'zig build-lib @args.rsp' 3217 // See @file syntax here: https://gcc.gnu.org/onlinedocs/gcc/Overall-Options.html 3218 var args_length: usize = 0; 3219 for (zig_args.items) |arg| { 3220 args_length += arg.len + 1; // +1 to account for null terminator 3221 } 3222 if (args_length >= 30 * 1024) { 3223 const args_dir = try fs.path.join( 3224 builder.allocator, 3225 &[_][]const u8{ builder.pathFromRoot("zig-cache"), "args" }, 3226 ); 3227 try std.fs.cwd().makePath(args_dir); 3228 3229 var args_arena = std.heap.ArenaAllocator.init(builder.allocator); 3230 defer args_arena.deinit(); 3231 3232 const args_to_escape = zig_args.items[2..]; 3233 var escaped_args = try ArrayList([]const u8).initCapacity(args_arena.allocator(), args_to_escape.len); 3234 3235 arg_blk: for (args_to_escape) |arg| { 3236 for (arg) |c, arg_idx| { 3237 if (c == '\\' or c == '"') { 3238 // Slow path for arguments that need to be escaped. We'll need to allocate and copy 3239 var escaped = try ArrayList(u8).initCapacity(args_arena.allocator(), arg.len + 1); 3240 const writer = escaped.writer(); 3241 writer.writeAll(arg[0..arg_idx]) catch unreachable; 3242 for (arg[arg_idx..]) |to_escape| { 3243 if (to_escape == '\\' or to_escape == '"') try writer.writeByte('\\'); 3244 try writer.writeByte(to_escape); 3245 } 3246 escaped_args.appendAssumeCapacity(escaped.items); 3247 continue :arg_blk; 3248 } 3249 } 3250 escaped_args.appendAssumeCapacity(arg); // no escaping needed so just use original argument 3251 } 3252 3253 // Write the args to zig-cache/args/<SHA256 hash of args> to avoid conflicts with 3254 // other zig build commands running in parallel. 3255 const partially_quoted = try std.mem.join(builder.allocator, "\" \"", escaped_args.items); 3256 const args = try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "\"", partially_quoted, "\"" }); 3257 3258 var args_hash: [Sha256.digest_length]u8 = undefined; 3259 Sha256.hash(args, &args_hash, .{}); 3260 var args_hex_hash: [Sha256.digest_length * 2]u8 = undefined; 3261 _ = try std.fmt.bufPrint( 3262 &args_hex_hash, 3263 "{s}", 3264 .{std.fmt.fmtSliceHexLower(&args_hash)}, 3265 ); 3266 3267 const args_file = try fs.path.join(builder.allocator, &[_][]const u8{ args_dir, args_hex_hash[0..] }); 3268 try std.fs.cwd().writeFile(args_file, args); 3269 3270 zig_args.shrinkRetainingCapacity(2); 3271 try zig_args.append(try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "@", args_file })); 3272 } 3273 3274 const output_dir_nl = try builder.execFromStep(zig_args.items, &self.step); 3275 const build_output_dir = mem.trimRight(u8, output_dir_nl, "\r\n"); 3276 3277 if (self.output_dir) |output_dir| { 3278 var src_dir = try std.fs.cwd().openIterableDir(build_output_dir, .{}); 3279 defer src_dir.close(); 3280 3281 // Create the output directory if it doesn't exist. 3282 try std.fs.cwd().makePath(output_dir); 3283 3284 var dest_dir = try std.fs.cwd().openDir(output_dir, .{}); 3285 defer dest_dir.close(); 3286 3287 var it = src_dir.iterate(); 3288 while (try it.next()) |entry| { 3289 // The compiler can put these files into the same directory, but we don't 3290 // want to copy them over. 3291 if (mem.eql(u8, entry.name, "stage1.id") or 3292 mem.eql(u8, entry.name, "llvm-ar.id") or 3293 mem.eql(u8, entry.name, "libs.txt") or 3294 mem.eql(u8, entry.name, "builtin.zig") or 3295 mem.eql(u8, entry.name, "zld.id") or 3296 mem.eql(u8, entry.name, "lld.id")) continue; 3297 3298 _ = try src_dir.dir.updateFile(entry.name, dest_dir, entry.name, .{}); 3299 } 3300 } else { 3301 self.output_dir = build_output_dir; 3302 } 3303 3304 // This will ensure all output filenames will now have the output_dir available! 3305 self.computeOutFileNames(); 3306 3307 // Update generated files 3308 if (self.output_dir != null) { 3309 self.output_path_source.path = builder.pathJoin( 3310 &.{ self.output_dir.?, self.out_filename }, 3311 ); 3312 3313 if (self.emit_h) { 3314 self.output_h_path_source.path = builder.pathJoin( 3315 &.{ self.output_dir.?, self.out_h_filename }, 3316 ); 3317 } 3318 3319 if (self.target.isWindows() or self.target.isUefi()) { 3320 self.output_pdb_path_source.path = builder.pathJoin( 3321 &.{ self.output_dir.?, self.out_pdb_filename }, 3322 ); 3323 } 3324 } 3325 3326 if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic and self.version != null and self.target.wantSharedLibSymLinks()) { 3327 try doAtomicSymLinks(builder.allocator, self.getOutputSource().getPath(builder), self.major_only_filename.?, self.name_only_filename.?); 3328 } 3329 } 3330 }; 3331 3332 /// Allocates a new string for assigning a value to a named macro. 3333 /// If the value is omitted, it is set to 1. 3334 /// `name` and `value` need not live longer than the function call. 3335 pub fn constructCMacro(allocator: Allocator, name: []const u8, value: ?[]const u8) []const u8 { 3336 var macro = allocator.alloc( 3337 u8, 3338 name.len + if (value) |value_slice| value_slice.len + 1 else 0, 3339 ) catch |err| if (err == error.OutOfMemory) @panic("Out of memory") else unreachable; 3340 mem.copy(u8, macro, name); 3341 if (value) |value_slice| { 3342 macro[name.len] = '='; 3343 mem.copy(u8, macro[name.len + 1 ..], value_slice); 3344 } 3345 return macro; 3346 } 3347 3348 pub const InstallArtifactStep = struct { 3349 pub const base_id = .install_artifact; 3350 3351 step: Step, 3352 builder: *Builder, 3353 artifact: *LibExeObjStep, 3354 dest_dir: InstallDir, 3355 pdb_dir: ?InstallDir, 3356 h_dir: ?InstallDir, 3357 3358 const Self = @This(); 3359 3360 pub fn create(builder: *Builder, artifact: *LibExeObjStep) *Self { 3361 if (artifact.install_step) |s| return s; 3362 3363 const self = builder.allocator.create(Self) catch unreachable; 3364 self.* = Self{ 3365 .builder = builder, 3366 .step = Step.init(.install_artifact, builder.fmt("install {s}", .{artifact.step.name}), builder.allocator, make), 3367 .artifact = artifact, 3368 .dest_dir = artifact.override_dest_dir orelse switch (artifact.kind) { 3369 .obj => @panic("Cannot install a .obj build artifact."), 3370 .@"test" => @panic("Cannot install a test build artifact, use addTestExe instead."), 3371 .exe, .test_exe => InstallDir{ .bin = {} }, 3372 .lib => InstallDir{ .lib = {} }, 3373 }, 3374 .pdb_dir = if (artifact.producesPdbFile()) blk: { 3375 if (artifact.kind == .exe or artifact.kind == .test_exe) { 3376 break :blk InstallDir{ .bin = {} }; 3377 } else { 3378 break :blk InstallDir{ .lib = {} }; 3379 } 3380 } else null, 3381 .h_dir = if (artifact.kind == .lib and artifact.emit_h) .header else null, 3382 }; 3383 self.step.dependOn(&artifact.step); 3384 artifact.install_step = self; 3385 3386 builder.pushInstalledFile(self.dest_dir, artifact.out_filename); 3387 if (self.artifact.isDynamicLibrary()) { 3388 if (artifact.major_only_filename) |name| { 3389 builder.pushInstalledFile(.lib, name); 3390 } 3391 if (artifact.name_only_filename) |name| { 3392 builder.pushInstalledFile(.lib, name); 3393 } 3394 if (self.artifact.target.isWindows()) { 3395 builder.pushInstalledFile(.lib, artifact.out_lib_filename); 3396 } 3397 } 3398 if (self.pdb_dir) |pdb_dir| { 3399 builder.pushInstalledFile(pdb_dir, artifact.out_pdb_filename); 3400 } 3401 if (self.h_dir) |h_dir| { 3402 builder.pushInstalledFile(h_dir, artifact.out_h_filename); 3403 } 3404 return self; 3405 } 3406 3407 fn make(step: *Step) !void { 3408 const self = @fieldParentPtr(Self, "step", step); 3409 const builder = self.builder; 3410 3411 const full_dest_path = builder.getInstallPath(self.dest_dir, self.artifact.out_filename); 3412 try builder.updateFile(self.artifact.getOutputSource().getPath(builder), full_dest_path); 3413 if (self.artifact.isDynamicLibrary() and self.artifact.version != null and self.artifact.target.wantSharedLibSymLinks()) { 3414 try doAtomicSymLinks(builder.allocator, full_dest_path, self.artifact.major_only_filename.?, self.artifact.name_only_filename.?); 3415 } 3416 if (self.artifact.isDynamicLibrary() and self.artifact.target.isWindows() and self.artifact.emit_implib != .no_emit) { 3417 const full_implib_path = builder.getInstallPath(self.dest_dir, self.artifact.out_lib_filename); 3418 try builder.updateFile(self.artifact.getOutputLibSource().getPath(builder), full_implib_path); 3419 } 3420 if (self.pdb_dir) |pdb_dir| { 3421 const full_pdb_path = builder.getInstallPath(pdb_dir, self.artifact.out_pdb_filename); 3422 try builder.updateFile(self.artifact.getOutputPdbSource().getPath(builder), full_pdb_path); 3423 } 3424 if (self.h_dir) |h_dir| { 3425 const full_pdb_path = builder.getInstallPath(h_dir, self.artifact.out_h_filename); 3426 try builder.updateFile(self.artifact.getOutputHSource().getPath(builder), full_pdb_path); 3427 } 3428 self.artifact.installed_path = full_dest_path; 3429 } 3430 }; 3431 3432 pub const InstallFileStep = struct { 3433 pub const base_id = .install_file; 3434 3435 step: Step, 3436 builder: *Builder, 3437 source: FileSource, 3438 dir: InstallDir, 3439 dest_rel_path: []const u8, 3440 3441 pub fn init( 3442 builder: *Builder, 3443 source: FileSource, 3444 dir: InstallDir, 3445 dest_rel_path: []const u8, 3446 ) InstallFileStep { 3447 builder.pushInstalledFile(dir, dest_rel_path); 3448 return InstallFileStep{ 3449 .builder = builder, 3450 .step = Step.init(.install_file, builder.fmt("install {s} to {s}", .{ source.getDisplayName(), dest_rel_path }), builder.allocator, make), 3451 .source = source.dupe(builder), 3452 .dir = dir.dupe(builder), 3453 .dest_rel_path = builder.dupePath(dest_rel_path), 3454 }; 3455 } 3456 3457 fn make(step: *Step) !void { 3458 const self = @fieldParentPtr(InstallFileStep, "step", step); 3459 const full_dest_path = self.builder.getInstallPath(self.dir, self.dest_rel_path); 3460 const full_src_path = self.source.getPath(self.builder); 3461 try self.builder.updateFile(full_src_path, full_dest_path); 3462 } 3463 }; 3464 3465 pub const InstallDirectoryOptions = struct { 3466 source_dir: []const u8, 3467 install_dir: InstallDir, 3468 install_subdir: []const u8, 3469 /// File paths which end in any of these suffixes will be excluded 3470 /// from being installed. 3471 exclude_extensions: []const []const u8 = &.{}, 3472 /// File paths which end in any of these suffixes will result in 3473 /// empty files being installed. This is mainly intended for large 3474 /// test.zig files in order to prevent needless installation bloat. 3475 /// However if the files were not present at all, then 3476 /// `@import("test.zig")` would be a compile error. 3477 blank_extensions: []const []const u8 = &.{}, 3478 3479 fn dupe(self: InstallDirectoryOptions, b: *Builder) InstallDirectoryOptions { 3480 return .{ 3481 .source_dir = b.dupe(self.source_dir), 3482 .install_dir = self.install_dir.dupe(b), 3483 .install_subdir = b.dupe(self.install_subdir), 3484 .exclude_extensions = b.dupeStrings(self.exclude_extensions), 3485 .blank_extensions = b.dupeStrings(self.blank_extensions), 3486 }; 3487 } 3488 }; 3489 3490 pub const InstallDirStep = struct { 3491 pub const base_id = .install_dir; 3492 3493 step: Step, 3494 builder: *Builder, 3495 options: InstallDirectoryOptions, 3496 3497 pub fn init( 3498 builder: *Builder, 3499 options: InstallDirectoryOptions, 3500 ) InstallDirStep { 3501 builder.pushInstalledFile(options.install_dir, options.install_subdir); 3502 return InstallDirStep{ 3503 .builder = builder, 3504 .step = Step.init(.install_dir, builder.fmt("install {s}/", .{options.source_dir}), builder.allocator, make), 3505 .options = options.dupe(builder), 3506 }; 3507 } 3508 3509 fn make(step: *Step) !void { 3510 const self = @fieldParentPtr(InstallDirStep, "step", step); 3511 const dest_prefix = self.builder.getInstallPath(self.options.install_dir, self.options.install_subdir); 3512 const full_src_dir = self.builder.pathFromRoot(self.options.source_dir); 3513 var src_dir = try std.fs.cwd().openIterableDir(full_src_dir, .{}); 3514 defer src_dir.close(); 3515 var it = try src_dir.walk(self.builder.allocator); 3516 next_entry: while (try it.next()) |entry| { 3517 for (self.options.exclude_extensions) |ext| { 3518 if (mem.endsWith(u8, entry.path, ext)) { 3519 continue :next_entry; 3520 } 3521 } 3522 3523 const full_path = self.builder.pathJoin(&.{ 3524 full_src_dir, entry.path, 3525 }); 3526 3527 const dest_path = self.builder.pathJoin(&.{ 3528 dest_prefix, entry.path, 3529 }); 3530 3531 switch (entry.kind) { 3532 .Directory => try fs.cwd().makePath(dest_path), 3533 .File => { 3534 for (self.options.blank_extensions) |ext| { 3535 if (mem.endsWith(u8, entry.path, ext)) { 3536 try self.builder.truncateFile(dest_path); 3537 continue :next_entry; 3538 } 3539 } 3540 3541 try self.builder.updateFile(full_path, dest_path); 3542 }, 3543 else => continue, 3544 } 3545 } 3546 } 3547 }; 3548 3549 pub const LogStep = struct { 3550 pub const base_id = .log; 3551 3552 step: Step, 3553 builder: *Builder, 3554 data: []const u8, 3555 3556 pub fn init(builder: *Builder, data: []const u8) LogStep { 3557 return LogStep{ 3558 .builder = builder, 3559 .step = Step.init(.log, builder.fmt("log {s}", .{data}), builder.allocator, make), 3560 .data = builder.dupe(data), 3561 }; 3562 } 3563 3564 fn make(step: *Step) anyerror!void { 3565 const self = @fieldParentPtr(LogStep, "step", step); 3566 log.info("{s}", .{self.data}); 3567 } 3568 }; 3569 3570 pub const RemoveDirStep = struct { 3571 pub const base_id = .remove_dir; 3572 3573 step: Step, 3574 builder: *Builder, 3575 dir_path: []const u8, 3576 3577 pub fn init(builder: *Builder, dir_path: []const u8) RemoveDirStep { 3578 return RemoveDirStep{ 3579 .builder = builder, 3580 .step = Step.init(.remove_dir, builder.fmt("RemoveDir {s}", .{dir_path}), builder.allocator, make), 3581 .dir_path = builder.dupePath(dir_path), 3582 }; 3583 } 3584 3585 fn make(step: *Step) !void { 3586 const self = @fieldParentPtr(RemoveDirStep, "step", step); 3587 3588 const full_path = self.builder.pathFromRoot(self.dir_path); 3589 fs.cwd().deleteTree(full_path) catch |err| { 3590 log.err("Unable to remove {s}: {s}", .{ full_path, @errorName(err) }); 3591 return err; 3592 }; 3593 } 3594 }; 3595 3596 const ThisModule = @This(); 3597 pub const Step = struct { 3598 id: Id, 3599 name: []const u8, 3600 makeFn: MakeFn, 3601 dependencies: ArrayList(*Step), 3602 loop_flag: bool, 3603 done_flag: bool, 3604 3605 const MakeFn = switch (builtin.zig_backend) { 3606 .stage1 => fn (self: *Step) anyerror!void, 3607 else => *const fn (self: *Step) anyerror!void, 3608 }; 3609 3610 pub const Id = enum { 3611 top_level, 3612 lib_exe_obj, 3613 install_artifact, 3614 install_file, 3615 install_dir, 3616 log, 3617 remove_dir, 3618 fmt, 3619 translate_c, 3620 write_file, 3621 run, 3622 emulatable_run, 3623 check_file, 3624 check_object, 3625 install_raw, 3626 options, 3627 custom, 3628 }; 3629 3630 pub fn init(id: Id, name: []const u8, allocator: Allocator, makeFn: MakeFn) Step { 3631 return Step{ 3632 .id = id, 3633 .name = allocator.dupe(u8, name) catch unreachable, 3634 .makeFn = makeFn, 3635 .dependencies = ArrayList(*Step).init(allocator), 3636 .loop_flag = false, 3637 .done_flag = false, 3638 }; 3639 } 3640 pub fn initNoOp(id: Id, name: []const u8, allocator: Allocator) Step { 3641 return init(id, name, allocator, makeNoOp); 3642 } 3643 3644 pub fn make(self: *Step) !void { 3645 if (self.done_flag) return; 3646 3647 try self.makeFn(self); 3648 self.done_flag = true; 3649 } 3650 3651 pub fn dependOn(self: *Step, other: *Step) void { 3652 self.dependencies.append(other) catch unreachable; 3653 } 3654 3655 fn makeNoOp(self: *Step) anyerror!void { 3656 _ = self; 3657 } 3658 3659 pub fn cast(step: *Step, comptime T: type) ?*T { 3660 if (step.id == T.base_id) { 3661 return @fieldParentPtr(T, "step", step); 3662 } 3663 return null; 3664 } 3665 }; 3666 3667 fn doAtomicSymLinks(allocator: Allocator, output_path: []const u8, filename_major_only: []const u8, filename_name_only: []const u8) !void { 3668 const out_dir = fs.path.dirname(output_path) orelse "."; 3669 const out_basename = fs.path.basename(output_path); 3670 // sym link for libfoo.so.1 to libfoo.so.1.2.3 3671 const major_only_path = fs.path.join( 3672 allocator, 3673 &[_][]const u8{ out_dir, filename_major_only }, 3674 ) catch unreachable; 3675 fs.atomicSymLink(allocator, out_basename, major_only_path) catch |err| { 3676 log.err("Unable to symlink {s} -> {s}", .{ major_only_path, out_basename }); 3677 return err; 3678 }; 3679 // sym link for libfoo.so to libfoo.so.1 3680 const name_only_path = fs.path.join( 3681 allocator, 3682 &[_][]const u8{ out_dir, filename_name_only }, 3683 ) catch unreachable; 3684 fs.atomicSymLink(allocator, filename_major_only, name_only_path) catch |err| { 3685 log.err("Unable to symlink {s} -> {s}", .{ name_only_path, filename_major_only }); 3686 return err; 3687 }; 3688 } 3689 3690 /// Returned slice must be freed by the caller. 3691 fn findVcpkgRoot(allocator: Allocator) !?[]const u8 { 3692 const appdata_path = try fs.getAppDataDir(allocator, "vcpkg"); 3693 defer allocator.free(appdata_path); 3694 3695 const path_file = try fs.path.join(allocator, &[_][]const u8{ appdata_path, "vcpkg.path.txt" }); 3696 defer allocator.free(path_file); 3697 3698 const file = fs.cwd().openFile(path_file, .{}) catch return null; 3699 defer file.close(); 3700 3701 const size = @intCast(usize, try file.getEndPos()); 3702 const vcpkg_path = try allocator.alloc(u8, size); 3703 const size_read = try file.read(vcpkg_path); 3704 std.debug.assert(size == size_read); 3705 3706 return vcpkg_path; 3707 } 3708 3709 const VcpkgRoot = union(VcpkgRootStatus) { 3710 unattempted: void, 3711 not_found: void, 3712 found: []const u8, 3713 }; 3714 3715 const VcpkgRootStatus = enum { 3716 unattempted, 3717 not_found, 3718 found, 3719 }; 3720 3721 pub const InstallDir = union(enum) { 3722 prefix: void, 3723 lib: void, 3724 bin: void, 3725 header: void, 3726 /// A path relative to the prefix 3727 custom: []const u8, 3728 3729 /// Duplicates the install directory including the path if set to custom. 3730 pub fn dupe(self: InstallDir, builder: *Builder) InstallDir { 3731 if (self == .custom) { 3732 // Written with this temporary to avoid RLS problems 3733 const duped_path = builder.dupe(self.custom); 3734 return .{ .custom = duped_path }; 3735 } else { 3736 return self; 3737 } 3738 } 3739 }; 3740 3741 pub const InstalledFile = struct { 3742 dir: InstallDir, 3743 path: []const u8, 3744 3745 /// Duplicates the installed file path and directory. 3746 pub fn dupe(self: InstalledFile, builder: *Builder) InstalledFile { 3747 return .{ 3748 .dir = self.dir.dupe(builder), 3749 .path = builder.dupe(self.path), 3750 }; 3751 } 3752 }; 3753 3754 test "Builder.dupePkg()" { 3755 if (builtin.os.tag == .wasi) return error.SkipZigTest; 3756 3757 var arena = std.heap.ArenaAllocator.init(std.testing.allocator); 3758 defer arena.deinit(); 3759 var builder = try Builder.create( 3760 arena.allocator(), 3761 "test", 3762 "test", 3763 "test", 3764 "test", 3765 ); 3766 defer builder.destroy(); 3767 3768 var pkg_dep = Pkg{ 3769 .name = "pkg_dep", 3770 .source = .{ .path = "/not/a/pkg_dep.zig" }, 3771 }; 3772 var pkg_top = Pkg{ 3773 .name = "pkg_top", 3774 .source = .{ .path = "/not/a/pkg_top.zig" }, 3775 .dependencies = &[_]Pkg{pkg_dep}, 3776 }; 3777 const dupe = builder.dupePkg(pkg_top); 3778 3779 const original_deps = pkg_top.dependencies.?; 3780 const dupe_deps = dupe.dependencies.?; 3781 3782 // probably the same top level package details 3783 try std.testing.expectEqualStrings(pkg_top.name, dupe.name); 3784 3785 // probably the same dependencies 3786 try std.testing.expectEqual(original_deps.len, dupe_deps.len); 3787 try std.testing.expectEqual(original_deps[0].name, pkg_dep.name); 3788 3789 // could segfault otherwise if pointers in duplicated package's fields are 3790 // the same as those in stack allocated package's fields 3791 try std.testing.expect(dupe_deps.ptr != original_deps.ptr); 3792 try std.testing.expect(dupe.name.ptr != pkg_top.name.ptr); 3793 try std.testing.expect(dupe.source.path.ptr != pkg_top.source.path.ptr); 3794 try std.testing.expect(dupe_deps[0].name.ptr != pkg_dep.name.ptr); 3795 try std.testing.expect(dupe_deps[0].source.path.ptr != pkg_dep.source.path.ptr); 3796 } 3797 3798 test "LibExeObjStep.addPackage" { 3799 if (builtin.os.tag == .wasi) return error.SkipZigTest; 3800 3801 var arena = std.heap.ArenaAllocator.init(std.testing.allocator); 3802 defer arena.deinit(); 3803 3804 var builder = try Builder.create( 3805 arena.allocator(), 3806 "test", 3807 "test", 3808 "test", 3809 "test", 3810 ); 3811 defer builder.destroy(); 3812 3813 const pkg_dep = Pkg{ 3814 .name = "pkg_dep", 3815 .source = .{ .path = "/not/a/pkg_dep.zig" }, 3816 }; 3817 const pkg_top = Pkg{ 3818 .name = "pkg_dep", 3819 .source = .{ .path = "/not/a/pkg_top.zig" }, 3820 .dependencies = &[_]Pkg{pkg_dep}, 3821 }; 3822 3823 var exe = builder.addExecutable("not_an_executable", "/not/an/executable.zig"); 3824 exe.addPackage(pkg_top); 3825 3826 try std.testing.expectEqual(@as(usize, 1), exe.packages.items.len); 3827 3828 const dupe = exe.packages.items[0]; 3829 try std.testing.expectEqualStrings(pkg_top.name, dupe.name); 3830 }