Tcl Library Source Code

View Ticket
Login
Ticket UUID: 8f0ff730836505e298defe424329cd0fb9527efc
Title: tcllibc and sha256c leads to error "Headers for API sha256c not found in"
Type: Bug Version:
Submitter: pooryorick Created on: 2024-09-17 11:28:24
Subsystem: sha1 Assigned To: aku
Priority: 5 Medium Severity: Minor
Status: Open Last Modified: 2024-09-20 21:48:46
Resolution: None Closed By: nobody
    Closed on:
Description:
After removing ~/.critcl, the following script,

<code><verbatim>
package require critcl
package require sha256
package require sha256c
::critcl::tcl 9.0 
::critcl::ccode {}
::critcl::api import sha256c 1
</verbatim></code>

, results in the error:

<code><verbatim>
Headers for API sha256c not found in 
-       /path/to/.critcl/linux-unknown-x86_64
    while executing
"error "Headers for API $name not found in \n-\t[join $searched \n-\t]""
    (procedure "::critcl::APIimport" line 24)
    invoked from within
"::critcl::APIimport /path/to/testcript sha256c 1"
    ("eval" body line 1)
    invoked from within
"eval [linsert $args 0 ::critcl::API$cmd $file]"
    (procedure "::critcl::api" line 10)
    invoked from within
"::critcl::api import sha256c 1"
</verbatim></code>

With the following modification the script completes successfully :

