Skip to content

Commit

Permalink
more windows support
Browse files Browse the repository at this point in the history
  • Loading branch information
marler8997 committed Jun 17, 2021
1 parent 5280013 commit 780690e
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 28 deletions.
35 changes: 35 additions & 0 deletions fixdeletetree.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
const std = @import("std");
const builtin = std.builtin;

//
// TODO: we should fix std library to address these issues
//
pub fn deleteTree(dir: std.fs.Dir, sub_path: []const u8) !void {
if (builtin.os.tag != .windows) {
return dir.deleteTree(sub_path);
}

// workaround issue on windows where it just doesn't delete things
const MAX_ATTEMPTS = 10;
var attempt: u8 = 0;
while (true) : (attempt += 1) {
return dir.deleteTree(sub_path) catch |e| {
if (attempt == MAX_ATTEMPTS) return e;
switch (e) {
error.FileBusy => {
if (attempt == MAX_ATTEMPTS) return e;
std.log.warn("path '{s}' is busy, will retry", .{sub_path});
std.time.sleep(std.time.ns_per_ms * 100); // sleep for 100 ms
},
else => return e,
}
};
}
}
pub fn deleteTreeAbsolute(dir_absolute: []const u8) !void {
if (builtin.os.tag != .windows) {
return std.fs.deleteTreeAbsolute(dir_absolute);
}
std.debug.assert(std.fs.path.isAbsolute(dir_absolute));
return deleteTree(std.fs.cwd(), dir_absolute);
}
38 changes: 32 additions & 6 deletions test.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ const testing = std.testing;

const sep = std.fs.path.sep_str;

const fixdeletetree = @import("fixdeletetree.zig");

