<> <> <> DIRECTORY BasicTime, BcdDefs, BcdOps, Commander, CommandTool, FileNames, FS, FSBackdoor, GenerateDFClosure, IO, List, MessagesOut, Process, RedBlackTree, Rope, TiogaAccess, UserProfile, VM; Dependencies: CEDAR MONITOR IMPORTS BcdOps, Commander, CommandTool, FileNames, FS, FSBackdoor, GenerateDFClosure, IO, List, MessagesOut, Process, RedBlackTree, Rope, TiogaAccess, UserProfile, VM = BEGIN GMT: TYPE = BasicTime.GMT; ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; suspectFrom: ROPE _ NIL; suspectTo: ROPE _ NIL; SuspectSeen: SIGNAL = CODE; MyData: TYPE = REF MyDataRep; MyDataRep: TYPE = RECORD [ table: RedBlackTree.Table _ NIL, abortRequested: BOOL _ FALSE, errs: STREAM _ NIL ]; BaseEntry: TYPE = REF BaseEntryRep; BaseEntryList: TYPE = LIST OF BaseEntry; BaseEntryRep: TYPE = RECORD [ name: ROPE, from: ROPE _ NIL, version: BcdDefs.VersionStamp, dependents: BaseEntryList _ NIL, next: BaseEntry _ NIL ]; triesForServerGlitch: NAT _ 8; <<# of times to retry a server glitch>> pauseForServerGlitch: NAT _ 15; <<# of seconds to pause for a server glitch>> forkers: NAT _ 1; <<# of processes to fork for help>> DependenciesCommandProc: Commander.CommandProc = { <<[cmd: Handle] RETURNS [result: REF _ NIL, msg: ROPE _ NIL]>> <> out: STREAM = cmd.out; switches: PACKED ARRAY CHAR['a..'z] OF BOOL _ ALL[FALSE]; ProcessSwitches: PROC [arg: ROPE] = { sense: BOOL _ TRUE; FOR index: INT IN [0..Rope.Length[arg]) DO char: CHAR _ Rope.Fetch[arg, 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, errs: out]]; dfName: ROPE _ Rope.Concat[arg, ".df"]; outName: ROPE _ Rope.Concat[arg, ".depends"]; Process.CheckForAbort[]; [] _ GenerateDFClosure.GenerateClosureToProc[dfName, out, EachFile, myData, [toFork: forkers, followImports: NOT switches['s]] ! ABORTED, UNWIND => myData.abortRequested _ TRUE]; <> <> <<[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: FALSE, endOfNode: TRUE, deltaLevel: 1, propList: List.PutAssoc[key: $FromTiogaFile, val: $Yes, aList: NIL] ]; PutRope: PROC [rope: ROPE] = { [] _ Rope.Map[base: rope, action: PutCharB]; }; PutRopeBold: PROC [rope: ROPE] = { tc.looks['b] _ TRUE; [] _ Rope.Map[base: rope, action: PutCharB]; tc.looks['b] _ FALSE; }; PutRopeItalic: PROC [rope: ROPE] = { tc.looks['i] _ TRUE; [] _ Rope.Map[base: rope, action: PutCharB]; tc.looks['i] _ FALSE; }; PutCharB: Rope.ActionType = { <<[c: CHAR] RETURNS [quit: BOOL _ FALSE]>> tc.char _ c; TiogaAccess.Put[writer, tc]; }; EndNode: PROC [delta: INTEGER _ 0, format: ATOM _ NIL] = { tc.endOfNode _ TRUE; tc.char _ '\n; tc.format _ format; tc.deltaLevel _ delta; TiogaAccess.Put[writer, tc]; tc.endOfNode _ FALSE; }; eachNode: RedBlackTree.EachNode = { <<[data: RedBlackTree.UserData] RETURNS [stop: BOOL _ FALSE]>> 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] => {}; base.from = NIL AND NOT switches['v] => {}; ENDCASE => { tc.format _ $block; PutRopeBold[base.name]; PutRope[": "]; PutRopeItalic["("]; IF base.from = NIL THEN PutRopeItalic["??"] ELSE PutRopeItalic[base.from]; PutRopeItalic[")"]; EndNode[1, $block]; tc.format _ $block; WHILE dependents # NIL DO PutRope[dependents.first.name]; PutRope[" "]; dependents _ dependents.rest; ENDLOOP; EndNode[-1, $indent]; }; IF Rope.Match[suspectTo, base.name, FALSE] THEN SIGNAL SuspectSeen; base _ base.next; ENDLOOP; ENDCASE; }; writer: TiogaAccess.Writer _ TiogaAccess.Create[]; TiogaAccess.Put[writer, tc]; tc.comment _ TRUE; tc.endOfNode _ FALSE; tc.propList _ NIL; PutRope[outName]; EndNode[]; EndNode[]; tc.comment _ FALSE; IO.PutF1[out, "Writing dependencies to %g", [rope[outName]] ]; RedBlackTree.EnumerateIncreasing[table, eachNode]; TiogaAccess.WriteFile[writer, outName]; IO.PutRope[out, ".\n"]; }; }; argsProcessed: NAT _ 0; <<# of arguments processed>> argv: CommandTool.ArgumentVector _ CommandTool.Parse[cmd: cmd, starExpand: FALSE ! CommandTool.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 Rope.Length[arg] = 0 THEN LOOP; <> IF Rope.Fetch[arg, 0] = '- THEN { <> ProcessSwitches[arg]; LOOP; }; ProcessArgument[arg]; <> ENDLOOP; EXITS failed => {result _ $Failure}; }; EachFile: GenerateDFClosure.ActionProc = { <<[data: REF ANY, kind: GenerateDFClosure.ActionKind, name: ROPE, date: DFUtilities.Date, from: ROPE]>> myData: MyData _ CheckAbort[data]; table: RedBlackTree.Table _ myData.table; EnterDependency: ENTRY PROC [bcdName: ROPE, version: BcdDefs.VersionStamp, to: ROPE, toVersion: BcdDefs.VersionStamp] = { <> ENABLE UNWIND => NULL; baseFrom: BaseEntry _ GetBaseRecord[bcdName, version]; baseTo: BaseEntry _ GetBaseRecord[to, toVersion]; baseTo.dependents _ CONS[baseFrom, baseTo.dependents]; baseFrom.from _ FileNames.StripVersionNumber[from]; IF Rope.Match[suspectFrom, bcdName, FALSE] THEN SIGNAL SuspectSeen; IF Rope.Match[suspectTo, to, FALSE] THEN SIGNAL SuspectSeen; }; EnsureFrom: ENTRY PROC [bcdName: ROPE, version: BcdDefs.VersionStamp] = { ENABLE UNWIND => NULL; base: BaseEntry _ GetBaseRecord[bcdName, version]; IF base.from = NIL THEN base.from _ FileNames.StripVersionNumber[from]; }; GetBaseRecord: INTERNAL PROC [file: ROPE, version: BcdDefs.VersionStamp] RETURNS [base: BaseEntry] = { old: BaseEntry _ NIL; WITH RedBlackTree.Lookup[table, file] SELECT FROM be: BaseEntry => { WHILE be # NIL DO IF be.version = version THEN RETURN [be]; old _ be; be _ be.next; ENDLOOP; }; ENDCASE; base _ NEW[BaseEntryRep _ [name: file, version: version]]; IF old = NIL THEN RedBlackTree.Insert[table, base, file] ELSE old.next _ base; }; SELECT kind FROM file => { triesLeft: NAT _ triesForServerGlitch; shortName: ROPE _ FileNames.GetShortName[name]; IF Rope.Match["*.bcd", shortName, FALSE] THEN { shortName _ Rope.Flatten[shortName, 0, Rope.Length[shortName]-4]; DO ENABLE FS.Error => { SELECT error.code FROM $serverInaccessible => { MessagesOut.PutRopes[myData.errs, "Server glitch: ", name]; Process.Pause[Process.SecondsToTicks[pauseForServerGlitch]]; IF (triesLeft _ triesLeft - 1) # 0 THEN LOOP; }; ENDCASE; MessagesOut.PutRopes[ myData.errs, "File not found: ", name, IO.PutFR["\n from: %g\n reason: %g\n", [rope[from]], [rope[error.explanation]]]]; IF error.group # bug THEN EXIT ELSE REJECT; }; <> file: FS.OpenFile; tempName: ROPE _ name; useTemp: BOOL _ NOT IsInFileCache[name, date.gmt]; IF useTemp THEN tempName _ FS.Copy[ from: name, to: "///Temp/Dependencies.temp$", setKeep: TRUE, keep: 20, wantedCreatedTime: date.gmt, remoteCheck: FALSE ]; file _ FS.Open[name: tempName, wantedCreatedTime: date.gmt, remoteCheck: FALSE]; TRUSTED { pages: INT _ FS.GetInfo[file].pages; interval: VM.Interval _ VM.SimpleAllocate[pages]; bcd: BcdDefs.BcdBase _ VM.AddressForPageNumber[interval.page]; RopeForNameRecord: PROC [bcd: BcdDefs.BcdBase, name: BcdDefs.NameRecord] RETURNS [r: ROPE] = TRUSTED { ssb: BcdDefs.NameString = LOOPHOLE[bcd + bcd.ssOffset]; len: NAT; i: INT _ name; GetFromNameString: SAFE PROC RETURNS [char: CHAR] = TRUSTED { char _ ssb.string[i]; i _ i + 1}; r _ Rope.FromProc[ssb.size[name], GetFromNameString]; len _ r.Length[]; IF len > 0 AND r.Fetch[len-1] = '. THEN r _ r.Substr[len: len-1]; }; DoOneFile: PROC [fth: BcdDefs.FTHandle, fti: BcdDefs.FTIndex] RETURNS [BOOL _ FALSE] = TRUSTED { file: ROPE _ RopeForNameRecord[bcd, fth.name]; EnterDependency[shortName, bcd.version, file, fth.version]; }; FS.Read[file: file, from: 0, nPages: pages, to: bcd]; <> IF bcd.nConfigs = 0 THEN [] _ BcdOps.ProcessFiles[bcd, DoOneFile]; EnsureFrom[shortName, bcd.version]; VM.Free[interval]; FS.Close[file]; IF useTemp THEN FS.Delete[tempName]; }; EXIT; ENDLOOP; }; }; ENDCASE; }; IsInFileCache: PUBLIC PROC [name: ROPE, gmt: GMT] RETURNS [inCache: BOOL _ FALSE] = { cacheChecker: FSBackdoor.InfoProc = { <<[fullGName: ROPE, created: BasicTime.GMT, bytes: INT, keep: CARDINAL]>> <> IF bytes > 0 THEN { IF gmt # BasicTime.nullGMT THEN { <> IF created # gmt THEN RETURN [TRUE]; }; <> inCache _ TRUE; RETURN [FALSE]; }; RETURN [TRUE]; }; FSBackdoor.EnumerateCacheForInfo[cacheChecker, NIL, name]; }; 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 base: BaseEntry => key _ base.name; rope: ROPE => key _ rope; ENDCASE => ERROR; WITH data SELECT FROM base: BaseEntry => RETURN [Rope.Compare[key, base.name, FALSE]]; ENDCASE; ERROR; }; CompareEntries: List.CompareProc = { <<[ref1: REF ANY, ref2: REF ANY] RETURNS [Basics.Comparison]>> WITH ref1 SELECT FROM base1: BaseEntry => WITH ref2 SELECT FROM base2: BaseEntry => RETURN [Rope.Compare[base1.name, base2.name, FALSE]]; ENDCASE; 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 => {}; ENDCASE => RETURN [mine]; }; ENDCASE; ERROR ABORTED; }; doc: ROPE = "{switch | item}*\nGenerates object file dependency list -s: shallow (don't follow imports) -u: show NIL dependents -v: show files not from DF files"; Commander.Register[ key: "Dependencies", proc: DependenciesCommandProc, doc: doc, clientData: NIL, interpreted: TRUE ]; END. <<>>