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

Add ZIGUP_INSTALL_PATH environment variable #146

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

ve-nt
Copy link

@ve-nt ve-nt commented Jul 27, 2024

Added for convenience so that a user can just set this in their RC file instead of setting an alias. It is overrided by the --install-dir flag.

Added for convenience so that a user can just set this in their RC
file instead of setting an alias. It is overrided by the --install-dir
flag.
@sweetbbak
Copy link

sweetbbak commented Jan 3, 2025

Id love to see this merged. It's generally bad practice to put these files in the $HOME directory despite it happening quite often. Theres even a tool that "fixes" this called https://github.com/b3nj5m1n/xdg-ninja with 2k stars and being updated constantly with 100s of CLI tools added that make this mistake, as well as another called "boxxy" that forces config and cache files to respect XDG spec by using containers. Its safe to say that this is an important thing to many users.

This library abstracts this https://github.com/ziglibs/known-folders a bit, but generally the order of precedence goes as follows:

  • CLI tool flag is explicitly passed
  • CLI tool specifc env var (ZIGUP_DIR)
  • XDG_CACHE_DIR + CLI_TOOL_NAME
  • HOME + ".cache" + CLI_TOOL_NAME
  • HOME + CLI_TOOL_NAME

I wrote an example function here of how to correctly handle this for all operating systems, let me know if anyone's serious about accepting the PR and I will flesh this out further and make a new PR if needed.

const std = @import("std");
const os = std.os;
const path = std.fs.path;
const Allocator = std.mem.Allocator;
const builtin = @import("builtin");
const testing = std.testing;
const getenv = std.c.getenv;

pub fn UserCacheDir(allocator: Allocator) ![]const u8 {
    switch (builtin.os.tag) {
        .windows => {
            var xdg_dir: ?[]const u8 = null;
            xdg_dir = std.process.getEnvVarOwned(allocator, "LocalAppData") catch null;
            if (xdg_dir) |dir| {
                return dir;
            }

            return error.CacheDirNotFound;
        },
        .macos, .ios => {
            var xdg_dir: ?[]const u8 = null;

            xdg_dir = try std.process.getEnvVarOwned(allocator, "HOME");
            if (xdg_dir) |dir| {
                return try path.join(allocator, &[_][]const u8{ dir, "Library", "Caches" });
            }

            return error.CacheDirNotFound;
        },
        .plan9 => {
            var xdg_dir: ?[]const u8 = null;

            xdg_dir = try std.process.getEnvVarOwned(allocator, "home");
            if (xdg_dir) |dir| {
                return try path.join(allocator, &[_][]const u8{ dir, "lib", "cache" });
            }

            return error.CacheDirNotFound;
        },
        // Unix like operating systems
        else => {
            var xdg_dir: ?[]const u8 = null;

            xdg_dir = std.process.getEnvVarOwned(allocator, "XDG_CACHE_HOME") catch null;
            if (xdg_dir) |dir| {
                return dir;
            }

            xdg_dir = try std.process.getEnvVarOwned(allocator, "HOME");
            if (xdg_dir) |dir| {
                return try path.join(allocator, &[_][]const u8{ dir, ".cache" });
            }

            return error.CacheDirNotFound;
        },
    }
}

obviously could use a little better error handling, but I want to gauge whether it is aligned with the project or what the general consensus is here before I do anything else.

@marler8997
Copy link
Owner

I've shared my reservations in adding configuration via environment variables here: ziglang/zig#10754 (comment)

I've seen people repeat that it's "generally bad practice" to put things in the $HOME directory but I've never had someone explain what problems this causes. The cases where I imagine it could be a problem would be if zigup is being included in some sort of package database like the AUR, in which case I'd expect different package managers to prefer different directories, and is why I've suggested adding a build-time option to set this to different locations, but I've yet to see someone actually submit a PR for this.

I'm open to being persuaded/convinced otherwise on these points though, just haven't seen any good counterarguments yet.

@sweetbbak
Copy link

sweetbbak commented Jan 3, 2025

An apt comparison for Windows would be like if Node, Go, Zig, Rust, Java, Android Studio, etc... all insisted on putting their cache, config and state folders and files on your Desktop, and moving those files anywhere else would irreparably break those tools, and if you deleted them, they would just be re-created.

Sure, maybe one isn't that annoying, but a ton of tools have done this prior to the xdg-basedir-spec (and many still do) and they add up to a ridiculous amount, which this spec was created to address exactly this problem (same story as hidden files/dirs that are prefixed with a "." which was a previous attempt to address this issue, which also became untenable) Often times you don't even know where these files came from or what they do, or what repercussions deleting it would have... on top of the clutter this causes.

or another analogy, would be someone else using your room as a storage unit instead of the closet because the closet is a little further back. As one somewhat dramatically put it in one of the links below:

I like how Filip from 0x46.net explained the issue from the perspective of a user:

My own home directory contains 25 ordinary files and 144 hidden files. The dotfiles contain data that doesn't belong to me: it belongs to the programmers whose programs decided to hijack the primary location designed as a storage for my personal files. I can't place those dotfiles anywhere else and they will appear again if I try to delete them. All I can do is sit here knowing that in the darkness, behind the scenes, they are there. . . . I dread the day in which I will hear a loud knock on my door and one of those programmers will barge in informing me that he is going to store a piece of his furniture in the middle of my living room, If I don't mind.

some writings on the matter:

and then on the other side of this, I don't really seeing it being a hinder to anyone, especially since this also lets everyone place this folder wherever they want it to reside. You could even leave the default as $HOME if you are really set on it, or throw it in a build option, or both. On Mac and Windows its not really an issue anyways since HOME doesn't really serve the same purpose as it does on Linux. Lastly, one of the reasons this tool can feasibly exist, is because upstream Zig does not force a hard coded path, and takes a similar approach by making all paths relative to the executable.

I'd say that this PR is a minimal way to address this. Its a pretty straightforward and simple change without making any structural or significant changes to the tool, and most people could just ignore it if they wanted to, but as I pointed out with tools like xdg-ninja and boxxy, and the creation of a literal xdg spec its a pretty significant issue that a lot of people put a ton of time into fixing.

edit: and to tack on to this, I think what you highlighted in the issue that you linked is a compromise already being made by having a CLI flag that overrides this. I also don't think the place where a folder lives is the same compromise as running some obscure libc or ld_preload hack where the stakes are a lot higher and things like env vars actually do make things more complex. At the very least, it would be nice for the default (or a build option) to allow for conformance with the XDG spec. I don't know of a single Linux system that doesn't follow it, and that could be a feasible alternative solution if you were really firm on the no environment variables thing.

@marler8997
Copy link
Owner

marler8997 commented Jan 3, 2025

To preface this, I'm a big proponent of questioning why we do things the way we do so that we can learn/grow and improve. I try to understand they "why" behind what we call "good practices" so I can evaluate whether they actually improve things on a case-by-case basis.

I've skimmed/read through some of the material you've referenced (thanks for compiling those). The overall impression I get is this XDG standard allows you to understand a bit more about the files that programs are accessing (i.e. is it configuration, data, state, cache, etc) which gives the user/system some control over them (i.e. which filesystems to use for each and/or which permissions to grant and/or whether to share those files across a network, etc). This seems particularly useful for files that are "internal" to programs that the user isn't necessarily suppose to interact with. Now if we consider zigup, it's purpose is to fetch and store multiple versions of the zig compiler somewhere on the filesystem and provide access to those files to the user. These files aren't strictly "internal" to zigup, they are meant to be exposed to the user. This install directory doesn't seem to fit into any of the XDG categories I saw listed, I think the closest match might be "cache" but still doesn't seem to fit. Please let me know if this isn't the case...maybe there is a perfect location for zigup's install directory that has eluded me (i.e. is there a standard location to put user programs/applications)? However, if my summary is accurate, I think a case can be made for setting zigup's install directory in $HOME by default. Furthermore, if zigup had any configuration/data/cache files/etc, I believe putting those files in one of these XDG locations absolutely makes sense. In fact, if/when I get around to implementing #135 zigup will no longer have an install directory and I'll likely need to add some sort of metadata files which would definitely go somewhere based on the XDG spec, and the compilers themselves will go into zig's global cache so all this actually becomes moot.

