Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use the known-folders package to find install dir #139

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,13 @@ zigup run <version> <args>...

# 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 a global "install directory" in a versioned subdirectory. The "install directory" is a subdirectory called `zigup` of a platform-specific directory. The platform-specific parent directories are:

- `XDG_DATA_HOME` on Linux—*i.e.* `$XDG_DATA_HOME` with a default of `~/.local/share`,
- `~/Library/Application Support/zigup` on macOS,
- `%APPDATA/zigup` on Windows.

Using the build option `-Ddefault-dir=[DIR]` changes the install directory so that on posix systems the install directory is `$HOME/[DIR]` and on windows the install directory will be a directory named `[DIR]` in the same directory as the "zigup.exe".

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.

Expand Down
37 changes: 33 additions & 4 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,27 @@ pub fn build(b: *std.Build) !void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});

const default_dir = b.option(
[]const u8,
"default-dir",
"Use a static default install directory",
);

const build_options = b.addOptions();
build_options.addOption(bool, "use_known_folders", default_dir == null);
if (default_dir) |dir| {
build_options.addOption([]const u8, "default_dir", dir);
}

const known_folders = if (default_dir != null)
null
else if (b.lazyDependency("known_folders", .{})) |pkg|
pkg.module("known-folders")
else
null;

const zigup_exe_native = blk: {
const exe = addZigupExe(b, target, optimize);
const exe = addZigupExe(b, target, optimize, build_options, known_folders);
b.installArtifact(exe);
const run_cmd = b.addRunArtifact(exe);
run_cmd.step.dependOn(b.getInstallStep());
Expand Down Expand Up @@ -74,13 +93,15 @@ pub fn build(b: *std.Build) !void {
ci_step.dependOn(test_step);
ci_step.dependOn(unzip_step);
ci_step.dependOn(zip_step);
try ci(b, ci_step, test_step, host_zip_exe);
try ci(b, ci_step, test_step, host_zip_exe, build_options, known_folders);
}

fn addZigupExe(
b: *std.Build,
target: std.Build.ResolvedTarget,
optimize: std.builtin.Mode,
build_options: *std.Build.Step.Options,
known_folders: ?*std.Build.Module,
) *std.Build.Step.Compile {
const win32exelink_mod: ?*std.Build.Module = blk: {
if (target.result.os.tag == .windows) {
Expand All @@ -104,6 +125,12 @@ fn addZigupExe(
.optimize = optimize,
});

exe.root_module.addOptions("build-options", build_options);

if (known_folders) |mod| {
exe.root_module.addImport("known-folders", mod);
}

if (target.result.os.tag == .windows) {
exe.root_module.addImport("win32exelink", win32exelink_mod.?);
}
Expand All @@ -115,6 +142,8 @@ fn ci(
ci_step: *std.Build.Step,
test_step: *std.Build.Step,
host_zip_exe: *std.Build.Step.Compile,
build_options: *std.Build.Step.Options,
known_folders: ?*std.Build.Module,
) !void {
const ci_targets = [_][]const u8{
"x86_64-linux",
Expand All @@ -134,13 +163,13 @@ fn ci(
var previous_test_step = test_step;

for (ci_targets) |ci_target_str| {
const target = b.resolveTargetQuery(try std.zig.CrossTarget.parse(
const target = b.resolveTargetQuery(try std.Target.Query.parse(
.{ .arch_os_abi = ci_target_str },
));
const optimize: std.builtin.OptimizeMode =
// Compile in ReleaseSafe on Windows for faster extraction
if (target.result.os.tag == .windows) .ReleaseSafe else .Debug;
const zigup_exe = addZigupExe(b, target, optimize);
const zigup_exe = addZigupExe(b, target, optimize, build_options, known_folders);
const zigup_exe_install = b.addInstallArtifact(zigup_exe, .{
.dest_dir = .{ .override = .{ .custom = ci_target_str } },
});
Expand Down
7 changes: 7 additions & 0 deletions build.zig.zon
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
.{
.name = "zigup",
.version = "0.0.1",
.dependencies = .{
.known_folders = .{
.url = "https://github.com/ziglibs/known-folders/archive/47076c6b11214a218e9244471d8762310820911a.tar.gz",
.hash = "12209d2738a2e1dbd3781c2e5f01a2ea877dcfeea53efdfa1913247297d328e6b207",
.lazy = true,
},
},

.paths = .{
"LICENSE",
Expand Down
38 changes: 25 additions & 13 deletions zigup.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ const std = @import("std");
const builtin = @import("builtin");
const mem = std.mem;

const build_options = @import("build-options");
const known_folders = @import("known-folders");

const ArrayList = std.ArrayList;
const Allocator = mem.Allocator;

Expand Down Expand Up @@ -126,19 +129,28 @@ fn getHomeDir() ![]const u8 {
}

fn allocInstallDirString(allocator: Allocator) ![]const u8 {
if (build_options.use_known_folders) {
if (try known_folders.getPath(allocator, .data)) |path| {
defer allocator.free(path);
return std.fs.path.join(allocator, &.{ path, "zigup" });
} else {
std.log.err("Could not find install directory", .{});
return error.AlreadyReported;
}
}
// 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" });
return std.fs.path.join(allocator, &.{ self_exe_dir, build_options.default_dir });
}
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" });
return std.fs.path.join(allocator, &[_][]const u8{ home, build_options.default_dir });
}
const GetInstallDirOptions = struct {
create: bool,
Expand Down Expand Up @@ -501,7 +513,7 @@ pub fn loggySymlinkAbsolute(target_path: []const u8, sym_link_path: []const u8,

/// returns: true if the symlink was updated, false if it was already set to the given `target_path`
pub fn loggyUpdateSymlink(target_path: []const u8, sym_link_path: []const u8, flags: std.fs.Dir.SymLinkFlags) !bool {
var current_target_path_buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
var current_target_path_buffer: [std.fs.max_path_bytes]u8 = undefined;
if (std.fs.readLinkAbsolute(sym_link_path, &current_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 });
Expand Down Expand Up @@ -631,7 +643,7 @@ fn cleanCompilers(allocator: Allocator, compiler_name_opt: ?[]const u8) !void {
}
}
}
fn readDefaultCompiler(allocator: Allocator, buffer: *[std.fs.MAX_PATH_BYTES + 1]u8) !?[]const u8 {
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);

Expand All @@ -651,7 +663,7 @@ fn readDefaultCompiler(allocator: Allocator, buffer: *[std.fs.MAX_PATH_BYTES + 1
return try allocator.dupe(u8, targetPathToVersion(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(path_link, buffer[0..std.fs.max_path_bytes]) catch |e| switch (e) {
error.FileNotFound => return null,
else => return e,
};
Expand All @@ -662,7 +674,7 @@ 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(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,
Expand All @@ -678,15 +690,15 @@ fn readMasterDir(buffer: *[std.fs.MAX_PATH_BYTES]u8, install_dir: *std.fs.Dir) !
}

fn getDefaultCompiler(allocator: Allocator) !?[]const u8 {
var buffer: [std.fs.MAX_PATH_BYTES + 1]u8 = undefined;
var buffer: [std.fs.max_path_bytes + 1]u8 = undefined;
const slice_path = (try readDefaultCompiler(allocator, &buffer)) orelse return null;
const path_to_return = try allocator.alloc(u8, slice_path.len);
@memcpy(path_to_return, slice_path);
return path_to_return;
}

fn getMasterDir(allocator: Allocator, install_dir: *std.fs.Dir) !?[]const u8 {
var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
var buffer: [std.fs.max_path_bytes]u8 = undefined;
const slice_path = (try readMasterDir(&buffer, install_dir)) orelse return null;
const path_to_return = try allocator.alloc(u8, slice_path.len);
@memcpy(path_to_return, slice_path);
Expand Down Expand Up @@ -773,7 +785,7 @@ fn verifyPathLink(allocator: Allocator, path_link: []const u8) !void {
break :blk "";
};

var path_it = std.mem.tokenize(u8, path_env, ";");
var path_it = std.mem.tokenizeScalar(u8, path_env, ';');
while (path_it.next()) |path| {
switch (try compareDir(path_link_dir_id, path)) {
.missing => continue,
Expand All @@ -789,7 +801,7 @@ fn verifyPathLink(allocator: Allocator, path_link: []const u8) !void {
try enforceNoZig(path_link, exe);
}

var ext_it = std.mem.tokenize(u8, pathext_env, ";");
var ext_it = std.mem.tokenizeScalar(u8, pathext_env, ';');
while (ext_it.next()) |ext| {
if (ext.len == 0) continue;
const basename = try std.mem.concat(allocator, u8, &.{ "zig", ext });
Expand All @@ -802,7 +814,7 @@ fn verifyPathLink(allocator: Allocator, path_link: []const u8) !void {
}
}
} else {
var path_it = std.mem.tokenize(u8, std.posix.getenv("PATH") orelse "", ":");
var path_it = std.mem.tokenizeScalar(u8, std.posix.getenv("PATH") orelse "", ':');
while (path_it.next()) |path| {
switch (try compareDir(path_link_dir_id, path)) {
.missing => continue,
Expand Down Expand Up @@ -924,8 +936,8 @@ const win32exelink = struct {
};
};
fn createExeLink(link_target: []const u8, path_link: []const u8) !void {
if (path_link.len > std.fs.MAX_PATH_BYTES) {
std.debug.print("Error: path_link (size {}) is too large (max {})\n", .{ path_link.len, std.fs.MAX_PATH_BYTES });
if (path_link.len > std.fs.max_path_bytes) {
std.debug.print("Error: path_link (size {}) is too large (max {})\n", .{ path_link.len, std.fs.max_path_bytes });
return error.AlreadyReported;
}
const file = std.fs.cwd().createFile(path_link, .{}) catch |err| switch (err) {
Expand Down