CompNeeds.Mesa, from CompNeeds.DF
Last Edited by: Spreitzer, November 25, 1983 4:45 pm
IF a module's source exists &
(
the module is needed & its BCD does not exist OR
its BCD exists & its source is later than its BCD)
THEN the module needs to be compiled
IF module A's source exists &
module A depends on module B &
(module B needs to be compiled OR
(module B's BCD exists &
module A's BCD exists &
module B's BCD is later than module A's BCD))
THEN
module A needs to be compiled
IF module A depends on module B &
module A needs to be compiled
THEN module B is needed
IF module A is mentioned on a command line
THEN module A is needed
DIRECTORY
Atom, BasicTime, Commander, CompDeps, FS, IO, IOClasses, Rope;
CompNeeds: CEDAR MONITOR
IMPORTS Atom, BasicTime, Commander, FS, IO, IOClasses, Rope
EXPORTS CompDeps =
BEGIN OPEN CompDeps;
SyntaxError: PUBLIC SIGNAL [sourceName: ROPE, index: INT] = CODE;
GotoSyntaxError: ERROR = CODE;
moduleDataKey: ATOM ← Atom.MakeAtom["Mike Spreitzer November 25, 1983 3:41 pm"];
lastFor: CARDINAL ← 0;
ListNeeds: --PROC [cmd: Handle] RETURNS [result: REF ← NIL, msg: Rope.ROPE ← NIL]--ENTRY Commander.CommandProc =
BEGIN ENABLE UNWIND => NULL;
in: IO.STREAMIO.RIS[cmd.commandLine];
out: IO.STREAM ← cmd.out;
lastFor ← lastFor + 1;
[] ← in.SkipWhitespace[];
WHILE NOT in.EndOf[] DO
moduleName: ROPE ← in.GetTokenRope[Break].token;
moduleData: ModuleData;
IF moduleName.Length[] < 1 THEN EXIT;
moduleData ← GetModuleData[moduleName];
AnalyzeWork[moduleData, lastFor !SyntaxError =>
BEGIN
err: IO.STREAM ← cmd.err;
err.PutF["Syntax error in %g before %g; analysis of %g terminated.\n",
IO.rope[sourceName],
IO.int[index],
IO.rope[sourceName]];
RESUME;
END];
PutModuleData[out, moduleData, 0];
FOR deps: ModuleDataList ← moduleData.dependsOn, deps.rest WHILE deps # NIL DO
dmd: ModuleData ← deps.first;
IF dmd # NIL THEN PutModuleData[out, dmd, 1];
ENDLOOP;
[] ← in.SkipWhitespace[];
ENDLOOP;
END;
PutModuleData: PROC [to: IO.STREAM, md: ModuleData, depth: CARDINAL] =
BEGIN
THROUGH [1 .. depth] DO to.PutChar['\t] ENDLOOP;
to.PutRope[md.name];
IF md.needed THEN to.PutChar['!];
IF md.sourceExists THEN to.PutF[" .%g @ %g",
IO.rope[SELECT md.sourceType FROM
mesa => "Mesa",
config => "Config",
ENDCASE => ERROR],
IO.time[BasicTime.Update[BasicTime.earliestGMT, md.sourceCreateTime]]];
IF md.bcdExists THEN to.PutF[" .BCD @ %g", IO.time[BasicTime.Update[BasicTime.earliestGMT, md.bcdCreateTime]]];
to.PutRope["\n"];
END;
CompileNeedy: --PROC [cmd: Handle] RETURNS [result: REF ← NIL, msg: Rope.ROPE ← NIL]--ENTRY Commander.CommandProc =
BEGIN ENABLE UNWIND => NULL;
ans: Command ← [];
in: IO.STREAMIO.RIS[cmd.commandLine];
lastFor ← lastFor + 1;
[] ← in.SkipWhitespace[];
WHILE NOT in.EndOf[] DO
moduleName: ROPE ← in.GetTokenRope[Break].token;
moduleData: ModuleData;
IF moduleName.Length[] < 1 THEN EXIT;
moduleData ← GetModuleData[moduleName];
AnalyzeWork[moduleData, lastFor !SyntaxError =>
BEGIN
err: IO.STREAM ← cmd.err;
err.PutF["Syntax error in %g before %g; analysis of %g terminated.\n",
IO.rope[sourceName],
IO.int[index],
IO.rope[sourceName]];
RESUME;
END];
ans ← CompileIfNecessary[moduleData: moduleData, soFar: ans, by: lastFor, needed: TRUE, childCompd: FALSE];
[] ← in.SkipWhitespace[];
ENDLOOP;
msg ← ans.rope;
END;
Command: TYPE = RECORD [
kind: ROPENIL,
rope: ROPENIL];
CompileIfNecessary: PROC [moduleData: ModuleData, soFar: Command, by: CARDINAL, needed, childCompd: BOOLEAN] RETURNS [meToo: Command] =
BEGIN
Decide: PROC = {
moduleData.willCompile ←
moduleData.sourceExists AND
(
moduleData.someWillCompile OR
(IF moduleData.bcdExists THEN MAX[moduleData.latestDescendantBcdCreateTime, moduleData.sourceCreateTime] > moduleData.bcdCreateTime ELSE moduleData.needed));
moduleData.someWillCompile ← moduleData.someWillCompile OR moduleData.willCompile};
DoChildren: PROC [noteSucker: BOOLEAN] =
BEGIN
FOR al: ModuleDataList ← moduleData.dependsOn, al.rest WHILE al # NIL DO
meToo ← CompileIfNecessary[al.first, meToo, by, moduleData.willCompile, FALSE];
IF noteSucker THEN al.first.suckers ← CONS[moduleData, al.first.suckers];
moduleData.someWillCompile ← moduleData.someWillCompile OR al.first.someWillCompile;
moduleData.latestDescendantBcdCreateTime ← MAX[moduleData.latestDescendantBcdCreateTime, al.first.latestDescendantBcdCreateTime];
Decide[];
ENDLOOP;
END;
firstTime, news: BOOLEANFALSE;
IF moduleData = NIL THEN ERROR;
meToo ← soFar;
IF news ← firstTime ← moduleData.by # by THEN {
moduleData.by ← by;
moduleData.needed ← needed;
moduleData.willCompile ← moduleData.someWillCompile ← FALSE;
moduleData.latestDescendantBcdCreateTime ← moduleData.bcdCreateTime}
ELSE IF news ← needed AND NOT moduleData.needed THEN moduleData.needed ← needed;
moduleData.someWillCompile ← moduleData.someWillCompile OR childCompd;
IF (NOT (news OR childCompd)) OR moduleData.willCompile THEN RETURN;
Decide[];
IF firstTime THEN DoChildren[firstTime];
IF moduleData.willCompile THEN
BEGIN
DoChildren[FALSE];
meToo ← Add[
meToo,
SELECT moduleData.sourceType FROM
mesa => "Compile",
config => "Bind",
ENDCASE => ERROR,
moduleData.name];
FOR pl: ModuleDataList ← moduleData.suckers, pl.rest WHILE pl # NIL DO
meToo ← CompileIfNecessary[pl.first, meToo, by, FALSE, TRUE];
ENDLOOP;
END;
END;
Add: PROC [cmd: Command, kind, subject: ROPE] RETURNS [new: Command] =
BEGIN
new ← cmd;
IF NOT kind.Equal[new.kind] THEN
BEGIN
IF new.rope.Length[] > 0 THEN new.rope ← new.rope.Concat["; "];
new.kind ← kind;
new.rope ← new.rope.Concat[kind];
END;
new.rope ← new.rope.Cat[" ", subject];
END;
Analyze: PUBLIC ENTRY PROC [moduleName: ROPE] =
BEGIN ENABLE UNWIND => NULL;
AnalyzeWork[GetModuleData[moduleName], NextFor[]];
END;
GetModuleData: PUBLIC PROC [moduleName: ROPE] RETURNS [md: ModuleData] = {
moduleAtom: ATOM ← Atom.MakeAtom[moduleName];
md ← NARROW[Atom.GetProp[atom: moduleAtom, prop: moduleDataKey]];
IF md = NIL THEN Atom.PutProp[atom: moduleAtom, prop: moduleDataKey, val: md ← NEW [ModuleDataRep ← [name: moduleName]]];
};
NextFor: PUBLIC PROC RETURNS [CARDINAL] = {RETURN [lastFor ← lastFor + 1]};
AnalyzeWork: PUBLIC PROC [moduleData: ModuleData, for: CARDINAL] =
BEGIN
Consume: PROC [moduleName: ROPE] =
BEGIN
moduleData.dependsOn ← CONS[GetModuleData[moduleName], moduleData.dependsOn];
END;
moduleName: ROPE ← moduleData.name;
mesaName: ROPE ← moduleName.Concat[".Mesa"];
configName: ROPE ← moduleName.Concat[".Config"];
sourceExists: BOOLEAN;
sourceCreateTime: LONG CARDINAL;
IF moduleData.for = for THEN RETURN;
moduleData.for ← for;
moduleData.suckers ← NIL;
[moduleData.bcdExists, moduleData.bcdCreateTime] ← GetTime[moduleName.Concat[".BCD"]];
moduleData.sourceType ← mesa;
[sourceExists, sourceCreateTime] ← GetTime[mesaName];
IF NOT sourceExists
THEN {
moduleData.sourceType ← config;
[sourceExists, sourceCreateTime] ← GetTime[configName];
};
IF sourceCreateTime # moduleData.sourceCreateTime THEN
BEGIN
moduleData.sourceCreateTime ← sourceCreateTime;
moduleData.dependsOn ← NIL;
IF moduleData.sourceExists ← sourceExists THEN
BEGIN
exists: BOOLEANSELECT moduleData.sourceType FROM
mesa => EnumerateMesaDependancies[sourceName: mesaName, consume: Consume],
config => EnumerateConfigDependancies[sourceName: configName, consume: Consume],
ENDCASE => ERROR;
IF NOT exists THEN ERROR;
END;
END;
FOR al: ModuleDataList ← moduleData.dependsOn, al.rest WHILE al # NIL DO
AnalyzeWork[al.first, for];
ENDLOOP;
END;
GetTime: PROC [fileName: ROPE] RETURNS [exists: BOOLEAN, sinceEpoch: LONG CARDINAL] = TRUSTED
BEGIN
t: BasicTime.GMT;
exists ← TRUE;
[created: t] ← FS.FileInfo[name: fileName !FS.Error => {exists ← FALSE; CONTINUE}];
sinceEpoch ← IF exists THEN BasicTime.Period[from: BasicTime.earliestGMT, to: t] ELSE 0;
END;
Break: IO.BreakProc = {RETURN [SELECT char FROM
IN ['a .. 'z], IN ['A .. 'Z], IN ['0 .. '9] => other,
IO.SP, IO.CR, IO.TAB, IO.LF, IO.FF => sepr,
ENDCASE => break]};
EnumerateConfigDependancies: PROC [sourceName: ROPE, consume: PROC [moduleName: ROPE]] RETURNS [exists: BOOLEAN] =
BEGIN
NextToken: PROC RETURNS [ROPE] = {RETURN [source.GetTokenRope[Break].token]};
ParseConfigDescription: PROC =
BEGIN
next: ROPE ← ParseCDirectory[];
next ← ParseCPacking[next];
next ← ParseConfiguration[next];
IF NOT next.Equal["."] THEN ERROR GotoSyntaxError;
END;
ParseConfiguration: PROC [first: ROPE] RETURNS [next: ROPE] =
BEGIN
IF NOT Letter[first.Fetch[0]] THEN ERROR GotoSyntaxError;
IF NOT (next ← NextToken[]).Equal[":"] THEN ERROR GotoSyntaxError;
IF NOT (next ← NextToken[]).Equal["CONFIGURATION"] THEN ERROR GotoSyntaxError;
next ← ParseConfigurationRemains[];
END;
ParseConfigurationRemains: PROC RETURNS [next: ROPE] =
BEGIN
next ← ParseCHeadRemains[];
IF NOT next.Equal["="] THEN ERROR GotoSyntaxError;
ParseCBody[];
next ← NextToken[];
END;
ParseCBody: PROC =
BEGIN
next: ROPE ← NextToken[];
curly: BOOLEAN;
IF next.Equal["BEGIN"] THEN curly ← FALSE
ELSE IF next.Equal["{"] THEN curly ← TRUE
ELSE ERROR GotoSyntaxError;
next ← NextToken[];
DO
next ← ParseCStatement[next];
WHILE next.Equal[";"] DO next ← NextToken[] ENDLOOP;
IF next.Equal[IF curly THEN "}" ELSE "END"] THEN EXIT;
ENDLOOP;
END;
ParseCStatement: PROC [first: ROPE] RETURNS [next: ROPE] =
BEGIN
next ← first;
IF next.Equal["["] THEN
BEGIN
next ← ParseItemList[FALSE];
IF NOT next.Equal["]"] THEN ERROR GotoSyntaxError;
next ← NextToken[];
IF NOT next.Equal["←"] THEN ERROR GotoSyntaxError;
next ← ParseCExpression[];
END
ELSE BEGIN
rhs: ROPE ← next;
IF (next ← NextToken[]).Equal[":"] THEN
BEGIN
IF (rhs ← NextToken[]).Equal["CONFIGURATION"] THEN {next ← ParseConfigurationRemains[]; RETURN};
next ← NextToken[];
END;
IF next.Equal["←"] THEN
BEGIN
next ← ParseCExpression[];
END
ELSE BEGIN
consume[rhs];
IF next.Equal["["] THEN
BEGIN
IF NOT (next ← NextToken[]).Equal["]"] THEN next ← EatIDList[next, FALSE];
IF NOT next.Equal["]"] THEN ERROR GotoSyntaxError;
next ← ParseCLinks[];
END;
END;
END;
END;
ParseCExpression: PROC RETURNS [next: ROPE] =
BEGIN
next ← ParseCRightSide[];
WHILE next.Equal["PLUS"] DO
next ← ParseCRightSide[];
ENDLOOP;
WHILE next.Equal["THEN"] DO
next ← ParseCRightSide[];
ENDLOOP;
END;
ParseCRightSide: PROC RETURNS [next: ROPE] =
BEGIN
next ← ParseItem[NIL, TRUE];
IF next.Equal["["] THEN
BEGIN
IF NOT (next ← NextToken[]).Equal["]"] THEN next ← EatIDList[next, FALSE];
IF NOT next.Equal["]"] THEN ERROR GotoSyntaxError;
next ← ParseCLinks[];
END;
END;
ParseCHeadRemains: PROC RETURNS [next: ROPE] =
BEGIN
next ← ParseCLinks[];
next ← ParseImports[next];
next ← ParseCExports[next];
next ← ParseControlClause[next];
END;
ParseCLinks: PROC RETURNS [next: ROPE] =
BEGIN
next ← NextToken[];
IF NOT next.Equal["LINKS"] THEN RETURN;
IF NOT (next ← NextToken[]).Equal[":"] THEN ERROR GotoSyntaxError;
next ← NextToken[];
IF NOT (next.Equal["CODE"] OR next.Equal["FRAME"]) THEN ERROR GotoSyntaxError;
next ← NextToken[];
END;
ParseImports: PROC [first: ROPE] RETURNS [next: ROPE] =
BEGIN
IF NOT first.Equal["IMPORTS"] THEN RETURN [first];
next ← ParseItemList[TRUE];
END;
ParseCExports: PROC [first: ROPE] RETURNS [next: ROPE] =
BEGIN
IF NOT first.Equal["EXPORTS"] THEN RETURN [first];
next ← ParseItemList[TRUE];
END;
ParseControlClause: PROC [first: ROPE] RETURNS [next: ROPE] =
BEGIN
IF NOT first.Equal["CONTROL"] THEN RETURN [first];
next ← EatIDList[NIL, TRUE];
END;
ParseItemList: PROC [notice: BOOLEAN] RETURNS [next: ROPE] =
BEGIN
DO
next ← ParseItem[NIL, notice];
IF NOT next.Equal[","] THEN EXIT;
ENDLOOP;
END;
ParseItem: PROC [first: ROPE, notice: BOOLEAN] RETURNS [next: ROPE] =
BEGIN
rhs: ROPEIF first = NIL THEN NextToken[] ELSE first;
IF (next ← NextToken[]).Equal[":"] THEN
BEGIN
rhs ← NextToken[];
next ← NextToken[];
END;
IF notice THEN consume[rhs];
END;
ParseCDirectory: PROC RETURNS [next: ROPE] =
BEGIN
ParseClause: PROC RETURNS [next: ROPE] =
BEGIN
consume[NextToken[]];
next ← NextToken[];
IF next.Equal[":"] THEN
BEGIN
IF NOT (next ← NextToken[]).Equal["TYPE"] THEN ERROR GotoSyntaxError;
next ← NextToken[];
IF Letter[next.Fetch[0]] THEN next ← NextToken[];
END;
END;
IF NOT (next ← NextToken[]).Equal["DIRECTORY"] THEN RETURN;
DO
IF (next ← ParseClause[]).Equal[";"] THEN EXIT;
IF NOT next.Equal[","] THEN ERROR GotoSyntaxError;
ENDLOOP;
next ← NextToken[];
END;
ParseCPacking: PROC [first: ROPE] RETURNS [next: ROPE] =
BEGIN
next ← first;
WHILE next.Equal["PACK"] DO
next ← EatIDList[NIL, FALSE];
IF NOT next.Equal[";"] THEN ERROR GotoSyntaxError;
next ← NextToken[];
ENDLOOP;
END;
EatIDList: PROC [first: ROPE, notice: BOOLEAN] RETURNS [next: ROPE] =
BEGIN
IF first = NIL THEN next ← NextToken[];
DO
IF NOT Letter[next.Fetch[0]] THEN ERROR GotoSyntaxError;
IF notice THEN consume[next];
next ← NextToken[];
IF NOT next.Equal[","] THEN EXIT;
next ← NextToken[];
ENDLOOP;
END;
source: IO.STREAMNIL;
source ← FS.StreamOpen[fileName: sourceName, accessOptions: $read !FS.Error => {source ← NIL; CONTINUE}];
IF exists ← (source # NIL) THEN NULL ELSE RETURN;
source ← IOClasses.CreateCommentFilterStream[source];
[] ← source.GetIndex[];
ParseConfigDescription[!GotoSyntaxError =>
BEGIN
SIGNAL SyntaxError[sourceName, source.GetIndex[]];
CONTINUE
END];
source.Close[];
END;
Letter: PROC [c: CHAR] RETURNS [letter: BOOLEAN] = INLINE
{letter ← (c IN ['a .. 'z]) OR (c IN ['A .. 'Z])};
log: LIST OF ROPENIL;
EnumerateMesaDependancies: PROC [sourceName: ROPE, consume: PROC [moduleName: ROPE]] RETURNS [exists: BOOLEAN] =
BEGIN
NextToken: PROC RETURNS [ROPE] =
{RETURN [(log ← CONS[source.GetTokenRope[Break].token, log]).first]};
ParseDirectory: PROC RETURNS [next: ROPE] =
BEGIN
ParseClause: PROC RETURNS [next: ROPE] =
BEGIN
consume[NextToken[]];
next ← NextToken[];
IF next.Equal[":"] THEN
BEGIN
IF NOT (next ← NextToken[]).Equal["TYPE"] THEN ERROR GotoSyntaxError;
next ← NextToken[];
IF Letter[next.Fetch[0]] AND NOT next.Equal["USING"] THEN next ← NextToken[];
END;
IF next.Equal["USING"] THEN
BEGIN
IF NOT (next ← NextToken[]).Equal["["] THEN ERROR GotoSyntaxError;
WHILE NOT (next ← NextToken[]).Equal["]"] DO NULL ENDLOOP;
next ← NextToken[];
END;
END;
IF (next ← NextToken[]).Equal["DIRECTORY"] THEN
BEGIN
DO
IF (next ← ParseClause[]).Equal[";"] THEN EXIT;
IF NOT next.Equal[","] THEN ERROR GotoSyntaxError;
ENDLOOP;
next ← NextToken[];
END;
IF NOT Letter[next.Fetch[0]] THEN ERROR GotoSyntaxError;
END;
source: IO.STREAMNIL;
source ← FS.StreamOpen[fileName: sourceName, accessOptions: $read !FS.Error => {source ← NIL; CONTINUE}];
IF exists ← (source # NIL) THEN NULL ELSE RETURN;
source ← IOClasses.CreateCommentFilterStream[source];
[] ← source.GetIndex[];
[] ← ParseDirectory[!GotoSyntaxError =>
BEGIN
SIGNAL SyntaxError[sourceName, source.GetIndex[]];
CONTINUE
END];
source.Close[];
END;
Commander.Register[key: "CompNeeds", proc: CompileNeedy, doc: "Trace dependencies and produce command to compile needy"];
Commander.Register[key: "ListNeeds", proc: ListNeeds, doc: "List dependencies"];
END.