public override int Execute(CommandLineArguments Arguments)
        {
            FileReference ManifestFile = Arguments.GetFileReference("-ManifestFile=");

            string[] ParsedFileNames = FileReference.ReadAllLines(ManifestFile);

            // Load all the file timing data and summarize them for the aggregate.
            FileTimingData = new TimingData()
            {
                Name = "Files", Type = TimingDataType.Summary
            };
            Parallel.ForEach(ParsedFileNames, new ParallelOptions()
            {
                MaxDegreeOfParallelism = 4
            }, ParseTimingDataFile);

            // Create aggregate summary. Duration is the duration of the files in the aggregate.
            string     AggregateName = Arguments.GetString("-Name=");
            TimingData AggregateData = new TimingData()
            {
                Name = AggregateName, Type = TimingDataType.Aggregate
            };

            AggregateData.AddChild(FileTimingData);

            // Group the includes, classes, and functions by name and sum them up then add to the aggregate.
            GroupTimingDataOnName(AggregateData, "Include Timings", AggregateIncludes);
            GroupTimingDataOnName(AggregateData, "Class Timings", AggregateClasses);
            GroupTimingDataOnName(AggregateData, "Function Timings", AggregateFunctions);

            // Write out aggregate summary.
            string OutputFile = Path.Combine(ManifestFile.Directory.FullName, String.Format("{0}.timing.bin", AggregateName));

            using (BinaryWriter Writer = new BinaryWriter(File.Open(OutputFile, FileMode.Create)))
            {
                // Write out the aggregate data.
                Writer.Write(AggregateData);

                // Write the look up table for the compressed binary blobs.
                int Offset = 0;
                Writer.Write(CompressedFiles.Count);
                foreach (KeyValuePair <string, byte[]> CompressedFile in CompressedFiles)
                {
                    Writer.Write(CompressedFile.Key);
                    Writer.Write(Offset);
                    Writer.Write(CompressedFile.Value.Length);
                    Writer.Write(DecompressedFileSizes[CompressedFile.Key]);
                    Offset += CompressedFile.Value.Length;
                }

                // Write the compressed binary blobs.
                foreach (KeyValuePair <string, byte[]> CompressedFile in CompressedFiles)
                {
                    Writer.Write(CompressedFile.Value);
                }
            }

            return(0);
        }
Exemplo n.º 2
0
 void ParseTimingDataToTracingFiles(FileReference InputFile)
 {
     string[] Lines = FileReference.ReadAllLines(InputFile);
     for (int LineIdx = 0; LineIdx < Lines.Length;)
     {
         string Line = Lines[LineIdx];
         if (Line.StartsWith("Include Headers:", StringComparison.Ordinal))
         {
             LineIdx = ParseIncludeHeadersToTraces(Lines, LineIdx + 1, InputFile.ChangeExtension(".json"));
         }
         else if (Line.StartsWith("Class Definitions:", StringComparison.Ordinal))
         {
             LineIdx = ParseDefinitions(Lines, LineIdx + 1, InputFile.ChangeExtension(".classes.txt"));
         }
         else if (Line.StartsWith("Function Definitions:", StringComparison.Ordinal))
         {
             LineIdx = ParseDefinitions(Lines, LineIdx + 1, InputFile.ChangeExtension(".functions.txt"));
         }
         else
         {
             LineIdx++;
         }
     }
 }
Exemplo n.º 3
0
        /// <summary>
        /// Execute the command
        /// </summary>
        /// <param name="Arguments">List of command line arguments</param>
        /// <returns>Always zero, or throws an exception</returns>
        public override int Execute(CommandLineArguments Arguments)
        {
            Arguments.ApplyTo(this);
            Arguments.CheckAllArgumentsUsed();

            Log.TraceInformation("{0}", OutputFile.GetFileName());

            // Read the input files
            string[]        InputFileLines = FileReference.ReadAllLines(InputFileList);
            FileReference[] InputFiles     = InputFileLines.Select(x => x.Trim()).Where(x => x.Length > 0).Select(x => new FileReference(x)).ToArray();

            // Create the combined output file, and print the diagnostics to the log
            HashSet <string> UniqueItems = new HashSet <string>();

            using (StreamWriter RawWriter = new StreamWriter(OutputFile.FullName))
            {
                foreach (FileReference InputFile in InputFiles)
                {
                    string[] Lines = File.ReadAllLines(InputFile.FullName);
                    for (int LineIdx = 0; LineIdx < Lines.Length; LineIdx++)
                    {
                        string Line = Lines[LineIdx];
                        if (!String.IsNullOrWhiteSpace(Line) && UniqueItems.Add(Line))
                        {
                            bool bCanParse = false;

                            string[] Tokens = Line.Split(new string[] { "<#~>" }, StringSplitOptions.None);
                            if (Tokens.Length >= 9)
                            {
                                //string Trial = Tokens[1];
                                string LineNumberStr  = Tokens[2];
                                string FileName       = Tokens[3];
                                string WarningCode    = Tokens[5];
                                string WarningMessage = Tokens[6];
                                string FalseAlarmStr  = Tokens[7];
                                string LevelStr       = Tokens[8];

                                int  LineNumber;
                                bool bFalseAlarm;
                                int  Level;
                                if (int.TryParse(LineNumberStr, out LineNumber) && bool.TryParse(FalseAlarmStr, out bFalseAlarm) && int.TryParse(LevelStr, out Level))
                                {
                                    bCanParse = true;

                                    // Ignore anything in ThirdParty folders
                                    if (FileName.Replace('/', '\\').IndexOf("\\ThirdParty\\", StringComparison.InvariantCultureIgnoreCase) == -1)
                                    {
                                        // Output the line to the raw output file
                                        RawWriter.WriteLine(Line);

                                        // Output the line to the log
                                        if (!bFalseAlarm && Level == 1)
                                        {
                                            Log.WriteLine(LogEventType.Warning, LogFormatOptions.NoSeverityPrefix, "{0}({1}): warning {2}: {3}", FileName, LineNumber, WarningCode, WarningMessage);
                                        }
                                    }
                                }
                            }

                            if (!bCanParse)
                            {
                                Log.WriteLine(LogEventType.Warning, LogFormatOptions.NoSeverityPrefix, "{0}({1}): warning: Unable to parse PVS output line '{2}' (tokens=|{3}|)", InputFile, LineIdx + 1, Line, String.Join("|", Tokens));
                            }
                        }
                    }
                }
            }
            Log.TraceInformation("Written {0} {1} to {2}.", UniqueItems.Count, (UniqueItems.Count == 1)? "diagnostic" : "diagnostics", OutputFile.FullName);
            return(0);
        }
