Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions src/Package/Fetch.zig
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ omit_missing_hash_error: bool,
/// which specifies inclusion rules. This is intended to be true for the first
/// fetch task and false for the recursive dependencies.
allow_missing_paths_field: bool,
tar_strip_components: u32 = 1,

// Above this are fields provided as inputs to `run`.
// Below this are fields populated by `run`.
Expand Down Expand Up @@ -674,6 +675,7 @@ fn queueJobsForDeps(f: *Fetch) RunError!void {
prog_names[new_fetch_index] = dep_name;
new_fetch_index += 1;
f.job_queue.all_fetches.appendAssumeCapacity(new_fetch);

new_fetch.* = .{
.arena = std.heap.ArenaAllocator.init(gpa),
.location = location,
Expand All @@ -687,6 +689,7 @@ fn queueJobsForDeps(f: *Fetch) RunError!void {
.job_queue = f.job_queue,
.omit_missing_hash_error = false,
.allow_missing_paths_field = true,
.tar_strip_components = dep.tar_strip_components,

.package_root = undefined,
.error_bundle = undefined,
Expand Down Expand Up @@ -867,9 +870,9 @@ const FileType = enum {
try std.testing.expectEqual(@as(?FileType, .@"tar.xz"), fromContentDisposition("ATTACHMENT; filename=\"stuff.tar.xz\""));
try std.testing.expectEqual(@as(?FileType, .@"tar.xz"), fromContentDisposition("attachment; FileName=\"stuff.tar.xz\""));
try std.testing.expectEqual(@as(?FileType, .@"tar.gz"), fromContentDisposition("attachment; FileName*=UTF-8\'\'xyz%2Fstuff.tar.gz"));
try std.testing.expectEqual(@as(?FileType, .tar), fromContentDisposition("attachment; FileName=\"stuff.tar\""));

try std.testing.expect(fromContentDisposition("attachment FileName=\"stuff.tar.gz\"") == null);
try std.testing.expect(fromContentDisposition("attachment; FileName=\"stuff.tar\"") == null);
try std.testing.expect(fromContentDisposition("attachment; FileName\"stuff.gz\"") == null);
try std.testing.expect(fromContentDisposition("attachment; size=42") == null);
try std.testing.expect(fromContentDisposition("inline; size=42") == null);
Expand Down Expand Up @@ -1152,7 +1155,7 @@ fn unpackTarball(f: *Fetch, out_dir: fs.Dir, reader: anytype) RunError!void {

std.tar.pipeToFileSystem(out_dir, reader, .{
.diagnostics = &diagnostics,
.strip_components = 1,
.strip_components = f.tar_strip_components,
// TODO: we would like to set this to executable_bit_only, but two
// things need to happen before that:
// 1. the tar implementation needs to support it
Expand Down
63 changes: 63 additions & 0 deletions src/Package/Manifest.zig
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pub const Dependency = struct {
node: Ast.Node.Index,
name_tok: Ast.TokenIndex,
lazy: bool,
tar_strip_components: u32 = 1,

pub const Location = union(enum) {
url: []const u8,
Expand Down Expand Up @@ -307,6 +308,7 @@ const Parse = struct {
.node = node,
.name_tok = 0,
.lazy = false,
.tar_strip_components = 1,
};
var has_location = false;

Expand Down Expand Up @@ -352,6 +354,11 @@ const Parse = struct {
error.ParseFailure => continue,
else => |e| return e,
};
} else if (mem.eql(u8, field_name, "tar_strip_components")) {
dep.tar_strip_components = parseInt(u32, p, field_init) catch |err| switch (err) {
error.ParseFailure => continue,
else => |e| return e,
};
} else {
// Ignore unknown fields so that we can add fields in future zig
// versions without breaking older zig versions.
Expand Down Expand Up @@ -402,6 +409,22 @@ const Parse = struct {
}
}

fn parseInt(comptime T: type, p: *Parse, node: Ast.Node.Index) !T {
const ast = p.ast;
const node_tags = ast.nodes.items(.tag);
const main_tokens = ast.nodes.items(.main_token);
if (node_tags[node] != .number_literal) {
return fail(p, main_tokens[node], "expected identifier", .{});
}
const ident_token = main_tokens[node];
const token_bytes = ast.tokenSlice(ident_token);

const value: T = std.fmt.parseInt(T, token_bytes, 10) catch {
return fail(p, ident_token, "expected integer", .{});
};
return value;
}

fn parseString(p: *Parse, node: Ast.Node.Index) ![]const u8 {
const ast = p.ast;
const node_tags = ast.nodes.items(.tag);
Expand Down Expand Up @@ -703,3 +726,43 @@ test "minimum_zig_version - invalid version" {

try testing.expect(manifest.minimum_zig_version == null);
}

test "tar_strip_components" {
const gpa = testing.allocator;

const example =
\\.{
\\ .name = "foo",
\\ .version = "3.2.1",
\\ .dependencies = .{
\\ .bar = .{
\\ .url = "https://example.com/baz.tar.gz",
\\ .tar_strip_components = 123,
\\ },
\\ },
\\ .paths = .{""},
\\}
;

var ast = try Ast.parse(gpa, example, .zon);
defer ast.deinit(gpa);

try testing.expect(ast.errors.len == 0);

var manifest = try Manifest.parse(gpa, ast, .{});
defer manifest.deinit(gpa);

try testing.expect(manifest.errors.len == 0);
try testing.expectEqualStrings("foo", manifest.name);

try testing.expect(manifest.dependencies.count() == 1);
try testing.expectEqualStrings("bar", manifest.dependencies.keys()[0]);
try testing.expectEqualStrings(
"https://example.com/baz.tar.gz",
manifest.dependencies.values()[0].location.url,
);
try testing.expectEqual(
123,
manifest.dependencies.values()[0].tar_strip_components,
);
}