在windows starpack下包含ddb for tdbc :: mysql& tdbc :: postgres的最佳方法是什么?

时间:2013-10-26 21:44:03

标签: mysql windows postgresql tcl

软件包tdbc :: mysql和tdbc :: postgresql需要dll libmysql.dll和libpq.dll一些在PATH中。将这个dll包含在单个starpack中的最佳方法是什么?

现在我正在使用以下pkgIndex.tcl:

if {[catch {package require Tcl 8.6}]} {
    return
}
package ifneeded tdbc::postgres 1.0.0 [list apply {{dir} {
  if { $::tcl_platform(os) eq "Windows NT" &&
        ($::tcl_platform(machine) eq "intel" || 
         $::tcl_platform(machine) ne "amd64") } {
    foreach n {libpq libeay32 ssleay32 comerr32 gssapi32 
               k5sprt32 krb5_32 libiconv-2 libintl-8} {     
      file copy -force [file join $dir ${n}.dll] \
        [file join $::env(WINDIR) System32 ${n}.dll]
    }
  }
  source [file join $dir tdbcpostgres.tcl]
  load [file join $dir tdbcpostgres100.dll] tdbcpostgres
}} $dir]

但这看起来很丑陋。

我试图找到一种方法将必要的库复制到解释器用来加载dll的临时文件夹中。但是通过检查Tcl源代码,找出临时目录的名称是不可能的脚本。

更新:目前,我决定使用twapi来确定Tcl解释器使用的临时文件夹的名称。我得到以下代码:

if {[catch {package require Tcl 8.6}]} {
    return
}
package ifneeded tdbc::postgres 1.0.0 [list apply {{dir} {
  if { $::tcl_platform(os) eq "Windows NT" &&
        ($::tcl_platform(machine) eq "intel" || 
         $::tcl_platform(machine) eq "amd64") } {
    package require twapi
    set _ [file dirname [lindex [lsearch -inline -index 1 -glob \
          [twapi::get_process_modules [twapi::get_current_process_id] -path] \
          {*/twapi_base*.dll}] 1]]
    if { $_ eq "." } { 
      error "couldn't find temp folder name for tdbc::postgres support library" 
    }
    foreach fn [glob -types f -tails -directory $dir "*.dll"] {
      if { [string match -nocase "tdbcpostgres*" $fn] } continue
      file copy -force [file join $dir $fn] [file join $_ $fn]
    }
  } {
    set _ [pwd]
  }
  source [file join $dir tdbcpostgres.tcl]
  set tpwd [pwd]
  cd $_
  catch { load [file join $dir tdbcpostgres100.dll] tdbcpostgres } r o
  cd $tpwd
  return -options $o $r
}} $dir]

但是在程序退出后删除临时文件仍然存在问题。我只看到一个解决方案:在程序开始时扫描文件夹$::env(TEMP)并尝试删除所有名为TCLXXXXXXXX的临时文件夹。

2 个答案:

答案 0 :(得分:1)

如果没有管理员访问权限,将文件复制到c:\windows\system32的“解决方案”将无法运行,而大多数以Windows Vista开头的应用程序都没有。 (你必须选择“以管理员身份运行”)那么system32目录中的新文件怎么样?你只需要替换它们。

一些替代方案:

  • 将所有dll自己复制到一个临时目录,切换到该目录并加载dll(利用您查看的事实以及Windows上的内容):

    package ifneeded tdbc::postgres 1.0.0 [list apply {{dir} {
        set dest [file join $::env(TEMP) tcl[file seconds]]
        file mkdir $dest
        foreach dll [glob -dir $dir *.dll] {
            file copy $dll $dest
        }
        set cwd [pwd]
        cd $dest
        catch {
            source [file join $dir tdbcpostgres.tcl]
            load [file join $dest tdbcpostgres100.dll] tdbcpostgres
        } res opt
        cd $cwd
        return -options $opt $res
    }} $dir]
    

    但我们该如何清理呢?

  • 将dll编译成starpack。这很难。

  • 自己编译扩展,因此它没有任何依赖项。我不知道该怎么做。
  • 自己加载每个必需的dll。这是我最喜欢的解决方案,但它需要twapi:

    package ifneeded tdbc::postgres 1.0.0 [list apply {{dir} {
        package require twapi
        foreach dll [glob -dir $dir *.dll] {
            ::twapi::load_library $dll
        }
        source [file join $dir tdbcpostgres.tcl]
        load [file join $dir tdbcpostgres100.dll] tdbcpostgres
    }} $dir]
    

答案 1 :(得分:1)

该技巧的主要问题是它需要对系统目录的写访问权。你不想那样做。但是,如果找不到引导符号,则可以使用load不撤消库加载的事实。 (这是Tcl通常的“在故障模式下尽可能干净”模型的变化,但它在这里非常有用。)

package ifneeded tdbc::postgres 1.0.0 [list apply {{dir} {
    global tcl_platform
    if {$tcl_platform(os) eq "Windows NT" && $tcl_platform(machine) ne "amd64"} {
        foreach n {libpq libeay32 ssleay32 comerr32 gssapi32 
                   k5sprt32 krb5_32 libiconv-2 libintl-8} {
            if {![file exist [file join $::env(WINDIR) System32 ${n}.dll]]} {
                # Leverage Tcl's built-in loading magic
                catch {load [file join $dir ${n}.dll]}
            }
        }
    }
    source [file join $dir tdbcpostgres.tcl]
    load [file join $dir tdbcpostgres100.dll] tdbcpostgres
}} $dir]

这仍然不是很优雅,但拦截真正的依赖加载机制真是太难了;预加载更容易。 (如果用户已经拥有特定的库,我也停止了代码的操作。)


正确的修复是获取tdbcpostgres100.dll的构建版,其中包含 static 库的其他依赖项。我猜这是相当多的工作;我没试过。