From 17cf964c25d708c5b28c4811538cd5af4a56b42e Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Fri, 16 Oct 2026 17:55:21 +0200 Subject: [PATCH] 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 --- lib/compiler/Maker.zig | 105 ++++++++++++++++++++------------ lib/std/Build.zig | 4 ++ lib/std/Build/Configuration.zig | 30 +++++++++ 3 files changed, 99 insertions(+), 40 deletions(-) diff --git a/lib/compiler/Maker.zig b/lib/compiler/Maker.zig index 0c416713fcd0757d96525ecccd05f99d60d7a658..526ab1298aaed9c857eca786f556daa540c0883b 100644 --- a/lib/compiler/Maker.zig +++ b/lib/compiler/Maker.zig @@ -42,7 +42,8 @@ pub const std_options: std.Options = .{ gpa: Allocator, graph: *Graph, -install_paths: InstallPaths, +install_paths: PrefixPaths, +deployed_paths: PrefixPaths, scanned_config: *const ScannedConfig, /// Includes an extra auto-generated placeholder Step at the end that indicates /// configure must be rerun. It is done this way so that the hot path of file @@ -93,7 +94,7 @@ const use_safe_allocator = switch (builtin.mode) { .fast, .small => false, }; -const InstallPaths = struct { +const PrefixPaths = struct { prefix: Path, lib: Path, bin: Path, @@ -663,42 +664,62 @@ pub fn main(init: process.Init.Minimal) !void { }); defer main_progress_node.end(); - const install_prefix_path: Path = if (graph.environ_map.get("DESTDIR")) |dest_dir| .{ - .root_dir = .cwd(), - .sub_path = try Dir.path.join(arena, &.{ dest_dir, override_install_prefix orelse "/usr" }), - } else if (override_install_prefix) |cwd_relative| .{ - .root_dir = .cwd(), - .sub_path = cwd_relative, - } else .{ - .root_dir = graph.build_root_directory, - .sub_path = "zig-out", + const deployed_paths: PrefixPaths = blk: { + const prefix: Path = if (override_install_prefix) |cwd_relative| .{ + .root_dir = .cwd(), + .sub_path = cwd_relative, + } else if (graph.environ_map.get("DESTDIR") != null) .{ + .root_dir = .cwd(), + .sub_path = "/usr", + } else .{ + .root_dir = graph.build_root_directory, + .sub_path = "zig-out", + }; + break :blk .{ + .prefix = prefix, + // The lib/bin/include overrides are interpreted relative to the + // install prefix unless absolute paths are used. + .lib = if (override_lib_dir) |lib_dir| + if (Dir.path.isAbsolute(lib_dir)) .{ + .root_dir = .cwd(), + .sub_path = lib_dir, + } else try prefix.join(arena, lib_dir) + else + try prefix.join(arena, "lib"), + .bin = if (override_bin_dir) |bin_dir| + if (Dir.path.isAbsolute(bin_dir)) .{ + .root_dir = .cwd(), + .sub_path = bin_dir, + } else try prefix.join(arena, bin_dir) + else + try prefix.join(arena, "bin"), + .include = if (override_include_dir) |include_dir| + if (Dir.path.isAbsolute(include_dir)) .{ + .root_dir = .cwd(), + .sub_path = include_dir, + } else try prefix.join(arena, include_dir) + else + try prefix.join(arena, "include"), + }; }; - - // These three overrides are meant to be relative to the install prefix, - // not current working directory, unless absolute paths are used. - const install_lib_path: Path = if (override_lib_dir) |lib_dir| - if (Dir.path.isAbsolute(lib_dir)) .{ + const install_paths: PrefixPaths = if (graph.environ_map.get("DESTDIR")) |destdir| .{ + .prefix = .{ .root_dir = .cwd(), - .sub_path = lib_dir, - } else try install_prefix_path.join(arena, lib_dir) - else - try install_prefix_path.join(arena, "lib"); - - const install_bin_path: Path = if (override_bin_dir) |bin_dir| - if (Dir.path.isAbsolute(bin_dir)) .{ + .sub_path = try Dir.path.join(arena, &.{ destdir, deployed_paths.prefix.sub_path }), + }, + .lib = .{ .root_dir = .cwd(), - .sub_path = bin_dir, - } else try install_prefix_path.join(arena, bin_dir) - else - try install_prefix_path.join(arena, "bin"); - - const install_include_path: Path = if (override_include_dir) |include_dir| - if (Dir.path.isAbsolute(include_dir)) .{ + .sub_path = try Dir.path.join(arena, &.{ destdir, deployed_paths.lib.sub_path }), + }, + .bin = .{ .root_dir = .cwd(), - .sub_path = include_dir, - } else try install_prefix_path.join(arena, include_dir) - else - try install_prefix_path.join(arena, "include"); + .sub_path = try Dir.path.join(arena, &.{ destdir, deployed_paths.bin.sub_path }), + }, + .include = .{ + .root_dir = .cwd(), + .sub_path = try Dir.path.join(arena, &.{ destdir, deployed_paths.include.sub_path }), + }, + } else deployed_paths; const now = Io.Clock.Timestamp.now(io, .awake); @@ -795,12 +816,8 @@ pub fn main(init: process.Init.Minimal) !void { .gpa = gpa, .graph = &graph, .scanned_config = &scanned_config, - .install_paths = .{ - .prefix = install_prefix_path, - .lib = install_lib_path, - .bin = install_bin_path, - .include = install_include_path, - }, + .install_paths = install_paths, + .deployed_paths = deployed_paths, .steps = &.{}, .generated_files = &.{}, @@ -3829,6 +3846,10 @@ pub fn relativePath( .install_lib => try maker.install_paths.lib.join(arena, sub_path), .install_bin => try maker.install_paths.bin.join(arena, sub_path), .install_include => try maker.install_paths.include.join(arena, sub_path), + .deployed_prefix => try maker.deployed_paths.prefix.join(arena, sub_path), + .deployed_lib => try maker.deployed_paths.lib.join(arena, sub_path), + .deployed_bin => try maker.deployed_paths.bin.join(arena, sub_path), + .deployed_include => try maker.deployed_paths.include.join(arena, sub_path), .libc_runtimes => if (maker.graph.libc_runtimes_dir) |libc_runtimes_dir| .{ .root_dir = .cwd(), .sub_path = try Dir.path.join(arena, &.{ libc_runtimes_dir, sub_path }), @@ -4435,6 +4456,10 @@ fn confPathDepToCachePath( .install_lib => @panic("TODO"), .install_bin => @panic("TODO"), .install_include => @panic("TODO"), + .deployed_prefix => @panic("TODO"), + .deployed_lib => @panic("TODO"), + .deployed_bin => @panic("TODO"), + .deployed_include => @panic("TODO"), .libc_runtimes => @panic("TODO"), }; } diff --git a/lib/std/Build.zig b/lib/std/Build.zig index 36b48d62cf8075024dddc8dc21b339b929d34c5e..29e8dda05fdd4fa566c69541b62b6e468ff99127 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -2699,6 +2699,10 @@ fn validateConfigureDependency(lazy_path: LazyPath) void { .install_lib, .install_bin, .install_include, + .deployed_prefix, + .deployed_lib, + .deployed_bin, + .deployed_include, => @panic("configure phase cannot depend on files installed during make phase"), .libc_runtimes, => @panic("configure phase cannot depend on directory known only during make phase"), diff --git a/lib/std/Build/Configuration.zig b/lib/std/Build/Configuration.zig index 53bd11500b0d70d4448bd461cfcd924fec6482a2..b35a8a228fc36dd1a2741897eafc3053e14bbc63 100644 --- a/lib/std/Build/Configuration.zig +++ b/lib/std/Build/Configuration.zig @@ -1563,6 +1563,13 @@ pub const LazyPath = union(@This().Tag) { _: u16 = 0, }; + /// The 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. pub const Base = enum(u8) { cwd, local_cache, @@ -1571,10 +1578,33 @@ pub const LazyPath = union(@This().Tag) { build_root, zig_exe, zig_lib, + + /// Path that artifacts will be written to by the build system. + /// Equivalent to $DESTDIR/deployed_prefix if $DESTDIR is set. install_prefix, + /// Path that artifacts will be written to by the build system. + /// Equivalent to $DESTDIR/deployed_lib if $DESTDIR is set. install_lib, + /// Path that artifacts will be written to by the build system. + /// Equivalent to $DESTDIR/deployed_bin if $DESTDIR is set. install_bin, + /// Path that artifacts will be written to by the build system. + /// Equivalent to $DESTDIR/deployed_include if $DESTDIR is set. install_include, + + /// Path that artifacts will be deployed to on the target system. + /// Unaffected by $DESTDIR. + deployed_prefix, + /// Path that artifacts will be deployed to on the target system. + /// Unaffected by $DESTDIR. + deployed_lib, + /// Path that artifacts will be deployed to on the target system. + /// Unaffected by $DESTDIR. + deployed_bin, + /// Path that artifacts will be deployed to on the target system. + /// Unaffected by $DESTDIR. + deployed_include, + libc_runtimes, }; }; -- 2.54.0