Skip to content

Commit

Permalink
Refactoring the zig download section
Browse files Browse the repository at this point in the history
  • Loading branch information
jinzhongjia committed Aug 4, 2024
1 parent b0d1698 commit 7e3cd05
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 199 deletions.
2 changes: 1 addition & 1 deletion src/alias.zig
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ pub fn set_zig_version(version: []const u8) !void {
const arena_allocator = arena.allocator();

const user_home = tools.get_home();
const version_path = try std.fs.path.join(arena_allocator, &[_][]const u8{ user_home, ".zm", "versions", version });
const version_path = try std.fs.path.join(arena_allocator, &[_][]const u8{ user_home, ".zm", "version", version });
const symlink_path = try tools.get_zvm_path_segment(arena_allocator, "current");

try update_current(version_path, symlink_path);
Expand Down
4 changes: 2 additions & 2 deletions src/command.zig
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ fn install_version(subcmd: ?[]const u8, param: ?[]const u8) !void {
if (subcmd) |scmd| {
if (std.mem.eql(u8, scmd, "zig")) {
if (param) |version| {
try install.from_version(version);
try install.install_zig(version);
} else {
std.debug.print("Please specify a version to install using 'install zig <version>'.\n", .{});
}
Expand All @@ -179,7 +179,7 @@ fn install_version(subcmd: ?[]const u8, param: ?[]const u8) !void {
std.debug.print("Unknown subcommand '{s}'. Use 'install zig <version>' or 'install zls <version>'.\n", .{scmd});
}
} else if (param) |version| {
try install.from_version(version);
try install.install_zig(version);
} else {
std.debug.print("Error: Please specify a version to install using 'install <version>'.\n", .{});
}
Expand Down
3 changes: 3 additions & 0 deletions src/config.zig
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ pub var allocator: std.mem.Allocator = undefined;
/// home dir environment variable
pub var home_dir: []const u8 = undefined;

/// global progress root node
pub var progress_root: std.Progress.Node = undefined;

/// zig meta data url
pub const zig_meta_url: []const u8 = "https://ziglang.org/download/index.json";
/// zls meta data url
Expand Down
203 changes: 50 additions & 153 deletions src/download.zig
Original file line number Diff line number Diff line change
@@ -1,184 +1,81 @@
const std = @import("std");
const assert = std.debug.assert;
const builtin = @import("builtin");
const tools = @import("tools.zig");
const sha2 = std.crypto.hash.sha2;
const architecture = @import("architecture.zig");
const alias = @import("alias.zig");
const lib = @import("extract.zig");
const config = @import("config.zig");

pub fn content(allocator: std.mem.Allocator, version: []const u8, url: []const u8) !?[32]u8 {
assert(version.len > 0);
assert(url.len > 0);

const root_node = std.Progress.start(.{
.root_name = "",
.estimated_total_items = 4,
});
defer root_node.end();

const data_allocator = tools.get_allocator();
const version_path = try tools.get_zvm_path_segment(data_allocator, "versions");
defer data_allocator.free(version_path);

try tools.try_create_path(version_path);

const uri = std.Uri.parse(url) catch unreachable;
const version_folder_name = try std.fmt.allocPrint(allocator, "versions/{s}", .{version});
defer allocator.free(version_folder_name);

const version_folder_path = try tools.get_zvm_path_segment(data_allocator, version_folder_name);
defer data_allocator.free(version_folder_path);

if (tools.does_path_exist(version_folder_path)) {
std.debug.print("→ Version {s} is already installed.\n", .{version});
std.debug.print("Do you want to reinstall? (\x1b[1mY\x1b[0mes/\x1b[1mN\x1b[0mo): ", .{});

if (!confirm_user_choice()) {
std.debug.print("Do you want to set version {s} as the default? (\x1b[1mY\x1b[0mes/\x1b[1mN\x1b[0mo): ", .{version});
if (confirm_user_choice()) {
try alias.set_zig_version(version);
std.debug.print("Version {s} has been set as the default.\n", .{version});
return null;
} else {
std.debug.print("Aborting...\n", .{});
return null;
}
}

try std.fs.cwd().deleteTree(version_folder_path);
} else {
std.debug.print("→ Version {s} is not installed. Beginning download...\n", .{version});
}

const computed_hash = try download_and_extract(allocator, uri, version_path, version, root_node);

var set_version_node = root_node.start("Setting Version", 1);
try alias.set_zig_version(version);
set_version_node.end();

return computed_hash;
}

fn confirm_user_choice() bool {
var buffer: [4]u8 = undefined;
_ = std.io.getStdIn().read(buffer[0..]) catch return false;

return std.ascii.toLower(buffer[0]) == 'y';
}
const sha2 = std.crypto.hash.sha2;

fn download_and_extract(
allocator: std.mem.Allocator,
/// download the url
/// and verify hashsum (if exist)
pub fn download(
uri: std.Uri,
version_path: []const u8,
version: []const u8,
root_node: std.Progress.Node,
) ![32]u8 {
assert(version_path.len > 0);
assert(version.len > 0);
file_name: []const u8,
shasum: ?[64]u8,
) !std.fs.File {
// whether verify hashsum
const if_hash = shasum != null;

// allocator
const allocator = tools.get_allocator();

// http client
var client = std.http.Client{ .allocator = allocator };
defer client.deinit();

var header_buffer: [262144]u8 = undefined; // 256 * 1024 = 262kb
var header_buffer: [1024]u8 = undefined; // 1024b

var req = try client.open(.GET, uri, .{ .server_header_buffer = &header_buffer });
defer req.deinit();

try req.send();
try req.wait();
try std.testing.expect(req.response.status == .ok);

const zvm_path = try tools.get_zvm_path_segment(allocator, "");
defer allocator.free(zvm_path);

var zvm_dir = std.fs.cwd().makeOpenPath(zvm_path, .{}) catch |err| {
std.debug.print("sorry, open zvm path failed, erro is {}\n", .{err});
std.process.exit(1);
};

defer zvm_dir.close();

const platform_str = try architecture.platform_str(architecture.DetectParams{
.os = builtin.os.tag,
.arch = builtin.cpu.arch,
.reverse = false,
}) orelse unreachable;

const file_name = try std.mem.concat(allocator, u8, &.{
"zig-",
platform_str,
"-",
version,
".",
config.zig_archive_ext,
});
defer allocator.free(file_name);
// ensure req successfully
if (req.response.status != .ok)
return error.DownFailed;

const total_size: usize = @intCast(req.response.content_length orelse 0);
var downloaded_bytes: usize = 0;
// NOTE:
// const total_size: usize = @intCast(req.response.content_length orelse 0);

const download_message = try std.fmt.allocPrint(allocator, "Downloading Zig version {s} for platform {s}...", .{ version, platform_str });
defer allocator.free(download_message);
var download_node = root_node.start(download_message, total_size);
// this file store the downloaded src
const zvm_path = try tools.get_zvm_path_segment(allocator, "store");
defer allocator.free(zvm_path);

const file_stream = try zvm_dir.createFile(file_name, .{});
var store = try std.fs.cwd().makeOpenPath(zvm_path, .{});
defer store.close();

std.debug.print("Download complete, file written: {s}\n", .{file_name});
// create a new file
const new_file = try store.createFile(file_name, .{
.read = true,
});

var sha256 = sha2.Sha256.init(.{});
// whether enable hashsum
var sha256 = if (if_hash) sha2.Sha256.init(.{}) else undefined;

// the tmp buffer to store the receive data
var buffer: [512]u8 = undefined;
// get reader
const reader = req.reader();
while (true) {
var buffer: [8192]u8 = undefined;
const bytes_read = try req.reader().read(buffer[0..]);
if (bytes_read == 0) break;

downloaded_bytes += bytes_read;
download_node.setCompletedItems(downloaded_bytes);
sha256.update(buffer[0..bytes_read]);
try file_stream.writeAll(buffer[0..bytes_read]);
// the read byte number
const byte_nums = try reader.read(&buffer);
if (byte_nums == 0)
break;
if (if_hash)
sha256.update(buffer[0..byte_nums]);
// write to file
try new_file.writeAll(buffer[0..byte_nums]);
}
file_stream.close();

download_node.end();

var extract_node = root_node.start("Extracting", 1);
const data_allocator = tools.get_allocator();

const downloaded_file_path = try std.fs.path.join(data_allocator, &.{ zvm_path, file_name });
defer data_allocator.free(downloaded_file_path);

std.debug.print("Downloaded file path: {s}\n", .{downloaded_file_path});

const folder_path = try std.fs.path.join(allocator, &.{ version_path, version });
defer allocator.free(folder_path);
// when calculate hashsum
if (if_hash) {
var result = std.mem.zeroes([32]u8);
sha256.final(&result);

std.fs.makeDirAbsolute(folder_path) catch |err| {
std.debug.print("makeDirAbsolute: {any}\n", .{err});
};

const zvm_dir_version = try std.fs.openDirAbsolute(folder_path, .{});
const downloaded_file = try zvm_dir.openFile(downloaded_file_path, .{});
defer downloaded_file.close();

if (builtin.os.tag == .windows) {
try lib.extract_zip_dir(zvm_dir_version, downloaded_file);
} else {
try lib.extract_tarxz_to_dir(allocator, zvm_dir_version, downloaded_file);
if (!tools.verify_hash(result, shasum.?))
return error.HashMismatch;
}

extract_node.end();

var result: [32]u8 = undefined;
sha256.final(&result);
return result;
}

fn open_or_create_zvm_dir() !std.fs.Dir {
const allocator = tools.get_allocator();
const zvm_path = try tools.get_zvm_path_segment(allocator, "");
defer allocator.free(zvm_path);
try new_file.seekTo(0);

return try std.fs.cwd().makeOpenPath(zvm_path, .{});
return new_file;
}
18 changes: 15 additions & 3 deletions src/extract.zig
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,23 @@ const tools = @import("tools.zig");
const xz = std.compress.xz;
const tar = std.tar;

/// extract file to out_dir
pub fn extrace(
out_dir: std.fs.Dir,
file: std.fs.File,
file_type: enum { tarxz, zip },
) !void {
switch (file_type) {
.zip => try extract_zip_dir(out_dir, file),
.tarxz => try extract_tarxz_to_dir(out_dir, file),
}
}

/// extract tar.xz to dir
pub fn extract_tarxz_to_dir(allocator: std.mem.Allocator, out_dir: std.fs.Dir, file: std.fs.File) !void {
fn extract_tarxz_to_dir(out_dir: std.fs.Dir, file: std.fs.File) !void {
var buffered_reader = std.io.bufferedReader(file.reader());

var decompressed = try xz.decompress(allocator, buffered_reader.reader());
var decompressed = try xz.decompress(tools.get_allocator(), buffered_reader.reader());
defer decompressed.deinit();

try tar.pipeToFileSystem(
Expand All @@ -20,7 +32,7 @@ pub fn extract_tarxz_to_dir(allocator: std.mem.Allocator, out_dir: std.fs.Dir, f
}

/// extract zip to directory
pub fn extract_zip_dir(out_dir: std.fs.Dir, file: std.fs.File) !void {
fn extract_zip_dir(out_dir: std.fs.Dir, file: std.fs.File) !void {
var arena = std.heap.ArenaAllocator.init(tools.get_allocator());
defer arena.deinit();

Expand Down
Loading

0 comments on commit 7e3cd05

Please sign in to comment.