Sample installation script

Arjen Markus (4 november 2003) I have put this little script on the Wiki, because questions as to how to deal with "installations" came up on the starkit mailing list.

The idea is simple:

  • Put the directory structure of your application (consisting of whatever files) in a subdirectory "myapp" of a separate but ordinary starkit/starpack
  • Use this little script as the code to be run
  • Create a starpack out of the whole directory

So here is an example:

   ./install.vfs/
        main.tcl
        lib/
           app-install/
              install.tcl
              pkgIndex.tcl      <--- All standard starkit stuff!
        myapp/                  <--- Copy of the directory to hold all
           bin/                      files in the application
              exe1
              exe2
           lib/
              config1
              config2
              config3
           data/
              mydata

When you create a starkit/starpack from this directory structure, all the files get copied into it. You can reach them via:

   #
   # At the global level - otherwise [info script] does not return the
   # proper directory (It is the location of "install.tcl" we need)
   #
   set scriptdir  [file dirname [info script]]

   #
   # Now we have stored the script's location, we
   # can manipulate it to reach our application directory
   #
   set appdir [file join $scriptdir ".." ".." "myapp"]

The procedure is really simple and straightforward:

  • The user-interface lets the user select a directory
  • All the files in the "virtual" directory myapp/ get copied to that selected directory
  • Via the script "progman.tcl" (see Windows shell links - it is the script by Steve Cassidy) it will insert a new menu and the relevant items in the Windows Start menu.

You will need to customise the various bits and pieces but this is the gist of my installation script.

Note: For reasons unknown to me it sometimes takes a long time for it to finish copying the files out of the virtual directory. The files are there on disk, but the script is still processing the copy action.

Note: I have removed references to my specific software - not that is terribly confidential, but it is terribly irrelevant :).

Note: I use "mysomething" for all the things that need customisation (or removal).


 package provide app-install 1.0
 #  install.tcl --
 #     "Install" myapp
 #
 #  The procedure is very simple:
 #  - Allow the user to select a directory for installation
 #  - Copy the files from the starkit/myapp directory into the
 #    directory on disk
 #  - Create the new menu in the Start menu (assuming we are
 #    on Windows)
 #

 package require Tk

 # fillCanvas --
 #    Fill the canvas with a picture and some text
 #
 # Arguments
 #    cnv       Canvas widget
 #    cnv2      Second canvas widget
 # Results
 #    None
 # Side effects:
 #    Canvas filled
 #
 proc fillCanvas { cnv cnv2 } {

    image create photo logo -file [file join [file dirname [info script]] mybanner.gif]

    $cnv  create image   0   0 -image logo                   -anchor nw

    $cnv2 create text   20   5 -text "My app:"               -anchor nw
    $cnv2 create text   20  25 -text "Dedicated software"    -anchor nw
    $cnv2 create text   20  45 -text "for your eyes only"    -anchor nw
    $cnv2 create text   20  65 -text "NO WARRANTIES"         -anchor nw
    $cnv2 create text   20  85 -text "(c) Me"                -anchor nw
 }

 # showInfo --
 #    Show some textual information
 #
 # Arguments
 #    None
 # Results
 #    None
 # Side effects:
 #    Message window with short information
 #
 proc showInfo {} {

    tk_messageBox -parent . -icon info -title "My app - information" \
       -message {

 Whatever text you want to show
 ....


 }

 }

 # selectDir --
 #    Select the directory for installation
 #
 # Arguments
 #    None
 # Results
 #    None
 # Side effects:
 #
 #
 proc selectDir {} {
    global installdir

    set prevdir $installdir
    set installdir [tk_chooseDirectory -initialdir $installdir -parent . \
 -title "Select a directory to install into" -mustexist 0]

    if { $installdir == "" } {
       set installdir $prevdir
    }
 }

 # copyFiles --
 #    Recursively copy files from one directory to the target,
 #    to avoid problems with existing directories etc.
 #
 # Arguments
 #    sourcedir     Source directory
 #    targetdir     Target directory
 # Results
 #    None
 #
 proc copyFiles { sourcedir targetdir } {

    foreach file [glob [file join $sourcedir *]] {
       if { [file isdirectory $file] } {
          set newsource $file
          set newtarget [file join $targetdir [file tail $file]]
          copyFiles $newsource $newtarget
       } else {
          file copy -force $file "$targetdir"
       }
    }

 }

 # installCourse --
 #    Install the course material
 #
 # Arguments
 #    None
 # Results
 #    None
 #
 proc installApp {} {
    global scriptdir
    global installdir

    set orgcursor [. cget -cursor]
    . configure -cursor watch

    set appdir [file join $scriptdir ".." ".." "myapp"]

    #
    # Check whether the installation directory is already there.
    # If not, create it first. # Check if there is a file. If
    # not, create a dummy file.
    # The reason:
    # - glob will complain otherwise
    # - glob -nocomplain causes a different behaviour for
    #   directories with no subdirectories (at least on Windows XP)
    #
    if { ! [file exists $installdir] } {
       file mkdir $installdir
    }
    set contents [glob -nocomplain [file join $installdir *]]
    if { [llength $contents] == 0 } {
       set dummyfile [open [file join $installdir dummy] "w"]
       close $dummyfile
    }

    if { [file exists [file join $installdir waq]] } {
       foreach file [glob [file join $installdir waq *]] {
          file delete -force $file
       }
    }

    foreach file [glob [file join $appdir *]] {
       file copy -force $file $installdir
    }
    cd $installdir

    #
    # Add "My App" to the Start menu
    #
    file copy -force [file join $scriptdir "myapp.ico"] "."

    set curdir   [file join [pwd] bin]
    set workdir  [file normalize $curdir]
    set progfile [file nativename [file join $curdir myapp1.exe]]
    set iconfile [file nativename [file join $curdir "myapp.ico"]]
    progman_creategroup "My app"
    progman_additem     "$progfile" \
        "My app 1" "$iconfile" "$workdir"
    progman_additem     "[file nativename [file join $curdir myapp2.exe]]" \
        "My app 2" "$iconfile" "$workdir"

    tk_messageBox -message "Installation completed"
    .cancel configure -text "Exit"
    #exit
 }

 # main --
 #    Get the whole thing started
 #
 global scriptdir
 global installdir

 wm title      . "MY APP"
 wm iconbitmap . -default [file join [file dirname [info script]] "myapp.ico"]

 canvas .canvas   -height 90  -width 400 -background white
 canvas .disclaim -height 120 -width 150

 fillCanvas .canvas .disclaim

 label  .ltext   -text "Install in:"
 label  .empty1  -text " "
 label  .empty2  -text " "
 entry  .edir    -textvariable installdir
 button .dirsel  -text "Browse"  -command selectDir     -width 10
 button .info    -text "Info"    -command showInfo      -width 10
 button .install -text "Install" -command installCourse -width 10
 button .cancel  -text "Cancel"  -command exit          -width 10

 grid   .canvas   -       - -          -sticky ws
 grid   .ltext    -       - .disclaim  -sticky ws         -padx 10
 grid   .edir     .dirsel - ^          -sticky ws -pady 4 -padx 10
 grid   .empty1   -       - -
 grid   .install -        .cancel .info           -padx 10 -pady 10


 #
 # Some global stuff to take care of
 #
 set scriptdir  [file dirname [info script]]
 set installdir "C:/myapp"
 source [file join [file dirname [info script]] "progman.tcl"]