<> <> <> DIRECTORY BasicTime, Commander, DFOperations, DFUtilities, FS, IO, MessageWindow, Process, ReleaseToolVerify, Rope, SymTab, VersionMap, ViewerIO; ReleaseToolDriver: CEDAR PROGRAM IMPORTS BasicTime, Commander, DFUtilities, FS, IO, MessageWindow, Process, ReleaseToolVerify, Rope, SymTab, VersionMap, ViewerIO SHARES VersionMap = BEGIN CommentItem: TYPE = DFUtilities.CommentItem; Date: TYPE = DFUtilities.Date; DirectoryItem: TYPE = DFUtilities.DirectoryItem; FileItem: TYPE = DFUtilities.FileItem; Filter: TYPE = DFUtilities.Filter; ImportsItem: TYPE = DFUtilities.ImportsItem; IncludeItem: TYPE = DFUtilities.IncludeItem; Map: TYPE = VersionMap.Map; MapList: TYPE = VersionMap.MapList; ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; WhiteSpaceItem: TYPE = DFUtilities.WhiteSpaceItem; ReleaseToolCommand: Commander.CommandProc = { EachItem: DFUtilities.ProcessItemProc = { <<[item: REF ANY] RETURNS [stop: BOOL _ FALSE]>> WITH item SELECT FROM incl: REF DFUtilities.IncludeItem => { name: ROPE = incl.path1; IO.PutRope[outV, "\n\nVerifyDF of "]; IO.PutRope[outV, name]; IF Rope.Equal[ShortName[name], shortDfFile, FALSE] THEN { IO.PutRope[outV, "\n not done, short name equals root DF name.\n"]; RETURN; }; IO.PutF[outV, "\n starting at %g\n", [time[BasicTime.Now[]]]]; [] _ ReleaseToolVerify.Verify[ name, bcdCache, sourceMaps, symbolsMaps, Interact, outV, outV]; }; ENDCASE; }; debugging,verifying,moving: BOOL _ FALSE; inV,outV: STREAM _ NIL; inStream: STREAM _ NIL; dfTable: SymTab.Ref _ NIL; dfFile: ROPE _ "CurrentCedar.df"; shortDfFile: ROPE _ dfFile; mapPrefix: ROPE _ "Cedar"; releaseDirectory: ROPE _ NIL; releaseHost: ROPE _ NIL; ris: STREAM = IO.RIS[cmd.commandLine]; sourceMapName,symbolsMapName: ROPE _ NIL; sourceMaps,symbolsMaps: MapList _ NIL; bcdCache: ReleaseToolVerify.BcdCache _ NIL; Cleanup: PROC = { IF bcdCache # NIL THEN {ReleaseToolVerify.FlushBcdCache[bcdCache]; bcdCache _ NIL}; IF inStream # NIL THEN {IO.Close[inStream]; inStream _ NIL}; IF inV # NIL THEN {IO.Close[inV]; inV _ NIL}; IF outV # NIL THEN {IO.Close[outV]; outV _ NIL}; }; TimedMessage: PROC [msg: ROPE] = { IO.PutF[outV, msg, [time[BasicTime.Now[]]]]; }; WITH cmd.procData.clientData SELECT FROM list: LIST OF REF ANY => { WHILE list # NIL DO SELECT list.first FROM $debug => debugging _ TRUE; $verify => verifying _ TRUE; $move => moving _ TRUE; ENDCASE; list _ list.rest; ENDLOOP; }; ENDCASE; <> <> dfFile _ DefaultExtension[ IO.GetTokenRope[ris, IO.IDProc ! IO.EndOfStream => CONTINUE].token, ".df"]; shortDfFile _ ShortName[dfFile]; mapPrefix _ IO.GetTokenRope[ris, IO.IDProc ! IO.EndOfStream => CONTINUE].token; releaseHost _ IO.GetTokenRope[ris, IO.IDProc ! IO.EndOfStream => CONTINUE].token; releaseDirectory _ IO.GetTokenRope[ris, IO.IDProc ! IO.EndOfStream => CONTINUE].token; sourceMapName _ DefaultExtension[mapPrefix, "Source.VersionMap"]; sourceMaps _ LIST[VersionMap.RestoreMapFromFile[sourceMapName]]; symbolsMapName _ DefaultExtension[mapPrefix, "Symbols.VersionMap"]; symbolsMaps _ LIST[VersionMap.RestoreMapFromFile[symbolsMapName]]; inStream _ FS.StreamOpen[dfFile]; [inV,outV] _ ViewerIO.CreateViewerStreams[ "ReleaseTool.log", NIL, "ReleaseTool.log", FALSE]; {ENABLE UNWIND => { TimedMessage["\n\n**** Aborting at %g ****\n"]; Cleanup[]; }; IF verifying THEN { TimedMessage["\n\nVerification Phase of ReleaseTool starting at %g\n"]; bcdCache _ ReleaseToolVerify.CreateBcdCache[80]; DFUtilities.ParseFromStream[inStream, EachItem]; TimedMessage["\n\nVerification Phase of ReleaseTool ending at %g\n"]; IO.Close[inStream]; inStream _ NIL; ReleaseToolVerify.FlushBcdCache[bcdCache]; bcdCache _ NIL; }; IF moving THEN { TimedMessage["\n\nFile Moving Phase of ReleaseTool starting at %g\n"]; dfTable _ BuildDFTable[dfFile, outV, releaseHost, releaseDirectory]; TimedMessage["\nDF table built at %g\n"]; MoveFiles[dfTable, outV, releaseHost, releaseDirectory, debugging]; TimedMessage["\n\nFile Moving Phase of ReleaseTool ending at %g\n"]; }; }; Cleanup[]; }; showInfo: BOOL _ FALSE; Interact: DFOperations.InteractionProc = { <<[interaction: REF, clientData: REF]>> <> out: STREAM = NARROW[clientData]; IF showInfo THEN WITH interaction SELECT FROM info: REF DFOperations.InfoInteraction => { IO.PutRope[out, info.message]; }; dfInfo: REF DFOperations.DFInfoInteraction => { IO.PutRope[out, dfInfo.message]; }; abort: REF DFOperations.AbortInteraction => { Process.CheckForAbort[]; }; ENDCASE; }; DefaultExtension: PROC [name: ROPE, ext: ROPE] RETURNS [ROPE] = { len: INT = Rope.Length[name]; eLen: INT = Rope.Length[ext]; pos, bang, dot: INT _ len; WHILE pos > 0 DO posM: INT = pos-1; SELECT Rope.Fetch[name, posM] FROM '! => bang _ dot _ posM; '. => dot _ posM; '>, '/, '] => EXIT; ENDCASE; pos _ posM; ENDLOOP; IF bang = len AND (bang-dot # eLen OR Rope.Run[name, dot, ext, 0, FALSE] # eLen) THEN name _ Rope.Concat[name, ext]; RETURN [name]; }; DFTableEntry: TYPE = REF DFTableEntryRep; DFTableEntryRep: TYPE = RECORD [ tempName: ROPE, destName: ROPE, time: BasicTime.GMT ]; BuildDFTable: PROC [topDF: ROPE, log: STREAM, host,dir: ROPE] RETURNS [tab: SymTab.Ref] = { <> now: BasicTime.GMT = BasicTime.Now[]; index: INT _ 0; EachDF: PROC [dfName: ROPE, dfDate: Date _ []] = { EachDFPass[dfName, dfDate, TRUE]; EachDFPass[dfName, dfDate, FALSE]; }; EachDFPass: PROC [dfName: ROPE, dfDate: Date _ [], selfReferenceOnly: BOOL] = { inStream: STREAM _ NIL; currentDir: REF DirectoryItem _ NIL; EachItem: DFUtilities.ProcessItemProc = { WITH item SELECT FROM dirItem: REF DirectoryItem => { currentDir _ dirItem; }; fileItem: REF FileItem => { SELECT TRUE FROM NOT selfReferenceOnly => RETURN; currentDir = NIL => IO.PutF[log, "\n**** NIL directory: %g", [rope[dfName]]]; currentDir.path2 = NIL => IO.PutF[log, "\n**** no ReleaseAs clause for: %g", [rope[dfName]]]; currentDir.path2IsCameFrom => { IO.PutF[log, "\n**** CameFrom clause for : %g", [rope[dfName]]]; }; ENDCASE => { destName: ROPE _ Rope.Concat[ ReplaceHostAndDir[currentDir.path2, host, dir], RemoveVersion[fileItem.name]]; entry.destName _ destName; }; stop _ selfReferenceOnly; }; inclItem: REF IncludeItem => { IF selfReferenceOnly THEN RETURN; EachDF[inclItem.path1, inclItem.date]; }; ENDCASE; }; Cleanup: PROC = { IF inStream # NIL THEN {IO.Close[inStream]; inStream _ NIL}; }; <> entry: DFTableEntry _ NIL; stripped: ROPE = RemoveVersion[dfName]; realName: ROPE _ NIL; key: ROPE _ NIL; [realName, dfDate] _ MakeExplicit[dfName, dfDate]; IF realName = NIL THEN { IO.PutF[log, "\n**** DF file not found: %g", [rope[dfName]]]; GO TO skipIt }; inStream _ FS.StreamOpen[dfName _ realName]; key _ RemoveVersion[dfName]; WITH SymTab.Fetch[tab, key].val SELECT FROM e: DFTableEntry => { <> IF selfReferenceOnly THEN RETURN; entry _ e; }; ENDCASE => { <> entry _ NEW[DFTableEntryRep _ [ tempName: IO.PutFR["Temp.%g.df", [integer[index]]], destName: ReplaceHostAndDir[dfName, host, dir], time: BasicTime.Update[now, index] ]]; IO.PutF[log, "\nDF table entry for: %g", [rope[dfName]]]; index _ index + 1; [] _ SymTab.Store[tab, key, entry]; }; {ENABLE UNWIND => Cleanup[]; filter: Filter _ [filterA: source, filterB: public, filterC: defining]; <> IF selfReferenceOnly THEN { <> using: REF DFUtilities.UsingList _ NEW[DFUtilities.UsingList[1]]; using[0] _ [name: ShortName[dfName]]; using.nEntries _ 1; filter _ [filterA: source, filterB: all, filterC: defining, list: using]; }; DFUtilities.ParseFromStream[ inStream, EachItem, filter ! DFUtilities.SyntaxError => { IO.PutF[log, "\n**** Syntax Error: %g", [rope[reason]]]; CONTINUE; }]; }; Cleanup[]; EXITS skipIt => {}; }; tab _ SymTab.Create[151, FALSE]; EachDF[topDF]; }; MakeExplicit: PROC [name: ROPE, date: Date] RETURNS [realName: ROPE _ NIL, realDate: Date] = { realDate.format _ explicit; [fullFName: realName, created: realDate.gmt] _ FS.FileInfo[name, date.gmt ! FS.Error => IF error.group # bug THEN CONTINUE]; IF realName = NIL THEN [fullFName: realName, created: realDate.gmt] _ FS.FileInfo[RemoveVersion[name], date.gmt ! FS.Error => IF error.group # bug THEN CONTINUE]; }; ReplaceHostAndDir: PROC [name,host,dir: ROPE] RETURNS [dest: ROPE] = { rbPos: INT = Rope.SkipTo[dest _ name, 1, "]"]; laPos: INT = Rope.SkipTo[dest, rbPos+1, "<"]; raPos: INT = Rope.SkipTo[dest, laPos+1, ">"]; IF dir # NIL THEN dest _ Rope.Replace[name, laPos+1, raPos-laPos-1, dir]; IF host # NIL THEN dest _ Rope.Replace[dest, 1, rbPos-1, host]; dest _ RemoveVersion[dest]; }; RemoveVersion: PROC [name: ROPE] RETURNS [ROPE] = { pos: INT _ Rope.Length[name]; WHILE (pos _ pos - 1) > 0 DO SELECT Rope.Fetch[name, pos] FROM '! => RETURN [Rope.Flatten[name, 0, pos]]; '>, '], '. => EXIT; ENDCASE; ENDLOOP; RETURN [name]; }; ShortName: PROC [name: ROPE] RETURNS [ROPE] = { pos: INT _ Rope.Length[name]; bang: INT _ Rope.Length[name]; WHILE (pos _ pos - 1) > 0 DO SELECT Rope.Fetch[name, pos] FROM '! => bang _ pos; '>, '] => RETURN [Rope.Flatten[name, pos+1, bang-pos-1]]; ENDCASE; ENDLOOP; RETURN [Rope.Flatten[name, 0, bang]]; }; FindDFName: PROC [table: SymTab.Ref, name: ROPE, date: Date] RETURNS [destName: ROPE, destDate: Date, tempName: ROPE _ NIL] = { destName _ RemoveVersion[name]; destDate _ date; WITH SymTab.Fetch[table, destName].val SELECT FROM entry: DFTableEntry => { destName _ entry.destName; destDate.format _ explicit; destDate.gmt _ entry.time; tempName _ entry.tempName; }; ENDCASE; }; FindNonDF: PROC [table: SymTab.Ref, name: ROPE, date: Date] RETURNS [realName: ROPE _ NIL, realDate: Date _ []] = { stripped: ROPE _ RemoveVersion[name]; WITH SymTab.Fetch[table, stripped].val SELECT FROM entry: DFTableEntry => { <> }; ENDCASE => { [realName, realDate] _ MakeExplicit[name, date]; }; }; MoveFiles: PROC [table: SymTab.Ref, log: STREAM, host, dir: ROPE, debugging: BOOL] = { nonDfAction: SymTab.EachPairAction = { <<[key: Key, val: Val] RETURNS [quit: BOOL]>> quit _ FALSE; totalCount _ MoveDFContents[key, table, log, host, dir, totalCount, debugging]; }; dfAction: SymTab.EachPairAction = { <<[key: Key, val: Val] RETURNS [quit: BOOL]>> quit _ FALSE; totalCount _ MoveDFTemp[key, val, log, totalCount, debugging]; }; totalCount: INT _ 0; [] _ SymTab.Pairs[table, nonDfAction]; [] _ SymTab.Pairs[table, dfAction]; }; MoveDFContents: PROC [dfName: ROPE, table: SymTab.Ref, log: STREAM, host,dir: ROPE, count: INT, debugging: BOOL] RETURNS [INT _ 0] = { entry: DFTableEntry _ NIL; inStream,outStream: STREAM _ NIL; currentDirItem: REF DirectoryItem; currentSourcePrefix: ROPE _ NIL; currentDestPrefix: ROPE _ NIL; EachItem: DFUtilities.ProcessItemProc = { WITH item SELECT FROM dirItem: REF DirectoryItem => { IF NOT dirItem.path2IsCameFrom AND dirItem.path2 # NIL THEN { currentSourcePrefix _ dirItem.path1; currentDestPrefix _ ReplaceHostAndDir[dirItem.path2, host, dir]; dirItem.path1 _ currentDestPrefix; dirItem.path2 _ currentSourcePrefix; dirItem.path2IsCameFrom _ TRUE; }; currentDirItem _ dirItem; }; file: REF FileItem => { date: Date _ file.date; sourceName: ROPE _ Rope.Concat[currentSourcePrefix, file.name]; destName: ROPE _ Rope.Concat[currentDestPrefix, RemoveVersion[file.name]]; destExists: BOOL _ FALSE; realName,tempName: ROPE _ NIL; [realName, date] _ FindNonDF[table, sourceName, file.date]; IF realName = NIL THEN { <> [destName, date, tempName] _ FindDFName[table, sourceName, date]; IF NOT Rope.Match["*.df", destName, FALSE] THEN { IO.PutF[log, "\n**** File not found: %g", [rope[sourceName]]]; GO TO skip1 }; IO.PutF[log, "\nFuture copy %g (%g)\n to %g", [rope[sourceName]], [rope[tempName]], [rope[destName]]]; file.date _ date; GO TO skip1; }; <> file.date _ date; IO.PutF[log, "\nCopy %g\n to ", [rope[sourceName _ realName]]]; IF debugging THEN IO.PutF[log, "%g\n (not copied, debugging)", [rope[destName]]] ELSE { ENABLE FS.Error => IF error.group # bug THEN { IO.PutF[log, "\n**** Copy failed: %g", [rope[error.explanation]]]; GO TO skip1 }; gmt: BasicTime.GMT; realName _ NIL; <> [fullFName: realName, created: gmt] _ FS.FileInfo[destName ! FS.Error => IF error.group # bug THEN CONTINUE]; IF realName # NIL AND gmt = date.gmt THEN { <> destName _ realName; IO.PutRope[log, destName]; IO.PutRope[log, "\n (already there)"]; } ELSE { <> tempName: ROPE _ "ReleaseTool.Temp"; IF date.gmt # BasicTime.nullGMT AND ReleaseToolVerify.IsInFileCache[sourceName, date] THEN { <> tempName _ sourceName; } ELSE { <> tempName _ FS.Copy[from: sourceName, to: tempName]; }; destName _ FS.Copy[from: tempName, to: destName]; IO.PutRope[log, destName]; date.format _ explicit; date.gmt _ gmt; }; file.name _ Rope.Substr[destName, Rope.Length[currentDestPrefix]]; }; MessageWindow.Append[ IO.PutFR[" %g files moved.", [integer[count _ count + 1]]], TRUE]; EXITS skip1 => {}; }; imports: REF ImportsItem => { destName, tempName: ROPE; destDate: Date; [destName, destDate, tempName] _ FindDFName[table, imports.path1, imports.date]; IF tempName = NIL THEN { <> [destName, destDate] _ MakeExplicit[imports.path1, imports.date]; IF destName = NIL THEN IO.PutF[log, "\n**** File not found: %g", [rope[imports.path1]]] ELSE {imports.path1 _ destName; imports.date _ destDate}; } ELSE { <> imports.path2 _ imports.path1; imports.path1 _ destName; imports.date _ destDate; }; }; include: REF IncludeItem => { IF NOT include.path2IsCameFrom OR include.path2 = NIL THEN { destName, tempName: ROPE; destDate: Date; [destName, destDate, tempName] _ FindDFName[table, include.path1, include.date]; IF tempName = NIL THEN { <> [destName, destDate] _ MakeExplicit[include.path1, include.date]; IF destName = NIL THEN IO.PutF[log, "\n**** File not found: %g", [rope[include.path1]]] ELSE {include.path1 _ destName; include.date _ destDate}; } ELSE { <> include.path2IsCameFrom _ TRUE; include.path2 _ include.path1; include.path1 _ destName; include.date _ destDate; }; }; }; ENDCASE; DFUtilities.WriteItemToStream[outStream, item]; }; WITH SymTab.Fetch[table, dfName].val SELECT FROM e: DFTableEntry => entry _ e; ENDCASE => { IO.PutF[log, "\n**** DF file not in table: %g", [rope[dfName]]]; GO TO quit }; inStream _ FS.StreamOpen[dfName ! FS.Error => IF error.group # bug THEN { IO.PutF[log, "\n**** DF file not found: %g", [rope[error.explanation]]]; GO TO quit }; ]; outStream _ FS.StreamOpen[entry.tempName, create ! FS.Error => IF error.group # bug THEN { IO.PutF[log, "\n**** temp DF file not opened: %g", [rope[error.explanation]]]; IO.Close[inStream]; GO TO quit }; ]; IO.PutF[log, "\n\nMoving contents of %g (%g)", [rope[dfName]], [rope[entry.tempName]]]; DFUtilities.ParseFromStream[inStream, EachItem, [comments: TRUE] ! DFUtilities.SyntaxError => { IO.PutF[log, "\n**** Syntax Error: %g", [rope[reason]]]; CONTINUE; }]; IO.Close[inStream]; FS.SetByteCountAndCreatedTime[FS.OpenFileFromStream[outStream], -1, entry.time]; IO.Close[outStream]; RETURN [count]; EXITS quit => {}; }; MoveDFTemp: PROC [name: ROPE, val: REF, log: STREAM, count: INT, debugging: BOOL] RETURNS [INT] = { WITH val SELECT FROM entry: DFTableEntry => { IO.PutF[log, "\nCopy %g (%g)\n to %g", [rope[name]], [rope[entry.tempName]], [rope[entry.destName]]]; IF debugging THEN IO.PutRope[log, "\n (not copied, debugging)"] ELSE [] _ FS.Copy[from: entry.tempName, to: entry.destName ! FS.Error => IF error.group # bug THEN { IO.PutF[log, "\n**** Copy failed: %g", [rope[error.explanation]]]; CONTINUE; }; ]; MessageWindow.Append[ IO.PutFR[" %g files moved.", [integer[count _ count + 1]]], TRUE]; }; ENDCASE; RETURN [count]; }; Commander.Register[ "TestMovingPhase", ReleaseToolCommand, "tests the file moving phase of the ReleaseTool without moving files. The command line has, in order (all optional): ", LIST[$debug, $move]]; Commander.Register[ "VerifyRelease", ReleaseToolCommand, "verifies the files in a release. We assume that the version maps are valid. The command line has, in order (all optional): ", LIST[$verify]]; Commander.Register[ "MoveRelease", ReleaseToolCommand, "moves the files in a release. We assume that the version maps are valid, and that the files have been verified (although no state is retained between verification and moving). The command line has, in order (all optional): ", LIST[$move]]; END.