/// <summary>
        /// Checks that a manifest matches the files on disk
        /// </summary>
        /// <param name="RootDir">Root directory for relative paths in the manifest</param>
        /// <param name="Manifest">Manifest to check</param>
        /// <param name="Files">Mapping of filename to timestamp as expected in the manifest</param>
        static void CheckManifest(DirectoryReference RootDir, TempStorageManifest Manifest, Dictionary <FileReference, DateTime> Files)
        {
            if (Files.Count != Manifest.Files.Length)
            {
                throw new AutomationException("Number of files in manifest does not match");
            }
            foreach (TempStorageFile ManifestFile in Manifest.Files)
            {
                FileReference File = ManifestFile.ToFileReference(RootDir);
                if (!File.Exists())
                {
                    throw new AutomationException("File in manifest does not exist");
                }

                DateTime OriginalTime;
                if (!Files.TryGetValue(File, out OriginalTime))
                {
                    throw new AutomationException("File in manifest did not exist previously");
                }

                double DiffSeconds = (new FileInfo(File.FullName).LastWriteTimeUtc - OriginalTime).TotalSeconds;
                if (Math.Abs(DiffSeconds) > 2)
                {
                    throw new AutomationException("Incorrect timestamp for {0}", ManifestFile.RelativePath);
                }
            }
        }
        /// <summary>
        /// Checks the integrity of the give node's local build products.
        /// </summary>
        /// <param name="NodeName">The node to retrieve build products for</param>
        /// <param name="OutputNames">List of output names from this node.</param>
        /// <returns>True if the node is complete and valid, false if not (and typically followed by a call to CleanNode()).</returns>
        public bool CheckLocalIntegrity(string NodeName, IEnumerable <string> OutputNames)
        {
            // If the node is not locally complete, fail immediately.
            FileReference CompleteMarkerFile = GetCompleteMarkerFile(LocalDir, NodeName);

            if (!CompleteMarkerFile.Exists())
            {
                return(false);
            }

            // Check that each of the outputs match
            foreach (string OutputName in OutputNames)
            {
                // Check the local manifest exists
                FileReference LocalManifestFile = GetManifestFile(LocalDir, NodeName, OutputName);
                if (!LocalManifestFile.Exists())
                {
                    return(false);
                }

                // Check the local manifest matches the shared manifest
                if (SharedDir != null)
                {
                    // Check the shared manifest exists
                    FileReference SharedManifestFile = GetManifestFile(SharedDir, NodeName, OutputName);
                    if (!SharedManifestFile.Exists())
                    {
                        return(false);
                    }

                    // Check the manifests are identical, byte by byte
                    byte[] LocalManifestBytes  = File.ReadAllBytes(LocalManifestFile.FullName);
                    byte[] SharedManifestBytes = File.ReadAllBytes(SharedManifestFile.FullName);
                    if (!LocalManifestBytes.SequenceEqual(SharedManifestBytes))
                    {
                        return(false);
                    }
                }

                // Read the manifest and check the files
                TempStorageManifest LocalManifest = TempStorageManifest.Load(LocalManifestFile);
                if (LocalManifest.Files.Any(x => !x.Compare(RootDir)))
                {
                    return(false);
                }
            }
            return(true);
        }
예제 #3
0
        public static TempStorageManifest SaveTempStorageManifest(string RootDir, string FinalFilename, List <string> Files)
        {
            var Saver = new TempStorageManifest();

            Saver.Create(Files, RootDir);
            if (Saver.GetFileCount() != Files.Count)
            {
                throw new AutomationException("Saver manifest differs has wrong number of files {0} != {1}", Saver.GetFileCount(), Files.Count);
            }
            var TempFilename = FinalFilename + ".temp";

            if (FileExists_NoExceptions(true, TempFilename))
            {
                throw new AutomationException("Temp manifest file already exists {0}", TempFilename);
            }
            CreateDirectory(true, Path.GetDirectoryName(FinalFilename));
            Saver.Save(TempFilename);

            var Tester = new TempStorageManifest();

            Tester.Load(TempFilename, true);

            if (!Saver.Compare(Tester))
            {
                throw new AutomationException("Temp manifest differs {0}", TempFilename);
            }

            RenameFile(TempFilename, FinalFilename, true);
            if (FileExists_NoExceptions(true, TempFilename))
            {
                throw new AutomationException("Temp manifest didn't go away {0}", TempFilename);
            }
            var FinalTester = new TempStorageManifest();

            FinalTester.Load(FinalFilename, true);

            if (!Saver.Compare(FinalTester))
            {
                throw new AutomationException("Final manifest differs {0}", TempFilename);
            }
            Log("Saved {0} with {1} files and total size {2}", FinalFilename, Saver.GetFileCount(), Saver.GetTotalSize());
            return(Saver);
        }
        /// <summary>
        /// Saves the given files (that should be rooted at the branch root) to a shared temp storage manifest with the given temp storage node and game.
        /// </summary>
        /// <param name="NodeName">The node which these build products belong to</param>
        /// <param name="OutputName">The output name of the node.</param>
        /// <param name="BuildProducts">Array of build products to be archived</param>
        /// <param name="bPushToRemote">Allow skipping the copying of this manifest to shared storage, because it's not required by any other agent</param>
        /// <returns>The created manifest instance (which has already been saved to disk).</returns>
        public TempStorageManifest Archive(string NodeName, string OutputName, FileReference[] BuildProducts, bool bPushToRemote = true)
        {
            using (TelemetryStopwatch TelemetryStopwatch = new TelemetryStopwatch("StoreToTempStorage"))
            {
                // Create a manifest for the given build products
                FileInfo[]          Files    = BuildProducts.Select(x => new FileInfo(x.FullName)).ToArray();
                TempStorageManifest Manifest = new TempStorageManifest(Files, RootDir);

                // Create the local directory for this node
                DirectoryReference LocalNodeDir = GetDirectoryForNode(LocalDir, NodeName);
                LocalNodeDir.CreateDirectory();

                // Compress the files and copy to shared storage if necessary
                bool bRemote = SharedDir != null && bPushToRemote && bWriteToSharedStorage;
                if (bRemote)
                {
                    // Create the shared directory for this node
                    DirectoryReference SharedNodeDir = GetDirectoryForNode(SharedDir, NodeName);
                    SharedNodeDir.CreateDirectory();

                    // Zip all the build products
                    FileInfo[] ZipFiles = ParallelZipFiles(Files, RootDir, SharedNodeDir, LocalNodeDir, OutputName);
                    Manifest.ZipFiles = ZipFiles.Select(x => new TempStorageZipFile(x)).ToArray();

                    // Save the shared manifest
                    FileReference SharedManifestFile = GetManifestFile(SharedDir, NodeName, OutputName);
                    CommandUtils.Log("Saving shared manifest to {0}", SharedManifestFile.FullName);
                    Manifest.Save(SharedManifestFile);
                }

                // Save the local manifest
                FileReference LocalManifestFile = GetManifestFile(LocalDir, NodeName, OutputName);
                CommandUtils.Log("Saving local manifest to {0}", LocalManifestFile.FullName);
                Manifest.Save(LocalManifestFile);

                // Update the stats
                long ZipFilesTotalSize = (Manifest.ZipFiles == null)? 0 : Manifest.ZipFiles.Sum(x => x.Length);
                TelemetryStopwatch.Finish(string.Format("StoreToTempStorage.{0}.{1}.{2}.{3}.{4}.{5}.{6}", Files.Length, Manifest.GetTotalSize(), ZipFilesTotalSize, bRemote? "Remote" : "Local", 0, 0, OutputName));
                return(Manifest);
            }
        }
