installer.tcl

Go to the documentation of this file.
00001 /* !/bin/sh*/
00002 /*  -*- tcl -*- \*/
00003 exec tclsh "$0" ${1+"$@"}
00004 
00005 /*  --------------------------------------------------------------*/
00006 /*  Installer for Tcllib. The lowest version of the tcl core supported*/
00007 /*  by any module is 8.2. So we enforce that the installer is run with*/
00008 /*  at least that.*/
00009 
00010 package require Tcl 8.2
00011 
00012  distribution =    [file dirname [info script]]
00013 lappend auto_path  [file join $distribution modules]
00014 
00015 
00016 /*  --------------------------------------------------------------*/
00017 /*  Version information for tcllib.*/
00018 /*  List of modules to install (and definitions guiding the process)*/
00019 
00020 ret  package_name    (type text) {global package_name    ; set package_name    $text}
00021 ret  package_version (type text) {global package_version ; set package_version $text}
00022 ret  dist_exclude    (type path) {}
00023 ret  critcl       (type name , type files) {}
00024 ret  critcl_main  (type name , type files) {}
00025 ret  critcl_notes (type text) {}
00026 
00027 source [file join $distribution support installation version.tcl] ; /*  Get version information.*/
00028 source [file join $distribution support installation modules.tcl] ; /*  Get list of installed modules.*/
00029 source [file join $distribution support installation actions.tcl] ; /*  Get code to perform install actions.*/
00030 
00031  package = _nv ${package_name}-${package_version}
00032  package = _name_cap [string toupper [string index $package_name 0]][string range $package_name 1 end]
00033 
00034 /*  --------------------------------------------------------------*/
00035 /*  Low-level commands of the installation engine.*/
00036 
00037 ret  gen_main_index (type outdir , type package , type version) {
00038     global config
00039 
00040     log "\nGenerating [file join $outdir pkgIndex.tcl]"
00041     if {$config(dry)} {return}
00042 
00043     set   index [open [file join $outdir pkgIndex.tcl] w]
00044 
00045     puts $index "# Tcl package index file, version 1.1"
00046     puts $index "# Do NOT edit by hand.  Let $package install generate this file."
00047     puts $index "# Generated by $package installer for version $version"
00048 
00049     puts $index {
00050 # All tcllib packages need Tcl 8 (use [namespace])
00051 if {![package vsatisfies [package provide Tcl] 8]} {return}
00052 
00053 # Extend the auto_path to make tcllib packages available
00054 if {[lsearch -exact $::auto_path $dir] == -1} {
00055     lappend ::auto_path $dir
00056 }
00057 
00058 # For Tcl 8.3.1 and later, that's all we need
00059 if {[package vsatisfies [package provide Tcl] 8.4]} {return}
00060 if {(0 == [catch {
00061     package vcompare [info patchlevel] [info patchlevel]
00062 }]) && (
00063     [package vcompare [info patchlevel] 8.3.1] >= 0
00064 )} {return}
00065 
00066 # For older Tcl releases, here are equivalent contents
00067 # of the pkgIndex.tcl files of all the modules
00068 
00069 if {![package vsatisfies [package provide Tcl] 8.0]} {return}
00070 }
00071     puts $index ""
00072     puts $index "set maindir \$dir"
00073 
00074     foreach pi [lsort [glob -nocomplain [file join $outdir * pkgIndex.tcl]]] {
00075     set subdir [file tail [file dirname $pi]]
00076     puts $index "set dir \[file join \$maindir [list $subdir]\] ;\t source \[file join \$dir pkgIndex.tcl\]"
00077     }
00078 
00079     puts  $index "unset maindir"
00080     puts  $index ""
00081     close $index
00082     return
00083 }
00084 
00085 ret  xcopyfile (type src , type dest) {
00086     # dest can be dir or file
00087     run file copy -force $src $dest
00088     return
00089 }
00090 
00091 ret  xcopy (type src , type dest , type recurse , optional pattern =*) {
00092     run file mkdir $dest
00093 
00094     if {[string equal $pattern *] || !$recurse} {
00095     foreach file [glob [file join $src $pattern]] {
00096         set base [file tail $file]
00097         set sub  [file join $dest $base]
00098 
00099         if {0 == [string compare CVS $base]} {continue}
00100 
00101         if {[file isdirectory $file]} then {
00102         if {$recurse} {
00103             run file mkdir  $sub
00104             xcopy $file $sub $recurse $pattern
00105 
00106             # If the directory is empty after the recursion remove it again.
00107             if {![llength [glob -nocomplain [file join $sub *]]]} {
00108             file delete $sub
00109             }
00110         }
00111         } else {
00112         xcopyfile $file $sub
00113         }
00114     }
00115     } else {
00116     foreach file [glob [file join $src *]] {
00117         set base [file tail $file]
00118         set sub  [file join $dest $base]
00119 
00120         if {[string equal CVS $base]} {continue}
00121 
00122         if {[file isdirectory $file]} then {
00123         if {$recurse} {
00124             run file mkdir $sub
00125             xcopy $file $sub $recurse $pattern
00126 
00127             # If the directory is empty after the recursion remove it again.
00128             if {![llength [glob -nocomplain [file join $sub *]]]} {
00129             run file delete $sub
00130             }
00131         }
00132         } else {
00133         if {![string match $pattern $base]} {continue}
00134         xcopyfile $file $sub
00135         }
00136     }
00137     }
00138 }
00139 
00140 ret  get_input (type f) {return [read [set if [open $f r]]][close $if]}
00141 ret  write_out (type f , type text) {
00142     global config
00143     if {$config(dry)} {log "Generate $f" ; return}
00144     catch {file delete -force $f}
00145     puts -nonewline [set of [open $f w]] $text
00146     close $of
00147 }
00148 
00149 
00150 /*  --------------------------------------------------------------*/
00151 /*  Use configuration to perform installation*/
00152 
00153 ret  clear ()     {global message ; set     message ""}
00154 ret  msg   (type text) {global message ; append  message $text \n ; return}
00155 ret  get   ()     {global message ; return $message}
00156 
00157 ret  log (type text) {
00158     global config
00159     if {!$config(gui)} {puts stdout $text ; flush stdout ; return}
00160     .l.t insert end $text\n
00161     .l.t see    end
00162     update
00163     return
00164 }
00165 ret  log* (type text) {
00166     global config
00167     if {!$config(gui)} {puts -nonewline stdout $text ; flush stdout ; return}
00168     .l.t insert end $text
00169     .l.t see    end
00170     update
00171     return
00172 }
00173 
00174 ret  run (type args) {
00175     global config
00176     if {$config(dry)} {
00177     log [join $args]
00178     return
00179     }
00180     if {[catch {eval $args} msg]} {
00181         if {$config(gui)} {
00182             installErrorMsgBox $msg
00183         } else {
00184             return -code error "Install error:\n $msg" 
00185         }
00186     }
00187     log* .
00188     return
00189 }
00190 
00191 ret  xinstall (type type , type args) {
00192     global modules guide
00193     foreach m $modules {
00194     eval $guide($m,$type) $m $args
00195     }
00196     return
00197 }
00198 
00199 ret  ainstall () {
00200     global apps config tcl_platform distribution
00201 
00202     if {[string compare $tcl_platform(platform) windows] == 0} {
00203     set ext .tcl
00204     } else {
00205     set ext ""
00206     }
00207 
00208     foreach a $apps {
00209     set aexe [file join $distribution apps $a]
00210     set adst [file join $config(app,path) ${a}$ext]
00211 
00212     log "\nGenerating $adst"
00213     if {!$config(dry)} {
00214         file mkdir [file dirname  $adst]
00215         catch {file delete -force $adst}
00216         file copy -force $aexe    $adst
00217     }
00218 
00219     if {[file exists $aexe.man]} {
00220         if {$config(doc,nroff)} {
00221         _manfile $aexe.man nroff n $config(doc,nroff,path)
00222         }
00223         if {$config(doc,html)} {
00224         _manfile $aexe.man html html $config(doc,html,path)
00225         }
00226     }
00227     }
00228     return
00229 }
00230 
00231 ret  doinstall () {
00232     global config package_version distribution package_name modules excluded
00233 
00234     if {!$config(no-exclude)} {
00235     foreach p $excluded {
00236         set pos [lsearch -exact $modules $p]
00237         if {$pos < 0} {continue}
00238         set modules [lreplace $modules $pos $pos]
00239     }
00240     }
00241 
00242     if {$config(doc,nroff)} {
00243     set config(man.macros) [string trim [get_input \
00244         [file join $distribution support installation man.macros]]]
00245     }
00246     if {$config(pkg)}       {
00247     xinstall   pkg $config(pkg,path)
00248     gen_main_index $config(pkg,path) $package_name $package_version
00249     if {$config(doc,nroff)} {
00250         xinstall doc nroff n    $config(doc,nroff,path)
00251     }
00252     if {$config(doc,html)}  {
00253         xinstall doc html  html $config(doc,html,path)
00254     }
00255     }
00256     if {$config(exa)}       {xinstall exa $config(exa,path)}
00257     if {$config(app)}       {ainstall}
00258     log ""
00259     return
00260 }
00261 
00262 
00263 /*  --------------------------------------------------------------*/
00264 /*  Initialize configuration.*/
00265 
00266 array  config =  {
00267     pkg 1 pkg,path {}
00268     app 1 app,path {}
00269     doc,nroff 0 doc,nroff,path {}
00270     doc,html  0 doc,html,path  {}
00271     exa 1 exa,path {}
00272     dry 0 wait 1 valid 1
00273     gui 0 no-gui 0 no-exclude 0
00274 }
00275 
00276 /*  --------------------------------------------------------------*/
00277 /*  Determine a default configuration, if possible*/
00278 
00279 ret  defaults () {
00280     global tcl_platform config package_version package_name distribution
00281 
00282     if {[string compare $distribution [info nameofexecutable]] == 0} {
00283     # Starpack. No defaults for location.
00284     } else {
00285     # Starkit, or unwrapped. Derive defaults location from the
00286     # location of the executable running the installer, or the
00287     # location of its library.
00288 
00289     # For a starkit [info library] is inside the running
00290     # tclkit. Detect this and derive the lcoation from the
00291     # location of the executable itself for that case.
00292 
00293     if {[string match [info nameofexecutable]* [info library]]} {
00294         # Starkit
00295         set libdir [file join [file dirname [file dirname [info nameofexecutable]]] lib]
00296     } else {
00297         # Unwrapped.
00298         if {[catch {set libdir [lindex $::tcl_pkgPath end]}]} {
00299         set libdir [file dirname [info library]]
00300         }
00301     }
00302 
00303     set basedir [file dirname $libdir]
00304     set bindir  [file join $basedir bin]
00305 
00306     if {[string compare $tcl_platform(platform) windows] == 0} {
00307         set mandir  {}
00308         set htmldir [file join $basedir ${package_name}_doc]
00309     } else {
00310         set mandir  [file join $basedir man mann]
00311         set htmldir [file join $libdir  ${package_name}${package_version} ${package_name}_doc]
00312     }
00313 
00314     set config(app,path)       $bindir
00315     set config(pkg,path)       [file join $libdir ${package_name}${package_version}]
00316     set config(doc,nroff,path) $mandir
00317     set config(doc,html,path)  $htmldir
00318     set config(exa,path)       [file join $bindir ${package_name}_examples${package_version}]
00319     }
00320 
00321     if {[string compare $tcl_platform(platform) windows] == 0} {
00322     set config(doc,nroff) 0
00323     set config(doc,html)  1
00324     } else {
00325     set config(doc,nroff) 1
00326     set config(doc,html)  0
00327     }
00328     return
00329 }
00330 
00331 /*  --------------------------------------------------------------*/
00332 /*  Show configuration on stdout.*/
00333 
00334 ret  showpath (type prefix , type key) {
00335     global config
00336 
00337     if {$config($key)} {
00338     if {[string length $config($key,path)] == 0} {
00339         puts "${prefix}Empty path, invalid."
00340         set config(valid) 0
00341         msg "Invalid path: [string trim $prefix "   :"]"
00342     } else {
00343         puts "${prefix}$config($key,path)"
00344     }
00345     } else {
00346     puts "${prefix}Not installed."
00347     }
00348 }
00349 
00350 ret  showconfiguration () {
00351     global config package_version package_name_cap
00352 
00353     puts "Installing $package_name_cap $package_version"
00354     if {$config(dry)} {
00355     puts "\tDry run, simulation, no actual activity."
00356     puts ""
00357     }
00358 
00359     puts "You have chosen the following configuration ..."
00360     puts ""
00361 
00362     showpath "Packages:      " pkg
00363     showpath "Applications:  " app
00364     showpath "Examples:      " exa
00365 
00366     if {$config(doc,nroff) || $config(doc,html)} {
00367     puts "Documentation:"
00368     puts ""
00369 
00370     showpath "\tNROFF:  " doc,nroff
00371     showpath "\tHTML:   " doc,html
00372     } else {
00373     puts "Documentation: Not installed."
00374     }
00375     puts ""
00376     return
00377 }
00378 
00379 /*  --------------------------------------------------------------*/
00380 /*  Setup the installer user interface*/
00381 
00382 ret  browse (type label , type key) {
00383     global config
00384 
00385     set  initial $config($key)
00386     if {$initial == {}} {set initial [pwd]}
00387 
00388     set dir [tk_chooseDirectory \
00389         -title    "Select directory for $label" \
00390         -parent    . \
00391         -initialdir $initial \
00392         ]
00393 
00394     if {$dir == {}} {return} ; # Cancellation
00395 
00396     set config($key)  $dir
00397     return
00398 }
00399 
00400 ret  setupgui () {
00401     global config package_name_cap package_version
00402     set config(gui) 1
00403 
00404     wm withdraw .
00405     wm title . "Installing $package_name_cap $package_version"
00406 
00407     foreach {w type cspan col row opts} {
00408     .pkg checkbutton 1 0 0 {-anchor w -text {Packages:}     -variable config(pkg)}
00409     .app checkbutton 1 0 1 {-anchor w -text {Applications:} -variable config(app)}
00410     .dnr checkbutton 1 0 2 {-anchor w -text {Doc. Nroff:}   -variable config(doc,nroff)}
00411     .dht checkbutton 1 0 3 {-anchor w -text {Doc. HTML:}    -variable config(doc,html)}
00412     .exa checkbutton 1 0 4 {-anchor w -text {Examples:}     -variable config(exa)}
00413 
00414     .spa frame  3 0 5 {-bg black -height 2}
00415 
00416     .dry checkbutton 2 0 7 {-anchor w -text {Simulate installation} -variable config(dry)}
00417 
00418     .pkge entry 1 1 0 {-width 40 -textvariable config(pkg,path)}
00419     .appe entry 1 1 1 {-width 40 -textvariable config(app,path)}
00420     .dnre entry 1 1 2 {-width 40 -textvariable config(doc,nroff,path)}
00421     .dhte entry 1 1 3 {-width 40 -textvariable config(doc,html,path)}
00422     .exae entry 1 1 4 {-width 40 -textvariable config(exa,path)}
00423 
00424     .pkgb button 1 2 0 {-text ... -command {browse Packages     pkg,path}}
00425     .appb button 1 2 1 {-text ... -command {browse Applications app,path}}
00426     .dnrb button 1 2 2 {-text ... -command {browse Nroff        doc,nroff,path}}
00427     .dhtb button 1 2 3 {-text ... -command {browse HTML         doc,html,path}}
00428     .exab button 1 2 4 {-text ... -command {browse Examples     exa,path}}
00429 
00430     .sep  frame  3 0 8 {-bg black -height 2}
00431 
00432     .run  button 1 0 9 {-text {Install} -command {set ::run 1}}
00433     .can  button 1 1 9 {-text {Cancel}  -command {exit}}
00434     } {
00435     eval [list $type $w] $opts
00436     grid $w -column $col -row $row -sticky ew -columnspan $cspan
00437     grid rowconfigure . $row -weight 0
00438     }
00439 
00440     grid .can -sticky e
00441 
00442     grid rowconfigure    . 9 -weight 1
00443     grid columnconfigure . 0 -weight 0
00444     grid columnconfigure . 1 -weight 1
00445 
00446     wm deiconify .
00447     return
00448 }
00449 
00450 ret  handlegui () {
00451     setupgui
00452     vwait ::run
00453     showconfiguration
00454     validate
00455 
00456     toplevel .l
00457     wm title .l "Install log"
00458     text     .l.t -width 70 -height 25 -relief sunken -bd 2
00459     pack     .l.t -expand 1 -fill both
00460 
00461     return
00462 }
00463 
00464 /*  --------------------------------------------------------------*/
00465 /*  Handle a command line*/
00466 
00467 ret  handlecmdline () {
00468     showconfiguration
00469     validate
00470     wait
00471     return
00472 }
00473 
00474 ret  processargs () {
00475     global argv argv0 config
00476 
00477     while {[llength $argv] > 0} {
00478     switch -exact -- [lindex $argv 0] {
00479         +excluded    {set config(no-exclude) 1}
00480         -no-wait     {set config(wait) 0}
00481         -no-gui      {set config(no-gui) 1}
00482         -simulate    -
00483         -dry-run     {set config(dry) 1}
00484         -html        {set config(doc,html) 1}
00485         -nroff       {set config(doc,nroff) 1}
00486         -examples    {set config(exa) 1}
00487         -pkgs        {set config(pkg) 1}
00488         -apps        {set config(app) 1}
00489         -no-html     {set config(doc,html) 0}
00490         -no-nroff    {set config(doc,nroff) 0}
00491         -no-examples {set config(exa) 0}
00492         -no-pkgs     {set config(pkg) 0}
00493         -no-apps     {set config(app) 0}
00494         -pkg-path {
00495         set config(pkg) 1
00496         set config(pkg,path) [lindex $argv 1]
00497         set argv             [lrange $argv 1 end]
00498         }
00499         -app-path {
00500         set config(app) 1
00501         set config(app,path) [lindex $argv 1]
00502         set argv             [lrange $argv 1 end]
00503         }
00504         -nroff-path {
00505         set config(doc,nroff) 1
00506         set config(doc,nroff,path) [lindex $argv 1]
00507         set argv                   [lrange $argv 1 end]
00508         }
00509         -html-path {
00510         set config(doc,html) 1
00511         set config(doc,html,path) [lindex $argv 1]
00512         set argv                  [lrange $argv 1 end]
00513         }
00514         -example-path {
00515         set config(exa) 1
00516         set config(exa,path) [lindex $argv 1]
00517         set argv             [lrange $argv 1 end]
00518         }
00519         -help   -
00520         default {
00521         puts stderr "usage: $argv0 ?-dry-run/-simulate? ?-no-wait? ?-no-gui? ?-html|-no-html? ?-nroff|-no-nroff? ?-examples|-no-examples? ?-pkgs|-no-pkgs? ?-pkg-path path? ?-apps|-no-apps? ?-app-path path? ?-nroff-path path? ?-html-path path? ?-example-path path?"
00522         exit 1
00523         }
00524     }
00525     set argv [lrange $argv 1 end]
00526     }
00527     return
00528 }
00529 
00530 ret  validate () {
00531    global config
00532 
00533     if {$config(valid)} {return}
00534 
00535     puts "Invalid configuration detected, aborting."
00536     puts ""
00537     puts "Please use the option -help to get more information"
00538     puts ""
00539 
00540     if {$config(gui)} {
00541     tk_messageBox \
00542         -icon error -type ok \
00543         -default ok \
00544         -title "Illegal configuration" \
00545         -parent . -message [get]
00546     clear
00547     }
00548     exit 1
00549 }
00550 
00551 ret  installErrorMsgBox (type msg) {
00552     tk_messageBox \
00553         -icon error -type ok \
00554         -default ok \
00555         -title "Install error" \
00556         -parent . -message $msg
00557     exit 1
00558 }
00559 
00560 ret  wait () {
00561    global config
00562 
00563     if {!$config(wait)} {return}
00564 
00565     puts -nonewline stdout "Is the chosen configuration ok ? y/N: "
00566     flush stdout
00567     set answer [gets stdin]
00568     if {($answer == {}) || [string match "\[Nn\]*" $answer]} {
00569     puts stdout "\tNo. Aborting."
00570     puts stdout ""
00571     exit 0
00572     }
00573     return
00574 }
00575 
00576 /*  --------------------------------------------------------------*/
00577 /*  Main code*/
00578 
00579 ret  main () {
00580     global config
00581 
00582     defaults
00583     processargs
00584     if {$config(no-gui) || [catch {package require Tk}]} {
00585     handlecmdline
00586     } else {
00587     handlegui
00588     }
00589     doinstall
00590     return
00591 }
00592 
00593 /*  --------------------------------------------------------------*/
00594 main
00595 exit 0
00596 /*  --------------------------------------------------------------*/
00597 

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1