/// <summary>
        /// Main entry point
        /// </summary>
        /// <param name="Arguments">Command-line arguments</param>
        /// <returns>One of the values of ECompilationResult</returns>
        public override int Execute(CommandLineArguments Arguments)
        {
            Arguments.ApplyTo(this);

            // Read the XML configuration files
            XmlConfig.ApplyTo(this);

            // Create the build configuration object, and read the settings
            BuildConfiguration BuildConfiguration = new BuildConfiguration();

            XmlConfig.ApplyTo(BuildConfiguration);
            Arguments.ApplyTo(BuildConfiguration);

            // Read the actions file
            List <Action> Actions;

            using (Timeline.ScopeEvent("ActionGraph.ReadActions()"))
            {
                Actions = ActionGraph.ImportJson(ActionsFile);
            }

            // Link the action graph
            using (Timeline.ScopeEvent("ActionGraph.Link()"))
            {
                ActionGraph.Link(Actions);
            }

            // Execute the actions
            using (Timeline.ScopeEvent("ActionGraph.ExecuteActions()"))
            {
                ActionGraph.ExecuteActions(BuildConfiguration, Actions);
            }

            return(0);
        }
        /// <summary>
        /// Builds a list of actions that need to be executed to produce the specified output items.
        /// </summary>
        public static List <Action> GetActionsToExecute(List <Action> Actions, CppDependencyCache CppDependencies, ActionHistory History, bool bIgnoreOutdatedImportLibraries)
        {
            using (Timeline.ScopeEvent("ActionGraph.GetActionsToExecute()"))
            {
                // For all targets, build a set of all actions that are outdated.
                Dictionary <Action, bool> OutdatedActionDictionary = new Dictionary <Action, bool>();
                GatherAllOutdatedActions(Actions, History, OutdatedActionDictionary, CppDependencies, bIgnoreOutdatedImportLibraries);

                // Build a list of actions that are both needed for this target and outdated.
                return(Actions.Where(Action => Action.CommandPath != null && OutdatedActionDictionary[Action]).ToList());
            }
        }
Example #3
0
        /// <summary>
        /// Creates a rules assembly
        /// </summary>
        /// <param name="RootDirectory">The root directory to create rules for</param>
        /// <param name="AssemblyPrefix">A prefix for the assembly file name</param>
        /// <param name="Plugins">List of plugins to include in this assembly</param>
        /// <param name="bReadOnly">Whether the assembly should be marked as installed</param>
        /// <param name="bSkipCompile">Whether to skip compilation for this assembly</param>
        /// <param name="Parent">The parent rules assembly</param>
        /// <returns>New rules assembly</returns>
        private static RulesAssembly CreateEngineOrEnterpriseRulesAssembly(DirectoryReference RootDirectory, string AssemblyPrefix, IReadOnlyList <PluginInfo> Plugins, bool bReadOnly, bool bSkipCompile, RulesAssembly Parent)
        {
            DirectoryReference SourceDirectory   = DirectoryReference.Combine(RootDirectory, "Source");
            DirectoryReference ProgramsDirectory = DirectoryReference.Combine(SourceDirectory, "Programs");

            // Find the shared modules, excluding the programs directory. These are used to create an assembly with the bContainsEngineModules flag set to true.
            List <FileReference> EngineModuleFiles = new List <FileReference>();

            using (Timeline.ScopeEvent("Finding engine modules"))
            {
                foreach (DirectoryReference SubDirectory in DirectoryLookupCache.EnumerateDirectories(SourceDirectory))
                {
                    if (SubDirectory != ProgramsDirectory)
                    {
                        EngineModuleFiles.AddRange(FindAllRulesFiles(SubDirectory, RulesFileType.Module));
                    }
                }
            }

            // Add all the plugin modules too
            Dictionary <FileReference, PluginInfo> ModuleFileToPluginInfo = new Dictionary <FileReference, PluginInfo>();

            using (Timeline.ScopeEvent("Finding plugin modules"))
            {
                FindModuleRulesForPlugins(Plugins, EngineModuleFiles, ModuleFileToPluginInfo);
            }

            // Create the assembly
            FileReference EngineAssemblyFileName = FileReference.Combine(RootDirectory, "Intermediate", "Build", "BuildRules", AssemblyPrefix + "Rules" + FrameworkAssemblyExtension);
            RulesAssembly EngineAssembly         = new RulesAssembly(RootDirectory, Plugins, EngineModuleFiles, new List <FileReference>(), ModuleFileToPluginInfo, EngineAssemblyFileName, bContainsEngineModules: true, bUseBackwardsCompatibleDefaults: false, bReadOnly: bReadOnly, bSkipCompile: bSkipCompile, Parent: Parent);

            // Find all the rules files
            List <FileReference> ProgramModuleFiles;

            using (Timeline.ScopeEvent("Finding program modules"))
            {
                ProgramModuleFiles = new List <FileReference>(FindAllRulesFiles(ProgramsDirectory, RulesFileType.Module));
            }
            List <FileReference> ProgramTargetFiles;

            using (Timeline.ScopeEvent("Finding program targets"))
            {
                ProgramTargetFiles = new List <FileReference>(FindAllRulesFiles(SourceDirectory, RulesFileType.Target));
            }

            // Create a path to the assembly that we'll either load or compile
            FileReference ProgramAssemblyFileName = FileReference.Combine(RootDirectory, "Intermediate", "Build", "BuildRules", AssemblyPrefix + "ProgramRules" + FrameworkAssemblyExtension);
            RulesAssembly ProgramAssembly         = new RulesAssembly(RootDirectory, new List <PluginInfo>().AsReadOnly(), ProgramModuleFiles, ProgramTargetFiles, new Dictionary <FileReference, PluginInfo>(), ProgramAssemblyFileName, bContainsEngineModules: false, bUseBackwardsCompatibleDefaults: false, bReadOnly: bReadOnly, bSkipCompile: bSkipCompile, Parent: EngineAssembly);

            // Return the combined assembly
            return(ProgramAssembly);
        }
        /// <summary>
        /// Constructs a dependency cache. This method is private; call CppDependencyCache.Create() to create a cache hierarchy for a given project.
        /// </summary>
        /// <param name="Location">File to store the cache</param>
        /// <param name="BaseDir">Base directory for files that this cache should store data for</param>
        /// <param name="Parent">The parent cache to use</param>
        private SourceFileMetadataCache(FileReference Location, DirectoryReference BaseDir, SourceFileMetadataCache Parent)
        {
            this.Location      = Location;
            this.BaseDirectory = BaseDir;
            this.Parent        = Parent;

            if (FileReference.Exists(Location))
            {
                using (Timeline.ScopeEvent("Reading source file metadata cache"))
                {
                    Read();
                }
            }
        }
Example #5
0
        /// <summary>
        /// Finds all the UEBuildPlatformFactory types in this assembly and uses them to register all the available platforms
        /// </summary>
        /// <param name="bIncludeNonInstalledPlatforms">Whether to register platforms that are not installed</param>
        /// <param name="bHostPlatformOnly">Only register the host platform</param>
        public static void RegisterPlatforms(bool bIncludeNonInstalledPlatforms, bool bHostPlatformOnly)
        {
            // Initialize the installed platform info
            using (Timeline.ScopeEvent("Initializing InstalledPlatformInfo"))
            {
                InstalledPlatformInfo.Initialize();
            }

            // Find and register all tool chains and build platforms that are present
            Type[] AllTypes;
            using (Timeline.ScopeEvent("Querying types"))
            {
                AllTypes = Assembly.GetExecutingAssembly().GetTypes();
            }

            // register all build platforms first, since they implement SDK-switching logic that can set environment variables
            foreach (Type CheckType in AllTypes)
            {
                if (CheckType.IsClass && !CheckType.IsAbstract)
                {
                    if (CheckType.IsSubclassOf(typeof(UEBuildPlatformFactory)))
                    {
                        Log.TraceVerbose("    Registering build platform: {0}", CheckType.ToString());
                        using (Timeline.ScopeEvent(CheckType.Name))
                        {
                            UEBuildPlatformFactory TempInst = (UEBuildPlatformFactory)Activator.CreateInstance(CheckType);

                            if (bHostPlatformOnly && TempInst.TargetPlatform != BuildHostPlatform.Current.Platform)
                            {
                                continue;
                            }


                            // We need all platforms to be registered when we run -validateplatform command to check SDK status of each
                            if (bIncludeNonInstalledPlatforms || InstalledPlatformInfo.IsValidPlatform(TempInst.TargetPlatform))
                            {
                                TempInst.RegisterBuildPlatforms();
                            }
                        }
                    }
                }
            }
        }
Example #6
0
        /// <summary>
        /// Builds a dictionary containing the actions from AllActions that are outdated by calling
        /// IsActionOutdated.
        /// </summary>
        static void GatherAllOutdatedActions(List <Action> Actions, ActionHistory ActionHistory, Dictionary <Action, bool> OutdatedActions, CppDependencyCache CppDependencies, bool bIgnoreOutdatedImportLibraries)
        {
            using (Timeline.ScopeEvent("Prefetching include dependencies"))
            {
                List <FileItem> Dependencies = new List <FileItem>();
                foreach (Action Action in Actions)
                {
                    if (Action.DependencyListFile != null)
                    {
                        Dependencies.Add(Action.DependencyListFile);
                    }
                }
                Parallel.ForEach(Dependencies, File => { List <FileItem> Temp; CppDependencies.TryGetDependencies(File, out Temp); });
            }

            using (Timeline.ScopeEvent("Cache outdated actions"))
            {
                Parallel.ForEach(Actions, Action => IsActionOutdated(Action, OutdatedActions, ActionHistory, CppDependencies, bIgnoreOutdatedImportLibraries));
            }
        }
Example #7
0
        /// <summary>
        /// Builds a list of actions that need to be executed to produce the specified output items.
        /// </summary>
        public static HashSet <Action> GetActionsToExecute(List <Action> Actions, List <Action> PrerequisiteActions, CppDependencyCache CppDependencies, ActionHistory History, bool bIgnoreOutdatedImportLibraries)
        {
            ITimelineEvent GetActionsToExecuteTimer = Timeline.ScopeEvent("ActionGraph.GetActionsToExecute()");

            // Build a set of all actions needed for this target.
            Dictionary <Action, bool> IsActionOutdatedMap = new Dictionary <Action, bool>();

            foreach (Action Action in PrerequisiteActions)
            {
                IsActionOutdatedMap.Add(Action, true);
            }

            // For all targets, build a set of all actions that are outdated.
            Dictionary <Action, bool> OutdatedActionDictionary = new Dictionary <Action, bool>();

            GatherAllOutdatedActions(Actions, History, OutdatedActionDictionary, CppDependencies, bIgnoreOutdatedImportLibraries);

            // Build a list of actions that are both needed for this target and outdated.
            HashSet <Action> ActionsToExecute = new HashSet <Action>(Actions.Where(Action => Action.CommandPath != null && IsActionOutdatedMap.ContainsKey(Action) && OutdatedActionDictionary[Action]));

            GetActionsToExecuteTimer.Finish();

            return(ActionsToExecute);
        }
