Help: Collision between packages after upgrading nix

After recently updating my nix installation as per the manual I’m now facing a strange collision between myPackages and jdk11 (specifically), though it’s likely a general change in behaviour I don’t understand.

% nix-channel --update; nix-env -iA nixpkgs.nix nixpkgs.cacert
% nix-shell -p nix-info --run "nix-info -m"
 - system: `"x86_64-darwin"`
 - host os: `Darwin 20.3.0, macOS 10.16`
 - multi-user?: `no`
 - sandbox: `no`
 - version: `nix-env (Nix) 2.3.10`
 - channels(ldeck): `"nixpkgs-21.05pre281594.6fc2b7ecc2a"`
 - nixpkgs: `/Users/ldeck/.nix-defexpr/channels/nixpkgs`

% nix-env -iA nixpkgs.myPackages
replacing old 'my-packages'
installing 'my-packages'
these derivations will be built:
  /nix/store/azz5r490c3pi4ppfcl77x4wdz185p9hr-my-packages.drv
building '/nix/store/azz5r490c3pi4ppfcl77x4wdz185p9hr-my-packages.drv'...
Use of uninitialized value $stat2 in numeric ne (!=) at /nix/store/vg1f180bvkk5ijmcfwflp8cwnf59al1r-builder.pl line 94.
Use of uninitialized value $stat1 in numeric ne (!=) at /nix/store/vg1f180bvkk5ijmcfwflp8cwnf59al1r-builder.pl line 94.
collision between `/nix/store/m2b1pdx0wf00sv01xw1shy5nscmk8fyl-zulu15.28.51-ca-jdk-15.0.1/share/man' and `'
builder for '/nix/store/azz5r490c3pi4ppfcl77x4wdz185p9hr-my-packages.drv' failed with exit code 2
error: build of '/nix/store/azz5r490c3pi4ppfcl77x4wdz185p9hr-my-packages.drv' failed

My single user config looks like this (and specifically available here, though I’ve not pushed the latest changes yet as it’s broken):

{
  allowUnfree = true;
  packageOverrides = pkgs: with pkgs; rec {
    ...

    java_default = jdk15_headless;

    java_home = pkgs.writeShellScriptBin "java_home" ''
      if [ "$#" -ne 2 ] || [ "$1" != "-v" ] || [ "$2" -lt 8 ]; then
        echo "Usage: $0 -v <version>";
        exit 1;
      fi
      case "$2" in
        8)
          JDK="${jdk8}"
          ;;
        *)
          JDK="${jdk11}"
          ;;
        esac
        echo "$JDK"
    '';

    myPackages = buildEnv {
      name = "my-packages";
      paths = [
        (runCommand "profile" {} ''
          mkdir -p $out/etc/profile.d
          cp ${myProfile} $out/etc/profile.d/my-profile.sh
      '')
      ...
      java_default
      java_home
      ...
    ];
    pathsToLink = [ "/share/man" "/share/doc" "/bin" "/etc" "/Applications" ];
    extraOutputsToInstall = [ "man" "doc" ];
  };
}

This used to update happily, so not sure what changed.
Is it a question of setting the priority, somehow, for one of the packages?
They seem to be attempting to install the same man page path.

Okay, it turns out that installing multiple versions of the jdk via nix can lead to such a conflict because the executables, man pages, etc want to occupy the same space.

As mentioned, my previous config wasn’t failing on this. So something’s changed under the hood.

So I’ve done the following for now — but if there’s a better way I’m all ears.

  1. removed jdk* from myPackages.paths
  2. add a variable to define the java_default additional relative path e.g.,
    java_default = jdk15_headless;
    java_default_relpath = "zulu-15.jdk/Contents/Home";
    
  3. updated myProfile to export JAVA_HOME pointing to the java_default home dir
    myProfile = pkgs.writeText "my-profile" ''
      export JAVA_HOME=${java_default}/${java_default_relpath}
    '';
    
  4. updated myProfile to prefix PATH and MANPATH with the java_default bin and man dirs respectively.
    export PATH=''${JAVA_HOME}/bin:$PATH
    export MANPATH=''${JAVA_HOME}/man:$MANPATH
    

That works for now, it just means ~/.nix-profile/bin/java etc aren’t set.

myProfile = pkgs.writeText "my-profile" ''
      ...
      export PATH=$HOME/.nix-profile/bin:/nix/var/nix/profiles/default/bin:/sbin:/bin:/usr/sbin:/usr/bin
      export MANPATH=$HOME/.nix-profile/share/man:/nix/var/nix/profiles/default/share/man:/usr/share/man

      export JAVA_HOME=${java_default}/${java_default_relpath}

      export PATH=''${JAVA_HOME}/bin:$PATH
      export MANPATH=''${JAVA_HOME}/man:$MANPATH

      ...
    '';

If the different Java versions are for projects, I’d just have a shell.nix in each project that specifies the Java version to use there versus having multiple installed to the system. Or just ad-hoc when you want a different version, run nix-shell -p zulu15 or whatever version you need.

Yeah, I suppose, that would in a nix-only world be the way to go @austin.
Not many in my company use nix, but direnv is often used.

So I’m using a use java x function from ~/.direnvrc to allow this to work. For me it just happens to use nix under the hood like so:

# Usage:
#   Add to .envrc file:
#     use java 11
use_java() {
    JAVA_HOME=$(~/.nix-profile/bin/java_home -v $1)
    export JAVA_HOME="$JAVA_HOME"
    echo "JAVA_HOME=$JAVA_HOME"
    load_prefix "$JAVA_HOME"
    PATH_add "$JAVA_HOME/bin"
}