DFDependenciesImpl:
CEDAR
PROGRAM
IMPORTS CedarProcess, Commander, DFOperations, FileSets, FS, HashTable, IO, MakeDo, RedBlackTree, Rope
EXPORTS DFDependencies
= {OPEN DFDependencies, DFDependenciesPrivate;
DependencyGraph: TYPE = REF DependencyGraphPrivate;
DependencyGraphPrivate: PUBLIC TYPE = DFDependenciesPrivate.DependencyGraphPrivate;
NewDG:
PROC
RETURNS [dg: DependencyGraph] = {
dg ←
NEW [DependencyGraphPrivate ← [
verticesByBase: HashTable.Create[hash: HashTable.HashRopeModCase, equal: HashTable.RopeEqualModCase]
]];
};
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: Vertex = 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: Vertex = GetVertex[dg, naming2];
Link[v, w];
};
};
mentions.EnumSet[SeeProvider];
};
dg ← NewDG[];
dfs.EnumSet[PerDF];
TopoSort[dg];
};
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 [v: Vertex] = {
v ← NARROW[dg.verticesByBase.Fetch[naming.base].value];
IF v =
NIL
THEN {
v ← NEW [VertexPrivate ← [naming]];
IF NOT dg.verticesByBase.Insert[naming.base, v] THEN ERROR;
};
};
Link:
PROC [dependent, provider: Vertex] = {
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 [key, value:
REF
ANY]
RETURNS [quit:
BOOLEAN ←
FALSE]
--HashTable.EachPairAction-- = {
v: Vertex = NARROW[value];
PrintName[v];
to.PutRope[":"];
ForDependents[v, PrintDependent];
to.PutRope[";\n\n"];
};
PrintDependent:
PROC [v: Vertex] = {
to.PutRope[" "];
PrintName[v];
};
PrintName:
PROC [v: Vertex] = {
name: ROPE = v.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.verticesByBase.Pairs[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: Vertex = 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: Vertex = GetVertex[dg, dependentNaming];
Link[dependent, provider];
ENDLOOP;
dg ← dg;
ENDLOOP;
TopoSort[dg];
};
Break:
PROC [char:
CHAR]
RETURNS [cc:
IO.CharClass]
--IO.BreakProc-- = {
cc ←
SELECT char
FROM
':, '; => break,
IN [IO.NUL .. IO.SP] => sepr,
ENDCASE => other;
};
TopoSort:
PROC [dg: DependencyGraph] = {
ClearRank: PROC [v: Vertex] = {v.rank ← notRanked};
EnsureRanked:
PROC [v: Vertex] = {
rank: INT ← 0;
RankProvider:
PROC [w: Vertex] = {
IF w.rank = ranking THEN ERROR--discovered cycle in graph--;
EnsureRanked[w];
rank ← MAX[rank, w.rank+1];
};
IF v.rank # notRanked THEN RETURN;
rank ← 0;
v.rank ← ranking;
ForProviders[v, RankProvider];
v.rank ← rank;
};
EnumVertices[dg, ClearRank];
EnumVertices[dg, EnsureRanked];
};
ranking: INT = LAST[INT];
Track:
PUBLIC
PROC [dg: DependencyGraph, from:
ROPE, parent: Commander.Handle, boo: BringOverOp]
RETURNS [failures: RopeList] = {
root: Vertex = NARROW[dg.verticesByBase.Fetch[from].value];
byRankThenName: RedBlackTree.Table = RedBlackTree.Create[GetRNKey, CompareRNs];
Setup:
PROC [v: Vertex] = {
IF byRankThenName.Lookup[v] # NIL THEN RETURN;
byRankThenName.Insert[v, v];
v.avoid ← v.tried ← v.failed ← FALSE;
ForDependents[v, Setup];
};
Avoid:
PROC [v: Vertex] = {
v.avoid ← TRUE;
ForDependents[v, Avoid];
};
FixIt:
PROC [data:
REF
ANY]
RETURNS [stop:
BOOL ←
FALSE]
--RedBlackTree.EachNode-- = {
v: Vertex = NARROW[data];
IF
NOT v.avoid
THEN {
success: BOOL = Update[v.naming.long, parent, boo];
v.avoid ← TRUE;
v.tried ← TRUE;
v.failed ← NOT success;
IF v.failed
THEN {
failures ← CONS[v.naming.base, failures];
ForDependents[v, Avoid];
};
data ← data;
};
};
ForDependents[root, Setup];
byRankThenName.EnumerateIncreasing[FixIt];
};
GetRNKey:
PROC [data:
REF
ANY]
RETURNS [v: Vertex]
--RedBlackTree.GetKey-- = {
v ← NARROW[data];
};
CompareRNs:
PROC [k, data:
REF
ANY]
RETURNS [c: Basics.Comparison]
--RedBlackTree.Compare-- = {
k1: Vertex = NARROW[k];
k2: Vertex = NARROW[data];
c ←
SELECT k1.rank
FROM
< k2.rank => less,
= k2.rank => k1.naming.long.Compare[k2.naming.long, FALSE],
> k2.rank => greater,
ENDCASE => ERROR;
};
EnumVertices:
PROC [dg: DependencyGraph,
Consume:
PROC [Vertex]] = {
SeeVertex:
PROC [key, value:
REF
ANY]
RETURNS [quit:
BOOL ←
FALSE]
--HashTable.EachPairAction-- = {
v: Vertex = NARROW[value];
Consume[v];
};
[] ← dg.verticesByBase.Pairs[SeeVertex];
};
ForDependents:
PROC [v: Vertex,
Do:
PROC [Vertex]] = {
v ← v;
FOR vl: DependentList ← v.dependents, vl.rest
WHILE vl #
NIL
DO
Do[vl.first];
ENDLOOP;
v ← v;
};
ForProviders:
PROC [v: Vertex,
Do:
PROC [Vertex]] = {
v ← v;
FOR vl: ProviderList ← v.providers, vl.rest
WHILE vl #
NIL
DO
Do[vl.first];
ENDLOOP;
v ← v;
};
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"];
}.