Commit 8eff0a0a66

antlilja <liljaanton2001@gmail.com>
2023-09-17 19:38:19
Support non zig dependencies
Dependencies no longer require a build.zig file. Adds path function to Dependency struct which returns a LazyPath into a dependency.
1 parent c9413a8
Changed files (3)
lib
std
src
lib/std/Build/Step/Compile.zig
@@ -1896,7 +1896,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
                     continue;
                 }
             },
-            .generated => {},
+            .generated, .dependency => {},
         };
 
         zig_args.appendAssumeCapacity(rpath.getPath2(b, step));
lib/std/Build.zig
@@ -1707,6 +1707,15 @@ pub const Dependency = struct {
             panic("unable to find module '{s}'", .{name});
         };
     }
+
+    pub fn path(d: *Dependency, sub_path: []const u8) LazyPath {
+        return .{
+            .dependency = .{
+                .dependency = d,
+                .sub_path = sub_path,
+            },
+        };
+    }
 };
 
 pub fn dependency(b: *Build, name: []const u8, args: anytype) *Dependency {
@@ -1724,7 +1733,7 @@ pub fn dependency(b: *Build, name: []const u8, args: anytype) *Dependency {
     inline for (@typeInfo(deps.packages).Struct.decls) |decl| {
         if (mem.eql(u8, decl.name, pkg_hash)) {
             const pkg = @field(deps.packages, decl.name);
-            return dependencyInner(b, name, pkg.build_root, pkg.build_zig, pkg.deps, args);
+            return dependencyInner(b, name, pkg.build_root, if (@hasDecl(pkg, "build_zig")) pkg.build_zig else null, pkg.deps, args);
         }
     }
 
@@ -1801,7 +1810,7 @@ pub fn dependencyInner(
     b: *Build,
     name: []const u8,
     build_root_string: []const u8,
-    comptime build_zig: type,
+    comptime build_zig: ?type,
     pkg_deps: AvailableDeps,
     args: anytype,
 ) *Dependency {
@@ -1821,11 +1830,14 @@ pub fn dependencyInner(
             process.exit(1);
         },
     };
+
     const sub_builder = b.createChild(name, build_root, pkg_deps, user_input_options) catch @panic("unhandled error");
-    sub_builder.runBuild(build_zig) catch @panic("unhandled error");
+    if (build_zig) |bz| {
+        sub_builder.runBuild(bz) catch @panic("unhandled error");
 
-    if (sub_builder.validateUserInputDidItFail()) {
-        std.debug.dumpCurrentStackTrace(@returnAddress());
+        if (sub_builder.validateUserInputDidItFail()) {
+            std.debug.dumpCurrentStackTrace(@returnAddress());
+        }
     }
 
     const dep = b.allocator.create(Dependency) catch @panic("OOM");
@@ -1892,6 +1904,11 @@ pub const LazyPath = union(enum) {
     /// Use of this tag indicates a dependency on the host system.
     cwd_relative: []const u8,
 
+    dependency: struct {
+        dependency: *Dependency,
+        sub_path: []const u8,
+    },
+
     /// Returns a new file source that will have a relative path to the build root guaranteed.
     /// Asserts the parameter is not an absolute path.
     pub fn relative(path: []const u8) LazyPath {
@@ -1905,13 +1922,14 @@ pub const LazyPath = union(enum) {
         return switch (self) {
             .path, .cwd_relative => self.path,
             .generated => "generated",
+            .dependency => "dependency",
         };
     }
 
     /// Adds dependencies this file source implies to the given step.
     pub fn addStepDependencies(self: LazyPath, other_step: *Step) void {
         switch (self) {
-            .path, .cwd_relative => {},
+            .path, .cwd_relative, .dependency => {},
             .generated => |gen| other_step.dependOn(gen.step),
         }
     }
@@ -1937,6 +1955,12 @@ pub const LazyPath = union(enum) {
                 dumpBadGetPathHelp(gen.step, stderr, src_builder, asking_step) catch {};
                 @panic("misconfigured build script");
             },
+            .dependency => |dep| {
+                return dep.dependency.builder.pathJoin(&[_][]const u8{
+                    dep.dependency.builder.build_root.path.?,
+                    dep.sub_path,
+                });
+            },
         }
     }
 
@@ -1946,6 +1970,7 @@ pub const LazyPath = union(enum) {
             .path => |p| .{ .path = b.dupePath(p) },
             .cwd_relative => |p| .{ .cwd_relative = b.dupePath(p) },
             .generated => |gen| .{ .generated = gen },
+            .dependency => |dep| .{ .dependency = dep },
         };
     }
 };
src/Package.zig
@@ -327,30 +327,45 @@ pub fn fetchAndAddDependencies(
             name,
         );
 
-        if (!sub.found_existing) {
-            try sub.mod.fetchAndAddDependencies(
-                deps_pkg,
-                arena,
-                thread_pool,
-                http_client,
-                sub.mod.root_src_directory,
-                global_cache_directory,
-                local_cache_directory,
-                dependencies_source,
-                error_bundle,
-                all_modules,
-                root_prog_node,
-                dep.hash.?,
-            );
-        }
+        if (sub.mod) |mod| {
+            if (!sub.found_existing) {
+                try mod.fetchAndAddDependencies(
+                    deps_pkg,
+                    arena,
+                    thread_pool,
+                    http_client,
+                    mod.root_src_directory,
+                    global_cache_directory,
+                    local_cache_directory,
+                    dependencies_source,
+                    error_bundle,
+                    all_modules,
+                    root_prog_node,
+                    dep.hash.?,
+                );
+            }
 
-        try pkg.add(gpa, name, sub.mod);
-        if (deps_pkg.table.get(dep.hash.?)) |other_sub| {
-            // This should be the same package (and hence module) since it's the same hash
-            // TODO: dedup multiple versions of the same package
-            assert(other_sub == sub.mod);
-        } else {
-            try deps_pkg.add(gpa, dep.hash.?, sub.mod);
+            try pkg.add(gpa, name, mod);
+            if (deps_pkg.table.get(dep.hash.?)) |other_sub| {
+                // This should be the same package (and hence module) since it's the same hash
+                // TODO: dedup multiple versions of the same package
+                assert(other_sub == mod);
+            } else {
+                try deps_pkg.add(gpa, dep.hash.?, mod);
+            }
+        } else if (!sub.found_existing) {
+            const pkg_dir_sub_path = "p" ++ fs.path.sep_str ++ (dep.hash.?)[0..hex_multihash_len];
+            const build_root = try global_cache_directory.join(arena, &.{pkg_dir_sub_path});
+            try dependencies_source.writer().print(
+                \\    pub const {} = struct {{
+                \\        pub const build_root = "{}";
+                \\        pub const deps: []const struct {{ []const u8, []const u8 }} = &.{{}};
+                \\    }};
+                \\
+            , .{
+                std.zig.fmtId(dep.hash.?),
+                std.zig.fmtEscapes(build_root),
+            });
         }
     }
 
@@ -480,7 +495,10 @@ const MultiHashHexDigest = [hex_multihash_len]u8;
 /// This is to avoid creating multiple modules for the same build.zig file.
 /// If the value is `null`, the package is a known dependency, but has not yet
 /// been fetched.
-pub const AllModules = std.AutoHashMapUnmanaged(MultiHashHexDigest, ?*Package);
+pub const AllModules = std.AutoHashMapUnmanaged(MultiHashHexDigest, ?union(enum) {
+    zig_pkg: *Package,
+    non_zig_pkg: void,
+});
 
 fn ProgressReader(comptime ReaderType: type) type {
     return struct {
@@ -535,7 +553,7 @@ fn fetchAndUnpack(
     /// This does not have to be any form of canonical or fully-qualified name: it
     /// is only intended to be human-readable for progress reporting.
     name_for_prog: []const u8,
-) !struct { mod: *Package, found_existing: bool } {
+) !struct { mod: ?*Package, found_existing: bool } {
     const gpa = http_client.allocator;
     const s = fs.path.sep_str;
 
@@ -556,13 +574,27 @@ fn fetchAndUnpack(
         const gop = try all_modules.getOrPut(gpa, hex_digest.*);
         if (gop.found_existing) {
             if (gop.value_ptr.*) |mod| {
-                return .{
-                    .mod = mod,
-                    .found_existing = true,
+                return switch (mod) {
+                    .zig_pkg => |pkg| .{
+                        .mod = pkg,
+                        .found_existing = true,
+                    },
+                    .non_zig_pkg => .{
+                        .mod = null,
+                        .found_existing = true,
+                    },
                 };
             }
         }
 
+        pkg_dir.access(build_zig_basename, .{}) catch {
+            gop.value_ptr.* = .non_zig_pkg;
+            return .{
+                .mod = null,
+                .found_existing = false,
+            };
+        };
+
         const build_root = try global_cache_directory.join(gpa, &.{pkg_dir_sub_path});
         errdefer gpa.free(build_root);
 
@@ -583,7 +615,7 @@ fn fetchAndUnpack(
             .root_src_path = owned_src_path,
         };
 
-        gop.value_ptr.* = ptr;
+        gop.value_ptr.* = .{ .zig_pkg = ptr };
         return .{
             .mod = ptr,
             .found_existing = false,
@@ -722,8 +754,22 @@ fn fetchAndUnpack(
         return error.PackageFetchFailed;
     }
 
+    const build_zig_path = try std.fs.path.join(gpa, &.{ pkg_dir_sub_path, build_zig_basename });
+    defer gpa.free(build_zig_path);
+
+    global_cache_directory.handle.access(build_zig_path, .{}) catch |err| switch (err) {
+        error.FileNotFound => {
+            try all_modules.put(gpa, actual_hex, .non_zig_pkg);
+            return .{
+                .mod = null,
+                .found_existing = false,
+            };
+        },
+        else => return err,
+    };
+
     const mod = try createWithDir(gpa, global_cache_directory, pkg_dir_sub_path, build_zig_basename);
-    try all_modules.put(gpa, actual_hex, mod);
+    try all_modules.put(gpa, actual_hex, .{ .zig_pkg = mod });
     return .{
         .mod = mod,
         .found_existing = false,