# pkg_mkIndexDirect.tcl --
#
# Improved version of pkg_mkIndex
# (back ported from tcl8.1a2/library/package.tcl)
# Used to generate pkgIndex.tcl so packages are loaded directly when
# required and thus also works with namespace imports...
#
# Copyright (c) 1998 Sun Microsystems, Inc.
# Copyright (c) 2000 by Scriptics Corporation.
#
# See the file "license.terms" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
#
# SCCS: @(#) pkg_mkIndexDirect.tcl 1.2 98/02/26 15:16:59
# RCS:  @(#) $Id: pkg_mkIndexDirect.tcl,v 1.2 2000/05/13 08:20:38 davygrvy Exp $

# pkg_mkIndex --
# This procedure creates a package index in a given directory.  The
# package index consists of a "pkgIndex.tcl" file whose contents are
# a Tcl script that sets up package information with "package require"
# commands.  The commands describe all of the packages defined by the
# files given as arguments.
#
# Arguments:
# dir -			Name of the directory in which to create the index.
# args -		Any number of additional arguments, each giving
#			a glob pattern that matches the names of one or
#			more shared libraries or Tcl script files in
#			dir.

proc pkg_mkIndex {args} {
    global errorCode errorInfo
    set first [lindex $args 0]
    set direct [string match "-d*" $first]
    set more ""
    if {$direct} {
	set args [lrange $args 1 end]
	set more " -direct"
    }
    if {[llength $args] == 0} {
	return -code error "wrong # args: should be\
		\"pkg_mkIndex ?-direct? dir ?pattern ...?\"";
    }
    set dir [lindex $args 0]
    set patternList [lrange $args 1 end]
    if {[llength $patternList] == 0} {
	set patternList [list "*.tcl" "*[info sharedlibextension]"]
    }
    append index "# Tcl package index file, version 1.1\n"
    append index "# This file is generated by the \"pkg_mkIndex$more\" command\n"
    append index "# and sourced either when an application starts up or\n"
    append index "# by a \"package unknown\" script.  It invokes the\n"
    append index "# \"package ifneeded\" command to set up package-related\n"
    append index "# information so that packages will be loaded automatically\n"
    append index "# in response to \"package require\" commands.  When this\n"
    append index "# script is sourced, the variable \$dir must contain the\n"
    append index "# full path name of this file's directory.\n"
    set oldDir [pwd]
    cd $dir
    # This code would benefit from a rewrite... if time permitted...
    foreach file [eval glob $patternList] {
	# For each file, figure out what commands and packages it provides.
	# To do this, create a child interpreter, load the file into the
	# interpreter, and get a list of the new commands and packages
	# that are defined.  Define an empty "package unknown" script so
	# that there are no recursive package inclusions.

	set c [interp create]

	# If Tk is loaded in the parent interpreter, load it into the
	# child also, in case the extension depends on it.

	foreach pkg [info loaded] {
	    if {[lindex $pkg 1] == "Tk"} {
		$c eval {set argv {-geometry +0+0}}
		load [lindex $pkg 0] Tk $c
		break
	    }
	}
	$c eval [list set __file $file]
	$c eval [list set __direct $direct]
	if {[catch {
	    $c eval {
		proc __dummy args {}
		rename package __package_orig
		proc package {what args} {
		    switch -- $what {
			require { return ; # ignore transitive requires }
			default { eval __package_orig [list $what] $args }
		    }
		}
		if {!$__direct} {
		    proc __pkgGetAllNamespaces {{root {}}} {
			set list $root
			foreach ns [namespace children $root] {
			    eval lappend list [__pkgGetAllNamespaces $ns]
			}
			return $list
		    }
		    set __origCmds [info commands]
		}
		package unknown __dummy

		set dir ""		;# in case file is pkgIndex.tcl

		# Try to load the file if it has the shared library extension,
		# otherwise source it.  It's important not to try to load
		# files that aren't shared libraries, because on some systems
		# (like SunOS) the loader will abort the whole application
		# when it gets an error.

		if {[string compare [file extension $__file] \
			[info sharedlibextension]] == 0} {

		    # The "file join ." command below is necessary.  Without
		    # it, if the file name has no \'s and we're on UNIX, the
		    # load command will invoke the LD_LIBRARY_PATH search
		    # mechanism, which could cause the wrong file to be used.

		    if {[catch {load [file join . $__file]} __msg]} {
			tclLog "warning: error while loading $__file: $__msg"
		    }
		    set __type load
		} else {
		    if {[catch {source $__file} __msg]} {
			tclLog "warning: error while sourcing $__file: $__msg"
		    }
		    set __type source
		}
		# Using __ variable names to avoid potential namespaces
		# clash, even here in post processing because the
		# loaded package could have set up traces,...
		if {!$__direct} {
		    foreach __ns [__pkgGetAllNamespaces] {
			namespace import ${__ns}::*
		    }
		    foreach __i [info commands] {
			set __cmds($__i) 1
		    }
		    foreach __i $__origCmds {
			catch {unset __cmds($__i)}
		    }
		    foreach __i [array names __cmds] {
			# reverse engineer which namespace a command comes from
			set __absolute [namespace origin $__i]
			if {[string compare ::$__i $__absolute] != 0} {
			    set __cmds($__absolute) 1
			    unset __cmds($__i)
			}
		    }
		}
		set __pkgs {}
		foreach __i [package names] {
		    if {([string compare [package provide $__i] ""] != 0)
			    && ([string compare $__i Tcl] != 0)
			    && ([string compare $__i Tk] != 0)} {
			lappend __pkgs [list $__i [package provide $__i]]
		    }
		}
	    }
	} msg]} {
	    tclLog "error while loading or sourcing $file: $msg"
	}
	set type [$c eval set __type]
	set cmds [lsort [$c eval array names __cmds]]
	set pkgs [$c eval set __pkgs]
	if {[llength $pkgs] > 1} {
	    tclLog "warning: \"$file\" provides more than one package ($pkgs)"
	}
	foreach pkg $pkgs {
	    # cmds is empty/not used in the direct case
	    lappend files($pkg) [list $file $type $cmds]
	}
	interp delete $c
    }
    foreach pkg [lsort [array names files]] {
	append index "\npackage ifneeded $pkg "
	if {$direct} {
	    set cmdList {}
	    foreach elem $files($pkg) {
		set file [lindex $elem 0]
		set type [lindex $elem 1]
		lappend cmdList "\[list $type \[file join \$dir\
			[list $file]\]\]"
	    }
	    append index [join $cmdList "\\n"]
	} else {
	    append index "\[list tclPkgSetup \$dir [lrange $pkg 0 0]\
		    [lrange $pkg 1 1] [list $files($pkg)]\]"
	}
    }
    set f [open pkgIndex.tcl w]
    puts $f $index
    close $f
    cd $oldDir
}

