tcl-quill 0.3.0 -- Quill Project Automation System for Tcl/Tk

Quill 0.3.0 User's Guide

By Will Duquette
will -at- wjduquette.com
November, 2014


Table of Contents

Introduction
Acknowledgements
1. Quill Overview
2. Using Quill
2.1 Starting a New Project
2.2 The Project File
2.2.1 Automatic Code Updates
2.2.2 The quillinfo(n) Package
2.3 The Project Tree
2.3.1 The Quill Tool and the Project Tree
2.4 Testing Your Project
2.5 Project Documentation
2.6 Interactive Development
2.7 Managing External Dependencies
2.7.1 Using Required Packages
2.7.2 Setting Up a Local Teapot
2.8 Providing Libraries
2.9 Providing Applications
2.10 Building Distribution .zip Files
2.11 Building the Project for Distribution
2.12 Building for Other Platforms
2.12.1 Acquiring Basekits
2.12.2 Building with a Basekit
2.13 Adding Elements to a Project
2.14 Configuring Quill
3. Library Packages
3.1 The 'pkgIndex.tcl' File
3.2 The 'pkgModules.tcl' File
3.3 Normal Package Modules
4. Application Structure
4.1 The Application Loader Script
4.2 The Application Implementation Package
5. Project Testing
5.1 Standard Test Files
5.2 Adding New Test Targets


Introduction

Quill is a tool for automating the various tasks involved in developing, testing, documenting, and distributing a modern Tcl/Tk application. It was inspired by Leiningen, a similar tool for the Clojure programming language, a tool whose convenience and ease of use made me distinctly jealous.

Quill is intended for those learning Tcl/Tk for the first time, and also for those who build libraries or applications in Tcl/Tk that are intended for use by others.

It is still a young project; if it doesn't yet meet your needs, please leave an enhancement request at the Quill issue tracker.

Acknowledgements

Many thanks to the following people who have helped with Quill development:

1. Quill Overview

This user's guide assumes that Quill is already installed; see INSTALL.md for details on how to install Quill. If you don't have Quill, you can find the latest release at the Quill Release Page Quill will help you do the following things:

Of course, you can do almost all of these things without Quill...but Quill aims to make them so easy that all you need to do is write your code.

2. Using Quill

Quill is a command-line tool with many subcommands, just like so many other software development tools. To see what tools are available, enter 'quill' or 'quill help' at the command line. To get help about a specific subcommand, enter 'quill help subcommand'.

2.1 Starting a New Project

A Quill project is the collection of files related to a Tcl application or library package (or both together). To begin working with Quill, then, the first thing to do is create a new project. And the first thing to do when starting a new project is to choose the most suitable project template.

FUTURE: Quill will support multiple project templates. At present, it supports a single template, 'app', for a generic application.

To create a new project, switch to the parent directory of the new project's directory tree, and use the 'quill new' tool:

$ cd ~/github
$ quill new app my-project myapp
...
$ cd my-project
$ quill info
...
The 'quill new' command takes the project template name ('app') and the project name, ('my-project'), and any additional information required by the template (the application name, 'myapp', in this case). It creates a project tree for the project, rooted at my-project/ in the current directory.

The 'quill info' command displays the new project's metadata in a human-readable form:

$ quill info
my-project 0.0a0: Your project description

Project Tree:
    /Users/will/github/my-project

Applications:
    Name   Mode     ExeType
    -----  -------  -------
    myapp  Console  kit    

Required Packages:
    Tcl  8.6.1  

Distribution Sets:
    install
In this listing we see the project's name, and its initial version string ('0.0a0') and description. It contains code for one application, 'myapp', which is a console-mode application to be delivered as a starkit. The project has only one dependency, on Tcl 8.6.1, which is the version of Tcl currently installed on the machine in use. It defines a single distribution set, 'install', which in turn defines the files to be included in the installation .zip file.

Typically you will wish to change some or all of these things. You might want a GUI application built as a standalone executable, and you'll probably have additional external dependencies. Ultimately you'll need to change the version number.

All of these things are defined in the project file, which is called project.quill.

2.2 The Project File

The project file is the heart of the Quill system. Everything Quill needs to know about your project is put into the project file, which resides in the root directory of your project's directory tree. Here is the project file for the project we just created in the previous section:

$ cd ~/github/my-project
$ cat project.quill
project my-project 0.0a0 "Your project description"
homepage http://home.page.url
app myapp
require Tcl 8.6.1

dist install {
    %apps
    docs/*.html
    docs/man*/*.html
    README.md
    LICENSE
}
The project file consists of a sequence of statements that define the entities in the project. It always begins with the project statement, which defines the project's name, version number, and description. The app statement declares that the project defines an application called 'myapp', and that it requires version 8.6.1 of a package called 'Tcl'. Finally, it defines an installation set called 'install' which contains the built applications, some HTML files from the project/docs/ subtree, and the README and LICENSE files.