Exemplo n.º 4
0
        /// <summary>
        /// Patches a set of actions for use with live coding. The new action list will output object files to a different location.
        /// </summary>
        /// <param name="Actions">Set of actions</param>
        /// <param name="OriginalFileToPatchedFile">Dictionary that receives a map of original object file to patched object file</param>
        public static void PatchActionGraphForLiveCoding(IEnumerable <Action> Actions, Dictionary <FileReference, FileReference> OriginalFileToPatchedFile)
        {
            foreach (Action Action in Actions)
            {
                if (Action.ActionType == ActionType.Compile)
                {
                    if (!Action.CommandPath.GetFileName().Equals("cl-filter.exe", StringComparison.OrdinalIgnoreCase))
                    {
                        throw new BuildException("Unable to patch action graph - unexpected executable in compile action ({0})", Action.CommandPath);
                    }

                    List <string> Arguments = Utils.ParseArgumentList(Action.CommandArguments);

                    // Find the index of the cl-filter argument delimiter
                    int DelimiterIdx = Arguments.IndexOf("--");
                    if (DelimiterIdx == -1)
                    {
                        throw new BuildException("Unable to patch action graph - missing '--' delimiter to cl-filter");
                    }

                    // Fix the dependencies path
                    const string DependenciesPrefix = "-dependencies=";

                    int DependenciesIdx = 0;
                    for (;; DependenciesIdx++)
                    {
                        if (DependenciesIdx == DelimiterIdx)
                        {
                            throw new BuildException("Unable to patch action graph - missing '{0}' argument to cl-filter", DependenciesPrefix);
                        }
                        else if (Arguments[DependenciesIdx].StartsWith(DependenciesPrefix, StringComparison.OrdinalIgnoreCase))
                        {
                            break;
                        }
                    }

                    FileReference OldDependenciesFile     = new FileReference(Arguments[DependenciesIdx].Substring(DependenciesPrefix.Length));
                    FileItem      OldDependenciesFileItem = Action.ProducedItems.First(x => x.Location == OldDependenciesFile);
                    Action.ProducedItems.Remove(OldDependenciesFileItem);

                    FileReference NewDependenciesFile     = OldDependenciesFile.ChangeExtension(".lc.response");
                    FileItem      NewDependenciesFileItem = FileItem.GetItemByFileReference(NewDependenciesFile);
                    Action.ProducedItems.Add(NewDependenciesFileItem);

                    Arguments[DependenciesIdx] = DependenciesPrefix + NewDependenciesFile.FullName;

                    // Fix the response file
                    int ResponseFileIdx = DelimiterIdx + 1;
                    for (; ; ResponseFileIdx++)
                    {
                        if (ResponseFileIdx == Arguments.Count)
                        {
                            throw new BuildException("Unable to patch action graph - missing response file argument to cl-filter");
                        }
                        else if (Arguments[ResponseFileIdx].StartsWith("@", StringComparison.Ordinal))
                        {
                            break;
                        }
                    }

                    FileReference OldResponseFile = new FileReference(Arguments[ResponseFileIdx].Substring(1));
                    FileReference NewResponseFile = new FileReference(OldResponseFile.FullName + ".lc");

                    const string OutputFilePrefix = "/Fo";

                    string[] ResponseLines = FileReference.ReadAllLines(OldResponseFile);
                    for (int Idx = 0; Idx < ResponseLines.Length; Idx++)
                    {
                        string ResponseLine = ResponseLines[Idx];
                        if (ResponseLine.StartsWith(OutputFilePrefix, StringComparison.Ordinal))
                        {
                            FileReference OldOutputFile     = new FileReference(ResponseLine.Substring(3).Trim('\"'));
                            FileItem      OldOutputFileItem = Action.ProducedItems.First(x => x.Location == OldOutputFile);
                            Action.ProducedItems.Remove(OldOutputFileItem);

                            FileReference NewOutputFile     = OldOutputFile.ChangeExtension(".lc.obj");
                            FileItem      NewOutputFileItem = FileItem.GetItemByFileReference(NewOutputFile);
                            Action.ProducedItems.Add(NewOutputFileItem);

                            OriginalFileToPatchedFile[OldOutputFile] = NewOutputFile;

                            ResponseLines[Idx] = OutputFilePrefix + "\"" + NewOutputFile.FullName + "\"";
                            break;
                        }
                    }
                    FileReference.WriteAllLines(NewResponseFile, ResponseLines);

                    Arguments[ResponseFileIdx] = "@" + NewResponseFile.FullName;

                    // Update the final arguments
                    Action.CommandArguments = Utils.FormatCommandLine(Arguments);
                }
            }
        }
Exemplo n.º 5
0
        /// <summary>
        /// Upload all the files in the workspace for the current project
        /// </summary>
        void UploadWorkspace(DirectoryReference TempDir)
        {
            // Path to the scripts to be uploaded
            FileReference ScriptPathsFileName = FileReference.Combine(UnrealBuildTool.EngineDirectory, "Build", "Rsync", "RsyncEngineScripts.txt");

            // Read the list of scripts to be uploaded
            List <string> ScriptPaths = new List <string>();

            foreach (string Line in FileReference.ReadAllLines(ScriptPathsFileName))
            {
                string FileToUpload = Line.Trim();
                if (FileToUpload.Length > 0 && FileToUpload[0] != ';')
                {
                    ScriptPaths.Add(FileToUpload);
                }
            }

            // Fixup the line endings
            List <FileReference> TargetFiles = new List <FileReference>();

            foreach (string ScriptPath in ScriptPaths)
            {
                FileReference SourceFile = FileReference.Combine(UnrealBuildTool.EngineDirectory, ScriptPath.TrimStart('/'));
                if (!FileReference.Exists(SourceFile))
                {
                    throw new BuildException("Missing script required for remote upload: {0}", SourceFile);
                }

                FileReference TargetFile = FileReference.Combine(TempDir, SourceFile.MakeRelativeTo(UnrealBuildTool.EngineDirectory));
                if (!FileReference.Exists(TargetFile) || FileReference.GetLastWriteTimeUtc(TargetFile) < FileReference.GetLastWriteTimeUtc(SourceFile))
                {
                    DirectoryReference.CreateDirectory(TargetFile.Directory);
                    string ScriptText = FileReference.ReadAllText(SourceFile);
                    FileReference.WriteAllText(TargetFile, ScriptText.Replace("\r\n", "\n"));
                }
                TargetFiles.Add(TargetFile);
            }

            // Write a file that protects all the scripts from being overridden by the standard engine filters
            FileReference ScriptUploadList = FileReference.Combine(TempDir, "RsyncEngineScripts-Upload.txt");

            using (StreamWriter Writer = new StreamWriter(ScriptUploadList.FullName))
            {
                foreach (string ScriptPath in ScriptPaths)
                {
                    for (int SlashIdx = ScriptPath.IndexOf('/', 1); SlashIdx != -1; SlashIdx = ScriptPath.IndexOf('/', SlashIdx + 1))
                    {
                        Writer.WriteLine("+ {0}", ScriptPath.Substring(0, SlashIdx));
                    }
                    Writer.WriteLine("+ {0}", ScriptPath);
                }
                Writer.WriteLine("protect *");
            }

            // Write a file that protects all the scripts from being overridden by the standard engine filters
            FileReference ScriptProtectList = FileReference.Combine(TempDir, "RsyncEngineScripts-Protect.txt");

            using (StreamWriter Writer = new StreamWriter(ScriptProtectList.FullName))
            {
                foreach (string ScriptPath in ScriptPaths)
                {
                    Writer.WriteLine("protect {0}", ScriptPath);
                }
            }

            // Upload these files to the remote
            List <FileReference> FilterLocations = new List <FileReference>();

            FilterLocations.Add(ScriptUploadList);
            UploadDirectory(TempDir, GetRemotePath(UnrealBuildTool.EngineDirectory), FilterLocations);

            // Upload the engine files
            List <FileReference> EngineFilters = new List <FileReference>();

            EngineFilters.Add(ScriptProtectList);
            EngineFilters.Add(FileReference.Combine(UnrealBuildTool.EngineDirectory, "Build", "Rsync", "RsyncEngine.txt"));
            UploadDirectory(UnrealBuildTool.EngineDirectory, GetRemotePath(UnrealBuildTool.EngineDirectory), EngineFilters);

            // Upload the project files
            if (ProjectFile != null && !ProjectFile.IsUnderDirectory(UnrealBuildTool.EngineDirectory))
            {
                List <FileReference> ProjectFilters = new List <FileReference>();

                FileReference CustomProjectFilter = FileReference.Combine(ProjectFile.Directory, "Build", "Rsync", "RsyncProject.txt");
                if (FileReference.Exists(CustomProjectFilter))
                {
                    ProjectFilters.Add(CustomProjectFilter);
                }
                ProjectFilters.Add(FileReference.Combine(UnrealBuildTool.EngineDirectory, "Build", "Rsync", "RsyncProject.txt"));

                UploadDirectory(ProjectFile.Directory, GetRemotePath(ProjectFile.Directory), ProjectFilters);
            }
        }
        /// <summary>
        /// Parse crypto settings from INI file
        /// </summary>
        public static CryptoSettings ParseCryptoSettings(DirectoryReference InProjectDirectory, UnrealTargetPlatform InTargetPlatform)
        {
            CryptoSettings Settings = new CryptoSettings();

            ConfigHierarchy Ini = ConfigCache.ReadHierarchy(ConfigHierarchyType.Engine, InProjectDirectory, InTargetPlatform);

            Ini.GetBool("PlatformCrypto", "PlatformRequiresDataCrypto", out Settings.bDataCryptoRequired);
            Ini.GetBool("PlatformCrypto", "PakSigningRequired", out Settings.PakSigningRequired);
            Ini.GetBool("PlatformCrypto", "PakEncryptionRequired", out Settings.PakEncryptionRequired);

            {
                // Start by parsing the legacy encryption.ini settings
                Ini = ConfigCache.ReadHierarchy(ConfigHierarchyType.Encryption, InProjectDirectory, InTargetPlatform);
                Ini.GetBool("Core.Encryption", "SignPak", out Settings.bEnablePakSigning);

                string[] SigningKeyStrings = new string[3];
                Ini.GetString("Core.Encryption", "rsa.privateexp", out SigningKeyStrings[0]);
                Ini.GetString("Core.Encryption", "rsa.modulus", out SigningKeyStrings[1]);
                Ini.GetString("Core.Encryption", "rsa.publicexp", out SigningKeyStrings[2]);

                if (String.IsNullOrEmpty(SigningKeyStrings[0]) || String.IsNullOrEmpty(SigningKeyStrings[1]) || String.IsNullOrEmpty(SigningKeyStrings[2]))
                {
                    SigningKeyStrings = null;
                }
                else
                {
                    Settings.SigningKey = new SigningKeyPair();
                    Settings.SigningKey.PrivateKey.Exponent = ParseHexStringToByteArray(ProcessSigningKeyInputStrings(SigningKeyStrings[0]), 64);
                    Settings.SigningKey.PrivateKey.Modulus  = ParseHexStringToByteArray(ProcessSigningKeyInputStrings(SigningKeyStrings[1]), 64);
                    Settings.SigningKey.PublicKey.Exponent  = ParseHexStringToByteArray(ProcessSigningKeyInputStrings(SigningKeyStrings[2]), 64);
                    Settings.SigningKey.PublicKey.Modulus   = Settings.SigningKey.PrivateKey.Modulus;

                    if ((Settings.SigningKey.PrivateKey.Exponent.Length > 64) ||
                        (Settings.SigningKey.PrivateKey.Modulus.Length > 64) ||
                        (Settings.SigningKey.PublicKey.Exponent.Length > 64) ||
                        (Settings.SigningKey.PublicKey.Modulus.Length > 64))
                    {
                        throw new Exception(string.Format("[{0}] Signing keys parsed from encryption.ini are too long. They must be a maximum of 64 bytes long!", InProjectDirectory));
                    }
                }

                Ini.GetBool("Core.Encryption", "EncryptPak", out Settings.bEnablePakIndexEncryption);
                Settings.bEnablePakFullAssetEncryption = false;
                Settings.bEnablePakUAssetEncryption    = false;
                Settings.bEnablePakIniEncryption       = Settings.bEnablePakIndexEncryption;

                string EncryptionKeyString;
                Ini.GetString("Core.Encryption", "aes.key", out EncryptionKeyString);
                Settings.EncryptionKey = new EncryptionKey();

                if (EncryptionKeyString.Length > 0)
                {
                    if (EncryptionKeyString.Length < 32)
                    {
                        Log.WriteLine(LogEventType.Warning, "AES key parsed from encryption.ini is too short. It must be 32 bytes, so will be padded with 0s, giving sub-optimal security!");
                    }
                    else if (EncryptionKeyString.Length > 32)
                    {
                        Log.WriteLine(LogEventType.Warning, "AES key parsed from encryption.ini is too long. It must be 32 bytes, so will be truncated!");
                    }

                    Settings.EncryptionKey.Key = ParseAnsiStringToByteArray(EncryptionKeyString, 32);
                }
            }

            Ini = ConfigCache.ReadHierarchy(ConfigHierarchyType.Crypto, InProjectDirectory, InTargetPlatform);
            string SectionName = "/Script/CryptoKeys.CryptoKeysSettings";
            ConfigHierarchySection CryptoSection = Ini.FindSection(SectionName);

            // If we have new format crypto keys, read them in over the top of the legacy settings
            if (CryptoSection != null && CryptoSection.KeyNames.Count() > 0)
            {
                Ini.GetBool(SectionName, "bEnablePakSigning", out Settings.bEnablePakSigning);
                Ini.GetBool(SectionName, "bEncryptPakIniFiles", out Settings.bEnablePakIniEncryption);
                Ini.GetBool(SectionName, "bEncryptPakIndex", out Settings.bEnablePakIndexEncryption);
                Ini.GetBool(SectionName, "bEncryptUAssetFiles", out Settings.bEnablePakUAssetEncryption);
                Ini.GetBool(SectionName, "bEncryptAllAssetFiles", out Settings.bEnablePakFullAssetEncryption);

                // Parse encryption key
                string EncryptionKeyString;
                Ini.GetString(SectionName, "EncryptionKey", out EncryptionKeyString);
                if (!string.IsNullOrEmpty(EncryptionKeyString))
                {
                    Settings.EncryptionKey      = new EncryptionKey();
                    Settings.EncryptionKey.Key  = System.Convert.FromBase64String(EncryptionKeyString);
                    Settings.EncryptionKey.Guid = Guid.Empty.ToString();
                    Settings.EncryptionKey.Name = "Embedded";
                }

                // Parse secondary encryption keys
                List <EncryptionKey> SecondaryEncryptionKeys = new List <EncryptionKey>();
                List <string>        SecondaryEncryptionKeyStrings;

                if (Ini.GetArray(SectionName, "SecondaryEncryptionKeys", out SecondaryEncryptionKeyStrings))
                {
                    foreach (string KeySource in SecondaryEncryptionKeyStrings)
                    {
                        EncryptionKey NewKey = new EncryptionKey();
                        SecondaryEncryptionKeys.Add(NewKey);

                        Regex Search = new Regex("\\(Guid=(?\'Guid\'.*),Name=\\\"(?\'Name\'.*)\\\",Key=\\\"(?\'Key\'.*)\\\"\\)");
                        Match Match  = Search.Match(KeySource);
                        if (Match.Success)
                        {
                            foreach (string GroupName in Search.GetGroupNames())
                            {
                                string Value = Match.Groups[GroupName].Value;
                                if (GroupName == "Guid")
                                {
                                    NewKey.Guid = Value;
                                }
                                else if (GroupName == "Name")
                                {
                                    NewKey.Name = Value;
                                }
                                else if (GroupName == "Key")
                                {
                                    NewKey.Key = System.Convert.FromBase64String(Value);
                                }
                            }
                        }
                    }
                }

                Settings.SecondaryEncryptionKeys = SecondaryEncryptionKeys.ToArray();

                // Parse signing key
                string PrivateExponent, PublicExponent, Modulus;
                Ini.GetString(SectionName, "SigningPrivateExponent", out PrivateExponent);
                Ini.GetString(SectionName, "SigningModulus", out Modulus);
                Ini.GetString(SectionName, "SigningPublicExponent", out PublicExponent);

                if (!String.IsNullOrEmpty(PrivateExponent) && !String.IsNullOrEmpty(PublicExponent) && !String.IsNullOrEmpty(Modulus))
                {
                    Settings.SigningKey = new SigningKeyPair();
                    Settings.SigningKey.PublicKey.Exponent  = System.Convert.FromBase64String(PublicExponent);
                    Settings.SigningKey.PublicKey.Modulus   = System.Convert.FromBase64String(Modulus);
                    Settings.SigningKey.PrivateKey.Exponent = System.Convert.FromBase64String(PrivateExponent);
                    Settings.SigningKey.PrivateKey.Modulus  = Settings.SigningKey.PublicKey.Modulus;
                }
            }

            // Parse project dynamic keychain keys
            if (InProjectDirectory != null)
            {
                ConfigHierarchy GameIni = ConfigCache.ReadHierarchy(ConfigHierarchyType.Game, InProjectDirectory, InTargetPlatform);
                if (GameIni != null)
                {
                    string Filename;
                    if (GameIni.GetString("ContentEncryption", "ProjectKeyChain", out Filename))
                    {
                        FileReference ProjectKeyChainFile = FileReference.Combine(InProjectDirectory, "Content", Filename);
                        if (FileReference.Exists(ProjectKeyChainFile))
                        {
                            List <EncryptionKey> EncryptionKeys = new List <EncryptionKey>();

                            if (Settings.SecondaryEncryptionKeys != null)
                            {
                                EncryptionKeys.AddRange(Settings.SecondaryEncryptionKeys);
                            }

                            string[] Lines = FileReference.ReadAllLines(ProjectKeyChainFile);
                            foreach (string Line in Lines)
                            {
                                string[] KeyParts = Line.Split(':');
                                if (KeyParts.Length == 4)
                                {
                                    EncryptionKey NewKey = new EncryptionKey();

                                    NewKey.Name = KeyParts[0];
                                    NewKey.Guid = KeyParts[2];
                                    NewKey.Key  = System.Convert.FromBase64String(KeyParts[3]);

                                    EncryptionKey ExistingKey = EncryptionKeys.Find((EncryptionKey OtherKey) => { return(OtherKey.Guid == NewKey.Guid); });
                                    if (ExistingKey != null && !CompareKey(ExistingKey.Key, NewKey.Key))
                                    {
                                        throw new Exception("Found multiple encryption keys with the same guid but different AES keys while merging secondary keys from the project key-chain!");
                                    }

                                    EncryptionKeys.Add(NewKey);
                                }
                            }

                            Settings.SecondaryEncryptionKeys = EncryptionKeys.ToArray();
                        }
                    }
                }
            }

            if (!Settings.bDataCryptoRequired)
            {
                CryptoSettings NewSettings = new CryptoSettings();
                NewSettings.SecondaryEncryptionKeys = Settings.SecondaryEncryptionKeys;
                Settings = NewSettings;
            }
            else
            {
                if (!Settings.PakSigningRequired)
                {
                    Settings.bEnablePakSigning = false;
                    Settings.SigningKey        = null;
                }

                if (!Settings.PakEncryptionRequired)
                {
                    Settings.bEnablePakFullAssetEncryption = false;
                    Settings.bEnablePakIndexEncryption     = false;
                    Settings.bEnablePakIniEncryption       = false;
                    Settings.EncryptionKey = null;
                    Settings.SigningKey    = null;
                }
            }

            // Check if we have a valid signing key that is of the old short form
            if (Settings.SigningKey != null && Settings.SigningKey.IsValid() && Settings.SigningKey.IsUnsecureLegacyKey())
            {
                Log.TraceWarningOnce("Project signing keys found in '{0}' are of the old insecure short format. Please regenerate them using the project crypto settings panel in the editor!", InProjectDirectory);
            }

            // Validate the settings we have read
            if (Settings.bDataCryptoRequired && Settings.bEnablePakSigning && (Settings.SigningKey == null || !Settings.SigningKey.IsValid()))
            {
                Log.TraceWarningOnce("Pak signing is enabled, but no valid signing keys were found. Please generate a key in the editor project crypto settings. Signing will be disabled");
                Settings.bEnablePakSigning = false;
            }

            if (Settings.bDataCryptoRequired && Settings.IsAnyEncryptionEnabled() && (Settings.EncryptionKey == null || !Settings.EncryptionKey.IsValid()))
            {
                Log.TraceWarningOnce("Pak encryption is enabled, but no valid encryption key was found. Please generate a key in the editor project crypto settings. Encryption will be disabled");
                Settings.bEnablePakUAssetEncryption    = false;
                Settings.bEnablePakFullAssetEncryption = false;
                Settings.bEnablePakIndexEncryption     = false;
                Settings.bEnablePakIniEncryption       = false;
            }

            return(Settings);
        }
        /// <summary>
        /// Determine what needs to be built for a target
        /// </summary>
        /// <param name="BuildConfiguration">The build configuration</param>
        /// <param name="TargetDescriptor">Target being built</param>
        /// <param name="Makefile">Makefile generated for this target</param>
        /// <returns>Set of actions to execute</returns>
        static HashSet <Action> GetActionsForTarget(BuildConfiguration BuildConfiguration, TargetDescriptor TargetDescriptor, TargetMakefile Makefile)
        {
            // Create the action graph
            ActionGraph.Link(Makefile.Actions);

            // Get the hot-reload mode
            HotReloadMode HotReloadMode = TargetDescriptor.HotReloadMode;

            if (HotReloadMode == HotReloadMode.Default)
            {
                if (TargetDescriptor.HotReloadModuleNameToSuffix.Count > 0 && TargetDescriptor.ForeignPlugin == null)
                {
                    HotReloadMode = HotReloadMode.FromEditor;
                }
                else if (BuildConfiguration.bAllowHotReloadFromIDE && HotReload.ShouldDoHotReloadFromIDE(BuildConfiguration, TargetDescriptor))
                {
                    HotReloadMode = HotReloadMode.FromIDE;
                }
                else
                {
                    HotReloadMode = HotReloadMode.Disabled;
                }
            }

            // Guard against a live coding session for this target being active
            if (HotReloadMode != HotReloadMode.LiveCoding && TargetDescriptor.ForeignPlugin == null && HotReload.IsLiveCodingSessionActive(Makefile))
            {
                throw new BuildException("Unable to start regular build while Live Coding is active. Press Ctrl+Alt+F11 to trigger a Live Coding compile.");
            }

            // Get the root prerequisite actions
            List <Action> PrerequisiteActions = GatherPrerequisiteActions(TargetDescriptor, Makefile);

            // Get the path to the hot reload state file for this target
            FileReference HotReloadStateFile = global::UnrealBuildTool.HotReloadState.GetLocation(TargetDescriptor.ProjectFile, TargetDescriptor.Name, TargetDescriptor.Platform, TargetDescriptor.Configuration, TargetDescriptor.Architecture);

            // Apply the previous hot reload state
            HotReloadState HotReloadState = null;

            if (HotReloadMode == HotReloadMode.Disabled)
            {
                // Make sure we're not doing a partial build from the editor (eg. compiling a new plugin)
                if (TargetDescriptor.ForeignPlugin == null && TargetDescriptor.SingleFileToCompile == null)
                {
                    // Delete the previous state file
                    HotReload.DeleteTemporaryFiles(HotReloadStateFile);
                }
            }
            else
            {
                // Read the previous state file and apply it to the action graph
                if (FileReference.Exists(HotReloadStateFile))
                {
                    HotReloadState = HotReloadState.Load(HotReloadStateFile);
                }
                else
                {
                    HotReloadState = new HotReloadState();
                }

                // Apply the old state to the makefile
                HotReload.ApplyState(HotReloadState, Makefile);

                // If we want a specific suffix on any modules, apply that now. We'll track the outputs later, but the suffix has to be forced (and is always out of date if it doesn't exist).
                HotReload.PatchActionGraphWithNames(PrerequisiteActions, TargetDescriptor.HotReloadModuleNameToSuffix, Makefile);

                // Re-link the action graph
                ActionGraph.Link(PrerequisiteActions);
            }

            // Create the dependencies cache
            CppDependencyCache CppDependencies;

            using (Timeline.ScopeEvent("Reading dependency cache"))
            {
                CppDependencies = CppDependencyCache.CreateHierarchy(TargetDescriptor.ProjectFile, TargetDescriptor.Name, TargetDescriptor.Platform, TargetDescriptor.Configuration, Makefile.TargetType, TargetDescriptor.Architecture);
            }

            // Create the action history
            ActionHistory History;

            using (Timeline.ScopeEvent("Reading action history"))
            {
                History = ActionHistory.CreateHierarchy(TargetDescriptor.ProjectFile, TargetDescriptor.Name, TargetDescriptor.Platform, Makefile.TargetType, TargetDescriptor.Architecture);
            }

            // Plan the actions to execute for the build. For single file compiles, always rebuild the source file regardless of whether it's out of date.
            HashSet <Action> TargetActionsToExecute;

            if (TargetDescriptor.SingleFileToCompile == null)
            {
                TargetActionsToExecute = ActionGraph.GetActionsToExecute(Makefile.Actions, PrerequisiteActions, CppDependencies, History, BuildConfiguration.bIgnoreOutdatedImportLibraries);
            }
            else
            {
                TargetActionsToExecute = new HashSet <Action>(PrerequisiteActions);
            }

            // Additional processing for hot reload
            if (HotReloadMode == HotReloadMode.LiveCoding)
            {
                // Make sure we're not overwriting any lazy-loaded modules
                if (TargetDescriptor.LiveCodingModules != null)
                {
                    // Read the list of modules that we're allowed to build
                    string[] Lines = FileReference.ReadAllLines(TargetDescriptor.LiveCodingModules);

                    // Parse it out into a set of filenames
                    HashSet <string> AllowedOutputFileNames = new HashSet <string>(FileReference.Comparer);
                    foreach (string Line in Lines)
                    {
                        string TrimLine = Line.Trim();
                        if (TrimLine.Length > 0)
                        {
                            AllowedOutputFileNames.Add(Path.GetFileName(TrimLine));
                        }
                    }

                    // Find all the binaries that we're actually going to build
                    HashSet <FileReference> OutputFiles = new HashSet <FileReference>();
                    foreach (Action Action in TargetActionsToExecute)
                    {
                        if (Action.ActionType == ActionType.Link)
                        {
                            OutputFiles.UnionWith(Action.ProducedItems.Where(x => x.HasExtension(".exe") || x.HasExtension(".dll")).Select(x => x.Location));
                        }
                    }

                    // Find all the files that will be built that aren't allowed
                    List <FileReference> ProtectedOutputFiles = OutputFiles.Where(x => !AllowedOutputFileNames.Contains(x.GetFileName())).ToList();
                    if (ProtectedOutputFiles.Count > 0)
                    {
                        FileReference.WriteAllLines(new FileReference(TargetDescriptor.LiveCodingModules.FullName + ".out"), ProtectedOutputFiles.Select(x => x.ToString()));
                        foreach (FileReference ProtectedOutputFile in ProtectedOutputFiles)
                        {
                            Log.TraceInformation("Module {0} is not currently enabled for Live Coding", ProtectedOutputFile);
                        }
                        throw new CompilationResultException(CompilationResult.Canceled);
                    }
                }

                // Filter the prerequisite actions down to just the compile actions, then recompute all the actions to execute
                PrerequisiteActions    = new List <Action>(TargetActionsToExecute.Where(x => x.ActionType == ActionType.Compile));
                TargetActionsToExecute = ActionGraph.GetActionsToExecute(Makefile.Actions, PrerequisiteActions, CppDependencies, History, BuildConfiguration.bIgnoreOutdatedImportLibraries);

                // Update the action graph with these new paths
                Dictionary <FileReference, FileReference> OriginalFileToPatchedFile = new Dictionary <FileReference, FileReference>();
                HotReload.PatchActionGraphForLiveCoding(PrerequisiteActions, OriginalFileToPatchedFile);

                // Get a new list of actions to execute now that the graph has been modified
                TargetActionsToExecute = ActionGraph.GetActionsToExecute(Makefile.Actions, PrerequisiteActions, CppDependencies, History, BuildConfiguration.bIgnoreOutdatedImportLibraries);

                // Output the Live Coding manifest
                if (TargetDescriptor.LiveCodingManifest != null)
                {
                    HotReload.WriteLiveCodingManifest(TargetDescriptor.LiveCodingManifest, Makefile.Actions, OriginalFileToPatchedFile);
                }
            }
            else if (HotReloadMode == HotReloadMode.FromEditor || HotReloadMode == HotReloadMode.FromIDE)
            {
                // Patch action history for hot reload when running in assembler mode.  In assembler mode, the suffix on the output file will be
                // the same for every invocation on that makefile, but we need a new suffix each time.

                // For all the hot-reloadable modules that may need a unique suffix appended, build a mapping from output item to all the output items in that module. We can't
                // apply a suffix to one without applying a suffix to all of them.
                Dictionary <FileItem, FileItem[]> HotReloadItemToDependentItems = new Dictionary <FileItem, FileItem[]>();
                foreach (string HotReloadModuleName in Makefile.HotReloadModuleNames)
                {
                    int ModuleSuffix;
                    if (!TargetDescriptor.HotReloadModuleNameToSuffix.TryGetValue(HotReloadModuleName, out ModuleSuffix) || ModuleSuffix == -1)
                    {
                        FileItem[] ModuleOutputItems;
                        if (Makefile.ModuleNameToOutputItems.TryGetValue(HotReloadModuleName, out ModuleOutputItems))
                        {
                            foreach (FileItem ModuleOutputItem in ModuleOutputItems)
                            {
                                HotReloadItemToDependentItems[ModuleOutputItem] = ModuleOutputItems;
                            }
                        }
                    }
                }

                // Expand the list of actions to execute to include everything that references any files with a new suffix. Unlike a regular build, we can't ignore
                // dependencies on import libraries under the assumption that a header would change if the API changes, because the dependency will be on a different DLL.
                HashSet <FileItem> FilesRequiringSuffix = new HashSet <FileItem>(TargetActionsToExecute.SelectMany(x => x.ProducedItems).Where(x => HotReloadItemToDependentItems.ContainsKey(x)));
                for (int LastNumFilesWithNewSuffix = 0; FilesRequiringSuffix.Count > LastNumFilesWithNewSuffix;)
                {
                    LastNumFilesWithNewSuffix = FilesRequiringSuffix.Count;
                    foreach (Action PrerequisiteAction in PrerequisiteActions)
                    {
                        if (!TargetActionsToExecute.Contains(PrerequisiteAction))
                        {
                            foreach (FileItem ProducedItem in PrerequisiteAction.ProducedItems)
                            {
                                FileItem[] DependentItems;
                                if (HotReloadItemToDependentItems.TryGetValue(ProducedItem, out DependentItems))
                                {
                                    TargetActionsToExecute.Add(PrerequisiteAction);
                                    FilesRequiringSuffix.UnionWith(DependentItems);
                                }
                            }
                        }
                    }
                }

                // Build a list of file mappings
                Dictionary <FileReference, FileReference> OldLocationToNewLocation = new Dictionary <FileReference, FileReference>();
                foreach (FileItem FileRequiringSuffix in FilesRequiringSuffix)
                {
                    FileReference OldLocation = FileRequiringSuffix.Location;
                    FileReference NewLocation = HotReload.ReplaceSuffix(OldLocation, HotReloadState.NextSuffix);
                    OldLocationToNewLocation[OldLocation] = NewLocation;
                }

                // Update the action graph with these new paths
                HotReload.PatchActionGraph(PrerequisiteActions, OldLocationToNewLocation);

                // Get a new list of actions to execute now that the graph has been modified
                TargetActionsToExecute = ActionGraph.GetActionsToExecute(Makefile.Actions, PrerequisiteActions, CppDependencies, History, BuildConfiguration.bIgnoreOutdatedImportLibraries);

                // Build a mapping of all file items to their original
                Dictionary <FileReference, FileReference> HotReloadFileToOriginalFile = new Dictionary <FileReference, FileReference>();
                foreach (KeyValuePair <FileReference, FileReference> Pair in HotReloadState.OriginalFileToHotReloadFile)
                {
                    HotReloadFileToOriginalFile[Pair.Value] = Pair.Key;
                }
                foreach (KeyValuePair <FileReference, FileReference> Pair in OldLocationToNewLocation)
                {
                    FileReference OriginalLocation;
                    if (!HotReloadFileToOriginalFile.TryGetValue(Pair.Key, out OriginalLocation))
                    {
                        OriginalLocation = Pair.Key;
                    }
                    HotReloadFileToOriginalFile[Pair.Value] = OriginalLocation;
                }

                // Now filter out all the hot reload files and update the state
                foreach (Action Action in TargetActionsToExecute)
                {
                    foreach (FileItem ProducedItem in Action.ProducedItems)
                    {
                        FileReference OriginalLocation;
                        if (HotReloadFileToOriginalFile.TryGetValue(ProducedItem.Location, out OriginalLocation))
                        {
                            HotReloadState.OriginalFileToHotReloadFile[OriginalLocation] = ProducedItem.Location;
                            HotReloadState.TemporaryFiles.Add(ProducedItem.Location);
                        }
                    }
                }

                // Increment the suffix for the next iteration
                if (TargetActionsToExecute.Count > 0)
                {
                    HotReloadState.NextSuffix++;
                }

                // Save the new state
                HotReloadState.Save(HotReloadStateFile);

                // Prevent this target from deploying
                Makefile.bDeployAfterCompile = false;
            }

            return(TargetActionsToExecute);
        }
        /// <summary>
        /// Reads dependencies from the given file.
        /// </summary>
        /// <param name="InputFile">The file to read from</param>
        /// <returns>List of included dependencies</returns>
        static List <FileItem> ReadDependenciesFile(FileReference InputFile)
        {
            if (InputFile.HasExtension(".txt"))
            {
                string[] Lines = FileReference.ReadAllLines(InputFile);

                HashSet <FileItem> DependencyItems = new HashSet <FileItem>();
                foreach (string Line in Lines)
                {
                    if (Line.Length > 0)
                    {
                        // Ignore *.tlh and *.tli files generated by the compiler from COM DLLs
                        if (!Line.EndsWith(".tlh", StringComparison.OrdinalIgnoreCase) && !Line.EndsWith(".tli", StringComparison.OrdinalIgnoreCase))
                        {
                            DependencyItems.Add(FileItem.GetItemByPath(Line));
                        }
                    }
                }
                return(DependencyItems.ToList());
            }
            else if (InputFile.HasExtension(".d"))
            {
                string Text = FileReference.ReadAllText(InputFile);

                List <string> Tokens = new List <string>();

                StringBuilder Token = new StringBuilder();
                for (int Idx = 0; TryReadMakefileToken(Text, ref Idx, Token);)
                {
                    Tokens.Add(Token.ToString());
                }

                int TokenIdx = 0;
                while (TokenIdx < Tokens.Count && Tokens[TokenIdx] == "\n")
                {
                    TokenIdx++;
                }

                if (TokenIdx + 1 >= Tokens.Count || Tokens[TokenIdx + 1] != ":")
                {
                    throw new BuildException("Unable to parse dependency file");
                }

                TokenIdx += 2;

                List <FileItem> NewDependencyFiles = new List <FileItem>();
                for (; TokenIdx < Tokens.Count && Tokens[TokenIdx] != "\n"; TokenIdx++)
                {
                    NewDependencyFiles.Add(FileItem.GetItemByPath(Tokens[TokenIdx]));
                }

                while (TokenIdx < Tokens.Count && Tokens[TokenIdx] == "\n")
                {
                    TokenIdx++;
                }

                if (TokenIdx != Tokens.Count)
                {
                    throw new BuildException("Unable to parse dependency file");
                }

                return(NewDependencyFiles);
            }
            else
            {
                throw new BuildException("Unknown dependency list file type: {0}", InputFile);
            }
        }
Exemplo n.º 9
0
        /// <summary>
        /// Checks to see if the assembly needs compilation
        /// </summary>
        /// <param name="SourceFiles">Set of source files</param>
        /// <param name="AssemblySourceListFilePath">File to use to cache source file names</param>
        /// <param name="OutputAssemblyPath">Output path for the assembly</param>
        /// <returns>True if the assembly needs to be built</returns>
        private static bool RequiresCompilation(HashSet <FileReference> SourceFiles, FileReference AssemblySourceListFilePath, FileReference OutputAssemblyPath)
        {
            // Check to see if we already have a compiled assembly file on disk
            FileItem OutputAssemblyInfo = FileItem.GetItemByFileReference(OutputAssemblyPath);

            if (!OutputAssemblyInfo.Exists)
            {
                Log.TraceLog("Compiling {0}: Assembly does not exist", OutputAssemblyPath);
                return(true);
            }

            // Check the time stamp of the UnrealBuildTool.exe file.  If Unreal Build Tool was compiled more
            // recently than the dynamically-compiled assembly, then we'll always recompile it.  This is
            // because Unreal Build Tool's code may have changed in such a way that invalidate these
            // previously-compiled assembly files.
            FileItem ExecutableItem = FileItem.GetItemByFileReference(UnrealBuildTool.GetUBTPath());

            if (ExecutableItem.LastWriteTimeUtc > OutputAssemblyInfo.LastWriteTimeUtc)
            {
                Log.TraceLog("Compiling {0}: {1} is newer", OutputAssemblyPath, ExecutableItem.Name);
                return(true);
            }

            // Make sure we have a manifest of source files used to compile the output assembly.  If it doesn't exist
            // for some reason (not an expected case) then we'll need to recompile.
            FileItem AssemblySourceListFile = FileItem.GetItemByFileReference(AssemblySourceListFilePath);

            if (!AssemblySourceListFile.Exists)
            {
                Log.TraceLog("Compiling {0}: Missing source file list ({1})", OutputAssemblyPath, AssemblySourceListFilePath);
                return(true);
            }

            // Make sure the source files we're compiling are the same as the source files that were compiled
            // for the assembly that we want to load
            HashSet <FileItem> CurrentSourceFileItems = new HashSet <FileItem>();

            foreach (string Line in FileReference.ReadAllLines(AssemblySourceListFile.Location))
            {
                CurrentSourceFileItems.Add(FileItem.GetItemByPath(Line));
            }

            // Get the new source files
            HashSet <FileItem> SourceFileItems = new HashSet <FileItem>();

            foreach (FileReference SourceFile in SourceFiles)
            {
                SourceFileItems.Add(FileItem.GetItemByFileReference(SourceFile));
            }

            // Check if there are any differences between the sets
            foreach (FileItem CurrentSourceFileItem in CurrentSourceFileItems)
            {
                if (!SourceFileItems.Contains(CurrentSourceFileItem))
                {
                    Log.TraceLog("Compiling {0}: Removed source file ({1})", OutputAssemblyPath, AssemblySourceListFilePath);
                    return(true);
                }
            }
            foreach (FileItem SourceFileItem in SourceFileItems)
            {
                if (!CurrentSourceFileItems.Contains(SourceFileItem))
                {
                    Log.TraceLog("Compiling {0}: Added source file ({1})", OutputAssemblyPath, AssemblySourceListFilePath);
                    return(true);
                }
            }

            // Check if any of the timestamps are newer
            foreach (FileItem SourceFileItem in SourceFileItems)
            {
                if (SourceFileItem.LastWriteTimeUtc > OutputAssemblyInfo.LastWriteTimeUtc)
                {
                    Log.TraceLog("Compiling {0}: {1} is newer", OutputAssemblyPath, SourceFileItem);
                    return(true);
                }
            }

            return(false);
        }
