From f2f0d97a9b9df2e4aa43c1e4b7876bcc97cb83ff 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 5f1d34cbe9f95c8dc8536231d7fdfe09b3eaeaf0..bbf9044814e18fff0bb51c4710ffbfa9ba34b32b 100644 --- a/lib/compiler/Maker.zig +++ b/lib/compiler/Maker.zig @@ -44,7 +44,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 @@ -95,7 +96,7 @@ const use_safe_allocator = switch (builtin.mode) { .fast, .small => false, }; -const InstallPaths = struct { +const PrefixPaths = struct { prefix: Path, lib: Path, bin: Path, @@ -692,42 +693,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); @@ -830,12 +851,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 = &.{}, @@ -4099,6 +4116,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 }), @@ -4708,6 +4729,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 ec2f57d2911a681571e9116ab48828868642e637..86f36b3722e359c1076236d16cce94d3b900b369 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -2760,6 +2760,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 ee7b77085b177a456bacbe6115619197c0d86e49..e85e19cd1e7d7ba37128ab208de61e4e39690a3e 100644 --- a/lib/std/Build/Configuration.zig +++ b/lib/std/Build/Configuration.zig @@ -1607,6 +1607,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, @@ -1615,10 +1622,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