DIRECTORY BasicTime USING [ GMT, Now, nullGMT, Unpack ], MobDefs USING [ Base, MobBase, FTHandle, FTIndex, FTNull, FTRecord, NameRecord, NameString, VersionStamp ], Commander USING [ CommandProc, Register ], CommanderOps USING [ ArgumentVector, Failed, Parse ], Convert USING [RopeFromInt], DFUtilities USING [ Date ], FileNames USING [ GetShortName, StripVersionNumber ], GenerateDFClosure USING [ ActionProc, GenerateClosureToProc ], IO, List USING [ Compare, CompareProc, Sort ], MobMapper USING [AlterMob, AlterMobResults, BadMobContents], MessagesOut USING [ PutRopes ], PFS USING [ Close, Error, GetInfo, Open, OpenFile, PathFromRope, Read ], Process USING [ CheckForAbort, Pause, SecondsToTicks ], Prop USING [ PropList, Put ], RedBlackTree USING [ Compare, Create, EachNode, EnumerateIncreasing, GetKey, Insert, Lookup, Table ], Rope USING [ ActionType, Compare, Concat, Fetch, Flatten, FromProc, Length, Map, Match, ROPE, Substr ], TiogaAccess USING [ Create, Put, TiogaChar, WriteFile, Writer ], UserProfile USING [ Token ], VM USING [ AddressForPageNumber, Free, Interval, PagesForBytes, SimpleAllocate ]; Dependencies: CEDAR MONITOR IMPORTS BasicTime, Commander, CommanderOps, Convert, FileNames, GenerateDFClosure, IO, List, MobMapper, MessagesOut, PFS, Process, Prop, RedBlackTree, Rope, TiogaAccess, UserProfile, VM = BEGIN GMT: TYPE = BasicTime.GMT; ROPE: TYPE = Rope.ROPE; Switches: TYPE ~ PACKED ARRAY CHAR['a..'z] OF BOOL; tempfile: ROPE ฌ "///Temp/Dependencies.temp$"; triesForServerGlitch: NAT ฌ 8; pauseForServerGlitch: NAT ฌ 15; forkers: NAT ฌ 1; suspectFrom: ROPE ฌ NIL; suspectTo: ROPE ฌ NIL; SuspectSeen: SIGNAL = CODE; BaseEntry: TYPE = REF BaseEntryRep; BaseEntryList: TYPE = LIST OF BaseEntry; BaseEntryRep: TYPE = RECORD [ name: ROPE, from: ROPE ฌ NIL, version: VersionStamp, dependents: BaseEntryList ฌ NIL, next: BaseEntry ฌ NIL ]; ObjectStyle: TYPE ~ { bcd, mob }; VersionStamp: TYPE ~ RECORD [ SELECT style: ObjectStyle FROM mob => [ mob: MobDefs.VersionStamp ] ENDCASE ]; DumpOutput: PROC [ outName: ROPE, table: RedBlackTree.Table, switches: Switches ] ~ { tc: TiogaAccess.TiogaChar ฌ [charSet: 0, char: '\n, looks: ALL[FALSE], format: NIL, comment: FALSE, 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; }; EachNode: RedBlackTree.EachNode = { WITH data SELECT FROM base: BaseEntry => { WHILE ( base # NIL ) DO dependents: BaseEntryList ฌ base.dependents ฌ SortDependents[base.dependents]; SELECT TRUE FROM ( ( dependents = NIL ) AND ( NOT switches['u] ) ) => { NULL }; ( ( base.from = NIL ) AND ( NOT switches['v] ) ) => { NULL }; ENDCASE => { tc.format ฌ $block; PutRopeBold[base.name]; SELECT base.version.style FROM mob => { IF ( switches['m] ) THEN PutRope["[mob]"] }; ENDCASE => { NULL }; -- ??? PutRope[": "]; PutRopeItalic["("]; PutRopeItalic[IF ( base.from = NIL ) THEN "??" ELSE base.from]; PutRopeItalic[")"]; IF ( dependents = NIL ) THEN { EndNode[0, $block] } ELSE { EndNode[1, $block]; tc.format ฌ $ragged; WHILE ( dependents # NIL ) DO PutRope[dependents.first.name]; PutRope[" "]; dependents ฌ dependents.rest; ENDLOOP; EndNode[-1, $ragged]; }; }; IF ( suspectTo.Match[base.name, FALSE] ) THEN SIGNAL SuspectSeen; base ฌ base.next; ENDLOOP; }; ENDCASE => { NULL }; }; writer: TiogaAccess.Writer ฌ TiogaAccess.Create[]; tyme: BasicTime.GMT = BasicTime.Now[]; 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]; EndNode[]; tc.comment ฌ FALSE; RedBlackTree.EnumerateIncreasing[table, EachNode]; TiogaAccess.WriteFile[writer, outName]; }; MyData: TYPE = REF MyDataRep; MyDataRep: TYPE = RECORD [ table: RedBlackTree.Table ฌ NIL, abortRequested: BOOL ฌ FALSE, fsCareful: BOOL ฌ TRUE, errs: IO.STREAM ฌ NIL ]; EnsureProc: TYPE ~ PROC [objectName: ROPE, version: VersionStamp]; DependencyProc: TYPE ~ PROC [fromObject: ROPE, version: VersionStamp, toName: ROPE, toVersion: VersionStamp]; EachFile: GenerateDFClosure.ActionProc = { myData: MyData ฌ CheckAbort[data]; table: RedBlackTree.Table ฌ myData.table; EnterDependency: ENTRY DependencyProc = { ENABLE UNWIND => { NULL }; baseFrom: BaseEntry ฌ GetBaseRecord[table, fromObject, version]; baseTo: BaseEntry ฌ GetBaseRecord[table, toName, toVersion]; baseTo.dependents ฌ CONS[baseFrom, baseTo.dependents]; baseFrom.from ฌ FileNames.StripVersionNumber[from]; IF ( suspectFrom.Match[fromObject, FALSE] ) THEN SIGNAL SuspectSeen; IF ( suspectTo.Match[toName, FALSE] ) THEN SIGNAL SuspectSeen; }; EnsureFrom: ENTRY EnsureProc = { ENABLE UNWIND => { NULL }; base: BaseEntry ฌ GetBaseRecord[table, objectName, version]; IF ( base.from = NIL ) THEN base.from ฌ FileNames.StripVersionNumber[from]; }; ProcessObjectFile: PROC [ kind: ObjectStyle, shortName: ROPE, triesLeft: NAT ] ~ { shortName ฌ shortName.Flatten[len: ( shortName.Length[] - 4 )]; DO ENABLE PFS.Error => { SELECT error.code FROM $serverInaccessible => { MessagesOut.PutRopes[myData.errs, "Server glitch: ", name]; Process.Pause[Process.SecondsToTicks[pauseForServerGlitch]]; IF ( (triesLeft ฌ triesLeft.PRED) # 0 ) THEN LOOP; }; ENDCASE; MessagesOut.PutRopes[myData.errs, "File not found: ", name, IO.PutFR["\n from: %g\n reason: %g\n", IO.rope[from], IO.rope[error.explanation] ] ]; IF ( error.group # bug ) THEN EXIT ELSE REJECT; }; file: PFS.OpenFile; file ฌ PFS.Open[name: PFS.PathFromRope[name], wantedUniqueID: [egmt: [gmt: date.gmt, usecs: 0]] ]; { bytes: INT ~ file.GetInfo[].bytes; interval: VM.Interval ~ VM.SimpleAllocate[VM.PagesForBytes[bytes]]; p: LONG POINTER ~ VM.AddressForPageNumber[interval.page]; TRUSTED { [] ฌ file.Read[filePosition: 0, nBytes: bytes, to: p] }; SELECT kind FROM mob => TRUSTED { mob: MobDefs.MobBase ~ LOOPHOLE[p]; limit: CARD ~ bytes / BYTES[UNIT]; mobRes: MobMapper.AlterMobResults ฌ noop; mobRes ฌ MobMapper.AlterMob[mob, LOOPHOLE[mob], limit ! MobMapper.BadMobContents => { mobRes ฌ badVersion; CONTINUE} ]; IF mobRes = badVersion THEN MessagesOut.PutRopes[myData.errs, "MobMapper.AlterMob failed for ", name] ELSE MobCase[shortName, mob, EnsureFrom, EnterDependency]; }; ENDCASE => { NULL }; TRUSTED { VM.Free[interval] }; file.Close[]; }; EXIT; ENDLOOP; }; SELECT kind FROM file => { triesLeft: NAT ฌ triesForServerGlitch; shortName: ROPE ฌ FileNames.GetShortName[name]; SELECT TRUE FROM Rope.Match["*.mob", shortName, FALSE] => { ProcessObjectFile[mob, shortName, triesLeft]; }; ENDCASE => { NULL }; }; ENDCASE; }; CharSeq: TYPE = RECORD [ PACKED SEQUENCE COMPUTED CARD16 OF CHAR ]; RopeForNameRecord: PROC [ mob: MobDefs.MobBase, n: MobDefs.NameRecord ] RETURNS [ rope: ROPE ] = TRUSTED { ssb: MobDefs.NameString ~ LOOPHOLE[mob + mob.ssOffset.units]; index: CARD16 ~ n+4; ss: LONG POINTER TO CharSeq ~ LOOPHOLE[ssb]; len: CARD16 ~ ss[index]-0C; i: INT ฌ index.SUCC; GetFromNameString: SAFE PROC RETURNS [ char: CHAR ] = TRUSTED { char ฌ ss[i]; i ฌ i + 1 }; rope ฌ Rope.FromProc[len, GetFromNameString]; IF ( ( len > 0 ) AND ( rope.Fetch[len.PRED] = '. ) ) THEN rope ฌ rope.Substr[len: len.PRED]; -- ??? }; MobCase: PROC [ shortName: ROPE, mob: MobDefs.MobBase, ensureFrom: EnsureProc, enterDependency: DependencyProc ] ~ TRUSTED { DoOneFile: PROC [ fth: MobDefs.FTHandle, fti: MobDefs.FTIndex ] RETURNS [ BOOL ฌ FALSE ] = TRUSTED { file: ROPE ฌ RopeForNameRecord[mob, fth.name]; enterDependency[shortName, [mob[mob.version]], file, [mob[fth.version]]]; }; IF ( mob.nConfigs = 0 ) THEN [] ฌ ProcessFiles[mob, DoOneFile]; ensureFrom[shortName, [mob[mob.version]]]; }; ProcessFiles: PROC [ mob: MobDefs.MobBase, proc: PROC [MobDefs.FTHandle, MobDefs.FTIndex] RETURNS [BOOL] ] RETURNS [ fth: MobDefs.FTHandle, fti: MobDefs.FTIndex ] = TRUSTED { ftb: MobDefs.Base = LOOPHOLE[mob + mob.ftOffset.units]; FOR fti ฌ FIRST[MobDefs.FTIndex], fti + SIZE[MobDefs.FTRecord] UNTIL ( fti = mob.ftLimit ) DO fth ฌ @ftb[fti]; IF ( proc[fth, fti] ) THEN RETURN; ENDLOOP; RETURN[NIL, MobDefs.FTNull]; }; GetBaseRecord: INTERNAL PROC [ table: RedBlackTree.Table, file: ROPE, version: VersionStamp ] RETURNS [ base: BaseEntry ] = { old: BaseEntry ฌ NIL; WITH table.Lookup[file] SELECT FROM be: BaseEntry => WHILE ( be # NIL ) DO IF ( SameVersion[be.version, version] ) THEN { base ฌ be; RETURN }; old ฌ be; be ฌ be.next; ENDLOOP; ENDCASE; base ฌ NEW[BaseEntryRep ฌ [name: file, version: version]]; IF ( old # NIL ) THEN old.next ฌ base ELSE table.Insert[base, file]; }; SameVersion: PROC [ first: VersionStamp, second: VersionStamp ] RETURNS [ yup: BOOL ฌ FALSE ] ~ { IF ( first.style # second.style ) THEN RETURN; WITH first SELECT FROM left: mob VersionStamp => { WITH second SELECT FROM right: mob VersionStamp => { IF ( left # right ) THEN RETURN }; ENDCASE => { ERROR }; }; ENDCASE => { ERROR }; yup ฌ TRUE; }; GetKey: RedBlackTree.GetKey = { RETURN [data] }; Compare: RedBlackTree.Compare = { key: ROPE ฌ WITH k SELECT FROM base: BaseEntry => base.name, rope: ROPE => rope, ENDCASE => ERROR; WITH data SELECT FROM base: BaseEntry => { RETURN [key.Compare[base.name, FALSE]] }; ENDCASE => { ERROR }; }; CompareEntries: List.CompareProc = { WITH ref1 SELECT FROM base1: BaseEntry => { WITH ref2 SELECT FROM base2: BaseEntry => { RETURN [base1.name.Compare[base2.name, FALSE]] }; ENDCASE => { ERROR }; }; ENDCASE => { ERROR }; }; SortDependents: PROC [list: BaseEntryList] RETURNS [BaseEntryList] = TRUSTED { RETURN [LOOPHOLE[List.Sort[LOOPHOLE[list], CompareEntries]]]; }; CheckAbort: PROC [ref: REF] RETURNS [MyData] = { WITH ref SELECT FROM mine: MyData => { SELECT TRUE FROM mine.abortRequested => { NULL }; ENDCASE => RETURN [mine]; }; ENDCASE => { NULL }; ERROR ABORTED; }; DependenciesCommandProc: Commander.CommandProc = { out: IO.STREAM = cmd.out; switches: Switches ฌ ALL[FALSE]; ProcessSwitches: PROC [arg: ROPE] = { sense: BOOL ฌ TRUE; FOR index: INT IN [0..arg.Length[]) DO char: CHAR ฌ arg.Fetch[index]; SELECT char FROM '- => { LOOP }; '~ => { sense ฌ NOT sense; LOOP }; IN ['a..'z] => switches[char] ฌ sense; IN ['A..'Z] => switches[char + ('a-'A)] ฌ sense; ENDCASE; sense ฌ TRUE; ENDLOOP; }; ProcessArgument: PROC [arg: ROPE] = { table: RedBlackTree.Table ฌ RedBlackTree.Create[getKey: GetKey, compare: Compare]; myData: MyData ฌ NEW[MyDataRep ฌ [table: table, fsCareful: NOT switches['f], errs: out]]; dfName: ROPE ฌ arg.Concat[".df"]; outName: ROPE ฌ arg.Concat[".depends"]; Process.CheckForAbort[]; IO.PutF1[out, "Generating dependencies for %g\n", IO.rope[dfName] ]; IO.PutF1[out, " %g\n", IO.time[] ]; [] ฌ GenerateDFClosure.GenerateClosureToProc[dfName, out, EachFile, myData, [toFork: forkers, followImports: NOT switches['s] ] ! ABORTED, UNWIND => { myData.abortRequested ฌ TRUE } ]; IO.PutF1[out, "\nWriting dependencies to %g", IO.rope[outName] ]; DumpOutput[outName, table, switches]; IO.PutF1[out, ".\n\n%g\n", IO.time[] ]; }; argsProcessed: NAT ฌ 0; argv: CommanderOps.ArgumentVector ฌ CommanderOps.Parse[cmd: cmd ! CommanderOps.Failed => { msg ฌ errorMsg; GO TO failed } ]; ProcessSwitches[UserProfile.Token["Dependencies.DefaultSwitches"]]; FOR i: NAT IN [1..argv.argc) DO arg: ROPE ~ argv[i]; Process.CheckForAbort[]; IF ( arg.Length[] = 0 ) THEN LOOP; IF ( arg.Fetch[0] = '- ) THEN { ProcessSwitches[arg]; LOOP }; ProcessArgument[arg]; ENDLOOP; EXITS failed => { result ฌ $Failure }; }; doc: ROPE = "{switch | item}*\nGenerates object file dependency list -m: mobs (qualify entries with [MOB]) -s: shallow (don't follow imports) -u: show NIL dependents -v: show files not from DF files -f: use fs caching when reading files"; Commander.Register["Dependencies", DependenciesCommandProc, doc]; END. P Dependencies.mesa Copyright ำ 1985, 1987, 1989, 1990, 1991, 1992 by Xerox Corporation. All rights reserved. Russ Atkinson (RRA) March 12, 1987 10:54:55 am PST Bill Jackson (bj) October 29, 1988 4:19:42 pm PDT Willie-Sue, October 6, 1989 10:13:54 pm PDT Willie-s, January 23, 1992 2:15 pm PST # of times to retry a server glitch # of seconds to pause for a server glitch # of processes to fork for help 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] From file depends on the to file; version stamps are present to remove name ambiguity. context/continuation includes [name, from: ROPE, date: DFUtilities.Date] and [myData: MyData] Process this file for dependencies Ok, now we've got the bits in "file"; lets process the contents Mob Specific code context/continuation includes [shortName: ROPE, file: FS.OpenFile, mob: MobDefs.MobBase] RedBlackTree for BaseEntry (plus List-buckets) We can use List.Sort because BaseEntryList is a list of REFs, and List.Sort only plays with the rest fields, not the first fields. Utilities Now that we have our data structure, print it. ส‚•NewlineDelimiter –(cedarcode) style™codešœ™Kšœ ฯeœO™ZKšœฯkœž™2Kšœ1™1K™+K™&K˜šž ˜ Kšœ žœžœžœ˜.Kšœžœ^˜kKšœ žœ˜*Kšœ žœ#˜5Kšœžœ˜Kšœ žœ ˜Kšœ žœ&˜5Kšœžœ'˜>Kšžœ˜Kšœžœ ˜*Kšœ žœ-˜ -- [data: RedBlackTree.UserData] RETURNS [stop: BOOL _ FALSE]šŸœ˜#šžœžœž˜˜šžœ žœž˜K˜Nšžœžœž˜Kš œžœžœžœžœ˜>Kš œžœžœžœžœ˜=šžœ˜ K˜Kšœ˜šžœž˜Kšœ žœžœ˜5Kšžœžœฯc˜—Kšœ˜Kšœ˜Kš œžœžœžœžœ ˜?Kšœ˜šžœžœ˜Kšžœ˜šžœ˜Kšœ˜K˜šžœžœž˜Kšœ˜Kšœ ˜ K˜Kšžœ˜—Kšœ˜K˜——K˜——Kšžœžœžœžœ ˜AK˜Kšžœ˜—Kšœ˜—Kšžœžœ˜—K˜—K˜2Kšœžœ˜&Kšœ˜Kšœžœ˜Kšœ žœ˜Kšœžœ˜Kšœ˜Kšœ ˜ Kšœ žœƒ˜ŽKšœ ˜ Kšœžœ&˜0Kšœ ˜ Kšœ ˜ K˜Kšœ žœ˜Kšœ2˜2Kšœ'˜'K˜K˜—Kšœžœžœ ˜šœ žœžœ˜Kšœžœ˜ Kšœžœžœ˜Kšœ žœž˜Kšœž œž˜K˜—K˜Kšœ žœžœžœ˜BKš œžœžœžœ!žœ˜mK˜–g -- [data: REF ANY, kind: GenerateDFClosure.ActionKind, name: ROPE, date: DFUtilities.Date, from: ROPE]šŸœ"˜*K˜"K˜)šŸœžœ˜)KšœV™VKšžœžœžœ˜K˜@K˜K˜—šŸ œžœ˜ Kšžœžœžœ˜K˜Kšžœžœ˜—K˜K˜—–> -- [ref1: REF ANY, ref2: REF ANY] RETURNS [Basics.Comparison]šŸœ˜$šžœžœž˜šœ˜šžœžœž˜Kšœžœ!žœ˜GKšžœžœ˜—Kšœ˜—Kšžœžœ˜—K˜K˜—šŸœžœžœžœ˜NKšœ‚™‚K–:[list: LIST OF REF ANY, compareProc: List.CompareProc]šžœžœ žœ˜=K˜——šข ™ šŸ œžœžœžœ ˜0šžœžœž˜šœ˜šžœžœž˜Kšœžœ˜ Kšžœžœ˜—K˜—Kšžœžœ˜—Kšžœžœ˜K˜K˜——šŸœ˜2Kšœž œ ˜Kšœžœžœ˜ šŸœžœžœ˜%Kšœžœžœ˜šžœžœžœž˜&Kšœžœ˜šžœž˜Kšœžœ˜Kšœžœžœ˜"Kšžœ$˜&Kšžœ.˜0Kšžœ˜—Kšœžœ˜ Kšžœ˜—K˜—šŸœžœžœ˜%K˜RKšœžœ'žœ˜YKšœžœ˜!Kšœ žœ˜'K˜K˜Kšžœ0žœ˜DKšžœžœ ˜$K˜Kš œmžœžœžœžœ˜ธKšœ.™.Kšžœ,žœ˜AKšœ%˜%Kšžœžœ ˜'K˜K˜—Kšœžœ˜K–I[cmd: Commander.Handle, starExpand: BOOL _ FALSE, switchChar: CHAR]šœ?žœ+žœžœ ˜|KšœC˜Cšžœžœžœž˜Kšœžœ ˜K˜Kšžœžœžœ˜"Kšžœžœžœ˜=Kšœ˜Kšžœ˜—šž˜K˜ —K˜K˜—Kšœžœ๙˜‚K˜KšœA˜AKšžœ˜K˜——…—0fJ8