Beispiel #1
0
 public static bool WriteWorkingManifest(string FileName, string TemporaryFileName, WorkingManifest Manifest)
 {
     if (!WriteXmlObject(TemporaryFileName, Manifest))
     {
         return(false);
     }
     if (!SafeModifyFileAttributes(TemporaryFileName, FileAttributes.Hidden, 0))
     {
         return(false);
     }
     if (!SafeDeleteFile(FileName))
     {
         return(false);
     }
     if (!SafeMoveFile(TemporaryFileName, FileName))
     {
         return(false);
     }
     return(true);
 }
Beispiel #2
0
        static bool UpdateWorkingTree(bool bDryRun, string RootPath, HashSet <string> ExcludeFolders, int NumThreads, int MaxRetries, string ProxyUrl, OverwriteMode Overwrite)
        {
            // Start scanning on the working directory
            if (ExcludeFolders.Count > 0)
            {
                Log.WriteLine("Checking dependencies (excluding {0})...", String.Join(", ", ExcludeFolders));
            }
            else
            {
                Log.WriteLine("Checking dependencies...");
            }

            // Figure out the path to the working manifest
            string WorkingManifestPath = Path.Combine(RootPath, ".ue4dependencies");

            // Recover from any interrupted transaction to the working manifest, by moving the temporary file into place.
            string TempWorkingManifestPath = WorkingManifestPath + TempManifestExtension;

            if (File.Exists(TempWorkingManifestPath) && !File.Exists(WorkingManifestPath) && !SafeMoveFile(TempWorkingManifestPath, WorkingManifestPath))
            {
                return(false);
            }

            // Read the initial manifest, or create a new one
            WorkingManifest CurrentManifest;

            if (!File.Exists(WorkingManifestPath) || !ReadXmlObject(WorkingManifestPath, out CurrentManifest))
            {
                CurrentManifest = new WorkingManifest();
            }

            // Remove all the in-progress download files left over from previous runs
            foreach (WorkingFile InitialFile in CurrentManifest.Files)
            {
                if (InitialFile.Timestamp == 0)
                {
                    string IncomingFilePath = Path.Combine(RootPath, InitialFile.Name + IncomingFileSuffix);
                    if (File.Exists(IncomingFilePath) && !SafeDeleteFile(IncomingFilePath))
                    {
                        return(false);
                    }
                }
            }

            // Find all the manifests and push them into dictionaries
            Dictionary <string, DependencyFile> TargetFiles = new Dictionary <string, DependencyFile>(StringComparer.InvariantCultureIgnoreCase);
            Dictionary <string, DependencyBlob> TargetBlobs = new Dictionary <string, DependencyBlob>(StringComparer.InvariantCultureIgnoreCase);
            Dictionary <string, DependencyPack> TargetPacks = new Dictionary <string, DependencyPack>(StringComparer.InvariantCultureIgnoreCase);

            foreach (string BaseFolder in Directory.EnumerateDirectories(RootPath))
            {
                string BuildFolder = Path.Combine(BaseFolder, "Build");
                if (Directory.Exists(BuildFolder))
                {
                    foreach (string ManifestFileName in Directory.EnumerateFiles(BuildFolder, "*.gitdeps.xml"))
                    {
                        // Read this manifest
                        DependencyManifest NewTargetManifest;
                        if (!ReadXmlObject(ManifestFileName, out NewTargetManifest))
                        {
                            return(false);
                        }

                        // Add all the files, blobs and packs into the shared dictionaries
                        foreach (DependencyFile NewFile in NewTargetManifest.Files)
                        {
                            TargetFiles[NewFile.Name] = NewFile;
                        }
                        foreach (DependencyBlob NewBlob in NewTargetManifest.Blobs)
                        {
                            TargetBlobs[NewBlob.Hash] = NewBlob;
                        }
                        foreach (DependencyPack NewPack in NewTargetManifest.Packs)
                        {
                            TargetPacks[NewPack.Hash] = NewPack;
                        }
                    }
                }
            }

            // Find all the existing files in the working directory from previous runs. Use the working manifest to cache hashes for them based on timestamp, but recalculate them as needed.
            Dictionary <string, WorkingFile> CurrentFileLookup = new Dictionary <string, WorkingFile>();

            foreach (WorkingFile CurrentFile in CurrentManifest.Files)
            {
                // Update the hash for this file
                string CurrentFilePath = Path.Combine(RootPath, CurrentFile.Name);
                if (File.Exists(CurrentFilePath))
                {
                    long LastWriteTime = File.GetLastWriteTimeUtc(CurrentFilePath).Ticks;
                    if (LastWriteTime != CurrentFile.Timestamp)
                    {
                        CurrentFile.Hash      = ComputeHashForFile(CurrentFilePath);
                        CurrentFile.Timestamp = LastWriteTime;
                    }
                    CurrentFileLookup.Add(CurrentFile.Name, CurrentFile);
                }
            }

            // Also add all the untracked files which already exist, but weren't downloaded by this program
            foreach (DependencyFile TargetFile in TargetFiles.Values)
            {
                if (!CurrentFileLookup.ContainsKey(TargetFile.Name))
                {
                    string CurrentFilePath = Path.Combine(RootPath, TargetFile.Name);
                    if (File.Exists(CurrentFilePath))
                    {
                        WorkingFile CurrentFile = new WorkingFile();
                        CurrentFile.Name      = TargetFile.Name;
                        CurrentFile.Hash      = ComputeHashForFile(CurrentFilePath);
                        CurrentFile.Timestamp = File.GetLastWriteTimeUtc(CurrentFilePath).Ticks;
                        CurrentFileLookup.Add(CurrentFile.Name, CurrentFile);
                    }
                }
            }

            // Build a list of all the filtered target files
            List <DependencyFile> FilteredTargetFiles = new List <DependencyFile>();

            foreach (DependencyFile TargetFile in TargetFiles.Values)
            {
                if (!IsExcludedFolder(TargetFile.Name, ExcludeFolders))
                {
                    FilteredTargetFiles.Add(TargetFile);
                }
            }

            // Create a list of files which need to be updated, and a list of the executable files in the
            List <DependencyFile> FilesToDownload = new List <DependencyFile>();

            // Create a new working manifest for the working directory, moving over files that we already have. Add any missing dependencies into the download queue.
            WorkingManifest NewWorkingManifest = new WorkingManifest();

            foreach (DependencyFile TargetFile in FilteredTargetFiles)
            {
                WorkingFile NewFile;
                if (CurrentFileLookup.TryGetValue(TargetFile.Name, out NewFile) && NewFile.Hash == TargetFile.Hash)
                {
                    // Update the expected hash to match what we're looking for
                    NewFile.ExpectedHash = TargetFile.Hash;

                    // Move the existing file to the new working set
                    CurrentFileLookup.Remove(NewFile.Name);
                }
                else
                {
                    // Create a new working file
                    NewFile              = new WorkingFile();
                    NewFile.Name         = TargetFile.Name;
                    NewFile.ExpectedHash = TargetFile.Hash;

                    // Add it to the download list
                    FilesToDownload.Add(TargetFile);
                }
                NewWorkingManifest.Files.Add(NewFile);
            }

            // Print out everything that we'd change in a dry run
            if (bDryRun)
            {
                HashSet <string> NewFiles = new HashSet <string>(FilesToDownload.Select(x => x.Name));
                foreach (string RemoveFile in CurrentFileLookup.Keys.Where(x => !NewFiles.Contains(x)))
                {
                    Log.WriteLine("Remove {0}", RemoveFile);
                }
                foreach (string UpdateFile in CurrentFileLookup.Keys.Where(x => NewFiles.Contains(x)))
                {
                    Log.WriteLine("Update {0}", UpdateFile);
                }
                foreach (string AddFile in NewFiles.Where(x => !CurrentFileLookup.ContainsKey(x)))
                {
                    Log.WriteLine("Add {0}", AddFile);
                }
                return(true);
            }

            // Delete any files which are no longer needed
            List <WorkingFile> TamperedFiles = new List <WorkingFile>();

            foreach (WorkingFile FileToRemove in CurrentFileLookup.Values)
            {
                if (Overwrite != OverwriteMode.Force && FileToRemove.Hash != FileToRemove.ExpectedHash)
                {
                    TamperedFiles.Add(FileToRemove);
                }
                else if (!SafeDeleteFile(Path.Combine(RootPath, FileToRemove.Name)))
                {
                    return(false);
                }
            }

            // Warn if there were any files that have been tampered with, and allow the user to choose whether to overwrite them
            bool bOverwriteTamperedFiles = true;

            if (TamperedFiles.Count > 0 && Overwrite != OverwriteMode.Force)
            {
                // List the files that have changed
                Log.WriteError("The following file(s) have been modified:");
                foreach (WorkingFile TamperedFile in TamperedFiles)
                {
                    Log.WriteError("  {0}", TamperedFile.Name);
                }

                // Figure out whether to overwrite the files
                if (Overwrite == OverwriteMode.Unchanged)
                {
                    Log.WriteError("Re-run with the --force parameter to overwrite them.");
                    bOverwriteTamperedFiles = false;
                }
                else
                {
                    Log.WriteStatus("Would you like to overwrite your changes (y/n)? ");
                    ConsoleKeyInfo KeyInfo = Console.ReadKey(false);
                    bOverwriteTamperedFiles = (KeyInfo.KeyChar == 'y' || KeyInfo.KeyChar == 'Y');
                    Log.FlushStatus();
                }
            }

            // Overwrite any tampered files, or remove them from the download list
            if (bOverwriteTamperedFiles)
            {
                foreach (WorkingFile TamperedFile in TamperedFiles)
                {
                    if (!SafeDeleteFile(Path.Combine(RootPath, TamperedFile.Name)))
                    {
                        return(false);
                    }
                }
            }
            else
            {
                foreach (WorkingFile TamperedFile in TamperedFiles)
                {
                    DependencyFile TargetFile;
                    if (TargetFiles.TryGetValue(TamperedFile.Name, out TargetFile))
                    {
                        TargetFiles.Remove(TamperedFile.Name);
                        FilesToDownload.Remove(TargetFile);
                    }
                }
            }

            // Write out the new working manifest, so we can track any files that we're going to download. We always verify missing files on startup, so it's ok that things don't exist yet.
            if (!WriteWorkingManifest(WorkingManifestPath, TempWorkingManifestPath, NewWorkingManifest))
            {
                return(false);
            }

            // If there's nothing to do, just print a simpler message and exit early
            if (FilesToDownload.Count > 0)
            {
                // Download all the new dependencies
                if (!DownloadDependencies(RootPath, FilesToDownload, TargetBlobs.Values, TargetPacks.Values, NumThreads, MaxRetries, ProxyUrl))
                {
                    return(false);
                }

                // Update all the timestamps and hashes for the output files
                foreach (WorkingFile NewFile in NewWorkingManifest.Files)
                {
                    if (NewFile.Hash != NewFile.ExpectedHash)
                    {
                        string NewFileName = Path.Combine(RootPath, NewFile.Name);
                        NewFile.Hash      = NewFile.ExpectedHash;
                        NewFile.Timestamp = File.GetLastWriteTimeUtc(NewFileName).Ticks;
                    }
                }

                // Rewrite the manifest with the results
                if (!WriteWorkingManifest(WorkingManifestPath, TempWorkingManifestPath, NewWorkingManifest))
                {
                    return(false);
                }
            }

            // Update all the executable permissions
            if (!SetExecutablePermissions(RootPath, FilteredTargetFiles))
            {
                return(false);
            }

            return(true);
        }