00001
00002
00003 exec tclsh "$0" ${1+"$@"}
00004
00005
00006
00007
00008
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
00018
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] ;
00028 source [file join $distribution support installation modules.tcl] ;
00029 source [file join $distribution support installation actions.tcl] ;
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
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