Skip to content

Commit

Permalink
feat: windows support
Browse files Browse the repository at this point in the history
  • Loading branch information
hendriknielaender authored Jun 19, 2024
2 parents c6fba40 + b0b8dc4 commit d53b2b4
Show file tree
Hide file tree
Showing 7 changed files with 205 additions and 35 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ zig-out-*
# libarchive
*.o
*.a
.zig-cache
2 changes: 1 addition & 1 deletion build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ pub fn build(b: *std.Build) void {
.name = "zvm",
.root_source_file = .{ .src_path = .{ .owner = b, .sub_path = "src/main.zig" } },
.target = target,
.optimize = .ReleaseFast,
.optimize = optimize,
.version = version,
});

Expand Down
98 changes: 87 additions & 11 deletions src/alias.zig
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
const std = @import("std");
const builtin = @import("builtin");
const tools = @import("tools.zig");

pub fn setZigVersion(version: []const u8) !void {
const allocator = std.heap.page_allocator;
const userHome = getUserHome();
const userHome = tools.getHome();

const zigPath = try std.fs.path.join(allocator, &[_][]const u8{ userHome, ".zm", "versions", version });
defer allocator.free(zigPath);
Expand All @@ -14,19 +16,93 @@ pub fn setZigVersion(version: []const u8) !void {
try verifyZigVersion(allocator, version);
}

fn getUserHome() []const u8 {
return std.posix.getenv("HOME") orelse ".";
fn updateSymlink(zigPath: []const u8, symlinkPath: []const u8) !void {
if (builtin.os.tag == .windows) {
if (std.fs.path.dirname(symlinkPath)) |dirname| {
var parent_dir = try std.fs.openDirAbsolute(dirname, .{
.iterate = true,
});
defer parent_dir.close();
try parent_dir.deleteTree(std.fs.path.basename(symlinkPath));
} else {
@panic("sorry, dirname is not avaiable!");
}
if (doesDirExist(symlinkPath)) try std.fs.deleteDirAbsolute(symlinkPath);
try copyDir(zigPath, symlinkPath);
} else {
if (doesFileExist(symlinkPath)) try std.fs.cwd().deleteFile(symlinkPath);
std.posix.symlink(zigPath, symlinkPath) catch |err| switch (err) {
error.PathAlreadyExists => {
try std.fs.cwd().deleteFile(symlinkPath);
try std.posix.symlink(zigPath, symlinkPath);
},
else => return err,
};
}
}

fn updateSymlink(zigPath: []const u8, symlinkPath: []const u8) !void {
if (doesFileExist(symlinkPath)) try std.fs.cwd().deleteFile(symlinkPath);
std.posix.symlink(zigPath, symlinkPath) catch |err| switch (err) {
error.PathAlreadyExists => {
try std.fs.cwd().deleteFile(symlinkPath);
try std.posix.symlink(zigPath, symlinkPath);
fn copyDir(source_dir: []const u8, dest_dir: []const u8) !void {
var source = try std.fs.openDirAbsolute(
source_dir,
.{ .iterate = true },
);
defer source.close();

// try make dir
std.fs.makeDirAbsolute(dest_dir) catch |err| switch (err) {
error.PathAlreadyExists => {},
else => {
tools.log.err("Failed to create directory: {s}", .{dest_dir});
return err;
},
else => return err,
};

var dest = try std.fs.openDirAbsolute(
dest_dir,
.{ .iterate = true },
);
defer dest.close();

var iterate = source.iterate();
const allocator = tools.getAllocator();
while (try iterate.next()) |entry| {
const entry_name = entry.name;

const source_sub_path = try std.fs.path.join(
allocator,
&.{ source_dir, entry_name },
);
defer allocator.free(source_sub_path);

const dest_sub_path = try std.fs.path.join(
allocator,
&.{ dest_dir, entry_name },
);
defer allocator.free(dest_sub_path);

switch (entry.kind) {
.directory => {
try copyDir(source_sub_path, dest_sub_path);
},
.file => {
try std.fs.copyFileAbsolute(source_sub_path, dest_sub_path, .{});
},
else => {},
}
}
}

fn doesDirExist(path: []const u8) bool {
const result = blk: {
_ = std.fs.openDirAbsolute(path, .{}) catch |err| {
switch (err) {
error.FileNotFound => break :blk false,
else => break :blk true,
}
};
break :blk true;
};
return result;
}

fn doesFileExist(path: []const u8) bool {
Expand Down Expand Up @@ -54,7 +130,7 @@ fn verifyZigVersion(allocator: std.mem.Allocator, expectedVersion: []const u8) !
}

fn retrieveZigVersion(allocator: std.mem.Allocator) ![]u8 {
const userHome = getUserHome();
const userHome = tools.getHome();
const symlinkPath = try std.fs.path.join(allocator, &[_][]const u8{ userHome, ".zm", "current" });
defer allocator.free(symlinkPath);

Expand Down
38 changes: 22 additions & 16 deletions src/download.zig
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const std = @import("std");
const builtin = @import("builtin");
const tools = @import("tools.zig");
const sha2 = @import("std").crypto.hash.sha2;
const architecture = @import("architecture.zig");
const Progress = std.Progress;
Expand All @@ -10,11 +11,6 @@ const crypto = std.crypto;

const archive_ext = if (builtin.os.tag == .windows) "zip" else "tar.xz";

fn getZvmPathSegment(segment: []const u8) ![]u8 {
const user_home = std.posix.getenv("HOME") orelse ".";
return std.fs.path.join(std.heap.page_allocator, &[_][]const u8{ user_home, ".zm", segment });
}

pub fn content(allocator: std.mem.Allocator, version: []const u8, url: []const u8) !?[32]u8 {
// Initialize the Progress structure
const root_node = Progress.start(.{
Expand All @@ -24,11 +20,11 @@ pub fn content(allocator: std.mem.Allocator, version: []const u8, url: []const u

defer root_node.end();

const data_allocator = tools.getAllocator();
// Ensure version directory exists before any operation
const version_path = try getZvmPathSegment("versions");
defer allocator.free(version_path);
const version_path = try tools.getZvmPathSegment(data_allocator, "versions");
defer data_allocator.free(version_path);

defer allocator.free(version_path);
std.fs.cwd().makePath(version_path) catch |err| switch (err) {
error.PathAlreadyExists => {
// The path already exists and is a directory, nothing to do here
Expand All @@ -44,8 +40,8 @@ pub fn content(allocator: std.mem.Allocator, version: []const u8, url: []const u
const version_folder_name = try std.fmt.allocPrint(allocator, "versions/{s}", .{version});
defer allocator.free(version_folder_name);

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

if (checkExistingVersion(version_folder_path)) {
std.debug.print("→ Version {s} is already installed.\n", .{version});
Expand Down Expand Up @@ -91,7 +87,13 @@ fn confirmUserChoice() bool {
return std.ascii.toLower(buffer[0]) == 'y';
}

fn downloadAndExtract(allocator: std.mem.Allocator, uri: std.Uri, version_path: []const u8, version: []const u8, root_node: std.Progress.Node) ![32]u8 {
fn downloadAndExtract(
allocator: std.mem.Allocator,
uri: std.Uri,
version_path: []const u8,
version: []const u8,
root_node: std.Progress.Node,
) ![32]u8 {
var client = std.http.Client{ .allocator = allocator };
defer client.deinit();

Expand Down Expand Up @@ -149,9 +151,13 @@ fn downloadAndExtract(allocator: std.mem.Allocator, uri: std.Uri, version_path:
const c_allocator = std.heap.c_allocator;

// ~/.zm/versions/zig-macos-x86_64-0.10.0.tar.xz
const zvm_path = try getZvmPathSegment("");
const downloaded_file_path = try std.fs.path.join(allocator, &.{ zvm_path, file_name });
defer allocator.free(downloaded_file_path);
const data_allocator = tools.getAllocator();

const zvm_path = try tools.getZvmPathSegment(data_allocator, "");
defer data_allocator.free(zvm_path);

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});

Expand Down Expand Up @@ -179,8 +185,8 @@ fn downloadAndExtract(allocator: std.mem.Allocator, uri: std.Uri, version_path:
}

fn openOrCreateZvmDir() !std.fs.Dir {
const zvm_path = try getZvmPathSegment("");
defer std.heap.page_allocator.free(zvm_path);
const zvm_path = try tools.getZvmPathSegment(tools.getAllocator(), "");
defer tools.getAllocator().free(zvm_path);

const openDirOptions = .{ .access_sub_paths = true, .no_follow = false };
const potentialDir = std.fs.cwd().openDir(zvm_path, openDirOptions);
Expand Down
48 changes: 44 additions & 4 deletions src/extract.zig
Original file line number Diff line number Diff line change
@@ -1,8 +1,48 @@
const std = @import("std");
const builtin = @import("builtin");
const tools = @import("tools.zig");

pub fn extract_tarxz_to_dir(allocator: std.mem.Allocator, outDir: std.fs.Dir, file: std.fs.File) !void {
var buffered_reader = std.io.bufferedReader(file.reader());
var decompressed = try std.compress.xz.decompress(allocator, buffered_reader.reader());
defer decompressed.deinit();
try std.tar.pipeToFileSystem(outDir, decompressed.reader(), .{ .mode_mode = .executable_bit_only, .strip_components = 1 });
if (builtin.os.tag == .windows) {
try extract_zip_dir(outDir, file);
} else {
var buffered_reader = std.io.bufferedReader(file.reader());
var decompressed = try std.compress.xz.decompress(allocator, buffered_reader.reader());
defer decompressed.deinit();
try std.tar.pipeToFileSystem(outDir, decompressed.reader(), .{ .mode_mode = .executable_bit_only, .strip_components = 1 });
}
}

pub fn extract_zip_dir(outDir: std.fs.Dir, file: std.fs.File) !void {
var arena = std.heap.ArenaAllocator.init(tools.getAllocator());
defer arena.deinit();

const allocator = arena.allocator();

const tmp_path = try tools.getZvmPathSegment(allocator, "tmpdir");
defer std.fs.deleteDirAbsolute(tmp_path) catch unreachable;

// make tmp dir
try std.fs.makeDirAbsolute(tmp_path);
var tmp_dir = try std.fs.openDirAbsolute(tmp_path, .{ .iterate = true });

try std.zip.extract(tmp_dir, file.seekableStream(), .{});

var iterate = tmp_dir.iterate();

var sub_dir = blk: {
const entry = try iterate.next() orelse return error.NotFound;
break :blk try tmp_dir.openDir(entry.name, .{
.iterate = true,
});
};
defer sub_dir.close();
const sub_path = try sub_dir.realpathAlloc(allocator, "");
defer std.fs.deleteDirAbsolute(sub_path) catch unreachable;

var sub_iterate = sub_dir.iterate();

while (try sub_iterate.next()) |entry| {
try std.fs.rename(sub_dir, entry.name, outDir, entry.name);
}
}
12 changes: 9 additions & 3 deletions src/main.zig
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const std = @import("std");
const tools = @import("tools.zig");
const Command = @import("command.zig").Command;
const handleCommands = @import("command.zig").handleCommands;

Expand All @@ -14,9 +15,14 @@ const CommandOption = struct {
};

pub fn main() !void {
const allocator = std.heap.page_allocator;
const args = try std.process.argsAlloc(allocator);
defer std.process.argsFree(allocator, args);
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer if (gpa.deinit() == .leak) @panic("memory leaked!");

try tools.dataInit(gpa.allocator());
defer tools.dataDeinit();

const args = try std.process.argsAlloc(tools.getAllocator());
defer std.process.argsFree(tools.getAllocator(), args);

const cmd_data = try parseArgs(args);
try handleCommands(cmd_data.cmd, cmd_data.params);
Expand Down
41 changes: 41 additions & 0 deletions src/tools.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
const std = @import("std");
const builtin = @import("builtin");

var allocator: std.mem.Allocator = undefined;

var home_dir: []const u8 = undefined;

pub const log = std.log.scoped(.zvm);

/// init the data
pub fn dataInit(tmp_allocator: std.mem.Allocator) !void {
allocator = tmp_allocator;
// setting the home dir
home_dir = if (builtin.os.tag == .windows)
try std.process.getEnvVarOwned(allocator, "USERPROFILE")
else
std.posix.getenv("HOME") orelse ".";
}

/// deinit the data
pub fn dataDeinit() void {
if (builtin.os.tag == .windows)
allocator.free(home_dir);
}

/// get home dir
pub fn getHome() []const u8 {
return home_dir;
}

/// get the allocator
pub fn getAllocator() std.mem.Allocator {
return allocator;
}

pub fn getZvmPathSegment(_allocator: std.mem.Allocator, segment: []const u8) ![]u8 {
return std.fs.path.join(
_allocator,
&[_][]const u8{ getHome(), ".zm", segment },
);
}

0 comments on commit d53b2b4

Please sign in to comment.