1{
2 lib,
3 stdenv,
4 callPackage,
5 makeSetupHook,
6 runCommand,
7 tzdata,
8 zip,
9 zlib,
10
11 # Version specific stuff
12 release,
13 version,
14 src,
15 extraPatch ? "",
16 ...
17}:
18
19let
20 baseInterp = stdenv.mkDerivation rec {
21 pname = "tcl";
22 inherit version src;
23
24 outputs = [
25 "out"
26 "man"
27 ];
28
29 setOutputFlags = false;
30
31 postPatch = ''
32 substituteInPlace library/clock.tcl \
33 --replace "/usr/share/zoneinfo" "${tzdata}/share/zoneinfo" \
34 --replace "/usr/share/lib/zoneinfo" "" \
35 --replace "/usr/lib/zoneinfo" "" \
36 --replace "/usr/local/etc/zoneinfo" ""
37 ''
38 + extraPatch;
39
40 nativeBuildInputs = lib.optionals (lib.versionAtLeast version "9.0") [
41 # Only used to detect the presence of zlib. Could be replaced with a stub.
42 zip
43 ];
44
45 buildInputs = lib.optionals (lib.versionAtLeast version "9.0") [
46 zlib
47 ];
48
49 preConfigure = ''
50 cd unix
51 '';
52
53 # Note: pre-9.0 flags are temporarily interspersed to avoid a mass rebuild.
54 configureFlags =
55 lib.optionals (lib.versionOlder version "9.0") [
56 "--enable-threads"
57 ]
58 ++ [
59 # Note: using $out instead of $man to prevent a runtime dependency on $man.
60 "--mandir=${placeholder "out"}/share/man"
61 ]
62 ++ lib.optionals (lib.versionOlder version "9.0") [
63 "--enable-man-symlinks"
64 # Don't install tzdata because NixOS already has a more up-to-date copy.
65 "--with-tzdata=no"
66 ]
67 ++ lib.optionals (lib.versionOlder version "8.6") [
68 # configure check broke due to GCC 14
69 "ac_cv_header_stdc=yes"
70 ]
71 ++ lib.optionals (lib.versionAtLeast version "9.0") [
72 # By default, tcl libraries get zipped and embedded into libtcl*.so,
73 # which gets `zipfs mount`ed at runtime. This is fragile (for example
74 # stripping the .so removes the zip trailer), so we install them as
75 # traditional files.
76 # This might make tcl slower to start from slower storage on cold cache,
77 # however according to my benchmarks on fast storage and warm cache
78 # tcl built with --disable-zipfs actually starts in half the time.
79 "--disable-zipfs"
80 ]
81 ++ [
82 # During cross compilation, the tcl build system assumes that libc
83 # functions are broken if it cannot test if they are broken or not and
84 # then causes a link error on static platforms due to symbol conflict.
85 # These functions are *checks notes* strtoul and strstr. These are
86 # never broken on modern platforms!
87 "tcl_cv_strtod_unbroken=ok"
88 "tcl_cv_strtoul_unbroken=ok"
89 "tcl_cv_strstr_unbroken=ok"
90 ]
91 ++ lib.optional stdenv.hostPlatform.is64bit "--enable-64bit";
92
93 enableParallelBuilding = true;
94
95 postInstall =
96 let
97 dllExtension = stdenv.hostPlatform.extensions.sharedLibrary;
98 staticExtension = stdenv.hostPlatform.extensions.staticLibrary;
99 in
100 ''
101 make install-private-headers
102 ln -s $out/bin/tclsh${release} $out/bin/tclsh
103 if [[ -e $out/lib/libtcl${release}${staticExtension} ]]; then
104 ln -s $out/lib/libtcl${release}${staticExtension} $out/lib/libtcl${staticExtension}
105 fi
106 ${lib.optionalString (!stdenv.hostPlatform.isStatic) ''
107 ln -s $out/lib/libtcl${release}${dllExtension} $out/lib/libtcl${dllExtension}
108 ''}
109 '';
110
111 meta = with lib; {
112 description = "Tcl scripting language";
113 homepage = "https://www.tcl.tk/";
114 license = licenses.tcltk;
115 platforms = platforms.all;
116 maintainers = with maintainers; [ agbrooks ];
117 };
118
119 passthru = rec {
120 inherit release version;
121 libPrefix = "tcl${release}";
122 libdir = "lib/${libPrefix}";
123 tclPackageHook = callPackage (
124 { buildPackages }:
125 makeSetupHook {
126 name = "tcl-package-hook";
127 propagatedBuildInputs = [ buildPackages.makeBinaryWrapper ];
128 meta = {
129 inherit (meta) maintainers platforms;
130 };
131 } ./tcl-package-hook.sh
132 ) { };
133 # verify that Tcl's clock library can access tzdata
134 tests.tzdata = runCommand "${pname}-test-tzdata" { } ''
135 ${baseInterp}/bin/tclsh <(echo "set t [clock scan {2004-10-30 05:00:00} \
136 -format {%Y-%m-%d %H:%M:%S} \
137 -timezone :America/New_York]") > $out
138 '';
139 };
140 };
141
142 mkTclDerivation = callPackage ./mk-tcl-derivation.nix { tcl = baseInterp; };
143
144in
145baseInterp.overrideAttrs (self: {
146 passthru = self.passthru // {
147 inherit mkTclDerivation;
148 };
149})