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