To expand on this a bit, one way to think of zigup is as a package manager...a very minimal one that only manages one package with multiple versions :) Package managers will typically install packages to various global hardcoded paths on the system depending on the package and/or distribution. Selecting a default hardcoded global path like $HOME/zig seems consistent with what you might expect a package manager to do in my opinion.

@ve-nt
Copy link
Author

ve-nt commented Jan 3, 2025

For my two cents I care about keeping a clean and organized home directory, and find software that automatically creates unhidden files/directories in my $HOME to be rather annoying. I have to go spend time working out how to move them, which is different for each program, and this is a task I feel I shouldn't have to do when XDG exists. Catch me on a bad day and I'll liken it to littering.

Consider also that package managers may invoke zigup as a dependency, and so this $HOME/zig directory suddenly appears after installing some program you didnt even know (or care) was written in Zig. For cases like this the user may not even know what Zig is. This happens with Go, Python and Java programs all the time with their default settings.

For this I propose that zigup file itself away somewhere by default. On Linux at least I can suggest $XDG_DATA_HOME/zigup or $HOME/.local/share/zigup. And ensure we print the path we're installing it to so the user is aware of where their installation files are. If the user wants their $HOME/zig then they can explicitly ask for it via configuration.

I do not mind this being configured via an environment variable, configuration file, or any other means. The only option I am not a fan of is having to create an alias, as that feels hacky to me because it depends on my shell configuration which may change from shell-to-shell and is less universal than an envvar.

I've read and sympathized with your thoughts on environment variables @marler8997, and IMO an envvar is fine for controlling this specific use case as the configuration is so minimal. We should print out the install path for logging purposes though, and ensure that CLI arguments always override envvars.

Alternatively a config file would work just as well and would scale better if more config is added in the future, but may be a bit funny if there's only one configuration to set. For this option I can suggest a ZON file in $XDG_CONFIG_HOME/zigup.zon

@sweetbbak
Copy link

sweetbbak commented Jan 3, 2025

I've skimmed/read through some of the material you've referenced (thanks for compiling those). The overall impression I get is this XDG standard allows you to understand a bit more about the files that programs are accessing (i.e. is it configuration, data, state, cache, etc) which gives the user/system some control over them (i.e. which filesystems to use for each and/or which permissions to grant and/or whether to share those files across a network, etc).

This, and, stops a ton of clutter in the HOME directory. Which is arguably the primary reason XDG and dotfiles exist. If I run ls -1 | wc -l in my xdg dirs, I get over 500 files (not including cache) thats 500 files and directories that would insist on living in my home directory. That is an absurd amount of files, there is no real reason this needs to be the case.

This seems particularly useful for files that are "internal" to programs that the user isn't necessarily suppose to interact with. Now if we consider zigup, it's purpose is to fetch and store multiple versions of the zig compiler somewhere on the filesystem and provide access to those files to the user. These files aren't strictly "internal" to zigup, they are meant to be exposed to the user.

I don't see how allowing the user to move these files out of the $HOME directory makes them not exposed to the user. I also have to imagine most people are just adding this directory (wherever it may be) to the PATH (another env var here we heavily rely on) I am also confused here, from the issue you linked, you want to move the Zig libs and executable into the zig global cache (which ironically lives in $XDG_CACHE_HOME lol) but I don't see how this point would also not apply.

This install directory doesn't seem to fit into any of the XDG categories I saw listed, I think the closest match might be "cache" but still doesn't seem to fit. Please let me know if this isn't the case...maybe there is a perfect location for zigup's install directory that has eluded me (i.e. is there a standard location to put user programs/applications)?