Example #8
0
        /// <summary>
        /// Main entry point
        /// </summary>
        /// <param name="Arguments">Command-line arguments</param>
        /// <returns>One of the values of ECompilationResult</returns>
        public override int Execute(CommandLineArguments Arguments)
        {
            Arguments.ApplyTo(this);

            // Initialize the log system, buffering the output until we can create the log file
            StartupTraceListener StartupListener = new StartupTraceListener();

            Trace.Listeners.Add(StartupListener);

            // Write the command line
            Log.TraceLog("Command line: {0}", Environment.CommandLine);

            // Grab the environment.
            UnrealBuildTool.InitialEnvironment = Environment.GetEnvironmentVariables();
            if (UnrealBuildTool.InitialEnvironment.Count < 1)
            {
                throw new BuildException("Environment could not be read");
            }

            // Read the XML configuration files
            XmlConfig.ApplyTo(this);

            // Fixup the log path if it wasn't overridden by a config file
            if (BaseLogFileName == null)
            {
                BaseLogFileName = FileReference.Combine(UnrealBuildTool.EngineProgramSavedDirectory, "UnrealBuildTool", "Log.txt").FullName;
            }

            // Create the log file, and flush the startup listener to it
            if (!Arguments.HasOption("-NoLog") && !Log.HasFileWriter())
            {
                FileReference LogFile = new FileReference(BaseLogFileName);
                foreach (string LogSuffix in Arguments.GetValues("-LogSuffix="))
                {
                    LogFile = LogFile.ChangeExtension(null) + "_" + LogSuffix + LogFile.GetExtension();
                }

                TextWriterTraceListener LogTraceListener = Log.AddFileWriter("DefaultLogTraceListener", LogFile);
                StartupListener.CopyTo(LogTraceListener);
            }
            Trace.Listeners.Remove(StartupListener);

            // Create the build configuration object, and read the settings
            BuildConfiguration BuildConfiguration = new BuildConfiguration();

            XmlConfig.ApplyTo(BuildConfiguration);
            Arguments.ApplyTo(BuildConfiguration);

            // Check the root path length isn't too long
            if (BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Win64 && UnrealBuildTool.RootDirectory.FullName.Length > BuildConfiguration.MaxRootPathLength)
            {
                Log.TraceWarning("Running from a path with a long directory name (\"{0}\" = {1} characters). Root paths shorter than {2} characters are recommended to avoid exceeding maximum path lengths on Windows.", UnrealBuildTool.RootDirectory, UnrealBuildTool.RootDirectory.FullName.Length, BuildConfiguration.MaxRootPathLength);
            }

            // now that we know the available platforms, we can delete other platforms' junk. if we're only building specific modules from the editor, don't touch anything else (it may be in use).
            if (!bIgnoreJunk && !UnrealBuildTool.IsEngineInstalled())
            {
                using (Timeline.ScopeEvent("DeleteJunk()"))
                {
                    JunkDeleter.DeleteJunk();
                }
            }

            // Parse and build the targets
            try
            {
                List <TargetDescriptor> TargetDescriptors;

                // Parse all the target descriptors
                using (Timeline.ScopeEvent("TargetDescriptor.ParseCommandLine()"))
                {
                    TargetDescriptors = TargetDescriptor.ParseCommandLine(Arguments, BuildConfiguration.bUsePrecompiled, BuildConfiguration.bSkipRulesCompile);
                }

                // Hack for specific files compile; don't build the ShaderCompileWorker target that's added to the command line for generated project files
                if (TargetDescriptors.Count >= 2)
                {
                    TargetDescriptors.RemoveAll(x => (x.Name == "ShaderCompileWorker" || x.Name == "LiveCodingConsole") && x.SpecificFilesToCompile.Count > 0);
                }

                // Handle remote builds
                for (int Idx = 0; Idx < TargetDescriptors.Count; ++Idx)
                {
                    TargetDescriptor TargetDesc = TargetDescriptors[Idx];
                    if (RemoteMac.HandlesTargetPlatform(TargetDesc.Platform))
                    {
                        FileReference BaseLogFile   = Log.OutputFile ?? new FileReference(BaseLogFileName);
                        FileReference RemoteLogFile = FileReference.Combine(BaseLogFile.Directory, BaseLogFile.GetFileNameWithoutExtension() + "_Remote.txt");

                        RemoteMac RemoteMac = new RemoteMac(TargetDesc.ProjectFile);
                        if (!RemoteMac.Build(TargetDesc, RemoteLogFile, bSkipPreBuildTargets))
                        {
                            return((int)CompilationResult.Unknown);
                        }

                        TargetDescriptors.RemoveAt(Idx--);
                    }
                }

                // Handle local builds
                if (TargetDescriptors.Count > 0)
                {
                    // Get a set of all the project directories
                    HashSet <DirectoryReference> ProjectDirs = new HashSet <DirectoryReference>();
                    foreach (TargetDescriptor TargetDesc in TargetDescriptors)
                    {
                        if (TargetDesc.ProjectFile != null)
                        {
                            DirectoryReference ProjectDirectory = TargetDesc.ProjectFile.Directory;
                            FileMetadataPrefetch.QueueProjectDirectory(ProjectDirectory);
                            ProjectDirs.Add(ProjectDirectory);
                        }
                    }

                    // Get all the build options
                    BuildOptions Options = BuildOptions.None;
                    if (bSkipBuild)
                    {
                        Options |= BuildOptions.SkipBuild;
                    }
                    if (bXGEExport)
                    {
                        Options |= BuildOptions.XGEExport;
                    }
                    if (bNoEngineChanges)
                    {
                        Options |= BuildOptions.NoEngineChanges;
                    }

                    // Create the working set provider per group.
                    using (ISourceFileWorkingSet WorkingSet = SourceFileWorkingSet.Create(UnrealBuildTool.RootDirectory, ProjectDirs))
                    {
                        Build(TargetDescriptors, BuildConfiguration, WorkingSet, Options, WriteOutdatedActionsFile, bSkipPreBuildTargets);
                    }
                }
            }
            finally
            {
                // Save all the caches
                SourceFileMetadataCache.SaveAll();
                CppDependencyCache.SaveAll();
            }
            return(0);
        }
Example #9
0
        /// <summary>
        /// Creates the makefile for a target. If an existing, valid makefile already exists on disk, loads that instead.
        /// </summary>
        /// <param name="BuildConfiguration">The build configuration</param>
        /// <param name="TargetDescriptor">Target being built</param>
        /// <param name="WorkingSet">Set of source files which are part of the working set</param>
        /// <returns>Makefile for the given target</returns>
        static TargetMakefile CreateMakefile(BuildConfiguration BuildConfiguration, TargetDescriptor TargetDescriptor, ISourceFileWorkingSet WorkingSet)
        {
            // Get the path to the makefile for this target
            FileReference MakefileLocation = null;

            if (BuildConfiguration.bUseUBTMakefiles && TargetDescriptor.SpecificFilesToCompile.Count == 0)
            {
                MakefileLocation = TargetMakefile.GetLocation(TargetDescriptor.ProjectFile, TargetDescriptor.Name, TargetDescriptor.Platform, TargetDescriptor.Architecture, TargetDescriptor.Configuration);
            }

            // Try to load an existing makefile
            TargetMakefile Makefile = null;

            if (MakefileLocation != null)
            {
                using (Timeline.ScopeEvent("TargetMakefile.Load()"))
                {
                    string ReasonNotLoaded;
                    Makefile = TargetMakefile.Load(MakefileLocation, TargetDescriptor.ProjectFile, TargetDescriptor.Platform, TargetDescriptor.AdditionalArguments.GetRawArray(), out ReasonNotLoaded);
                    if (Makefile == null)
                    {
                        Log.TraceInformation("Creating makefile for {0} ({1})", TargetDescriptor.Name, ReasonNotLoaded);
                    }
                }
            }

            // If we have a makefile, execute the pre-build steps and check it's still valid
            bool bHasRunPreBuildScripts = false;

            if (Makefile != null)
            {
                // Execute the scripts. We have to invalidate all cached file info after doing so, because we don't know what may have changed.
                if (Makefile.PreBuildScripts.Length > 0)
                {
                    Utils.ExecuteCustomBuildSteps(Makefile.PreBuildScripts);
                    DirectoryItem.ResetAllCachedInfo_SLOW();
                }

                // Don't run the pre-build steps again, even if we invalidate the makefile.
                bHasRunPreBuildScripts = true;

                // Check that the makefile is still valid
                string Reason;
                if (!TargetMakefile.IsValidForSourceFiles(Makefile, TargetDescriptor.ProjectFile, TargetDescriptor.Platform, WorkingSet, out Reason))
                {
                    Log.TraceInformation("Invalidating makefile for {0} ({1})", TargetDescriptor.Name, Reason);
                    Makefile = null;
                }
            }

            // If we couldn't load a makefile, create a new one
            if (Makefile == null)
            {
                // Create the target
                UEBuildTarget Target;
                using (Timeline.ScopeEvent("UEBuildTarget.Create()"))
                {
                    Target = UEBuildTarget.Create(TargetDescriptor, BuildConfiguration.bSkipRulesCompile, BuildConfiguration.bUsePrecompiled);
                }

                // Create the pre-build scripts
                FileReference[] PreBuildScripts = Target.CreatePreBuildScripts();

                // Execute the pre-build scripts
                if (!bHasRunPreBuildScripts)
                {
                    Utils.ExecuteCustomBuildSteps(PreBuildScripts);
                    bHasRunPreBuildScripts = true;
                }

                // Build the target
                using (Timeline.ScopeEvent("UEBuildTarget.Build()"))
                {
                    const bool bIsAssemblingBuild = true;
                    Makefile = Target.Build(BuildConfiguration, WorkingSet, bIsAssemblingBuild, TargetDescriptor.SpecificFilesToCompile);
                }

                // Save the pre-build scripts onto the makefile
                Makefile.PreBuildScripts = PreBuildScripts;

                // Save the additional command line arguments
                Makefile.AdditionalArguments = TargetDescriptor.AdditionalArguments.GetRawArray();

                // Save the environment variables
                foreach (System.Collections.DictionaryEntry EnvironmentVariable in Environment.GetEnvironmentVariables())
                {
                    Makefile.EnvironmentVariables.Add(Tuple.Create((string)EnvironmentVariable.Key, (string)EnvironmentVariable.Value));
                }

                // Save the makefile for next time
                if (MakefileLocation != null)
                {
                    using (Timeline.ScopeEvent("TargetMakefile.Save()"))
                    {
                        Makefile.Save(MakefileLocation);
                    }
                }
            }
            else
            {
                // Restore the environment variables
                foreach (Tuple <string, string> EnvironmentVariable in Makefile.EnvironmentVariables)
                {
                    Environment.SetEnvironmentVariable(EnvironmentVariable.Item1, EnvironmentVariable.Item2);
                }

                // If the target needs UHT to be run, we'll go ahead and do that now
                if (Makefile.UObjectModules.Count > 0)
                {
                    const bool bIsGatheringBuild  = false;
                    const bool bIsAssemblingBuild = true;

                    FileReference ModuleInfoFileName = FileReference.Combine(Makefile.ProjectIntermediateDirectory, TargetDescriptor.Name + ".uhtmanifest");
                    ExternalExecution.ExecuteHeaderToolIfNecessary(BuildConfiguration, TargetDescriptor.ProjectFile, TargetDescriptor.Name, Makefile.TargetType, Makefile.bHasProjectScriptPlugin, UObjectModules: Makefile.UObjectModules, ModuleInfoFileName: ModuleInfoFileName, bIsGatheringBuild: bIsGatheringBuild, bIsAssemblingBuild: bIsAssemblingBuild, WorkingSet: WorkingSet);
                }
            }
            return(Makefile);
        }
