-- MDCompImpl.mesa -- last edit by Schmidt, April 21, 1982 2:55 pm -- last edit by Satterthwaite, January 31, 1983 10:52 am -- Pilot 6.0/ Mesa 7.0 -- procedures to determine compilation, etc. for the system modeller DIRECTORY CompilerOps: TYPE USING [LetterSwitches], CWF: TYPE USING [FWF1, FWF2, SWF1, SWF2, SWF3, WF0, WF1, WF2, WF3, WF4, WFCR], Dir: TYPE USING [DepSeq, FileInfo, NewVersion], Directory: TYPE USING [Error, Handle, ignore, Lookup, Rename], ExecOps: TYPE USING [Outcome], File: TYPE USING [Capability], IO: TYPE USING [Handle], LowLoader: TYPE USING [ReplaceResult], LongString: TYPE USING [AppendChar, AppendString, EquivalentString], MDComp: TYPE USING [], MDDB: TYPE USING [GetBcdDepSeq], MDMain: TYPE USING [DebugWP], MDModel: TYPE USING [ AddToEndOfList, APPLSymbol, CkType, EraseCacheEntry, FoldInParms, GenerateUniqueName, GetBcdCreate, GetFileInfo, GetSrcCreate, LETSymbol, LISTSymbol, LocForType, LOCSymbol, MODELSymbol, NarrowToLIST, NarrowToPROC, NewSymAPPL, OPENSymbol, PROCSymbol, SpliceBefore, STRINGSymbol, Symbol, SymbolSeq, TraverseList, TYPESymbol], MDUtil: TYPE USING [MakeConfig, RunBinder, SetModelCreateProperty], RComp: TYPE USING [Compile, StopBatchCompile], RTOS: TYPE USING [CheckForModuleReplacement], Runtime: TYPE USING [IsBound], Stream: TYPE USING [Delete, Handle], String: TYPE USING [AppendString], Subr: TYPE USING [ AbortMyself, CheckForModify, CopyString, debugflg, EndsIn, NewStream, strcpy, TTYProcs, Write], TimeStamp: TYPE USING [Null], TypeScript: TYPE USING[TS, UserAbort]; MDCompImpl: PROGRAM IMPORTS CWF, Dir, Directory, LongString, MDDB, MDMain, MDModel, MDUtil, RComp, RTOS, Runtime, Stream, String, Subr, TypeScript EXPORTS MDComp = { RTCallable: BOOL = TRUE; -- no MDS Usage!!! OutCome: TYPE = { compNotNecc, compDeclined, compFailed, compSuccNotRepl, compSuccRepl, compSucc}; -- this may call a procedure to look at bcd header DetermineRecomp: PUBLIC PROC[ sproot: MDModel.Symbol, symbolseq: MDModel.SymbolSeq, officialwindow: Subr.TTYProcs, uniquename, tryreplacement: BOOL, confirm: REF BOOL, typeScript: TypeScript.TS, ttyin, ttyout, msgout: IO.Handle] RETURNS[wascompiled, didfail: BOOL] = { m, dontProceed: BOOL ← FALSE; numberOfErrors, numberOfWarnings, numberSuccessful: CARDINAL; RecompRecur: PROC[ sp: MDModel.Symbol, mustcomp, failed: BOOL, spparent: MDModel.Symbol, spmodel: MDModel.MODELSymbol] RETURNS[BOOL, BOOL] = { mustcomp1, mustcomp2, mustcomp3, f1, f2, f3: BOOL; IF sp = NIL THEN RETURN[mustcomp, failed]; IF sp.visited THEN RETURN[sp.changed OR mustcomp, sp.failed OR failed]; sp.visited ← TRUE; WITH sp SELECT FROM spt: MDModel.TYPESymbol => { [mustcomp1, f1] ← RecompRecur[spt.typeval, FALSE, FALSE, sp, spmodel]; [mustcomp2, f2] ← RecompRecur[spt.letparent, FALSE, FALSE, sp, spmodel]; failed ← f1 OR f2; mustcomp ← mustcomp1 OR mustcomp2}; spt: MDModel.PROCSymbol => { [mustcomp1, f1] ← RecompRecur[spt.procparm, FALSE, FALSE, sp, spmodel]; [mustcomp2, f2] ← RecompRecur[spt.procret, FALSE, FALSE, sp, spmodel]; [mustcomp3, f3] ← RecompRecur[spt.procval, FALSE, FALSE, sp, spmodel]; mustcomp ← mustcomp1 OR mustcomp2 OR mustcomp3; failed ← f1 OR f2 OR f3}; spt: MDModel.APPLSymbol => { [mustcomp1, f1] ← RecompRecur[spt.appltype, FALSE, FALSE, sp, spmodel]; [mustcomp, f2] ← RecompRecur[spt.applval, mustcomp1, FALSE, sp, spmodel]; failed ← f1 OR f2}; spt: MDModel.LISTSymbol => { ignoreappls: BOOL = (spparent.stype = typeLOC); flist, mlist: BOOL ← FALSE; RunDownList: PROC[spinner: MDModel.Symbol] = { m1, f1: BOOL; -- special case for parameter that is an instance; -- an instance may change but the Importer need not change -- this is only set to true if the parent is a LOC -- forthermore, we want to avoid analyzing the value of an APPL -- but since the LOC is parameterized by TYPES, it is ok to skip them now -- (problem arose with FRAMEPTRTYPEs) IF ignoreappls AND ISTYPE[spinner, MDModel.APPLSymbol] THEN m1 ← f1 ← FALSE ELSE [m1, f1] ← RecompRecur[spinner, FALSE, FALSE, sp, spmodel]; mlist ← mlist OR m1; flist ← flist OR f1}; MDModel.TraverseList[spt, RunDownList]; mustcomp ← mlist; failed ← flist}; spt: MDModel.LETSymbol => { [mustcomp1, f1] ← RecompRecur[spt.letgrp, FALSE, FALSE, sp, spmodel]; [mustcomp, f2] ← RecompRecur[spt.letval, mustcomp1, FALSE, sp, spmodel]; failed ← f1 OR f2}; spt: MDModel.LOCSymbol => { o: OutCome; [mustcomp1, f1] ← RecompRecur[spt.parmlist, FALSE, FALSE, sp, spmodel]; [mustcomp2, f2] ← RecompRecur[spt.nestedmodel, FALSE, FALSE, sp, spmodel]; failed ← failed OR f1 OR f2; mustcomp ← mustcomp OR mustcomp1 OR mustcomp2; IF TypeScript.UserAbort[typeScript] THEN SIGNAL Subr.AbortMyself; o ← GenerateBcd[spt, mustcomp, uniquename, tryreplacement, failed, confirm, symbolseq, officialwindow, spmodel, typeScript, ttyin, ttyout, msgout]; SELECT o FROM $compNotNecc => {mustcomp ← FALSE; failed ← FALSE}; $compDeclined => {failed ← FALSE; dontProceed ← TRUE}; $compFailed => {mustcomp ← TRUE; failed ← TRUE; dontProceed ← TRUE}; $compSuccNotRepl => {mustcomp ← TRUE; failed ← FALSE; dontProceed ← TRUE}; $compSuccRepl => {mustcomp ← TRUE; failed ← FALSE}; $compSucc => {mustcomp ← TRUE; failed ← FALSE}; ENDCASE => ERROR; -- dontProceed means do not do anything after trying to compile --}; spt: MDModel.MODELSymbol => { IF Subr.debugflg THEN CWF.WF1["About to analyze %s.\n"L, spt.modelfilename]; [mustcomp, failed] ← RecompRecur[spt.model, mustcomp, FALSE, sp, spt]}; spt: MDModel.OPENSymbol => NULL; spt: MDModel.STRINGSymbol => NULL; ENDCASE => ERROR; -- Unknown stype sp.changed ← mustcomp; sp.failed ← failed; RETURN[mustcomp, failed]}; -- print is used temporarily here to mean compilation failed ,??? IF symbolseq.traversalInProgress THEN ERROR; symbolseq.traversalInProgress ← TRUE; FOR i: CARDINAL IN [0.. symbolseq.size) DO symbolseq[i].failed ← symbolseq[i].visited ← symbolseq[i].changed ← FALSE; ENDLOOP; [m, didfail] ← RecompRecur[sproot, FALSE, FALSE, NIL, NIL ! UNWIND => { symbolseq.traversalInProgress ← FALSE; [] ← RComp.StopBatchCompile[]} ]; sproot.changed ← m; [numberSuccessful, numberOfWarnings, numberOfErrors] ← RComp.StopBatchCompile[]; IF numberSuccessful = 0 AND numberOfErrors = 0 AND numberOfWarnings = 0 THEN CWF.WF0["Nothing was compiled.\n"L] ELSE { CWF.WF1["%u successful; "L, @numberSuccessful]; IF numberOfErrors > 0 THEN CWF.WF1["%u w/errors; "L, @numberOfErrors]; IF numberOfWarnings > 0 THEN CWF.WF1["%u w/warnings; "L, @numberOfWarnings]; CWF.WFCR[]}; CWF.WFCR[]; symbolseq.traversalInProgress ← FALSE; RETURN[m, (numberOfErrors > 0) OR dontProceed]}; oType: TYPE = {mesa, config, model}; -- sploc will be of type typeLOC -- this procedure may look at the bcd header GenerateBcd: PROC[ sploc: MDModel.LOCSymbol, mustcomp, uniquename, tryreplacement, failed: BOOL, confirm: REF BOOL, symbolseq: MDModel.SymbolSeq, officialwindow: Subr.TTYProcs, spmodel: MDModel.MODELSymbol, typeScript: TypeScript.TS, ttyin, ttyout, msgout: IO.Handle] RETURNS [outc: OutCome] = { need: BOOL; ot: oType; fi: Dir.FileInfo; MDModel.CkType[sploc, typeLOC]; fi ← MDModel.GetFileInfo[sploc]; IF fi.isBcd THEN { IF fi.bcdPresent THEN SetVersAndModulename[sploc]; RETURN[$compNotNecc]}; IF Subr.debugflg AND ~fi.srcPresent THEN CWF.WF1["Check - Cannot find %s.\n"L, fi.srcFileName]; IF sploc.sext = NIL OR LongString.EquivalentString[sploc.sext, "mesa"L] THEN ot ← $mesa ELSE IF LongString.EquivalentString[sploc.sext, "config"L] THEN ot ← $config ELSE IF LongString.EquivalentString[sploc.sext, "model"L] THEN ot ← $model ELSE ERROR; IF ot=$model THEN RETURN[$compNotNecc]; -- IF failed THEN { -- the bcdVers has not been set! CWF.WF1["Don't bother with %s since something failed before it.\n"L, fi.srcFileName]; RETURN[$compFailed]}; SetVersAndModulename[sploc]; -- this analyzes the bcd to get the bcd version stamp IF fi.srcPresent AND sploc.createtime > 0 AND MDModel.GetSrcCreate[fi] ~= sploc.createtime THEN { CWF.WF3["You want %s of %lt but the disk has %lt.\n"L, fi.srcFileName, @sploc.createtime, @fi.srcCreate]; RETURN[$compNotNecc]}; -- we used to skip checking the bcd to see if it needs to be compiled -- if a paramter had changed, but recompiling a defs file may not changed the -- functional time stamp so we always check now need ← BcdNoGood[sploc]; IF need THEN { errors, warnings, replaceable: BOOL ← FALSE; declined: BOOL ← FALSE; -- oldBcdCap: File.Capability ← fi.bcdCap; IF ~fi.srcPresent THEN { CWF.WF1["Error - Cannot compile/bind %s.\n"L, fi.srcFileName]; RETURN[$compFailed]}; SELECT ot FROM $mesa => { IF CheckParametersOnDisk[sploc] THEN RETURN[$compFailed]; -- always tries for replacement IF -- tryreplacement AND -- fi.loadInfoSeq ~= NIL AND fi.loadInfoSeq.size = 1 THEN { -- should do something about checking -- both for local frames and copied and shared replaceResult: LowLoader.ReplaceResult; oldname: STRING ← [100]; GenUniqueBcdName[oldname, sploc]; replaceResult ← SELECT TRUE FROM LongString.EquivalentString[oldname, fi.bcdFileName] => $cantCopyOldBcd, RTCallable AND Runtime.IsBound[RTOS.CheckForModuleReplacement] AND ~RTOS.CheckForModuleReplacement[fi.loadInfoSeq[0].frame] => $checkForMRFailed, ENDCASE => $ok; IF replaceResult ~= $ok THEN { CWF.WF2["%s cannot be replaced because %s.\n"L, fi.bcdFileName, SELECT replaceResult FROM $cantCopyOldBcd => "can't copy old bcd"L, $checkForMRFailed => "RT check for module replacement failed"L, ENDCASE => ERROR]; declined ← TRUE; GOTO skip}; -- make sure depseq is ready for old bcd [] ← MDDB.GetBcdDepSeq[fi, 0]; Directory.Rename[newName: oldname, oldName: fi.bcdFileName]; CWF.WF2["Old version of %s renamed to %s.*N"L, fi.bcdFileName, oldname]; [errors, warnings, replaceable, declined] ← RComp.Compile[ symbolseq, sploc, TRUE, oldname, spmodel, confirm, typeScript, ttyin, ttyout, msgout]; IF ~replaceable THEN replaceResult ← $compilerSaysNo; IF replaceable AND ~errors AND ~declined THEN { CWF.WF1["%s passes compiler's test for replaceability.\n"L, fi.bcdFileName]; fi.loadInfoSeq.mustreplace ← TRUE} ELSE { fi.loadInfoSeq.mustreplace ← FALSE; IF declined OR errors THEN { -- new version has to be deleted Directory.Rename[newName: fi.bcdFileName, oldName: oldname]; CWF.WF1["Old, loaded version of %s has been left on disk.\n"L, fi.bcdFileName]} ELSE CWF.WF3[ "%s is not replaceable%s, new version has been left on disk, \n\told loaded version is called %s.\n"L, fi.bcdFileName, IF replaceResult = $compilerSaysNo THEN " (Compiler refuses)"L ELSE ""L, oldname]}; EXITS skip => NULL; } ELSE -- not currently called -- { [errors, warnings, , declined] ← RComp.Compile[ symbolseq, sploc, FALSE, NIL, spmodel, confirm, typeScript, ttyin, ttyout, msgout]}}; $config => { outcome: ExecOps.Outcome; cmd: STRING ← [1500]; objectfile: STRING ← [100]; FormatBinderCmd[cmd, objectfile, fi.srcFileName, uniquename, NIL]; outcome ← MDUtil.RunBinder[cmd, typeScript, ttyin, ttyout, msgout, confirm]; SELECT outcome FROM $ok, $aborted => NULL; $warnings => warnings ← TRUE; $errors => errors ← TRUE; $errorsAndWarnings => warnings ← errors ← TRUE; ENDCASE => ERROR} ENDCASE => ERROR; -- leave undisturbed if declined IF ~declined THEN { IF errors THEN { -- there were errors, remove any capabilities for it MDModel.EraseCacheEntry[fi: fi, src: FALSE]; CWF.FWF1[MDMain.DebugWP, "Erasing fi entry for %s.\n"L, fi.bcdFileName]} ELSE { -- record new version and update cache fdepseq: Dir.DepSeq; [] ← Dir.NewVersion[fi: fi, src: FALSE]; fdepseq ← MDDB.GetBcdDepSeq[fi, 0]; -- this will use the DB cache IF fdepseq = NIL THEN ERROR; fi.bcdVers ← fdepseq.bcdVers; CWF.FWF1[MDMain.DebugWP, "Resetting fi entry for %s.\n"L, fi.bcdFileName]}}; RETURN[SELECT TRUE FROM declined => $compDeclined, errors => $compFailed, replaceable AND fi.loadInfoSeq ~= NIL => $compSuccRepl, ~replaceable AND fi.loadInfoSeq ~= NIL => $compSuccNotRepl, ENDCASE => $compSucc]}; RETURN[$compNotNecc]}; SetVersAndModulename: PUBLIC PROC[sploc: MDModel.LOCSymbol] = { fi: Dir.FileInfo = MDModel.GetFileInfo[sploc]; IF fi.bcdPresent THEN { fdepseq: Dir.DepSeq = MDDB.GetBcdDepSeq[fi, 0]; IF fdepseq ~= NIL THEN { IF fdepseq.moduleName ~= NIL AND fi.moduleName = NIL THEN fi.moduleName ← Subr.CopyString[fdepseq.moduleName]; IF fi.bcdVers = TimeStamp.Null OR fi.bcdVers.time ~= sploc.createtime OR sploc.createtime = MDModel.GetBcdCreate[fi] THEN { IF Subr.debugflg AND fi.bcdVers ~= fdepseq.bcdVers THEN CWF.WF2["%s bcdVers set to %v.\n"L, fi.bcdFileName, @fdepseq.bcdVers]; fi.bcdVers ← fdepseq.bcdVers}}}; }; CheckParametersOnDisk: PROC[sploctop: MDModel.LOCSymbol] RETURNS [willfail: BOOL ← FALSE] = { FOR splist: MDModel.LISTSymbol ← sploctop.parmlist, splist.rest WHILE splist ~= NIL DO WITH splist.first SELECT FROM sptype: MDModel.TYPESymbol => { sploc: MDModel.LOCSymbol = MDModel.LocForType[sptype]; fi: Dir.FileInfo; IF sploc = NIL THEN RETURN; fi ← MDModel.GetFileInfo[sploc]; IF fi.bcdVers = TimeStamp.Null THEN { IF Subr.debugflg THEN CWF.WF1["Warning- no version stamp for %s.\n"L, fi.bcdFileName]} ELSE IF ~fi.bcdPresent THEN { CWF.WF2["Error - to compile %s.Mesa you need %s on the local disk.\n"L, sploctop.tail, fi.bcdFileName]; RETURN[TRUE]}}; ENDCASE => NULL; ENDLOOP}; GenUniqueBcdName: PROC[newname: LONG STRING, sploc: MDModel.LOCSymbol] = { inx: CARDINAL ← 1; fi: Dir.FileInfo = MDModel.GetFileInfo[sploc]; Subr.strcpy[newname, fi.bcdFileName]; IF ~fi.bcdPresent THEN RETURN; DO CWF.SWF2[newname, "%s.%u.Bcd$"L, sploc.tail, @inx]; [] ← Directory.Lookup[fileName: newname, permissions: Directory.ignore ! Directory.Error => {GOTO out}]; inx ← inx + 1; ENDLOOP; EXITS out => NULL; }; -- verify the Bcd is ok BcdNoGood: PROC[splocsrc: MDModel.LOCSymbol] RETURNS[terrible: BOOL] = { bcddepseq: Dir.DepSeq; fi: Dir.FileInfo; wantsw: CompilerOps.LetterSwitches; explicitSortSwitch: BOOL ← FALSE; -- this only checks the time part of the version stamp -- in case the sploc.bcdVers came from the model and -- the bcd is not on the local disk -- you only need to check the number and types of TYPES (defs files) -- to verify the parameters (also, should check parms) ProcParm: PROC[sp: MDModel.Symbol] = { WITH sp SELECT FROM spt: MDModel.STRINGSymbol => [wantsw, explicitSortSwitch] ← MDModel.FoldInParms[spt.strval]; sptype: MDModel.TYPESymbol => { sploc: MDModel.LOCSymbol; fiInner: Dir.FileInfo; IF terrible THEN RETURN; sploc ← MDModel.LocForType[sptype]; IF sploc = NIL THEN RETURN; fiInner ← MDModel.GetFileInfo[sploc]; IF fiInner.bcdVers = TimeStamp.Null THEN { CWF.FWF1[MDMain.DebugWP, "Bcdvers for %s is 0.\n"L, fiInner.bcdFileName]; RETURN}; IF ~fiInner.bcdPresent AND ~fiInner.srcPresent THEN { CWF.FWF2[MDMain.DebugWP, "Neither src nor bcd present for %s, needed by %s.\n"L, fiInner.bcdFileName, fi.bcdFileName]; RETURN}; IF fiInner.moduleName = NIL THEN ERROR; -- since the bcddepseq[i].modulename may be NIL, we must match -- on bcdfilename and bcdvers rather than sptype.typeName FOR i: CARDINAL IN [0.. bcddepseq.size) DO IF bcddepseq[i].relation ~= directory THEN LOOP; IF LongString.EquivalentString[fiInner.bcdFileName, bcddepseq[i].bcdFileName] THEN { IF bcddepseq[i].bcdVers.time ~= fiInner.bcdVers.time THEN { CWF.WF2["\nMust recompile %s since it depends on %s.\n"L, fi.bcdFileName, fiInner.bcdFileName]; CWF.WF4[" %s is now dated %v\n\tbut %s was compiled with %v.\n"L, fiInner.bcdFileName, @fiInner.bcdVers, fi.bcdFileName, @bcddepseq[i].bcdVers]; terrible ← TRUE}; RETURN}; ENDLOOP; terrible ← TRUE; CWF.WF4[ " \nMust recompile %s since it was compiled with type %s, which is %s of %v in the model\n"L, fi.bcdFileName, sptype.typeName, fiInner.bcdFileName, @fiInner.bcdVers]; CWF.WF1[" but %s does not use it.\n"L, fi.bcdFileName]}; ENDCASE => NULL}; [wantsw] ← MDModel.FoldInParms[NIL]; -- get default switches wantsw['s] ← FALSE; -- default for modeller is /-s MDModel.CkType[splocsrc, typeLOC]; fi ← MDModel.GetFileInfo[splocsrc]; IF ~fi.bcdPresent THEN { CWF.WF2[ "Must compile %s since there is no %s on the disk.\n"L, fi.srcFileName, fi.bcdFileName]; RETURN[TRUE]}; IF ~fi.srcPresent THEN RETURN[FALSE]; -- can't recompile anyway -- don't ever free this depseq bcddepseq ← MDDB.GetBcdDepSeq[fi, 0]; IF bcddepseq = NIL THEN RETURN[TRUE]; -- not in Bcd format, must recompile terrible ← FALSE; -- do the file names agree? IF ~LongString.EquivalentString[fi.srcFileName, bcddepseq.srcFileName] THEN { CWF.WF3["Must recompile %s since the source for %s on the disk is %s,\n"L, fi.bcdFileName, fi.bcdFileName, bcddepseq.srcFileName]; CWF.WF1[" so it cannot be used as a .Bcd for %s.\n"L, fi.srcFileName]; terrible ← TRUE} -- do the create dates agree? ELSE IF MDModel.GetSrcCreate[fi] ~= bcddepseq.srcCreate THEN { CWF.WF3["Must recompile %s since it was compiled with %s of %lt,\n"L, fi.bcdFileName, fi.srcFileName, @bcddepseq.srcCreate]; CWF.WF2[" but %s is now dated %lt.\n"L, fi.srcFileName, @fi.srcCreate]; terrible ← TRUE} -- do the parameters agree in type? ELSE MDModel.TraverseList[splocsrc.parmlist, ProcParm]; -- check parameter switches, these must agree (only for implementors) -- /b (bounds checks) -- /c (cedar fork) -- /j (cross jump) -- /l (links in code, new interpretation) -- /n (nil check) -- /s (sort by usage), only check if the user explicitly specified switches IF ~terrible AND ~bcddepseq.isdefns AND (wantsw['b] ~= bcddepseq.switches['b] OR wantsw['c] ~= bcddepseq.switches['c] OR wantsw['j] ~= bcddepseq.switches['j] OR wantsw['l] ~= bcddepseq.switches['l] OR wantsw['n] ~= bcddepseq.switches['n] OR (wantsw['s] ~= bcddepseq.switches['s] AND explicitSortSwitch)) THEN { s1: STRING ← [20]; s2: STRING ← [20]; AppendBcdSwitches[s1, wantsw]; AppendBcdSwitches[s2, bcddepseq.switches]; CWF.WF2[ "Must compile %s since the model specifies compiler options %s,\n"L, fi.srcFileName, s1]; CWF.WF2["but %s was compiled with %s.\n"L, fi.bcdFileName, s2]; terrible ← TRUE}; -- if the file is ok, then make sure the loc has recorded in it -- the bcd time stamp; if the file is not ok, then give a bogus date -- can't do this because this module may be replaced, and we need the bcdVers -- fi.bcdVers ← IF terrible THEN [net: 0, host: 0, time: 1] ELSE depseq.bcdVers; fi.bcdVers ← bcddepseq.bcdVers; IF Subr.debugflg AND ~terrible THEN CWF.WF1["%s is ok.\n"L, fi.bcdFileName]; RETURN[terrible]}; -- only does this for -- /b (bounds checks) -- /c (cedar fork) -- /j (cross jump) -- /l (links in code, new interpretation) -- /n (nil check) AppendBcdSwitches: PROC[to: LONG STRING, switches: CompilerOps.LetterSwitches] = { arr: ARRAY [0..5] OF CHAR = ['b, 'c, 'j, 'l, 'n, 's]; to.length ← 0; LongString.AppendChar[to, '/]; FOR i: CARDINAL IN [0..arr.LENGTH) DO c: CHAR = arr[i]; IF ~switches[c] THEN LongString.AppendChar[to, '-]; LongString.AppendChar[to, c]; ENDLOOP}; -- stores the binder command in "cmd" -- the objectfile name in "objectfile" FormatBinderCmd: PROC[ cmd, objectfile, sourcefile: LONG STRING, uniquename: BOOL, fileparameters: LONG STRING] = { try: STRING ← [100]; Subr.strcpy[objectfile, sourcefile]; IF Subr.EndsIn[objectfile, ".config"L] THEN objectfile.length ← objectfile.length - 7; CWF.SWF1[try, "%s.bcd"L, objectfile]; IF uniquename THEN { num: CARDINAL ← 1; [] ← Directory.Lookup[fileName: try, permissions: Directory.ignore ! Directory.Error => {GOTO ok}]; DO CWF.SWF2[try, "%s%u.bcd"L, objectfile, @num]; [] ← Directory.Lookup[fileName: try, permissions: Directory.ignore ! Directory.Error => {EXIT}]; num ← num + 1; ENDLOOP; -- try is the name we will give it EXITS ok => NULL; }; Subr.strcpy[objectfile, try]; CWF.SWF3[cmd, "[bcd: %s] ← %s[%s]/e"L, objectfile, sourcefile, IF fileparameters = NIL THEN ""L ELSE fileparameters]}; -- take PLUS nodes and convert them to format acceptible to the modeller loader HandlePlus: PUBLIC PROC[symbolseq: MDModel.SymbolSeq] = { start: MDModel.PROCSymbol; splist : MDModel.LISTSymbol ← symbolseq.toploc.nestedmodel.model; WHILE splist ~= NIL AND ~ISTYPE[splist.first, MDModel.PROCSymbol] DO splist ← splist.rest; IF splist = NIL THEN RETURN; MDModel.CkType[splist, typeLIST]; ENDLOOP; start ← MDModel.NarrowToPROC[splist.first]; splist ← MDModel.NarrowToLIST[start.procval]; WHILE splist ~= NIL DO MDModel.CkType[splist, typeLIST]; WITH splist.first SELECT FROM spa: MDModel.APPLSymbol => WITH spa.applval SELECT FROM spp: MDModel.LISTSymbol => IF spp.listtype = $plus AND ISTYPE[spp.first, MDModel.LOCSymbol] THEN { spnewlist: MDModel.LISTSymbol ← NIL; WHILE spp ~= NIL DO spnew: MDModel.APPLSymbol = MDModel.NewSymAPPL[symbolseq]; MDModel.CkType[spp, typeLIST]; spnew.applsym ← MDModel.GenerateUniqueName[spa]; spnew.applval ← spp.first; spnew.appltype ← spa.appltype; spnew.recursive ← spa.recursive; start.procval ← MDModel.SpliceBefore[symbolseq, spnew, splist, MDModel.NarrowToLIST[start.procval]]; spnewlist ← MDModel.AddToEndOfList[spnewlist, spnew, $plus, symbolseq]; spp ← spp.rest; ENDLOOP; -- now replace list val by new one -- this discards the old list -- FreeListHeaders[spa.applval]; spa.applval ← spnewlist}; ENDCASE => NULL; ENDCASE => NULL; splist ← splist.rest; ENDLOOP}; -- add the UID (create date) for the model to the config NewBind: PUBLIC PROC[ sproot: MDModel.MODELSymbol, symbolseq: MDModel.SymbolSeq, needsconfig, uniquename: BOOL, confirm: REF BOOL, modelfile: LONG STRING, modelcreate: LONG CARDINAL, officialwindow: Subr.TTYProcs, typeScript: TypeScript.TS, ttyin, ttyout, msgout: IO.Handle] RETURNS[successful: BOOL ← FALSE] = { fileparameters: STRING ← [2000]; cmd: STRING ← [2000]; sourcefile: STRING ← [100]; objectfile: STRING ← [100]; outcome: ExecOps.Outcome; IF ~Subr.EndsIn[modelfile, ".model"L] THEN ERROR; Subr.strcpy[sourcefile, "MODEL"L]; LongString.AppendString[sourcefile, modelfile]; sourcefile.length ← sourcefile.length - 5; String.AppendString[sourcefile, "config"L]; IF needsconfig THEN { sh: Stream.Handle; CWF.WF1["\nThe New Config File Is %s.\n"L, sourcefile]; -- this changes the model to allow for -- Binder limitations; don't save this version HandlePlus[symbolseq]; IF Subr.debugflg THEN MDUtil.MakeConfig[sproot, symbolseq, NIL, 0, ttyout, NIL]; IF Subr.CheckForModify[sourcefile, officialwindow] THEN { cap: File.Capability; sh ← Subr.NewStream[sourcefile, Subr.Write]; MDUtil.MakeConfig[sproot, symbolseq, sh, modelcreate, ttyout, fileparameters]; Stream.Delete[sh]; cap ← Directory.Lookup[fileName: sourcefile, permissions: Directory.ignore]; MDUtil.SetModelCreateProperty[cap, modelcreate]}}; FormatBinderCmd[cmd, objectfile, sourcefile, uniquename, IF fileparameters.length = 0 THEN NIL ELSE fileparameters]; outcome ← MDUtil.RunBinder[cmd, typeScript, ttyin, ttyout, msgout, confirm]; IF outcome = $ok OR outcome = $warnings THEN { cap: File.Capability; successful ← TRUE; cap ← Directory.Lookup[fileName: sourcefile, permissions: Directory.ignore]; MDUtil.SetModelCreateProperty[cap, modelcreate]; -- call with the top-level bcd name -- strip off ".Bcd", it's not needed objectfile.length ← objectfile.length - 4}; CWF.WFCR[]; RETURN}; }. TimeToStamp: PROC [time: TimeStamp.Stamp] RETURNS [Stamp] = INLINE { RETURN [LOOPHOLE[time]]}; -- new version stamp operations StampSize: NAT = 3; Stamp: TYPE = RECORD [word: ARRAY [0..StampSize) OF CARDINAL]; AddStamps: PROC [s1, s2: Stamp] RETURNS [sum: Stamp] = { carry: [0..1] ← 0; i: NAT; FOR i DECREASING IN [0..StampSize) DO t: Inline.LongNumber ← [lc[LONG[s1.word[i]] + LONG[s2.word[i]] + LONG[carry]]]; sum.word[i] ← t.lowbits; carry ← t.highbits; ENDLOOP; FOR i DECREASING IN [0..StampSize) WHILE carry # 0 DO t: Inline.LongNumber ← [lc[LONG[sum.word[i]] + LONG[carry]]]; sum.word[i] ← t.lowbits; carry ← t.highbits; ENDLOOP}; RotateStamp: PROC [s: Stamp] RETURNS [Stamp] = INLINE {RETURN [AddStamps[s, s]]}; MergeStamps: PUBLIC PROC [sum, item: Stamp] RETURNS [Stamp] = { RETURN [AddStamps[RotateStamp[sum], item]]}; QuickCheck: PROC[diskbcd, disksrc: Dir.Disk, sploctop: MDModel.LOCSymbol] RETURNS[bcdisok: BOOL] = { switches: PACKED ARRAY CHAR ['a..'z] OF BOOL; compilerVersion: TimeStamp.Stamp = CompilerOps.CompilerVersion[]; -- current Cedar release trystamp: Stamp; actualstamp: TimeStamp.Stamp; depseq: Dir.DepSeq; willfail: BOOL ← FALSE; t: TimeStamp.Stamp; GetSwitches: PROC[sp: MDModel.Symbol] = { spstr: MDModel.STRINGSymbol; IF sp.stype ~= typeSTRING THEN RETURN; spstr ← MDModel.NarrowToSTRING[sp]; switches ← MDModel.FoldInParms[spstr.strval]; }; GetTYPES: PROC[sp: MDModel.Symbol] = { sptype: MDModel.TYPESymbol; sploc: MDModel.LOCSymbol; IF sp.stype ~= typeTYPE THEN RETURN; sptype ← MDModel.NarrowToTYPE[sp]; sploc ← MDModel.LocForType[sptype]; IF sploc = NIL THEN RETURN; IF sploc.bcdVers = TimeStamp.Null AND Subr.debugflg THEN CWF.WF1["Warning- no version stamp for %s.Bcd.\n"L, sploc.tail]; IF sploc.bcdVers.net = 0 AND sploc.bcdVers.host = 0 THEN { willfail ← TRUE; -- this means a bcdVers is from a bcd not on the disk IF Subr.debugflg THEN CWF.WF3["QuickCheck will fail for %s because of %s (time = %lt).\n"L, sploctop.tail, sploc.tail, @sploc.bcdVers.time]; RETURN; }; trystamp ← MergeStamps[trystamp, TimeToStamp[sploc.bcdVers -- mdb[c.module].stamp -- ]]; }; -- figure out what stamp should be if the model were correct trystamp ← TimeToStamp[[net: 0, host: 0, time: disksrc.create]]; -- encode switches, compiler version -- set defaults switches ← MDModel.FoldInParms[NIL]; MDModel.TraverseList[sploctop.parmlist, GetSwitches]; switches['g] ← FALSE; switches['p] ← FALSE; trystamp ← MergeStamps[trystamp, TimeToStamp[[0, 0, LOOPHOLE[switches]]]]; trystamp ← MergeStamps[trystamp, TimeToStamp[compilerVersion]]; MDModel.TraverseList[sploctop.parmlist, GetTYPES]; IF willfail THEN RETURN[FALSE]; -- now look in the bcd to get the actual stamp IF sploctop.bcdVers ~= TimeStamp.Null AND sploctop.bcdVers.net ~= 0 THEN actualstamp ← sploctop.bcdVers -- use this if bonafide ELSE IF (depseq ← diskbcd.depseq) ~= NIL OR (depseq ← DBStash.Lookup[diskbcd.create]) ~= NIL THEN actualstamp ← depseq.bcdtime ELSE { bcd: BcdOps.BcdBase; space: Space.Handle ← Space.Create[size: 1, parent: Space.virtualMemory]; Space.Map[space, [diskbcd.cap, 1]]; MDModel.numberofbcdsmapped ← MDModel.numberofbcdsmapped + 1; bcd ← Space.LongPointer[space]; actualstamp ← bcd.version; -- this is the bcd version stamp IF sploctop.bcdVers = TimeStamp.Null OR sploctop.bcdVers.time ~= sploctop.createtime OR sploctop.createtime = diskbcd.create THEN sploctop.bcdVers ← actualstamp; Space.Delete[space]; }; IF trystamp = TimeToStamp[actualstamp] THEN { IF Subr.debugflg THEN CWF.WF1["Quick check succeeded for %s.Bcd.\n"L, sploctop.tail]; RETURN[TRUE]; } ELSE IF actualstamp.time ~= diskbcd.create THEN { -- only give msg for Cedar t ← LOOPHOLE[trystamp]; IF Subr.debugflg THEN CWF.WF3["Quick check failed for %s.Bcd: %lu ~= %lu.\n"L, sploctop.tail, @t.time, @actualstamp.time]; }; RETURN[FALSE]; };