CHAPTER 4: Making a Task This chapter describes how to make SPP source into a working pro- gram. In most cases, this means creating an IRAF task. That is, a command to be executed in the IRAF cl. Inherent in creating the task is compiling and linking the source to create an executable program. We also describe the conventional structure of packages of tasks in the cl. Program Structure An SPP source file may contain any number of procedure declara- tions, zero or one task statements, any number of define or include statements, and any number of help text segments. By convention, global definitions and include file references should appear at the beginning of the file, followed by the task statement, if any, and the procedure declarations. The task Statement The task statement is used to make an IRAF task. That is, a command recognized in the cl as an executable program. Primarily, this is accom- plished with the task statement, part of the SPP code. A file need not con- tain a task statement, and may not contain more than a single task statement. Files without task statements are separately compiled to produce object modules, which may subsequently be linked together to make a task, or which may be installed in a library. A single physical task (ptask) may contain one or more logical tasks (ltasks). These tasks need not be related. Several ltasks may be grouped together into a single ptask merely to save disk storage, or to minimize the overhead of task execution. Logical tasks should communicate with one another only via disk files, even if they reside in the same physical task. task ltask1, ltask2, ltask3 = proc3 The task statement defines a set of ltasks, and associates each with a compiled procedure (see Example 4.1). If only the name of the ltask is given in the task statement, the associated procedure is assumed to have the same name. A file may contain any number of ordinary procedures which are not associated (directly) with an ltask. The source for the procedure associated with a given ltask need not reside in the same file as the task statement. An ltask associated procedure must not have any arguments. An ltask procedure gets its parameters from the cl via the cl interface. Most commonly used are the clgetT() procedures. The clputT() proce- dures may be used to change the values of parameters. Example 4.1: Making an IRAF Task. An IRAF task be run by the cl or called from the command interpreter provided by the host operating system (the shell or DCL for example) with- out change. Parameter requests and I/O to the standard input and output will function properly in both cases. When running without the cl, of course, the interface is much more primitive. To run an IRAF task directly, without the cl begin by simply running the program. Such stand-alone operation is especially useful when debugging. The task will sense that it is being run without the cl and issue a prompt, see Example 4.2. Example 4.2: Parameter Prompting. Every IRAF task has some special commands built in. The command ? will list the names of the ltasks recognized by the interpreter. The com- mand bye is used to exit the interpreter, returning to the host command interpreter. To execute a host command at the > prompt, precede the com- mand by an exclamation point (!). Compiling and Linking The steps necessary to transform SPP code into a working program are: 1. Preprocesses SPP to Ratfor and then to Fortran 2. Translate Ratfor to Fortran 3. Compile Fortran to object code 4. Link object with IRAF and system libraries resulting in executable binary These could be performed individually and manually. However, to pro- vide a simple and portable mechanism (remember that the goal is for IRAF to be host independent), IRAF provides tools to do this. While the tools are straightforward for simple cases, they provide the power to handle more sophisticated operations. mkpkg The mkpkg utility is used to make or update IRAF packages or libraries. It is the highest level means of compiling and linking in the IRAF environ- ment. There is a mkpkg command available in the cl as well as the host environment. Usage is identical in either case, except that the details of when a particular argument may need to be quoted will vary depending on the command language used. It is analogous to the make utility in Unix in that it not only performs compilation and linking, but it also performs enough revision control to perform only the needed updates. While mkpkg uses several command line options to control its operation, the particular actions to perform are specified in a text file, the mkpkg file. This section provides only the briefest introduction to mkpkg. For a complete discussion see the help pages in the cl by typing help mkpkg. mkpkg provides two major facilities: a library update capability and a macro preprocessor. The macro preprocessor provides symbol definition and replacement, conditional execution, and a number of built-in commands. The usefulness of these facilities is enhanced by the ability of mkpkg to update entire directory trees, or to enter the hierarchy of mkpkg descriptors at any level. For example, typing mkpkg in the root directory of IRAF will make or update the entire system, whereas in the iraf$sys directory mkpkg will update only the system libraries, and in the iraf$sys/fio directory mkpkg will update only the fio portion of the system library libsys.a. The mkpkg utility is quite simple to use to maintain small packages or libraries, despite its full complexity of the discussion which follows. The reader is encouraged to study several examples of working mkpkg files before reading further; examples will be found throughout the IRAF sys- tem. The mkpkg files for applications packages tend to be very similar to one another, and it is quite possible to successfully copy and modify the mkpkg file from another package without studying the reference informa- tion given here. A very simple mkpkg file is shown below: $omake imtoal.x $link imtoal.o This will compile and link the SPP program in the file named imtoal.x, resulting in an executable program in the file imtoal.e. Note the $ characters beginning the lines. The source file (imtoal.x) is assumed to have a task statement. This type of mkpkg file would be used for the most simple applications with a small number of procedures in one or at most a few source files and requiring no libraries other than the IRAF system libraries. A slightly more complicated example (Example 4.3) maintains a library for a small package of tasks. Example 4.3: MKPKG File for Maintaining Small Library. This introduces two features of mkpkg: calling modules and maintain- ing a library. The $call statement allows different blocks of statements to be executed. These are named by labels terminated by a colon. Note that each module block must terminate with a semicolon. Otherwise, the fol- lowing block will also be executed. A block may also be called directly as an entry point by specifying the label name on the mkpkg command line, for example: mkpkg update The $update command maintains the library of procedures for the package (tutor in this case). The label tutor.a delimits the "depen- dencies" section which lists include files used by each source file. A source file will be compiled if either the source itself or any of the include files upon which it depends has changed since the last update. Note also the -o option on the $link statement, specifying the name of the output execut- able binary file. A library may include references to libraries in other direc- tories, using the @ syntax. These are mkpkg file in the specified directory. A $link statement may reference other libraries in addition to the implicit IRAF system libraries and local libraries defined in the current mkpkg. If these reside in the IRAF system (or an external package) library directory, they may be referenced using a -l prefix. For example: $link x_stplot.o stplot.a -ltbtables -lxtools -o xx_stplot.e Most often, an installed package will maintain binary executables in a common directory. These are maintained using mkpkg with the $move command: install: $move xx_stplot.e stsdasbin$x_stplot.e ; This example is from the STSDAS external package, hence the symbol stsdasbin pointing to the location of the binary. Note that the execut- able is renamed in the move. The original has a prefix xx_ while the target file has the prefix x_. This is conventional for tasks installed in packages. This permits the package to be remade without disturbing the installed binary until necessary. Even though the binaries are installed in a directory separate from the package directory, the tasks are defined pointing to the package directory as the location of the executable. xc The xc utility is a machine independent program for compiling and link- ing IRAF tasks or files. The xc utility may also be used to compile or link non-IRAF files and tasks. The VMS version of xc supports all of the impor- tant flags except -D which VMS C doesn't support in any way. It can be used to generate Fortran from SPP or Ratfor code, to compile any number of files, and then link them if desired. xc accepts and maps IRAF virtual filenames, but since it is a standalone utility (i.e., it need not run in the cl), the environment is not passed, hence logical names for directories cannot be used. Table 4.1 shows the IRAF virtual file name extensions that are supported by xc: Extension File Type -------------------------------------------------------------------- .x SPP code .r Ratfor code .f Fortran code .c C code .s Macro assembler code .o Object module .a Library file .e Executable image -------------------------------------------------------------------- Table 4.1: XC-supported Virtual File Name Extensions. xc is available both in the cl, via the foreign task interface, and as a stan- dalone task callable in the host system. Usage is equivalent in either case. The simple example below compiles and links the source file mytask.x to produce the executable mytask.e. xc mytask.x The next example compiles but does not link mytask.x and the sup- port file util.x. xc -c file.x util.x Now link these for debugging and link in the library libdeboor.a (the DeBoor spline routines in the lib directory). xc -x file.o util.o -ldeboor xc is often combined with mkpkg to automatically maintain large pack- ages or libraries. For complete information on xc see the help pages in the cl by typing help xc. Generic Preprocessor The generic preprocessor is provided in addition to SPP to convert a generic operator into a set of type specific operators. Since Fortran requires that the data types of the calling and called procedure arguments match, it is the programmer's responsibility to ensure this. The generi preprocessor makes this easier. By coding only generic operators, the programmer only has to maintain a single piece of code, reducing the possibility of an error, and greatly reducing the amount of work. Note that this section is taken substantially verbatim from the help text for the generic task. Type help generic in the cl to see it. The term "operator" here in general refers to an SPP procedure or function. The generic preprocessor takes as input files written in either the IRAF SPP language or C with embedded preprocessor directives and keywords. The calling sequence for the preprocessor (on the Unix system) is as follows: generic [-t types] [-p prefix] [-o outfile] file [file...] Any number of files may be processed. Flags The following (optional) flags are provided to control the types and names of the generated files: o -k Allow the output files generated by generic to overwrite (clobber) any existing files. o -o If an output filename is specified with the -o flag, only a single input file may be processed. Any $t sequences embedded in the output file name will be replaced by the type "suffix" character to generate the filenames of the type specific files in the generic family. If no $t sequence is given, the type suffix is appended to the filename. If no -o output filename is given, the names of the output files are formed by concatenating the type suffix to the root of the input filename. o -p An optional prefix string to be added to each file name generated. Provided to make it convenient to place all generated files in a subdirec- tory. If the name of the file(s) being preprocessed is aadd.x, and the prefix is d/, the names of the generated files will be d/aadds.x, d/aaddi.x, d/aaddl.x, and so on. o -t Used to specify the data types of the files to be produced. The default value is silrdx, meaning types SHORT through COMPLEX. Other possible types are bu, i.e., unsigned byte and unsigned short. The generic preprocessor does not support type boolean. Directives The action of the preprocessor is directed by placing $xxx directives in the text to be processed. The identifiers INDEF and PIXEL are also known to the preprocessor, and will be replaced by their type specific equivalents. INDEF will be replaced by INDEFS, INDEFI, etc., and PIXEL will be replaced by short, int, real, etc. in the generated text. Comments (#... or /*...*/), quoted strings ("...") and escaped lines (^%) are passed on unchanged. The generic operator shown in Example 4.4 computes the square root of a vector. The members of the generic family would be called asqrs, asqri, and so on. Example 4.4: Generic Operator. The operators are explained in the following list. o $/text/ - The text enclosed by the matching slashes is passed through unchanged. o $t - The lowercase value of the current type suffix character (one of the characters bucsilrdx). o $T - The uppercase value of the current type suffix character (one of the characters BUCSILRDX). o digits$f - Replaced by digits.0 if the current type is real, by digits .0D0 if the current type is double, by (digits,digits) if the type is com- plex, or by digits for all other datatypes. o $if - Conditional compilation. Two forms of the $if statement are implemented: - $if (datatype == t) or $if (datatype != t) where t is one or more of the data type characters (s, i, l, r, d, etc.). - $if (sizeof(t1) op sizeof(t2)) where t1 and t2 are type suffix characters (silrd, etc.), and where op is one of the rela- tional operators ==, !=, <=, <, >=, or >. Nesting is permitted. Conditional statements need not be left justified, i.e., white space may be placed between the beginning of the line (BOL) and a $xx preprocessor directive. o $$if - Replaced by $if. Not evaluated until the second time the file is processed. These may include an $else or $$else block executed if the $if condition was false and should be terminated by an $endif or $$endif. o TY_PIXEL - Replaced by TY_INT, TY_REAL, and so on. o SZ_PIXEL - Replaced by SZ_INT, SZ_REAL, and so on. o PIXEL - Replaced by the datatype keyword of the file currently being generated (int, real, etc.). o XPIXEL - Replaced by the defined type (XCHAR, XINT, etc.). Used in generic C programs which will be called from the subset preprocessor, and which must manipulate the subset preprocessor datatypes. o $PIXEL - Replaced by the string PIXEL (used to postpone substitution until the next pass). o INDEF - Replaced by the INDEF symbol for the current data type (INDEFS, INDEFI, INDEFL, INDEF, or INDEFX). o $INDEF - Replaced by the string INDEF. Doubly Generic Operators The preprocessor can also be used to generate doubly generic operators (operators which have two type suffixes). A good example is the type con- version operator achtxy, which converts a vector of type x to a vector of type y. If there are seven datatypes (c, s, i, l, r, d, x), this generic family will consist of 49 members. Doubly generic programs are preprocessed once to expand the first suffix, then each file generated by the first pass is processed to expand the second suffix. On the Unix system, this might be done by a command such as generic acht.x; generic -p dir/ acht[silrd].x rm acht[silrd].x This would expand acht in the current directory (generating five files), then expand each of the acht$t files in the subdirectory dir/, creating a total of 25 files in the subdirectory. The final command removes the 5 inter- mediate files. For an example of double generic code, see source for the vops proce- dure family acht() in vops$acht.gx. Parameter Files Each logical task that reads parameters from the cl using clio may spec- ify attributes of those parameters using a parameter file. Parameter attributes include the name, data type, default value, and others. The file is a text file created by the programmer and should be located in the same directory as the physical task. There is one parameter file for each logical task. Its root name is the same as the name of the associated logical task and there is an extension .par. Each task parameter is described by an entry in the parameter file consisting of positional fields separated by com- mas: name,type,mode,value,minimum,maximum,prompt All of the fields after value are optional. Fields may be omitted with adjacent commas. o name - The parameter name as known to the cl and to the application task. This is the value of the string used in the clio clgetT() and clputT() procedures. Examples of code to read task parameters are in "Interaction with the cl - clio" on page 45. o type - The data type of the parameter. That is, the type as known to the cl. Note that this need not match the data type of the corresponding SPP variable used in the application, but it makes sense to do so. This attribute takes a string value representing the type. String Value Data Type -------------------------------------------------------------------- b Boolean i Integer r Floating point s String f File name struct Structure gcur Graphics cursor imcur Image cursor pset Parameter set -------------------------------------------------------------------- Table 4.2: cl Parameter Data Types. Note that there is no distinction between sizes of numeric parameters; i.e., there is no concept of a "short" integer or a "double precision" floating point parameter. The character * preceding a type attribute indicates a "list structured" parameter. The cursor parameters must be declared as list struc- tured: *gcur and *imcur. A pset specifies a pointer to another parameter file. See the document Named External Parameter Sets in the CL [Tody86] for a complete description (on line in the IRAF file doc$pset.ms). o mode - The manner in which the cl handles prompting and learning of the parameter. - q - Query the user each time. Prompt for the parameter value even if the default is not null. - l - Learn the value of the parameter. Store the value as the new default value. - a - Automatically take the mode of the next higher level in the cl, such as the task, package or the cl itself. - h - Hide any prompting for the parameter value unless the cl cannot resolve the default value. o value - The default or initial value for the parameter. o minimum - The minimum acceptable value for the parameter. If the entered value is smaller, the cl will prompt again. In addition, a string type parameter may be defined with an "enumeration string" as the min- imum value. The parameter's value may then take on only one of the enumerated values. The enumeration string is enclosed in quotes and each enumerated value should be separated by pipe characters (|), for example: color,s,h,"white","white|black|red|green|blue",, "Graphics color" o maximum - The maximum acceptable value for the parameter. If the entered value is larger, the cl will prompt again. o prompt - The string printed by the cl as part of the prompt to describe the parameter. This may be enclosed in double quotes, required if the string contains commas. There are other fields as well that are slightly beyond this brief explana- tion. For a more detailed explanation of parameter files and parameter fields, see the CL Programmer's Manual [Downey82], a copy of which is on line in the file iraf$doc/clman.ms. Package Structure Tasks in IRAF (and external packages such as STSDAS) are organized by package in the cl. The structure directories containing the source and run-time files reflects the package structure apparent from the cl. For exam- ple, in the case of STSDAS, each package resides in a directory under the stsdas root directory just as the STSDAS packages are organized under the stsdas package in the cl. There are several files common to the pack- age as a whole and several similar files required for each task in the pack- age. These files need to be modified when installing a new task. The required common files in the package directory are: o package.cl - Package cl procedure, cl task definitions o x_package.x - SPP task definitions o mkpkg - How to build the package In the above file names, the name of the package is used in place of package. For example, the playpen package in STSDAS is in the direc- tory stsdas$pkg/playpen and the procedure script is called play- pen.cl. In addition, documentation files exist in the package level directory as well as a doc directory containing individual help files for the tasks in the package. o package.hd - Help database pointers o package.hlp - Package level help o package.men - Package menu, one line task descriptions o doc - Directory containing task help files Tasks in the Package Each task has additional files, the type of which depends on the nature of the task. These files would be added when you install a new task. Each task must also have entries in the package files. A cl procedure task requires only a task.cl file in the package directory, containing the cl statements and parameter definitions. For example, disconlab.cl in the playpen package. It also requires a task.hlp file in the doc subdi- rectory. An SPP (physical) task requires SPP source, at least one source file, by convention called t_task.x (with task replaced by the task name) playpen$t_wcslab.x, for example. Additional source files may reside in the package directory or in subdirectories. The task may use an include (header) file with the name task.h, playpen$wcslab.h, e.g. Each task requires a parameter file (unless it is a script, defined by a .cl file), task.par, containing definitions of the task parameters, such as playpen$wcslab.par. The doc directory contains the task help files, one for each task in the package. Implementation The procedure, then, is to develop the application in a private directory with a structure similar to the intended target package. Development should be done in a local user directory rather than the system directories, not even the development system. Use an existing package as an example of how to proceed. When you are ready to install the package, copy the task files to the intended package directory and edit the existing package files to include references to the new package. Run mkpkg to rebuild the package with the changes (the added task). When you are satisfied that things work, run mkpkg install to move the executable to the appropriate binaries directory.