GetFromReleaseImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Michael Plass, March 12, 1985 12:51:10 pm PST
Rick Beach, June 15, 1985 4:43:22 pm PDT
Spreitzer, September 17, 1985 9:52:39 pm PDT
Mike Spreitzer July 14, 1986 5:05:31 pm PDT
Eric Nickell, September 5, 1986 10:56:40 am PDT
DIRECTORY
Basics USING [Comparison],
BasicTime USING [GMT, nullGMT, Period],
Commander USING [CommandProc, Handle, Register],
CommandTool USING [ArgumentVector, Parse],
DFUtilities USING [Date, ImportsItem, ParseFromStream, SearchUsingList, SortUsingList, UsingEntry, UsingList, WriteItemToStream],
FS USING [ComponentPositions, Copy, Error, ExpandName, FileInfo, StreamOpen],
IO USING [Close, PutF, PutRope, rope, STREAM],
RedBlackTree USING [Create, Delete, EnumerateIncreasing, Insert, Lookup, LookupNextLarger, Size, Table],
Rope USING [Cat, Compare, Concat, Equal, Fetch, Find, IsEmpty, Length, ROPE, Substr],
RopeFile USING [Create],
VersionMap USING [MapAndNameList, MapList, ShortNameToNames],
VersionMapDefaults USING [GetMapList];
GetFromReleaseImpl: CEDAR PROGRAM
IMPORTS BasicTime, Commander, CommandTool, DFUtilities, FS, IO, RedBlackTree, Rope, RopeFile, VersionMap, VersionMapDefaults
~ BEGIN
ROPE: TYPE ~ Rope.ROPE;
SymbolTable: TYPE ~ RedBlackTree.Table;
ROPEList: TYPE ~ LIST OF ROPE;
GetFromReleaseCommand: PROC [cmd: Commander.Handle] RETURNS [result: REFNIL, msg: ROPENIL] --Commander.CommandProc-- ~ {
argv: CommandTool.ArgumentVector ← CommandTool.Parse[cmd];
dfFileName, self: ROPENIL;
additions: SymbolTable ← RedBlackTree.Create[GetClauseKey, CompareClauses];
mapList: VersionMap.MapList ~ VersionMapDefaults.GetMapList[$Symbols];
doMakeDo: BOOLFALSE;
toDo: {makeDo, compile, determine} ← determine;
explicitGoals: ROPEList ← NIL;
i: NAT ← 1;
IF argv.argc >= 3 AND argv[2].Equal["←"] THEN {
[dfFileName, self] ← DefaultExtension[argv[1], "DF"];
i ← 3;
};
FOR i ← i, i+1 WHILE i < argv.argc DO
SELECT TRUE FROM
argv[i].Equal["-md"] => toDo ← makeDo;
argv[i].Equal["-c"] => toDo ← compile;
ENDCASE => explicitGoals ← CONS[argv[i], explicitGoals];
ENDLOOP;
IF toDo = determine AND explicitGoals = NIL THEN {
dm: BasicTime.GMT = GetCreateDate["MakeDo.log"];
dc: BasicTime.GMT = GetCreateDate["Compiler.log"];
SELECT TRUE FROM
dm = BasicTime.nullGMT => IF dc = BasicTime.nullGMT THEN RETURN [NIL, "Usage: GetFromRelease [DFFileName ←] [-md|-c|fileNameList]"] ELSE toDo ← compile;
dc = BasicTime.nullGMT => toDo ← makeDo;
dm.Period[dc] > 0 => toDo ← compile;
ENDCASE => toDo ← makeDo;
};
SELECT TRUE FROM
explicitGoals # NIL => {
FOR egl: ROPEList ← explicitGoals, egl.rest WHILE egl # NIL DO
TryToAdd[cmd, self, egl.first, mapList, additions];
ENDLOOP;
};
toDo = makeDo => {
dfFileName ← ParseMakeDoLog[cmd, self, RopeFile.Create["MakeDo.log" !FS.Error => CONTINUE], mapList, additions, dfFileName];
};
toDo = compile => {
log: ROPE ~ RopeFile.Create["Compiler.log"];
IF Rope.Find[s1: log, s2: "Command: /-g", case: FALSE] > 0 THEN
ParseSeparateLogs[cmd, self, log, mapList, additions]
ELSE
ParseCompilerLog[cmd, self, log, mapList, additions];
};
ENDCASE => ERROR;
WriteAdditions[cmd, dfFileName, additions];
};
GetCreateDate: PROC [fileName: ROPE] RETURNS [created: BasicTime.GMT] = {
created ← BasicTime.nullGMT;
created ← FS.FileInfo[fileName !FS.Error => CONTINUE].created;
};
packageIntro: ROPE ← "MakingDo package in ";
packageExtro: ROPE ← ".\n";
ParseMakeDoLog: PROC [cmd: Commander.Handle, self, log: ROPE, mapList: VersionMap.MapList, additions: SymbolTable, inDFFileName: ROPE] RETURNS [outDFFileName: ROPE] = {
expect the MakeDo.log to contain entries like this
MakingDo package in GetFromRelease.DF.
Couldn't Compile []<>Users>Spreitzer.pa>Gr>GetFromReleaseImpl.Mesa because []<>Users>Spreitzer.pa>Gr>Foo.BCD, []<>Users>Spreitzer.pa>Gr>Bar.BCD, and []<>Users>Spreitzer.pa>Gr>Gronk.BCD don't exist.
tail: ROPE = "don't exist";
introStart, pEnd: INT;
SearchBackward: PROC [from: INT] RETURNS [firstNonBlank: INT] = {
FOR firstNonBlank ← from - 1, firstNonBlank-1 WHILE log.Fetch[firstNonBlank] IN [0C .. ' ] DO --skip trailing whitespace-- NULL ENDLOOP;
FOR firstNonBlank ← firstNonBlank - 1, firstNonBlank-1 WHILE NOT log.Fetch[firstNonBlank] IN [0C .. ' ] DO --skip nonblanks-- NULL ENDLOOP;
firstNonBlank ← firstNonBlank + 1;
};
SearchForward: PROC [from: INT] RETURNS [firstPastName: INT] = {
FOR firstPastName ← from, firstPastName+1 DO
char: CHAR ← log.Fetch[firstPastName];
IF char IN [0C .. ' ] OR char = ', THEN EXIT;
ENDLOOP;
};
outDFFileName ← inDFFileName;
IF inDFFileName = NIL AND (introStart ← Rope.Find[s1: log, s2: packageIntro]) >= 0 THEN {
pStart: INT = introStart + packageIntro.Length[];
IF inDFFileName # NIL THEN ERROR;
pEnd ← Rope.Find[s1: log, s2: packageExtro, pos1: pStart];
IF pEnd <= pStart THEN ERROR;
[outDFFileName, self] ← DefaultExtension[log.Substr[start: pStart, len: pEnd - pStart], "DF"];
};
FOR i: INT ← Rope.Find[s1: log, s2: tail, pos1: 0, case: FALSE],
Rope.Find[s1: log, s2: tail, pos1: i+10, case: FALSE]
UNTIL i < 0 DO
FOR j: INT ← SearchBackward[i], SearchBackward[j] DO
k: INT ← SearchForward[j];
raw, fullFName, fileName, ext: ROPE;
cp: FS.ComponentPositions;
raw ← log.Substr[start: j, len: k - j];
IF raw.Equal["because"] THEN EXIT;
IF raw.Equal["and"] THEN LOOP;
[fullFName, cp] ← FS.ExpandName[raw !FS.Error => EXIT];
ext ← fullFName.Substr[start: cp.ext.start, len: cp.ext.length];
IF NOT ext.Equal["BCD", FALSE] THEN LOOP;
fileName ← fullFName.Substr[start: cp.base.start, len: (cp.ext.start + cp.ext.length) - cp.base.start];
TryToAdd[cmd, self, fileName, mapList, additions];
ENDLOOP;
ENDLOOP;
};
ParseSeparateLogs: PROC [cmd: Commander.Handle, self, log: ROPE, mapList: VersionMap.MapList, additions: SymbolTable] ~ {
expect the Compiler.log to contain entries like this
Command: GetFromReleaseImpl.mesa
GetFromReleaseImpl.mesa -- aborted, 1 errors on ///Users/Beach.pa/GetFromRelease/GetFromReleaseImpl.errlog, seconds: 6
FOR i: INT ← Rope.Find[s1: log, s2: " on ///", pos1: 0, case: FALSE],
Rope.Find[s1: log, s2: " on ///", pos1: i+10, case: FALSE] UNTIL i < 0 DO
j: INT ← Rope.Find[s1: log, s2: ", ", pos1: i, case: FALSE];
errlogName: ROPE ~ Rope.Substr[log, i+4, j-(i+4)];
errlog: ROPE ~ RopeFile.Create[errlogName];
ParseCompilerLog[cmd, self, errlog, mapList, additions];
ENDLOOP;
};
ParseCompilerLog: PROC [cmd: Commander.Handle, self, log: ROPE, mapList: VersionMap.MapList, additions: SymbolTable] ~ {
SearchBackForNewline: PROC [i: INT] RETURNS [INT] ~ {
UNTIL log.Fetch[i] = '\n DO i ← i - 1 ENDLOOP;
RETURN [i]
};
FOR i: INT ← Rope.Find[s1: log, s2: "cannot be opened", pos1: 0, case: FALSE],
Rope.Find[s1: log, s2: "cannot be opened", pos1: i+10, case: FALSE]
UNTIL i < 0 DO
j: INT ← SearchBackForNewline[i];
bcdName: ROPE ~ Rope.Substr[log, j+1, Rope.Find[log, " ", j+1]-(j+1)].Concat[".bcd"];
TryToAdd[cmd, self, bcdName, mapList, additions];
ENDLOOP;
};
TryToAdd: PROC [cmd: Commander.Handle, self, fileName: ROPE, mapList: VersionMap.MapList, additions: SymbolTable] = {
list: VersionMap.MapAndNameList ~ VersionMap.ShortNameToNames[mapList, fileName];
FOR p: VersionMap.MapAndNameList ← list, p.rest UNTIL p=NIL DO
remoteFileName: ROPE ~ p.first.name;
fullFName, package, result, dfFile: ROPENIL;
cp: FS.ComponentPositions;
[fullFName, cp] ← FS.ExpandName[remoteFileName];
package ← Rope.Substr[fullFName, cp.subDirs.start, cp.subDirs.length];
IF package.Equal[self, FALSE] THEN --ain't this clever?-- {
cmd.out.PutF["I won't import %g from its own package (%g)\n", IO.rope[fileName], IO.rope[package]];
EXIT;
};
attach the remote bcd filename
cmd.out.PutRope[remoteFileName];
cmd.out.PutRope[" --> "];
cmd.out.PutRope[FS.Copy[from: remoteFileName, to: fileName, setKeep: FALSE, keep: 1, remoteCheck: TRUE, attach: TRUE ! FS.Error => {result ← error.explanation; CONTINUE}]];
cmd.out.PutRope[result];
cmd.out.PutRope["\n"];
IF NOT result.IsEmpty THEN LOOP; -- avoid attaching df file when failed to attach bcd
{
determine DF file from package directory name from the convention [Cedar]<Cedar6.0>Package>Interface.bcd
dfFile ← Rope.Cat[Rope.Substr[fullFName, 0, cp.subDirs.start], "Top>", package, ".df"];
[] ← FS.Copy[from: dfFile, to: Rope.Concat[package, ".df"], setKeep: FALSE, keep: 1, remoteCheck: TRUE, attach: TRUE ! FS.Error => {result ← error.explanation; CONTINUE}];
IF result.IsEmpty THEN {
EnsureFileImport[additions, dfFile, fileName];
EXIT;
}
ELSE {
cmd.out.PutF["DF file name guess %g for %g failed (%g)\n", IO.rope[dfFile], IO.rope[fileName], IO.rope[result]];
};
};
REPEAT
FINISHED => cmd.out.PutF["Couldn't guess a DF-file for %g\n", IO.rope[fileName]];
ENDLOOP;
};
EnsureFileImport: PROC [clauses: SymbolTable, dfFileName, fileName: ROPE] = {
ii: REF DFUtilities.ImportsItem ← NARROW[clauses.Lookup[dfFileName]];
IF ii = NIL THEN {
clauses.Insert[
ii ← NEW [DFUtilities.ImportsItem ← [
path1: dfFileName,
date: [notEqual],
exported: FALSE,
form: list,
list: NEW [DFUtilities.UsingList[1]]
]],
dfFileName];
ii.list.nEntries ← 0;
};
EnsureFileInList[ii, [name: fileName]];
};
EnsureFileInList: PROC [ii: REF DFUtilities.ImportsItem, ue: DFUtilities.UsingEntry] = {
found: BOOL;
index: NAT;
[found, index] ← DFUtilities.SearchUsingList[ue.name, ii.list];
IF found THEN {
ii.list[index].verifyRoot ← ii.list[index].verifyRoot OR ue.verifyRoot;
RETURN;
};
IF ii.list.nEntries = ii.list.length THEN {
new: REF DFUtilities.UsingList ← NEW [DFUtilities.UsingList[ii.list.length*2]];
FOR i: NAT IN [0 .. ii.list.nEntries) DO
new[i] ← ii.list[i];
ENDLOOP;
new.nEntries ← ii.list.nEntries;
ii.list ← new;
};
ii.list[ii.list.nEntries] ← ue;
ii.list.nEntries ← ii.list.nEntries + 1;
DFUtilities.SortUsingList[ii.list, TRUE];
};
WriteAdditions: PROC [cmd: Commander.Handle, dfFileName: ROPE, additions: SymbolTable] = {
PerDFFile: PROC [data: REF ANY] RETURNS [stop: BOOLFALSE] --RedBlackTree.EachNode-- = {
DFUtilities.WriteItemToStream[cmd.out, data];
cmd.out.PutRope["\n"];
};
IF additions.Size[] = 0 THEN RETURN;
IF dfFileName # NIL AND UpdateDFFile[cmd.out, dfFileName, additions] THEN RETURN;
cmd.out.PutRope["\nAdd the following to your df file:\n\n"];
additions.EnumerateIncreasing[PerDFFile];
};
DefaultExtension: PROC [raw, ext: ROPE] RETURNS [fileName, base: ROPE] = {
fullFName: ROPE;
cp: FS.ComponentPositions;
IF raw.Length[] = 0 THEN RETURN [NIL, NIL];
[fullFName, cp] ← FS.ExpandName[fileName ← raw];
base ← fullFName.Substr[start: cp.base.start, len: cp.base.length];
IF cp.ext.start = (cp.base.start+cp.base.length) THEN fileName ← fileName.Cat[".", ext];
};
UpdateDFFile: PROC [log: IO.STREAM, dfFileName: ROPE, clauses: SymbolTable] RETURNS [success: BOOL] = {
oldin, newout: IO.STREAMNIL;
lastKey: ROPENIL;
DoUpTo: PROC [limit: REF DFUtilities.ImportsItem] = {
FOR ti: REF DFUtilities.ImportsItem ← NARROW[clauses.LookupNextLarger[lastKey]], NARROW[clauses.LookupNextLarger[ti.path1]] WHILE ti # NIL AND (limit = NIL OR CompareClauses[ti.path1, limit] < equal) DO
DFUtilities.WriteItemToStream[newout, ti];
newout.PutRope["\n"];
lastKey ← ti.path1;
ENDLOOP;
};
PerItem: PROC [item: REF ANY] RETURNS [stop: BOOLFALSE] --DFUtilities.ProcessItemProc-- = {
WITH item SELECT FROM
ii: REF DFUtilities.ImportsItem => IF ~ii.exported THEN {
mergeable: BOOL ← Vanilla[ii];
IF mergeable THEN {
ti: REF DFUtilities.ImportsItem ← NARROW[clauses.Lookup[ii.path1]];
IF ti # NIL AND CompareClauses[lastKey, ti] < equal THEN {
IF NOT Vanilla[ti] THEN ERROR;
FOR i: NAT IN [0 .. ti.list.nEntries) DO
EnsureFileInList[ii, ti.list[i]];
ENDLOOP;
IF clauses.Delete[ti.path1].data # ti THEN ERROR;
};
};
DoUpTo[ii];
};
ENDCASE;
DFUtilities.WriteItemToStream[newout, item];
};
oldin ← FS.StreamOpen[dfFileName !FS.Error => {
log.PutF["FS.Error %g --- DF file not updated\n", IO.rope[error.explanation]];
oldin ← NIL;
CONTINUE
}];
IF oldin = NIL THEN RETURN [FALSE];
newout ← FS.StreamOpen[dfFileName, create !FS.Error => {
log.PutF["FS.Error %g --- DF file not updated\n", IO.rope[error.explanation]];
oldin ← NIL;
CONTINUE
}];
IF newout = NIL THEN RETURN [FALSE];
DFUtilities.ParseFromStream[oldin, PerItem, [comments: TRUE]];
newout.PutRope["\n"];
DoUpTo[NIL];
oldin.Close[];
newout.Close[];
success ← TRUE;
};
Vanilla: PROC [ii: REF DFUtilities.ImportsItem] RETURNS [vanilla: BOOL] =
{vanilla ← ii.date = [notEqual] AND ii.path2.Length[] = 0 AND (NOT ii.exported) AND ii.form = list AND ii.list # NIL};
GetClauseKey: PROC [data: REF ANY] RETURNS [key: REF ANY] --RedBlackTree.GetKey-- = {
ii: REF DFUtilities.ImportsItem ← NARROW[data];
key ← ii.path1;
};
CompareClauses: PROC [k, data: REF ANY] RETURNS [c: Basics.Comparison] --RedBlackTree.Compare-- = {
k1: ROPENARROW[k];
i2: REF DFUtilities.ImportsItem ← NARROW[data];
k2: ROPE ← i2.path1;
fn1, fn2: ROPE;
cp1, cp2: FS.ComponentPositions;
SELECT TRUE FROM
(k1=NIL) AND (k2#NIL) => RETURN [less];
(k1#NIL) AND (k2=NIL) => RETURN [greater];
(k1=NIL) AND (k2=NIL) => RETURN [equal];
ENDCASE;
[fn1, cp1] ← FS.ExpandName[k1];
[fn2, cp2] ← FS.ExpandName[k2];
IF (c ← fn1.Substr[start: cp1.base.start].Compare[fn2.Substr[start: cp2.base.start], FALSE]) # equal THEN RETURN;
c ← fn1.Substr[len: cp1.base.start].Compare[fn2.Substr[len: cp2.base.start], FALSE];
};
Commander.Register["GetFromRelease", GetFromReleaseCommand, "GetFromRelease [-md] [DF file name] Parses MakeDo or Compiler error logs for error messages about missing files, attaches the requisite files from the release, and updates the DF file (if given)."];
END.