<<>> <> <> <> <<>> 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 <> 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]>> <> 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]>> <> 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; <> 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; <> 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; <> 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 <> 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; <> 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; <> WITH RedBlackTree.Lookup[rbTable, this.lookupName] SELECT FROM entry: FileEntry => RETURN[entry]; ENDCASE; RedBlackTree.Insert[rbTable, this, this.lookupName]; <> 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] = { <> <<[charSet: 0, char: '\n, looks: LOOKS[], format: NIL, comment: FALSE, endOfNode: TRUE, deltaLevel: 1, propList: LIST[^[key: $FromTiogaFile, val: $Yes]]]>> <> <<[charSet: 0, char: 'F, looks: LOOKS[], format: $code, comment: TRUE, endOfNode: FALSE, deltaLevel: 0, propList: NIL]>> <> <<[charSet: 0, char: 'F, looks: LOOKS[], format: $code, comment: FALSE, endOfNode: FALSE, deltaLevel: 0, propList: NIL]>> <> <<[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]>> <> 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[-1, $block]; }; ENDCASE => out.PutRope["\n**Non-FileEntry in RedBlackTree table\n"]; }; DumpInvertedInfo: RedBlackTree.EachNode = { <<[data: RedBlackTree.UserData] RETURNS [stop: BOOL _ FALSE]>> <> 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. ..