Example #10
0
        /// <summary>
        /// Build a list of targets with a given set of makefiles.
        /// </summary>
        /// <param name="Makefiles">Makefiles created with CreateMakefiles</param>
        /// <param name="TargetDescriptors">Target descriptors</param>
        /// <param name="BuildConfiguration">Current build configuration</param>
        /// <param name="WorkingSet">The source file working set</param>
        /// <param name="Options">Additional options for the build</param>
        /// <param name="WriteOutdatedActionsFile">Files to write the list of outdated actions to (rather than building them)</param>
        /// <returns>Result from the compilation</returns>
        static void Build(TargetMakefile[] Makefiles, List <TargetDescriptor> TargetDescriptors, BuildConfiguration BuildConfiguration, ISourceFileWorkingSet WorkingSet, BuildOptions Options, FileReference WriteOutdatedActionsFile)
        {
            // Export the actions for each target
            for (int TargetIdx = 0; TargetIdx < TargetDescriptors.Count; TargetIdx++)
            {
                TargetDescriptor TargetDescriptor = TargetDescriptors[TargetIdx];
                foreach (FileReference WriteActionFile in TargetDescriptor.WriteActionFiles)
                {
                    Log.TraceInformation("Writing actions to {0}", WriteActionFile);
                    ActionGraph.ExportJson(Makefiles[TargetIdx].Actions, WriteActionFile);
                }
            }

            // Execute the build
            if ((Options & BuildOptions.SkipBuild) == 0)
            {
                // Make sure that none of the actions conflict with any other (producing output files differently, etc...)
                ActionGraph.CheckForConflicts(Makefiles.SelectMany(x => x.Actions));

                // Check we don't exceed the nominal max path length
                using (Timeline.ScopeEvent("ActionGraph.CheckPathLengths"))
                {
                    ActionGraph.CheckPathLengths(BuildConfiguration, Makefiles.SelectMany(x => x.Actions));
                }

                // Clean up any previous hot reload runs, and reapply the current state if it's already active
                for (int TargetIdx = 0; TargetIdx < TargetDescriptors.Count; TargetIdx++)
                {
                    HotReload.Setup(TargetDescriptors[TargetIdx], Makefiles[TargetIdx], BuildConfiguration);
                }

                // Merge the action graphs together
                List <Action> MergedActions;
                if (TargetDescriptors.Count == 1)
                {
                    MergedActions = new List <Action>(Makefiles[0].Actions);
                }
                else
                {
                    MergedActions = MergeActionGraphs(TargetDescriptors, Makefiles);
                }

                // Gather all the prerequisite actions that are part of the targets
                HashSet <FileItem> MergedOutputItems = new HashSet <FileItem>();
                for (int TargetIdx = 0; TargetIdx < TargetDescriptors.Count; TargetIdx++)
                {
                    GatherOutputItems(TargetDescriptors[TargetIdx], Makefiles[TargetIdx], MergedOutputItems);
                }

                // Link all the actions together
                ActionGraph.Link(MergedActions);

                // Get all the actions that are prerequisites for these targets. This forms the list of actions that we want executed.
                List <Action> PrerequisiteActions = ActionGraph.GatherPrerequisiteActions(MergedActions, MergedOutputItems);

                // Create the action history
                ActionHistory History = new ActionHistory();
                for (int TargetIdx = 0; TargetIdx < TargetDescriptors.Count; TargetIdx++)
                {
                    using (Timeline.ScopeEvent("Reading action history"))
                    {
                        TargetDescriptor TargetDescriptor = TargetDescriptors[TargetIdx];
                        if (TargetDescriptor.ProjectFile != null)
                        {
                            History.Mount(TargetDescriptor.ProjectFile.Directory);
                        }
                    }
                }

                // Figure out which actions need to be built
                Dictionary <Action, bool> ActionToOutdatedFlag = new Dictionary <Action, bool>();
                for (int TargetIdx = 0; TargetIdx < TargetDescriptors.Count; TargetIdx++)
                {
                    TargetDescriptor TargetDescriptor = TargetDescriptors[TargetIdx];

                    // Create the dependencies cache
                    CppDependencyCache CppDependencies;
                    using (Timeline.ScopeEvent("Reading dependency cache"))
                    {
                        CppDependencies = CppDependencyCache.CreateHierarchy(TargetDescriptor.ProjectFile, TargetDescriptor.Name, TargetDescriptor.Platform, TargetDescriptor.Configuration, Makefiles[TargetIdx].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.
                    if (TargetDescriptor.SpecificFilesToCompile.Count == 0)
                    {
                        ActionGraph.GatherAllOutdatedActions(PrerequisiteActions, History, ActionToOutdatedFlag, CppDependencies, BuildConfiguration.bIgnoreOutdatedImportLibraries);
                    }
                    else
                    {
                        foreach (FileReference SpecificFile in TargetDescriptor.SpecificFilesToCompile)
                        {
                            foreach (Action PrerequisiteAction in PrerequisiteActions.Where(x => x.PrerequisiteItems.Any(y => y.Location == SpecificFile)))
                            {
                                ActionToOutdatedFlag[PrerequisiteAction] = true;
                            }
                        }
                    }
                }

                // Link the action graph again to sort it
                List <Action> MergedActionsToExecute = ActionToOutdatedFlag.Where(x => x.Value).Select(x => x.Key).ToList();
                ActionGraph.Link(MergedActionsToExecute);

                // Allow hot reload to override the actions
                int HotReloadTargetIdx = -1;
                for (int Idx = 0; Idx < TargetDescriptors.Count; Idx++)
                {
                    if (TargetDescriptors[Idx].HotReloadMode != HotReloadMode.Disabled)
                    {
                        if (HotReloadTargetIdx != -1)
                        {
                            throw new BuildException("Unable to perform hot reload with multiple targets.");
                        }
                        else
                        {
                            MergedActionsToExecute = HotReload.PatchActionsForTarget(BuildConfiguration, TargetDescriptors[Idx], Makefiles[Idx], PrerequisiteActions, MergedActionsToExecute);
                        }
                        HotReloadTargetIdx = Idx;
                    }
                }

                // Make sure we're not modifying any engine files
                if ((Options & BuildOptions.NoEngineChanges) != 0)
                {
                    List <FileItem> EngineChanges = MergedActionsToExecute.SelectMany(x => x.ProducedItems).Where(x => x.Location.IsUnderDirectory(UnrealBuildTool.EngineDirectory)).Distinct().OrderBy(x => x.FullName).ToList();
                    if (EngineChanges.Count > 0)
                    {
                        StringBuilder Result = new StringBuilder("Building would modify the following engine files:\n");
                        foreach (FileItem EngineChange in EngineChanges)
                        {
                            Result.AppendFormat("\n{0}", EngineChange.FullName);
                        }
                        Result.Append("\n\nPlease rebuild from an IDE instead.");
                        Log.TraceError("{0}", Result.ToString());
                        throw new CompilationResultException(CompilationResult.FailedDueToEngineChange);
                    }
                }

                // Make sure the appropriate executor is selected
                foreach (TargetDescriptor TargetDescriptor in TargetDescriptors)
                {
                    UEBuildPlatform BuildPlatform = UEBuildPlatform.GetBuildPlatform(TargetDescriptor.Platform);
                    BuildConfiguration.bAllowXGE    &= BuildPlatform.CanUseXGE();
                    BuildConfiguration.bAllowDistcc &= BuildPlatform.CanUseDistcc();
                    BuildConfiguration.bAllowSNDBS  &= BuildPlatform.CanUseSNDBS();
                }

                // Delete produced items that are outdated.
                ActionGraph.DeleteOutdatedProducedItems(MergedActionsToExecute);

                // Save all the action histories now that files have been removed. We have to do this after deleting produced items to ensure that any
                // items created during the build don't have the wrong command line.
                History.Save();

                // Create directories for the outdated produced items.
                ActionGraph.CreateDirectoriesForProducedItems(MergedActionsToExecute);

                // Execute the actions
                if ((Options & BuildOptions.XGEExport) != 0)
                {
                    OutputToolchainInfo(TargetDescriptors, Makefiles);

                    // Just export to an XML file
                    using (Timeline.ScopeEvent("XGE.ExportActions()"))
                    {
                        XGE.ExportActions(MergedActionsToExecute);
                    }
                }
                else if (WriteOutdatedActionsFile != null)
                {
                    OutputToolchainInfo(TargetDescriptors, Makefiles);

                    // Write actions to an output file
                    using (Timeline.ScopeEvent("ActionGraph.WriteActions"))
                    {
                        ActionGraph.ExportJson(MergedActionsToExecute, WriteOutdatedActionsFile);
                    }
                }
                else
                {
                    // Execute the actions
                    if (MergedActionsToExecute.Count == 0)
                    {
                        if (TargetDescriptors.Any(x => !x.bQuiet))
                        {
                            Log.TraceInformation((TargetDescriptors.Count == 1)? "Target is up to date" : "Targets are up to date");
                        }
                    }
                    else
                    {
                        if (TargetDescriptors.Any(x => !x.bQuiet))
                        {
                            Log.TraceInformation("Building {0}...", StringUtils.FormatList(TargetDescriptors.Select(x => x.Name).Distinct()));
                        }

                        OutputToolchainInfo(TargetDescriptors, Makefiles);

                        using (Timeline.ScopeEvent("ActionGraph.ExecuteActions()"))
                        {
                            ActionGraph.ExecuteActions(BuildConfiguration, MergedActionsToExecute);
                        }
                    }

                    // Run the deployment steps
                    foreach (TargetMakefile Makefile in Makefiles)
                    {
                        if (Makefile.bDeployAfterCompile)
                        {
                            TargetReceipt Receipt = TargetReceipt.Read(Makefile.ReceiptFile);
                            Log.TraceInformation("Deploying {0} {1} {2}...", Receipt.TargetName, Receipt.Platform, Receipt.Configuration);

                            UEBuildPlatform.GetBuildPlatform(Receipt.Platform).Deploy(Receipt);
                        }
                    }
                }
            }
        }
Example #11
0
        /// <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;
                }
            }

            // 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);
            }

            // Create the action history
            ActionHistory History;

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

            // 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)
            {
                // 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);
            }
            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);
        }
