ms_stud
ms_stud

Reputation: 381

Tcl - split a package into several files with dependencies between them

I'm a beginner on tcl/itcl and I'm trying to create a package.
The problem is that my files inside the package are dependent one on each other.

For example:

pkgIndex.tcl

package ifneeded test 1.0 [list ::apply {dir {

  package require Itcl
   namespace eval ::test {
       namespace export *
       variable version 1.0
  }
  source [file join $dir system.itcl]
  source [file join $dir subsystem.itcl]
  package provide test 1.0
}} $dir]   

system.itcl

itcl::class ::test::system {
   ...
   private variable _subsystems ""
   ...
   constructor {} { lappend _subsystems [::test::subsystem #auto] }
   ...
}    

subsystem.itcl

itcl::class ::test::subsystem {
   ...
   private variable data ""
   ...
   constructor {} { set data "new data" }
   ...
}

In this case, I should include subsystem.itcl file in system.itcl.
If I wasn't providing a package, I would use the source command but I read that when a package is provided you don't use source command instead there are other methods that will do the "include" operation.
The problem is that I couldn't find anywhere example / website that explain how to do it.

Upvotes: 1

Views: 563

Answers (2)

Peter Lewerin
Peter Lewerin

Reputation: 13252

I would do this:

(system and subsystem aren't packages in this scenario: see below)

(DIR is a directory in or added to $auto_path)

DIR/pkgIndex.tcl

package ifneeded test 1.0 [list source [file join $dir test.tcl]]

DIR/test.tcl

package require Itcl

set dir [file dirname [file normalize [info script]]]
source [file join $dir subsystem.itcl]
source [file join $dir system.itcl]

namespace eval ::test {
       namespace export *
       variable version 1.0
}

package provide test $::test::version

DIR/system.itcl

itcl::class ::test::system {
   ...
   private variable _subsystems ""
   ...
   constructor {} { lappend _subsystems [::test::subsystem #auto] }
   ...
}    

DIR/subsystem.itcl

itcl::class ::test::subsystem {
   ...
   private variable data ""
   ...
   constructor {} { set data "new data" }
   ...
}

Note that I don't use Itcl, so I don't know if I'm mangling it here.

The point of doing it like this is that I would want

  1. the pkgIndex.tcl as simple as possible
  2. the whole shebang as easy to load as possible (a single package require here)
  3. the parts (system and subsystem) written as if they were part of main.tcl, just separated away in their own files for neatness
  4. main.tcl expressing all information on how the parts of the code connect to each other

If system and subsystem should be packages, I would do:

(DIR is a directory in or added to [::tcl::tm::path list])

DIR/test-1.0.tm

package require test::system

namespace eval ::test {
       namespace export *
       variable version 1.0
}

DIR/test/system-1.0.tm

package require Itcl
package require test::subsystem

itcl::class ::test::system {
   ...
   private variable _subsystems ""
   ...
   constructor {} { lappend _subsystems [::test::subsystem #auto] }
   ...
}    

DIR/test/subsystem-1.0.tm

package require Itcl

itcl::class ::test::subsystem {
   ...
   private variable data ""
   ...
   constructor {} { set data "new data" }
   ...
}

Note that package provide isn't necessary here: it's done automagically when the module is loaded.

The point of doing it like this is that I would want

  1. to avoid having to maintain an up-to-date pkgIndex.tcl

  2. the parts as self-contained as possible

  3. every part expressing what it needs for itself to work

Upvotes: 1

ms_stud
ms_stud

Reputation: 381

Ok. I did this in the following way and it worked.

pkgIndex.tcl

package ifneeded test 1.0 [list source [file join $dir test.tcl]]
package ifneeded system 1.0 [list source [file join $dir system.itcl]]
package ifneeded subsystem 1.0 [list source [file join $dir subsystem.itcl]]

test.tcl

package require ::test::system
namespace eval ::test {
       namespace export *
       variable version 1.0
}
package provide test 1.0

system.itcl

package require ::test::subsystem
package require Itcl
itcl::class ::test::system {
   ...
   private variable _subsystems ""
   ...
   constructor {} { lappend _subsystems [::test::subsystem #auto] }
   ...
}    
package provide ::test::system 1.0

subsystem.itcl

package require Itcl
itcl::class ::test::subsystem {
   ...
   private variable data ""
   ...
   constructor {} { set data "new data" }
   ...
}
package provide ::test::subsystem 1.0

so every file is providing me a different package and requiring a different packages and pkgIndex file just making it to be possible by indeed command.

Upvotes: 0

Related Questions