无法从用 Tcl 编写的应用程序中找到包

Posted

技术标签:

【中文标题】无法从用 Tcl 编写的应用程序中找到包【英文标题】:Can't find package from application written in Tcl 【发布时间】:2020-08-18 14:40:12 【问题描述】:

感谢 cmets,更好地理解了这个问题。变量:

thufir@dur:~/tcl/packages$ 
thufir@dur:~/tcl/packages$ echo 'puts $auto_path' | tclsh
/usr/share/tcltk/tcl8.6 /usr/share/tcltk /usr/lib /usr/local/lib/tcltk /usr/local/share/tcltk /usr/lib/tcltk/x86_64-linux-gnu /usr/lib/tcltk /usr/lib/tcltk/tcl8.6
thufir@dur:~/tcl/packages$ 
thufir@dur:~/tcl/packages$ echo 'puts $tcl_pkgPath' | tclsh
/usr/local/lib/tcltk /usr/local/share/tcltk           /usr/lib/tcltk/x86_64-linux-gnu /usr/lib/tcltk /usr/share/tcltk         /usr/lib/tcltk/tcl8.6 /usr/lib
thufir@dur:~/tcl/packages$ 

代码:

thufir@dur:~/tcl/packages$ 
thufir@dur:~/tcl/packages$ ll
total 16
drwxrwxr-x 2 thufir thufir 4096 May  4 02:22 ./
drwxrwxr-x 6 thufir thufir 4096 May  4 02:22 ../
-rw-rw-r-- 1 thufir thufir  215 May  4 02:21 foo.tcl
-rw-rw-r-- 1 thufir thufir 1207 May  4 02:20 tutstack.tcl
thufir@dur:~/tcl/packages$ 
thufir@dur:~/tcl/packages$ cat foo.tcl 
package require tutstack 1.0

set stack [tutstack::create]
foreach num 1 2 3 4 5  tutstack::push $stack $num 

while  ![tutstack::empty $stack]  
    puts "[tutstack::pop $stack]"


tutstack::destroy $stack
thufir@dur:~/tcl/packages$ 
thufir@dur:~/tcl/packages$ cat tutstack.tcl 
# Register the package
package provide tutstack 1.0
package require Tcl      8.5

# Create the namespace
namespace eval ::tutstack 
    # Export commands
    namespace export create destroy push pop peek empty

    # Set up state
    variable stack
    variable id 0


# Create a new stack
proc ::tutstack::create  
    variable stack
    variable id

    set token "stack[incr id]"
    set stack($token) [list]
    return $token


# Destroy a stack
proc ::tutstack::destroy token 
    variable stack

    unset stack($token)


# Push an element onto a stack
proc ::tutstack::push token elem 
    variable stack

    lappend stack($token) $elem


# Check if stack is empty
proc ::tutstack::empty token 
    variable stack

    set num [llength $stack($token)]
    return [expr $num == 0]


# See what is on top of the stack without removing it
proc ::tutstack::peek token 
    variable stack

    if [empty $token] 
    error "stack empty"
    

    return [lindex $stack($token) end]


# Remove an element from the top of the stack
proc ::tutstack::pop token 
    variable stack

    set ret [peek $token]
    set stack($token) [lrange $stack($token) 0 end-1]
    return $ret

thufir@dur:~/tcl/packages$ 
thufir@dur:~/tcl/packages$ tclsh foo.tcl 
can't find package tutstack 1.0
    while executing
"package require tutstack 1.0"
    (file "foo.tcl" line 1)
thufir@dur:~/tcl/packages$ 

据我了解,我需要compile 提供包裹所在位置的列表或地图。

【问题讨论】:

从技术上讲,这不是语法错误,而是语义错误。命令的语法是对的,但是找不到包…… 您必须在 Tcl 中检查 auto_path,而不是在 bash 环境中:echo 'puts $auto_path' | tclsh @SchelteBron 谢谢,我采纳了你的观点,希望能澄清问题。 【参考方案1】:

运行结果:

thufir@dur:~/tcl/foo$ 
thufir@dur:~/tcl/foo$ tree
.
├── api
│   ├── pkgIndex.tcl
│   └── tutstack.tcl
└── main.tcl

1 directory, 3 files
thufir@dur:~/tcl/foo$ 
thufir@dur:~/tcl/foo$ cat main.tcl 
lappend auto_path /home/thufir/tcl/foo/api
package require tutstack 1.0


set stack [tutstack::create]
foreach num 1 2 3 4 5  tutstack::push $stack $num 