<code><verbatim>
--- modules/sha1/sha256.tcl
+++ modules/sha1/sha256.tcl
@@ -81,12 +81,12 @@
         tcl {
             # Already present (this file)
             set r 1
         }
         critcl {
-            if {![catch {package require tcllibc}]
-                || ![catch {package require sha256c}]} {
+            if {
+                ![catch {package require sha256c}]} {
                 set r [expr {[info commands ::sha2::sha256c_update] != {}}]
             }
         }
         default {
             return -code error "invalid accelerator $key:\
</verbatim></code>

The issue is that if [package require tcllibc] is ecxecuted first, then
[package require sha256c] does not lead to the evaluation of sha256c.tcl, and
therefore the line "critcl::cheaders sha256.h" is never encountered.
User Comments: aku added on 2024-09-20 21:48:46:
The only change done so far was the changed search order, i.e. sha256c before tcllibc.

Hm.

pooryorick added on 2024-09-20 21:28:06:

The "package forget sha256c" trick no-longer works. In fact, I had to remove it to avoid this error:

This implementation of Sha256c does not support stubs


aku added on 2024-09-19 13:56:00:

Option -includedir implies -I. I.e. whatever is set as destination for stubs headers, is also a search path for stubs headers. When providing multiple -includedirs all become search paths, and the last is the set destination.

At the critcl package level the relevant command for the search paths is critcl::config I, see

proc ::critcl::app::AddIncludePath {path} {
    set dirs [critcl::config I]
    lappend dirs [file normalize $path]
    critcl::config I $dirs
    return
}

for the code handling the -I. And -includedir is handled via

	    includedir {
		set v::incdir  $arg
		AddIncludePath $arg
	    }

The main issue seems to be to get a proper match between where the include files were stored during build of Tcllibc/Sha256c, and where the include files are searched at when compiling the othr package.

And that is configurable with either options, when the critcl command is used, and via the critcl::config command, when the critcl package is used, instead of the app around it.


aku added on 2024-09-19 08:16:05:

The headers are installed when critcl is run, either directly, or through sak.tcl critcl.

Running the command ./sak.tcl critcl from the top dir of the tcllib checkout executes critcl as

<path/to/critcl> -cache <pwd>/.critcl -force -libdir ./modules -pkg tcllibc <files...>

This places the generated tcllibc package into <pwd>/modules/tcllibc (See -libdir setting above). It also places the include files into the include sibling directory of the libdir, i.e. <pwd>/include. This means that the sha256 headers land in <pwd>/include/sha256c/.

This is noted in the output of ./sak.tcl / critcl.

Package Placed Into: modules/tcllibc
Headers Placed Into: include/sha256c

critcl's -includedir option can be used to change the destination for the include files.

I believe that the -includedir is also made on the directories where headers will be searched. If not, then -I ... will extend the search path. See output of critcl -h.

If you do not specify the lib- and include dir critcl uses local lib and include.

Examples:

critcl -cache .critcl-sha -force -libdir ./x -includedir ./x/include -pkg sha256c ./modules/sha1/sha256c.tcl
=>
Package Placed Into: x/sha256c
Headers Placed Into: ./x/include/sha256c

critcl -cache .critcl-sha -force -pkg sha256c ./modules/sha1/sha256c.tcl
=>
Package Placed Into: lib/sha256c
Headers Placed Into: include/sha256c

Notes:

  1. My use of -cache in the examples is just to avoid clashing with any kind of defaults.

  2. ./sak.tcl critcl does not pass most of critcl's options to the underlying command. IOW to play with -libdir, etc. you have to use critcl directly.

    I believe I will work on lifting that restriction as part of ticket https://core.tcl-lang.org/tcllib/tktview/4799af766d


pooryorick added on 2024-09-19 05:32:24:

Would the "real solution" be something like generating tcllibc/pkgIndex.tcl in such a way that in addition to loading the shared object it also makes relevant header files available to critcl somehow?


aku added on 2024-09-17 19:39:10:
For now I have committed the search order tweak, see commit [692adc1efb].
Bumped to version 1.0.6

aku added on 2024-09-17 19:29:54:

Real solution. Heh. ... Currently letting this simmer in the back of my mind.

I was about to write that the issue is a consequence of tcllibc a superset/bundle of all the individual accelerators present in Tcllib.

Then I decided to check something.

Running ./sak.tcl critcl -keep generates the modules/tcllibc and also keeps the intermediate files, both generated .c and .o.

Grepping for Pkg finds only

.critcl/v321_00000000000000000000000000000205.c:Tcl_PkgProvideEx (ip, "sha256c", "1.0.5", (ClientData) &sha256cStubs);

Nothing for anything else with an accelerator.

Playing interactively with the tcllibc shlib:

% pack re tcllibc
0.4
% info loaded
{/home/aku/opt/ActiveTcl/lib/tcllibc/linux-x86_64/tcllibc.so Tcllibc}
% pa ifneeded tcllibc 0.4
::apply {dir {
    source [file join $dir critcl-rt.tcl]
    set path [file join $dir [::critcl::runtime::MapPlatform]]
    set ext [info sharedlibextension]
    set lib [file join $path "tcllibc$ext"]
    load $lib Tcllibc
    package provide tcllibc 0.4
}} /home/aku/opt/ActiveTcl/lib/tcllibc
% pa versions sha256c 
1.0.4
% pa ifneeded sha256c 1.0.4
source /home/aku/opt/ActiveTcl/lib/tcllib1.21/sha1/sha256c.tcl
% pa versions md5c
%  

Confirmed. It seems that sha256c is the only module which registers itself within the bundled tcllibc. Nothing else looks to be.

And the difference looks to be that the sha256c has critcl:api calls to export a stubs table.

This triggers the addition of the Tcl_PkgProvideEx line.

Adding a temp fake stubs table to sha1, and it gets a Tcl_PkgProvideEx line as well.

And your demo case absolutely desires (to import) the stubs table of sha256.

We cannot get rid of this provide call.

Without it the stubs table is not registered, and the corresponding Tcl_PkgRequireEx cannot pull it for use. See lib/stubs_gen_lib/gen_lib.tcl in Critcl.

No ideas coming yet beyond the known package forget sha256c workaround.

For sha256c it is indeed the issue that both sha256c and tcllibc will register the sha256c package for stub table support.


pooryorick added on 2024-09-17 14:54:40:

My use case is the latter: compile-at runtime. package forget sha256c does work around the issue. Do you foresee a "real" solution as well later?


aku added on 2024-09-17 14:17:18:

Ah. The package require tcllibc loads the tcllibc shlib, and that runs a package provide sha256c internally, so the require sha256c short-circuits, i.e. becomes a nop.

Is your situation the ahead-of-time compilation of a package using sha256c ? Or is it using critcl/sha256c in compile-at-runtime mode ? From the description it looks it feels to be latter. However it may simply be the reduction of your larger test case.

You could try to put a package forget sha256c just before the package require sha256c. That should be a nop if neither sha256c nor tcllibc are loaded.

However, when a tcllibc was loaded before, then the forget should cancel the provide sha256 tcllibc made, forcing critcl into the execution of sha256c.tcl.


pooryorick added on 2024-09-17 13:27:12:

Yes, as long as sha256c is required before tcllibc is required, then things are fine. The problem with that, of course, is that my own package can't control whether someone else requires tcllibc before requiring my package.


aku added on 2024-09-17 11:41:53:
Does it work if the test for tcllibc is left in, just done after sha256c ?