-
-
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
1 changed file
with
178 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,178 @@ | ||
{ | ||
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. | ||
`runtimeInputs` (List of strings or derivations, _optional_) | ||
: Paths containing executables that should be added to the script's `$PATH` variable, making them usable inside the script at runtime. | ||
`runtimeEnv` (Attribute sets of strings, _optional_) | ||
: Environment variables to be set at runtime. | ||
`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. | ||
# Type | ||
``` | ||
writableDirWrapper :: | ||
{ | ||
name :: String; | ||
version? :: String; | ||
path :: String; | ||
links? :: [{ src :: String; dst? :: String }]; | ||
runtimeInputs? :: [String|Derivation]; | ||
runtimeEnv? :: { ... :: 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 ? [ ], | ||
runtimeInputs ? [ ], | ||
runtimeEnv ? { }, | ||
postLink ? "", | ||
exec, | ||
}: | ||
let | ||
unlinkCommand = link: ''find "${path}/${link.dst or ""}" -type l -lname "${link.src}" -delete''; | ||
linkCommand = link: ''lndir -silent ${link.src} "./${link.dst or ""}"''; | ||
in | ||
writeShellApplication { | ||
inherit name runtimeEnv; | ||
|
||
runtimeInputs = runtimeInputs ++ [ xorg.lndir ]; | ||
|
||
text = | ||
'' | ||
relink=false | ||
for arg in "$@"; do | ||
case "$arg" in | ||
--relink-files) relink=true ;; | ||
# Passthrough all other args | ||
*) args+=("$arg") ;; | ||
esac | ||
done | ||
versionFile="${path}/.${name}.version" | ||
if [[ "$relink" = "true" ]]; then | ||
echo "Manually relinking files" | ||
elif [[ ! -d "${path}" ]]; then | ||
echo "Directory ${path} not found. Linking files..." | ||
relink=true | ||
'' | ||
+ lib.optionalString (version != null) '' | ||
elif [[ ! -e "$versionFile" ]]; then | ||
echo "Version file not found. Relinking files just to be safe..." | ||
relink=true | ||
elif [[ "${version}" != "$(<"$versionFile")" ]]; then | ||
echo "Current version is not the same as the newest version (${version}). Relinking files" | ||
relink=true | ||
'' | ||
+ '' | ||
fi | ||
if [[ "$relink" = "true" ]]; then | ||
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 | ||
pushd "$(mktemp -d)" | ||
${lib.concatMapStringsSep "\n" linkCommand links} | ||
${postLink} | ||
cp -r ./* -t "${path}" | ||
popd | ||
fi | ||
${exec} "''${args[@]}" | ||
''; | ||
} |