Skip to content

Instantly share code, notes, and snippets.

@PedroHLC
Created October 6, 2023 17:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save PedroHLC/b930cbbacdcb2a664e6cf20ab5acc82c to your computer and use it in GitHub Desktop.
Save PedroHLC/b930cbbacdcb2a664e6cf20ab5acc82c to your computer and use it in GitHub Desktop.
A more complex version of writeShellScriptBin that generates a lib directory and make some other stuff easy too
{ lib
, runtimeShell
, stdenvNoCC
}@p:
{ pname
, lib ? p.lib
, runtimeShell ? p.runtimeShell
, stdenv ? stdenvNoCC
, version ? null
, critical ? false
, meta ? { }
, passthru ? { }
, checkPhase ? "${stdenv.shellDryRun} \"$mainProgram\""
, extraScripts ? { }
, extraFiles ? { }
, resolve ? { }
, external ? [ ]
, depends ? [ ]
, path ? ""
}: userMainScript:
let
inherit (builtins) attrNames concatStringsSep foldl' map;
inherit (lib) escapeShellArg;
inherit (lib.strings) optionalString toUpper;
extraScriptsNames =
attrNames extraScripts;
extraScriptTarget = sName: "lib/${escapeShellArg pname}/${escapeShellArg sName}";
# everybody deserves to be a file
prefixExtraScriptsName = sName: "script_${sName}";
prefixExtraScripts = accu: sName:
let value = extraScripts.${sName}; in
if builtins.isPath value
then accu
else accu // { "${prefixExtraScriptsName sName}" = value; };
prefixedExtraScripts =
foldl' prefixExtraScripts { } extraScriptsNames;
prefixedExtraScriptsNames =
attrNames prefixedExtraScripts;
resolved =
map
(rName: ''
function ${rName} { ${escapeShellArg resolve.${rName}} "$@"; }
'')
(attrNames resolve);
findExternals =
map
(eName:
let eUpName = toUpper eName; in ''
${eUpName}=''${${eUpName}:-$(which ${escapeShellArg eName})}
function ${eName} { "${"$" + eUpName}" "$@"; }
'')
external;
sourceDepends =
map (dName: "source ${escapeShellArg dName}") depends;
buddy = bName: ''
source "$BASH_SOURCE_DIRNAME"/${extraScriptTarget bName}
'';
buddies =
map buddy extraScriptsNames;
mainScript =
''
#!${runtimeShell}
BASH_SOURCE_DIRNAME="''${BASH_SOURCE[0]%/*}/.."
'' + (optionalString critical ''
set -euo pipefail
'') + ''
${concatStringsSep "\n" (resolved ++ findExternals ++ sourceDepends)}
PATH=${escapeShellArg path}
'' + (
concatStringsSep "\n" buddies
) + userMainScript;
extraScriptsWrite = sName:
if prefixedExtraScripts ? sName then
let
prefixedName = "$" + (prefixExtraScriptsName sName);
in
''
writeTextFile $out/${extraScriptTarget sName} "${prefixedName}Path" "${prefixedName}" "y";
''
else ''
linkFile $out/${extraScriptTarget sName} "${extraScripts.${sName}}";
'';
extraScriptsWrites =
concatStringsSep "\n" (map extraScriptsWrite extraScriptsNames);
extraFilesLink = fName: ''
linkFile $out/${escapeShellArg fName} ${escapeShellArg extraFiles.${fName}}
'';
extraFilesLinks =
concatStringsSep "\n" (map extraFilesLink (attrNames extraFiles));
finalMeta = { mainProgram = "bin/${pname}"; } // meta;
name =
if version == null
then pname
else "${pname}-${version}";
build = buildCommand:
stdenv.mkDerivation (prefixedExtraScripts // {
inherit name;
inherit buildCommand;
inherit passthru;
inherit mainScript;
strictDeps = true;
enableParallelBuilding = true;
passAsFile = [ "buildCommand" "mainScript" ] ++ prefixedExtraScriptsNames;
meta = finalMeta;
});
in
build ''
function writeTextFile() {
target="$1"
textPath="$2"
text="$3"
executable="''${4:-n}"
mkdir -p "$(dirname "$target")"
if [ -e "$textPath" ]; then
mv "$textPath" "$target"
else
echo -n "$text" > "$target"
fi
if [[ "$executable" == "y" ]]; then
chmod +x "$target"
fi
}
function linkFile() {
target="$1"
source="$2"
mkdir -p "$(dirname "$target")"
ln -s "$source" "$target"
}
mainProgram=$out/${escapeShellArg finalMeta.mainProgram}
writeTextFile "$mainProgram" "$mainScriptPath" "$mainScript" "y"
${extraScriptsWrites}
${extraFilesLinks}
eval "$checkPhase"
''
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment