authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-12-11 20:08:05-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-12-11 20:08:05-05:00
log5238f9c409cdf40db6a1484ab79a7bc518f8f6ad
tree072178dc3d3f783462c82b54838e415d99fd225d
parent4efdbd304488207490e9c686aae98053d258ebd4
parente3ef01c6c7b2e0734c3a21e7c3a69f921f195968
signature Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #13865 from ziglang/std-build-reorg

std.build: extract steps to separate files

15 files changed, 2534 insertions(+), 2469 deletions(-)

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