Getting local path in flake

Is there anyway for a flake to get it’s local path? I know this breaks the intended deterministic properties of flakes but what about with the --impure flag?

Goal

I’m writing a wrapper for Matlab to create isolated environments, analogous to rWrapper or python.withPackages. Packages are built by copying the source code to $out/packages then symlinkJoin is used to link each package directory in the nix store to the wrapper’s $out/packages.

Problem

This works but for developing new packages, I would like to have the package set up as a flake and pass a Matlab environment with it’s dependencies and itself to devShell. By default, Matlab will watch for changes to source code and automatically reload functions that have changed. But since the version of the package Matlab sees is actually a copy of the source code in the nix store, it doesn’t notice changes and would require a rebuild and restart of Matlab anytime changes are made.

Solution

As part of the solution, in addition to the packages attribute in the Matlab wrapper, I’ve added an optional devPackages list. This list accepts the local path of the package and creates a symlink in the wrapper that points to the local package. But I cannot get the local path to the flake to pass directly into the Matlab wrapper. Instead, I have to hard code the full path, which reduces portability.

Looking at https://discourse.nixos.org/t/how-to-refer-to-current-directory-in-shell-nix/9526 and https://github.com/nix-community/home-manager/issues/2660 it sounds like it is not possible. Using the --impure flag seems like it should work but based on “Flakes are also copied to the Nix store, so the relative directory is always relative to the store.” builtins.toString ./. will always return a path in under /nix/store and my own attempts verify that. Without the --impure flag I get a double hashed path and with it I get a single hashed path. So it seems like without the flag, the flake copy to the store before being evaluated then copies the contents of itself to the nix store with using the previous hash as the base name and prepending a second hash onto that. Which seems like odd behavior.

Is there anyway to get around this?

1 Like

Did you ever figure something out for this? I’m trying to figure out how to set an environment variable based on the project directory in devShell in a flake.nix.

I’m not 100% certain if op was looking for this, but the self input (that is always implicitly passed to a flake) refers to the directory of the flake.

So to get the flake.nix file to refer to itself in a string you can use "${self}/flake.nix".

This will point to its copy in the nix store, though, because that’s how flakes work. I think op didn’t want that in this case, but I don’t understand the idea well enough for lack of MATLAB admin experience.

No, I don’t think it’s possible due to wanting to ensure flakes are completely reproducible. But again think reducing the restriction purely for the development stage would make things much easier.

@TLATER, self seemed promising but as you said it refers to the path in /nix/store/ where the flake is copied to. I want to be able to create a wrapper around MATLAB so only the builtins + passed package’s are on MATLAB’s path. Same as with python.withPackages or using R with specific packages. But I would like to also be able to pass local versions of packages so I can confirm everything is working before pushing commits and without needing to rebuild and reload MATLAB after every changes. I have the same issues with working in python but at least python has the PYTHONPATH variable that I can add . to which is a reasonably good hack work around so that tests can actually find the current package.

1 Like

Let’s put it this way, if there was ever discovered a way to do this, it would probably be patched as it breaks the intention of pure evaluation. That said, if you really need something like this, the closest you could get is to put it in a well known location on the filesystem.

This is only in the case for pure evaluation though, and only during evaluation. There is nothing stopping you from setting an environmental variable in the shellHook to ROOT_PATH=$(git rev-parse --show-toplevel), since this happens outside of the context of a build, or you could pass --impure if you really need it during evaluation, and use builtins.getEnv which is only available in impure eval, but this breaks a lot of nice guarantees and I would discourage it unless absolutely necessary.

Not sure if it helps, but as I said earlier, it is also possible to refer to and even import well known directories inside pure evaluation with builtins.path and builtins.fetchurl with a file:// url, so if placing the flake in some well known directly would solve the problem, that’s another option.

2 Likes

Using the shell hook is a good idea, and I’m not particularly concerned about hermetic purity of the dev environment. Using well-known directories and assigning environment variables based on the flake name is another potential solution, although it might be a bit impure if we need to create those directories.

The use case for me is wanting to set CARGO_HOME for rust projects to a unique .cargo directory (currently within the project directory and gitignored), to avoid mixing with any global cargo stuff the user may have installed. Either the shell hook or the well-known directory option should work well for that. Thanks for the reply!