<> <> <> <> <> <> <> <<>> 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: REF _ NIL, msg: ROPE _ NIL] --Commander.CommandProc-- ~ { argv: CommandTool.ArgumentVector _ CommandTool.Parse[cmd]; dfFileName, self: ROPE _ NIL; additions: SymbolTable _ RedBlackTree.Create[GetClauseKey, CompareClauses]; mapList: VersionMap.MapList ~ VersionMapDefaults.GetMapList[$Symbols]; doMakeDo: BOOL _ FALSE; 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] = { <> <> <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] ~ { <> <> <> 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: ROPE _ NIL; 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; }; <> 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 <<>> { <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: BOOL _ FALSE] --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.STREAM _ NIL; lastKey: ROPE _ NIL; 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: BOOL _ FALSE] --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: ROPE _ NARROW[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.