Ticket UUID: | a998df01f3256f79f4d7d635517130012b4001f5 | |||
Title: | Attempt to load platform package in a pkgIndex.tcl generates circular dependency error | |||
Type: | Bug | Version: | 8.7 | |
Submitter: | apnadkarni | Created on: | 2022-07-04 17:08:12 | |
Subsystem: | 39. Package Manager | Assigned To: | jan.nijtmans | |
Priority: | 5 Medium | Severity: | Minor | |
Status: | Closed | Last Modified: | 2022-07-05 09:43:39 | |
Resolution: | Invalid | Closed By: | apnadkarni | |
Closed on: | 2022-07-05 09:43:39 | |||
Description: |
If the
This does not happen with 8.6. It also does not seem to happen if other packages are required within the A sample testpkg is attached. | |||
User Comments: |
apnadkarni added on 2022-07-05 09:43:27:
I'll close this with one last comment. I know the workarounds possible using apply etc. I already do this in some cases, see for example https://github.com/apnadkarni/tcl-cffi/blob/main/pkgIndex.tcl.in. My beef is with the breaking of valid (as in nothing banned this) 8.6 pkgIndex.tcl scripts that took the simpler approach. jan.nijtmans added on 2022-07-05 09:11:00: > I'd construct the pkgIndex.tcl like this: Seconded dkf added on 2022-07-05 09:01:44:
I'd construct the package ifneeded testpkg 0.1 [list apply {{dir} { package require platform set plat [platform::identify] set ext [info sharedlibextension] uplevel 1 [list load [file join $dir $plat mybinaryextension.$ext]] }} $dir]The uplevel 1 wrapper is, of course, hyper-correctness that only a few C packages would ever need. You could probably get away with:
package ifneeded testpkg 0.1 [list apply {{dir} { package require platform set plat [platform::identify] set ext [info sharedlibextension] load [file join $dir $plat mybinaryextension.$ext] }} $dir]As you can see, this gives you local variables and simple substitution rules without needing an extra command. (It's only a problem if you want to support 8.4 and before, but that's out of all support now...) jan.nijtmans added on 2022-07-05 06:42:22:
Of course, a ';' is missing, and "platform::identity" should be delayed: package require platform package ifneeded testpkg 0.1 "package require platform; \ load \[file join [list $dir] \[platform::identify\] mybinaryextension.[info sharedlibextension]\]" I wouldn't use "platform::identify", since "platform::identify" contains the OS version as well: Do you really want separate directories for MacOS 10.4, up to 12.4? Maybe "platform::generic" is better. Or $tcl_platform(os) (but that wouldn't distingish processor) or $tcl_platform(os)/$tcl_platform(machine) jan.nijtmans added on 2022-07-05 06:20:39: In stead of this: package require platform package ifneeded testpkg 0.1 [list load [file join $dir [platform::identify] mybinaryextension.[info sharedlibextension]] You should do: package require platform package ifneeded testpkg 0.1 "package require platform \ [list load [file join $dir [platform::identify] mybinaryextension.[info sharedlibextension]]" Then the "package require platform" is executed when testpkg is required. The problem with your approach is that the following could happen: $ tclsh8.7 $ package files platform; # empty result, since "platform" is not loaded yet $ package require Thread % package files platform; # "platform" is - unexpectedly - loaded now /usr/local/lib/tcl8/8.4/platform-1.0.18.tm(whether this happens or not depends on the order the "pkgIndex.tcl" files are found) So, a "package require Thread" results in pkgIndex.tcl files being sourced (in order to see where "Thread" is. This has a side-effect that the "platform" package is loaded, but it is not needed by "Thread" at all. It should be documented that a "pkgIndex.tcl" file should not contain another "package require" nor "package load", unless it's within a "package ifneeded". apnadkarni added on 2022-07-05 02:37:51: I understand why this is happening but that does not mean it's not a bug.
Given the above, I'm not sure why you say the correct way is to move it to testpkg.tcl. I would say it's a workaround for a bug! As to why not move it into the testpkg.tcl file, the reason is that there may not be a testpkg.tcl file at all, only a binary. The example I gave was just for illustration. The actual pkgIndex.tcl file would look something like this (pseudo-code, no existence checks etc)
This permits a multi-platform install/distribution. You are now saying pkgIndex.tcl should not do a load either. Now in 8.7, an extra unnecessary .tcl file is needed where none was needed before. This is a step back but more important, binary extensions as above will no longer load in 8.7. Not just that, they actually break the package loading for all packages. Re-opening as I do not consider this as an invalid ticket or user error. /Ashok jan.nijtmans added on 2022-07-04 19:09:05: Well - first - if 'testpkg' needs 'platform', `the correct` way to do this is as follows: ########## pkgIndex.tcl ######## package ifneeded testpkg 0.1 [list source [file join $dir testpkg.tcl]] The pkgIndex system is complex. If a package is not found when doing a "package require", more file paths are searched for a 'pkgIndex' file. Therefore, a 'pkgIndex' file should only contain commands which have no side-effects, it should NEVER do another 'package require' directly or a 'load'. 'pkgIndex' files should hardly do anything more than 'package ifneeded'. The reason Tcl 8.7 discovers this and 8.6 doesn't is TIP #459. Tcl 8.7 keeps track, which files are sourced in which package initialization, so 'package files' can report it. Maybe the error-message could be better ..... Does this explanation make it a little bit more clear? apnadkarni added on 2022-07-04 17:24:55: The errorInfo from the above:
|