-
-
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
245 additions
and
0 deletions.
There are no files selected for viewing
116 changes: 116 additions & 0 deletions
116
pkgs/by-name/wr/writableDirWrapper/writable-dir-wrapper/default.nix
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,116 @@ | ||
{ | ||
lib, | ||
writeShellApplication, | ||
xorg, | ||
makeSetupHook, | ||
|
||
makeWrapper, | ||
}: | ||
makeSetupHook { | ||
name = "writable-dir-wrapper"; | ||
|
||
propagatedBuildInputs = [ | ||
makeWrapper | ||
]; | ||
|
||
substitutions = { | ||
lndir = lib.getExe xorg.lndir; | ||
}; | ||
|
||
} ./wrapper.sh | ||
|
||
/** | ||
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== | ||
``` | ||
::: | ||
*/ |
129 changes: 129 additions & 0 deletions
129
pkgs/by-name/wr/writableDirWrapper/writable-dir-wrapper/wrapper.sh
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,129 @@ | ||
# makeWritableDirWrapper EXECUTABLE OUT_PATH DIR ARGS | ||
|
||
# ARGS: | ||
# --dont-track-version : don't relink when version mismatches | ||
# | ||
# --post-link-run COMMAND : run command after linking | ||
# | ||
# --link SRC DST : link SRC to DST | ||
|
||
makeWritableDirWrapper() { | ||
local original="$1"; shift | ||
local wrapper="$1"; shift | ||
local dir="$1"; shift | ||
local postLinkRun dontTrackVersion | ||
local -A links | ||
|
||
local makeWrapperParams=("$original" "$wrapper") | ||
while [[ "$#" -gt 0 ]]; do | ||
case "$1" in | ||
--post-link-run) | ||
postLinkRun="$2"; shift | ||
;; | ||
--dont-track-version) | ||
dontTrackVersion=true | ||
;; | ||
--link) | ||
src="$2"; shift | ||
dst="$2"; shift | ||
links["$src"]="$dst" | ||
;; | ||
*) makeWrapperParams+=("$1") ;; | ||
esac | ||
shift | ||
done | ||
|
||
local fragment=$(mktemp) | ||
|
||
local unlinkCommands=() | ||
local linkCommands=() | ||
for src in "${!links[@]}"; do | ||
dst=${links[$src]} | ||
unlinkCommands+=("[[ -d \"$dir/$dst\" ]] && find \"$dir/$dst\" -type l -lname '$src' -delete;") | ||
linkCommands+=( | ||
"mkdir -p './$dst';" | ||
"@lndir@ -silent '$src' './$dst';" | ||
) | ||
done | ||
|
||
|
||
{ | ||
cat <<EOF | ||
unlink() { | ||
echo 'Removing existing file links' | ||
${unlinkCommands[@]} | ||
} | ||
link() { | ||
echo 'Linking files' | ||
mkdir -p "$dir" | ||
EOF | ||
|
||
if [[ -z "$dontTrackVersion" ]]; then | ||
echo " echo '$version' > \"$dir/.$pname.version\"" | ||
fi | ||
|
||
# In case that postLink needs to remove some links to avoid conflict, | ||
# we first link everything into a temporary directory then move over | ||
cat <<EOF | ||
( | ||
cd \$(mktemp -d) | ||
${linkCommands[@]} | ||
$postLinkRun | ||
cp -a ./. -t "$dir" | ||
) | ||
} | ||
relink() { | ||
[[ -d "$dir" ]] && unlink | ||
link | ||
} | ||
declare manuallyRelink | ||
for arg in "\$@"; do | ||
case "\$arg" in | ||
--relink-files) manuallyRelink=true ;; | ||
*) args+=("\$arg") ;; # Passthrough all other args | ||
esac | ||
done | ||
set -- "\${args[@]}" | ||
if [[ -n "\$manuallyRelink" ]]; then | ||
echo "Manually relinking files"; relink | ||
elif [[ ! -d "$dir" ]]; then | ||
echo "Directory $dir not found. Linking files..."; relink | ||
EOF | ||
|
||
if [[ -z "$dontTrackVersion" ]]; then | ||
cat <<EOF | ||
elif [[ ! -e "$dir/.$name.version" ]]; then | ||
echo "Version file not found. Relinking files just to be safe..."; relink | ||
elif [[ '$version' != "\$(<"$dir/.$pname.version")" ]]; then | ||
echo "Current version is not the same as the newest version ($version). Relinking files"; relink | ||
EOF | ||
fi | ||
|
||
echo "fi" | ||
} >> "$fragment" | ||
|
||
makeWrapper "${makeWrapperParams[@]}" | ||
|
||
# Inject after the shebang | ||
sed -i "1r $fragment" "$wrapper" | ||
} | ||
|
||
# Syntax: wrapProgramInWritableDir <PROGRAM> <DIRECTORY> <MAKE-WRAPPER FLAGS...> | ||
wrapProgramInWritableDir() { | ||
local prog="$1" | ||
local dir="$2" | ||
local hidden | ||
|
||
assertExecutable "$prog" | ||
|
||
hidden="$(dirname "$prog")/.$(basename "$prog")"-wrapped | ||
while [ -e "$hidden" ]; do | ||
hidden="${hidden}_" | ||
done | ||
mv "$prog" "$hidden" | ||
makeWritableDirWrapper "$hidden" "$prog" "$dir" --inherit-argv0 "${@:3}" | ||
} |