diff options
| author | 2022-04-25 05:09:55 +0300 | |
|---|---|---|
| committer | 2022-04-25 23:34:05 +0300 | |
| commit | d303b53f2ced75703bf022a5d337ee3ba530b288 (patch) | |
| tree | f2e64057120d01ee8a821596ea01b8fc37c53c2c /src/install.zig | |
| download | zup-d303b53f2ced75703bf022a5d337ee3ba530b288.tar.gz zup-d303b53f2ced75703bf022a5d337ee3ba530b288.tar.xz zup-d303b53f2ced75703bf022a5d337ee3ba530b288.zip | |
Initial commit0.1.0
Diffstat (limited to 'src/install.zig')
| -rw-r--r-- | src/install.zig | 101 |
1 files changed, 101 insertions, 0 deletions
diff --git a/src/install.zig b/src/install.zig new file mode 100644 index 0000000..7a52d44 --- /dev/null +++ b/src/install.zig | |||
| @@ -0,0 +1,101 @@ | |||
| 1 | const libarchive = @import("libarchive"); | ||
| 2 | const curl = @import("curl"); | ||
| 3 | const std = @import("std"); | ||
| 4 | const xdg = @import("xdg"); | ||
| 5 | const zup = @import("zup"); | ||
| 6 | |||
| 7 | const Allocator = std.mem.Allocator; | ||
| 8 | const ArchiveRead = libarchive.Read; | ||
| 9 | const Installation = zup.Installation; | ||
| 10 | const Installations = zup.Installations; | ||
| 11 | |||
| 12 | pub const params = | ||
| 13 | \\-f, --force Install over existing installations | ||
| 14 | \\<NAME> | ||
| 15 | ; | ||
| 16 | pub const description = "Installs a Zig version. Run `zup list -i` to see installed <NAME>s."; | ||
| 17 | pub const min_args = 1; | ||
| 18 | pub const max_args = 1; | ||
| 19 | |||
| 20 | pub fn main(comptime Result: type, allocator: Allocator, res: Result) !void { | ||
| 21 | var available = try Installation.getAvailableList(allocator); | ||
| 22 | defer Installation.deinitMap(allocator, &available); | ||
| 23 | |||
| 24 | return perform(allocator, res.positionals[0], res.args.force, available); | ||
| 25 | } | ||
| 26 | |||
| 27 | pub fn perform(allocator: Allocator, name: []const u8, force: bool, available: Installations) !void { | ||
| 28 | if (!force and try Installation.isInstalled(allocator, name)) { | ||
| 29 | std.log.err("{s} already installed, not overwriting without --force!", .{name}); | ||
| 30 | return error.AlreadyInstalled; | ||
| 31 | } | ||
| 32 | |||
| 33 | const installation = available.get(name) orelse { | ||
| 34 | std.log.err("Installation by name {s} not available!", .{name}); | ||
| 35 | return error.InstallationNotFound; | ||
| 36 | }; | ||
| 37 | |||
| 38 | if (installation.url == null) { | ||
| 39 | std.log.err("No tarball URL for {s} found!", .{name}); | ||
| 40 | } | ||
| 41 | |||
| 42 | const installation_dir = blk: { | ||
| 43 | const data_home = try xdg.getDataHome(allocator, "zup"); | ||
| 44 | defer allocator.free(data_home); | ||
| 45 | |||
| 46 | break :blk try std.fs.path.join(allocator, &.{ data_home, name }); | ||
| 47 | }; | ||
| 48 | defer allocator.free(installation_dir); | ||
| 49 | |||
| 50 | std.log.info("Installing {s}, version {}", .{ name, installation.version }); | ||
| 51 | const filename = std.fs.path.basename(installation.url.?); | ||
| 52 | |||
| 53 | // TODO: Platform-agnostic tempfile creation | ||
| 54 | var tmpname = try std.fmt.allocPrintZ(allocator, "/tmp/{s}", .{filename}); | ||
| 55 | defer allocator.free(tmpname); | ||
| 56 | |||
| 57 | var tmpdir = try std.fs.openDirAbsolute(std.fs.path.dirname(tmpname).?, .{}); | ||
| 58 | defer tmpdir.close(); | ||
| 59 | |||
| 60 | var tmpfile = try tmpdir.createFile(filename, .{}); | ||
| 61 | defer { | ||
| 62 | tmpfile.close(); | ||
| 63 | std.log.info("Deleting /tmp/{s}...", .{filename}); | ||
| 64 | tmpdir.deleteFile(filename) catch |err| { | ||
| 65 | std.log.warn("Failed to delete /tmp/{s}: {}", .{ filename, err }); | ||
| 66 | }; | ||
| 67 | } | ||
| 68 | |||
| 69 | std.log.info("Downloading to /tmp/{s}...", .{filename}); | ||
| 70 | try curl.easyDownloadToFile(&tmpfile, installation.url.?); | ||
| 71 | |||
| 72 | std.log.info("Extracting...", .{}); | ||
| 73 | var archive = try ArchiveRead.init(); | ||
| 74 | defer archive.deinit(); | ||
| 75 | |||
| 76 | try archive.supportFilter(.all); | ||
| 77 | try archive.supportFormat(.all); | ||
| 78 | try archive.openFilename(tmpname, 10240); | ||
| 79 | |||
| 80 | while (try archive.nextHeader()) |*entry| { | ||
| 81 | const source = entry.pathname(); | ||
| 82 | const dest = try preparePathname(allocator, installation_dir, source); | ||
| 83 | defer allocator.free(dest); | ||
| 84 | |||
| 85 | entry.setPathname(dest); | ||
| 86 | try archive.extract(entry.*, 0); | ||
| 87 | } | ||
| 88 | |||
| 89 | std.log.info("Installed to {s}", .{installation_dir}); | ||
| 90 | // TODO: Check if it is already active | ||
| 91 | std.log.info("If you want to use this installation, run `zup switch {s}`", .{name}); | ||
| 92 | } | ||
| 93 | |||
| 94 | fn preparePathname(allocator: Allocator, installation_dir: []const u8, source: []const u8) ![:0]u8 { | ||
| 95 | const stripped = if (std.mem.indexOfScalar(u8, source, '/')) |idx| | ||
| 96 | source[idx + 1 ..] | ||
| 97 | else | ||
| 98 | ""; | ||
| 99 | |||
| 100 | return std.fs.path.joinZ(allocator, &.{ installation_dir, stripped }); | ||
| 101 | } | ||