diff options
Diffstat (limited to 'pkgs/build-support')
-rw-r--r-- | pkgs/build-support/build-fhs-userenv/chrootenv/chrootenv.c | 2 | ||||
-rw-r--r-- | pkgs/build-support/emacs/melpa.nix | 29 | ||||
-rw-r--r-- | pkgs/build-support/emacs/trivial.nix | 13 | ||||
-rw-r--r-- | pkgs/build-support/setup-hooks/auto-patchelf.sh | 67 | ||||
-rw-r--r-- | pkgs/build-support/writers/default.nix | 239 | ||||
-rw-r--r-- | pkgs/build-support/writers/test.nix | 149 |
6 files changed, 465 insertions, 34 deletions
diff --git a/pkgs/build-support/build-fhs-userenv/chrootenv/chrootenv.c b/pkgs/build-support/build-fhs-userenv/chrootenv/chrootenv.c index 7e49e9e78d78..0e9e36bc3014 100644 --- a/pkgs/build-support/build-fhs-userenv/chrootenv/chrootenv.c +++ b/pkgs/build-support/build-fhs-userenv/chrootenv/chrootenv.c @@ -19,7 +19,7 @@ #include <sys/types.h> #include <sys/wait.h> -const gchar *bind_blacklist[] = {"bin", "etc", "host", "usr", NULL}; +const gchar *bind_blacklist[] = {"bin", "etc", "host", "usr", "lib", "lib64", "lib32", "sbin", NULL}; void bind_mount(const gchar *source, const gchar *target) { fail_if(g_mkdir(target, 0755)); diff --git a/pkgs/build-support/emacs/melpa.nix b/pkgs/build-support/emacs/melpa.nix index 2ee99ce973ef..96e61bbf90e5 100644 --- a/pkgs/build-support/emacs/melpa.nix +++ b/pkgs/build-support/emacs/melpa.nix @@ -35,11 +35,11 @@ import ./generic.nix { inherit lib stdenv emacs texinfo; } ({ then pname else ename; - melpa = fetchFromGitHub { + packageBuild = fetchFromGitHub { owner = "melpa"; - repo = "melpa"; - rev = "7103313a7c31bb1ebb71419e365cd2e279ee4609"; - sha256 = "0m10f83ix0mzjk0vjd4kkb1m1p4b8ha0ll2yjsgk9bqjd7fwapqb"; + repo = "package-build"; + rev = "0a22c3fbbf661822ec1791739953b937a12fa623"; + sha256 = "0dpy5p34il600sc8ic5jdgb3glya9si3lrvhxab0swks8fdydjgs"; }; elpa2nix = ./elpa2nix.el; @@ -51,7 +51,7 @@ import ./generic.nix { inherit lib stdenv emacs texinfo; } ({ cp "$recipe" "$NIX_BUILD_TOP/recipes/$ename" fi - ln -s "$melpa/package-build" "$NIX_BUILD_TOP/package-build" + ln -s "$packageBuild" "$NIX_BUILD_TOP/package-build" mkdir -p "$NIX_BUILD_TOP/packages" ''; @@ -61,19 +61,18 @@ import ./generic.nix { inherit lib stdenv emacs texinfo; } ({ ln -s "$NIX_BUILD_TOP/$sourceRoot" "$NIX_BUILD_TOP/working/$ename" ''; - buildPhase = - '' - runHook preBuild + buildPhase = '' + runHook preBuild - cd "$NIX_BUILD_TOP" + cd "$NIX_BUILD_TOP" - emacs --batch -Q \ - -L "$melpa/package-build" \ - -l "$melpa2nix" \ - -f melpa2nix-build-package \ - $ename $version + emacs --batch -Q \ + -L "$NIX_BUILD_TOP/package-build" \ + -l "$melpa2nix" \ + -f melpa2nix-build-package \ + $ename $version - runHook postBuild + runHook postBuild ''; installPhase = '' diff --git a/pkgs/build-support/emacs/trivial.nix b/pkgs/build-support/emacs/trivial.nix index 98463c56ba96..396c971b2f01 100644 --- a/pkgs/build-support/emacs/trivial.nix +++ b/pkgs/build-support/emacs/trivial.nix @@ -7,27 +7,22 @@ with lib; args: import ./generic.nix envargs ({ - #preConfigure = '' - # export LISPDIR=$out/share/emacs/site-lisp - # export VERSION_SPECIFIC_LISPDIR=$out/share/emacs/site-lisp - #''; - buildPhase = '' - eval "$preBuild" + runHook preBuild emacs -L . --batch -f batch-byte-compile *.el - eval "$postBuild" + runHook postBuild ''; installPhase = '' - eval "$preInstall" + runHook preInstall LISPDIR=$out/share/emacs/site-lisp install -d $LISPDIR install *.el *.elc $LISPDIR - eval "$postInstall" + runHook postInstall ''; } diff --git a/pkgs/build-support/setup-hooks/auto-patchelf.sh b/pkgs/build-support/setup-hooks/auto-patchelf.sh index d1ae317ff9a4..5bedd1a9f9ca 100644 --- a/pkgs/build-support/setup-hooks/auto-patchelf.sh +++ b/pkgs/build-support/setup-hooks/auto-patchelf.sh @@ -147,15 +147,56 @@ autoPatchelfFile() { fi } +# Can be used to manually add additional directories with shared object files +# to be included for the next autoPatchelf invocation. +addAutoPatchelfSearchPath() { + local -a findOpts=() + + # XXX: Somewhat similar to the one in the autoPatchelf function, maybe make + # it DRY someday... + while [ $# -gt 0 ]; do + case "$1" in + --) shift; break;; + --no-recurse) shift; findOpts+=("-maxdepth" 1);; + --*) + echo "addAutoPatchelfSearchPath: ERROR: Invalid command line" \ + "argument: $1" >&2 + return 1;; + *) break;; + esac + done + + cachedDependencies+=( + $(find "$@" "${findOpts[@]}" \! -type d \ + \( -name '*.so' -o -name '*.so.*' \)) + ) +} + autoPatchelf() { + local norecurse= + + while [ $# -gt 0 ]; do + case "$1" in + --) shift; break;; + --no-recurse) shift; norecurse=1;; + --*) + echo "autoPatchelf: ERROR: Invalid command line" \ + "argument: $1" >&2 + return 1;; + *) break;; + esac + done + + if [ $# -eq 0 ]; then + echo "autoPatchelf: No paths to patch specified." >&2 + return 1 + fi + echo "automatically fixing dependencies for ELF files" >&2 # Add all shared objects of the current output path to the start of # cachedDependencies so that it's choosen first in findDependency. - cachedDependencies+=( - $(find "$prefix" \! -type d \( -name '*.so' -o -name '*.so.*' \)) - ) - local elffile + addAutoPatchelfSearchPath ${norecurse:+--no-recurse} -- "$@" # Here we actually have a subshell, which also means that # $cachedDependencies is final at this point, so whenever we want to run @@ -164,12 +205,15 @@ autoPatchelf() { # outside of this function. while IFS= read -r -d $'\0' file; do isELF "$file" || continue + segmentHeaders="$(LANG=C readelf -l "$file")" + # Skip if the ELF file doesn't have segment headers (eg. object files). + echo "$segmentHeaders" | grep -q '^Program Headers:' || continue if isExecutable "$file"; then # Skip if the executable is statically linked. - LANG=C readelf -l "$file" | grep -q "^ *INTERP\\>" || continue + echo "$segmentHeaders" | grep -q "^ *INTERP\\>" || continue fi autoPatchelfFile "$file" - done < <(find "$prefix" -type f -print0) + done < <(find "$@" ${norecurse:+-maxdepth 1} -type f -print0) } # XXX: This should ultimately use fixupOutputHooks but we currently don't have @@ -180,6 +224,11 @@ autoPatchelf() { # So what we do here is basically run in postFixup and emulate the same # behaviour as fixupOutputHooks because the setup hook for patchelf is run in # fixupOutput and the postFixup hook runs later. -postFixupHooks+=( - 'for output in $outputs; do prefix="${!output}" autoPatchelf; done' -) +postFixupHooks+=(' + if [ -z "$dontAutoPatchelf" ]; then + autoPatchelf -- $(for output in $outputs; do + [ -e "${!output}" ] || continue + echo "${!output}" + done) + fi +') diff --git a/pkgs/build-support/writers/default.nix b/pkgs/build-support/writers/default.nix new file mode 100644 index 000000000000..8aa3e52f5e8b --- /dev/null +++ b/pkgs/build-support/writers/default.nix @@ -0,0 +1,239 @@ +{ pkgs, lib }: + +with lib; +rec { + # Base implementation for non-compiled executables. + # Takes an interpreter, for example `${pkgs.bash}/bin/bash` + # + # Examples: + # writeBash = makeScriptWriter { interpreter = "${pkgs.bash}/bin/bash"; } + # makeScriptWriter { interpreter = "${pkgs.dash}/bin/dash"; } "hello" "echo hello world" + makeScriptWriter = { interpreter, check ? "" }: name: text: + assert lib.or (types.path.check name) (builtins.match "([0-9A-Za-z._])[0-9A-Za-z._-]*" name != null); + + pkgs.writeTextFile { + name = last (builtins.split "/" name); + executable = true; + destination = if types.path.check name then name else ""; + text = '' + #! ${interpreter} + ${text} + ''; + checkPhase = check; + }; + + # Like writeScript but the first line is a shebang to bash + # + # Example: + # writeBash "example" '' + # echo hello world + # '' + writeBash = makeScriptWriter { + interpreter = "${pkgs.bash}/bin/bash"; + }; + + # Like writeScriptBIn but the first line is a shebang to bash + writeBashBin = name: + writeBash "/bin/${name}"; + + # writeC writes an executable c package called `name` to `destination` using `libraries`. + # + # Examples: + # writeC "hello-world-ncurses" { libraries = [ pkgs.ncurses ]; } '' + # #include <ncurses.h> + # int main() { + # initscr(); + # printw("Hello World !!!"); + # refresh(); endwin(); + # return 0; + # } + # '' + writeC = name: { + libraries ? [], + }: text: pkgs.runCommand name { + inherit text; + buildInputs = [ pkgs.pkgconfig ] ++ libraries; + passAsFile = [ "text" ]; + } '' + PATH=${makeBinPath [ + pkgs.binutils-unwrapped + pkgs.coreutils + pkgs.gcc + pkgs.pkgconfig + ]} + mkdir -p "$(dirname "$out")" + gcc \ + ${optionalString (libraries != []) + "$(pkg-config --cflags --libs ${ + concatMapStringsSep " " (lib: escapeShellArg (builtins.parseDrvName lib.name).name) (libraries) + })" + } \ + -O \ + -o "$out" \ + -Wall \ + -x c \ + "$textPath" + strip --strip-unneeded "$out" + ''; + + # writeCBin takes the same arguments as writeC but outputs a directory (like writeScriptBin) + writeCBin = name: spec: text: + pkgs.runCommand 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 + # + # Example: + # writeDash "example" '' + # echo hello world + # '' + writeDash = makeScriptWriter { + interpreter = "${pkgs.dash}/bin/dash"; + }; + + # Like writeScriptBin but the first line is a shebang to dash + writeDashBin = name: + writeDash "/bin/${name}"; + + # writeHaskell takes a name, an attrset with libraries and haskell version (both optional) + # and some haskell source code and returns an executable. + # + # Example: + # writeHaskell "missiles" { libraries = [ pkgs.haskellPackages.acme-missiles ]; } '' + # Import Acme.Missiles + # + # main = launchMissiles + # ''; + writeHaskell = name: { + libraries ? [], + ghc ? pkgs.ghc + }: text: pkgs.runCommand name { + inherit text; + passAsFile = [ "text" ]; + } '' + cp $textPath ${name}.hs + ${ghc.withPackages (_: libraries )}/bin/ghc ${name}.hs + cp ${name} $out + ''; + + # writeHaskellBin takes the same arguments as writeHaskell but outputs a directory (like writeScriptBin) + writeHaskellBin = name: spec: text: + pkgs.runCommand 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 + # returns an executable + # + # Example: + # writeJS "example" { libraries = [ pkgs.nodePackages.uglify-js ]; } '' + # var UglifyJS = require("uglify-js"); + # var code = "function add(first, second) { return first + second; }"; + # var result = UglifyJS.minify(code); + # console.log(result.code); + # '' + writeJS = name: { libraries ? [] }: text: + let + node-env = pkgs.buildEnv { + name = "node"; + paths = libraries; + pathsToLink = [ + "/lib/node_modules" + ]; + }; + in writeDash name '' + export NODE_PATH=${node-env}/lib/node_modules + exec ${pkgs.nodejs}/bin/node ${pkgs.writeText "js" text} + ''; + + # writeJSBin takes the same arguments as writeJS but outputs a directory (like writeScriptBin) + writeJSBin = name: + writeJS "/bin/${name}"; + + # writePerl takes a name an attributeset with libraries and some perl sourcecode and + # returns an executable + # + # Example: + # writePerl "example" { libraries = [ pkgs.perlPackages.boolean ]; } '' + # use boolean; + # print "Howdy!\n" if true; + # '' + writePerl = name: { libraries ? [] }: + let + perl-env = pkgs.buildEnv { + name = "perl-environment"; + paths = libraries; + pathsToLink = [ + "/lib/perl5/site_perl" + ]; + }; + in + makeScriptWriter { + interpreter = "${pkgs.perl}/bin/perl -I ${perl-env}/lib/perl5/site_perl"; + } name; + + # writePerlBin takes the same arguments as writePerl but outputs a directory (like writeScriptBin) + writePerlBin = name: + writePerl "/bin/${name}"; + + # writePython2 takes a name an attributeset with libraries and some python2 sourcecode and + # returns an executable + # + # Example: + # writePython2 "test_python2" { libraries = [ pkgs.python2Packages.enum ]; } '' + # from enum import Enum + # + # class Test(Enum): + # a = "success" + # + # print Test.a + # '' + writePython2 = name: { libraries ? [], flakeIgnore ? [] }: + let + py = pkgs.python2.withPackages (ps: libraries); + ignoreAttribute = optionalString (flakeIgnore != []) "--ignore ${concatMapStringsSep "," escapeShellArg flakeIgnore}"; + in + makeScriptWriter { + interpreter = "${py}/bin/python"; + check = writeDash "python2check.sh" '' + exec ${pkgs.python2Packages.flake8}/bin/flake8 --show-source ${ignoreAttribute} "$1" + ''; + } name; + + # writePython2Bin takes the same arguments as writePython2 but outputs a directory (like writeScriptBin) + writePython2Bin = name: + writePython2 "/bin/${name}"; + + # writePython3 takes a name an attributeset with libraries and some python3 sourcecode and + # returns an executable + # + # Example: + # writePython3 "test_python3" { libraries = [ pkgs.python3Packages.pyyaml ]; } '' + # import yaml + # + # y = yaml.load(""" + # - test: success + # """) + # print(y[0]['test']) + # '' + writePython3 = name: { libraries ? [], flakeIgnore ? [] }: + let + py = pkgs.python3.withPackages (ps: libraries); + ignoreAttribute = optionalString (flakeIgnore != []) "--ignore ${concatMapStringsSep "," escapeShellArg flakeIgnore}"; + in + makeScriptWriter { + interpreter = "${py}/bin/python"; + check = writeDash "python3check.sh" '' + exec ${pkgs.python3Packages.flake8}/bin/flake8 --show-source ${ignoreAttribute} "$1" + ''; + } name; + + # writePython3Bin takes the same arguments as writePython3 but outputs a directory (like writeScriptBin) + writePython3Bin = name: + writePython3 "/bin/${name}"; +} diff --git a/pkgs/build-support/writers/test.nix b/pkgs/build-support/writers/test.nix new file mode 100644 index 000000000000..68b7b27e6130 --- /dev/null +++ b/pkgs/build-support/writers/test.nix @@ -0,0 +1,149 @@ +{ stdenv, lib, runCommand, haskellPackages, nodePackages, perlPackages, python2Packages, python3Packages, writers}: +with writers; +let + + bin = { + bash = writeBashBin "test_writers" '' + if [[ "test" == "test" ]]; then echo "success"; fi + ''; + + c = writeCBin "test_writers" { libraries = [ ]; } '' + #include <stdio.h> + int main() { + printf("success\n"); + return 0; + } + ''; + + dash = writeDashBin "test_writers" '' + test '~' = '~' && echo 'success' + ''; + + haskell = writeHaskellBin "test_writers" { libraries = [ haskellPackages.acme-default ]; } '' + import Data.Default + + int :: Int + int = def + + main :: IO () + main = case int of + 18871 -> putStrLn $ id "success" + _ -> print "fail" + ''; + + js = writeJSBin "test_writers" { libraries = [ nodePackages.semver ]; } '' + var semver = require('semver'); + + if (semver.valid('1.2.3')) { + console.log('success') + } else { + console.log('fail') + } + ''; + + perl = writePerlBin "test_writers" { libraries = [ perlPackages.boolean ]; } '' + use boolean; + print "success\n" if true; + ''; + + python2 = writePython2Bin "test_writers" { libraries = [ python2Packages.enum ]; } '' + from enum import Enum + + class Test(Enum): + a = "success" + + print Test.a + ''; + + python3 = writePython3Bin "test_writers" { libraries = [ python3Packages.pyyaml ]; } '' + import yaml + + y = yaml.load(""" + - test: success + """) + print(y[0]['test']) + ''; + }; + + simple = { + bash = writeBash "test_bash" '' + if [[ "test" == "test" ]]; then echo "success"; fi + ''; + + c = writeC "test_c" { libraries = [ ]; } '' + #include <stdio.h> + int main() { + printf("success\n"); + return 0; + } + ''; + + dash = writeDash "test_dash" '' + test '~' = '~' && echo 'success' + ''; + + haskell = writeHaskell "test_haskell" { libraries = [ haskellPackages.acme-default ]; } '' + import Data.Default + + int :: Int + int = def + + main :: IO () + main = case int of + 18871 -> putStrLn $ id "success" + _ -> print "fail" + ''; + + js = writeJS "test_js" { libraries = [ nodePackages.semver ]; } '' + var semver = require('semver'); + + if (semver.valid('1.2.3')) { + console.log('success') + } else { + console.log('fail') + } + ''; + + perl = writePerl "test_perl" { libraries = [ perlPackages.boolean ]; } '' + use boolean; + print "success\n" if true; + ''; + + python2 = writePython2 "test_python2" { libraries = [ python2Packages.enum ]; } '' + from enum import Enum + + class Test(Enum): + a = "success" + + print Test.a + ''; + + python3 = writePython3 "test_python3" { libraries = [ python3Packages.pyyaml ]; } '' + import yaml + + y = yaml.load(""" + - test: success + """) + print(y[0]['test']) + ''; + }; + + writeTest = expectedValue: test: + writeDash "test-writers" '' + if test "$(${test})" != "${expectedValue}"; then + echo 'test ${test} failed' + exit 1 + fi + ''; + +in runCommand "test-writers" { + passthru = { inherit writeTest bin simple; }; + meta.platforms = stdenv.lib.platforms.all; +} '' + ${lib.concatMapStringsSep "\n" (test: writeTest "success" "${test}/bin/test_writers") (lib.attrValues bin)} + ${lib.concatMapStringsSep "\n" (test: writeTest "success" "${test}") (lib.attrValues simple)} + + echo 'nix-writers successfully tested' >&2 + touch $out +'' + |