<<>> <> <> <> <> <> <> <> <> <> <> <> <<>> DIRECTORY Atom, Basics USING [IsBound], BasicTime USING [GMT, Period, ToNSTime], FileNames USING [CurrentWorkingDirectory], FS USING [ComponentPositions, Error, ExpandName, StreamOpen], IO, MakeDo USING [Action, ActionClass, ActionClassRep, AddFinder, fileClass, From, GetNode, GetProp, InnerGetCreated, Node, NodeList, notExistTime, PublicPartsOfAction, PublicPartsOfNode, SetProp, Time, Warning], MakeDoParsers, MakeDoPrivate USING [MDInstall], MakeDoPorting USING [logPath], MobDefs USING [Base, FTHandle, FTIndex, FTNull, FTRecord, FTSelf, MobBase, NameRecord, NameString, NullVersion, VersionID, VersionStamp], Mobery USING [FlushCache, StampAndNameFromFile], MobListerUtils USING [FreeMob, MobErr, PrintVersion, ReadMob], RefTab, Rope, SimpleFeedback, UserProfile USING [CallWhenProfileChanges, ProfileChangedProc, Boolean]; MimosaAndCinderDeps: CEDAR MONITOR IMPORTS Atom, Basics, BasicTime, FileNames, FS, IO, MakeDo, MakeDoParsers, MakeDoPrivate, MakeDoPorting, Mobery, MobListerUtils, RefTab, Rope, SimpleFeedback, UserProfile = <> <> BEGIN OPEN MakeDo, MDPs:MakeDoParsers; ShouldNotHappen: ERROR = CODE; ROPE: TYPE = Rope.ROPE; CachedMobData: TYPE = REF CachedMobDataRep; CachedMobDataRep: TYPE = RECORD [ created: BasicTime.GMT, <> stamp: MobDefs.VersionStamp, <> sourceStamp: MobDefs.VersionStamp, <> dependList: RefTab.Ref <> ]; SourceData: TYPE = REF SourceDataRep; SourceDataRep: TYPE = RECORD [ mobName, cName, shortName: ROPE, sourceType: SourceType ¬ Unknown, resultType: ResultType ¬ Unknown, supports: RefTab.Ref, mesaNode, cedarNode, configNode, sourceNode, switchesNode, mobNode, cNode: Node, sourceStamp, mobStamp: MobDefs.VersionStamp ¬ MobDefs.NullVersion, <> <> mobCreateTime, cCreateTime: BasicTime.GMT, mobReadable, cReadable: BOOL ¬ FALSE, supportsInvalid: BOOL ¬ FALSE, cmd: ROPE ¬ NIL ]; SourceType: TYPE = MDPs.SourceType[Unknown .. Config]; ResultType: TYPE = MDPs.ResultType[Unknown .. MobOnly]; Support: TYPE = REF SupportRep; SupportRep: TYPE = RECORD [ node: Node, version: MobDefs.VersionStamp]; <> installDir: ROPE ~ FileNames.CurrentWorkingDirectory[]; installed, badInstall: BOOL ¬ FALSE; dolog: BOOL ¬ FALSE; myRouterName: ATOM ~ Atom.MakeAtom["MakeDo.MimosaAndCinderDeps"]; <> cinderDefaultSwitch: ROPE ¬ " -m~g "; -- used to be /l - bj mimosaDefaultSwitch: ROPE ¬ " -kc "; cTail: ROPE = ".c2c.c"; cTailLength: CARDINAL = cTail.Length[]; mobExt: ROPE = "mob"; mobTail: ROPE = Rope.Concat[".", mobExt]; mesaTail: ROPE = ".mesa"; cedarTail: ROPE = ".cedar"; configTail: ROPE = ".config"; switchesTail: ROPE = ".switches"; MimosaAndCinderClass: ActionClass ¬ NEW [ActionClassRep ¬ [ CheckConsistency: CheckConsistency, Rederive: RederiveSource, EnumHiddenDeps: EnumHiddenDeps, ClearCaches: ClearCaches ]]; ClearCaches: PROC [ac: ActionClass] ~ { Mobery.FlushCache[]; MDPs.FlushCache[]; installed ¬ badInstall ¬ FALSE; }; SourceFind: PROC [resultName: ROPE, finderData: REF ANY] RETURNS [found: BOOLEAN, sought: Node, makes, cmdFrom: NodeList, from: From, cmd: ROPE, class: ActionClass, foundData: REF ANY] -- FinderProc -- ~ { <> <> mobExpanded, baseName, shortName, mobName, cName: ROPE; mobCP: FS.ComponentPositions; mobNode, cNode: Node; isMob, isC2C: BOOL ¬ FALSE; md: SourceData; found ¬ TRUE; IF badInstall THEN {found ¬ FALSE; RETURN}; [mobExpanded, mobCP, ] ¬ FS.ExpandName[resultName !FS.Error => {found ¬ FALSE; CONTINUE}]; IF NOT found THEN RETURN; isMob ¬ mobExpanded.EqualSubstrs[start1: mobCP.ext.start, len1: mobCP.ext.length, s2: mobExt, case: FALSE]; IF mobCP.ext.start + mobCP.ext.length >= cTailLength THEN { isC2C ¬ mobExpanded.EqualSubstrs[start1: mobCP.ext.start+mobCP.ext.length-cTailLength, len1: cTailLength, s2: cTail, case: FALSE]; } ELSE { isC2C ¬ FALSE; }; IF NOT (found ¬ isMob OR isC2C) THEN RETURN; <> <<>> IF NOT installed THEN InstallMe[]; found ¬ NOT badInstall; IF NOT found THEN RETURN; baseName ¬ mobExpanded.Substr[start: 0, len: mobCP.base.start + mobCP.base.length]; shortName ¬ mobExpanded.Substr[start: mobCP.base.start, len: mobCP.base.length]; IF isC2C THEN { <> baseName ¬ Rope.Substr[baseName, 0, baseName.Length[]-(cTailLength-2)]; shortName ¬ Rope.Substr[shortName, 0, shortName.Length[]-(cTailLength-2)]; }; mobNode ¬ GetNode[mobName ¬ Rope.Concat[baseName, mobTail], fileClass]; cNode ¬ GetNode[cName ¬ Rope.Concat[baseName, cTail], fileClass]; foundData ¬ md ¬ NEW [SourceDataRep ¬ [ mobName: mobName, cName: cName, shortName: shortName, supports: RefTab.Create[], mesaNode: GetNode[baseName.Concat[mesaTail], fileClass], cedarNode: GetNode[baseName.Concat[cedarTail], fileClass], configNode: GetNode[baseName.Concat[configTail], fileClass], cNode: cNode, sourceNode: NIL, switchesNode: GetNode[mobName.Concat[switchesTail], fileClass], mobNode: mobNode, mobCreateTime: MakeDo.notExistTime, cCreateTime: MakeDo.notExistTime ]]; cmdFrom ¬ LIST[md.cedarNode, md.mesaNode, md.configNode, md.switchesNode]; sought ¬ IF isC2C THEN md.cNode ELSE md.mobNode; class ¬ MimosaAndCinderClass; [from, cmd] ¬ RederiveWork[md]; makes ¬ LIST[md.cNode, md.mobNode]; <> <> RETURN}; GetSwitches: PROC [mobName: ROPE, default: ROPE ¬ NIL] RETURNS [switches: ROPE] = BEGIN ss: IO.STREAM ¬ NIL; ss ¬ FS.StreamOpen[Rope.Concat[mobName, switchesTail] !FS.Error => CONTINUE]; IF ss = NIL THEN RETURN [default]; [] ¬ ss.SkipWhitespace[]; IF ss.EndOf[] THEN { ss.Close[]; RETURN [default] } ; switches ¬ ss.GetTokenRope[IO.IDProc].token; ss.Close[]; END; SupportIsLoaded: PROC [] RETURNS [BOOL] ~ INLINE { moberies: BOOL; moblisteries: BOOL; moberies ¬ Basics.IsBound[LOOPHOLE[Mobery.StampAndNameFromFile]]; moblisteries ¬ Basics.IsBound[LOOPHOLE[MobListerUtils.PrintVersion]] AND Basics.IsBound[LOOPHOLE[MobListerUtils.ReadMob]]; <> RETURN [moberies AND moblisteries]; }; InstallMe: PROC ~ { foundFile, failed: BOOL; IF SupportIsLoaded[] THEN { installed ¬ TRUE; RETURN; }; [foundFile, failed] ¬ MakeDoPrivate.MDInstall[installDir, "MimosaAndCinderDepsSupport"]; badInstall ¬ failed OR NOT foundFile; installed ¬ TRUE; IF NOT foundFile THEN Warning[Rope.Cat["Couldn't install MimosaAndCinder support because couldn't find ", installDir, "MimosaAndCinderDepsSupport.Install (see ", MakeDoPorting.logPath, "MimosaAndCinderDepsSupport.InstallLog); proceeding without knowledge of MimosaAndCinder"]] ELSE IF failed THEN Warning[Rope.Cat["Couldn't install MimosaAndCinder support because of error in install file (", installDir, "MimosaAndCinderDepsSupport.Install) (see ", MakeDoPorting.logPath, "MimosaAndCinderDepsSupport.InstallLog); proceeding without knowledge of MimosaAndCinder"]]; RETURN}; CheckConsistency: PROC [a: Action, result: Node] RETURNS [consistent: BOOL, reason: ROPE] --ConsistencyChecker-- ~ { <> md: SourceData = NARROW[a.PublicPartsOfAction[].foundData]; switchesCreate: BasicTime.GMT = InnerGetCreated[md.switchesNode]; resultName: ROPE ~ result.PublicPartsOfNode[].name; resultCreateTime: Time = InnerGetCreated[result]; resultExists: BOOL ~ resultCreateTime # MakeDo.notExistTime; curMobStamp: MobDefs.VersionStamp; -- Stamp from the current Mob file of the action -- supportFound: BOOL ¬ FALSE; seekMob: BOOL ~ result = md.mobNode; seekC: BOOL ~ result = md.cNode; CheckSupport: PROC [key, val: REF ANY] RETURNS [stop: BOOL ¬ FALSE] --RefTab.EachPairAction-- ~ { <> <> <> s: Support = NARROW[val]; thisTime: Time = InnerGetCreated[s.node]; thisMobStamp: MobDefs.VersionStamp = CurMobStamp[s.node]; thisName: Rope.ROPE ~ s.node.PublicPartsOfNode[].name; extension, fullName: Rope.ROPE; cp: FS.ComponentPositions; [fullFName: fullName, cp: cp] ¬ FS.ExpandName[thisName ! FS.Error => { ERROR ShouldNotHappen}]; extension ¬ Rope.Substr[fullName, cp.ext.start, cp.ext.length]; SELECT TRUE FROM Rope.Equal[s1: extension, s2: mobExt, case: FALSE] => { <> IF thisMobStamp = MobDefs.NullVersion THEN RETURN [FALSE]; supportFound ¬ TRUE; IF NOT resultExists THEN RETURN [supportFound]; IF thisMobStamp # s.version THEN { out: IO.STREAM ¬ IO.ROS[]; out.PutRope["last used version "]; TRUSTED {MobListerUtils.PrintVersion[s.version, out, FALSE]}; out.PutF1[ " of %g, but current version is ", [rope[thisName]] ]; TRUSTED {MobListerUtils.PrintVersion[thisMobStamp, out, FALSE]}; consistent ¬ FALSE; reason ¬ out.RopeFromROS[]; RETURN [TRUE]; } ELSE RETURN [NOT resultExists]; }; ENDCASE => { <> IF thisTime = MakeDo.notExistTime THEN RETURN [FALSE]; supportFound ¬ TRUE; IF NOT resultExists THEN RETURN [supportFound]; IF BasicTime.Period[thisTime, resultCreateTime] < 0 THEN { out: IO.STREAM ¬ IO.ROS[]; out.PutRope["File created in "]; TRUSTED {MobListerUtils.PrintVersion[MobStampFromTime[resultCreateTime], out, TRUE]}; out.PutF1[ " but %g was created in ", [rope[thisName]] ]; TRUSTED {MobListerUtils.PrintVersion[MobStampFromTime[thisTime], out, TRUE]}; consistent ¬ FALSE; reason ¬ out.RopeFromROS[]; RETURN [TRUE]; } ELSE RETURN [NOT resultExists]; }; }; UpdateSupport[md]; SELECT md.sourceType FROM Mesa, Config => NULL; Unknown => RETURN [TRUE, "No source is known for this result"]; ENDCASE => ERROR ShouldNotHappen; IF md.resultType = MobOnly AND seekC THEN IF md.cCreateTime = MakeDo.notExistTime THEN RETURN [TRUE, "no amount of recompilation will derive a .c2c.c file from a .mesa interface"] ELSE RETURN [FALSE, "C2C file should not exist for a .mesa interface"]; SELECT TRUE FROM seekC => IF resultExists AND NOT md.cReadable THEN RETURN [FALSE, "c not readable"]; seekMob => IF resultExists AND NOT md.mobReadable THEN RETURN [FALSE, "mob not readable"]; ENDCASE => ERROR ShouldNotHappen; {curSourceStamp: MobDefs.VersionStamp ~ CurSourceStamp[md.sourceNode]; IF curSourceStamp # MobDefs.NullVersion THEN { supportFound ¬ TRUE; IF md.mobReadable AND resultExists AND curSourceStamp # md.sourceStamp THEN { out: IO.STREAM ¬ IO.ROS[]; out.PutRope["last used source "]; TRUSTED {MobListerUtils.PrintVersion[md.sourceStamp, out, TRUE]}; out.PutRope[", but current source is "]; TRUSTED {MobListerUtils.PrintVersion[curSourceStamp, out, TRUE]}; RETURN [FALSE, out.RopeFromROS[]]; }; }; }; curMobStamp ¬ CurMobStamp[md.mobNode]; IF seekMob AND resultExists THEN { IF curMobStamp = MobDefs.NullVersion THEN RETURN [FALSE, "New compiler version"]; <> }; IF seekC AND resultExists THEN { IF md.mobStamp # MobDefs.NullVersion THEN { <> IF curMobStamp # md.mobStamp THEN { out: IO.STREAM ¬ IO.ROS[]; out.PutRope["last used mob "]; TRUSTED {MobListerUtils.PrintVersion[md.mobStamp, out, TRUE]}; out.PutRope[", but current mob is "]; TRUSTED {MobListerUtils.PrintVersion[curMobStamp, out, TRUE]}; RETURN [FALSE, out.RopeFromROS[]]; }; } ELSE { <> IF BasicTime.Period[md.cCreateTime, md.mobCreateTime] > 0 THEN RETURN [FALSE, "C2C is older than MobFile file"]; } }; IF switchesCreate # MakeDo.notExistTime THEN { supportFound ¬ TRUE; IF BasicTime.Period[resultCreateTime, switchesCreate] > 0 THEN RETURN [FALSE, IO.PutFR1["switches file created later than result %g", [rope[resultName]]]]; }; consistent ¬ TRUE; reason ¬ "all version stamps match"; [] ¬ md.supports.Pairs[CheckSupport]; IF NOT supportFound THEN RETURN [TRUE, "no inputs exist"]; IF NOT resultExists THEN RETURN [FALSE, IO.PutFR1["some inputs exist, but not output %g", [rope[resultName]]]]; RETURN}; UpdateSupport: PROC [md: SourceData] = { <> oldMob: Time = md.mobCreateTime; oldC: Time = md.cCreateTime; md.mobCreateTime ¬ InnerGetCreated[md.mobNode]; md.cCreateTime ¬ InnerGetCreated[md.cNode]; IF md.mobCreateTime = MakeDo.notExistTime THEN RETURN; IF md.mobCreateTime = oldMob AND md.cCreateTime = oldC AND NOT md.supportsInvalid THEN RETURN; MakeSupports[md]; }; CurSourceStamp: PROC [node: Node] RETURNS [mobStamp: MobDefs.VersionStamp] = <> BEGIN time: Time ¬ InnerGetCreated[node]; RETURN [MobStampFromTime[time]]; END; MobStampFromTime: PROC [time: Time] RETURNS [mobStamp: MobDefs.VersionStamp] = <> BEGIN mobStamp ¬ MobDefs.NullVersion; IF time = MakeDo.notExistTime THEN RETURN [mobStamp]; mobStamp[0] ¬ BasicTime.ToNSTime[time]; END; CurMobStamp: PROC [node: Node] RETURNS [stamp: MobDefs.VersionStamp] ~ { sr: CachedMobData ¬ CurMobData[node]; RETURN [sr.stamp]; }; CurMobData: ENTRY PROC [node: Node] RETURNS [sr: CachedMobData] = <> BEGIN ENABLE UNWIND => {}; sr: CachedMobData ¬ NARROW[node.GetProp[$MobData]]; created: BasicTime.GMT ¬ InnerGetCreated[node]; mob: MobDefs.MobBase ¬ NIL; IF sr = NIL THEN node.SetProp[ prop: $MobData, val: sr ¬ NEW [CachedMobDataRep ¬ [ created: notExistTime, stamp: MobDefs.NullVersion, sourceStamp: MobDefs.NullVersion, dependList: RefTab.Create[]]] ]; IF created = sr.created THEN RETURN [sr]; sr.created ¬ created; IF created = MakeDo.notExistTime THEN { sr.stamp ¬ MobDefs.NullVersion; sr.sourceStamp ¬ MobDefs.NullVersion; sr.dependList ¬ RefTab.Create[]; } ELSE TRUSTED { fileName: ROPE ~ node.PublicPartsOfNode[].name; mob ¬ MobListerUtils.ReadMob[fileName ! MobListerUtils.MobErr => CONTINUE]; IF mob = NIL OR mob.versionIdent # MobDefs.VersionID THEN { sr.stamp ¬ MobDefs.NullVersion; sr.sourceStamp ¬ MobDefs.NullVersion; sr.dependList ¬ RefTab.Create[]; } ELSE { InsertInDependList: PROC [name: ROPE, version: MobDefs.VersionStamp] = TRUSTED { extName: ROPE = FS.ExpandName[Rope.Concat[name, mobTail]].fullFName; node: Node = GetNode[extName, fileClass]; s: Support ¬ NARROW[sr.dependList.Fetch[node].val]; IF s # NIL THEN s.version ¬ version ELSE { s ¬ NEW [SupportRep ¬ [node, version]]; IF NOT sr.dependList.Insert[node, s] THEN ERROR; }; RETURN}; sr.stamp ¬ mob.version; sr.sourceStamp ¬ mob.sourceVersion; EnumerateFiles[mob: mob, to: InsertInDependList]; }; IF mob # NIL THEN MobListerUtils.FreeMob[mob]; }; RETURN [sr]; END; EnumerateFiles: PROC [mob: MobDefs.MobBase, to: PROC [name: ROPE, version: MobDefs.VersionStamp]] = TRUSTED BEGIN fti: MobDefs.FTIndex ¬ MobDefs.FTIndex.FIRST; ssb: MobDefs.NameString = LOOPHOLE[mob + mob.ssOffset.units]; ftb: MobDefs.Base = LOOPHOLE[mob + mob.ftOffset.units]; UNTIL fti = mob.ftLimit DO SELECT fti FROM MobDefs.FTNull => NULL; MobDefs.FTSelf => NULL; ENDCASE => { ftr: MobDefs.FTHandle = @ftb[fti]; name: ROPE = NameToRope[ftr.name, ssb]; to[name: name, version: ftr.version]}; fti ¬ fti + MobDefs.FTRecord.SIZE; IF LOOPHOLE[fti, CARD] > LOOPHOLE[mob.ftLimit, CARD] THEN ERROR; ENDLOOP; END; NameToRope: PROC [n: MobDefs.NameRecord, ssb: MobDefs.NameString] RETURNS [ROPE] = TRUSTED { CharSeq: TYPE = RECORD[PACKED SEQUENCE COMPUTED CARDINAL OF CHAR]; ss: LONG POINTER TO CharSeq = LOOPHOLE[ssb]; index: CARDINAL = n+4; len: CARDINAL = ss[index]-0C; ros: IO.STREAM = IO.ROS[]; FOR i: NAT IN [index+1..index+len] DO IO.PutChar[ros, ss[i]]; ENDLOOP; RETURN [IO.RopeFromROS[ros]]; }; MakeSupports: PROC [md: SourceData] = { <> NoteSupport: PROC [key, val: REF ANY] RETURNS [stop: BOOL ¬ FALSE] = --RefTab.EachPairAction-- { mobSupport: Support = NARROW[val]; node: Node = mobSupport.node; version: MobDefs.VersionStamp ~ mobSupport.version; s: Support = NARROW[md.supports.Fetch[node].val]; IF s # NIL THEN s.version ¬ version; RETURN}; stamp: MobDefs.VersionStamp; name: ROPE; md.sourceStamp ¬ MobDefs.NullVersion; { sr: CachedMobData ¬ CurMobData[md.mobNode]; IF sr.created = notExistTime THEN { md.mobReadable ¬ FALSE; md.cReadable ¬ FALSE; RETURN; }; md.sourceStamp ¬ sr.sourceStamp; md.mobReadable ¬ TRUE; IF sr.dependList.Pairs[NoteSupport] THEN ERROR; }; md.cReadable ¬ TRUE; [stamp: stamp, name: name] ¬ Mobery.StampAndNameFromFile[md.cName! FS.Error => {md.cReadable ¬ FALSE; Log["C file %g not readable (1)\n", [rope[md.cName]] ]; CONTINUE}]; IF NOT md.cReadable THEN RETURN; IF NOT Rope.Equal[name, md.shortName] THEN { md.cReadable ¬ FALSE; Log["name %g is different from expected %g\n", IO.rope[name], IO.rope[md.shortName]]; }; IF NOT md.cReadable THEN RETURN; md.mobStamp ¬ stamp; md.supportsInvalid ¬ FALSE; RETURN}; EnumHiddenDeps: PROC [a: Action, Consume: PROC [Node]] = { md: SourceData ~ NARROW[a.PublicPartsOfAction[].foundData]; CheckSupport: PROC [key, val: REF ANY] RETURNS [stop: BOOL ¬ FALSE] --RefTab.EachPairAction-- ~ { mobSupport: Support = NARROW[val]; node: Node = mobSupport.node; IF NOT md.supports.Fetch[node].found THEN Consume[node]; RETURN}; SELECT md.sourceType FROM Mesa => NULL; Config => RETURN; ENDCASE => ERROR; { sr: CachedMobData ¬ CurMobData[md.mobNode]; IF sr.created = notExistTime THEN { RETURN; }; IF sr.dependList.Pairs[CheckSupport] THEN ERROR; }; RETURN}; RederiveSource: PROC [a: Action] RETURNS [from: From, cmd: ROPE] --RederiveProc-- = { md: SourceData ~ NARROW[a.PublicPartsOfAction[].foundData]; RETURN RederiveWork[md]}; <> <<>> RederiveWork: PROC [md: SourceData] RETURNS [from: From, cmd: ROPE] ~ { <> NoteDep: PROC [fileName: ROPE] ~ { <> n: Node = GetNode[fileName, fileClass]; s: Support ¬ NARROW[md.supports.Fetch[n].val]; IF s = NIL THEN { s ¬ NEW [SupportRep ¬ [n, MobDefs.NullVersion]]; IF NOT md.supports.Insert[n, s] THEN ERROR; <> }; from.mustHave ¬ CONS[n, from.mustHave]; RETURN}; switches: Rope.ROPE; exists: BOOL; dbxDebug: BOOL = UserProfile.Boolean[key: "MakeDo.dbxDebug", default: FALSE]; from ¬ [mustHave: NIL, optional: LIST[md.switchesNode]]; md.supports.Erase[]; md.sourceNode ¬ md.mesaNode; md.sourceType ¬ Mesa; [exists: exists, resultType: md.resultType] ¬ EnumerateDependancies[md.mesaNode, Mesa, NoteDep]; IF exists THEN NULL ELSE { [exists: exists, resultType: md.resultType] ¬ EnumerateDependancies[md.cedarNode, Mesa, NoteDep]; IF exists THEN NULL ELSE { [exists: exists, resultType: md.resultType] ¬ EnumerateDependancies[md.configNode, Config, NoteDep]; IF exists THEN { md.sourceNode ¬ md.configNode; md.sourceType ¬ Config; } ELSE { md.sourceType ¬ Unknown; md.resultType ¬ Unknown; }}}; md.supportsInvalid ¬ TRUE; UpdateSupport[md]; from.mustHave ¬ CONS[md.sourceNode, from.mustHave]; SELECT md.sourceType FROM Mesa => { switches ¬ GetSwitches[md.mobName, mimosaDefaultSwitch]; <> cmd ¬ Rope.Cat["Mimosa ", IF dbxDebug THEN "-a " ELSE NIL, switches, " ", md.shortName]; <