diff --git a/build.zig b/build.zig index 1e662d6..6f73740 100644 --- a/build.zig +++ b/build.zig @@ -28,6 +28,7 @@ comptime { pub fn build(b: *Build) void { const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); + // Add a global option for versioning const options = b.addOptions(); options.addOption(std.log.Level, "log_level", b.option(std.log.Level, "log_level", "The Log Level to be used.") orelse .info); @@ -46,6 +47,16 @@ pub fn build(b: *Build) void { b.installArtifact(exe); + // add run step + const run_exe = b.addRunArtifact(exe); + run_exe.step.dependOn(b.getInstallStep()); + + if (b.args) |args| + run_exe.addArgs(args); + + const run_step = b.step("run", "Run the application"); + run_step.dependOn(&run_exe.step); + const release = b.step("release", "make an upstream binary release"); const release_targets = [_]std.Target.Query{ .{ .cpu_arch = .x86_64, .os_tag = .linux }, diff --git a/src/alias.zig b/src/alias.zig index af913e4..aeb84f2 100644 --- a/src/alias.zig +++ b/src/alias.zig @@ -11,7 +11,7 @@ pub fn set_zig_version(version: []const u8) !void { const user_home = tools.get_home(); const zig_path = try std.fs.path.join(arena_allocator, &[_][]const u8{ user_home, ".zm", "versions", version }); - const symlink_path = try std.fs.path.join(arena_allocator, &[_][]const u8{ user_home, ".zm", "current" }); + const symlink_path = try tools.get_zvm_path_segment(arena_allocator, "current"); try update_symlink(zig_path, symlink_path); try verify_zig_version(allocator, version); @@ -121,8 +121,7 @@ fn verify_zig_version(allocator: std.mem.Allocator, expected_version: []const u8 } fn retrieve_zig_version(allocator: std.mem.Allocator) ![]u8 { - const user_home = tools.get_home(); - const symlink_path = try std.fs.path.join(allocator, &[_][]const u8{ user_home, ".zm", "current" }); + const symlink_path = try tools.get_zvm_path_segment(allocator, "current"); defer allocator.free(symlink_path); var child_process = std.process.Child.init(&[_][]const u8{ "zig", "version" }, allocator); diff --git a/src/command.zig b/src/command.zig index 50e1040..9631745 100644 --- a/src/command.zig +++ b/src/command.zig @@ -1,11 +1,13 @@ const std = @import("std"); +const builtin = @import("builtin"); +const options = @import("options"); + const versions = @import("versions.zig"); const install = @import("install.zig"); const alias = @import("alias.zig"); const tools = @import("tools.zig"); -const options = @import("options"); - +// command species pub const Command = enum { List, Install, @@ -16,32 +18,100 @@ pub const Command = enum { Unknown, }; -pub fn handle_commands(cmd: Command, params: ?[]const u8) !void { - switch (cmd) { - .List => { - try handle_list(); - }, - .Install => { - try install_version(params); - }, - .Use => { - try use_version(params); - }, - .Default => { - try set_default(); - }, - .Version => { - try get_version(); - }, - .Help => { - try display_help(); - }, - .Unknown => { - try handle_unknown(); - }, +const CommandData = struct { + cmd: Command = .Unknown, + param: ?[]const u8 = null, +}; + +const CommandOption = struct { + short_handle: ?[]const u8, + handle: []const u8, + cmd: Command, +}; + +/// now all available commands +const command_opts = [_]CommandOption{ + .{ .short_handle = "ls", .handle = "list", .cmd = Command.List }, + .{ .short_handle = "i", .handle = "install", .cmd = Command.Install }, + .{ .short_handle = null, .handle = "use", .cmd = Command.Use }, + .{ .short_handle = null, .handle = "--version", .cmd = Command.Version }, + .{ .short_handle = null, .handle = "--help", .cmd = Command.Help }, + .{ .short_handle = null, .handle = "--default", .cmd = Command.Default }, +}; + +/// parse command and handle commands +pub fn handle_command(params: []const []const u8) !void { + if (builtin.os.tag != .windows) { + if (std.mem.eql(u8, std.fs.path.basename(params[0]), "zig")) + try handleAlias(params); + } + + // get command data, get the first command and its arg + const command: CommandData = blk: { + // when args len is less than 2, that mean no extra args! + if (params.len < 2) break :blk CommandData{}; + + const args = params[1..]; + + for (args, 0..) |arg, index| { + for (command_opts) |opt| { + + // whether eql short handle + const is_eql_short_handle = + if (opt.short_handle) |short_handle| + std.mem.eql(u8, arg, short_handle) + else + false; + + // whether eql handle + const is_eql_handle = std.mem.eql(u8, arg, opt.handle); + + if (!is_eql_short_handle and !is_eql_handle) + continue; + + break :blk CommandData{ + .cmd = opt.cmd, + .param = if (index + 1 < args.len) args[index + 1] else null, + }; + } + } + break :blk CommandData{}; + }; + + switch (command.cmd) { + .List => try handle_list(), + .Install => try install_version(command.param), + .Use => try use_version(command.param), + .Default => try set_default(), + .Version => try get_version(), + .Help => try display_help(), + .Unknown => try handle_unknown(), } } +fn handleAlias(params: []const []const u8) !void { + var arena = std.heap.ArenaAllocator.init(tools.get_allocator()); + defer arena.deinit(); + + const allocator = arena.allocator(); + + const new_params = try allocator.dupe([]const u8, params); + + const home = tools.get_home(); + const current_zig_path = try std.fs.path.join(allocator, &.{ home, ".zm", "current", "zig" }); + + std.fs.accessAbsolute(current_zig_path, .{}) catch |err| { + if (err == std.fs.Dir.AccessError.FileNotFound) { + std.debug.print("Zig has not been installed yet, please install zig with zvm!\n", .{}); + std.process.exit(1); + } + return err; + }; + + new_params[0] = current_zig_path; + return std.process.execv(allocator, new_params); +} + fn handle_list() !void { const allocator = tools.get_allocator(); var version_list = try versions.VersionList.init(allocator); @@ -95,6 +165,7 @@ fn display_help() !void { \\ zvm use 0.8.0 Switch to using Zig version 0.8.0. \\ \\For additional information and contributions, please visit the GitHub repository. + \\ ; std.debug.print(help_message, .{}); diff --git a/src/main.zig b/src/main.zig index 645ee00..fbdd213 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,64 +1,24 @@ const std = @import("std"); const tools = @import("tools.zig"); -const Command = @import("command.zig").Command; -const handle_commands = @import("command.zig").handle_commands; - -const CommandData = struct { - cmd: Command, - params: ?[]const u8, -}; - -const CommandOption = struct { - short_handle: ?[]const u8, - handle: []const u8, - cmd: Command, -}; +const command = @import("command.zig"); pub fn main() !void { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + // this will detect the memory whether leak defer if (gpa.deinit() == .leak) @panic("memory leaked!"); + // init some useful data try tools.data_init(gpa.allocator()); + // deinit some data defer tools.data_deinit(); + // get allocator const allocator = tools.get_allocator(); + // get and free args const args = try std.process.argsAlloc(allocator); defer std.process.argsFree(allocator, args); - const cmd_data = try parse_args(args); - try handle_commands(cmd_data.cmd, cmd_data.params); -} - -fn parse_args(args: []const []const u8) !CommandData { - const options = get_available_commands(); - if (args.len < 2) return CommandData{ .cmd = Command.Unknown, .params = null }; - return find_command_in_args(args[1..], options) orelse CommandData{ .cmd = Command.Unknown, .params = null }; -} - -fn get_available_commands() []const CommandOption { - return &[_]CommandOption{ - CommandOption{ .short_handle = "ls", .handle = "list", .cmd = Command.List }, - CommandOption{ .short_handle = "i", .handle = "install", .cmd = Command.Install }, - CommandOption{ .short_handle = null, .handle = "use", .cmd = Command.Use }, - CommandOption{ .short_handle = null, .handle = "--version", .cmd = Command.Version }, - CommandOption{ .short_handle = null, .handle = "--help", .cmd = Command.Help }, - CommandOption{ .short_handle = null, .handle = "--default", .cmd = Command.Default }, - }; -} - -fn find_command_in_args(args: []const []const u8, options: []const CommandOption) ?CommandData { - var i: usize = 0; - for (args) |arg| { - for (options) |option| { - if ((option.short_handle != null and std.mem.eql(u8, arg, option.short_handle.?)) or - std.mem.eql(u8, arg, option.handle)) - { - const params = if (i + 1 < args.len) args[i + 1] else null; - return CommandData{ .cmd = option.cmd, .params = params }; - } - } - i += 1; - } - return null; + // parse the args and handle command + try command.handle_command(args); } diff --git a/src/tools.zig b/src/tools.zig index 1c2f75d..9adfb00 100644 --- a/src/tools.zig +++ b/src/tools.zig @@ -31,6 +31,7 @@ pub fn get_allocator() std.mem.Allocator { return allocator; } +/// get zvm path segment pub fn get_zvm_path_segment(tmp_allocator: std.mem.Allocator, segment: []const u8) ![]u8 { return std.fs.path.join( tmp_allocator, diff --git a/src/versions.zig b/src/versions.zig index 5e27cac..b1d8182 100644 --- a/src/versions.zig +++ b/src/versions.zig @@ -1,22 +1,31 @@ +//! For getting zig version info json from offical website const std = @import("std"); const config = @import("config.zig"); const uri = std.Uri.parse(config.download_manifest_url) catch unreachable; pub const VersionList = struct { + // this type will store const List = std.ArrayList([]const u8); + + // store the version message lists: List, allocator: std.mem.Allocator, + /// init the VersionList pub fn init(allocator: std.mem.Allocator) !VersionList { + // create a http client var client = std.http.Client{ .allocator = allocator }; defer client.deinit(); + // we ceate a buffer to store the http response var buffer: [262144]u8 = undefined; // 256 * 1024 = 262kb + // try open a request var req = try client.open(.GET, uri, .{ .server_header_buffer = &buffer }); defer req.deinit(); + // send request and wait response try req.send(); try req.wait(); @@ -26,6 +35,7 @@ pub const VersionList = struct { const len = try req.readAll(buffer[0..]); + // parse json const json = try std.json.parseFromSlice(std.json.Value, allocator, buffer[0..len], .{}); defer json.deinit(); const root = json.value; @@ -47,10 +57,12 @@ pub const VersionList = struct { }; } + // get the slice items pub fn slice(self: *VersionList) [][]const u8 { return self.lists.items; } + /// deinit will free memory pub fn deinit(self: *VersionList) void { defer self.lists.deinit(); for (self.lists.items) |value| {