{ pkgs, tmpLib, lib, config, ... }: let homeFiles = lib.filterAttrs (name: conf: conf.enable) config.home.file; sourceStorePath = file: let sourcePath = toString file.source; sourceName = tmpLib.storeFileName (baseNameOf sourcePath); in if builtins.hasContext sourcePath then file.source else builtins.path { path = file.source; name = sourceName; }; in { options = { home = { user = lib.mkOption { type = lib.types.nullOr lib.types.str; default = null; }; group = lib.mkOption { type = lib.types.nullOr lib.types.str; default = null; }; homeDirectory = lib.mkOption { type = lib.types.str; default = "/home/${config.home.user}"; }; file = lib.mkOption { type = lib.types.attrsOf tmpLib.homeFileType; default = {}; }; packages = lib.mkOption { type = lib.types.listOf lib.types.package; default = []; }; homeFilesPackage = lib.mkOption { type = lib.types.package; }; path = lib.mkOption { type = lib.types.package; }; pathsToLink = lib.mkOption { type = lib.types.listOf lib.types.str; default = [ "/bin" "/share" ]; }; }; }; config = lib.mkIf (config.home.user != null) { # tmpfiles.selfManagement.targetDir = "${config.home.homeDirectory}/.local/share/user-tmpfiles.d"; tmpfiles.defaultUser = config.home.user; tmpfiles.defaultGroup = config.home.group; home.path = pkgs.buildEnv { name = "tmpactivator-profile"; paths = config.home.packages; pathsToLink = config.home.pathsToLink; }; tmpfiles.instances.home.files = [ { type = "L+"; mode = "-"; user = "-"; group = "-"; text = "${config.home.path}"; target = "${config.home.homeDirectory}/.tmpactivator-profile"; } ] ++ (lib.attrValues (lib.mapAttrs (name: conf: { type = "L+"; mode = "-"; user = "-"; group = "-"; text = "${config.home.homeFilesPackage}/${name}"; target = "${config.home.homeDirectory}/${name}"; }) homeFiles)) ++ (lib.attrValues (lib.mapAttrs (name: conf: { type = "r"; mode = "-"; user = "-"; group = "-"; text = "-"; target = "${config.home.homeDirectory}/${name}"; }) homeFiles)); # This is from home-manager, see # https://github.com/nix-community/home-manager/blob/6e090576c4824b16e8759ebca3958c5b09659ee8/modules/files.nix#L285 home.homeFilesPackage = pkgs.runCommandLocal "home-files" { nativeBuildInputs = [ pkgs.xorg.lndir ]; } ('' mkdir -p $out # Needed in case /nix is a symbolic link. realOut="$(realpath -m "$out")" function insertFile() { local source="$1" local relTarget="$2" local executable="$3" local recursive="$4" # If the target already exists then we have a collision. Note, this # should not happen due to the assertion found in the 'files' module. # We therefore simply log the conflict and otherwise ignore it, mainly # to make the `files-target-config` test work as expected. if [[ -e "$realOut/$relTarget" ]]; then echo "File conflict for file '$relTarget'" >&2 return fi # Figure out the real absolute path to the target. local target target="$(realpath -m "$realOut/$relTarget")" # Target path must be within $HOME. if [[ ! $target == $realOut* ]] ; then echo "Error installing file '$relTarget' outside \$HOME" >&2 exit 1 fi mkdir -p "$(dirname "$target")" if [[ -d $source ]]; then if [[ $recursive ]]; then mkdir -p "$target" lndir -silent "$source" "$target" else ln -s "$source" "$target" fi else [[ -x $source ]] && isExecutable=1 || isExecutable="" # Link the file into the home file directory if possible, # i.e., if the executable bit of the source is the same we # expect for the target. Otherwise, we copy the file and # set the executable bit to the expected value. if [[ $executable == inherit || $isExecutable == $executable ]]; then ln -s "$source" "$target" else cp "$source" "$target" if [[ $executable == inherit ]]; then # Don't change file mode if it should match the source. : elif [[ $executable ]]; then chmod +x "$target" else chmod -x "$target" fi fi fi } '' + lib.concatStrings ( lib.mapAttrsToList (n: v: '' insertFile ${ lib.escapeShellArgs [ (sourceStorePath v) n (if v.executable == null then "inherit" else toString v.executable) "false" ]} '') homeFiles )); }; }