blob bfde2f52 (93196B) - 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.warn; 10 const ArrayList = std.ArrayList; 11 const StringHashMap = std.StringHashMap; 12 const Allocator = mem.Allocator; 13 const process = std.process; 14 const BufSet = std.BufSet; 15 const BufMap = std.BufMap; 16 const fmt_lib = std.fmt; 17 const File = std.fs.File; 18 19 pub const FmtStep = @import("build/fmt.zig").FmtStep; 20 21 pub const Builder = struct { 22 install_tls: TopLevelStep, 23 uninstall_tls: TopLevelStep, 24 allocator: *Allocator, 25 native_system_lib_paths: ArrayList([]const u8), 26 native_system_include_dirs: ArrayList([]const u8), 27 native_system_rpaths: ArrayList([]const u8), 28 user_input_options: UserInputOptionsMap, 29 available_options_map: AvailableOptionsMap, 30 available_options_list: ArrayList(AvailableOption), 31 verbose: bool, 32 verbose_tokenize: bool, 33 verbose_ast: bool, 34 verbose_link: bool, 35 verbose_cc: bool, 36 verbose_ir: bool, 37 verbose_llvm_ir: bool, 38 verbose_cimport: bool, 39 invalid_user_input: bool, 40 zig_exe: []const u8, 41 default_step: *Step, 42 env_map: *BufMap, 43 top_level_steps: ArrayList(*TopLevelStep), 44 install_prefix: ?[]const u8, 45 dest_dir: ?[]const u8, 46 lib_dir: []const u8, 47 exe_dir: []const u8, 48 install_path: []const u8, 49 search_prefixes: ArrayList([]const u8), 50 installed_files: ArrayList(InstalledFile), 51 build_root: []const u8, 52 cache_root: []const u8, 53 release_mode: ?builtin.Mode, 54 is_release: bool, 55 override_lib_dir: ?[]const u8, 56 vcpkg_root: VcpkgRoot, 57 pkg_config_pkg_list: ?(PkgConfigError![]const PkgConfigPkg) = null, 58 59 const PkgConfigError = error{ 60 PkgConfigCrashed, 61 PkgConfigFailed, 62 PkgConfigNotInstalled, 63 PkgConfigInvalidOutput, 64 }; 65 66 pub const PkgConfigPkg = struct { 67 name: []const u8, 68 desc: []const u8, 69 }; 70 71 pub const CStd = enum { 72 C89, 73 C99, 74 C11, 75 }; 76 77 const UserInputOptionsMap = StringHashMap(UserInputOption); 78 const AvailableOptionsMap = StringHashMap(AvailableOption); 79 80 const AvailableOption = struct { 81 name: []const u8, 82 type_id: TypeId, 83 description: []const u8, 84 }; 85 86 const UserInputOption = struct { 87 name: []const u8, 88 value: UserValue, 89 used: bool, 90 }; 91 92 const UserValue = union(enum) { 93 Flag: void, 94 Scalar: []const u8, 95 List: ArrayList([]const u8), 96 }; 97 98 const TypeId = enum { 99 Bool, 100 Int, 101 Float, 102 String, 103 List, 104 }; 105 106 const TopLevelStep = struct { 107 step: Step, 108 description: []const u8, 109 }; 110 111 pub fn create( 112 allocator: *Allocator, 113 zig_exe: []const u8, 114 build_root: []const u8, 115 cache_root: []const u8, 116 ) !*Builder { 117 const env_map = try allocator.create(BufMap); 118 env_map.* = try process.getEnvMap(allocator); 119 120 const self = try allocator.create(Builder); 121 self.* = Builder{ 122 .zig_exe = zig_exe, 123 .build_root = build_root, 124 .cache_root = try fs.path.relative(allocator, build_root, cache_root), 125 .verbose = false, 126 .verbose_tokenize = false, 127 .verbose_ast = false, 128 .verbose_link = false, 129 .verbose_cc = false, 130 .verbose_ir = false, 131 .verbose_llvm_ir = false, 132 .verbose_cimport = false, 133 .invalid_user_input = false, 134 .allocator = allocator, 135 .native_system_lib_paths = ArrayList([]const u8).init(allocator), 136 .native_system_include_dirs = ArrayList([]const u8).init(allocator), 137 .native_system_rpaths = ArrayList([]const u8).init(allocator), 138 .user_input_options = UserInputOptionsMap.init(allocator), 139 .available_options_map = AvailableOptionsMap.init(allocator), 140 .available_options_list = ArrayList(AvailableOption).init(allocator), 141 .top_level_steps = ArrayList(*TopLevelStep).init(allocator), 142 .default_step = undefined, 143 .env_map = env_map, 144 .search_prefixes = ArrayList([]const u8).init(allocator), 145 .install_prefix = null, 146 .lib_dir = undefined, 147 .exe_dir = undefined, 148 .dest_dir = env_map.get("DESTDIR"), 149 .installed_files = ArrayList(InstalledFile).init(allocator), 150 .install_tls = TopLevelStep{ 151 .step = Step.initNoOp("install", allocator), 152 .description = "Copy build artifacts to prefix path", 153 }, 154 .uninstall_tls = TopLevelStep{ 155 .step = Step.init("uninstall", allocator, makeUninstall), 156 .description = "Remove build artifacts from prefix path", 157 }, 158 .release_mode = null, 159 .is_release = false, 160 .override_lib_dir = null, 161 .install_path = undefined, 162 .vcpkg_root = VcpkgRoot{ .Unattempted = {} }, 163 }; 164 try self.top_level_steps.append(&self.install_tls); 165 try self.top_level_steps.append(&self.uninstall_tls); 166 self.detectNativeSystemPaths(); 167 self.default_step = &self.install_tls.step; 168 return self; 169 } 170 171 pub fn destroy(self: *Builder) void { 172 self.native_system_lib_paths.deinit(); 173 self.native_system_include_dirs.deinit(); 174 self.native_system_rpaths.deinit(); 175 self.env_map.deinit(); 176 self.top_level_steps.deinit(); 177 self.allocator.destroy(self); 178 } 179 180 /// This function is intended to be called by std/special/build_runner.zig, not a build.zig file. 181 pub fn setInstallPrefix(self: *Builder, optional_prefix: ?[]const u8) void { 182 self.install_prefix = optional_prefix; 183 } 184 185 /// This function is intended to be called by std/special/build_runner.zig, not a build.zig file. 186 pub fn resolveInstallPrefix(self: *Builder) void { 187 if (self.dest_dir) |dest_dir| { 188 const install_prefix = self.install_prefix orelse "/usr"; 189 self.install_path = fs.path.join(self.allocator, &[_][]const u8{ dest_dir, install_prefix }) catch unreachable; 190 } else { 191 const install_prefix = self.install_prefix orelse blk: { 192 const p = self.cache_root; 193 self.install_prefix = p; 194 break :blk p; 195 }; 196 self.install_path = install_prefix; 197 } 198 self.lib_dir = fs.path.join(self.allocator, &[_][]const u8{ self.install_path, "lib" }) catch unreachable; 199 self.exe_dir = fs.path.join(self.allocator, &[_][]const u8{ self.install_path, "bin" }) catch unreachable; 200 } 201 202 pub fn addExecutable(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { 203 return LibExeObjStep.createExecutable(self, name, root_src, false); 204 } 205 206 pub fn addObject(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { 207 return LibExeObjStep.createObject(self, name, root_src); 208 } 209 210 pub fn addSharedLibrary(self: *Builder, name: []const u8, root_src: ?[]const u8, ver: Version) *LibExeObjStep { 211 return LibExeObjStep.createSharedLibrary(self, name, root_src, ver); 212 } 213 214 pub fn addStaticLibrary(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { 215 return LibExeObjStep.createStaticLibrary(self, name, root_src); 216 } 217 218 pub fn addTest(self: *Builder, root_src: []const u8) *LibExeObjStep { 219 return LibExeObjStep.createTest(self, "test", root_src); 220 } 221 222 pub fn addAssemble(self: *Builder, name: []const u8, src: []const u8) *LibExeObjStep { 223 const obj_step = LibExeObjStep.createObject(self, name, null); 224 obj_step.addAssemblyFile(src); 225 return obj_step; 226 } 227 228 /// Initializes a RunStep with argv, which must at least have the path to the 229 /// executable. More command line arguments can be added with `addArg`, 230 /// `addArgs`, and `addArtifactArg`. 231 /// Be careful using this function, as it introduces a system dependency. 232 /// To run an executable built with zig build, see `LibExeObjStep.run`. 233 pub fn addSystemCommand(self: *Builder, argv: []const []const u8) *RunStep { 234 assert(argv.len >= 1); 235 const run_step = RunStep.create(self, self.fmt("run {}", argv[0])); 236 run_step.addArgs(argv); 237 return run_step; 238 } 239 240 fn dupe(self: *Builder, bytes: []const u8) []u8 { 241 return mem.dupe(self.allocator, u8, bytes) catch unreachable; 242 } 243 244 fn dupePath(self: *Builder, bytes: []const u8) []u8 { 245 const the_copy = self.dupe(bytes); 246 for (the_copy) |*byte| { 247 switch (byte.*) { 248 '/', '\\' => byte.* = fs.path.sep, 249 else => {}, 250 } 251 } 252 return the_copy; 253 } 254 255 pub fn addWriteFile(self: *Builder, file_path: []const u8, data: []const u8) *WriteFileStep { 256 const write_file_step = self.allocator.create(WriteFileStep) catch unreachable; 257 write_file_step.* = WriteFileStep.init(self, file_path, data); 258 return write_file_step; 259 } 260 261 pub fn addLog(self: *Builder, comptime format: []const u8, args: ...) *LogStep { 262 const data = self.fmt(format, args); 263 const log_step = self.allocator.create(LogStep) catch unreachable; 264 log_step.* = LogStep.init(self, data); 265 return log_step; 266 } 267 268 pub fn addRemoveDirTree(self: *Builder, dir_path: []const u8) *RemoveDirStep { 269 const remove_dir_step = self.allocator.create(RemoveDirStep) catch unreachable; 270 remove_dir_step.* = RemoveDirStep.init(self, dir_path); 271 return remove_dir_step; 272 } 273 274 pub fn addFmt(self: *Builder, paths: []const []const u8) *FmtStep { 275 return FmtStep.create(self, paths); 276 } 277 278 pub fn version(self: *const Builder, major: u32, minor: u32, patch: u32) Version { 279 return Version{ 280 .major = major, 281 .minor = minor, 282 .patch = patch, 283 }; 284 } 285 286 pub fn addNativeSystemIncludeDir(self: *Builder, path: []const u8) void { 287 self.native_system_include_dirs.append(path) catch unreachable; 288 } 289 290 pub fn addNativeSystemRPath(self: *Builder, path: []const u8) void { 291 self.native_system_rpaths.append(path) catch unreachable; 292 } 293 294 pub fn addNativeSystemLibPath(self: *Builder, path: []const u8) void { 295 self.native_system_lib_paths.append(path) catch unreachable; 296 } 297 298 pub fn make(self: *Builder, step_names: []const []const u8) !void { 299 try self.makePath(self.cache_root); 300 301 var wanted_steps = ArrayList(*Step).init(self.allocator); 302 defer wanted_steps.deinit(); 303 304 if (step_names.len == 0) { 305 try wanted_steps.append(self.default_step); 306 } else { 307 for (step_names) |step_name| { 308 const s = try self.getTopLevelStepByName(step_name); 309 try wanted_steps.append(s); 310 } 311 } 312 313 for (wanted_steps.toSliceConst()) |s| { 314 try self.makeOneStep(s); 315 } 316 } 317 318 pub fn getInstallStep(self: *Builder) *Step { 319 return &self.install_tls.step; 320 } 321 322 pub fn getUninstallStep(self: *Builder) *Step { 323 return &self.uninstall_tls.step; 324 } 325 326 fn makeUninstall(uninstall_step: *Step) anyerror!void { 327 const uninstall_tls = @fieldParentPtr(TopLevelStep, "step", uninstall_step); 328 const self = @fieldParentPtr(Builder, "uninstall_tls", uninstall_tls); 329 330 for (self.installed_files.toSliceConst()) |installed_file| { 331 const full_path = self.getInstallPath(installed_file.dir, installed_file.path); 332 if (self.verbose) { 333 warn("rm {}\n", full_path); 334 } 335 fs.deleteTree(full_path) catch {}; 336 } 337 338 // TODO remove empty directories 339 } 340 341 fn makeOneStep(self: *Builder, s: *Step) anyerror!void { 342 if (s.loop_flag) { 343 warn("Dependency loop detected:\n {}\n", s.name); 344 return error.DependencyLoopDetected; 345 } 346 s.loop_flag = true; 347 348 for (s.dependencies.toSlice()) |dep| { 349 self.makeOneStep(dep) catch |err| { 350 if (err == error.DependencyLoopDetected) { 351 warn(" {}\n", s.name); 352 } 353 return err; 354 }; 355 } 356 357 s.loop_flag = false; 358 359 try s.make(); 360 } 361 362 fn getTopLevelStepByName(self: *Builder, name: []const u8) !*Step { 363 for (self.top_level_steps.toSliceConst()) |top_level_step| { 364 if (mem.eql(u8, top_level_step.step.name, name)) { 365 return &top_level_step.step; 366 } 367 } 368 warn("Cannot run step '{}' because it does not exist\n", name); 369 return error.InvalidStepName; 370 } 371 372 fn detectNativeSystemPaths(self: *Builder) void { 373 var is_nixos = false; 374 if (process.getEnvVarOwned(self.allocator, "NIX_CFLAGS_COMPILE")) |nix_cflags_compile| { 375 is_nixos = true; 376 var it = mem.tokenize(nix_cflags_compile, " "); 377 while (true) { 378 const word = it.next() orelse break; 379 if (mem.eql(u8, word, "-isystem")) { 380 const include_path = it.next() orelse { 381 warn("Expected argument after -isystem in NIX_CFLAGS_COMPILE\n"); 382 break; 383 }; 384 self.addNativeSystemIncludeDir(include_path); 385 } else { 386 warn("Unrecognized C flag from NIX_CFLAGS_COMPILE: {}\n", word); 387 break; 388 } 389 } 390 } else |err| { 391 assert(err == error.EnvironmentVariableNotFound); 392 } 393 if (process.getEnvVarOwned(self.allocator, "NIX_LDFLAGS")) |nix_ldflags| { 394 is_nixos = true; 395 var it = mem.tokenize(nix_ldflags, " "); 396 while (true) { 397 const word = it.next() orelse break; 398 if (mem.eql(u8, word, "-rpath")) { 399 const rpath = it.next() orelse { 400 warn("Expected argument after -rpath in NIX_LDFLAGS\n"); 401 break; 402 }; 403 self.addNativeSystemRPath(rpath); 404 } else if (word.len > 2 and word[0] == '-' and word[1] == 'L') { 405 const lib_path = word[2..]; 406 self.addNativeSystemLibPath(lib_path); 407 } else { 408 warn("Unrecognized C flag from NIX_LDFLAGS: {}\n", word); 409 break; 410 } 411 } 412 } else |err| { 413 assert(err == error.EnvironmentVariableNotFound); 414 } 415 if (is_nixos) return; 416 switch (builtin.os) { 417 .windows => {}, 418 else => { 419 const triple = (Target{ 420 .Cross = CrossTarget{ 421 .arch = builtin.arch, 422 .os = builtin.os, 423 .abi = builtin.abi, 424 }, 425 }).linuxTriple(self.allocator); 426 427 // TODO: $ ld --verbose | grep SEARCH_DIR 428 // the output contains some paths that end with lib64, maybe include them too? 429 // also, what is the best possible order of things? 430 431 self.addNativeSystemIncludeDir("/usr/local/include"); 432 self.addNativeSystemLibPath("/usr/local/lib"); 433 434 self.addNativeSystemIncludeDir(self.fmt("/usr/include/{}", triple)); 435 self.addNativeSystemLibPath(self.fmt("/usr/lib/{}", triple)); 436 437 self.addNativeSystemIncludeDir("/usr/include"); 438 self.addNativeSystemLibPath("/usr/lib"); 439 440 // example: on a 64-bit debian-based linux distro, with zlib installed from apt: 441 // zlib.h is in /usr/include (added above) 442 // libz.so.1 is in /lib/x86_64-linux-gnu (added here) 443 self.addNativeSystemLibPath(self.fmt("/lib/{}", triple)); 444 }, 445 } 446 } 447 448 pub fn option(self: *Builder, comptime T: type, name: []const u8, description: []const u8) ?T { 449 const type_id = comptime typeToEnum(T); 450 const available_option = AvailableOption{ 451 .name = name, 452 .type_id = type_id, 453 .description = description, 454 }; 455 if ((self.available_options_map.put(name, available_option) catch unreachable) != null) { 456 panic("Option '{}' declared twice", name); 457 } 458 self.available_options_list.append(available_option) catch unreachable; 459 460 const entry = self.user_input_options.get(name) orelse return null; 461 entry.value.used = true; 462 switch (type_id) { 463 TypeId.Bool => switch (entry.value.value) { 464 UserValue.Flag => return true, 465 UserValue.Scalar => |s| { 466 if (mem.eql(u8, s, "true")) { 467 return true; 468 } else if (mem.eql(u8, s, "false")) { 469 return false; 470 } else { 471 warn("Expected -D{} to be a boolean, but received '{}'\n", name, s); 472 self.markInvalidUserInput(); 473 return null; 474 } 475 }, 476 UserValue.List => { 477 warn("Expected -D{} to be a boolean, but received a list.\n", name); 478 self.markInvalidUserInput(); 479 return null; 480 }, 481 }, 482 TypeId.Int => panic("TODO integer options to build script"), 483 TypeId.Float => panic("TODO float options to build script"), 484 TypeId.String => switch (entry.value.value) { 485 UserValue.Flag => { 486 warn("Expected -D{} to be a string, but received a boolean.\n", name); 487 self.markInvalidUserInput(); 488 return null; 489 }, 490 UserValue.List => { 491 warn("Expected -D{} to be a string, but received a list.\n", name); 492 self.markInvalidUserInput(); 493 return null; 494 }, 495 UserValue.Scalar => |s| return s, 496 }, 497 TypeId.List => panic("TODO list options to build script"), 498 } 499 } 500 501 pub fn step(self: *Builder, name: []const u8, description: []const u8) *Step { 502 const step_info = self.allocator.create(TopLevelStep) catch unreachable; 503 step_info.* = TopLevelStep{ 504 .step = Step.initNoOp(name, self.allocator), 505 .description = description, 506 }; 507 self.top_level_steps.append(step_info) catch unreachable; 508 return &step_info.step; 509 } 510 511 /// This provides the -Drelease option to the build user and does not give them the choice. 512 pub fn setPreferredReleaseMode(self: *Builder, mode: builtin.Mode) void { 513 if (self.release_mode != null) { 514 @panic("setPreferredReleaseMode must be called before standardReleaseOptions and may not be called twice"); 515 } 516 const description = self.fmt("create a release build ({})", @tagName(mode)); 517 self.is_release = self.option(bool, "release", description) orelse false; 518 self.release_mode = if (self.is_release) mode else builtin.Mode.Debug; 519 } 520 521 /// If you call this without first calling `setPreferredReleaseMode` then it gives the build user 522 /// the choice of what kind of release. 523 pub fn standardReleaseOptions(self: *Builder) builtin.Mode { 524 if (self.release_mode) |mode| return mode; 525 526 const release_safe = self.option(bool, "release-safe", "optimizations on and safety on") orelse false; 527 const release_fast = self.option(bool, "release-fast", "optimizations on and safety off") orelse false; 528 const release_small = self.option(bool, "release-small", "size optimizations on and safety off") orelse false; 529 530 const mode = if (release_safe and !release_fast and !release_small) 531 builtin.Mode.ReleaseSafe 532 else if (release_fast and !release_safe and !release_small) 533 builtin.Mode.ReleaseFast 534 else if (release_small and !release_fast and !release_safe) 535 builtin.Mode.ReleaseSmall 536 else if (!release_fast and !release_safe and !release_small) 537 builtin.Mode.Debug 538 else x: { 539 warn("Multiple release modes (of -Drelease-safe, -Drelease-fast and -Drelease-small)"); 540 self.markInvalidUserInput(); 541 break :x builtin.Mode.Debug; 542 }; 543 self.is_release = mode != .Debug; 544 self.release_mode = mode; 545 return mode; 546 } 547 548 /// Exposes standard `zig build` options for choosing a target. Pass `null` to support all targets. 549 pub fn standardTargetOptions(self: *Builder, supported_targets: ?[]const Target) Target { 550 if (supported_targets) |target_list| { 551 // TODO detect multiple args and emit an error message 552 // there's probably a better way to collect the target 553 for (target_list) |targ| { 554 const targ_str = targ.zigTriple(self.allocator) catch unreachable; 555 const targ_desc = targ.allocDescription(self.allocator) catch unreachable; 556 const this_targ_opt = self.option(bool, targ_str, targ_desc) orelse false; 557 if (this_targ_opt) { 558 return targ; 559 } 560 } 561 return Target.Native; 562 } else { 563 const target_str = self.option([]const u8, "target", "the target to build for") orelse return Target.Native; 564 return Target.parse(target_str) catch unreachable; // TODO better error message for bad target 565 } 566 } 567 568 pub fn addUserInputOption(self: *Builder, name: []const u8, value: []const u8) !bool { 569 const gop = try self.user_input_options.getOrPut(name); 570 if (!gop.found_existing) { 571 gop.kv.value = UserInputOption{ 572 .name = name, 573 .value = UserValue{ .Scalar = value }, 574 .used = false, 575 }; 576 return false; 577 } 578 579 // option already exists 580 switch (gop.kv.value.value) { 581 UserValue.Scalar => |s| { 582 // turn it into a list 583 var list = ArrayList([]const u8).init(self.allocator); 584 list.append(s) catch unreachable; 585 list.append(value) catch unreachable; 586 _ = self.user_input_options.put(name, UserInputOption{ 587 .name = name, 588 .value = UserValue{ .List = list }, 589 .used = false, 590 }) catch unreachable; 591 }, 592 UserValue.List => |*list| { 593 // append to the list 594 list.append(value) catch unreachable; 595 _ = self.user_input_options.put(name, UserInputOption{ 596 .name = name, 597 .value = UserValue{ .List = list.* }, 598 .used = false, 599 }) catch unreachable; 600 }, 601 UserValue.Flag => { 602 warn("Option '-D{}={}' conflicts with flag '-D{}'.\n", name, value, name); 603 return true; 604 }, 605 } 606 return false; 607 } 608 609 pub fn addUserInputFlag(self: *Builder, name: []const u8) !bool { 610 const gop = try self.user_input_options.getOrPut(name); 611 if (!gop.found_existing) { 612 gop.kv.value = UserInputOption{ 613 .name = name, 614 .value = UserValue{ .Flag = {} }, 615 .used = false, 616 }; 617 return false; 618 } 619 620 // option already exists 621 switch (gop.kv.value.value) { 622 UserValue.Scalar => |s| { 623 warn("Flag '-D{}' conflicts with option '-D{}={}'.\n", name, name, s); 624 return true; 625 }, 626 UserValue.List => { 627 warn("Flag '-D{}' conflicts with multiple options of the same name.\n", name); 628 return true; 629 }, 630 UserValue.Flag => {}, 631 } 632 return false; 633 } 634 635 fn typeToEnum(comptime T: type) TypeId { 636 return switch (@typeId(T)) { 637 builtin.TypeId.Int => TypeId.Int, 638 builtin.TypeId.Float => TypeId.Float, 639 builtin.TypeId.Bool => TypeId.Bool, 640 else => switch (T) { 641 []const u8 => TypeId.String, 642 []const []const u8 => TypeId.List, 643 else => @compileError("Unsupported type: " ++ @typeName(T)), 644 }, 645 }; 646 } 647 648 fn markInvalidUserInput(self: *Builder) void { 649 self.invalid_user_input = true; 650 } 651 652 pub fn typeIdName(id: TypeId) []const u8 { 653 return switch (id) { 654 TypeId.Bool => "bool", 655 TypeId.Int => "int", 656 TypeId.Float => "float", 657 TypeId.String => "string", 658 TypeId.List => "list", 659 }; 660 } 661 662 pub fn validateUserInputDidItFail(self: *Builder) bool { 663 // make sure all args are used 664 var it = self.user_input_options.iterator(); 665 while (true) { 666 const entry = it.next() orelse break; 667 if (!entry.value.used) { 668 warn("Invalid option: -D{}\n\n", entry.key); 669 self.markInvalidUserInput(); 670 } 671 } 672 673 return self.invalid_user_input; 674 } 675 676 fn spawnChild(self: *Builder, argv: []const []const u8) !void { 677 return self.spawnChildEnvMap(null, self.env_map, argv); 678 } 679 680 fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void { 681 if (cwd) |yes_cwd| warn("cd {} && ", yes_cwd); 682 for (argv) |arg| { 683 warn("{} ", arg); 684 } 685 warn("\n"); 686 } 687 688 fn spawnChildEnvMap(self: *Builder, cwd: ?[]const u8, env_map: *const BufMap, argv: []const []const u8) !void { 689 if (self.verbose) { 690 printCmd(cwd, argv); 691 } 692 693 const child = std.ChildProcess.init(argv, self.allocator) catch unreachable; 694 defer child.deinit(); 695 696 child.cwd = cwd; 697 child.env_map = env_map; 698 699 const term = child.spawnAndWait() catch |err| { 700 warn("Unable to spawn {}: {}\n", argv[0], @errorName(err)); 701 return err; 702 }; 703 704 switch (term) { 705 .Exited => |code| { 706 if (code != 0) { 707 warn("The following command exited with error code {}:\n", code); 708 printCmd(cwd, argv); 709 return error.UncleanExit; 710 } 711 }, 712 else => { 713 warn("The following command terminated unexpectedly:\n"); 714 printCmd(cwd, argv); 715 716 return error.UncleanExit; 717 }, 718 } 719 } 720 721 pub fn makePath(self: *Builder, path: []const u8) !void { 722 fs.makePath(self.allocator, self.pathFromRoot(path)) catch |err| { 723 warn("Unable to create path {}: {}\n", path, @errorName(err)); 724 return err; 725 }; 726 } 727 728 pub fn installArtifact(self: *Builder, artifact: *LibExeObjStep) void { 729 self.getInstallStep().dependOn(&self.addInstallArtifact(artifact).step); 730 } 731 732 pub fn addInstallArtifact(self: *Builder, artifact: *LibExeObjStep) *InstallArtifactStep { 733 return InstallArtifactStep.create(self, artifact); 734 } 735 736 ///`dest_rel_path` is relative to prefix path 737 pub fn installFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) void { 738 self.getInstallStep().dependOn(&self.addInstallFileWithDir(src_path, .Prefix, dest_rel_path).step); 739 } 740 741 pub fn installDirectory(self: *Builder, options: InstallDirectoryOptions) void { 742 self.getInstallStep().dependOn(&self.addInstallDirectory(options).step); 743 } 744 745 ///`dest_rel_path` is relative to bin path 746 pub fn installBinFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) void { 747 self.getInstallStep().dependOn(&self.addInstallFileWithDir(src_path, .Bin, dest_rel_path).step); 748 } 749 750 ///`dest_rel_path` is relative to lib path 751 pub fn installLibFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) void { 752 self.getInstallStep().dependOn(&self.addInstallFileWithDir(src_path, .Lib, dest_rel_path).step); 753 } 754 755 ///`dest_rel_path` is relative to install prefix path 756 pub fn addInstallFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) *InstallFileStep { 757 return self.addInstallFileWithDir(src_path, .Prefix, dest_rel_path); 758 } 759 760 ///`dest_rel_path` is relative to bin path 761 pub fn addInstallBinFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) *InstallFileStep { 762 return self.addInstallFileWithDir(src_path, .Bin, dest_rel_path); 763 } 764 765 ///`dest_rel_path` is relative to lib path 766 pub fn addInstallLibFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) *InstallFileStep { 767 return self.addInstallFileWithDir(src_path, .Lib, dest_rel_path); 768 } 769 770 pub fn addInstallFileWithDir( 771 self: *Builder, 772 src_path: []const u8, 773 install_dir: InstallDir, 774 dest_rel_path: []const u8, 775 ) *InstallFileStep { 776 const install_step = self.allocator.create(InstallFileStep) catch unreachable; 777 install_step.* = InstallFileStep.init(self, src_path, install_dir, dest_rel_path); 778 return install_step; 779 } 780 781 pub fn addInstallDirectory(self: *Builder, options: InstallDirectoryOptions) *InstallDirStep { 782 const install_step = self.allocator.create(InstallDirStep) catch unreachable; 783 install_step.* = InstallDirStep.init(self, options); 784 return install_step; 785 } 786 787 pub fn pushInstalledFile(self: *Builder, dir: InstallDir, dest_rel_path: []const u8) void { 788 self.installed_files.append(InstalledFile{ 789 .dir = dir, 790 .path = dest_rel_path, 791 }) catch unreachable; 792 } 793 794 fn updateFile(self: *Builder, source_path: []const u8, dest_path: []const u8) !void { 795 if (self.verbose) { 796 warn("cp {} {} ", source_path, dest_path); 797 } 798 const prev_status = try fs.updateFile(source_path, dest_path); 799 if (self.verbose) switch (prev_status) { 800 .stale => warn("# installed\n"), 801 .fresh => warn("# up-to-date\n"), 802 }; 803 } 804 805 fn pathFromRoot(self: *Builder, rel_path: []const u8) []u8 { 806 return fs.path.resolve(self.allocator, &[_][]const u8{ self.build_root, rel_path }) catch unreachable; 807 } 808 809 pub fn fmt(self: *Builder, comptime format: []const u8, args: ...) []u8 { 810 return fmt_lib.allocPrint(self.allocator, format, args) catch unreachable; 811 } 812 813 pub fn findProgram(self: *Builder, names: []const []const u8, paths: []const []const u8) ![]const u8 { 814 // TODO report error for ambiguous situations 815 const exe_extension = (Target{ .Native = {} }).exeFileExt(); 816 for (self.search_prefixes.toSliceConst()) |search_prefix| { 817 for (names) |name| { 818 if (fs.path.isAbsolute(name)) { 819 return name; 820 } 821 const full_path = try fs.path.join(self.allocator, &[_][]const u8{ search_prefix, "bin", self.fmt("{}{}", name, exe_extension) }); 822 return fs.realpathAlloc(self.allocator, full_path) catch continue; 823 } 824 } 825 if (self.env_map.get("PATH")) |PATH| { 826 for (names) |name| { 827 if (fs.path.isAbsolute(name)) { 828 return name; 829 } 830 var it = mem.tokenize(PATH, &[_]u8{fs.path.delimiter}); 831 while (it.next()) |path| { 832 const full_path = try fs.path.join(self.allocator, &[_][]const u8{ path, self.fmt("{}{}", name, exe_extension) }); 833 return fs.realpathAlloc(self.allocator, full_path) catch continue; 834 } 835 } 836 } 837 for (names) |name| { 838 if (fs.path.isAbsolute(name)) { 839 return name; 840 } 841 for (paths) |path| { 842 const full_path = try fs.path.join(self.allocator, &[_][]const u8{ path, self.fmt("{}{}", name, exe_extension) }); 843 return fs.realpathAlloc(self.allocator, full_path) catch continue; 844 } 845 } 846 return error.FileNotFound; 847 } 848 849 pub fn execAllowFail( 850 self: *Builder, 851 argv: []const []const u8, 852 out_code: *u8, 853 stderr_behavior: std.ChildProcess.StdIo, 854 ) ![]u8 { 855 assert(argv.len != 0); 856 857 const max_output_size = 400 * 1024; 858 const child = try std.ChildProcess.init(argv, self.allocator); 859 defer child.deinit(); 860 861 child.stdin_behavior = .Ignore; 862 child.stdout_behavior = .Pipe; 863 child.stderr_behavior = stderr_behavior; 864 865 try child.spawn(); 866 867 var stdout = std.Buffer.initNull(self.allocator); 868 defer std.Buffer.deinit(&stdout); 869 870 var stdout_file_in_stream = child.stdout.?.inStream(); 871 try stdout_file_in_stream.stream.readAllBuffer(&stdout, max_output_size); 872 873 const term = try child.wait(); 874 switch (term) { 875 .Exited => |code| { 876 if (code != 0) { 877 out_code.* = @truncate(u8, code); 878 return error.ExitCodeFailure; 879 } 880 return stdout.toOwnedSlice(); 881 }, 882 .Signal, .Stopped, .Unknown => |code| { 883 out_code.* = @truncate(u8, code); 884 return error.ProcessTerminated; 885 }, 886 } 887 } 888 889 pub fn exec(self: *Builder, argv: []const []const u8) ![]u8 { 890 assert(argv.len != 0); 891 892 if (self.verbose) { 893 printCmd(null, argv); 894 } 895 896 var code: u8 = undefined; 897 return self.execAllowFail(argv, &code, .Inherit) catch |err| switch (err) { 898 error.FileNotFound => { 899 warn("Unable to spawn the following command: file not found\n"); 900 printCmd(null, argv); 901 std.os.exit(@truncate(u8, code)); 902 }, 903 error.ExitCodeFailure => { 904 warn("The following command exited with error code {}:\n", code); 905 printCmd(null, argv); 906 std.os.exit(@truncate(u8, code)); 907 }, 908 error.ProcessTerminated => { 909 warn("The following command terminated unexpectedly:\n"); 910 printCmd(null, argv); 911 std.os.exit(@truncate(u8, code)); 912 }, 913 else => |e| return e, 914 }; 915 } 916 917 pub fn addSearchPrefix(self: *Builder, search_prefix: []const u8) void { 918 self.search_prefixes.append(search_prefix) catch unreachable; 919 } 920 921 fn getInstallPath(self: *Builder, dir: InstallDir, dest_rel_path: []const u8) []const u8 { 922 const base_dir = switch (dir) { 923 .Prefix => self.install_path, 924 .Bin => self.exe_dir, 925 .Lib => self.lib_dir, 926 }; 927 return fs.path.resolve( 928 self.allocator, 929 &[_][]const u8{ base_dir, dest_rel_path }, 930 ) catch unreachable; 931 } 932 933 fn execPkgConfigList(self: *Builder, out_code: *u8) ![]const PkgConfigPkg { 934 const stdout = try self.execAllowFail(&[_][]const u8{ "pkg-config", "--list-all" }, out_code, .Ignore); 935 var list = ArrayList(PkgConfigPkg).init(self.allocator); 936 var line_it = mem.tokenize(stdout, "\r\n"); 937 while (line_it.next()) |line| { 938 if (mem.trim(u8, line, " \t").len == 0) continue; 939 var tok_it = mem.tokenize(line, " \t"); 940 try list.append(PkgConfigPkg{ 941 .name = tok_it.next() orelse return error.PkgConfigInvalidOutput, 942 .desc = tok_it.rest(), 943 }); 944 } 945 return list.toSliceConst(); 946 } 947 948 fn getPkgConfigList(self: *Builder) ![]const PkgConfigPkg { 949 if (self.pkg_config_pkg_list) |res| { 950 return res; 951 } 952 var code: u8 = undefined; 953 if (self.execPkgConfigList(&code)) |list| { 954 self.pkg_config_pkg_list = list; 955 return list; 956 } else |err| { 957 const result = switch (err) { 958 error.ProcessTerminated => error.PkgConfigCrashed, 959 error.ExitCodeFailure => error.PkgConfigFailed, 960 error.FileNotFound => error.PkgConfigNotInstalled, 961 error.InvalidName => error.PkgConfigNotInstalled, 962 error.PkgConfigInvalidOutput => error.PkgConfigInvalidOutput, 963 else => return err, 964 }; 965 self.pkg_config_pkg_list = result; 966 return result; 967 } 968 } 969 }; 970 971 test "builder.findProgram compiles" { 972 const builder = try Builder.create(std.heap.page_allocator, "zig", "zig-cache", "zig-cache"); 973 _ = builder.findProgram(&[_][]const u8{}, &[_][]const u8{}) catch null; 974 } 975 976 /// Deprecated. Use `builtin.Version`. 977 pub const Version = builtin.Version; 978 979 /// Deprecated. Use `std.Target.Cross`. 980 pub const CrossTarget = std.Target.Cross; 981 982 /// Deprecated. Use `std.Target`. 983 pub const Target = std.Target; 984 985 const Pkg = struct { 986 name: []const u8, 987 path: []const u8, 988 }; 989 990 const CSourceFile = struct { 991 source_path: []const u8, 992 args: []const []const u8, 993 }; 994 995 fn isLibCLibrary(name: []const u8) bool { 996 const libc_libraries = [_][]const u8{ "c", "m", "dl", "rt", "pthread" }; 997 for (libc_libraries) |libc_lib_name| { 998 if (mem.eql(u8, name, libc_lib_name)) 999 return true; 1000 } 1001 return false; 1002 } 1003 1004 pub const LibExeObjStep = struct { 1005 step: Step, 1006 builder: *Builder, 1007 name: []const u8, 1008 target: Target, 1009 linker_script: ?[]const u8 = null, 1010 version_script: ?[]const u8 = null, 1011 out_filename: []const u8, 1012 is_dynamic: bool, 1013 version: Version, 1014 build_mode: builtin.Mode, 1015 kind: Kind, 1016 major_only_filename: []const u8, 1017 name_only_filename: []const u8, 1018 strip: bool, 1019 lib_paths: ArrayList([]const u8), 1020 framework_dirs: ArrayList([]const u8), 1021 frameworks: BufSet, 1022 verbose_link: bool, 1023 verbose_cc: bool, 1024 disable_gen_h: bool, 1025 bundle_compiler_rt: bool, 1026 disable_stack_probing: bool, 1027 c_std: Builder.CStd, 1028 override_lib_dir: ?[]const u8, 1029 main_pkg_path: ?[]const u8, 1030 exec_cmd_args: ?[]const ?[]const u8, 1031 name_prefix: []const u8, 1032 filter: ?[]const u8, 1033 single_threaded: bool, 1034 1035 root_src: ?[]const u8, 1036 out_h_filename: []const u8, 1037 out_lib_filename: []const u8, 1038 out_pdb_filename: []const u8, 1039 packages: ArrayList(Pkg), 1040 build_options_contents: std.Buffer, 1041 system_linker_hack: bool, 1042 1043 object_src: []const u8, 1044 1045 link_objects: ArrayList(LinkObject), 1046 include_dirs: ArrayList(IncludeDir), 1047 c_macros: ArrayList([]const u8), 1048 output_dir: ?[]const u8, 1049 need_system_paths: bool, 1050 is_linking_libc: bool = false, 1051 vcpkg_bin_path: ?[]const u8 = null, 1052 1053 installed_path: ?[]const u8, 1054 install_step: ?*InstallArtifactStep, 1055 1056 libc_file: ?[]const u8 = null, 1057 target_glibc: ?Version = null, 1058 1059 valgrind_support: ?bool = null, 1060 1061 /// Uses system Wine installation to run cross compiled Windows build artifacts. 1062 enable_wine: bool = false, 1063 1064 /// Uses system QEMU installation to run cross compiled foreign architecture build artifacts. 1065 enable_qemu: bool = false, 1066 1067 /// Uses system Wasmtime installation to run cross compiled wasm/wasi build artifacts. 1068 enable_wasmtime: bool = false, 1069 1070 /// After following the steps in https://github.com/ziglang/zig/wiki/Updating-libc#glibc, 1071 /// this will be the directory $glibc-build-dir/install/glibcs 1072 /// Given the example of the aarch64 target, this is the directory 1073 /// that contains the path `aarch64-linux-gnu/lib/ld-linux-aarch64.so.1`. 1074 glibc_multi_install_dir: ?[]const u8 = null, 1075 1076 dynamic_linker: ?[]const u8 = null, 1077 1078 /// Position Independent Code 1079 force_pic: ?bool = null, 1080 1081 subsystem: ?builtin.SubSystem = null, 1082 1083 const LinkObject = union(enum) { 1084 StaticPath: []const u8, 1085 OtherStep: *LibExeObjStep, 1086 SystemLib: []const u8, 1087 AssemblyFile: []const u8, 1088 CSourceFile: *CSourceFile, 1089 }; 1090 1091 const IncludeDir = union(enum) { 1092 RawPath: []const u8, 1093 RawPathSystem: []const u8, 1094 OtherStep: *LibExeObjStep, 1095 }; 1096 1097 const Kind = enum { 1098 Exe, 1099 Lib, 1100 Obj, 1101 Test, 1102 }; 1103 1104 pub fn createSharedLibrary(builder: *Builder, name: []const u8, root_src: ?[]const u8, ver: Version) *LibExeObjStep { 1105 const self = builder.allocator.create(LibExeObjStep) catch unreachable; 1106 self.* = initExtraArgs(builder, name, root_src, Kind.Lib, true, ver); 1107 return self; 1108 } 1109 1110 pub fn createStaticLibrary(builder: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { 1111 const self = builder.allocator.create(LibExeObjStep) catch unreachable; 1112 self.* = initExtraArgs(builder, name, root_src, Kind.Lib, false, builder.version(0, 0, 0)); 1113 return self; 1114 } 1115 1116 pub fn createObject(builder: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { 1117 const self = builder.allocator.create(LibExeObjStep) catch unreachable; 1118 self.* = initExtraArgs(builder, name, root_src, Kind.Obj, false, builder.version(0, 0, 0)); 1119 return self; 1120 } 1121 1122 pub fn createExecutable(builder: *Builder, name: []const u8, root_src: ?[]const u8, is_dynamic: bool) *LibExeObjStep { 1123 const self = builder.allocator.create(LibExeObjStep) catch unreachable; 1124 self.* = initExtraArgs(builder, name, root_src, Kind.Exe, is_dynamic, builder.version(0, 0, 0)); 1125 return self; 1126 } 1127 1128 pub fn createTest(builder: *Builder, name: []const u8, root_src: []const u8) *LibExeObjStep { 1129 const self = builder.allocator.create(LibExeObjStep) catch unreachable; 1130 self.* = initExtraArgs(builder, name, root_src, Kind.Test, false, builder.version(0, 0, 0)); 1131 return self; 1132 } 1133 1134 fn initExtraArgs(builder: *Builder, name: []const u8, root_src: ?[]const u8, kind: Kind, is_dynamic: bool, ver: Version) LibExeObjStep { 1135 if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) { 1136 panic("invalid name: '{}'. It looks like a file path, but it is supposed to be the library or application name.", name); 1137 } 1138 var self = LibExeObjStep{ 1139 .strip = false, 1140 .builder = builder, 1141 .verbose_link = false, 1142 .verbose_cc = false, 1143 .build_mode = builtin.Mode.Debug, 1144 .is_dynamic = is_dynamic, 1145 .kind = kind, 1146 .root_src = root_src, 1147 .name = name, 1148 .target = Target.Native, 1149 .frameworks = BufSet.init(builder.allocator), 1150 .step = Step.init(name, builder.allocator, make), 1151 .version = ver, 1152 .out_filename = undefined, 1153 .out_h_filename = builder.fmt("{}.h", name), 1154 .out_lib_filename = undefined, 1155 .out_pdb_filename = builder.fmt("{}.pdb", name), 1156 .major_only_filename = undefined, 1157 .name_only_filename = undefined, 1158 .packages = ArrayList(Pkg).init(builder.allocator), 1159 .include_dirs = ArrayList(IncludeDir).init(builder.allocator), 1160 .link_objects = ArrayList(LinkObject).init(builder.allocator), 1161 .c_macros = ArrayList([]const u8).init(builder.allocator), 1162 .lib_paths = ArrayList([]const u8).init(builder.allocator), 1163 .framework_dirs = ArrayList([]const u8).init(builder.allocator), 1164 .object_src = undefined, 1165 .build_options_contents = std.Buffer.initSize(builder.allocator, 0) catch unreachable, 1166 .c_std = Builder.CStd.C99, 1167 .system_linker_hack = false, 1168 .override_lib_dir = null, 1169 .main_pkg_path = null, 1170 .exec_cmd_args = null, 1171 .name_prefix = "", 1172 .filter = null, 1173 .disable_gen_h = false, 1174 .bundle_compiler_rt = false, 1175 .disable_stack_probing = false, 1176 .output_dir = null, 1177 .need_system_paths = false, 1178 .single_threaded = false, 1179 .installed_path = null, 1180 .install_step = null, 1181 }; 1182 self.computeOutFileNames(); 1183 return self; 1184 } 1185 1186 fn computeOutFileNames(self: *LibExeObjStep) void { 1187 switch (self.kind) { 1188 .Obj => { 1189 self.out_filename = self.builder.fmt("{}{}", self.name, self.target.oFileExt()); 1190 }, 1191 .Exe => { 1192 self.out_filename = self.builder.fmt("{}{}", self.name, self.target.exeFileExt()); 1193 }, 1194 .Test => { 1195 self.out_filename = self.builder.fmt("test{}", self.target.exeFileExt()); 1196 }, 1197 .Lib => { 1198 if (!self.is_dynamic) { 1199 self.out_filename = self.builder.fmt( 1200 "{}{}{}", 1201 self.target.libPrefix(), 1202 self.name, 1203 self.target.staticLibSuffix(), 1204 ); 1205 self.out_lib_filename = self.out_filename; 1206 } else { 1207 if (self.target.isDarwin()) { 1208 self.out_filename = self.builder.fmt("lib{}.{d}.{d}.{d}.dylib", self.name, self.version.major, self.version.minor, self.version.patch); 1209 self.major_only_filename = self.builder.fmt("lib{}.{d}.dylib", self.name, self.version.major); 1210 self.name_only_filename = self.builder.fmt("lib{}.dylib", self.name); 1211 self.out_lib_filename = self.out_filename; 1212 } else if (self.target.isWindows()) { 1213 self.out_filename = self.builder.fmt("{}.dll", self.name); 1214 self.out_lib_filename = self.builder.fmt("{}.lib", self.name); 1215 } else { 1216 self.out_filename = self.builder.fmt("lib{}.so.{d}.{d}.{d}", self.name, self.version.major, self.version.minor, self.version.patch); 1217 self.major_only_filename = self.builder.fmt("lib{}.so.{d}", self.name, self.version.major); 1218 self.name_only_filename = self.builder.fmt("lib{}.so", self.name); 1219 self.out_lib_filename = self.out_filename; 1220 } 1221 } 1222 }, 1223 } 1224 } 1225 1226 /// Deprecated. Use `setTheTarget`. 1227 pub fn setTarget( 1228 self: *LibExeObjStep, 1229 target_arch: builtin.Arch, 1230 target_os: builtin.Os, 1231 target_abi: builtin.Abi, 1232 ) void { 1233 return self.setTheTarget(Target{ 1234 .Cross = CrossTarget{ 1235 .arch = target_arch, 1236 .os = target_os, 1237 .abi = target_abi, 1238 }, 1239 }); 1240 } 1241 1242 pub fn setTheTarget(self: *LibExeObjStep, target: Target) void { 1243 self.target = target; 1244 self.computeOutFileNames(); 1245 } 1246 1247 pub fn setTargetGLibC(self: *LibExeObjStep, major: u32, minor: u32, patch: u32) void { 1248 self.target_glibc = Version{ 1249 .major = major, 1250 .minor = minor, 1251 .patch = patch, 1252 }; 1253 } 1254 1255 pub fn setOutputDir(self: *LibExeObjStep, dir: []const u8) void { 1256 self.output_dir = self.builder.dupePath(dir); 1257 } 1258 1259 pub fn install(self: *LibExeObjStep) void { 1260 self.builder.installArtifact(self); 1261 } 1262 1263 /// Creates a `RunStep` with an executable built with `addExecutable`. 1264 /// Add command line arguments with `addArg`. 1265 pub fn run(exe: *LibExeObjStep) *RunStep { 1266 assert(exe.kind == Kind.Exe); 1267 1268 // It doesn't have to be native. We catch that if you actually try to run it. 1269 // Consider that this is declarative; the run step may not be run unless a user 1270 // option is supplied. 1271 const run_step = RunStep.create(exe.builder, exe.builder.fmt("run {}", exe.step.name)); 1272 run_step.addArtifactArg(exe); 1273 1274 if (exe.vcpkg_bin_path) |path| { 1275 run_step.addPathDir(path); 1276 } 1277 1278 return run_step; 1279 } 1280 1281 pub fn setLinkerScriptPath(self: *LibExeObjStep, path: []const u8) void { 1282 self.linker_script = path; 1283 } 1284 1285 pub fn linkFramework(self: *LibExeObjStep, framework_name: []const u8) void { 1286 assert(self.target.isDarwin()); 1287 self.frameworks.put(framework_name) catch unreachable; 1288 } 1289 1290 /// Returns whether the library, executable, or object depends on a particular system library. 1291 pub fn dependsOnSystemLibrary(self: LibExeObjStep, name: []const u8) bool { 1292 if (isLibCLibrary(name)) { 1293 return self.is_linking_libc; 1294 } 1295 for (self.link_objects.toSliceConst()) |link_object| { 1296 switch (link_object) { 1297 LinkObject.SystemLib => |n| if (mem.eql(u8, n, name)) return true, 1298 else => continue, 1299 } 1300 } 1301 return false; 1302 } 1303 1304 pub fn linkLibrary(self: *LibExeObjStep, lib: *LibExeObjStep) void { 1305 assert(lib.kind == Kind.Lib); 1306 self.linkLibraryOrObject(lib); 1307 } 1308 1309 pub fn isDynamicLibrary(self: *LibExeObjStep) bool { 1310 return self.kind == Kind.Lib and self.is_dynamic; 1311 } 1312 1313 pub fn producesPdbFile(self: *LibExeObjStep) bool { 1314 if (!self.target.isWindows() and !self.target.isUefi()) return false; 1315 if (self.strip) return false; 1316 return self.isDynamicLibrary() or self.kind == .Exe; 1317 } 1318 1319 pub fn linkLibC(self: *LibExeObjStep) void { 1320 if (!self.is_linking_libc) { 1321 self.is_linking_libc = true; 1322 self.link_objects.append(LinkObject{ .SystemLib = "c" }) catch unreachable; 1323 } 1324 } 1325 1326 /// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1. 1327 pub fn defineCMacro(self: *LibExeObjStep, name_and_value: []const u8) void { 1328 self.c_macros.append(self.builder.dupe(name_and_value)) catch unreachable; 1329 } 1330 1331 /// This one has no integration with anything, it just puts -lname on the command line. 1332 /// Prefer to use `linkSystemLibrary` instead. 1333 pub fn linkSystemLibraryName(self: *LibExeObjStep, name: []const u8) void { 1334 self.link_objects.append(LinkObject{ .SystemLib = self.builder.dupe(name) }) catch unreachable; 1335 self.need_system_paths = true; 1336 } 1337 1338 /// This links against a system library, exclusively using pkg-config to find the library. 1339 /// Prefer to use `linkSystemLibrary` instead. 1340 pub fn linkSystemLibraryPkgConfigOnly(self: *LibExeObjStep, lib_name: []const u8) !void { 1341 const pkg_name = match: { 1342 // First we have to map the library name to pkg config name. Unfortunately, 1343 // there are several examples where this is not straightforward: 1344 // -lSDL2 -> pkg-config sdl2 1345 // -lgdk-3 -> pkg-config gdk-3.0 1346 // -latk-1.0 -> pkg-config atk 1347 const pkgs = try self.builder.getPkgConfigList(); 1348 1349 // Exact match means instant winner. 1350 for (pkgs) |pkg| { 1351 if (mem.eql(u8, pkg.name, lib_name)) { 1352 break :match pkg.name; 1353 } 1354 } 1355 1356 // Next we'll try ignoring case. 1357 for (pkgs) |pkg| { 1358 if (std.ascii.eqlIgnoreCase(pkg.name, lib_name)) { 1359 break :match pkg.name; 1360 } 1361 } 1362 1363 // Now try appending ".0". 1364 for (pkgs) |pkg| { 1365 if (std.ascii.indexOfIgnoreCase(pkg.name, lib_name)) |pos| { 1366 if (pos != 0) continue; 1367 if (mem.eql(u8, pkg.name[lib_name.len..], ".0")) { 1368 break :match pkg.name; 1369 } 1370 } 1371 } 1372 1373 // Trimming "-1.0". 1374 if (mem.endsWith(u8, lib_name, "-1.0")) { 1375 const trimmed_lib_name = lib_name[0 .. lib_name.len - "-1.0".len]; 1376 for (pkgs) |pkg| { 1377 if (std.ascii.eqlIgnoreCase(pkg.name, trimmed_lib_name)) { 1378 break :match pkg.name; 1379 } 1380 } 1381 } 1382 1383 return error.PackageNotFound; 1384 }; 1385 1386 var code: u8 = undefined; 1387 const stdout = if (self.builder.execAllowFail(&[_][]const u8{ 1388 "pkg-config", 1389 pkg_name, 1390 "--cflags", 1391 "--libs", 1392 }, &code, .Ignore)) |stdout| stdout else |err| switch (err) { 1393 error.ProcessTerminated => return error.PkgConfigCrashed, 1394 error.ExitCodeFailure => return error.PkgConfigFailed, 1395 error.FileNotFound => return error.PkgConfigNotInstalled, 1396 else => return err, 1397 }; 1398 var it = mem.tokenize(stdout, " \r\n\t"); 1399 while (it.next()) |tok| { 1400 if (mem.eql(u8, tok, "-I")) { 1401 const dir = it.next() orelse return error.PkgConfigInvalidOutput; 1402 self.addIncludeDir(dir); 1403 } else if (mem.startsWith(u8, tok, "-I")) { 1404 self.addIncludeDir(tok["-I".len..]); 1405 } else if (mem.eql(u8, tok, "-L")) { 1406 const dir = it.next() orelse return error.PkgConfigInvalidOutput; 1407 self.addLibPath(dir); 1408 } else if (mem.startsWith(u8, tok, "-L")) { 1409 self.addLibPath(tok["-L".len..]); 1410 } else if (mem.eql(u8, tok, "-l")) { 1411 const lib = it.next() orelse return error.PkgConfigInvalidOutput; 1412 self.linkSystemLibraryName(lib); 1413 } else if (mem.startsWith(u8, tok, "-l")) { 1414 self.linkSystemLibraryName(tok["-l".len..]); 1415 } else if (mem.eql(u8, tok, "-D")) { 1416 const macro = it.next() orelse return error.PkgConfigInvalidOutput; 1417 self.defineCMacro(macro); 1418 } else if (mem.startsWith(u8, tok, "-D")) { 1419 self.defineCMacro(tok["-D".len..]); 1420 } else if (mem.eql(u8, tok, "-pthread")) { 1421 self.linkLibC(); 1422 } else if (self.builder.verbose) { 1423 warn("Ignoring pkg-config flag '{}'\n", tok); 1424 } 1425 } 1426 } 1427 1428 pub fn linkSystemLibrary(self: *LibExeObjStep, name: []const u8) void { 1429 if (isLibCLibrary(name)) { 1430 self.linkLibC(); 1431 return; 1432 } 1433 if (self.linkSystemLibraryPkgConfigOnly(name)) |_| { 1434 // pkg-config worked, so nothing further needed to do. 1435 return; 1436 } else |err| switch (err) { 1437 error.PkgConfigInvalidOutput, 1438 error.PkgConfigCrashed, 1439 error.PkgConfigFailed, 1440 error.PkgConfigNotInstalled, 1441 error.PackageNotFound, 1442 => {}, 1443 1444 else => unreachable, 1445 } 1446 1447 self.linkSystemLibraryName(name); 1448 } 1449 1450 pub fn setNamePrefix(self: *LibExeObjStep, text: []const u8) void { 1451 assert(self.kind == Kind.Test); 1452 self.name_prefix = text; 1453 } 1454 1455 pub fn setFilter(self: *LibExeObjStep, text: ?[]const u8) void { 1456 assert(self.kind == Kind.Test); 1457 self.filter = text; 1458 } 1459 1460 pub fn addCSourceFile(self: *LibExeObjStep, file: []const u8, args: []const []const u8) void { 1461 const c_source_file = self.builder.allocator.create(CSourceFile) catch unreachable; 1462 const args_copy = self.builder.allocator.alloc([]u8, args.len) catch unreachable; 1463 for (args) |arg, i| { 1464 args_copy[i] = self.builder.dupe(arg); 1465 } 1466 c_source_file.* = CSourceFile{ 1467 .source_path = self.builder.dupe(file), 1468 .args = args_copy, 1469 }; 1470 self.link_objects.append(LinkObject{ .CSourceFile = c_source_file }) catch unreachable; 1471 } 1472 1473 pub fn setVerboseLink(self: *LibExeObjStep, value: bool) void { 1474 self.verbose_link = value; 1475 } 1476 1477 pub fn setVerboseCC(self: *LibExeObjStep, value: bool) void { 1478 self.verbose_cc = value; 1479 } 1480 1481 pub fn setBuildMode(self: *LibExeObjStep, mode: builtin.Mode) void { 1482 self.build_mode = mode; 1483 } 1484 1485 pub fn overrideZigLibDir(self: *LibExeObjStep, dir_path: []const u8) void { 1486 self.override_lib_dir = self.builder.dupe(dir_path); 1487 } 1488 1489 pub fn setMainPkgPath(self: *LibExeObjStep, dir_path: []const u8) void { 1490 self.main_pkg_path = dir_path; 1491 } 1492 1493 pub fn setDisableGenH(self: *LibExeObjStep, value: bool) void { 1494 self.disable_gen_h = value; 1495 } 1496 1497 pub fn setLibCFile(self: *LibExeObjStep, libc_file: ?[]const u8) void { 1498 self.libc_file = libc_file; 1499 } 1500 1501 /// Unless setOutputDir was called, this function must be called only in 1502 /// the make step, from a step that has declared a dependency on this one. 1503 /// To run an executable built with zig build, use `run`, or create an install step and invoke it. 1504 pub fn getOutputPath(self: *LibExeObjStep) []const u8 { 1505 return fs.path.join( 1506 self.builder.allocator, 1507 &[_][]const u8{ self.output_dir.?, self.out_filename }, 1508 ) catch unreachable; 1509 } 1510 1511 /// Unless setOutputDir was called, this function must be called only in 1512 /// the make step, from a step that has declared a dependency on this one. 1513 pub fn getOutputLibPath(self: *LibExeObjStep) []const u8 { 1514 assert(self.kind == Kind.Lib); 1515 return fs.path.join( 1516 self.builder.allocator, 1517 &[_][]const u8{ self.output_dir.?, self.out_lib_filename }, 1518 ) catch unreachable; 1519 } 1520 1521 /// Unless setOutputDir was called, this function must be called only in 1522 /// the make step, from a step that has declared a dependency on this one. 1523 pub fn getOutputHPath(self: *LibExeObjStep) []const u8 { 1524 assert(self.kind != Kind.Exe); 1525 assert(!self.disable_gen_h); 1526 return fs.path.join( 1527 self.builder.allocator, 1528 &[_][]const u8{ self.output_dir.?, self.out_h_filename }, 1529 ) catch unreachable; 1530 } 1531 1532 /// Unless setOutputDir was called, this function must be called only in 1533 /// the make step, from a step that has declared a dependency on this one. 1534 pub fn getOutputPdbPath(self: *LibExeObjStep) []const u8 { 1535 assert(self.target.isWindows() or self.target.isUefi()); 1536 return fs.path.join( 1537 self.builder.allocator, 1538 &[_][]const u8{ self.output_dir.?, self.out_pdb_filename }, 1539 ) catch unreachable; 1540 } 1541 1542 pub fn addAssemblyFile(self: *LibExeObjStep, path: []const u8) void { 1543 self.link_objects.append(LinkObject{ .AssemblyFile = self.builder.dupe(path) }) catch unreachable; 1544 } 1545 1546 pub fn addObjectFile(self: *LibExeObjStep, path: []const u8) void { 1547 self.link_objects.append(LinkObject{ .StaticPath = self.builder.dupe(path) }) catch unreachable; 1548 } 1549 1550 pub fn addObject(self: *LibExeObjStep, obj: *LibExeObjStep) void { 1551 assert(obj.kind == Kind.Obj); 1552 self.linkLibraryOrObject(obj); 1553 } 1554 1555 pub fn addBuildOption(self: *LibExeObjStep, comptime T: type, name: []const u8, value: T) void { 1556 const out = &std.io.BufferOutStream.init(&self.build_options_contents).stream; 1557 out.print("pub const {} = {};\n", name, value) catch unreachable; 1558 } 1559 1560 pub fn addSystemIncludeDir(self: *LibExeObjStep, path: []const u8) void { 1561 self.include_dirs.append(IncludeDir{ .RawPathSystem = self.builder.dupe(path) }) catch unreachable; 1562 } 1563 1564 pub fn addIncludeDir(self: *LibExeObjStep, path: []const u8) void { 1565 self.include_dirs.append(IncludeDir{ .RawPath = self.builder.dupe(path) }) catch unreachable; 1566 } 1567 1568 pub fn addLibPath(self: *LibExeObjStep, path: []const u8) void { 1569 self.lib_paths.append(path) catch unreachable; 1570 } 1571 1572 pub fn addFrameworkDir(self: *LibExeObjStep, dir_path: []const u8) void { 1573 self.framework_dirs.append(dir_path) catch unreachable; 1574 } 1575 1576 pub fn addPackagePath(self: *LibExeObjStep, name: []const u8, pkg_index_path: []const u8) void { 1577 self.packages.append(Pkg{ 1578 .name = name, 1579 .path = pkg_index_path, 1580 }) catch unreachable; 1581 } 1582 1583 /// If Vcpkg was found on the system, it will be added to include and lib 1584 /// paths for the specified target. 1585 pub fn addVcpkgPaths(self: *LibExeObjStep, linkage: VcpkgLinkage) !void { 1586 // Ideally in the Unattempted case we would call the function recursively 1587 // after findVcpkgRoot and have only one switch statement, but the compiler 1588 // cannot resolve the error set. 1589 switch (self.builder.vcpkg_root) { 1590 .Unattempted => { 1591 self.builder.vcpkg_root = if (try findVcpkgRoot(self.builder.allocator)) |root| 1592 VcpkgRoot{ .Found = root } 1593 else 1594 .NotFound; 1595 }, 1596 .NotFound => return error.VcpkgNotFound, 1597 .Found => {}, 1598 } 1599 1600 switch (self.builder.vcpkg_root) { 1601 .Unattempted => unreachable, 1602 .NotFound => return error.VcpkgNotFound, 1603 .Found => |root| { 1604 const allocator = self.builder.allocator; 1605 const triplet = try Target.vcpkgTriplet(allocator, self.target, linkage); 1606 defer self.builder.allocator.free(triplet); 1607 1608 const include_path = try fs.path.join(allocator, &[_][]const u8{ root, "installed", triplet, "include" }); 1609 errdefer allocator.free(include_path); 1610 try self.include_dirs.append(IncludeDir{ .RawPath = include_path }); 1611 1612 const lib_path = try fs.path.join(allocator, &[_][]const u8{ root, "installed", triplet, "lib" }); 1613 try self.lib_paths.append(lib_path); 1614 1615 self.vcpkg_bin_path = try fs.path.join(allocator, &[_][]const u8{ root, "installed", triplet, "bin" }); 1616 }, 1617 } 1618 } 1619 1620 pub fn setExecCmd(self: *LibExeObjStep, args: []const ?[]const u8) void { 1621 assert(self.kind == Kind.Test); 1622 self.exec_cmd_args = args; 1623 } 1624 1625 pub fn enableSystemLinkerHack(self: *LibExeObjStep) void { 1626 self.system_linker_hack = true; 1627 } 1628 1629 fn linkLibraryOrObject(self: *LibExeObjStep, other: *LibExeObjStep) void { 1630 self.step.dependOn(&other.step); 1631 self.link_objects.append(LinkObject{ .OtherStep = other }) catch unreachable; 1632 self.include_dirs.append(IncludeDir{ .OtherStep = other }) catch unreachable; 1633 1634 // Inherit dependency on system libraries 1635 for (other.link_objects.toSliceConst()) |link_object| { 1636 switch (link_object) { 1637 .SystemLib => |name| self.linkSystemLibrary(name), 1638 else => continue, 1639 } 1640 } 1641 1642 // Inherit dependencies on darwin frameworks 1643 if (self.target.isDarwin() and !other.isDynamicLibrary()) { 1644 var it = other.frameworks.iterator(); 1645 while (it.next()) |entry| { 1646 self.frameworks.put(entry.key) catch unreachable; 1647 } 1648 } 1649 } 1650 1651 fn make(step: *Step) !void { 1652 const self = @fieldParentPtr(LibExeObjStep, "step", step); 1653 const builder = self.builder; 1654 1655 if (self.root_src == null and self.link_objects.len == 0) { 1656 warn("{}: linker needs 1 or more objects to link\n", self.step.name); 1657 return error.NeedAnObject; 1658 } 1659 1660 var zig_args = ArrayList([]const u8).init(builder.allocator); 1661 defer zig_args.deinit(); 1662 1663 zig_args.append(builder.zig_exe) catch unreachable; 1664 1665 const cmd = switch (self.kind) { 1666 Kind.Lib => "build-lib", 1667 Kind.Exe => "build-exe", 1668 Kind.Obj => "build-obj", 1669 Kind.Test => "test", 1670 }; 1671 zig_args.append(cmd) catch unreachable; 1672 1673 if (self.root_src) |root_src| { 1674 zig_args.append(builder.pathFromRoot(root_src)) catch unreachable; 1675 } 1676 1677 for (self.link_objects.toSlice()) |link_object| { 1678 switch (link_object) { 1679 LinkObject.StaticPath => |static_path| { 1680 try zig_args.append("--object"); 1681 try zig_args.append(builder.pathFromRoot(static_path)); 1682 }, 1683 1684 LinkObject.OtherStep => |other| switch (other.kind) { 1685 LibExeObjStep.Kind.Exe => unreachable, 1686 LibExeObjStep.Kind.Test => unreachable, 1687 LibExeObjStep.Kind.Obj => { 1688 try zig_args.append("--object"); 1689 try zig_args.append(other.getOutputPath()); 1690 }, 1691 LibExeObjStep.Kind.Lib => { 1692 if (!other.is_dynamic or self.target.isWindows()) { 1693 try zig_args.append("--object"); 1694 try zig_args.append(other.getOutputLibPath()); 1695 } else { 1696 const full_path_lib = other.getOutputPath(); 1697 try zig_args.append("--library"); 1698 try zig_args.append(full_path_lib); 1699 1700 if (fs.path.dirname(full_path_lib)) |dirname| { 1701 try zig_args.append("-rpath"); 1702 try zig_args.append(dirname); 1703 } 1704 } 1705 }, 1706 }, 1707 LinkObject.SystemLib => |name| { 1708 try zig_args.append("--library"); 1709 try zig_args.append(name); 1710 }, 1711 LinkObject.AssemblyFile => |asm_file| { 1712 try zig_args.append("--c-source"); 1713 try zig_args.append(builder.pathFromRoot(asm_file)); 1714 }, 1715 LinkObject.CSourceFile => |c_source_file| { 1716 try zig_args.append("--c-source"); 1717 for (c_source_file.args) |arg| { 1718 try zig_args.append(arg); 1719 } 1720 try zig_args.append(self.builder.pathFromRoot(c_source_file.source_path)); 1721 }, 1722 } 1723 } 1724 1725 if (self.build_options_contents.len() > 0) { 1726 const build_options_file = try fs.path.join( 1727 builder.allocator, 1728 &[_][]const u8{ builder.cache_root, builder.fmt("{}_build_options.zig", self.name) }, 1729 ); 1730 try std.io.writeFile(build_options_file, self.build_options_contents.toSliceConst()); 1731 try zig_args.append("--pkg-begin"); 1732 try zig_args.append("build_options"); 1733 try zig_args.append(builder.pathFromRoot(build_options_file)); 1734 try zig_args.append("--pkg-end"); 1735 } 1736 1737 if (self.filter) |filter| { 1738 try zig_args.append("--test-filter"); 1739 try zig_args.append(filter); 1740 } 1741 1742 if (self.name_prefix.len != 0) { 1743 try zig_args.append("--test-name-prefix"); 1744 try zig_args.append(self.name_prefix); 1745 } 1746 1747 if (builder.verbose_tokenize) zig_args.append("--verbose-tokenize") catch unreachable; 1748 if (builder.verbose_ast) zig_args.append("--verbose-ast") catch unreachable; 1749 if (builder.verbose_cimport) zig_args.append("--verbose-cimport") catch unreachable; 1750 if (builder.verbose_ir) zig_args.append("--verbose-ir") catch unreachable; 1751 if (builder.verbose_llvm_ir) zig_args.append("--verbose-llvm-ir") catch unreachable; 1752 if (builder.verbose_link or self.verbose_link) zig_args.append("--verbose-link") catch unreachable; 1753 if (builder.verbose_cc or self.verbose_cc) zig_args.append("--verbose-cc") catch unreachable; 1754 1755 if (self.strip) { 1756 zig_args.append("--strip") catch unreachable; 1757 } 1758 1759 if (self.single_threaded) { 1760 try zig_args.append("--single-threaded"); 1761 } 1762 1763 if (self.libc_file) |libc_file| { 1764 try zig_args.append("--libc"); 1765 try zig_args.append(builder.pathFromRoot(libc_file)); 1766 } 1767 1768 switch (self.build_mode) { 1769 builtin.Mode.Debug => {}, 1770 builtin.Mode.ReleaseSafe => zig_args.append("--release-safe") catch unreachable, 1771 builtin.Mode.ReleaseFast => zig_args.append("--release-fast") catch unreachable, 1772 builtin.Mode.ReleaseSmall => zig_args.append("--release-small") catch unreachable, 1773 } 1774 1775 try zig_args.append("--cache-dir"); 1776 try zig_args.append(builder.pathFromRoot(builder.cache_root)); 1777 1778 zig_args.append("--name") catch unreachable; 1779 zig_args.append(self.name) catch unreachable; 1780 1781 if (self.kind == Kind.Lib and self.is_dynamic) { 1782 zig_args.append("--ver-major") catch unreachable; 1783 zig_args.append(builder.fmt("{}", self.version.major)) catch unreachable; 1784 1785 zig_args.append("--ver-minor") catch unreachable; 1786 zig_args.append(builder.fmt("{}", self.version.minor)) catch unreachable; 1787 1788 zig_args.append("--ver-patch") catch unreachable; 1789 zig_args.append(builder.fmt("{}", self.version.patch)) catch unreachable; 1790 } 1791 if (self.is_dynamic) { 1792 try zig_args.append("-dynamic"); 1793 } 1794 if (self.disable_gen_h) { 1795 try zig_args.append("--disable-gen-h"); 1796 } 1797 if (self.bundle_compiler_rt) { 1798 try zig_args.append("--bundle-compiler-rt"); 1799 } 1800 if (self.disable_stack_probing) { 1801 try zig_args.append("-fno-stack-check"); 1802 } 1803 1804 switch (self.target) { 1805 .Native => {}, 1806 .Cross => { 1807 try zig_args.append("-target"); 1808 try zig_args.append(self.target.zigTriple(builder.allocator) catch unreachable); 1809 }, 1810 } 1811 1812 if (self.target_glibc) |ver| { 1813 try zig_args.append("-target-glibc"); 1814 try zig_args.append(builder.fmt("{}.{}.{}", ver.major, ver.minor, ver.patch)); 1815 } 1816 1817 if (self.linker_script) |linker_script| { 1818 zig_args.append("--linker-script") catch unreachable; 1819 zig_args.append(builder.pathFromRoot(linker_script)) catch unreachable; 1820 } 1821 1822 if (self.dynamic_linker) |dynamic_linker| { 1823 try zig_args.append("--dynamic-linker"); 1824 try zig_args.append(dynamic_linker); 1825 } 1826 1827 if (self.version_script) |version_script| { 1828 try zig_args.append("--version-script"); 1829 try zig_args.append(builder.pathFromRoot(version_script)); 1830 } 1831 1832 if (self.exec_cmd_args) |exec_cmd_args| { 1833 for (exec_cmd_args) |cmd_arg| { 1834 if (cmd_arg) |arg| { 1835 try zig_args.append("--test-cmd"); 1836 try zig_args.append(arg); 1837 } else { 1838 try zig_args.append("--test-cmd-bin"); 1839 } 1840 } 1841 } else switch (self.target.getExternalExecutor()) { 1842 .native, .unavailable => {}, 1843 .qemu => |bin_name| if (self.enable_qemu) qemu: { 1844 const need_cross_glibc = self.target.isGnu() and self.target.isLinux() and self.is_linking_libc; 1845 const glibc_dir_arg = if (need_cross_glibc) 1846 self.glibc_multi_install_dir orelse break :qemu 1847 else 1848 null; 1849 try zig_args.append("--test-cmd"); 1850 try zig_args.append(bin_name); 1851 if (glibc_dir_arg) |dir| { 1852 const full_dir = try fs.path.join(builder.allocator, &[_][]const u8{ 1853 dir, 1854 try self.target.linuxTriple(builder.allocator), 1855 }); 1856 1857 try zig_args.append("--test-cmd"); 1858 try zig_args.append("-L"); 1859 try zig_args.append("--test-cmd"); 1860 try zig_args.append(full_dir); 1861 } 1862 try zig_args.append("--test-cmd-bin"); 1863 }, 1864 .wine => |bin_name| if (self.enable_wine) { 1865 try zig_args.append("--test-cmd"); 1866 try zig_args.append(bin_name); 1867 try zig_args.append("--test-cmd-bin"); 1868 }, 1869 .wasmtime => |bin_name| if (self.enable_wasmtime) { 1870 try zig_args.append("--test-cmd"); 1871 try zig_args.append(bin_name); 1872 try zig_args.append("--test-cmd-bin"); 1873 }, 1874 } 1875 for (self.packages.toSliceConst()) |pkg| { 1876 zig_args.append("--pkg-begin") catch unreachable; 1877 zig_args.append(pkg.name) catch unreachable; 1878 zig_args.append(builder.pathFromRoot(pkg.path)) catch unreachable; 1879 zig_args.append("--pkg-end") catch unreachable; 1880 } 1881 1882 for (self.include_dirs.toSliceConst()) |include_dir| { 1883 switch (include_dir) { 1884 .RawPath => |include_path| { 1885 try zig_args.append("-I"); 1886 try zig_args.append(self.builder.pathFromRoot(include_path)); 1887 }, 1888 .RawPathSystem => |include_path| { 1889 try zig_args.append("-isystem"); 1890 try zig_args.append(self.builder.pathFromRoot(include_path)); 1891 }, 1892 .OtherStep => |other| { 1893 const h_path = other.getOutputHPath(); 1894 try zig_args.append("-isystem"); 1895 try zig_args.append(fs.path.dirname(h_path).?); 1896 }, 1897 } 1898 } 1899 1900 for (self.lib_paths.toSliceConst()) |lib_path| { 1901 try zig_args.append("-L"); 1902 try zig_args.append(lib_path); 1903 } 1904 1905 if (self.need_system_paths and self.target == Target.Native) { 1906 for (builder.native_system_include_dirs.toSliceConst()) |include_path| { 1907 zig_args.append("-isystem") catch unreachable; 1908 zig_args.append(builder.pathFromRoot(include_path)) catch unreachable; 1909 } 1910 1911 for (builder.native_system_rpaths.toSliceConst()) |rpath| { 1912 zig_args.append("-rpath") catch unreachable; 1913 zig_args.append(rpath) catch unreachable; 1914 } 1915 1916 for (builder.native_system_lib_paths.toSliceConst()) |lib_path| { 1917 zig_args.append("--library-path") catch unreachable; 1918 zig_args.append(lib_path) catch unreachable; 1919 } 1920 } 1921 1922 for (self.c_macros.toSliceConst()) |c_macro| { 1923 try zig_args.append("-D"); 1924 try zig_args.append(c_macro); 1925 } 1926 1927 if (self.target.isDarwin()) { 1928 for (self.framework_dirs.toSliceConst()) |dir| { 1929 try zig_args.append("-F"); 1930 try zig_args.append(dir); 1931 } 1932 1933 var it = self.frameworks.iterator(); 1934 while (it.next()) |entry| { 1935 zig_args.append("-framework") catch unreachable; 1936 zig_args.append(entry.key) catch unreachable; 1937 } 1938 } 1939 1940 if (self.system_linker_hack) { 1941 try zig_args.append("--system-linker-hack"); 1942 } 1943 1944 if (self.valgrind_support) |valgrind_support| { 1945 if (valgrind_support) { 1946 try zig_args.append("--enable-valgrind"); 1947 } else { 1948 try zig_args.append("--disable-valgrind"); 1949 } 1950 } 1951 1952 if (self.override_lib_dir) |dir| { 1953 try zig_args.append("--override-lib-dir"); 1954 try zig_args.append(builder.pathFromRoot(dir)); 1955 } else if (self.builder.override_lib_dir) |dir| { 1956 try zig_args.append("--override-lib-dir"); 1957 try zig_args.append(builder.pathFromRoot(dir)); 1958 } 1959 1960 if (self.main_pkg_path) |dir| { 1961 try zig_args.append("--main-pkg-path"); 1962 try zig_args.append(builder.pathFromRoot(dir)); 1963 } 1964 1965 if (self.force_pic) |pic| { 1966 if (pic) { 1967 try zig_args.append("-fPIC"); 1968 } else { 1969 try zig_args.append("-fno-PIC"); 1970 } 1971 } 1972 1973 if (self.subsystem) |subsystem| { 1974 try zig_args.append("--subsystem"); 1975 try zig_args.append(switch (subsystem) { 1976 .Console => "console", 1977 .Windows => "windows", 1978 .Posix => "posix", 1979 .Native => "native", 1980 .EfiApplication => "efi_application", 1981 .EfiBootServiceDriver => "efi_boot_service_driver", 1982 .EfiRom => "efi_rom", 1983 .EfiRuntimeDriver => "efi_runtime_driver", 1984 }); 1985 } 1986 1987 if (self.kind == Kind.Test) { 1988 try builder.spawnChild(zig_args.toSliceConst()); 1989 } else { 1990 try zig_args.append("--cache"); 1991 try zig_args.append("on"); 1992 1993 const output_path_nl = try builder.exec(zig_args.toSliceConst()); 1994 const output_path = mem.trimRight(u8, output_path_nl, "\r\n"); 1995 1996 if (self.output_dir) |output_dir| { 1997 const full_dest = try fs.path.join(builder.allocator, &[_][]const u8{ 1998 output_dir, 1999 fs.path.basename(output_path), 2000 }); 2001 try builder.updateFile(output_path, full_dest); 2002 } else { 2003 self.output_dir = fs.path.dirname(output_path).?; 2004 } 2005 } 2006 2007 if (self.kind == Kind.Lib and self.is_dynamic and self.target.wantSharedLibSymLinks()) { 2008 try doAtomicSymLinks(builder.allocator, self.getOutputPath(), self.major_only_filename, self.name_only_filename); 2009 } 2010 } 2011 }; 2012 2013 pub const RunStep = struct { 2014 step: Step, 2015 builder: *Builder, 2016 2017 /// See also addArg and addArgs to modifying this directly 2018 argv: ArrayList(Arg), 2019 2020 /// Set this to modify the current working directory 2021 cwd: ?[]const u8, 2022 2023 /// Override this field to modify the environment, or use setEnvironmentVariable 2024 env_map: ?*BufMap, 2025 2026 pub const Arg = union(enum) { 2027 Artifact: *LibExeObjStep, 2028 Bytes: []u8, 2029 }; 2030 2031 pub fn create(builder: *Builder, name: []const u8) *RunStep { 2032 const self = builder.allocator.create(RunStep) catch unreachable; 2033 self.* = RunStep{ 2034 .builder = builder, 2035 .step = Step.init(name, builder.allocator, make), 2036 .argv = ArrayList(Arg).init(builder.allocator), 2037 .cwd = null, 2038 .env_map = null, 2039 }; 2040 return self; 2041 } 2042 2043 pub fn addArtifactArg(self: *RunStep, artifact: *LibExeObjStep) void { 2044 self.argv.append(Arg{ .Artifact = artifact }) catch unreachable; 2045 self.step.dependOn(&artifact.step); 2046 } 2047 2048 pub fn addArg(self: *RunStep, arg: []const u8) void { 2049 self.argv.append(Arg{ .Bytes = self.builder.dupe(arg) }) catch unreachable; 2050 } 2051 2052 pub fn addArgs(self: *RunStep, args: []const []const u8) void { 2053 for (args) |arg| { 2054 self.addArg(arg); 2055 } 2056 } 2057 2058 pub fn clearEnvironment(self: *RunStep) void { 2059 const new_env_map = self.builder.allocator.create(BufMap) catch unreachable; 2060 new_env_map.* = BufMap.init(self.builder.allocator); 2061 self.env_map = new_env_map; 2062 } 2063 2064 pub fn addPathDir(self: *RunStep, search_path: []const u8) void { 2065 const env_map = self.getEnvMap(); 2066 2067 var key: []const u8 = undefined; 2068 var prev_path: ?[]const u8 = undefined; 2069 if (builtin.os == .windows) { 2070 key = "Path"; 2071 prev_path = env_map.get(key); 2072 if (prev_path == null) { 2073 key = "PATH"; 2074 prev_path = env_map.get(key); 2075 } 2076 } else { 2077 key = "PATH"; 2078 prev_path = env_map.get(key); 2079 } 2080 2081 if (prev_path) |pp| { 2082 const new_path = self.builder.fmt("{}" ++ [1]u8{fs.path.delimiter} ++ "{}", pp, search_path); 2083 env_map.set(key, new_path) catch unreachable; 2084 } else { 2085 env_map.set(key, search_path) catch unreachable; 2086 } 2087 } 2088 2089 pub fn getEnvMap(self: *RunStep) *BufMap { 2090 return self.env_map orelse { 2091 const env_map = self.builder.allocator.create(BufMap) catch unreachable; 2092 env_map.* = process.getEnvMap(self.builder.allocator) catch unreachable; 2093 self.env_map = env_map; 2094 return env_map; 2095 }; 2096 } 2097 2098 pub fn setEnvironmentVariable(self: *RunStep, key: []const u8, value: []const u8) void { 2099 const env_map = self.getEnvMap(); 2100 env_map.set(key, value) catch unreachable; 2101 } 2102 2103 fn make(step: *Step) !void { 2104 const self = @fieldParentPtr(RunStep, "step", step); 2105 2106 const cwd = if (self.cwd) |cwd| self.builder.pathFromRoot(cwd) else self.builder.build_root; 2107 2108 var argv = ArrayList([]const u8).init(self.builder.allocator); 2109 for (self.argv.toSlice()) |arg| { 2110 switch (arg) { 2111 Arg.Bytes => |bytes| try argv.append(bytes), 2112 Arg.Artifact => |artifact| { 2113 if (artifact.target.isWindows()) { 2114 // On Windows we don't have rpaths so we have to add .dll search paths to PATH 2115 self.addPathForDynLibs(artifact); 2116 } 2117 const executable_path = artifact.installed_path orelse artifact.getOutputPath(); 2118 try argv.append(executable_path); 2119 }, 2120 } 2121 } 2122 2123 return self.builder.spawnChildEnvMap(cwd, self.env_map orelse self.builder.env_map, argv.toSliceConst()); 2124 } 2125 2126 fn addPathForDynLibs(self: *RunStep, artifact: *LibExeObjStep) void { 2127 for (artifact.link_objects.toSliceConst()) |link_object| { 2128 switch (link_object) { 2129 LibExeObjStep.LinkObject.OtherStep => |other| { 2130 if (other.target.isWindows() and other.isDynamicLibrary()) { 2131 self.addPathDir(fs.path.dirname(other.getOutputPath()).?); 2132 self.addPathForDynLibs(other); 2133 } 2134 }, 2135 else => {}, 2136 } 2137 } 2138 } 2139 }; 2140 2141 const InstallArtifactStep = struct { 2142 step: Step, 2143 builder: *Builder, 2144 artifact: *LibExeObjStep, 2145 dest_dir: InstallDir, 2146 pdb_dir: ?InstallDir, 2147 2148 const Self = @This(); 2149 2150 pub fn create(builder: *Builder, artifact: *LibExeObjStep) *Self { 2151 if (artifact.install_step) |s| return s; 2152 2153 const self = builder.allocator.create(Self) catch unreachable; 2154 self.* = Self{ 2155 .builder = builder, 2156 .step = Step.init(builder.fmt("install {}", artifact.step.name), builder.allocator, make), 2157 .artifact = artifact, 2158 .dest_dir = switch (artifact.kind) { 2159 .Obj => unreachable, 2160 .Test => unreachable, 2161 .Exe => InstallDir.Bin, 2162 .Lib => InstallDir.Lib, 2163 }, 2164 .pdb_dir = if (artifact.producesPdbFile()) blk: { 2165 if (artifact.kind == .Exe) { 2166 break :blk InstallDir.Bin; 2167 } else { 2168 break :blk InstallDir.Lib; 2169 } 2170 } else null, 2171 }; 2172 self.step.dependOn(&artifact.step); 2173 artifact.install_step = self; 2174 2175 builder.pushInstalledFile(self.dest_dir, artifact.out_filename); 2176 if (self.artifact.isDynamicLibrary()) { 2177 builder.pushInstalledFile(.Lib, artifact.major_only_filename); 2178 builder.pushInstalledFile(.Lib, artifact.name_only_filename); 2179 if (self.artifact.target.isWindows()) { 2180 builder.pushInstalledFile(.Lib, artifact.out_lib_filename); 2181 } 2182 } 2183 if (self.pdb_dir) |pdb_dir| { 2184 builder.pushInstalledFile(pdb_dir, artifact.out_pdb_filename); 2185 } 2186 return self; 2187 } 2188 2189 fn make(step: *Step) !void { 2190 const self = @fieldParentPtr(Self, "step", step); 2191 const builder = self.builder; 2192 2193 const full_dest_path = builder.getInstallPath(self.dest_dir, self.artifact.out_filename); 2194 try builder.updateFile(self.artifact.getOutputPath(), full_dest_path); 2195 if (self.artifact.isDynamicLibrary() and self.artifact.target.wantSharedLibSymLinks()) { 2196 try doAtomicSymLinks(builder.allocator, full_dest_path, self.artifact.major_only_filename, self.artifact.name_only_filename); 2197 } 2198 if (self.pdb_dir) |pdb_dir| { 2199 const full_pdb_path = builder.getInstallPath(pdb_dir, self.artifact.out_pdb_filename); 2200 try builder.updateFile(self.artifact.getOutputPdbPath(), full_pdb_path); 2201 } 2202 self.artifact.installed_path = full_dest_path; 2203 } 2204 }; 2205 2206 pub const InstallFileStep = struct { 2207 step: Step, 2208 builder: *Builder, 2209 src_path: []const u8, 2210 dir: InstallDir, 2211 dest_rel_path: []const u8, 2212 2213 pub fn init( 2214 builder: *Builder, 2215 src_path: []const u8, 2216 dir: InstallDir, 2217 dest_rel_path: []const u8, 2218 ) InstallFileStep { 2219 builder.pushInstalledFile(dir, dest_rel_path); 2220 return InstallFileStep{ 2221 .builder = builder, 2222 .step = Step.init(builder.fmt("install {}", src_path), builder.allocator, make), 2223 .src_path = src_path, 2224 .dir = dir, 2225 .dest_rel_path = dest_rel_path, 2226 }; 2227 } 2228 2229 fn make(step: *Step) !void { 2230 const self = @fieldParentPtr(InstallFileStep, "step", step); 2231 const full_dest_path = self.builder.getInstallPath(self.dir, self.dest_rel_path); 2232 const full_src_path = self.builder.pathFromRoot(self.src_path); 2233 try self.builder.updateFile(full_src_path, full_dest_path); 2234 } 2235 }; 2236 2237 pub const InstallDirectoryOptions = struct { 2238 source_dir: []const u8, 2239 install_dir: InstallDir, 2240 install_subdir: []const u8, 2241 exclude_extensions: ?[]const []const u8 = null, 2242 }; 2243 2244 pub const InstallDirStep = struct { 2245 step: Step, 2246 builder: *Builder, 2247 options: InstallDirectoryOptions, 2248 2249 pub fn init( 2250 builder: *Builder, 2251 options: InstallDirectoryOptions, 2252 ) InstallDirStep { 2253 builder.pushInstalledFile(options.install_dir, options.install_subdir); 2254 return InstallDirStep{ 2255 .builder = builder, 2256 .step = Step.init(builder.fmt("install {}/", options.source_dir), builder.allocator, make), 2257 .options = options, 2258 }; 2259 } 2260 2261 fn make(step: *Step) !void { 2262 const self = @fieldParentPtr(InstallDirStep, "step", step); 2263 const dest_prefix = self.builder.getInstallPath(self.options.install_dir, self.options.install_subdir); 2264 const full_src_dir = self.builder.pathFromRoot(self.options.source_dir); 2265 var it = try fs.walkPath(self.builder.allocator, full_src_dir); 2266 next_entry: while (try it.next()) |entry| { 2267 if (self.options.exclude_extensions) |ext_list| for (ext_list) |ext| { 2268 if (mem.endsWith(u8, entry.path, ext)) { 2269 continue :next_entry; 2270 } 2271 }; 2272 2273 const rel_path = entry.path[full_src_dir.len + 1 ..]; 2274 const dest_path = try fs.path.join(self.builder.allocator, &[_][]const u8{ dest_prefix, rel_path }); 2275 switch (entry.kind) { 2276 .Directory => try fs.makePath(self.builder.allocator, dest_path), 2277 .File => try self.builder.updateFile(entry.path, dest_path), 2278 else => continue, 2279 } 2280 } 2281 } 2282 }; 2283 2284 pub const WriteFileStep = struct { 2285 step: Step, 2286 builder: *Builder, 2287 file_path: []const u8, 2288 data: []const u8, 2289 2290 pub fn init(builder: *Builder, file_path: []const u8, data: []const u8) WriteFileStep { 2291 return WriteFileStep{ 2292 .builder = builder, 2293 .step = Step.init(builder.fmt("writefile {}", file_path), builder.allocator, make), 2294 .file_path = file_path, 2295 .data = data, 2296 }; 2297 } 2298 2299 fn make(step: *Step) !void { 2300 const self = @fieldParentPtr(WriteFileStep, "step", step); 2301 const full_path = self.builder.pathFromRoot(self.file_path); 2302 const full_path_dir = fs.path.dirname(full_path) orelse "."; 2303 fs.makePath(self.builder.allocator, full_path_dir) catch |err| { 2304 warn("unable to make path {}: {}\n", full_path_dir, @errorName(err)); 2305 return err; 2306 }; 2307 io.writeFile(full_path, self.data) catch |err| { 2308 warn("unable to write {}: {}\n", full_path, @errorName(err)); 2309 return err; 2310 }; 2311 } 2312 }; 2313 2314 pub const LogStep = struct { 2315 step: Step, 2316 builder: *Builder, 2317 data: []const u8, 2318 2319 pub fn init(builder: *Builder, data: []const u8) LogStep { 2320 return LogStep{ 2321 .builder = builder, 2322 .step = Step.init(builder.fmt("log {}", data), builder.allocator, make), 2323 .data = data, 2324 }; 2325 } 2326 2327 fn make(step: *Step) anyerror!void { 2328 const self = @fieldParentPtr(LogStep, "step", step); 2329 warn("{}", self.data); 2330 } 2331 }; 2332 2333 pub const RemoveDirStep = struct { 2334 step: Step, 2335 builder: *Builder, 2336 dir_path: []const u8, 2337 2338 pub fn init(builder: *Builder, dir_path: []const u8) RemoveDirStep { 2339 return RemoveDirStep{ 2340 .builder = builder, 2341 .step = Step.init(builder.fmt("RemoveDir {}", dir_path), builder.allocator, make), 2342 .dir_path = dir_path, 2343 }; 2344 } 2345 2346 fn make(step: *Step) !void { 2347 const self = @fieldParentPtr(RemoveDirStep, "step", step); 2348 2349 const full_path = self.builder.pathFromRoot(self.dir_path); 2350 fs.deleteTree(full_path) catch |err| { 2351 warn("Unable to remove {}: {}\n", full_path, @errorName(err)); 2352 return err; 2353 }; 2354 } 2355 }; 2356 2357 pub const Step = struct { 2358 name: []const u8, 2359 makeFn: fn (self: *Step) anyerror!void, 2360 dependencies: ArrayList(*Step), 2361 loop_flag: bool, 2362 done_flag: bool, 2363 2364 pub fn init(name: []const u8, allocator: *Allocator, makeFn: fn (*Step) anyerror!void) Step { 2365 return Step{ 2366 .name = name, 2367 .makeFn = makeFn, 2368 .dependencies = ArrayList(*Step).init(allocator), 2369 .loop_flag = false, 2370 .done_flag = false, 2371 }; 2372 } 2373 pub fn initNoOp(name: []const u8, allocator: *Allocator) Step { 2374 return init(name, allocator, makeNoOp); 2375 } 2376 2377 pub fn make(self: *Step) !void { 2378 if (self.done_flag) return; 2379 2380 try self.makeFn(self); 2381 self.done_flag = true; 2382 } 2383 2384 pub fn dependOn(self: *Step, other: *Step) void { 2385 self.dependencies.append(other) catch unreachable; 2386 } 2387 2388 fn makeNoOp(self: *Step) anyerror!void {} 2389 }; 2390 2391 fn doAtomicSymLinks(allocator: *Allocator, output_path: []const u8, filename_major_only: []const u8, filename_name_only: []const u8) !void { 2392 const out_dir = fs.path.dirname(output_path) orelse "."; 2393 const out_basename = fs.path.basename(output_path); 2394 // sym link for libfoo.so.1 to libfoo.so.1.2.3 2395 const major_only_path = fs.path.join( 2396 allocator, 2397 &[_][]const u8{ out_dir, filename_major_only }, 2398 ) catch unreachable; 2399 fs.atomicSymLink(allocator, out_basename, major_only_path) catch |err| { 2400 warn("Unable to symlink {} -> {}\n", major_only_path, out_basename); 2401 return err; 2402 }; 2403 // sym link for libfoo.so to libfoo.so.1 2404 const name_only_path = fs.path.join( 2405 allocator, 2406 &[_][]const u8{ out_dir, filename_name_only }, 2407 ) catch unreachable; 2408 fs.atomicSymLink(allocator, filename_major_only, name_only_path) catch |err| { 2409 warn("Unable to symlink {} -> {}\n", name_only_path, filename_major_only); 2410 return err; 2411 }; 2412 } 2413 2414 /// Returned slice must be freed by the caller. 2415 fn findVcpkgRoot(allocator: *Allocator) !?[]const u8 { 2416 const appdata_path = try fs.getAppDataDir(allocator, "vcpkg"); 2417 defer allocator.free(appdata_path); 2418 2419 const path_file = try fs.path.join(allocator, &[_][]const u8{ appdata_path, "vcpkg.path.txt" }); 2420 defer allocator.free(path_file); 2421 2422 const file = fs.cwd().openFile(path_file, .{}) catch return null; 2423 defer file.close(); 2424 2425 const size = @intCast(usize, try file.getEndPos()); 2426 const vcpkg_path = try allocator.alloc(u8, size); 2427 const size_read = try file.read(vcpkg_path); 2428 std.debug.assert(size == size_read); 2429 2430 return vcpkg_path; 2431 } 2432 2433 const VcpkgRoot = union(VcpkgRootStatus) { 2434 Unattempted: void, 2435 NotFound: void, 2436 Found: []const u8, 2437 }; 2438 2439 const VcpkgRootStatus = enum { 2440 Unattempted, 2441 NotFound, 2442 Found, 2443 }; 2444 2445 pub const VcpkgLinkage = enum { 2446 Static, 2447 Dynamic, 2448 }; 2449 2450 pub const InstallDir = enum { 2451 Prefix, 2452 Lib, 2453 Bin, 2454 }; 2455 2456 pub const InstalledFile = struct { 2457 dir: InstallDir, 2458 path: []const u8, 2459 };