<> <> DIRECTORY Asserting, Basics, CedarProcess, Commander, CommandTool, DFDependencies, DFDependenciesPrivate, DFOperations, FileSets, FS, GraphOps, Graphs, HashTable, IO, MakeDo, RedBlackTree, Rope; DFDependenciesImpl: CEDAR PROGRAM IMPORTS CedarProcess, Commander, DFOperations, FileSets, FS, Graphs, GraphOps, HashTable, IO, MakeDo, RedBlackTree, Rope EXPORTS DFDependencies = {OPEN DFDependencies, DFDependenciesPrivate; NewDG: PROC RETURNS [dg: DependencyGraph] = { dgr: DependencyGraphRep = NEW [DependencyGraphRepPrivate _ [ verticesByBase: HashTable.Create[hash: HashTable.HashRopeModCase, equal: HashTable.RopeEqualModCase] ]]; dg _ [dgr, dependencyGraphClass]; }; dependencyGraphClass: Graphs.GraphClass = NEW [Graphs.GraphClassPrivate _ [ ChangeOther: ChangeGraphOther, EnumerateVertices: EnumerateVertices ]]; ChangeGraphOther: PROC [g: Graphs.Graph, Change: PROC [Asserting.Assertions] RETURNS [Asserting.Assertions]] = { dgr: DependencyGraphRep = NARROW[g.rep]; dgr.other _ Change[dgr.other]; }; EnumerateVertices: PROC [graph: Graph, Consume: PROC [Graphs.Vertex]] = { dgr: DependencyGraphRep = NARROW[graph.rep]; SeeVertex: PROC [key, value: REF ANY] RETURNS [quit: BOOL _ FALSE]--HashTable.EachPairAction-- = { dgv: DGVertex = NARROW[value]; Consume[[dgv, vClass]]; }; [] _ dgr.verticesByBase.Pairs[SeeVertex]; }; vClass: Graphs.VertexClass = NEW [Graphs.VertexClassPrivate _ [ ChangeOther: ChangeOther, Expand: Expand, GetLabel: GetLabel]]; GetLabel: PROC [vertex: Graphs.Vertex] RETURNS [label: ROPE] = { dgv: DGVertex = NARROW[vertex.rep]; label _ dgv.naming.base; }; ChangeOther: PROC [v: Graphs.Vertex, Change: PROC [Asserting.Assertions] RETURNS [Asserting.Assertions]] = { dgv: DGVertex = NARROW[v.rep]; dgv.other _ Change[dgv.other]; }; Expand: PROC [vertex: Graphs.Vertex, Consume: Graphs.EdgeConsumer, filter: Graphs.DirectionFilter _ Graphs.allDirections] = { dgv: DGVertex = NARROW[vertex.rep]; IF filter[Incoming] THEN { FOR vl: DependentList _ dgv.dependents, vl.rest WHILE vl # NIL DO Consume[Graphs.dullEdge, Incoming, [vl.first, vClass]]; ENDLOOP; }; IF filter[Outgoing] THEN { FOR vl: ProviderList _ dgv.providers, vl.rest WHILE vl # NIL DO Consume[Graphs.dullEdge, Outgoing, [vl.first, vClass]]; ENDLOOP; }; }; ForDependents: PROC [dgv: DGVertex, Consume: PROC [DGVertex]] = { FOR vl: DependentList _ dgv.dependents, vl.rest WHILE vl # NIL DO Consume[vl.first]; ENDLOOP; dgv _ dgv; }; ForProviders: PROC [dgv: DGVertex, Consume: PROC [DGVertex]] = { FOR vl: DependentList _ dgv.providers, vl.rest WHILE vl # NIL DO Consume[vl.first]; ENDLOOP; dgv _ dgv; }; Analyze: PUBLIC PROC [dfs: FileSet, filter: FileSets.Filter _ NIL] RETURNS [dg: DependencyGraph] = { PerDF: PROC [fn: FileSets.FileNote] --FileSets.FileConsumer-- = { naming: Naming = Canonize[fn.fsName]; v: DGVertex = GetVertex[dg, naming]; mentions: FileSet = FileSets.MentionedDFs[fn.fsName, [], [version: FALSE]]; SeeProvider: PROC [fn: FileSets.FileNote] --FileSets.FileConsumer-- = { IF filter = NIL OR filter.Eval[fn, filter.data] THEN { naming2: Naming = Canonize[fn.fsName]; w: DGVertex = GetVertex[dg, naming2]; Link[v, w]; }; }; mentions.EnumSet[SeeProvider]; }; dg _ NewDG[]; dfs.EnumSet[PerDF]; }; Canonize: PROC [given: ROPE, wDir: ROPE _ NIL] RETURNS [naming: Naming] = { cp: FS.ComponentPositions; [naming.long, cp] _ FS.ExpandName[given, wDir]; naming.long _ naming.long.Substr[len: cp.base.start + cp.base.length]; naming.base _ naming.long.Substr[start: cp.base.start]; }; GetVertex: PROC [dg: DependencyGraph, naming: Naming] RETURNS [dgv: DGVertex] = { dgr: DependencyGraphRep = NARROW[dg.rep]; dgv _ NARROW[dgr.verticesByBase.Fetch[naming.base].value]; IF dgv = NIL THEN { dgv _ NEW [DGVertexPrivate _ [naming]]; IF NOT dgr.verticesByBase.Insert[naming.base, dgv] THEN ERROR; }; }; Link: PROC [dependent, provider: DGVertex] = { dependent.providers _ CONS[provider, dependent.providers]; provider.dependents _ CONS[dependent, provider.dependents]; }; PrintToFile: PUBLIC PROC [fileName: ROPE, dg: DependencyGraph, wDir: ROPE _ NIL] = { out: IO.STREAM = FS.StreamOpen[fileName: fileName, accessOptions: create]; Print[out, dg, wDir]; out.Close[]; }; Print: PUBLIC PROC [to: IO.STREAM, dg: DependencyGraph, wDir: ROPE _ NIL] = { wDirLen: INT = wDir.Length[]; PerVertex: PROC [v: Graphs.Vertex] = { dgv: DGVertex = NARROW[v.rep]; PrintName[dgv]; to.PutRope[":"]; ForDependents[dgv, PrintDependent]; to.PutRope[";\n\n"]; }; PrintDependent: PROC [dgv: DGVertex] = { to.PutRope[" "]; PrintName[dgv]; }; PrintName: PROC [dgv: DGVertex] = { name: ROPE = dgv.naming.long; clip: BOOL = wDirLen > 0 AND name.Length[] > wDirLen AND wDir.Equal[name.Substr[len: wDirLen], FALSE]; to.PutRope[IF clip THEN name.Substr[start: wDirLen] ELSE name]; }; [] _ dg.class.EnumerateVertices[dg, PerVertex]; dg _ dg; }; ReadFromFile: PUBLIC PROC [fileName: ROPE, wDir: ROPE _ NIL] RETURNS [dg: DependencyGraph] = { in: IO.STREAM = FS.StreamOpen[fileName]; dg _ Read[in, wDir]; in.Close[]; }; Read: PUBLIC PROC [from: IO.STREAM, wDir: ROPE _ NIL] RETURNS [dg: DependencyGraph] = { dg _ NewDG[]; FOR i: INT _ from.SkipWhitespace[], from.SkipWhitespace[] WHILE NOT from.EndOf[] DO providerGiven: ROPE = from.GetTokenRope[Break].token; providerNaming: Naming = Canonize[providerGiven, wDir]; provider: DGVertex = GetVertex[dg, providerNaming]; toke: ROPE _ from.GetTokenRope[Break].token; IF NOT toke.Equal[":"] THEN ERROR; FOR toke _ from.GetTokenRope[Break].token, from.GetTokenRope[Break].token WHILE NOT toke.Equal[";"] DO dependentNaming: Naming = Canonize[toke, wDir]; dependent: DGVertex = GetVertex[dg, dependentNaming]; Link[dependent, provider]; ENDLOOP; dg _ dg; ENDLOOP; }; Break: PROC [char: CHAR] RETURNS [cc: IO.CharClass] --IO.BreakProc-- = { cc _ SELECT char FROM ':, '; => break, IN [IO.NUL .. IO.SP] => sepr, ENDCASE => other; }; Track: PUBLIC PROC [dg: DependencyGraph, from: ROPE, parent: Commander.Handle, boo: BringOverOp] RETURNS [failures: RopeList] = { dgr: DependencyGraphRep = NARROW[dg.rep]; root: DGVertex = NARROW[dgr.verticesByBase.Fetch[from].value]; byRankThenName: RedBlackTree.Table = RedBlackTree.Create[GetRNKey, CompareRNs]; Setup: PROC [dgv: DGVertex] = { IF byRankThenName.Lookup[dgv] # NIL THEN RETURN; byRankThenName.Insert[dgv, dgv]; dgv.avoid _ dgv.tried _ dgv.failed _ FALSE; ForDependents[dgv, Setup]; }; Avoid: PROC [dgv: DGVertex] = { dgv.avoid _ TRUE; ForDependents[dgv, Avoid]; }; FixIt: PROC [data: REF ANY] RETURNS [stop: BOOL _ FALSE] --RedBlackTree.EachNode-- = { dgv: DGVertex = NARROW[data]; IF NOT dgv.avoid THEN { success: BOOL = Update[dgv.naming.long, parent, boo]; dgv.avoid _ TRUE; dgv.tried _ TRUE; dgv.failed _ NOT success; IF dgv.failed THEN { failures _ CONS[dgv.naming.base, failures]; ForDependents[dgv, Avoid]; }; data _ data; }; }; IF NOT GraphOps.GraphRanked[dg, depDir] THEN GraphOps.RankGraph[dg, depDir]; ForDependents[root, Setup]; byRankThenName.EnumerateIncreasing[FixIt]; }; depDir: Graphs.Direction _ Outgoing; GetRNKey: PROC [data: REF ANY] RETURNS [dgv: DGVertex] --RedBlackTree.GetKey-- = { dgv _ NARROW[data]; }; CompareRNs: PROC [k, data: REF ANY] RETURNS [c: Basics.Comparison] --RedBlackTree.Compare-- = { k1: DGVertex = NARROW[k]; k2: DGVertex = NARROW[data]; r1: INT = GraphOps.VertexRank[[k1, vClass], depDir]; r2: INT = GraphOps.VertexRank[[k2, vClass], depDir]; c _ SELECT r1 FROM < r2 => less, = r2 => k1.naming.long.Compare[k2.naming.long, FALSE], > r2 => greater, ENDCASE => ERROR; }; Tracking: TYPE = REF TrackingPrivate; TrackingPrivate: TYPE = RECORD [ bogons: INT _ 0, op: {BringOver, SModel, VerifyDF} _ BringOver, myMsgs: IO.STREAM _ IO.ROS[] ]; UpdateCmd: PROC [cmd: Commander.Handle] RETURNS [result: REF ANY _ NIL, msg: ROPE _ NIL] --Commander.CommandProc-- = { in: IO.STREAM = IO.RIS[cmd.commandLine]; doBringOver: BOOL _ TRUE; FOR i: INT _ in.SkipWhitespace[], in.SkipWhitespace[] WHILE NOT in.EndOf[] DO given: ROPE = in.GetTokenRope[IO.IDProc].token; SELECT TRUE FROM given.Equal["-b", FALSE] => doBringOver _ FALSE; given.Equal["+b", FALSE] => doBringOver _ TRUE; ENDCASE => { success: BOOL = Update[given, cmd, [doBringOver, []]]; IF NOT success THEN RETURN [$Failure]; }; ENDLOOP; in.Close[]; }; Update: PUBLIC PROC [subject: ROPE, cmd: Commander.Handle, boo: BringOverOp] RETURNS [success: BOOL] = { naming: Naming = Canonize[subject]; log: IO.STREAM = cmd.out; dfName: ROPE = naming.long.Concat[".DF"]; dfShortName: ROPE = naming.base.Concat[".DF"]; t: Tracking = NEW [TrackingPrivate _ []]; ros: IO.STREAM _ IO.ROS[]; errors, warnings, bogons: INT _ 0; goals: MakeDo.RefTable = MakeDo.MakeRefTable[]; modifiable: MakeDo.ModifiabilitySpec = MakeDo.MakeRefTable[]; changes: BOOL _ FALSE; CedarProcess.CheckAbort[]; log.PutF["\n%l%g:%l ", [rope["lb"]], [rope[naming.base]], [rope["LB"]]]; IF boo.doBringOver THEN { log.PutRope[" . BringOver . "]; [errors: errors, warnings: warnings] _ DFOperations.BringOver[ dfFile: dfName, filter: boo.filter, action: checkAndEnter, interact: Interact, clientData: t, log: ros ]; IF errors#0 OR warnings#0 THEN { log.PutRope["\n"]; cmd.out.PutRope[ros.RopeFromROS[]]; RETURN [FALSE]; }; }; log.PutRope[" . MakeDo . "]; warnings _ 0; {ENABLE MakeDo.Warning => { log.PutRope[Rope.Cat["\n", message, "\n"]]; warnings _ warnings + 1; RESUME }; nSteps: INT; nonOKGoalList: MakeDo.NodeList _ NIL; MakeDo.AnalyzeDFFile[dfShortName, goals, modifiable, makeGoal, makeModifiable]; [nonOKGoalCount: errors, nonOKGoalList: nonOKGoalList, nSteps: nSteps] _ MakeDo.Ensure[goals, modifiable, cmd]; IF nonOKGoalList # NIL THEN { cmd.out.PutF["%lNot OK:", [rope["b"]]]; FOR nonOKGoalList _ nonOKGoalList, nonOKGoalList.rest WHILE nonOKGoalList # NIL DO cmd.out.PutRope[" "]; cmd.out.PutRope[nonOKGoalList.first.DescribeNode[]]; ENDLOOP; cmd.out.PutF["%l\n", [rope["B"]]]; }; changes _ nSteps # 0; }; IF errors#0 OR warnings#0 THEN RETURN [FALSE]; IF changes THEN { log.PutRope[" . SModel . "]; ros _ IO.ROS[]; t.myMsgs _ IO.ROS[]; t.op _ SModel; [errors: errors, warnings: warnings] _ DFOperations.SModel[ dfFile: dfName, interact: Interact, clientData: t, log: ros ]; IF errors#0 OR warnings#0 THEN { log.PutRope["\n"]; cmd.out.PutRope[ros.RopeFromROS[]]; RETURN [FALSE]; }; log.PutRope[" . VerifyDF . "]; ros _ IO.ROS[]; t.myMsgs _ IO.ROS[]; t.op _ VerifyDF; [errors: errors, warnings: warnings] _ DFOperations.Verify[ dfFile: dfName, interact: Interact, clientData: t, log: ros ]; IF errors#0 OR warnings-t.bogons#0 THEN { log.PutRope["\n"]; cmd.out.PutRope[ros.RopeFromROS[]]; RETURN [FALSE]; }; }; log.PutRope["\n"]; success _ TRUE; }; Interact: PROC [interaction: REF ANY, clientData: REF ANY] RETURNS [abort: BOOL _ FALSE, abortMessageForLog: ROPE _ NIL, response: REF ANY _ NIL] --DFOperations.InteractionProc-- = { t: Tracking = NARROW[clientData]; WITH interaction SELECT FROM x: REF DFOperations.InfoInteraction => { IF t.op = VerifyDF AND x.class = warning AND Rope.Match[pattern: "*Imports*without a Using*", object: x.message, case: FALSE] THEN t.bogons _ t.bogons + 1 ELSE SELECT x.class FROM info => NULL; warning, error, abort => t.myMsgs.PutF["%g\n", [rope[x.message]]]; ENDCASE; }; ENDCASE; }; Commander.Register["Update", UpdateCmd, "Updates a package's DF file by BringOver; MakeDo; SModel; VerifyDF"]; }.