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