/// <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 #2
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(Ex.Message.ToString());
                Log.TraceLog(ExceptionUtils.FormatExceptionDetails(Ex));
                return((int)CompilationResult.OtherCompilationError);
            }
            catch (Exception Ex)
            {
                // Unhandled exception.
                Log.TraceError("Unhandled exception: {0}", 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();
                }
            }
        }
Example #3
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 AssemblySourcesListFilePath = FileReference.Combine(OutputAssemblyPath.Directory, Path.GetFileNameWithoutExtension(OutputAssemblyPath.FullName) + "SourceFiles.txt");
            bool          bNeedsCompilation           = false;

            if (!DoNotCompile)
            {
                bNeedsCompilation = RequiresCompilation(SourceFileNames, AssemblySourcesListFilePath, 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);
                }

                // 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!
                FileReference.WriteAllLines(AssemblySourcesListFilePath, SourceFileNames.Select(x => x.FullName));
            }

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

            // Create the assembly
            FileReference EngineAssemblyFileName = FileReference.Combine(RootDirectories[0], "Intermediate", "Build", "BuildRules", AssemblyPrefix + "Rules" + FrameworkAssemblyExtension);
            RulesAssembly EngineAssembly         = new RulesAssembly(Scope, RootDirectories[0], 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(RootDirectories[0], "Intermediate", "Build", "BuildRules", AssemblyPrefix + "ProgramRules" + FrameworkAssemblyExtension);
            RulesAssembly ProgramAssembly         = new RulesAssembly(ProgramsScope, RootDirectories[0], new List <PluginInfo>().AsReadOnly(), ProgramModuleFiles, ProgramTargetFiles, ProgramAssemblyFileName, bContainsEngineModules: false, DefaultBuildSettings: BuildSettingsVersion.Latest, bReadOnly: bReadOnly, bSkipCompile: bSkipCompile, Parent: EngineAssembly);

            // Return the combined assembly
            return(ProgramAssembly);
        }