/// <summary> /// Overrides base class to add module runtime dependencies to the build receipt. /// </summary> /// <param name="ToolChain">The platform toolchain</param> public override BuildReceipt MakeReceipt(IUEToolChain ToolChain) { BuildReceipt Receipt = base.MakeReceipt(ToolChain); // Set the IsPrecompiled flag on all the build products if we're not actually building this binary if (!Config.bAllowCompilation) { foreach (BuildProduct BuildProduct in Receipt.BuildProducts) { BuildProduct.IsPrecompiled = true; } } // Add the compiled resource file if we're building a static library containing the launch module on Windows if (Config.Type == UEBuildBinaryType.StaticLibrary && ModuleNames.Contains("Launch") && (Target.Platform == UnrealTargetPlatform.Win32 || Target.Platform == UnrealTargetPlatform.Win64)) { string ResourceFilePath = Path.Combine(Config.IntermediateDirectory, "Launch", "PCLaunch.rc.res"); Receipt.AddBuildProduct(ResourceFilePath, BuildProductType.StaticLibrary); } // Add runtime dependencies for all the modules in this binary, and build up a list of all the referenced modules var ReferencedModules = new CaselessDictionary <UEBuildModule.ModuleIndexPair>(); foreach (string ModuleName in ModuleNames) { UEBuildModule Module = Target.GetModuleByName(ModuleName); foreach (RuntimeDependency RuntimeDependency in Module.RuntimeDependencies) { Receipt.RuntimeDependencies.Add(new RuntimeDependency(RuntimeDependency)); } Module.GetAllDependencyModules(ReferencedModules, true, false, true); } // Add runtime dependencies for all the referenced external modules. These may be introduce dependencies for the binary without actually being listed for inclusion in it. foreach (UEBuildExternalModule ExternalModule in ReferencedModules.Values.OrderBy(x => x.Index).Select(x => x.Module).OfType <UEBuildExternalModule>()) { foreach (RuntimeDependency RuntimeDependency in ExternalModule.RuntimeDependencies) { Receipt.RuntimeDependencies.Add(new RuntimeDependency(RuntimeDependency)); } } return(Receipt); }
/// <summary> /// Generates a list of all modules referenced by this binary /// </summary> /// <param name="bIncludeDynamicallyLoaded">True if dynamically loaded modules (and all of their dependent modules) should be included.</param> /// <param name="bForceCircular">True if circular dependencies should be process</param> /// <returns>List of all referenced modules</returns> public override List <UEBuildModule> GetAllDependencyModules(bool bIncludeDynamicallyLoaded, bool bForceCircular) { var ReferencedModules = new CaselessDictionary <UEBuildModule.ModuleIndexPair>(); foreach (var ModuleName in ModuleNames) { if (!ReferencedModules.ContainsKey(ModuleName)) { var Module = Target.GetModuleByName(ModuleName); ReferencedModules[ModuleName] = null; Module.GetAllDependencyModules(ReferencedModules, bIncludeDynamicallyLoaded, bForceCircular, bOnlyDirectDependencies: false); ReferencedModules[ModuleName] = new UEBuildModule.ModuleIndexPair { Module = Module, Index = ReferencedModules.Count }; } } return(ReferencedModules.Values.OrderBy(M => M.Index).Select(M => M.Module).ToList()); }
// UEBuildModule interface. public override List<FileItem> Compile(CPPEnvironment GlobalCompileEnvironment, CPPEnvironment CompileEnvironment) { var BuildPlatform = UEBuildPlatform.GetBuildPlatformForCPPTargetPlatform(CompileEnvironment.Config.Target.Platform); var LinkInputFiles = new List<FileItem>(); if( ProjectFileGenerator.bGenerateProjectFiles && IntelliSenseGatherer == null) { // Nothing to do for IntelliSense, bail out early return LinkInputFiles; } var ModuleCompileEnvironment = CreateModuleCompileEnvironment(CompileEnvironment); IncludeSearchPaths = ModuleCompileEnvironment.Config.CPPIncludeInfo.IncludePaths.ToList(); IncludeSearchPaths.AddRange(ModuleCompileEnvironment.Config.CPPIncludeInfo.SystemIncludePaths.ToList()); if( IntelliSenseGatherer != null ) { // Update project file's set of preprocessor definitions and include paths IntelliSenseGatherer.AddIntelliSensePreprocessorDefinitions( ModuleCompileEnvironment.Config.Definitions ); IntelliSenseGatherer.AddInteliiSenseIncludePaths( ModuleCompileEnvironment.Config.CPPIncludeInfo.SystemIncludePaths, bAddingSystemIncludes: true ); IntelliSenseGatherer.AddInteliiSenseIncludePaths( ModuleCompileEnvironment.Config.CPPIncludeInfo.IncludePaths, bAddingSystemIncludes: false ); // Bail out. We don't need to actually compile anything while generating project files. return LinkInputFiles; } // Throw an error if the module's source file list referenced any non-existent files. if (SourceFilesToBuild.MissingFiles.Count > 0) { throw new BuildException( "UBT ERROR: Module \"{0}\" references non-existent files:\n{1} (perhaps a file was added to the project but not checked in)", Name, string.Join("\n", SourceFilesToBuild.MissingFiles.Select(M => M.AbsolutePath)) ); } // For an executable or a static library do not use the default RC file - // If the executable wants it, it will be in their source list anyway. // The issue here is that when making a monolithic game, the processing // of the other game modules will stomp the game-specific rc file. if (Binary.Config.Type == UEBuildBinaryType.DynamicLinkLibrary) { // Add default PCLaunch.rc file if this module has no own resource file specified if (SourceFilesToBuild.RCFiles.Count <= 0) { string DefRC = Utils.CleanDirectorySeparators( Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), "Runtime/Launch/Resources/Windows/PCLaunch.rc")) ); FileItem Item = FileItem.GetItemByFullPath(DefRC); SourceFilesToBuild.RCFiles.Add(Item); } // Always compile in the API version resource separately. This is required for the module manager to detect compatible API versions. string ModuleVersionRC = Utils.CleanDirectorySeparators( Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), "Runtime/Core/Resources/Windows/ModuleVersionResource.rc.inl")) ); FileItem ModuleVersionItem = FileItem.GetItemByFullPath(ModuleVersionRC); if( !SourceFilesToBuild.RCFiles.Contains(ModuleVersionItem) ) { SourceFilesToBuild.RCFiles.Add(ModuleVersionItem); } } { // Process all of the header file dependencies for this module this.CachePCHUsageForModuleSourceFiles( ModuleCompileEnvironment ); // Make sure our RC files have cached includes. foreach( var RCFile in SourceFilesToBuild.RCFiles ) { RCFile.CachedCPPIncludeInfo = ModuleCompileEnvironment.Config.CPPIncludeInfo; } } // Check to see if this is an Engine module (including program or plugin modules). That is, the module is located under the "Engine" folder var IsGameModule = !Utils.IsFileUnderDirectory( this.ModuleDirectory, Path.Combine( ProjectFileGenerator.EngineRelativePath ) ); // Should we force a precompiled header to be generated for this module? Usually, we only bother with a // precompiled header if there are at least several source files in the module (after combining them for unity // builds.) But for game modules, it can be convenient to always have a precompiled header to single-file // changes to code is really quick to compile. int MinFilesUsingPrecompiledHeader = BuildConfiguration.MinFilesUsingPrecompiledHeader; if( MinFilesUsingPrecompiledHeaderOverride != 0 ) { MinFilesUsingPrecompiledHeader = MinFilesUsingPrecompiledHeaderOverride; } else if( IsGameModule && BuildConfiguration.bForcePrecompiledHeaderForGameModules ) { // This is a game module with only a small number of source files, so go ahead and force a precompiled header // to be generated to make incremental changes to source files as fast as possible for small projects. MinFilesUsingPrecompiledHeader = 1; } // Should we use unity build mode for this module? bool bModuleUsesUnityBuild = BuildConfiguration.bUseUnityBuild || BuildConfiguration.bForceUnityBuild; if (!BuildConfiguration.bForceUnityBuild) { if (bFasterWithoutUnity) { bModuleUsesUnityBuild = false; } else if (IsGameModule && SourceFilesToBuild.CPPFiles.Count < BuildConfiguration.MinGameModuleSourceFilesForUnityBuild) { // Game modules with only a small number of source files are usually better off having faster iteration times // on single source file changes, so we forcibly disable unity build for those modules bModuleUsesUnityBuild = false; } } // The environment with which to compile the CPP files var CPPCompileEnvironment = ModuleCompileEnvironment; // Precompiled header support. bool bWasModuleCodeCompiled = false; if (BuildPlatform.ShouldUsePCHFiles(CompileEnvironment.Config.Target.Platform, CompileEnvironment.Config.Target.Configuration)) { var PCHGenTimerStart = DateTime.UtcNow; // The code below will figure out whether this module will either use a "unique PCH" (private PCH that will only be included by // this module's code files), or a "shared PCH" (potentially included by many code files in many modules.) Only one or the other // will be used. FileItem SharedPCHHeaderFile = null; // In the case of a shared PCH, we also need to keep track of which module that PCH's header file is a member of string SharedPCHModuleName = String.Empty; if( BuildConfiguration.bUseSharedPCHs && CompileEnvironment.Config.bIsBuildingLibrary ) { Log.TraceVerbose("Module '{0}' was not allowed to use Shared PCHs, because we're compiling to a library", this.Name ); } bool bUseSharedPCHFiles = BuildConfiguration.bUseSharedPCHs && !CompileEnvironment.Config.bIsBuildingLibrary && GlobalCompileEnvironment.SharedPCHHeaderFiles.Count > 0; if( bUseSharedPCHFiles ) { string SharingPCHHeaderFilePath = null; bool bIsASharedPCHModule = bUseSharedPCHFiles && GlobalCompileEnvironment.SharedPCHHeaderFiles.Any( PCH => PCH.Module == this ); if( bIsASharedPCHModule ) { SharingPCHHeaderFilePath = Path.GetFullPath( Path.Combine( ProjectFileGenerator.RootRelativePath, "Engine", "Source", this.SharedPCHHeaderFile ) ); } // We can't use a shared PCH file when compiling a module // with exports, because the shared PCH can only have imports in it to work correctly. bool bCanModuleUseOwnSharedPCH = bAllowSharedPCH && bIsASharedPCHModule && !Binary.Config.bAllowExports && ProcessedDependencies.UniquePCHHeaderFile.AbsolutePath.Equals( SharingPCHHeaderFilePath, StringComparison.InvariantCultureIgnoreCase ); if( bAllowSharedPCH && ( !bIsASharedPCHModule || bCanModuleUseOwnSharedPCH ) ) { // Figure out which shared PCH tier we're in var ReferencedModules = new CaselessDictionary<ModuleIndexPair>(); { this.GetAllDependencyModules( ReferencedModules, bIncludeDynamicallyLoaded:false, bForceCircular:false, bOnlyDirectDependencies:true ); } int LargestSharedPCHHeaderFileIndex = -1; foreach( var DependencyModule in ReferencedModules.Values.OrderBy(P => P.Index).Select(P => P.Module) ) { // These Shared PCHs are ordered from least complex to most complex. We'll start at the last one and search backwards. for( var SharedPCHHeaderFileIndex = GlobalCompileEnvironment.SharedPCHHeaderFiles.Count - 1; SharedPCHHeaderFileIndex > LargestSharedPCHHeaderFileIndex; --SharedPCHHeaderFileIndex ) { var CurSharedPCHHeaderFile = GlobalCompileEnvironment.SharedPCHHeaderFiles[ SharedPCHHeaderFileIndex ]; if( DependencyModule == CurSharedPCHHeaderFile.Module || ( bIsASharedPCHModule && CurSharedPCHHeaderFile.Module == this ) ) // If we ourselves are a shared PCH module, always at least use our own module as our shared PCH header if we can't find anything better { SharedPCHModuleName = CurSharedPCHHeaderFile.Module.Name; SharedPCHHeaderFile = CurSharedPCHHeaderFile.PCHHeaderFile; LargestSharedPCHHeaderFileIndex = SharedPCHHeaderFileIndex; break; } } if( LargestSharedPCHHeaderFileIndex == GlobalCompileEnvironment.SharedPCHHeaderFiles.Count - 1 ) { // We've determined that the module is using our most complex PCH header, so we can early-out break; } } // Did we not find a shared PCH header that is being included by this module? This could happen if the module is not including Core.h, even indirectly. if( String.IsNullOrEmpty( SharedPCHModuleName ) ) { throw new BuildException( "Module {0} doesn't use a Shared PCH! Please add a dependency on a Shared PCH module to this module's dependency list", this.Name); } // Keep track of how many modules make use of this PCH for performance diagnostics var LargestSharedPCHHeader = GlobalCompileEnvironment.SharedPCHHeaderFiles[ LargestSharedPCHHeaderFileIndex ]; ++LargestSharedPCHHeader.NumModulesUsingThisPCH; } else { Log.TraceVerbose("Module '{0}' cannot create or use Shared PCHs, because it needs its own private PCH", this.Name); } } // The precompiled header environment for all source files in this module that use a precompiled header, if we even need one PrecompileHeaderEnvironment ModulePCHEnvironment = null; // If there was one header that was included first by enough C++ files, use it as the precompiled header. // Only use precompiled headers for projects with enough files to make the PCH creation worthwhile. if( SharedPCHHeaderFile != null || SourceFilesToBuild.CPPFiles.Count >= MinFilesUsingPrecompiledHeader ) { FileItem PCHToUse; if( SharedPCHHeaderFile != null ) { ModulePCHEnvironment = ApplySharedPCH(GlobalCompileEnvironment, CompileEnvironment, ModuleCompileEnvironment, SourceFilesToBuild.CPPFiles, ref SharedPCHHeaderFile); if (ModulePCHEnvironment != null) { // @todo SharedPCH: Ideally we would exhaustively check for a compatible compile environment (definitions, imports/exports, etc) // Currently, it's possible for the shared PCH to be compiled differently depending on which module UBT happened to have // include it first during the build phase. This could create problems with deterministic builds, or turn up compile // errors unexpectedly due to compile environment differences. Log.TraceVerbose("Module " + Name + " uses existing Shared PCH '" + ModulePCHEnvironment.PrecompiledHeaderIncludeFilename + "' (from module " + ModulePCHEnvironment.ModuleName + ")"); } PCHToUse = SharedPCHHeaderFile; } else { PCHToUse = ProcessedDependencies.UniquePCHHeaderFile; } if (PCHToUse != null) { // Update all CPPFiles to point to the PCH foreach (var CPPFile in SourceFilesToBuild.CPPFiles) { CPPFile.PCHHeaderNameInCode = PCHToUse.AbsolutePath; CPPFile.PrecompiledHeaderIncludeFilename = PCHToUse.AbsolutePath; } } // A shared PCH was not already set up for us, so set one up. if( ModulePCHEnvironment == null ) { var PCHHeaderFile = ProcessedDependencies.UniquePCHHeaderFile; var PCHModuleName = this.Name; if( SharedPCHHeaderFile != null ) { PCHHeaderFile = SharedPCHHeaderFile; PCHModuleName = SharedPCHModuleName; } var PCHHeaderNameInCode = SourceFilesToBuild.CPPFiles[ 0 ].PCHHeaderNameInCode; ModulePCHEnvironment = new PrecompileHeaderEnvironment( PCHModuleName, PCHHeaderNameInCode, PCHHeaderFile, ModuleCompileEnvironment.Config.CLRMode, ModuleCompileEnvironment.Config.OptimizeCode ); if( SharedPCHHeaderFile != null ) { // Add to list of shared PCH environments GlobalCompileEnvironment.SharedPCHEnvironments.Add( ModulePCHEnvironment ); Log.TraceVerbose( "Module " + Name + " uses new Shared PCH '" + ModulePCHEnvironment.PrecompiledHeaderIncludeFilename + "'" ); } else { Log.TraceVerbose( "Module " + Name + " uses a Unique PCH '" + ModulePCHEnvironment.PrecompiledHeaderIncludeFilename + "'" ); } } } else { Log.TraceVerbose( "Module " + Name + " doesn't use a Shared PCH, and only has " + SourceFilesToBuild.CPPFiles.Count.ToString() + " source file(s). No Unique PCH will be generated." ); } // Compile the C++ source or the unity C++ files that use a PCH environment. if( ModulePCHEnvironment != null ) { // Setup a new compile environment for this module's source files. It's pretty much the exact same as the // module's compile environment, except that it will include a PCH file. var ModulePCHCompileEnvironment = ModuleCompileEnvironment.DeepCopy(); ModulePCHCompileEnvironment.Config.PrecompiledHeaderAction = PrecompiledHeaderAction.Include; ModulePCHCompileEnvironment.Config.PrecompiledHeaderIncludeFilename = ModulePCHEnvironment.PrecompiledHeaderIncludeFilename.AbsolutePath; ModulePCHCompileEnvironment.Config.PCHHeaderNameInCode = ModulePCHEnvironment.PCHHeaderNameInCode; if( SharedPCHHeaderFile != null ) { // Shared PCH headers need to be force included, because we're basically forcing the module to use // the precompiled header that we want, instead of the "first include" in each respective .cpp file ModulePCHCompileEnvironment.Config.bForceIncludePrecompiledHeader = true; } var CPPFilesToBuild = SourceFilesToBuild.CPPFiles; if (bModuleUsesUnityBuild) { // unity files generated for only the set of files which share the same PCH environment CPPFilesToBuild = Unity.GenerateUnityCPPs( Target, CPPFilesToBuild, ModulePCHCompileEnvironment, Name ); } // Check if there are enough unity files to warrant pch generation (and we haven't already generated the shared one) if( ModulePCHEnvironment.PrecompiledHeaderFile == null ) { if( SharedPCHHeaderFile != null || CPPFilesToBuild.Count >= MinFilesUsingPrecompiledHeader ) { CPPOutput PCHOutput; if (SharedPCHHeaderFile == null) { PCHOutput = PrecompileHeaderEnvironment.GeneratePCHCreationAction( Target, CPPFilesToBuild[0].PCHHeaderNameInCode, ModulePCHEnvironment.PrecompiledHeaderIncludeFilename, ModuleCompileEnvironment, ModuleCompileEnvironment.Config.OutputDirectory, Name, true ); } else { UEBuildModuleCPP SharedPCHModule = (UEBuildModuleCPP)Target.FindOrCreateModuleByName(SharedPCHModuleName); CPPEnvironment SharedPCHCompileEnvironment = GlobalCompileEnvironment.DeepCopy(); SharedPCHCompileEnvironment.Config.bEnableShadowVariableWarning = SharedPCHModule.bEnableShadowVariableWarnings; SharedPCHModule.SetupPublicCompileEnvironment( Binary, false, SharedPCHCompileEnvironment.Config.CPPIncludeInfo.IncludePaths, SharedPCHCompileEnvironment.Config.CPPIncludeInfo.SystemIncludePaths, SharedPCHCompileEnvironment.Config.Definitions, SharedPCHCompileEnvironment.Config.AdditionalFrameworks, new Dictionary<UEBuildModule,bool>()); PCHOutput = PrecompileHeaderEnvironment.GeneratePCHCreationAction( Target, CPPFilesToBuild[0].PCHHeaderNameInCode, ModulePCHEnvironment.PrecompiledHeaderIncludeFilename, SharedPCHCompileEnvironment, Path.Combine( CompileEnvironment.Config.OutputDirectory, "SharedPCHs" ), "Shared", false ); } ModulePCHEnvironment.PrecompiledHeaderFile = PCHOutput.PrecompiledHeaderFile; ModulePCHEnvironment.OutputObjectFiles.Clear(); ModulePCHEnvironment.OutputObjectFiles.AddRange( PCHOutput.ObjectFiles ); } else if( CPPFilesToBuild.Count < MinFilesUsingPrecompiledHeader ) { Log.TraceVerbose( "Module " + Name + " doesn't use a Shared PCH, and only has " + CPPFilesToBuild.Count.ToString() + " unity source file(s). No Unique PCH will be generated." ); } } if( ModulePCHEnvironment.PrecompiledHeaderFile != null ) { // Link in the object files produced by creating the precompiled header. LinkInputFiles.AddRange( ModulePCHEnvironment.OutputObjectFiles ); // if pch action was generated for the environment then use pch ModulePCHCompileEnvironment.PrecompiledHeaderFile = ModulePCHEnvironment.PrecompiledHeaderFile; // Use this compile environment from now on CPPCompileEnvironment = ModulePCHCompileEnvironment; } LinkInputFiles.AddRange( CPPCompileEnvironment.CompileFiles( Target, CPPFilesToBuild, Name ).ObjectFiles ); bWasModuleCodeCompiled = true; } if( BuildConfiguration.bPrintPerformanceInfo ) { var PCHGenTime = ( DateTime.UtcNow - PCHGenTimerStart ).TotalSeconds; TotalPCHGenTime += PCHGenTime; } } if( !bWasModuleCodeCompiled && SourceFilesToBuild.CPPFiles.Count > 0 ) { var CPPFilesToCompile = SourceFilesToBuild.CPPFiles; if (bModuleUsesUnityBuild) { CPPFilesToCompile = Unity.GenerateUnityCPPs( Target, CPPFilesToCompile, CPPCompileEnvironment, Name ); } LinkInputFiles.AddRange( CPPCompileEnvironment.CompileFiles( Target, CPPFilesToCompile, Name ).ObjectFiles ); } if (AutoGenerateCppInfo != null && AutoGenerateCppInfo.BuildInfo != null && !CPPCompileEnvironment.bHackHeaderGenerator) { string[] GeneratedFiles = Directory.GetFiles(Path.GetDirectoryName(AutoGenerateCppInfo.BuildInfo.FileWildcard), Path.GetFileName(AutoGenerateCppInfo.BuildInfo.FileWildcard)); foreach (string GeneratedFilename in GeneratedFiles) { var GeneratedCppFileItem = FileItem.GetItemByPath(GeneratedFilename); CachePCHUsageForModuleSourceFile(this.Target, CPPCompileEnvironment, GeneratedCppFileItem); // @todo ubtmake: Check for ALL other places where we might be injecting .cpp or .rc files for compiling without caching CachedCPPIncludeInfo first (anything platform specific?) LinkInputFiles.AddRange(CPPCompileEnvironment.CompileFiles(Target, new List<FileItem> { GeneratedCppFileItem }, Name).ObjectFiles); } } // Compile C files directly. LinkInputFiles.AddRange(CPPCompileEnvironment.CompileFiles( Target, SourceFilesToBuild.CFiles, Name).ObjectFiles); // Compile CC files directly. LinkInputFiles.AddRange(CPPCompileEnvironment.CompileFiles( Target, SourceFilesToBuild.CCFiles, Name).ObjectFiles); // Compile MM files directly. LinkInputFiles.AddRange(CPPCompileEnvironment.CompileFiles( Target, SourceFilesToBuild.MMFiles, Name).ObjectFiles); // Compile RC files. LinkInputFiles.AddRange(CPPCompileEnvironment.CompileRCFiles(Target, SourceFilesToBuild.RCFiles).ObjectFiles); return LinkInputFiles; }
/// <summary> /// Checks if any of the commands to execute has [RequireP4] attribute. /// </summary> /// <param name="CommandsToExecute">List of commands to be executed.</param> /// <param name="Commands">Commands.</param> /// <returns>True if any of the commands to execute has [RequireP4] attribute.</returns> private static bool CheckIfCommandsRequireP4(List<CommandInfo> CommandsToExecute, CaselessDictionary<Type> Commands) { foreach (var CommandInfo in CommandsToExecute) { Type Command; if (Commands.TryGetValue(CommandInfo.CommandName, out Command)) { var RequireP4Attributes = Command.GetCustomAttributes(typeof(RequireP4Attribute), true); if (!CommandUtils.IsNullOrEmpty(RequireP4Attributes)) { Log("Command {0} requires P4 functionality.", Command.Name); return true; } } } return false; }
/// <summary> /// Sets up P4Enabled, AllowSubmit properties. Note that this does not intialize P4 environment. /// </summary> /// <param name="CommandsToExecute">Commands to execute</param> /// <param name="Commands">Commands</param> internal static void InitP4Support(List<CommandInfo> CommandsToExecute, CaselessDictionary<Type> Commands) { // Init AllowSubmit // If we do not specify on the commandline if submitting is allowed or not, this is // depending on whether we run locally or on a build machine. Log("Initializing AllowSubmit."); if (GlobalCommandLine.Submit || GlobalCommandLine.NoSubmit) { AllowSubmit = GlobalCommandLine.Submit; } else { AllowSubmit = Automation.IsBuildMachine; } Log("AllowSubmit={0}", AllowSubmit); // Init P4Enabled Log("Initializing P4Enabled."); if (Automation.IsBuildMachine) { P4Enabled = !GlobalCommandLine.NoP4; } else { P4Enabled = GlobalCommandLine.P4; if (!P4Enabled && !GlobalCommandLine.NoP4) { // Check if any of the commands to execute require P4 P4Enabled = CheckIfCommandsRequireP4(CommandsToExecute, Commands); } } Log("P4Enabled={0}", P4Enabled); }
/// <summary> /// Parses P4 forms and stores them as a key/value pairs. /// </summary> /// <param name="Output">P4 command output (must be a form).</param> /// <returns>Parsed output.</returns> public static CaselessDictionary<string> ParseTaggedP4Output(string Output) { var Tags = new CaselessDictionary<string>(); var Lines = Output.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries); string DelayKey = ""; int DelayIndex = 0; foreach (var Line in Lines) { var TrimmedLine = Line.Trim(); if (TrimmedLine.StartsWith("#") == false) { if (DelayKey != "") { if (Line.StartsWith("\t")) { if (DelayIndex > 0) { Tags.Add(String.Format("{0}{1}", DelayKey, DelayIndex), TrimmedLine); } else { Tags.Add(DelayKey, TrimmedLine); } DelayIndex++; continue; } DelayKey = ""; DelayIndex = 0; } var KeyEndIndex = TrimmedLine.IndexOf(':'); if (KeyEndIndex >= 0) { var Key = TrimmedLine.Substring(0, KeyEndIndex); var Value = TrimmedLine.Substring(KeyEndIndex + 1).Trim(); if (Value == "") { DelayKey = Key; } else { Tags.Add(Key, Value); } } } } return Tags; }
/// <summary> /// Finds and/or compiles all script files and assemblies. /// </summary> /// <param name="ScriptsForProjectFileName">Path to the current project. May be null, in which case we compile scripts for all projects.</param> /// <param name="AdditionalScriptsFolders">Additional script fodlers to look for source files in.</param> public void FindAndCompileAllScripts(string ScriptsForProjectFileName, List<string> AdditionalScriptsFolders) { bool DoCompile = false; if (GlobalCommandLine.Compile) { DoCompile = true; } // Change to Engine\Source (if exists) to properly discover all UBT classes var OldCWD = Environment.CurrentDirectory; var UnrealBuildToolCWD = CommandUtils.CombinePaths(CommandUtils.CmdEnv.LocalRoot, "Engine", "Source"); if (Directory.Exists(UnrealBuildToolCWD)) { Environment.CurrentDirectory = UnrealBuildToolCWD; } // Register all the classes inside UBT Log.TraceVerbose("Registering UBT Classes."); UnrealBuildTool.UnrealBuildTool.RegisterAllUBTClasses(); Environment.CurrentDirectory = OldCWD; // Compile only if not disallowed. if (DoCompile && !String.IsNullOrEmpty(CommandUtils.CmdEnv.MsBuildExe)) { CleanupScriptsAssemblies(); FindAndCompileScriptModules(ScriptsForProjectFileName, AdditionalScriptsFolders); } var ScriptAssemblies = new List<Assembly>(); LoadPreCompiledScriptAssemblies(ScriptAssemblies); // Setup platforms Platform.InitializePlatforms(ScriptAssemblies.ToArray()); // Instantiate all the automation classes for interrogation Log.TraceVerbose("Creating commands."); ScriptCommands = new CaselessDictionary<Type>(); foreach (var CompiledScripts in ScriptAssemblies) { foreach (var ClassType in CompiledScripts.GetTypes()) { if (ClassType.IsSubclassOf(typeof(BuildCommand)) && ClassType.IsAbstract == false) { if (ScriptCommands.ContainsKey(ClassType.Name) == false) { ScriptCommands.Add(ClassType.Name, ClassType); } else { Log.TraceWarning("Unable to add command {0} twice. Previous: {1}, Current: {2}", ClassType.Name, ClassType.AssemblyQualifiedName, ScriptCommands[ClassType.Name].AssemblyQualifiedName); } } } } }
/// <summary> /// For any dependent module that has "SharedPCHHeaderFile" set in its module rules, we gather these headers /// and sort them in order from least-dependent to most-dependent such that larger, more complex PCH headers /// appear last in the list /// </summary> /// <returns>List of shared PCH headers to use</returns> private List<SharedPCHHeaderInfo> FindSharedPCHHeaders() { // List of modules, with all of the dependencies of that module var SharedPCHHeaderFiles = new List<SharedPCHHeaderInfo>(); // Build up our list of modules with "shared PCH headers". The list will be in dependency order, with modules // that depend on previous modules appearing later in the list foreach( var Binary in AppBinaries ) { var CPPBinary = Binary as UEBuildBinaryCPP; if( CPPBinary != null ) { foreach( var ModuleName in CPPBinary.ModuleNames ) { var CPPModule = GetModuleByName( ModuleName ) as UEBuildModuleCPP; if( CPPModule != null ) { if( !String.IsNullOrEmpty( CPPModule.SharedPCHHeaderFile ) && CPPModule.Binary.Config.bAllowCompilation ) { // @todo SharedPCH: Ideally we could figure the PCH header name automatically, and simply use a boolean in the module // definition to opt into exposing a shared PCH. Unfortunately we don't determine which private PCH header "goes with" // a module until a bit later in the process. It shouldn't be hard to change that though. var SharedPCHHeaderFilePath = ProjectFileGenerator.RootRelativePath + "/Engine/Source/" + CPPModule.SharedPCHHeaderFile; var SharedPCHHeaderFileItem = FileItem.GetExistingItemByPath( SharedPCHHeaderFilePath ); if( SharedPCHHeaderFileItem != null ) { var ModuleDependencies = new CaselessDictionary<UEBuildModule.ModuleIndexPair>(); bool bIncludeDynamicallyLoaded = false; CPPModule.GetAllDependencyModules(ModuleDependencies, bIncludeDynamicallyLoaded, bForceCircular: false, bOnlyDirectDependencies:false); // Figure out where to insert the shared PCH into our list, based off the module dependency ordering int InsertAtIndex = SharedPCHHeaderFiles.Count; for( var ExistingModuleIndex = SharedPCHHeaderFiles.Count - 1; ExistingModuleIndex >= 0; --ExistingModuleIndex ) { var ExistingModule = SharedPCHHeaderFiles[ ExistingModuleIndex ].Module; var ExistingModuleDependencies = SharedPCHHeaderFiles[ ExistingModuleIndex ].Dependencies; // If the module to add to the list is dependent on any modules already in our header list, that module // must be inserted after any of those dependencies in the list foreach( var ExistingModuleDependency in ExistingModuleDependencies ) { if( ExistingModuleDependency.Value == CPPModule ) { // Make sure we're not a circular dependency of this module. Circular dependencies always // point "upstream". That is, modules like Engine point to UnrealEd in their // CircularlyReferencedDependentModules array, but the natural dependency order is // that UnrealEd depends on Engine. We use this to avoid having modules such as UnrealEd // appear before Engine in our shared PCH list. // @todo SharedPCH: This is not very easy for people to discover. Luckily we won't have many shared PCHs in total. if( !ExistingModule.CircularlyReferencedDependentModules.Contains( CPPModule.Name ) ) { // We are at least dependent on this module. We'll keep searching the list to find // further-descendant modules we might be dependent on InsertAtIndex = ExistingModuleIndex; break; } } } } var NewSharedPCHHeaderInfo = new SharedPCHHeaderInfo(); NewSharedPCHHeaderInfo.PCHHeaderFile = SharedPCHHeaderFileItem; NewSharedPCHHeaderInfo.Module = CPPModule; NewSharedPCHHeaderInfo.Dependencies = ModuleDependencies.Values.OrderBy(x => x.Index).Select(x => x.Module).ToDictionary(M => M.Name); SharedPCHHeaderFiles.Insert( InsertAtIndex, NewSharedPCHHeaderInfo ); } else { throw new BuildException( "Could not locate the specified SharedPCH header file '{0}' for module '{1}'.", SharedPCHHeaderFilePath, CPPModule.Name ); } } } } } } if( SharedPCHHeaderFiles.Count > 0 ) { Log.TraceVerbose("Detected {0} shared PCH headers (listed in dependency order):", SharedPCHHeaderFiles.Count); foreach( var CurSharedPCH in SharedPCHHeaderFiles ) { Log.TraceVerbose(" " + CurSharedPCH.PCHHeaderFile.AbsolutePath + " (module: " + CurSharedPCH.Module.Name + ")"); } } else { Log.TraceVerbose("Did not detect any shared PCH headers"); } return SharedPCHHeaderFiles; }
/// <summary> /// Checks if any of the commands to execute has [RequireP4] attribute. /// </summary> /// <param name="CommandsToExecute">List of commands to be executed.</param> /// <param name="Commands">Commands.</param> private static void CheckIfCommandsRequireP4(List<CommandInfo> CommandsToExecute, CaselessDictionary<Type> Commands, out bool bRequireP4, out bool bRequireCL) { bRequireP4 = false; bRequireCL = false; foreach (var CommandInfo in CommandsToExecute) { Type Command; if (Commands.TryGetValue(CommandInfo.CommandName, out Command)) { var RequireP4Attributes = Command.GetCustomAttributes(typeof(RequireP4Attribute), true); if (!CommandUtils.IsNullOrEmpty(RequireP4Attributes)) { LogWarning("Command {0} requires P4 functionality.", Command.Name); bRequireP4 = true; var DoesNotNeedP4CLAttributes = Command.GetCustomAttributes(typeof(DoesNotNeedP4CLAttribute), true); if (CommandUtils.IsNullOrEmpty(DoesNotNeedP4CLAttributes)) { bRequireCL = true; } } } } }
/// <summary> /// Sets up P4Enabled, AllowSubmit properties. Note that this does not intialize P4 environment. /// </summary> /// <param name="CommandsToExecute">Commands to execute</param> /// <param name="Commands">Commands</param> internal static void InitP4Support(List<CommandInfo> CommandsToExecute, CaselessDictionary<Type> Commands) { // Init AllowSubmit // If we do not specify on the commandline if submitting is allowed or not, this is // depending on whether we run locally or on a build machine. LogVerbose("Initializing AllowSubmit."); if (GlobalCommandLine.Submit || GlobalCommandLine.NoSubmit) { AllowSubmit = GlobalCommandLine.Submit; } else { AllowSubmit = Automation.IsBuildMachine; } LogVerbose("AllowSubmit={0}", AllowSubmit); // Init P4Enabled LogVerbose("Initializing P4Enabled."); if (Automation.IsBuildMachine) { P4Enabled = !GlobalCommandLine.NoP4; P4CLRequired = P4Enabled; } else { bool bRequireP4; bool bRequireCL; CheckIfCommandsRequireP4(CommandsToExecute, Commands, out bRequireP4, out bRequireCL); P4Enabled = GlobalCommandLine.P4 || bRequireP4; P4CLRequired = GlobalCommandLine.P4 || bRequireCL; } LogVerbose("P4Enabled={0}", P4Enabled); LogVerbose("P4CLRequired={0}", P4CLRequired); }
/// <summary> /// List all available commands. /// </summary> /// <param name="Commands">All vailable commands.</param> private static void ListAvailableCommands(CaselessDictionary<Type> Commands) { string Message = Environment.NewLine; Message += "Available commands:" + Environment.NewLine; foreach (var AvailableCommand in Commands) { Message += String.Format(" {0}{1}", AvailableCommand.Key, Environment.NewLine); } CommandUtils.Log(Message); }
/// <summary> /// Display help for the specified commands (to execute) /// </summary> /// <param name="CommandsToExecute">List of commands specified in the command line.</param> /// <param name="Commands">All discovered command objects.</param> private static void DisplayHelp(List<CommandInfo> CommandsToExecute, CaselessDictionary<Type> Commands) { for (int CommandIndex = 0; CommandIndex < CommandsToExecute.Count; ++CommandIndex) { var CommandInfo = CommandsToExecute[CommandIndex]; Type CommandType; if (Commands.TryGetValue(CommandInfo.CommandName, out CommandType) == false) { Log.TraceError("Help: Failed to find command {0}", CommandInfo.CommandName); } else { CommandUtils.Help(CommandType); } } }
/// <summary> /// Execute commands specified in the command line. /// </summary> /// <param name="CommandsToExecute"></param> /// <param name="Commands"></param> private static ExitCode Execute(List<CommandInfo> CommandsToExecute, CaselessDictionary<Type> Commands) { for (int CommandIndex = 0; CommandIndex < CommandsToExecute.Count; ++CommandIndex) { var CommandInfo = CommandsToExecute[CommandIndex]; Log.TraceVerbose("Attempting to execute {0}", CommandInfo.ToString()); Type CommandType; if (!Commands.TryGetValue(CommandInfo.CommandName, out CommandType)) { throw new AutomationException("Failed to find command {0}", CommandInfo.CommandName); } BuildCommand Command = (BuildCommand)Activator.CreateInstance(CommandType); Command.Params = CommandInfo.Arguments.ToArray(); try { ExitCode Result = Command.Execute(); if(Result != ExitCode.Success) { return Result; } CommandUtils.Log("BUILD SUCCESSFUL"); } finally { // dispose of the class if necessary var CommandDisposable = Command as IDisposable; if (CommandDisposable != null) { CommandDisposable.Dispose(); } } // Make sure there's no directories on the stack. CommandUtils.ClearDirStack(); } return ExitCode.Success; }
/// <summary> /// Execute commands specified in the command line. /// </summary> /// <param name="CommandsToExecute"></param> /// <param name="Commands"></param> private static void Execute(List<CommandInfo> CommandsToExecute, CaselessDictionary<Type> Commands) { for (int CommandIndex = 0; CommandIndex < CommandsToExecute.Count; ++CommandIndex) { var CommandInfo = CommandsToExecute[CommandIndex]; Log.TraceVerbose("Attempting to execute {0}", CommandInfo.ToString()); Type CommandType; if (!Commands.TryGetValue(CommandInfo.CommandName, out CommandType)) { throw new AutomationException("Failed to find command {0}", CommandInfo.CommandName); } else { BuildCommand Command = (BuildCommand)Activator.CreateInstance(CommandType); Command.Params = CommandInfo.Arguments.ToArray(); Command.Execute(); // dispose of the class if necessary { var CommandDisposable = Command as IDisposable; if (CommandDisposable != null) { CommandDisposable.Dispose(); } } // Make sure there's no directories on the stack. CommandUtils.ClearDirStack(); } } Log.TraceInformation("Script execution successful, exiting."); }
public override void GetAllDependencyModules( CaselessDictionary<ModuleIndexPair> ReferencedModules, bool bIncludeDynamicallyLoaded, bool bForceCircular, bool bOnlyDirectDependencies ) { var AllModuleNames = new List<string>(); AllModuleNames.AddRange(PrivateDependencyModuleNames); AllModuleNames.AddRange(PublicDependencyModuleNames); if( bIncludeDynamicallyLoaded ) { AllModuleNames.AddRange(DynamicallyLoadedModuleNames); AllModuleNames.AddRange(PlatformSpecificDynamicallyLoadedModuleNames); } foreach (var DependencyName in AllModuleNames) { if (!ReferencedModules.ContainsKey(DependencyName)) { // Don't follow circular back-references! bool bIsCircular = CircularlyReferencedDependentModules.Contains( DependencyName ); if (bForceCircular || !bIsCircular) { var Module = Target.GetModuleByName( DependencyName ); ReferencedModules[ DependencyName ] = null; if( !bOnlyDirectDependencies ) { // Recurse into dependent modules first Module.GetAllDependencyModules(ReferencedModules, bIncludeDynamicallyLoaded, bForceCircular, bOnlyDirectDependencies); } ReferencedModules[ DependencyName ] = new ModuleIndexPair{ Module = Module, Index = ReferencedModules.Where(x => x.Value != null).Count() - 1 }; } } } }
/** * Gets all of the modules referenced by this module * * @param ReferencedModules Hash of all referenced modules with their addition index. * @param bIncludeDynamicallyLoaded True if dynamically loaded modules (and all of their dependent modules) should be included. * @param bForceCircular True if circular dependencies should be processed * @param bOnlyDirectDependencies True to return only this module's direct dependencies */ public virtual void GetAllDependencyModules(CaselessDictionary<ModuleIndexPair> ReferencedModules, bool bIncludeDynamicallyLoaded, bool bForceCircular, bool bOnlyDirectDependencies) { }
/// <summary> /// Generates a list of all modules referenced by this binary /// </summary> /// <param name="bIncludeDynamicallyLoaded">True if dynamically loaded modules (and all of their dependent modules) should be included.</param> /// <param name="bForceCircular">True if circular dependencies should be process</param> /// <returns>List of all referenced modules</returns> public override List<UEBuildModule> GetAllDependencyModules(bool bIncludeDynamicallyLoaded, bool bForceCircular) { var ReferencedModules = new CaselessDictionary<UEBuildModule.ModuleIndexPair>(); foreach( var ModuleName in ModuleNames ) { if( !ReferencedModules.ContainsKey( ModuleName ) ) { var Module = Target.GetModuleByName( ModuleName ); ReferencedModules[ ModuleName ] = null; Module.GetAllDependencyModules(ReferencedModules, bIncludeDynamicallyLoaded, bForceCircular, bOnlyDirectDependencies: false); ReferencedModules[ ModuleName ] = new UEBuildModule.ModuleIndexPair{ Module = Module, Index = ReferencedModules.Count }; } } return ReferencedModules.Values.OrderBy(M => M.Index).Select(M => M.Module).ToList(); }
/// <summary> /// Overrides base class to add module runtime dependencies to the build receipt. /// </summary> /// <param name="ToolChain">The platform toolchain</param> public override BuildReceipt MakeReceipt(IUEToolChain ToolChain) { BuildReceipt Receipt = base.MakeReceipt(ToolChain); // Set the IsPrecompiled flag on all the build products if we're not actually building this binary if(!Config.bAllowCompilation) { foreach(BuildProduct BuildProduct in Receipt.BuildProducts) { BuildProduct.IsPrecompiled = true; } } // Add the compiled resource file if we're building a static library containing the launch module on Windows if(Config.Type == UEBuildBinaryType.StaticLibrary && ModuleNames.Contains("Launch") && (Target.Platform == UnrealTargetPlatform.Win32 || Target.Platform == UnrealTargetPlatform.Win64)) { string ResourceFilePath = Path.Combine(Config.IntermediateDirectory, "Launch", "PCLaunch.rc.res"); Receipt.AddBuildProduct(ResourceFilePath, BuildProductType.StaticLibrary); } // Add runtime dependencies for all the modules in this binary, and build up a list of all the referenced modules var ReferencedModules = new CaselessDictionary<UEBuildModule.ModuleIndexPair>(); foreach (string ModuleName in ModuleNames) { UEBuildModule Module = Target.GetModuleByName(ModuleName); foreach(RuntimeDependency RuntimeDependency in Module.RuntimeDependencies) { Receipt.RuntimeDependencies.Add(new RuntimeDependency(RuntimeDependency)); } Module.GetAllDependencyModules(ReferencedModules, true, false, true); } // Add runtime dependencies for all the referenced external modules. These may be introduce dependencies for the binary without actually being listed for inclusion in it. foreach(UEBuildExternalModule ExternalModule in ReferencedModules.Values.OrderBy(x => x.Index).Select(x => x.Module).OfType<UEBuildExternalModule>()) { foreach(RuntimeDependency RuntimeDependency in ExternalModule.RuntimeDependencies) { Receipt.RuntimeDependencies.Add(new RuntimeDependency(RuntimeDependency)); } } return Receipt; }
void IDeserializationCallback.OnDeserialization(object sender) { if (OnlyModules == null) { OnlyModules = new List<OnlyModule>(); } if (FlatModuleCsData == null) { FlatModuleCsData = new CaselessDictionary<FlatModuleCsDataType>(); } }