| ... | ... | @@ -17,16 +17,23 @@ const File = std.fs.File; |
| 17 | 17 | const CrossTarget = std.zig.CrossTarget; |
| 18 | 18 | const NativeTargetInfo = std.zig.system.NativeTargetInfo; |
| 19 | 19 | const Sha256 = std.crypto.hash.sha2.Sha256; |
| 20 | const ThisModule = @This(); |
| 20 | 21 | |
| 21 | | pub const FmtStep = @import("build/FmtStep.zig"); |
| 22 | | pub const TranslateCStep = @import("build/TranslateCStep.zig"); |
| 23 | | pub const WriteFileStep = @import("build/WriteFileStep.zig"); |
| 24 | | pub const RunStep = @import("build/RunStep.zig"); |
| 25 | 22 | pub const CheckFileStep = @import("build/CheckFileStep.zig"); |
| 26 | 23 | pub const CheckObjectStep = @import("build/CheckObjectStep.zig"); |
| 24 | pub const EmulatableRunStep = @import("build/EmulatableRunStep.zig"); |
| 25 | pub const FmtStep = @import("build/FmtStep.zig"); |
| 26 | pub const InstallArtifactStep = @import("build/InstallArtifactStep.zig"); |
| 27 | pub const InstallDirStep = @import("build/InstallDirStep.zig"); |
| 28 | pub const InstallFileStep = @import("build/InstallFileStep.zig"); |
| 27 | 29 | pub const InstallRawStep = @import("build/InstallRawStep.zig"); |
| 30 | pub const LibExeObjStep = @import("build/LibExeObjStep.zig"); |
| 31 | pub const LogStep = @import("build/LogStep.zig"); |
| 28 | 32 | pub const OptionsStep = @import("build/OptionsStep.zig"); |
| 29 | | pub const EmulatableRunStep = @import("build/EmulatableRunStep.zig"); |
| 33 | pub const RemoveDirStep = @import("build/RemoveDirStep.zig"); |
| 34 | pub const RunStep = @import("build/RunStep.zig"); |
| 35 | pub const TranslateCStep = @import("build/TranslateCStep.zig"); |
| 36 | pub const WriteFileStep = @import("build/WriteFileStep.zig"); |
| 30 | 37 | |
| 31 | 38 | pub const Builder = struct { |
| 32 | 39 | install_tls: TopLevelStep, |
| ... | ... | @@ -1281,46 +1288,6 @@ pub const Builder = struct { |
| 1281 | 1288 | &[_][]const u8{ base_dir, dest_rel_path }, |
| 1282 | 1289 | ) catch unreachable; |
| 1283 | 1290 | } |
| 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 | | } |
| 1324 | 1291 | }; |
| 1325 | 1292 | |
| 1326 | 1293 | test "builder.findProgram compiles" { |
| ... | ... | @@ -1346,41 +1313,6 @@ pub const Pkg = struct { |
| 1346 | 1313 | dependencies: ?[]const Pkg = null, |
| 1347 | 1314 | }; |
| 1348 | 1315 | |
| 1349 | | pub 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 | | |
| 1361 | | const CSourceFiles = struct { |
| 1362 | | files: []const []const u8, |
| 1363 | | flags: []const []const u8, |
| 1364 | | }; |
| 1365 | | |
| 1366 | | fn 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 | | |
| 1375 | | fn 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 | | |
| 1384 | 1316 | /// A file that is generated by a build step. |
| 1385 | 1317 | /// This struct is an interface that is meant to be used with `@fieldParentPtr` to implement the actual path logic. |
| 1386 | 1318 | pub const GeneratedFile = struct { |
| ... | ... | @@ -1451,2381 +1383,195 @@ pub const FileSource = union(enum) { |
| 1451 | 1383 | } |
| 1452 | 1384 | }; |
| 1453 | 1385 | |
| 1454 | | pub 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. |
| 1389 | pub 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); |
| 1711 | 1398 | } |
| 1399 | return macro; |
| 1400 | } |
| 1712 | 1401 | |
| 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` |
| 1403 | pub const InstallDirectoryOptions = InstallDirStep.Options; |
| 1716 | 1404 | |
| 1717 | | pub fn createTest(builder: *Builder, name: []const u8, root_src: FileSource) *LibExeObjStep { |
| 1718 | | return initExtraArgs(builder, name, root_src, .@"test", null, null); |
| 1719 | | } |
| 1405 | pub 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, |
| 1720 | 1412 | |
| 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; |
| 1724 | 1414 | |
| 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 | }; |
| 1738 | 1434 | |
| 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, |
| 1790 | 1443 | }; |
| 1791 | | self.computeOutFileNames(); |
| 1792 | | if (root_src) |rs| rs.addStepDependencies(&self.step); |
| 1793 | | return self; |
| 1794 | 1444 | } |
| 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); |
| 1850 | 1447 | } |
| 1851 | 1448 | |
| 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; |
| 1856 | 1451 | |
| 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; |
| 1859 | 1454 | } |
| 1860 | 1455 | |
| 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; |
| 1863 | 1458 | } |
| 1864 | 1459 | |
| 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; |
| 1867 | 1462 | } |
| 1868 | 1463 | |
| 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); |
| 1882 | 1467 | } |
| 1468 | return null; |
| 1469 | } |
| 1470 | }; |
| 1883 | 1471 | |
| 1884 | | if (exe.vcpkg_bin_path) |path| { |
| 1885 | | run_step.addPathDir(path); |
| 1886 | | } |
| 1472 | const VcpkgRoot = union(VcpkgRootStatus) { |
| 1473 | unattempted: void, |
| 1474 | not_found: void, |
| 1475 | found: []const u8, |
| 1476 | }; |
| 1887 | 1477 | |
| 1888 | | return run_step; |
| 1889 | | } |
| 1478 | const VcpkgRootStatus = enum { |
| 1479 | unattempted, |
| 1480 | not_found, |
| 1481 | found, |
| 1482 | }; |
| 1890 | 1483 | |
| 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); |
| 1484 | pub 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, |
| 1897 | 1491 | |
| 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; |
| 1901 | 1500 | } |
| 1902 | | return run_step; |
| 1903 | 1501 | } |
| 1502 | }; |
| 1904 | 1503 | |
| 1905 | | pub fn checkObject(self: *LibExeObjStep, obj_format: std.Target.ObjectFormat) *CheckObjectStep { |
| 1906 | | return CheckObjectStep.create(self.builder, self.getOutputSource(), obj_format); |
| 1907 | | } |
| 1504 | pub const InstalledFile = struct { |
| 1505 | dir: InstallDir, |
| 1506 | path: []const u8, |
| 1908 | 1507 | |
| 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 | }; |
| 1912 | 1514 | } |
| 1515 | }; |
| 1913 | 1516 | |
| 1914 | | pub fn linkFramework(self: *LibExeObjStep, framework_name: []const u8) void { |
| 1915 | | self.frameworks.put(self.builder.dupe(framework_name), .{}) catch unreachable; |
| 1916 | | } |
| 1517 | test "dupePkg()" { |
| 1518 | if (builtin.os.tag == .wasi) return error.SkipZigTest; |
| 1917 | 1519 | |
| 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(); |
| 1923 | 1530 | |
| 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); |
| 1929 | 1541 | |
| 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.?; |
| 1946 | 1544 | |
| 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); |
| 1951 | 1547 | |
| 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); |
| 1955 | 1551 | |
| 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 | } |
| 1961 | 1560 | |
| 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. |
| 3339 | | pub 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 | | |
| 3352 | | pub 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 | | |
| 3436 | | pub 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 | | |
| 3469 | | pub 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 | | |
| 3494 | | pub 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 | | |
| 3553 | | pub 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 | | |
| 3574 | | pub 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 | | |
| 3600 | | const ThisModule = @This(); |
| 3601 | | pub 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 | | |
| 3668 | | fn 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. |
| 3692 | | fn 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 | | |
| 3710 | | const VcpkgRoot = union(VcpkgRootStatus) { |
| 3711 | | unattempted: void, |
| 3712 | | not_found: void, |
| 3713 | | found: []const u8, |
| 3714 | | }; |
| 3715 | | |
| 3716 | | const VcpkgRootStatus = enum { |
| 3717 | | unattempted, |
| 3718 | | not_found, |
| 3719 | | found, |
| 3720 | | }; |
| 3721 | | |
| 3722 | | pub 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 | | |
| 3742 | | pub 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 | | |
| 3755 | | test "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 | | |
| 3799 | | test "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); |
| 1561 | test { |
| 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; |
| 3831 | 1577 | } |