-- 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];
};
};
}.