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;
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];