예제 #5
0
        /// <summary>
        /// Build a node
        /// </summary>
        /// <param name="Job">Information about the current job</param>
        /// <param name="Graph">The graph to which the node belongs. Used to determine which outputs need to be transferred to temp storage.</param>
        /// <param name="Node">The node to build</param>
        /// <param name="Storage">The temp storage backend which stores the shared state</param>
        /// <param name="bWithBanner">Whether to write a banner before and after this node's log output</param>
        /// <returns>True if the node built successfully, false otherwise.</returns>
        bool BuildNode(JobContext Job, Graph Graph, Node Node, TempStorage Storage, bool bWithBanner)
        {
            DirectoryReference RootDir = new DirectoryReference(CommandUtils.CmdEnv.LocalRoot);

            // Create the mapping of tag names to file sets
            Dictionary <string, HashSet <FileReference> > TagNameToFileSet = new Dictionary <string, HashSet <FileReference> >();

            // Read all the input tags for this node, and build a list of referenced input storage blocks
            HashSet <TempStorageBlock> InputStorageBlocks = new HashSet <TempStorageBlock>();

            foreach (NodeOutput Input in Node.Inputs)
            {
                TempStorageFileList FileList = Storage.ReadFileList(Input.ProducingNode.Name, Input.TagName);
                TagNameToFileSet[Input.TagName] = FileList.ToFileSet(RootDir);
                InputStorageBlocks.UnionWith(FileList.Blocks);
            }

            // Read the manifests for all the input storage blocks
            Dictionary <TempStorageBlock, TempStorageManifest> InputManifests = new Dictionary <TempStorageBlock, TempStorageManifest>();

            foreach (TempStorageBlock InputStorageBlock in InputStorageBlocks)
            {
                TempStorageManifest Manifest = Storage.Retreive(InputStorageBlock.NodeName, InputStorageBlock.OutputName);
                InputManifests[InputStorageBlock] = Manifest;
            }

            // Read all the input storage blocks, keeping track of which block each file came from
            Dictionary <FileReference, TempStorageBlock> FileToStorageBlock = new Dictionary <FileReference, TempStorageBlock>();

            foreach (KeyValuePair <TempStorageBlock, TempStorageManifest> Pair in InputManifests)
            {
                TempStorageBlock InputStorageBlock = Pair.Key;
                foreach (FileReference File in Pair.Value.Files.Select(x => x.ToFileReference(RootDir)))
                {
                    TempStorageBlock CurrentStorageBlock;
                    if (FileToStorageBlock.TryGetValue(File, out CurrentStorageBlock) && !TempStorage.IsDuplicateBuildProduct(File))
                    {
                        LogError("File '{0}' was produced by {1} and {2}", File, InputStorageBlock, CurrentStorageBlock);
                    }
                    FileToStorageBlock[File] = InputStorageBlock;
                }
            }

            // Add placeholder outputs for the current node
            foreach (NodeOutput Output in Node.Outputs)
            {
                TagNameToFileSet.Add(Output.TagName, new HashSet <FileReference>());
            }

            // Execute the node
            if (bWithBanner)
            {
                Console.WriteLine();
                CommandUtils.LogInformation("========== Starting: {0} ==========", Node.Name);
            }
            if (!Node.Build(Job, TagNameToFileSet))
            {
                return(false);
            }
            if (bWithBanner)
            {
                CommandUtils.LogInformation("========== Finished: {0} ==========", Node.Name);
                Console.WriteLine();
            }

            // Check that none of the inputs have been clobbered
            Dictionary <string, string> ModifiedFiles = new Dictionary <string, string>(StringComparer.InvariantCultureIgnoreCase);

            foreach (TempStorageFile File in InputManifests.Values.SelectMany(x => x.Files))
            {
                string Message;
                if (!ModifiedFiles.ContainsKey(File.RelativePath) && !File.Compare(CommandUtils.RootDirectory, out Message))
                {
                    ModifiedFiles.Add(File.RelativePath, Message);
                }
            }
            if (ModifiedFiles.Count > 0)
            {
                throw new AutomationException("Build {0} from a previous step have been modified:\n{1}", (ModifiedFiles.Count == 1)? "product" : "products", String.Join("\n", ModifiedFiles.Select(x => x.Value)));
            }

            // Determine all the output files which are required to be copied to temp storage (because they're referenced by nodes in another agent)
            HashSet <FileReference> ReferencedOutputFiles = new HashSet <FileReference>();

            foreach (Agent Agent in Graph.Agents)
            {
                bool bSameAgent = Agent.Nodes.Contains(Node);
                foreach (Node OtherNode in Agent.Nodes)
                {
                    if (!bSameAgent || Node.ControllingTrigger != OtherNode.ControllingTrigger)
                    {
                        foreach (NodeOutput Input in OtherNode.Inputs.Where(x => x.ProducingNode == Node))
                        {
                            ReferencedOutputFiles.UnionWith(TagNameToFileSet[Input.TagName]);
                        }
                    }
                }
            }

            // Find a block name for all new outputs
            Dictionary <FileReference, string> FileToOutputName = new Dictionary <FileReference, string>();

            foreach (NodeOutput Output in Node.Outputs)
            {
                HashSet <FileReference> Files = TagNameToFileSet[Output.TagName];
                foreach (FileReference File in Files)
                {
                    if (!FileToStorageBlock.ContainsKey(File) && File.IsUnderDirectory(RootDir))
                    {
                        if (Output == Node.DefaultOutput)
                        {
                            if (!FileToOutputName.ContainsKey(File))
                            {
                                FileToOutputName[File] = "";
                            }
                        }
                        else
                        {
                            string OutputName;
                            if (FileToOutputName.TryGetValue(File, out OutputName) && OutputName.Length > 0)
                            {
                                FileToOutputName[File] = String.Format("{0}+{1}", OutputName, Output.TagName.Substring(1));
                            }
                            else
                            {
                                FileToOutputName[File] = Output.TagName.Substring(1);
                            }
                        }
                    }
                }
            }

            // Invert the dictionary to make a mapping of storage block to the files each contains
            Dictionary <string, HashSet <FileReference> > OutputStorageBlockToFiles = new Dictionary <string, HashSet <FileReference> >();

            foreach (KeyValuePair <FileReference, string> Pair in FileToOutputName)
            {
                HashSet <FileReference> Files;
                if (!OutputStorageBlockToFiles.TryGetValue(Pair.Value, out Files))
                {
                    Files = new HashSet <FileReference>();
                    OutputStorageBlockToFiles.Add(Pair.Value, Files);
                }
                Files.Add(Pair.Key);
            }

            // Write all the storage blocks, and update the mapping from file to storage block
            foreach (KeyValuePair <string, HashSet <FileReference> > Pair in OutputStorageBlockToFiles)
            {
                TempStorageBlock OutputBlock = new TempStorageBlock(Node.Name, Pair.Key);
                foreach (FileReference File in Pair.Value)
                {
                    FileToStorageBlock.Add(File, OutputBlock);
                }
                Storage.Archive(Node.Name, Pair.Key, Pair.Value.ToArray(), Pair.Value.Any(x => ReferencedOutputFiles.Contains(x)));
            }

            // Publish all the output tags
            foreach (NodeOutput Output in Node.Outputs)
            {
                HashSet <FileReference> Files = TagNameToFileSet[Output.TagName];

                HashSet <TempStorageBlock> StorageBlocks = new HashSet <TempStorageBlock>();
                foreach (FileReference File in Files)
                {
                    TempStorageBlock StorageBlock;
                    if (FileToStorageBlock.TryGetValue(File, out StorageBlock))
                    {
                        StorageBlocks.Add(StorageBlock);
                    }
                }

                Storage.WriteFileList(Node.Name, Output.TagName, Files, StorageBlocks.ToArray());
            }

            // Mark the node as succeeded
            Storage.MarkAsComplete(Node.Name);
            return(true);
        }
