From 910bdbb033609558bba2d2bb443819b31ad60709 Mon Sep 17 00:00:00 2001 From: Francesco Gazzetta Date: Mon, 16 Mar 2026 17:57:47 +0100 Subject: [PATCH 1/5] tcl.tclRequiresCheckHook: init Add a hook that checks that Tcl packages can be `require`d correctly, like `pythonImportsCheck`. --- doc/languages-frameworks/tcl.section.md | 32 +++++++++++++++++++ doc/redirects.json | 3 ++ pkgs/development/interpreters/tcl/generic.nix | 10 ++++++ .../interpreters/tcl/mk-tcl-derivation.nix | 13 +++++--- .../tcl/tcl-requires-check-hook.sh | 17 ++++++++++ 5 files changed, 71 insertions(+), 4 deletions(-) create mode 100644 pkgs/development/interpreters/tcl/tcl-requires-check-hook.sh diff --git a/doc/languages-frameworks/tcl.section.md b/doc/languages-frameworks/tcl.section.md index 71ec9d89eb50..5215e2b3f6a2 100644 --- a/doc/languages-frameworks/tcl.section.md +++ b/doc/languages-frameworks/tcl.section.md @@ -52,3 +52,35 @@ Its use is documented in `pkgs/development/tcl-modules/by-name/README.md`. All Tcl applications reside elsewhere. In case a package is used as both a library and an application (for example `expect`), it should be defined in `tcl-packages.nix`, with an alias elsewhere. + +### Using tclRequiresCheck {#using-tclrequirescheck} + +Although unit tests are highly preferred to validate correctness of a package, not +all packages have test suites that can be run easily, and some have none at all. +To help ensure the package still works, [`tclRequiresCheck`](#using-tclrequirescheck) can attempt to `package require` +the listed modules. + +```nix +{ + tclRequiresCheck = [ + "json" + "doctools" + ]; +} +``` + +roughly translates to: + +```nix +{ + preDist = '' + TCLLIBPATH="$out/lib $TCLLIBPATH" + tclsh <<<'exit [catch {package require json; package require doctools}]' + ''; +} +``` + +However, this is done in its own phase, and not dependent on whether [`doCheck = true;`](#var-stdenv-doCheck). + +This can also be useful in verifying that the package doesn't assume commonly +present packages (e.g. `tcllib`). diff --git a/doc/redirects.json b/doc/redirects.json index 03285903666e..8623f60f067d 100644 --- a/doc/redirects.json +++ b/doc/redirects.json @@ -752,6 +752,9 @@ "typst-package-scope-and-usage": [ "index.html#typst-package-scope-and-usage" ], + "using-tclrequirescheck": [ + "index.html#using-tclrequirescheck" + ], "var-go-buildTestBinaries": [ "index.html#var-go-buildTestBinaries" ], diff --git a/pkgs/development/interpreters/tcl/generic.nix b/pkgs/development/interpreters/tcl/generic.nix index 9f2e781124e5..53d076f878d1 100644 --- a/pkgs/development/interpreters/tcl/generic.nix +++ b/pkgs/development/interpreters/tcl/generic.nix @@ -142,6 +142,16 @@ let }; } ./tcl-package-hook.sh ) { }; + tclRequiresCheckHook = callPackage ( + { buildPackages }: + makeSetupHook { + name = "tcl-requires-check-hook"; + propagatedBuildInputs = [ buildPackages.makeBinaryWrapper ]; + meta = { + inherit (meta) maintainers platforms; + }; + } ./tcl-requires-check-hook.sh + ) { }; # verify that Tcl's clock library can access tzdata tests.tzdata = runCommand "${pname}-test-tzdata" { } '' ${baseInterp}/bin/tclsh <(echo "set t [clock scan {2004-10-30 05:00:00} \ diff --git a/pkgs/development/interpreters/tcl/mk-tcl-derivation.nix b/pkgs/development/interpreters/tcl/mk-tcl-derivation.nix index 55da68361071..11f46d414ab5 100644 --- a/pkgs/development/interpreters/tcl/mk-tcl-derivation.nix +++ b/pkgs/development/interpreters/tcl/mk-tcl-derivation.nix @@ -51,10 +51,15 @@ let // { buildInputs = buildInputs ++ [ tcl.tclPackageHook ]; - nativeBuildInputs = nativeBuildInputs ++ [ - makeWrapper - tcl - ]; + nativeBuildInputs = + nativeBuildInputs + ++ [ + makeWrapper + tcl + ] + ++ lib.optionals (stdenv.buildPlatform.canExecute stdenv.hostPlatform) [ + tcl.tclRequiresCheckHook + ]; propagatedBuildInputs = propagatedBuildInputs ++ [ tcl ]; env = { diff --git a/pkgs/development/interpreters/tcl/tcl-requires-check-hook.sh b/pkgs/development/interpreters/tcl/tcl-requires-check-hook.sh new file mode 100644 index 000000000000..0027a0c4b386 --- /dev/null +++ b/pkgs/development/interpreters/tcl/tcl-requires-check-hook.sh @@ -0,0 +1,17 @@ +# Setup hook for checking whether Tcl requires succeed +echo "Sourcing tcl-requires-check-hook.sh" + +tclRequiresCheckPhase () { + echo "Executing tclRequiresCheckPhase" + + if [ -n "$tclRequiresCheck" ]; then + echo "Check whether the following packages can be required: $tclRequiresCheck" + export TCLLIBPATH="$out/lib $TCLLIBPATH" # Redundant if tcl-package-hook is also used + tclsh <<<'exit [catch {foreach req $env(tclRequiresCheck) {package require $req}}]' + fi +} + +if [ -z "${dontUseTclRequiresCheck-}" ]; then + echo "Using tclRequiresCheckPhase" + preDistPhases+=" tclRequiresCheckPhase" +fi From dcc17ba988a0e1b6abc651d709c168e84e8af52d Mon Sep 17 00:00:00 2001 From: Francesco Gazzetta Date: Mon, 16 Mar 2026 18:01:41 +0100 Subject: [PATCH 2/5] tclPackages.expect: use tclRequiresCheck --- pkgs/development/tcl-modules/by-name/ex/expect/package.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkgs/development/tcl-modules/by-name/ex/expect/package.nix b/pkgs/development/tcl-modules/by-name/ex/expect/package.nix index a30305b890b7..868a64cdfd3e 100644 --- a/pkgs/development/tcl-modules/by-name/ex/expect/package.nix +++ b/pkgs/development/tcl-modules/by-name/ex/expect/package.nix @@ -62,6 +62,8 @@ tcl.mkTclDerivation rec { ${lib.optionalString stdenv.hostPlatform.isDarwin "tclWrapperArgs+=(--prefix DYLD_LIBRARY_PATH : $out/lib/expect${version})"} ''; + tclRequiresCheck = [ "Expect" ]; + outputs = [ "out" "dev" From b748eac9991f87504a072942e300b7a9274d9866 Mon Sep 17 00:00:00 2001 From: Francesco Gazzetta Date: Mon, 16 Mar 2026 18:01:57 +0100 Subject: [PATCH 3/5] tclPackages.tcllib: use tclRequiresCheck --- .../tcl-modules/by-name/tc/tcllib/package.nix | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pkgs/development/tcl-modules/by-name/tc/tcllib/package.nix b/pkgs/development/tcl-modules/by-name/tc/tcllib/package.nix index 33ca7b53001b..1d57dc6ec0c2 100644 --- a/pkgs/development/tcl-modules/by-name/tc/tcllib/package.nix +++ b/pkgs/development/tcl-modules/by-name/tc/tcllib/package.nix @@ -19,6 +19,15 @@ mkTclDerivation rec { buildFlags = [ "all" ] ++ lib.optional withCritcl "critcl"; + # Tcllib contains a huge amount of Tcl packages: + # https://core.tcl-lang.org/tcllib/doc/trunk/embedded/md/toc.md + # We sample a small amount of popular ones. + tclRequiresCheck = [ + "struct::graph" + "doctools" + "json" + ]; + meta = { homepage = "https://core.tcl-lang.org/tcllib/"; description = "Tcl-only library of standard routines for Tcl"; From 469a917cd6013440170ea8d9d6565f31f92f30b7 Mon Sep 17 00:00:00 2001 From: Francesco Gazzetta Date: Mon, 16 Mar 2026 18:02:21 +0100 Subject: [PATCH 4/5] tclPackages.tclreadline: use tclRequiresCheck --- pkgs/development/tcl-modules/by-name/tc/tclreadline/package.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkgs/development/tcl-modules/by-name/tc/tclreadline/package.nix b/pkgs/development/tcl-modules/by-name/tc/tclreadline/package.nix index 6ba554caacf8..56e061838e97 100644 --- a/pkgs/development/tcl-modules/by-name/tc/tclreadline/package.nix +++ b/pkgs/development/tcl-modules/by-name/tc/tclreadline/package.nix @@ -58,6 +58,8 @@ mkTclDerivation rec { done ''; + tclRequiresCheck = [ "tclreadline" ]; + meta = { description = "GNU readline for interactive tcl shells"; homepage = "https://github.com/flightaware/tclreadline"; From ef4315efe9f18f8d69f5cb5ff94fbdd602936b78 Mon Sep 17 00:00:00 2001 From: Francesco Gazzetta Date: Mon, 16 Mar 2026 18:02:36 +0100 Subject: [PATCH 5/5] tclPackages.tcltls: use tclRequiresCheck --- pkgs/development/tcl-modules/by-name/tc/tcltls/package.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkgs/development/tcl-modules/by-name/tc/tcltls/package.nix b/pkgs/development/tcl-modules/by-name/tc/tcltls/package.nix index f5f2d99b284d..02621ea9a8c3 100644 --- a/pkgs/development/tcl-modules/by-name/tc/tcltls/package.nix +++ b/pkgs/development/tcl-modules/by-name/tc/tcltls/package.nix @@ -20,6 +20,8 @@ mkTclDerivation rec { "--with-ssl-dir=${openssl.dev}" ]; + tclRequiresCheck = [ "tls" ]; + meta = { homepage = "https://core.tcl-lang.org/tcltls/index"; description = "OpenSSL / RSA-bsafe Tcl extension";