build-support writers: content can be string or file

This commit is contained in:
lassulus 2018-12-02 00:56:16 +01:00
parent 90f0a7c390
commit 995defbb9b
2 changed files with 105 additions and 63 deletions

View File

@ -8,19 +8,54 @@ rec {
# Examples: # Examples:
# writeBash = makeScriptWriter { interpreter = "${pkgs.bash}/bin/bash"; } # writeBash = makeScriptWriter { interpreter = "${pkgs.bash}/bin/bash"; }
# makeScriptWriter { interpreter = "${pkgs.dash}/bin/dash"; } "hello" "echo hello world" # makeScriptWriter { interpreter = "${pkgs.dash}/bin/dash"; } "hello" "echo hello world"
makeScriptWriter = { interpreter, check ? "" }: name: text: makeScriptWriter = { interpreter, check ? "" }: nameOrPath: content:
assert lib.or (types.path.check name) (builtins.match "([0-9A-Za-z._])[0-9A-Za-z._-]*" name != null); assert lib.or (types.path.check nameOrPath) (builtins.match "([0-9A-Za-z._])[0-9A-Za-z._-]*" nameOrPath != null);
assert lib.or (types.path.check content) (types.string.check content);
let
name = last (builtins.split "/" nameOrPath);
in
pkgs.writeTextFile { pkgs.runCommand name (if (types.string.check content) then {
name = last (builtins.split "/" name); inherit content interpreter;
executable = true; passAsFile = [ "content" ];
destination = if types.path.check name then name else ""; } else {
text = '' inherit interpreter;
#! ${interpreter} contentPath = content;
${text} }) ''
echo "#! $interpreter" > $out
cat "$contentPath" >> $out
chmod +x $out
${optionalString (types.path.check nameOrPath) ''
mv $out tmp
mkdir -p $out/$(dirname "${nameOrPath}")
mv tmp $out/${nameOrPath}
''}
'';
# Base implementation for compiled executables.
# Takes a compile script, which in turn takes the name as an argument.
#
# Examples:
# writeSimpleC = makeBinWriter { compileScript = name: "gcc -o $out $contentPath"; }
makeBinWriter = { compileScript }: nameOrPath: content:
assert lib.or (types.path.check nameOrPath) (builtins.match "([0-9A-Za-z._])[0-9A-Za-z._-]*" nameOrPath != null);
assert lib.or (types.path.check content) (types.string.check content);
let
name = last (builtins.split "/" nameOrPath);
in
pkgs.runCommand name (if (types.string.check content) then {
inherit content;
passAsFile = [ "content" ];
} else {
contentPath = content;
}) ''
${compileScript}
${optionalString (types.path.check nameOrPath) ''
mv $out tmp
mkdir -p $out/$(dirname "${nameOrPath}")
mv tmp $out/${nameOrPath}
''}
''; '';
checkPhase = check;
};
# Like writeScript but the first line is a shebang to bash # Like writeScript but the first line is a shebang to bash
# #
@ -48,23 +83,18 @@ rec {
# return 0; # return 0;
# } # }
# '' # ''
writeC = name: { writeC = name: { libraries ? [] }:
libraries ? [], makeBinWriter {
}: text: pkgs.runCommand name { compileScript = ''
inherit text;
buildInputs = [ pkgs.pkgconfig ] ++ libraries;
passAsFile = [ "text" ];
} ''
PATH=${makeBinPath [ PATH=${makeBinPath [
pkgs.binutils-unwrapped pkgs.binutils-unwrapped
pkgs.coreutils pkgs.coreutils
pkgs.gcc pkgs.gcc
pkgs.pkgconfig pkgs.pkgconfig
]} ]}
mkdir -p "$(dirname "$out")"
gcc \ gcc \
${optionalString (libraries != []) ${optionalString (libraries != [])
"$(pkg-config --cflags --libs ${ "$(pkgs.pkgconfig}/bin/pkg-config --cflags --libs ${
concatMapStringsSep " " (lib: escapeShellArg (builtins.parseDrvName lib.name).name) (libraries) concatMapStringsSep " " (lib: escapeShellArg (builtins.parseDrvName lib.name).name) (libraries)
})" })"
} \ } \
@ -72,17 +102,14 @@ rec {
-o "$out" \ -o "$out" \
-Wall \ -Wall \
-x c \ -x c \
"$textPath" "$contentPath"
strip --strip-unneeded "$out" strip --strip-unneeded "$out"
''; '';
} name;
# writeCBin takes the same arguments as writeC but outputs a directory (like writeScriptBin) # writeCBin takes the same arguments as writeC but outputs a directory (like writeScriptBin)
writeCBin = name: spec: text: writeCBin = name:
pkgs.runCommand name { writeC "/bin/${name}";
} ''
mkdir -p $out/bin
ln -s ${writeC name spec text} $out/bin/${name}
'';
# Like writeScript but the first line is a shebang to dash # Like writeScript but the first line is a shebang to dash
# #
@ -103,29 +130,25 @@ rec {
# #
# Example: # Example:
# writeHaskell "missiles" { libraries = [ pkgs.haskellPackages.acme-missiles ]; } '' # writeHaskell "missiles" { libraries = [ pkgs.haskellPackages.acme-missiles ]; } ''
# Import Acme.Missiles # import Acme.Missiles
# #
# main = launchMissiles # main = launchMissiles
# ''; # '';
writeHaskell = name: { writeHaskell = name: {
libraries ? [], libraries ? [],
ghc ? pkgs.ghc ghc ? pkgs.ghc
}: text: pkgs.runCommand name { }:
inherit text; makeBinWriter {
passAsFile = [ "text" ]; compileScript = ''
} '' cp $contentPath tmp.hs
cp $textPath ${name}.hs ${ghc.withPackages (_: libraries )}/bin/ghc tmp.hs
${ghc.withPackages (_: libraries )}/bin/ghc ${name}.hs mv tmp $out
cp ${name} $out
''; '';
} name;
# writeHaskellBin takes the same arguments as writeHaskell but outputs a directory (like writeScriptBin) # writeHaskellBin takes the same arguments as writeHaskell but outputs a directory (like writeScriptBin)
writeHaskellBin = name: spec: text: writeHaskellBin = name:
pkgs.runCommand name { writeHaskell "/bin/${name}";
} ''
mkdir -p $out/bin
ln -s ${writeHaskell name spec text} $out/bin/${name}
'';
# writeJS takes a name an attributeset with libraries and some JavaScript sourcecode and # writeJS takes a name an attributeset with libraries and some JavaScript sourcecode and
# returns an executable # returns an executable
@ -137,7 +160,7 @@ rec {
# var result = UglifyJS.minify(code); # var result = UglifyJS.minify(code);
# console.log(result.code); # console.log(result.code);
# '' # ''
writeJS = name: { libraries ? [] }: text: writeJS = name: { libraries ? [] }: content:
let let
node-env = pkgs.buildEnv { node-env = pkgs.buildEnv {
name = "node"; name = "node";
@ -148,7 +171,7 @@ rec {
}; };
in writeDash name '' in writeDash name ''
export NODE_PATH=${node-env}/lib/node_modules export NODE_PATH=${node-env}/lib/node_modules
exec ${pkgs.nodejs}/bin/node ${pkgs.writeText "js" text} exec ${pkgs.nodejs}/bin/node ${pkgs.writeText "js" content}
''; '';
# writeJSBin takes the same arguments as writeJS but outputs a directory (like writeScriptBin) # writeJSBin takes the same arguments as writeJS but outputs a directory (like writeScriptBin)

View File

@ -1,4 +1,4 @@
{ stdenv, lib, runCommand, haskellPackages, nodePackages, perlPackages, python2Packages, python3Packages, writers}: { stdenv, lib, runCommand, haskellPackages, nodePackages, perlPackages, python2Packages, python3Packages, writers, writeText }:
with writers; with writers;
let let
@ -128,6 +128,24 @@ let
''; '';
}; };
path = {
bash = writeBash "test_bash" (writeText "test" ''
if [[ "test" == "test" ]]; then echo "success"; fi
'');
haskell = writeHaskell "test_haskell" { libraries = [ haskellPackages.acme-default ]; } (writeText "test" ''
import Data.Default
int :: Int
int = def
main :: IO ()
main = case int of
18871 -> putStrLn $ id "success"
_ -> print "fail"
'');
};
writeTest = expectedValue: test: writeTest = expectedValue: test:
writeDash "test-writers" '' writeDash "test-writers" ''
if test "$(${test})" != "${expectedValue}"; then if test "$(${test})" != "${expectedValue}"; then
@ -142,6 +160,7 @@ in runCommand "test-writers" {
} '' } ''
${lib.concatMapStringsSep "\n" (test: writeTest "success" "${test}/bin/test_writers") (lib.attrValues bin)} ${lib.concatMapStringsSep "\n" (test: writeTest "success" "${test}/bin/test_writers") (lib.attrValues bin)}
${lib.concatMapStringsSep "\n" (test: writeTest "success" "${test}") (lib.attrValues simple)} ${lib.concatMapStringsSep "\n" (test: writeTest "success" "${test}") (lib.attrValues simple)}
${lib.concatMapStringsSep "\n" (test: writeTest "success" "${test}") (lib.attrValues path)}
echo 'nix-writers successfully tested' >&2 echo 'nix-writers successfully tested' >&2
touch $out touch $out