Exemplo n.º 10
0
        public override int Execute(CommandLineArguments Arguments)
        {
            FileReference InputFile = Arguments.GetFileReference("-TimingFile=");

            // If the tracing argument was passed, hand off to the logic to generate a JSON file compatible with
            // chrome://tracing
            if (Arguments.HasOption("-Tracing"))
            {
                ParseTimingDataToTracingFiles(InputFile);
                return(0);
            }

            // Break the input file into the various sections for processing.
            string[]       AllLines    = FileReference.ReadAllLines(InputFile);
            List <string>  Includes    = new List <string>();
            List <string>  Classes     = new List <string>();
            List <string>  Functions   = new List <string>();
            TimingDataType CurrentType = TimingDataType.None;

            foreach (string Line in AllLines)
            {
                if (string.IsNullOrWhiteSpace(Line))
                {
                    continue;
                }

                // Check for a change of type.
                if (Line.StartsWith("Include Headers:", StringComparison.OrdinalIgnoreCase))
                {
                    CurrentType = TimingDataType.Include;
                    continue;
                }
                else if (Line.StartsWith("Class Definitions:", StringComparison.OrdinalIgnoreCase))
                {
                    CurrentType = TimingDataType.Class;
                    continue;
                }
                else if (Line.StartsWith("Function Definitions:", StringComparison.OrdinalIgnoreCase))
                {
                    CurrentType = TimingDataType.Function;
                    continue;
                }

                // Skip the count line, we don't need it.
                if (Regex.IsMatch(Line, @"^\tCount\:\s*\d*$"))
                {
                    continue;
                }

                // If we didn't change types and this isn't the count line and it doesn't match the expected output,
                //  clear the current type and move on.
                Match TimingDataMatch = Regex.Match(Line, TimingDataRegex);
                if (!TimingDataMatch.Success)
                {
                    CurrentType = TimingDataType.None;
                    continue;
                }

                // If we get to here this is a line we want to parse. Add it to the correct collection.
                switch (CurrentType)
                {
                case TimingDataType.Include:
                {
                    Includes.Add(Line);
                    break;
                }

                case TimingDataType.Class:
                {
                    Classes.Add(Line);
                    break;
                }

                case TimingDataType.Function:
                {
                    Functions.Add(Line);
                    break;
                }
                }
            }

            // Build the summary.
            TimingData Summary = new TimingData()
            {
                Name = InputFile.FullName.Replace(".timing.txt", string.Empty), Type = TimingDataType.Summary
            };

            Summary.AddChild(SummarizeParsedTimingData("IncludeTimings", TimingDataType.Include, Includes));
            Summary.AddChild(SummarizeParsedTimingData("ClassTimings", TimingDataType.Class, Classes));
            Summary.AddChild(SummarizeParsedTimingData("FunctionTimings", TimingDataType.Function, Functions));

            // Write out the timing binary file.
            using (BinaryWriter Writer = new BinaryWriter(File.Open(InputFile.ChangeExtension(".timing.bin").FullName, FileMode.Create)))
            {
                Writer.Write(Summary);
            }

            return(0);
        }