The file is usually edited by hand; to change the project's version number, just edit the file. Then, run 'quill info' to verify that the file is readable:

$ vim project.quill
$ quill info
...
$
In addition to the kind of information shown above, you can also provide library packages; these are Tcl libraries exported by your project for use by other project.

The project file syntax is documented in Quill's project(5) man page. It's a Tcl-formatted file, naturally, but it allows only the commands defined in project(5); and on occasion Quill will write it out again with edits, so you shouldn't get too attached to your formatting.

2.2.1 Automatic Code Updates

Your project code will depend on some of the metadata defined in the project file. For example, any library packages in your project will have the same version number as the project itself; and naturally this project number needs to appear in the code in a variety of places (i.e., in package ifneeded and package provide statements).

Similarly, your applications will need to package require some or all of the external dependencies list in project.quill, and will need to include the package version numbers.

Quill handles all of this. Each time a Quill command is executed in the project tree. Quill loads project.quill and updates the project's codebase as needed to match the project's new metadata.

This is another reason why it is a good idea to execute 'quill info' after editing project.quill: it gives Quill a chance to make any necessary changes.

2.2.2 The quillinfo(n) Package

In addition to updating version numbers in the relevant package ifneeded, package provide, and package require statements, Quill also creates a library package, quillinfo(n), that contains all of the project's metadata, for use by your applications.

As an example, your application can retrieve the project version number like this:

set version [quillinfo version]
See the quillinfo(n) man page for the complete API. Only a few pieces of information are made available at present, but in principle everything in project.quill can be made available.

2.3 The Project Tree

There are a nearly infinite number of ways to set up a project directory tree; some of those ways work well, and some do not. A succesful project tree is the result of many small decisions, each of which makes the pieces work well together, and many of which are not obvious ahead of time.

But if there are many bad project trees, there are many good ones; the choice is to some extent arbitrary. Quill makes the choice for you, mandating a project tree layout that has been known to work in real projects.

root/ The project root directory.
    project.quill The project file; see Section 2.2.
    README.md The project's "read me" file.
    LICENSE The project's license file.
    .quill/ Quill's private working directory for this project. It should not be checked into a VCS.
    bin/ The project "bin" directory.
        app.tcl Application loader script for application app.
        tool A project-specific tool, usually a Tcl script.
    docs/ The project documentation directory. See Section 2.5.
        index.quilldoc The root of the project's documentation tree, in quilldoc(5) format.
        document.quilldoc Some other document, in quilldoc(5) format.
        man1/ Man page directory for application man pages. (Section 1).
            app.manpage Man page for application app in manpage(5) format.
        man5/ Man page directory for project file formats. (Section 5).
            format.manpage Man page for the given file format in manpage(5) format.
        mann/ Man page directory for Tcl package APIs. (Section n).
            module.manpage Man page for the given package module in manpage(5) format.
        mani/ Man page directory for Tcl interfaces. (Section i).
            interface.manpage Man page for the given interface in manpage(5) format. An "interface" is a set of subcommands and semantics supported by multiple otherwise unrelated Tcl objects.
    lib/ Parent directory for the project's Tcl packages.
        app_app/ Directory for the application app's implementation package.
            pkgIndex.tcl The package's package index file.
            pkgModules.tcl The package's module loader script. See Section 3.
            main.tcl Defines the application's main procedure.
            module.tcl Another package module.
        lib/ Directory for package lib, which might or might not be "provided" for external use.
            pkgIndex.tcl The package's package index file.
            pkgModules.tcl The package's module loader script. See Section 3.
            module.tcl Another package module.
    test/ Parent directory for the project's test target directories.
        target/ A test target directory. The "target" is usually a package name.
            /all_tests.test The target's main tcltest(n) script. (Boilerplate; See Section 5.)
            /module.test The tcltest(n) script for the given module.

The practices and usage of these directories are discussed in the following sections. And, of course, you can define any other subdirectories you need.

2.3.1 The Quill Tool and the Project Tree

Some of Quill's subcommands need to be run within a project tree, and some do not. Of those that do, such as 'quill build' and 'quill test', you can execute the command anywhere in the project tree; you do not need to be in the project's root directory.

2.4 Testing Your Project

The 'quill test' command executes all of your project's test targets, displays a summary of the results:

$ quill test
Summarizing test results.  Use 'quill -verbose test'
to see the details.

app_quill:      Total   35  Passed  35  Skipped 0   Failed  0
quill:          Total   169 Passed  167 Skipped 2   Failed  0

$
You can execute a particular target or module test as well:

$ quill test quill
... output from running test/quill/all_tests.test ...

$ quill test quill listutils
... output from running test/quill/listutils.test ...

$
Finally, you can add any tcltest(n) options you like to the command line. To run just one family of tests, for example, you can add the -match option:

$ quill test quill listutils -match lshift-*
... output from running the lshift-* tests in listutils(n) ...

