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.STREAM ← IO.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.STREAM ← IO.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: ROPE ← NIL,
rope: ROPE ← NIL];
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: BOOLEAN ← FALSE;
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:
BOOLEAN ←
SELECT 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: ROPE ← IF 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.STREAM ← NIL;
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 ROPE ← NIL;
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.STREAM ← NIL;
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.