瀏覽代碼

more windows support

Jonathan Marler 3 年之前
父節點
當前提交
780690e169
共有 3 個文件被更改,包括 136 次插入28 次删除
  1. 35 0
      fixdeletetree.zig
  2. 32 6
      test.zig
  3. 69 22
      zigup.zig

+ 35 - 0
fixdeletetree.zig

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

+ 32 - 6
test.zig

@@ -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";
@@ -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();
@@ -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"});
@@ -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); }
@@ -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); }
@@ -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)});
     }
 }
 

+ 69 - 22
zigup.zig

@@ -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;
@@ -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" });
 }
 
@@ -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 });
         }
@@ -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 {
@@ -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| {
@@ -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,
     };
 }
@@ -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;
@@ -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 {
@@ -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;