authorgravatar for mail@isaacfreund.comIsaac Freund <mail@isaacfreund.com> 2026-10-16 17:55:21+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-10-17 20:12:39+02:00
logf2f0d97a9b9df2e4aa43c1e4b7876bcc97cb83ff
tree58224cd6c8a44abfdfb492c3fb9e3c5f6533b34a
parent093d28dfae39a64ef315c334e70ab3b72a698bb7

std.Build.Configuration: add $DESTDIR-relative LazyPaths

The new deployed_prefix and related paths exist to support the use-case of embedding absolute paths to other build artifacts in the build artifacts themselves. For example pkg-config files are artifacts which must contain absolute paths to the other library/header artifacts they describe. Note that this feature is only supported to faciliate interoperability with such legacy systems. In general, embedding absolute paths in build artifacts should be avoided. This patch also fixes a bug: currently passing an absolute path to e.g. --prefix-lib-dir will cause $DESTDIR to be ignored for build artifacts installed to that path. I'm not entirely satisfied with the new "deployed" naming convention, I want to avoid the breaking change of renaming "installed" to something else and this is the best option I came up with. Closes: https://codeberg.org/ziglang/zig/issues/37124

3 files changed, 99 insertions(+), 40 deletions(-)

lib/compiler/Maker.zig+65-40
......@@ -44,7 +44,8 @@ pub const std_options: std.Options = .{
4444
4545gpa: Allocator,
4646graph: *Graph,
47install_paths: InstallPaths,
47install_paths: PrefixPaths,
48deployed_paths: PrefixPaths,
4849scanned_config: *const ScannedConfig,
4950/// Includes an extra auto-generated placeholder Step at the end that indicates
5051/// configure must be rerun. It is done this way so that the hot path of file
......@@ -95,7 +96,7 @@ const use_safe_allocator = switch (builtin.mode) {
9596 .fast, .small => false,
9697};
9798
98const InstallPaths = struct {
99const PrefixPaths = struct {
99100 prefix: Path,
100101 lib: Path,
101102 bin: Path,
......@@ -692,42 +693,62 @@ pub fn main(init: process.Init.Minimal) !void {
692693 });
693694 defer main_progress_node.end();
694695
695 const install_prefix_path: Path = if (graph.environ_map.get("DESTDIR")) |dest_dir| .{
696 .root_dir = .cwd(),
697 .sub_path = try Dir.path.join(arena, &.{ dest_dir, override_install_prefix orelse "/usr" }),
698 } else if (override_install_prefix) |cwd_relative| .{
699 .root_dir = .cwd(),
700 .sub_path = cwd_relative,
701 } else .{
702 .root_dir = graph.build_root_directory,
703 .sub_path = "zig-out",
696 const deployed_paths: PrefixPaths = blk: {
697 const prefix: Path = if (override_install_prefix) |cwd_relative| .{
698 .root_dir = .cwd(),
699 .sub_path = cwd_relative,
700 } else if (graph.environ_map.get("DESTDIR") != null) .{
701 .root_dir = .cwd(),
702 .sub_path = "/usr",
703 } else .{
704 .root_dir = graph.build_root_directory,
705 .sub_path = "zig-out",
706 };
707 break :blk .{
708 .prefix = prefix,
709 // The lib/bin/include overrides are interpreted relative to the
710 // install prefix unless absolute paths are used.
711 .lib = if (override_lib_dir) |lib_dir|
712 if (Dir.path.isAbsolute(lib_dir)) .{
713 .root_dir = .cwd(),
714 .sub_path = lib_dir,
715 } else try prefix.join(arena, lib_dir)
716 else
717 try prefix.join(arena, "lib"),
718 .bin = if (override_bin_dir) |bin_dir|
719 if (Dir.path.isAbsolute(bin_dir)) .{
720 .root_dir = .cwd(),
721 .sub_path = bin_dir,
722 } else try prefix.join(arena, bin_dir)
723 else
724 try prefix.join(arena, "bin"),
725 .include = if (override_include_dir) |include_dir|
726 if (Dir.path.isAbsolute(include_dir)) .{
727 .root_dir = .cwd(),
728 .sub_path = include_dir,
729 } else try prefix.join(arena, include_dir)
730 else
731 try prefix.join(arena, "include"),
732 };
704733 };
705
706 // These three overrides are meant to be relative to the install prefix,
707 // not current working directory, unless absolute paths are used.
708 const install_lib_path: Path = if (override_lib_dir) |lib_dir|
709 if (Dir.path.isAbsolute(lib_dir)) .{
734 const install_paths: PrefixPaths = if (graph.environ_map.get("DESTDIR")) |destdir| .{
735 .prefix = .{
710736 .root_dir = .cwd(),
711 .sub_path = lib_dir,
712 } else try install_prefix_path.join(arena, lib_dir)
713 else
714 try install_prefix_path.join(arena, "lib");
715
716 const install_bin_path: Path = if (override_bin_dir) |bin_dir|
717 if (Dir.path.isAbsolute(bin_dir)) .{
737 .sub_path = try Dir.path.join(arena, &.{ destdir, deployed_paths.prefix.sub_path }),
738 },
739 .lib = .{
718740 .root_dir = .cwd(),
719 .sub_path = bin_dir,
720 } else try install_prefix_path.join(arena, bin_dir)
721 else
722 try install_prefix_path.join(arena, "bin");
723
724 const install_include_path: Path = if (override_include_dir) |include_dir|
725 if (Dir.path.isAbsolute(include_dir)) .{
741 .sub_path = try Dir.path.join(arena, &.{ destdir, deployed_paths.lib.sub_path }),
742 },
743 .bin = .{
726744 .root_dir = .cwd(),
727 .sub_path = include_dir,
728 } else try install_prefix_path.join(arena, include_dir)
729 else
730 try install_prefix_path.join(arena, "include");
745 .sub_path = try Dir.path.join(arena, &.{ destdir, deployed_paths.bin.sub_path }),
746 },
747 .include = .{
748 .root_dir = .cwd(),
749 .sub_path = try Dir.path.join(arena, &.{ destdir, deployed_paths.include.sub_path }),
750 },
751 } else deployed_paths;
731752
732753 const now = Io.Clock.Timestamp.now(io, .awake);
733754
......@@ -830,12 +851,8 @@ pub fn main(init: process.Init.Minimal) !void {
830851 .gpa = gpa,
831852 .graph = &graph,
832853 .scanned_config = &scanned_config,
833 .install_paths = .{
834 .prefix = install_prefix_path,
835 .lib = install_lib_path,
836 .bin = install_bin_path,
837 .include = install_include_path,
838 },
854 .install_paths = install_paths,
855 .deployed_paths = deployed_paths,
839856
840857 .steps = &.{},
841858 .generated_files = &.{},
......@@ -4099,6 +4116,10 @@ pub fn relativePath(
40994116 .install_lib => try maker.install_paths.lib.join(arena, sub_path),
41004117 .install_bin => try maker.install_paths.bin.join(arena, sub_path),
41014118 .install_include => try maker.install_paths.include.join(arena, sub_path),
4119 .deployed_prefix => try maker.deployed_paths.prefix.join(arena, sub_path),
4120 .deployed_lib => try maker.deployed_paths.lib.join(arena, sub_path),
4121 .deployed_bin => try maker.deployed_paths.bin.join(arena, sub_path),
4122 .deployed_include => try maker.deployed_paths.include.join(arena, sub_path),
41024123 .libc_runtimes => if (maker.graph.libc_runtimes_dir) |libc_runtimes_dir| .{
41034124 .root_dir = .cwd(),
41044125 .sub_path = try Dir.path.join(arena, &.{ libc_runtimes_dir, sub_path }),
......@@ -4708,6 +4729,10 @@ fn confPathDepToCachePath(
47084729 .install_lib => @panic("TODO"),
47094730 .install_bin => @panic("TODO"),
47104731 .install_include => @panic("TODO"),
4732 .deployed_prefix => @panic("TODO"),
4733 .deployed_lib => @panic("TODO"),
4734 .deployed_bin => @panic("TODO"),
4735 .deployed_include => @panic("TODO"),
47114736 .libc_runtimes => @panic("TODO"),
47124737 };
47134738}
lib/std/Build.zig+4
......@@ -2760,6 +2760,10 @@ fn validateConfigureDependency(lazy_path: LazyPath) void {
27602760 .install_lib,
27612761 .install_bin,
27622762 .install_include,
2763 .deployed_prefix,
2764 .deployed_lib,
2765 .deployed_bin,
2766 .deployed_include,
27632767 => @panic("configure phase cannot depend on files installed during make phase"),
27642768 .libc_runtimes,
27652769 => @panic("configure phase cannot depend on directory known only during make phase"),
lib/std/Build/Configuration.zig+30
......@@ -1607,6 +1607,13 @@ pub const LazyPath = union(@This().Tag) {
16071607 _: u16 = 0,
16081608 };
16091609
1610 /// The deployed_prefix and related paths exist to support the use-case
1611 /// of embedding absolute paths to other build artifacts in the build
1612 /// artifacts themselves. For example pkg-config files are artifacts
1613 /// which must contain absolute paths to the other library/header
1614 /// artifacts they describe. Note that this feature is only supported to
1615 /// faciliate interoperability with such legacy systems. In general,
1616 /// embedding absolute paths in build artifacts should be avoided.
16101617 pub const Base = enum(u8) {
16111618 cwd,
16121619 local_cache,
......@@ -1615,10 +1622,33 @@ pub const LazyPath = union(@This().Tag) {
16151622 build_root,
16161623 zig_exe,
16171624 zig_lib,
1625
1626 /// Path that artifacts will be written to by the build system.
1627 /// Equivalent to $DESTDIR/deployed_prefix if $DESTDIR is set.
16181628 install_prefix,
1629 /// Path that artifacts will be written to by the build system.
1630 /// Equivalent to $DESTDIR/deployed_lib if $DESTDIR is set.
16191631 install_lib,
1632 /// Path that artifacts will be written to by the build system.
1633 /// Equivalent to $DESTDIR/deployed_bin if $DESTDIR is set.
16201634 install_bin,
1635 /// Path that artifacts will be written to by the build system.
1636 /// Equivalent to $DESTDIR/deployed_include if $DESTDIR is set.
16211637 install_include,
1638
1639 /// Path that artifacts will be deployed to on the target system.
1640 /// Unaffected by $DESTDIR.
1641 deployed_prefix,
1642 /// Path that artifacts will be deployed to on the target system.
1643 /// Unaffected by $DESTDIR.
1644 deployed_lib,
1645 /// Path that artifacts will be deployed to on the target system.
1646 /// Unaffected by $DESTDIR.
1647 deployed_bin,
1648 /// Path that artifacts will be deployed to on the target system.
1649 /// Unaffected by $DESTDIR.
1650 deployed_include,
1651
16221652 libc_runtimes,
16231653 };
16241654 };