예제 #6
0
        public static List<string> RetrieveFromTempStorage(CommandEnvironment Env, string StorageBlockName, out bool WasLocal, string GameFolder = "", string BaseFolder = "")
        {
            if (String.IsNullOrEmpty(BaseFolder))
            {
                BaseFolder = Env.LocalRoot;
            }

            BaseFolder = CombinePaths(BaseFolder, "/");
            if (!BaseFolder.EndsWith("/") && !BaseFolder.EndsWith("\\"))
            {
                throw new AutomationException("base folder {0} should end with a separator", BaseFolder);
            }

            var Files = new List<string>();
            var LocalManifest = LocalTempStorageManifestFilename(Env, StorageBlockName);
            if (FileExists_NoExceptions(LocalManifest))
            {
                Log("Found local manifest {0}", LocalManifest);
                var Local = new TempStorageManifest();
                Local.Load(LocalManifest);
                Files = Local.GetFiles(BaseFolder);
                var LocalTest = new TempStorageManifest();
                LocalTest.Create(Files, BaseFolder);
                if (!Local.Compare(LocalTest))
                {
                    throw new AutomationException("Local files in manifest {0} were tampered with.", LocalManifest);
                }
                WasLocal = true;
                return Files;
            }
            WasLocal = false;
            var StartTime = DateTime.UtcNow;

            var BlockPath = CombinePaths(SharedTempStorageDirectory(StorageBlockName, GameFolder), "/");
            if (!BlockPath.EndsWith("/") && !BlockPath.EndsWith("\\"))
            {
                throw new AutomationException("base folder {0} should end with a separator", BlockPath);
            }
            Log("Attempting to retrieve from {0}", BlockPath);
            if (!DirectoryExists_NoExceptions(BlockPath))
            {
                throw new AutomationException("Storage Block Does Not Exists! {0}", BlockPath);
            }
            var SharedManifest = SharedTempStorageManifestFilename(Env, StorageBlockName, GameFolder);
            Robust_FileExists_NoExceptions(SharedManifest, "Storage Block Manifest Does Not Exists! {0}");

            var Shared = new TempStorageManifest();
            Shared.Load(SharedManifest);

            var SharedFiles = Shared.GetFiles(BlockPath);

            var DestFiles = new List<string>();
            if (ThreadsToCopyWith() < 2)
            {
                foreach (string InFilename in SharedFiles)
                {
                    var Filename = CombinePaths(InFilename);
                    Robust_FileExists_NoExceptions(true, Filename, "Could not add {0} to manifest because it does not exist");

                    if (!Filename.StartsWith(BlockPath, StringComparison.InvariantCultureIgnoreCase))
                    {
                        throw new AutomationException("Could not add {0} to manifest because it does not start with the base folder {1}", Filename, BlockPath);
                    }
                    var RelativeFile = Filename.Substring(BlockPath.Length);
                    var DestFile = CombinePaths(BaseFolder, RelativeFile);
                    if (FileExists_NoExceptions(true, DestFile))
                    {
                        Log("Dest file {0} already exists, deleting and overwriting", DestFile);
                        DeleteFile(DestFile);
                    }
                    CopyFile(Filename, DestFile, true);

                    Robust_FileExists_NoExceptions(true, DestFile, "Could not copy to {0}");

                    if (UnrealBuildTool.Utils.IsRunningOnMono)
                    {
						FixUnixFilePermissions(DestFile);
                    }

                    FileInfo Info = new FileInfo(DestFile);
                    DestFiles.Add(Info.FullName);
                }
            }
            else
            {
                var SrcFiles = new List<string>();
                foreach (string InFilename in SharedFiles)
                {
                    var Filename = CombinePaths(InFilename);
                    //Robust_FileExists_NoExceptions(true, Filename, "Could not add {0} to manifest because it does not exist");

                    if (!Filename.StartsWith(BlockPath, StringComparison.InvariantCultureIgnoreCase))
                    {
                        throw new AutomationException("Could not add {0} to manifest because it does not start with the base folder {1}", Filename, BlockPath);
                    }
                    var RelativeFile = Filename.Substring(BlockPath.Length);
                    var DestFile = CombinePaths(BaseFolder, RelativeFile);
                    if (FileExists_NoExceptions(true, DestFile))
                    {
                        Log("Dest file {0} already exists, deleting and overwriting", DestFile);
                        DeleteFile(DestFile);
                    }
                    SrcFiles.Add(Filename);
                    DestFiles.Add(DestFile);
                }
                ThreadedCopyFiles(SrcFiles.ToArray(), DestFiles.ToArray(), ThreadsToCopyWith());
                var NewDestFiles = new List<string>();
                foreach (string DestFile in DestFiles)
                {
                    Robust_FileExists_NoExceptions(true, DestFile, "Could not copy to {0}");
                    if (UnrealBuildTool.Utils.IsRunningOnMono)
                    {
						FixUnixFilePermissions(DestFile);
                    }
                    FileInfo Info = new FileInfo(DestFile);
                    NewDestFiles.Add(Info.FullName);
                }
                DestFiles = NewDestFiles;
            }
            var NewLocal = SaveLocalTempStorageManifest(Env, BaseFolder, StorageBlockName, DestFiles);
            if (!NewLocal.Compare(Shared))
            {
                // we will rename this so it can't be used, but leave it around for inspection
                RenameFile_NoExceptions(LocalManifest, LocalManifest + ".broken");
                throw new AutomationException("Shared and Local manifest mismatch.");
            }
            float BuildDuration = (float)((DateTime.UtcNow - StartTime).TotalSeconds);
            if (BuildDuration > 60.0f && Shared.GetTotalSize() > 0)
            {
                var MBSec = (((float)(Shared.GetTotalSize())) / (1024.0f * 1024.0f)) / BuildDuration;
                Log("Read from shared temp storage at {0} MB/s    {1}B {2}s", MBSec, Shared.GetTotalSize(), BuildDuration);
            }
            return DestFiles;
        }
