예제 #1
0
        /// <summary>
        /// Creates a text file with the given contents.  If the contents of the text file aren't changed, it won't write the new contents to
        /// the file to avoid causing an action to be considered outdated.
        /// </summary>
        /// <param name="AbsolutePath">Path to the intermediate file to create</param>
        /// <param name="Contents">Contents of the new file</param>
        /// <returns>File item for the newly created file</returns>
        public static FileItem CreateIntermediateTextFile(FileReference AbsolutePath, string Contents)
        {
            // Create the directory if it doesn't exist.
            Directory.CreateDirectory(Path.GetDirectoryName(AbsolutePath.FullName));

            // Only write the file if its contents have changed.
            if (!FileReference.Exists(AbsolutePath))
            {
                File.WriteAllText(AbsolutePath.FullName, Contents, GetEncodingForString(Contents));
            }
            else
            {
                string CurrentContents = Utils.ReadAllText(AbsolutePath.FullName);
                if (!String.Equals(CurrentContents, Contents, StringComparison.InvariantCultureIgnoreCase))
                {
                    try
                    {
                        FileReference PrevAbsolutePath = new FileReference(AbsolutePath.FullName + ".prev");
                        FileReference.Delete(PrevAbsolutePath);
                        FileReference.Move(AbsolutePath, PrevAbsolutePath);
                        Log.TraceLog("Updating {0} - contents have changed (previous version renamed to {1}).", AbsolutePath.FullName, PrevAbsolutePath);
                    }
                    catch
                    {
                        Log.TraceLog("Updating {0} - contents have changed (unable to rename). Previous:\n  {1}\nNew:\n  {2}", AbsolutePath.FullName, CurrentContents.Replace("\n", "\n  "), Contents.Replace("\n", "\n  "));
                    }
                    File.WriteAllText(AbsolutePath.FullName, Contents, GetEncodingForString(Contents));
                }
            }
            return(GetItemByFileReference(AbsolutePath));
        }
예제 #2
0
        /// <summary>
        /// Creates a text file with the given contents.  If the contents of the text file aren't changed, it won't write the new contents to
        /// the file to avoid causing an action to be considered outdated.
        /// </summary>
        /// <param name="Location">Path to the intermediate file to create</param>
        /// <param name="Contents">Contents of the new file</param>
        /// <returns>File item for the newly created file</returns>
        public static FileItem CreateIntermediateTextFile(FileReference Location, string Contents)
        {
            // Only write the file if its contents have changed.
            if (!FileReference.Exists(Location))
            {
                DirectoryReference.CreateDirectory(Location.Directory);
                FileReference.WriteAllText(Location, Contents, GetEncodingForString(Contents));
            }
            else
            {
                string CurrentContents = Utils.ReadAllText(Location.FullName);
                if (!String.Equals(CurrentContents, Contents, StringComparison.InvariantCultureIgnoreCase))
                {
                    FileReference BackupFile = new FileReference(Location.FullName + ".old");
                    try
                    {
                        Log.TraceLog("Updating {0}: contents have changed. Saving previous version to {1}.", Location, BackupFile);
                        FileReference.Delete(BackupFile);
                        FileReference.Move(Location, BackupFile);
                    }
                    catch (Exception Ex)
                    {
                        Log.TraceWarning("Unable to rename {0} to {1}", Location, BackupFile);
                        Log.TraceLog("{0}", ExceptionUtils.FormatExceptionDetails(Ex));
                    }
                    FileReference.WriteAllText(Location, Contents, GetEncodingForString(Contents));
                }
            }

            // Reset the file info, in case it already knows about the old file
            FileItem Item = GetItemByFileReference(Location);

            Item.ResetCachedInfo();
            return(Item);
        }
예제 #3
0
        /**
         * Creates a text file with the given contents.  If the contents of the text file aren't changed, it won't write the new contents to
         * the file to avoid causing an action to be considered outdated.
         */
        public static FileItem CreateIntermediateTextFile(string AbsolutePath, string Contents)
        {
            // Create the directory if it doesn't exist.
            Directory.CreateDirectory(Path.GetDirectoryName(AbsolutePath));

            // Only write the file if its contents have changed.
            if (!File.Exists(AbsolutePath) || !String.Equals(Utils.ReadAllText(AbsolutePath), Contents, StringComparison.InvariantCultureIgnoreCase))
            {
                File.WriteAllText(AbsolutePath, Contents, GetEncodingForString(Contents));
            }

            return(GetItemByPath(AbsolutePath));
        }
예제 #4
0
        /// <summary>
        /// Patch the action graph for hot reloading, mapping files according to the given dictionary.
        /// </summary>
        public static void PatchActionGraph(IEnumerable <Action> Actions, Dictionary <FileReference, FileReference> OriginalFileToHotReloadFile)
        {
            // Gather all of the response files for link actions.  We're going to need to patch 'em up after we figure out new
            // names for all of the output files and import libraries
            List <string> ResponseFilePaths = new List <string>();

            // Same as Response files but for all of the link.sh files for link actions.
            // Only used on BuildHostPlatform Linux
            List <string> LinkScriptFilePaths = new List <string>();

            // Keep a map of the original file names and their new file names, so we can fix up response files after
            Dictionary <string, string> OriginalFileNameAndNewFileNameList_NoExtensions = new Dictionary <string, string>();

            // Finally, we'll keep track of any file items that we had to create counterparts for change file names, so we can fix those up too
            Dictionary <FileItem, FileItem> AffectedOriginalFileItemAndNewFileItemMap = new Dictionary <FileItem, FileItem>();

            foreach (Action Action in Actions.Where((Action) => Action.ActionType == ActionType.Link))
            {
                // Assume that the first produced item (with no extension) is our output file name
                FileReference HotReloadFile;
                if (!OriginalFileToHotReloadFile.TryGetValue(Action.ProducedItems[0].Location, out HotReloadFile))
                {
                    continue;
                }

                string OriginalFileNameWithoutExtension = Utils.GetFilenameWithoutAnyExtensions(Action.ProducedItems[0].AbsolutePath);
                string NewFileNameWithoutExtension      = Utils.GetFilenameWithoutAnyExtensions(HotReloadFile.FullName);

                // Find the response file in the command line.  We'll need to make a copy of it with our new file name.
                string ResponseFileExtension  = ".response";
                int    ResponseExtensionIndex = Action.CommandArguments.IndexOf(ResponseFileExtension, StringComparison.InvariantCultureIgnoreCase);
                if (ResponseExtensionIndex != -1)
                {
                    int ResponseFilePathIndex = Action.CommandArguments.LastIndexOf("@\"", ResponseExtensionIndex);
                    if (ResponseFilePathIndex == -1)
                    {
                        throw new BuildException("Couldn't find response file path in action's command arguments when hot reloading");
                    }

                    string OriginalResponseFilePathWithoutExtension = Action.CommandArguments.Substring(ResponseFilePathIndex + 2, (ResponseExtensionIndex - ResponseFilePathIndex) - 2);
                    string OriginalResponseFilePath = OriginalResponseFilePathWithoutExtension + ResponseFileExtension;

                    string NewResponseFilePath = ReplaceBaseFileName(OriginalResponseFilePath, OriginalFileNameWithoutExtension, NewFileNameWithoutExtension);

                    // Copy the old response file to the new path
                    if (String.Compare(OriginalResponseFilePath, NewResponseFilePath, StringComparison.OrdinalIgnoreCase) != 0)
                    {
                        File.Copy(OriginalResponseFilePath, NewResponseFilePath, overwrite: true);
                    }

                    // Keep track of the new response file name.  We'll have to do some edits afterwards.
                    ResponseFilePaths.Add(NewResponseFilePath);
                }

                // Find the *.link.sh file in the command line.  We'll need to make a copy of it with our new file name.
                // Only currently used on Linux
                if (UEBuildPlatform.IsPlatformInGroup(BuildHostPlatform.Current.Platform, UnrealPlatformGroup.Unix))
                {
                    string LinkScriptFileExtension  = ".link.sh";
                    int    LinkScriptExtensionIndex = Action.CommandArguments.IndexOf(LinkScriptFileExtension, StringComparison.InvariantCultureIgnoreCase);
                    if (LinkScriptExtensionIndex != -1)
                    {
                        // We expect the script invocation to be quoted
                        int LinkScriptFilePathIndex = Action.CommandArguments.LastIndexOf("\"", LinkScriptExtensionIndex);
                        if (LinkScriptFilePathIndex == -1)
                        {
                            throw new BuildException("Couldn't find link script file path in action's command arguments when hot reloading. Is the path quoted?");
                        }

                        string OriginalLinkScriptFilePathWithoutExtension = Action.CommandArguments.Substring(LinkScriptFilePathIndex + 1, (LinkScriptExtensionIndex - LinkScriptFilePathIndex) - 1);
                        string OriginalLinkScriptFilePath = OriginalLinkScriptFilePathWithoutExtension + LinkScriptFileExtension;

                        string NewLinkScriptFilePath = ReplaceBaseFileName(OriginalLinkScriptFilePath, OriginalFileNameWithoutExtension, NewFileNameWithoutExtension);

                        // Copy the old response file to the new path
                        File.Copy(OriginalLinkScriptFilePath, NewLinkScriptFilePath, overwrite: true);

                        // Keep track of the new response file name.  We'll have to do some edits afterwards.
                        LinkScriptFilePaths.Add(NewLinkScriptFilePath);
                    }

                    // Update this action's list of prerequisite items too
                    for (int ItemIndex = 0; ItemIndex < Action.PrerequisiteItems.Count; ++ItemIndex)
                    {
                        FileItem OriginalPrerequisiteItem    = Action.PrerequisiteItems[ItemIndex];
                        string   NewPrerequisiteItemFilePath = ReplaceBaseFileName(OriginalPrerequisiteItem.AbsolutePath, OriginalFileNameWithoutExtension, NewFileNameWithoutExtension);

                        if (OriginalPrerequisiteItem.AbsolutePath != NewPrerequisiteItemFilePath)
                        {
                            // OK, the prerequisite item's file name changed so we'll update it to point to our new file
                            FileItem NewPrerequisiteItem = FileItem.GetItemByPath(NewPrerequisiteItemFilePath);
                            Action.PrerequisiteItems[ItemIndex] = NewPrerequisiteItem;

                            // Keep track of it so we can fix up dependencies in a second pass afterwards
                            AffectedOriginalFileItemAndNewFileItemMap.Add(OriginalPrerequisiteItem, NewPrerequisiteItem);

                            ResponseExtensionIndex = OriginalPrerequisiteItem.AbsolutePath.IndexOf(ResponseFileExtension, StringComparison.InvariantCultureIgnoreCase);
                            if (ResponseExtensionIndex != -1)
                            {
                                string OriginalResponseFilePathWithoutExtension = OriginalPrerequisiteItem.AbsolutePath.Substring(0, ResponseExtensionIndex);
                                string OriginalResponseFilePath = OriginalResponseFilePathWithoutExtension + ResponseFileExtension;

                                string NewResponseFilePath = ReplaceBaseFileName(OriginalResponseFilePath, OriginalFileNameWithoutExtension, NewFileNameWithoutExtension);

                                // Copy the old response file to the new path
                                File.Copy(OriginalResponseFilePath, NewResponseFilePath, overwrite: true);

                                // Keep track of the new response file name.  We'll have to do some edits afterwards.
                                ResponseFilePaths.Add(NewResponseFilePath);
                            }
                        }
                    }
                }

                // Update this action's list of produced items too
                for (int ItemIndex = 0; ItemIndex < Action.ProducedItems.Count; ++ItemIndex)
                {
                    FileItem OriginalProducedItem = Action.ProducedItems[ItemIndex];

                    string NewProducedItemFilePath = ReplaceBaseFileName(OriginalProducedItem.AbsolutePath, OriginalFileNameWithoutExtension, NewFileNameWithoutExtension);
                    if (OriginalProducedItem.AbsolutePath != NewProducedItemFilePath)
                    {
                        // OK, the produced item's file name changed so we'll update it to point to our new file
                        FileItem NewProducedItem = FileItem.GetItemByPath(NewProducedItemFilePath);
                        Action.ProducedItems[ItemIndex] = NewProducedItem;

                        // Keep track of it so we can fix up dependencies in a second pass afterwards
                        AffectedOriginalFileItemAndNewFileItemMap.Add(OriginalProducedItem, NewProducedItem);
                    }
                }

                // Fix up the list of items to delete too
                for (int Idx = 0; Idx < Action.DeleteItems.Count; Idx++)
                {
                    FileItem NewItem;
                    if (AffectedOriginalFileItemAndNewFileItemMap.TryGetValue(Action.DeleteItems[Idx], out NewItem))
                    {
                        Action.DeleteItems[Idx] = NewItem;
                    }
                }

                // The status description of the item has the file name, so we'll update it too
                Action.StatusDescription = ReplaceBaseFileName(Action.StatusDescription, OriginalFileNameWithoutExtension, NewFileNameWithoutExtension);

                // Keep track of the file names, so we can fix up response files afterwards.
                if (!OriginalFileNameAndNewFileNameList_NoExtensions.ContainsKey(OriginalFileNameWithoutExtension))
                {
                    OriginalFileNameAndNewFileNameList_NoExtensions[OriginalFileNameWithoutExtension] = NewFileNameWithoutExtension;
                }
                else if (OriginalFileNameAndNewFileNameList_NoExtensions[OriginalFileNameWithoutExtension] != NewFileNameWithoutExtension)
                {
                    throw new BuildException("Unexpected conflict in renaming files; {0} maps to {1} and {2}", OriginalFileNameWithoutExtension, OriginalFileNameAndNewFileNameList_NoExtensions[OriginalFileNameWithoutExtension], NewFileNameWithoutExtension);
                }
            }


            // Do another pass and update any actions that depended on the original file names that we changed
            foreach (Action Action in Actions)
            {
                for (int ItemIndex = 0; ItemIndex < Action.PrerequisiteItems.Count; ++ItemIndex)
                {
                    FileItem OriginalFileItem = Action.PrerequisiteItems[ItemIndex];

                    FileItem NewFileItem;
                    if (AffectedOriginalFileItemAndNewFileItemMap.TryGetValue(OriginalFileItem, out NewFileItem))
                    {
                        // OK, looks like we need to replace this file item because we've renamed the file
                        Action.PrerequisiteItems[ItemIndex] = NewFileItem;
                    }
                }
            }


            if (OriginalFileNameAndNewFileNameList_NoExtensions.Count > 0)
            {
                // Update all the paths in link actions
                foreach (Action Action in Actions.Where((Action) => Action.ActionType == ActionType.Link))
                {
                    foreach (KeyValuePair <string, string> FileNameTuple in OriginalFileNameAndNewFileNameList_NoExtensions)
                    {
                        string OriginalFileNameWithoutExtension = FileNameTuple.Key;
                        string NewFileNameWithoutExtension      = FileNameTuple.Value;

                        Action.CommandArguments = ReplaceBaseFileName(Action.CommandArguments, OriginalFileNameWithoutExtension, NewFileNameWithoutExtension);
                    }
                }

                foreach (string ResponseFilePath in ResponseFilePaths)
                {
                    // Load the file up
                    string FileContents = Utils.ReadAllText(ResponseFilePath);

                    // Replace all of the old file names with new ones
                    foreach (KeyValuePair <string, string> FileNameTuple in OriginalFileNameAndNewFileNameList_NoExtensions)
                    {
                        string OriginalFileNameWithoutExtension = FileNameTuple.Key;
                        string NewFileNameWithoutExtension      = FileNameTuple.Value;

                        FileContents = ReplaceBaseFileName(FileContents, OriginalFileNameWithoutExtension, NewFileNameWithoutExtension);
                    }

                    // Overwrite the original file
                    File.WriteAllText(ResponseFilePath, FileContents, new System.Text.UTF8Encoding(false));
                }

                if (UEBuildPlatform.IsPlatformInGroup(BuildHostPlatform.Current.Platform, UnrealPlatformGroup.Unix))
                {
                    foreach (string LinkScriptFilePath in LinkScriptFilePaths)
                    {
                        // Load the file up
                        string FileContents = Utils.ReadAllText(LinkScriptFilePath);

                        // Replace all of the old file names with new ones
                        foreach (KeyValuePair <string, string> FileNameTuple in OriginalFileNameAndNewFileNameList_NoExtensions)
                        {
                            string OriginalFileNameWithoutExtension = FileNameTuple.Key;
                            string NewFileNameWithoutExtension      = FileNameTuple.Value;

                            FileContents = ReplaceBaseFileName(FileContents, OriginalFileNameWithoutExtension, NewFileNameWithoutExtension);
                        }

                        // Overwrite the original file
                        File.WriteAllText(LinkScriptFilePath, FileContents, new System.Text.UTF8Encoding(false));
                    }
                }
            }

            // Update the action that writes out the module manifests
            foreach (Action Action in Actions)
            {
                if (Action.ActionType == ActionType.WriteMetadata)
                {
                    string Arguments = Action.CommandArguments;

                    // Find the argument for the metadata file
                    const string InputArgument = "-Input=";

                    int InputIdx = Arguments.IndexOf(InputArgument);
                    if (InputIdx == -1)
                    {
                        throw new Exception("Missing -Input= argument to WriteMetadata command when patching action graph.");
                    }

                    int FileNameIdx = InputIdx + InputArgument.Length;
                    if (Arguments[FileNameIdx] == '\"')
                    {
                        FileNameIdx++;
                    }

                    int FileNameEndIdx = FileNameIdx;
                    while (FileNameEndIdx < Arguments.Length && (Arguments[FileNameEndIdx] != ' ' || Arguments[FileNameIdx - 1] == '\"') && Arguments[FileNameEndIdx] != '\"')
                    {
                        FileNameEndIdx++;
                    }

                    // Read the metadata file
                    FileReference TargetInfoFile = new FileReference(Arguments.Substring(FileNameIdx, FileNameEndIdx - FileNameIdx));
                    if (!FileReference.Exists(TargetInfoFile))
                    {
                        throw new Exception(String.Format("Unable to find metadata file to patch action graph ({0})", TargetInfoFile));
                    }

                    // Update the module names
                    WriteMetadataTargetInfo TargetInfo = BinaryFormatterUtils.Load <WriteMetadataTargetInfo>(TargetInfoFile);
                    foreach (KeyValuePair <FileReference, ModuleManifest> FileNameToVersionManifest in TargetInfo.FileToManifest)
                    {
                        KeyValuePair <string, string>[] ManifestEntries = FileNameToVersionManifest.Value.ModuleNameToFileName.ToArray();
                        foreach (KeyValuePair <string, string> Manifest in ManifestEntries)
                        {
                            FileReference OriginalFile = FileReference.Combine(FileNameToVersionManifest.Key.Directory, Manifest.Value);

                            FileReference HotReloadFile;
                            if (OriginalFileToHotReloadFile.TryGetValue(OriginalFile, out HotReloadFile))
                            {
                                FileNameToVersionManifest.Value.ModuleNameToFileName[Manifest.Key] = HotReloadFile.GetFileName();
                            }
                        }
                    }

                    // Write the hot-reload metadata file and update the argument list
                    FileReference HotReloadTargetInfoFile = FileReference.Combine(TargetInfoFile.Directory, "Metadata-HotReload.dat");
                    BinaryFormatterUtils.SaveIfDifferent(HotReloadTargetInfoFile, TargetInfo);

                    Action.PrerequisiteItems.RemoveAll(x => x.Location == TargetInfoFile);
                    Action.PrerequisiteItems.Add(FileItem.GetItemByFileReference(HotReloadTargetInfoFile));

                    Action.CommandArguments = Arguments.Substring(0, FileNameIdx) + HotReloadTargetInfoFile + Arguments.Substring(FileNameEndIdx);
                }
            }
        }
예제 #5
0
        /** 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);
        }