packages: avoid creating multiple modules with same build.zig

When there is a diamond dependency, reuse a *Module instead of creating
a redundant one using the same build.zig file. Otherwise, the compile
error "file exists in multiple modules" would occur.
This commit is contained in:
Andrew Kelley 2023-03-01 12:11:17 -07:00
parent 874d3a17ae
commit db8217f9a0
2 changed files with 30 additions and 8 deletions

View File

@ -225,6 +225,7 @@ pub fn fetchAndAddDependencies(
build_roots_source: *std.ArrayList(u8), build_roots_source: *std.ArrayList(u8),
name_prefix: []const u8, name_prefix: []const u8,
color: main.Color, color: main.Color,
all_modules: *AllModules,
) !void { ) !void {
const max_bytes = 10 * 1024 * 1024; const max_bytes = 10 * 1024 * 1024;
const gpa = thread_pool.allocator; const gpa = thread_pool.allocator;
@ -291,6 +292,7 @@ pub fn fetchAndAddDependencies(
report, report,
build_roots_source, build_roots_source,
fqn, fqn,
all_modules,
); );
try pkg.fetchAndAddDependencies( try pkg.fetchAndAddDependencies(
@ -304,6 +306,7 @@ pub fn fetchAndAddDependencies(
build_roots_source, build_roots_source,
sub_prefix, sub_prefix,
color, color,
all_modules,
); );
try add(pkg, gpa, fqn, sub_pkg); try add(pkg, gpa, fqn, sub_pkg);
@ -402,6 +405,11 @@ const Report = struct {
} }
}; };
const hex_multihash_len = 2 * Manifest.multihash_len;
const MultiHashHexDigest = [hex_multihash_len]u8;
/// This is to avoid creating multiple modules for the same build.zig file.
pub const AllModules = std.AutoHashMapUnmanaged(MultiHashHexDigest, *Package);
fn fetchAndUnpack( fn fetchAndUnpack(
thread_pool: *ThreadPool, thread_pool: *ThreadPool,
http_client: *std.http.Client, http_client: *std.http.Client,
@ -410,6 +418,7 @@ fn fetchAndUnpack(
report: Report, report: Report,
build_roots_source: *std.ArrayList(u8), build_roots_source: *std.ArrayList(u8),
fqn: []const u8, fqn: []const u8,
all_modules: *AllModules,
) !*Package { ) !*Package {
const gpa = http_client.allocator; const gpa = http_client.allocator;
const s = fs.path.sep_str; const s = fs.path.sep_str;
@ -417,9 +426,24 @@ fn fetchAndUnpack(
// Check if the expected_hash is already present in the global package // Check if the expected_hash is already present in the global package
// cache, and thereby avoid both fetching and unpacking. // cache, and thereby avoid both fetching and unpacking.
if (dep.hash) |h| cached: { if (dep.hash) |h| cached: {
const hex_multihash_len = 2 * Manifest.multihash_len;
const hex_digest = h[0..hex_multihash_len]; const hex_digest = h[0..hex_multihash_len];
const pkg_dir_sub_path = "p" ++ s ++ hex_digest; const pkg_dir_sub_path = "p" ++ s ++ hex_digest;
const build_root = try global_cache_directory.join(gpa, &.{pkg_dir_sub_path});
errdefer gpa.free(build_root);
try build_roots_source.writer().print(" pub const {s} = \"{}\";\n", .{
std.zig.fmtId(fqn), std.zig.fmtEscapes(build_root),
});
// The compiler has a rule that a file must not be included in multiple modules,
// so we must detect if a module has been created for this package and reuse it.
const gop = try all_modules.getOrPut(gpa, hex_digest.*);
if (gop.found_existing) {
gpa.free(build_root);
return gop.value_ptr.*;
}
var pkg_dir = global_cache_directory.handle.openDir(pkg_dir_sub_path, .{}) catch |err| switch (err) { var pkg_dir = global_cache_directory.handle.openDir(pkg_dir_sub_path, .{}) catch |err| switch (err) {
error.FileNotFound => break :cached, error.FileNotFound => break :cached,
else => |e| return e, else => |e| return e,
@ -432,13 +456,6 @@ fn fetchAndUnpack(
const owned_src_path = try gpa.dupe(u8, build_zig_basename); const owned_src_path = try gpa.dupe(u8, build_zig_basename);
errdefer gpa.free(owned_src_path); errdefer gpa.free(owned_src_path);
const build_root = try global_cache_directory.join(gpa, &.{pkg_dir_sub_path});
errdefer gpa.free(build_root);
try build_roots_source.writer().print(" pub const {s} = \"{}\";\n", .{
std.zig.fmtId(fqn), std.zig.fmtEscapes(build_root),
});
ptr.* = .{ ptr.* = .{
.root_src_directory = .{ .root_src_directory = .{
.path = build_root, .path = build_root,
@ -448,6 +465,7 @@ fn fetchAndUnpack(
.root_src_path = owned_src_path, .root_src_path = owned_src_path,
}; };
gop.value_ptr.* = ptr;
return ptr; return ptr;
} }

View File

@ -4244,6 +4244,9 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
var build_roots_source = std.ArrayList(u8).init(gpa); var build_roots_source = std.ArrayList(u8).init(gpa);
defer build_roots_source.deinit(); defer build_roots_source.deinit();
var all_modules: Package.AllModules = .{};
defer all_modules.deinit(gpa);
// Here we borrow main package's table and will replace it with a fresh // Here we borrow main package's table and will replace it with a fresh
// one after this process completes. // one after this process completes.
main_pkg.fetchAndAddDependencies( main_pkg.fetchAndAddDependencies(
@ -4257,6 +4260,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
&build_roots_source, &build_roots_source,
"", "",
color, color,
&all_modules,
) catch |err| switch (err) { ) catch |err| switch (err) {
error.PackageFetchFailed => process.exit(1), error.PackageFetchFailed => process.exit(1),
else => |e| return e, else => |e| return e,