protected void AddPrerequisiteSourceFile(UEBuildTarget Target, IUEBuildPlatform BuildPlatform, CPPEnvironment CompileEnvironment, FileItem SourceFile, List <FileItem> PrerequisiteItems) { PrerequisiteItems.Add(SourceFile); var RemoteThis = this as RemoteToolChain; bool bAllowUploading = RemoteThis != null && BuildHostPlatform.Current.Platform != UnrealTargetPlatform.Mac; // Don't use remote features when compiling from a Mac if (bAllowUploading) { RemoteThis.QueueFileForBatchUpload(SourceFile); } if (!BuildConfiguration.bUseExperimentalFastBuildIteration) // In fast build iteration mode, we'll gather includes later on { // @todo fastubt: What if one of the prerequisite files has become missing since it was updated in our cache? (usually, because a coder eliminated the source file) // -> Two CASES: // 1) NOT WORKING: Non-unity file went away (SourceFile in this context). That seems like an existing old use case. Compile params or Response file should have changed? // 2) WORKING: Indirect file went away (unity'd original source file or include). This would return a file that no longer exists and adds to the prerequiteitems list var IncludedFileList = CPPEnvironment.FindAndCacheAllIncludedFiles(Target, SourceFile, BuildPlatform, CompileEnvironment.Config.CPPIncludeInfo, bOnlyCachedDependencies: BuildConfiguration.bUseExperimentalFastDependencyScan); foreach (FileItem IncludedFile in IncludedFileList) { PrerequisiteItems.Add(IncludedFile); if (bAllowUploading && !BuildConfiguration.bUseExperimentalFastDependencyScan) // With fast dependency scanning, we will not have an exhaustive list of dependencies here. We rely on PostCodeGeneration() to upload these files. { RemoteThis.QueueFileForBatchUpload(IncludedFile); } } } }
private static FileItem CachePCHUsageForCPPFile(UEBuildTarget Target, FileItem CPPFile, IUEBuildPlatform BuildPlatform, List<string> IncludePathsToSearch, Dictionary<string, FileItem> IncludeFileSearchDictionary) { // @todo ubtmake: We don't really need to scan every file looking for PCH headers, just need one. The rest is just for error checking. // @todo ubtmake: We don't need all of the direct includes either. We just need the first, unless we want to check for errors. List<DependencyInclude> DirectIncludeFilenames = CPPEnvironment.GetDirectIncludeDependencies(Target, CPPFile, BuildPlatform, bOnlyCachedDependencies:false); if (BuildConfiguration.bPrintDebugInfo) { Log.TraceVerbose("Found direct includes for {0}: {1}", Path.GetFileName(CPPFile.AbsolutePath), string.Join(", ", DirectIncludeFilenames.Select(F => F.IncludeName))); } if (DirectIncludeFilenames.Count == 0) { return null; } var FirstInclude = DirectIncludeFilenames[0]; // The pch header should always be the first include in the source file. // NOTE: This is not an absolute path. This is just the literal include string from the source file! CPPFile.PCHHeaderNameInCode = FirstInclude.IncludeName; // Resolve the PCH header to an absolute path. // Check NullOrEmpty here because if the file could not be resolved we need to throw an exception if (!string.IsNullOrEmpty(FirstInclude.IncludeResolvedName) && // ignore any preexisting resolve cache if we are not configured to use it. BuildConfiguration.bUseIncludeDependencyResolveCache && // if we are testing the resolve cache, we force UBT to resolve every time to look for conflicts !BuildConfiguration.bTestIncludeDependencyResolveCache) { CPPFile.PrecompiledHeaderIncludeFilename = FirstInclude.IncludeResolvedName; return FileItem.GetItemByFullPath(CPPFile.PrecompiledHeaderIncludeFilename); } // search the include paths to resolve the file. FileItem PrecompiledHeaderIncludeFile = CPPEnvironment.FindIncludedFile(CPPFile.PCHHeaderNameInCode, !BuildConfiguration.bCheckExternalHeadersForModification, IncludePathsToSearch, IncludeFileSearchDictionary ); if (PrecompiledHeaderIncludeFile == null) { throw new BuildException("The first include statement in source file '{0}' is trying to include the file '{1}' as the precompiled header, but that file could not be located in any of the module's include search paths.", CPPFile.AbsolutePath, CPPFile.PCHHeaderNameInCode); } CPPEnvironment.IncludeDependencyCache[Target].CacheResolvedIncludeFullPath(CPPFile, 0, PrecompiledHeaderIncludeFile.AbsolutePath); CPPFile.PrecompiledHeaderIncludeFilename = PrecompiledHeaderIncludeFile.AbsolutePath; return PrecompiledHeaderIncludeFile; }
private static bool RequiresTempTarget(string RawProjectPath, List <UnrealTargetPlatform> ClientTargetPlatforms) { // check to see if we already have a Target.cs file // @todo: Target.cs may not be the same name as the project in the future, so look at a better way to determine this if (File.Exists(Path.Combine(Path.GetDirectoryName(RawProjectPath), "Source", Path.GetFileNameWithoutExtension(RawProjectPath) + ".Target.cs"))) { return(false); } // no Target file, now check to see if build settings have changed List <UnrealTargetPlatform> TargetPlatforms = ClientTargetPlatforms; if (ClientTargetPlatforms == null || ClientTargetPlatforms.Count < 1) { // No client target platforms, add all in TargetPlatforms = new List <UnrealTargetPlatform>(); foreach (UnrealTargetPlatform TargetPlatformType in Enum.GetValues(typeof(UnrealTargetPlatform))) { if (TargetPlatformType != UnrealTargetPlatform.Unknown) { TargetPlatforms.Add(TargetPlatformType); } } } // check the target platforms for any differences in build settings or additional plugins foreach (UnrealTargetPlatform TargetPlatformType in TargetPlatforms) { IUEBuildPlatform BuildPlat = UEBuildPlatform.GetBuildPlatform(TargetPlatformType, true); if (BuildPlat != null && !(BuildPlat as UEBuildPlatform).HasDefaultBuildConfig(TargetPlatformType, Path.GetDirectoryName(RawProjectPath))) { return(true); } // find if there are any plugins List <string> PluginList = new List <string>(); // Use the project settings to update the plugin list for this target PluginList = UProjectInfo.GetEnabledPlugins(RawProjectPath, PluginList, TargetPlatformType); if (PluginList.Count > 0) { foreach (var PluginName in PluginList) { // check the plugin info for this plugin itself foreach (var Plugin in Plugins.AllPlugins) { if (Plugin.Name == PluginName) { foreach (var Module in Plugin.Modules) { if (Module.Platforms.Count > 0 && Module.Platforms.Contains(TargetPlatformType)) { return(true); } } break; } } } } } return(false); }
protected void AddPrerequisiteSourceFile( UEBuildTarget Target, IUEBuildPlatform BuildPlatform, CPPEnvironment CompileEnvironment, FileItem SourceFile, List<FileItem> PrerequisiteItems ) { PrerequisiteItems.Add( SourceFile ); var RemoteThis = this as RemoteToolChain; bool bAllowUploading = RemoteThis != null && BuildHostPlatform.Current.Platform != UnrealTargetPlatform.Mac; // Don't use remote features when compiling from a Mac if( bAllowUploading ) { RemoteThis.QueueFileForBatchUpload(SourceFile); } if( !BuildConfiguration.bUseUBTMakefiles ) // In fast build iteration mode, we'll gather includes later on { // @todo ubtmake: What if one of the prerequisite files has become missing since it was updated in our cache? (usually, because a coder eliminated the source file) // -> Two CASES: // 1) NOT WORKING: Non-unity file went away (SourceFile in this context). That seems like an existing old use case. Compile params or Response file should have changed? // 2) WORKING: Indirect file went away (unity'd original source file or include). This would return a file that no longer exists and adds to the prerequiteitems list var IncludedFileList = CPPEnvironment.FindAndCacheAllIncludedFiles( Target, SourceFile, BuildPlatform, CompileEnvironment.Config.CPPIncludeInfo, bOnlyCachedDependencies:BuildConfiguration.bUseUBTMakefiles ); if( IncludedFileList != null ) { foreach (FileItem IncludedFile in IncludedFileList) { PrerequisiteItems.Add( IncludedFile ); if( bAllowUploading && !BuildConfiguration.bUseUBTMakefiles ) // With fast dependency scanning, we will not have an exhaustive list of dependencies here. We rely on PostCodeGeneration() to upload these files. { RemoteThis.QueueFileForBatchUpload(IncludedFile); } } } } }
/** Finds the names of files directly included by the given C++ file. */ public static List <DependencyInclude> GetDirectIncludeDependencies(UEBuildTarget Target, FileItem CPPFile, IUEBuildPlatform BuildPlatform, bool bOnlyCachedDependencies, out bool HasUObjects) { // Try to fulfill request from cache first. List <DependencyInclude> Result; if (IncludeDependencyCache[Target].GetCachedDirectDependencies(CPPFile, out Result, out HasUObjects)) { return(Result); } else if (bOnlyCachedDependencies) { return(new List <DependencyInclude>()); } var TimerStartTime = DateTime.UtcNow; ++CPPEnvironment.TotalDirectIncludeCacheMisses; // Get the adjusted filename string FileToRead = CPPFile.AbsolutePath; if (BuildPlatform.RequiresExtraUnityCPPWriter() == true && Path.GetFileName(FileToRead).StartsWith("Module.")) { FileToRead += ".ex"; } // Read lines from the C++ file. string FileContents = Utils.ReadAllText(FileToRead); if (string.IsNullOrEmpty(FileContents)) { return(new List <DependencyInclude>()); } HasUObjects = CPPEnvironment.UObjectRegex.IsMatch(FileContents); // Note: This depends on UBT executing w/ a working directory of the Engine/Source folder! string EngineSourceFolder = Directory.GetCurrentDirectory(); string InstalledFolder = EngineSourceFolder; Int32 EngineSourceIdx = EngineSourceFolder.IndexOf("\\Engine\\Source"); if (EngineSourceIdx != -1) { InstalledFolder = EngineSourceFolder.Substring(0, EngineSourceIdx); } Result = new List <DependencyInclude>(); if (Utils.IsRunningOnMono) { // Mono crashes when running a regex on a string longer than about 5000 characters, so we parse the file in chunks int StartIndex = 0; const int SafeTextLength = 4000; while (StartIndex < FileContents.Length) { int EndIndex = StartIndex + SafeTextLength < FileContents.Length ? FileContents.IndexOf("\n", StartIndex + SafeTextLength) : FileContents.Length; if (EndIndex == -1) { EndIndex = FileContents.Length; } CollectHeaders(CPPFile, Result, FileToRead, FileContents, InstalledFolder, StartIndex, EndIndex); StartIndex = EndIndex + 1; } } else { CollectHeaders(CPPFile, Result, FileToRead, FileContents, InstalledFolder, 0, FileContents.Length); } // Populate cache with results. IncludeDependencyCache[Target].SetDependencyInfo(CPPFile, Result, HasUObjects); CPPEnvironment.DirectIncludeCacheMissesTotalTime += (DateTime.UtcNow - TimerStartTime).TotalSeconds; return(Result); }
/// <summary> /// Finds the files directly or indirectly included by the given C++ file. /// </summary> /// <param name="CPPFile">C++ file to get the dependencies for.</param> /// <param name="Result">List of CPPFile dependencies.</param> /// <returns>false if CPPFile is still being processed further down the callstack, true otherwise.</returns> public static bool FindAndCacheAllIncludedFiles(UEBuildTarget Target, FileItem CPPFile, IUEBuildPlatform BuildPlatform, CPPIncludeInfo CPPIncludeInfo, ref IncludedFilesSet Result, bool bOnlyCachedDependencies) { IncludedFilesSet IncludedFileList; var IncludedFilesMap = bOnlyCachedDependencies ? OnlyCachedIncludedFilesMap : ExhaustiveIncludedFilesMap; if (!IncludedFilesMap.TryGetValue(CPPFile, out IncludedFileList)) { var TimerStartTime = DateTime.UtcNow; IncludedFileList = new IncludedFilesSet(); // Add an uninitialized entry for the include file to avoid infinitely recursing on include file loops. IncludedFilesMap.Add(CPPFile, IncludedFileList); // Gather a list of names of files directly included by this C++ file. bool HasUObjects; List <DependencyInclude> DirectIncludes = GetDirectIncludeDependencies(Target, CPPFile, BuildPlatform, bOnlyCachedDependencies: bOnlyCachedDependencies, HasUObjects: out HasUObjects); // Build a list of the unique set of files that are included by this file. var DirectlyIncludedFiles = new HashSet <FileItem>(); // require a for loop here because we need to keep track of the index in the list. for (int DirectlyIncludedFileNameIndex = 0; DirectlyIncludedFileNameIndex < DirectIncludes.Count; ++DirectlyIncludedFileNameIndex) { // Resolve the included file name to an actual file. DependencyInclude DirectInclude = DirectIncludes[DirectlyIncludedFileNameIndex]; if (DirectInclude.IncludeResolvedName == null || // ignore any preexisting resolve cache if we are not configured to use it. !BuildConfiguration.bUseIncludeDependencyResolveCache || // if we are testing the resolve cache, we force UBT to resolve every time to look for conflicts BuildConfiguration.bTestIncludeDependencyResolveCache ) { ++TotalDirectIncludeResolveCacheMisses; // search the include paths to resolve the file FileItem DirectIncludeResolvedFile = CPPEnvironment.FindIncludedFile(DirectInclude.IncludeName, !BuildConfiguration.bCheckExternalHeadersForModification, CPPIncludeInfo.GetIncludesPathsToSearch(CPPFile), CPPIncludeInfo.IncludeFileSearchDictionary); if (DirectIncludeResolvedFile != null) { DirectlyIncludedFiles.Add(DirectIncludeResolvedFile); } IncludeDependencyCache[Target].CacheResolvedIncludeFullPath(CPPFile, DirectlyIncludedFileNameIndex, DirectIncludeResolvedFile != null ? DirectIncludeResolvedFile.AbsolutePath : ""); } else { // we might have cached an attempt to resolve the file, but couldn't actually find the file (system headers, etc). if (DirectInclude.IncludeResolvedName != string.Empty) { DirectlyIncludedFiles.Add(FileItem.GetItemByFullPath(DirectInclude.IncludeResolvedName)); } } } TotalDirectIncludeResolves += DirectIncludes.Count; // Convert the dictionary of files included by this file into a list. foreach (var DirectlyIncludedFile in DirectlyIncludedFiles) { // Add the file we're directly including IncludedFileList.Add(DirectlyIncludedFile); // Also add all of the indirectly included files! if (FindAndCacheAllIncludedFiles(Target, DirectlyIncludedFile, BuildPlatform, CPPIncludeInfo, ref IncludedFileList, bOnlyCachedDependencies:bOnlyCachedDependencies) == false) { // DirectlyIncludedFile is a circular dependency which is still being processed // further down the callstack. Add this file to its circular dependencies list // so that it can update its dependencies later. IncludedFilesSet DirectlyIncludedFileIncludedFileList; if (IncludedFilesMap.TryGetValue(DirectlyIncludedFile, out DirectlyIncludedFileIncludedFileList)) { DirectlyIncludedFileIncludedFileList.CircularDependencies.Add(CPPFile); } } } // All dependencies have been processed by now so update all circular dependencies // with the full list. foreach (var CircularDependency in IncludedFileList.CircularDependencies) { IncludedFilesSet CircularDependencyIncludedFiles = IncludedFilesMap[CircularDependency]; foreach (FileItem IncludedFile in IncludedFileList) { CircularDependencyIncludedFiles.Add(IncludedFile); } } // No need to keep this around anymore. IncludedFileList.CircularDependencies.Clear(); // Done collecting files. IncludedFileList.bIsInitialized = true; var TimerDuration = DateTime.UtcNow - TimerStartTime; TotalTimeSpentGettingIncludes += TimerDuration.TotalSeconds; } if (IncludedFileList.bIsInitialized) { // Copy the list of files included by this file into the result list. foreach (FileItem IncludedFile in IncludedFileList) { // If the result list doesn't contain this file yet, add the file and the files it includes. // NOTE: For some reason in .NET 4, Add() is over twice as fast as calling UnionWith() on the set Result.Add(IncludedFile); } return(true); } else { // The IncludedFileList.bIsInitialized was false because we added a dummy entry further down the call stack. We're already processing // the include list for this header elsewhere in the stack frame, so we don't need to add anything here. return(false); } }
public static List <FileItem> FindAndCacheAllIncludedFiles(UEBuildTarget Target, FileItem SourceFile, IUEBuildPlatform BuildPlatform, CPPIncludeInfo CPPIncludeInfo, bool bOnlyCachedDependencies) { List <FileItem> Result = null; if (CPPIncludeInfo.IncludeFileSearchDictionary == null) { CPPIncludeInfo.IncludeFileSearchDictionary = new Dictionary <string, FileItem>(); } bool bUseFlatCPPIncludeDependencyCache = BuildConfiguration.bUseUBTMakefiles && UnrealBuildTool.IsAssemblingBuild; if (bOnlyCachedDependencies && bUseFlatCPPIncludeDependencyCache) { Result = FlatCPPIncludeDependencyCache[Target].GetDependenciesForFile(SourceFile.AbsolutePath); if (Result == null) { // Nothing cached for this file! It is new to us. This is the expected flow when our CPPIncludeDepencencyCache is missing. } } else { // @todo ubtmake: HeaderParser.h is missing from the include set for Module.UnrealHeaderTool.cpp (failed to find include using: FileItem DirectIncludeResolvedFile = CPPEnvironment.FindIncludedFile(DirectInclude.IncludeName, !BuildConfiguration.bCheckExternalHeadersForModification, IncludePathsToSearch, IncludeFileSearchDictionary );) // If we're doing an exhaustive include scan, make sure that we have our include dependency cache loaded and ready if (!bOnlyCachedDependencies) { if (!IncludeDependencyCache.ContainsKey(Target)) { IncludeDependencyCache.Add(Target, DependencyCache.Create(DependencyCache.GetDependencyCachePathForTarget(Target))); } } Result = new List <FileItem>(); var IncludedFileList = new IncludedFilesSet(); CPPEnvironment.FindAndCacheAllIncludedFiles(Target, SourceFile, BuildPlatform, CPPIncludeInfo, ref IncludedFileList, bOnlyCachedDependencies: bOnlyCachedDependencies); foreach (FileItem IncludedFile in IncludedFileList) { Result.Add(IncludedFile); } // Update cache if (bUseFlatCPPIncludeDependencyCache && !bOnlyCachedDependencies) { var Dependencies = new List <string>(); foreach (var IncludedFile in Result) { Dependencies.Add(IncludedFile.AbsolutePath); } string PCHName = SourceFile.PrecompiledHeaderIncludeFilename; FlatCPPIncludeDependencyCache[Target].SetDependenciesForFile(SourceFile.AbsolutePath, PCHName, Dependencies); } } return(Result); }
public static List <FileItem> FindAndCacheAllIncludedFiles(UEBuildTarget Target, FileItem SourceFile, IUEBuildPlatform BuildPlatform, CPPIncludeInfo CPPIncludeInfo, bool bOnlyCachedDependencies) { var Result = new List <FileItem>(); if (CPPIncludeInfo.IncludeFileSearchDictionary == null) { CPPIncludeInfo.IncludeFileSearchDictionary = new Dictionary <string, FileItem>(); } bool bUseFlatCPPIncludeDependencyCache = (BuildConfiguration.bUseExperimentalFastDependencyScan && (!BuildConfiguration.bUseExperimentalFastBuildIteration || UnrealBuildTool.IsAssemblingBuild)); if (bUseFlatCPPIncludeDependencyCache && bOnlyCachedDependencies) { var Dependencies = FlatCPPIncludeDependencyCache[Target].GetDependenciesForFile(SourceFile.AbsolutePath); if (Dependencies != null) { foreach (string Dependency in Dependencies) { Result.Add(FileItem.GetItemByFullPath(Dependency)); // @todo fastubt: Make sure this is as fast as possible (convert to FileItem) } } else { // Nothing cached for this file! It is new to us. This is the expected flow when our CPPIncludeDepencencyCache is missing. } } else { // @todo fastubt: HeaderParser.h is missing from the include set for Module.UnrealHeaderTool.cpp (failed to find include using: FileItem DirectIncludeResolvedFile = CPPEnvironment.FindIncludedFile(DirectInclude.IncludeName, !BuildConfiguration.bCheckExternalHeadersForModification, IncludePathsToSearch, IncludeFileSearchDictionary );) var IncludedFileList = new IncludedFilesSet(); CPPEnvironment.FindAndCacheAllIncludedFiles(Target, SourceFile, BuildPlatform, CPPIncludeInfo, ref IncludedFileList, bOnlyCachedDependencies: bOnlyCachedDependencies); foreach (FileItem IncludedFile in IncludedFileList) { Result.Add(IncludedFile); } // Update cache if (bUseFlatCPPIncludeDependencyCache && !bOnlyCachedDependencies) { var Dependencies = new List <string>(); foreach (var IncludedFile in Result) { Dependencies.Add(IncludedFile.AbsolutePath); } string PCHName = SourceFile.PrecompiledHeaderIncludeFilename; FlatCPPIncludeDependencyCache[Target].SetDependenciesForFile(SourceFile.AbsolutePath, PCHName, Dependencies); } } return(Result); }
private static bool RequiresTempTarget(string RawProjectPath, List <UnrealTargetPlatform> ClientTargetPlatforms) { // check to see if we already have a Target.cs file if (File.Exists(Path.Combine(Path.GetDirectoryName(RawProjectPath), "Source", Path.GetFileNameWithoutExtension(RawProjectPath) + ".Target.cs"))) { return(false); } else if (Directory.Exists(Path.Combine(Path.GetDirectoryName(RawProjectPath), "Source"))) { // wasn't one in the main Source directory, let's check all sub-directories //@todo: may want to read each target.cs to see if it has a target corresponding to the project name as a final check FileInfo[] Files = (new DirectoryInfo(Path.Combine(Path.GetDirectoryName(RawProjectPath), "Source")).GetFiles("*.Target.cs", SearchOption.AllDirectories)); if (Files.Length > 0) { return(false); } } // no Target file, now check to see if build settings have changed List <UnrealTargetPlatform> TargetPlatforms = ClientTargetPlatforms; if (ClientTargetPlatforms == null || ClientTargetPlatforms.Count < 1) { // No client target platforms, add all in TargetPlatforms = new List <UnrealTargetPlatform>(); foreach (UnrealTargetPlatform TargetPlatformType in Enum.GetValues(typeof(UnrealTargetPlatform))) { if (TargetPlatformType != UnrealTargetPlatform.Unknown) { TargetPlatforms.Add(TargetPlatformType); } } } // Change the working directory to be the Engine/Source folder. We are running from Engine/Binaries/DotNET string oldCWD = Directory.GetCurrentDirectory(); if (BuildConfiguration.RelativeEnginePath == "../../Engine/") { string EngineSourceDirectory = Path.Combine(UnrealBuildTool.Utils.GetExecutingAssemblyDirectory(), "..", "..", "..", "Engine", "Source"); if (!Directory.Exists(EngineSourceDirectory)) // only set the directory if it exists, this should only happen if we are launching the editor from an artist sync { EngineSourceDirectory = Path.Combine(UnrealBuildTool.Utils.GetExecutingAssemblyDirectory(), "..", "..", "..", "Engine", "Binaries"); } Directory.SetCurrentDirectory(EngineSourceDirectory); } // Read the project descriptor, and find all the plugins available to this project ProjectDescriptor Project = ProjectDescriptor.FromFile(RawProjectPath); List <PluginInfo> AvailablePlugins = Plugins.ReadAvailablePlugins(RawProjectPath); // check the target platforms for any differences in build settings or additional plugins bool RetVal = false; foreach (UnrealTargetPlatform TargetPlatformType in TargetPlatforms) { IUEBuildPlatform BuildPlat = UEBuildPlatform.GetBuildPlatform(TargetPlatformType, true); if (!GlobalCommandLine.Rocket && BuildPlat != null && !(BuildPlat as UEBuildPlatform).HasDefaultBuildConfig(TargetPlatformType, Path.GetDirectoryName(RawProjectPath))) { RetVal = true; break; } // find if there are any plugins enabled or disabled which differ from the default foreach (PluginInfo Plugin in AvailablePlugins) { bool bPluginEnabledForProject = UProjectInfo.IsPluginEnabledForProject(Plugin, Project, TargetPlatformType); if ((bPluginEnabledForProject && !Plugin.Descriptor.bEnabledByDefault) || (bPluginEnabledForProject && Plugin.Descriptor.bInstalled)) { if (Plugin.Descriptor.Modules.Any(Module => Module.IsCompiledInConfiguration(TargetPlatformType, TargetRules.TargetType.Game, bBuildDeveloperTools: false, bBuildEditor: false))) { RetVal = true; break; } } } } // Change back to the original directory Directory.SetCurrentDirectory(oldCWD); return(RetVal); }