$
See Section 5 for information on how to set up test target directories for use with Quill.

2.5 Project Documentation

Quill directly supports two documentation formats, quilldoc(5) and manpage(5). The two formats are HTML-like, and share a great deal of their syntax. Both are extensible in Tcl, and are translated into styled HTML.

The quilldoc(5) format is for short HTML files and full documents with section numbers, like this user's guide. The manpage(5) format is for man pages. The quilldoc(5) and manpage(5) man pages go into detail about the formats and how to make use of them.

To format the documentation, use the 'quill docs' command:

$ quill docs
... formats all .quilldoc and .manpage files found in the docs/ tree ...

$ quill docs mann
... formats all manpages in docs/mann/ ...

$ quill docs docs
... formats all .quilldoc files in docs/ ...

$ quill docs docs/ug.quilldoc
... formats the specific file ...

$
If any errors are found, they are described in the console output.

2.6 Interactive Development

When working with new code or debugging a problem, there is often no substitute for opening a Tcl shell and exercising the code directly. The 'quill shell' command supports this by invoking tkcon in the context of your code base, with the auto_path set so that your packages can be loaded.

Further, if your project defines at least one application then 'quill shell' automatically load the package for the first app in your project.quill file. (The app's main procedure is not executed.)

FUTURE: The 'quill shell' command will allow you to select the app whose code you want loaded, or none.

2.7 Managing External Dependencies

Quill will manage your project's external dependencies; which is to say the library packages written by others that are required by your project. In particular, it will determine whether these libraries are available in your local environment, and if necessary it will go acquire them from teapot.activestate.com.

NOTE: Before this mechanism will work properly, it may be necessary to set up your own local "teapot"; see Section 2.7.2.

External dependencies are declared in project.quill using the require statement:

require snit 2.3
require mylib 1.2 -local

This example requires two packages, "snit" and "mylib". The "mylib" package is declared to be "local", which is to say it's a package that's built and installed locally; if it isn't available, there's no point in looking for it at teapot.activestate.com.

To check whether all of your project's external dependencies are available locally, use this:

$ quill deps
Dependency Status:
  snit 2.3      (Missing)
  mylib 1.2     (OK)

To retrieve missing dependencies,

$ quill deps update
...

Quill will attempt to retrieve all missing depedencies, and will report on its success or failure. Finally, you can refresh any or all dependencies, explicitly downloading a new copy, by using:

$ quill deps refresh
...

2.7.1 Using Required Packages

Quill makes sure that the declared dependencies are available; it does not load them into your applications automatically. This is because your project might define multiple applications and libraries, each of which may use a subset of the declared dependencies.

Instead, your applications and library packages must call package require in the usual way for each needed dependency. However, Quill does provide a mechanism to keep the package version numbers in sync between project.quill and your code. See Section 3 for details.

2.7.2 Setting Up a Local Teapot

ActiveTcl provides a local "teapot repository", which is a place to put library packages such any script running on the local machine can access them. Quill requires that this local teapot be writable by the user; otherwise Quill can't update it to contain your project's external dependencies. Unfortunately, the standard installation locations for ActiveTcl on most platforms are such that the average user will not have write access to the installation teapot. If this is the case, the user will need to create their own local teapot; and Quill can help.

First, see if there is a problem:

$ quill teapot
Teapot Status:
    Location:  /Users/name/.quill/teapot
    Writable?  Yes
    Linked?    Yes

The local teapot is writable, and is properly linked
to the default Tcl shell.  

Everything appears to be OK.
In this the local teapot exists, and is writable, and everything is fine. If you see output like this there is nothing more to do.

The first time Quill is used, though, this is not likely to be the case. To resolve the problem, then, do this:

$ quill teapot fix
...
This command will do two things. First, it will create a new local teapot in your home directory, e.g., as ~/.quill/teapot/. (The precise location varies by operating system). Being in your home directory, this teapot is writable.

Second, 'quill teapot fix' will output a short script; executing this script with admin privileges will link the new teapot to your development tclsh, and will also resolve several issues related to running the teacup executable with admin privileges.

On a Linux or OSX system, for example, you would run this script using sudo:

$ sudo ~/.quill/fixteapot
...
$
On Windows, the script would be called fixteapot.bat, and would need to be run by a user with Admin privileges.

The 'quill teapot fix' command will output the script file's full path name; you are encouraged to look at the script and verify that it isn't doing anything nefarious.

2.8 Providing Libraries

A Quill project can define library packages for use by other projects. Such a package is called a provided package, because it is declared using the provide statement in project.quill:

provide mylib

Quill does two special things for provided packages. First, on 'quill build' Quill builds a teapot .zip file for the package. This .zip file is constructed so that it can be installed into any local teapot repository for use by other Tcl programs. The file is stashed in the project/.quill/teapot/ directory, and has a name like this:

package-name-version-tcl.zip

where name is the package name and version is the package version.

Second, 'quill install' will install the package's .zip file into your local teapot, for use by your other projects.

And of course you can define a test target (Section 2.4) and man pages (Section 2.5) for the provided package, just as you can for any of the project's libraries.

See Section 3 for the structure of library packages, whether provided or not.

FUTURE: At present, Quill requires TclDevKit to build the teapot .zip files. This is expected to change in the near future.

2.9 Providing Applications

A project can also define applications for use by others. Applications are declared in project.quill using the app statement. Applications may be delivered as starkits or starpacks (standalone executables), and starpacks may be console or GUI applications. For example, the following app statement defines a GUI application delivered as a standalone executable:

app myapp -exetype exe -gui
The first app statement in project.quill defines the project's primary application. Quill provides many tools for working with applications:

'quill run' invokes the project's primary application, passing it any command-line arguments. This is convenient if you don't want to add the project's project/bin/ directory to your PATH.

'quill shell' loads the primary application's code into a tkcon shell for interactive development and debugging.

'quill build' builds the application as starkit or starpack in the project's project/bin/ directory. The executable will have a name like

name-version-tcl.kit

(for starkits) or

name-version-platform[.exe]

for standalone executables, where the name is the application name, version is the project version number, and platform is the current platform string as returned by platform::identify.

And finally, 'quill install' will install the application into the user's ~/bin directory for local use as name.kit, name.exe, or simply name, depending on the application and current platform.

FUTURE: At present, Quill requires TclDevKit in order to build the application executables. This will remain an option, but it is hoped that other choices will be available in the future.

2.10 Building Distribution .zip Files

So you've written your application or library, and you've installed it for local use on your own machine; now you want to make it available to others. Quill allows you to define one or more distribution sets. Each distribution set is comprised of a name and a set of file patterns; the 'quill dist' command will build each distribution set as a .zip file containing the files that match the patterns.

Distribution sets are defined in project.quill using the dist statement. For example, a project that provides a set of pure-Tcl libraries might have a distribution set like this:

dist install {
    %libs
    docs/*.html
    docs/man*/*.html
    README.md
    LICENSE
}
It includes the .html files from the project's documentation tree, the project's README and LICENSE files, and the teapot .zip files for each provided library, as indicated by the special pattern %libs.

When built, this distribution set will result in a file with a name like this:

project-version-install.zip

where project is the project's name and version is the project's version number.

If the project provides an application built as a starpack, it might have an installation distribution set that looks like this:

dist install-%platform {
    %apps
    docs/*.html
    docs/man*/*.html
    README.md
    LICENSE
}
The %apps pattern is like the %libs pattern; it matches the executable files built by 'quill build'.

The "%platform" in the distribution set name is present because starpack executables are specific to a particular platform; when the distribution set is built, the "%platform" token will be replaced with the actual platform. If it were built for a 32-bit Windows system, for example, the distribution .zip file would have a name like this:

project-version-install-win32_x86.zip

The project can define any number of distribution sets for different purposes. For example, a project might want to define a "docs" distribution.

2.11 Building the Project for Distribution

Once your project is in good order, you can build it for distribution on your development platform with one command:
$ quill build all
...
$
This command will:

If there is a problem anywhere along the way, the build will halt with a detailed error message.

2.12 Building for Other Platforms

Quill can build your applications for any platform for which you have a basekit, provided that your application code and any local dependencies are all pure-Tcl. (External dependencies from teapot.activestate.com need not be pure-Tcl.) The first step is acquire the required basekit(s); the second is to build your project using them.

2.12.1 Acquiring Basekits

When building your apps for your development platform, Quill uses the basekits delivered with ActiveTcl. For other platforms you'll need to acquire the necessary basekits; and the easiest source is teapot.activestate.com. Quill will manage this for you. First, use the 'quill basekit' command to see what basekits are available to you:

$ quill basekit
Finding basekits at teapot.activestate.com...

Platforms for which cross-platform builds can be done:

platform                version         name             tcltk  source
----------------------  --------------  ---------------  -----  ------
linux-glibc2.3-ix86     8.6.3.0.298609  base-tcl-thread  tcl    web   
                                        base-tk-thread   tk     web   
                        8.6.1.1.298358  base-tcl-thread  tcl    local 
                        8.6.1.1.297892  base-tcl         tcl    web   
                                        base-tk          tk     web   
linux-glibc2.3-x86_64   8.6.3.0.298584  base-tcl-thread  tcl    web   
                                        base-tk-thread   tk     web   
                        8.6.2.1.298536  base-tcl-thread  tcl    local 
macosx-universal        8.6.0.0.296279  base-tcl-thread  tcl    web   
                                        base-tk-thread   tk     web   
macosx10.5-i386-x86_64  8.6.3.0.298584  base-tcl-thread  tcl    web   
                                        base-tk-thread   tk     web   
win32-ix86                              base-tcl-thread  tcl    web   
                                        base-tk-thread   tk     web   
                        8.6.2.1.298536  base-tk-thread   tk     local 
                        8.6.2.0.298433  base-tcl-thread  tcl    local 
win32-x86_64            8.6.3.0.298609  base-tcl-thread  tcl    web   
                                        base-tk-thread   tk     web   

$
Basekits with a source of "local" have already been downloaded to your machine; basekits with a source of "web" are available for download. Note that only Tcl 8.6 basekits are listed; that's because the project I'm working on has the 'require Tcl 8.6' statement in project.quill. If it required Tcl 8.5, we'd see Tcl 8.5 basekits instead.

You then have your choices of the precise platform and version of Tcl, whether you want the Tk GUI toolkit included, and whether you want threaded or unthreaded Tcl (most distributions are now threaded).

The next step is to retrieve the basekits you want using quill basekit get. For example, to get threaded Tcl and Tk basekits for all platforms for Tcl 8.6.2,

$ quill basekit get "*" "8.6.2*" "*"
...
$
This command takes three arguments, the basekit name, the version, and the platform; each argument can contain glob-style wildcard characters as shown. (The double-quotes are to protect the wildcard characters from your shell.)

Quill will download all matching basekits and save them locally. If you wish to see which basekits you have available locally, use this command:

$ quill basekit list -source local
Platforms for which cross-platform builds can be done:

platform               version         name             tcltk  source
---------------------  --------------  ---------------  -----  ------
linux-glibc2.3-ix86    8.6.1.1.298358  base-tcl-thread  tcl    local 
linux-glibc2.3-x86_64  8.6.2.1.298536  base-tcl-thread  tcl    local 
win32-ix86                             base-tk-thread   tk     local 
                       8.6.2.0.298433  base-tcl-thread  tcl    local 
$
See 'quill help basekit' for the full syntax of the 'quill basekit' command.

2.12.2 Building with a Basekit

To build your apps for a particular platform using a basekit, first acquire the needed basekit(s) as required above. You'll want a 'base-tcl-*' basekit for all non-GUI apps, and a 'base-tk-*' basekit for all GUI apps. Then, use 'quill build all' to build for your current platform. (These steps can be done in either order).

Then use 'quill build for' for the desired platform. For example, to build for 32-bit Windows,

$ quill build for win32-ix86
...
$
Quill will build all applications with -exetype exe using the locally available basekits for win32-ix86 with the highest version number. Then, it will build any distribution sets that have '%platform' embedded in their names, substituting win32-ix86 for '%platform'.

This is why you must run 'quill build all' before running 'quill build for'; the latter builds only the applications, assuming that any documentation and library teapot .zip files are already available.

If you wish to run with a specific version or without threading (if both threaded and unthreaded are available) you can use the -version and -threads options. See 'quill help build' for specifics.

2.13 Adding Elements to a Project

FUTURE: The 'quill add' command will add application, library, and other skeleton elements to your project using templates, updating project.quill as required.

In the mean time, the easiest way to add a new library or application is to copy and rename the required files and directories. See Sections 3, 4, and 5 for complete descriptions of Quill's expectations with regard to library packages, applications, and test targets.

2.14 Configuring Quill

Quill typically requires very little configuration. It will find your development tclsh, tkcon, teacup, tclapp, and basekits on your path and use them without any further ado. To see what tools Quill plans to use, execute this command:

$ quill env
Quill 0.3.0 thinks it is running on Mac OSX.

Local Teapot: /Users/will/.quill/teapot

Helper Tools:
    tclsh        /usr/local/bin/tclsh (v8.6.1)                                  
    tkcon        /usr/bin/tkcon                                                 
    teacup       /usr/local/bin/teacup (v8.5.15.1.298288)                       
    tclapp       /usr/local/bin/tclapp                                          
    basekit.tcl  /Library/Tcl/basekits/base-tcl8.6-thread-macosx10.5-i386-x86_64
    basekit.tk   /Library/Tcl/basekits/base-tk8.6-thread-macosx10.5-i386-x86_64 
    teapot-pkg   /usr/local/bin/teapot-pkg                                      

!  - Helper tool could not be found on disk.
+  - Path is configured explicitly.
If you have multiple versions of any tool on your system, or if Quill cannot locate the required tools, you can use the 'quill config' command to tell Quill where to find them:

$ quill config list
... list of configuration parameters and value ...
$ quill config set helper.tclsh pathToTclShell...
$
See 'quill help config' for the complete syntax of the 'quill config' command.


3. Library Packages

This section explains how Quill manages TCL library packages. A Quill project can contain three different kinds of Tcl library: A project library called name, of any of these three kinds, resides in the project's project/lib/name directory.

At least one library package is included in each new project. Additional packages can be added using the quill add command (not yet implemented) or by hand, by copying an existing library.

Quill has some very specific expectations of project library packages; for the details, see the following subsections.

3.1 The 'pkgIndex.tcl' File

Every library package will have its pkgIndex.tcl file. These are usually created with the library package by Quill. Here is a typical library package:

0001 #-------------------------------------------------------------------------
0002 # TITLE: 
0003 #    pkgIndex.tcl
0004 #
0005 # PROJECT:
0006 #    my-project: Your project description 
A
0007 # 0008 # DESCRIPTION: 0009 # mylib(n): pkgIndex file 0010 # 0011 # Generated by Quill 0012 # 0013 #------------------------------------------------------------------------- 0014 0015 # -quill-ifneeded-begin
B
DO NOT EDIT BY HAND
C
0016 package ifneeded mylib 1.2.3
D
[list source [file join $dir pkgModules.tcl]]
E
0017 # -quill-ifneeded-end
B
A
Quill uses your project's metadata when creating the file header.

B
These are Quill block markers. They are used to mark code segments that Quill will update automatically, as needed.

C
The contents of this kind of block (quill-ifneeded-*) is replaced in its entirety; do not edit the code between the markers.

D
The package ifneeded command loads the package's code when TCL is asked to package require it, and references the package's name and version number. The version number of a package in a Quill project is always the same as the project version number. When the version number changes, Quill will update this command with the latest version number.

E
The package's code is loaded by calling the module's pkgModules.tcl file. See 3.2.

3.2 The 'pkgModules.tcl' File

A package's code is loaded by sourcing the package's pkgModules.tcl file, which then sources the individual package modules. The pkgModules.tcl file is usually created automatically by Quill, and then updated by the user as needed (see below). Here is a typical pkgModules.tcl file:

0001 #-------------------------------------------------------------------------
0002 # TITLE: 
0003 #    pkgModules.tcl
0004 #
0005 # PROJECT:
0006 #    my-project: Your project description 
A
0007 # 0008 # DESCRIPTION: 0009 # mylib(n): Package Loader 0010 # 0011 # Generated by Quill 0012 # 0013 #------------------------------------------------------------------------- 0014 0015 #------------------------------------------------------------------------- 0016 # Provide Package 0017 0018 # -quill-provide-begin
B
DO NOT EDIT BY HAND
C
0019 package provide mylib 1.2.3
D
0020 # -quill-provide-end
B
0021 0022 #------------------------------------------------------------------------- 0023 # Require Packages 0024 0025 # -quill-require-begin
B
INSERT PACKAGE REQUIRES HERE
E
0026 package require snit 2.3
F
0027 package require -exact myotherlib 1.2.3
G
0028 # -quill-require-end
B
0029 0030 #------------------------------------------------------------------------- 0031 # Get the library directory 0032 0033 namespace eval ::mylib::
H
{ 0034 variable library [file dirname [info script]]
I
0035 } 0036 0037 source [file join $::mylib::library mymodule.tcl]
J
0038 ...
A
Quill uses your project's metadata when creating the file header.

B
These are Quill block markers. They are used to mark code segments that Quill will update automatically, as needed.

C
The contents of this kind of block (quill-provide-*) is replaced in its entirety; do not edit the line between the markers.

D
The package provide command notifies TCL that the given version of the package is available to be required and used. The version number for a package in a Quill project is always the same as the project's version number, so Quill automatically updates this code when the project version number changes.

E
Your package's package require commands should go between these quill-require-* marks, so that Quill can keep the version numbers up to date. Quill updates only the lines whose first non-whitespace tokens are "package require".

F
In this line, Snit is an external dependency named in the project file using the require statement. Quill consequently knows the required version number, and will make sure to keep this command up-to-date with it.

G
In this line, myotherlib is another library provided by this same project. In this case, Quill not only keeps the version number up-to-date with the project's version number, it adds the -exact flag. myotherlib might exist in the local teapot repository as well as in the project tree, and using -exact ensures that mylib will load the version in the same project tree during development and a consistent version when deployed.

H
Every package defines a namespace of the same name, just as a matter of convention. Provided packages should usually put their code in the package namespace; for application and infrastructure packages, it's a matter of taste.

I
Every package saves the path its library directory in a variable called library in its namespace. This variable has multiple uses. If the package has resources (e.g., image files) that need to be loaded at runtime, it can find them relative to this path. And if it isn't clear which version of a package is being loaded, a glance at the library variable will usually make things plain.

J
The package's modules should always be sourced using this idiom, which is guaranteed to work on all platforms.

There are two temptations to be avoided with regard to this file. First, do not try to change the file name. The consistent use of the name pkgModules.tcl allows Quill to find it and update it properly as version numbers change.

Second, do not put your package's implementation at the tail end of the pkgModules.tcl file, even if you have only one file. Instead, put the implementation in another file or files, and update pkgModules.tcl only when adding or deleting a module or a package require.

3.3 Normal Package Modules

The library package's code goes in one or more normal .tcl files, which are sourced by the package's pkgModules.tcl file (see Section 3.2). They typically look like this:

0001 #-------------------------------------------------------------------------
0002 # TITLE: 
0003 #    mymodule.tcl
0004 #
0005 # PROJECT:
0006 #    mylib: Your project description 
A
0007 # 0008 # DESCRIPTION: 0009 # mylib(n): my module for this and that 0010 # 0011 #------------------------------------------------------------------------- 0012 0013
B
0014 0015 #------------------------------------------------------------------------- 0016 # Exported Commands 0017 0018 namespace eval ::mylib { 0019 namespace export myproc
C
0020 } 0021 0022 #------------------------------------------------------------------------- 0023 # Commands 0024 0025 # myproc text 0026 # 0027 # Dummy procedure 0028 0029 proc ::mylib::myproc {text} {
D
0030 puts "Hello, $text!" 0031 }
A
Quill uses your project's metadata when creating the file header. Update it to describe your module as desired.

