GenerateRequireInfo.mesa
Copyright Ó 1991, 1992 by Xerox Corporation. All rights reserved.
Willie-s, May 12, 1992 3:42 pm PDT
DIRECTORY
Basics, BasicTime, Commander, CommanderOps, Convert, IO, PFS, PFSNames, Prop, Rope, RedBlackTree, TiogaAccess;
GenerateRequireInfo: CEDAR MONITOR
IMPORTS BasicTime, Commander, CommanderOps, Convert, IO, PFS, PFSNames, Prop, Rope, RedBlackTree, TiogaAccess
~ BEGIN
PATH: TYPE ~ PFS.PATH;
STREAM: TYPE ~ IO.STREAM;
ROPE: TYPE ~ Rope.ROPE;
FileEntry: TYPE = REF FileEntryRep;
FileEntryRep: TYPE = RECORD [
lookupName: ROPE, -- without version number
fullNamePath: PATH,
fullNameRope: ROPE,
shortName: ROPE,
isCMFile: BOOL ¬ FALSE,
processed: BOOL ¬ FALSE,
requireList: ROPE,
runList: ROPE,
invoker: ROPE-- for real files, name of require file
];
rbTable: RedBlackTree.Table;
bangH: PATH ~ PFS.PathFromRope["!H"];
starBangH: PATH ~ PFS.PathFromRope["*!H"];
defaultEnumPath: ROPE ~ "/Cedar/Commands/";
maxSanityCount: INT ¬ 7;
verbose: BOOL ¬ FALSE;
GenerateRequireInfo: ENTRY Commander.CommandProc = {
ENABLE UNWIND => NULL;
out: STREAM ¬ cmd.out;
enumRope, outFile: ROPE;
enumPath: PATH;
cmdEntryList: LIST OF FileEntry ¬ NIL;
numFiles: INT ¬ 0;
fileListStream: STREAM;
eachEntryList: LIST OF FileEntry;
sanityCount: INT ¬ 0;
mightbeCMName, arg: ROPE;
count: CARD ¬ 0;
CheckCount: PROC = {
IF ( count ¬ count.SUCC ) MOD 10 = 0 THEN out.PutF1["(%g) ", [cardinal[count]] ]
ELSE out.PutChar['.];
};
CollectCmdFiles: PFS.InfoProc = {
short, lookupName: ROPE;
new: FileEntry ¬ NIL;
IF fileType = PFS.tDirectory THEN RETURN; -- ignore directories
short ¬ PFSNames.ComponentRope[PFSNames.ShortName[fullFName]];
IF short.Equal["."] OR short.Equal[".."] THEN RETURN; -- ignore these
we assume it is not in the table already
lookupName ¬ PFS.RopeFromPath[PFSNames.StripVersionNumber[fullFName]];
new ¬ NEW[FileEntryRep ¬ [
lookupName: lookupName,
fullNamePath: fullFName,
fullNameRope: lookupName,
shortName: PFSNames.ComponentRope[PFSNames.ShortName[fullFName]],
isCMFile: TRUE ]];
cmdEntryList ¬ CONS[new, cmdEntryList];
RedBlackTree.Insert[rbTable, new, lookupName];
};
EachEntry: RedBlackTree.EachNode = {
[data: RedBlackTree.UserData] RETURNS [stop: BOOL ← FALSE]
visiting the nodes entered from the .cm & .command files
WITH data SELECT FROM
entry: FileEntry => {
IF NOT entry.processed THEN eachEntryList ¬ CONS[entry, eachEntryList];
RETURN;
};
ENDCASE => out.PutRope["\n**Non-FileEntry in RedBlackTree table\n"];
};
WriteInvertedInfoOnFile: RedBlackTree.EachNode = {
[data: RedBlackTree.UserData] RETURNS [stop: BOOL ← FALSE]
called for each item in the table to write name on fileListStream.
WITH data SELECT FROM
entry: FileEntry => {
IF entry.invoker = NIL THEN RETURN;
fileListStream.PutF["%g (%g)\n\n", [rope[entry.shortName]], [rope[entry.invoker]] ];
};
ENDCASE => out.PutRope["\n**Non-FileEntry in RedBlackTree table\n"];
};
arg ¬ CommanderOps.NextArgument[cmd];
IF arg.Equal["-o"] THEN {
outFile ¬ CommanderOps.NextArgument[cmd];
enumRope ¬ CommanderOps.NextArgument[cmd];
}
ELSE enumRope ¬ arg;
IF enumRope = NIL THEN enumRope ¬ defaultEnumPath;
enumPath ¬ PFS.PathFromRope[enumRope];
out.PutF["\nStarting GenerateRequireInfo %g @ %g\n\n", [rope[enumRope]], [time[BasicTime.Now[]]] ];
rbTable ¬ RedBlackTree.Create[getKey: GetKey, compare: Compare];
IF ( mightbeCMName ¬ CommanderOps.NextArgument[cmd] ) # NIL THEN {
DO
new: FileEntry ¬ NIL;
fullFName: PATH;
fullFName ¬ PFS.FileInfo[PFS.AbsoluteName[PFS.PathFromRope[mightbeCMName], enumPath]
! PFS.Error => { out.PutF1["\n***Couldn't find %g\n", [rope[mightbeCMName]] ]; CONTINUE }].fullFName;
IF fullFName # NIL THEN {
lookupName: ROPE ~ PFS.RopeFromPath[PFSNames.StripVersionNumber[fullFName]];
new ¬ NEW[FileEntryRep ¬ [
lookupName: lookupName,
fullNamePath: fullFName,
fullNameRope: lookupName,
shortName: PFSNames.ComponentRope[PFSNames.ShortName[fullFName]],
isCMFile: TRUE]];
cmdEntryList ¬ CONS[new, cmdEntryList];
RedBlackTree.Insert[rbTable, new, lookupName];
};
IF ( mightbeCMName ¬ CommanderOps.NextArgument[cmd] ) = NIL THEN EXIT;
ENDLOOP;
}
ELSE PFS.EnumerateForInfo[PFS.AbsoluteName[starBangH, enumPath], CollectCmdFiles];
FOR ceL: LIST OF FileEntry ¬ cmdEntryList, ceL.rest UNTIL ceL = NIL DO
DoOneFile[out, cmd, ceL.first];
CheckCount[];
ceL.first.processed ¬ TRUE;
ENDLOOP;
up to now, we've just looked at the .command & .cm files
DO
RedBlackTree.EnumerateIncreasing[rbTable, EachEntry];
IF eachEntryList = NIL THEN EXIT;
FOR eeL: LIST OF FileEntry ¬ eachEntryList, eeL.rest UNTIL eeL = NIL DO
DoOneFile[out, cmd, eeL.first];
CheckCount[];
eeL.first.processed ¬ TRUE;
ENDLOOP;
eachEntryList ¬ NIL;
IF ( sanityCount ¬ sanityCount + 1 ) > maxSanityCount THEN {
out.PutF1["\n** sanityCount > %g, exitting\n", [integer[maxSanityCount]] ];
EXIT
};
IF verbose THEN out.PutF1["\n~~~ sanityCount: %g\n", [integer[sanityCount]] ];
ENDLOOP;
now everything should be in the tree, so dump the tree
DumpToFile[out, outFile, rbTable];
out.PutF1["Finished at %g\n", [time[BasicTime.Now[]]] ];
};
DoOneFile: PROC[out: STREAM, cmd: Commander.Handle, entry: FileEntry] = {
ENABLE PFS.Error => {
out.PutF1["\n**PFS.Error: %g\n", [rope[error.explanation]] ];
GOTO quitThisOne;
};
strm: STREAM ¬ PFS.StreamOpen[entry.fullNamePath];
which: ROPE ¬ PFS.RopeFromPath[entry.fullNamePath];
this, line: ROPE;
alreadyIgnoring: BOOL ¬ FALSE;
IF verbose THEN out.PutF1["Starting %g\n", [rope[which]] ];
UNTIL strm.EndOf[] DO
from: STREAM;
line ¬ IO.GetLineRope[strm];
from ¬ IO.RIS[line];
this ¬ NextArgToken[from];
IF this = NIL THEN LOOP;
out.PutF["*line: %g\n\tthis: %g\n", [rope[line]], [rope[this]] ];
SELECT TRUE FROM
this.Equal["Require", FALSE] => ProcessRequireLine[out, from, line, entry];
this.Equal["Run", FALSE] => ProcessRunLine[out, from, line, entry];
this.Equal["From", FALSE] => ProcessFromLine[out, from, line, which, entry];
this.Equal["Install", FALSE] => ProcessInstallLine[out, from, line, entry];
this.Equal["Alias", FALSE] => NULL;
this.Equal["Echo", FALSE] => NULL;
this.Equal["CD", FALSE] => NULL;
this.Equal["Synonym", FALSE] => NULL;
this.Equal[";", FALSE] => NULL;
this.Equal["UnixCommand", FALSE] => NULL;
this.Equal["interp", FALSE] => NULL;
this.Equal["RunGlobalDefaultSwitches", FALSE] => NULL;
this.Equal["CommanderViewer", FALSE] => NULL;
this.Equal["repaint", FALSE] => NULL;
ENDCASE => {
IF NOT alreadyIgnoring THEN
out.PutF1["~~From %g, ignoring:\n", [rope[which]] ];
out.PutF1["\t%g\n", [rope[line]] ];
alreadyIgnoring ¬ TRUE;
};
ENDLOOP;
strm.Close[];
IF verbose THEN out.PutF1["Done with %g\n", [rope[which]] ];
EXITS quitThisOne => {};
};
ProcessRunLine: PROC[out, from: STREAM, line: ROPE, caller: FileEntry] = {
relPath: PATH ¬ PFSNames.Directory[caller.fullNamePath];
fullFName: PATH;
lookupEntry: FileEntry;
insideMinusU: BOOL ¬ FALSE;
DO
next: ROPE ¬ NextArgToken[from];
IF next = NIL THEN RETURN;
IF ( next.Fetch[0] = '- ) THEN {
SELECT next.Fetch[1] FROM
'u, 'U => insideMinusU ¬ TRUE;
'x, 'X => insideMinusU ¬ FALSE;
'p, 'P => [] ¬ NextArgToken[from];
ENDCASE => NULL;
LOOP;
};
IF insideMinusU THEN LOOP; -- ignore these
here is something to run
lookupEntry ¬ LookupSomethingToRun[out, next, relPath];
IF lookupEntry # NIL THEN AddToRunList[out, caller, lookupEntry, FALSE]
ELSE out.PutF["\nCould not find %g (%g)\n(%g)\n", [rope[next]], [rope[caller.fullNameRope]], [rope[line]] ];
ENDLOOP;
};
LookupSomethingToRun: PROC[out: STREAM, file: ROPE, relPath: PATH]
RETURNS[lookupEntry: FileEntry ¬ NIL] = {
lookupPath: PFS.PATH ¬
PFSNames.Cat[relPath, PFS.PathFromRope[Rope.Cat["sun4-o3/", file, ".c2c.o"]]];
IF ( lookupEntry ¬ CheckAndEnter[out, lookupPath, TRUE, TRUE] ) # NIL THEN RETURN;
lookupPath ¬ PFSNames.Cat[relPath, PFS.PathFromRope[Rope.Concat["sun4-o3/", file]]];
IF ( lookupEntry ¬ CheckAndEnter[out, lookupPath, TRUE, TRUE] ) # NIL THEN RETURN;
lookupPath ¬ PFSNames.Cat[relPath, PFS.PathFromRope[Rope.Cat["sun4/", file, ".c2c.o"]]];
IF ( lookupEntry ¬ CheckAndEnter[out, lookupPath, TRUE, TRUE] ) # NIL THEN RETURN;
lookupPath ¬ PFSNames.Cat[relPath, PFS.PathFromRope[Rope.Concat["sun4/", file]]];
IF ( lookupEntry ¬ CheckAndEnter[out, lookupPath, TRUE, TRUE] ) # NIL THEN RETURN;
lookupPath ¬ PFSNames.Cat[relPath, PFS.PathFromRope[file]];
lookupEntry ¬ CheckAndEnter[out, lookupPath, TRUE, TRUE];
};
ProcessRequireLine: PROC[out, from: STREAM, line: ROPE, caller: FileEntry] = {
lookupPath: PATH;
this: FileEntry;
world, component, resource: ROPE;
world ¬ NextArgToken[from];
component ¬ NextArgToken[from];
resource ¬ NextArgToken[from];
IF ( world = NIL ) OR ( component = NIL ) OR ( resource = NIL ) THEN {
out.PutF1["\n*** problems with the line: %g\n", [rope[line]] ];
RETURN;
};
lookupPath ¬ PFS.PathFromRope[IO.PutFR["/%g/%g/%g.require", [rope[world]], [rope[component]], [rope[resource]] ]];
this ¬ CheckAndEnter[out, lookupPath, FALSE, FALSE];
IF this = NIL THEN RETURN;
AddToRequireList[caller, this.fullNameRope];
};
ProcessInstallLine: PROC[out, from: STREAM, line: ROPE, caller: FileEntry] = {
relPath: PATH ¬ PFSNames.Directory[caller.fullNamePath];
lookupPath: PATH;
lookupEntry: FileEntry;
resource: ROPE ¬ NextArgToken[from];
IF ( resource = NIL ) THEN {
out.PutF1["\n*** problems with the line: %g\n", [rope[line]] ];
RETURN;
};
lookupPath ¬ PFSNames.Cat[relPath, PFS.PathFromRope[resource.Concat[".require"]]];
lookupEntry ¬ CheckAndEnter[out, lookupPath, FALSE, FALSE];
IF lookupEntry = NIL THEN RETURN;
AddToRequireList[caller, lookupEntry.fullNameRope];
};
ProcessFromLine: PROC[out, from: STREAM, line, which: ROPE, caller: FileEntry] = {
lookupEntry: FileEntry;
dir, shouldBeRun, resource: ROPE;
dir ¬ NextArgToken[from];
shouldBeRun ¬ NextArgToken[from];
IF NOT shouldBeRun.Equal["Run", FALSE] THEN {
out.PutF["\n* (from %g) found %g instead of Run in %g\n", [rope[which]], [rope[shouldBeRun]], [rope[line]] ];
RETURN;
};
resource ¬ NextArgToken[from];
IF ( dir = NIL ) OR ( resource = NIL ) THEN {
out.PutF1["\n*** problems with the line: %g\n", [rope[line]] ];
RETURN;
};
lookupEntry ¬ LookupSomethingToRun[out, resource, PFS.PathFromRope[dir]];
IF lookupEntry # NIL THEN AddToRunList[out, caller, lookupEntry, TRUE]
ELSE out.PutF[" (from %g), couldn't find %g, %g\n", [rope[which]], [rope[dir]], [rope[resource]] ];
};
CheckAndEnter: PROC[out: STREAM, path: PATH, markAsDone, useShortName: BOOL]
RETURNS[this: FileEntry] = {
fullFName: PATH;
out.PutF["~& LookupPath: %g\n", [rope[PFS.RopeFromPath[path]]] ];
fullFName ¬ PFS.FileInfo[name: path !
PFS.Error => {
IF verbose THEN out.PutF["\n*PFS.Error[%g]\n\tasking for %g\n", [rope[error.explanation]], [rope[PFS.RopeFromPath[path]]] ];
fullFName ¬ NIL;
CONTINUE;
} ].fullFName;
IF fullFName = NIL THEN RETURN[NIL];
this ¬ NEW[FileEntryRep ¬ [
lookupName: NIL,
fullNamePath: fullFName,
fullNameRope: PFS.RopeFromPath[PFSNames.StripVersionNumber[fullFName]],
shortName: PFSNames.ComponentRope[PFSNames.ShortName[fullFName]],
processed: markAsDone]];
this.lookupName ¬ IF useShortName THEN this.shortName ELSE this.fullNameRope;
be careful to do the lookup with the lookupName
WITH RedBlackTree.Lookup[rbTable, this.lookupName] SELECT FROM
entry: FileEntry => RETURN[entry];
ENDCASE;
RedBlackTree.Insert[rbTable, this, this.lookupName];
out.PutF["~* Added with fullNamePath: %g\n", [rope[PFS.RopeFromPath[fullFName]]] ];
RETURN[this];
};
AddToRunList: PROC[out: STREAM, caller, lookupEntry: FileEntry, useFullName: BOOL] = {
who: ROPE ~ IF useFullName THEN lookupEntry.fullNameRope ELSE lookupEntry.shortName;
IF caller.runList = NIL THEN caller.runList ¬ who
ELSE caller.runList ¬ caller.runList.Cat[" ", who];
IF lookupEntry.invoker # NIL THEN
out.PutF["%g is already being invoked by %g\n", [rope[lookupEntry.fullNameRope]], [rope[lookupEntry.invoker]] ];
lookupEntry.invoker ¬ caller.fullNameRope;
};
AddToRequireList: PROC[caller: FileEntry, who: ROPE] = {
IF caller.requireList = NIL THEN caller.requireList ¬ who
ELSE caller.requireList ¬ caller.requireList.Cat[" ", who];
};
NextArgToken: PROC[from: STREAM] RETURNS[token: ROPE ¬ NIL] = {
[] ¬ from.SkipWhitespace[];
token ¬ from.GetTokenRope[IO.IDProc ! IO.EndOfStream => CONTINUE].token;
};
GetKey: RedBlackTree.GetKey = {
[data: RedBlackTree.UserData] RETURNS [RedBlackTree.Key]
RETURN [data];
};
Compare: RedBlackTree.Compare = {
[k: RedBlackTree.Key, data: RedBlackTree.UserData] RETURNS [Basics.Comparison]
key: ROPE ¬ NIL;
WITH k SELECT FROM
ent: FileEntry => key ¬ ent.lookupName;
rope: ROPE => key ¬ rope;
ENDCASE => ERROR;
WITH data SELECT FROM
ent: FileEntry => RETURN [Rope.Compare[key, ent.lookupName, FALSE]];
ENDCASE;
ERROR;
};
DumpToFile: PROC[out: STREAM, outName: ROPE, rbTable: RedBlackTree.Table] = {
First Char:
[charSet: 0, char: '\n, looks: LOOKS[], format: NIL, comment: FALSE, endOfNode: TRUE, deltaLevel: 1, propList: LIST[^[key: $FromTiogaFile, val: $Yes]]]
Comment Char:
[charSet: 0, char: 'F, looks: LOOKS[], format: $code, comment: TRUE, endOfNode: FALSE, deltaLevel: 0, propList: NIL]
Normal Char:
[charSet: 0, char: 'F, looks: LOOKS[], format: $code, comment: FALSE, endOfNode: FALSE, deltaLevel: 0, propList: NIL]
Comment end of node (next node nested):
[charSet: 0, char: '\n, looks: LOOKS[], format: $code, comment: TRUE, endOfNode: TRUE, deltaLevel: 1, propList: NIL]
tc: TiogaAccess.TiogaChar ¬
[charSet: 0, char: '\n, looks: ALL[FALSE], format: NIL, comment: TRUE,
endOfNode: TRUE, deltaLevel: 1,
propList: Prop.Put[propList: NIL, key: $NewlineDelimiter, val: Rope.Flatten["\n"]] ];
PutCharB: Rope.ActionType = { tc.char ¬ c; TiogaAccess.Put[writer, tc] };
PutRope: PROC [ rope: ROPE ] = { [] ¬ rope.Map[action: PutCharB] };
PutRopeBold: PROC [ rope: ROPE ] = {
tc.looks['b] ¬ TRUE;
[] ¬ rope.Map[action: PutCharB];
tc.looks['b] ¬ FALSE;
};
PutRopeItalic: PROC [ rope: ROPE ] = {
tc.looks['i] ¬ TRUE;
[] ¬ rope.Map[action: PutCharB];
tc.looks['i] ¬ FALSE;
};
EndNode: PROC [ delta: INTEGER ¬ 0, format: ATOM ¬ NIL ] = {
tc.char ¬ '\n;
tc.format ¬ format;
tc.deltaLevel ¬ delta;
tc.endOfNode ¬ TRUE;
TiogaAccess.Put[writer, tc];
tc.endOfNode ¬ FALSE;
};
DumpRequireInfo: RedBlackTree.EachNode = {
[data: RedBlackTree.UserData] RETURNS [stop: BOOL ← FALSE]
called for each item in the table to write name on fileListStream.
WITH data SELECT FROM
entry: FileEntry => {
level: INT ¬ 0;
IF entry.invoker # NIL THEN RETURN;
tc.format ¬ $block;
PutRopeBold[entry.fullNameRope];
IF entry.requireList # NIL THEN {
EndNode[1, $block];
PutRopeItalic["Requires: "];
EndNode[1, $block];
tc.format ¬ $ragged;
PutRope[entry.requireList];
EndNode[-1, $ragged];
level ¬ 1;
};
IF entry.runList # NIL THEN {
IF level = 0 THEN EndNode[1, $block];
PutRopeItalic["Runs: "];
EndNode[1, $block];
tc.format ¬ $ragged;
PutRope[entry.runList];
EndNode[-1, $ragged];
level ¬ 1;
};
IF level = 1 THEN EndNode[-2, $block] ELSE EndNode[-1, $block];
IF level = 1 THEN EndNode[-1, $block];
};
ENDCASE => out.PutRope["\n**Non-FileEntry in RedBlackTree table\n"];
};
DumpInvertedInfo: RedBlackTree.EachNode = {
[data: RedBlackTree.UserData] RETURNS [stop: BOOL ← FALSE]
called for each item in the table to write name on fileListStream.
WITH data SELECT FROM
entry: FileEntry => {
IF entry.invoker = NIL THEN RETURN;
PutRopeBold[entry.shortName];
PutRope[" "];
PutRopeItalic[entry.invoker];
EndNode[0, $block];
};
ENDCASE => out.PutRope["\n**Non-FileEntry in RedBlackTree table\n"];
};
defaultName: ROPE ~ "Cedar.requireInfo";
writer: TiogaAccess.Writer ¬ TiogaAccess.Create[];
tyme: BasicTime.GMT = BasicTime.Now[];
IF outName = NIL THEN outName ¬ defaultName;
out.PutF1["\nStarting to generate %g\n", [rope[outName]] ];
TiogaAccess.Put[writer, tc];
tc.propList ¬ NIL;
tc.comment ¬ TRUE;
tc.endOfNode ¬ FALSE;
PutRope[outName];
EndNode[1];
PutRope[ IO.PutFR1["Copyright Ó %g by Xerox Corporation. All rights reserved.",
[rope[Convert.RopeFromInt[BasicTime.Unpack[tyme].year]]] ] ];
EndNode[];
PutRope[IO.PutFR1["Written %g", [time[tyme]] ]];
EndNode[-1];
PutRope["Command and Require files"];
EndNode[1, $head];
tc.comment ¬ FALSE;
RedBlackTree.EnumerateIncreasing[rbTable, DumpRequireInfo];
EndNode[-1, $block]; -- seems like it should be -2, but ...
tc.comment ¬ TRUE;
PutRope["Modules and which require file runs them"];
tc.comment ¬ FALSE;
EndNode[1, $head];
RedBlackTree.EnumerateIncreasing[rbTable, DumpInvertedInfo];
TiogaAccess.WriteFile[writer, outName];
out.PutF1["\nFinished %g\n", [rope[outName]] ];
};
SetSanityCount: Commander.CommandProc = {
intR: ROPE ¬ CommanderOps.NextArgument[cmd];
IF intR = NIL THEN RETURN;
maxSanityCount ¬ Convert.IntFromRope[intR ! Convert.Error => CONTINUE];
};
ShowSanityCount: Commander.CommandProc =
{ cmd.out.PutF1["SanityCount: %g\n", [integer[maxSanityCount]] ] };
SetVerbose: Commander.CommandProc = { verbose ¬ TRUE };
UnSetVerbose: Commander.CommandProc = { verbose ¬ FALSE };
Commander.Register["GenerateRequireInfo", GenerateRequireInfo, "Usage is: GenerateRequireInfo {-o outfile} {dir} {optional list of files in dir}"];
Commander.Register["gri", GenerateRequireInfo, "Usage is: GenerateRequireInfo {-o outfile} {dir} {optional list of files in dir}"];
Commander.Register["SetSanityCount", SetSanityCount];
Commander.Register["ShowSanityCount", ShowSanityCount];
Commander.Register["SetVerbose", SetVerbose];
Commander.Register["UnSetVerbose", UnSetVerbose];
END. ..