Skip to content

Commit 9acd8a7

Browse files
committed
dep-hash: add dep-hash cli tool
1 parent 0937ca7 commit 9acd8a7

File tree

2 files changed

+224
-0
lines changed

2 files changed

+224
-0
lines changed

lib/compiler/dep-hash.zig

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
const usage_dep_hash =
2+
\\Usage: zig dep-hash [--list] [dep-name]
3+
\\
4+
\\ List the hashes of packages in the build.zig.zon manifest.
5+
\\
6+
\\Options:
7+
\\ --list List all package hashes
8+
\\ --build-root [path] Set package root directory
9+
\\ --global-cache-dir [path] Override the global cache directory
10+
\\ -h, --help Print this help and exit
11+
\\
12+
\\
13+
;
14+
15+
pub fn main() !void {
16+
var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);
17+
defer arena_instance.deinit();
18+
const arena = arena_instance.allocator();
19+
const gpa = arena;
20+
21+
const args = try std.process.argsAlloc(arena);
22+
const color: std.zig.Color = .auto;
23+
var list = false;
24+
var package_opt: ?[]const u8 = null;
25+
var build_root_path: ?[]const u8 = null;
26+
var override_global_cache_dir: ?[]const u8 = try std.zig.EnvVar.ZIG_GLOBAL_CACHE_DIR.get(arena);
27+
28+
assert(args.len > 0);
29+
30+
{
31+
var i: usize = 1;
32+
while (i < args.len) : (i += 1) {
33+
const arg = args[i];
34+
if (arg[0] == '-') {
35+
if (std.mem.eql(u8, arg, "-h") or std.mem.eql(u8, arg, "--help")) {
36+
const stdout = std.io.getStdOut();
37+
try stdout.writeAll(usage_dep_hash);
38+
return std.process.cleanExit();
39+
} else if (std.mem.eql(u8, arg, "--list")) {
40+
list = true;
41+
} else if (std.mem.eql(u8, arg, "--build-root")) {
42+
i += 1;
43+
if (i >= args.len) fatal("build root expected after --build-root", .{});
44+
build_root_path = args[i];
45+
} else if (std.mem.eql(u8, arg, "--global-cache-dir")) {
46+
i += 1;
47+
if (i >= args.len) fatal("cache directory expected after --global-cache-dir", .{});
48+
override_global_cache_dir = args[i];
49+
} else {
50+
fatal("unrecognized parameter: '{s}'", .{arg});
51+
}
52+
} else if (package_opt != null) {
53+
fatal("unexpected extra parameter: '{s}'", .{arg});
54+
} else {
55+
package_opt = arg;
56+
}
57+
}
58+
}
59+
60+
var build_root = try std.zig.findBuildRoot(arena, .{
61+
.cwd_path = build_root_path,
62+
.hint = build_root_path == null,
63+
});
64+
defer build_root.deinit();
65+
66+
var global_cache_package_directory: std.fs.Dir = l: {
67+
const p = try std.fs.path.join(arena, &.{
68+
override_global_cache_dir orelse try std.zig.introspect.resolveGlobalCacheDir(arena),
69+
"p",
70+
});
71+
72+
break :l try std.fs.cwd().makeOpenPath(p, .{});
73+
};
74+
defer global_cache_package_directory.close();
75+
76+
var manifest, var ast = std.zig.loadManifest(gpa, arena, .{
77+
.root_name = null,
78+
.dir = build_root.directory.handle,
79+
.color = color,
80+
}) catch |err| switch (err) {
81+
error.FileNotFound => fatal("no manifest found in build root", .{}),
82+
else => |e| return e,
83+
};
84+
defer {
85+
manifest.deinit(gpa);
86+
ast.deinit(gpa);
87+
}
88+
89+
if (package_opt) |package| {
90+
if (package.len == 0) {
91+
fatal("package name must not be empty", .{});
92+
}
93+
const dep: std.zig.Manifest.Dependency = dep: {
94+
var iter = std.mem.tokenizeScalar(u8, package, '.');
95+
96+
var dep = manifest.dependencies.get(iter.next().?) orelse {
97+
fatal("there is no dependency named '{s}' in the manifest\n", .{package});
98+
};
99+
100+
var dep_name = iter.buffer[0 .. iter.index - 1];
101+
102+
while (iter.next()) |p| {
103+
if (dep.hash) |hash| {
104+
var package_dir = global_cache_package_directory.openDir(hash, .{}) catch |e| switch (e) {
105+
error.FileNotFound => fatal("{s} is not in the global cache (hash: {s})", .{
106+
dep_name, hash,
107+
}),
108+
else => |err| return err,
109+
};
110+
defer package_dir.close();
111+
112+
const sub_manifest, _ = try std.zig.loadManifest(arena, arena, .{
113+
.root_name = null,
114+
.dir = package_dir,
115+
.color = color,
116+
});
117+
118+
dep = sub_manifest.dependencies.get(p) orelse {
119+
fatal("{s} has no dependency named '{s}' in its manifest", .{ dep_name, package });
120+
};
121+
dep_name = iter.buffer[0 .. iter.index - 1];
122+
} else switch (dep.location) {
123+
.url => fatal("the hash for {s} is missing from the manifest.\n", .{
124+
dep_name,
125+
}),
126+
.path => |path| fatal("{s} is a local dependency located at {s}\n", .{
127+
dep_name, path,
128+
}),
129+
}
130+
}
131+
132+
break :dep dep;
133+
};
134+
135+
const stdout = std.io.getStdOut().writer();
136+
137+
if (dep.hash) |hash| {
138+
if (list) {
139+
var package_dir = global_cache_package_directory.openDir(hash, .{}) catch |e| switch (e) {
140+
error.FileNotFound => fatal("{s} is not in the global cache (hash: {s})", .{
141+
package, hash,
142+
}),
143+
else => |err| return err,
144+
};
145+
defer package_dir.close();
146+
147+
var sub_manifest, var sub_ast = std.zig.loadManifest(gpa, arena, .{
148+
.root_name = null,
149+
.dir = package_dir,
150+
.color = color,
151+
}) catch |err| switch (err) {
152+
error.FileNotFound => fatal("no manifest found in build root", .{}),
153+
else => |e| return e,
154+
};
155+
defer {
156+
sub_manifest.deinit(gpa);
157+
sub_ast.deinit(gpa);
158+
}
159+
160+
const prefix = prefix: {
161+
const buffer = try arena.alloc(u8, package.len + 1);
162+
@memcpy(buffer[0..package.len], package);
163+
buffer[buffer.len - 1] = '.';
164+
break :prefix buffer;
165+
};
166+
167+
try listDepHashes(prefix, sub_manifest);
168+
} else {
169+
try stdout.print("{s}\n", .{hash});
170+
}
171+
} else switch (dep.location) {
172+
.url => fatal("the hash for {s} is missing from the manifest.\n", .{package}),
173+
.path => |path| fatal("{s} is a local dependency located at {s}\n", .{ package, path }),
174+
}
175+
} else {
176+
try listDepHashes("", manifest);
177+
}
178+
}
179+
180+
fn listDepHashes(parent_prefix: []const u8, manifest: std.zig.Manifest) !void {
181+
assert(parent_prefix.len != 1);
182+
if (manifest.dependencies.count() == 0) {
183+
const name = if (parent_prefix.len > 0)
184+
parent_prefix[0 .. parent_prefix.len - 1]
185+
else
186+
manifest.name;
187+
188+
const stdout = std.io.getStdOut().writer();
189+
try stdout.print("{s} has no dependencies\n", .{name});
190+
return;
191+
}
192+
193+
var deps = manifest.dependencies.iterator();
194+
while (deps.next()) |entry| {
195+
const stdout = std.io.getStdOut().writer();
196+
const name = entry.key_ptr.*;
197+
if (entry.value_ptr.hash) |hash| {
198+
try stdout.print("{s}{s} {s}\n", .{ parent_prefix, name, hash });
199+
} else {
200+
switch (entry.value_ptr.location) {
201+
.url => try stdout.print("{s}{s} {s}\n", .{
202+
parent_prefix, name, "(missing)",
203+
}),
204+
.path => |p| try stdout.print("{s}{s} {s} (local)\n", .{
205+
parent_prefix, name, p,
206+
}),
207+
}
208+
}
209+
}
210+
}
211+
212+
fn fatal(comptime format: []const u8, args: anytype) noreturn {
213+
std.log.err(format, args);
214+
std.process.exit(1);
215+
}
216+
217+
const std = @import("std");
218+
const assert = std.debug.assert;
219+
const Allocator = std.mem.Allocator;

src/main.zig

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,11 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
319319
.cmd_name = "objcopy",
320320
.root_src_path = "objcopy.zig",
321321
});
322+
} else if (mem.eql(u8, cmd, "dep-hash")) {
323+
return jitCmd(gpa, arena, cmd_args, .{
324+
.cmd_name = "dep-hash",
325+
.root_src_path = "dep-hash.zig",
326+
});
322327
} else if (mem.eql(u8, cmd, "fetch")) {
323328
return cmdFetch(gpa, arena, cmd_args);
324329
} else if (mem.eql(u8, cmd, "libc")) {

0 commit comments

Comments
 (0)