예제 #7
0
        public static TempStorageManifest SaveTempStorageManifest(string RootDir, string FinalFilename, List<string> Files)
        {
            var Saver = new TempStorageManifest();
            Saver.Create(Files, RootDir);
            if (Saver.GetFileCount() != Files.Count)
            {
                throw new AutomationException("Saver manifest differs has wrong number of files {0} != {1}", Saver.GetFileCount(), Files.Count);
            }
            var TempFilename = FinalFilename + ".temp";
            if (FileExists_NoExceptions(true, TempFilename))
            {
                throw new AutomationException("Temp manifest file already exists {0}", TempFilename);
            }
            CreateDirectory(true, Path.GetDirectoryName(FinalFilename));
            Saver.Save(TempFilename);

            var Tester = new TempStorageManifest();
            Tester.Load(TempFilename, true);

            if (!Saver.Compare(Tester))
            {
                throw new AutomationException("Temp manifest differs {0}", TempFilename);
            }

            RenameFile(TempFilename, FinalFilename, true);
            if (FileExists_NoExceptions(true, TempFilename))
            {
                throw new AutomationException("Temp manifest didn't go away {0}", TempFilename);
            }
            var FinalTester = new TempStorageManifest();
            FinalTester.Load(FinalFilename, true);

            if (!Saver.Compare(FinalTester))
            {
                throw new AutomationException("Final manifest differs {0}", TempFilename);
            }
            Log("Saved {0} with {1} files and total size {2}", FinalFilename, Saver.GetFileCount(), Saver.GetTotalSize());
            return Saver;
        }
예제 #8
0
            public bool Compare(TempStorageManifest Other)
            {
                if (Directories.Count != Other.Directories.Count)
                {
                    Log(System.Diagnostics.TraceEventType.Error, "Directory count mismatch {0} {1}", Directories.Count, Other.Directories.Count);
                    foreach (KeyValuePair<string, List<TempStorageFileInfo>> Directory in Directories)
                    {
                        List<TempStorageFileInfo> OtherDirectory;
                        if (Other.Directories.TryGetValue(Directory.Key, out OtherDirectory) == false)
                        {
                            Log(System.Diagnostics.TraceEventType.Error, "Missing Directory {0}", Directory.Key);
                            return false;
                        }
                    }
                    foreach (KeyValuePair<string, List<TempStorageFileInfo>> Directory in Other.Directories)
                    {
                        List<TempStorageFileInfo> OtherDirectory;
                        if (Directories.TryGetValue(Directory.Key, out OtherDirectory) == false)
                        {
                            Log(System.Diagnostics.TraceEventType.Error, "Missing Other Directory {0}", Directory.Key);
                            return false;
                        }
                    }
                    return false;
                }

                foreach (KeyValuePair<string, List<TempStorageFileInfo>> Directory in Directories)
                {
                    List<TempStorageFileInfo> OtherDirectory;
                    if (Other.Directories.TryGetValue(Directory.Key, out OtherDirectory) == false)
                    {
                        Log(System.Diagnostics.TraceEventType.Error, "Missing Directory {0}", Directory.Key); 
                        return false;
                    }
                    if (OtherDirectory.Count != Directory.Value.Count)
                    {
                        Log(System.Diagnostics.TraceEventType.Error, "File count mismatch {0} {1} {2}", Directory.Key, OtherDirectory.Count, Directory.Value.Count);
                        for (int FileIndex = 0; FileIndex < Directory.Value.Count; ++FileIndex)
                        {
                            Log("Manifest1: {0}", Directory.Value[FileIndex].Name);
                        }
                        for (int FileIndex = 0; FileIndex < OtherDirectory.Count; ++FileIndex)
                        {
                            Log("Manifest2: {0}", OtherDirectory[FileIndex].Name);
                        }
                        return false;
                    }
                    bool bResult = true;
                    for (int FileIndex = 0; FileIndex < Directory.Value.Count; ++FileIndex)
                    {
                        TempStorageFileInfo File = Directory.Value[FileIndex];
                        TempStorageFileInfo OtherFile = OtherDirectory[FileIndex];
                        if (File.Compare(OtherFile) == false)
                        {
                            bResult = false;
                        }
                    }
                    return bResult;
                }

                return true;
            }
예제 #9
0
        /// <summary>
        /// Build a node
        /// </summary>
        /// <param name="Job">Information about the current job</param>
        /// <param name="Graph">The graph to which the node belongs. Used to determine which outputs need to be transferred to temp storage.</param>
        /// <param name="Node">The node to build</param>
        /// <returns>True if the node built successfully, false otherwise.</returns>
        bool BuildNode(JobContext Job, Graph Graph, Node Node, TempStorage Storage, bool bWithBanner)
        {
            // Create a mapping from tag name to the files it contains, and seed it with invalid entries for everything in the graph
            Dictionary <string, HashSet <FileReference> > TagNameToFileSet = new Dictionary <string, HashSet <FileReference> >();

            foreach (NodeOutput Output in Graph.Groups.SelectMany(x => x.Nodes).SelectMany(x => x.Outputs))
            {
                TagNameToFileSet[Output.Name] = null;
            }

            // Fetch all the input dependencies for this node, and fill in the tag names with those files
            DirectoryReference RootDir = new DirectoryReference(CommandUtils.CmdEnv.LocalRoot);

            foreach (NodeOutput Input in Node.Inputs)
            {
                TempStorageManifest Manifest = Storage.Retreive(Input.ProducingNode.Name, Input.Name);
                TagNameToFileSet[Input.Name] = new HashSet <FileReference>(Manifest.Files.Select(x => x.ToFileReference(RootDir)));
            }

            // Add placeholder outputs for the current node
            foreach (NodeOutput Output in Node.Outputs)
            {
                TagNameToFileSet[Output.Name] = new HashSet <FileReference>();
            }

            // Execute the node
            if (bWithBanner)
            {
                Console.WriteLine();
                CommandUtils.Log("========== Starting: {0} ==========", Node.Name);
            }
            if (!Node.Build(Job, TagNameToFileSet))
            {
                return(false);
            }
            if (bWithBanner)
            {
                CommandUtils.Log("========== Finished: {0} ==========", Node.Name);
                Console.WriteLine();
            }

            // Determine all the outputs which are required to be copied to temp storage (because they're referenced by nodes in another agent group)
            HashSet <NodeOutput> ReferencedOutputs = new HashSet <NodeOutput>();

            foreach (AgentGroup Group in Graph.Groups)
            {
                bool bSameGroup = Group.Nodes.Contains(Node);
                foreach (Node OtherNode in Group.Nodes)
                {
                    if (!bSameGroup || Node.ControllingTrigger != OtherNode.ControllingTrigger)
                    {
                        ReferencedOutputs.UnionWith(OtherNode.Inputs);
                    }
                }
            }

            // Publish all the outputs
            foreach (NodeOutput Output in Node.Outputs)
            {
                Storage.Archive(Node.Name, Output.Name, TagNameToFileSet[Output.Name].ToArray(), ReferencedOutputs.Contains(Output));
            }

            // Mark the node as succeeded
            Storage.MarkAsComplete(Node.Name);
            return(true);
        }
예제 #10
0
        public static List <string> RetrieveFromTempStorage(CommandEnvironment Env, string StorageBlockName, out bool WasLocal, string GameFolder = "", string BaseFolder = "")
        {
            if (String.IsNullOrEmpty(BaseFolder))
            {
                BaseFolder = Env.LocalRoot;
            }

            BaseFolder = CombinePaths(BaseFolder, "/");
            if (!BaseFolder.EndsWith("/") && !BaseFolder.EndsWith("\\"))
            {
                throw new AutomationException("base folder {0} should end with a separator", BaseFolder);
            }

            var Files         = new List <string>();
            var LocalManifest = LocalTempStorageManifestFilename(Env, StorageBlockName);

            if (FileExists_NoExceptions(LocalManifest))
            {
                Log("Found local manifest {0}", LocalManifest);
                var Local = new TempStorageManifest();
                Local.Load(LocalManifest);
                Files = Local.GetFiles(BaseFolder);
                var LocalTest = new TempStorageManifest();
                LocalTest.Create(Files, BaseFolder);
                if (!Local.Compare(LocalTest))
                {
                    throw new AutomationException("Local files in manifest {0} were tampered with.", LocalManifest);
                }
                WasLocal = true;
                return(Files);
            }
            WasLocal = false;
            var StartTime = DateTime.UtcNow;

            var BlockPath = CombinePaths(SharedTempStorageDirectory(StorageBlockName, GameFolder), "/");

            if (!BlockPath.EndsWith("/") && !BlockPath.EndsWith("\\"))
            {
                throw new AutomationException("base folder {0} should end with a separator", BlockPath);
            }
            Log("Attempting to retrieve from {0}", BlockPath);
            if (!DirectoryExists_NoExceptions(BlockPath))
            {
                throw new AutomationException("Storage Block Does Not Exists! {0}", BlockPath);
            }
            var SharedManifest = SharedTempStorageManifestFilename(Env, StorageBlockName, GameFolder);

            Robust_FileExists_NoExceptions(SharedManifest, "Storage Block Manifest Does Not Exists! {0}");

            var Shared = new TempStorageManifest();

            Shared.Load(SharedManifest);

            var SharedFiles = Shared.GetFiles(BlockPath);

            var DestFiles = new List <string>();

            if (ThreadsToCopyWith() < 2)
            {
                foreach (string InFilename in SharedFiles)
                {
                    var Filename = CombinePaths(InFilename);
                    Robust_FileExists_NoExceptions(true, Filename, "Could not add {0} to manifest because it does not exist");

                    if (!Filename.StartsWith(BlockPath, StringComparison.InvariantCultureIgnoreCase))
                    {
                        throw new AutomationException("Could not add {0} to manifest because it does not start with the base folder {1}", Filename, BlockPath);
                    }
                    var RelativeFile = Filename.Substring(BlockPath.Length);
                    var DestFile     = CombinePaths(BaseFolder, RelativeFile);
                    if (FileExists_NoExceptions(true, DestFile))
                    {
                        Log("Dest file {0} already exists, deleting and overwriting", DestFile);
                        DeleteFile(DestFile);
                    }
                    CopyFile(Filename, DestFile, true);

                    Robust_FileExists_NoExceptions(true, DestFile, "Could not copy to {0}");

                    if (UnrealBuildTool.Utils.IsRunningOnMono)
                    {
                        FixUnixFilePermissions(DestFile);
                    }

                    FileInfo Info = new FileInfo(DestFile);
                    DestFiles.Add(Info.FullName);
                }
            }
            else
            {
                var SrcFiles = new List <string>();
                foreach (string InFilename in SharedFiles)
                {
                    var Filename = CombinePaths(InFilename);
                    //Robust_FileExists_NoExceptions(true, Filename, "Could not add {0} to manifest because it does not exist");

                    if (!Filename.StartsWith(BlockPath, StringComparison.InvariantCultureIgnoreCase))
                    {
                        throw new AutomationException("Could not add {0} to manifest because it does not start with the base folder {1}", Filename, BlockPath);
                    }
                    var RelativeFile = Filename.Substring(BlockPath.Length);
                    var DestFile     = CombinePaths(BaseFolder, RelativeFile);
                    if (FileExists_NoExceptions(true, DestFile))
                    {
                        Log("Dest file {0} already exists, deleting and overwriting", DestFile);
                        DeleteFile(DestFile);
                    }
                    SrcFiles.Add(Filename);
                    DestFiles.Add(DestFile);
                }
                ThreadedCopyFiles(SrcFiles.ToArray(), DestFiles.ToArray(), ThreadsToCopyWith());
                var NewDestFiles = new List <string>();
                foreach (string DestFile in DestFiles)
                {
                    Robust_FileExists_NoExceptions(true, DestFile, "Could not copy to {0}");
                    if (UnrealBuildTool.Utils.IsRunningOnMono)
                    {
                        FixUnixFilePermissions(DestFile);
                    }
                    FileInfo Info = new FileInfo(DestFile);
                    NewDestFiles.Add(Info.FullName);
                }
                DestFiles = NewDestFiles;
            }
            var NewLocal = SaveLocalTempStorageManifest(Env, BaseFolder, StorageBlockName, DestFiles);

            if (!NewLocal.Compare(Shared))
            {
                // we will rename this so it can't be used, but leave it around for inspection
                RenameFile_NoExceptions(LocalManifest, LocalManifest + ".broken");
                throw new AutomationException("Shared and Local manifest mismatch.");
            }
            float BuildDuration = (float)((DateTime.UtcNow - StartTime).TotalSeconds);

            if (BuildDuration > 60.0f && Shared.GetTotalSize() > 0)
            {
                var MBSec = (((float)(Shared.GetTotalSize())) / (1024.0f * 1024.0f)) / BuildDuration;
                Log("Read from shared temp storage at {0} MB/s    {1}B {2}s", MBSec, Shared.GetTotalSize(), BuildDuration);
            }
            return(DestFiles);
        }