The directory for storing files of this kind would be $XDG_DATA_HOME which generally equates to ~/.local/share which is where program data files go, which is how people classify this. This is where pipx, virtualenv, OCI containers, user systemd timers, user Go etc... This distinction to make is purely up to the developer, if not configurable. At the very least, a lot of legacy programs who were created prior to the xdg spec, and are fearful of moving them by default, have opted for an environment variable so the user can handle it themselves.

XDG_DATA_HOME
    Where user-specific data files should be written (analogous to /usr/share).
    Should default to $HOME/.local/share.

To expand on this a bit, one way to think of zigup is as a package manager...a very minimal one that only manages one package with multiple versions :) Package managers will typically install packages to various global hardcoded paths on the system depending on the package and/or distribution. Selecting a default hardcoded global path like $HOME/zig seems consistent with what you might expect a package manager to do in my opinion.

IMO this is also bad practice for the exact same reasons. In fact, Zig itself doesn't store packages in $HOME either, it stores them in the cache directory. This is where cache files go, and it should be known that people should be able to delete these files at any time. Which Zig does abide by. It doesn't put packages haphazardly in the $HOME directory.

Please help me understand what the resistance to this is? I'm not sure how the issue you linked originally, directly applies to this project. Especially when we are already relying on CLI flags that feasibly do the same thing and other env vars (PATH and HOME) as well as upstream Zig itself respecting the XDG base dir spec... It is your project obviously, and you can do whatever you'd like, but this a pretty hard line for me.

@marler8997
Copy link
Owner

marler8997 commented Jan 4, 2025

Having alot of files in the home directory doesn't seem like a problem to me, but I'm willing to hear you out on what problems this causes. Moving files from one place to another doesn't really seem like it's solving anything...all the files are still there, and adding an extra heirarchy makes some things easier and some things harder, so I'm not sure it's a net benefit.

Enabling more use cases/control/uniformity seem like the main benefits of XDG which I can get behind.

I don't see how allowing the user to move these files out of the $HOME directory makes them not exposed to the user. I also have to imagine most people are just adding this directory (wherever it may be) to the PATH (another env var here we heavily rely on) I am also confused here, from the issue you linked, you want to move the Zig libs and executable into the zig global cache (which ironically lives in $XDG_CACHE_HOME lol) but I don't see how this point would also not apply.

Yes XDG_CACHE_HOME is a perfect example of a good time to use the XDG standard. I'm not resistant to using the XDG standard, I'm just not sure XDG has any place that's a good fit for zigup's install directory.

The directory for storing files of this kind would be $XDG_DATA_HOME which generally equates to ~/.local/share which is where program data files go, which is how people classify this.

I'm not so sure. If you were installing zig via a package manager, I would expect zig to go into a directory in the PATH such as /usr/bin, not /usr/share. I'm not sure I would classify the various zig versions as "data files"...I would classify them as their own programs/applications, which would typically be split amongst multiple system directories. My understanding is XDG leaves programs/applications up to the distribution, hence why I mention that zigup is sort of like it's own package manager and XDG may not have a good place to put a fully hermetic toolchain like zig.

I appreciate some of the points you've brought up and think it's good to exchange perspectives/ideas, which is why I asked for it, however, there's not much point in trying to convince me to move the install directory or add an environment variable to configure it as it's likely going to go away soon. If/when I update zigup to use zig's global cache, compiler's will be stored in XDG_CACHE_HOME like you say. There's also the "exe path link" which is separate from the install directory, and the current mechanism for placing that is to put it in the same directory as zigup. I mention this because you said people might add the install directory to PATH which is incompatible with how zigup works. Checkout the README for a quick explanation that might clear up a few things: https://github.com/marler8997/zigup?tab=readme-ov-file#how-the-compilers-are-managed

@sweetbbak
Copy link

I feel like I've already written a lengthy explanation on why this is not the best approach, how it could lead to potential issues, and why it is not considerate use of the user's space. I’d like to think users are capable enough to decide where their files go. Personally, I find the idea of dumping compiler files in the home directory a bit... unconventional, but hey, it’s your choice. A 'no' for any reason is totally fine. I'm not here to rehash the XDG spec, so I’ll leave it at that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants