/// <summary> /// Discover and fill in the project info /// </summary> public static void FillProjectInfo() { DateTime StartTime = DateTime.Now; List <DirectoryInfo> DirectoriesToSearch = GetNonForeignProjectBaseDirs().Select(x => new DirectoryInfo(x.FullName)).ToList(); Log.TraceVerbose("\tFound {0} directories to search", DirectoriesToSearch.Count); foreach (DirectoryInfo DirToSearch in DirectoriesToSearch) { if (DirToSearch.Exists) { foreach (DirectoryInfo SubDir in DirToSearch.EnumerateDirectories()) { foreach (FileInfo UProjFile in SubDir.EnumerateFiles("*.uproject", SearchOption.TopDirectoryOnly)) { AddProject(new FileReference(UProjFile)); } } } else { Log.TraceVerbose("ProjectInfo: Skipping directory {0} from .uprojectdirs file as it doesn't exist.", DirToSearch); } } DateTime StopTime = DateTime.Now; if (UnrealBuildTool.bPrintPerformanceInfo) { TimeSpan TotalProjectInfoTime = StopTime - StartTime; Log.TraceInformation("FillProjectInfo took {0} milliseconds", TotalProjectInfoTime.TotalMilliseconds); } }
// 재귀 단계 조정 //private static void ProcessDir( string SourceDir, int ReCursionLevel ) //{ // //if( ReCursionLevel <= HowDeepToScan ) // if( ReCursionLevel <= HowDeepToScan ) // { // // Process the list of files found in the directory. // fileEntries = Directory.GetFiles( SourceDir ); // foreach( string fileName in fileEntries ) // { // using( StreamWriter fs = new StreamWriter( @"G:\Baidu\allfile.txt", true ) ) // { // fs.WriteLine( fileName ); // } // // do something with fileName // //Console.WriteLine( fileName ); // } // // Recurse into subdirectories of this directory. // SubDirectoryEntries = Directory.GetDirectories( SourceDir ); // foreach( string SubDirectory in SubDirectoryEntries ) // // Do not iterate through reparse points // if( ( File.GetAttributes( SubDirectory ) & // FileAttributes.ReparsePoint ) != // FileAttributes.ReparsePoint ) // ProcessDir( SubDirectory, ReCursionLevel + 1 ); // } //} // 일반적으로 재귀 방식을 쓰지만 복잡하거나 중첩 규모가 크면 스택 오버 플로우 발생 가능성 private static void TraverseTree(string SourceDir) { Stack <string> dirs = new Stack <string>(20); if (!Directory.Exists(SourceDir)) { throw new DirectoryNotFoundException(); } // 스택에 소스 경로 넣기( Push ) dirs.Push(SourceDir); while (dirs.Count > 0) { string CurrentDir = dirs.Pop(); string[] SubDirs = null; try { SubDirs = Directory.GetDirectories(CurrentDir); foreach (var SubDir in SubDirs) { DeleteSubDirs.Push(SubDir.ToString()); //Console.WriteLine(SubDir.ToString() ); } } catch (UnauthorizedAccessException e) { Debug.WriteLine(e.Message); } string[] files = null; try { files = Directory.GetFiles(CurrentDir); } catch (UnauthorizedAccessException e) { Debug.WriteLine(e.Message); } foreach (string file in files) { try { FileInfo fi = new FileInfo(file); //Console.WriteLine( "{0}: {1}, {2}", fi.Name, fi.Length, fi.CreationTime ); } catch (FileNotFoundException e) { Debug.WriteLine(e.Message); } } foreach (string SubDir in SubDirs) { dirs.Push(SubDir); } } }
/// <inheritdoc/> public override int GetHashCode() { unchecked { // NOTE: Exclude Path from comparison to allow easy testing with randomized TemporaryFiles int hashCode = (SubDir != null ? SubDir.GetHashCode() : 0); hashCode = (hashCode * 397) ^ (Destination != null ? Destination.GetHashCode() : 0); hashCode = (hashCode * 397) ^ (MimeType != null ? MimeType.GetHashCode() : 0); hashCode = (hashCode * 397) ^ StartOffset.GetHashCode(); hashCode = (hashCode * 397) ^ (OriginalSource != null ? OriginalSource.GetHashCode() : 0); return(hashCode); } }
/// <summary> /// Finds files to stage under a given base directory. /// </summary> /// <param name="BaseDir">The directory to search under</param> /// <param name="Pattern">Pattern for files to match</param> /// <param name="ExcludePatterns">Patterns to exclude from staging</param> /// <param name="Option">Options for the search</param> /// <param name="Files">List to receive the enumerated files</param> private void FindFilesToStageInternal(DirectoryReference BaseDir, string Pattern, StageFilesSearch Option, List <FileReference> Files) { // Enumerate all the files in this directory Files.AddRange(DirectoryReference.EnumerateFiles(BaseDir, Pattern)); // Recurse through subdirectories if necessary if (Option == StageFilesSearch.AllDirectories) { foreach (DirectoryReference SubDir in DirectoryReference.EnumerateDirectories(BaseDir)) { string Name = SubDir.GetDirectoryName(); if (!RestrictedFolderNames.Contains(Name)) { FindFilesToStageInternal(SubDir, Pattern, Option, Files); } } } }
/// <summary> /// Removes any directory at the specified path which has a file matching the provided name /// older than the specified number of days. Used by code that writes a .token file to temp /// folders /// </summary> /// <param name="InPath"></param> /// <param name="FileName"></param> /// <param name="Days"></param> public static void CleanupMarkedDirectories(string InPath, int Days) { DirectoryInfo Di = new DirectoryInfo(InPath); if (Di.Exists == false) { return; } foreach (DirectoryInfo SubDir in Di.GetDirectories()) { bool HasFile = SubDir.GetFiles().Where(F => { int DaysOld = (DateTime.Now - F.LastWriteTime).Days; if (DaysOld >= Days) { // use the old and new tokennames return(string.Equals(F.Name, "gauntlet.tempdir", StringComparison.OrdinalIgnoreCase) || string.Equals(F.Name, "gauntlet.token", StringComparison.OrdinalIgnoreCase)); } return(false); }).Count() > 0; if (HasFile) { Log.Info("Removing old directory {0}", SubDir.Name); try { SubDir.Delete(true); } catch (Exception Ex) { Log.Warning("Failed to remove old directory {0}. {1}", SubDir.FullName, Ex.Message); } } else { CleanupMarkedDirectories(SubDir.FullName, Days); } } }
// 일반적으로 재귀 방식을 쓰지만 복잡하거나 중첩 규모가 크면 스택 오버 플로우 발생 가능성 private void TraverseTree(string[] SourceDirs) { Stack <string> dirs = new Stack <string>(30); // 스택에 소스 경로 넣기( Push ) foreach (var SourceDir in SourceDirs) { dirs.Push(SourceDir); } while (dirs.Count > 0) { string CurrentDir = dirs.Pop(); string[] SubDirs = null; string[] files = null; try { SubDirs = Directory.GetDirectories(CurrentDir); foreach (var SubDir in SubDirs) { // 폴더 일반 속성으로 변경 File.SetAttributes(SubDir, FileAttributes.Normal); // 스택에 폴더 넣기( 후입선출 - LIFO ) DeleteSubDirs.Push(SubDir.ToString()); } files = Directory.GetFiles(CurrentDir); ListView_AddFile(files); } catch (UnauthorizedAccessException e) { Debug.WriteLine(e.Message); } foreach (string SubDir in SubDirs) { dirs.Push(SubDir); } } }
internal void CopyDirectoryTree(string FromDir, string FromDirBaseString, string ToDirBaseString) { try { if (!Directory.Exists(ToDirBaseString)) { Directory.CreateDirectory(ToDirBaseString); ShowStatus(ToDirBaseString); } string [] SubDirEntries = Directory.GetDirectories(FromDir); foreach (string SubDir in SubDirEntries) { if (!MForm.CheckEvents()) { return; } string NewDir = SubDir.Replace( FromDirBaseString, ToDirBaseString); if (!Directory.Exists(NewDir)) { Directory.CreateDirectory(NewDir); } ShowStatus(NewDir); // Call itself recursively. CopyDirectoryTree(SubDir, FromDirBaseString, ToDirBaseString); } } catch (Exception Except) { ShowStatus("Exception in SearchOneDirectory():"); ShowStatus(Except.Message); } }
/// <summary> /// Searches a directory tree for build products to be cleaned. /// </summary> /// <param name="BaseDir">The directory to search</param> /// <param name="NamePrefixes">Target or application names that may appear at the start of the build product name (eg. "UE4Editor", "ShooterGameEditor")</param> /// <param name="NameSuffixes">Suffixes which may appear at the end of the build product name</param> /// <param name="FilesToClean">List to receive a list of files to be cleaned</param> /// <param name="DirectoriesToClean">List to receive a list of directories to be cleaned</param> public void FindBuildProductsToClean(DirectoryReference BaseDir, string[] NamePrefixes, string[] NameSuffixes, List <FileReference> FilesToClean, List <DirectoryReference> DirectoriesToClean) { foreach (FileReference File in DirectoryReference.EnumerateFiles(BaseDir)) { string FileName = File.GetFileName(); if (IsDefaultBuildProduct(FileName, NamePrefixes, NameSuffixes) || IsBuildProduct(FileName, NamePrefixes, NameSuffixes)) { FilesToClean.Add(File); } } foreach (DirectoryReference SubDir in DirectoryReference.EnumerateDirectories(BaseDir)) { string SubDirName = SubDir.GetDirectoryName(); if (IsBuildProduct(SubDirName, NamePrefixes, NameSuffixes)) { DirectoriesToClean.Add(SubDir); } else { FindBuildProductsToClean(SubDir, NamePrefixes, NameSuffixes, FilesToClean, DirectoriesToClean); } } }
/// <summary> /// Copies src to dest by comparing files sizes and time stamps and only copying files that are different in src. Basically a more flexible /// robocopy /// </summary> /// <param name="SourcePath"></param> /// <param name="DestPath"></param> /// <param name="Verbose"></param> public static void CopyDirectory(string SourceDirPath, string DestDirPath, CopyOptions Options, Func <string, string> Transform, int RetryCount = 5) { DateTime StartTime = DateTime.Now; DirectoryInfo SourceDir = new DirectoryInfo(SourceDirPath); DirectoryInfo DestDir = new DirectoryInfo(DestDirPath); if (DestDir.Exists == false) { DestDir = Directory.CreateDirectory(DestDir.FullName); } System.IO.FileInfo[] SourceFiles = SourceDir.GetFiles("*", SearchOption.AllDirectories); System.IO.FileInfo[] DestFiles = DestDir.GetFiles("*", SearchOption.AllDirectories); // Convert dest into a map of relative paths to absolute Dictionary <string, System.IO.FileInfo> DestStructure = new Dictionary <string, System.IO.FileInfo>(); foreach (FileInfo Info in DestFiles) { string RelativePath = Info.FullName.Replace(DestDir.FullName, ""); // remove leading seperator if (RelativePath.First() == Path.DirectorySeparatorChar) { RelativePath = RelativePath.Substring(1); } DestStructure[RelativePath] = Info; } // List of relative-path files to copy to dest List <string> CopyList = new List <string>(); // List of relative path files in dest to delete List <string> DeletionList = new List <string>(); foreach (FileInfo SourceInfo in SourceFiles) { string SourceFilePath = SourceInfo.FullName.Replace(SourceDir.FullName, ""); // remove leading seperator if (SourceFilePath.First() == Path.DirectorySeparatorChar) { SourceFilePath = SourceFilePath.Substring(1); } string DestFilePath = Transform(SourceFilePath); if (DestStructure.ContainsKey(DestFilePath) == false) { // No copy in dest, add it to the list CopyList.Add(SourceFilePath); } else { // Check the file is the same version FileInfo DestInfo = DestStructure[DestFilePath]; // Difference in ticks. Even though we set the dest to the src there still appears to be minute // differences in ticks. 1ms is 10k ticks... Int64 TimeDelta = Math.Abs(DestInfo.LastWriteTime.Ticks - SourceInfo.LastWriteTime.Ticks); Int64 Threshhold = 100000; if (DestInfo.Length != SourceInfo.Length || TimeDelta > Threshhold) { CopyList.Add(SourceFilePath); } // Remove it from the map DestStructure.Remove(DestFilePath); } } // If set to mirror, delete all the files that were not in source if ((Options & CopyOptions.Mirror) == CopyOptions.Mirror) { // Now go through the remaining map items and delete them foreach (var Pair in DestStructure) { DeletionList.Add(Pair.Key); } foreach (string RelativePath in DeletionList) { FileInfo DestInfo = new FileInfo(Path.Combine(DestDir.FullName, RelativePath)); Log.Verbose("Deleting extra file {0}", DestInfo.FullName); try { // avoid an UnauthorizedAccessException by making sure file isn't read only DestInfo.IsReadOnly = false; DestInfo.Delete(); } catch (Exception Ex) { Log.Warning("Failed to delete file {0}. {1}", DestInfo.FullName, Ex); } } // delete empty directories DirectoryInfo DestDirInfo = new DirectoryInfo(DestDirPath); DirectoryInfo[] AllSubDirs = DestDirInfo.GetDirectories("*", SearchOption.AllDirectories); foreach (DirectoryInfo SubDir in AllSubDirs) { try { if (SubDir.GetFiles().Length == 0 && SubDir.GetDirectories().Length == 0) { Log.Verbose("Deleting empty dir {0}", SubDir.FullName); SubDir.Delete(true); } } catch (Exception Ex) { // handle the case where a file is locked Log.Info("Failed to delete directory {0}. {1}", SubDir.FullName, Ex); } } } CancellationTokenSource CTS = new CancellationTokenSource(); // todo - make param.. var POptions = new ParallelOptions { MaxDegreeOfParallelism = 1, CancellationToken = CTS.Token }; // install a cancel handler so we can stop parallel-for gracefully Action CancelHandler = delegate() { CTS.Cancel(); }; Globals.AbortHandlers.Add(CancelHandler); // now do the work Parallel.ForEach(CopyList, POptions, RelativePath => { // ensure path exists string DestPath = Path.Combine(DestDir.FullName, RelativePath); if (Transform != null) { DestPath = Transform(DestPath); } FileInfo DestInfo = new FileInfo(DestPath); FileInfo SrcInfo = new FileInfo(Path.Combine(SourceDir.FullName, RelativePath)); // ensure directory exists DestInfo.Directory.Create(); string DestFile = DestInfo.FullName; if (Transform != null) { DestFile = Transform(DestFile); } int Tries = 0; bool Copied = false; do { try { Log.Verbose("Copying to {0}", DestFile); SrcInfo.CopyTo(DestFile, true); // Clear and read-only attrib and set last write time FileInfo DestFileInfo = new FileInfo(DestFile); DestFileInfo.IsReadOnly = false; DestFileInfo.LastWriteTime = SrcInfo.LastWriteTime; Copied = true; } catch (Exception ex) { if (Tries++ < RetryCount) { Log.Info("Copy to {0} failed, retrying {1} of {2} in 30 secs..", DestFile, Tries, RetryCount); // todo - make param.. Thread.Sleep(30000); } else { Log.Error("File Copy failed with {0}.", ex.Message); throw new Exception(string.Format("File Copy failed with {0}.", ex.Message)); } } } while (Copied == false); }); TimeSpan Duration = DateTime.Now - StartTime; if (Duration.TotalSeconds > 10) { Log.Verbose("Copied Directory in {0}", Duration.ToString(@"mm\m\:ss\s")); } // remove cancel handler Globals.AbortHandlers.Remove(CancelHandler); }
// 일반적으로 재귀 방식을 쓰지만 복잡하거나 중첩 규모가 크면 스택 오버 플로우 발생 가능성 private void TraverseTree(string[] SourceDirs) { Stack <string> dirs = new Stack <string>(30); // 스택에 소스 경로 넣기( Push ) foreach (var SourceDir in SourceDirs) { dirs.Push(SourceDir); } while (dirs.Count > 0) { string CurrentDir = dirs.Pop(); string[] SubDirs = null; string[] files = null; try { SubDirs = Directory.GetDirectories(CurrentDir); foreach (var SubDir in SubDirs) { // 폴더 일반 속성으로 변경 File.SetAttributes(SubDir, FileAttributes.Normal); // 스택에 폴더 넣기( 선입후출 - FILO ) DeleteSubDirs.Push(SubDir.ToString()); } files = Directory.GetFiles(CurrentDir); ListView_AddFile(files); } catch (UnauthorizedAccessException e) { Debug.WriteLine(e.Message); } //try //{ // files = Directory.GetFiles( CurrentDir ); // ListView_AddFile( files ); //} //catch( UnauthorizedAccessException e ) //{ // Debug.WriteLine( e.Message ); //} //foreach( string file in files ) //{ // try // { //FileInfo fi = new FileInfo( file ); //Console.WriteLine( "{0}: {1}, {2}", fi.Name, fi.Length, fi.CreationTime ); // } // catch( FileNotFoundException e ) // { // Debug.WriteLine( e.Message ); // } //} foreach (string SubDir in SubDirs) { dirs.Push(SubDir); } } }
/// <summary> /// Discover and fill in the project info /// </summary> public static void FillProjectInfo() { DateTime StartTime = DateTime.Now; List <DirectoryInfo> DirectoriesToSearch = new List <DirectoryInfo>(); // Find all the .uprojectdirs files contained in the root folder and add their entries to the search array string EngineSourceDirectory = Path.GetFullPath(Path.Combine(RootDirectory, "Engine", "Source")); foreach (FileReference ProjectDirsFile in DirectoryReference.EnumerateFiles(UnrealBuildTool.RootDirectory, "*.uprojectdirs", SearchOption.TopDirectoryOnly)) { Log.TraceVerbose("\tFound uprojectdirs file {0}", ProjectDirsFile.FullName); foreach (string Line in File.ReadAllLines(ProjectDirsFile.FullName)) { string TrimLine = Line.Trim(); if (!TrimLine.StartsWith(";")) { DirectoryReference BaseProjectDir = DirectoryReference.Combine(UnrealBuildTool.RootDirectory, TrimLine); if (BaseProjectDir.IsUnderDirectory(UnrealBuildTool.RootDirectory)) { DirectoriesToSearch.Add(new DirectoryInfo(BaseProjectDir.FullName)); } else { Log.TraceWarning("Project search path '{0}' is not under root directory, ignoring.", TrimLine); } } } } Log.TraceVerbose("\tFound {0} directories to search", DirectoriesToSearch.Count); foreach (DirectoryInfo DirToSearch in DirectoriesToSearch) { Log.TraceVerbose("\t\tSearching {0}", DirToSearch.FullName); if (DirToSearch.Exists) { foreach (DirectoryInfo SubDir in DirToSearch.EnumerateDirectories()) { Log.TraceVerbose("\t\t\tFound subdir {0} ({1})", SubDir.FullName, SubDir.Name); foreach (FileInfo UProjFile in SubDir.EnumerateFiles("*.uproject", SearchOption.TopDirectoryOnly)) { Log.TraceVerbose("\t\t\t\t{0}", UProjFile.FullName); AddProject(new FileReference(UProjFile)); } } } else { Log.TraceVerbose("ProjectInfo: Skipping directory {0} from .uprojectdirs file as it doesn't exist.", DirToSearch); } } DateTime StopTime = DateTime.Now; if (UnrealBuildTool.bPrintPerformanceInfo) { TimeSpan TotalProjectInfoTime = StopTime - StartTime; Log.TraceInformation("FillProjectInfo took {0} milliseconds", TotalProjectInfoTime.TotalMilliseconds); } }
/// <summary> /// Copies src to dest by comparing files sizes and time stamps and only copying files that are different in src. Basically a more flexible /// robocopy /// </summary> /// <param name="SourcePath"></param> /// <param name="DestPath"></param> /// <param name="Verbose"></param> public static void CopyDirectory(string SourceDirPath, string DestDirPath, CopyDirectoryOptions Options) { DateTime StartTime = DateTime.Now; DirectoryInfo SourceDir = new DirectoryInfo(SourceDirPath); DirectoryInfo DestDir = new DirectoryInfo(DestDirPath); if (DestDir.Exists == false) { DestDir = Directory.CreateDirectory(DestDir.FullName); } bool IsMirroring = (Options.Mode & CopyOptions.Mirror) == CopyOptions.Mirror; if (IsMirroring && !Options.IsDirectoryPattern) { Log.Warning("Can only use mirror with pattern that includes whole directories (e.g. '*')"); IsMirroring = false; } IEnumerable <FileInfo> SourceFiles = null; FileInfo[] DestFiles = null; // find all files. If a directory get them all, else use the pattern/regex if (Options.IsDirectoryPattern) { SourceFiles = SourceDir.GetFiles("*", Options.Recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly); } else { if (Options.Regex == null) { SourceFiles = SourceDir.GetFiles(Options.Pattern, Options.Recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly); } else { SourceFiles = SourceDir.GetFiles("*", Options.Recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly); SourceFiles = SourceFiles.Where(F => Options.Regex.IsMatch(F.Name)); } } // Convert dest into a map of relative paths to absolute Dictionary <string, System.IO.FileInfo> DestStructure = new Dictionary <string, System.IO.FileInfo>(); if (IsMirroring) { DestFiles = DestDir.GetFiles("*", SearchOption.AllDirectories); foreach (FileInfo Info in DestFiles) { string RelativePath = Info.FullName.Replace(DestDir.FullName, ""); // remove leading seperator if (RelativePath.First() == Path.DirectorySeparatorChar) { RelativePath = RelativePath.Substring(1); } DestStructure[RelativePath] = Info; } } // List of relative-path files to copy to dest List <string> CopyList = new List <string>(); // List of relative path files in dest to delete List <string> DeletionList = new List <string>(); foreach (FileInfo SourceInfo in SourceFiles) { string SourceFilePath = SourceInfo.FullName.Replace(SourceDir.FullName, ""); // remove leading seperator if (SourceFilePath.First() == Path.DirectorySeparatorChar) { SourceFilePath = SourceFilePath.Substring(1); } string DestFilePath = Options.Transform(SourceFilePath); FileInfo DestInfo = null; // We may have destination info if mirroring where we prebuild it all, if not // grab it now if (DestStructure.ContainsKey(DestFilePath)) { DestInfo = DestStructure[DestFilePath]; } else { string FullDestPath = Path.Combine(DestDir.FullName, DestFilePath); if (File.Exists(FullDestPath)) { DestInfo = new FileInfo(FullDestPath); } } if (DestInfo == null) { // No copy in dest, add it to the list CopyList.Add(SourceFilePath); } else { // Check the file is the same version // Difference in ticks. Even though we set the dest to the src there still appears to be minute // differences in ticks. 1ms is 10k ticks... Int64 TimeDelta = Math.Abs(DestInfo.LastWriteTime.Ticks - SourceInfo.LastWriteTime.Ticks); Int64 Threshhold = 100000; if (DestInfo.Length != SourceInfo.Length || TimeDelta > Threshhold) { CopyList.Add(SourceFilePath); } else { if (Options.Verbose) { Log.Info("Will skip copy to {0}. File up to date.", DestInfo.FullName); } else { Log.Verbose("Will skip copy to {0}. File up to date.", DestInfo.FullName); } } // Remove it from the map DestStructure.Remove(DestFilePath); } } // If set to mirror, delete all the files that were not in source if (IsMirroring) { // Now go through the remaining map items and delete them foreach (var Pair in DestStructure) { DeletionList.Add(Pair.Key); } foreach (string RelativePath in DeletionList) { FileInfo DestInfo = new FileInfo(Path.Combine(DestDir.FullName, RelativePath)); if (Options.Verbose) { Log.Info("Deleting extra file {0}", DestInfo.FullName); } else { Log.Verbose("Deleting extra file {0}", DestInfo.FullName); } try { // avoid an UnauthorizedAccessException by making sure file isn't read only DestInfo.IsReadOnly = false; DestInfo.Delete(); } catch (Exception Ex) { Log.Warning("Failed to delete file {0}. {1}", DestInfo.FullName, Ex); } } // delete empty directories DirectoryInfo DestDirInfo = new DirectoryInfo(DestDirPath); DirectoryInfo[] AllSubDirs = DestDirInfo.GetDirectories("*", SearchOption.AllDirectories); foreach (DirectoryInfo SubDir in AllSubDirs) { try { if (SubDir.GetFiles().Length == 0 && SubDir.GetDirectories().Length == 0) { if (Options.Verbose) { Log.Info("Deleting empty dir {0}", SubDir.FullName); } else { Log.Verbose("Deleting empty dir {0}", SubDir.FullName); } SubDir.Delete(true); } } catch (Exception Ex) { // handle the case where a file is locked Log.Info("Failed to delete directory {0}. {1}", SubDir.FullName, Ex); } } } CancellationTokenSource CTS = new CancellationTokenSource(); // todo - make param.. var POptions = new ParallelOptions { MaxDegreeOfParallelism = 1, CancellationToken = CTS.Token }; // install a cancel handler so we can stop parallel-for gracefully Action CancelHandler = delegate() { CTS.Cancel(); }; Globals.AbortHandlers.Add(CancelHandler); // now do the work Parallel.ForEach(CopyList, POptions, RelativePath => { // ensure path exists string DestPath = Path.Combine(DestDir.FullName, RelativePath); if (Options.Transform != null) { DestPath = Options.Transform(DestPath); } string SourcePath = Path.Combine(SourceDir.FullName, RelativePath); FileInfo DestInfo; FileInfo SrcInfo; // wrap FileInfo creation with exception handler as can throw and want informative error try { DestInfo = new FileInfo(DestPath); SrcInfo = new FileInfo(SourcePath); } catch (Exception Ex) { throw new Exception(string.Format("FileInfo creation failed for Source:{0}, Dest:{1}, with: {2}", SourcePath, DestPath, Ex.Message)); } // ensure directory exists DestInfo.Directory.Create(); string DestFile = DestInfo.FullName; if (Options.Transform != null) { DestFile = Options.Transform(DestFile); } int Tries = 0; bool Copied = false; do { try { if (Options.Verbose) { Log.Info("Copying to {0}", DestFile); } else { Log.Verbose("Copying to {0}", DestFile); } SrcInfo.CopyTo(DestFile, true); // Clear and read-only attrib and set last write time FileInfo DestFileInfo = new FileInfo(DestFile); DestFileInfo.IsReadOnly = false; DestFileInfo.LastWriteTime = SrcInfo.LastWriteTime; Copied = true; } catch (Exception ex) { if (Tries++ < Options.Retries) { Log.Info("Copy to {0} failed, retrying {1} of {2} in 30 secs..", DestFile, Tries, Options.Retries); // todo - make param.. Thread.Sleep(30000); } else { using (var PauseEC = new ScopedSuspendECErrorParsing()) { Log.Error("File Copy failed with {0}.", ex.Message); } throw new Exception(string.Format("File Copy failed with {0}.", ex.Message)); } } } while (Copied == false); }); TimeSpan Duration = DateTime.Now - StartTime; if (Duration.TotalSeconds > 10) { if (Options.Verbose) { Log.Info("Copied Directory in {0}", Duration.ToString(@"mm\m\:ss\s")); } else { Log.Verbose("Copied Directory in {0}", Duration.ToString(@"mm\m\:ss\s")); } } // remove cancel handler Globals.AbortHandlers.Remove(CancelHandler); }