VersionMapFromDFImpl.mesa
Copyright Ó 1984, 1985, 1986, 1987, 1990, 1992 by Xerox Corporation. All rights reserved.
Russ Atkinson (RRA) June 25, 1985 7:23:40 pm PDT
Doug Wyatt, January 26, 1987 2:46:58 pm PST
Bill Jackson (bj) August 16, 1988 4:40:15 pm PDT
Willie-Sue, February 9, 1990 1:01:47 pm PST
Willie-s, March 16, 1992 2:37 pm PST
Spreitze, August 17, 1990 2:55 pm PDT
Mna, October 25, 1990 4:13 pm PDT
Laurie Horton, November 1, 1990 9:53 am PST
DIRECTORY
Basics,
BasicTime USING [GMT, Now, nullGMT, ToNSTime],
CedarProcess,
Commander USING [CommandProc, Handle, Register],
CommanderOps,
DFUtilities USING [Date, RemoveVersionNumber],
FS,
GenerateDFClosure USING [ActionProc, ClosureInfo, GenerateClosureToProc],
GenMapIO USING [CreateOutputStreams, CloseTeeStream, PutRopes],
IO,
MobDefs,
PFS,
Process USING [CheckForAbort, Pause, SecondsToTicks],
Rope,
SystemNames USING [CedarDir],
VersionMap,
VersionMapBuilding,
VersionMapClassify;
VersionMapFromDFImpl: CEDAR MONITOR
LOCKS myData USING myData: MyData
IMPORTS BasicTime, CedarProcess, Commander, CommanderOps, DFUtilities, FS, GenerateDFClosure, GenMapIO, IO, PFS, Process, Rope, SystemNames, VersionMap, VersionMapBuilding, VersionMapClassify
SHARES IO, VersionMap
= BEGIN
T Y P E S
Date: TYPE = DFUtilities.Date;
LORA: TYPE = LIST OF REF ANY;
GMT: TYPE = BasicTime.GMT;
Map: TYPE = VersionMap.Map;
MapList: TYPE = VersionMap.MapList;
MapEntry: TYPE = VersionMap.MapEntry;
MapRep: TYPE = VersionMap.MapRep;
MyStamp: TYPE = VersionMap.MyStamp;
NullStamp: MyStamp = VersionMap.nullStamp;
MyStampAsHex: TYPE = PACKED ARRAY [0..2*BYTES[MyStamp]) OF [0..16);
ROPE: TYPE = Rope.ROPE;
RopeList: TYPE = LIST OF ROPE;
STREAM: TYPE = IO.STREAM;
VersionStamp: TYPE = VersionMap.VersionStamp;
NullVersion: VersionStamp = VersionMapClassify.NullVersion;
BadVersion: VersionStamp = VersionMapClassify.BadVersion;
FileEntry: TYPE = REF FileEntryRep;
FileEntryRep: TYPE = RECORD [
name: ROPE, date: Date, version: VersionStamp, from: ROPE];
NameSeq: TYPE = REF NameSeqRep;
NameSeqRep: TYPE = RECORD [
SEQUENCE len: NAT OF NameEntry];
NameEntry: TYPE = RECORD [hasPrefix: BOOL, name: ROPE];
WhichMap: TYPE ~ VersionMapClassify.KindSet;
WhichMapData: TYPE = RECORD[
muc: VersionMapBuilding.MapUnderConstruction ¬ NIL,
numFiles, numOld: INT ¬ 0,
mapName: PFS.PATH,
oldMap: MapList ¬ NIL];
WhichMapArray: TYPE = ARRAY WhichMap OF WhichMapData;
MyData: TYPE = REF MyDataRep;
MyDataRep: TYPE = MONITORED RECORD [
mapData: WhichMapArray ¬ ALL[],
prefixList: RopeList ¬ NIL,
warningMatch: ROPE ¬ NIL,
errs: STREAM ¬ NIL,
totalFiles, totalOld: INT ¬ 0,
checkRemote: BOOL ¬ TRUE,
abortRequested, mobStamps: BOOL ¬ FALSE
];
Global variables
pauseForServerGlitch: [0..3600] ¬ 90; -- seconds to pause if server busy
triesForServerGlitch: NAT ¬ 12; -- times to retry if server busy
forkers: NAT ¬ 2; -- # of processes to fork
veryVerbose: BOOL ¬ FALSE;
outLog: IO.STREAM ¬ NIL;
pacifyPeriod: INTEGER ¬ 250;
Single file processing
EachFile: GenerateDFClosure.ActionProc = {
[data: REF, kind: {file, notFound, syntaxError}, name: ROPE, date: Date, from: ROPE]
myData: MyData ¬ NARROW[data];
which: WhichMap ¬ unknown;
CheckAbort[myData];
SELECT kind FROM
file => {
needsVersion: BOOL ¬ FALSE;
entry: FileEntryRep ¬ [
name: name, date: date, version: NullVersion, from: from];
triesLeft: NAT ¬ triesForServerGlitch;
which ¬ unknown;
IF Rope.Match[myData.warningMatch, name, FALSE] THEN
GenMapIO.PutRopes[myData.errs,
"Warning match\n for ", name, "\n from ", from];
which ¬ VersionMapClassify.Classify[name];
IF NOT Rope.Match["[]*", name] THEN {
We need to merge this prefix in with the rest
pos: INT ¬ Rope.Find[name, ">"];
prefix: ROPE ¬ Rope.Flatten[name, 0, pos+1];
lag: RopeList ¬ NIL;
FOR each: RopeList ¬ myData.prefixList, each.rest WHILE each # NIL DO
IF Rope.Equal[prefix, each.first, FALSE] THEN GO TO found;
lag ¬ each;
ENDLOOP;
IF lag = NIL THEN myData.prefixList ¬ LIST[prefix] ELSE lag.rest ¬ LIST[prefix];
EXITS found => {};
};
DO
ENABLE
FS.Error => {
SELECT error.code FROM
$serverInaccessible => {
GenMapIO.PutRopes[myData.errs, "Server glitch: ", name];
Process.Pause[Process.SecondsToTicks[pauseForServerGlitch]];
IF (triesLeft ¬ triesLeft - 1) # 0 THEN LOOP;
};
ENDCASE;
GenMapIO.PutRopes[
myData.errs, "File not found: ", name,
IO.PutFR["\n from: %g\n reason: %g\n",
[rope[from]], [rope[error.explanation]]]];
IF error.group # bug THEN EXIT ELSE REJECT;
};
IF myData.mobStamps THEN SELECT which FROM
unknown, source => FillInSourceVersion[which, myData, entry, myData.checkRemote];
intermediate, sparc, sparcOpt => FillInObjectVersion[which, myData, entry, myData.checkRemote];
ENDCASE => ERROR
ELSE FillInSourceVersion[which, myData, entry, myData.checkRemote];
EXIT;
ENDLOOP;
};
ENDCASE;
};
FillInSourceVersion: PROC [which: WhichMap, myData: MyData, entry: FileEntryRep, checkRemote: BOOL ¬ TRUE] = {
created: BasicTime.GMT ¬ entry.date.gmt;
initName: ROPE ¬ entry.name;
full: ROPE ¬ initName;
attached: ROPE ¬ NIL;
version: VersionStamp;
useFileSystem: BOOL ¬ FALSE;
IF entry.date.format = explicit AND created # BasicTime.nullGMT
THEN {
There is an explicit date in the DF file
version ¬ CreatedToMimosaVersionStamp[created];
IF checkRemote THEN {
We have been instructed to verify the DF file date, either through the remote server OR through the old source version map.
IF myData.mapData[which].oldMap = NIL
THEN useFileSystem ¬ TRUE
ELSE {
We have been instructed to TRUST the old map (if the file is present). However, to guard against bogus matches for names that have the same versions, we also check the name for agreement up to the version.
valid: BOOL;
fromMap: ROPE;
[valid, fromMap, ] ¬ VersionMapClassify.MapListLookup[myData.mapData[which].oldMap, full, created, TRUE, FALSE, TRUE];
IF valid THEN full ¬ fromMap ELSE useFileSystem ¬ TRUE;
};
};
}
ELSE {
For a non-explicit date, we can't trust anything.
useFileSystem ¬ TRUE;
created ¬ BasicTime.nullGMT;
checkRemote ¬ TRUE;
};
At this point, created = BasicTime.nullGMT if we are supposed to check the remote file server. If this check fails, it is the caller's responsibility to handle the error.
IF useFileSystem THEN {
IF created = BasicTime.nullGMT THEN full ¬ DFUtilities.RemoveVersionNumber[full];
[created: created, fullFName: full, attachedTo: attached] ¬
FS.FileInfo[name: full, wantedCreatedTime: created, remoteCheck: checkRemote];
version ¬ CreatedToMimosaVersionStamp[created];
};
At this point, created # BasicTime.nullGMT, and the full name is presumed correct (although attached may take precedence as the 'real' name). So it is time to insert this file entry into the priority queue.
IF attached # NIL THEN entry.name ¬ attached ELSE entry.name ¬ full;
IF Rope.Length[entry.name] = 0
THEN {
I wonder how this happened?
GenMapIO.PutRopes[
myData.errs, "Warning, failed to get source version for ", initName];
}
ELSE {
Put the entry into the priority queue for later handling
myData.mapData[which].muc.AddFile[[entry.name, entry.from, created, version]];
IncCount[which, myData, NOT useFileSystem];
};
};
IncCount: PROC [which: WhichMap, myData: MyData, old: BOOL] ~ {
myData.mapData[which].numFiles ¬ myData.mapData[which].numFiles + 1;
IF old THEN {
myData.mapData[which].numOld ¬ myData.mapData[which].numOld + 1;
myData.totalOld ¬ myData.totalOld.SUCC};
IF (myData.totalFiles ¬ myData.totalFiles + 1) MOD pacifyPeriod = 0 THEN
GenMapIO.PutRopes[myData.errs, IO.PutFR["%g'th file (%g old) processed at %g.", [integer[myData.totalFiles]], [integer[myData.totalOld]], [time[BasicTime.Now[]]] ]];
RETURN};
FillInObjectVersion: PROC [which: WhichMap, myData: MyData, entry: FileEntryRep, checkRemote: BOOL ¬ TRUE] = {
Read the version number from the .mob, .c, or .o. Too bad we have to get the whole file (in DCedar) !
outerCreated: BasicTime.GMT ¬ entry.date.gmt;
full: ROPE ¬ entry.name;
attached, whyNot, warning: ROPE ¬ NIL;
valid: BOOL ¬ FALSE; -- indicates validity of version stamp
{
SELECT TRUE FROM
Rope.SkipTo[full, 0, "!"] = Rope.Length[full] => {
How did this happen?
valid ¬ FALSE;
};
entry.date.format # explicit OR outerCreated = BasicTime.nullGMT => {
Don't trust the version map, we have to look at the server.
valid ¬ FALSE;
};
myData.mapData[which].oldMap # NIL => {
We have an explicit date with which we can check the existing version map
[valid, , entry.version] ¬ VersionMapClassify.MapListLookup[myData.mapData[which].oldMap, full, outerCreated, FALSE, TRUE, FALSE];
IF valid THEN GOTO enter;
};
ENDCASE => {
valid ¬ FALSE;
};
outerCreated ¬ BasicTime.nullGMT;
full ¬ DFUtilities.RemoveVersionNumber[full];
For object files we are not sure whether we can trust the version number, so we MUST ask the file system. We use the resulting full file name as the truth when searching the old object map. As a side effect, the date in the entry is now explicit.
[created: outerCreated, fullFName: full, attachedTo: attached] ¬ FS.FileInfo[
name: full, wantedCreatedTime: outerCreated, remoteCheck: checkRemote];
entry.date ¬ [format: explicit, gmt: outerCreated];
IF attached # NIL THEN full ¬ attached;
entry.name ¬ full;
[valid, , entry.version] ¬ VersionMapClassify.MapListLookup[myData.mapData[which].oldMap, full, BasicTime.nullGMT, FALSE, FALSE, TRUE];
IF NOT valid THEN [entry.version, whyNot, warning] ¬ VersionMapClassify.ReadVersionStamp[which, full, outerCreated, TRUE];
IF warning#NIL THEN GenMapIO.PutRopes[myData.errs, "Note: ", warning, "."];
IF whyNot#NIL THEN GenMapIO.PutRopes[myData.errs, "Warning: can't read version stamp because ", whyNot, "."];
EXITS enter => valid ¬ valid;
};
myData.mapData[which].muc.AddFile[[entry.name, entry.from, entry.date.gmt, entry.version]];
IncCount[which, myData, valid];
RETURN};
VsToMs: PROC [vs: VersionStamp] RETURNS [MyStamp] = {
l0: Basics.LongNumber ~ [card[vs[0]]];
l1: Basics.LongNumber ~ [card[vs[1]]];
RETURN [[lo: l1.hi, num: l0.lo, hi: l0.hi, extra: l1.lo]]};
MsToVs: PROC [ms: MyStamp] RETURNS [VersionStamp] = {
l0: Basics.LongNumber ~ [pair[lo: ms.num, hi: ms.hi]];
l1: Basics.LongNumber ~ [pair[lo: ms.extra, hi: ms.lo]];
RETURN [[l0.card, l1.card]]};
CreatedToMimosaVersionStamp: PUBLIC PROC [created: BasicTime.GMT] RETURNS [VersionStamp] = {
RETURN [[BasicTime.ToNSTime[created], 0]]};
WriteMapToFile: PROC [lm: VersionMapBuilding.LimitedMap, name: ROPE] = {
map: Map ~ lm.map;
prefix: ROPE ¬ NIL;
st: IO.STREAM ¬ FS.StreamOpen[name, create];
count: NAT ¬ lm.limit;
names: ROPE ¬ map.names;
firstCR: INT ¬ names.Index[0, "\n"];
Process.CheckForAbort[];
prefix ¬ names.Flatten[0, firstCR];
st.PutRope[prefix];
st.PutRope["\n"];
FOR i: NAT IN [0..count) DO
Display the entry.
entry: MapEntry ¬ map[i];
index: INT ¬ entry.index;
stamp: MyStamp ¬ entry.stamp;
PutStampAsHex[st, stamp];
st.PutRope[" "];
DO
c: CHAR ¬ names.Fetch[index];
index ¬ index + 1;
st.PutChar[c];
IF c = '\n THEN EXIT;
ENDLOOP;
ENDLOOP;
st.PutRope["\n"];
IO.SetLength[st, IO.GetIndex[st]]; -- force goddamm truncation already!!!
IO.Close[st];
};
IndexToShortName: PROC [map: Map, index: INT] RETURNS [ROPE] = {
Return the name without the prefix or version
names: ROPE ¬ map.names;
end: INT ¬ names.SkipTo[index, "!\n"];
pos: INT ¬ end;
WHILE pos > index DO
SELECT names.Fetch[pos] FROM
'/, '], '> => EXIT;
ENDCASE;
pos ¬ pos - 1;
ENDLOOP;
RETURN [names.Substr[pos, end-pos]];
};
PutStampAsHex: PROC [st: IO.STREAM, stamp: MyStamp] = {
hex: MyStampAsHex ¬ LOOPHOLE[stamp];
FOR index: CARDINAL IN [0..2*BYTES[MyStamp]) DO
x: CARDINAL [0..15] ¬ hex[index];
c: CHAR ¬ IF x IN [0..9] THEN '0 + x ELSE 'A + (x-10);
st.PutChar[c];
ENDLOOP;
};
ScanName: PROC [name: ROPE] RETURNS [pos,bang,dot: INT] = {
len: INT = Rope.Length[name];
pos ¬ bang ¬ dot ¬ len;
WHILE pos > 0 DO
posM: INT = pos-1;
SELECT Rope.Fetch[name, posM] FROM
'! => bang ¬ dot ¬ posM;
'. => dot ¬ posM;
'>, '/, '] => RETURN;
ENDCASE;
pos ¬ posM;
ENDLOOP;
};
CheckAbort: ENTRY PROC [myData: MyData] = {
ENABLE UNWIND => myData.abortRequested ¬ TRUE;
IF myData.abortRequested THEN RETURN WITH ERROR ABORTED;
Process.CheckForAbort[];
};
CheckAbortInternal: INTERNAL PROC [myData: MyData] = {
ENABLE UNWIND => myData.abortRequested ¬ TRUE;
IF myData.abortRequested THEN RETURN WITH ERROR ABORTED;
Process.CheckForAbort[];
};
hashMark: ROPE = Rope.Cat["@(", "#)mob", "←version ["]; --broken up to avoid spurrious one
sourceStampByteLimit: INT ¬ 8000;
headerBytes: INT = 4+4*7; -- size of header for .o files
textSizeOffset: INT = 4; -- byte offset of text size field in .o files
Routines to use the old maps for verification of names
ShortName: PROC [full: ROPE] RETURNS [ROPE] = {
pos: INT ¬ Rope.Length[full];
lim: INT ¬ pos;
WHILE pos > 0 DO
c: CHAR = Rope.Fetch[full, pos¬pos-1];
SELECT c FROM
'! => lim ¬ pos;
'>, '] => {pos ¬ pos + 1; EXIT};
ENDCASE;
ENDLOOP;
RETURN [Rope.Flatten[full, pos, lim-pos]];
};
RopeFromWhich: PROC[which: WhichMap] RETURNS[ROPE] = {
RETURN[SELECT which FROM
source => "Source",
intermediate => "Intermediate",
sparc => "SparcExecutable",
sparcOpt => "SparcOptExecutable",
ENDCASE => "Unknown"
];
};
Main command routine
Quit: ERROR = CODE;
GenMap: Commander.CommandProc = {
[cmd: Handle] RETURNS [result: REFNIL, msg: ROPENIL]
dump: BOOL ¬ TRUE;
dumpDebug: BOOL ¬ TRUE;
namesOnly: BOOL ¬ FALSE;
remoteOpened: BOOL ¬ FALSE;
pos, bang, dot: INT ¬ 0;
inName, sourceName, derivedName, outPrefix: ROPE ¬ NIL;
command: ROPE ¬ cmd.command;
results: GenerateDFClosure.ClosureInfo;
myData: MyData;
newMaps: ARRAY WhichMap OF VersionMapBuilding.LimitedMap ¬ ALL[[NIL,0]];
checkRemote: BOOL ¬ TRUE;
genMaps: BOOL ¬ FALSE; -- gen regular version maps if TRUE
trustOld: BOOL ¬ FALSE;  -- trust old version maps if TRUE
prefix: ROPE ¬ NIL;  -- the first prefix seen
oldPath: ROPE ¬ SystemNames.CedarDir[release: previous]; -- the previous release
switches: PACKED ARRAY CHAR['a..'z] OF BOOL ¬ ALL[FALSE];
logFileName: ROPE ¬ "VersionMapBuilder.log";
ProcessArgument: PROC = {
[pos,bang,dot] ¬ ScanName[inName];
IF bang = Rope.Size[inName] -- no version stuff
AND (bang-dot # 3 OR Rope.Run[inName, dot, ".df", 0, FALSE] # 3) -- no DF extension
THEN inName ¬ Rope.Concat[inName, ".df"];
inName ¬ FS.FileInfo[inName
! FS.Error =>
IF error.group # bug THEN {
IO.PutF[
cmd.out, "\nCan't open %g\n %g\n",
[rope[inName]], [rope[error.explanation]]];
ERROR Quit;
};
].fullFName;
outLog ¬ GenMapIO.CreateOutputStreams[logFileName, IF switches['t] THEN cmd.out ELSE NIL];
[pos,bang,dot] ¬ ScanName[inName];
outPrefix ¬ Rope.Flatten[inName, pos, dot-pos];
outLog.PutF1["\nVersion Maps for Cedar Builder started at %g", [time[BasicTime.Now[]]]];
outLog.PutF1["\n\nInput from: %g\n", [rope[inName]]];
{
SELECT cmd.procData.clientData FROM
$GenMap => {
genMaps ¬ TRUE;
};
$GenMapX => {
genMaps ¬ TRUE;
veryVerbose ¬ TRUE;
};
$MergeMap => {
trustOld ¬ genMaps ¬ TRUE;
};
$GenCedarMap => {
genMaps ¬ TRUE;
outPrefix ¬ "Cedar";
};
$MergeCedarMap => {
trustOld ¬ genMaps ¬ TRUE;
outPrefix ¬ "Cedar";
};
ENDCASE;
myData ¬ NEW[MyDataRep ¬ [
errs: outLog,
checkRemote: checkRemote,
mobStamps: switches['l]
]];
IF myData.mobStamps
THEN outLog.PutRope["\nExtracting Mimosa-style version stamps, and writing 64-bit-version-format files."]
ELSE outLog.PutRope["\nUsing create dates as version stamps, and writing 48-bit-version-format files."];
IF switches['d]
THEN outLog.PutRope["\nAllowing duplicates."]
ELSE outLog.PutRope["\nSuppressing duplicates."];
FOR i: WhichMap IN [source .. sparcOpt] DO
myData.mapData[i].muc ¬ VersionMapBuilding.StartConstruction[2000];
myData.mapData[i].mapName ¬ PFS.PathFromRope[IO.PutFR["%g%g.VersionMap", [rope[outPrefix]], [rope[RopeFromWhich[i]]]] ];
ENDLOOP;
SELECT cmd.procData.clientData FROM
$GenCedarMap, $MergeCedarMap =>
myData.warningMatch ¬ Rope.Concat[oldPath, "*.df*"];
ENDCASE;
IF trustOld THEN {
Setup the old maps (if any); also make an index for the derived map.
outLog.PutRope["\nReading old maps."];
FOR i: WhichMap IN [source .. sparcOpt] DO
myData.mapData[i].oldMap ¬ LIST[
VersionMap.RestoreMapFromFile[myData.mapData[i].mapName,
! FS.Error => {
outLog.PutRope["\nWarning, can't read "];
outLog.PutRope[ PFS.RopeFromPath[myData.mapData[i].mapName] ];
CONTINUE}] ]
ENDLOOP;
};
CheckAbort[myData];
outLog.PutRope["\nGenerating DF closure."];
results ¬ GenerateDFClosure.GenerateClosureToProc[
inName, outLog, EachFile, myData, [toFork: forkers, followImports: NOT switches['s]]];
};
{
outLog.PutF1["\n\n%g DF files", [integer[results.dfFiles]] ];
FOR which: WhichMap IN [source .. sparcOpt] DO
num: INT = myData.mapData[which].numFiles;
IF num = 0 THEN LOOP;
outLog.PutF[", %g %g files (%g from old map)", [integer[num]], [rope[RopeFromWhich[which]]], [integer[myData.mapData[which].numOld]] ];
ENDLOOP;
outLog.PutRope["\nPrefixes seen:"];
FOR each: RopeList ¬ myData.prefixList, each.rest WHILE each # NIL DO
IF prefix = NIL THEN prefix ¬ each.first;
outLog.PutF1["\n %g", [rope[each.first]]];
ENDLOOP;
FOR which: WhichMap IN [source .. sparcOpt] DO
ReportDuplicate: PROC [a, b: VersionMapBuilding.ConsTuple] ~ {
dc: VersionMapBuilding.DifferenceClass ~ VersionMapBuilding.ClassifyDifference[a, b];
IF dc#identicalModExtensionAndCreated THEN IO.PutFL[outLog,
"\n%g, duplicate versions for\n %g\n from: %g\n %g\n from: %g",
LIST[ [rope[IF dc=identical THEN "Note" ELSE "Warning"]], [rope[a.name]], [rope[a.from]], [rope[b.name]], [rope[b.from]]] ];
RETURN};
thisName: ROPE ~ RopeFromWhich[which];
thisFile: ROPE;
IF myData.mapData[which].numFiles = 0 THEN {
outLog.PutF1["\n\nWarning: %gMap empty\n", [rope[thisName]] ];
LOOP;
};
outLog.PutF1["\n\nMaking short name index for %g map.", [rope[RopeFromWhich[which]]] ];
newMaps[which] ¬ VersionMapBuilding.FinishLimitedConstruction[myData.mapData[which].muc, prefix, ReportDuplicate, NOT switches['d]];
CheckAbort[myData];
thisFile ¬ IO.PutFR["%g%g.VersionMap", [rope[outPrefix]], [rope[thisName]] ];
outLog.PutRope["\n\nWriting "];
outLog.PutRope[thisFile];
outLog.PutRope["\n"];
IF genMaps
THEN VersionMapBuilding.LimitedSave[newMaps[which], PFS.PathFromRope[thisFile], switches['l]]
ELSE WriteMapToFile[newMaps[which], thisFile];
ENDLOOP;
outLog.PutF1["\n\nVersion maps built at %g\n\n", [time[BasicTime.Now[]]]];
IF NOT switches['t] THEN outLog.Close[] ELSE GenMapIO.CloseTeeStream[];
};
};
ProcessSwitches: PROC [arg: ROPE] = {
sense: BOOL ¬ TRUE;
FOR index: INT IN [0..Rope.Length[arg]) DO
char: CHAR ¬ Rope.Fetch[arg, index];
SELECT char FROM
'- => LOOP;
'~ => {sense ¬ NOT sense; LOOP};
IN ['a..'z] => switches[char] ¬ sense;
IN ['A..'Z] => switches[char + ('a-'A)] ¬ sense;
ENDCASE;
sense ¬ TRUE;
ENDLOOP;
};
argv: CommanderOps.ArgumentVector ¬ CommanderOps.Parse[cmd: cmd
! CommanderOps.Failed => {msg ¬ errorMsg; GO TO failed}];
When parsing the command line, be prepared for failure. The error is reported to the user
veryVerbose ¬ FALSE;
FOR i: NAT IN [1..argv.argc) DO
Each argument can either be a switch specification or a genuine argument to be processed. The first argument (argv[0]) is not examined, because by convention it is the name of the command as given by the user.
arg: ROPE = argv[i];
IF Rope.Length[arg] = 0 THEN LOOP;
Ignore null arguments (it is not easy to generate them, even).
IF Rope.Fetch[arg, 0] = '- THEN {
This argument sets switches for the remaining patterns. By convention, switches are normally "sticky", in that they stay set until explicitly changed.
ProcessSwitches[arg];
LOOP;
};
inName ¬ arg;
CedarProcess.DoWithPriority[background, ProcessArgument
! Quit, ABORTED => GO TO quit];
ENDLOOP;
IF inName = NIL THEN {
inName ¬ "Cedar.df";
CedarProcess.DoWithPriority[background, ProcessArgument
! Quit, ABORTED => GO TO quit];
};
EXITS
quit => {};
failed => result ¬ $Failed;
};
GenMapFromPatterns: Commander.CommandProc = {
[cmd: Handle] RETURNS [result: REFNIL, msg: ROPENIL]
dump: BOOL ¬ TRUE;
dumpDebug: BOOL ¬ TRUE;
namesOnly: BOOL ¬ FALSE;
remoteOpened: BOOL ¬ FALSE;
pos, bang, dot: INT ¬ 0;
startName, sourceName, derivedName, outPrefix: ROPE ¬ NIL;
command: ROPE ¬ cmd.command;
npats: INT ¬ 0;
myData: MyData;
newMaps: ARRAY WhichMap OF VersionMapBuilding.LimitedMap ¬ ALL[[NIL,0]];
checkRemote: BOOL ¬ TRUE;
genMaps: BOOL ¬ TRUE;  -- gen regular version maps if TRUE
trustOld: BOOL ¬ FALSE;  -- trust old version maps if TRUE
long: BOOL ¬ FALSE;  -- use mob stamps, and write long
suppressDuplicates: BOOL ¬ TRUE;
toTypescript: BOOL ¬ FALSE; -- output to ts instead of viewer
prefix: ROPE ¬ NIL;  -- the first prefix seen
logFileName: ROPE ¬ "VersionMapBuilder.log";
oldPath: ROPE ¬ SystemNames.CedarDir[release: previous]; -- the previous release
recursive: BOOL ¬ FALSE;
Setup: PROC = {
outLog ¬ GenMapIO.CreateOutputStreams[logFileName, IF toTypescript THEN cmd.out ELSE NIL];
outLog.PutF1["\nVersion Maps for Cedar Builder started at %g", [time[BasicTime.Now[]]]];
myData ¬ NEW[MyDataRep ¬ [
errs: outLog,
checkRemote: checkRemote,
mobStamps: long
]];
IF myData.mobStamps
THEN outLog.PutRope["\nExtracting Mimosa-style version stamps, and writing 64-bit-version-format files."]
ELSE outLog.PutRope["\nUsing create dates as version stamps, and writing 48-bit-version-format files."];
IF suppressDuplicates
THEN outLog.PutRope["\nSuppressing duplicates."]
ELSE outLog.PutRope["\nAllowing duplicates."];
FOR i: WhichMap IN [source .. sparcOpt] DO
myData.mapData[i].muc ¬ VersionMapBuilding.StartConstruction[2000];
myData.mapData[i].mapName ¬ PFS.PathFromRope[IO.PutFR["%g%g.VersionMap", [rope[outPrefix]], [rope[RopeFromWhich[i]]]] ];
ENDLOOP;
IF trustOld THEN {
Setup the old maps (if any); also make an index for the derived map.
outLog.PutRope["\nReading old maps."];
FOR i: WhichMap IN [source .. sparcOpt] DO
myData.mapData[i].oldMap ¬ LIST[
VersionMap.RestoreMapFromFile[myData.mapData[i].mapName,
! FS.Error => {
outLog.PutRope["\nWarning, can't read "];
outLog.PutRope[ PFS.RopeFromPath[myData.mapData[i].mapName] ];
CONTINUE}] ]
ENDLOOP;
};
};
ProcessArgument: PROC = { DoProcessArgument[startName] };
DoProcessArgument: PROC[inName: ROPE] = {
PassInfo: PROC [fullFName, attachedTo: ROPE, created: BasicTime.GMT, bytes: INT, keep: CARDINAL, fileType: FS.FileType] RETURNS [continue: BOOL] --FS.InfoProc-- ~ {
CheckAbort[myData];
SELECT TRUE FROM
( fileType = FS.tDirectory ) AND recursive =>
DoProcessArgument[Rope.Concat[fullFName, ">*"]];
( bytes>=0 ) => EachFile[myData, file, fullFName, [explicit, created], inName];
ENDCASE;
RETURN [TRUE]
};
CheckAbort[myData];
outLog.PutF1["\n\nEnumerating %g\n", [rope[inName]]];
FS.EnumerateForInfo[inName, PassInfo !FS.Error => {
outLog.PutF["\nFS.Error[%g, %g] while enumerating %g; enumeration aborted.\n", [atom[error.code]], [rope[error.explanation]], [rope[inName]] ];
CONTINUE}];
npats ¬ npats + 1};
WriteResults: PROC = {
outLog.PutF1["\n\n%g patterns", [integer[npats]] ];
FOR which: WhichMap IN [source .. sparcOpt] DO
num: INT = myData.mapData[which].numFiles;
IF num = 0 THEN LOOP;
outLog.PutF[", %g %g files (%g from old map)", [integer[num]], [rope[RopeFromWhich[which]]], [integer[myData.mapData[which].numOld]] ];
ENDLOOP;
outLog.PutRope["\nPrefixes seen:"];
FOR each: RopeList ¬ myData.prefixList, each.rest WHILE each # NIL DO
IF prefix = NIL THEN prefix ¬ each.first;
outLog.PutF1["\n %g", [rope[each.first]]];
ENDLOOP;
FOR which: WhichMap IN [source .. sparcOpt] DO
ReportDuplicate: PROC [a, b: VersionMapBuilding.ConsTuple] ~ {
dc: VersionMapBuilding.DifferenceClass ~ VersionMapBuilding.ClassifyDifference[a, b];
IF dc#identicalModExtensionAndCreated THEN IO.PutFL[outLog,
"%g, duplicate versions for\n %g\n from: %g\n %g\n from: %g\n",
LIST[ [rope[IF dc=identical THEN "Note" ELSE "Warning"]], [rope[a.name]], [rope[a.from]], [rope[b.name]], [rope[b.from]]] ];
RETURN};
thisName: ROPE ~ RopeFromWhich[which];
thisFile: ROPE;
IF myData.mapData[which].numFiles = 0 THEN {
outLog.PutF1["\n\nWarning: %gMap empty\n", [rope[thisName]] ];
LOOP;
};
outLog.PutF1["\n\nMaking short name index for %g map.", [rope[RopeFromWhich[which]]] ];
newMaps[which] ¬ VersionMapBuilding.FinishLimitedConstruction[myData.mapData[which].muc, prefix, ReportDuplicate, suppressDuplicates];
CheckAbort[myData];
thisFile ¬ IO.PutFR["%g%g.VersionMap", [rope[outPrefix]], [rope[thisName]] ];
outLog.PutRope["\n\nWriting "];
outLog.PutRope[thisFile];
outLog.PutRope["\n"];
IF genMaps
THEN VersionMapBuilding.LimitedSave[newMaps[which], PFS.PathFromRope[thisFile], long]
ELSE WriteMapToFile[newMaps[which], thisFile];
ENDLOOP;
outLog.PutF1["\n\nVersion maps built at %g\n\n", [time[BasicTime.Now[]]]];
IF NOT toTypescript THEN outLog.Close[] ELSE GenMapIO.CloseTeeStream[];
};
ProcessSwitches: PROC [arg: ROPE] = {
sense: BOOL ¬ TRUE;
FOR index: INT IN [0..Rope.Length[arg]) DO
char: CHAR ¬ Rope.Fetch[arg, index];
SELECT char FROM
'- => LOOP;
'~ => {sense ¬ NOT sense; LOOP};
'd, 'D => suppressDuplicates ¬ NOT sense;
'l, 'L => long ¬ sense;
'm, 'M => trustOld ¬ sense;
'r, 'R => recursive ¬ sense;
't, 'T => toTypescript ¬ sense;
'x, 'X => veryVerbose ¬ sense;
ENDCASE;
sense ¬ TRUE;
ENDLOOP;
};
argv: CommanderOps.ArgumentVector ¬ CommanderOps.Parse[cmd: cmd
! CommanderOps.Failed => {msg ¬ errorMsg; GO TO failed}];
When parsing the command line, be prepared for failure. The error is reported to the user
committed: BOOL ¬ FALSE;
IF argv.argc < 3 THEN RETURN [$Failed, "Usage: GenMapFromPatterns Root (-(d|l|m|t|x)*)* pattern+"];
outPrefix ¬ argv[1];
veryVerbose ¬ FALSE;
FOR i: NAT IN [2..argv.argc) DO
Each argument can either be a switch specification or a genuine argument to be processed. The first argument (argv[0]) is not examined, because by convention it is the name of the command as given by the user.
arg: ROPE = argv[i];
IF Rope.Length[arg] = 0 THEN LOOP;
Ignore null arguments (it is not easy to generate them, even).
IF Rope.Fetch[arg, 0] = '- THEN {
This argument sets switches for the remaining patterns. By convention, switches are normally "sticky", in that they stay set until explicitly changed.
IF committed
THEN cmd.err.PutF1["Don't give switches (like %g) after patterns.\n", [rope[arg]]]
ELSE
ProcessSwitches[arg];
LOOP;
};
IF NOT committed THEN {
CedarProcess.DoWithPriority[background, Setup
! Quit, ABORTED => GO TO quit];
committed ¬ TRUE;
};
startName ¬ arg;
CedarProcess.DoWithPriority[background, ProcessArgument
! Quit, ABORTED => GO TO quit];
ENDLOOP;
IF committed
THEN CedarProcess.DoWithPriority[background, WriteResults
! Quit, ABORTED => GO TO quit]
ELSE cmd.err.PutRope["No work to do!\n"];
EXITS
quit => {};
failed => result ¬ $Failed;
};
ReadVersionStampCommand: PROC [cmd: Commander.Handle] RETURNS [result: REF ANY ¬ NIL, msg: ROPE ¬ NIL] --Commander.CommandProc-- ~ {
argv: CommanderOps.ArgumentVector ~ CommanderOps.Parse[cmd];
FOR i: NAT IN (0 .. argv.argc) DO
name: ROPE ~ argv[i];
kind: VersionMapClassify.Kind ~ VersionMapClassify.Classify[name];
created: BasicTime.GMT;
stamp: VersionStamp;
whyNot, warning: ROPE;
created ¬ FS.FileInfo[name !FS.Error => {cmd.err.PutF["FS.FileInfo[%g] => FS.Error[%g, %g]\n", [rope[name]], [atom[error.code]], [rope[error.explanation]] ]; LOOP}].created;
[stamp, whyNot, warning] ¬ VersionMapClassify.ReadVersionStamp[kind, name, created, FALSE];
IO.PutFL[cmd.out, "ReadVersionStamp[%g] => (%g) %08x%08x",
LIST[ [rope[name]], [rope[VersionMapClassify.KindName[kind]]], [cardinal[stamp[0]]], [cardinal[stamp[1]]]] ];
IF whyNot#NIL THEN IO.PutF1[cmd.out, ", whyNot: %g", [rope[whyNot]] ];
IF warning#NIL THEN IO.PutF1[cmd.out, ", warning: %g", [rope[warning]] ];
IO.PutRope[cmd.out, ".\n"];
ENDLOOP;
RETURN};
Commander.Register[
"ReadVersionStamp", ReadVersionStampCommand, "reads version stamp from given files"];
Commander.Register[
"GenMap", GenMap, "Generate version maps", $GenMap];
stores to *Source.VersionMap, *Intermediate.VersionMap, *SparcExecutable.VersionMap , & *SparcOptExecutable.VersionMap
Commander.Register[
"GenMapX", GenMap, "Generate version maps", $GenMapX];
stores to *Source.VersionMap, *Intermediate.VersionMap, *SparcExecutable.VersionMap , & *SparcOptExecutable.VersionMap
Commander.Register[
"MergeMap", GenMap, "Merge version maps", $MergeMap];
stores to *Source.VersionMap, *Intermediate.VersionMap, *SparcExecutable.VersionMap , & *SparcOptExecutable.VersionMap
Commander.Register[
"GenCedarMap", GenMap, "Generate Cedar version maps", $GenCedarMap];
stores to CedarSource.VersionMap, CedarIntermediate.VersionMap, CedarSparcExecutable.VersionMap, & CedarSparcOptExecutable.VersionMap
Commander.Register[
"MergeCedarMap", GenMap, "Merge Cedar version maps", $MergeCedarMap];
stores to CedarSource.VersionMap, CedarIntermediate.VersionMap, CedarSparcExecutable.VersionMap, & CedarSparcOptExecutable.VersionMap
Commander.Register[
"GenMapFromPatterns", GenMapFromPatterns, "Root (-(d|l|m|r|t|x)*)* pattern+ --- generates version maps from file name patterns"];
END.