while  ![tutstack::empty $stack]  
    puts "[tutstack::pop $stack]"


tutstack::destroy $stack
thufir@dur:~/tcl/foo$ 
thufir@dur:~/tcl/foo$ cat api/pkgIndex.tcl 
# Tcl package index file, version 1.1
# This file is generated by the "pkg_mkIndex" command
# and sourced either when an application starts up or
# by a "package unknown" script.  It invokes the
# "package ifneeded" command to set up package-related
# information so that packages will be loaded automatically
# in response to "package require" commands.  When this
# script is sourced, the variable $dir must contain the
# full path name of this file's directory.

package ifneeded tutstack 1.0 [list source [file join $dir tutstack.tcl]]
thufir@dur:~/tcl/foo$ 
thufir@dur:~/tcl/foo$ cat api/tutstack.tcl 
# Register the package
package provide tutstack 1.0
package require Tcl      8.5

# Create the namespace
namespace eval ::tutstack 
    # Export commands
    namespace export create destroy push pop peek empty

    # Set up state
    variable stack
    variable id 0


# Create a new stack
proc ::tutstack::create  
    variable stack
    variable id

    set token "stack[incr id]"
    set stack($token) [list]
    return $token


# Destroy a stack
proc ::tutstack::destroy token 
    variable stack

    unset stack($token)


# Push an element onto a stack
proc ::tutstack::push token elem 
    variable stack

    lappend stack($token) $elem


# Check if stack is empty
proc ::tutstack::empty token 
    variable stack

    set num [llength $stack($token)]
    return [expr $num == 0]


# See what is on top of the stack without removing it
proc ::tutstack::peek token 
    variable stack

    if [empty $token] 
    error "stack empty"
    

    return [lindex $stack($token) end]


# Remove an element from the top of the stack
proc ::tutstack::pop token 
    variable stack

    set ret [peek $token]
    set stack($token) [lrange $stack($token) 0 end-1]
    return $ret

thufir@dur:~/tcl/foo$ 
thufir@dur:~/tcl/foo$ tclsh main.tcl 
5
4
3
2
1
thufir@dur:~/tcl/foo$ 

生成配置文件:

thufir@dur:~/tcl/foo/api$ 
thufir@dur:~/tcl/foo/api$ tclsh
% 
%                                             
% pkg_mkIndex . *.tcl
% 
% exit
thufir@dur:~/tcl/foo/api$ 

【讨论】:

【参考方案2】:

问题是 Tcl 没有找到你的包的索引文件(应该叫做pkgIndex.tcl)。如果您已将 weather 1.0 包实现为文件 weather.tcl,那么您可能希望在同一目录中拥有类似这样的索引文件

package ifneeded weather 1.0 [list source [file join $dir weather.tcl]]

这就是说“要加载weather 包的1.0 版本,运行此脚本”,其中脚本在运行时生成并绑定$dir(这是一个始终在包索引加载器所在的上下文中定义的变量)运行package ifneeded)。

一旦找到了,您需要允许 Tcl 找到索引文件。这可以通过将该目录或其直接父目录放在 Tcl 全局 auto_path 列表中来完成;在加载任何包之前在脚本中执行此操作(对于具有内部包的应用程序非常有用),或者您也可以通过设置 TCLLIBPATH 环境变量从 Tcl 外部初始化它。请注意,该变量的值是目录的Tcl 列表,而不是像env(PATH) 这样的系统路径。如果目录名称中有反斜杠或空格,或者如果您想在列表中有多个元素,这很重要。幸运的是,在将单个目录添加为环境变量的情况下,即使在 Windows 上,您通常也可以避免所有这些问题,方法是使用 / 而不是 \,并遵循通常的安装实践并且不在名称中添加空格.在应用程序启动期间添加路径时更容易:您只需使用lappend,可能像这样(在您的主脚本中很早):

lappend auto_path [file join [file dirname [info script]] my_app_pacakges]
# If the script is in foo/bar.tcl then packages are in or below foo/my_app_packages

【讨论】:

以上是关于无法从用 Tcl 编写的应用程序中找到包的主要内容,如果未能解决你的问题,请参考以下文章

无法从 C++ 调用 .Net 服务

从用 C++ 编写的桌面应用程序过渡到基于 Web 的应用程序

如何从用 Swift 编写的 Flutter 平台特定代码启动 ViewController?

如何从用 MFC (MSVC 2008) 编写的应用程序查询基于 CGI 的网络服务器并处理结果?

如何找到TCL程序的位置?

我如何从用 swift 编写的 iOS 中准确导出 csv 文件?