dukelow@cod.UUCP (01/25/87)
MakeMake is a program written by Steve Tynor to create makefiles for Modula-2 programs. The following is a port of MakeMake to work in the Logitech Modula-2/86 envirnoment. See comments in MakeMake.MOD for differences and some observations on Modula-2 portability. In addition to Steve's work I used the CmdEnv module written by Gregory Elder (with a small modification to allow for expanded environment sizes) for getting the command line arguments and environment variables. The file MakeMake.MAK which I have included is a makefile produced by MakeMake. You may have to change drive and path names to make it work on your system. You will also have to compile CmdEnv separately since I have it in a separate directory on my system. Thanks to both Steve and Gregory for some very useful software. Bob Dukelow dukelow@nosc.arpa ====== MakeMake.MOD ========================================================== MODULE MakeMake; (* * MAKEMAKE. Create a MAKEFILE for a MODULA-2 program. * * Written by Steve Tynor, 30 September 1986. * UUCP : tynor@gitpyr * USNAIL: 2550 Akers Mill Rd. T-2, Atlanta GA. 30339 * * Permission is granted to distribute, copy and change this program as long * as this notice remains... *) (* * Modified for Logitech MODULA-2/86 by * Robert A. Dukelow, 24 January 1986 * 3467 Mt. Ariane Dr. * San Diego, CA 92111 * UUCP: dukelow@nosc * * Program is set up for my working environment which has the compiler and * linker on drive D:. Most people will probably have to change * CompilerPathname and LinkerPathname. * * Primary changes: * 1. Changed philosophy when .DEF/.MOD files not found in local directory. * Assume that we are *not* trying to maintain these files with this * makefile. Include corresponding .SYM/.LNK files only in dependancies * for other files and attempt to find the correct paths using the * M2LNK/M2SYM environment variables. * 2. Uses CmdEnv written by Gregory Elder to both get the command line * arguments and the environment variables since Logitech doesn't have * a CommandLine module. (note that I had to Modify CmdEnv to allow for * my expanded environment) * 3. The output file is now named after the main program module with * a .MAK extension instead of MAKEFILE to allow maintaining more than * one program in a directory. * 4. Creates an EXE file in addition to a LOD. * 5. Removed VAR's on parameters where I thought they were not necessary * (mostly to reduce my own confusions). * * Along the way I learned some things about Modula-2 portability: * 1. I discovered a couple of uninitialized variables in the original * code. Maybe the TDI Modula-2 initializes memory? * 2. Seems that TDI allows passing a string constant as the actual * parameter when parameter is declared as VAR? Don't know what it * would do if you tried to change the value. * 3. TDI has a Strings module *very* similar to Logitech's (but not * quite the same - sigh). * a. Strings.Compare => Strings.CompareStr (no big deal) * b. Logitech uses +1/0/-1 vs Strings.?/Equal/Less * (easy to figure out) * c. Strings.Pos has same name but different number of arguments * (I guessed right the first time :-) * d. Strings.Assign has the arguments reversed (ugh - debug time) * 4. ASCII.CR/LF/HT => ASCII.cr/lf/ht (no real problem) * 5. TDI Modula apparently doesn't require EXPORTs in the DEF files. * Overall - I sure would like to see more standardization. * * (see below for original authors comments) *) (* * MAKEMAKE. Create a MAKEFILE for a MODULA-2 program. * Usage: * MAKEMAKE "main-module-modulename" * * Even though module dependencies are explicit in MODULA-2, it can still * be quite a chore to figure them out and create a MAKEFILE. This program * reads the text file associated with the module name given on the command * line and recursively checks dependencies for all imported files. It then * creates a MAKEFILE suitable for use with the Unix MAKE utility. If a * file is not found and is not a recognized library module, then the * MAKEFILE will include a comment: * ## File NOT Found ## * on the right hand side of the dependency. * * * This program was written on the Atari ST using TDI Modula-2. * * BUGS: * the CommandLine module dies when a null command line is seen. * Unfortunately, I no longer have the source to the module, so I can't * fix it. This bug also seems to consider a command line consisting of * a single character as a reason for dieing. * * Suggestions for porting to another compiler: * * 1) module CommandLine is non-standard. It exports 2 procedures: * PROCEDURE GetArgument ( num : CARDINAL; * VAR filename : ARRAY OF CHAR); * PROCEDURE NumberOfArguments() : CARDINAL; * they do just what their names imply. (ie. like C's argv, argc) * * 2) the IMPLEMENTATION MODULE MakeLibraries will have to be edited to * reflect the standard library that your compiler knows about. List * any modules that you don't want included in your MAKEFILE. Library * modules are listed in the MAKEFILE, but are commented out. * * 3) the procedure ModulenameToFilename in MakeParse will have to be * modified so that it creates the correct filename for your machine. * * 4) if your MAKE program has default MODULA-2 rules, set the constant * MAKEHasModulaRules (in this module) to TRUE. This will prevent * MAKEMAKE from producing remake lines. * * 5) the procedure WriteFilename takes a parameter: extension. Trace * all the calls to this function and make sure the proper extentions * are used. in TDI Modula, '.DEF' = definition module text * '.MOD' = implementation module text * '.SYM' = symbol file (compiled DEF) * '.LNK' = symbol file (compiled MOD) *) FROM SYSTEM IMPORT BYTE; IMPORT Debug; IMPORT Break; IMPORT InOut; IMPORT CmdEnv; IMPORT MakeForest; IMPORT MakeParse; IMPORT ASCII; IMPORT FileSystem; IMPORT Strings; CONST MAKEHasModulaRules = FALSE; CompilerPathname = 'm2 d:comp '; CompilerOptions = '/b'; LinkerPathname = 'm2 d:link '; VAR outputStream : FileSystem.File; column: CARDINAL; (* Output file column *) indent: CARDINAL; (* amount to indent when extending lines *) (*----------------------------------------------------------------------*) PROCEDURE CloseOutput; VAR reply : INTEGER; BEGIN FileSystem.Close(outputStream); END CloseOutput; (*----------------------------------------------------------------------*) PROCEDURE OpenOutput(): BOOLEAN; VAR filename: ARRAY [0..11] OF CHAR; ColPeriod: CARDINAL; BEGIN (* create a "makefile" with a name of the form mainmodule.MAK *) ColPeriod := Strings.Pos('.', MakeForest.FileForestEnd^.filename); Strings.Copy(MakeForest.FileForestEnd^.filename, 0, ColPeriod, filename); Strings.Concat(filename, '.MAK', filename); (* delete any existing file since FileSystem won't truncate old file *) FileSystem.Delete(filename, outputStream); (* create new output file *) FileSystem.Lookup(outputStream, filename, TRUE); column := 0; RETURN outputStream.res = FileSystem.done; END OpenOutput; (*----------------------------------------------------------------------*) PROCEDURE Write (ch : CHAR); BEGIN FileSystem.WriteByte (outputStream, BYTE(ch)); INC(column); END Write; (*----------------------------------------------------------------------*) PROCEDURE WriteString (str : ARRAY OF CHAR); VAR c : CARDINAL; BEGIN c := 0; WHILE (c <= HIGH(str)) AND (str[c] <> 0C) DO Write(str[c]); INC(c); END; (* WHILE *) END WriteString; (*----------------------------------------------------------------------*) PROCEDURE WriteLn; BEGIN Write ( ASCII.cr ); Write ( ASCII.lf ); column := 0; END WriteLn; (*----------------------------------------------------------------------*) PROCEDURE SetIndent(i: CARDINAL); BEGIN indent := i; END SetIndent; (*----------------------------------------------------------------------*) PROCEDURE DoIndent; VAR i: CARDINAL; BEGIN FOR i := 1 TO indent DO Write(' ') END; END DoIndent; (*----------------------------------------------------------------------*) PROCEDURE WriteMakefile; (*--------------------------------------------------------------------*) PROCEDURE WriteFileDependency (file : MakeForest.FileDefinitionList; lhsExtention : ARRAY OF CHAR); VAR iptr : MakeForest.ImportList; TmpStr: ARRAY[0..100] OF CHAR; BEGIN IF file <> NIL THEN IF file^.filename[0] = '#' THEN RETURN; END; (* IF *) IF (NOT file^.local) OR (file^.path[0] <> 0C) OR file^.library THEN HALT; (* double check for debugging *) END; WriteLn; WriteFilename (file^.filename, lhsExtention); WriteString (': '); SetIndent(Strings.Length(file^.filename) + 1); WriteString (file^.filename); (* if this is a .LNK file, then don't forget the .SYM file: *) (* but don't do it for the main module! *) IF (file <> MakeForest.FileForestEnd) AND (Strings.CompareStr (lhsExtention, '.LNK') = 0 (* Strings.Equal *)) THEN Write (' '); WriteFilename (file^.filename, '.SYM'); END; (* IF *) iptr := file^.imports; WHILE iptr <> NIL DO IF NOT iptr^.file^.library THEN MakeFullFilename(iptr^.file^.path, iptr^.file^.filename, '.SYM', TmpStr); IF (column + Strings.Length(TmpStr) + 3) > 79 THEN WriteString (' \'); WriteLn; DoIndent; END; (* IF *) Write (' '); WriteString(TmpStr); END; (* IF *) iptr := iptr^.next; END; (* WHILE *) iptr := file^.imports; column := 1000; (* <-- force a WriteLn *) WHILE iptr <> NIL DO IF iptr^.file^.library THEN MakeFullFilename(iptr^.file^.path, iptr^.file^.filename, '.SYM', TmpStr); IF (column + Strings.Length(TmpStr) + 1) > 79 THEN WriteLn; WriteString ('#libs: '); END; (* IF *) Write (' '); WriteString(TmpStr); END; (* IF *) iptr := iptr^.next; END; (* WHILE *) WriteLn; IF NOT MAKEHasModulaRules THEN Write (ASCII.ht); WriteString (CompilerPathname); WriteString (file^.filename); WriteString (CompilerOptions); WriteLn; END; (* IF *) END; (* IF *) END WriteFileDependency; (*--------------------------------------------------------------------*) PROCEDURE WriteAllLnkFiles; VAR ptr : MakeForest.FileDefinitionList; TmpStr: ARRAY[0..100] OF CHAR; BEGIN ptr := MakeForest.FileForest; REPEAT IF Strings.Pos('.MOD', ptr^.filename) <= HIGH(ptr^.filename) THEN IF NOT ptr^.library THEN MakeFullFilename(ptr^.path, ptr^.filename, '.LNK', TmpStr); IF (column + Strings.Length(TmpStr) + 3) > 79 THEN WriteString (' \'); WriteLn; DoIndent; END; (* IF *) Write (' '); WriteString(TmpStr); END; (* IF *) END; (* IF *) ptr := ptr^.next; UNTIL ptr = NIL; WriteLn; END WriteAllLnkFiles; (*--------------------------------------------------------------------*) PROCEDURE MakeFullFilename (path, filename, extention : ARRAY OF CHAR; VAR fullname: ARRAY OF CHAR); VAR c : CARDINAL; BEGIN c := 0; REPEAT fullname[c] := filename[c]; INC(c); UNTIL (c > HIGH (filename)) OR (filename[c] = '.') OR (filename[c] = 0C); fullname[c] := 0C; Strings.Concat(path, fullname, fullname); Strings.Concat(fullname, extention, fullname); END MakeFullFilename; (*--------------------------------------------------------------------*) PROCEDURE WriteFilename (filename, extention : ARRAY OF CHAR); VAR c : CARDINAL; BEGIN c := 0; REPEAT Write (filename[c]); INC(c); UNTIL (c > HIGH (filename)) OR (filename[c] = '.') OR (filename[c] = 0C); WriteString (extention); END WriteFilename; (*--------------------------------------------------------------------*) PROCEDURE WriteLODDependency (file : MakeForest.FileDefinitionList); BEGIN IF (NOT file^.local) OR (file^.path[0] <> 0C) OR file^.library THEN HALT; (* double check for debugging *) END; (* this is special stuff that didn't seem worth generalizing *) WriteLn; WriteString('# The following converts to EXE file'); WriteLn; WriteFilename (file^.filename, '.EXE: '); WriteFilename (file^.filename, '.LOD'); WriteLn; Write(ASCII.ht); WriteString('m2 d:lod2exe '); WriteFilename (file^.filename, '.LOD'); WriteLn; WriteLn; WriteFilename (file^.filename, '.LOD'); WriteString (':'); SetIndent(Strings.Length(file^.filename) + 1); WriteAllLnkFiles; IF NOT MAKEHasModulaRules THEN Write (ASCII.ht); WriteString (LinkerPathname); WriteFilename (file^.filename, '.LNK'); WriteLn; END; (* IF *) END WriteLODDependency; VAR ptr : MakeForest.FileDefinitionList; (* c : CARDINAL; *) BEGIN IF OpenOutput() THEN (* print out the .LOD dependency first.... *) WriteLODDependency (MakeForest.FileForestEnd); WriteFileDependency (MakeForest.FileForestEnd, '.LNK'); ptr := MakeForest.FileForest; WHILE ptr <> MakeForest.FileForestEnd DO IF (NOT ptr^.library) AND ptr^.local THEN IF Strings.Pos ('.MOD', ptr^.filename) <= HIGH(ptr^.filename) THEN WriteFileDependency (ptr, '.LNK'); ELSE WriteFileDependency (ptr, '.SYM'); END; (* IF *) END; (* IF *) ptr := ptr^.next; END; (* WHILE *) ELSE InOut.WriteString ('Error opening MAKEFILE'); InOut.WriteLn; END; (* IF *) CloseOutput; END WriteMakefile; VAR modulename, filename : ARRAY [0 .. 132] OF CHAR; BEGIN IF CmdEnv.ArgC < 1 THEN InOut.WriteString ("Usage: MAKEMAKE main-module-modulename"); InOut.WriteLn; ELSE Strings.Assign(CmdEnv.ArgV[1]^, modulename); MakeParse.ParseModule (modulename, MakeParse.main, filename); WriteMakefile; END; (* IF *) END MakeMake. ====== MakeFore.DEF ========================================================== DEFINITION MODULE MakeForest; (* * MAKEMAKE. Create a MAKEFILE for a MODULA-2 program. * * Written by Steve Tynor, 30 September 1986. * UUCP : tynor@gitpyr * USNAIL: 2550 Akers Mill Rd. T-2, Atlanta GA. 30339 * * Permission is granted to distribute, copy and change this program as long * as this notice remains... *) (* * Modified by R.A. Dukelow (24 January 1987) for to run under Logitech * Modula-2/86. See MakeMake.MOD for additional comments. *) EXPORT QUALIFIED FileDefinitionList, ImportList, SearchPathList, FileForestEnd, FileForest, LnkSearchPaths, SymSearchPaths, AddImport, AddFile; TYPE String = ARRAY [0 .. 100] OF CHAR; FileDefinitionList = POINTER TO FileDefinitionRec; ImportList = POINTER TO ImportRec; SearchPathList = POINTER TO SearchPathRec; FileDefinitionRec = RECORD filename : String; local : BOOLEAN; (* file in local directory *) path : String; (* path to find .LNK/.SYM file if this is a .MOD/.DEF file *) library : BOOLEAN; (* this file is a library module *) next : FileDefinitionList; imports : ImportList; (* points to linked list of imported files *) END; (* RECORD *) ImportRec = RECORD file : FileDefinitionList; next : ImportList; END; (* RECORD *) SearchPathRec = RECORD path : String; next : SearchPathList; END; (* RECORD *) VAR FileForestEnd, FileForest : FileDefinitionList; LnkSearchPaths, (* paths to search for .MOD files *) SymSearchPaths: SearchPathList; (* paths to search for .DEF files *) (*----------------------------------------------------------------------*) PROCEDURE AddImport (fileDefinition : FileDefinitionList; fname : ARRAY OF CHAR); (*----------------------------------------------------------------------*) PROCEDURE AddFile (fname : ARRAY OF CHAR; VAR fileDefinition : FileDefinitionList ) : BOOLEAN; (* returns TRUE if file added, FALSE if the file was already there *) END MakeForest. ====== MakeFore.MOD ========================================================== IMPLEMENTATION MODULE MakeForest; (* * MAKEMAKE. Create a MAKEFILE for a MODULA-2 program. * * Written by Steve Tynor, 30 September 1986. * UUCP : tynor@gitpyr * USNAIL: 2550 Akers Mill Rd. T-2, Atlanta GA. 30339 * * Permission is granted to distribute, copy and change this program as long * as this notice remains... *) (* * Modified by R.A. Dukelow (24 January 1987) for to run under Logitech * Modula-2/86. See MakeMake.MOD for additional comments. *) (* --------- FileForst ----->| zzz.MOD |--> import list --------- | . . . V --------- ---------- ---------- | xxx.MOD |-->| import N |-->...--> | import 1 | --------- ---------- ---------- | --------- | xxx.DEF |--> import list --------- | ---------- FileForestEnd-->| main.MOD |--> import list ---------- Each element of an import list points back to a file descriptor (which may contain a ## comment ## rather than a real file name). *) IMPORT Strings, CmdEnv; FROM Storage IMPORT ALLOCATE; (*----------------------------------------------------------------------*) PROCEDURE FindFile ( fname : ARRAY OF CHAR; VAR fileDefinition : FileDefinitionList); VAR found : BOOLEAN; BEGIN fileDefinition := FileForest; found := FALSE; WHILE (fileDefinition <> NIL) AND (NOT found) DO found := Strings.CompareStr (fname, fileDefinition^.filename) = 0 (* Strings.Equal *); IF NOT found THEN fileDefinition := fileDefinition^.next; END; (* IF *) END; (* WHILE *) END FindFile; (*----------------------------------------------------------------------*) PROCEDURE AddFile ( fname : ARRAY OF CHAR; VAR fileDefinition : FileDefinitionList) : BOOLEAN; BEGIN FindFile (fname, fileDefinition); IF fileDefinition = NIL THEN NEW (fileDefinition); Strings.Assign (fname, fileDefinition^.filename); fileDefinition^.next := FileForest; fileDefinition^.imports := NIL; (* until shown otherwise *) fileDefinition^.library := FALSE; fileDefinition^.local := TRUE; fileDefinition^.path := ''; IF FileForest = NIL THEN FileForestEnd := fileDefinition; END; (* IF *) FileForest := fileDefinition; RETURN TRUE; ELSE RETURN FALSE; END; (* IF *) END AddFile; (*----------------------------------------------------------------------*) PROCEDURE AddImport (fileDefinition : FileDefinitionList; fname : ARRAY OF CHAR ); VAR iPtr : ImportList; BEGIN NEW (iPtr); iPtr^.next := fileDefinition^.imports; fileDefinition^.imports := iPtr; FindFile (fname, iPtr^.file); IF iPtr^.file = NIL THEN HALT END; (* file should already be exist *) END AddImport; PROCEDURE InitSearchPaths(VAR SearchPaths: SearchPathList; EnvName: ARRAY OF CHAR); VAR str: String; i, j: CARDINAL; TmpPtr, TmpList, NewPathPtr: SearchPathList; BEGIN (* build a temporary list in wrong order - reverse it later *) SearchPaths := NIL; TmpList := NIL; CmdEnv.GetEnv(EnvName, str); i := 0; WHILE (i <= HIGH(str)) AND ((str[i] = ' ') OR (str[i] = ';')) DO INC(i) END; WHILE (i <= HIGH(str)) AND (str[i] <> 0C) DO NEW(NewPathPtr); NewPathPtr^.next := TmpList; TmpList := NewPathPtr; j := 0; REPEAT NewPathPtr^.path[j] := str[i]; INC(i); INC(j); UNTIL (i > HIGH(str)) OR (str[i] = 0C) OR (str[i] = ' ') OR (str[i] = ';'); IF NewPathPtr^.path[j - 1] <> '\' THEN NewPathPtr^.path[j] := '\'; INC(j); END; NewPathPtr^.path[j] := 0C; WHILE (i <= HIGH(str)) AND ((str[i] = ' ') OR (str[i] = ';')) DO INC(i); END; END; (* WHILE *) (* reverse the order of the list while attaching to SearchPaths *) WHILE TmpList <> NIL DO TmpPtr := TmpList; TmpList := TmpPtr^.next; TmpPtr^.next := SearchPaths; SearchPaths := TmpPtr; END; END InitSearchPaths; BEGIN FileForest := NIL; FileForestEnd := NIL; InitSearchPaths(LnkSearchPaths, 'M2LNK'); InitSearchPaths(SymSearchPaths, 'M2SYM'); END MakeForest. ====== MakeLibr.DEF ========================================================== DEFINITION MODULE MakeLibraries; (* * MAKEMAKE. Create a MAKEFILE for a MODULA-2 program. * * Written by Steve Tynor, 30 September 1986. * UUCP : tynor@gitpyr * USNAIL: 2550 Akers Mill Rd. T-2, Atlanta GA. 30339 * * Permission is granted to distribute, copy and change this program as long * as this notice remains... *) (* * Modified by R.A. Dukelow (24 January 1987) for to run under Logitech * Modula-2/86. See MakeMake.MOD for additional comments. *) EXPORT QUALIFIED IsALibraryModule; PROCEDURE IsALibraryModule (modulename : ARRAY OF CHAR) : BOOLEAN; END MakeLibraries. ====== MakeLibr.MOD ========================================================== IMPLEMENTATION MODULE MakeLibraries; (* * MAKEMAKE. Create a MAKEFILE for a MODULA-2 program. * * Written by Steve Tynor, 30 September 1986. * UUCP : tynor@gitpyr * USNAIL: 2550 Akers Mill Rd. T-2, Atlanta GA. 30339 * * Permission is granted to distribute, copy and change this program as long * as this notice remains... *) (* * Modified by R.A. Dukelow (24 January 1987) for to run under Logitech * Modula-2/86. See MakeMake.MOD for additional comments. *) (* NOTE: this module is implementation specific. Substitute the library * modules supplied by your compiler. *) IMPORT Strings; CONST NumLibraryModules = 39; VAR i: CARDINAL; LibraryModule : ARRAY [1 .. NumLibraryModules] OF ARRAY [0 .. 20] OF CHAR; (*-----------------------------------------------------------------------*) PROCEDURE IsALibraryModule (modulename : ARRAY OF CHAR) : BOOLEAN; (* binary search... *) VAR low, mid, high : CARDINAL; BEGIN low := 1; high := NumLibraryModules; WHILE low <= high DO mid := low + (high - low) DIV 2; CASE Strings.CompareStr (modulename, LibraryModule[mid]) OF 0 : RETURN TRUE; (* Strings.Equal *) | -1 : high := mid - 1; (* Strings.Less *) ELSE low := mid + 1; END; (* CASE *) END; (* WHILE *) RETURN FALSE; END IsALibraryModule; BEGIN (* NOTE: we're doing a binary search, so these must be in sorted order: *) (* Logitech supplied libraries: *) i := 1; LibraryModule [i] := 'ASCII'; INC(i); LibraryModule [i] := 'Break'; INC(i); LibraryModule [i] := 'CardinalIO'; INC(i); LibraryModule [i] := 'Conversions'; INC(i); LibraryModule [i] := 'DOS3'; INC(i); (* ASCII DO... < De... *) LibraryModule [i] := 'DOS31'; INC(i); LibraryModule [i] := 'Debug'; INC(i); LibraryModule [i] := 'Decimals'; INC(i); LibraryModule [i] := 'Devices'; INC(i); LibraryModule [i] := 'Directories'; INC(i); LibraryModule [i] := 'DiskDirectory'; INC(i); LibraryModule [i] := 'DiskFiles'; INC(i); LibraryModule [i] := 'Display'; INC(i); LibraryModule [i] := 'ErrorCode'; INC(i); LibraryModule [i] := 'FileMessage'; INC(i); LibraryModule [i] := 'FileNames'; INC(i); LibraryModule [i] := 'FileSystem'; INC(i); LibraryModule [i] := 'InOut'; INC(i); LibraryModule [i] := 'Keyboard'; INC(i); LibraryModule [i] := 'LogiMouse'; INC(i); LibraryModule [i] := 'MathLib0'; INC(i); LibraryModule [i] := 'Mouse'; INC(i); LibraryModule [i] := 'NumberConversion'; INC(i); LibraryModule [i] := 'Options'; INC(i); LibraryModule [i] := 'Processes'; INC(i); LibraryModule [i] := 'ProgMessage'; INC(i); LibraryModule [i] := 'Program'; INC(i); LibraryModule [i] := 'RealConversions'; INC(i); LibraryModule [i] := 'RealInOut'; INC(i); LibraryModule [i] := 'RS232Code'; INC(i); LibraryModule [i] := 'RS232Int'; INC(i); LibraryModule [i] := 'RS232Polling'; INC(i); (* note : Strings.Compare thinks 'SY' < 'St'. (it just compares ASCII * values...) So this is not exactly alphabetical... just sorted * as Strings.Compare would... *) LibraryModule [i] := 'SYSTEM'; INC(i); LibraryModule [i] := 'Storage'; INC(i); LibraryModule [i] := 'Strings'; INC(i); LibraryModule [i] := 'System'; INC(i); LibraryModule [i] := 'Termbase'; INC(i); LibraryModule [i] := 'Terminal'; INC(i); LibraryModule [i] := 'TimeDate'; INC(i); END MakeLibraries. ====== MakePars.DEF ========================================================== DEFINITION MODULE MakeParse; (* * MAKEMAKE. Create a MAKEFILE for a MODULA-2 program. * * Written by Steve Tynor, 30 September 1986. * UUCP : tynor@gitpyr * USNAIL: 2550 Akers Mill Rd. T-2, Atlanta GA. 30339 * * Permission is granted to distribute, copy and change this program as long * as this notice remains... *) (* * Modified by R.A. Dukelow (24 January 1987) for to run under Logitech * Modula-2/86. See MakeMake.MOD for additional comments. *) EXPORT QUALIFIED FileType, ParseModule; TYPE FileType = (def, mod, main); (*----------------------------------------------------------------------*) PROCEDURE ParseModule ( modulename : ARRAY OF CHAR; type : FileType; VAR filename : ARRAY OF CHAR); END MakeParse. ====== MakePars.MOD ========================================================== IMPLEMENTATION MODULE MakeParse; (* * MAKEMAKE. Create a MAKEFILE for a MODULA-2 program. * * Written by Steve Tynor, 30 September 1986. * UUCP : tynor@gitpyr * USNAIL: 2550 Akers Mill Rd. T-2, Atlanta GA. 30339 * * Permission is granted to distribute, copy and change this program as long * as this notice remains... *) (* * Modified by R.A. Dukelow (24 January 1987) for to run under Logitech * Modula-2/86. See MakeMake.MOD for additional comments. *) IMPORT MakeToken; IMPORT MakeForest; IMPORT Strings; IMPORT MakeLibraries; (*----------------------------------------------------------------------*) PROCEDURE ModulenameToFilename ( modulename : ARRAY OF CHAR; defOrMod : FileType; VAR fname : ARRAY OF CHAR); CONST EOS = 0C; VAR c : CARDINAL; BEGIN c := 0; WHILE (c < HIGH (modulename)) AND (c < 8) AND (modulename[c] <> EOS) DO fname[c] := modulename[c]; INC (c); END; (* WHILE *) fname[c] := '.'; IF defOrMod = def THEN fname[c+1] := 'D'; fname[c+2] := 'E'; fname[c+3] := 'F'; fname[c+4] := EOS; ELSE fname[c+1] := 'M'; fname[c+2] := 'O'; fname[c+3] := 'D'; fname[c+4] := EOS; END; (* IF *) END ModulenameToFilename; (*----------------------------------------------------------------------*) PROCEDURE TerminatingToken(token : ARRAY OF CHAR) : BOOLEAN; VAR temp : BOOLEAN; BEGIN (* had to split up due to compiler limitation... *) temp := (Strings.CompareStr (token, 'CONST') = 0 (* Strings.Equal *)) OR (Strings.CompareStr (token, 'TYPE') = 0 (* Strings.Equal *)) OR (Strings.CompareStr (token, 'VAR') = 0 (* Strings.Equal *)) OR (Strings.CompareStr (token, 'MODULE') = 0 (* Strings.Equal *)); RETURN temp OR (Strings.CompareStr (token, 'PROCEDURE') = 0 (* Strings.Equal *)) OR (Strings.CompareStr (token, 'BEGIN') = 0 (* Strings.Equal *)) OR (Strings.CompareStr (token, 'END') = 0 (* Strings.Equal *)); END TerminatingToken; (*----------------------------------------------------------------------*) PROCEDURE ParseFROM ( fileDef : MakeForest.FileDefinitionList; VAR eof : BOOLEAN ); VAR modulename, filename : ARRAY [0 .. 100] OF CHAR; BEGIN MakeToken.NextToken (modulename, eof); ParseModule (modulename, def, filename); (* AddImport follows ParseModule(..., def, ...) because this creates the needed filename (xxx.DEF) for the dependency list and creates a file record on the list with all necessary checking. *) MakeForest.AddImport (fileDef, filename); MakeToken.SkipTillSemicolon (eof); ParseModule (modulename, mod, filename); END ParseFROM; (*----------------------------------------------------------------------*) PROCEDURE ParseIMPORT ( fileDef : MakeForest.FileDefinitionList; VAR eof : BOOLEAN ); VAR modulename, filename : ARRAY [0 .. 100] OF CHAR; BEGIN MakeToken.NextToken (modulename, eof); WHILE modulename[0] <> ';' DO ParseModule (modulename, def, filename); (* AddImport follows ParseModule(..., def, ...) because this creates the needed filename (xxx.DEF) for the dependency list and creates a file record on the list with all necessary checking. *) MakeForest.AddImport (fileDef, filename); ParseModule (modulename, mod, filename); MakeToken.NextToken (modulename, eof); END; (* WHILE *) END ParseIMPORT; (*----------------------------------------------------------------------*) PROCEDURE FindPath (VAR FileDefinition : MakeForest.FileDefinitionList ); (* Try to find a path to the indicated file *) VAR PartFilename, FullFilename: ARRAY[0..100] OF CHAR; success: BOOLEAN; PathPtr: MakeForest.SearchPathList; ColPeriod: CARDINAL; BEGIN (* strip off the extension *) ColPeriod := Strings.Pos('.', FileDefinition^.filename); Strings.Copy(FileDefinition^.filename, 0, ColPeriod, PartFilename); (* add extension and find appropriate search path list *) IF Strings.Pos('.MOD', FileDefinition^.filename) <= HIGH(FileDefinition^.filename) THEN PathPtr := MakeForest.LnkSearchPaths; Strings.Concat(PartFilename, '.LNK', PartFilename); ELSE PathPtr := MakeForest.SymSearchPaths; Strings.Concat(PartFilename, '.SYM', PartFilename); END; (* do the search *) success := FALSE; WHILE (NOT success) AND (PathPtr <> NIL) DO Strings.Concat(PathPtr^.path, PartFilename, FullFilename); MakeToken.OpenFile(FullFilename, success); IF NOT success THEN PathPtr := PathPtr^.next END; MakeToken.CloseFile; END; IF success THEN FileDefinition^.path := PathPtr^.path; ELSE FileDefinition^.path := '##UnknownPath##\'; END; END FindPath; (*----------------------------------------------------------------------*) PROCEDURE ParseModule ( modulename : ARRAY OF CHAR; type : FileType; VAR filename : ARRAY OF CHAR); VAR token : ARRAY [0 .. 132] OF CHAR; fileDef : MakeForest.FileDefinitionList; success, eof : BOOLEAN; BEGIN ModulenameToFilename (modulename, type, filename); (* add current filename to list if necessary *) IF MakeForest.AddFile (filename, fileDef) THEN (* this is a new file - map it out if can find it *) MakeToken.OpenFile (filename, success); IF NOT success THEN fileDef^.local := FALSE; IF MakeLibraries.IsALibraryModule (modulename) THEN fileDef^.library := TRUE; ELSE FindPath(fileDef); END; (* IF *) ELSE MakeToken.NextToken (token, eof); IF (type = def) AND NOT (Strings.CompareStr (token, 'DEFINITION') = 0 (* Strings.Equal *)) THEN MakeForest.AddImport (fileDef, "## Expected a 'DEFINITION' module ##"); ELSIF (type = mod) AND NOT (Strings.CompareStr (token, 'IMPLEMENTATION') = 0 (* Strings.Equal *)) THEN MakeForest.AddImport (fileDef, "## Expected an 'IMPLEMENTATION' module ##"); ELSIF (type = main) AND NOT (Strings.CompareStr (token, 'MODULE') = 0 (* Strings.Equal *)) THEN MakeForest.AddImport (fileDef, "## Expected a 'MODULE' module ##"); END; (* IF / ELSE / ENDIF *) MakeToken.SkipTillSemicolon (eof); token := ';'; WHILE (NOT eof) AND (NOT TerminatingToken(token)) DO IF Strings.CompareStr (token, 'FROM') = 0 (* Strings.Equal *) THEN ParseFROM (fileDef, eof); ELSIF Strings.CompareStr (token, 'IMPORT') = 0 (* Strings.Equal *) THEN ParseIMPORT (fileDef, eof); END; (* IF *) MakeToken.NextToken (token, eof); END; (* WHILE *) END; (* IF file opened *) MakeToken.CloseFile; END; (* IF we need to check this one *) END ParseModule; END MakeParse. ====== MakeToke.DEF ========================================================== DEFINITION MODULE MakeToken; (* * MAKEMAKE. Create a MAKEFILE for a MODULA-2 program. * * Written by Steve Tynor, 30 September 1986. * UUCP : tynor@gitpyr * USNAIL: 2550 Akers Mill Rd. T-2, Atlanta GA. 30339 * * Permission is granted to distribute, copy and change this program as long * as this notice remains... *) (* * Modified by R.A. Dukelow (24 January 1987) for to run under Logitech * Modula-2/86. See MakeMake.MOD for additional comments. *) EXPORT QUALIFIED NextToken, SkipTillSemicolon, OpenFile, CloseFile; (*----------------------------------------------------------------------*) PROCEDURE OpenFile ( filename : ARRAY OF CHAR; VAR success : BOOLEAN); (*----------------------------------------------------------------------*) PROCEDURE CloseFile; (*----------------------------------------------------------------------*) PROCEDURE NextToken (VAR token : ARRAY OF CHAR; VAR eof : BOOLEAN); (*----------------------------------------------------------------------*) PROCEDURE SkipTillSemicolon (VAR eof : BOOLEAN); END MakeToken. ====== MakeToke.MOD ========================================================== IMPLEMENTATION MODULE MakeToken; (* * MAKEMAKE. Create a MAKEFILE for a MODULA-2 program. * * Written by Steve Tynor, 30 September 1986. * UUCP : tynor@gitpyr * USNAIL: 2550 Akers Mill Rd. T-2, Atlanta GA. 30339 * * Permission is granted to distribute, copy and change this program as long * as this notice remains... *) (* * Modified by R.A. Dukelow (24 January 1987) for to run under Logitech * Modula-2/86. See MakeMake.MOD for additional comments. *) FROM SYSTEM IMPORT BYTE; IMPORT FileSystem; VAR currentCh : CHAR; currentStream : FileSystem.File; (*======================================================================*) MODULE StreamStack; IMPORT FileSystem; EXPORT Pop, Push; CONST StackSize = 20; VAR StrStack : ARRAY [1 .. StackSize] OF FileSystem.File; CharStack : ARRAY [1 .. StackSize] OF CHAR; StackPtr : CARDINAL; (*--------------------------------------------------------------------*) PROCEDURE Push (str : FileSystem.File; ch : CHAR); BEGIN IF StackPtr + 1 <= StackSize THEN INC (StackPtr); StrStack [StackPtr] := str; CharStack[StackPtr] := ch; END; (* IF *) END Push; (*--------------------------------------------------------------------*) PROCEDURE Pop (VAR str : FileSystem.File; VAR ch : CHAR); BEGIN IF StackPtr > 0 THEN str := StrStack [StackPtr]; ch := CharStack[StackPtr]; DEC (StackPtr); END; (* IF *) END Pop; BEGIN StackPtr := 0; END StreamStack; (*======================================================================*) (*----------------------------------------------------------------------*) PROCEDURE OpenFile ( filename : ARRAY OF CHAR; VAR success : BOOLEAN); BEGIN Push (currentStream, currentCh); (* open file for reading only *) FileSystem.Lookup(currentStream, filename, FALSE); currentCh := ' '; success := currentStream.res = FileSystem.done; END OpenFile; (*----------------------------------------------------------------------*) PROCEDURE CloseFile; BEGIN FileSystem.Close(currentStream); Pop (currentStream, currentCh); END CloseFile; (*----------------------------------------------------------------------*) PROCEDURE SeparatingChar (ch : CHAR) : BOOLEAN; (* we're just worried about a subset of the real separating chars... *) BEGIN RETURN NOT (((ORD(ch) >= ORD('a')) AND (ORD(ch) <= ORD('z'))) OR ((ORD(ch) >= ORD('A')) AND (ORD(ch) <= ORD('Z'))) OR ((ORD(ch) >= ORD('0')) AND (ORD(ch) <= ORD('9')))); END SeparatingChar; (*----------------------------------------------------------------------*) PROCEDURE NextToken (VAR token : ARRAY OF CHAR; VAR eof : BOOLEAN); VAR c : CARDINAL; b: BYTE; BEGIN c := 0; IF currentCh = ';' THEN token[0] := ';'; token[1] := 0C; eof := currentStream.eof; FileSystem.ReadByte(currentStream, b); currentCh := CHAR(b); RETURN; END; (* IF *) WHILE SeparatingChar (currentCh) DO FileSystem.ReadByte(currentStream, b); currentCh := CHAR(b); IF currentCh = ';' THEN token[0] := ';'; token[1] := 0C; eof := currentStream.eof; FileSystem.ReadByte(currentStream, b); currentCh := CHAR(b); RETURN; ELSIF currentCh = '(' THEN token[c] := currentCh; FileSystem.ReadByte(currentStream, b); currentCh := CHAR(b); IF currentCh = '*' THEN SkipComment; ELSE token[0] := '('; token[1] := currentCh; c := 2; END; (* IF *) END; (* IF *) END; (* WHILE *) REPEAT IF currentCh = '(' THEN token[c] := currentCh; FileSystem.ReadByte(currentStream, b); currentCh := CHAR(b); IF currentCh = '*' THEN IF c > 0 THEN token[c+1] := 0C; SkipComment; RETURN; ELSE SkipComment; END; (* IF *) END; (* IF *) END; (* IF *) token[c] := currentCh; INC (c); FileSystem.ReadByte(currentStream, b); currentCh := CHAR(b); UNTIL currentStream.eof OR (c > HIGH (token)) OR SeparatingChar (currentCh); token[c] := 0C; eof := currentStream.eof; END NextToken; (*----------------------------------------------------------------------*) PROCEDURE SkipTillSemicolon (VAR eof : BOOLEAN); VAR ch : CHAR; b: BYTE; BEGIN REPEAT IF (NOT currentStream.eof) AND (currentCh = '(') THEN FileSystem.ReadByte(currentStream, b); currentCh := CHAR(b); IF (NOT currentStream.eof) AND (currentCh = '*') THEN SkipComment; END; (* IF *) ELSE FileSystem.ReadByte(currentStream, b); currentCh := CHAR(b); END; (* IF *) UNTIL currentStream.eof OR (currentCh = ';'); END SkipTillSemicolon; (*----------------------------------------------------------------------*) PROCEDURE SkipComment; VAR level : CARDINAL; done : BOOLEAN; b: BYTE; BEGIN level := 1; FileSystem.ReadByte(currentStream, b); currentCh := CHAR(b); done := FALSE; REPEAT IF (NOT currentStream.eof) AND (currentCh = '*') THEN FileSystem.ReadByte(currentStream, b); currentCh := CHAR(b); IF (NOT currentStream.eof) AND (currentCh = ')') THEN DEC(level); done := level = 0; END; (* IF *) ELSIF (NOT currentStream.eof) AND (currentCh = '(') THEN FileSystem.ReadByte(currentStream, b); currentCh := CHAR(b); IF (NOT currentStream.eof) AND (currentCh = '*') THEN INC(level); END; (* IF *) ELSE FileSystem.ReadByte(currentStream, b); currentCh := CHAR(b); END; (* IF *) UNTIL currentStream.eof OR done; END SkipComment; BEGIN currentCh := ' '; END MakeToken. ====== CmdEnv.DEF ============================================================ (* * Title: Command Line/Environment Information (CmdEnv) * Author: Gregory Elder * Last Update (by Gregory Elder): September 25, 1986 * Last Update (by Bob Dukelow): January 19, 1987 * Compiler: Logitech Modula-2/86 * Description: * This module provides argc and argv features similar to C and a procedure * for getting the values of environment variables. ArgC is the count of the * number of parameters on the command line. ArgV is an array of pointers * to strings containing the various command line parameters. GetEnv is * a procedure which, when given an enviroment variable, will return the * value the variable is set to. * *) DEFINITION MODULE CmdEnv; EXPORT QUALIFIED ArgC, ArgV, GetEnv; CONST MaxArgs = 10; (* Max arguments on the command line *) ArraySize = 200; (* Max size for each argument *) TYPE StringPTR = POINTER TO ARRAY[0..ArraySize] OF CHAR; Args = ARRAY[0..MaxArgs] OF StringPTR; VAR ArgC : CARDINAL; ArgV : Args; PROCEDURE GetEnv(EnvVar : ARRAY OF CHAR; VAR Strg : ARRAY OF CHAR); END CmdEnv. ====== CmdEnv.MOD ============================================================ (* * Title: Command Line/Environment Information * Author: Gregory Elder * Last Update (by Gregory Elder): September 25, 1986 * Last Update (by Bob Dukelow): January 19, 1987 * Compiler: Logitech Modula-2/86 * Description: * This module provides argc and argv features similar to C and a procedure * for getting the values of environment variables. ArgC is the count of the * number of parameters on the command line. ArgV is an array of pointers * to strings containing the various command line parameters. GetEnv is * a procedure which, when given an enviroment variable, will return the * value the variable is set to. * *) IMPLEMENTATION MODULE CmdEnv; FROM SYSTEM IMPORT AX, BX, CX, GETREG, SETREG, SWI, RTSVECTOR, ADDRESS; FROM Storage IMPORT ALLOCATE; FROM ASCII IMPORT nul; FROM Strings IMPORT Length, CompareStr; CONST CmdTailAdr = 80H; EnvOffSet = 2CH; EnvSize = 0FFFEH (* MAX(CARDINAL) - 1 *); TYPE CardPTR = POINTER TO CARDINAL; EnvStringPTR = POINTER TO ARRAY[0..EnvSize] OF CHAR; VAR PSP, AnAddress : ADDRESS; EnvPTR : EnvStringPTR; EnvAdrSegment : CardPTR; PROCEDURE ParseCmdLine; VAR CmdLength, i, j, start : CARDINAL; CmdLine, Sptr : StringPTR; CmdAdr : ADDRESS; BEGIN CmdAdr := PSP; INC(CmdAdr, CmdTailAdr); (* Get command tail *) CmdLine := CmdAdr; i := 0; CmdLength := ORD(CmdLine^[i]) - 1; INC(i); WHILE (i <= CmdLength) AND (CmdLine^[i] = ' ') DO INC(i); END; WHILE (i <= CmdLength) AND (CmdLine^[i] # ' ') DO INC(i); END; ArgV[0] := NIL; ArgC := 0; WHILE i <= CmdLength DO WHILE (CmdLine^[i] = ' ') AND (i <= CmdLength) DO INC(i); END; IF i <= CmdLength THEN start := i; WHILE (CmdLine^[i] # ' ') AND (i <= CmdLength) DO INC(i); END; ALLOCATE(Sptr, i - start + 2); j := 0; WHILE start <= i DO Sptr^[j] := CmdLine^[start]; INC(start); INC(j); END; Sptr^[j] := nul; IF ArgC < MaxArgs THEN INC(ArgC); ArgV[ArgC] := Sptr; END; END; END; i := ArgC + 1; LOOP IF i > MaxArgs THEN EXIT END; ArgV[i] := NIL; INC(i); END; END ParseCmdLine; PROCEDURE GetEnv(EnvVar : ARRAY OF CHAR; VAR Strg : ARRAY OF CHAR); CONST StringSize = 40; VAR i, j, len : CARDINAL; Str1, Str2 : ARRAY[0..StringSize-1] OF CHAR; BEGIN len := Length(EnvVar); FOR i := 0 TO (len - 1) DO IF (EnvVar[i] >= 'a') AND (EnvVar[i] <= 'z') THEN Str1[i] := CHR(ORD(EnvVar[i]) - ORD('a') + ORD('A')); ELSE Str1[i] := EnvVar[i]; END; END; Str1[i + 1] := nul; i := 0; LOOP j := 0; WHILE (EnvPTR^[i] # '=') AND (EnvPTR^[i] # nul) DO Str2[j] := EnvPTR^[i]; INC(i); INC(j); END; Str2[j] := nul; INC(i); IF CompareStr(Str1, Str2) = 0 THEN j := 0; WHILE EnvPTR^[i] # nul DO Strg[j] := EnvPTR^[i]; INC(i); INC(j); END; Strg[j] := nul; EXIT; ELSE WHILE EnvPTR^[i] # nul DO INC(i); END; END; INC(i); IF EnvPTR^[i] = nul THEN Strg[0] := nul; EXIT; END; END; END GetEnv; BEGIN (* Get PSP *) SETREG(AX, 026H); SWI(RTSVECTOR); GETREG(BX, PSP.OFFSET); GETREG(CX, PSP.SEGMENT); ParseCmdLine; (* Set up environment pointer *) AnAddress := PSP; INC(AnAddress, EnvOffSet); EnvAdrSegment := AnAddress; AnAddress.SEGMENT := EnvAdrSegment^; AnAddress.OFFSET := 0; EnvPTR := AnAddress; END CmdEnv. ====== MakeMake.MAK ========================================================== # The following converts to EXE file MakeMake.EXE: MakeMake.LOD m2 d:lod2exe MakeMake.LOD MakeMake.LOD: MakeLibr.LNK MakeToke.LNK MakePars.LNK MakeFore.LNK \ c:\m2tools\CmdEnv.LNK MakeMake.LNK m2 d:link MakeMake.LNK MakeMake.LNK: MakeMake.MOD MakePars.SYM MakeFore.SYM c:\m2tools\CmdEnv.SYM #libs: Strings.SYM FileSyst.SYM ASCII.SYM InOut.SYM Break.SYM Debug.SYM #libs: SYSTEM.SYM m2 d:comp MakeMake.MOD/b MakeLibr.LNK: MakeLibr.MOD MakeLibr.SYM #libs: Strings.SYM m2 d:comp MakeLibr.MOD/b MakeLibr.SYM: MakeLibr.DEF m2 d:comp MakeLibr.DEF/b MakeToke.LNK: MakeToke.MOD MakeToke.SYM #libs: FileSyst.SYM SYSTEM.SYM m2 d:comp MakeToke.MOD/b MakeToke.SYM: MakeToke.DEF m2 d:comp MakeToke.DEF/b MakePars.LNK: MakePars.MOD MakePars.SYM MakeLibr.SYM MakeFore.SYM \ MakeToke.SYM #libs: Strings.SYM m2 d:comp MakePars.MOD/b MakePars.SYM: MakePars.DEF m2 d:comp MakePars.DEF/b MakeFore.LNK: MakeFore.MOD MakeFore.SYM c:\m2tools\CmdEnv.SYM #libs: Storage.SYM Strings.SYM m2 d:comp MakeFore.MOD/b MakeFore.SYM: MakeFore.DEF m2 d:comp MakeFore.DEF/b ====== END ===================================================================