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 DO IF (next _ ParseClause[]).Equal[";"] THEN EXIT; IF NOT next.Equal[","] THEN ERROR GotoSyntaxError; ENDLOOP; next _ NextToken[]; 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. †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 Κ1– "cedar" style˜J™!J™4Icode˜™™K™0K™2—K™$—K™™™™!K™K™K™-——™K™——K™™!K™K™—K™™*K™—K˜šΟk ˜ Kšœ&œœ˜>—K˜šΠbx œœ˜Kšœœœ˜;Kšœ ˜—K˜Kšœœ ˜K˜Kš Πbl œœœœ œœ˜AK˜KšŸœœœ˜K˜Kšœœ=˜PK˜Kšœ œ˜K˜šΟn œΟcHœ˜pKšœœœœ˜Kš œœœœœ˜(Kšœœœ ˜K˜K˜šœœ ˜Kšœ œ ˜0Kšœ˜Kšœœœ˜%Kšœ'˜'šœ/˜/Kš˜Kšœœœ ˜šœF˜FKšœ˜Kšœ ˜Kšœ˜—Kšœ˜Kšœ˜—Kšœ"˜"šœ8œœ˜NKšœ˜Kšœœœ˜-Kšœ˜—K˜Kšœ˜—Kšœ˜—K˜š   œœœœœ˜FKš˜Kšœœœ˜0K˜Kšœ œ˜!šœœ˜-šœœ˜!Kšœ˜Kšœ˜Kšœœ˜—KšœE˜G—KšœœœB˜pK˜Kšœ˜—K˜š  œ‘Hœ˜sKšœœœœ˜Kšœ˜Kš œœœœœ˜(K˜K˜šœœ ˜Kšœ œ ˜0Kšœ˜Kšœœœ˜%Kšœ'˜'šœ/˜/Kš˜Kšœœœ ˜šœF˜FKšœ˜Kšœ ˜Kšœ˜—Kšœ˜Kšœ˜—KšœRœœ˜kK˜Kšœ˜—K˜Kšœ˜—K˜šœ œœ˜Kšœœœ˜Kšœœœ˜—K˜š  œœ.œœœ˜‡Kš˜š œœ˜šœ˜Kšœ˜šœ˜Kšœ˜Kš œœœœcœ˜——Kšœ8œ˜S—š  œœœ˜(Kš˜šœ4œœ˜HKšœHœ˜OKšœ œœ˜IKšœ8œ˜TKšœ+œS˜K˜ Kšœ˜—Kšœ˜—Kšœœœ˜!Kšœœœœ˜K˜šœ'œ˜/Kšœ˜Kšœ˜Kšœ6œ˜