B
Notice that there are no package require or package provide commands here. Those go in pkgModules.tcl; see 3.2.

C
If you put the module's code in the package namespace, you might want to export some commands.

D
This part is up to the user.


4. Application Structure

An application called name in a Quill project is defined by:

4.1 The Application Loader Script

The application loader script is created automatically by 'quill new'. FUTURE: 'quill add app' will also add application loader scripts automatically. It is almost completely boilerplate.

The application loader script performs the following functions:

The programmer can edit the loader script as desired, but there should generally be little need to do so. If particular common cases arise that this loader doesn't handle, the application metadata and the loader boilerplate should be updated to handle them.

Here is an example loader script:

0001 #!/bin/sh
0002 # -*-tcl-*-
0003 # the next line restarts using tclsh\ 
A
0004 exec tclsh "$0" "$@" 0005 0006 #------------------------------------------------------------------------- 0007 # NAME: myapp.tcl
B
0008 # 0009 # PROJECT: 0010 # my-project: Your project description 0011 # 0012 # DESCRIPTION: 0013 # Loader script for the myapp(1) tool. 0014 # 0015 #------------------------------------------------------------------------- 0016 0017 #------------------------------------------------------------------------- 0018 # Prepare to load application 0019 0020 set bindir [file dirname [info script]]
C
0021 set libdir [file normalize [file join $bindir .. lib]] 0022 0023 set auto_path [linsert $auto_path 0 $libdir] 0024 0025 # -quill-tcl-begin
D
0026 package require Tcl 8.6.1
E
0027 # -quill-tcl-end
D
0028 0029 # quillinfo(n) is a generated package containing this project's 0030 # metadata. 0031 package require quillinfo
F
0032 0033 # If it's a gui, load Tk. 0034 if {[quillinfo isgui myapp]} {
G
0035 # -quill-tk-begin
D
0036 package require Tk 8.6.1
E
0037 # -quill-tk-end
D
0038 } 0039 0040 # app_myapp(n) is the package containing the bulk of the 0041 # myapp code. In particular, this package defines the 0042 # "main" procedure. 0043 package require app_myapp
H
0044 namespace import app_myapp::* 0045 0046 #------------------------------------------------------------------------- 0047 # Invoke the application 0048 0049 if {!$tcl_interactive} {
I
0050 if {[catch { 0051 main $argv 0052 } result eopts]} { 0053 if {[dict get $eopts -errorcode] eq "FATAL"} {
J
0054 # The application has flagged a FATAL error; display it 0055 # and halt. 0056 puts $result 0057 exit 1 0058 } else { 0059 puts "Unexpected error: $result" 0060 puts "Error Code: ([dict get $eopts -errorcode])\n" 0061 puts [dict get $eopts -errorinfo] 0062 } 0063 } 0064 }
A
The application loader script can be run directly on Un*x-like platforms; it will be run against whatever tclsh is on the PATH. Alternatively, you can execute it using 'quill run'.

B
Quill uses your project's metadata when creating the file header. Update it to describe your module as desired.

C
The loader script sets up the auto_path to point at the user's code. Any local teapots are presumed to be on the path already. Note: it can cause confusion to set the TCL_LIB_PATH environment variable when working with Quill projects.

D
These marks allow Quill to update the Tcl and Tk version as your project changes. Do not edit the code between the marks.

E
Quill determines the Tcl/Tk version from the 'require Tcl' statement in project.quill, and from the version of the development tclsh, as found on the PATH or overridden by a configuration setting

F
The loader always requires the quillinfo(n) package, which gives the application access to the project metadata.

G
If the metadata indicates that this app is a GUI app, then Tk is loaded automatically.

H
The loader requires the application's implementation package, and imports all exported commands from the package's namespace. Note that the global namespace is there for the user of the application; it is up to the developer whether application code resides in other namespaces or not.

I
The loader doesn't execute the application's main routine when the application is loaded into an interactive shell. This gives the developer the ability to run it interactive from the shell command line, as desired.

J
This code allows the application to exit with a fatal error message just by calling 'throw FATAL' with the error message. Any other error code will result in an "Unexpected error" message and a stack trace.

4.2 The Application Implementation Package

Application name's implementation package is called 'app_name' and has the same structure as any other library package, as described in Section 3. There are two differences:

By convention, the main routine is defined in a module called main.tcl, though this is not required. A typical main.tcl might look like this:

0001 #-------------------------------------------------------------------------
0002 # TITLE: 
0003 #    main.tcl 
A
0004 # 0005 # PROJECT: 0006 # my-project: Your project description 0007 # 0008 # DESCRIPTION: 0009 # app_myapp(n): main procedure 0010 # 0011 #------------------------------------------------------------------------- 0012 0013
B
0014 0015 #------------------------------------------------------------------------- 0016 # Exported Commands 0017 0018 namespace eval ::app_myapp {
C
0019 namespace export \ 0020 main 0021 } 0022 0023 #------------------------------------------------------------------------- 0024 # Commands 0025 0026 # main argv 0027 # 0028 # Dummy procedure 0029 0030 proc ::app_myapp::main {argv} { 0031 puts "[quillinfo project] [quillinfo version]"
D
0032 puts "" 0033 puts "Args: <$argv>" 0034 }
A
Quill uses your project's metadata when creating the file header. Update it to describe your module as desired.

