MPW, C
Volume Number: | | 4
|
Issue Number: | | 1
|
Column Tag: | | MPW Workshop
|
Using Consulair C & TML in MPW
By William Powell, Salt Lake City, UT
William Powell is a candidate for a doctorate degree in Geophysics from the University of Utah, and is responsible for the design, documentation and delivery of software used to operate the Voyager spacecraft while a technical assistant at the Jet Propulsion Laboratory in Pasadena. He is presently doing research in the thermal physics of the Colorado Plateau
I am highly resistant to changing development systems, but after after all the fuss over MPW and reading J. Wests MacTutor article [vol. 3, no. 2] on the resource tools in the Macintosh Programmers Workshop (MPW), I decided that I couldnt live without this product. Along with everyone else, I have frequently experienced frustration from the limitations of earlier resource-building programs. I have also wished for a programmable shell on my Mac for certain data processing operations which I used to perform very conveniently on Unix systems. So I broke down and purchased the APDA release of MPW, and a hard disk to hold it. I have subsequently made some efforts toward integrating the MPW shell with older applications. I hope that some of my discoveries and command scripts may be generally useful to others who are using applications with MPW.
The programmable shell and tools of Apples Programmers Workshop (MPW) give a new dimension of flexibility to Macintosh development. The MPW brings the Macintosh a long step closer to the capabilities of more established development systems such as those available in the Bourne shell or C shell of Unix systems. This necessarily means that MPW has made a long step awayfrom earlier Macintosh application-type development systems. MPW introduces a new class of compiled program, the MPW Tool, which interacts with its shell more intimately than earlier applications interact with the Finder and similar shells.
Bridging the gulf between the MPW shell and earlier Finder application development systems is the topic for this article. I will present examples for my development systems of choice, Consulair MacC and TML Pascal. There are three core issues in joining the MPW shell and application-type development systems. First is the problem of conveniently running applications in concert with programmable shell scripts and the automatic make utility. Second is the problem of writing a new class of program, called a MPW tool, using a development system which does not contain provision for such a thing. Third is object file compatibility with the MPW standard. This article will concentrate on the first item.
Many of the techniques discussed here are relevant to almost any programming of the MPW shell and will be of interest even if the reader does not need the sample command scripts for running development applications. This article includes examples of advanced MPW editor commands, examples of complicated shell quoting problems, and examples showing the use of shell variables. Information here about launching applications from the shell and about the distinction between windows and files is not found in the MPW Reference document. I also use the Make tool three times in this article -- only one time involving building a program! I will assume that the reader is slightly familiar with the shell and has access to the MPW Reference document for more detailed background.
Motivation
Why did I make the effort to mix older development systems into MPW when an assembler and C and Pascal compilers are already being distributed? One consideration is budget; it is hard to justify the cost of anotherC or Pascal compiler when the ones I have actually work just fine. Source portability is never perfect across Macintosh compilers and I havent felt too anxious about converting yet another program to yet another compiler. Also, as noted by F. Alviani [MacTutor vol.3, no.4] the MPW system contains its own quirks, such as segmentation established in compilation rather than in linking.
There may be hardware considerations as well. The MPW documentation suggests that the MPW C compiler needs every byte of a 1M machine. This should be a serious consideration for those of us who develop in C on the pre-1987 Macs. It is good that the MPW system is forward looking in terms of newer machines, but many of us cant (or wont) upgrade our hardware and software as frequently as it is offered to the market.
On the other hand, the MPW shell offers the most flexible programmable shell and scripting abilities available for the Mac. The text editing features of the MPW shell are quite convenient, and more significantly, programmable. The first complete and extendable set of resource tools resides in the MPW. The programmable shell allows you to do things your way instead of locking you into someone elses concept of good development tools. It also provides a suitable environment for many less human-interactive data processing needs.
So there are excellent reasons to add the MPW shell to the set of tools available to most Mac programmers and there are good reasons for some of us to keep our older development systems. The puzzle now is to find convenient ways to use the power and flexibility of the MPW shell in conjunction with older Finder-based development applications which have no facility to interact with the shell.
Applications and the MPW shell
The MPW shell is primarily a command interpreter, which parses lines typed by the user and interprets them as some sort of instructions to be performed by the computer. A simple command normally appears as a line of text. The first word on a command line is the name of the command to be performed -- conventionally a verb describing the action performed (e.g. Count, Make, Delete, Open, etc.). Subsequent words on the command line are arguments which either indicate objects of the action (e.g. files to be read or written) or specify modes or options for the action (choices which would be selected with menus or modal dialogs in Macintosh applications). As with most programming languages, syntax is available to produce compound command statements from several simple commands as described here. The shell also allows the setting and testing of values of variables. When I refer to variables in the following discussion, I will use the notation {variableName}.
Commands are implemented in four different ways. Some are built into the code of the MPW shell application itself. Some are separately compiled programs called MPW Tools which run from within the shell, much as desk accessories run from within other applications. The name of a TEXT file containing one or more lines which the shell can interpret as commands may itself be used as a command. Finally, for compatibility, the authors of the MPW shell allowed the names of Finder applications to be used as commands which cause the applications to start.
Applications and the Finder environment
Normally when an application runs, it starts off with a nearly clean slate. It initializes most of the ROM toolbox managers. The only high-level data in the application areas of memory which are normally available to an application are any resources which were stored on the clipboard before the application was launched and the Finder Information. The Finder Information structure is essentially a list of files to be opened or printed by the application. For example, when a user uses shift-clicking to select several documents and an application in the Finder, the names of those documents selected by the user are passed to the application in the Finder Info structure.
There are no standards for passing any other type of information from the launch environment to the application. Some development systems and other systems of several applications use chaining instead of launching [Inside Macintosh v. II, ch. 2], which allows resources and other data to be passed in the application heap. Unfortunately this requires knowledge of coding details of the applications, and will not be suitable for integrating an arbitrary application with the MPW shell. Applications have been designed under an implicit assumption that all information about operational modes was to be provided interactively by a human user. This leads to the most common impediment in using applications as MPW commands -- it is very rare that applications can complete their job without some human intervention (at the very minimum it is usually necessary for the user to select Quit from the application menu to return to the shell). This is unfortunate, because the function and mode of applications such as compilers may be fully defined at the time they are invoked, and the user intervention becomes an unnecessary redundancy.
Figure 2. The ins and outs of MPW shell variable scope.
In spite of the inconsistency between the highly interactive user interface of applications and the more mechanized programmable MPW shell, it is possible to develop procedures to integrate shell commands and applications (particularly development systems) with reasonable success. While it is not possible to eliminate human intervention in the applications, we can minimize the actions which the user must perform. Some of the tips in this article might be viewed as an ad hoc system of MPW interface guidelines to this end.
Applications and MPW
As mentioned above, the MPW shell allows us to launch an application by invoking its name as a command. MPW maintains a shell variable called {Commands} with a list of directories (folders) to search for commands. If the application resides somewhere in this command search path list, merely the filename of the application is sufficient as a command. Otherwise the full pathname, with the volume and all subdirectories leading to the applications resource file must be used as the command name.
The MPW shell provides the standard mechanisms for putting resources on the clipboard or retrieving them from the clipboard. So passing data to or from an application on the clipboard can be performed without any special considerations. The only other information an application might expect from its launch environment is a list of files in the Finder Information structure. The MPW shell has provided a standard method to pass this information. Filenames which are to be passed in the Finder Information to the application are simply listed as arguments on the command line following the application name. Thus, we can fully configure the standard application launch environment using mechanisms explicitly provided by the shell.
Example 1. Launching application with Finder Info.
There may be a number of applications which are commonly launched by the user from within the shell; this will be especially true if the user has completely replaced the Finder with the MPW shell. I have found it convenient to add a Run menu to the MPW shell, using the shell command AddMenu. This allows selection of my most commonly used applications via menu choices. Such a menu is illustrated in Figure 1.
Most of the items in this menu are straightforward; selecting the menu item simply launches the application because the command field of the AddMenu command consisted only of the filename of the application. The last item on the menu, Word-Paint is more interesting because it launches a Switcher set of MS Word and SuperPaint. This menu selection launches the application Switcher and passes a Switcher document in the Finder Information. The command line to perform this appears as an argument in the AddMenu command which created the menu item:
AddMenu Run Word-Paint
XP40:Word:Switcher XP40:Word:Word+SuperPaint
The word AddMenu is the command which will add this item to a menu in the MPW shell. Three arguments follow the command name. The first argument is the word Run which is the name of the menu we wish to affect. The second argument Word-Paint is the text of the item being added to the menu. The curly-d () is followed immediately by a return and signifies that the command continues on the following line. The third argument is the text appearing between single quote marks. This is the command which will be executed when the menu choice is selected. The command consists of two words separated by a space. The first is the full pathname of the application to launch (Switcher) and the second is the full pathname of the document to pass to the application in the Finder Information. The document name in the Word folder is Word+SuperPaint; the extra curly-d is a literal quote of the following character, +. This quote is necessary because the unquoted + symbol is used by the shell as a metacharacter implying certain filename matching rules.
When we select this menu item, the MPW shell closes and Switcher is launched. Switcher then automatically opens the document Word+SuperPaint which in turn tells Switcher to launch those two applications. When we quit from both applications and the Switcher, the MPW shell will be restarted.
Of shell variables and search paths
A MPW shell variable is a unique symbolic name that represents storage for a string of characters. Shell commands are available to set the value of a variable, test values, and shell syntax allows the value of a variable to serve as text in a command simply by referring to the variable name. The Evaluate command also allows arithmetic operations on strings which can be interpreted as integers. Variables in the shell are entirely analogous to variables in other programming languages. The following examples will use shell variables extensively, so a very brief review of the MPW rules on variables will be helpful.
Values are assigned to variables by the Set command:
Set VarName the String value
The value of a shell variable is substituted in a command line by enclosing the variable name in curly braces, {}. Thus, when we follow the Set command above with the command:
Echo {VarName}
the output printed is:
the String value
A variable is defined when its name is first encountered by the shell. The Set command can be used to assign a value to the variable; if the first occurrence of the variable is in a command other than Set, the null string is used as a default value. The only exceptions to this are some predefined variables which are created by the shell when it is launched [ MPW Reference Manual, Table 3-2].
The scope of variables is an important consideration in designing shell scripts. Normally, the value of a variable is maintained only within the command file where that variable is defined. For the purposes of this discussion, we include commands typed by the user in windows created by the shell as the outermost scope. When a command file is executed by invoking its filename as a command, a new nested level of variable scope is created to correspond to the command. Variables defined at the level where the command file is invoked are not defined within the inner executing command file. Conversely, variables defined within an executing command file are no longer defined when that command file terminates and control returns to the level of invocation. Variables VarA and VarB in Figure 2 offer examples of these scope rules.
Two important commands modify these rules of variable scope. Using the Execute command to run a command script file, instead of using the filename as a command, causes the commands in the file to operate within the current scope rather than creating a new level of variable scope. The Export command causes a variable and its value to become available to script files which are nested within the level containing the Export command. Using Export to pass the values of variables into nested command scripts is analogous to passing subroutine arguments by value in languages such as C and Pascal. Changes in value of exported variables are therefore not passed back out to the enclosing command file. The variable VarC shown in Figure 2 illustrates the behavior of an exported variable in a shell script. Another somewhat anomalous problem of variable scope occurs when applications are started from shell scripts, but I leave further discussion of this to one of the examples below.
Standard search paths for files
Many programs need to be able to find and use auxiliary files to process their input and generate their output. For example, compilers often need to be able to merge include files containing system definitions with the current source file, and linkers need to be able to find object code libraries when constructing programs. Searching a large hierarchical file system for these auxiliary files may take a prohibitive amount of time, so it is conventional to supply a search path -- a directory or a small list of directories where the desired files are most likely to be found.
The MPW development systems use shell variables to store search paths for various items. The MPW variables are listed in the top section of Table 1 (and are also discussed in the MPW Reference Manual). Some of the variables (marked with S in the table) contain the full pathname of a single directory to be searched; other variables (marked L in the table) contain a list of full pathnames of several directories, with the pathnames separated by commas. Using this system, changes of the directory structure or locations of files is accommodated simply by adjusting the values of these variables.
Other development systems, such as TML Pascal and Consulair MacC, also need to address this search path problem. These systems use special resource files in the System Folder to save the search directories they need, and provide software allowing the user to modify the stored paths. In the rest of this section, we try to integrate these two methods of specifying search paths. We want to follow the MPW standard to save search paths in shell variables (Table 1, lower part) and then use these variables in shell scripts which will create the search path resources expected by the applications. Figure 3 shows a directory pattern for libraries, and indicates what folders are listed in various shell variables from Table 1.
Example 2. TML Search Paths.
When TML Pascal or Linker need to find include files, compiled symbol tables for Uses or object file libraries, they first look in the directory containing the source file being processed. If the file is not found there, up to five other directories are searched. The full pathname of each of these five directories is saved as a resource (type STR ) in the file Paths File in the System Folder. The TML package includes a desk accessory which provides a dialog where the user types directory pathnames for the Paths File.
Following the MPW model, the desired search paths are saved as the values of shell variables {TMLPasLib} and {TMLPasInc}. We must now write the pathname strings from these variables as the resources in the Paths File. The obvious way to do this is to use the pathname text from the variables to generate an input text file for Rez, the MPW resource compiler; Rez, in turn, will create the STR resources. Shell script file MakePPath, in the listings following the article, accomplishes this.
Table 1. Shell variables for file search paths.
System Var Name List * Purpose
Libraries S MPW runtime libraries
AIncludes L MPW Assembler header files
RIncludes S Type definitions for MPW Rez
MPW CLibraries S MPW C object libraries
CIncludes L MPW C header files
PLibraries S MPW Pascal object libraries
PIncludes L MPW Pascal include files
MacCLib S Folder containing Consulair non-objects, headers in sub folders
MPW TMLPasLib S TML Pascal object libraries
TMLPasInc L more TML object libraries and includes
* S means variable contains only one directory to search
* L means variable contains comma-separated list
MakePPath opens two temporary windows for use as scratch files. Into one window, the pathname list from shell variable {TMLPasInc} is written, so that it can be parsed with shell editor commands. The second window, TMLPathOut, is where we create the input for Rez. In MakePPath, we write the HFS path from variable {TMLPasLib} into string resource 128, and the first four HFS paths from {TMLPasInc} are written into resources 129 - 132. If {TMLPasInc} contains more than four, only the first four are used.
The main novelty in MakePPath is the loop which breaks the comma-separated list of paths from {TMLPasInc} into individual paths using the MPW editing and selection commands; this could also be useful in breaking down other lists in MPW variables. Lets consider the editor commands which do this. It is assumed that 1)the list of paths has been written as a single line in the file named Tmpfil, and 2)none of the directory names in any of the paths contain a comma.
Before entering the loop, we move the selection in the Tmpfil window (the insertion point) to the beginning of the file, before the first character.
Find Tmpfil
Then we loop until we find all the paths in the list. The first selection we make inside the loop is:
Find §:/,/ Tmpfil
This selects text from the character after the previous selection to the character preceding the next comma. This selection, if it exists, is a valid path, and we can use the notation Tmpfil.§ to refer to this selection. After we use this selection, the command:
Find §:§!1 Tmpfil
selects the comma following the interesting text. This way when the loop repeats, our next string will not begin with an extraneous comma. If the selection at the beginning of the loop could not be made, then we have reached the last path (which of course will not end with a comma) or there was no text in the variable to begin with. The command:
Find §: Tmpfil
selects all the text to the end of the file and so selects the last path, if it exists. Since all commands in the shell, including Find, return a status value, we are easily able to test whether each of these selections is successful.
The script file MakePPath is a little more elaborate because it must use only the first four strings from the variable. Also it must write null strings into the STR resources when fewer than four are specified. After MakePPath has run, the STR resources it created must still be turned into paths used by the system; the Set Paths desk accessory must be used. When the DA starts, the search paths specified by shell variable are already listed in the DAs dialog box. The user simply needs to select the Set button in the dialog to establish these search paths. The MPW script file is helpful because the version of the DA I have doesnt recognize Edit menu operations like Cut and Paste. A sometimes useful/sometimes troubling side effect of Set Paths is that most other applications which search the System Folder for help files or other support data will also search the Set Paths directories for those files.
Example 3. Consulairs Path Manager application.
The Consulair method for specifying search paths is considerably more complicated, flexible and robust than TMLs. There are many classes of files which may be sought in different directories. Consulair provides an application, Path Manager, which takes a user-written text file as input and writes a resource file containing search path information for the applications. The input source is a list of any number of search paths for the various classes of target file. Consulair has not published the format of their path resource, so rather than using Rez, we will use MPW variables holding search paths for libraries and includes to augment a Path Manager input file.
Many of the paths used with the Consulair applications are source relative, meaning that the directories to be searched are specified by partial pathnames appended to the directory where the current source file resides, rather than by full pathnames. This is a strong point in the design of the Consulair Path Manager strategy, and because many paths are source relative, many specifications in the Path Manager input will not change when the directory structure changes. The only paths we really need to worry about are the same ones considered in the MPW and TML systems: the locations of libraries and system include files. Therefore I prepare a template file for Path Manager input which contains the relative paths exactly as needed and which includes MPW variable references only for absolute pathnames to library objects and includes. When the paths change, I just need to change the values of the variables, expand the variables in the template file, and send this as input to the Path Manager application.
Lets consider two sample lines in this template file which specify some directories to search for header files (included .h files) needed by the C compiler. Path Manager input lines look like:
:C_Include*S*S:Includes:
C_Include {MacCLib}CIncludes: {MacCLib}Includes:
The first item on each input line (C_Include in this example) indicates the class of sought object for the Path Manager and applications to use. A tab-separated list of directories to search follows the class identifier on each line. In the first line of the example, *S is a Consulair metanotation which refers to the directory which contains the source file which is being compiled. This input line will cause the C compiler to look in the directory containing the source file and in a folder in that same directory which is named Includes. This input line is interpretable as is by the Path Manager and does not need to be changed when different source directories are used. More information and examples of Path Manager input can be found in the Consulair documentation.
Figure 4. Shell variable scope anomaly associated with the launching of applications. Shell script FileB is implicitly closed.
The second line of the example fits our present purpose. It lists two directories which will contain system-wide include files. This line uses shell variable {MacCLib} to refer to the two directories as shown in Figure 3. But the Path Manager does not recognize the MPW shell variables, so we must expand the variable in this input line before passing the input file to the application. Shell script MakeCPath accomplishes this task, and launches the Path Manager. Note that the placement of the template file as shown in Figure 3 allows MakeCPath to use variable {MacCLib} to locate the template. If the template isnt found we will know that the variable is not up to date!
MakeCPath uses the template file as input and writes the text file which will be read by the Path Manager application. The file for the Path Manager is named MacC.Path and placed into the System Folder for later reference. Expanding the variables in the template file without altering any other text requires a little bit of mental contortion and the use of an intermediate scratch window. The first step in the process is to open a window to the template file. In the template window, we literalize any characters which might be mistaken for shell metacharacters. For example, the asterisks in the *S entries above must be literalized. The sequence of commands
Find
Replace -c /*/ *
inserts three curly-ds in front of every asterisk in the template. The three adjacent curly-ds cause literal interpretation of the asterisk through the next two shell commands which interpret the line. Note that in general, 2n-1 curly ds will literalize a character n times. Any other MPW shell special characters which appear in the template must be similarly quoted. MakeCPath only literalizes asterisks and tabs; add your own as needed. Note that the curly-ds are being added directly to the template window. When we are finished with the template, we must close this window with the -n option so the changes wont be written to the file on the disk.
Once the template is prepared, loop to process it one line at a time. MacTutor masochists who read the code listings first may be mystified at the command
Echo Echo Catenate § >> {tmpfile2}
There may be a prettier way to do it, but this does work. This is an echo command which writes another echo command
as a line in the scratch window. The name of the scratch window is in the variable {tmpfile2}. The Catenate§ inside grave symbols is replaced in the scratch file by the line currently selected in the template file. This catenate command consumes one level of literalization in the template, so * in the template becomes * in the scratch file. Shell variables such as {MacCLib} are not expanded in the scratch file.
When the loop finishes, the scratch file contains one echo command corresponding to each line of the original template file. Execute the commands in the scratch file using
Execute {tmpfile2} >> {outfile}
and the variable outfile contains the filename MacC.Path which will become the input to the Path Manager application. The echo commands in the scratch file consume one more level of literal quotes (so * becomes * in the output file) and they also expand any shell variables into their values.
MakeCPath contains additional commands which facilitate convenient behavior after the application runs; these features will be considered in the next section. Finally the MakeCPath script launches the Path Manager, passing the file MacC.Path in the Finder Information structure.
Now we run into some of the difficulties associated with applications in shell scripts. Although I passed the input with the Finder Info, the Path Manager (ver. 5 at least) doesnt notice it. I still have to manually select a menu option, and then locate the text file using a StandardGetFile dialog. The best I can do is make this process as painless as possible. In the MakeCPath script, just prior to launching the application, make the System Folder into the default directory using the Directory command. Now when the Path Manager throws up the StandardFile dialog, it will initially be showing the files in the System Folder. Since MakeCPath wrote the file MacC.Path into the System Folder, we wont have to search through the directory hierarchy to find it. When the Path Manager generates the PATH resources, I will also have to manually specify the output file in a StandardPutFile dialog, and then manually select Quit from the menus. Weve saved ourselves some work in editing the input file, but if the application just used the Finder Information all the manual interaction could be eliminated.
Altered states
Now lets consider the things which happen in the shell environment when we launch an application. In the absence of Switcher-like trickery, we must terminate any current application prior to launching another one. The MPW Shell is itself an application and so must terminate before launching another and must be relaunched when the external application quits. In the parlance of the MPW Reference, MPW is suspended, the external application runs, and then MPW is resumed. The processes necessary to suspend and resume the MPW shell are performed by shell scripts provided with the MPW system, named (appropriately) Suspend and Resume.
The Suspend script saves all currently defined shell variables, exported variables, command aliases, menu items, the current working directory, and any open windows. Then when the MPW shell is relaunched, the Resume script uses this saved information to return the shell to nearly the same condition as when it was suspended. There is one important difference between the suspended state and the resumed state, which is tersely documented in the MPW Reference. An application may be launched from a nested command script, such as MakeCPath from the preceeding example, but the shell always resumes at the outermost command level. The shell implicitly exits any nested command scripts when suspended.
This is the variable scope anomaly I to which I earlier alluded. The Suspend script saves the names and values of variables at the current level of scope, but the Resume script sets these variables again at the outermost level (see Fig. 4). WARNING: I use this as a feature, but future versions of the shell may consider this a bug; some of my shell scripts will require minor modification if future releases change this behavior (I am using MPW version 1.0 for the examples of this article). [Current version is 2.0b1. -Ed]
If you launch applications within scripts, as I do, some defensive scripting practices are essential. Any outer-level shell variables you want around after Resume must be exported from the outer level and not modified by nested shells. Any inner-level variables you dont want around after Resume must be Unexported and Unset in the script file prior to the command which starts the application. Remember that you only need to worry about this when you are using shell scripts like MakeCPath which launch applications.
Often, launching an application is not the final step in the process, but the shell script which launches the application is not restarted from that point. I get around this by setting up commands to be executed by the Resume process. I put these commands into a file and save the name of this file in a shell variable called {PostApp}. I have modified the Resume script file to include the commands:
Execute {PostApp}
Set PostApp NullFile
When the MPW shell resumes after an application, commands which I saved prior to suspending can be executed. Variable {PostApp} is reset to a default empty command file which is named NullFile. An alternate method that doesnt depend on the Suspend/Resume shell variable scope anomaly is to save the commands under a specific file name, say UserResume. Then the Resume script should contain
Execute UserResume
Echo > UserResume#Deletes contents
I prefer the first method (as long as it works) because the command file contents stay available for debugging when I am writing a new command script. Note that in either method the changes to file Resume are permanant, and we merely change variable {PostApp} or file UserResume to fit the needs of the moment.
I return to the MakeCPath script for an example of a useful Resume task. Recall that MakeCPath changed the current working directory to the System Folder just prior to launching the Path Manager. This means that the Suspend script will save this directory and Resume will restore the System Folder as the current working directory. This will probably not be the directory from which the user started the MakeCPath script, so when we Resume, we are not where the user expects. A more user-friendly thing to do is to save the original working directory and restore it at the end of the Resume process. The commands which set this up are found near the beginning of MakeCPath. Variable {PostApp} is given the name of the resume command file UserResume. The original directory is used in a command stored in file UserResume:
Directory OldDirectoryName
Now when MPW resumes, the user will be in the same place in the directory hierarchy as when MakeCPath was invoked. This technique of adding transient commands when resuming from applications has considerable utility and we will see more extensive examples presently.
MPW Make with Applications
The shell scripts developed in the preceding text, while they do provide instructive examples of technique, are only moderately useful. The directory structure and library file locations shouldnt change too often, and these shell scripts are unlikely to be needed more than a couple of times a year.
Building and maintaining programs, on the other hand, is more of a daily occupation. Within the MPW, these tasks center around the tool Make, which is an elegant system of rules to emit commands which depend upon the relative ages of files. If we apply what we learned in the previous examples to this problem, we are rewarded by a system of genuine utility -- an integration of MacC and TML Pascal systems which the developer accesses from entirely within the MPW environment.
Basics of Make
Make programs are widely available utilities found in the MPW, in a variety of Mac shareware and commercial offerings and in most Unix--based systems, among others. The traditional use of a Make utility is to keep programs up to date with a minimum of processing, compiling only when source files change and linking only when relocatable objects change. This behavior is controlled by a text file containing 1)dependency rules specifying which files are constructed from which predecessor files and 2)a list of commands which perform the operations necessary to update the target files. Since F. Alvianis April 87 MacTutor article elaborated some of the sketchy documentation on Make, I wont repeat that detail here. An important point in the adaptation of MPW Make for application--based development is that the full range of legal commands can be generated by a make script. This allows us to generate smart sequences of commands which do much more than just compile and link (and Rez).
The MakeCPath command described earlier provides the simplest possible illustration of Make which does not compile or link. The final output of MakeCPath is a resource file in the System Folder named Paths.Rsrc. This output depends, naturally enough, on the input -- the template file, C.Path.Template, and any shell variables , such as {MacCLib}, which are used in the template file. Dependencies in Make can only be specified with respect to files, not variables, so the values of {MacCLib} and related variables are initialized in the MPW script file Startup. Startup is a script supplied by the MPW system which is executed when the MPW shell is launched from the Finder. If I change the value of variable {MacCLib} by editing this script file, the dependency of Paths.Rsrc on the variable becomes equivalent to a dependency on file Startup. (I put these variables into Startup instead of UserStartup because I edit file Startup much less frequently).
The input to Make specifies these dependencies in a special notation:
Paths.Rsrc Startup C.Path.TemplateMakeCPath
The line containing the symbol is the dependency statement. Files listed to the left of the depend upon (or are made from) files listed to the right of the symbol. Dependent files (left side) need to be updated if they are older than any of the predecessor files (right side). Note that in an actual Make script, you might need to specify the appropriate directories as well; I have omitted this detail for the clarity of the example. The dependency statement may be followed by a list of commands which perform the necessary update. Commands are distinguished in the Make input because each line of a command list begins with whitespace (space or tab characters). In the example above, MakeCPath is the command which will update the target, Paths.Rsrc.
Example 4. The MixMake command
Here I develop a shell script and Make input which work together to build programs. To effectively use applications with shell scripts, we need an understanding of their behavior in this environment. I start this example, therefore, by cataloging what happens to each application when it is launched from the MPW shell with an appropriate input file specified in the Finder Information structure. MacC (ver. 5) does not use the Finder Info properly, and in fact gives an ID 02 bomb (or worse) when you try from the shell. The Consulair linker will process a link script, but when finished puts up a standard file dialog, and requires user interaction to quit to the shell. If the link fails, the Consulair linker will launch Edit, instead of the shell, if it can find it. Removing the Edit application completely from the disk is helpful in using the MixMake shell script. TML Pascal will compile one file from the Finder Info (even if several were listed). After compiling, it requires the user to click OK buttons, to select other files or to quit manually. The TML linker will use a link script passed in the Finder Info, and then runs the program it built and/or returns to the shell. Amazingly, the Microsoft FORTRAN compiler (ugh) has almost ideal performance in this environment; it compiles the file passed in the Finder Info and returns to the MPW shell, and only requires the user click an OK button if a compile fails.
So far, things are looking a bit dim, but utility applications can save the day. The Exec application, provided with the Consulair system, is a simple utility which runs Consulair applications in sequence according to an input text file. Like the Path Manager, when this is launched from the shell the user must select the input file manually, but it generally will return to the shell when done. Furthermore, the Consulair linker and compiler recognize that they have been launched by Exec instead of the shell, and they accept and process input files and return to the Exec without user intervention. The MPW method simply needs to use this launching indirection through Exec to perform an automated build. The TML applications unfortunately do not change their behavior for Exec. Many other free/cheap utilities (for example, Darin Adlers Sequencer) will run applications with input files in sequence and may be more appropriate for the non-Consulair applications.
The MixMake command, which builds programs with MacC and TML involves a significant amount of programming beyond the simple Make utility. Since Make will, however, form the heart of the MixMake command, lets consider first the nature of a MixMake makefile. When the dependency rules indicate that a source must be recompiled or a program relinked, a MixMake makefile generates a line of input for the Exec application instead of a command to run the compiler or linker directly.
A sample makefile for an application is given in the Listings. The default dependency rules, which are applied to any filename with the appropriate filename extension, effectively handle all the compile steps. These default dependency rules may require directory dependency rules as well to operate correctly, but this will tend to vary with each build target. The default compiling rules are:
.Rel .C
Delete -i {TargDir}{Default}.Rel
Echo {DepDir}{Default}.C >>{Cfile}
Set RunMacC 1
.Rel .Asm
Delete -i {TargDir}{Default}.Rel
Echo {DepDir}{Default}.Asm >>{Cfile}
Set RunMacC 1
.Rel .Pas
Delete -i {TargDir}{Default}.Rel
Echo Pascal {DepDir}{Default}.Pas Exec
>>{Efile}
Set RunTPas 1
.Rsrc .R
Delete -i {TargDir}{Default}.Rsrc
Rez {RezOpt} -o {TargDir}{Default}.Rsrc
{DepDir}{Default}.R
Variable {Efile} is the file which is being prepared for Exec input. Variable {Cfile} is a filename with extension .Files which contains a list of sources to be compiled by the MacC application. Setting the variable {RunMacC} tells the enclosing MixMake shell command to add a single command to the Exec input file which will launch the MacC compiler with the .Files file. If resources are to be compiled with Rez, the commands are generated by Make and executed directly by the MixMake script. This arrangement assumes that non-CODE resources are compiled by Rez but the application is assembled from CODE and these resources by a subsequent link step. When a Pascal source is to be compiled, the makefile appends an appropriate command to the Exec input file. Since the Pascal compiler does not interface well with Exec, the user must select (and know in the first place) all the sources to be compiled in Standard File dialogs. When the user selects Quit from Pascal, the Exec processing is terminated and MPW resumes. To complete any MacC compiling and linking, the user would have to run the MixMake command a second time.
A much nicer method uses Adlers Sequencer and could be adapted most to any application-running utility; a Pascal compiler build rule is implemented as Rez input which is used to produce a Sequencer which will build the desired Pascal files. The user does not need to know which Pascal sources are to be rebuilt; the user just selects Quit in Pascals file menu after each compile, and the next out-of-date file is compiled automatically. The final operation by the Sequencer after all Pascal files are compiled is to launch the Exec to handle C compiling and Linking. I have included both versions of MixMake with the source code, but will not describe this one further in the text. The extra effort to use the Sequencer (or similar utility) has significant benefit. The Suspend/Resume scripts take a noticeable time to execute, so we want to do as much work as possible outside the shell for each Suspend/Resume cycle.
The sample Makefile.Template in the listings illustrates the appropriate specifications needed to build a particular file. It adds a directory dependency rule before the default compiler rules. A dependency rule for the link step must explicitly list the link script (.Link file), relocatable object files (.Rel files) and compiled resources (.Rsrc files) which are needed. The command generated by this dependency rule is again an appropriate line of Exec input. Particular care must be taken to prevent building a program with some up-to-date object files and some which are not up-to-date because of compiler errors. The delete command in the compiler default rules described above is executed by the MixMake script and prevents a successful link until the object is successfully rebuilt by the compiler. The MacC compiler rules dont need to delete obsolete objects because the Exec-MacC system allows a time saving alternative. Each line of Exec input consists of four fields: 1)the application to launch, 2)the input file for the application to process, 3)an application to run if the application in field 1 runs successfully (normally Exec to continue processing), and 4)an application to run if the application in field 1 fails. The line of Exec input which MixMake uses is
C input.Files Exec Done
The application Done is a trivial program containing only an ExitToShell command. If any of the files to be compiled or assembled by MacC have errors, the Exec processing terminates before any attempt to link, and the Done program Resumes the MPW shell instead.
It is important not to list the MPW Shell directly as an application in the Exec input. The MPW shell could be launchedby Exec, but then selecting Quit from the MPW File menu resumesthe MPW shell instead of starting the Finder. This is quite confusing, so we dont do it.
There are few restrictions on MixMake. The MixMake command shell takes user input arguments exactly like Make, and uses a makefile with the appropriate default rules described above to generate an input file (set up to run in the appropriate order) for the Exec application. The Make tools options -d, -e, -f can be used in arguments for MixMake. Make options -p, -r, -s, -t, -u should not be passed to MixMake; they may not behave as expected. Options -r, -s and -u can be used by invoking Make instead of MixMake and using the MixMake input dependency file. The key to adapting the sample makescript to a particular purpose is in the list of predecessors (usually for a link rule) and directory dependency rules. It is important to follow the advice in the MPW Reference and assure that directories are specified in default directory dependency rules in precisely the same form (full or partial pathnames) as the corresponding explicit predecessor files. These should also be consistent with the search paths of the applications. The version of MixMake which uses the Sequencer requires that full paths are specified for all directories.
In the simplest implementation, MixMake would simply launch the Exec or another application runner. A more sophisticated MixMake, however, provides Resume tasks for nice error handling when the build steps dont succeed. Ill finish up the article with a description of commands which help with analyzing diagnostics.
OOPS! What about those errors?
All of the Consulair and TML applications write diagnostic files when compiling or linking fails. A convenient feature to add to the mixed system is a facility to automatically open a bad source listing its corresponding diagnostic file simultaneously. Shell script CompErr is just such a diagnostic viewer, running sequentially through all bad .C and .Asm sources, then all bad .Pas sources, and finally any failed linker scripts. The information needed by CompErr to tell which are the bad sources from the most recent build attempt consists of several files and shell variables. These files and variables are provided by a Resume script which is installed by the MixMake command. A copy of the commands MixMake generates as a Resume task is kept in file MixMakeResume. These Resume tasks and the CompErr command are more limited than MixMake itself, and generally work only when all the necessary sources and diagnostics are located in a single directory and if that directory is the current working directory when the MixMake command is invoked. This restriction has not been onerous for me, but if necessary more elaborate Resume and CompErr scripts could circumvent the limitation.
The MixMake Resume commands write files which contain lists of paired source and diagnostic files. The Resume commands also install an Open Diagnostics item in the MPW shell File menu that runs the CompErr command. The files summarizing the diagnostic file names are scanned sequentially by CompErr; each time the menu item is selected, the next pair of source and diagnostic files is opened for the user to view and edit. When all the diagnostic files have been opened, the menu item deletes itself.
The MacC compiler creates a file with the basename of its .Files input and the name extension .Fer, in which it writes a list of files which failed to compile and their corresponding diagnostic files. For Pascal compiler and linker errors, the Resume task needs to explicitly search the directory to create such a list. For example, in a directory containing a Pascal source with extension .Pas, the existence of the same name with extension .Per suggests a failed compilation. Existence of the diagnostic file is not sufficient evidence however; the source might have been corrected since the diagnostic file was created. The Resume task only lists source files which are out-of-date with respect to their corresponding diagnostic files. The Make tool, a real workhorse, assures this. Here are dependency rules for finding bad sources from diagnostic files:
.Pas .Perr
Echo {DepDir}{Default}.Pas
{DepDir}{Default}.Per >>TMLP.Fer
.Link .Lerr
Echo {DepDir}{Default}.Link
{DepDir}{Default}.Lerr >>TMLP.Fer
All the Pascal diagnostics are listed in a file named TMLP.Fer and all the link diagnostics in a file named Link.Fer. The dependency rules above are in a makefile permanantly stored (never accessed by the user). This has the advantage of allowing a general command to be built around it, but the disadvantage that it will only work within the current directory.
CompErr uses these files (from the most recent MixMake build only), opening one source/diagnostic pair for each invocation. CompErr should always be accessed through the Open Diagnostics menu item (Dial Cmd-E for Errors) because the menu procedure moves to the directory where the build occurred, opens the files, then returns the user to the current directory. Since CompErr works sequentially through the .Fer files, we need to save the current position in the files after each invocation. Counting line numbers might work, but I have chosen to use the method of saving the current window selection with the file.
In MPW, files and windows into those files are distinct, although intimately related. When we save a window, the selection is saved too. After the window is closed, the saved selection reappears when the window is opened. The Save command is pretty smart, and only writes changes to the disk when the text has changed, not when only the selection changes. The CompErr script forces saving new selections by writing innocuous white space into the .Fer file after each change of selection. The marker for where CompErr is in the file is therefore saved elegantly in the file itself.
OK, lets finish up with an ugly scenario. It is Friday afternoon, you are about to leave on vacation and a lengthy build has just resulted in 30 or 40 erroneous source files. You dont particularly want to re-Make the whole shebang just to prime CompErr when you return. NO PROBLEM! If we can have Suspend/Resume tasks, we can also have Quit/Startup tasks.
Two scripts delivered with the MPW system, appropriately enough named Quit and Startup, handle special tasks when the user elects to terminate the MPW shell by quitting to the Finder and when the shell is launched from the Finder. I have added routines to Quit and Startup which allow me to preserve any state variables I want between invocations of the shell. In the Quit script, I execute a command file named QuitPermVars. In QuitPermVars are commands which save two exported state variables required by CompErr: {MakeErr} and {CompErrDir}. QuitPermVars writes Set commands to reproduce these two variables, corresponding Export commands, and an AddMenu command to install the Open Diagnostics item. These commands are saved in a file named StartPermVars. Then the Startup script executes StartPermVars when the shell is launched from the Finder. So you can leave your problems behind, and when you come back with clear and refreshed mind, CompErr will dutifully pitch them out to be solved.
Conclusions
Although it may look a bit ungainly because of the number of small command files, the system I have presented here as an example is quite workable and I actually use it every day. Programming the shell is very challenging compared to other programming because of the global nature of many of the manipulated data and the resulting high data connectivity of independent modules. But the rewards of mastering shell programming can be great. Before I attempted this project, in a fit of frustration, I had presumed it would be impossible to integrate these diverse applications with the shell. The flexibility of the MPW should not be underestimated however, and this collection of command scripts provides me with the same level of performance from the development applications as when they stand alone plus an improved text handling and resource handling facility.
I should add that the systems integration designed into the Consulair C applications contributes a greatly to the effectiveness of my scheme. The behavior of the various applications (Consulairs, TMLs and others Ive tried) could have been better for use from the shell however. A few simple considerations can insure smooth integration of applications and MPW shell.
Making applications MPW friendly
First and foremost, applications which may be launched from the MPW shell (or other non-Finder shells, like A-UX?) should handle the Finder Information structure. No assumptions should be based on Finder behavior (for example, assuming that documents and application must reside in the same folder when they have different file creator words).
Secondly, applications with strongly modal behavior should probably have the ability to read an input file to set those modes and perform its action. An application which can do its job with just one or two mouse clicks by the user in menus or dialog boxes has strongly modal behavior, and compilers are an excellent example. There is unfortunately no way to establish a standard, but the following suggestion should provide a sufficiently convenient interface with the MPW shell. If you have a strongly modal application, consider using a TEXT file, perhaps with a distinctive name or filename extension, as an optional way to set the modes and actions to be performed. And finally, when an application can be controlled modally in this way, provide an option to Quit or Transfer without user intervention. Yes, I know this flies in the face of the User Interface Guidelines, but the example of this article shows that the User Interface is not always the Best Interface.
A challenge to future MacTutor authors
The MPW system provides a Help tool which provides on-line documentation for shell commands. In the listings for this article I have included a script named Man (for Manual) which extends the help procedure to search both the default help file and a second help file named Local.Help. The Man procedure also uses the Canonical spelling tool to convert your standard command aliases to the command names known to the system. Just put an alias and corresponding command on a line in file Alias.Dict.
With the Man command, I have provided a Local.help file with entries for the command scripts developed in this article. Hopefully the command scripts and tools in future MacTutor articles can include relevant help file entries with their source code contributions.
Listings
###file ReadMe
###ReadMe - instructions for installing
###MixMake and related commands in your
###MPW system.
###W.G. Powell 1987
###Because parts of the MPW system will usually
###be scattered throughout a number of HFS
###folders (directories) the following guide
###will be helpful in adding the sources
###provided here. You may need to customize file
###paths in some of the commands to match your
###directory arrangement.
###Into the MPW directory (the folder containing
###the MPW Shell application itself, and referred
###to by shell variable {MPW}, place the files
###contained in the source disk folder for MPW folder
###Local.Help
###QuitPermVars
###StartPermVars
###:MakeSupport: the folder and its contents:
###MakePErrs.mk
###Mixmake.Template.1
###Mixmake.Template.2
###MixMakeResume
###Paths.mk
###Into the Tools directory (or any directory listed
###in the MPW command search path (Shell variable
###{Commands} ), place the files contained
###in the source disk folder for Tools folder
###Alias.Dict
###CompErr
###MakeCPath
###MakePPath
###Man
###MixMake ver.1
###MixMake ver.2
###NullFile
###UpdatePaths
###If you use Darin Adlers Sequencer with MixMake,
###rename version 2 MixMake.
###If you use Consulair/MDS Exec only with MixMake,
###rename version 1 MixMake.
###Use the appropriate template in directory
###{MPW}MakeSupport: as a guide in preparing
###your own MixMake makefiles.
###Into the Applications directory (or any directory
###listed in the MPW command search path
###(Shell variable {Commands} ), place the applications
###you will be using with the system.
###Consulair applications:
###C
###Exec
###Link
###Path Manager
###TML applications:
###Pascal
###TML Link
###Other applications
###Sequencer #if used
###Done # supplied with this disk
###Note that if both TML and Consulair
###Linkers are present, one must be
###renamed.
###Also, since TML Desk Acc. Set Paths
###is rarely used, I install it in the
###MPW Shell application, instead of a
###precious System File slot.
###The following changes should be made to the
###scripts supplied with the MPW shell
################################################
### Add these commands to file Startup
###Modify to match your directory hierarchy.
# {PostApp} - name of command to execute when
# MPW is RESUMED, after running a Finder application (WGP)
Set PostApp NullFile
Export PostApp
# {MacCLib}is directory for MacC includes and Libs
Set MacCLib {Libraries}MacC:
Export MacCLib
# {TMLPasLib} is directory for TML Pascal interfaces, libs
Set TMLPasLib {Libraries}TMLPas:Pascal System TML:
Export TMLPasLib
#{TMLPasInc} - Directories to search for
#TML Pascal interface files
Set TMLPasInc {Libraries}TMLPas:
Export TMLPasInc
##################################################
###Add these commands to file UserStartup
###Modify to match your directory hierarchy.
# Recover values of saved state variables
Execute {MPW}StartPermVars
##################################################
###Add these commands to file Quit
###Modify to match your directory hierarchy.
###Values of variables below are saved
###between invocations of the shell
Execute {MPW}QuitPermVars
##################################################
###Add these commands to the end of file Resume
###Modify to match your directory hierarchy.
# System for nice return from applications
#The shell variable PostApp contains a command to
#Execute when resuming. This variable is reset to a
#default so unexpected results are avoided when another
# application is run.
Execute {PostApp}
Set PostApp NullFile
##################################################
###File MakePPath
# Try using Rez to write path info to the TML path file
# in the system folder. Basically, just need to expand the
# TMLPasLib and TMLPasInc vars and write some Rez input
# with it.
#W.G. Powell 1987for MacTutor
# Nonzero command status doesnt mean errors,
# so dont terminate
Set __oldExit {Exit}
Set Exit 0
# Open temporary windows
Open -n -t Tmpfil
Open -n -t TMLPathOut
# Write Rez input using shells TML path variables
Echo #include Types.r>TMLPathOut
# Create STR resource for single path in {TMLPasLib}
echo resource STR (128) { >> TMLPathOut
echo {TMLPasLib}>> TMLPathOut
echo };>> TMLPathOut
# Put list of paths from {TMLPasInc} into a window
echo {TMLPasInc} > Tmpfil
# Break comma-separated list into individual paths
Find Tmpfil
Set IDno 129
Loop
If ({IDno} < 133)
Find §:/,/ Tmpfil
If ({Status} == 0)
echo resource STR ({IDno}) {>>TMLPathOut
echo `Catenate Tmpfil.§`>>TMLPathOut
echo };>>TMLPathOut
Set IDno Evaluate ({IDno} + 1)
Find §:§!1 Tmpfil
Else
Find §: Tmpfil
If ({Status} == 0)
echo resource STR ({IDno}) {>>TMLPathOut
echo `Catenate Tmpfil.§`>>TMLPathOut
echo };>>TMLPathOut
Set IDno Evaluate ({IDno} + 1)
Loop
If ({IDno} < 133)
Echo resource STR ({IDno}) {};
>>TMLPathOut
Set IDno Evaluate ( {IDno} + 1)
Else
Break
End
End
Else
Loop
If ({IDno} < 133)
Echo resource STR ({IDno}) {};
>>TMLPathOut
Set IDno Evaluate ( {IDno} + 1)
Else
Break
End
End
End
Break
End
Else
Break
End
End
# Use Rez to write the Paths file.
# Close temporary windows.
Close -n Tmpfil
Rez -o {SystemFolder}Paths File -t ZSYS -c MACS
TMLPathOut
Close -n TMLPathOut
Set Exit {__oldExit}
Unset __oldExit
Exit 0
############################################
###File MakeCPath
###MakeCPath - MPW Shell Script
###Set up input for Consulair Path Manager
### using appropriate MPW shell variables.
### William G. Powell 1987for MacTutor
#Save current working directory
#and return to it after Path Manager quits.
Set PostApp {MPW}UserResume
Echo Directory Directory> {MPW}UserResume
Echo Delete -i MakePtOut>> {MPW}UserResume
Echo Unset outfile >> {MPW}UserResume
#Specify the input template file
Set infile {MacCLib}C.Path.Template
#Place the output text file (input for Path Manager)
#into the system folder where easily found
Set outfile {SystemFolder}MacC.Path
#Assign temporary file to window and clear it.
Set tmpfile2 {MPW}tmpfile2
Open -t -n {tmpfile2}
Clear : {tmpfile2}
#Open the template window
Set _oldExit {Exit}
Set Exit 0
Open -t {infile} Dev:Null
If {Status} != 0
Alert Cannot Find/Open the input template file.n
Check shell variable MacCLib, currentlyn{MacCLib}
Close -n {tmpfile2}
Set PostApp NullFile # Reset default Resume tasks
Exit 2
End
Set Exit {_oldExit}
Unset _oldExit
#Add necessary quote characters to the template file.
Find
Replace -c / / # tab characters
Find
Replace -c /*/ *
#Loop to copy each line of input,
#expanding variables where necessary.
Set loopend count -l {infile}
Set loopcnt 1
Loop
If {loopcnt} > {loopend}
break
End
Find {loopcnt} # Select next line of input
# Write command into temporary window which will
# expand shell variable paths when writing to {outfile}
Echo Echo Catenate §>> {tmpfile2}
Set loopcnt Evaluate ( {loopcnt} + 1 ) # increment
End
# Dont terminate script file on errors below - the possible
# errors are not important enough!
Set _oldExit {Exit}
Set Exit 0
# Open, clear, and write the output file
Open -t -n {outfile}
Clear : {outfile}
Execute {tmpfile2} >> {outfile}
# Close all working windows, saving only the output.
#Close -n {tmpfile2}
Close -n {infile}
Close -y {outfile}
# Remove all unneeded variables.
Unset infile
Unset tmpfile2
Unset loopend
Unset loopcnt
# Change exit on error flag back to original value
Set Exit {_oldExit}
Unset _oldExit
# Change default directory to System Folder
Directory {SystemFolder}
# Execute the Path Manager, passing our new file
# in the Finder Information structure.
Path Manager {outfile}
Exit 0 # Control should never reach this statement
#########################################
###File Paths.mk
# Makefile to ensure that Consulair and TML search paths are
# up to date with appropriate shell variables
#W. Powell1987 for MacTutor
#Variables containing search paths for library files and
#include files are defined in the Startup script so we
#want to rebuild when the Startup file is changed.
### TML Pascal Paths File
{SystemFolder}Paths File {MPW}Startup
MakePPath
### Consulair MacC Path.Rsrc File
{SystemFolder}Paths.Rsrc {MPW}Startup
{MacCLib}C.Path.Template
MakeCPath
############################################
###File MixMake ver.1
###MixMake version 1.
###Uses only Consulair Exec application runner
### Script file to build programs with
### MacC or TML systems
###W.G. Powell 1987for MacTutor
### Initialize variables
#Exec and MacC input and scratch files
Set Efile Exec.Job
Set Cfile MacC.Files
Set Lfile Link.Tmp
Delete -i {Efile} {Cfile} {Lfile}
#Application run flags
Set RunMacC 0
Set RunLink 0 # Consulair
Set RunTMLLink 0
#Makes working directory
Set MakeWD `Directory`
Export MakeWD
### Run the Make tool
make {Parameters} > MakeOutApps Dev:StdOut
### Construct the input for the Exec application
Execute MakeOutApps
Delete MakeOutApps
If ({RunMacC} == 1)
Echo C{Cfile} ExecDone>>{Efile}
End
If ({RunLink} == 1 || {RunTMLLink} == 1)
Catenate {Lfile} >>{Efile}
Delete -i {Lfile}
End
Unset RunMacC
Unset RunLink
Unset RunTMLLink
Unset Lfile
Unset Cfile
Unset MakeWD
### Set up the Resume tasks for after the applications
### Primarily for error handling
Set PostApp {MPW}UserResume
Echo Unset Efile >{MPW}UserResume
Catenate {MPW}MakeSupport:MixMakeResume >>{MPW}UserResume
### If Diagnostic menu item is still around from
###an earlier build cycle, delete it.
Set __oldExit {Exit}
Set Exit 0
DeleteMenu File Open Diagnostics Dev:Null
Set Exit {__oldExit}
Unset __oldExit
### Run applications using Consulairs Exec
Exec {Efile}
########################################
###File MixMake ver.2
###MixMake version 2.
#### Uses Sequencer application and Consulair Exec
### Script file to build programs with
### MacC or TML systems
###W.G. Powell 1987for MacTutor
### Initialize variables
#Exec and MacC input and scratch files
Set Efile MixMake.Job
Set Cfile MacC.Files
Set Lfile CLink.Tmp
SetTfile TLink.Tmp
Set Seqfile Sequencer.R
Delete -i {Efile} {Cfile} {Lfile} {Tfile}
Echo #include Types.r>{Seqfile}
Echo include {MPW}Applications:Sequencer CODE ;
>>{Seqfile}
Echo resource STR# (128,MixMake Compile commands) {>>{Seqfile}
Echo -n { >>{Seqfile}
#Application run flags
Set RunMacC 0
Set RunLink 0 # Consulair
Set RunTMLLink 0
#Makes working directory
Set MakeWD `Directory`
Export MakeWD
### Run the Make tool
make {Parameters} > MakeOutApps Dev:StdOut
### Construct the input for the Exec application
Execute MakeOutApps
Delete MakeOutApps
If ({RunMacC} == 1)
Echo C{Cfile} ExecDone>>{Efile}
End
If ({RunLink} == 1)
Catenate {Lfile} >>{Efile}
Delete -i {Lfile}
End
If ({RunTMLLink} == 1)
Catenate {Tfile} >>{Seqfile}
Delete -i {Tfile}
End
If ({RunMacC} == 1 || {RunLink} == 1)
Echo {MakeWD}{Efile};>>{Seqfile}
Echo {MPW}Applications:Exec;>>{Seqfile}
End
Open -t {Seqfile}
Find {Seqfile}
Clear \;\ {Seqfile}
Echo n}n};>>{Seqfile}
Close -y {Seqfile}
Rez -o {MPW}Applications:PasRunner {Seqfile}
If {Status} == 0
Delete -i {Seqfile}
End
Unset RunMacC
Unset RunLink
Unset RunTMLLink
Unset Lfile
Unset Cfile
Unset Tfile
Unset MakeWD
Unset Seqfile
### Set up the Resume tasks for after the applications
### Primarily for error handling
Set PostApp {MPW}UserResume
Echo Unset Efile >{MPW}UserResume
Catenate {MPW}MakeSupport:MixMakeResume >>{MPW}UserResume
### If Diagnostic menu item is still around from
###an earlier build cycle, delete it.
Set __oldExit {Exit}
Set Exit 0
DeleteMenu File Open Diagnostics Dev:Null
Set Exit {__oldExit}
Unset __oldExit
### Run applications using the custom Sequencer
{MPW}Applications:PasRunner
########################################
###File MixMake.Template.1
###MixMake.Template.1
### Sample makefile for version 1 MixMake Command
###W.G. Powell 1987for MacTutor
### Make variable definitions
RezOpt = -t RSRC
### Directory dependency rules go here
# This one specifies that rels are
# in sister directory to that of the sources
::Rels: :
# Can add others of form
# {AltSrcDir}:Rels: {AltSrcDir}
### Default dependency rules for compilers
# Default C compilation rule
.Rel .c
Delete -i {TargDir}{Default}.Rel
Echo {DepDir}{Default}.c >> {Cfile}
Set RunMacC 1
# Default assembly rule
.Rel .Asm
Delete -i {TargDir}{Default}.Rel
echo {DepDir}{Default}.Asm >> {Cfile}
Set RunMacC 1
# Default Pascal compilation rule
.Rel .Pas
Delete -i {TargDir}{Default}.Rel
echo Pascal {DepDir}{Default}.Pas Exec Exec
>> {Efile}
# Default resource compilation rule
.Rsrc .R
Delete -i {TargDir}{Default}.Rsrc
Rez {RezOpt} -o {TargDir}{Default}.Rsrc {DepDir}{Default}.R
### Program-specific dependency rules go here
### For a program, should explicitly list the
### link script (.Link file), all objects (.Rel files),
###and all resource files (.RSRC) needed to build.
# Sample dependency rule for Consulair linker
# prog1 prog1.Link prog1.Rel sub1.rel prog.Rsrc
#Echo Link prog1.Link Exec Done>>{Lfile}
#Set RunLink 1
#
# Sample dependency rule for TML Linker
#prog2 prog2.Link prog2.rel lib.rel prog.RSRC
#Echo TML Link prog2.Link Exec Done>>{Lfile}
#Set RunTMLLink 1
################################################
###File MixMake.Template.2
###MixMake.Template.2
### Sample makefile for version 2 MixMake Command
###W.G. Powell 1987for MacTutor
### Make variable definitions
RezOpt = -t RSRC
### Directory dependency rules go here
# This one specifies that rels are
# in sister directory to that of the sources
::Rels: :
# Can add others of form
# {AltSrcDir}:Rels: {AltSrcDir}
### Default dependency rules for compilers
# Default C compilation rule
.Rel .c
Delete -i {TargDir}{Default}.rel
Echo {DepDir}{Default}.c >> {Cfile}
Set RunMacC 1
# Default assembly rule
.Rel .Asm
Delete -i {TargDir}{Default}.Rel
echo {DepDir}{Default}.Asm >> {Cfile}
Set RunMacC 1
# Default Pascal compilation rule
.Rel .Pas
Delete -i {TargDir}{Default}.Rel
Echo {DepDir}{Default}.Pas>>{Seqfile}
Echo {MPW}Applications:Pascal>>{Seqfile}
# Default resource compilation rule
.Rsrc .R
Delete -i {TargDir}{Default}.Rsrc
Rez {RezOpt} -o {TargDir}{Default}.Rsrc
{DepDir}{Default}.R
### Program-specific dependency rules go here
### For a program, should explicitly list the
### link script (.Link file), all objects (.Rel files),
###and all resource files (.RSRC) needed to build.
# Sample dependency rule for Consulair linker
# prog1 prog1.Link prog1.Rel sub1.rel prog.Rsrc
#Echo Link prog1.Link Exec Done>>{Lfile}
#Set RunLink 1
#
# Sample dependency rule for TML Linker
# prog2 prog2.Link prog2.rel lib.rel prog.RSRC
#Echo {full directory}prog2.Link;>>{Seqfile}
#Echo {MPW}Applications:TML Link;>>{Seqfile}
#Set RunTMLLink 1
############################################
###File Done.c
############################################
###File Done.c
/**************************************************/
/* Done.c */
/*Bogus Application for Exec to use to */
/*Return to MPW shell */
/*Written for Consulair MacC */
/**************************************************/
#Options E G H
main ()
{
void exit() ;
exit (0) ;
}
###############################################
### File MixMakeResume
# MakeResume -- a command file to be executed when
# resuming from applications such as MacC
#W. PowellMay 1987 for MacTutor
# Dont terminate command file on non-zero status
Set __oldExit {Exit}
Set Exit 0
Set CompErrDir `Directory`
Export CompErrDir
# Establish a menu item for error handling
DeleteMenu File Open Diagnostics Dev:Null
AddMenu File Open Diagnostics /E Set Exit 0;
Export MakeErr ; Set __origDir `Directory` ;
Directory {CompErrDir} ;
CompErr ;
Set MakeErr {Status} ; Directory {__origDir} ;
Unset __origDir
# {MakeErr} is a flag used by CompErr, the command
#file which opens compiler error files
Set MakeErr 0
Export MakeErr
# Create summary files listing all files containing
# current compiler or linker diagnostics
echo > TMLP.Fer
echo > Link.Fer
Files .Pas >Dev:Null Dev:Null
If ( {Status} == 0 )
For __fil In .Pas
If ({__fil} != )
Make {__fil} -f {MPW}MakeSupport:MakePErrs.mk
End
End > MakeOutApps Dev:Null
End
Files .Link >Dev:Null Dev:Null
If ( {Status} == 0 )
For __fil In .Link
If ({__fil} != )
Make {__fil} -f {MPW}MakeSupport:MakePErrs.mk
End
End >> MakeOutApps Dev:Null
End
Unset __fil
Files MakeOutApps >Dev:Null Dev:Null
If ( {Status} == 0 )
Execute MakeOutApps
rm MakeOutApps
End
# Move insertion point to front of each
#summary file window and save window
#with that selection
Begin
Open -t MACC.Fer
Find MACC.Fer
Replace § MACC.Fer
Save MACC.Fer
Close MACC.Fer
Open -t TMLP.Fer
Find TMLP.Fer
Replace § TMLP.Fer
Save TMLP.Fer
Close TMLP.Fer
Open -t Link.Fer
Find Link.Fer
Replace § Link.Fer
Save Link.Fer
Close Link.Fer
End Dev:Null
# Reset {Exit} variable to previous value
Set Exit {__oldExit}
Unset __oldExit
#################################################
###File MakePErrs.mk
# Make file to create error summary files from
# build cycles with TML Pascal and Consulair and TML Linkers
# This makefile is used by MixMake commands Resume tasks
# (see file MixMakeResume)
#W. Powell1987 for MacTutor
#These dependency rules ensure we get only
#source files which
#have NOT been changed since the error files
#were written.
.Pas .PErr # for TML Pascal
echo {DepDir}{Default}.Pas{DepDir}{Default}.PErr
>> TMLP.Fer
.Link .LErr # for Consulair and TML Link
echo {DepDir}{Default}.Link {DepDir}{Default}.LErr
>>Link.Fer
#################################################
###File CompErr
### Shell script to find and open error files
### generated by MixMake
### command, opening a different files in sequence
### on subsequent
### calls. .Fer files should have been created by MixMakes
### Resume tasks. This script MUST be run by the menu item
###provided by the Resume task to work properly.
### {Status} returned by this command on exit is the new
###value for global shell variable {MakeErr}.
###W.G. Powell 1987for MacTutor
# Some commands intentionally return nonzero status, so
# dont terminate this command file
Set _oldExit {Exit}
Set Exit 0
###Part 1
### Process MacC error files
Files MACC.Fer >Dev:Null Dev:Null # Dont need output
If ({Status} == 0 && {MakeErr} == 0 )
# File MACC.Fer exists AND still processing Mac C errors.
Open -t MACC.Fer
Find /Errors in File: /:/ (/ MACC.Fer
# Open bad source
If ({Status} == 0)
# find directory of source file
Set __tmpSrcDir `Catenate MACC.Fer.§`
Open -t -n MACC.tmp.tmp Dev:Null
Echo -n {__tmpSrcDir}>MACC.tmp.tmp Dev:Null
Find MACC.tmp.tmp Dev:Null
Clear \:\: MACC.tmp.tmp Dev:Null
Find : MACC.tmp.tmp Dev:Null
Set __tmpSrcDir `Catenate MACC.tmp.tmp.§` Dev:Null
Close -n MACC.tmp.tmp
# open the file
Open `Catenate MACC.Fer.§`
If ({Status} 0)
Alert Cannot open source file `Catenate MACC.Fer.§`
End
Find /(See /:/)/ MACC.Fer # Open diagnostic file
If ({Status} == 0)
Open `Catenate MACC.Fer.§` Dev:Null
If ({Status} 0)
# try again in source directory
Open {__tmpSrcDir}`Catenate MACC.Fer.§`
If ({Status} 0)
Alert Cannot open error file `Catenate MACC.Fer.§`
End
Unset __tmpSrcDir
End
Else
Alert Dont know corresponding error file
End
Else # Open file MACC.Fer after processing all
# the files it named
Open MACC.Fer
If ({Status} 0)
Alert Cannot open error file MACC.Fer
End
Alert Finished last Consulair C Error File.nTML Pascal Next
# Save current selection
Find § MACC.Fer Dev:Null
Replace § MACC.Fer Dev:Null
Save MACC.Fer Dev:Null
Close -y MACC.Fer Dev:Null
Exit Evaluate {MakeErr} + 1
End
# Save current selection
Find § MACC.Fer Dev:Null
Replace § MACC.Fer Dev:Null
Save MACC.Fer Dev:Null
Close -y MACC.Fer Dev:Null
Exit {MakeErr}
Else If ({MakeErr} == 0)
Set MakeErr Evaluate {MakeErr} + 1
# Go on to Pascal files
End
###Part 2
### Process TML Pascal Error files
Files TMLP.Fer >>Dev:Null Dev:Null
If ({Status} == 0 && {MakeErr} == 1)
Open -t TMLP.Fer
Find //:/ / TMLP.Fer
If ({Status} == 0)
Open `Catenate TMLP.Fer.§` # Open bad source
If ({Status} != 0)
Alert Cannot open source file `Catenate TMLP.Fer`
End
Find //:/ / TMLP.Fer
If ({Status} == 0)
Open `Catenate TMLP.Fer.§` # Open diagnostic file
If ({Status} != 0)
Alert Cannot open error file `Catenate TMLP.Fer.§`
End
Else
Alert No corresponding error file
End
Else
Alert Finished last TML Pascal Error File.nLink diagns next.
# Save current selection
Find § TMLP.Fer Dev:Null
Replace § TMLP.Fer Dev:Null
Save TMLP.Fer Dev:Null
Close -y TMLP.Fer Dev:Null
Exit Evaluate {MakeErr} + 1
End
# Save current selection
Find § TMLP.Fer Dev:Null
Replace § TMLP.Fer Dev:Null
Save TMLP.Fer Dev:Null
Close -y TMLP.Fer Dev:Null
Exit {MakeErr}
Else If ({MakeErr} == 1)
Set MakeErr Evaluate {MakeErr} + 1
# Go on to linker errors
End
###Part 3
### Process Linker Error files
Files Link.Fer >>Dev:Null Dev:Null
If ({Status} == 0 && {MakeErr} == 2)
Open -t Link.Fer
Find //:/ / Link.Fer
If ({Status} == 0)
Open `Catenate Link.Fer.§` # Open bad source
If ({Status} != 0)
Alert Cannot open link script `Catenate TMLP.Fer`
End
Find //:/ / Link.Fer
If ({Status} == 0)
Open `Catenate Link.Fer.§` # Open diagn files
If ({Status} != 0)
Alert Cannot open error file `Catenate TMLP.Fer.§`
End
End
Else
Alert All error files have now been processed!
# Save current selection
Find § Link.Fer Dev:Null
Replace § Link.Fer Dev:Null
Save Link.Fer Dev:Null
Close -y Link.Fer Dev:Null
DeleteMenu File Open Diagnostics
Exit Evaluate {MakeErr} + 1
End
Exit {MakeErr}
Else If ({MakeErr} == 2)
Alert All error files have now been processed!
DeleteMenu File Open Diagnostics
Exit Evaluate {MakeErr} + 1
End
Exit {MakeErr}
#############################################
###File QuitPermVars
#### This shell script is run by the Quit script
#### to save any shell variables which must be
#### preserved through a Quit
#### W.G. Powell 1987 for MacTutor
#Clear the file
Echo > {MPW}StartPermVars
# Save CompErr (Diagnostic viewer) state variables
Echo # State vars for CompErr >>{MPW}StartPermVars
Set MakeErr >> {MPW}StartPermVars
Echo Export MakeErr >> {MPW}StartPermVars
Set CompErrDir >>{MPW}StartPermVars
Echo Export CompErrDir >> {MPW}StartPermVars
If ({MakeErr} < 3)
# Still some compiler errors to process?
AddMenu File Open Diagnostics
>> {MPW}StartPermVars
End
################################################
###File StartPermVars
#initial default values for CompErr state variables
Set MakeErr 3
Export MakeErr
Set CompErrDir
Export CompErrDir
################################################
###File Man
###MPW command man for on-line help
###Written 1987 W.G. Powell
###for MacTutor
###Search more than one file sequentially for help text
Set Exit 0
Set Reval 0
### Get standard command names from alias dictionary
# need temporary file
Set _dmfl_ dumrxfx_
#Unalias the input arguments
Echo {Parameters} |
Canon {MPW}billTools:Alias.Dict > {_dmfl_}
#Set new input arguments
Set HelpList `Catenate {_dmfl_}`
Delete -n {_dmfl_}
###Look for each requested item individually
For HelpItem In {HelpList}
# Look in default help file first
Help {HelpItem} Dev:Null
Set Retval {Status}
If ({Retval} == 2 || {Retval} == 1)
# Look in local help file if not in default
Help -f {MPW}Local.Help {HelpItem} Dev:Null
Set Retval {Status}
End
If ({Retval} != 0)
# Keep a list of items NOT found for diagnostic
Set Reval {Retval}
Set NotFnd {NotFnd} {HelpItem}
End
End
###Send error message if items not found.
If ({Reval} != 0)
Echo ### Help: Cannot find items:>Dev:StdErr
Echo ###{NotFnd}>Dev:StdErr
End
Unset NotFnd
Unset _dmfl_
Unset HelpItem
Unset HelpList
Unset Retval
Exit {Reval}
################################################
###File Local.Help
Help files for locally developed commands
W.G. Powell 1987 for MacTutor
-
Local - MPW Local Custom Commands
Get info on specific command by entering
Man CommandName
CompErr # Open compiler diagnostics and erroneous
#sources
MacC # Consulair applications: compiler, linker, etc.
MakeCPath # Use shell variables to create Consulair paths
MakePPath # Use shell variables to make TML paths
Man# help utility including local help files
MixMake # MPW make with Mac C, TML systems
NullFile# A do nothing command file
TML# TML Pascal compiler and linker
UpdatePaths # Update TML Pascal, Mac C search paths
-
Man [(CommandName | Keyword) ]
Abbreviated on-line manual entries for commands. This
is an elaborate version of MPW help, which searches
several files for help info.
-
UpdatePaths
Create new search paths for Consulair and TML, but only
1) If {MPW}Startup (which contains path variables) changes
2) If {MacCLib}C.Path.Template (Path Manager input) changes
SEE ALSO: MakePPath, MakeCPath
-
MakePPath
Create new Paths File for TML Pascal in system folder.
Pathnames come from shell variables defined in Startup:
{TMLPasLib} contains one directory for libraries
{TMLPasInc} contains up to 4 directory pathnames
separated by commas.
User must select Set Paths DA
and click OK afterwards.
-
MakeCPath
Create new Paths.Rsrc for Consulair in system folder.
Also writes equivalent Path Manager text input file in
system folder as MacC.Path. Input text file uses
{MacCLib}C.Path.Template as main form, with additional
pathnames from shell variables defined in Startup file:
{MacCLib} contains one directory pathname for libraries.
C.Path.Template names several paths relative to
{MacCLib}.
-
MixMake [option ] target
Build programs using MacC and TML compilers
Options:
-d name[=value]# define variable name as value
# (override definitions in makefile)
-e # rebuild everything regardless of dates
-f makefile # read dependencies from makefile --
# (default MakeFile)
Other standard Make options are not supported.
The following options
are helpful using the standard Make tool with the
MixMake input dependency file
-r # write roots of dependency graph to output
-s # write structure of target dependencies to output
-t # touch dates of targets and prerequisities
-u # identify targets in makefile not reached in build
-v # write verbose explanations to diagnostics
SEE ALSO: Make, CompErr
-
CompErr
(ALWAYS access via item Open Diagnostics in File menu)
Opens MacC, TML Pascal, or Linker diagnostic
and source files. Generally only will work following a build
attempt using command MixMake.
Opens only one pair of files on each invocation
- must be called repeatly for each source.
-
NullFile
Contains no command text. Does nothing.
-
######################################
File Alias.Dict
File Target