The three qef tutorials in this chapter cover building "Hello, world" with qef, the structure of a qeffile, adding files to a project, installing a project, building multiple directories, qvrs, qdsrv, and other processes. In addition to the tutorials, there are some miscellaneous notes at the end of the chapter that may be helpful. |
This chapter describes three qef tutorials. Each tutorial introduces new concepts and approaches to the software construction problem. Work through all the tutorials in sequence. The tutorials are:
|
In this first tutorial, we look at the actual construction of a program using the ubiquitous "Hello, world" program that is everyone's introduction to the C language. Our focus is on the directory it is in, the call to the C compiler, the link phase, and the resulting binary. Using this simple example, we will see how qef affects the process, and will introduce a few basic qef concepts and operations. The first step is to install the tutorial. To do this, use the commands: % dirsetup -d /tut/tut1 qeftut1 # install the tutorial % cd /tut/tut1 The dirsetup tool creates the specified directories (tut and tut/tut1) and passes it the name of a script, qeftut1, which creates the three files comprising this tutorial: README contains a thumbnail description of this tutorial; hello.c the project source; and qeffile a simple qef configuration file for this project. In this simple example there is only one configuration file for the project, qeffile. (There is a second configuration file involved, sysnm.vrs, that defines the settings for the platform; it is installed once on each platform.) Obviously, in more complex projects, there may be thousands of source files in hundreds of directories, as well as numerous configuration files. |
|||||||||
The Qef Command |
First, run qef and see what happens. The command is: % qef #{ @host <date> <time> # header line # QEFHALTFILE: /%qef99999a.hlt # Location of the Qef HaltFile New: hello.c # new source file detected Warning: "qmkhist._" does not exist -- everything will be redone See warnMimk1(x-qmisc) + cc -c hello.c # compile hello + cc hello.o -o hello # link binary #} E0 host@/tut/tut1 <date> <time>(<elapsed-time>) The output is delimited by #{ and #}. qef writes the machine name, the start and completion times of the job, the elapsed time and finally the exit status of the job to the output stream. The header and trailer lines are generated at run-time and will vary. The HaltFile, mentioned in the above example, exists as a way of gently halting qef, qsh, or mimk. Each process checks for its existence and writability after every forked process exits. If at any time it does not exist or it is not writable, then that job exits. By using the HaltFile instead of simply killing the process, various cleanup and exit operations can be performed. During a qef run, the HaltFile also contains a trace of the individual QEF processes, which is useful when running large project builds. QEF examines the contents of the directory and finds a new source file, qef then invokes the C compiler twice, once to create the object file and again to create the binary. Now remove the binary and run qef again: % rm hello % qef #{ @<machine name> <date> <time> # QEFHALTFILE: /%qef99999a.hlt + cc hello.o -o hello #} E0 host@/tut/tut1 <date> <time>(#) Like make with a standard script, qef determines that the object file hello.o is up to date, and does not bother recompiling hello.c. It simply invokes cc to link the binary. In addition to the hello* files, a number of other administration files have been created: |
||||||||
incls._ | A cache of the list of include files referenced in the source files; the dependency list for the source. | ||||||||
qmkhist._ | The history file of mimk builds -- what was built, when and how (e.g. what compiler flags were used, the full source path name, etc.) | ||||||||
srclist._ |
The list of source files found in this directory.
These files are typically created each time qef is run, although their creation can be suppressed by changing some configuration options. How did qef know to construct the binary? Before answering this question, let us examine the configuration file, qeffile. |
||||||||
Structure of a qeffile |
The qeffile contains: # Section 1: qvrs settings set Project Tutorial1 # Additional qvrs settings can go here # End Section 1 Lines beginning with # are comments. As the comments indicate, a qeffile consists of 3 units:
In this particular script, Section 1 consists of a comment followed by a single command, "set Project Tutorial1". The set command assigns the value "Tutorial1" to the variable Project. Incidentally, this set command is not required; it is there for illustration. Many qeffiles have nothing in Section 1. Section 2 consists of a single line that invokes a routine called commands contained in the qsg library. This is one of the standard qsg library scripts included in the product. The @argv is the argument list passed to the script, which in this case (due to the M flag) will be the list of source files for the directory. More detail about argv can be found by typing x-qsg argv. The output of this invocation of qsg is a script, which when processed by the qef pre-processor will be a mimk input file. mimk is run to interpret the processed script, resulting in the product's construction. |
||||||||
Listing the Sub- Commands |
qef runs a number of other tools as part of its processing. For example, incls is used to create the dependencies. Normally, the listing of the sub-commands is suppressed, but you can override this with the v (verbose) flag, as in: % qef -v #{ -v @host <date> <time> # qvrs -Bp -LQefArgs= -o/tmp/qv_12345a.tmp # QEFHALTFILE: /tut/tut1/%qef12345a.hlt # cwd=/tut/tut1 # sls -Ls # qsg -M # incls -MUS # mimk -f /tmp/qefa12345 #} E0 host@/tut/tut1 <date> <time>(#) The commands run by qef in this directory are:
|
||||||||
Building Debugging Versions |
To build the project with the compiler's debug flag turned on, run qef flag, LDEBUGGING. Actually, this is just temporarily setting a qvrs variable called DEBUGGING to 1. Any qvrs variable can be set using the -L flag and the default value is 1. % qef -LDEBUGGING #{ -LDEBUGGING @host <date> <time> # QEFHALTFILE: /<filename>.hlt + cc -c -g hello.c + cc -g hello.o -o hello #} E0 host@/tut/tut1 <date> <time>(#) When the debugging flag is turned on, all object files compiled without debugging become obsolete. qef recognizes this, since the recipes have changed, and rebuilds the system completely. |
||||||||
Adding Files to the Project |
In the life of a project, source files are added, modified and sometimes removed. Traditionally this has involved maintenance of the project's make scripts -- with all the attendant opportunities for error, such as dependency omitted in the script, dependency undeclared, omitted from the link statement, file moved, file renamed, wrong include file (<> vs. ""), and so on. qef eliminates hand-written make scripts, generating them instead. New source files (or moved files) usually require no extra script maintenance -- simply create the files or move them into place. |
||||||||
Adding a Header File |
To demonstrate this, you will add an include file to hello.c, then rebuild the project. Create the file hello.h, containing one text line: #define MSG_HELLO "Hello, world\n" Modify hello.c, replacing the literal text with the MSG_HELLO, and including the new header file. Note, include the new header file as <hello.h>, not "hello.h": #include <stdio.h> #include <hello.h> /* note: not "hello.h"*/ Now rebuild the program: % qef -LDEBUGGING #{ -LDEBUGGING @host <date> <time> # QEFHALTFILE: /%qef19717a.hlt # qvrs -Bp -LDEBUGGING -LQefArgs= -o/tmp/qv_19717b.tmp # cwd=/tut/tut1 # sls -Ls create source database # qsg -M produce script # incls -MUS produce dependency list # mimk -f /tmp/qefa00879 run the back end + cc -c -g hello.c hello.c:2:hello.h: No such file or directory *** Error code 1 + unlink hello.o Warning: could not remove hello.o Fatal error - waiting for active jobs to complete. The problem here is that hello.h is not visible to the compiler, due to how it is referenced in hello.c. This |
||||||||
Extending Include Search Path |
A better approach is to add the current directory to the include path. A convenient place to do this is in Section 1 of qeffile. Edit this file and add the following line to the qeffile before the line containing Begin: addpath InclPath @SrcPath The addpath is a qvrs command, and InclPath holds a list of include directories. The line adds the value of SrcPath to the list in InclPath. Save the file and run qef again: % qef -LDEBUGGING Now the source compiles successfully and the binary is rebuilt. Note the I flags added to the cc commands. |
||||||||
Querying the History File, qmkhist._ |
The file qmkhist._ records each build performed on the current project. This file contains information about what was built, when it was built and why. The qef system includes a tool, qhy, that allows you to query this information for constructed files. % qhy hello.o hello.o 2000/02/25 01:42:51 -> hello.o (2000/02/25 01:42:46) < /tut/tut1/hello.c (2000/02/25 01:42:48) < hello.c (2000/02/25 01:42:48) + cc -c hello.c The output shows you the current timestamp for hello.o, its timestamp prior to being built, the timestamp of its prerequisite hello.c and finally the command that was used to construct the file. |
||||||||
Adding a New Source File |
The second type of change frequently made to a construction is the addition of new sources. In the typical make environment, this involves changing all the relevant make scripts to include the new file(s). qef automates this process. For simplicity's sake, instead of creating a new file simply copy hello.c: % cp hello.c newfile.c % qef -LDEBUGGING #{ -LDEBUGGING @host <date> <time> # QEFHALTFILE: /tut/tut1/%qef19724b.hlt New: /tut/tut1/newfile.c + cc -c -g -I. newfile.c compile newfile.c + cc -g newfile.o -o newfile create newfile binary #} E1 host@/tut/tut1 <date> <time>(#) There are now two binaries, hello and newfile. qef processes the new source file automatically. The "commands @argv" line includes newfile.c. Since the hello product is up-to-date, it was not rebuilt. This illustrates some important concepts behind qef:
In fact, not all files in the directory are treated as source, only files with specific extensions. The list of relevant extensions is specified by the qvrs variable @Suffixes or its default value as applied by sls. |
||||||||
The Commands Script |
Of all qsg scripts, commands is the second most frequently used. Its generated targets include the program, its debugging, purify, profile, and quantify versions, and many other commonly required products for C, yacc, lex, shell, man and other languages. It can be extended to handle new files as required. (For a directory in which all of the files go into a single command, you would use the program script.) |
||||||||
Other Useful Commands | |||||||||
qef offers a number of other useful options, including the following: |
|||||||||
Forcing Rebuild of a Target |
To force the rebuilding of a target one uses the F flag as in:
% qef -F hello #{ -F hello @host 2000/02/25 00:48:30 # QEFHALTFILE: /g/dt/tut/tut1/%qef19802b.hlt + cc -c hello.c + cc -o hello hello.o #} E0 host@/tut/tut1 2000/02/25 00:48:30(0)This flag is passed to mimk which will force all prerequisites and targets to be assumed to be out of date. |
||||||||
Checking What Will Happen |
To check what will be done, but not do it, one can use the n
flag, as in:
% rm hello ; qef -n # rm hello and check what is to be done #{ -n @host 2000/02/25 00:54:39 # QEFHALTFILE: /tut/tut1/%qef19824b.hlt + cc -o hello hello.o #} E0 host@/tut/tut1 2000/02/25 00:54:39(0)Note that the recipe to rebuild hello is listed since hello does not exist. However, hello.o's recipe is not shown, since hello.o is up-to-date. |
||||||||
Showing Target's Recipe |
The n and F flags can be combined to show the recipes
for the selected targets, as in:
% qef -nF helloI #{ -nF helloI @host 2000/02/25 01:01:28 # QEFHALTFILE: /tut/tut1/%qef19895b.hlt + cc -c hello.c + cc -o hello hello.o + instal -E -d @NotSet/bin/hello hello # more later #} E0 host@/tut/tut1 2000/02/25 01:01:28(0)Given the nF flags, mimk assumes that all targets and their prerequisites are out of date and shows what would be done, without actually doing it. Note the use of the pseudo target helloI which is an alias for the installation of hello. |
||||||||
Generating Assembly Code |
To generate the assembly code for a particular file, use a command of the form % qef hello.s |
||||||||
Creating a Dependency List |
This process occurs automatically during a qef run, but on occasion you may wish to create a list of inclusions for some specific source file. You can do so with this command: % incls hello.c To produce slightly different output, call the incls program with the l option: % incls -l hello.c To produce the dependency list in the format preferred by make, use the m option: % incls -m hello.c If you have Purify and/or Quantify use hello_p and hello_q to request the associated binaries. |
||||||||
Creating the Object Files |
Because qef uses techniques which are more sophisticated than timestamps to keep track of whether the product is up-to-date, some tricks that work with make do not work with qef. For example, try removing the object file hello.o and then running qef again. With make, this would cause the product hello to be produced again. Not with qef: % rm hello.o % qef #{ @host <date> <time>header line #} E0 host@/tut/tut1 <date> <time>(#) The target hello is deemed up-to-date whether or not hello.o exists. hello.o will be built if requested explicitly or implicitly as part of the Objects target. To build the object files for the directory, use the target Objects: % qef Objects #{ Objects @host <date> <time>header line + cc -c newfile.c + cc -c hello.c #} E0 host@/tut/tut1 <date> <time>(#) |
||||||||
Installing the Product |
Now let us attempt to install the product of this project. The standard qef target for this is Install: % qef Install #{ Install @host <date> <time> + cc -c -I. hello.c + cc -c -I. newfile.c + cc newfile.o -o newfile + cc hello.o -o hello + instal -d @NotSet/bin/newfile newfile + instal -d @NotSet/bin/hello hello instal: @NotSet/bin/newfile: will not install @NotSet files *** Error code 255 instal: @NotSet/bin/hello: will not install @NotSet files *** Error code 255 Fatal error - waiting for active jobs to complete.
The instal program failed as it will not install a file whose pathname begins with "@NotSet". The destination directory is specified by the qvrs variable _DestDir_. Given its special use, if _DestDir_ is not explicitly set, qvrs sets it to @NotSet. To set _DestDir_ you could add the corrective line to qeffile, but in practice this is not a good idea. One of the goals of qef is to avoid changing configuration data just to perform some local twiddling or to add a variable definition. Rather than change qeffile, create a new file called leaf.vrs, consisting of the following line: cset _DestDir_ @(home)/tut/tut1
The effects of leaf.vrs are restricted to the current directory. It is intended as a temporary file to change the configuration in the current directory. For example, in a larger project, you may want to temporarily compile with the debugging option in only a portion of the project, or to add a path to the library path only for the current directory. You would never make permanent changes in leaf.vrs, and you would never set _DestDir_ there. We use it here just to demonstrate a qvrs setting. The correct way to set _DestDir_ is discussed in the next tutorial. You can now install the project successfully: % qef Install The installation proceeds correctly. The sub-directory /tut/tut1/bin is created, and the binaries hello and newfile are installed there. The default directory for programs is _DestDir_/_BinDir_ where _BinDir_ defaults to bin. |
||||||||
Viewing a Build Script |
Sometimes you may wish to see the mimk script without actually running it. (Typically, you would do this to investigate the unexpected inclusion or exclusion of files.) To do so, run the command: % qef -Pscript #:Qef=qsg -M #srcmap /ph/dt/tut/tut1/hello.c #include mimkvars.qh #include deps.qh Note the script contains qef macros and variables: _S_(), _Touch_(), _T_cc, etc. See var(x-qefpp) for a description of any macro var. The names of development tools, as well as the arguments they accept, are set using such variables. Two special forms: _T_prog and _F_prog are of particular importance:
|
||||||||
Targets |
Targets are the possible products of a given construction, i.e., items you can ask the system to build for you. For a list of the possible targets, use the command: % qef -Ptargets Local: do local constructions Install: do installations RemoveInst: remove objects installed into other directories RemoveObjs: remove objects in local directories RemoveAll: remove installed and local objects Any of above can be followed by `List' which suppresses removals and leaves list in place. Man: install manual sections Objects: produce all *.o files <module>.o: produce object file for module <module>.i: produce preprocessed C code for module.[cly] Sfiles: produce *.s files <module>.s: produce assembly code for module.[cly] <module>I: do Install actions for module These are the targets offered by the commands script. The list covers all the typical outputs desired by a developer. To support new targets, you would revise the commands script and add the new targets, but that is beyond the scope of this tutorial and will be covered later. Before going further, you may wish to experiment with qef, building the various targets and removing part(s) of the system. |
||||||||
Summary | |||||||||
You now know how to run qef and how to
indicate which of the possible targets you wish to create.
You have examined the configuration file qeffile, and learned about its structure. By applying the v(erbose) flag, you have seen how qef calls its subordinate tools when performing the construction. So far, you have seen only a few of the subordinate tools, but they serve important purposes. qvrs establishes the construction environment, which is not dependent upon simple shell variables, but instead maintains a database and uses it when creating scripts. Thus it steps around both the limitations of shell variables and the isolation of construction information embedded in make scripts. In practice, this means that all your development tools will have all the information they require when they require it. sls is an ls-like program that understands the qef environment. It searches the source path defined in a project's configuration files, and can produce a listing of source files that spans the directories of the source search path. qsg is one of the two script generators used in almost all cases. (The other is qefdirs.) incls is used to generate a list of the included files required either for a specific file or for an entire project. It is exhaustive, walking the list of files recursively until all references have been resolved. mimk is qef's version of make. It differs from typical make programs in two important ways. First, since its input scripts are generated, mimk has no need and therefore no facility for variables. Secondly, it uses much more advanced consistency checking mechanisms, much better than make's simplistic and insufficient "out-of-date" rule. It also maintains a record of all information used in commands it executes, including the reason for the last build. Besides building the desired products, qef produces or maintains a variety of files:
This concludes the first tutorial; the second tutorial builds on the basics covered here, extending them to more interesting cases. |
In the second tutorial, you will use several more of qef's command options, and examine the contents of the various configuration (qvrs) files. The following concepts are introduced:
To install the second tutorial, use the commands: % dirsetup -d /tut/tut2/src qeftut2 - create /tut/tut2/src/README - create /tut/tut2/src/QefAdm/confvrs/confdefl.vrs - create /tut/tut2/src/hello.c - create /tut/tut2/src/qeffile - create /tut/tut2/src/root.vrs % cd /tut/tut2/src This will create the appropriate directories and files and then change to the new source directory. |
|||||||||||||
Exploring the Project |
This tutorial's source is much the same as that for the first tutorial, with the addition of a directory QefAdm/confvrs and two files:
Examining the root.vrs file, we get: % cat root.vrs cset Project tut2 cset TreeType baseline cset Revision 1.1 The cset keyword sets a qvrs variable, but only if it is not set already. Now try building in this tree: % qef #{ @host <date> <time> # QEFHALTFILE: /tut/tut2/src/%qef00783b.hlt qef: Building in a tree with TreeType baseline prohibited #} E-1 host@/tut/tut2/src <time>(0) |
||||||||||||
Trees and Tree Types |
The error message above mentions a variable TreeType. This variable identifies the tree's type which, in turn, controls certain aspects of qef's behavior within that tree. Each project in qef is a "forest" of trees. There are three main tree types: baseline, working, and object. The baseline tree represents the read-only source base that is shared by all developers. A working tree contains the source used by a single developer. An object tree is used to contain the builds for a single configuration, thus a developer or build manager may have multiple object trees.
One of the software construction principles supported by qef is the separation of source files from generated or object files. In order to protect the project's source trees from contamination, qef will not normally run if the tree's type is not object or undefined. You can override this restriction -- we did in the first tutorial -- but it is not recommended. Where do you build the product, then? In a mirror (or parallel) tree called the object tree. Each hardware platform or unique configuration has its own object tree, generated from a common source tree. It is worth noting that the product is not installed in a destination tree, but in a destination directory. The reason is that the baseline source tree should be organized according to the logical structure of the source. The working and object trees mirror that organization. But the logical structure of the source may have no relation to the placement of the final products. For example, the modules for two different libraries should be kept in separate directories because the libraries are separate components. But both finished libraries will probably be installed in the same destination directory, namely lib in the tree named by _DestDir_. |
||||||||||||
Creating an Object Tree |
To create an object tree for the current source tree, use mkqtree as in: % mkqtree -o ../obj # create object tree in /tut/tut2/obj + mkdir ../obj + chdir ../obj + rootvrs -t object /tut/tut2/src # output to root.vrs addpath RootPath /tut/tut2/src cset RootAlias @NotSet cset Project tut2 cset Revision 1.1 cset TreeType object cset TreeType[/tut/tut2/src] baseline cset BuildHost @NotSet cset BuildSys Linux-2.0.34-i686 cset QremoteEnv @NotSet # defaults to qremote cset _DestDir_ @NotSet cset PrereqList tcl(7.4) cset ConfigName @NotSet cset ConfVrs @NotSet # defaults to conf.vrs cset SysVrs @NotSet # defaults to linux2.vrs cset TreedirsList @NotSet # defaults to treedirs.lst cset VCSys @NotSet cset VCRoot @NotSet # defaults to @LastRoot cset VCBranch @NotSet cset OldRoot @NotSet cset EditBranches @NotSet # defaults to 0 qdupd: Added: 12 tut2 1.1 object dt gobo - /tut/tut2/obj ... + confvrs -cn Copying /tut/tut2/src/QefAdm/confvrs/confdefl.vrs to conf.vrs + treedirs -s The "+" lines in the above output indicates commands that are executed. The above mkqtree created the new directory and invoked rootvrs (creates root.vrs), confvrs (copies the default configuration file to conf.vrs in the new directory), treedirs (creates directories in the new tree duplicating the directory structure of the source tree), and qdupd (adds new path to the database). Two important variables are worth noting: TreeType is set to the value "object" and RootPath has the baseline source directory added to it.
Now change directory into the new tree and run sls to list the files in the project's source path. % qd -12 # using the newly created path Using 12 tut2 1.1 object dt host - /tut/tut2/obj /tut/tut2/src % sls /tut/tut2/src/QefAdm /tut/tut2/src/README /tut/tut2/obj/conf.vrs /tut/tut2/src/hello.c /tut/tut2/src/qeffile /tut/tut2/obj/root.vrs The project's configuration is now split among three qvrs files: root.vrs, containing tree specific settings; conf.vrs, containing the user configuration specific variables; and qeffile, containing the local configuration. List the files in the newly created object tree: % ls conf.vrs root.vrs % qvrs -f # list qvrs files /tut/tut2/obj/root.vrs /tut/tut2/obj/conf.vrs <qtree>/lib/sysvrs/linux2.vrs /tut/tut2/src/qeffile With these project configuration variables established in these files, build the system: % qef #{ @host <date> <time> + cc -c /tut/tut2/src/hello.c + cc hello.o hello #} E0 host@/tut/tut2/obj <date> <time>(#) Note that the source files are accessed with their full paths. They could (and in production installations, typically do) reside on another machine, a file server somewhere on the network. qef constructs the system as expected, but this is the developer's build, not the final system. Now we'll install the final system: % qef Install #{ Install @host 2000/02/26 02:26:02 # QEFHALTFILE: /tut/tut2/obj/%qef25682b.hlt + instal -E -d @NotSet/bin/hello hello instal: @NotSet/bin/hello: will not install @NotSet files *** Error code 255 Fatal error - waiting for active jobs to complete. mimk: Fatal error during construction #} E1 host@/tut/tut2/obj 2000/02/26 02:26:02(0) In the last tutorial, you fixed this by setting the variable _DestDir_ in the leaf.vrs file. That is not really the appropriate place to set it. Given the importance of this variable, it is set in the root.vrs file. Rather than edit the root.vrs file directly, use the rootvrs utility to set the destination directory: % rootvrs -d ../dest # output to root.vrs ... # unchanged lines not shown cset _DestDir_ @(resolve @RootDir/../dest) qdupd: Unchanged: 12 tut2 1.1 object dt host - /tut/... You can now install the system: % qef Install #{ Install @host 2000/02/26 02:34:26 # QEFHALTFILE: /tut/tut2/obj/%qef25756b.hlt + instal -E -d /tut/tut2/dest/bin/hello hello #} E0 host@/tut/tut2/obj 2000/02/26 02:34:26(0) qef builds the system, then does the installation, placing the binary in the destination directory. |
||||||||||||
Configuration (Qvrs) Files |
qef looks at a group of files called qvrs files for construction information. These files are arranged in a hierarchy, and their scope operates according to the hierarchy. For a list of the qvrs files that apply to the current directory, type: % qvrs -f /tut/tut2/obj/root.vrs /tut/tut2/obj/conf.vrs <qtree>/lib/sysvrs/linux2.vrs /tut/tut2/src/qeffile Depending on their location and name, the qvrs files exercise varying degrees of control over the behavior of qef. Some files affect the entire system (the system defaults file, sysnm.vrs, where sysnm is replaced by the type of the system); some affect a given directory and its children (root.vrs, conf.vrs); and some affect only the directory in which they reside (qeffile). qvrs is one of the key tools in the qef toolbox. As you work with qef, you will find it useful for inspecting the construction variables affecting a project, branch or directory. To list all the configuration variables currently defined, run qvrs with no arguments: % qvrs AsSuffix s BeginLine qsg M Branch . BuildPath <qtree-home>/bin /bin /usr/bin BuildSys Linux-2.0.34-i686 CcInclDirs GNUDIR/include /usr/include CcLibDirs GNUDIR /lib /usr/lib ConfVrsFile /tut/tut2/obj/conf.vrs ConfVrsPath /tut/tut2/obj /tut/tut2/src/QefAdm/confvrs CurDir /tut/tut2/obj # rest elided Use x-qvrs <variable> to learn more about a specific variable, as in: % x-qvrs CurDir CurDir : current working directory Prior to processing its first input file, qvrs sets CurDir to the full pathname of the current directory. See also: RootDir Branch SubDir Dots RootAlias You can also run qvrs to find the value of a particular variable, using the @ sign to indicate the variable of interest. For example: % qvrs @_DestDir_ /tut/tut2/dest % qvrs -VD # a short cut for the above /tut/tut2/dest % qvrs -V? # show list of other variable short cuts List of -V arguments -V output version number and exit -V? output this list and exit -VBH @BuildHost ... Note that the _DestDir_ path has been resolved. This is an important concept -- project specifics are abstracted into symbolic representations, so that no matter how complex they are, references to them remain simple. Change one variable in one place, and you make that change throughout the whole system or any desired part. The qvrs W flag is used to find out where a variable is set, as in: % qvrs -W _DestDir_ /tut/tut2/obj/root.vrs:10: set _DestDir_ /tut/tut2/dest |
||||||||||||
Removing Files |
To remove installed and/or object files, qef provides three special targets: RemoveInst, RemoveObjs, and RemoveAll: % qef RemoveInst #{ RemoveInst @host 2000/02/26 03:43:25 # QEFHALTFILE: /tut/tut2/obj/%qef26177b.hlt - rm /tut/tut2/dest/bin/hello #} E0 host@/tut/tut2/obj 2000/02/26 03:43:25(0) This command removes all installed files that qef knows how to build, wherever they may be and no matter how many there are. It does not remove files qef does not know how to build. The RemoveInst, RemoveObjs, and RemoveAll options actually traverse the directories of the build, constructing a list of all the files to be removed. Once all the directories have been visited, the files in the list are removed. The removals are not done until all directories have been processed to deal with those situations in which tools that would be removed are used in later directories in script generation. |
||||||||||||
Qef Options |
The LDEBUGGING option used earlier is one of more than a dozen qef (and qvrs) variables used as options by various qef utilities. The following table is a list of some of the most commonly used options. You are free to create new options, either globally or on a project-specific level.
Usually such options are set in the user's conf.vrs file at the root of the tree. |
||||||||||||
Standard Construction Methods |
One problem experienced by many software production groups is the absence of a standard construction method. qef addresses this issue. The options in the table above are close to universal: virtually every project you build should support them. While you may not have certain development tools such as Purify and Quantify, you will almost certainly want to build debugging and profiling versions, produce manual pages, create shared libraries, and so on. Using qef, all of these options are built into every software construction process. Moreover, every developer will be able to select the same options on every project, on every platform.
To find out more about any particular option, use x-qvrs, as in: % x-qvrs DEBUGGING ... To find out the options supported within most projects, use the command: % x-qvrs opt-vars ... |
The third tutorial deals with problems in a real-world project, with application code and two libraries, one of which depends upon the other. This section presents several new concepts and components:
To install the tutorial, use the following commands: % dirsetup -d /tut/tut3/src qeftut3 - mkdir /tut/tut3/src/QefAdm/confvrs/RCS - mkdir /tut/tut3/src/QefAdm/relinfo/RCS ... % cd /tut/tut3/src This creates the /tut/tut3/src directory and within it several sub-directories. |
|||
Outline of Tutorial |
This tutorial goes through the typical steps followed by a developer working on an ongoing project. Some of the initial steps are executed only once, such as setting the configuration name. Other steps, such as copying files to and from the baseline source and the work directory, are repeated frequently. A typical use of qef will resemble the following steps:
|
||
qdsrv: The QEF Directory Server |
For this tutorial, qdsrv, the QEF directory server, must be running. To determine if it is, issue the command: % qdmgt -v # if qdsrv is running ... Qdsrv 9.1(162) - 2000/01/19 01:08:00 - Linux-2.0.34-i686 pid=455; uid=0; host=host; port=6631 Database: <qtree>/data/qdsrv.db # If qdsrv is not running ... qdmgt: connect failed -- Connection refused check that qdsrv is running -- See errQdsrv1(x-qmisc)If the qdsrv is not running, it needs to be started using: % qdsrv qdsrv started: errors will be logged in <qtree>/data/qdsrv.logThe above might fail for a variety of reasons. For a more detailed description of starting the qdsrv, see Chapter 6. One of the qdsrv client programs qds show entries in database. qds's d flag, it outputs a directory name that can be used as an argument to cd. Rather than constantly typing cd $(qds -d <query-options>), you should define an alias or function qd as described in Chapter 3.1: define qd. The qd alias is used in this tutorial; you may want to define it now, using: % eval `qfunc qd` # if using sh % eval `qfunc -c qd` # if using csh |
||
The Source Tree |
This tutorial uses the following source tree: src/ QefAdm/ confvrs/ RCS/ confdefl.vrs,v # other RCS files not listed confdefl.vrs relinfo/ qeffile touchdir/ qeffile README appl/ appl/ appl.h qeffile cmd/ appl.c qeffile lib/ qeffile rtn.c man/ appl.1 qeffile qeffile tree.vrs cmd/ hello.c qeffile qeffile root.vrs tree.vrs This tree demonstrates most of the configuration files and
structures used for a large project, other than the FSIC package
described in Chapter 9.
Only appl/ and cmd/ contain source.
|
||
Adding the Baseline to the Database |
Now change directory to the source tree, and add the tree to the directory server's database: % cd /tut/tut3/src % qdupd qdupd: Added: 3 tut3 1.1 baseline you host - /tut/tut3/src An entry has been added to the server database marking this as a baseline tree. This is normally done as soon as the project is created. For future use note the record index (e.g., "3") that appears immediately after "Added:" in the qdupd output. |
||
Creating the Developer's Workspace |
In this tutorial, the src directory is the baseline. In practice, no developer would make changes in the baseline code -- the baseline tree is intended to be the approved, ready-for-production code, changed by the project librarian or manager. To perform maintenance on the project, the first thing the developer does is create a personal work directory or workspace. With the root of the source tree as your current directory, create a working source directory named "work": % mkqtree -w ../work + mkdir ../work + chdir ../work + rootvrs -t working /tut/tut3/src # output to root.vrs addpath RootPath /tut/tut3/src cset Project tut3 cset Revision 1.1 cset TreeType working cset TreeType[/tut/tut3/src] baseline cset QefAdm QefAdm qdupd: Added: 9 tut3 1.1 working you host - /tut/tut3/work /tut/tut3/src # confvrs not done as TreeType not `object' + treedirs -s + mkdir QefAdm + mkdir QefAdm/relinfo + mkdir QefAdm/touchdir + mkdir appl + mkdir appl/appl + mkdir appl/cmd + mkdir appl/lib + mkdir appl/man + mkdir cmd The w is actually a short form for tworking. This command duplicates the relevant part of the src hierarchy into a directory called work, The work directory hierarchy contains no files yet, other than the root.vrs file, which establishes the link to the baseline tree. Instead of copying or linking all the baseline source files, you create working copies of the files you will change, storing them remotely from the originals and including the copies rather than the originals in the project construction. All your working copies mask their counterparts in the source tree. Ideally, you should be able to copy any file(s) to your workspace at any time, then build the project using these files without making any other adjustments. Since the workspace is before the baseline source directory in the source path list, any file residing there will be found and used before a file of the same name in the baseline source tree. Similarly, moving a completed file out of the workspace and back to the source tree also requires no changes in the construction scripts. The move will be noticed, and documented by QEF during the build, so the developer is always certain which files were used to build the product |
||
Creating the Object Tree |
Next, you need an object tree to receive all the object files created during the construction. Chdir to the work tree and run mkqtree as follows: % cd ../work % mkqtree -o ../obj + mkdir ../obj + chdir ../obj + rootvrs -t object /tut/tut3/work # output to root.vrs addpath RootPath /tut/tut3/work /tut/tut3/src cset Project tut3 cset Revision 1.1 cset TreeType object cset TreeType[/tut/tut3/work] working cset TreeType[/tut/tut3/src] baseline cset BuildSys Linux-2.0.34-i686 cset QefAdm QefAdm qdupd: Added: 12 tut3 1.1 object you host - /tut/tut3/obj /tut/tut3/work /tut/tut3/src + confvrs -cn Copying /tut/tut3/src/QefAdm/confvrs/confdefl.vrs to conf.vrs + treedirs -s + mkdir QefAdm + mkdir ... |
||
Listing the Configuration Files |
To see which configuration files affect builds in the current directory, list the active configuration files with qvrs f. Chdir to the object tree and list the configuration files: % cd ../obj % qvrs -f /tut/tut3/obj/root.vrs /tut/tut3/obj/conf.vrs /tut/tut3/src/tree.vrs <qtree>/lib/sysvrs/sysdefl.vrs /tut/tut3/src/qeffile You can run qvrs with multiple arguments, including variable names and even expressions. For example, to display the assignments of the variables RootPath and SrcPath: % qvrs "RootPath = @RootPath" "SrcPath = @SrcPath" RootPath = /tut/tut3/obj /tut/tut3/work /tut/tut3/src SrcPath = . /tut/tut3/work /tut/tut3/src Viewing the configuration files can teach you more about their relative purpose in the scheme of the project, You may wish to examine them from time to time as you do the builds. |
||
_DestDir_ and ConfigName |
At this point you must set the destination directory (the variable _DestDir_), You should also define the ConfigName variable. ConfigName is a one-word name to describe this particular platform or project. It is usually included in the version strings and appears in the qdsrv database paths. In this case, set it to the default for the current system. The rootvrs command can be used to set both these variables, as in: % rootvrs -d ../dest -c . ... cset _DestDir_ @(resolve @RootDir/../dest) cset ConfigName linux2_0i ...
Setup is now complete. You are ready to build the entire project, but before we continue, let us explore the use of the qdsrv database to navigate back and forth from tree to tree. |
||
Using qd to change trees |
For a list of the currently relevant trees use qds. This will list the tree records for the current project, host and user. % qds 2 tut3 1.1 baseline you host - /tut3/src 9 tut3 1.1 working you host - /tut3/work /tut3/src 12 tut3 1.1 object you host linux2_0i /tut3/obj /tut3/work /tut3/src The qd alias or function that we defined previously can now be used to move back and forth between the trees. qds only not serves to help navigate the forest of project trees, but it also helps navigate the local woods, as represented by the three trees that we have now. The following is a series of qdsrv commands that will illustrate some of the more important facilities. % qd -ptut3 -tobject / # cd to your tut3 object tree # the slash indicates to go to the root % qd -ptut3 -o # - -o equivalent to -tobject % qd -w # cd to the working tree % qd -b # cd to the baseline tree % qd -o # cd back to the object tree % qd -n # chdir to next tree (i.e., working) % qd -n # chdir to next tree (i.e., baseline) % qd -n # nowhere to go, so we stay where we are % qd -w appl/cmd # chdir to appl/cmd in working tree % qd -o # back to appl/cmd in object tree % qd -w .. # back to working tree but up one % qds -R # print current Root % qd -R /cmd # cmd directory in current root % qd -o # get back to the object tree % qdid -s # bind user identifier to current tree % cd /etc # go wandering % qd -i # return to bound tree % cd /usr/lib # go wandering again % qd -i1 # return to dir1 of path (the working tree) % qd -0 # path 0 is always your home % qd -ptut3 # Ambiguous ... you will be shown numbered list % qd -<num> # chdir to path <num> That should be enough for now. Be sure to end up in the root of the object tree before trying to build the system, i.e.: % qd -i / |
||
Building the Entire Project |
Building the entire project at this point has two purposes: it confirms that everything is running smoothly, and it initializes the build log. Type the command: % qd -i / # chdir to the root of the project % go qef All # go will redirect output to ,.g % g -f # tail the qef output; ^C to interrupt ... You should be able to examine the output in ,.g and get a general sense of what is happening in the build. % g # cat the ,.g file #{ All @host 2000/02/27 04:03:51 # QEFHALTFILE: /tut/tut3/obj/%qef00527b.hlt #-{ -d appl Install @host 2000/02/27 04:03:52 #--{ -d appl Install @host 2000/02/27 04:03:52 ... % g -t # tail the ,.g file ... #-} E0 host@/tut/tut3/obj/appl 2000/02/27 04:03:59(1) #-{ -d QefAdm/relinfo Post @host 2000/02/27 04:03:59 + mkvernum -o relinfo -0Tut3 -1"" -l relinfo.vf release + instal -S /tut/tut3/dest/relinfo relinfo #-} E0 host@/tut/tut3/obj/QefAdm/relinfo 2000/02/27 04:03:59(0) #} E0 host@/tut/tut3/obj 2000/02/27 04:03:59(8)Now look at the files in the current directory. % l -f # list files in directory ,.g conf.vrs instal.log root.vrsThe instal.log file is a record of all the installations done by instal. |
||
Multi- Directory Builds and qefdirs |
This is an appropriate time to look at the qeffile that builds the project. % cat `sls qeffile` # sls searches SrcPath directories # for argument # A qefdirs qeffile set _DefaultArgs_ All Begin qefdirs # Note: `qefdirs -X' outputs a describe syntax and semantics P = Post # add Post construction key P; qefdirs already # provides I=Install, L=Local, and M=Man All = Install Man Post # add directories and operations after this line appl ILM cmd IL appl # cmd requires appl's installation + a new level QefAdm/relinfo PThis is the first qeffile we have examined that uses qefdirs as its script generator. As a rule, qefdirs is used in nodes of the tree hierarchy, and qsg is used in leaves of the tree. The simplest qeffile using qefdirs is the Begin line and a list of directories. When a qef construction is specified in the current directory, the same construction is triggered in the listed directories that support that construction. The first statement defines a new construction key, Post. The second statement redefines All so it is a combination of Install, Man, and Post. (Normally it is a combination of Install and Man.) The rest of the script defines the directories, their constructions, and the relationships:
The + defines a new level. The levels mechanism allows you to split the directory list into groups that can be selected or suppressed. In this case, the done directory represents another level. You can invoke a construction on either or both levels. qefdirs provides a flag that shows the directories and the constructions they support: % qefdirs -L # L for levels Install 1 appl cmd Man 1 appl Post 2 QefAdm/relinfoThe first column names the construction, the second the level number, and the third the directories supporting the construction in that level. The sub-directory appl also has a qefdirs qeffile: % cd appl % cat `sls qeffile` Begin qefdirs appl I lib IL + cmd IL -lib man m # m == Man, but only if @NO_MAN not set % qefdirs -L Install 1 appl lib 2 cmd Man 2 man |
||
Modifying Source Files in the Work Tree |
To protect the baseline source files, developers copy files they wish to edit to their workspace, and work on them there. Files located anywhere in the workspace take precedence over the corresponding files in the source tree. To illustrate, check out a file: % qd -o /appl/cmd # chdir to obj/appl/cmd % sls -c # list the files corresponding to this directory /tut/tut3/obj/appl/cmd: appl appl.o incls._ qmkhist._ srclist._ /tut/tut3/src/appl/cmd: RCS appl.c qeffile % sls appl.c # determine the location of a specific file /tut/tut3/src/appl/cmd/appl.c % vci co -e appl.c # check out appl..c for editing /tut/tut3/src/appl/cmd/RCS/appl.c,v --> appl.c revision 1.1 (locked) done % sls appl.c # visible appl.c now in working tree /tut/tut3/work/appl/cmd/appl.c % sls -a appl.c # list all the appl.c files in the path /tut/tut3/work/appl/cmd/appl.c /tut/tut3/src/appl/cmd/appl.c % vci -X # list supported commands changed tell what files have been changed (RCS) ci check in the argument files (RCS) co check out the argument files (RCS) diff run rcsdiff on the g-file and the v-file (RCS) ... tell tell what files are being edited (RCS) ... % vci tell # list locked files /tut/tut3/src/appl/cmd/appl.c Now, build the project again: % qef #{ @host <date> <time> Moved: /tut/tut3/src/appl/cmd/appl.c /tut/tut3/work/appl/cmd/appl.c ... #} E0 host@/tut/tut3/obj/appl/cmd <date> <time>(1) Since this version of appl.c is new, this portion of the system is rebuilt. Now make some minor changes to appl.c % vi `sls appl.c` # replace vi by whatever you want # make some changes such as adding, deleting lines % vci diff appl.c # view changes using diff ... % vci vdiff appl.c # view changes using qgdiff # see Cook's Tour - Page 8 |
||
Undoing a Change |
In this project, there are no new source files. Everything in the working tree is a copy of a file in the baseline tree. Thus you are free to experiment. From time to time, you may wish to undo a particular change to the working copy of a project. With QEF, just "unget" the copy from the work tree and rebuild the project: % vci unget appl.c % qef #{ @host <date> <start time> Moved: /tut/tut3/work/appl/appl.c /tut/tut3/src/appl/appl.c ... #} E0 host@/tut/tut3/obj <date> <end time>(1) |
||
Building the Complete System |
You have already seen how easily qef can build any portion of a system: simply go into the tree to the directory of interest and run qef from there. You can also perform a system-wide build (of the files that need to be rebuilt): % cd /tut/tut3/obj # root of the object tree % qef All #{ All @host <date> <start time> # QEFHALTFILE: /tut/tut3/obj/%qef01195b.hlt #-{ -d appl Install @host <date> <start time> ... #-} E0 host@/tut/tut3/obj/done <end.time>(1) #} E0 host@/tut/tut3/obj <date> <end time>(9) |
||
Specifying Libraries |
The qef tools support several idioms for specifying the libraries to be linked with program modules. The most convenient way when writing code is to include a LIBS comment in the main source file. A LIBS comment should be placed in the first 1000 characters of source and should have the following form: /* LIBS: -lX11 -lm */ This example specifies the X11 and the math libraries. The argument libraries are automatically extracted from the source and included in the build script. If you do not want to edit all of your source code, you may also specify the libraries using qvrs variables. Set the qvrs variable LIBS[file.c] to the symbolic library list. |
||
Review of the Project Config Files |
The next several sections document this project's configuration files.
By reviewing these,
you should arrive at an understanding of how the project's
components are configured.
|
||
The QEF Version Control Interface |
Many software shops use a version control tool such as SCCS or RCS to monitor changes to the source. qef provides a standard interface to version control tools. The version system control interface (vci) runs the version control commands with full knowledge of qef's viewpathing facilities. The baseline source is the source under version control. Instead of copying a file to be changed from the baseline source, the developer gives the command vci co <file> (for "version control interface checkout"). To submit the changed source, the developer gives the command vci ci <file> (for "version control interface checkin"). Depending upon how your qef system is configured, submitting the changed source may incorporate it into the baseline source, or it may move the source into a mirror tree which contains "unpublished" or unapproved sources. Someone (the project manager, the code librarian, the quality assurance staff) then authorizes the code to be incorporated into the baseline source. |
||
Summary | |||
In this tutorial, you have examined and worked with a real-world project whose files are organized as a tree of directories. You learned a number of important lessons about QEF, including:
As stated at the beginning of this chapter, the reader should look at "A Cook's Tour of the QEF", as it contains a more comprehensive coverage of the QEF system. |
This section has a vague title because we expect it to grow as we discover new things that the user needs or wants to know early in their QEF use. | ||||||||||||||||||||||||||
Shell, traits, qvrs, & qefpp variables | ||||||||||||||||||||||||||
The QEF system uses four different types of variables in configuring or parameterizing processes. The purpose of this note is to bring to the reader's attention how these variables work together. The types of variables are: | ||||||||||||||||||||||||||
shell environment variables | such as $PATH or $QTREE. Shell variables are typically set by the user's login process or user-invoked processes such as envset. Shell variables are commonly used to establish the user's environment and to store information about the user and the user's session. The QEF system supports the manipulation and use of shell variables, but avoids using them as much as is possible due to their poor control. Other than $QTREE, QEF uses only the standard variables (e.g., PATH, TERM, TZ, and HOME), plus variables to override the default file names for various tools, and settings to pass information to child processes. For a list of the environment variables used by QEF see *env(x-qvrs). | |||||||||||||||||||||||||
traits variables |
traits are typically used to store
characteristics of the host system such as special directories or tool
names and capabilities that cannot be determined by system type alone.
traits are set via the system <qtree>/lib/traits.vrs
and the local <qtree>/data/traits.ext files, which
are converted into a host specific traits binary database
by mktraits.
|
|||||||||||||||||||||||||
qvrs variables | The qvrs system is described in great detail in the x_db database x-qvrs, the qvrs(1) man page, and in Chapter 8 of this guide. In many ways qvrs is the single most important program in the system as it establishes the environment in which all other tools run. qvrs primary role is to find the tree, project, user, system, directory, and project/system specific configuration files, parse them, and deliver to other programs the settings as a variable/value tuple database. The variables specified via qvrs variables encompass every aspect of the system, from the names of tools, search paths, semantic options, library mappings and search mechanisms, user and system environment information, to flags for the processing of specific files by specific tools. | |||||||||||||||||||||||||
qefpp macros |
The qef pre-processor uses macros similar to those of
the C pre-processor in C, but with very different restrictions.
Their names must be a least three characters and begin with a `_'
or an upper case letter.
In fact, due to their use within text files that do not have a rigourous
syntax, all internal macros and nearly all macros in provided scripts
begin and end with an `_' to minimize the likelihood of the name being
the same as that of a file.
These variables are used throughout the QEF system to control the way tools behave and the environment in which they function. The preservation and management of the variables is crucial to the health of your software. The following are notes on the care and management of your variables and some explanations of the naming conventions.
|
c020.qh - 9.4 - 03/10/27 |