B
Notice that there are no package require or package provide commands here. Those go in pkgModules.tcl; see 3.2.

C
The default template puts main in the package namespace, and exports it; alternatively, you can put main in the global namespace.

D
This part is up to the developer.


5. Project Testing

Quill provides easy management of your project test suite using the tcltest(n) test harness. To execute all project tests,

$ quill test
...
Quill will run all test targets, and output the results. Alternatively, Quill will run a single test target given its name:

$ quill test mylib
...
Test targets are defined by adding subdirectories to project/test/; the name of the subdirectory is the name of the target.

Each test directory contains an all_tests.test file, and one or more normal tcltest(n) files, e.g., mymodule.test. To run a specific test file, add its name to the command line:

$ quill test mylib mymodule
Finally, any options are passed along to tcltest:

$ quill test mylib mymodule -match "mytest-1.*"

5.1 Standard Test Files

Each test subdirectory is assumed to contain a file called all_tests.test, plus any number of normal tcltest(n) files, e.g., mymodule.test.

The content of all_tests.test is pure boilerplate; it simply arranges to execute all other *.test files in the directory, each in its own instance of the Tcl interpreter, and accumulate the results. It is generally created automatically by Quill, or by copying all_tests.test from an existing test subdirectory.

Here is the skeleton of a typical module test script, as created by Quill:

0001 #-------------------------------------------------------------------------
0002 # TITLE:
0003 #    mymodule.test
0004 #
0005 # PROJECT:
0006 #    myproject: Description of my project
0007 #
0008 # DESCRIPTION:
0009 #    mymodule(n): Test Suite
0010 #-------------------------------------------------------------------------
0011 
0012 #-------------------------------------------------------------------------
0013 # Load the tcltest package
0014 
0015 if {[lsearch [namespace children] ::tcltest] == -1} { 
A
0016 package require tcltest 2.3 0017 eval ::tcltest::configure $argv 0018 } 0019 0020 namespace import ::tcltest::test
B
0021 0022 #------------------------------------------------------------------------- 0023 # Load the package to be tested 0024 0025 source
C
../../lib/mymodule/pkgModules.tcl
D
0026 namespace import ::mymodule::* 0027 0028 #------------------------------------------------------------------------- 0029 # dummy 0030 0031 test dummy-1.1 {dummy test} -body { 0032 set a false 0033 } -result {true}
There are several things to note in the above listing:

A
The tcltest(n) package is loaded, and any options passed to quill test are passed along to tcltest.

B
Only the test command is imported from the tcltest:: namespace. Used fully-qualified names for other tcltest(n) commands, or edit this line to import additional commands.

C
The test script uses source to load the package, rather than package require; if the library package is installed into the local environment (not unusual), it can be difficult to ensure that package require always loads the code in the project tree.

D
Quill generally creates library packages and test directories together, and assumes that the tests in the directory are for the package in the project/lib subdirectory of the same name.

Also, notice that the test script sources the library package's pkgModules.tcl file. By convention, all library packages in a Quill project have such a file; see 3.2 for details.

5.2 Adding New Test Targets

At present each Quill project contains one test target, for the application's implementation package. Ultimately, you will be able to use the quill add command (FIXME) to add new library packages, as well as new applications (each of which gets its corresponding implementation package); each library created in this way will each get its own test directory.

In the meantime, test directories must be added by hand, usually by copying an existing directory and changing the names.



Generated from ug.quilldoc on Sat Nov 08 09:38:04 PST 2014