Example #12
0
        /// <summary>
        /// Build a list of targets
        /// </summary>
        /// <param name="TargetDescriptors">Target descriptors</param>
        /// <param name="BuildConfiguration">Current build configuration</param>
        /// <param name="WorkingSet">The source file working set</param>
        /// <param name="Options">Additional options for the build</param>
        /// <param name="LiveCodingManifest">Path to write the live coding manifest to</param>
        /// <param name="WriteOutdatedActionsFile">Files to write the list of outdated actions to (rather than building them)</param>
        /// <returns>Result from the compilation</returns>
        public static void Build(List <TargetDescriptor> TargetDescriptors, BuildConfiguration BuildConfiguration, ISourceFileWorkingSet WorkingSet, BuildOptions Options, FileReference LiveCodingManifest, FileReference WriteOutdatedActionsFile)
        {
            // Create a makefile for each target
            TargetMakefile[] Makefiles = new TargetMakefile[TargetDescriptors.Count];
            for (int TargetIdx = 0; TargetIdx < TargetDescriptors.Count; TargetIdx++)
            {
                Makefiles[TargetIdx] = CreateMakefile(BuildConfiguration, TargetDescriptors[TargetIdx], WorkingSet);
            }

            // Output the Live Coding manifest
            if (LiveCodingManifest != null)
            {
                List <Action> AllActions = Makefiles.SelectMany(x => x.Actions).ToList();
                HotReload.WriteLiveCodingManifest(LiveCodingManifest, AllActions);
            }

            // Export the actions for each target
            for (int TargetIdx = 0; TargetIdx < TargetDescriptors.Count; TargetIdx++)
            {
                TargetDescriptor TargetDescriptor = TargetDescriptors[TargetIdx];
                foreach (FileReference WriteActionFile in TargetDescriptor.WriteActionFiles)
                {
                    Log.TraceInformation("Writing actions to {0}", WriteActionFile);
                    ActionGraph.ExportJson(Makefiles[TargetIdx].Actions, WriteActionFile);
                }
            }

            // Execute the build
            if ((Options & BuildOptions.SkipBuild) == 0)
            {
                // Make sure that none of the actions conflict with any other (producing output files differently, etc...)
                ActionGraph.CheckForConflicts(Makefiles.SelectMany(x => x.Actions));

                // Find all the actions to be executed
                HashSet <Action>[] ActionsToExecute = new HashSet <Action> [TargetDescriptors.Count];
                for (int TargetIdx = 0; TargetIdx < TargetDescriptors.Count; TargetIdx++)
                {
                    ActionsToExecute[TargetIdx] = GetActionsForTarget(BuildConfiguration, TargetDescriptors[TargetIdx], Makefiles[TargetIdx]);
                }

                // If there are multiple targets being built, merge the actions together
                List <Action> MergedActionsToExecute;
                if (TargetDescriptors.Count == 1)
                {
                    MergedActionsToExecute = new List <Action>(ActionsToExecute[0]);
                }
                else
                {
                    MergedActionsToExecute = MergeActionGraphs(TargetDescriptors, ActionsToExecute);
                }

                // Link all the actions together
                ActionGraph.Link(MergedActionsToExecute);

                // Make sure the appropriate executor is selected
                foreach (TargetDescriptor TargetDescriptor in TargetDescriptors)
                {
                    UEBuildPlatform BuildPlatform = UEBuildPlatform.GetBuildPlatform(TargetDescriptor.Platform);
                    BuildConfiguration.bAllowXGE    &= BuildPlatform.CanUseXGE();
                    BuildConfiguration.bAllowDistcc &= BuildPlatform.CanUseDistcc();
                    BuildConfiguration.bAllowSNDBS  &= BuildPlatform.CanUseSNDBS();
                }

                // Delete produced items that are outdated.
                ActionGraph.DeleteOutdatedProducedItems(MergedActionsToExecute);

                // Save all the action histories now that files have been removed. We have to do this after deleting produced items to ensure that any
                // items created during the build don't have the wrong command line.
                ActionHistory.SaveAll();

                // Create directories for the outdated produced items.
                ActionGraph.CreateDirectoriesForProducedItems(MergedActionsToExecute);

                // Execute the actions
                if ((Options & BuildOptions.XGEExport) != 0)
                {
                    // Just export to an XML file
                    using (Timeline.ScopeEvent("XGE.ExportActions()"))
                    {
                        XGE.ExportActions(MergedActionsToExecute);
                    }
                }
                else if (WriteOutdatedActionsFile != null)
                {
                    // Write actions to an output file
                    using (Timeline.ScopeEvent("ActionGraph.WriteActions"))
                    {
                        ActionGraph.ExportJson(MergedActionsToExecute, WriteOutdatedActionsFile);
                    }
                }
                else
                {
                    // Execute the actions
                    if (MergedActionsToExecute.Count == 0)
                    {
                        if ((Options & BuildOptions.Quiet) == 0)
                        {
                            Log.TraceInformation((TargetDescriptors.Count == 1)? "Target is up to date" : "Targets are up to date");
                        }
                    }
                    else
                    {
                        if ((Options & BuildOptions.Quiet) != 0)
                        {
                            Log.TraceInformation("Building {0}...", StringUtils.FormatList(TargetDescriptors.Select(x => x.Name).Distinct()));
                        }

                        OutputToolchainInfo(TargetDescriptors, Makefiles);

                        using (Timeline.ScopeEvent("ActionGraph.ExecuteActions()"))
                        {
                            ActionGraph.ExecuteActions(BuildConfiguration, MergedActionsToExecute);
                        }
                    }

                    // Run the deployment steps
                    foreach (TargetMakefile Makefile in Makefiles)
                    {
                        if (Makefile.bDeployAfterCompile)
                        {
                            TargetReceipt Receipt = TargetReceipt.Read(Makefile.ReceiptFile);
                            Log.TraceInformation("Deploying {0} {1} {2}...", Receipt.TargetName, Receipt.Platform, Receipt.Configuration);
                            UEBuildPlatform.GetBuildPlatform(Receipt.Platform).Deploy(Receipt);
                        }
                    }
                }
            }
        }
Example #13
0
        /// <summary>
        /// Dynamically compiles an assembly for the specified source file and loads that assembly into the application's
        /// current domain.  If an assembly has already been compiled and is not out of date, then it will be loaded and
        /// no compilation is necessary.
        /// </summary>
        /// <param name="OutputAssemblyPath">Full path to the assembly to be created</param>
        /// <param name="SourceFileNames">List of source file name</param>
        /// <param name="ReferencedAssembies"></param>
        /// <param name="PreprocessorDefines"></param>
        /// <param name="DoNotCompile"></param>
        /// <param name="TreatWarningsAsErrors"></param>
        /// <returns>The assembly that was loaded</returns>
        public static Assembly CompileAndLoadAssembly(FileReference OutputAssemblyPath, HashSet <FileReference> SourceFileNames, List <string> ReferencedAssembies = null, List <string> PreprocessorDefines = null, bool DoNotCompile = false, bool TreatWarningsAsErrors = false)
        {
            // Check to see if the resulting assembly is compiled and up to date
            FileReference AssemblyManifestFilePath = FileReference.Combine(OutputAssemblyPath.Directory, Path.GetFileNameWithoutExtension(OutputAssemblyPath.FullName) + "Manifest.json");

            bool bNeedsCompilation = false;

            if (!DoNotCompile)
            {
                bNeedsCompilation = RequiresCompilation(SourceFileNames, AssemblyManifestFilePath, OutputAssemblyPath);
            }

            // Load the assembly to ensure it is correct
            Assembly CompiledAssembly = null;

            if (!bNeedsCompilation)
            {
                try
                {
                    // Load the previously-compiled assembly from disk
                    CompiledAssembly = Assembly.LoadFile(OutputAssemblyPath.FullName);
                }
                catch (FileLoadException Ex)
                {
                    Log.TraceInformation(String.Format("Unable to load the previously-compiled assembly file '{0}'.  Unreal Build Tool will try to recompile this assembly now.  (Exception: {1})", OutputAssemblyPath, Ex.Message));
                    bNeedsCompilation = true;
                }
                catch (BadImageFormatException Ex)
                {
                    Log.TraceInformation(String.Format("Compiled assembly file '{0}' appears to be for a newer CLR version or is otherwise invalid.  Unreal Build Tool will try to recompile this assembly now.  (Exception: {1})", OutputAssemblyPath, Ex.Message));
                    bNeedsCompilation = true;
                }
                catch (FileNotFoundException)
                {
                    throw new BuildException("Precompiled rules assembly '{0}' does not exist.", OutputAssemblyPath);
                }
                catch (Exception Ex)
                {
                    throw new BuildException(Ex, "Error while loading previously-compiled assembly file '{0}'.  (Exception: {1})", OutputAssemblyPath, Ex.Message);
                }
            }

            // Compile the assembly if me
            if (bNeedsCompilation)
            {
                using (Timeline.ScopeEvent(String.Format("Compiling rules assembly ({0})", OutputAssemblyPath.GetFileName())))
                {
                    CompiledAssembly = CompileAssembly(OutputAssemblyPath, SourceFileNames, ReferencedAssembies, PreprocessorDefines, TreatWarningsAsErrors);
                }

                using (JsonWriter Writer = new JsonWriter(AssemblyManifestFilePath))
                {
                    ReadOnlyBuildVersion Version = ReadOnlyBuildVersion.Current;

                    Writer.WriteObjectStart();
                    // Save out a list of all the source files we compiled.  This is so that we can tell if whole files were added or removed
                    // since the previous time we compiled the assembly.  In that case, we'll always want to recompile it!
                    Writer.WriteStringArrayField("SourceFiles", SourceFileNames.Select(x => x.FullName));
                    Writer.WriteValue("EngineVersion", FormatVersionNumber(Version));
                    Writer.WriteObjectEnd();
                }
            }

#if !NET_CORE
            // Load the assembly into our app domain
            try
            {
                AppDomain.CurrentDomain.Load(CompiledAssembly.GetName());
            }
            catch (Exception Ex)
            {
                throw new BuildException(Ex, "Unable to load the compiled build assembly '{0}' into our application's domain.  (Exception: {1})", OutputAssemblyPath, Ex.Message);
            }
#endif

            return(CompiledAssembly);
        }
Example #14
0
        /// <summary>
        /// Creates a rules assembly
        /// </summary>
        /// <param name="Scope">Scope for items created from this assembly</param>
        /// <param name="RootDirectories">The root directories to create rules for</param>
        /// <param name="AssemblyPrefix">A prefix for the assembly file name</param>
        /// <param name="Plugins">List of plugins to include in this assembly</param>
        /// <param name="bReadOnly">Whether the assembly should be marked as installed</param>
        /// <param name="bSkipCompile">Whether to skip compilation for this assembly</param>
        /// <param name="Parent">The parent rules assembly</param>
        /// <returns>New rules assembly</returns>
        private static RulesAssembly CreateEngineOrEnterpriseRulesAssembly(RulesScope Scope, List <DirectoryReference> RootDirectories, string AssemblyPrefix, IReadOnlyList <PluginInfo> Plugins, bool bReadOnly, bool bSkipCompile, RulesAssembly Parent)
        {
            // Scope hierarchy
            RulesScope PluginsScope  = new RulesScope(Scope.Name + " Plugins", Scope);
            RulesScope ProgramsScope = new RulesScope(Scope.Name + " Programs", PluginsScope);

            // Find the shared modules, excluding the programs directory. These are used to create an assembly with the bContainsEngineModules flag set to true.
            Dictionary <FileReference, ModuleRulesContext> ModuleFileToContext = new Dictionary <FileReference, ModuleRulesContext>();
            ModuleRulesContext DefaultModuleContext = new ModuleRulesContext(Scope, RootDirectories[0]);

            foreach (DirectoryReference RootDirectory in RootDirectories)
            {
                using (Timeline.ScopeEvent("Finding engine modules"))
                {
                    DirectoryReference SourceDirectory = DirectoryReference.Combine(RootDirectory, "Source");

                    AddEngineModuleRulesWithContext(SourceDirectory, "Runtime", DefaultModuleContext, UHTModuleType.EngineRuntime, ModuleFileToContext);
                    AddEngineModuleRulesWithContext(SourceDirectory, "Developer", DefaultModuleContext, UHTModuleType.EngineDeveloper, ModuleFileToContext);
                    AddEngineModuleRulesWithContext(SourceDirectory, "Editor", DefaultModuleContext, UHTModuleType.EngineEditor, ModuleFileToContext);
                    AddEngineModuleRulesWithContext(SourceDirectory, "ThirdParty", DefaultModuleContext, UHTModuleType.EngineThirdParty, ModuleFileToContext);
                }
            }

            // Add all the plugin modules too (don't need to loop over RootDirectories since the plugins come in already found
            using (Timeline.ScopeEvent("Finding plugin modules"))
            {
                ModuleRulesContext PluginsModuleContext = new ModuleRulesContext(PluginsScope, RootDirectories[0]);
                FindModuleRulesForPlugins(Plugins, PluginsModuleContext, ModuleFileToContext);
            }

            // Get the path to store any generated assemblies
            DirectoryReference AssemblyDir = RootDirectories[0];

            if (UnrealBuildTool.IsFileInstalled(FileReference.Combine(AssemblyDir, AssemblyPrefix)))
            {
                DirectoryReference UserDir = Utils.GetUserSettingDirectory();
                if (UserDir != null)
                {
                    ReadOnlyBuildVersion Version = ReadOnlyBuildVersion.Current;
                    AssemblyDir = DirectoryReference.Combine(UserDir, "UnrealEngine", String.Format("{0}.{1}", Version.MajorVersion, Version.MinorVersion));
                }
            }

            // Create the assembly
            FileReference EngineAssemblyFileName = FileReference.Combine(AssemblyDir, "Intermediate", "Build", "BuildRules", AssemblyPrefix + "Rules" + FrameworkAssemblyExtension);
            RulesAssembly EngineAssembly         = new RulesAssembly(Scope, RootDirectories, Plugins, ModuleFileToContext, new List <FileReference>(), EngineAssemblyFileName, bContainsEngineModules: true, DefaultBuildSettings: BuildSettingsVersion.Latest, bReadOnly: bReadOnly, bSkipCompile: bSkipCompile, Parent: Parent);

            List <FileReference> ProgramTargetFiles = new List <FileReference>();
            Dictionary <FileReference, ModuleRulesContext> ProgramModuleFiles = new Dictionary <FileReference, ModuleRulesContext>();

            foreach (DirectoryReference RootDirectory in RootDirectories)
            {
                DirectoryReference SourceDirectory   = DirectoryReference.Combine(RootDirectory, "Source");
                DirectoryReference ProgramsDirectory = DirectoryReference.Combine(SourceDirectory, "Programs");

                // Also create a scope for them, and update the UHT module type
                ModuleRulesContext ProgramsModuleContext = new ModuleRulesContext(ProgramsScope, RootDirectory);
                ProgramsModuleContext.DefaultUHTModuleType = UHTModuleType.Program;

                using (Timeline.ScopeEvent("Finding program modules"))
                {
                    // Find all the rules files
                    AddModuleRulesWithContext(ProgramsDirectory, ProgramsModuleContext, ProgramModuleFiles);
                }

                using (Timeline.ScopeEvent("Finding program targets"))
                {
                    ProgramTargetFiles.AddRange(FindAllRulesFiles(SourceDirectory, RulesFileType.Target));
                }
            }

            // Create a path to the assembly that we'll either load or compile
            FileReference ProgramAssemblyFileName = FileReference.Combine(AssemblyDir, "Intermediate", "Build", "BuildRules", AssemblyPrefix + "ProgramRules" + FrameworkAssemblyExtension);
            RulesAssembly ProgramAssembly         = new RulesAssembly(ProgramsScope, RootDirectories, new List <PluginInfo>().AsReadOnly(), ProgramModuleFiles, ProgramTargetFiles, ProgramAssemblyFileName, bContainsEngineModules: false, DefaultBuildSettings: BuildSettingsVersion.Latest, bReadOnly: bReadOnly, bSkipCompile: bSkipCompile, Parent: EngineAssembly);

            // Return the combined assembly
            return(ProgramAssembly);
        }