pub fn main() !void {
std.log.info("running test!", .{});
try std.fs.cwd().deleteTree("scratch");
try fixdeletetree.deleteTree(std.fs.cwd(), "scratch");
try std.fs.cwd().makeDir("scratch");
const install_dir = "scratch" ++ sep ++ "install";
const bin_dir = "scratch" ++ sep ++ "bin";
Expand All @@ -14,7 +16,8 @@ pub fn main() !void {

// NOTE: for now we are incorrectly assuming the install dir is CWD/zig-out
const zigup = "." ++ sep ++ "zig-out" ++ sep ++ "bin" ++ sep ++ "zigup" ++ std.builtin.target.exeFileExt();
const zigup_args = &[_][]const u8 { zigup, "--install-dir", install_dir, "--path-link", bin_dir ++ sep ++ "zig" };
const path_link = if (std.builtin.os.tag == .windows) "scratch\\zig.bat" else (bin_dir ++ sep ++ "zig");
const zigup_args = &[_][]const u8 { zigup, "--install-dir", install_dir, "--path-link", path_link };

var allocator_store = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer allocator_store.deinit();
Expand All @@ -35,7 +38,7 @@ pub fn main() !void {
const result = try runCaptureOuts(allocator, ".", zigup_args ++ &[_][]const u8 {"default"});
defer { allocator.free(result.stdout); allocator.free(result.stderr); }
try passOrDumpAndThrow(result);
try testing.expect(std.mem.containsAtLeast(u8, result.stderr, 1, "<no-default>"));
try testing.expect(std.mem.eql(u8, result.stdout, "<no-default>\n"));
}
{
const result = try runCaptureOuts(allocator, ".", zigup_args ++ &[_][]const u8 {"fetch-index"});
Expand All @@ -44,6 +47,13 @@ pub fn main() !void {
try testing.expect(std.mem.containsAtLeast(u8, result.stdout, 1, "master"));
}
try runNoCapture(".", zigup_args ++ &[_][]const u8 {"0.5.0"});
{
const result = try runCaptureOuts(allocator, ".", zigup_args ++ &[_][]const u8 {"default"});
defer { allocator.free(result.stdout); allocator.free(result.stderr); }
try passOrDumpAndThrow(result);
dumpExecResult(result);
try testing.expect(std.mem.eql(u8, result.stdout, "0.5.0\n"));
}
{
const result = try runCaptureOuts(allocator, ".", zigup_args ++ &[_][]const u8 {"fetch", "0.5.0"});
defer { allocator.free(result.stdout); allocator.free(result.stderr); }
Expand All @@ -52,6 +62,13 @@ pub fn main() !void {
}
try runNoCapture(".", zigup_args ++ &[_][]const u8 {"master"});
try runNoCapture(".", zigup_args ++ &[_][]const u8 {"0.6.0"});
{
const result = try runCaptureOuts(allocator, ".", zigup_args ++ &[_][]const u8 {"default"});
defer { allocator.free(result.stdout); allocator.free(result.stderr); }
try passOrDumpAndThrow(result);
dumpExecResult(result);
try testing.expect(std.mem.eql(u8, result.stdout, "0.6.0\n"));
}
{
const result = try runCaptureOuts(allocator, ".", zigup_args ++ &[_][]const u8 {"list"});
defer { allocator.free(result.stdout); allocator.free(result.stderr); }
Expand Down Expand Up @@ -104,18 +121,27 @@ fn getCompilerCount(install_dir: []const u8) !u32 {
if (entry.kind == .Directory) {
count += 1;
} else {
try testing.expect(entry.kind == .SymLink);
if (std.builtin.os.tag == .windows) {
try testing.expect(entry.kind == .File);
} else {
try testing.expect(entry.kind == .SymLink);
}
}
}
return count;
}


fn trailNl(s: []const u8) []const u8 {
return if (s.len == 0 or s[s.len-1] != '\n') "\n" else "";
}

fn dumpExecResult(result: std.ChildProcess.ExecResult) void {
if (result.stdout.len > 0) {
std.log.info("STDOUT: '{s}'", .{result.stdout});
std.debug.print("--- STDOUT ---\n{s}{s}--------------\n", .{result.stdout, trailNl(result.stdout)});
}
if (result.stderr.len > 0) {
std.log.info("STDERR: '{s}'", .{result.stderr});
std.debug.print("--- STDERR ---\n{s}{s}--------------\n", .{result.stderr, trailNl(result.stderr)});
}
}

Expand Down
91 changes: 69 additions & 22 deletions zigup.zig
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@ const Allocator = mem.Allocator;

const ziget = @import("ziget");

const fixdeletetree = @import("fixdeletetree.zig");

const arch = "x86_64";
const os = if (builtin.os.tag == .windows) "windows" else "linux";
const url_platform = os ++ "-" ++ arch;
const json_platform = arch ++ "-" ++ os;
const archive_ext = if (builtin.os.tag == .windows) "zip" else "tar.xz";

var global_optional_install_dir: ?[]const u8 = null;
var global_optional_path_link: ?[]const u8 = null;
Expand Down Expand Up @@ -125,6 +128,9 @@ fn makeZigPathLinkString(allocator: *Allocator) ![]const u8 {

// for now we're just going to hardcode the path to $HOME/bin/zig
const home = try getHomeDir();
if (builtin.os.tag == .windows) {
return try std.fs.path.join(allocator, &[_][]const u8{ home, "zig.bat" });
}
return try std.fs.path.join(allocator, &[_][]const u8{ home, "bin", "zig" });
}

Expand Down Expand Up @@ -333,7 +339,9 @@ fn fetchCompiler(allocator: *Allocator, version_arg: []const u8, set_default: Se
const master_symlink = try std.fs.path.join(allocator, &[_][]const u8{ install_dir, "master" });
defer allocator.free(master_symlink);
if (builtin.os.tag == .windows) {
@panic("TODO: what to do about master symlink on windows?");
var file = try std.fs.createFileAbsolute(master_symlink, .{});
defer file.close();
try file.writer().writeAll(version_url.version);
} else {
_ = try loggyUpdateSymlink(version_url.version, master_symlink, .{ .is_directory = true });
}
Expand Down Expand Up @@ -386,7 +394,7 @@ fn loggyDeleteTreeAbsolute(dir_absolute: []const u8) !void {
} else {
std.debug.print("rm -rf '{s}'\n", .{dir_absolute});
}
try std.fs.deleteTreeAbsolute(dir_absolute);
try fixdeletetree.deleteTreeAbsolute(dir_absolute);
}

pub fn loggyRenameAbsolute(old_path: []const u8, new_path: []const u8) !void {
Expand Down Expand Up @@ -499,7 +507,7 @@ fn cleanCompilers(allocator: *Allocator, compiler_name_opt: ?[]const u8) !void {
return error.AlreadyReported;
}
std.debug.print("deleting '{s}{c}{s}'\n", .{ install_dir_string, std.fs.path.sep, compiler_name });
try install_dir.deleteTree(compiler_name);
try fixdeletetree.deleteTree(install_dir, compiler_name);
} else {
var it = install_dir.iterate();
while (try it.next()) |entry| {
Expand All @@ -520,28 +528,60 @@ fn cleanCompilers(allocator: *Allocator, compiler_name_opt: ?[]const u8) !void {
else => return e,
}
std.debug.print("deleting '{s}{c}{s}'\n", .{ install_dir_string, std.fs.path.sep, entry.name });
try install_dir.deleteTree(entry.name);
try fixdeletetree.deleteTree(install_dir, entry.name);
}
}
}
fn readDefaultCompiler(allocator: *Allocator, buffer: *[std.fs.MAX_PATH_BYTES]u8) !?[]const u8 {
const path_link = try makeZigPathLinkString(allocator);
defer allocator.free(path_link);
if (std.fs.readLinkAbsolute(path_link, buffer)) |targetPath| {
return std.fs.path.basename(std.fs.path.dirname(std.fs.path.dirname(targetPath).?).?);
} else |e| switch (e) {
error.FileNotFound => {
return null;
},
else => return e,

if (builtin.os.tag == .windows) {
var file = std.fs.openFileAbsolute(path_link, .{}) catch |e| switch (e) {
error.FileNotFound => return null,
else => return e,
};
defer file.close();
const content = try file.readToEndAlloc(allocator, 99999);
defer allocator.free(content);
if (content.len == 0) {
std.log.err("path link file '{s}' is empty", .{path_link});
return error.AlreadyReported;
}
if (content[0] != '@') {
std.log.err("path link file '{s}' does not begin with the '@' character", .{path_link});
return error.AlreadyReported;
}
if (!std.mem.endsWith(u8, content, " %*")) {
std.log.err("path link file '{s}' does not end with ' %*'", .{path_link});
return error.AlreadyReported;
}
const target_exe = content[1..content.len - 3];
return try std.mem.dupe(allocator, u8, targetPathToVersion(target_exe));
}

const target_path = std.fs.readLinkAbsolute(path_link, buffer) catch |e| switch (e) {
error.FileNotFound => return null,
else => return e,
};
defer allocator.free(target_path);
return try std.mem.dupe(allocator, u8, targetPathToVersion(target_path));
}
fn targetPathToVersion(target_path: []const u8) []const u8 {
return std.fs.path.basename(std.fs.path.dirname(std.fs.path.dirname(target_path).?).?);
}

fn readMasterDir(allocator: *Allocator, buffer: *[std.fs.MAX_PATH_BYTES]u8, install_dir: *std.fs.Dir) !?[]const u8 {
fn readMasterDir(buffer: *[std.fs.MAX_PATH_BYTES]u8, install_dir: *std.fs.Dir) !?[]const u8 {
if (builtin.os.tag == .windows) {
var file = install_dir.openFile("master", .{}) catch |e| switch (e) {
error.FileNotFound => return null,
else => return e,
};
defer file.close();
return buffer[0..try file.readAll(buffer)];
}
return install_dir.readLink("master", buffer) catch |e| switch (e) {
error.FileNotFound => {
return null;
},
error.FileNotFound => return null,
else => return e,
};
}
Expand All @@ -556,7 +596,7 @@ fn getDefaultCompiler(allocator: *Allocator) !?[]const u8 {

fn getMasterDir(allocator: *Allocator, install_dir: *std.fs.Dir) !?[]const u8 {
var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
const slice_path = (try readMasterDir(allocator, &buffer, install_dir)) orelse return null;
const slice_path = (try readMasterDir(&buffer, install_dir)) orelse return null;
var path_to_return = try allocator.alloc(u8, slice_path.len);
std.mem.copy(u8, path_to_return, slice_path);
return path_to_return;
Expand All @@ -565,28 +605,30 @@ fn getMasterDir(allocator: *Allocator, install_dir: *std.fs.Dir) !?[]const u8 {
fn printDefaultCompiler(allocator: *Allocator) !void {
const default_compiler_opt = try getDefaultCompiler(allocator);
defer if (default_compiler_opt) |default_compiler| allocator.free(default_compiler);
const stdout = std.io.getStdOut().writer();
if (default_compiler_opt) |default_compiler| {
std.debug.print("{s}\n", .{default_compiler});
try stdout.print("{s}\n", .{default_compiler});
} else {
std.debug.print("<no-default>\n", .{});
try stdout.writeAll("<no-default>\n");
}
}

fn setDefaultCompiler(allocator: *Allocator, compiler_dir: []const u8) !void {
const path_link = try makeZigPathLinkString(allocator);
defer allocator.free(path_link);
const link_target = try std.fs.path.join(allocator, &[_][]const u8{ compiler_dir, "files", "zig" });
const link_target = try std.fs.path.join(allocator, &[_][]const u8{ compiler_dir, "files", "zig" ++ builtin.target.exeFileExt() });
defer allocator.free(link_target);
if (builtin.os.tag == .windows) {
// TODO: create zig.bat file
@panic("setDefaultCompiler not implemented in Windows");
var file = try std.fs.cwd().createFile(path_link, .{});
defer file.close();
try file.writer().print("@{s} %*", .{link_target});
} else {
_ = try loggyUpdateSymlink(link_target, path_link, .{});
}
}

fn getDefaultUrl(allocator: *Allocator, compiler_version: []const u8) ![]const u8 {
return try std.fmt.allocPrint(allocator, "https://ziglang.org/download/{s}/zig-" ++ url_platform ++ "-{s}.tar.xz", .{ compiler_version, compiler_version });
return try std.fmt.allocPrint(allocator, "https://ziglang.org/download/{s}/zig-" ++ url_platform ++ "-{0s}." ++ archive_ext, .{ compiler_version });
}

fn installCompiler(allocator: *Allocator, compiler_dir: []const u8, url: []const u8) !void {
Expand Down Expand Up @@ -622,6 +664,11 @@ fn installCompiler(allocator: *Allocator, compiler_dir: []const u8, url: []const
if (std.mem.endsWith(u8, archive_basename, ".tar.xz")) {
archive_root_dir = archive_basename[0 .. archive_basename.len - ".tar.xz".len];
_ = try run(allocator, &[_][]const u8{ "tar", "xf", archive_absolute, "-C", installing_dir });
} else if (std.mem.endsWith(u8, archive_basename, ".zip")) {
// for now we'll use "tar" which seems to exist on windows 10, but we should switch
// to a zig implementation (i.e. https://github.com/SuperAuguste/zzip)
archive_root_dir = archive_basename[0 .. archive_basename.len - ".zip".len];
_ = try run(allocator, &[_][]const u8{ "tar", "-xf", archive_absolute, "-C", installing_dir });
} else {
std.debug.print("Error: unknown archive extension '{s}'\n", .{archive_basename});
return error.UnknownArchiveExtension;
Expand Down

0 comments on commit 780690e

Please sign in to comment.