예제 #11
0
            public bool Compare(TempStorageManifest Other)
            {
                if (Directories.Count != Other.Directories.Count)
                {
                    Log(System.Diagnostics.TraceEventType.Error, "Directory count mismatch {0} {1}", Directories.Count, Other.Directories.Count);
                    foreach (KeyValuePair <string, List <TempStorageFileInfo> > Directory in Directories)
                    {
                        List <TempStorageFileInfo> OtherDirectory;
                        if (Other.Directories.TryGetValue(Directory.Key, out OtherDirectory) == false)
                        {
                            Log(System.Diagnostics.TraceEventType.Error, "Missing Directory {0}", Directory.Key);
                            return(false);
                        }
                    }
                    foreach (KeyValuePair <string, List <TempStorageFileInfo> > Directory in Other.Directories)
                    {
                        List <TempStorageFileInfo> OtherDirectory;
                        if (Directories.TryGetValue(Directory.Key, out OtherDirectory) == false)
                        {
                            Log(System.Diagnostics.TraceEventType.Error, "Missing Other Directory {0}", Directory.Key);
                            return(false);
                        }
                    }
                    return(false);
                }

                foreach (KeyValuePair <string, List <TempStorageFileInfo> > Directory in Directories)
                {
                    List <TempStorageFileInfo> OtherDirectory;
                    if (Other.Directories.TryGetValue(Directory.Key, out OtherDirectory) == false)
                    {
                        Log(System.Diagnostics.TraceEventType.Error, "Missing Directory {0}", Directory.Key);
                        return(false);
                    }
                    if (OtherDirectory.Count != Directory.Value.Count)
                    {
                        Log(System.Diagnostics.TraceEventType.Error, "File count mismatch {0} {1} {2}", Directory.Key, OtherDirectory.Count, Directory.Value.Count);
                        for (int FileIndex = 0; FileIndex < Directory.Value.Count; ++FileIndex)
                        {
                            Log("Manifest1: {0}", Directory.Value[FileIndex].Name);
                        }
                        for (int FileIndex = 0; FileIndex < OtherDirectory.Count; ++FileIndex)
                        {
                            Log("Manifest2: {0}", OtherDirectory[FileIndex].Name);
                        }
                        return(false);
                    }
                    bool bResult = true;
                    for (int FileIndex = 0; FileIndex < Directory.Value.Count; ++FileIndex)
                    {
                        TempStorageFileInfo File      = Directory.Value[FileIndex];
                        TempStorageFileInfo OtherFile = OtherDirectory[FileIndex];
                        if (File.Compare(OtherFile) == false)
                        {
                            bResult = false;
                        }
                    }
                    return(bResult);
                }

                return(true);
            }
예제 #12
0
		/// <summary>
		/// Saves the given files (that should be rooted at the branch root) to a shared temp storage manifest with the given temp storage node and game.
		/// </summary>
		/// <param name="NodeName">The node which created the storage block</param>
		/// <param name="BlockName">Name of the block to retrieve. May be null or empty.</param>
		/// <param name="BuildProducts">Array of build products to be archived</param>
		/// <param name="bPushToRemote">Allow skipping the copying of this manifest to shared storage, because it's not required by any other agent</param>
		/// <returns>The created manifest instance (which has already been saved to disk).</returns>
		public TempStorageManifest Archive(string NodeName, string BlockName, FileReference[] BuildProducts, bool bPushToRemote = true)
		{
			using(TelemetryStopwatch TelemetryStopwatch = new TelemetryStopwatch("StoreToTempStorage"))
			{
				// Create a manifest for the given build products
				FileInfo[] Files = BuildProducts.Select(x => new FileInfo(x.FullName)).ToArray();
				TempStorageManifest Manifest = new TempStorageManifest(Files, RootDir);

				// Create the local directory for this node
				DirectoryReference LocalNodeDir = GetDirectoryForNode(LocalDir, NodeName);
				LocalNodeDir.CreateDirectory();

				// Compress the files and copy to shared storage if necessary
				bool bRemote = SharedDir != null && bPushToRemote && bWriteToSharedStorage;
				if(bRemote)
				{
					// Create the shared directory for this node
					FileReference SharedManifestFile = GetManifestLocation(SharedDir, NodeName, BlockName);
					SharedManifestFile.Directory.CreateDirectory();

					// Zip all the build products
					FileInfo[] ZipFiles = ParallelZipFiles(Files, RootDir, SharedManifestFile.Directory, LocalNodeDir, SharedManifestFile.GetFileNameWithoutExtension());
					Manifest.ZipFiles = ZipFiles.Select(x => new TempStorageZipFile(x)).ToArray();

					// Save the shared manifest
					CommandUtils.Log("Saving shared manifest to {0}", SharedManifestFile.FullName);
					Manifest.Save(SharedManifestFile);
				}

				// Save the local manifest
				FileReference LocalManifestFile = GetManifestLocation(LocalDir, NodeName, BlockName);
				CommandUtils.Log("Saving local manifest to {0}", LocalManifestFile.FullName);
				Manifest.Save(LocalManifestFile);

				// Update the stats
				long ZipFilesTotalSize = (Manifest.ZipFiles == null)? 0 : Manifest.ZipFiles.Sum(x => x.Length);
				TelemetryStopwatch.Finish(string.Format("StoreToTempStorage.{0}.{1}.{2}.{3}.{4}.{5}.{6}", Files.Length, Manifest.GetTotalSize(), ZipFilesTotalSize, bRemote? "Remote" : "Local", 0, 0, BlockName));
				return Manifest;
			}
		}