Example #15
0
        /// <summary>
        /// Main entry point
        /// </summary>
        /// <param name="Arguments">Command-line arguments</param>
        /// <returns>One of the values of ECompilationResult</returns>
        public override int Execute(CommandLineArguments Arguments)
        {
            Arguments.ApplyTo(this);

            // Initialize the log system, buffering the output until we can create the log file
            StartupTraceListener StartupListener = new StartupTraceListener();

            Trace.Listeners.Add(StartupListener);

            // Write the command line
            Log.TraceLog("Command line: {0}", Environment.CommandLine);

            // Grab the environment.
            UnrealBuildTool.InitialEnvironment = Environment.GetEnvironmentVariables();
            if (UnrealBuildTool.InitialEnvironment.Count < 1)
            {
                throw new BuildException("Environment could not be read");
            }

            // Read the XML configuration files
            XmlConfig.ApplyTo(this);

            // Create the log file, and flush the startup listener to it
            if (!Arguments.HasOption("-NoLog") && !Log.HasFileWriter())
            {
                FileReference LogFile = new FileReference(BaseLogFileName);
                foreach (string LogSuffix in Arguments.GetValues("-LogSuffix="))
                {
                    LogFile = LogFile.ChangeExtension(null) + "_" + LogSuffix + LogFile.GetExtension();
                }

                TextWriterTraceListener LogTraceListener = Log.AddFileWriter("DefaultLogTraceListener", LogFile);
                StartupListener.CopyTo(LogTraceListener);
            }
            Trace.Listeners.Remove(StartupListener);

            // Create the build configuration object, and read the settings
            BuildConfiguration BuildConfiguration = new BuildConfiguration();

            XmlConfig.ApplyTo(BuildConfiguration);
            Arguments.ApplyTo(BuildConfiguration);

            // now that we know the available platforms, we can delete other platforms' junk. if we're only building specific modules from the editor, don't touch anything else (it may be in use).
            if (!bIgnoreJunk && !UnrealBuildTool.IsEngineInstalled())
            {
                using (Timeline.ScopeEvent("DeleteJunk()"))
                {
                    JunkDeleter.DeleteJunk();
                }
            }

            // Parse and build the targets
            try
            {
                // Parse all the target descriptors
                List <TargetDescriptor> TargetDescriptors;
                using (Timeline.ScopeEvent("TargetDescriptor.ParseCommandLine()"))
                {
                    TargetDescriptors = TargetDescriptor.ParseCommandLine(Arguments, BuildConfiguration.bUsePrecompiled, BuildConfiguration.bSkipRulesCompile);
                }

                // Hack for single file compile; don't build the ShaderCompileWorker target that's added to the command line for generated project files
                if (TargetDescriptors.Count >= 2)
                {
                    TargetDescriptors.RemoveAll(x => x.Name == "ShaderCompileWorker" && x.SingleFileToCompile != null);
                }

                // Handle remote builds
                for (int Idx = 0; Idx < TargetDescriptors.Count; Idx++)
                {
                    TargetDescriptor TargetDesc = TargetDescriptors[Idx];
                    if (RemoteMac.HandlesTargetPlatform(TargetDesc.Platform))
                    {
                        FileReference BaseLogFile   = Log.OutputFile ?? new FileReference(BaseLogFileName);
                        FileReference RemoteLogFile = FileReference.Combine(BaseLogFile.Directory, BaseLogFile.GetFileNameWithoutExtension() + "_Remote.txt");

                        RemoteMac RemoteMac = new RemoteMac(TargetDesc.ProjectFile);
                        if (!RemoteMac.Build(TargetDesc, RemoteLogFile))
                        {
                            return((int)CompilationResult.Unknown);
                        }

                        TargetDescriptors.RemoveAt(Idx--);
                    }
                }

                // Handle local builds
                if (TargetDescriptors.Count > 0)
                {
                    // Get a set of all the project directories
                    HashSet <DirectoryReference> ProjectDirs = new HashSet <DirectoryReference>();
                    foreach (TargetDescriptor TargetDesc in TargetDescriptors)
                    {
                        if (TargetDesc.ProjectFile != null)
                        {
                            DirectoryReference ProjectDirectory = TargetDesc.ProjectFile.Directory;
                            FileMetadataPrefetch.QueueProjectDirectory(ProjectDirectory);
                            ProjectDirs.Add(ProjectDirectory);
                        }
                    }

                    // Get all the build options
                    BuildOptions Options = BuildOptions.None;
                    if (bSkipBuild)
                    {
                        Options |= BuildOptions.SkipBuild;
                    }
                    if (bXGEExport)
                    {
                        Options |= BuildOptions.XGEExport;
                    }

                    // Create the working set provider
                    using (ISourceFileWorkingSet WorkingSet = SourceFileWorkingSet.Create(UnrealBuildTool.RootDirectory, ProjectDirs))
                    {
                        Build(TargetDescriptors, BuildConfiguration, WorkingSet, Options);
                    }
                }
            }
            finally
            {
                // Save all the caches
                SourceFileMetadataCache.SaveAll();
                CppDependencyCache.SaveAll();
            }
            return(0);
        }
Example #16
0
        /// <summary>
        /// Checks if the makefile is valid for the current set of source files. This is done separately to the Load() method to allow pre-build steps to modify source files.
        /// </summary>
        /// <param name="Makefile">The makefile that has been loaded</param>
        /// <param name="ProjectFile">Path to the project file</param>
        /// <param name="WorkingSet">The current working set of source files</param>
        /// <param name="ReasonNotLoaded">If the makefile is not valid, is set to a message describing why</param>
        /// <returns>True if the makefile is valid, false otherwise</returns>
        public static bool IsValidForSourceFiles(TargetMakefile Makefile, FileReference ProjectFile, ISourceFileWorkingSet WorkingSet, out string ReasonNotLoaded)
        {
            using (Timeline.ScopeEvent("TargetMakefile.IsValidForSourceFiles()"))
            {
                // Check if any source files have been added or removed
                foreach (KeyValuePair <DirectoryItem, FileItem[]> Pair in Makefile.DirectoryToSourceFiles)
                {
                    DirectoryItem InputDirectory = Pair.Key;
                    if (!InputDirectory.Exists || InputDirectory.LastWriteTimeUtc > Makefile.CreateTimeUtc)
                    {
                        FileItem[] SourceFiles = UEBuildModuleCPP.GetSourceFiles(InputDirectory);
                        if (SourceFiles.Length < Pair.Value.Length)
                        {
                            ReasonNotLoaded = "source file removed";
                            return(false);
                        }
                        else if (SourceFiles.Length > Pair.Value.Length)
                        {
                            ReasonNotLoaded = "source file added";
                            return(false);
                        }
                        else if (SourceFiles.Intersect(Pair.Value).Count() != SourceFiles.Length)
                        {
                            ReasonNotLoaded = "source file modified";
                            return(false);
                        }
                    }
                }

                // Check if any of the additional dependencies has changed
                foreach (FileItem AdditionalDependency in Makefile.AdditionalDependencies)
                {
                    if (!AdditionalDependency.Exists)
                    {
                        Log.TraceLog("{0} has been deleted since makefile was built.", AdditionalDependency.Location);
                        ReasonNotLoaded = string.Format("{0} deleted", AdditionalDependency.Location.GetFileName());
                        return(false);
                    }
                    if (AdditionalDependency.LastWriteTimeUtc > Makefile.CreateTimeUtc)
                    {
                        Log.TraceLog("{0} has been modified since makefile was built.", AdditionalDependency.Location);
                        ReasonNotLoaded = string.Format("{0} modified", AdditionalDependency.Location.GetFileName());
                        return(false);
                    }
                }

                // Check that no new plugins have been added
                foreach (FileReference PluginFile in Plugins.EnumeratePlugins(ProjectFile))
                {
                    FileItem PluginFileItem = FileItem.GetItemByFileReference(PluginFile);
                    if (!Makefile.PluginFiles.Contains(PluginFileItem))
                    {
                        Log.TraceLog("{0} has been added", PluginFile.GetFileName());
                        ReasonNotLoaded = string.Format("{0} has been added", PluginFile.GetFileName());
                        return(false);
                    }
                }

                // We do a check to see if any modules' headers have changed which have
                // acquired or lost UHT types.  If so, which should be rare,
                // we'll just invalidate the entire makefile and force it to be rebuilt.

                // Get all H files in processed modules newer than the makefile itself
                HashSet <FileItem> HFilesNewerThanMakefile = new HashSet <FileItem>();
                foreach (UHTModuleHeaderInfo ModuleHeaderInfo in Makefile.UObjectModuleHeaders)
                {
                    foreach (FileItem HeaderFile in ModuleHeaderInfo.SourceFolder.EnumerateFiles())
                    {
                        if (HeaderFile.HasExtension(".h") && HeaderFile.LastWriteTimeUtc > Makefile.CreateTimeUtc)
                        {
                            HFilesNewerThanMakefile.Add(HeaderFile);
                        }
                    }
                }

                // Get all H files in all modules processed in the last makefile build
                HashSet <FileItem> AllUHTHeaders = new HashSet <FileItem>(Makefile.UObjectModuleHeaders.SelectMany(x => x.HeaderFiles));

                // Check whether any headers have been deleted. If they have, we need to regenerate the makefile since the module might now be empty. If we don't,
                // and the file has been moved to a different module, we may include stale generated headers.
                foreach (FileItem HeaderFile in AllUHTHeaders)
                {
                    if (!HeaderFile.Exists)
                    {
                        Log.TraceLog("File processed by UHT was deleted ({0}); invalidating makefile", HeaderFile);
                        ReasonNotLoaded = string.Format("UHT file was deleted");
                        return(false);
                    }
                }

                // Makefile is invalid if:
                // * There are any newer files which contain no UHT data, but were previously in the makefile
                // * There are any newer files contain data which needs processing by UHT, but weren't not previously in the makefile
                SourceFileMetadataCache MetadataCache = SourceFileMetadataCache.CreateHierarchy(ProjectFile);
                foreach (FileItem HeaderFile in HFilesNewerThanMakefile)
                {
                    bool bContainsUHTData = MetadataCache.ContainsReflectionMarkup(HeaderFile);
                    bool bWasProcessed    = AllUHTHeaders.Contains(HeaderFile);
                    if (bContainsUHTData != bWasProcessed)
                    {
                        Log.TraceLog("{0} {1} contain UHT types and now {2} , ignoring it", HeaderFile, bWasProcessed ? "used to" : "didn't", bWasProcessed ? "doesn't" : "does");
                        ReasonNotLoaded = string.Format("new files with reflected types");
                        return(false);
                    }
                }

                // If adaptive unity build is enabled, do a check to see if there are any source files that became part of the
                // working set since the Makefile was created (or, source files were removed from the working set.)  If anything
                // changed, then we'll force a new Makefile to be created so that we have fresh unity build blobs.  We always
                // want to make sure that source files in the working set are excluded from those unity blobs (for fastest possible
                // iteration times.)

                // Check if any source files in the working set no longer belong in it
                foreach (FileItem SourceFile in Makefile.WorkingSet)
                {
                    if (!WorkingSet.Contains(SourceFile) && SourceFile.LastWriteTimeUtc > Makefile.CreateTimeUtc)
                    {
                        Log.TraceLog("{0} was part of source working set and now is not; invalidating makefile", SourceFile.AbsolutePath);
                        ReasonNotLoaded = string.Format("working set of source files changed");
                        return(false);
                    }
                }

                // Check if any source files that are eligible for being in the working set have been modified
                foreach (FileItem SourceFile in Makefile.CandidatesForWorkingSet)
                {
                    if (WorkingSet.Contains(SourceFile) && SourceFile.LastWriteTimeUtc > Makefile.CreateTimeUtc)
                    {
                        Log.TraceLog("{0} was part of source working set and now is not", SourceFile.AbsolutePath);
                        ReasonNotLoaded = string.Format("working set of source files changed");
                        return(false);
                    }
                }
            }

            ReasonNotLoaded = null;
            return(true);
        }
