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