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