Example #17
0
        /// <summary>
        /// Loads a makefile  from disk
        /// </summary>
        /// <param name="MakefilePath">Path to the makefile to load</param>
        /// <param name="ProjectFile">Path to the project file</param>
        /// <param name="Platform">Platform for this makefile</param>
        /// <param name="Arguments">Command line arguments for this target</param>
        /// <param name="ReasonNotLoaded">If the function returns null, this string will contain the reason why</param>
        /// <returns>The loaded makefile, or null if it failed for some reason.  On failure, the 'ReasonNotLoaded' variable will contain information about why</returns>
        public static TargetMakefile Load(FileReference MakefilePath, FileReference ProjectFile, UnrealTargetPlatform Platform, string[] Arguments, out string ReasonNotLoaded)
        {
            using (Timeline.ScopeEvent("Checking dependent timestamps"))
            {
                // Check the directory timestamp on the project files directory.  If the user has generated project files more recently than the makefile, then we need to consider the file to be out of date
                FileInfo MakefileInfo = new FileInfo(MakefilePath.FullName);
                if (!MakefileInfo.Exists)
                {
                    // Makefile doesn't even exist, so we won't bother loading it
                    ReasonNotLoaded = "no existing makefile";
                    return(null);
                }

                // Check the build version
                FileInfo BuildVersionFileInfo = new FileInfo(BuildVersion.GetDefaultFileName().FullName);
                if (BuildVersionFileInfo.Exists && MakefileInfo.LastWriteTime.CompareTo(BuildVersionFileInfo.LastWriteTime) < 0)
                {
                    Log.TraceLog("Existing makefile is older than Build.version, ignoring it");
                    ReasonNotLoaded = "Build.version is newer";
                    return(null);
                }

                // @todo ubtmake: This will only work if the directory timestamp actually changes with every single GPF.  Force delete existing files before creating new ones?  Eh... really we probably just want to delete + create a file in that folder
                //			-> UPDATE: Seems to work OK right now though on Windows platform, maybe due to GUID changes
                // @todo ubtmake: Some platforms may not save any files into this folder.  We should delete + generate a "touch" file to force the directory timestamp to be updated (or just check the timestamp file itself.  We could put it ANYWHERE, actually)

                // Installed Build doesn't need to check engine projects for outdatedness
                if (!UnrealBuildTool.IsEngineInstalled())
                {
                    if (DirectoryReference.Exists(ProjectFileGenerator.IntermediateProjectFilesPath))
                    {
                        DateTime EngineProjectFilesLastUpdateTime = new FileInfo(ProjectFileGenerator.ProjectTimestampFile).LastWriteTime;
                        if (MakefileInfo.LastWriteTime.CompareTo(EngineProjectFilesLastUpdateTime) < 0)
                        {
                            // Engine project files are newer than makefile
                            Log.TraceLog("Existing makefile is older than generated engine project files, ignoring it");
                            ReasonNotLoaded = "project files are newer";
                            return(null);
                        }
                    }
                }

                // Check the game project directory too
                if (ProjectFile != null)
                {
                    string   ProjectFilename = ProjectFile.FullName;
                    FileInfo ProjectFileInfo = new FileInfo(ProjectFilename);
                    if (!ProjectFileInfo.Exists || MakefileInfo.LastWriteTime.CompareTo(ProjectFileInfo.LastWriteTime) < 0)
                    {
                        // .uproject file is newer than makefile
                        Log.TraceLog("Makefile is older than .uproject file, ignoring it");
                        ReasonNotLoaded = ".uproject file is newer";
                        return(null);
                    }

                    DirectoryReference MasterProjectRelativePath        = ProjectFile.Directory;
                    string             GameIntermediateProjectFilesPath = Path.Combine(MasterProjectRelativePath.FullName, "Intermediate", "ProjectFiles");
                    if (Directory.Exists(GameIntermediateProjectFilesPath))
                    {
                        DateTime GameProjectFilesLastUpdateTime = new DirectoryInfo(GameIntermediateProjectFilesPath).LastWriteTime;
                        if (MakefileInfo.LastWriteTime.CompareTo(GameProjectFilesLastUpdateTime) < 0)
                        {
                            // Game project files are newer than makefile
                            Log.TraceLog("Makefile is older than generated game project files, ignoring it");
                            ReasonNotLoaded = "game project files are newer";
                            return(null);
                        }
                    }
                }

                // Check to see if UnrealBuildTool.exe was compiled more recently than the makefile
                DateTime UnrealBuildToolTimestamp = new FileInfo(Assembly.GetExecutingAssembly().Location).LastWriteTime;
                if (MakefileInfo.LastWriteTime.CompareTo(UnrealBuildToolTimestamp) < 0)
                {
                    // UnrealBuildTool.exe was compiled more recently than the makefile
                    Log.TraceLog("Makefile is older than UnrealBuildTool.exe, ignoring it");
                    ReasonNotLoaded = "UnrealBuildTool.exe is newer";
                    return(null);
                }

                // Check to see if any BuildConfiguration files have changed since the last build
                List <XmlConfig.InputFile> InputFiles = XmlConfig.FindInputFiles();
                foreach (XmlConfig.InputFile InputFile in InputFiles)
                {
                    FileInfo InputFileInfo = new FileInfo(InputFile.Location.FullName);
                    if (InputFileInfo.LastWriteTime > MakefileInfo.LastWriteTime)
                    {
                        Log.TraceLog("Makefile is older than BuildConfiguration.xml, ignoring it");
                        ReasonNotLoaded = "BuildConfiguration.xml is newer";
                        return(null);
                    }
                }
            }

            TargetMakefile Makefile;

            using (Timeline.ScopeEvent("Loading makefile"))
            {
                try
                {
                    using (BinaryArchiveReader Reader = new BinaryArchiveReader(MakefilePath))
                    {
                        int Version = Reader.ReadInt();
                        if (Version != CurrentVersion)
                        {
                            ReasonNotLoaded = "makefile version does not match";
                            return(null);
                        }
                        Makefile = new TargetMakefile(Reader);
                    }
                }
                catch (Exception Ex)
                {
                    Log.TraceWarning("Failed to read makefile: {0}", Ex.Message);
                    Log.TraceLog("Exception: {0}", Ex.ToString());
                    ReasonNotLoaded = "couldn't read existing makefile";
                    return(null);
                }
            }

            using (Timeline.ScopeEvent("Checking makefile validity"))
            {
                // Check if the arguments are different
                if (!Enumerable.SequenceEqual(Makefile.AdditionalArguments, Arguments))
                {
                    ReasonNotLoaded = "command line arguments changed";
                    return(null);
                }

                // Check if ini files are newer. Ini files contain build settings too.
                DirectoryReference ProjectDirectory = DirectoryReference.FromFile(ProjectFile);
                foreach (ConfigHierarchyType IniType in (ConfigHierarchyType[])Enum.GetValues(typeof(ConfigHierarchyType)))
                {
                    foreach (FileReference IniFilename in ConfigHierarchy.EnumerateConfigFileLocations(IniType, ProjectDirectory, Platform))
                    {
                        FileInfo IniFileInfo = new FileInfo(IniFilename.FullName);
                        if (IniFileInfo.LastWriteTimeUtc > Makefile.CreateTimeUtc)
                        {
                            // Ini files are newer than makefile
                            ReasonNotLoaded = "ini files are newer than makefile";
                            return(null);
                        }
                    }
                }

                // Get the current build metadata from the platform
                string CurrentExternalMetadata = UEBuildPlatform.GetBuildPlatform(Platform).GetExternalBuildMetadata(ProjectFile);
                if (String.Compare(CurrentExternalMetadata, Makefile.ExternalMetadata, StringComparison.Ordinal) != 0)
                {
                    Log.TraceLog("Old metadata:\n", Makefile.ExternalMetadata);
                    Log.TraceLog("New metadata:\n", CurrentExternalMetadata);
                    ReasonNotLoaded = "build metadata has changed";
                    return(null);
                }
            }

            // The makefile is ok
            ReasonNotLoaded = null;
            return(Makefile);
        }
Example #18
0
        /// <summary>
        /// Checks if the makefile is valid for the current set of source files. This is done separately to the Load() method to allow pre-build steps to modify source files.
        /// </summary>
        /// <param name="Makefile">The makefile that has been loaded</param>
        /// <param name="ProjectFile">Path to the project file</param>
        /// <param name="Platform">The platform being built</param>
        /// <param name="WorkingSet">The current working set of source files</param>
        /// <param name="ReasonNotLoaded">If the makefile is not valid, is set to a message describing why</param>
        /// <returns>True if the makefile is valid, false otherwise</returns>
        public static bool IsValidForSourceFiles(TargetMakefile Makefile, FileReference ProjectFile, UnrealTargetPlatform Platform, ISourceFileWorkingSet WorkingSet, out string ReasonNotLoaded)
        {
            using (Timeline.ScopeEvent("TargetMakefile.IsValidForSourceFiles()"))
            {
                // Get the list of excluded folder names for this platform
                ReadOnlyHashSet <string> ExcludedFolderNames = UEBuildPlatform.GetBuildPlatform(Platform).GetExcludedFolderNames();

                // Check if any source files have been added or removed
                foreach (KeyValuePair <DirectoryItem, FileItem[]> Pair in Makefile.DirectoryToSourceFiles)
                {
                    DirectoryItem InputDirectory = Pair.Key;
                    if (!InputDirectory.Exists || InputDirectory.LastWriteTimeUtc > Makefile.CreateTimeUtc)
                    {
                        FileItem[] SourceFiles = UEBuildModuleCPP.GetSourceFiles(InputDirectory);
                        if (SourceFiles.Length < Pair.Value.Length)
                        {
                            ReasonNotLoaded = "source file removed";
                            return(false);
                        }
                        else if (SourceFiles.Length > Pair.Value.Length)
                        {
                            ReasonNotLoaded = "source file added";
                            return(false);
                        }
                        else if (SourceFiles.Intersect(Pair.Value).Count() != SourceFiles.Length)
                        {
                            ReasonNotLoaded = "source file modified";
                            return(false);
                        }

                        foreach (DirectoryItem Directory in InputDirectory.EnumerateDirectories())
                        {
                            if (!Makefile.DirectoryToSourceFiles.ContainsKey(Directory) && ContainsSourceFiles(Directory, ExcludedFolderNames))
                            {
                                ReasonNotLoaded = "directory added";
                                return(false);
                            }
                        }
                    }
                }

                // Check if any external dependencies have changed. These comparisons are done against the makefile creation time.
                foreach (FileItem ExternalDependency in Makefile.ExternalDependencies)
                {
                    if (!ExternalDependency.Exists)
                    {
                        Log.TraceLog("{0} has been deleted since makefile was built.", ExternalDependency.Location);
                        ReasonNotLoaded = string.Format("{0} deleted", ExternalDependency.Location.GetFileName());
                        return(false);
                    }
                    if (ExternalDependency.LastWriteTimeUtc > Makefile.CreateTimeUtc)
                    {
                        Log.TraceLog("{0} has been modified since makefile was built.", ExternalDependency.Location);
                        ReasonNotLoaded = string.Format("{0} modified", ExternalDependency.Location.GetFileName());
                        return(false);
                    }
                }

                // Check if any internal dependencies has changed. These comparisons are done against the makefile modified time.
                foreach (FileItem InternalDependency in Makefile.InternalDependencies)
                {
                    if (!InternalDependency.Exists)
                    {
                        Log.TraceLog("{0} has been deleted since makefile was written.", InternalDependency.Location);
                        ReasonNotLoaded = string.Format("{0} deleted", InternalDependency.Location.GetFileName());
                        return(false);
                    }
                    if (InternalDependency.LastWriteTimeUtc > Makefile.ModifiedTimeUtc)
                    {
                        Log.TraceLog("{0} has been modified since makefile was written.", InternalDependency.Location);
                        ReasonNotLoaded = string.Format("{0} modified", InternalDependency.Location.GetFileName());
                        return(false);
                    }
                }

                // Check that no new plugins have been added
                foreach (FileReference PluginFile in Plugins.EnumeratePlugins(ProjectFile))
                {
                    FileItem PluginFileItem = FileItem.GetItemByFileReference(PluginFile);
                    if (!Makefile.PluginFiles.Contains(PluginFileItem))
                    {
                        Log.TraceLog("{0} has been added", PluginFile.GetFileName());
                        ReasonNotLoaded = string.Format("{0} has been added", PluginFile.GetFileName());
                        return(false);
                    }
                }

                // Load the metadata cache
                SourceFileMetadataCache MetadataCache = SourceFileMetadataCache.CreateHierarchy(ProjectFile);

                // Find the set of files that contain reflection markup
                ConcurrentBag <FileItem> NewFilesWithMarkupBag = new ConcurrentBag <FileItem>();
                using (ThreadPoolWorkQueue Queue = new ThreadPoolWorkQueue())
                {
                    foreach (DirectoryItem SourceDirectory in Makefile.SourceDirectories)
                    {
                        Queue.Enqueue(() => FindFilesWithMarkup(SourceDirectory, MetadataCache, ExcludedFolderNames, NewFilesWithMarkupBag, Queue));
                    }
                }

                // Check whether the list has changed
                List <FileItem> PrevFilesWithMarkup = Makefile.UObjectModuleHeaders.Where(x => !x.bUsePrecompiled).SelectMany(x => x.HeaderFiles).ToList();
                List <FileItem> NextFilesWithMarkup = NewFilesWithMarkupBag.ToList();
                if (NextFilesWithMarkup.Count != PrevFilesWithMarkup.Count || NextFilesWithMarkup.Intersect(PrevFilesWithMarkup).Count() != PrevFilesWithMarkup.Count)
                {
                    ReasonNotLoaded = "UHT files changed";
                    return(false);
                }

                // If adaptive unity build is enabled, do a check to see if there are any source files that became part of the
                // working set since the Makefile was created (or, source files were removed from the working set.)  If anything
                // changed, then we'll force a new Makefile to be created so that we have fresh unity build blobs.  We always
                // want to make sure that source files in the working set are excluded from those unity blobs (for fastest possible
                // iteration times.)

                // Check if any source files in the working set no longer belong in it
                foreach (FileItem SourceFile in Makefile.WorkingSet)
                {
                    if (!WorkingSet.Contains(SourceFile) && SourceFile.LastWriteTimeUtc > Makefile.CreateTimeUtc)
                    {
                        Log.TraceLog("{0} was part of source working set and now is not; invalidating makefile", SourceFile.AbsolutePath);
                        ReasonNotLoaded = string.Format("working set of source files changed");
                        return(false);
                    }
                }

                // Check if any source files that are eligible for being in the working set have been modified
                foreach (FileItem SourceFile in Makefile.CandidatesForWorkingSet)
                {
                    if (WorkingSet.Contains(SourceFile) && SourceFile.LastWriteTimeUtc > Makefile.CreateTimeUtc)
                    {
                        Log.TraceLog("{0} was part of source working set and now is not", SourceFile.AbsolutePath);
                        ReasonNotLoaded = string.Format("working set of source files changed");
                        return(false);
                    }
                }
            }

            ReasonNotLoaded = null;
            return(true);
        }
Example #19
0
        /// <summary>
        /// Build a list of targets
        /// </summary>
        /// <param name="TargetDescriptors">Target descriptors</param>
        /// <param name="BuildConfiguration">Current build configuration</param>
        /// <param name="WorkingSet">The source file working set</param>
        /// <param name="Options">Additional options for the build</param>
        /// <param name="WriteOutdatedActionsFile">Files to write the list of outdated actions to (rather than building them)</param>
        /// <returns>Result from the compilation</returns>
        public static void Build(List <TargetDescriptor> TargetDescriptors, BuildConfiguration BuildConfiguration, ISourceFileWorkingSet WorkingSet, BuildOptions Options, FileReference WriteOutdatedActionsFile)
        {
            // Create a makefile for each target
            TargetMakefile[] Makefiles = new TargetMakefile[TargetDescriptors.Count];
            for (int TargetIdx = 0; TargetIdx < TargetDescriptors.Count; TargetIdx++)
            {
                Makefiles[TargetIdx] = CreateMakefile(BuildConfiguration, TargetDescriptors[TargetIdx], WorkingSet);
            }

            // Export the actions for each target
            for (int TargetIdx = 0; TargetIdx < TargetDescriptors.Count; TargetIdx++)
            {
                TargetDescriptor TargetDescriptor = TargetDescriptors[TargetIdx];
                foreach (FileReference WriteActionFile in TargetDescriptor.WriteActionFiles)
                {
                    Log.TraceInformation("Writing actions to {0}", WriteActionFile);
                    ActionGraph.ExportJson(Makefiles[TargetIdx].Actions, WriteActionFile);
                }
            }

            // Execute the build
            if ((Options & BuildOptions.SkipBuild) == 0)
            {
                // Make sure that none of the actions conflict with any other (producing output files differently, etc...)
                ActionGraph.CheckForConflicts(Makefiles.SelectMany(x => x.Actions));

                // Check we don't exceed the nominal max path length
                using (Timeline.ScopeEvent("ActionGraph.CheckPathLengths"))
                {
                    ActionGraph.CheckPathLengths(BuildConfiguration, Makefiles.SelectMany(x => x.Actions));
                }

                // Find all the actions to be executed
                HashSet <Action>[] ActionsToExecute = new HashSet <Action> [TargetDescriptors.Count];
                for (int TargetIdx = 0; TargetIdx < TargetDescriptors.Count; TargetIdx++)
                {
                    ActionsToExecute[TargetIdx] = GetActionsForTarget(BuildConfiguration, TargetDescriptors[TargetIdx], Makefiles[TargetIdx]);
                }

                // If there are multiple targets being built, merge the actions together
                List <Action> MergedActionsToExecute;
                if (TargetDescriptors.Count == 1)
                {
                    MergedActionsToExecute = new List <Action>(ActionsToExecute[0]);
                }
                else
                {
                    MergedActionsToExecute = MergeActionGraphs(TargetDescriptors, ActionsToExecute);
                }

                // Link all the actions together
                ActionGraph.Link(MergedActionsToExecute);

                // Make sure we're not modifying any engine files
                if ((Options & BuildOptions.NoEngineChanges) != 0)
                {
                    List <FileItem> EngineChanges = MergedActionsToExecute.SelectMany(x => x.ProducedItems).Where(x => x.Location.IsUnderDirectory(UnrealBuildTool.EngineDirectory)).Distinct().OrderBy(x => x.FullName).ToList();
                    if (EngineChanges.Count > 0)
                    {
                        StringBuilder Result = new StringBuilder("Building would modify the following engine files:\n");
                        foreach (FileItem EngineChange in EngineChanges)
                        {
                            Result.AppendFormat("\n{0}", EngineChange.FullName);
                        }
                        Result.Append("\n\nPlease rebuild from an IDE instead.");
                        Log.TraceError("{0}", Result.ToString());
                        throw new CompilationResultException(CompilationResult.FailedDueToEngineChange);
                    }
                }

                // Make sure the appropriate executor is selected
                foreach (TargetDescriptor TargetDescriptor in TargetDescriptors)
                {
                    UEBuildPlatform BuildPlatform = UEBuildPlatform.GetBuildPlatform(TargetDescriptor.Platform);
                    BuildConfiguration.bAllowXGE    &= BuildPlatform.CanUseXGE();
                    BuildConfiguration.bAllowDistcc &= BuildPlatform.CanUseDistcc();
                    BuildConfiguration.bAllowSNDBS  &= BuildPlatform.CanUseSNDBS();
                }

                // Delete produced items that are outdated.
                ActionGraph.DeleteOutdatedProducedItems(MergedActionsToExecute);

                // Save all the action histories now that files have been removed. We have to do this after deleting produced items to ensure that any
                // items created during the build don't have the wrong command line.
                ActionHistory.SaveAll();

                // Create directories for the outdated produced items.
                ActionGraph.CreateDirectoriesForProducedItems(MergedActionsToExecute);

                // Execute the actions
                if ((Options & BuildOptions.XGEExport) != 0)
                {
                    OutputToolchainInfo(TargetDescriptors, Makefiles);

                    // Just export to an XML file
                    using (Timeline.ScopeEvent("XGE.ExportActions()"))
                    {
                        XGE.ExportActions(MergedActionsToExecute);
                    }
                }
                else if (WriteOutdatedActionsFile != null)
                {
                    OutputToolchainInfo(TargetDescriptors, Makefiles);

                    // Write actions to an output file
                    using (Timeline.ScopeEvent("ActionGraph.WriteActions"))
                    {
                        ActionGraph.ExportJson(MergedActionsToExecute, WriteOutdatedActionsFile);
                    }
                }
                else
                {
                    // Execute the actions
                    if (MergedActionsToExecute.Count == 0)
                    {
                        if (TargetDescriptors.Any(x => !x.bQuiet))
                        {
                            Log.TraceInformation((TargetDescriptors.Count == 1)? "Target is up to date" : "Targets are up to date");
                        }
                    }
                    else
                    {
                        if (TargetDescriptors.Any(x => !x.bQuiet))
                        {
                            Log.TraceInformation("Building {0}...", StringUtils.FormatList(TargetDescriptors.Select(x => x.Name).Distinct()));
                        }

                        OutputToolchainInfo(TargetDescriptors, Makefiles);

                        using (Timeline.ScopeEvent("ActionGraph.ExecuteActions()"))
                        {
                            ActionGraph.ExecuteActions(BuildConfiguration, MergedActionsToExecute);
                        }
                    }

                    // Run the deployment steps
                    foreach (TargetMakefile Makefile in Makefiles)
                    {
                        if (Makefile.bDeployAfterCompile)
                        {
                            TargetReceipt Receipt = TargetReceipt.Read(Makefile.ReceiptFile);
                            Log.TraceInformation("Deploying {0} {1} {2}...", Receipt.TargetName, Receipt.Platform, Receipt.Configuration);

                            UEBuildPlatform.GetBuildPlatform(Receipt.Platform).Deploy(Receipt);
                        }
                    }
                }
            }
        }
        /// <summary>
        /// Checks if the editor is currently running and this is a hot-reload
        /// </summary>
        public static bool ShouldDoHotReloadFromIDE(BuildConfiguration BuildConfiguration, TargetDescriptor TargetDesc)
        {
            // Check if Hot-reload is disabled globally for this project
            ConfigHierarchy Hierarchy = ConfigCache.ReadHierarchy(ConfigHierarchyType.Engine, DirectoryReference.FromFile(TargetDesc.ProjectFile), TargetDesc.Platform);
            bool            bAllowHotReloadFromIDE;

            if (Hierarchy.TryGetValue("BuildConfiguration", "bAllowHotReloadFromIDE", out bAllowHotReloadFromIDE) && !bAllowHotReloadFromIDE)
            {
                return(false);
            }

            if (!BuildConfiguration.bAllowHotReloadFromIDE)
            {
                return(false);
            }

            // Check if we're using LiveCode instead
            ConfigHierarchy EditorPerProjectHierarchy = ConfigCache.ReadHierarchy(ConfigHierarchyType.EditorPerProjectUserSettings, DirectoryReference.FromFile(TargetDesc.ProjectFile), TargetDesc.Platform);
            bool            bEnableLiveCode;

            if (EditorPerProjectHierarchy.GetBool("/Script/LiveCoding.LiveCodingSettings", "bEnabled", out bEnableLiveCode) && bEnableLiveCode)
            {
                return(false);
            }

            bool bIsRunning = false;

            // @todo ubtmake: Kind of cheating here to figure out if an editor target.  At this point we don't have access to the actual target description, and
            // this code must be able to execute before we create or load module rules DLLs so that hot reload can work with bUseUBTMakefiles
            if (TargetDesc.Name.EndsWith("Editor", StringComparison.OrdinalIgnoreCase))
            {
                string EditorBaseFileName = "UE4Editor";
                if (TargetDesc.Configuration != UnrealTargetConfiguration.Development)
                {
                    EditorBaseFileName = String.Format("{0}-{1}-{2}", EditorBaseFileName, TargetDesc.Platform, TargetDesc.Configuration);
                }

                FileReference EditorLocation;
                if (TargetDesc.Platform == UnrealTargetPlatform.Win64)
                {
                    EditorLocation = FileReference.Combine(UnrealBuildTool.EngineDirectory, "Binaries", "Win64", String.Format("{0}.exe", EditorBaseFileName));
                }
                else if (TargetDesc.Platform == UnrealTargetPlatform.Mac)
                {
                    EditorLocation = FileReference.Combine(UnrealBuildTool.EngineDirectory, "Binaries", "Mac", String.Format("{0}.app/Contents/MacOS/{0}", EditorBaseFileName));
                }
                else if (TargetDesc.Platform == UnrealTargetPlatform.Linux)
                {
                    EditorLocation = FileReference.Combine(UnrealBuildTool.EngineDirectory, "Binaries", "Linux", EditorBaseFileName);
                }
                else
                {
                    throw new BuildException("Unknown editor filename for this platform");
                }

                using (Timeline.ScopeEvent("Finding editor processes for hot-reload"))
                {
                    DirectoryReference EditorRunsDir = DirectoryReference.Combine(UnrealBuildTool.EngineDirectory, "Intermediate", "EditorRuns");
                    if (!DirectoryReference.Exists(EditorRunsDir))
                    {
                        return(false);
                    }

                    if (BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Win64)
                    {
                        foreach (FileReference EditorInstanceFile in DirectoryReference.EnumerateFiles(EditorRunsDir))
                        {
                            int ProcessId;
                            if (!Int32.TryParse(EditorInstanceFile.GetFileName(), out ProcessId))
                            {
                                FileReference.Delete(EditorInstanceFile);
                                continue;
                            }

                            Process RunningProcess;
                            try
                            {
                                RunningProcess = Process.GetProcessById(ProcessId);
                            }
                            catch
                            {
                                RunningProcess = null;
                            }

                            if (RunningProcess == null)
                            {
                                FileReference.Delete(EditorInstanceFile);
                                continue;
                            }

                            FileReference MainModuleFile;
                            try
                            {
                                MainModuleFile = new FileReference(RunningProcess.MainModule.FileName);
                            }
                            catch
                            {
                                MainModuleFile = null;
                            }

                            if (!bIsRunning && EditorLocation == MainModuleFile)
                            {
                                bIsRunning = true;
                            }
                        }
                    }
                    else
                    {
                        FileInfo[] EditorRunsFiles = new DirectoryInfo(EditorRunsDir.FullName).GetFiles();
                        BuildHostPlatform.ProcessInfo[] Processes = BuildHostPlatform.Current.GetProcesses();

                        foreach (FileInfo File in EditorRunsFiles)
                        {
                            int PID;
                            BuildHostPlatform.ProcessInfo Proc = null;
                            if (!Int32.TryParse(File.Name, out PID) || (Proc = Processes.FirstOrDefault(P => P.PID == PID)) == default(BuildHostPlatform.ProcessInfo))
                            {
                                // Delete stale files (it may happen if editor crashes).
                                File.Delete();
                                continue;
                            }

                            // Don't break here to allow clean-up of other stale files.
                            if (!bIsRunning)
                            {
                                // Otherwise check if the path matches.
                                bIsRunning = new FileReference(Proc.Filename) == EditorLocation;
                            }
                        }
                    }
                }
            }
            return(bIsRunning);
        }