예제 #13
0
		/// <summary>
		/// Checks that a manifest matches the files on disk
		/// </summary>
		/// <param name="RootDir">Root directory for relative paths in the manifest</param>
		/// <param name="Manifest">Manifest to check</param>
		/// <param name="Files">Mapping of filename to timestamp as expected in the manifest</param>
		static void CheckManifest(DirectoryReference RootDir, TempStorageManifest Manifest, Dictionary<FileReference, DateTime> Files)
		{
			if(Files.Count != Manifest.Files.Length)
			{
				throw new AutomationException("Number of files in manifest does not match");
			}
			foreach(TempStorageFile ManifestFile in Manifest.Files)
			{
				FileReference File = ManifestFile.ToFileReference(RootDir);
				if(!File.Exists())
				{
					throw new AutomationException("File in manifest does not exist");
				}

				DateTime OriginalTime;
				if(!Files.TryGetValue(File, out OriginalTime))
				{
					throw new AutomationException("File in manifest did not exist previously");
				}

				double DiffSeconds = (new FileInfo(File.FullName).LastWriteTimeUtc - OriginalTime).TotalSeconds;
				if(Math.Abs(DiffSeconds) > 2)
				{
					throw new AutomationException("Incorrect timestamp for {0}", ManifestFile.RelativePath);
				}
			}
		}
