From 8eff0a0a669dbdacf9cebbc96fdf20536f3073ee Mon Sep 17 00:00:00 2001 From: antlilja Date: Sun, 17 Sep 2023 19:38:19 +0200 Subject: [PATCH] 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. --- lib/std/Build.zig | 39 +++++++++--- lib/std/Build/Step/Compile.zig | 2 +- src/Package.zig | 106 +++++++++++++++++++++++---------- 3 files changed, 109 insertions(+), 38 deletions(-) diff --git a/lib/std/Build.zig b/lib/std/Build.zig index 1852f75fba..49afd73778 100644 --- a/lib/std/Build.zig +++ b/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 (sub_builder.validateUserInputDidItFail()) { - std.debug.dumpCurrentStackTrace(@returnAddress()); + const sub_builder = b.createChild(name, build_root, pkg_deps, user_input_options) catch @panic("unhandled error"); + if (build_zig) |bz| { + sub_builder.runBuild(bz) catch @panic("unhandled error"); + + 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 }, }; } }; diff --git a/lib/std/Build/Step/Compile.zig b/lib/std/Build/Step/Compile.zig index a4f1a279ed..64e757a7f5 100644 --- a/lib/std/Build/Step/Compile.zig +++ b/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)); diff --git a/src/Package.zig b/src/Package.zig index bb2ab837f7..03c4c72ab0 100644 --- a/src/Package.zig +++ b/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,