public override void FinalizeOutput(ReadOnlyTargetRules Target, TargetMakefile Makefile) { FileReference OutputFile; if (Target.ProjectFile == null) { OutputFile = FileReference.Combine(UnrealBuildTool.EngineDirectory, "Saved", "PVS-Studio", String.Format("{0}.pvslog", Target.Name)); } else { OutputFile = FileReference.Combine(Target.ProjectFile.Directory, "Saved", "PVS-Studio", String.Format("{0}.pvslog", Target.Name)); } List <FileReference> InputFiles = Makefile.OutputItems.Select(x => x.Location).Where(x => x.HasExtension(".pvslog")).ToList(); // Collect the prerequisite items off of the Compile action added in CompileCPPFiles so that in SingleFileCompile mode the PVSGather step is also not filtered out List <FileItem> AnalyzeActionPrerequisiteItems = Makefile.Actions.Where(x => x.ActionType == ActionType.Compile).SelectMany(x => x.PrerequisiteItems).ToList(); FileItem InputFileListItem = Makefile.CreateIntermediateTextFile(OutputFile.ChangeExtension(".input"), InputFiles.Select(x => x.FullName)); Action AnalyzeAction = Makefile.CreateAction(ActionType.Compile); AnalyzeAction.CommandPath = UnrealBuildTool.GetUBTPath(); AnalyzeAction.CommandArguments = String.Format("-Mode=PVSGather -Input=\"{0}\" -Output=\"{1}\"", InputFileListItem.Location, OutputFile); AnalyzeAction.WorkingDirectory = UnrealBuildTool.EngineSourceDirectory; AnalyzeAction.PrerequisiteItems.Add(InputFileListItem); AnalyzeAction.PrerequisiteItems.AddRange(Makefile.OutputItems); AnalyzeAction.PrerequisiteItems.AddRange(AnalyzeActionPrerequisiteItems); AnalyzeAction.ProducedItems.Add(FileItem.GetItemByFileReference(OutputFile)); AnalyzeAction.DeleteItems.AddRange(AnalyzeAction.ProducedItems); Makefile.OutputItems.AddRange(AnalyzeAction.ProducedItems); }
/// <summary> /// Creates an action which calls UBT recursively /// </summary> /// <param name="Type">Type of the action</param> /// <param name="Arguments">Arguments for the action</param> /// <returns>New action instance</returns> public static Action CreateRecursiveAction <T>(ActionType Type, string Arguments) where T : ToolMode { ToolModeAttribute Attribute = typeof(T).GetCustomAttribute <ToolModeAttribute>(); if (Attribute == null) { throw new BuildException("Missing ToolModeAttribute on {0}", typeof(T).Name); } Action NewAction = new Action(Type); NewAction.CommandPath = UnrealBuildTool.GetUBTPath(); NewAction.CommandArguments = String.Format("-Mode={0} {1}", Attribute.Name, Arguments); return(NewAction); }
public static string EmscriptenSDKPath() { var ConfigCache = new ConfigCacheIni(UnrealTargetPlatform.HTML5, "Engine", UnrealBuildTool.GetUProjectPath()); // always pick from the Engine the root directory and NOT the staged engine directory. string IniFile = Path.GetFullPath(Path.GetDirectoryName(UnrealBuildTool.GetUBTPath()) + "/../../") + "Config/HTML5/HTML5Engine.ini"; ConfigCache.ParseIniFile(IniFile); string PlatformName = ""; if (!Utils.IsRunningOnMono) { PlatformName = "Windows"; } else { PlatformName = "Mac"; } string EMCCPath; bool ok = ConfigCache.GetString("HTML5SDKPaths", PlatformName, out EMCCPath); if (ok && System.IO.Directory.Exists(EMCCPath)) { return(EMCCPath); } // try to find SDK Location from env. if (Environment.GetEnvironmentVariable("EMSCRIPTEN") != null && System.IO.Directory.Exists(Environment.GetEnvironmentVariable("EMSCRIPTEN")) ) { return(Environment.GetEnvironmentVariable("EMSCRIPTEN")); } return(""); }
public override bool WriteProjectFile(List <UnrealTargetPlatform> InPlatforms, List <UnrealTargetConfiguration> InConfigurations) { bool bSuccess = false; string ProjectPath = ProjectFilePath; string ProjectExtension = Path.GetExtension(ProjectFilePath); string ProjectPlatformName = BuildHostPlatform.Current.Platform.ToString(); string ProjectRelativeFilePath = this.RelativeProjectFilePath; // Get the output directory string EngineRootDirectory = Path.GetFullPath(ProjectFileGenerator.EngineRelativePath); // // Build the working directory of the Game executable. // string GameWorkingDirectory = ""; if (UnrealBuildTool.HasUProjectFile()) { GameWorkingDirectory = Path.Combine(Path.GetDirectoryName(UnrealBuildTool.GetUProjectFile()), "Binaries", ProjectPlatformName); } // // Build the working directory of the UE4Editor executable. // string UE4EditorWorkingDirectory = Path.Combine(EngineRootDirectory, "Binaries", ProjectPlatformName); // // Create the folder where the project files goes if it does not exist // String FilePath = Path.GetDirectoryName(ProjectFilePath); if ((FilePath.Length > 0) && !Directory.Exists(FilePath)) { Directory.CreateDirectory(FilePath); } string GameProjectFile = UnrealBuildTool.GetUProjectFile(); // // Write all targets which will be separate projects. // foreach (ProjectTarget target in ProjectTargets) { string[] tmp = target.ToString().Split('.'); string ProjectTargetFileName = Path.GetDirectoryName(ProjectFilePath) + "/" + tmp [0] + ProjectExtension; String ProjectName = tmp [0]; var ProjectTargetType = target.TargetRules.Type; // // Create the CodeLites root element. // XElement CodeLiteProject = new XElement("CodeLite_Project"); XAttribute CodeLiteProjectAttributeName = new XAttribute("Name", ProjectName); CodeLiteProject.Add(CodeLiteProjectAttributeName); // // Select only files we want to add. // TODO Maybe skipping those files directly in the following foreach loop is faster? // List <SourceFile> FilterSourceFile = SourceFiles.FindAll(s => ( Path.GetExtension(s.FilePath).Equals(".h") || Path.GetExtension(s.FilePath).Equals(".cpp") || Path.GetExtension(s.FilePath).Equals(".cs") || Path.GetExtension(s.FilePath).Equals(".uproject") || Path.GetExtension(s.FilePath).Equals(".ini") || Path.GetExtension(s.FilePath).Equals(".usf") )); // // Find/Create the correct virtual folder and place the file into it. // foreach (var CurrentFile in FilterSourceFile) { // // Try to get the correct relative folder representation for the project. // String CurrentFilePath = ""; // TODO It seems that the full pathname doesn't work for some files like .ini, .usf if ((ProjectTargetType == TargetRules.TargetType.Client) || (ProjectTargetType == TargetRules.TargetType.Editor) || (ProjectTargetType == TargetRules.TargetType.Server)) { if (ProjectName.Contains("UE4")) { int Idx = Path.GetFullPath(ProjectFileGenerator.EngineRelativePath).Length; CurrentFilePath = Path.GetDirectoryName(Path.GetFullPath(CurrentFile.FilePath)).Substring(Idx); } else { int IdxProjectName = ProjectName.IndexOf("Editor"); string ProjectNameRaw = ProjectName; if (IdxProjectName > 0) { ProjectNameRaw = ProjectName.Substring(0, IdxProjectName); } int Idx = Path.GetDirectoryName(Path.GetFullPath(CurrentFile.FilePath)).IndexOf(ProjectNameRaw) + ProjectNameRaw.Length; CurrentFilePath = Path.GetDirectoryName(Path.GetFullPath(CurrentFile.FilePath)).Substring(Idx); } } else if (ProjectTargetType == TargetRules.TargetType.Program) { // // We do not need all the editors subfolders to show the content. Find the correct programs subfolder. // int Idx = Path.GetDirectoryName(Path.GetFullPath(CurrentFile.FilePath)).IndexOf(ProjectName) + ProjectName.Length; CurrentFilePath = Path.GetFullPath(Path.GetDirectoryName(CurrentFile.FilePath)).Substring(Idx); } else if (ProjectTargetType == TargetRules.TargetType.Game) { // int lengthOfProjectRootPath = Path.GetFullPath(ProjectFileGenerator.MasterProjectRelativePath).Length; // CurrentFilePath = Path.GetDirectoryName(Path.GetFullPath(CurrentFile.FilePath)).Substring(lengthOfProjectRootPath); // int lengthOfProjectRootPath = EngineRootDirectory.Length; int Idx = Path.GetDirectoryName(Path.GetFullPath(CurrentFile.FilePath)).IndexOf(ProjectName) + ProjectName.Length; CurrentFilePath = Path.GetDirectoryName(Path.GetFullPath(CurrentFile.FilePath)).Substring(Idx); } string [] SplitFolders = CurrentFilePath.Split('/'); // // Set the CodeLite root folder again. // XElement root = CodeLiteProject; // // Iterate through all XElement virtual folders until we find the right place to put the file. // TODO this looks more like a hack to me. // foreach (var FolderName in SplitFolders) { if (FolderName.Equals("")) { continue; } // // Let's look if there is a virtual folder withint the current XElement. // IEnumerable <XElement> tests = root.Elements("VirtualDirectory"); if (IsEmpty(tests)) { // // No, then we have to create. // XElement vf = new XElement("VirtualDirectory"); XAttribute vfn = new XAttribute("Name", FolderName); vf.Add(vfn); root.Add(vf); root = vf; } else { // // Yes, then let's find the correct sub XElement. // bool notfound = true; // // We have some virtual directories let's find the correct one. // foreach (var element in tests) { // // Look the the following folder XAttribute attribute = element.Attribute("Name"); if (attribute.Value == FolderName) { // Ok, we found the folder as subfolder, let's use it. root = element; notfound = false; break; } } // // If we are here we didn't find any XElement with that subfolder, then we have to create. // if (notfound) { XElement vf = new XElement("VirtualDirectory"); XAttribute vfn = new XAttribute("Name", FolderName); vf.Add(vfn); root.Add(vf); root = vf; } } } // // If we are at this point we found the correct XElement folder // XElement file = new XElement("File"); XAttribute fileAttribute = new XAttribute("Name", Path.GetFullPath(CurrentFile.FilePath)); file.Add(fileAttribute); root.Add(file); } XElement CodeLiteSettings = new XElement("Settings"); CodeLiteProject.Add(CodeLiteSettings); XElement CodeLiteGlobalSettings = new XElement("GlobalSettings"); CodeLiteSettings.Add(CodeLiteSettings); foreach (var CurConf in InConfigurations) { XElement CodeLiteConfiguration = new XElement("Configuration"); XAttribute CodeLiteConfigurationName = new XAttribute("Name", CurConf.ToString()); CodeLiteConfiguration.Add(CodeLiteConfigurationName); // // Create Configuration General part. // XElement CodeLiteConfigurationGeneral = new XElement("General"); // // Create the executable filename. // string ExecutableToRun = ""; string PlatformConfiguration = "-" + ProjectPlatformName + "-" + CurConf.ToString(); switch (BuildHostPlatform.Current.Platform) { case UnrealTargetPlatform.Linux: { ExecutableToRun = "./" + ProjectName; if ((ProjectTargetType == TargetRules.TargetType.Game) || (ProjectTargetType == TargetRules.TargetType.Program)) { if (CurConf != UnrealTargetConfiguration.Development) { ExecutableToRun += PlatformConfiguration; } } else if (ProjectTargetType == TargetRules.TargetType.Editor) { ExecutableToRun = "./UE4Editor"; } } break; case UnrealTargetPlatform.Mac: { ExecutableToRun = "./" + ProjectName; if ((ProjectTargetType == TargetRules.TargetType.Game) || (ProjectTargetType == TargetRules.TargetType.Program)) { if (CurConf != UnrealTargetConfiguration.Development) { ExecutableToRun += PlatformConfiguration; } ExecutableToRun += ".app/Contents/MacOS/" + ProjectName; if (CurConf != UnrealTargetConfiguration.Development) { ExecutableToRun += PlatformConfiguration; } } else if (ProjectTargetType == TargetRules.TargetType.Editor) { ExecutableToRun = "./UE4Editor"; if (CurConf != UnrealTargetConfiguration.Development) { ExecutableToRun += PlatformConfiguration; } ExecutableToRun += ".app/Contents/MacOS/UE4Editor"; if (CurConf != UnrealTargetConfiguration.Development) { ExecutableToRun += PlatformConfiguration; } } } break; case UnrealTargetPlatform.Win64: case UnrealTargetPlatform.Win32: { ExecutableToRun = ProjectName; if ((ProjectTargetType == TargetRules.TargetType.Game) || (ProjectTargetType == TargetRules.TargetType.Program)) { if (CurConf != UnrealTargetConfiguration.Development) { ExecutableToRun += PlatformConfiguration; } } else if (ProjectTargetType == TargetRules.TargetType.Editor) { ExecutableToRun = "UE4Editor"; } ExecutableToRun += ".exe"; } break; default: throw new BuildException("Unsupported platform."); } // Is this project a Game type? XAttribute GeneralExecutableToRun = new XAttribute("Command", ExecutableToRun); if (ProjectTargetType == TargetRules.TargetType.Game) { if (CurConf.ToString().Contains("Debug")) { string commandArguments = " -debug"; XAttribute GeneralExecutableToRunArguments = new XAttribute("CommandArguments", commandArguments); CodeLiteConfigurationGeneral.Add(GeneralExecutableToRunArguments); } if (ProjectName.Equals("UE4Game")) { XAttribute GeneralExecutableWorkingDirectory = new XAttribute("WorkingDirectory", UE4EditorWorkingDirectory); CodeLiteConfigurationGeneral.Add(GeneralExecutableWorkingDirectory); } else { XAttribute GeneralExecutableWorkingDirectory = new XAttribute("WorkingDirectory", GameWorkingDirectory); CodeLiteConfigurationGeneral.Add(GeneralExecutableWorkingDirectory); } } else if (ProjectTargetType == TargetRules.TargetType.Editor) { if (ProjectName != "UE4Editor") { string commandArguments = "\"" + GameProjectFile + "\"" + " -game"; XAttribute CommandArguments = new XAttribute("CommandArguments", commandArguments); CodeLiteConfigurationGeneral.Add(CommandArguments); } XAttribute WorkingDirectory = new XAttribute("WorkingDirectory", UE4EditorWorkingDirectory); CodeLiteConfigurationGeneral.Add(WorkingDirectory); } else if (ProjectTargetType == TargetRules.TargetType.Program) { XAttribute WorkingDirectory = new XAttribute("WorkingDirectory", UE4EditorWorkingDirectory); CodeLiteConfigurationGeneral.Add(WorkingDirectory); } else if (ProjectTargetType == TargetRules.TargetType.Client) { XAttribute WorkingDirectory = new XAttribute("WorkingDirectory", UE4EditorWorkingDirectory); CodeLiteConfigurationGeneral.Add(WorkingDirectory); } else if (ProjectTargetType == TargetRules.TargetType.Server) { XAttribute WorkingDirectory = new XAttribute("WorkingDirectory", UE4EditorWorkingDirectory); CodeLiteConfigurationGeneral.Add(WorkingDirectory); } CodeLiteConfigurationGeneral.Add(GeneralExecutableToRun); CodeLiteConfiguration.Add(CodeLiteConfigurationGeneral); // // End of Create Configuration General part. // // // Create Configuration Custom Build part. // XElement CodeLiteConfigurationCustomBuild = new XElement("CustomBuild"); CodeLiteConfiguration.Add(CodeLiteConfigurationGeneral); XAttribute CodeLiteConfigurationCustomBuildEnabled = new XAttribute("Enabled", "yes"); CodeLiteConfigurationCustomBuild.Add(CodeLiteConfigurationCustomBuildEnabled); // // Add the working directory for the custom build commands. // XElement CustomBuildWorkingDirectory = new XElement("WorkingDirectory"); XText CustuomBuildWorkingDirectory = new XText(Path.GetDirectoryName(UnrealBuildTool.GetUBTPath())); CustomBuildWorkingDirectory.Add(CustuomBuildWorkingDirectory); CodeLiteConfigurationCustomBuild.Add(CustomBuildWorkingDirectory); // // End of Add the working directory for the custom build commands. // // // Make Build Target. // XElement CustomBuildCommand = new XElement("BuildCommand"); CodeLiteConfigurationCustomBuild.Add(CustomBuildCommand); string BuildTarget = Path.GetFileName(UnrealBuildTool.GetUBTPath()) + " " + ProjectName + " " + ProjectPlatformName + " " + CurConf.ToString(); if ((BuildHostPlatform.Current.Platform != UnrealTargetPlatform.Win64) && (BuildHostPlatform.Current.Platform != UnrealTargetPlatform.Win32)) { BuildTarget = "mono " + BuildTarget; } if (GameProjectFile.Length > 0) { BuildTarget += " -project=" + "\"" + GameProjectFile + "\""; } XText commandLine = new XText(BuildTarget); CustomBuildCommand.Add(commandLine); // // End of Make Build Target // // // Clean Build Target. // XElement CustomCleanCommand = new XElement("CleanCommand"); CodeLiteConfigurationCustomBuild.Add(CustomCleanCommand); string CleanTarget = BuildTarget + " -clean"; XText CleanCommandLine = new XText(CleanTarget); CustomCleanCommand.Add(CleanCommandLine); // // End of Clean Build Target. // // // Rebuild Build Target. // XElement CustomRebuildCommand = new XElement("RebuildCommand"); CodeLiteConfigurationCustomBuild.Add(CustomRebuildCommand); string RebuildTarget = CleanTarget + "\n" + BuildTarget; XText RebuildCommandLine = new XText(RebuildTarget); CustomRebuildCommand.Add(RebuildCommandLine); // // End of Clean Build Target. // // // Some other fun Custom Targets. // if (ProjectTargetType == TargetRules.TargetType.Game) { string CookGameCommandLine = "mono AutomationTool.exe BuildCookRun "; // Projects filename CookGameCommandLine += "-project=\"" + UnrealBuildTool.GetUProjectFile() + "\" "; // Disables Perforce functionality CookGameCommandLine += "-noP4 "; // Do not kill any spawned processes on exit CookGameCommandLine += "-nokill "; CookGameCommandLine += "-clientconfig=" + CurConf.ToString() + " "; CookGameCommandLine += "-serverconfig=" + CurConf.ToString() + " "; CookGameCommandLine += "-platform=" + ProjectPlatformName + " "; CookGameCommandLine += "-targetplatform=" + ProjectPlatformName + " "; // TODO Maybe I can add all the supported one. CookGameCommandLine += "-nocompile "; CookGameCommandLine += "-compressed -stage -deploy"; // // Cook Game. // XElement CookGame = new XElement("Target"); XAttribute CookGameName = new XAttribute("Name", "Cook Game"); XText CookGameCommand = new XText(CookGameCommandLine + " -cook"); CookGame.Add(CookGameName); CookGame.Add(CookGameCommand); CodeLiteConfigurationCustomBuild.Add(CookGame); XElement CookGameOnTheFly = new XElement("Target"); XAttribute CookGameNameOnTheFlyName = new XAttribute("Name", "Cook Game on the fly"); XText CookGameOnTheFlyCommand = new XText(CookGameCommandLine + " -cookonthefly"); CookGameOnTheFly.Add(CookGameNameOnTheFlyName); CookGameOnTheFly.Add(CookGameOnTheFlyCommand); CodeLiteConfigurationCustomBuild.Add(CookGameOnTheFly); XElement SkipCook = new XElement("Target"); XAttribute SkipCookName = new XAttribute("Name", "Skip Cook Game"); XText SkipCookCommand = new XText(CookGameCommandLine + " -skipcook"); SkipCook.Add(SkipCookName); SkipCook.Add(SkipCookCommand); CodeLiteConfigurationCustomBuild.Add(SkipCook); } // // End of Some other fun Custom Targets. // CodeLiteConfiguration.Add(CodeLiteConfigurationCustomBuild); // // End of Create Configuration Custom Build part. // CodeLiteSettings.Add(CodeLiteConfiguration); } CodeLiteSettings.Add(CodeLiteGlobalSettings); // // Save the XML file. // CodeLiteProject.Save(ProjectTargetFileName); bSuccess = true; } return(bSuccess); }
/// <summary> /// Builds and runs the header tool and touches the header directories. /// Performs any early outs if headers need no changes, given the UObject modules, tool path, game name, and configuration /// </summary> public static bool ExecuteHeaderToolIfNecessary(UEToolChain ToolChain, UEBuildTarget Target, CPPEnvironment GlobalCompileEnvironment, List <UHTModuleInfo> UObjectModules, FileReference ModuleInfoFileName, ref ECompilationResult UHTResult) { if (ProgressWriter.bWriteMarkup) { Log.WriteLine(LogEventType.Console, "@progress push 5%"); } using (ProgressWriter Progress = new ProgressWriter("Generating code...", false)) { // We never want to try to execute the header tool when we're already trying to build it! var bIsBuildingUHT = Target.GetTargetName().Equals("UnrealHeaderTool", StringComparison.InvariantCultureIgnoreCase); var RootLocalPath = Path.GetFullPath(ProjectFileGenerator.RootRelativePath); // check if UHT is out of date DateTime HeaderToolTimestamp = DateTime.MaxValue; bool bHaveHeaderTool = !bIsBuildingUHT && GetHeaderToolTimestamp(out HeaderToolTimestamp); // ensure the headers are up to date bool bUHTNeedsToRun = (UEBuildConfiguration.bForceHeaderGeneration == true || !bHaveHeaderTool || AreGeneratedCodeFilesOutOfDate(UObjectModules, HeaderToolTimestamp)); if (bUHTNeedsToRun || UnrealBuildTool.IsGatheringBuild) { // Since code files are definitely out of date, we'll now finish computing information about the UObject modules for UHT. We // want to save this work until we know that UHT actually needs to be run to speed up best-case iteration times. if (UnrealBuildTool.IsGatheringBuild) // In assembler-only mode, PCH info is loaded from our UBTMakefile! { foreach (var UHTModuleInfo in UObjectModules) { // Only cache the PCH name if we don't already have one. When running in 'gather only' mode, this will have already been cached if (string.IsNullOrEmpty(UHTModuleInfo.PCH)) { UHTModuleInfo.PCH = ""; // We need to figure out which PCH header this module is including, so that UHT can inject an include statement for it into any .cpp files it is synthesizing var DependencyModuleCPP = (UEBuildModuleCPP)Target.GetModuleByName(UHTModuleInfo.ModuleName); var ModuleCompileEnvironment = DependencyModuleCPP.CreateModuleCompileEnvironment(GlobalCompileEnvironment); DependencyModuleCPP.CachePCHUsageForModuleSourceFiles(ModuleCompileEnvironment); if (DependencyModuleCPP.ProcessedDependencies.UniquePCHHeaderFile != null) { UHTModuleInfo.PCH = DependencyModuleCPP.ProcessedDependencies.UniquePCHHeaderFile.AbsolutePath; } } } } } // @todo ubtmake: Optimization: Ideally we could avoid having to generate this data in the case where UHT doesn't even need to run! Can't we use the existing copy? (see below use of Manifest) UHTManifest Manifest = new UHTManifest(Target, RootLocalPath, ToolChain.ConvertPath(RootLocalPath + '\\'), UObjectModules); if (!bIsBuildingUHT && bUHTNeedsToRun) { // Always build UnrealHeaderTool if header regeneration is required, unless we're running within a Rocket ecosystem or hot-reloading if (UnrealBuildTool.RunningRocket() == false && UEBuildConfiguration.bDoNotBuildUHT == false && UEBuildConfiguration.bHotReloadFromIDE == false && !(bHaveHeaderTool && !UnrealBuildTool.IsGatheringBuild && UnrealBuildTool.IsAssemblingBuild)) // If running in "assembler only" mode, we assume UHT is already up to date for much faster iteration! { // If it is out of date or not there it will be built. // If it is there and up to date, it will add 0.8 seconds to the build time. Log.TraceInformation("Building UnrealHeaderTool..."); var UBTArguments = new StringBuilder(); UBTArguments.Append("UnrealHeaderTool"); // Which desktop platform do we need to compile UHT for? UBTArguments.Append(" " + BuildHostPlatform.Current.Platform.ToString()); // NOTE: We force Development configuration for UHT so that it runs quickly, even when compiling debug UBTArguments.Append(" " + UnrealTargetConfiguration.Development.ToString()); // NOTE: We disable mutex when launching UBT from within UBT to compile UHT UBTArguments.Append(" -NoMutex"); if (UnrealBuildTool.CommandLineContains("-noxge")) { UBTArguments.Append(" -noxge"); } // Propagate command-line option if (UnrealBuildTool.CommandLineContains("-2015")) { UBTArguments.Append(" -2015"); } if (UnrealBuildTool.CommandLineContains("-2013")) { UBTArguments.Append(" -2013"); } // Add UHT plugins to UBT command line as external plugins if (Target.UnrealHeaderToolPlugins != null && Target.UnrealHeaderToolPlugins.Count > 0) { foreach (PluginInfo Plugin in Target.UnrealHeaderToolPlugins) { UBTArguments.Append(" -PLUGIN \"" + Plugin.File + "\""); } } if (RunExternalExecutable(UnrealBuildTool.GetUBTPath(), UBTArguments.ToString()) != 0) { return(false); } } Progress.Write(1, 3); var ActualTargetName = String.IsNullOrEmpty(Target.GetTargetName()) ? "UE4" : Target.GetTargetName(); Log.TraceInformation("Parsing headers for {0}", ActualTargetName); string HeaderToolPath = GetHeaderToolPath(); if (!File.Exists(HeaderToolPath)) { throw new BuildException("Unable to generate headers because UnrealHeaderTool binary was not found ({0}).", Path.GetFullPath(HeaderToolPath)); } // Disable extensions when serializing to remove the $type fields Directory.CreateDirectory(ModuleInfoFileName.Directory.FullName); System.IO.File.WriteAllText(ModuleInfoFileName.FullName, fastJSON.JSON.Instance.ToJSON(Manifest, new fastJSON.JSONParameters { UseExtensions = false })); string CmdLine = (Target.ProjectFile != null) ? "\"" + Target.ProjectFile.FullName + "\"" : Target.GetTargetName(); CmdLine += " \"" + ModuleInfoFileName + "\" -LogCmds=\"loginit warning, logexit warning, logdatabase error\" -Unattended -WarningsAsErrors"; if (UnrealBuildTool.IsEngineInstalled()) { CmdLine += " -installed"; } if (UEBuildConfiguration.bFailIfGeneratedCodeChanges) { CmdLine += " -FailIfGeneratedCodeChanges"; } if (!bInvalidateUHTMakefile && BuildConfiguration.bUseUHTMakefiles) { CmdLine += " -UseMakefiles"; } if (!UEBuildConfiguration.bCompileAgainstEngine) { CmdLine += " -NoEnginePlugins"; } Log.TraceInformation(" Running UnrealHeaderTool {0}", CmdLine); Stopwatch s = new Stopwatch(); s.Start(); UHTResult = (ECompilationResult)RunExternalExecutable(ExternalExecution.GetHeaderToolPath(), CmdLine); s.Stop(); if (UHTResult != ECompilationResult.Succeeded) { // On Linux and Mac, the shell will return 128+signal number exit codes if UHT gets a signal (e.g. crashes or is interrupted) if ((BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Linux || BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Mac) && (int)(UHTResult) >= 128 ) { // SIGINT is 2, so 128 + SIGINT is 130 UHTResult = ((int)(UHTResult) == 130) ? ECompilationResult.Canceled : ECompilationResult.CrashOrAssert; } Log.TraceInformation("Error: Failed to generate code for {0} - error code: {2} ({1})", ActualTargetName, (int)UHTResult, UHTResult.ToString()); return(false); } Log.TraceInformation("Reflection code generated for {0} in {1} seconds", ActualTargetName, s.Elapsed.TotalSeconds); if (BuildConfiguration.bPrintPerformanceInfo) { Log.TraceInformation("UnrealHeaderTool took {1}", ActualTargetName, (double)s.ElapsedMilliseconds / 1000.0); } // Now that UHT has successfully finished generating code, we need to update all cached FileItems in case their last write time has changed. // Otherwise UBT might not detect changes UHT made. DateTime StartTime = DateTime.UtcNow; FileItem.ResetInfos(); double ResetDuration = (DateTime.UtcNow - StartTime).TotalSeconds; Log.TraceVerbose("FileItem.ResetInfos() duration: {0}s", ResetDuration); } else { Log.TraceVerbose("Generated code is up to date."); } Progress.Write(2, 3); // There will never be generated code if we're building UHT, so this should never be called. if (!bIsBuildingUHT) { // Allow generated code to be sync'd to remote machines if needed. This needs to be done even if UHT did not run because // generated headers include other generated headers using absolute paths which in case of building remotely are already // the remote machine absolute paths. Because of that parsing headers will not result in finding all includes properly. // @todo ubtmake: Need to figure out what this does in the assembler case, and whether we need to run it ToolChain.PostCodeGeneration(Manifest); } // touch the directories UpdateDirectoryTimestamps(UObjectModules); Progress.Write(3, 3); } if (ProgressWriter.bWriteMarkup) { Log.WriteLine(LogEventType.Console, "@progress pop"); } return(true); }
/// <summary> /// Checks to see if the assembly needs compilation /// </summary> /// <param name="SourceFiles">Set of source files</param> /// <param name="AssemblyManifestFilePath">File containing information about this assembly, like which source files it was built with and engine version</param> /// <param name="OutputAssemblyPath">Output path for the assembly</param> /// <returns>True if the assembly needs to be built</returns> private static bool RequiresCompilation(HashSet <FileReference> SourceFiles, FileReference AssemblyManifestFilePath, FileReference OutputAssemblyPath) { // Check to see if we already have a compiled assembly file on disk FileItem OutputAssemblyInfo = FileItem.GetItemByFileReference(OutputAssemblyPath); if (!OutputAssemblyInfo.Exists) { Log.TraceLog("Compiling {0}: Assembly does not exist", OutputAssemblyPath); return(true); } // Check the time stamp of the UnrealBuildTool.exe file. If Unreal Build Tool was compiled more // recently than the dynamically-compiled assembly, then we'll always recompile it. This is // because Unreal Build Tool's code may have changed in such a way that invalidate these // previously-compiled assembly files. FileItem ExecutableItem = FileItem.GetItemByFileReference(UnrealBuildTool.GetUBTPath()); if (ExecutableItem.LastWriteTimeUtc > OutputAssemblyInfo.LastWriteTimeUtc) { Log.TraceLog("Compiling {0}: {1} is newer", OutputAssemblyPath, ExecutableItem.Name); return(true); } // Make sure we have a manifest of source files used to compile the output assembly. If it doesn't exist // for some reason (not an expected case) then we'll need to recompile. FileItem AssemblySourceListFile = FileItem.GetItemByFileReference(AssemblyManifestFilePath); if (!AssemblySourceListFile.Exists) { Log.TraceLog("Compiling {0}: Missing source file list ({1})", OutputAssemblyPath, AssemblyManifestFilePath); return(true); } JsonObject Manifest = JsonObject.Read(AssemblyManifestFilePath); // check if the engine version is different string EngineVersionManifest = Manifest.GetStringField("EngineVersion"); string EngineVersionCurrent = FormatVersionNumber(ReadOnlyBuildVersion.Current); if (EngineVersionManifest != EngineVersionCurrent) { Log.TraceLog("Compiling {0}: Engine Version changed from {1} to {2}", OutputAssemblyPath, EngineVersionManifest, EngineVersionCurrent); return(true); } // Make sure the source files we're compiling are the same as the source files that were compiled // for the assembly that we want to load HashSet <FileItem> CurrentSourceFileItems = new HashSet <FileItem>(); foreach (string Line in Manifest.GetStringArrayField("SourceFiles")) { CurrentSourceFileItems.Add(FileItem.GetItemByPath(Line)); } // Get the new source files HashSet <FileItem> SourceFileItems = new HashSet <FileItem>(); foreach (FileReference SourceFile in SourceFiles) { SourceFileItems.Add(FileItem.GetItemByFileReference(SourceFile)); } // Check if there are any differences between the sets foreach (FileItem CurrentSourceFileItem in CurrentSourceFileItems) { if (!SourceFileItems.Contains(CurrentSourceFileItem)) { Log.TraceLog("Compiling {0}: Removed source file ({1})", OutputAssemblyPath, CurrentSourceFileItem); return(true); } } foreach (FileItem SourceFileItem in SourceFileItems) { if (!CurrentSourceFileItems.Contains(SourceFileItem)) { Log.TraceLog("Compiling {0}: Added source file ({1})", OutputAssemblyPath, SourceFileItem); return(true); } } // Check if any of the timestamps are newer foreach (FileItem SourceFileItem in SourceFileItems) { if (SourceFileItem.LastWriteTimeUtc > OutputAssemblyInfo.LastWriteTimeUtc) { Log.TraceLog("Compiling {0}: {1} is newer", OutputAssemblyPath, SourceFileItem); return(true); } } return(false); }