Skip to content

Commit df4853a

Browse files
authored
Merge pull request #17363 from ziglang/tar-symlinks
introduce the `zig fetch` subcommand and symlink support in zig packages
2 parents 4930094 + 573a13f commit df4853a

File tree

8 files changed

+681
-261
lines changed

8 files changed

+681
-261
lines changed

CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,7 @@ set(ZIG_STAGE2_SOURCES
527527
"${CMAKE_SOURCE_DIR}/src/Liveness.zig"
528528
"${CMAKE_SOURCE_DIR}/src/Module.zig"
529529
"${CMAKE_SOURCE_DIR}/src/Package.zig"
530+
"${CMAKE_SOURCE_DIR}/src/Package/hash.zig"
530531
"${CMAKE_SOURCE_DIR}/src/RangeSet.zig"
531532
"${CMAKE_SOURCE_DIR}/src/Sema.zig"
532533
"${CMAKE_SOURCE_DIR}/src/TypedValue.zig"

lib/std/fs.zig

+3-1
Original file line numberDiff line numberDiff line change
@@ -2003,10 +2003,12 @@ pub const Dir = struct {
20032003
return os.windows.CreateSymbolicLink(self.fd, sym_link_path_w, target_path_w, flags.is_directory);
20042004
}
20052005

2006+
pub const ReadLinkError = os.ReadLinkError;
2007+
20062008
/// Read value of a symbolic link.
20072009
/// The return value is a slice of `buffer`, from index `0`.
20082010
/// Asserts that the path parameter has no null bytes.
2009-
pub fn readLink(self: Dir, sub_path: []const u8, buffer: []u8) ![]u8 {
2011+
pub fn readLink(self: Dir, sub_path: []const u8, buffer: []u8) ReadLinkError![]u8 {
20102012
if (builtin.os.tag == .wasi and !builtin.link_libc) {
20112013
return self.readLinkWasi(sub_path, buffer);
20122014
}

lib/std/tar.zig

+69-7
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,13 @@ pub const Options = struct {
33
strip_components: u32 = 0,
44
/// How to handle the "mode" property of files from within the tar file.
55
mode_mode: ModeMode = .executable_bit_only,
6+
/// Provide this to receive detailed error messages.
7+
/// When this is provided, some errors which would otherwise be returned immediately
8+
/// will instead be added to this structure. The API user must check the errors
9+
/// in diagnostics to know whether the operation succeeded or failed.
10+
diagnostics: ?*Diagnostics = null,
611

7-
const ModeMode = enum {
12+
pub const ModeMode = enum {
813
/// The mode from the tar file is completely ignored. Files are created
914
/// with the default mode when creating files.
1015
ignore,
@@ -13,12 +18,46 @@ pub const Options = struct {
1318
/// Other bits of the mode are left as the default when creating files.
1419
executable_bit_only,
1520
};
21+
22+
pub const Diagnostics = struct {
23+
allocator: std.mem.Allocator,
24+
errors: std.ArrayListUnmanaged(Error) = .{},
25+
26+
pub const Error = union(enum) {
27+
unable_to_create_sym_link: struct {
28+
code: anyerror,
29+
file_name: []const u8,
30+
link_name: []const u8,
31+
},
32+
unsupported_file_type: struct {
33+
file_name: []const u8,
34+
file_type: Header.FileType,
35+
},
36+
};
37+
38+
pub fn deinit(d: *Diagnostics) void {
39+
for (d.errors.items) |item| {
40+
switch (item) {
41+
.unable_to_create_sym_link => |info| {
42+
d.allocator.free(info.file_name);
43+
d.allocator.free(info.link_name);
44+
},
45+
.unsupported_file_type => |info| {
46+
d.allocator.free(info.file_name);
47+
},
48+
}
49+
}
50+
d.errors.deinit(d.allocator);
51+
d.* = undefined;
52+
}
53+
};
1654
};
1755

1856
pub const Header = struct {
1957
bytes: *const [512]u8,
2058

2159
pub const FileType = enum(u8) {
60+
normal_alias = 0,
2261
normal = '0',
2362
hard_link = '1',
2463
symbolic_link = '2',
@@ -65,13 +104,18 @@ pub const Header = struct {
65104
return str(header, 0, 0 + 100);
66105
}
67106

107+
pub fn linkName(header: Header) []const u8 {
108+
return str(header, 157, 157 + 100);
109+
}
110+
68111
pub fn prefix(header: Header) []const u8 {
69112
return str(header, 345, 345 + 155);
70113
}
71114

72115
pub fn fileType(header: Header) FileType {
73-
const result = @as(FileType, @enumFromInt(header.bytes[156]));
74-
return if (result == @as(FileType, @enumFromInt(0))) .normal else result;
116+
const result: FileType = @enumFromInt(header.bytes[156]);
117+
if (result == .normal_alias) return .normal;
118+
return result;
75119
}
76120

77121
fn str(header: Header, start: usize, end: usize) []const u8 {
@@ -148,7 +192,7 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !voi
148192
const header: Header = .{ .bytes = chunk[0..512] };
149193
const file_size = try header.fileSize();
150194
const rounded_file_size = std.mem.alignForward(u64, file_size, 512);
151-
const pad_len = @as(usize, @intCast(rounded_file_size - file_size));
195+
const pad_len: usize = @intCast(rounded_file_size - file_size);
152196
const unstripped_file_name = if (file_name_override_len > 0)
153197
file_name_buffer[0..file_name_override_len]
154198
else
@@ -175,7 +219,7 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !voi
175219
while (true) {
176220
const temp = try buffer.readChunk(reader, @intCast(rounded_file_size + 512 - file_off));
177221
if (temp.len == 0) return error.UnexpectedEndOfStream;
178-
const slice = temp[0..@as(usize, @intCast(@min(file_size - file_off, temp.len)))];
222+
const slice = temp[0..@intCast(@min(file_size - file_off, temp.len))];
179223
try file.writeAll(slice);
180224

181225
file_off += slice.len;
@@ -228,8 +272,26 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !voi
228272
buffer.skip(reader, @intCast(rounded_file_size)) catch return error.TarHeadersTooBig;
229273
},
230274
.hard_link => return error.TarUnsupportedFileType,
231-
.symbolic_link => return error.TarUnsupportedFileType,
232-
else => return error.TarUnsupportedFileType,
275+
.symbolic_link => {
276+
const file_name = try stripComponents(unstripped_file_name, options.strip_components);
277+
const link_name = header.linkName();
278+
279+
dir.symLink(link_name, file_name, .{}) catch |err| {
280+
const d = options.diagnostics orelse return error.UnableToCreateSymLink;
281+
try d.errors.append(d.allocator, .{ .unable_to_create_sym_link = .{
282+
.code = err,
283+
.file_name = try d.allocator.dupe(u8, file_name),
284+
.link_name = try d.allocator.dupe(u8, link_name),
285+
} });
286+
};
287+
},
288+
else => |file_type| {
289+
const d = options.diagnostics orelse return error.TarUnsupportedFileType;
290+
try d.errors.append(d.allocator, .{ .unsupported_file_type = .{
291+
.file_name = try d.allocator.dupe(u8, unstripped_file_name),
292+
.file_type = file_type,
293+
} });
294+
},
233295
}
234296
}
235297
}

lib/std/zig/ErrorBundle.zig

+12-12
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ fn renderErrorMessageToWriter(
202202
try counting_stderr.writeAll(": ");
203203
// This is the length of the part before the error message:
204204
// e.g. "file.zig:4:5: error: "
205-
const prefix_len = @as(usize, @intCast(counting_stderr.context.bytes_written));
205+
const prefix_len: usize = @intCast(counting_stderr.context.bytes_written);
206206
try ttyconf.setColor(stderr, .reset);
207207
try ttyconf.setColor(stderr, .bold);
208208
if (err_msg.count == 1) {
@@ -356,16 +356,16 @@ pub const Wip = struct {
356356
}
357357

358358
const compile_log_str_index = if (compile_log_text.len == 0) 0 else str: {
359-
const str = @as(u32, @intCast(wip.string_bytes.items.len));
359+
const str: u32 = @intCast(wip.string_bytes.items.len);
360360
try wip.string_bytes.ensureUnusedCapacity(gpa, compile_log_text.len + 1);
361361
wip.string_bytes.appendSliceAssumeCapacity(compile_log_text);
362362
wip.string_bytes.appendAssumeCapacity(0);
363363
break :str str;
364364
};
365365

366366
wip.setExtra(0, ErrorMessageList{
367-
.len = @as(u32, @intCast(wip.root_list.items.len)),
368-
.start = @as(u32, @intCast(wip.extra.items.len)),
367+
.len = @intCast(wip.root_list.items.len),
368+
.start = @intCast(wip.extra.items.len),
369369
.compile_log_text = compile_log_str_index,
370370
});
371371
try wip.extra.appendSlice(gpa, @as([]const u32, @ptrCast(wip.root_list.items)));
@@ -385,7 +385,7 @@ pub const Wip = struct {
385385

386386
pub fn addString(wip: *Wip, s: []const u8) !u32 {
387387
const gpa = wip.gpa;
388-
const index = @as(u32, @intCast(wip.string_bytes.items.len));
388+
const index: u32 = @intCast(wip.string_bytes.items.len);
389389
try wip.string_bytes.ensureUnusedCapacity(gpa, s.len + 1);
390390
wip.string_bytes.appendSliceAssumeCapacity(s);
391391
wip.string_bytes.appendAssumeCapacity(0);
@@ -394,7 +394,7 @@ pub const Wip = struct {
394394

395395
pub fn printString(wip: *Wip, comptime fmt: []const u8, args: anytype) !u32 {
396396
const gpa = wip.gpa;
397-
const index = @as(u32, @intCast(wip.string_bytes.items.len));
397+
const index: u32 = @intCast(wip.string_bytes.items.len);
398398
try wip.string_bytes.writer(gpa).print(fmt, args);
399399
try wip.string_bytes.append(gpa, 0);
400400
return index;
@@ -406,15 +406,15 @@ pub const Wip = struct {
406406
}
407407

408408
pub fn addErrorMessage(wip: *Wip, em: ErrorMessage) !MessageIndex {
409-
return @as(MessageIndex, @enumFromInt(try addExtra(wip, em)));
409+
return @enumFromInt(try addExtra(wip, em));
410410
}
411411

412412
pub fn addErrorMessageAssumeCapacity(wip: *Wip, em: ErrorMessage) MessageIndex {
413-
return @as(MessageIndex, @enumFromInt(addExtraAssumeCapacity(wip, em)));
413+
return @enumFromInt(addExtraAssumeCapacity(wip, em));
414414
}
415415

416416
pub fn addSourceLocation(wip: *Wip, sl: SourceLocation) !SourceLocationIndex {
417-
return @as(SourceLocationIndex, @enumFromInt(try addExtra(wip, sl)));
417+
return @enumFromInt(try addExtra(wip, sl));
418418
}
419419

420420
pub fn addReferenceTrace(wip: *Wip, rt: ReferenceTrace) !void {
@@ -430,7 +430,7 @@ pub const Wip = struct {
430430
const other_list = other.getMessages();
431431

432432
// The ensureUnusedCapacity call above guarantees this.
433-
const notes_start = wip.reserveNotes(@as(u32, @intCast(other_list.len))) catch unreachable;
433+
const notes_start = wip.reserveNotes(@intCast(other_list.len)) catch unreachable;
434434
for (notes_start.., other_list) |note, message| {
435435
wip.extra.items[note] = @intFromEnum(wip.addOtherMessage(other, message) catch unreachable);
436436
}
@@ -455,7 +455,7 @@ pub const Wip = struct {
455455
try wip.extra.ensureUnusedCapacity(wip.gpa, notes_len +
456456
notes_len * @typeInfo(ErrorBundle.ErrorMessage).Struct.fields.len);
457457
wip.extra.items.len += notes_len;
458-
return @as(u32, @intCast(wip.extra.items.len - notes_len));
458+
return @intCast(wip.extra.items.len - notes_len);
459459
}
460460

461461
fn addOtherMessage(wip: *Wip, other: ErrorBundle, msg_index: MessageIndex) !MessageIndex {
@@ -510,7 +510,7 @@ pub const Wip = struct {
510510

511511
fn addExtraAssumeCapacity(wip: *Wip, extra: anytype) u32 {
512512
const fields = @typeInfo(@TypeOf(extra)).Struct.fields;
513-
const result = @as(u32, @intCast(wip.extra.items.len));
513+
const result: u32 = @intCast(wip.extra.items.len);
514514
wip.extra.items.len += fields.len;
515515
setExtra(wip, result, extra);
516516
return result;

0 commit comments

Comments
 (0)