about summary refs log tree commit diff
path: root/pkgs/development/interpreters/tcl
diff options
context:
space:
mode:
authorAndrew Brooks <andrew.brooks@flightaware.com>2021-04-30 17:25:06 -0500
committerAndrew Brooks <andrew.brooks@flightaware.com>2021-05-21 15:18:03 -0500
commitc6ca1ba9c7975dfab7dec0e7ee5c6f31c458333f (patch)
treefdbc3e96dd810db3e70c01b22009fcf34717f25a /pkgs/development/interpreters/tcl
parentd0fd8a029de0b3a1f4b81d020893b337f35a4348 (diff)
tcl: Add tclPackageHook and mkTclDerivation
Implement tclPackageHook, a setup hook that adjusts TCLLIBPATH to include the
paths of any installed Tcl packages, propagates that TCLLIBPATH to
anti-dependencies, and wraps any installed binaries to set their TCLLIBPATH.

Additionally, implement a makePythonPackage-style mkDerivation wrapper to use
reasonable defaults for Tcl packages and use tclPackageHook.
Diffstat (limited to 'pkgs/development/interpreters/tcl')
-rw-r--r--pkgs/development/interpreters/tcl/generic.nix112
-rw-r--r--pkgs/development/interpreters/tcl/mk-tcl-derivation.nix69
-rw-r--r--pkgs/development/interpreters/tcl/tcl-package-hook.sh74
3 files changed, 206 insertions, 49 deletions
diff --git a/pkgs/development/interpreters/tcl/generic.nix b/pkgs/development/interpreters/tcl/generic.nix
index e665e8cfb1ead..4c144699fc8fc 100644
--- a/pkgs/development/interpreters/tcl/generic.nix
+++ b/pkgs/development/interpreters/tcl/generic.nix
@@ -1,55 +1,69 @@
-{ lib, stdenv
+{ lib, stdenv, callPackage, makeSetupHook, makeWrapper
 
 # Version specific stuff
 , release, version, src
 , ...
 }:
 
-stdenv.mkDerivation {
-  pname = "tcl";
-  inherit version;
-
-  inherit src;
-
-  outputs = [ "out" "man" ];
-
-  setOutputFlags = false;
-
-  preConfigure = ''
-    cd unix
-  '';
-
-  configureFlags = [
-    "--enable-threads"
-    # Note: using $out instead of $man to prevent a runtime dependency on $man.
-    "--mandir=${placeholder "out"}/share/man"
-    "--enable-man-symlinks"
-    # Don't install tzdata because NixOS already has a more up-to-date copy.
-    "--with-tzdata=no"
-    "tcl_cv_strtod_unbroken=ok"
-  ] ++ lib.optional stdenv.is64bit "--enable-64bit";
-
-  enableParallelBuilding = true;
-
-  postInstall = let
-    dllExtension = stdenv.hostPlatform.extensions.sharedLibrary;
-  in ''
-    make install-private-headers
-    ln -s $out/bin/tclsh${release} $out/bin/tclsh
-    ln -s $out/lib/libtcl${release}${dllExtension} $out/lib/libtcl${dllExtension}
-  '';
-
-  meta = with lib; {
-    description = "The Tcl scripting language";
-    homepage = "https://www.tcl.tk/";
-    license = licenses.tcltk;
-    platforms = platforms.all;
-    maintainers = with maintainers; [ vrthra ];
-  };
-
-  passthru = rec {
-    inherit release version;
-    libPrefix = "tcl${release}";
-    libdir = "lib/${libPrefix}";
-  };
-}
+let
+  baseInterp =
+    stdenv.mkDerivation {
+      pname = "tcl";
+      inherit version;
+
+      inherit src;
+
+      outputs = [ "out" "man" ];
+
+      setOutputFlags = false;
+
+      preConfigure = ''
+        cd unix
+      '';
+
+      configureFlags = [
+        "--enable-threads"
+        # Note: using $out instead of $man to prevent a runtime dependency on $man.
+        "--mandir=${placeholder "out"}/share/man"
+        "--enable-man-symlinks"
+        # Don't install tzdata because NixOS already has a more up-to-date copy.
+        "--with-tzdata=no"
+        "tcl_cv_strtod_unbroken=ok"
+      ] ++ lib.optional stdenv.is64bit "--enable-64bit";
+
+      enableParallelBuilding = true;
+
+      postInstall = let
+        dllExtension = stdenv.hostPlatform.extensions.sharedLibrary;
+      in ''
+        make install-private-headers
+        ln -s $out/bin/tclsh${release} $out/bin/tclsh
+        ln -s $out/lib/libtcl${release}${dllExtension} $out/lib/libtcl${dllExtension}
+      '';
+
+      meta = with lib; {
+        description = "The Tcl scripting language";
+        homepage = "https://www.tcl.tk/";
+        license = licenses.tcltk;
+        platforms = platforms.all;
+        maintainers = with maintainers; [ vrthra ];
+      };
+
+      passthru = rec {
+        inherit release version;
+        libPrefix = "tcl${release}";
+        libdir = "lib/${libPrefix}";
+        tclPackageHook = callPackage ({}: makeSetupHook {
+          name = "tcl-package-hook";
+          deps = [ makeWrapper ];
+        } ./tcl-package-hook.sh) {};
+      };
+    };
+
+  mkTclDerivation = callPackage ./mk-tcl-derivation.nix { tcl = baseInterp; };
+
+in baseInterp.overrideAttrs (self: {
+     passthru = self.passthru // {
+       inherit mkTclDerivation;
+     };
+})
diff --git a/pkgs/development/interpreters/tcl/mk-tcl-derivation.nix b/pkgs/development/interpreters/tcl/mk-tcl-derivation.nix
new file mode 100644
index 0000000000000..4f3ffe6f995f4
--- /dev/null
+++ b/pkgs/development/interpreters/tcl/mk-tcl-derivation.nix
@@ -0,0 +1,69 @@
+# Generic builder for tcl packages/applications, generally based on mk-python-derivation.nix
+{ tcl
+, lib
+, makeWrapper
+, runCommand
+, writeScript
+}:
+
+{ buildInputs ? []
+, nativeBuildInputs ? []
+, propagatedBuildInputs ? []
+, checkInputs ? []
+
+# true if we should skip the configuration phase altogether
+, dontConfigure ? false
+
+# Extra flags passed to configure step
+, configureFlags ? []
+
+# Whether or not we should add common Tcl-related configure flags
+, addTclConfigureFlags ? true
+
+, meta ? {}
+, passthru ? {}
+, doCheck ? true
+, ... } @ attrs:
+
+let
+  inherit (tcl) stdenv;
+  inherit (lib) getBin optionalAttrs optionals;
+
+  defaultTclPkgConfigureFlags = [
+    "--with-tcl=${tcl}/lib"
+    "--with-tclinclude=${tcl}/include"
+    "--exec-prefix=\${out}"
+  ];
+
+  self = (stdenv.mkDerivation ((builtins.removeAttrs attrs [
+    "addTclConfigureFlags" "checkPhase" "checkInputs" "doCheck"
+  ]) // {
+
+    buildInputs = buildInputs ++ [ makeWrapper tcl.tclPackageHook ];
+    nativeBuildInputs = nativeBuildInputs ++ [ tcl ];
+    propagatedBuildInputs = propagatedBuildInputs ++ [ tcl ];
+
+    TCLSH = "${getBin tcl}/bin/tclsh";
+
+    # Run tests after install, at which point we've done all TCLLIBPATH setup
+    doCheck = false;
+    doInstallCheck = attrs.doCheck or ((attrs ? doInstallCheck) && attrs.doInstallCheck);
+    installCheckInputs = checkInputs ++ (optionals (attrs ? installCheckInputs) attrs.installCheckInputs);
+
+    # Add typical values expected by TEA for configureFlags
+    configureFlags =
+      if (!dontConfigure && addTclConfigureFlags)
+        then (configureFlags ++ defaultTclPkgConfigureFlags)
+        else configureFlags;
+
+    meta = {
+      platforms = tcl.meta.platforms;
+    } // meta;
+
+
+  } // optionalAttrs (attrs?checkPhase) {
+    installCheckPhase = attrs.checkPhase;
+  }
+  ));
+
+in lib.extendDerivation true passthru self
diff --git a/pkgs/development/interpreters/tcl/tcl-package-hook.sh b/pkgs/development/interpreters/tcl/tcl-package-hook.sh
new file mode 100644
index 0000000000000..5f105e2fc7ea7
--- /dev/null
+++ b/pkgs/development/interpreters/tcl/tcl-package-hook.sh
@@ -0,0 +1,74 @@
+# This hook ensures that we do the following in post-fixup:
+# * wrap any installed executables with a wrapper that configures TCLLIBPATH
+# * write a setup hook that extends the TCLLIBPATH of any anti-dependencies
+
+# Add a directory to TCLLIBPATH, provided that it exists
+_addToTclLibPath() {
+    local -r tclPkg="$1"
+    if [ -z "$tclPkg" ]; then
+        return
+    fi
+
+    if [ ! -d "$tclPkg" ]; then
+        >&2 echo "can't add $tclPkg to TCLLIBPATH; that directory doesn't exist"
+        exit 1
+    fi
+
+    if [[ "$tclPkg" == *" "* ]]; then
+        tclPkg="{$tclPkg}"
+    fi
+
+    if [ -z "${TCLLIBPATH-}" ]; then
+        export TCLLIBPATH="$tclPkg"
+    else
+        if [[ "$TCLLIBPATH" != *"$tclPkg"* ]]; then
+            export TCLLIBPATH="${TCLLIBPATH} $tclPkg"
+        fi
+    fi
+}
+
+# Locate any directory containing an installed pkgIndex file
+findInstalledTclPkgs() {
+    local -r newLibDir="${!outputLib}/lib"
+    if [ ! -d "$newLibDir" ]; then
+        >&2 echo "Assuming no loadable tcl packages installed ($newLibDir does not exist)"
+        return
+    fi
+    echo "$(find "$newLibDir" -name pkgIndex.tcl -exec dirname {} \;)"
+}
+
+# Wrap any freshly-installed binaries and set up their TCLLIBPATH
+wrapTclBins() {
+    if [ -z "${TCLLIBPATH-}" ]; then
+        echo "skipping automatic Tcl binary wrapping (nothing to do)"
+        return
+    fi
+
+    local -r tclBinsDir="${!outputBin}/bin"
+    if [ ! -d "$tclBinsDir" ]; then
+        echo "No outputBin found, not using any TCLLIBPATH wrapper"
+        return
+    fi
+
+    find "$tclBinsDir" -type f -executable -print |
+        while read -r someBin; do
+            echo "Adding TCLLIBPATH wrapper for $someBin"
+            wrapProgram "$someBin" --set TCLLIBPATH "$TCLLIBPATH"
+        done
+}
+
+# Generate hook to adjust TCLLIBPATH in anti-dependencies
+writeTclLibPathHook() {
+    local -r hookPath="${!outputLib}/nix-support/setup-hook"
+    mkdir -p "$(dirname "$hookPath")"
+
+    typeset -f _addToTclLibPath >> "$hookPath"
+    local -r tclPkgs=$(findInstalledTclPkgs)
+    while IFS= read -r tclPkg; do
+        echo "_addToTclLibPath \"$tclPkg\"" >> "$hookPath"
+        _addToTclLibPath "$tclPkg" true
+    done <<< "$tclPkgs"
+}
+
+postFixupHooks+=(writeTclLibPathHook)
+postFixupHooks+=(wrapTclBins)