Commit ed4ccea7ba

Andrew Kelley <andrew@ziglang.org>
2024-02-02 06:43:41
build system: implement --system [dir]
This prevents package fetching and enables system_package_mode in the build system which flips the defaults for system integrations.
1 parent 2253787
Changed files (4)
lib/std/Build.zig
@@ -1254,7 +1254,7 @@ pub fn standardOptimizeOption(b: *Build, options: StandardOptimizeOptionOptions)
     if (b.option(
         std.builtin.OptimizeMode,
         "optimize",
-        "Prioritize performance, safety, or binary size (-O flag)",
+        "Prioritize performance, safety, or binary size",
     )) |mode| {
         return mode;
     }
lib/build_runner.zig
@@ -202,6 +202,11 @@ pub fn main() !void {
                 builder.debug_pkg_config = true;
             } else if (mem.eql(u8, arg, "--debug-compile-errors")) {
                 builder.debug_compile_errors = true;
+            } else if (mem.eql(u8, arg, "--system")) {
+                // The usage text shows another argument after this parameter
+                // but it is handled by the parent process. The build runner
+                // only sees this flag.
+                graph.system_package_mode = true;
             } else if (mem.eql(u8, arg, "--glibc-runtimes")) {
                 builder.glibc_runtimes_dir = nextArgOrFatal(args, &arg_idx);
             } else if (mem.eql(u8, arg, "--verbose-link")) {
@@ -1053,18 +1058,14 @@ fn usage(b: *std.Build, out_stream: anytype) !void {
     try out_stream.writeAll(
         \\
         \\General Options:
-        \\  -p, --prefix [path]          Where to put installed files (default: zig-out)
-        \\  --prefix-lib-dir [path]      Where to put installed libraries
-        \\  --prefix-exe-dir [path]      Where to put installed executables
-        \\  --prefix-include-dir [path]  Where to put installed C header files
+        \\  -p, --prefix [path]          Where to install files (default: zig-out)
+        \\  --prefix-lib-dir [path]      Where to install libraries
+        \\  --prefix-exe-dir [path]      Where to install executables
+        \\  --prefix-include-dir [path]  Where to install C header files
         \\
         \\  --release[=mode]             Request release mode, optionally specifying a
         \\                               preferred optimization mode: fast, safe, small
         \\
-        \\  --sysroot [path]             Set the system root directory (usually /)
-        \\  --search-prefix [path]       Add a path to look for binaries, libraries, headers
-        \\  --libc [file]                Provide a file which specifies libc paths
-        \\
         \\  -fdarling,  -fno-darling     Integration with system-installed Darling to
         \\                               execute macOS programs on Linux hosts
         \\                               (default: no)
@@ -1122,13 +1123,18 @@ fn usage(b: *std.Build, out_stream: anytype) !void {
     try out_stream.writeAll(
         \\
         \\System Integration Options:
-        \\  --system [dir]               System Package Mode. Disable fetching; prefer system libs
-        \\  -fsys=[name]                 Enable a system integration
-        \\  -fno-sys=[name]              Disable a system integration
+        \\  --search-prefix [path]       Add a path to look for binaries, libraries, headers
+        \\  --sysroot [path]             Set the system root directory (usually /)
+        \\  --libc [file]                Provide a file which specifies libc paths
+        \\
         \\  --host-target [triple]       Use the provided target as the host
         \\  --host-cpu [cpu]             Use the provided CPU as the host
         \\  --host-dynamic-linker [path] Use the provided dynamic linker as the host
         \\
+        \\  --system [pkgdir]            Disable package fetching; enable all integrations
+        \\  -fsys=[name]                 Enable a system integration
+        \\  -fno-sys=[name]              Disable a system integration
+        \\
         \\  Available System Integrations:                Enabled:
         \\
     );
src/Package/Fetch.zig
@@ -80,6 +80,15 @@ pub const JobQueue = struct {
     thread_pool: *ThreadPool,
     wait_group: WaitGroup = .{},
     global_cache: Cache.Directory,
+    /// If true then, no fetching occurs, and:
+    /// * The `global_cache` directory is assumed to be the direct parent
+    ///   directory of on-disk packages rather than having the "p/" directory
+    ///   prefix inside of it.
+    /// * An error occurs if any non-lazy packages are not already present in
+    ///   the package cache directory.
+    /// * Missing hash field causes an error, and no fetching occurs so it does
+    ///   not print the correct hash like usual.
+    read_only: bool,
     recursive: bool,
     /// Dumps hash information to stdout which can be used to troubleshoot why
     /// two hashes of the same package do not match.
@@ -270,7 +279,8 @@ pub fn run(f: *Fetch) RunError!void {
                 // We want to fail unless the resolved relative path has a
                 // prefix of "p/$hash/".
                 const digest_len = @typeInfo(Manifest.MultiHashHexDigest).Array.len;
-                const expected_prefix = f.parent_package_root.sub_path[0 .. "p/".len + digest_len];
+                const prefix_len: usize = if (f.job_queue.read_only) 0 else "p/".len;
+                const expected_prefix = f.parent_package_root.sub_path[0 .. prefix_len + digest_len];
                 if (!std.mem.startsWith(u8, pkg_root.sub_path, expected_prefix)) {
                     return f.fail(
                         f.location_tok,
@@ -311,7 +321,9 @@ pub fn run(f: *Fetch) RunError!void {
 
     const s = fs.path.sep_str;
     if (remote.hash) |expected_hash| {
-        const pkg_sub_path = "p" ++ s ++ expected_hash;
+        const prefixed_pkg_sub_path = "p" ++ s ++ expected_hash;
+        const prefix_len: usize = if (f.job_queue.read_only) "p/".len else 0;
+        const pkg_sub_path = prefixed_pkg_sub_path[prefix_len..];
         if (cache_root.handle.access(pkg_sub_path, .{})) |_| {
             f.package_root = .{
                 .root_dir = cache_root,
@@ -322,7 +334,14 @@ pub fn run(f: *Fetch) RunError!void {
             if (!f.job_queue.recursive) return;
             return queueJobsForDeps(f);
         } else |err| switch (err) {
-            error.FileNotFound => {},
+            error.FileNotFound => {
+                if (f.job_queue.read_only) return f.fail(
+                    f.location_tok,
+                    try eb.printString("package not found at '{}{s}'", .{
+                        cache_root, pkg_sub_path,
+                    }),
+                );
+            },
             else => |e| {
                 try eb.addRootErrorMessage(.{
                     .msg = try eb.printString("unable to open global package cache directory '{}{s}': {s}", .{
@@ -332,6 +351,12 @@ pub fn run(f: *Fetch) RunError!void {
                 return error.FetchFailed;
             },
         }
+    } else {
+        try eb.addRootErrorMessage(.{
+            .msg = try eb.addString("dependency is missing hash field"),
+            .src_loc = try f.srcLoc(f.location_tok),
+        });
+        return error.FetchFailed;
     }
 
     // Fetch and unpack the remote into a temporary directory.
src/main.zig
@@ -5179,6 +5179,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
         var verbose_cimport = false;
         var verbose_llvm_cpu_features = false;
         var fetch_only = false;
+        var system_pkg_dir_path: ?[]const u8 = null;
 
         const argv_index_exe = child_argv.items.len;
         _ = try child_argv.addOne();
@@ -5235,6 +5236,12 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
                         reference_trace = 256;
                     } else if (mem.eql(u8, arg, "--fetch")) {
                         fetch_only = true;
+                    } else if (mem.eql(u8, arg, "--system")) {
+                        if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});
+                        i += 1;
+                        system_pkg_dir_path = args[i];
+                        try child_argv.append("--system");
+                        continue;
                     } else if (mem.startsWith(u8, arg, "-freference-trace=")) {
                         const num = arg["-freference-trace=".len..];
                         reference_trace = std.fmt.parseUnsigned(u32, num, 10) catch |err| {
@@ -5419,8 +5426,6 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
             var http_client: std.http.Client = .{ .allocator = gpa };
             defer http_client.deinit();
 
-            try http_client.loadDefaultProxies();
-
             var progress: std.Progress = .{ .dont_print_on_dumb = true };
             const root_prog_node = progress.start("Fetch Packages", 0);
             defer root_prog_node.end();
@@ -5429,12 +5434,28 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
                 .http_client = &http_client,
                 .thread_pool = &thread_pool,
                 .global_cache = global_cache_directory,
+                .read_only = false,
                 .recursive = true,
                 .debug_hash = false,
                 .work_around_btrfs_bug = work_around_btrfs_bug,
             };
             defer job_queue.deinit();
 
+            if (system_pkg_dir_path) |p| {
+                job_queue.global_cache = .{
+                    .path = p,
+                    .handle = fs.cwd().openDir(p, .{}) catch |err| {
+                        fatal("unable to open system package directory '{s}': {s}", .{
+                            p, @errorName(err),
+                        });
+                    },
+                };
+                job_queue.read_only = true;
+                cleanup_build_dir = job_queue.global_cache.handle;
+            } else {
+                try http_client.loadDefaultProxies();
+            }
+
             try job_queue.all_fetches.ensureUnusedCapacity(gpa, 1);
             try job_queue.table.ensureUnusedCapacity(gpa, 1);
 
@@ -7363,6 +7384,7 @@ fn cmdFetch(
         .thread_pool = &thread_pool,
         .global_cache = global_cache_directory,
         .recursive = false,
+        .read_only = false,
         .debug_hash = debug_hash,
         .work_around_btrfs_bug = work_around_btrfs_bug,
     };