Skip to content

Commit

Permalink
writableDirWrapper: init
Browse files Browse the repository at this point in the history
  • Loading branch information
pluiedev committed Dec 2, 2024
1 parent d13c9db commit eef6f0a
Show file tree
Hide file tree
Showing 2 changed files with 192 additions and 0 deletions.
190 changes: 190 additions & 0 deletions pkgs/build-support/writable-dir-wrapper/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
{
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.
# 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,
}@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}"
'';
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 -r ./* -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} "''${args[@]}"
'';
}
)
2 changes: 2 additions & 0 deletions pkgs/top-level/all-packages.nix
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,8 @@ with pkgs;

releaseTools = callPackage ../build-support/release { };

writableDirWrapper = callPackage ../build-support/writable-dir-wrapper { };

inherit (lib.systems) platforms;

setJavaClassPath = makeSetupHook {
Expand Down

0 comments on commit eef6f0a

Please sign in to comment.