-- CTCompImpl.Mesa, last edit May 13, 1983 3:14 pm DIRECTORY BcdDefs: TYPE USING [Base, FTIndex, MTIndex, NameRecord], BcdOps: TYPE USING [BcdBase, FTHandle, MTHandle, NameString, ProcessFiles], CompilerOps: TYPE USING [AppendHerald, DefaultSwitches, DoTransaction, LetterSwitches, Start, Stop, StreamId, Transaction], ConvertUnsafe: TYPE USING[ToRope], CS: TYPE USING [Confirm, EndsIn, EquivalentRope, MakeTS, NewFile, NewStream, ReadWrite, SetPFCodes, Write], CT: TYPE USING[BcdTab, BcdTabRecord, Global, MI, ModuleList], CTLoad: TYPE USING[ReplaceResult], Directory: TYPE USING [DeleteFile, Error, ignore, Lookup, Rename, UpdateDates], File: TYPE USING[Capability, nullCapability, read], FileParms: TYPE USING [ActualId, BindingProc, Name, nullActual, nullSymbolSpace, SymbolSpace], FileStream: TYPE USING[Create, GetLeaderPropertiesForCapability], Heap: TYPE USING[systemZone], IO: TYPE USING[card, Close, CreateProcsStream, CreateRefStreamProcs, Handle, PutChar, PutF, PutFR, STREAM, string, rope, UserAbort, UserAborted], List: TYPE USING[DRemove], LongString: TYPE USING [SubString, SubStringDescriptor], Rope: TYPE USING[Cat, Flatten, FromChar, IsEmpty, Length, ROPE, Text], RopeInline: TYPE USING[InlineFlatten], RTOS: TYPE USING[CheckForModuleReplacement], Space: TYPE USING [Create, CreateUniformSwapUnits, Delete, Handle, LongPointer, -- MakeReadOnly, -- Map, Unmap, virtualMemory], Stream: TYPE USING[Handle, Delete, PutChar], Time: TYPE USING[Current], TimeStamp: TYPE USING [Null, Stamp], ViewerClasses: TYPE USING [Viewer], ViewerOps: TYPE USING [CreateViewer, FindViewer, OpenIcon, RestoreViewer], WindowManager: TYPE USING[WaitCursor, UnWaitCursor]; CTCompImpl: CEDAR MONITOR IMPORTS BcdOps, CompilerOps, ConvertUnsafe, CS, Directory, FileStream, Heap, IO, List, Rope, RopeInline, RTOS, Space, Stream, Time, ViewerOps, WindowManager EXPORTS CT = { -- MDS usage -- all these variables are protected by the monitor compilerIsLocked: BOOL ← FALSE; compilerWait: CONDITION; logsh: IO.Handle ← NIL; -- out stream to Compiler.Log logpilotsh: Stream.Handle; g: CT.Global; sourcesh: Stream.Handle; -- in stream to source file good, warn, err: CARDINAL ← 0; compilerStarted: BOOL ← FALSE; timeCompilerStarted: LONG CARDINAL ← 0; onePageSpace: Space.Handle; -- this is used for getting version stamps manyPageSpace: Space.Handle; -- used to map in the entire Bcd manyPageSize: CARDINAL; -- number of pages in manyPageSpace -- endof MDS DetermineCompilation: PUBLIC SAFE PROC[gToUse: CT.Global, modRepl: BOOL] RETURNS[errors: BOOL] = TRUSTED { ENABLE UNWIND => ReleaseCompilerLock[]; time: LONG CARDINAL; gToUse.ttyout.PutF["Compilation Phase.\n"]; AcquireCompilerLock[gToUse]; g ← gToUse; time ← Time.Current[]; -- g.msgout.PutF["\n\n"]; errors ← CompileIfNecessary[g, modRepl ! UNWIND => [] ← StopBatchCompile[]]; g.ttyout.PutF["Elapsed time for compile: %r\n", IO.card[Time.Current[] - time]]; g.ttyout.PutF["--------------------------------\n"]; ReleaseCompilerLock[]; }; CompileIfNecessary: PROC[g: CT.Global, modRepl: BOOL] RETURNS[errors: BOOL] = TRUSTED { numberSuccessful, numberOfWarnings, numberOfErrors: CARDINAL; onePageSpace ← Space.Create[size: 1, parent: Space.virtualMemory]; manyPageSize ← 20; manyPageSpace ← Space.Create[size: manyPageSize, parent: Space.virtualMemory]; Space.CreateUniformSwapUnits[parent: manyPageSpace, size: 8]; -- Space.MakeReadOnly[manyPageSpace]; FOR l: CT.ModuleList ← g.moduleList, l.rest UNTIL l = NIL DO IF l.first.srcFileName = NIL THEN LOOP; IF g.ttyout.UserAbort[] THEN ERROR ABORTED; IF modRepl AND NOT l.first.possiblyBad AND l.first.bcdValid THEN g.ttyout.PutF["Skipping %s.\n", IO.rope[l.first.srcFileName]] ELSE IF l.first.dependencyBad THEN g.ttyout.PutF["Skipping %s, something failed before it.\n", IO.rope[l.first.srcFileName]] ELSE IF BcdNoGood[l.first] THEN { g.ttyout.PutF["%s needs to be recompiled.\n", IO.rope[l.first.srcFileName]]; ArrangeForCompile[l.first ! UNWIND => [] ← StopBatchCompile[]]; IF l.first.bcdValid THEN SetPossiblyBadAndValid[l.first]; RemoveBcdTabFile[l.first.bcdFileName]; } ELSE { l.first.possiblyBad ← FALSE; g.ttyout.PutF["%s does not need to be recompiled.\n", IO.rope[l.first.srcFileName]]; }; ENDLOOP; [numberSuccessful, numberOfWarnings, numberOfErrors] ← StopBatchCompile[]; IF numberSuccessful = 0 AND numberOfErrors = 0 AND numberOfWarnings = 0 THEN g.ttyout.PutF["Nothing was compiled.\n"] ELSE { g.ttyout.PutF["%d successful; ", IO.card[numberSuccessful]]; IF numberOfErrors > 0 THEN g.ttyout.PutF["%d w/errors; ", IO.card[numberOfErrors]]; IF numberOfWarnings > 0 THEN g.ttyout.PutF["%d w/warnings; ", IO.card[numberOfWarnings]]; g.ttyout.PutF["\n"]; }; Space.Delete[onePageSpace]; Space.Delete[manyPageSpace]; errors ← numberOfErrors > 0; }; BcdNoGood: PROC[mi: CT.MI] RETURNS[badBcd: BOOL] = TRUSTED { namestring: BcdOps.NameString; innerBcdBase: BcdOps.BcdBase; ForEachDirectory: PROC[fth: BcdOps.FTHandle, fti: BcdDefs.FTIndex] RETURNS[stop: BOOL ← FALSE] = TRUSTED { bcdTab: CT.BcdTab; innerCap: File.Capability; name: Rope.Text ← NameToRope[fth.name, namestring]; stop ← FALSE; name ← AppendExtension[name, ".bcd"L]; AddDependsInfo[mi, name]; bcdTab ← LookupBcdTabFile[name]; IF bcdTab ~= NIL THEN { IF bcdTab.bcdVers ~= fth.version THEN badBcd ← TRUE; g.dout.PutF["Hit on %s\n", IO.rope[name]]; RETURN; }; -- must look on disk innerCap ← Directory.Lookup[fileName: LOOPHOLE[name], permissions: Directory.ignore ! Directory.Error => GOTO fileNotFound]; Space.Map[space: onePageSpace, window: [file: innerCap, base: 1]]; AddToBcdTabFile[name, innerCap, innerBcdBase.version]; IF innerBcdBase.version ~= fth.version THEN { badBcd ← TRUE; Space.Unmap[onePageSpace]; RETURN[TRUE]; }; Space.Unmap[onePageSpace]; EXITS fileNotFound => NULL; -- can't tell if needs to be recompiled }; { bcdBase: BcdOps.BcdBase; npages: CARDINAL; mth: BcdOps.MTHandle; badBcd ← FALSE; mi.srcCap ← Directory.Lookup[fileName: LOOPHOLE[mi.srcFileName], permissions: Directory.ignore ! Directory.Error => GOTO srcNotFound]; [create: mi.srcCreate] ← FileStream.GetLeaderPropertiesForCapability[mi.srcCap]; mi.bcdCap ← Directory.Lookup[fileName: LOOPHOLE[mi.bcdFileName], permissions: Directory.ignore ! Directory.Error => GOTO bad]; Space.Map[space: onePageSpace, window: [file: mi.bcdCap, base: 1]]; bcdBase ← Space.LongPointer[onePageSpace]; IF bcdBase.sourceVersion.time ~= mi.srcCreate THEN badBcd ← TRUE; npages ← bcdBase.nPages; Space.Unmap[onePageSpace]; -- now map in the right number of pages IF npages > manyPageSize THEN { Space.Delete[manyPageSpace]; manyPageSize ← npages + 10; manyPageSpace ← Space.Create[size: manyPageSize, parent: Space.virtualMemory]; Space.CreateUniformSwapUnits[parent: manyPageSpace, size: 8]; -- Space.MakeReadOnly[manyPageSpace]; }; Space.Map[space: manyPageSpace, window: [file: mi.bcdCap, base: 1]]; bcdBase ← Space.LongPointer[manyPageSpace]; mth ← @(LOOPHOLE[bcdBase, BcdDefs.Base] + bcdBase.mtOffset)[FIRST[BcdDefs.MTIndex]]; IF NOT bcdBase.definitions AND (mi.switches['b] ~= mth.boundsChecks -- there are some omissions here OR mi.switches['c] ~= mth.long -- encoded in the old l OR mi.switches['j] ~= mth.crossJumped OR mi.switches['l] ~= (mth.linkLoc = code) -- new interp. of /l OR mi.switches['n] ~= mth.nilChecks OR ((mi.switches['s] = mth.initial) AND mi.explicitSortSwitch)) THEN badBcd ← TRUE; -- remember /s switch, initial = FALSE is /s, initial = TRUE is /-s -- if not explicitSortSwitch then set switches to whatever old bcd had IF NOT mi.explicitSortSwitch THEN mi.switches['s] ← NOT mth.initial; IF badBcd THEN { Space.Unmap[manyPageSpace]; GOTO bad; -- down here to make sure /s switch is known }; namestring ← LOOPHOLE[bcdBase + bcdBase.ssOffset]; innerBcdBase ← Space.LongPointer[onePageSpace]; [] ← BcdOps.ProcessFiles[bcdBase, ForEachDirectory]; IF NOT badBcd THEN { AddToBcdTabFile[mi.bcdFileName, mi.bcdCap, bcdBase.version]; mi.bcdVers ← bcdBase.version; mi.definitions ← bcdBase.definitions; }; Space.Unmap[manyPageSpace]; EXITS bad => badBcd ← TRUE; srcNotFound => g.ttyout.PutF["Cannot open %s.\n", IO.rope[mi.srcFileName]]; -- what to do? }}; ArrangeForCompile: PROC[mi: CT.MI] = TRUSTED { errors, warnings, replaceable, declined: BOOL; IF mi.loadInfoSeq ~= NIL AND mi.loadInfoSeq.size = 1 THEN { -- try for replacement oldBcdFileName: Rope.Text; replaceResult: CTLoad.ReplaceResult; oldBcdFileName ← GenUniqueBcdName[mi.bcdFileName]; SELECT TRUE FROM CS.EquivalentRope[oldBcdFileName, mi.bcdFileName] => replaceResult ← cantCopyOldBcd; NOT RTOS.CheckForModuleReplacement[mi.loadInfoSeq[0].frame] => replaceResult ← checkForMRFailed; ENDCASE => replaceResult ← ok; IF replaceResult ~= ok THEN { g.ttyout.PutF["%s cannot be replaced because %s.\n", IO.rope[mi.bcdFileName], IO.rope[SELECT replaceResult FROM cantCopyOldBcd => "can't copy old bcd", checkForMRFailed => "RT check for module replacement failed", ENDCASE => ERROR]]; declined ← TRUE; GOTO skip; }; Directory.Rename[newName: LOOPHOLE[oldBcdFileName], oldName: LOOPHOLE[mi.bcdFileName]]; RemoveBcdTabFile[mi.bcdFileName]; mi.bcdCap ← File.nullCapability; g.ttyout.PutF["Old version of %s renamed to %s.\n", IO.rope[mi.bcdFileName], IO.rope[oldBcdFileName]]; [errors, warnings, replaceable, declined] ← CompileIt[mi, TRUE, oldBcdFileName]; IF NOT replaceable THEN replaceResult ← compilerSaysNo; IF replaceable AND NOT errors AND NOT declined THEN { g.ttyout.PutF["%s passes compiler's test for replaceability.\n", IO.rope[mi.bcdFileName]]; mi.loadInfoSeq.mustreplace ← TRUE; } ELSE { mi.loadInfoSeq.mustreplace ← FALSE; IF declined OR errors THEN { -- new version has to be deleted Directory.Rename[newName: LOOPHOLE[mi.bcdFileName], oldName: LOOPHOLE[oldBcdFileName]]; g.ttyout.PutF["Old, loaded version of %s has been left on disk.\n", IO.rope[mi.bcdFileName]]; } ELSE g.ttyout.PutF["%s is not replaceable%s, new version has been left on disk, \n\told loaded version is called %s.\n", IO.rope[mi.bcdFileName], IO.rope[IF replaceResult = compilerSaysNo THEN " (Compiler refuses)" ELSE ""], IO.rope[oldBcdFileName]]; }; EXITS skip => NULL; } ELSE { [errors, warnings, , declined] ← CompileIt[mi, FALSE, NIL]; }; mi.bcdValid ← NOT errors; }; CompileIt: PROC[mi: CT.MI, tryreplacement: BOOL, oldBcdFileName: Rope.Text] RETURNS[errors, warnings, replaceable, declined: BOOL] = TRUSTED { inx: CARDINAL ← 1; t: CompilerOps.Transaction; cap: File.Capability; onestarttime: LONG CARDINAL; DirectoryBinding: PROC[formalId, formalType: FileParms.Name, defaultLocator: LONG STRING, binder: FileParms.BindingProc] = TRUSTED { idname: Rope.Text ← SubStringToRope[@formalId]; ref: REF ReturnRecord; ref ← GetActualIdForFile[idname, ConvertUnsafe.ToRope[defaultLocator]]; AddDependsInfo[mi, ref.bcdFileName]; IF ref.found THEN { actual: FileParms.ActualId ← [version: ref.version, locator: [base: LOOPHOLE[ref.bcdFileName], offset: 0, length: ref.bcdFileName.Length[]]]; binder[actual]; g.dout.PutF["match %g with %g of %v\n", IO.rope[idname], IO.rope[ref.bcdFileName], CS.MakeTS[ref.version]]; } ELSE g.ttyout.PutF["Error - %s not found.\n", IO.rope[ref.bcdFileName]]; }; -- called after DirectoryBinding, except for hidden directory entries DirectoryAcquire: PROC[type: LongString.SubStringDescriptor, actual: FileParms.ActualId] RETURNS[symbolSpace: FileParms.SymbolSpace] = TRUSTED { ref: REF ReturnRecord; ref ← GetActualIdForFile[SubStringToRope[@type], SubStringToRope[@actual.locator]]; IF ref.found THEN RETURN[ref.symbolSpace]; g.ttyout.PutF["%s of %v not found.\n", IO.rope[ref.bcdFileName], CS.MakeTS[actual.version]]; RETURN[FileParms.nullSymbolSpace]; }; DeleteBadBcd: PROC = TRUSTED { IF t.objectName ~= NIL THEN Directory.DeleteFile[t.objectName]; t.objectName ← NIL; RemoveBcdTabFile[mi.bcdFileName]; }; Cleanup: PROC = TRUSTED { IF t.sourceStream ~= NIL THEN Stream.Delete[t.sourceStream]; t.sourceStream ← NIL; sourcesh ← NIL; }; { ENABLE UNWIND => { DeleteBadBcd[]; Cleanup[]; }; errors ← warnings ← declined ← TRUE; replaceable ← FALSE; t.sourceStream ← NIL; t.objectName ← NIL; IF AskTheUser[mi.srcFileName, mi.switches] THEN RETURN; declined ← FALSE; -- make sure the compiler is loaded, etc. IF NOT compilerStarted THEN StartBatchCompile[]; -- set up Transaction record contents t.op ← IF tryreplacement THEN replace ELSE compile; t.source ← [version: [net: 0, host: 0, time: mi.srcCreate], locator: [base: LOOPHOLE[mi.srcFileName], offset: 0, length: mi.srcFileName.Length[]]]; cap ← Directory.UpdateDates[mi.srcCap, File.read]; sourcesh ← t.sourceStream ← FileStream.Create[cap]; t.fileParms ← [DirectoryBinding, DirectoryAcquire, DirectoryRelease, DirectoryForget]; t.switches ← mi.switches; t.switches['s] ← mi.explicitSortSwitch; IF tryreplacement THEN { IF mi.bcdVers = TimeStamp.Null THEN ERROR; t.pattern ← [version: mi.bcdVers, locator: [base: LOOPHOLE[oldBcdFileName], offset: 0, length: oldBcdFileName.Length[]]]; -- if this is replacement, try to set sorting the same as the old bcd IF NOT mi.explicitSortSwitch THEN t.switches['s] ← mi.switches['s] } ELSE t.pattern ← FileParms.nullActual; t.objectName ← LOOPHOLE[mi.bcdFileName]; mi.bcdCap ← t.objectFile ← CS.NewFile[mi.bcdFileName, CS.ReadWrite, 10]; t.debugPass ← LAST[CARDINAL]; t.getStream ← LogGetStream; t.startPass ← CompilerPass; PrintStartOne[@t]; onestarttime ← Time.Current[]; -- these are here to hide them from the user t.switches['d] ← TRUE; -- debugging t.switches['g] ← FALSE; -- log is always Compiler.Log -- actually call the Compiler! CompilerOps.DoTransaction[@t]; PrintStopOne[@t, onestarttime]; replaceable ← tryreplacement AND t.matched; errors ← t.nErrors # 0; warnings ← t.nWarnings # 0; IF errors THEN err ← err + 1; IF warnings THEN warn ← warn + 1; IF NOT errors AND NOT warnings THEN good ← good + 1; IF NOT errors THEN { mi.bcdVers ← t.objectVersion; mi.definitions ← (t.objectBytes = 0); -- kludge to tell if it is a Defs file } ELSE DeleteBadBcd[]; Cleanup[]; }}; -- local procedures AddDependsInfo: PROC[mi: CT.MI, bcdName: Rope.Text] = { FOR l: CT.ModuleList ← g.moduleList, l.rest UNTIL l = NIL DO IF CS.EquivalentRope[l.first.bcdFileName, bcdName] THEN { -- add to l.first the information that mi depends on it l.first.dependedBy ← CONS[mi.bcdFileName, l.first.dependedBy]; -- always set to TRUE, reset in SetPossiblyBadAndValid l.first.dependencyBad ← TRUE; RETURN; }; ENDLOOP; -- may not be able to add because this is a defs file not in the system -- e.g. Rope.bcd, IO.Bcd }; SetPossiblyBadAndValid: PUBLIC SAFE PROC[mi: CT.MI] = CHECKED { found: BOOL; FOR l: LIST OF Rope.Text ← mi.dependedBy, l.rest UNTIL l = NIL DO found ← FALSE; FOR ml: CT.ModuleList ← g.moduleList, ml.rest UNTIL ml = NIL DO IF CS.EquivalentRope[l.first, ml.first.bcdFileName] THEN { ml.first.possiblyBad ← TRUE; ml.first.dependencyBad ← FALSE; found ← TRUE; EXIT; }; ENDLOOP; IF NOT found THEN g.ttyout.PutF["Error - SetPossiblyBad: Can't find entry for %s.\n", IO.rope[l.first]]; ENDLOOP; }; FlushOldBcd: PROC[mi: CT.MI] = { RemoveBcdTabFile[mi.bcdFileName]; mi.bcdCap ← File.nullCapability; mi.bcdVers ← TimeStamp.Null; mi.bcdValid ← FALSE; }; StartBatchCompile: PROC = TRUSTED { herald: STRING ← [100]; good ← warn ← err ← 0; logsh ← NIL; timeCompilerStarted ← Time.Current[]; Directory.DeleteFile["Compiler.Log"L ! Directory.Error => CONTINUE]; [] ← LogGetStream[log]; -- creates new log CompilerOps.AppendHerald[herald]; g.ttyout.PutF["%s\n%t\n", IO.string[herald], IO.card[timeCompilerStarted]]; logsh.PutF["%s\n%t\n", IO.string[herald], IO.card[timeCompilerStarted]]; CompilerOps.Start[Heap.systemZone]; compilerStarted ← TRUE; }; StopBatchCompile: PROC RETURNS[nOk, nWarn, nErr: CARDINAL] = TRUSTED { log: ViewerClasses.Viewer; IF NOT compilerStarted THEN RETURN[0, 0, 0]; -- noop call; compiler not running IF good # 0 THEN logsh.PutF[" %d successful; ", IO.card[good]]; IF warn # 0 THEN logsh.PutF[" %d w/warnings; ", IO.card[warn]]; IF err # 0 THEN logsh.PutF[" %d w/errors; ", IO.card[err]]; timeCompilerStarted ← Time.Current[] - timeCompilerStarted; logsh.PutF["\nTotal elapsed time: %y\n", IO.card[timeCompilerStarted]]; Stream.Delete[logpilotsh]; logsh ← NIL; CompilerOps.Stop[]; compilerStarted ← FALSE; log ← ViewerOps.FindViewer["Compiler.Log"]; IF log ~= NIL THEN ViewerOps.RestoreViewer[log]; IF warn > 0 OR err > 0 THEN { IF log ~= NIL THEN ViewerOps.OpenIcon[log] ELSE { g.msgout.PutChar['\n]; CreateANewViewer["Compiler.log", g.ttyout]; }; }; g.ttyout.PutF["End of compilation\n"]; RETURN[good, warn, err]; }; DirectoryRelease: PROC[ss: FileParms.SymbolSpace] = {}; DirectoryForget: PROC[actual: FileParms.ActualId] = {}; PrintStartOne: PROC[t: POINTER TO CompilerOps.Transaction] = TRUSTED { first: BOOL ← TRUE; standardSwitches: CompilerOps.LetterSwitches ← CompilerOps.DefaultSwitches[]; g.msgout.PutF["Compiling: %s", IO.string[t.source.locator.base]]; logsh.PutF["\nCommand: %s", IO.string[t.source.locator.base]]; FOR c: CHAR IN ['a .. 'z] DO sd: BOOL ← IF c = 'p THEN FALSE ELSE standardSwitches[c]; IF t.switches[c] ~= sd THEN { IF first THEN { first ← FALSE; g.msgout.PutChar['/]; logsh.PutChar['/]; }; IF sd THEN { g.msgout.PutChar['-]; logsh.PutChar['-]; }; g.msgout.PutChar[c]; logsh.PutChar[c]; }; ENDLOOP; logsh.PutChar['\n]; }; PrintStopOne: PROC[t: POINTER TO CompilerOps.Transaction, oneStartTime: LONG CARDINAL] = TRUSTED { -- first MsgSW IF t.nErrors > 0 THEN g.msgout.PutF["%d errors", IO.card[t.nErrors]] ELSE g.msgout.PutF["no errors"]; IF t.nWarnings > 0 THEN g.msgout.PutF[", %d warnings", IO.card[t.nWarnings]]; g.msgout.PutChar['\n]; -- now log logsh.PutF["%s -- ", IO.string[t.source.locator.base]]; IF t.nErrors > 0 THEN { logsh.PutF[" aborted, %d errors", IO.card[t.nErrors]]; IF t.nWarnings > 0 THEN logsh.PutF[" and %d warnings", IO.card[t.nWarnings]]; oneStartTime ← Time.Current[] - oneStartTime; logsh.PutF[", time: %y.\n\n", IO.card[oneStartTime]]; } ELSE { oneStartTime ← Time.Current[] - oneStartTime; logsh.PutF["source tokens: %d, time: %y", IO.card[t.sourceTokens], IO.card[oneStartTime]]; IF t.objectBytes > 0 THEN logsh.PutF["\n code bytes: %d, links: %d, global frame words: %d", IO.card[t.objectBytes], IO.card[t.linkCount], IO.card[t.objectFrameSize]]; IF t.nWarnings > 0 THEN logsh.PutF["\n%d warnings", IO.card[t.nWarnings]]; logsh.PutF["\n\n"]; }; }; CreateANewViewer: PROC [name: Rope.Text, out: IO.Handle] = { viewer: ViewerClasses.Viewer; WindowManager.WaitCursor[]; viewer ← ViewerOps.CreateViewer[flavor: $Text, info: [name: name, file: name, iconic: FALSE, column: left]]; out.PutF["Created Viewer: %s\n", IO.rope[name]]; -- ViewerOps.SetNewFile[viewer]; WindowManager.UnWaitCursor[]; }; AskTheUser: PROC[filename: Rope.Text, wantsw: CompilerOps.LetterSwitches] RETURNS[declined: BOOL] = { ch: CHAR; dif: Rope.ROPE; declined ← TRUE; -- ask the user if he really wants it compiled g.ttyout.PutF["Compile %s", IO.rope[filename]]; dif ← ProduceDifferentialSwitches[wantsw]; IF NOT Rope.IsEmpty[dif] THEN g.ttyout.PutF["/%s", IO.rope[dif]]; g.ttyout.PutF[" ... "]; ch ← IF NOT g.confirmCompiles THEN 'y ELSE 'n; IF ch = 'n THEN ch ← CS.Confirm['y, g.ttyin, g.ttyout] ; IF ch = 'q THEN { g.ttyout.PutF["Quit.\n"]; ERROR IO.UserAborted[]; }; IF ch = 'a THEN { declined ← FALSE; g.confirmCompiles ← FALSE; g.ttyout.PutF["All Yes.\n"]; } ELSE IF ch = 'y THEN { declined ← FALSE; g.ttyout.PutF["Yes.\n"]; } ELSE g.ttyout.PutF["No.\n"]; }; ProduceDifferentialSwitches: PROC[sw: CompilerOps.LetterSwitches] RETURNS[dif: Rope.ROPE] = TRUSTED { standardSwitches: CompilerOps.LetterSwitches ← CompilerOps.DefaultSwitches[]; FOR c: CHAR IN ['a .. 'z] DO sd: BOOL ← IF c = 'p THEN FALSE ELSE standardSwitches[c]; IF sw[c] ~= sd THEN { IF sd THEN dif ← Rope.Cat[dif, Rope.FromChar['-]]; dif ← Rope.Cat[dif, Rope.FromChar[c]]; }; ENDLOOP; }; LogGetStream: PROC[sid: CompilerOps.StreamId] RETURNS[sh: Stream.Handle] = { IF sid = source THEN RETURN[sourcesh]; -- temporary IF sid ~= log THEN ERROR; IF logsh = NIL THEN TRUSTED { logpilotsh ← CS.NewStream["Compiler.Log", CS.Write]; logsh ← IO.CreateProcsStream[IO.CreateRefStreamProcs[putChar: LogStreamPutChar], NIL]; CS.SetPFCodes[logsh]; }; sh ← logpilotsh; }; LogStreamPutChar: SAFE PROC[self: IO.STREAM, char: CHAR] = TRUSTED { Stream.PutChar[logpilotsh, char]; }; CompilerPass: PROC[p: CARDINAL] RETURNS[goOn: BOOL] = { goOn ← NOT g.ttyin.UserAbort[]; g.msgout.PutF[". "]; }; NameToRope: PROC[name: BcdDefs.NameRecord, namestring: BcdOps.NameString] RETURNS[rope: Rope.Text] = TRUSTED { r: Rope.ROPE ← NIL; FOR i: CARDINAL IN [0 .. namestring.size[name]) DO r ← r.Cat[Rope.FromChar[namestring.string.text[name + i]]]; ENDLOOP; rope ← Rope.Flatten[r]; }; AcquireCompilerLock: ENTRY PROC[g: CT.Global] = { ENABLE UNWIND => NULL; p: BOOL ← TRUE; WHILE compilerIsLocked DO IF p THEN { g.ttyout.PutF["Waiting for free Compiler ... "]; p ← FALSE; }; WAIT compilerWait; ENDLOOP; compilerIsLocked ← TRUE; IF NOT p THEN g.ttyout.PutF["ok\n"]; }; ReleaseCompilerLock: ENTRY PROC = { ENABLE UNWIND => NULL; compilerIsLocked ← FALSE; NOTIFY compilerWait; }; GenUniqueBcdName: PUBLIC SAFE PROC[bcdFileName: Rope.Text] RETURNS[newName: Rope.Text] = TRUSTED { inx: CARDINAL ← 1; newName ← bcdFileName; DO newName ← RopeInline.InlineFlatten[ IO.PutFR["%s.%d.Bcd$", IO.rope[bcdFileName], IO.card[inx]]]; [] ← Directory.Lookup[fileName: LOOPHOLE[newName], permissions: Directory.ignore ! Directory.Error => GOTO out]; inx ← inx + 1; ENDLOOP; EXITS out => NULL; }; AddToBcdTabFile: PROC[bcdFileName: Rope.Text, cap: File.Capability, version: TimeStamp.Stamp] = { g.bcdTabList ← CONS[NEW[CT.BcdTabRecord ← [bcdFileName: bcdFileName, bcdCap: cap, bcdVers: version]], g.bcdTabList]; }; -- there may already be an entry for bcdFileName w/o a symbolSpace AddToBcdTabSymbolSpace: PROC[bcdFileName: Rope.Text, cap: File.Capability, version: TimeStamp.Stamp, symbolSpace: FileParms.SymbolSpace] = { g.bcdTabList ← CONS[NEW[CT.BcdTabRecord ← [bcdFileName: bcdFileName, bcdCap: cap, bcdVers: version, symbolSpace: symbolSpace]], g.bcdTabList]; }; LookupBcdTabFile: PROC[bcdFileName: Rope.Text] RETURNS [bcdTab: CT.BcdTab] = { FOR l: LIST OF CT.BcdTab ← g.bcdTabList, l.rest UNTIL l = NIL DO IF CS.EquivalentRope[l.first.bcdFileName, bcdFileName] THEN RETURN[l.first]; ENDLOOP; RETURN[NIL]; }; RemoveBcdTabFile: PROC[bcdFileName: Rope.Text] = TRUSTED { bcdTab: CT.BcdTab; WHILE (bcdTab ← LookupBcdTabFile[bcdFileName]) ~= NIL DO g.bcdTabList ← LOOPHOLE[List.DRemove[bcdTab, LOOPHOLE[g.bcdTabList]]]; ENDLOOP; }; SubStringToRope: PROC[lp: LongString.SubString] RETURNS[rope: Rope.Text] = TRUSTED { r: Rope.ROPE ← NIL; FOR i: CARDINAL IN [0 .. lp.length) DO r ← r.Cat[Rope.FromChar[lp.base[lp.offset+i]]]; ENDLOOP; rope ← Rope.Flatten[r]; }; AppendExtension: PUBLIC SAFE PROC[name: Rope.ROPE, ext: LONG STRING] RETURNS[r: Rope.Text] = TRUSTED { r ← RopeInline.InlineFlatten[name]; RETURN[ IF NOT CS.EndsIn[r, ext] THEN RopeInline.InlineFlatten[Rope.Cat[name, ConvertUnsafe.ToRope[ext]]] ELSE r]; }; -- to avoid long-ref containing unsafe warning message from compiler ReturnRecord: TYPE = RECORD[ found: BOOL ← FALSE, version: TimeStamp.Stamp ← TimeStamp.Null, bcdFileName: Rope.Text ← NIL, symbolSpace: FileParms.SymbolSpace ← FileParms.nullSymbolSpace ]; GetActualIdForFile: PROC[typename: Rope.Text, fileNameHint: Rope.Text] RETURNS[ref: REF ReturnRecord] = TRUSTED { bcdBase: BcdOps.BcdBase; sgb: BcdDefs.Base; firstMth: BcdOps.MTHandle; cap: File.Capability; namestring: BcdOps.NameString; bcdTab: CT.BcdTab; ref ← NEW[ReturnRecord ← []]; ref.bcdFileName ← fileNameHint; IF ref.bcdFileName.Length[] = 0 THEN ref.bcdFileName ← typename; IF NOT CS.EndsIn[ref.bcdFileName, "$"L] THEN ref.bcdFileName ← AppendExtension[ref.bcdFileName, ".Bcd"L]; bcdTab ← LookupBcdTabFile[ref.bcdFileName]; IF bcdTab ~= NIL AND bcdTab.symbolSpace ~= FileParms.nullSymbolSpace THEN { g.dout.PutF["Hit on %s\n", IO.rope[ref.bcdFileName]]; ref↑ ← [TRUE, bcdTab.bcdVers, ref.bcdFileName, bcdTab.symbolSpace]; RETURN; }; IF bcdTab = NIL THEN cap ← Directory.Lookup[fileName: LOOPHOLE[ref.bcdFileName], permissions: Directory.ignore ! Directory.Error => GOTO notFound] ELSE cap ← bcdTab.bcdCap; bcdBase ← LoadUpBcd[cap]; namestring ← LOOPHOLE[bcdBase + bcdBase.ssOffset]; firstMth ← @(LOOPHOLE[bcdBase,BcdDefs.Base] + bcdBase.mtOffset)[FIRST[BcdDefs.MTIndex]]; sgb ← LOOPHOLE[bcdBase, BcdDefs.Base] + bcdBase.sgOffset; ref.version ← bcdBase.version; ref.found ← TRUE; ref.symbolSpace ← [file: cap, span: [base: sgb[firstMth.sseg].base, pages: sgb[firstMth.sseg].pages]]; AddToBcdTabSymbolSpace[ref.bcdFileName, cap, ref.version, ref.symbolSpace]; Space.Unmap[manyPageSpace]; EXITS notFound => ref.found ← FALSE; }; LoadUpBcd: PROC[cap: File.Capability] RETURNS[bcdBase: BcdOps.BcdBase] = TRUSTED { npages: CARDINAL; IF cap = File.nullCapability THEN ERROR; Space.Map[space: manyPageSpace, window: [file: cap, base: 1]]; bcdBase ← Space.LongPointer[manyPageSpace]; npages ← bcdBase.nPages; IF npages > manyPageSize THEN { Space.Delete[manyPageSpace]; -- now map in the right number of pages manyPageSize ← npages + 10; manyPageSpace ← Space.Create[size: manyPageSize, parent: Space.virtualMemory]; Space.Map[space: manyPageSpace, window: [file: cap, base: 1]]; bcdBase ← Space.LongPointer[manyPageSpace]; Space.CreateUniformSwapUnits[parent: manyPageSpace, size: 8]; -- Space.MakeReadOnly[manyPageSpace]; }; }; }.