From de10e3f8866dc349f3f810a91f36578954522b92 Mon Sep 17 00:00:00 2001 From: Mulling Date: Sat, 9 Nov 2024 22:02:05 -0300 Subject: [PATCH 01/19] Make so that "$ZIGUP_INSTALL_DIR" defines the install directory * First step into making this actually useful without a lot of prior setup. Try to copy what rustup does. --- zigup.zig | 134 ++++++++++++++++++------------------------------------ 1 file changed, 45 insertions(+), 89 deletions(-) diff --git a/zigup.zig b/zigup.zig index f7a60b8..b99f395 100644 --- a/zigup.zig +++ b/zigup.zig @@ -51,33 +51,23 @@ const DownloadResult = union(enum) { } }; fn download(allocator: Allocator, url: []const u8, writer: anytype) DownloadResult { - const uri = std.Uri.parse(url) catch |err| std.debug.panic( - "failed to parse url '{s}' with {s}", .{url, @errorName(err)} - ); + const uri = std.Uri.parse(url) catch |err| std.debug.panic("failed to parse url '{s}' with {s}", .{ url, @errorName(err) }); var client = std.http.Client{ .allocator = allocator }; defer client.deinit(); - client.initDefaultProxies(allocator) catch |err| return .{ .err = std.fmt.allocPrint( - allocator, "failed to query the HTTP proxy settings with {s}", .{ @errorName(err) } - ) catch |e| oom(e) }; + client.initDefaultProxies(allocator) catch |err| return .{ .err = std.fmt.allocPrint(allocator, "failed to query the HTTP proxy settings with {s}", .{@errorName(err)}) catch |e| oom(e) }; var header_buffer: [4096]u8 = undefined; var request = client.open(.GET, uri, .{ .server_header_buffer = &header_buffer, .keep_alive = false, - }) catch |err| return .{ .err = std.fmt.allocPrint( - allocator, "failed to connect to the HTTP server with {s}", .{ @errorName(err) } - ) catch |e| oom(e) }; + }) catch |err| return .{ .err = std.fmt.allocPrint(allocator, "failed to connect to the HTTP server with {s}", .{@errorName(err)}) catch |e| oom(e) }; defer request.deinit(); - request.send() catch |err| return .{ .err = std.fmt.allocPrint( - allocator, "failed to send the HTTP request with {s}", .{ @errorName(err) } - ) catch |e| oom(e) }; - request.wait() catch |err| return .{ .err = std.fmt.allocPrint( - allocator, "failed to read the HTTP response headers with {s}", .{ @errorName(err) } - ) catch |e| oom(e) }; + request.send() catch |err| return .{ .err = std.fmt.allocPrint(allocator, "failed to send the HTTP request with {s}", .{@errorName(err)}) catch |e| oom(e) }; + request.wait() catch |err| return .{ .err = std.fmt.allocPrint(allocator, "failed to read the HTTP response headers with {s}", .{@errorName(err)}) catch |e| oom(e) }; if (request.response.status != .ok) return .{ .err = std.fmt.allocPrint( allocator, @@ -89,21 +79,16 @@ fn download(allocator: Allocator, url: []const u8, writer: anytype) DownloadResu var buf: [std.mem.page_size]u8 = undefined; while (true) { - const len = request.reader().read(&buf) catch |err| return .{ .err = std.fmt.allocPrint( - allocator, "failed to read the HTTP response body with {s}'", .{ @errorName(err) } - ) catch |e| oom(e) }; + const len = request.reader().read(&buf) catch |err| return .{ .err = std.fmt.allocPrint(allocator, "failed to read the HTTP response body with {s}'", .{@errorName(err)}) catch |e| oom(e) }; if (len == 0) return .ok; - writer.writeAll(buf[0..len]) catch |err| return .{ .err = std.fmt.allocPrint( - allocator, "failed to write the HTTP response body with {s}'", .{ @errorName(err) } - ) catch |e| oom(e) }; + writer.writeAll(buf[0..len]) catch |err| return .{ .err = std.fmt.allocPrint(allocator, "failed to write the HTTP response body with {s}'", .{@errorName(err)}) catch |e| oom(e) }; } } const DownloadStringResult = union(enum) { ok: []u8, err: []u8, - }; fn downloadToString(allocator: Allocator, url: []const u8) DownloadStringResult { var response_array_list = ArrayList(u8).initCapacity(allocator, 20 * 1024) catch |e| oom(e); // 20 KB (modify if response is expected to be bigger) @@ -125,36 +110,14 @@ fn getHomeDir() ![]const u8 { }; } -fn allocInstallDirString(allocator: Allocator) ![]const u8 { - // TODO: maybe support ZIG_INSTALL_DIR environment variable? - // TODO: maybe support a file on the filesystem to configure install dir? - if (builtin.os.tag == .windows) { - const self_exe_dir = try std.fs.selfExeDirPathAlloc(allocator); - defer allocator.free(self_exe_dir); - return std.fs.path.join(allocator, &.{ self_exe_dir, "zig" }); - } - const home = try getHomeDir(); - if (!std.fs.path.isAbsolute(home)) { - std.log.err("$HOME environment variable '{s}' is not an absolute path", .{home}); - return error.BadHomeEnvironmentVariable; - } - return std.fs.path.join(allocator, &[_][]const u8{ home, "zig" }); -} -const GetInstallDirOptions = struct { - create: bool, -}; -fn getInstallDir(allocator: Allocator, options: GetInstallDirOptions) ![]const u8 { - var optional_dir_to_free_on_error: ?[]const u8 = null; - errdefer if (optional_dir_to_free_on_error) |dir| allocator.free(dir); - - const install_dir = init: { - if (global_optional_install_dir) |dir| break :init dir; - optional_dir_to_free_on_error = try allocInstallDirString(allocator); - break :init optional_dir_to_free_on_error.?; - }; +fn getInstallDir(create_missing: bool) ![]const u8 { + const install_dir = std.posix.getenv("ZIGUP_INSTALL_DIR") orelse @panic("$ZIGUP_INSTALL_DIR is not defined."); + + // TODO: Need we only assert this on debug builds. std.debug.assert(std.fs.path.isAbsolute(install_dir)); + loginfo("install directory '{s}'", .{install_dir}); - if (options.create) { + if (create_missing) { loggyMakeDirAbsolute(install_dir) catch |e| switch (e) { error.PathAlreadyExists => {}, else => return e, @@ -218,12 +181,12 @@ fn getCmdOpt(args: [][]const u8, i: *usize) ![]const u8 { } pub fn main() !u8 { - return main2() catch |e| switch (e) { + return zigup() catch |e| switch (e) { error.AlreadyReported => return 1, else => return e, }; } -pub fn main2() !u8 { +pub fn zigup() !u8 { if (builtin.os.tag == .windows) { _ = try std.os.windows.WSAStartup(2, 2); } @@ -304,7 +267,7 @@ pub fn main2() !u8 { std.log.err("'keep' command requires 1 argument but got {d}", .{args.len - 1}); return 1; } - try keepCompiler(allocator, args[1]); + try keepCompiler(args[1]); return 0; } if (std.mem.eql(u8, "list", args[0])) { @@ -312,7 +275,7 @@ pub fn main2() !u8 { std.log.err("'list' command requires 0 arguments but got {d}", .{args.len - 1}); return 1; } - try listCompilers(allocator); + try listCompilers(); return 0; } if (std.mem.eql(u8, "default", args[0])) { @@ -322,8 +285,8 @@ pub fn main2() !u8 { } if (args.len == 2) { const version_string = args[1]; - const install_dir_string = try getInstallDir(allocator, .{ .create = true }); - defer allocator.free(install_dir_string); + const install_dir_string = try getInstallDir(true); + const resolved_version_string = init_resolved: { if (!std.mem.eql(u8, version_string, "master")) break :init_resolved version_string; @@ -344,7 +307,7 @@ pub fn main2() !u8 { }; const compiler_dir = try std.fs.path.join(allocator, &[_][]const u8{ install_dir_string, resolved_version_string }); defer allocator.free(compiler_dir); - try setDefaultCompiler(allocator, compiler_dir, .verify_existence); + try setDefaultCompiler(allocator, compiler_dir, true); return 0; } std.log.err("'default' command requires 1 or 2 arguments but got {d}", .{args.len - 1}); @@ -370,8 +333,7 @@ pub fn runCompiler(allocator: Allocator, args: []const []const u8) !u8 { return 1; } const version_string = args[0]; - const install_dir_string = try getInstallDir(allocator, .{ .create = true }); - defer allocator.free(install_dir_string); + const install_dir_string = try getInstallDir(true); const compiler_dir = try std.fs.path.join(allocator, &[_][]const u8{ install_dir_string, version_string }); defer allocator.free(compiler_dir); @@ -399,8 +361,7 @@ pub fn runCompiler(allocator: Allocator, args: []const []const u8) !u8 { const SetDefault = enum { set_default, leave_default }; fn fetchCompiler(allocator: Allocator, version_arg: []const u8, set_default: SetDefault) !void { - const install_dir = try getInstallDir(allocator, .{ .create = true }); - defer allocator.free(install_dir); + const install_dir = try getInstallDir(true); var optional_download_index: ?DownloadIndex = null; // This is causing an LLVM error @@ -438,7 +399,7 @@ fn fetchCompiler(allocator: Allocator, version_arg: []const u8, set_default: Set } } if (set_default == .set_default) { - try setDefaultCompiler(allocator, compiler_dir, .existence_verified); + try setDefaultCompiler(allocator, compiler_dir, false); } } @@ -457,7 +418,7 @@ fn fetchDownloadIndex(allocator: Allocator) !DownloadIndex { const text = switch (downloadToString(allocator, download_index_url)) { .ok => |text| text, .err => |err| { - std.log.err("download '{s}' failed: {s}", .{download_index_url, err}); + std.log.err("download '{s}' failed: {s}", .{ download_index_url, err }); return error.AlreadyReported; }, }; @@ -467,6 +428,8 @@ fn fetchDownloadIndex(allocator: Allocator) !DownloadIndex { return DownloadIndex{ .text = text, .json = json }; } +// XXX: What? +// TODO: Get rid of these fn loggyMakeDirAbsolute(dir_absolute: []const u8) !void { if (builtin.os.tag == .windows) { loginfo("mkdir \"{s}\"", .{dir_absolute}); @@ -513,7 +476,7 @@ pub fn loggyUpdateSymlink(target_path: []const u8, sym_link_path: []const u8, fl error.NotLink => { std.debug.print( "unable to update/overwrite the 'zig' PATH symlink, the file '{s}' already exists and is not a symlink\n", - .{ sym_link_path}, + .{sym_link_path}, ); std.process.exit(1); }, @@ -542,9 +505,8 @@ fn existsAbsolute(absolutePath: []const u8) !bool { return true; } -fn listCompilers(allocator: Allocator) !void { - const install_dir_string = try getInstallDir(allocator, .{ .create = false }); - defer allocator.free(install_dir_string); +fn listCompilers() !void { + const install_dir_string = try getInstallDir(false); var install_dir = std.fs.openDirAbsolute(install_dir_string, .{ .iterate = true }) catch |e| switch (e) { error.FileNotFound => return, @@ -565,9 +527,8 @@ fn listCompilers(allocator: Allocator) !void { } } -fn keepCompiler(allocator: Allocator, compiler_version: []const u8) !void { - const install_dir_string = try getInstallDir(allocator, .{ .create = true }); - defer allocator.free(install_dir_string); +fn keepCompiler(compiler_version: []const u8) !void { + const install_dir_string = try getInstallDir(true); var install_dir = try std.fs.openDirAbsolute(install_dir_string, .{ .iterate = true }); defer install_dir.close(); @@ -585,8 +546,8 @@ fn keepCompiler(allocator: Allocator, compiler_version: []const u8) !void { } fn cleanCompilers(allocator: Allocator, compiler_name_opt: ?[]const u8) !void { - const install_dir_string = try getInstallDir(allocator, .{ .create = true }); - defer allocator.free(install_dir_string); + const install_dir_string = try getInstallDir(true); + // getting the current compiler const default_comp_opt = try getDefaultCompiler(allocator); defer if (default_comp_opt) |default_compiler| allocator.free(default_compiler); @@ -704,21 +665,16 @@ fn printDefaultCompiler(allocator: Allocator) !void { } } -const ExistVerify = enum { existence_verified, verify_existence }; - -fn setDefaultCompiler(allocator: Allocator, compiler_dir: []const u8, exist_verify: ExistVerify) !void { - switch (exist_verify) { - .existence_verified => {}, - .verify_existence => { - var dir = std.fs.openDirAbsolute(compiler_dir, .{}) catch |err| switch (err) { - error.FileNotFound => { - std.log.err("compiler '{s}' is not installed", .{std.fs.path.basename(compiler_dir)}); - return error.AlreadyReported; - }, - else => |e| return e, - }; - dir.close(); - }, +fn setDefaultCompiler(allocator: Allocator, compiler_dir: []const u8, verify_exists: bool) !void { + if (verify_exists) { + var dir = std.fs.openDirAbsolute(compiler_dir, .{}) catch |err| switch (err) { + error.FileNotFound => { + std.log.err("compiler '{s}' is not installed", .{std.fs.path.basename(compiler_dir)}); + return error.AlreadyReported; + }, + else => |e| return e, + }; + dir.close(); } const path_link = try makeZigPathLinkString(allocator); @@ -932,7 +888,7 @@ fn createExeLink(link_target: []const u8, path_link: []const u8) !void { error.IsDir => { std.debug.print( "unable to create the exe link, the path '{s}' is a directory\n", - .{ path_link}, + .{path_link}, ); std.process.exit(1); }, @@ -985,7 +941,7 @@ fn installCompiler(allocator: Allocator, compiler_dir: []const u8, url: []const }) { .ok => {}, .err => |err| { - std.log.err("download '{s}' failed: {s}", .{url, err}); + std.log.err("download '{s}' failed: {s}", .{ url, err }); // this removes the installing dir if the http request fails so we dont have random directories try loggyDeleteTreeAbsolute(installing_dir); return error.AlreadyReported; From 034521e270cb4893c5e420a6f28e9130fb684bae Mon Sep 17 00:00:00 2001 From: Mulling Date: Sat, 9 Nov 2024 22:26:55 -0300 Subject: [PATCH 02/19] Don't point to zig executable, point to the zig directory * This make so that it's possible to configure zigup through an .env file which just sets `$ZIGUP_DIR` and `$ZIGUP_INSTALL_DIR`. The current active compiler then is just `$ZIGUP_DIR/current`, which is in the `$PATH`. --- zigup.zig | 53 +++++++++++++++++++++++++---------------------------- 1 file changed, 25 insertions(+), 28 deletions(-) diff --git a/zigup.zig b/zigup.zig index b99f395..2b3d974 100644 --- a/zigup.zig +++ b/zigup.zig @@ -103,13 +103,6 @@ fn ignoreHttpCallback(request: []const u8) void { _ = request; } -fn getHomeDir() ![]const u8 { - return std.posix.getenv("HOME") orelse { - std.log.err("cannot find install directory, $HOME environment variable is not set", .{}); - return error.MissingHomeEnvironmentVariable; - }; -} - fn getInstallDir(create_missing: bool) ![]const u8 { const install_dir = std.posix.getenv("ZIGUP_INSTALL_DIR") orelse @panic("$ZIGUP_INSTALL_DIR is not defined."); @@ -677,18 +670,22 @@ fn setDefaultCompiler(allocator: Allocator, compiler_dir: []const u8, verify_exi dir.close(); } - const path_link = try makeZigPathLinkString(allocator); - defer allocator.free(path_link); + const link_path = std.posix.getenv("ZIGUP_DIR") orelse @panic("$ZIGUP_DIR not defined"); + + loginfo("link path = {s}", .{link_path}); + + const target = try std.fs.path.join(allocator, &[_][]const u8{ compiler_dir, "files" }); + defer allocator.free(target); - const link_target = try std.fs.path.join(allocator, &[_][]const u8{ compiler_dir, "files", comptime "zig" ++ builtin.target.exeFileExt() }); - defer allocator.free(link_target); if (builtin.os.tag == .windows) { - try createExeLink(link_target, path_link); + try createExeLink(target, link_path); } else { - _ = try loggyUpdateSymlink(link_target, path_link, .{}); + _ = try loggyUpdateSymlink(target, link_path, .{}); } - try verifyPathLink(allocator, path_link); + // TODO: Keep or remove this?! + // FIXME: This is broken + //try verifyPathLink(allocator, link_path); } /// Verify that path_link will work. It verifies that `path_link` is @@ -742,7 +739,7 @@ fn verifyPathLink(allocator: Allocator, path_link: []const u8) !void { { const exe = try std.fs.path.join(allocator, &.{ path, "zig" }); defer allocator.free(exe); - try enforceNoZig(path_link, exe); + // try enforceNoZig(path_link, exe); } var ext_it = std.mem.tokenizeScalar(u8, pathext_env, ';'); @@ -754,7 +751,7 @@ fn verifyPathLink(allocator: Allocator, path_link: []const u8) !void { const exe = try std.fs.path.join(allocator, &.{ path, basename }); defer allocator.free(exe); - try enforceNoZig(path_link, exe); + // try enforceNoZig(path_link, exe); } } } else { @@ -770,7 +767,7 @@ fn verifyPathLink(allocator: Allocator, path_link: []const u8) !void { } const exe = try std.fs.path.join(allocator, &.{ path, "zig" }); defer allocator.free(exe); - try enforceNoZig(path_link, exe); + // try enforceNoZig(path_link, exe); } } @@ -788,17 +785,17 @@ fn compareDir(dir_id: FileId, other_dir: []const u8) !enum { missing, access_den return if (dir_id.eql(try FileId.initFromDir(dir, other_dir))) .match else .mismatch; } -fn enforceNoZig(path_link: []const u8, exe: []const u8) !void { - var file = std.fs.cwd().openFile(exe, .{}) catch |err| switch (err) { - error.FileNotFound, error.IsDir => return, - error.AccessDenied => return, // if there is a Zig it must not be accessible - else => |e| return e, - }; - defer file.close(); - - // todo: on posix systems ignore the file if it is not executable - std.log.err("zig compiler '{s}' is higher priority in PATH than the path-link '{s}'", .{ exe, path_link }); -} +// fn enforceNoZig(path_link: []const u8, exe: []const u8) !void { +// var file = std.fs.cwd().openFile(exe, .{}) catch |err| switch (err) { +// error.FileNotFound, error.IsDir => return, +// error.AccessDenied => return, // if there is a Zig it must not be accessible +// else => |e| return e, +// }; +// defer file.close(); +// +// // todo: on posix systems ignore the file if it is not executable +// std.log.err("zig compiler '{s}' is higher priority in PATH than the path-link '{s}'", .{ exe, path_link }); +// } const FileId = struct { dev: if (builtin.os.tag == .windows) u32 else blk: { From 8761368e2378092800e53f54fdc96e9bde74654a Mon Sep 17 00:00:00 2001 From: Mulling Date: Sat, 9 Nov 2024 22:37:27 -0300 Subject: [PATCH 03/19] Update README --- README.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 41617b7..5ddb2d7 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # zigup -Download and manage zig compilers. +Fork of [https://marler8997.github.io/zigup](zigup) which aims to make it easier to download and manage zig version. This fork is based on how rustup works. # How to Install @@ -10,6 +10,17 @@ Otherwise, you can manually find and download/extract the applicable archive fro # Usage +```sh +$ zig version +0.14.0-dev.2198+e5f5229fd +$ zigup fetch 0.13.0 +... +$ zigup default 0.13.0 +... +$ zig version +0.13.0 +``` + ``` # fetch a compiler and set it as the default zigup From 18df22862b96188102860385b01b25d31763950f Mon Sep 17 00:00:00 2001 From: Mulling Date: Sat, 9 Nov 2024 22:38:41 -0300 Subject: [PATCH 04/19] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5ddb2d7..840f169 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # zigup -Fork of [https://marler8997.github.io/zigup](zigup) which aims to make it easier to download and manage zig version. This fork is based on how rustup works. +Fork of [zigup](https://marler8997.github.io/zigup) which aims to make it easier to download and manage zig versions. This fork is based on how [rustup](https://rustup.rs/) works. # How to Install From ba51a7b17136cf7b3c584136eeca61329116a0c2 Mon Sep 17 00:00:00 2001 From: Mulling Date: Tue, 12 Nov 2024 23:35:33 -0300 Subject: [PATCH 05/19] WIP on autoconfig --- zigup.zig | 114 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 113 insertions(+), 1 deletion(-) diff --git a/zigup.zig b/zigup.zig index 2b3d974..1797f12 100644 --- a/zigup.zig +++ b/zigup.zig @@ -173,12 +173,115 @@ fn getCmdOpt(args: [][]const u8, i: *usize) ![]const u8 { return args[i.*]; } +pub fn promptUser(msg: []const u8, out: []u8) ![]u8 { + try std.io.getStdOut().writeAll(msg); + + return try std.io.getStdIn().reader().readUntilDelimiter(out, '\n'); +} + +pub fn promptUserAlloc(allocator: Allocator, msg: []const u8) ![]u8 { + try std.io.getStdOut().writeAll(msg); + + return try std.io.getStdIn().reader().readUntilDelimiterAlloc(allocator, '\n', 4096); +} + +pub fn yesOrNoP(comptime msg: []const u8) !bool { + const out_msg = msg ++ " [Y/n]"; + + var buf: [2]u8 = undefined; + + prompt: while (true) { + const ret = promptUser(out_msg, &buf) catch |err| if (err == error.StreamTooLong) { + std.debug.print("stream too long", .{}); + continue :prompt; + } else return err; + + // TODO: Make this configurable + // User just pressed ret, assume true; + if (ret.len == 0) { + return true; + } + + if (ret[0] == 'y' or ret[0] == 'Y') { + return true; + } else if (buf[0] == 'n' or buf[0] == 'N') { + return false; + } + } +} + +const ZigupConfig = struct { + path: []const u8, + install_path: []const u8, +}; + +pub fn configure(allocator: Allocator) !void { + if (!try yesOrNoP("Could not get config from env, configure zigup?")) { + return error.ConfigAborted; + } + + const home = std.posix.getenv("HOME") orelse @panic("$HOME is not in env"); + const default_path = try std.fmt.allocPrint(allocator, "{s}/.zigup", .{home}); + + loginfo("default zigup_path = '{s}'", .{default_path}); + + const path_prompt = try std.fmt.allocPrint(allocator, "Install path for zigup (default: {s}): ", .{default_path}); + + var path = try promptUserAlloc(allocator, path_prompt); + + if (path.len == 0) path = default_path; + + const install_path = try std.fmt.allocPrint(allocator, "{s}/cache", .{path}); + + // TODO: Support other shells + if (std.posix.getenv("SHELL")) |shell| { + if (std.mem.containsAtLeast(u8, shell, 1, "bash")) { + try outputEnvForBash(allocator, .{ .path = path, .install_path = install_path }); + } else { + @panic("Could not guess the current shell"); + } + } else { + @panic("Could not guess the current shell"); + } +} + +pub fn outputEnvForBash(allocator: Allocator, config: ZigupConfig) !void { + const env = try std.fmt.allocPrint( + allocator, + "export ZIGUP_DIR=\"{s}\"\nexport ZIGUP_INSTALL_DIR=\"{s}\"\nexport PATH=\"$PATH:{s}/default\"", + .{ config.path, config.install_path, config.path }, + ); + loginfo("bash env: \n{s}", .{env}); + + std.fs.makeDirAbsolute(config.path) catch |err| if (err != error.PathAlreadyExists) return err; + + const env_file = try std.fmt.allocPrint(allocator, "{s}/env", .{config.path}); + + const fd = try std.fs.createFileAbsolute(env_file, .{}); + errdefer fd.close(); + + try fd.writeAll(env); + + const bash_config = try std.fmt.allocPrint(allocator, "[ -f \"{s}\" ] && source \"{s}\"", .{ env_file, env_file }); + + try std.io.getStdOut().writeAll("Add this to your .bashrc:\n"); + try std.io.getStdOut().writeAll(bash_config); +} + +pub fn readConfigFromEnv() ?ZigupConfig { + return .{ + .path = std.posix.getenv("ZIGUP_DIR") orelse return null, + .install_path = std.posix.getenv("ZIGUP_INSTALL_DIR") orelse return null, + }; +} + pub fn main() !u8 { return zigup() catch |e| switch (e) { error.AlreadyReported => return 1, else => return e, }; } + pub fn zigup() !u8 { if (builtin.os.tag == .windows) { _ = try std.os.windows.WSAStartup(2, 2); @@ -191,6 +294,13 @@ pub fn zigup() !u8 { // no need to free, os will do it //defer std.process.argsFree(allocator, argsArray); + const config = readConfigFromEnv() orelse return { + try configure(allocator); + return 0; + }; + + _ = config; + var args = if (args_array.len == 0) args_array else args_array[1..]; // parse common options // @@ -670,7 +780,9 @@ fn setDefaultCompiler(allocator: Allocator, compiler_dir: []const u8, verify_exi dir.close(); } - const link_path = std.posix.getenv("ZIGUP_DIR") orelse @panic("$ZIGUP_DIR not defined"); + const zigup_path = std.posix.getenv("ZIGUP_DIR") orelse @panic("$ZIGUP_DIR not defined"); + + const link_path = try std.fmt.allocPrint(allocator, "{s}/default", .{zigup_path}); loginfo("link path = {s}", .{link_path}); From a0d2fd2cfcbd816ef7384bdb1d4f03133aede65e Mon Sep 17 00:00:00 2001 From: Mulling Date: Wed, 13 Nov 2024 23:15:28 -0300 Subject: [PATCH 06/19] Prepend to PATH to override system-wide zig install * Test if the current terminal is a tty so we can ask the user things. --- zigup.zig | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/zigup.zig b/zigup.zig index 1797f12..343ff18 100644 --- a/zigup.zig +++ b/zigup.zig @@ -233,10 +233,13 @@ pub fn configure(allocator: Allocator) !void { const install_path = try std.fmt.allocPrint(allocator, "{s}/cache", .{path}); + if (!std.posix.isatty(std.io.getStdOut().handle)) + return error.NonInteractiveTerminal; + // TODO: Support other shells if (std.posix.getenv("SHELL")) |shell| { if (std.mem.containsAtLeast(u8, shell, 1, "bash")) { - try outputEnvForBash(allocator, .{ .path = path, .install_path = install_path }); + try outputShellEnv(allocator, .{ .path = path, .install_path = install_path }); } else { @panic("Could not guess the current shell"); } @@ -245,11 +248,33 @@ pub fn configure(allocator: Allocator) !void { } } -pub fn outputEnvForBash(allocator: Allocator, config: ZigupConfig) !void { +const shellEnvFmt = + \\#!/bin/sh + \\ + \\# Path to env + \\export ZIGUP_DIR="{s}" + \\# Path to cache + \\export ZIGUP_INSTALL_DIR="$ZIGUP_DIR/cache" + \\ + \\case ":$PATH:" in + \\ *:"$ZIGUP_DIR/default":*) + \\ ;; + \\ *) + \\ # Prepend to override system-hide install + \\ export PATH="$ZIGUP_DIR/default:$PATH" + \\ ;; + \\esac +; + +const sourceEnvFmt = + \\[ -f \"{s}\" ] && source \"{s}\" +; + +pub fn outputShellEnv(allocator: Allocator, config: ZigupConfig) !void { const env = try std.fmt.allocPrint( allocator, - "export ZIGUP_DIR=\"{s}\"\nexport ZIGUP_INSTALL_DIR=\"{s}\"\nexport PATH=\"$PATH:{s}/default\"", - .{ config.path, config.install_path, config.path }, + shellEnvFmt, + .{config.path}, ); loginfo("bash env: \n{s}", .{env}); @@ -262,7 +287,7 @@ pub fn outputEnvForBash(allocator: Allocator, config: ZigupConfig) !void { try fd.writeAll(env); - const bash_config = try std.fmt.allocPrint(allocator, "[ -f \"{s}\" ] && source \"{s}\"", .{ env_file, env_file }); + const bash_config = try std.fmt.allocPrint(allocator, sourceEnvFmt, .{ env_file, env_file }); try std.io.getStdOut().writeAll("Add this to your .bashrc:\n"); try std.io.getStdOut().writeAll(bash_config); From 3dc43a325892f8fce6296d3eaf6227e58da7e51f Mon Sep 17 00:00:00 2001 From: Mulling Date: Thu, 14 Nov 2024 12:06:43 -0300 Subject: [PATCH 07/19] Fix fetch and default command, add undefine * `undefine` will undefine the current default compiler, this is useful for using a system-wide installation of zig. --- zigup.zig | 69 +++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 57 insertions(+), 12 deletions(-) diff --git a/zigup.zig b/zigup.zig index 343ff18..f407ede 100644 --- a/zigup.zig +++ b/zigup.zig @@ -30,12 +30,36 @@ var global_optional_install_dir: ?[]const u8 = null; var global_optional_path_link: ?[]const u8 = null; var global_enable_log = true; + +// TODO: Fix this fn loginfo(comptime fmt: []const u8, args: anytype) void { if (global_enable_log) { std.debug.print(fmt ++ "\n", args); } } +inline fn fix_format_string(comptime fmt: []const u8) []const u8 { + if (builtin.os.tag == .windows) { + // Not sure what is going on here, or why they are doing this on + // windows, but this follows the behaviour of `loginfo` on windows. + // ¯\_(ツ)_/¯ + var fixed_fmt = std.mem.zeroes([fmt.len]u8); + std.mem.replace(u8, fmt, '\'', '\"', &fixed_fmt); + + return fixed_fmt; + } else { + return fmt; + } +} + +inline fn loge(comptime fmt: []const u8, args: anytype) void { + std.log.err(fix_format_string(fmt), args); +} + +inline fn logw(comptime fmt: []const u8, args: anytype) void { + std.log.warn(fix_format_string(fmt), args); +} + pub fn oom(e: error{OutOfMemory}) noreturn { @panic(@errorName(e)); } @@ -248,6 +272,10 @@ pub fn configure(allocator: Allocator) !void { } } +inline fn isBash() bool { + return std.mem.endsWith(u8, std.posix.getenv("SHELL") orelse return false, "bash"); +} + const shellEnvFmt = \\#!/bin/sh \\ @@ -324,8 +352,6 @@ pub fn zigup() !u8 { return 0; }; - _ = config; - var args = if (args_array.len == 0) args_array else args_array[1..]; // parse common options // @@ -371,12 +397,24 @@ pub fn zigup() !u8 { try std.io.getStdOut().writeAll(download_index.text); return 0; } + if (std.mem.eql(u8, "undefine", args[0])) { + if (args.len != 1) { + loge("'undefine' does not take any arguments", .{}); + return 1; + } + + try unsetDefaultCompiler(allocator, &config); + + if (isBash()) logw("Use `hash -r` to reset the command location cache.", .{}); + return 0; + } if (std.mem.eql(u8, "fetch", args[0])) { if (args.len != 2) { std.log.err("'fetch' command requires 1 argument but got {d}", .{args.len - 1}); return 1; } - try fetchCompiler(allocator, args[1], .leave_default); + + try fetchCompiler(allocator, args[1], &config, .leave_default); return 0; } if (std.mem.eql(u8, "clean", args[0])) { @@ -435,14 +473,14 @@ pub fn zigup() !u8 { }; const compiler_dir = try std.fs.path.join(allocator, &[_][]const u8{ install_dir_string, resolved_version_string }); defer allocator.free(compiler_dir); - try setDefaultCompiler(allocator, compiler_dir, true); + try setDefaultCompiler(allocator, compiler_dir, &config, true); return 0; } std.log.err("'default' command requires 1 or 2 arguments but got {d}", .{args.len - 1}); return 1; } if (args.len == 1) { - try fetchCompiler(allocator, args[0], .set_default); + try fetchCompiler(allocator, args[0], &config, .set_default); return 0; } const command = args[0]; @@ -488,8 +526,8 @@ pub fn runCompiler(allocator: Allocator, args: []const []const u8) !u8 { const SetDefault = enum { set_default, leave_default }; -fn fetchCompiler(allocator: Allocator, version_arg: []const u8, set_default: SetDefault) !void { - const install_dir = try getInstallDir(true); +fn fetchCompiler(allocator: Allocator, version_arg: []const u8, config: *const ZigupConfig, set_default: SetDefault) !void { + const install_dir = config.install_path; var optional_download_index: ?DownloadIndex = null; // This is causing an LLVM error @@ -512,9 +550,12 @@ fn fetchCompiler(allocator: Allocator, version_arg: []const u8, set_default: Set const master_linux_tarball = master_linux.object.get("tarball").?.string; break :blk VersionUrl{ .version = compiler_version, .url = master_linux_tarball }; }; + const compiler_dir = try std.fs.path.join(allocator, &[_][]const u8{ install_dir, version_url.version }); defer allocator.free(compiler_dir); + try installCompiler(allocator, compiler_dir, version_url.url); + if (is_master) { const master_symlink = try std.fs.path.join(allocator, &[_][]const u8{ install_dir, "master" }); defer allocator.free(master_symlink); @@ -527,7 +568,7 @@ fn fetchCompiler(allocator: Allocator, version_arg: []const u8, set_default: Set } } if (set_default == .set_default) { - try setDefaultCompiler(allocator, compiler_dir, false); + try setDefaultCompiler(allocator, compiler_dir, config, false); } } @@ -793,7 +834,13 @@ fn printDefaultCompiler(allocator: Allocator) !void { } } -fn setDefaultCompiler(allocator: Allocator, compiler_dir: []const u8, verify_exists: bool) !void { +fn unsetDefaultCompiler(allocator: Allocator, config: *const ZigupConfig) !void { + const link_path = try std.fmt.allocPrint(allocator, "{s}/default", .{config.path}); + + try std.posix.unlink(link_path); +} + +fn setDefaultCompiler(allocator: Allocator, compiler_dir: []const u8, config: *const ZigupConfig, verify_exists: bool) !void { if (verify_exists) { var dir = std.fs.openDirAbsolute(compiler_dir, .{}) catch |err| switch (err) { error.FileNotFound => { @@ -805,9 +852,7 @@ fn setDefaultCompiler(allocator: Allocator, compiler_dir: []const u8, verify_exi dir.close(); } - const zigup_path = std.posix.getenv("ZIGUP_DIR") orelse @panic("$ZIGUP_DIR not defined"); - - const link_path = try std.fmt.allocPrint(allocator, "{s}/default", .{zigup_path}); + const link_path = try std.fmt.allocPrint(allocator, "{s}/default", .{config.path}); loginfo("link path = {s}", .{link_path}); From 4bfacc961ce387f6f13c7a3ced162cf16507ad2e Mon Sep 17 00:00:00 2001 From: Mulling Date: Thu, 14 Nov 2024 12:25:17 -0300 Subject: [PATCH 08/19] Update `help` --- zigup.zig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/zigup.zig b/zigup.zig index f407ede..4f89df4 100644 --- a/zigup.zig +++ b/zigup.zig @@ -169,9 +169,10 @@ fn help() void { \\ zigup VERSION download and set VERSION compiler as default \\ zigup fetch VERSION download VERSION compiler \\ zigup default [VERSION] get or set the default compiler + \\ zigup undefine unset the default compiler \\ zigup list list installed compiler versions \\ zigup clean [VERSION] deletes the given compiler version, otherwise, cleans all compilers - \\ that aren't the default, master, or marked to keep. + \\ that aren't the default, master, or marked to keep \\ zigup keep VERSION mark a compiler to be kept during clean \\ zigup run VERSION ARGS... run the given VERSION of the compiler with the given ARGS... \\ From 0c0881beff25d940d92e85242d4e8e5f73878a5f Mon Sep 17 00:00:00 2001 From: Mulling Date: Thu, 14 Nov 2024 19:57:24 -0300 Subject: [PATCH 09/19] Refactor logs --- zigup.zig | 191 +++++++++++++++++++++++++++++------------------------- 1 file changed, 101 insertions(+), 90 deletions(-) diff --git a/zigup.zig b/zigup.zig index 4f89df4..474c3e1 100644 --- a/zigup.zig +++ b/zigup.zig @@ -31,13 +31,6 @@ var global_optional_path_link: ?[]const u8 = null; var global_enable_log = true; -// TODO: Fix this -fn loginfo(comptime fmt: []const u8, args: anytype) void { - if (global_enable_log) { - std.debug.print(fmt ++ "\n", args); - } -} - inline fn fix_format_string(comptime fmt: []const u8) []const u8 { if (builtin.os.tag == .windows) { // Not sure what is going on here, or why they are doing this on @@ -52,6 +45,13 @@ inline fn fix_format_string(comptime fmt: []const u8) []const u8 { } } +inline fn logi(comptime fmt: []const u8, args: anytype) void { + // TODO: `--verbose` option!? + if (builtin.mode == std.builtin.OptimizeMode.Debug) { + std.log.info(fix_format_string(fmt), args); + } +} + inline fn loge(comptime fmt: []const u8, args: anytype) void { std.log.err(fix_format_string(fmt), args); } @@ -127,20 +127,18 @@ fn ignoreHttpCallback(request: []const u8) void { _ = request; } -fn getInstallDir(create_missing: bool) ![]const u8 { - const install_dir = std.posix.getenv("ZIGUP_INSTALL_DIR") orelse @panic("$ZIGUP_INSTALL_DIR is not defined."); +fn makeDirIfMissing(path: []const u8) !void { + if (builtin.mode == std.builtin.OptimizeMode.Debug) + std.debug.assert(std.fs.path.isAbsolute(path)); - // TODO: Need we only assert this on debug builds. - std.debug.assert(std.fs.path.isAbsolute(install_dir)); + logi("creating directory '{s}'", .{path}); - loginfo("install directory '{s}'", .{install_dir}); - if (create_missing) { - loggyMakeDirAbsolute(install_dir) catch |e| switch (e) { - error.PathAlreadyExists => {}, - else => return e, - }; - } - return install_dir; + std.fs.makeDirAbsolute(path) catch |e| switch (e) { + error.PathAlreadyExists => { + logi("directory '{s}' already exists!", .{path}); + }, + else => return e, + }; } fn makeZigPathLinkString(allocator: Allocator) ![]const u8 { @@ -236,8 +234,38 @@ pub fn yesOrNoP(comptime msg: []const u8) !bool { } const ZigupConfig = struct { + allocator: Allocator, + path: []const u8, - install_path: []const u8, + install_path: []u8, + default_path: []u8, + + const Self = @This(); + + pub fn init(allocator: Allocator, path: []const u8) !ZigupConfig { + const install_path = try std.fs.path.join(allocator, &[_][]const u8{ path, "cache" }); + const default_path = try std.fs.path.join(allocator, &[_][]const u8{ path, "default" }); + + return .{ + .allocator = allocator, + .path = path, + .install_path = install_path, + .default_path = default_path, + }; + } + + pub fn set_install_path(self: *Self, install_path: []const u8) !void { + self.allocator.free(self.install_path); + + self.install_path = try self.allocator.alloc(u8, install_path.len); + + @memcpy(self.install_path, install_path); + } + + pub fn deinit(self: *Self) void { + self.allocator.free(self.default_path); + self.allocator.free(self.install_path); + } }; pub fn configure(allocator: Allocator) !void { @@ -248,7 +276,7 @@ pub fn configure(allocator: Allocator) !void { const home = std.posix.getenv("HOME") orelse @panic("$HOME is not in env"); const default_path = try std.fmt.allocPrint(allocator, "{s}/.zigup", .{home}); - loginfo("default zigup_path = '{s}'", .{default_path}); + logi("default zigup_path = '{s}'", .{default_path}); const path_prompt = try std.fmt.allocPrint(allocator, "Install path for zigup (default: {s}): ", .{default_path}); @@ -256,18 +284,16 @@ pub fn configure(allocator: Allocator) !void { if (path.len == 0) path = default_path; - const install_path = try std.fmt.allocPrint(allocator, "{s}/cache", .{path}); + // TODO: Prompt if the users wants to change this + // const install_path = try std.fmt.allocPrint(allocator, "{s}/cache", .{path}); if (!std.posix.isatty(std.io.getStdOut().handle)) return error.NonInteractiveTerminal; - // TODO: Support other shells - if (std.posix.getenv("SHELL")) |shell| { - if (std.mem.containsAtLeast(u8, shell, 1, "bash")) { - try outputShellEnv(allocator, .{ .path = path, .install_path = install_path }); - } else { - @panic("Could not guess the current shell"); - } + // TODO: add zsh + // TODO: add fish + if (isBash()) { + try outputPosixShellEnv(allocator, path); } else { @panic("Could not guess the current shell"); } @@ -299,17 +325,17 @@ const sourceEnvFmt = \\[ -f \"{s}\" ] && source \"{s}\" ; -pub fn outputShellEnv(allocator: Allocator, config: ZigupConfig) !void { +pub fn outputPosixShellEnv(allocator: Allocator, path: []const u8) !void { const env = try std.fmt.allocPrint( allocator, shellEnvFmt, - .{config.path}, + .{path}, ); - loginfo("bash env: \n{s}", .{env}); + logi("bash env: \n{s}", .{env}); - std.fs.makeDirAbsolute(config.path) catch |err| if (err != error.PathAlreadyExists) return err; + try makeDirIfMissing(path); - const env_file = try std.fmt.allocPrint(allocator, "{s}/env", .{config.path}); + const env_file = try std.fs.path.join(allocator, &[_][]const u8{ path, "env" }); const fd = try std.fs.createFileAbsolute(env_file, .{}); errdefer fd.close(); @@ -322,11 +348,12 @@ pub fn outputShellEnv(allocator: Allocator, config: ZigupConfig) !void { try std.io.getStdOut().writeAll(bash_config); } -pub fn readConfigFromEnv() ?ZigupConfig { - return .{ - .path = std.posix.getenv("ZIGUP_DIR") orelse return null, - .install_path = std.posix.getenv("ZIGUP_INSTALL_DIR") orelse return null, - }; +pub fn readConfigFromEnv(allocator: Allocator) !?ZigupConfig { + // TODO: Allow override of install_path + return try ZigupConfig.init( + allocator, + std.posix.getenv("ZIGUP_DIR") orelse return null, + ); } pub fn main() !u8 { @@ -348,11 +375,14 @@ pub fn zigup() !u8 { // no need to free, os will do it //defer std.process.argsFree(allocator, argsArray); - const config = readConfigFromEnv() orelse return { + const config = try readConfigFromEnv(allocator) orelse { try configure(allocator); return 0; }; + try makeDirIfMissing(config.path); + try makeDirIfMissing(config.install_path); + var args = if (args_array.len == 0) args_array else args_array[1..]; // parse common options // @@ -376,7 +406,7 @@ pub fn zigup() !u8 { return 0; } else { if (newlen == 0 and std.mem.eql(u8, "run", arg)) { - return try runCompiler(allocator, args[i + 1 ..]); + return try runCompiler(allocator, &config, args[i + 1 ..]); } args[newlen] = args[i]; newlen += 1; @@ -420,9 +450,9 @@ pub fn zigup() !u8 { } if (std.mem.eql(u8, "clean", args[0])) { if (args.len == 1) { - try cleanCompilers(allocator, null); + try cleanCompilers(allocator, &config, null); } else if (args.len == 2) { - try cleanCompilers(allocator, args[1]); + try cleanCompilers(allocator, &config, args[1]); } else { std.log.err("'clean' command requires 0 or 1 arguments but got {d}", .{args.len - 1}); return 1; @@ -434,7 +464,7 @@ pub fn zigup() !u8 { std.log.err("'keep' command requires 1 argument but got {d}", .{args.len - 1}); return 1; } - try keepCompiler(args[1]); + try keepCompiler(&config, args[1]); return 0; } if (std.mem.eql(u8, "list", args[0])) { @@ -442,7 +472,7 @@ pub fn zigup() !u8 { std.log.err("'list' command requires 0 arguments but got {d}", .{args.len - 1}); return 1; } - try listCompilers(); + try listCompilers(&config); return 0; } if (std.mem.eql(u8, "default", args[0])) { @@ -452,14 +482,13 @@ pub fn zigup() !u8 { } if (args.len == 2) { const version_string = args[1]; - const install_dir_string = try getInstallDir(true); const resolved_version_string = init_resolved: { if (!std.mem.eql(u8, version_string, "master")) break :init_resolved version_string; const optional_master_dir: ?[]const u8 = blk: { - var install_dir = std.fs.openDirAbsolute(install_dir_string, .{ .iterate = true }) catch |e| switch (e) { + var install_dir = std.fs.openDirAbsolute(config.install_path, .{ .iterate = true }) catch |e| switch (e) { error.FileNotFound => break :blk null, else => return e, }; @@ -472,7 +501,7 @@ pub fn zigup() !u8 { return 1; }; }; - const compiler_dir = try std.fs.path.join(allocator, &[_][]const u8{ install_dir_string, resolved_version_string }); + const compiler_dir = try std.fs.path.join(allocator, &[_][]const u8{ config.install_path, resolved_version_string }); defer allocator.free(compiler_dir); try setDefaultCompiler(allocator, compiler_dir, &config, true); return 0; @@ -492,7 +521,7 @@ pub fn zigup() !u8 { //const optionalInstallPath = try find_zigs(allocator); } -pub fn runCompiler(allocator: Allocator, args: []const []const u8) !u8 { +pub fn runCompiler(allocator: Allocator, config: *const ZigupConfig, args: []const []const u8) !u8 { // disable log so we don't add extra output to whatever the compiler will output global_enable_log = false; if (args.len <= 1) { @@ -500,9 +529,8 @@ pub fn runCompiler(allocator: Allocator, args: []const []const u8) !u8 { return 1; } const version_string = args[0]; - const install_dir_string = try getInstallDir(true); - const compiler_dir = try std.fs.path.join(allocator, &[_][]const u8{ install_dir_string, version_string }); + const compiler_dir = try std.fs.path.join(allocator, &[_][]const u8{ config.install_path, version_string }); defer allocator.free(compiler_dir); if (!try existsAbsolute(compiler_dir)) { std.log.err("compiler '{s}' does not exist, fetch it first with: zigup fetch {0s}", .{version_string}); @@ -600,31 +628,22 @@ fn fetchDownloadIndex(allocator: Allocator) !DownloadIndex { // XXX: What? // TODO: Get rid of these -fn loggyMakeDirAbsolute(dir_absolute: []const u8) !void { - if (builtin.os.tag == .windows) { - loginfo("mkdir \"{s}\"", .{dir_absolute}); - } else { - loginfo("mkdir '{s}'", .{dir_absolute}); - } - try std.fs.makeDirAbsolute(dir_absolute); -} - fn loggyDeleteTreeAbsolute(dir_absolute: []const u8) !void { if (builtin.os.tag == .windows) { - loginfo("rd /s /q \"{s}\"", .{dir_absolute}); + logi("rd /s /q \"{s}\"", .{dir_absolute}); } else { - loginfo("rm -rf '{s}'", .{dir_absolute}); + logi("rm -rf '{s}'", .{dir_absolute}); } try fixdeletetree.deleteTreeAbsolute(dir_absolute); } pub fn loggyRenameAbsolute(old_path: []const u8, new_path: []const u8) !void { - loginfo("mv '{s}' '{s}'", .{ old_path, new_path }); + logi("mv '{s}' '{s}'", .{ old_path, new_path }); try std.fs.renameAbsolute(old_path, new_path); } pub fn loggySymlinkAbsolute(target_path: []const u8, sym_link_path: []const u8, flags: std.fs.Dir.SymLinkFlags) !void { - loginfo("ln -s '{s}' '{s}'", .{ target_path, sym_link_path }); + logi("ln -s '{s}' '{s}'", .{ target_path, sym_link_path }); // NOTE: can't use symLinkAbsolute because it requires target_path to be absolute but we don't want that // not sure if it is a bug in the standard lib or not //try std.fs.symLinkAbsolute(target_path, sym_link_path, flags); @@ -637,7 +656,7 @@ pub fn loggyUpdateSymlink(target_path: []const u8, sym_link_path: []const u8, fl var current_target_path_buffer: [std.fs.max_path_bytes]u8 = undefined; if (std.fs.readLinkAbsolute(sym_link_path, ¤t_target_path_buffer)) |current_target_path| { if (std.mem.eql(u8, target_path, current_target_path)) { - loginfo("symlink '{s}' already points to '{s}'", .{ sym_link_path, target_path }); + logi("symlink '{s}' already points to '{s}'", .{ sym_link_path, target_path }); return false; // already up-to-date } try std.posix.unlink(sym_link_path); @@ -675,10 +694,8 @@ fn existsAbsolute(absolutePath: []const u8) !bool { return true; } -fn listCompilers() !void { - const install_dir_string = try getInstallDir(false); - - var install_dir = std.fs.openDirAbsolute(install_dir_string, .{ .iterate = true }) catch |e| switch (e) { +fn listCompilers(config: *const ZigupConfig) !void { + var install_dir = std.fs.openDirAbsolute(config.install_path, .{ .iterate = true }) catch |e| switch (e) { error.FileNotFound => return, else => return e, }; @@ -697,10 +714,8 @@ fn listCompilers() !void { } } -fn keepCompiler(compiler_version: []const u8) !void { - const install_dir_string = try getInstallDir(true); - - var install_dir = try std.fs.openDirAbsolute(install_dir_string, .{ .iterate = true }); +fn keepCompiler(config: *const ZigupConfig, compiler_version: []const u8) !void { + var install_dir = try std.fs.openDirAbsolute(config.install_path, .{ .iterate = true }); defer install_dir.close(); var compiler_dir = install_dir.openDir(compiler_version, .{}) catch |e| switch (e) { @@ -712,17 +727,15 @@ fn keepCompiler(compiler_version: []const u8) !void { }; var keep_fd = try compiler_dir.createFile("keep", .{}); keep_fd.close(); - loginfo("created '{s}{c}{s}{c}{s}'", .{ install_dir_string, std.fs.path.sep, compiler_version, std.fs.path.sep, "keep" }); + logi("created '{s}{c}{s}{c}{s}'", .{ config.install_path, std.fs.path.sep, compiler_version, std.fs.path.sep, "keep" }); } -fn cleanCompilers(allocator: Allocator, compiler_name_opt: ?[]const u8) !void { - const install_dir_string = try getInstallDir(true); - +fn cleanCompilers(allocator: Allocator, config: *const ZigupConfig, compiler_name_opt: ?[]const u8) !void { // getting the current compiler const default_comp_opt = try getDefaultCompiler(allocator); defer if (default_comp_opt) |default_compiler| allocator.free(default_compiler); - var install_dir = std.fs.openDirAbsolute(install_dir_string, .{ .iterate = true }) catch |e| switch (e) { + var install_dir = std.fs.openDirAbsolute(config.install_path, .{ .iterate = true }) catch |e| switch (e) { error.FileNotFound => return, else => return e, }; @@ -734,7 +747,7 @@ fn cleanCompilers(allocator: Allocator, compiler_name_opt: ?[]const u8) !void { std.log.err("cannot clean '{s}' ({s})", .{ compiler_name, reason }); return error.AlreadyReported; } - loginfo("deleting '{s}{c}{s}'", .{ install_dir_string, std.fs.path.sep, compiler_name }); + logi("deleting '{s}{c}{s}'", .{ config.install_path, std.fs.path.sep, compiler_name }); try fixdeletetree.deleteTree(install_dir, compiler_name); } else { var it = install_dir.iterate(); @@ -742,7 +755,7 @@ fn cleanCompilers(allocator: Allocator, compiler_name_opt: ?[]const u8) !void { if (entry.kind != .directory) continue; if (getKeepReason(master_points_to_opt, default_comp_opt, entry.name)) |reason| { - loginfo("keeping '{s}' ({s})", .{ entry.name, reason }); + logi("keeping '{s}' ({s})", .{ entry.name, reason }); continue; } @@ -750,14 +763,14 @@ fn cleanCompilers(allocator: Allocator, compiler_name_opt: ?[]const u8) !void { var compiler_dir = try install_dir.openDir(entry.name, .{}); defer compiler_dir.close(); if (compiler_dir.access("keep", .{})) |_| { - loginfo("keeping '{s}' (has keep file)", .{entry.name}); + logi("keeping '{s}' (has keep file)", .{entry.name}); continue; } else |e| switch (e) { error.FileNotFound => {}, else => return e, } } - loginfo("deleting '{s}{c}{s}'", .{ install_dir_string, std.fs.path.sep, entry.name }); + logi("deleting '{s}{c}{s}'", .{ config.install_path, std.fs.path.sep, entry.name }); try fixdeletetree.deleteTree(install_dir, entry.name); } } @@ -855,8 +868,6 @@ fn setDefaultCompiler(allocator: Allocator, compiler_dir: []const u8, config: *c const link_path = try std.fmt.allocPrint(allocator, "{s}/default", .{config.path}); - loginfo("link path = {s}", .{link_path}); - const target = try std.fs.path.join(allocator, &[_][]const u8{ compiler_dir, "files" }); defer allocator.free(target); @@ -1094,14 +1105,14 @@ fn getDefaultUrl(allocator: Allocator, compiler_version: []const u8) ![]const u8 fn installCompiler(allocator: Allocator, compiler_dir: []const u8, url: []const u8) !void { if (try existsAbsolute(compiler_dir)) { - loginfo("compiler '{s}' already installed", .{compiler_dir}); + logi("compiler '{s}' already installed", .{compiler_dir}); return; } const installing_dir = try std.mem.concat(allocator, u8, &[_][]const u8{ compiler_dir, ".installing" }); defer allocator.free(installing_dir); try loggyDeleteTreeAbsolute(installing_dir); - try loggyMakeDirAbsolute(installing_dir); + try makeDirIfMissing(installing_dir); const archive_basename = std.fs.path.basename(url); var archive_root_dir: []const u8 = undefined; @@ -1110,7 +1121,7 @@ fn installCompiler(allocator: Allocator, compiler_dir: []const u8, url: []const { const archive_absolute = try std.fs.path.join(allocator, &[_][]const u8{ installing_dir, archive_basename }); defer allocator.free(archive_absolute); - loginfo("downloading '{s}' to '{s}'", .{ url, archive_absolute }); + logi("downloading '{s}' to '{s}'", .{ url, archive_absolute }); switch (blk: { const file = try std.fs.createFileAbsolute(archive_absolute, .{}); @@ -1140,13 +1151,13 @@ fn installCompiler(allocator: Allocator, compiler_dir: []const u8, url: []const var installing_dir_opened = try std.fs.openDirAbsolute(installing_dir, .{}); defer installing_dir_opened.close(); - loginfo("extracting archive to \"{s}\"", .{installing_dir}); + logi("extracting archive to \"{s}\"", .{installing_dir}); var timer = try std.time.Timer.start(); var archive_file = try std.fs.openFileAbsolute(archive_absolute, .{}); defer archive_file.close(); try std.zip.extract(installing_dir_opened, archive_file.seekableStream(), .{}); const time = timer.read(); - loginfo("extracted archive in {d:.2} s", .{@as(f32, @floatFromInt(time)) / @as(f32, @floatFromInt(std.time.ns_per_s))}); + logi("extracted archive in {d:.2} s", .{@as(f32, @floatFromInt(time)) / @as(f32, @floatFromInt(std.time.ns_per_s))}); } } @@ -1195,7 +1206,7 @@ fn logRun(allocator: Allocator, argv: []const []const u8) !void { offset += arg.len; } std.debug.assert(offset == buffer.len); - loginfo("[RUN] {s}", .{buffer}); + logi("[RUN] {s}", .{buffer}); } pub fn getCommandStringLength(argv: []const []const u8) usize { From d0d45257874d7339000fee05b9512bf3dd664ceb Mon Sep 17 00:00:00 2001 From: Mulling Date: Thu, 14 Nov 2024 20:12:04 -0300 Subject: [PATCH 10/19] Document behaviour --- zigup.zig | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/zigup.zig b/zigup.zig index 474c3e1..1a46328 100644 --- a/zigup.zig +++ b/zigup.zig @@ -377,12 +377,22 @@ pub fn zigup() !u8 { const config = try readConfigFromEnv(allocator) orelse { try configure(allocator); + // NOTE: We could just make zigup work here, but we should assert that + // the user does the proper shell configuration. One way of doing this + // is to just do noting, which will cause zigup to "not work" until the + // shell environment is up return 0; }; + // Assert that the dirs exist try makeDirIfMissing(config.path); try makeDirIfMissing(config.install_path); + // TODO: If the user removes `config.path` zigup will work while the shell + // is up, we should assert that the env exist, possible creating it if it's + // missing. This has the bonus of allowing the user to skip configuration + // with `ZIGUP_PATH="..." zigup ...`. + var args = if (args_array.len == 0) args_array else args_array[1..]; // parse common options // From 0a7de695210604fbd4d22415b4d63c0901d6ed0c Mon Sep 17 00:00:00 2001 From: Mulling Date: Thu, 14 Nov 2024 21:39:43 -0300 Subject: [PATCH 11/19] Newline terminate env --- zigup.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/zigup.zig b/zigup.zig index 1a46328..2f8a521 100644 --- a/zigup.zig +++ b/zigup.zig @@ -319,6 +319,7 @@ const shellEnvFmt = \\ export PATH="$ZIGUP_DIR/default:$PATH" \\ ;; \\esac + \\ ; const sourceEnvFmt = From d41de087f1343d86222d9fe0497ff02a51189bd0 Mon Sep 17 00:00:00 2001 From: Mulling Date: Thu, 14 Nov 2024 23:47:51 -0300 Subject: [PATCH 12/19] WIP on default compiler to path --- zigup.zig | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/zigup.zig b/zigup.zig index 2f8a521..9056a81 100644 --- a/zigup.zig +++ b/zigup.zig @@ -512,6 +512,10 @@ pub fn zigup() !u8 { return 1; }; }; + + // If the user suplys us with a path, make it the default compiler + if (try setDefaultCompilerFromPath(&config, resolved_version_string)) return 0; + const compiler_dir = try std.fs.path.join(allocator, &[_][]const u8{ config.install_path, resolved_version_string }); defer allocator.free(compiler_dir); try setDefaultCompiler(allocator, compiler_dir, &config, true); @@ -877,15 +881,13 @@ fn setDefaultCompiler(allocator: Allocator, compiler_dir: []const u8, config: *c dir.close(); } - const link_path = try std.fmt.allocPrint(allocator, "{s}/default", .{config.path}); - const target = try std.fs.path.join(allocator, &[_][]const u8{ compiler_dir, "files" }); defer allocator.free(target); if (builtin.os.tag == .windows) { - try createExeLink(target, link_path); + try createExeLink(target, config.default_path); } else { - _ = try loggyUpdateSymlink(target, link_path, .{}); + _ = try loggyUpdateSymlink(target, config.default_path, .{}); } // TODO: Keep or remove this?! @@ -893,6 +895,30 @@ fn setDefaultCompiler(allocator: Allocator, compiler_dir: []const u8, config: *c //try verifyPathLink(allocator, link_path); } +fn openDir(path: []const u8) !std.fs.Dir { + return if (std.fs.path.isAbsolute(path)) + std.fs.openDirAbsolute(path, .{}) + else + std.fs.cwd().openDir(path, .{}); +} + +fn setDefaultCompilerFromPath(config: *const ZigupConfig, path: []const u8) !bool { + var dir = openDir(path) catch |err| + if (err == error.FileNotFound) return false else return err; + errdefer dir.close(); + + var buf = std.mem.zeroes([std.os.linux.PATH_MAX]u8); + const real_path: []const u8 = try dir.realpath(".", &buf); + + logi("set default compiler directory '{s}'", .{real_path}); + + // TODO: Asset that the path leads to a valid zig compiler. + // TODO: Windows + _ = try loggyUpdateSymlink(real_path, config.default_path, .{}); + + return true; +} + /// Verify that path_link will work. It verifies that `path_link` is /// in PATH and there is no zig executable in an earlier directory in PATH. fn verifyPathLink(allocator: Allocator, path_link: []const u8) !void { From 439978ddbbc6943c042acc141eb9fc9dc0ef8270 Mon Sep 17 00:00:00 2001 From: Mulling Date: Mon, 18 Nov 2024 11:10:39 -0300 Subject: [PATCH 13/19] Add verbose option --- zigup.zig | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/zigup.zig b/zigup.zig index 9056a81..142c1ad 100644 --- a/zigup.zig +++ b/zigup.zig @@ -31,6 +31,8 @@ var global_optional_path_link: ?[]const u8 = null; var global_enable_log = true; +var verbose = false; + inline fn fix_format_string(comptime fmt: []const u8) []const u8 { if (builtin.os.tag == .windows) { // Not sure what is going on here, or why they are doing this on @@ -46,10 +48,7 @@ inline fn fix_format_string(comptime fmt: []const u8) []const u8 { } inline fn logi(comptime fmt: []const u8, args: anytype) void { - // TODO: `--verbose` option!? - if (builtin.mode == std.builtin.OptimizeMode.Debug) { - std.log.info(fix_format_string(fmt), args); - } + if (verbose) std.log.info(fix_format_string(fmt), args); } inline fn loge(comptime fmt: []const u8, args: anytype) void { @@ -396,7 +395,6 @@ pub fn zigup() !u8 { var args = if (args_array.len == 0) args_array else args_array[1..]; // parse common options - // { var i: usize = 0; var newlen: usize = 0; @@ -415,6 +413,8 @@ pub fn zigup() !u8 { } else if (std.mem.eql(u8, "-h", arg) or std.mem.eql(u8, "--help", arg)) { help(); return 0; + } else if (std.mem.eql(u8, "-v", arg) or std.mem.eql(u8, "--verbose", arg)) { + verbose = true; } else { if (newlen == 0 and std.mem.eql(u8, "run", arg)) { return try runCompiler(allocator, &config, args[i + 1 ..]); From d1dc20c607ce6d560630f5ed30bd9361d022f642 Mon Sep 17 00:00:00 2001 From: Mulling Date: Mon, 18 Nov 2024 12:46:18 -0300 Subject: [PATCH 14/19] Auto-search for zig when default compiler is a path --- zigup.zig | 46 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/zigup.zig b/zigup.zig index 142c1ad..96cb33f 100644 --- a/zigup.zig +++ b/zigup.zig @@ -31,8 +31,12 @@ var global_optional_path_link: ?[]const u8 = null; var global_enable_log = true; +/// Ouput debug info var verbose = false; +/// Force the given operation, i.e., `zigup --force default .` +var force = false; + inline fn fix_format_string(comptime fmt: []const u8) []const u8 { if (builtin.os.tag == .windows) { // Not sure what is going on here, or why they are doing this on @@ -514,7 +518,7 @@ pub fn zigup() !u8 { }; // If the user suplys us with a path, make it the default compiler - if (try setDefaultCompilerFromPath(&config, resolved_version_string)) return 0; + if (try setDefaultCompilerFromPath(allocator, &config, resolved_version_string)) return 0; const compiler_dir = try std.fs.path.join(allocator, &[_][]const u8{ config.install_path, resolved_version_string }); defer allocator.free(compiler_dir); @@ -897,18 +901,50 @@ fn setDefaultCompiler(allocator: Allocator, compiler_dir: []const u8, config: *c fn openDir(path: []const u8) !std.fs.Dir { return if (std.fs.path.isAbsolute(path)) - std.fs.openDirAbsolute(path, .{}) + std.fs.openDirAbsolute(path, .{ .iterate = true }) else - std.fs.cwd().openDir(path, .{}); + std.fs.cwd().openDir(path, .{ .iterate = true }); +} + +fn findCompiler(allocator: Allocator, dir: *const std.fs.Dir, buf: []u8) !?[]u8 { + var it = try dir.walk(allocator); + errdefer it.deinit(); + + while (try it.next()) |entry| { + if (entry.kind == .file and std.mem.eql(u8, entry.basename, "zig")) { + const real_path = try entry.dir.realpath(".", buf); + + logi("found zig at '{s}'", .{real_path}); + + return real_path; + } + + if (entry.kind == .directory) { + logi("searching '{s}'", .{entry.basename}); + + var next = try entry.dir.openDir(entry.basename, .{ .iterate = true }); + errdefer next.close(); + + if (try findCompiler(allocator, &next, buf)) |zig| return zig; + } + } + + return null; } -fn setDefaultCompilerFromPath(config: *const ZigupConfig, path: []const u8) !bool { +fn setDefaultCompilerFromPath(allocator: Allocator, config: *const ZigupConfig, path: []const u8) !bool { var dir = openDir(path) catch |err| if (err == error.FileNotFound) return false else return err; errdefer dir.close(); var buf = std.mem.zeroes([std.os.linux.PATH_MAX]u8); - const real_path: []const u8 = try dir.realpath(".", &buf); + var real_path: []const u8 = try dir.realpath(".", &buf); + + if (!force) { + logi("searching '{s}'", .{path}); + // XXX: Error out here? + real_path = try findCompiler(allocator, &dir, &buf) orelse return false; + } logi("set default compiler directory '{s}'", .{real_path}); From 29f71883e9d67b739a7348f39b2a8fa2ca379e78 Mon Sep 17 00:00:00 2001 From: Mulling Date: Mon, 18 Nov 2024 16:04:40 -0300 Subject: [PATCH 15/19] Fix getDefaultCompiler * Since now we allow for arbitrary paths to be set as the default compiler, we can't use the path to determine the version. Instead, use `zig version` to do so. --- zigup.zig | 81 +++++++++++++++++++++++-------------------------------- 1 file changed, 33 insertions(+), 48 deletions(-) diff --git a/zigup.zig b/zigup.zig index 96cb33f..f98fa0c 100644 --- a/zigup.zig +++ b/zigup.zig @@ -26,11 +26,6 @@ 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; - -var global_enable_log = true; - /// Ouput debug info var verbose = false; @@ -144,14 +139,12 @@ fn makeDirIfMissing(path: []const u8) !void { }; } -fn makeZigPathLinkString(allocator: Allocator) ![]const u8 { - if (global_optional_path_link) |path| return path; - - const zigup_dir = try std.fs.selfExeDirPathAlloc(allocator); - defer allocator.free(zigup_dir); - - return try std.fs.path.join(allocator, &[_][]const u8{ zigup_dir, comptime "zig" ++ builtin.target.exeFileExt() }); -} +// fn makeZigPathLinkString(allocator: Allocator) ![]const u8 { +// const zigup_dir = try std.fs.selfExeDirPathAlloc(allocator); +// defer allocator.free(zigup_dir); +// +// return try std.fs.path.join(allocator, &[_][]const u8{ zigup_dir, comptime "zig" ++ builtin.target.exeFileExt() }); +// } // TODO: this should be in standard lib fn toAbsolute(allocator: Allocator, path: []const u8) ![]u8 { @@ -404,17 +397,7 @@ pub fn zigup() !u8 { var newlen: usize = 0; while (i < args.len) : (i += 1) { const arg = args[i]; - if (std.mem.eql(u8, "--install-dir", arg)) { - global_optional_install_dir = try getCmdOpt(args, &i); - if (!std.fs.path.isAbsolute(global_optional_install_dir.?)) { - global_optional_install_dir = try toAbsolute(allocator, global_optional_install_dir.?); - } - } else if (std.mem.eql(u8, "--path-link", arg)) { - global_optional_path_link = try getCmdOpt(args, &i); - if (!std.fs.path.isAbsolute(global_optional_path_link.?)) { - global_optional_path_link = try toAbsolute(allocator, global_optional_path_link.?); - } - } else if (std.mem.eql(u8, "-h", arg) or std.mem.eql(u8, "--help", arg)) { + if (std.mem.eql(u8, "-h", arg) or std.mem.eql(u8, "--help", arg)) { help(); return 0; } else if (std.mem.eql(u8, "-v", arg) or std.mem.eql(u8, "--verbose", arg)) { @@ -492,7 +475,7 @@ pub fn zigup() !u8 { } if (std.mem.eql(u8, "default", args[0])) { if (args.len == 1) { - try printDefaultCompiler(allocator); + try printDefaultCompiler(allocator, &config); return 0; } if (args.len == 2) { @@ -541,8 +524,6 @@ pub fn zigup() !u8 { } pub fn runCompiler(allocator: Allocator, config: *const ZigupConfig, args: []const []const u8) !u8 { - // disable log so we don't add extra output to whatever the compiler will output - global_enable_log = false; if (args.len <= 1) { std.log.err("zigup run requires at least 2 arguments: zigup run VERSION PROG ARGS...", .{}); return 1; @@ -751,7 +732,7 @@ fn keepCompiler(config: *const ZigupConfig, compiler_version: []const u8) !void fn cleanCompilers(allocator: Allocator, config: *const ZigupConfig, compiler_name_opt: ?[]const u8) !void { // getting the current compiler - const default_comp_opt = try getDefaultCompiler(allocator); + const default_comp_opt = try getDefaultCompiler(allocator, config); defer if (default_comp_opt) |default_compiler| allocator.free(default_compiler); var install_dir = std.fs.openDirAbsolute(config.install_path, .{ .iterate = true }) catch |e| switch (e) { @@ -794,12 +775,13 @@ fn cleanCompilers(allocator: Allocator, config: *const ZigupConfig, compiler_nam } } } -fn readDefaultCompiler(allocator: Allocator, buffer: *[std.fs.max_path_bytes + 1]u8) !?[]const u8 { - const path_link = try makeZigPathLinkString(allocator); - defer allocator.free(path_link); +fn readDefaultCompiler(allocator: Allocator, buffer: *[std.fs.max_path_bytes + 1]u8, config: *const ZigupConfig) !?[]const u8 { + logi("read default compiler link '{s}'", .{config.default_path}); + + // TODO: Log for windows if (builtin.os.tag == .windows) { - var file = std.fs.openFileAbsolute(path_link, .{}) catch |e| switch (e) { + var file = std.fs.openFileAbsolute(config.default_path, .{}) catch |e| switch (e) { error.FileNotFound => return null, else => return e, }; @@ -807,22 +789,24 @@ fn readDefaultCompiler(allocator: Allocator, buffer: *[std.fs.max_path_bytes + 1 try file.seekTo(win32exelink.exe_offset); const len = try file.readAll(buffer); if (len != buffer.len) { - std.log.err("path link file '{s}' is too small", .{path_link}); + std.log.err("path link file '{s}' is too small", .{config.default_path}); return error.AlreadyReported; } const target_exe = std.mem.sliceTo(buffer, 0); - return try allocator.dupe(u8, targetPathToVersion(target_exe)); + try targetPathToVersion(allocator, target_exe); } - const target_path = std.fs.readLinkAbsolute(path_link, buffer[0..std.fs.max_path_bytes]) catch |e| switch (e) { + const target_path = std.fs.readLinkAbsolute(config.default_path, buffer[0..std.fs.max_path_bytes]) catch |e| switch (e) { error.FileNotFound => return null, else => return e, }; - defer allocator.free(target_path); - return try allocator.dupe(u8, targetPathToVersion(target_path)); + + return 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).?).?); + +inline fn targetPathToVersion(allocator: Allocator, path: []const u8) !void { + const zig_path = try std.fs.path.join(allocator, &[_][]const u8{ path, "zig" }); + _ = try run(allocator, &[_][]const u8{ zig_path, "version" }); } fn readMasterDir(buffer: *[std.fs.max_path_bytes]u8, install_dir: *std.fs.Dir) !?[]const u8 { @@ -840,9 +824,13 @@ fn readMasterDir(buffer: *[std.fs.max_path_bytes]u8, install_dir: *std.fs.Dir) ! }; } -fn getDefaultCompiler(allocator: Allocator) !?[]const u8 { +fn getDefaultCompiler(allocator: Allocator, config: *const ZigupConfig) !?[]const u8 { + // XXX: Why +1!? var buffer: [std.fs.max_path_bytes + 1]u8 = undefined; - const slice_path = (try readDefaultCompiler(allocator, &buffer)) orelse return null; + + // Dupped again!? + const slice_path = try readDefaultCompiler(allocator, &buffer, config) orelse return null; + const path_to_return = try allocator.alloc(u8, slice_path.len); @memcpy(path_to_return, slice_path); return path_to_return; @@ -856,14 +844,11 @@ fn getMasterDir(allocator: Allocator, install_dir: *std.fs.Dir) !?[]const u8 { return path_to_return; } -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| { - try stdout.print("{s}\n", .{default_compiler}); +fn printDefaultCompiler(allocator: Allocator, config: *const ZigupConfig) !void { + if (try getDefaultCompiler(allocator, config)) |default_compiler| { + try targetPathToVersion(allocator, default_compiler); } else { - try stdout.writeAll("\n"); + try std.io.getStdOut().writeAll("\n"); } } From 752d1f2bef3c29997a63cfaebab14acbc1c1478c Mon Sep 17 00:00:00 2001 From: Mulling Date: Wed, 20 Nov 2024 02:50:27 -0300 Subject: [PATCH 16/19] Override log level --- zigup.zig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/zigup.zig b/zigup.zig index f98fa0c..c99a3cc 100644 --- a/zigup.zig +++ b/zigup.zig @@ -5,6 +5,10 @@ const mem = std.mem; const ArrayList = std.ArrayList; const Allocator = mem.Allocator; +pub const std_options = .{ + .log_level = .info, +}; + const fixdeletetree = @import("fixdeletetree.zig"); const arch = switch (builtin.cpu.arch) { From 3e906161b537f1adcc9293fea05604bd4b3a25de Mon Sep 17 00:00:00 2001 From: Mulling Date: Wed, 20 Nov 2024 03:01:36 -0300 Subject: [PATCH 17/19] undefine: Warn if no compiler is set as default --- zigup.zig | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/zigup.zig b/zigup.zig index c99a3cc..c46a3b5 100644 --- a/zigup.zig +++ b/zigup.zig @@ -436,9 +436,8 @@ pub fn zigup() !u8 { return 1; } - try unsetDefaultCompiler(allocator, &config); + try unsetDefaultCompiler(&config); - if (isBash()) logw("Use `hash -r` to reset the command location cache.", .{}); return 0; } if (std.mem.eql(u8, "fetch", args[0])) { @@ -856,10 +855,13 @@ fn printDefaultCompiler(allocator: Allocator, config: *const ZigupConfig) !void } } -fn unsetDefaultCompiler(allocator: Allocator, config: *const ZigupConfig) !void { - const link_path = try std.fmt.allocPrint(allocator, "{s}/default", .{config.path}); +fn unsetDefaultCompiler(config: *const ZigupConfig) !void { + std.posix.unlink(config.default_path) catch |err| + return if (err == error.FileNotFound) logw("no default compiler is set", .{}) else err; - try std.posix.unlink(link_path); + if (isBash()) { + logw("Use `hash -r` to reset the command location cache.", .{}); + } } fn setDefaultCompiler(allocator: Allocator, compiler_dir: []const u8, config: *const ZigupConfig, verify_exists: bool) !void { From 861644ccd8abae97fd25c383e6dc3205345822a0 Mon Sep 17 00:00:00 2001 From: Mulling Date: Sun, 24 Nov 2024 14:36:23 -0300 Subject: [PATCH 18/19] Fix build and update README --- README.md | 31 ++++++++++++++----------------- zigup.zig | 7 ++----- 2 files changed, 16 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 840f169..557a3ee 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # zigup -Fork of [zigup](https://marler8997.github.io/zigup) which aims to make it easier to download and manage zig versions. This fork is based on how [rustup](https://rustup.rs/) works. +Download and manage zig compilers. # How to Install @@ -11,17 +11,6 @@ Otherwise, you can manually find and download/extract the applicable archive fro # Usage ```sh -$ zig version -0.14.0-dev.2198+e5f5229fd -$ zigup fetch 0.13.0 -... -$ zigup default 0.13.0 -... -$ zig version -0.13.0 -``` - -``` # fetch a compiler and set it as the default zigup zigup master @@ -37,6 +26,12 @@ zigup default # set the default compiler zigup default +# set the default compiler from a path +zigup default zig/build + +# unset the default compiler (for using a global installation) +zigup undefine + # list the installed compiler versions zigup list @@ -52,11 +47,12 @@ zigup run ... # How the compilers are managed -zigup stores each compiler in a global "install directory" in a versioned subdirectory. On posix systems the "install directory" is `$HOME/zig` and on windows the install directory will be a directory named "zig" in the same directory as the "zigup.exe". +`zigup` stores each compiler in `$ZIGUP_INSTALL_DIR`, in a versioned subdirectory. The default install directory is `$HOME/.zigup/cache`. + +`zigup` makes the zig available by creating a symlink at `$ZIGUP_INSTALL_DIR/` and `$ZIGUP_DIR/default` which points to the current active default compiler. -zigup makes the zig program available by creating an entry in a directory that occurs in the `PATH` environment variable. On posix systems this entry is a symlink to one of the `zig` executables in the install directory. On windows this is an executable that forwards invocations to one of the `zig` executables in the install directory. +Configuration on done during the first use of zigup and the generated environment is installed at `$ZIGUP_DIR/env`. -Both the "install directory" and "path link" are configurable through command-line options `--install-dir` and `--path-link` respectively. # Building Run `zig build` to build, `zig build test` to test and install with: @@ -67,11 +63,12 @@ cp zig-out/bin/zigup BIN_PATH # Building Zigup -Zigup is currently built/tested using zig 0.12.0. +Zigup is currently built/tested using zig 0.13.0+. # TODO -* set/remove compiler in current environment without overriding the system-wide version. +[ ] - Download to memory. +[ ] - Use `std.tar` (Unix) # Dependencies diff --git a/zigup.zig b/zigup.zig index c46a3b5..2c93b3f 100644 --- a/zigup.zig +++ b/zigup.zig @@ -5,7 +5,7 @@ const mem = std.mem; const ArrayList = std.ArrayList; const Allocator = mem.Allocator; -pub const std_options = .{ +pub const std_options: std.Options = .{ .log_level = .info, }; @@ -179,10 +179,7 @@ fn help() void { \\ zigup fetch-index download and print the download index json \\ \\Common Options: - \\ --install-dir DIR override the default install location - \\ --path-link PATH path to the `zig` symlink that points to the default compiler - \\ this will typically be a file path within a PATH directory so - \\ that the user can just run `zig` + \\ --verbose | -v output verbose information \\ ) catch unreachable; } From 61d4f0a42fa0a8f70f38a18c4a22c5fa99f1455e Mon Sep 17 00:00:00 2001 From: Mulling Date: Mon, 25 Nov 2024 11:31:41 -0300 Subject: [PATCH 19/19] Update README --- README.md | 4 ++-- zigup.zig | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 557a3ee..1a4be8a 100644 --- a/README.md +++ b/README.md @@ -67,8 +67,8 @@ Zigup is currently built/tested using zig 0.13.0+. # TODO -[ ] - Download to memory. -[ ] - Use `std.tar` (Unix) +- [ ] Download to memory +- [ ] Use `std.tar` (Unix) # Dependencies diff --git a/zigup.zig b/zigup.zig index 2c93b3f..b442b01 100644 --- a/zigup.zig +++ b/zigup.zig @@ -389,7 +389,7 @@ pub fn zigup() !u8 { // TODO: If the user removes `config.path` zigup will work while the shell // is up, we should assert that the env exist, possible creating it if it's // missing. This has the bonus of allowing the user to skip configuration - // with `ZIGUP_PATH="..." zigup ...`. + // dialog with `ZIGUP_PATH="..." zigup ...`. var args = if (args_array.len == 0) args_array else args_array[1..]; // parse common options