예제 #14
0
        public static List<string> RetrieveFromTempStorage(CommandEnvironment Env, string StorageBlockName, string GameFolder = "", string BaseFolder = "")
        {
            if (String.IsNullOrEmpty(BaseFolder))
            {
                BaseFolder = Env.LocalRoot;
            }

            BaseFolder = CombinePaths(BaseFolder, "/");
            if (!BaseFolder.EndsWith("/") && !BaseFolder.EndsWith("\\"))
            {
                throw new AutomationException("base folder {0} should end with a separator", BaseFolder);
            }

            var Files = new List<string>();
            var LocalManifest = LocalTempStorageManifestFilename(Env, StorageBlockName);
            if (FileExists_NoExceptions(LocalManifest))
            {
                Log("Found local manifest {0}", LocalManifest);
                var Local = new TempStorageManifest();
                Local.Load(LocalManifest);
                Files = Local.GetFiles(BaseFolder);
                var LocalTest = new TempStorageManifest();
                LocalTest.Create(Files, BaseFolder);
                if (!Local.Compare(LocalTest))
                {
                    throw new AutomationException("Local files in manifest {0} were tampered with.", LocalManifest);
                }
                return Files;
            }

            var BlockPath = CombinePaths(SharedTempStorageDirectory(StorageBlockName, GameFolder, false), "/");
            if (!BlockPath.EndsWith("/") && !BlockPath.EndsWith("\\"))
            {
                throw new AutomationException("base folder {0} should end with a separator", BlockPath);
            }
            Log("Attempting to retrieve from {0}", BlockPath);
            if (!DirectoryExists_NoExceptions(BlockPath))
            {
                throw new AutomationException("Storage Block Does Not Exists! {0}", BlockPath);
            }
            var SharedManifest = SharedTempStorageManifestFilename(Env, StorageBlockName, GameFolder);
            if (!FileExists_NoExceptions(SharedManifest))
            {
                throw new AutomationException("Storage Block Manifest Does Not Exists! {0}", SharedManifest);
            }
            var Shared = new TempStorageManifest();
            Shared.Load(SharedManifest);

            var SharedFiles = Shared.GetFiles(BlockPath);

            var DestFiles = new List<string>();
            foreach (string InFilename in SharedFiles)
            {
                var Filename = CombinePaths(InFilename);
                if (!FileExists_NoExceptions(true, Filename))
                {
                    throw new AutomationException("Could not add {0} to manifest because it does not exist", Filename);
                }
                if (!Filename.StartsWith(BlockPath, StringComparison.InvariantCultureIgnoreCase))
                {
                    throw new AutomationException("Could not add {0} to manifest because it does not start with the base folder {1}", Filename, BlockPath);
                }
                var RelativeFile = Filename.Substring(BlockPath.Length);
                var DestFile = CombinePaths(BaseFolder, RelativeFile);
                if (FileExists_NoExceptions(true, DestFile))
                {
                    Log("Dest file {0} already exists, deleting and overwriting", DestFile);
                    DeleteFile(DestFile);
                }
                CopyFile(Filename, DestFile, true);
                if (!FileExists_NoExceptions(true, DestFile))
                {
                    throw new AutomationException("Could not copy {0} to {1}", Filename, DestFile);
                }
                FileInfo Info = new FileInfo(DestFile);
                DestFiles.Add(Info.FullName);
            }
            var NewLocal = SaveLocalTempStorageManifest(Env, BaseFolder, StorageBlockName, DestFiles);
            if (!NewLocal.Compare(Shared))
            {
                // we will rename this so it can't be used, but leave it around for inspection
                RenameFile_NoExceptions(LocalManifest, LocalManifest + ".broken");
                throw new AutomationException("Shared and Local manifest mismatch.");
            }
            return DestFiles;
        }
        /// <summary>
        /// Run the automated tests
        /// </summary>
        public override void ExecuteBuild()
        {
            // Get all the shared directories
            DirectoryReference RootDir = new DirectoryReference(CommandUtils.CmdEnv.LocalRoot);

            DirectoryReference LocalDir = DirectoryReference.Combine(RootDir, "Engine", "Saved", "TestTempStorage-Local");

            CommandUtils.CreateDirectory_NoExceptions(LocalDir.FullName);
            CommandUtils.DeleteDirectoryContents(LocalDir.FullName);

            DirectoryReference SharedDir = DirectoryReference.Combine(RootDir, "Engine", "Saved", "TestTempStorage-Shared");

            CommandUtils.CreateDirectory_NoExceptions(SharedDir.FullName);
            CommandUtils.DeleteDirectoryContents(SharedDir.FullName);

            DirectoryReference WorkingDir = DirectoryReference.Combine(RootDir, "Engine", "Saved", "TestTempStorage-Working");

            CommandUtils.CreateDirectory_NoExceptions(WorkingDir.FullName);
            CommandUtils.DeleteDirectoryContents(WorkingDir.FullName);

            // Create the temp storage object
            TempStorage TempStore = new TempStorage(WorkingDir, LocalDir, SharedDir, true);

            // Create a working directory, and copy some source files into it
            DirectoryReference SourceDir = DirectoryReference.Combine(RootDir, "Engine", "Source", "Runtime");

            if (!CommandUtils.CopyDirectory_NoExceptions(SourceDir.FullName, WorkingDir.FullName, true))
            {
                throw new AutomationException("Couldn't copy {0} to {1}", SourceDir.FullName, WorkingDir.FullName);
            }

            // Save the default output
            Dictionary <FileReference, DateTime> DefaultOutput = SelectFiles(WorkingDir, 'a', 'f');

            TempStore.Archive("TestNode", null, DefaultOutput.Keys.ToArray(), false);

            Dictionary <FileReference, DateTime> NamedOutput = SelectFiles(WorkingDir, 'g', 'i');

            TempStore.Archive("TestNode", "NamedOutput", NamedOutput.Keys.ToArray(), true);

            // Check both outputs are still ok
            TempStorageManifest DefaultManifest = TempStore.Retreive("TestNode", null);

            CheckManifest(WorkingDir, DefaultManifest, DefaultOutput);

            TempStorageManifest NamedManifest = TempStore.Retreive("TestNode", "NamedOutput");

            CheckManifest(WorkingDir, NamedManifest, NamedOutput);

            // Delete local temp storage and the working directory and try again
            CommandUtils.Log("Clearing local folders...");
            CommandUtils.DeleteDirectoryContents(WorkingDir.FullName);
            CommandUtils.DeleteDirectoryContents(LocalDir.FullName);

            // First output should fail
            CommandUtils.Log("Checking default manifest is now unavailable...");
            bool bGotManifest = false;

            try
            {
                TempStore.Retreive("TestNode", null);
            }
            catch
            {
                bGotManifest = false;
            }
            if (bGotManifest)
            {
                throw new AutomationException("Did not expect shared temp storage manifest to exist");
            }

            // Second one should be fine
            TempStorageManifest NamedManifestFromShared = TempStore.Retreive("TestNode", "NamedOutput");

            CheckManifest(WorkingDir, NamedManifestFromShared, NamedOutput);
        }
        /// <summary>
        /// Retrieve an output of the given node. Fetches and decompresses the files from shared storage if necessary, or validates the local files.
        /// </summary>
        /// <param name="NodeName">The node to retrieve build products for</param>
        /// <param name="OutputName">The name of the node's output. May be null.</param>
        /// <returns>Manifest of the files retrieved</returns>
        public TempStorageManifest Retreive(string NodeName, string OutputName)
        {
            using (var TelemetryStopwatch = new TelemetryStopwatch("RetrieveFromTempStorage"))
            {
                // Get the path to the local manifest
                FileReference LocalManifestFile = GetManifestFile(LocalDir, NodeName, OutputName);
                bool          bLocal            = LocalManifestFile.Exists();

                // Read the manifest, either from local storage or shared storage
                TempStorageManifest Manifest;
                if (bLocal)
                {
                    CommandUtils.Log("Reading shared manifest from {0}", LocalManifestFile.FullName);
                    Manifest = TempStorageManifest.Load(LocalManifestFile);
                }
                else
                {
                    // Check we have shared storage
                    if (SharedDir == null)
                    {
                        throw new AutomationException("Missing local manifest for node - {0}", LocalManifestFile.FullName);
                    }

                    // Get the shared directory for this node
                    FileReference SharedManifestFile = GetManifestFile(SharedDir, NodeName, OutputName);

                    // Make sure the manifest exists
                    if (!SharedManifestFile.Exists())
                    {
                        throw new AutomationException("Missing local or shared manifest for node - {0}", SharedManifestFile.FullName);
                    }

                    // Read the shared manifest
                    CommandUtils.Log("Copying shared manifest from {0} to {1}", SharedManifestFile.FullName, LocalManifestFile.FullName);
                    Manifest = TempStorageManifest.Load(SharedManifestFile);

                    // Unzip all the build products
                    DirectoryReference SharedNodeDir = GetDirectoryForNode(SharedDir, NodeName);
                    FileInfo[]         ZipFiles      = Manifest.ZipFiles.Select(x => new FileInfo(FileReference.Combine(SharedNodeDir, x.Name).FullName)).ToArray();
                    ParallelUnzipFiles(ZipFiles, RootDir);

                    // Fix any Unix permissions/chmod issues, and update the timestamps to match the manifest. Zip files only use local time, and there's no guarantee it matches the local clock.
                    foreach (TempStorageFile ManifestFile in Manifest.Files)
                    {
                        FileReference File = ManifestFile.ToFileReference(RootDir);
                        if (Utils.IsRunningOnMono)
                        {
                            CommandUtils.FixUnixFilePermissions(File.FullName);
                        }
                        System.IO.File.SetLastWriteTimeUtc(File.FullName, new DateTime(ManifestFile.LastWriteTimeUtcTicks, DateTimeKind.Utc));
                    }

                    // Save the manifest locally
                    LocalManifestFile.Directory.CreateDirectory();
                    Manifest.Save(LocalManifestFile);
                }

                // Check all the local files are as expected
                bool bAllMatch = true;
                foreach (TempStorageFile File in Manifest.Files)
                {
                    bAllMatch &= File.Compare(RootDir);
                }
                if (!bAllMatch)
                {
                    throw new AutomationException("Files have been modified");
                }

                // Update the stats and return
                TelemetryStopwatch.Finish(string.Format("RetrieveFromTempStorage.{0}.{1}.{2}.{3}.{4}.{5}.{6}", Manifest.Files.Length, Manifest.Files.Sum(x => x.Length), bLocal? 0 : Manifest.ZipFiles.Sum(x => x.Length), bLocal? "Local" : "Remote", 0, 0, OutputName));
                return(Manifest);
            }
        }