Example #21
0
        /// <summary>
        /// Main entry point. Parses any global options and initializes the logging system, then invokes the appropriate command.
        /// </summary>
        /// <param name="ArgumentsArray">Command line arguments</param>
        /// <returns>Zero on success, non-zero on error</returns>
        private static int Main(string[] ArgumentsArray)
        {
            SingleInstanceMutex Mutex = null;

            try
            {
                // Start capturing performance info
                Timeline.Start();

                // Parse the command line arguments
                CommandLineArguments Arguments = new CommandLineArguments(ArgumentsArray);

                // Parse the global options
                GlobalOptions Options = new GlobalOptions(Arguments);

                // Configure the log system
                Log.OutputLevel       = Options.LogOutputLevel;
                Log.IncludeTimestamps = Options.bLogTimestamps;
                Log.IncludeProgramNameWithSeverityPrefix = Options.bLogFromMsBuild;

                // Configure the progress writer
                ProgressWriter.bWriteMarkup = Options.bWriteProgressMarkup;

                // Add the log writer if requested. When building a target, we'll create the writer for the default log file later.
                if (Options.LogFileName != null)
                {
                    Log.AddFileWriter("LogTraceListener", Options.LogFileName);
                }

                // Ensure we can resolve any external assemblies that are not in the same folder as our assembly.
                AssemblyUtils.InstallAssemblyResolver(Path.GetDirectoryName(Assembly.GetEntryAssembly().GetOriginalLocation()));

                // Change the working directory to be the Engine/Source folder. We are likely running from Engine/Binaries/DotNET
                // This is critical to be done early so any code that relies on the current directory being Engine/Source will work.
                DirectoryReference.SetCurrentDirectory(UnrealBuildTool.EngineSourceDirectory);

                // Get the type of the mode to execute, using a fast-path for the build mode.
                Type ModeType = typeof(BuildMode);
                if (Options.Mode != null)
                {
                    // Find all the valid modes
                    Dictionary <string, Type> ModeNameToType = new Dictionary <string, Type>(StringComparer.OrdinalIgnoreCase);
                    foreach (Type Type in Assembly.GetExecutingAssembly().GetTypes())
                    {
                        if (Type.IsClass && !Type.IsAbstract && Type.IsSubclassOf(typeof(ToolMode)))
                        {
                            ToolModeAttribute Attribute = Type.GetCustomAttribute <ToolModeAttribute>();
                            if (Attribute == null)
                            {
                                throw new BuildException("Class '{0}' should have a ToolModeAttribute", Type.Name);
                            }
                            ModeNameToType.Add(Attribute.Name, Type);
                        }
                    }

                    // Try to get the correct mode
                    if (!ModeNameToType.TryGetValue(Options.Mode, out ModeType))
                    {
                        Log.TraceError("No mode named '{0}'. Available modes are:\n  {1}", Options.Mode, String.Join("\n  ", ModeNameToType.Keys));
                        return(1);
                    }
                }

                // Get the options for which systems have to be initialized for this mode
                ToolModeOptions ModeOptions = ModeType.GetCustomAttribute <ToolModeAttribute>().Options;

                // Start prefetching the contents of the engine folder
                if ((ModeOptions & ToolModeOptions.StartPrefetchingEngine) != 0)
                {
                    using (Timeline.ScopeEvent("FileMetadataPrefetch.QueueEngineDirectory()"))
                    {
                        FileMetadataPrefetch.QueueEngineDirectory();
                    }
                }

                // Read the XML configuration files
                if ((ModeOptions & ToolModeOptions.XmlConfig) != 0)
                {
                    using (Timeline.ScopeEvent("XmlConfig.ReadConfigFiles()"))
                    {
                        string XmlConfigMutexName = SingleInstanceMutex.GetUniqueMutexForPath("UnrealBuildTool_Mutex_XmlConfig", Assembly.GetExecutingAssembly().CodeBase);
                        using (SingleInstanceMutex XmlConfigMutex = new SingleInstanceMutex(XmlConfigMutexName, true))
                        {
                            FileReference XmlConfigCache = Arguments.GetFileReferenceOrDefault("-XmlConfigCache=", null);
                            XmlConfig.ReadConfigFiles(XmlConfigCache);
                        }
                    }
                }

                // Acquire a lock for this branch
                if ((ModeOptions & ToolModeOptions.SingleInstance) != 0 && !Options.bNoMutex)
                {
                    using (Timeline.ScopeEvent("SingleInstanceMutex.Acquire()"))
                    {
                        string MutexName = SingleInstanceMutex.GetUniqueMutexForPath("UnrealBuildTool_Mutex", Assembly.GetExecutingAssembly().CodeBase);
                        Mutex = new SingleInstanceMutex(MutexName, Options.bWaitMutex);
                    }
                }

                // Register all the build platforms
                if ((ModeOptions & ToolModeOptions.BuildPlatforms) != 0)
                {
                    using (Timeline.ScopeEvent("UEBuildPlatform.RegisterPlatforms()"))
                    {
                        UEBuildPlatform.RegisterPlatforms(false);
                    }
                }
                if ((ModeOptions & ToolModeOptions.BuildPlatformsForValidation) != 0)
                {
                    using (Timeline.ScopeEvent("UEBuildPlatform.RegisterPlatforms()"))
                    {
                        UEBuildPlatform.RegisterPlatforms(true);
                    }
                }

                // Create the appropriate handler
                ToolMode Mode = (ToolMode)Activator.CreateInstance(ModeType);

                // Execute the mode
                int Result = Mode.Execute(Arguments);
                if ((ModeOptions & ToolModeOptions.ShowExecutionTime) != 0)
                {
                    Log.TraceInformation("Total execution time: {0:0.00} seconds", Timeline.Elapsed.TotalSeconds);
                }
                return(Result);
            }
            catch (CompilationResultException Ex)
            {
                // Used to return a propagate a specific exit code after an error has occurred. Does not log any message.
                Log.TraceLog(ExceptionUtils.FormatExceptionDetails(Ex));
                return((int)Ex.Result);
            }
            catch (BuildException Ex)
            {
                // BuildExceptions should have nicely formatted messages. We can log these directly.
                Log.TraceError(ExceptionUtils.FormatException(Ex));
                Log.TraceLog(ExceptionUtils.FormatExceptionDetails(Ex));
                return((int)CompilationResult.OtherCompilationError);
            }
            catch (Exception Ex)
            {
                // Unhandled exception.
                Log.TraceError("Unhandled exception: {0}", ExceptionUtils.FormatException(Ex));
                Log.TraceLog(ExceptionUtils.FormatExceptionDetails(Ex));
                return((int)CompilationResult.OtherCompilationError);
            }
            finally
            {
                // Cancel the prefetcher
                using (Timeline.ScopeEvent("FileMetadataPrefetch.Stop()"))
                {
                    FileMetadataPrefetch.Stop();
                }

                // Print out all the performance info
                Timeline.Print(TimeSpan.FromMilliseconds(20.0), LogEventType.Log);

                // Make sure we flush the logs however we exit
                Trace.Close();

                // Dispose of the mutex. Must be done last to ensure that another process does not startup and start trying to write to the same log file.
                if (Mutex != null)
                {
                    Mutex.Dispose();
                }
            }
        }