-
-
Notifications
You must be signed in to change notification settings - Fork 14.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
201 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,199 @@ | ||
{ | ||
lib, | ||
writeShellApplication, | ||
xorg, | ||
}: | ||
/** | ||
A wrapper for programs that require a writable directory to function, typically | ||
games or proprietary software that expect read-only resources to be placed alongside | ||
config files and state files (e.g. logs). | ||
# Inputs | ||
*`options`* (Attribute set) | ||
: Set of options for the wrapper. | ||
`name` (String) | ||
: File name of the wrapper. | ||
`version` (String, _optional_) | ||
: Version of the underlying program. When set, version checks will be enabled and existing links will be recreated if they belong to a different version. | ||
`path` (String) | ||
: Path of the directory that would hold the linked data. | ||
`links` (List of attrsets, _optional_) | ||
: List of links from a source directory to a directory relative to the output path. | ||
`src` (String) | ||
: Source directory of the link, usually from the Nix store. | ||
`dst` (String, _optional_) | ||
: Destination directory of the link. | ||
: _Default:_ the output path. | ||
`postLink` (String, _optional_) | ||
: Command to run after (re-)linking. Since links are first created in a temporary directory, `postLink` can be used to remove or add files that will be copied to the output path (for example, when some files need to be excluded from the linked result). | ||
`exec` (String) | ||
: Command to run. | ||
`flags` (List of strings, _optional_) | ||
: The list of flags given to the command. ***Unescaped and can refer to Bash variables and perform shell substitutions.*** | ||
# Type | ||
``` | ||
writableDirWrapper :: | ||
{ | ||
name :: String; | ||
version? :: String; | ||
path :: String; | ||
links? :: [{ src :: String; dst? :: String }]; | ||
postLink? :: String; | ||
exec :: String | ||
} -> Derivation | ||
``` | ||
# Examples | ||
:::{.example} | ||
## `pkgs.writableDirWrapper` usage example | ||
```nix | ||
{ | ||
writableDirWrapper, | ||
hello, | ||
}: | ||
writableDirWrapper { | ||
name = "writable-hello"; | ||
links = [ | ||
{ | ||
src = hello; | ||
dst = "hello"; | ||
} | ||
]; | ||
postLink = '' | ||
echo "Hello world!" | base64 > hello.txt | ||
''; | ||
exec = '' | ||
./hello/bin/hello --greeting=$(<"hello.txt") | ||
''; | ||
} | ||
``` | ||
When built and run, this should output: | ||
```console | ||
SGVsbG8gd29ybGQhCg== | ||
``` | ||
::: | ||
*/ | ||
{ | ||
name, | ||
version ? null, | ||
path, | ||
links ? [ ], | ||
postLink ? "", | ||
exec, | ||
flags ? [ ], | ||
}@args: | ||
let | ||
args' = builtins.removeAttrs args [ | ||
"version" | ||
"path" | ||
"links" | ||
"postLink" | ||
"exec" | ||
]; | ||
|
||
unlinkCommand = | ||
{ | ||
src, | ||
dst ? "", | ||
}: | ||
'' | ||
find "${path}/${dst}" -type l -lname "${src}" -delete | ||
''; | ||
|
||
linkCommand = | ||
{ | ||
src, | ||
dst ? "", | ||
}: | ||
'' | ||
mkdir -p ./${dst}; | ||
${lib.getExe xorg.lndir} -silent ${src} "./${dst}" | ||
''; | ||
|
||
# We *very* intentionally don't do escapeShellArgs here since it is useful for the flags | ||
# to do path substitutions (like --add-flags in makeWrapper/wrapProgram) | ||
flagsString = lib.concatMapStringsSep " " (f: ''"${f}"'') flags; | ||
in | ||
writeShellApplication ( | ||
args' | ||
// { | ||
text = | ||
'' | ||
relink() { | ||
mkdir -p "${path}" | ||
if [[ -d "${path}" ]]; then | ||
echo "Removing existing file links" | ||
${lib.concatMapStringsSep "\n" unlinkCommand links} | ||
fi | ||
echo "Linking files" | ||
${lib.optionalString (version != null) ''echo "${version}" > "$versionFile"''} | ||
# In case that postLink needs to remove some links to avoid conflict, | ||
# we first link everything into a temporary directory then move over | ||
( | ||
cd "$(mktemp -d)" | ||
${lib.concatMapStringsSep "\n" linkCommand links} | ||
${postLink} | ||
cp -a ./. -t "${path}" | ||
) | ||
} | ||
manuallyRelink=false | ||
for arg in "$@"; do | ||
case "$arg" in | ||
--relink-files) manuallyRelink=true ;; | ||
# Passthrough all other args | ||
*) args+=("$arg") ;; | ||
esac | ||
done | ||
versionFile="${path}/.${name}.version" | ||
if [[ "$manuallyRelink" = "true" ]]; then | ||
echo "Manually relinking files" | ||
relink | ||
elif [[ ! -d "${path}" ]]; then | ||
echo "Directory ${path} not found. Linking files..." | ||
relink | ||
'' | ||
+ lib.optionalString (version != null) '' | ||
elif [[ ! -e "$versionFile" ]]; then | ||
echo "Version file not found. Relinking files just to be safe..." | ||
relink | ||
elif [[ "${version}" != "$(<"$versionFile")" ]]; then | ||
echo "Current version is not the same as the newest version (${version}). Relinking files" | ||
relink | ||
'' | ||
+ '' | ||
fi | ||
exec ${exec} "${flagsString}" "''${args[@]}" | ||
''; | ||
} | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters