/// <summary> /// Builds UnrealHeaderTool for the specified platform. /// </summary> /// <param name="Command"></param> /// <param name="InPlatform"></param> public static void BuildUnrealHeaderTool(BuildCommand Command, UnrealBuildTool.UnrealTargetPlatform InPlatform) { BuildProduct(Command, new UE4Build.BuildTarget() { ProjectName = "", TargetName = "UnrealHeaderTool", Platform = InPlatform, Config = UnrealBuildTool.UnrealTargetConfiguration.Development, }); }
/// <summary> /// Builds BuildPatchTool for the specified platform. /// </summary> /// <param name="Command"></param> /// <param name="InPlatform"></param> public static void BuildBuildPatchTool(BuildCommand Command, UnrealBuildTool.UnrealTargetPlatform InPlatform) { BuildProduct(Command, new UE4Build.BuildTarget() { UprojectPath = null, TargetName = "BuildPatchTool", Platform = InPlatform, Config = UnrealBuildTool.UnrealTargetConfiguration.Shipping, }); }
/// <summary> /// Builds BuildPatchTool for the specified platform. /// </summary> /// <param name="Command"></param> /// <param name="InPlatform"></param> public static void BuildBuildPatchTool(BuildCommand Command, UnrealBuildTool.UnrealTargetPlatform InPlatform) { Log("Building BuildPatchTool"); var UE4Build = new UE4Build(Command); var Agenda = new UE4Build.BuildAgenda(); Agenda.Targets.Add(new UE4Build.BuildTarget() { ProjectName = "", TargetName = "BuildPatchTool", Platform = InPlatform, Config = UnrealBuildTool.UnrealTargetConfiguration.Development, }); UE4Build.Build(Agenda, InDeleteBuildProducts: true, InUpdateVersionFiles: true); UE4Build.CheckBuildProducts(UE4Build.BuildProductFiles); }
/** * 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 */ public static bool ExecuteHeaderToolIfNecessary(UEBuildTarget Target, CPPEnvironment GlobalCompileEnvironment, List <UHTModuleInfo> UObjectModules, string ModuleInfoFileName, ref ECompilationResult UHTResult) { 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 BuildPlatform = UEBuildPlatform.GetBuildPlatform(Target.Platform); var CppPlatform = BuildPlatform.GetCPPTargetPlatform(Target.Platform); var ToolChain = UEToolChain.GetPlatformToolChain(CppPlatform); var RootLocalPath = Path.GetFullPath(ProjectFileGenerator.RootRelativePath); // ensure the headers are up to date bool bUHTNeedsToRun = (UEBuildConfiguration.bForceHeaderGeneration == true || AreGeneratedCodeFilesOutOfDate(UObjectModules)); 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 && !(!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"); } 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(Path.GetDirectoryName(ModuleInfoFileName)); System.IO.File.WriteAllText(ModuleInfoFileName, fastJSON.JSON.Instance.ToJSON(Manifest, new fastJSON.JSONParameters { UseExtensions = false })); string CmdLine = (UnrealBuildTool.HasUProjectFile()) ? "\"" + UnrealBuildTool.GetUProjectFile() + "\"" : Target.GetTargetName(); CmdLine += " \"" + ModuleInfoFileName + "\" -LogCmds=\"loginit warning, logexit warning, logdatabase error\""; if (UnrealBuildTool.RunningRocket()) { CmdLine += " -rocket -installed"; } if (UEBuildConfiguration.bFailIfGeneratedCodeChanges) { CmdLine += " -FailIfGeneratedCodeChanges"; } Stopwatch s = new Stopwatch(); s.Start(); UHTResult = (ECompilationResult)RunExternalExecutable(ExternalExecution.GetHeaderToolPath(), CmdLine); s.Stop(); if (UHTResult != ECompilationResult.Succeeded) { 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}", ActualTargetName); 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); } return(true); }
/// <summary> /// Determines whether the given module name is a game module (as opposed to an engine module) /// </summary> public bool IsGameModule(string InModuleName) { FileReference ModuleFileName = GetModuleFileName(InModuleName); return(ModuleFileName != null && !UnrealBuildTool.IsUnderAnEngineDirectory(ModuleFileName.Directory)); }
/// <summary> /// Return all data driven infos found /// </summary> /// <returns></returns> public static Dictionary <string, ConfigDataDrivenPlatformInfo> GetAllPlatformInfos() { // need to init? if (PlatformInfos == null) { PlatformInfos = new Dictionary <string, ConfigDataDrivenPlatformInfo>(); Dictionary <string, string> IniParents = new Dictionary <string, string>(); // find all platform directories (skipping NFL/NoRedist) foreach (DirectoryReference EngineConfigDir in UnrealBuildTool.GetExtensionDirs(UnrealBuildTool.EngineDirectory, "Config", bIncludeRestrictedDirectories:false)) { // look through all config dirs looking for the data driven ini file foreach (string FilePath in Directory.EnumerateFiles(EngineConfigDir.FullName, "DataDrivenPlatformInfo.ini", SearchOption.AllDirectories)) { FileReference FileRef = new FileReference(FilePath); // get the platform name from the path string IniPlatformName; if (FileRef.IsUnderDirectory(DirectoryReference.Combine(UnrealBuildTool.EngineDirectory, "Config"))) { // Foo/Engine/Config/<Platform>/DataDrivenPlatformInfo.ini IniPlatformName = Path.GetFileName(Path.GetDirectoryName(FilePath)); } else { // Foo/Engine/Platforms/<Platform>/Config/DataDrivenPlatformInfo.ini IniPlatformName = Path.GetFileName(Path.GetDirectoryName(Path.GetDirectoryName(FilePath))); } // load the DataDrivenPlatformInfo from the path (with Add support in a file that doesn't use +'s in the arrays for C++ usage) ConfigFile Config = new ConfigFile(FileRef, ConfigLineAction.Add); ConfigDataDrivenPlatformInfo NewInfo = new ConfigDataDrivenPlatformInfo(); // we must have the key section ConfigFileSection Section = null; if (Config.TryGetSection("DataDrivenPlatformInfo", out Section)) { ConfigHierarchySection ParsedSection = new ConfigHierarchySection(new List <ConfigFileSection>() { Section }); // get string values string IniParent; if (ParsedSection.TryGetValue("IniParent", out IniParent)) { IniParents[IniPlatformName] = IniParent; } // slightly nasty bool parsing for bool values string Temp; if (ParsedSection.TryGetValue("bIsConfidential", out Temp) == false || ConfigHierarchy.TryParse(Temp, out NewInfo.bIsConfidential) == false) { NewInfo.bIsConfidential = false; } // get a list of additional restricted folders IReadOnlyList <string> AdditionalRestrictedFolders; if (ParsedSection.TryGetValues("AdditionalRestrictedFolders", out AdditionalRestrictedFolders) && AdditionalRestrictedFolders.Count > 0) { NewInfo.AdditionalRestrictedFolders = AdditionalRestrictedFolders.Select(x => x.Trim()).Where(x => x.Length > 0).ToArray(); } // create cache it PlatformInfos[IniPlatformName] = NewInfo; } } } // now that all are read in, calculate the ini parent chain, starting with parent-most foreach (KeyValuePair <string, ConfigDataDrivenPlatformInfo> Pair in PlatformInfos) { string CurrentPlatform; // walk up the chain and build up the ini chain List <string> Chain = new List <string>(); if (IniParents.TryGetValue(Pair.Key, out CurrentPlatform)) { while (!string.IsNullOrEmpty(CurrentPlatform)) { // insert at 0 to reverse the order Chain.Insert(0, CurrentPlatform); if (IniParents.TryGetValue(CurrentPlatform, out CurrentPlatform) == false) { break; } } } // bake it into the info if (Chain.Count > 0) { Pair.Value.IniParentChain = Chain.ToArray(); } } } return(PlatformInfos); }
/** Updates the intermediate include directory timestamps of all the passed in UObject modules */ private static void UpdateDirectoryTimestamps(List <UHTModuleInfo> UObjectModules) { foreach (var Module in UObjectModules) { string GeneratedCodeDirectory = Path.GetDirectoryName(Module.GeneratedCPPFilenameBase); var GeneratedCodeDirectoryInfo = new DirectoryInfo(GeneratedCodeDirectory); try { if (GeneratedCodeDirectoryInfo.Exists) { if (UnrealBuildTool.RunningRocket()) { // If it is an Engine folder and we are building a rocket project do NOT update the timestamp! // @todo Rocket: This contains check is hacky/fragile string FullGeneratedCodeDirectory = GeneratedCodeDirectoryInfo.FullName; FullGeneratedCodeDirectory = FullGeneratedCodeDirectory.Replace("\\", "/"); if (FullGeneratedCodeDirectory.Contains("Engine/Intermediate/Build")) { continue; } // Skip checking timestamps for engine plugin intermediate headers in Rocket PluginInfo Info = Plugins.GetPluginInfoForModule(Module.ModuleName); if (Info != null) { if (Info.LoadedFrom == PluginInfo.LoadedFromType.Engine) { continue; } } } // Touch the include directory since we have technically 'generated' the headers // However, the headers might not be touched at all since that would cause the compiler to recompile everything // We can't alter the directory timestamp directly, because this may throw exceptions when the directory is // open in visual studio or windows explorer, so instead we create a blank file that will change the timestamp for us string TimestampFile = GeneratedCodeDirectoryInfo.FullName + Path.DirectorySeparatorChar + @"Timestamp"; if (!GeneratedCodeDirectoryInfo.Exists) { GeneratedCodeDirectoryInfo.Create(); } // Save all of the UObject files to a timestamp file. We'll load these on the next run to see if any new // files with UObject classes were deleted, so that we'll know to run UHT even if the timestamps of all // of the other source files were unchanged { var AllUObjectFiles = new List <string>(); AllUObjectFiles.AddRange(Module.PublicUObjectClassesHeaders.ConvertAll(Item => Item.AbsolutePath)); AllUObjectFiles.AddRange(Module.PublicUObjectHeaders.ConvertAll(Item => Item.AbsolutePath)); AllUObjectFiles.AddRange(Module.PrivateUObjectHeaders.ConvertAll(Item => Item.AbsolutePath)); ResponseFile.Create(TimestampFile, AllUObjectFiles); } } } catch (Exception Exception) { throw new BuildException(Exception, "Couldn't touch header directories: " + Exception.Message); } } }
/// <summary> /// Determine if the given platform is available /// </summary> /// <param name="Platform">Platform to check</param> /// <param name="ProjectType">The type of project</param> /// <returns>True if supported</returns> public static bool IsValidPlatform(UnrealTargetPlatform Platform, EProjectType ProjectType = EProjectType.Any) { // HACK: For installed builds, we always need to treat Mac as a valid platform for generating project files. When remote building from PC, we won't have all the libraries to do this, so we need to fake it. if (Platform == UnrealTargetPlatform.Mac && ProjectType == EProjectType.Any && BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Mac && UnrealBuildTool.IsEngineInstalled()) { return(true); } return(ContainsValidConfiguration( (InstalledPlatformConfiguration CurConfig) => { return CurConfig.Platform == Platform && (ProjectType == EProjectType.Any || CurConfig.ProjectType == EProjectType.Any || CurConfig.ProjectType == ProjectType); } )); }
/// <summary> /// /// </summary> /// <param name="RulesFileType"></param> /// <param name="GameFolders"></param> /// <param name="ForeignPlugins"></param> /// <param name="AdditionalSearchPaths"></param> /// <param name="bIncludeEngine"></param> /// <param name="bIncludeEnterprise"></param> /// <param name="bIncludeTempTargets">Whether to include targets generated by UAT to accomodate content-only projects that need to be compiled to include plugins</param> /// <param name="bIncludePlatformExtensions"></param> /// <returns></returns> public static List <FileReference> FindAllRulesSourceFiles(RulesFileType RulesFileType, List <DirectoryReference> GameFolders, List <FileReference> ForeignPlugins, List <DirectoryReference> AdditionalSearchPaths, bool bIncludeEngine = true, bool bIncludeEnterprise = true, bool bIncludeTempTargets = true, bool bIncludePlatformExtensions = true) { List <DirectoryReference> Folders = new List <DirectoryReference>(); // Add all engine source (including third party source) if (bIncludeEngine) { Folders.Add(UnrealBuildTool.EngineSourceDirectory); } if (bIncludeEnterprise) { Folders.Add(UnrealBuildTool.EnterpriseSourceDirectory); } if (bIncludePlatformExtensions) { Folders.Add(UnrealBuildTool.EnginePlatformExtensionsDirectory); } // @todo plugin: Disallow modules from including plugin modules as dependency modules? (except when the module is part of that plugin) // Get all the root folders for plugins List <DirectoryReference> RootFolders = new List <DirectoryReference>(); if (bIncludeEngine) { RootFolders.AddRange(UnrealBuildTool.GetAllEngineDirectories()); } if (bIncludeEnterprise) { RootFolders.Add(UnrealBuildTool.EnterpriseDirectory); } if (GameFolders != null) { if (bIncludePlatformExtensions) { foreach (DirectoryReference GameFolder in GameFolders) { RootFolders.AddRange(UnrealBuildTool.GetAllProjectDirectories(GameFolder)); } } else { RootFolders.AddRange(GameFolders); } } // Find all the plugin source directories foreach (DirectoryReference RootFolder in RootFolders) { DirectoryReference PluginsFolder = DirectoryReference.Combine(RootFolder, "Plugins"); foreach (FileReference PluginFile in Plugins.EnumeratePlugins(PluginsFolder)) { Folders.Add(DirectoryReference.Combine(PluginFile.Directory, "Source")); } } // Add all the extra plugin folders if (ForeignPlugins != null) { foreach (FileReference ForeignPlugin in ForeignPlugins) { Folders.Add(DirectoryReference.Combine(ForeignPlugin.Directory, "Source")); } } // Add in the game folders to search if (GameFolders != null) { foreach (DirectoryReference GameFolder in GameFolders) { if (bIncludePlatformExtensions) { Folders.AddRange(UnrealBuildTool.GetAllProjectDirectories(GameFolder, "Source")); } else { Folders.Add(DirectoryReference.Combine(GameFolder, "Source")); } if (bIncludeTempTargets) { DirectoryReference GameIntermediateSourceFolder = DirectoryReference.Combine(GameFolder, "Intermediate", "Source"); Folders.Add(GameIntermediateSourceFolder); } } } // Process the additional search path, if sent in if (AdditionalSearchPaths != null) { foreach (DirectoryReference AdditionalSearchPath in AdditionalSearchPaths) { if (AdditionalSearchPath != null) { if (DirectoryReference.Exists(AdditionalSearchPath)) { Folders.Add(AdditionalSearchPath); } else { throw new BuildException("Couldn't find AdditionalSearchPath for rules source files '{0}'", AdditionalSearchPath); } } } } // Iterate over all the folders to check List <FileReference> SourceFiles = new List <FileReference>(); HashSet <FileReference> UniqueSourceFiles = new HashSet <FileReference>(); foreach (DirectoryReference Folder in Folders) { IReadOnlyList <FileReference> SourceFilesForFolder = FindAllRulesFiles(Folder, RulesFileType); foreach (FileReference SourceFile in SourceFilesForFolder) { if (UniqueSourceFiles.Add(SourceFile)) { SourceFiles.Add(SourceFile); } } } return(SourceFiles); }
void CleanWithUBT(string TargetName, UnrealBuildTool.UnrealTargetPlatform Platform, string Config, FileReference UprojectPath, bool ForceMonolithic = false, bool ForceNonUnity = false, bool ForceDebugInfo = false, string InAddArgs = "", bool ForceUnity = false, Dictionary<string, string> EnvVars = null) { string AddArgs = ""; if (UprojectPath != null) { AddArgs += " " + CommandUtils.MakePathSafeToUseWithCommandLine(UprojectPath.FullName); } AddArgs += " " + InAddArgs; if (ForceMonolithic) { AddArgs += " -monolithic"; } if (ForceNonUnity) { AddArgs += " -disableunity"; } if (ForceUnity) { AddArgs += " -forceunity"; } if (ForceDebugInfo) { AddArgs += " -forcedebuginfo"; } if (!TargetName.Equals("UnrealHeaderTool", StringComparison.InvariantCultureIgnoreCase)) { AddArgs += " -nobuilduht"; } PrepareUBT(); using(TelemetryStopwatch CleanStopwatch = new TelemetryStopwatch("CleanWithUBT.{0}.{1}.{2}", TargetName, Platform.ToString(), Config)) { RunUBT(CmdEnv, UBTExecutable: UBTExecutable, Project: UprojectPath, Target: TargetName, Platform: Platform.ToString(), Config: Config, AdditionalArgs: "-clean" + AddArgs, EnvVars: EnvVars); } }
private static string ResolveString(string Input, bool bIsPath) { string Result = Input; // these assume entire string is a path, and will do file operations on the whole string if (bIsPath) { if (Result.Contains("${PROGRAM_FILES}")) { // first look in real ProgramFiles string Temp = Result.Replace("${PROGRAM_FILES}", Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles, Environment.SpecialFolderOption.DoNotVerify)); if (File.Exists(Temp) || Directory.Exists(Temp)) { Result = Temp; } else { // fallback to ProgramFilesX86 Temp = Result.Replace("${PROGRAM_FILES}", Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86, Environment.SpecialFolderOption.DoNotVerify)); if (File.Exists(Temp) || Directory.Exists(Temp)) { Result = Temp; } } } if (Result.Contains("${ENGINE_ROOT}")) { string Temp = Result.Replace("${ENGINE_ROOT}", Path.GetFullPath(BuildConfiguration.RelativeEnginePath + "\\..")); // get the best version Result = LookForSpecialFile(Temp); } if (Result.Contains("${PROJECT_ROOT}")) { if (!UnrealBuildTool.HasUProjectFile()) { throw new BuildException("Configuration setting was using ${PROJECT_ROOT}, but there was no project specified"); } string Temp = Result.Replace("${PROJECT_ROOT}", Path.GetFullPath(UnrealBuildTool.GetUProjectPath())); // get the best version Result = LookForSpecialFile(Temp); } } // non path variables Result = Result.Replace("${CURRENT_USER}", Environment.UserName); // needs a resolved key (which isn't required if user is using alternate authentication) if (Result.Contains("${SSH_PRIVATE_KEY}") || Result.Contains("${CYGWIN_SSH_PRIVATE_KEY}")) { // if it needs the key, then make sure we have it! if (ResolvedSSHPrivateKey != null) { Result = Result.Replace("${SSH_PRIVATE_KEY}", ResolvedSSHPrivateKey); Result = Result.Replace("${CYGWIN_SSH_PRIVATE_KEY}", ConvertPathToCygwin(ResolvedSSHPrivateKey)); } else { Result = null; } } return(Result); }
/// <summary> /// Initialize the config system with the given types /// </summary> /// <param name="bForceLoadCache">Force use of the cached XML config without checking if it's valid (useful for remote builds)</param> public static bool ReadConfigFiles(bool bForceLoadCache) { // Find all the configurable types List <Type> ConfigTypes = FindConfigurableTypes(); // Get the path to the cache file FileReference CacheFile = FileReference.Combine(UnrealBuildTool.EngineDirectory, "Intermediate", "Build", "XmlConfigCache.bin"); if (UnrealBuildTool.IsEngineInstalled()) { DirectoryReference UserSettingsDir = Utils.GetUserSettingDirectory(); if (UserSettingsDir != null) { CacheFile = FileReference.Combine(UserSettingsDir, "UnrealEngine", String.Format("XmlConfigCache-{0}.bin", UnrealBuildTool.RootDirectory.FullName.Replace(":", "").Replace(Path.DirectorySeparatorChar, '+'))); } } // Update the cache if necessary if (bForceLoadCache) { // Never rebuild the cache; just try to load it. if (!XmlConfigData.TryRead(CacheFile, ConfigTypes, out Values)) { throw new BuildException("Unable to load XML config cache ({0})", CacheFile); } } else { // Find all the input files FileReference[] InputFiles = FindInputFiles().Select(x => x.Location).ToArray(); // Get the path to the schema FileReference SchemaFile = GetSchemaLocation(); // Try to read the existing cache from disk XmlConfigData CachedValues; if (IsCacheUpToDate(CacheFile, InputFiles) && FileReference.Exists(SchemaFile)) { if (XmlConfigData.TryRead(CacheFile, ConfigTypes, out CachedValues) && Enumerable.SequenceEqual(InputFiles, CachedValues.InputFiles)) { Values = CachedValues; } } // If that failed, regenerate it if (Values == null) { // Find all the configurable fields from the given types Dictionary <string, Dictionary <string, FieldInfo> > CategoryToFields = new Dictionary <string, Dictionary <string, FieldInfo> >(); FindConfigurableFields(ConfigTypes, CategoryToFields); // Create a schema for the config files XmlSchema Schema = CreateSchema(CategoryToFields); if (!UnrealBuildTool.IsEngineInstalled()) { WriteSchema(Schema, SchemaFile); } // Read all the XML files and validate them against the schema Dictionary <Type, Dictionary <FieldInfo, object> > TypeToValues = new Dictionary <Type, Dictionary <FieldInfo, object> >(); foreach (FileReference InputFile in InputFiles) { if (!TryReadFile(InputFile, CategoryToFields, TypeToValues, Schema)) { Log.TraceError("Failed to properly read XML file : {0}", InputFile.FullName); return(false); } } // Make sure the cache directory exists DirectoryReference.CreateDirectory(CacheFile.Directory); // Create the new cache Values = new XmlConfigData(InputFiles, TypeToValues.ToDictionary(x => x.Key, x => x.Value.ToArray())); Values.Write(CacheFile); } } // Apply all the static field values foreach (KeyValuePair <Type, KeyValuePair <FieldInfo, object>[]> TypeValuesPair in Values.TypeToValues) { foreach (KeyValuePair <FieldInfo, object> FieldValuePair in TypeValuesPair.Value) { if (FieldValuePair.Key.IsStatic) { object Value = InstanceValue(FieldValuePair.Value, FieldValuePair.Key.FieldType); FieldValuePair.Key.SetValue(null, Value); } } } return(true); }
private bool WriteCMakeLists() { string BuildCommand; const string CMakeSectionEnd = " )\n\n"; var CMakefileContent = new StringBuilder(); // Create Engine/Project specific lists StringBuilder CMakeEngineSourceFilesList = new StringBuilder("set(ENGINE_SOURCE_FILES \n"); StringBuilder CMakeProjectSourceFilesList = new StringBuilder("set(PROJECT_SOURCE_FILES \n"); StringBuilder CMakeEngineHeaderFilesList = new StringBuilder("set(ENGINE_HEADER_FILES \n"); StringBuilder CMakeProjectHeaderFilesList = new StringBuilder("set(PROJECT_HEADER_FILES \n"); StringBuilder CMakeEngineCSFilesList = new StringBuilder("set(ENGINE_CSHARP_FILES \n"); StringBuilder CMakeProjectCSFilesList = new StringBuilder("set(PROJECT_CSHARP_FILES \n"); StringBuilder CMakeEngineConfigFilesList = new StringBuilder("set(ENGINE_CONFIG_FILES \n"); StringBuilder CMakeProjectConfigFilesList = new StringBuilder("set(PROJECT_CONFIG_FILES \n"); StringBuilder CMakeEngineShaderFilesList = new StringBuilder("set(ENGINE_SHADER_FILES \n"); StringBuilder CMakeProjectShaderFilesList = new StringBuilder("set(PROJECT_SHADER_FILES \n"); StringBuilder IncludeDirectoriesList = new StringBuilder("include_directories( \n"); StringBuilder PreprocessorDefinitionsList = new StringBuilder("add_definitions( \n"); string UE4RootPath = Utils.CleanDirectorySeparators(UnrealBuildTool.RootDirectory.FullName, '/'); var CMakeGameRootPath = ""; string GameProjectPath = ""; string CMakeGameProjectFile = ""; string HostArchitecture; switch (BuildHostPlatform.Current.Platform) { case UnrealTargetPlatform.Win64: { HostArchitecture = "Win64"; BuildCommand = "call \"" + UE4RootPath + "/Engine/Build/BatchFiles/Build.bat\""; break; } case UnrealTargetPlatform.Mac: { HostArchitecture = "Mac"; BuildCommand = "cd \"" + UE4RootPath + "\" && bash \"" + UE4RootPath + "/Engine/Build/BatchFiles/" + HostArchitecture + "/Build.sh\""; bIncludeIOSTargets = true; bIncludeTVOSTargets = true; break; } case UnrealTargetPlatform.Linux: { HostArchitecture = "Linux"; BuildCommand = "cd \"" + UE4RootPath + "\" && bash \"" + UE4RootPath + "/Engine/Build/BatchFiles/" + HostArchitecture + "/Build.sh\""; break; } default: { throw new BuildException("ERROR: CMakefileGenerator does not support this platform"); } } if (IsProjectBuild) { GameProjectPath = OnlyGameProject.Directory.FullName; CMakeGameRootPath = Utils.CleanDirectorySeparators(OnlyGameProject.Directory.FullName, '/'); CMakeGameProjectFile = Utils.CleanDirectorySeparators(OnlyGameProject.FullName, '/'); } // Additional CMake file definitions string EngineHeadersFilePath = FileReference.Combine(IntermediateProjectFilesPath, CMakeEngineHeadersFileName).ToString(); string ProjectHeadersFilePath = FileReference.Combine(IntermediateProjectFilesPath, CMakeProjectHeadersFileName).ToString(); string EngineSourcesFilePath = FileReference.Combine(IntermediateProjectFilesPath, CMakeEngineSourcesFileName).ToString(); string ProjectSourcesFilePath = FileReference.Combine(IntermediateProjectFilesPath, CMakeProjectSourcesFileName).ToString(); string ProjectFilePath = FileReference.Combine(IntermediateProjectFilesPath, CMakeProjectSourcesFileName).ToString(); string IncludeFilePath = FileReference.Combine(IntermediateProjectFilesPath, CMakeIncludesFileName).ToString(); string EngineConfigsFilePath = FileReference.Combine(IntermediateProjectFilesPath, CMakeEngineConfigsFileName).ToString(); string ProjectConfigsFilePath = FileReference.Combine(IntermediateProjectFilesPath, CMakeProjectConfigsFileName).ToString(); string EngineCSFilePath = FileReference.Combine(IntermediateProjectFilesPath, CMakeEngineCSFileName).ToString(); string ProjectCSFilePath = FileReference.Combine(IntermediateProjectFilesPath, CMakeProjectCSFileName).ToString(); string EngineShadersFilePath = FileReference.Combine(IntermediateProjectFilesPath, CMakeEngineShadersFileName).ToString(); string ProjectShadersFilePath = FileReference.Combine(IntermediateProjectFilesPath, CMakeProjectShadersFileName).ToString(); string DefinitionsFilePath = FileReference.Combine(IntermediateProjectFilesPath, CMakeDefinitionsFileName).ToString(); CMakefileContent.Append( "# Makefile generated by CMakefileGenerator.cs (v1.2)\n" + "# *DO NOT EDIT*\n\n" + "cmake_minimum_required (VERSION 2.6)\n" + "project (UE4)\n\n" + "# CMake Flags\n" + "set(CMAKE_CXX_STANDARD 14)\n" + // Need to keep this updated "set(CMAKE_CXX_USE_RESPONSE_FILE_FOR_OBJECTS 1 CACHE BOOL \"\" FORCE)\n" + "set(CMAKE_CXX_USE_RESPONSE_FILE_FOR_INCLUDES 1 CACHE BOOL \"\" FORCE)\n\n" + "# Standard Includes\n" + "include(\"" + IncludeFilePath + "\")\n" + "include(\"" + DefinitionsFilePath + "\")\n" + "include(\"" + EngineHeadersFilePath + "\")\n" + "include(\"" + ProjectHeadersFilePath + "\")\n" + "include(\"" + EngineSourcesFilePath + "\")\n" + "include(\"" + ProjectSourcesFilePath + "\")\n" + "include(\"" + EngineCSFilePath + "\")\n" + "include(\"" + ProjectCSFilePath + "\")\n\n" ); List <string> IncludeDirectories = new List <string>(); List <string> PreprocessorDefinitions = new List <string>(); foreach (var CurProject in GeneratedProjectFiles) { foreach (var IncludeSearchPath in CurProject.IntelliSenseIncludeSearchPaths) { string IncludeDirectory = GetIncludeDirectory(IncludeSearchPath, Path.GetDirectoryName(CurProject.ProjectFilePath.FullName)); if (IncludeDirectory != null && !IncludeDirectories.Contains(IncludeDirectory)) { if (IncludeDirectory.Contains(UnrealBuildTool.RootDirectory.FullName)) { IncludeDirectories.Add(IncludeDirectory.Replace(UnrealBuildTool.RootDirectory.FullName, UE4RootPath)); } else { // If the path isn't rooted, then it is relative to the game root if (!Path.IsPathRooted(IncludeDirectory)) { IncludeDirectories.Add(CMakeGameRootPath + "/" + IncludeDirectory); } else { // This is a rooted path like /usr/local/sometool/include IncludeDirectories.Add(IncludeDirectory); } } } } foreach (var PreProcessorDefinition in CurProject.IntelliSensePreprocessorDefinitions) { string Definition = PreProcessorDefinition; string AlternateDefinition = Definition.Contains("=0") ? Definition.Replace("=0", "=1") : Definition.Replace("=1", "=0"); if (Definition.Equals("WITH_EDITORONLY_DATA=0") || Definition.Equals("WITH_DATABASE_SUPPORT=1")) { Definition = AlternateDefinition; } if (!PreprocessorDefinitions.Contains(Definition) && !PreprocessorDefinitions.Contains(AlternateDefinition) && !Definition.StartsWith("UE_ENGINE_DIRECTORY") && !Definition.StartsWith("ORIGINAL_FILE_NAME")) { PreprocessorDefinitions.Add(Definition); } } } // Create SourceFiles, HeaderFiles, and ConfigFiles sections. var AllModuleFiles = DiscoverModules(FindGameProjects()); foreach (FileReference CurModuleFile in AllModuleFiles) { var FoundFiles = SourceFileSearch.FindModuleSourceFiles(CurModuleFile); foreach (FileReference CurSourceFile in FoundFiles) { string SourceFileRelativeToRoot = CurSourceFile.MakeRelativeTo(UnrealBuildTool.EngineDirectory); // Exclude files/folders on a per-platform basis. if (!IsPathExcludedOnPlatform(SourceFileRelativeToRoot, BuildHostPlatform.Current.Platform)) { if (SourceFileRelativeToRoot.EndsWith(".cpp")) { AppendCleanedPathToList(CMakeEngineSourceFilesList, CMakeProjectSourceFilesList, SourceFileRelativeToRoot, CurSourceFile.FullName, GameProjectPath, UE4RootPath, CMakeGameRootPath); } else if (SourceFileRelativeToRoot.EndsWith(".h")) { AppendCleanedPathToList(CMakeEngineHeaderFilesList, CMakeProjectHeaderFilesList, SourceFileRelativeToRoot, CurSourceFile.FullName, GameProjectPath, UE4RootPath, CMakeGameRootPath); } else if (SourceFileRelativeToRoot.EndsWith(".cs")) { AppendCleanedPathToList(CMakeEngineCSFilesList, CMakeProjectCSFilesList, SourceFileRelativeToRoot, CurSourceFile.FullName, GameProjectPath, UE4RootPath, CMakeGameRootPath); } else if (SourceFileRelativeToRoot.EndsWith(".usf") || SourceFileRelativeToRoot.EndsWith(".ush")) { AppendCleanedPathToList(CMakeEngineShaderFilesList, CMakeProjectShaderFilesList, SourceFileRelativeToRoot, CurSourceFile.FullName, GameProjectPath, UE4RootPath, CMakeGameRootPath); } else if (SourceFileRelativeToRoot.EndsWith(".ini")) { AppendCleanedPathToList(CMakeEngineConfigFilesList, CMakeProjectConfigFilesList, SourceFileRelativeToRoot, CurSourceFile.FullName, GameProjectPath, UE4RootPath, CMakeGameRootPath); } } } } foreach (string IncludeDirectory in IncludeDirectories) { IncludeDirectoriesList.Append("\t\"" + Utils.CleanDirectorySeparators(IncludeDirectory, '/') + "\"\n"); } foreach (string PreprocessorDefinition in PreprocessorDefinitions) { PreprocessorDefinitionsList.Append("\t-D" + PreprocessorDefinition + "\n"); } // Add Engine/Shaders files (game are added via modules) var EngineShaderFiles = SourceFileSearch.FindFiles(DirectoryReference.Combine(UnrealBuildTool.EngineDirectory, "Shaders")); foreach (FileReference CurSourceFile in EngineShaderFiles) { string SourceFileRelativeToRoot = CurSourceFile.MakeRelativeTo(UnrealBuildTool.EngineDirectory); if (SourceFileRelativeToRoot.EndsWith(".usf") || SourceFileRelativeToRoot.EndsWith(".ush")) { AppendCleanedPathToList(CMakeEngineShaderFilesList, CMakeProjectShaderFilesList, SourceFileRelativeToRoot, CurSourceFile.FullName, GameProjectPath, UE4RootPath, CMakeGameRootPath); } } // Add Engine/Config ini files (game are added via modules) var EngineConfigFiles = SourceFileSearch.FindFiles(DirectoryReference.Combine(UnrealBuildTool.EngineDirectory, "Config")); foreach (FileReference CurSourceFile in EngineConfigFiles) { string SourceFileRelativeToRoot = CurSourceFile.MakeRelativeTo(UnrealBuildTool.EngineDirectory); if (SourceFileRelativeToRoot.EndsWith(".ini")) { AppendCleanedPathToList(CMakeEngineConfigFilesList, CMakeProjectConfigFilesList, SourceFileRelativeToRoot, CurSourceFile.FullName, GameProjectPath, UE4RootPath, CMakeGameRootPath); } } // Add section end to section strings; CMakeEngineSourceFilesList.Append(CMakeSectionEnd); CMakeEngineHeaderFilesList.Append(CMakeSectionEnd); CMakeEngineCSFilesList.Append(CMakeSectionEnd); CMakeEngineConfigFilesList.Append(CMakeSectionEnd); CMakeEngineShaderFilesList.Append(CMakeSectionEnd); CMakeProjectSourceFilesList.Append(CMakeSectionEnd); CMakeProjectHeaderFilesList.Append(CMakeSectionEnd); CMakeProjectCSFilesList.Append(CMakeSectionEnd); CMakeProjectConfigFilesList.Append(CMakeSectionEnd); CMakeProjectShaderFilesList.Append(CMakeSectionEnd); IncludeDirectoriesList.Append(CMakeSectionEnd); PreprocessorDefinitionsList.Append(CMakeSectionEnd); if (bIncludeShaderSource) { CMakefileContent.Append("# Optional Shader Include\n"); if (!IsProjectBuild || bIncludeEngineSource) { CMakefileContent.Append("include(\"" + EngineShadersFilePath + "\")\n"); CMakefileContent.Append("set_source_files_properties(${ENGINE_SHADER_FILES} PROPERTIES HEADER_FILE_ONLY TRUE)\n"); } CMakefileContent.Append("include(\"" + ProjectShadersFilePath + "\")\n"); CMakefileContent.Append("set_source_files_properties(${PROJECT_SHADER_FILES} PROPERTIES HEADER_FILE_ONLY TRUE)\n"); CMakefileContent.Append("source_group(\"Shader Files\" REGULAR_EXPRESSION .*.usf)\n\n"); } if (bIncludeConfigFiles) { CMakefileContent.Append("# Optional Config Include\n"); if (!IsProjectBuild || bIncludeEngineSource) { CMakefileContent.Append("include(\"" + EngineConfigsFilePath + "\")\n"); CMakefileContent.Append("set_source_files_properties(${ENGINE_CONFIG_FILES} PROPERTIES HEADER_FILE_ONLY TRUE)\n"); } CMakefileContent.Append("include(\"" + ProjectConfigsFilePath + "\")\n"); CMakefileContent.Append("set_source_files_properties(${PROJECT_CONFIG_FILES} PROPERTIES HEADER_FILE_ONLY TRUE)\n"); CMakefileContent.Append("source_group(\"Config Files\" REGULAR_EXPRESSION .*.ini)\n\n"); } string CMakeProjectCmdArg = ""; string UBTArguements = ""; if (bGeneratingGameProjectFiles) { UBTArguements += " -game"; } // Should the builder output progress ticks if (ProgressWriter.bWriteMarkup) { UBTArguements += " -progress"; } foreach (var Project in GeneratedProjectFiles) { foreach (var TargetFile in Project.ProjectTargets) { if (TargetFile.TargetFilePath == null) { continue; } var TargetName = TargetFile.TargetFilePath.GetFileNameWithoutAnyExtensions(); // Remove both ".cs" and ". foreach (UnrealTargetConfiguration CurConfiguration in Enum.GetValues(typeof(UnrealTargetConfiguration))) { if (CurConfiguration != UnrealTargetConfiguration.Unknown && CurConfiguration != UnrealTargetConfiguration.Development) { if (UnrealBuildTool.IsValidConfiguration(CurConfiguration) && !IsTargetExcluded(TargetName, BuildHostPlatform.Current.Platform, CurConfiguration)) { if (TargetName == GameProjectName || TargetName == (GameProjectName + "Editor")) { CMakeProjectCmdArg = "\"-project=" + CMakeGameProjectFile + "\""; } string ConfName = Enum.GetName(typeof(UnrealTargetConfiguration), CurConfiguration); CMakefileContent.Append(String.Format("add_custom_target({0}-{3}-{1} {5} {0} {3} {1} {2}{4} VERBATIM)\n", TargetName, ConfName, CMakeProjectCmdArg, HostArchitecture, UBTArguements, BuildCommand)); // Add iOS and TVOS targets if valid if (bIncludeIOSTargets && !IsTargetExcluded(TargetName, UnrealTargetPlatform.IOS, CurConfiguration)) { CMakefileContent.Append(String.Format("add_custom_target({0}-{3}-{1} {5} {0} {3} {1} {2}{4} VERBATIM)\n", TargetName, ConfName, CMakeProjectCmdArg, UnrealTargetPlatform.IOS, UBTArguements, BuildCommand)); } if (bIncludeTVOSTargets && !IsTargetExcluded(TargetName, UnrealTargetPlatform.TVOS, CurConfiguration)) { CMakefileContent.Append(String.Format("add_custom_target({0}-{3}-{1} {5} {0} {3} {1} {2}{4} VERBATIM)\n", TargetName, ConfName, CMakeProjectCmdArg, UnrealTargetPlatform.TVOS, UBTArguements, BuildCommand)); } } } } if (!IsTargetExcluded(TargetName, BuildHostPlatform.Current.Platform, UnrealTargetConfiguration.Development)) { if (TargetName == GameProjectName || TargetName == (GameProjectName + "Editor")) { CMakeProjectCmdArg = "\"-project=" + CMakeGameProjectFile + "\""; } CMakefileContent.Append(String.Format("add_custom_target({0} {4} {0} {2} Development {1}{3} VERBATIM)\n\n", TargetName, CMakeProjectCmdArg, HostArchitecture, UBTArguements, BuildCommand)); // Add iOS and TVOS targets if valid if (bIncludeIOSTargets && !IsTargetExcluded(TargetName, UnrealTargetPlatform.IOS, UnrealTargetConfiguration.Development)) { CMakefileContent.Append(String.Format("add_custom_target({0}-{3} {5} {0} {3} {1} {2}{4} VERBATIM)\n", TargetName, UnrealTargetConfiguration.Development, CMakeProjectCmdArg, UnrealTargetPlatform.IOS, UBTArguements, BuildCommand)); } if (bIncludeTVOSTargets && !IsTargetExcluded(TargetName, UnrealTargetPlatform.TVOS, UnrealTargetConfiguration.Development)) { CMakefileContent.Append(String.Format("add_custom_target({0}-{3} {5} {0} {3} {1} {2}{4} VERBATIM)\n", TargetName, UnrealTargetConfiguration.Development, CMakeProjectCmdArg, UnrealTargetPlatform.TVOS, UBTArguements, BuildCommand)); } } } } // Create Build Template if (IsProjectBuild && !bIncludeEngineSource) { CMakefileContent.AppendLine("add_executable(FakeTarget ${PROJECT_HEADER_FILES} ${PROJECT_SOURCE_FILES} ${PROJECT_CSHARP_FILES} ${PROJECT_SHADER_FILES} ${PROJECT_CONFIG_FILES})"); } else { CMakefileContent.AppendLine("add_executable(FakeTarget ${ENGINE_HEADER_FILES} ${ENGINE_SOURCE_FILES} ${ENGINE_CSHARP_FILES} ${ENGINE_SHADER_FILES} ${ENGINE_CONFIG_FILES} ${PROJECT_HEADER_FILES} ${PROJECT_SOURCE_FILES} ${PROJECT_CSHARP_FILES} ${PROJECT_SHADER_FILES} ${PROJECT_CONFIG_FILES})"); } var FullFileName = Path.Combine(MasterProjectPath.FullName, ProjectFileName); // Write out CMake files bool bWriteMakeList = WriteFileIfChanged(FullFileName, CMakefileContent.ToString()); bool bWriteEngineHeaders = WriteFileIfChanged(EngineHeadersFilePath, CMakeEngineHeaderFilesList.ToString()); bool bWriteProjectHeaders = WriteFileIfChanged(ProjectHeadersFilePath, CMakeProjectHeaderFilesList.ToString()); bool bWriteEngineSources = WriteFileIfChanged(EngineSourcesFilePath, CMakeEngineSourceFilesList.ToString()); bool bWriteProjectSources = WriteFileIfChanged(ProjectSourcesFilePath, CMakeProjectSourceFilesList.ToString()); bool bWriteIncludes = WriteFileIfChanged(IncludeFilePath, IncludeDirectoriesList.ToString()); bool bWriteDefinitions = WriteFileIfChanged(DefinitionsFilePath, PreprocessorDefinitionsList.ToString()); bool bWriteEngineConfigs = WriteFileIfChanged(EngineConfigsFilePath, CMakeEngineConfigFilesList.ToString()); bool bWriteProjectConfigs = WriteFileIfChanged(ProjectConfigsFilePath, CMakeProjectConfigFilesList.ToString()); bool bWriteEngineShaders = WriteFileIfChanged(EngineShadersFilePath, CMakeEngineShaderFilesList.ToString()); bool bWriteProjectShaders = WriteFileIfChanged(ProjectShadersFilePath, CMakeProjectShaderFilesList.ToString()); bool bWriteEngineCS = WriteFileIfChanged(EngineCSFilePath, CMakeEngineCSFilesList.ToString()); bool bWriteProjectCS = WriteFileIfChanged(ProjectCSFilePath, CMakeProjectCSFilesList.ToString()); // Return success flag if all files were written out successfully return(bWriteMakeList && bWriteEngineHeaders && bWriteProjectHeaders && bWriteEngineSources && bWriteProjectSources && bWriteEngineConfigs && bWriteProjectConfigs && bWriteEngineCS && bWriteProjectCS && bWriteEngineShaders && bWriteProjectShaders && bWriteIncludes && bWriteDefinitions); }
public static bool GeneratePList(string ProjectDirectory, bool bIsUE4Game, string GameName, string ProjectName, string InEngineDir, string ExeName) { string IntermediateDirectory = (bIsUE4Game ? InEngineDir : ProjectDirectory) + "/Intermediate/Mac"; string DestPListFile = IntermediateDirectory + "/" + ExeName + "-Info.plist"; string SrcPListFile = (bIsUE4Game ? (InEngineDir + "Source/Programs/") : (ProjectDirectory + "/Source/")) + GameName + "/Resources/Mac/Info.plist"; if (!File.Exists(SrcPListFile)) { SrcPListFile = InEngineDir + "/Source/Runtime/Launch/Resources/Mac/Info.plist"; } string PListData = null; if (File.Exists(SrcPListFile)) { PListData = File.ReadAllText(SrcPListFile); } else { return(false); } // bundle identifier // plist replacements DirectoryReference DirRef = bIsUE4Game ? (!string.IsNullOrEmpty(UnrealBuildTool.GetRemoteIniPath()) ? new DirectoryReference(UnrealBuildTool.GetRemoteIniPath()) : null) : new DirectoryReference(ProjectDirectory); ConfigHierarchy Ini = ConfigCache.ReadHierarchy(ConfigHierarchyType.Engine, DirRef, UnrealTargetPlatform.IOS); string BundleIdentifier; Ini.GetString("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "BundleIdentifier", out BundleIdentifier); string BundleVersion = MacToolChain.LoadEngineDisplayVersion(); PListData = PListData.Replace("${EXECUTABLE_NAME}", ExeName).Replace("${APP_NAME}", BundleIdentifier.Replace("[PROJECT_NAME]", ProjectName).Replace("_", "")).Replace("${ICON_NAME}", GameName).Replace("${MACOSX_DEPLOYMENT_TARGET}", MacToolChain.Settings.MinMacOSVersion).Replace("${BUNDLE_VERSION}", BundleVersion); if (!Directory.Exists(IntermediateDirectory)) { Directory.CreateDirectory(IntermediateDirectory); } File.WriteAllText(DestPListFile, PListData); return(true); }
protected override bool WriteMasterProjectFile(ProjectFile UBTProject) { bool bSuccess = true; var SolutionFileName = MasterProjectName + ".sln"; // Setup solution file content var VCSolutionFileContent = new StringBuilder(); // Solution file header if (ProjectFileFormat == VCProjectFileFormat.VisualStudio2013) { VCSolutionFileContent.Append( ProjectFileGenerator.NewLine + "Microsoft Visual Studio Solution File, Format Version 12.00" + ProjectFileGenerator.NewLine + "# Visual Studio 2013" + ProjectFileGenerator.NewLine); /* This is not required by VS 2013 to load the projects * VCSolutionFileContent.Append( * "VisualStudioVersion = 12.0.20617.1 PREVIEW" + ProjectFileGenerator.NewLine + * "MinimumVisualStudioVersion = 10.0.40219.1" + ProjectFileGenerator.NewLine );*/ } else if (ProjectFileFormat == VCProjectFileFormat.VisualStudio2012) { VCSolutionFileContent.Append( ProjectFileGenerator.NewLine + "Microsoft Visual Studio Solution File, Format Version 12.00" + ProjectFileGenerator.NewLine + "# Visual Studio 2012" + ProjectFileGenerator.NewLine); } else { throw new BuildException("Unexpected ProjectFileFormat"); } // Solution folders, files and project entries { // This the GUID that Visual Studio uses to identify a solution folder var SolutionFolderEntryGUID = "{2150E333-8FDC-42A3-9474-1A3956D46DE8}"; // Solution folders { var AllSolutionFolders = new List <MasterProjectFolder>(); System.Action <List <MasterProjectFolder> /* Folders */> GatherFoldersFunction = null; GatherFoldersFunction = FolderList => { AllSolutionFolders.AddRange(FolderList); foreach (var CurSubFolder in FolderList) { GatherFoldersFunction(CurSubFolder.SubFolders); } }; GatherFoldersFunction(RootFolder.SubFolders); foreach (VisualStudioSolutionFolder CurFolder in AllSolutionFolders) { var FolderGUIDString = CurFolder.FolderGUID.ToString("B").ToUpperInvariant(); VCSolutionFileContent.Append( "Project(\"" + SolutionFolderEntryGUID + "\") = \"" + CurFolder.FolderName + "\", \"" + CurFolder.FolderName + "\", \"" + FolderGUIDString + "\"" + ProjectFileGenerator.NewLine); // Add any files that are inlined right inside the solution folder if (CurFolder.Files.Count > 0) { VCSolutionFileContent.Append( " ProjectSection(SolutionItems) = preProject"+ ProjectFileGenerator.NewLine); foreach (var CurFile in CurFolder.Files) { // Syntax is: <relative file path> = <relative file path> VCSolutionFileContent.Append( " "+ CurFile + " = " + CurFile + ProjectFileGenerator.NewLine); } VCSolutionFileContent.Append( " EndProjectSection"+ ProjectFileGenerator.NewLine); } VCSolutionFileContent.Append( "EndProject" + ProjectFileGenerator.NewLine ); } } // Project files foreach (MSBuildProjectFile CurProject in AllProjectFiles) { // Visual Studio uses different GUID types depending on the project type string ProjectTypeGUID = CurProject.ProjectTypeGUID; // NOTE: The project name in the solution doesn't actually *have* to match the project file name on disk. However, // we prefer it when it does match so we use the actual file name here. var ProjectNameInSolution = Path.GetFileNameWithoutExtension(CurProject.ProjectFilePath); // Use the existing project's GUID that's already known to us var ProjectGUID = CurProject.ProjectGUID.ToString("B").ToUpperInvariant(); VCSolutionFileContent.Append( "Project(\"" + ProjectTypeGUID + "\") = \"" + ProjectNameInSolution + "\", \"" + CurProject.RelativeProjectFilePath + "\", \"" + ProjectGUID + "\"" + ProjectFileGenerator.NewLine); // Setup dependency on UnrealBuildTool, if we need that. This makes sure that UnrealBuildTool is // freshly compiled before kicking off any build operations on this target project if (!CurProject.IsStubProject) { // Don't add self as a project dependency! if ((CurProject != UBTProject) && (CurProject.IsGeneratedProject || (CurProject.DependsOnProjects.Count > 0))) { VCSolutionFileContent.Append( " ProjectSection(ProjectDependencies) = postProject"+ ProjectFileGenerator.NewLine); if (CurProject.IsGeneratedProject && UBTProject != null) { var UBTProjectGUID = ((MSBuildProjectFile)UBTProject).ProjectGUID.ToString("B").ToUpperInvariant(); VCSolutionFileContent.Append( " "+ UBTProjectGUID + " = " + UBTProjectGUID + ProjectFileGenerator.NewLine); } // Setup any addition dependencies this project has... foreach (var DependsOnProject in CurProject.DependsOnProjects) { var DependsOnProjectGUID = ((MSBuildProjectFile)DependsOnProject).ProjectGUID.ToString("B").ToUpperInvariant(); VCSolutionFileContent.Append( " "+ DependsOnProjectGUID + " = " + DependsOnProjectGUID + ProjectFileGenerator.NewLine); } VCSolutionFileContent.Append( " EndProjectSection"+ ProjectFileGenerator.NewLine); } } VCSolutionFileContent.Append( "EndProject" + ProjectFileGenerator.NewLine ); } } // Solution configuration platforms. This is just a list of all of the platforms and configurations that // appear in Visual Studio's build configuration selector. var SolutionConfigCombinations = new List <VCSolutionConfigCombination>(); // The "Global" section has source control, solution configurations, project configurations, // preferences, and project hierarchy data { VCSolutionFileContent.Append( "Global" + ProjectFileGenerator.NewLine); { { VCSolutionFileContent.Append( " GlobalSection(SolutionConfigurationPlatforms) = preSolution"+ ProjectFileGenerator.NewLine); var SolutionConfigurationsValidForProjects = new Dictionary <string, Tuple <UnrealTargetConfiguration, string> >(); var PlatformsValidForProjects = new HashSet <UnrealTargetPlatform>(); foreach (var CurConfiguration in SupportedConfigurations) { if (UnrealBuildTool.IsValidConfiguration(CurConfiguration)) { foreach (var CurPlatform in SupportedPlatforms) { if (UnrealBuildTool.IsValidPlatform(CurPlatform)) { foreach (var CurProject in AllProjectFiles) { if (!CurProject.IsStubProject) { if (CurProject.ProjectTargets.Count == 0) { throw new BuildException("Expecting project '" + CurProject.ProjectFilePath + "' to have at least one ProjectTarget associated with it!"); } // Figure out the set of valid target configuration names foreach (var ProjectTarget in CurProject.ProjectTargets) { if (VCProjectFile.IsValidProjectPlatformAndConfiguration(ProjectTarget, CurPlatform, CurConfiguration)) { PlatformsValidForProjects.Add(CurPlatform); // Default to a target configuration name of "Game", since that will collapse down to an empty string var TargetConfigurationName = TargetRules.TargetType.Game.ToString(); if (ProjectTarget.TargetRules != null) { TargetConfigurationName = ProjectTarget.TargetRules.ConfigurationName; } var SolutionConfigName = MakeSolutionConfigurationName(CurConfiguration, TargetConfigurationName); SolutionConfigurationsValidForProjects[SolutionConfigName] = new Tuple <UnrealTargetConfiguration, string>(CurConfiguration, TargetConfigurationName); } } } } } } } } foreach (var CurPlatform in PlatformsValidForProjects) { foreach (var SolutionConfigKeyValue in SolutionConfigurationsValidForProjects) { // e.g. "Development|Win64 = Development|Win64" var SolutionConfigName = SolutionConfigKeyValue.Key; var Configuration = SolutionConfigKeyValue.Value.Item1; var TargetConfigurationName = SolutionConfigKeyValue.Value.Item2; var SolutionPlatformName = CurPlatform.ToString(); // For Rocket, there are currently no targets that are valid to build both for Win32 and Win64. So we simply things by // only displaying a "Windows" platform and building for the appropriate Windows platform automatically based on whichever // configuration they have selected. if (UnrealBuildTool.RunningRocket() && (CurPlatform == UnrealTargetPlatform.Win32 || CurPlatform == UnrealTargetPlatform.Win64)) { SolutionPlatformName = "Windows"; if (Configuration == UnrealTargetConfiguration.Shipping) { if (CurPlatform != UnrealTargetPlatform.Win32) { continue; } } else { if (CurPlatform != UnrealTargetPlatform.Win64) { continue; } } } var SolutionConfigAndPlatformPair = SolutionConfigName + "|" + SolutionPlatformName; SolutionConfigCombinations.Add( new VCSolutionConfigCombination { VCSolutionConfigAndPlatformName = SolutionConfigAndPlatformPair, Configuration = Configuration, Platform = CurPlatform, TargetConfigurationName = TargetConfigurationName } ); } } // Sort the list of solution platform strings alphabetically (Visual Studio prefers it) SolutionConfigCombinations.Sort( new Comparison <VCSolutionConfigCombination>( (x, y) => { return(String.Compare(x.VCSolutionConfigAndPlatformName, y.VCSolutionConfigAndPlatformName, StringComparison.InvariantCultureIgnoreCase)); } ) ); var AppendedSolutionConfigAndPlatformNames = new HashSet <string>(StringComparer.InvariantCultureIgnoreCase); foreach (var SolutionConfigCombination in SolutionConfigCombinations) { // We alias "Game" and "Program" to both have the same solution configuration, so we're careful not to add the same combination twice. if (!AppendedSolutionConfigAndPlatformNames.Contains(SolutionConfigCombination.VCSolutionConfigAndPlatformName)) { VCSolutionFileContent.Append( " "+ SolutionConfigCombination.VCSolutionConfigAndPlatformName + " = " + SolutionConfigCombination.VCSolutionConfigAndPlatformName + ProjectFileGenerator.NewLine); AppendedSolutionConfigAndPlatformNames.Add(SolutionConfigCombination.VCSolutionConfigAndPlatformName); } } VCSolutionFileContent.Append( " EndGlobalSection"+ ProjectFileGenerator.NewLine); } // Assign each project's "project configuration" to our "solution platform + configuration" pairs. This // also sets up which projects are actually built when building the solution. { VCSolutionFileContent.Append( " GlobalSection(ProjectConfigurationPlatforms) = postSolution"+ ProjectFileGenerator.NewLine); var CombinationsThatWereMatchedToProjects = new List <VCSolutionConfigCombination>(); foreach (MSBuildProjectFile CurProject in AllProjectFiles) { // NOTE: We don't emit solution configuration entries for "stub" projects. Those projects are only // built using UnrealBuildTool and don't require a presence in the solution project list // NOTE: We also process projects that were "imported" here, hoping to match those to our solution // configurations. In some cases this may not be successful though. Imported projects // should always be carefully setup to match our project generator's solution configs. if (!CurProject.IsStubProject) { if (CurProject.ProjectTargets.Count == 0) { throw new BuildException("Expecting project '" + CurProject.ProjectFilePath + "' to have at least one ProjectTarget associated with it!"); } var IsProgramProject = CurProject.ProjectTargets[0].TargetRules != null && CurProject.ProjectTargets[0].TargetRules.Type == TargetRules.TargetType.Program; var GameOrProgramConfigsAlreadyMapped = new HashSet <string>(); foreach (var SolutionConfigCombination in SolutionConfigCombinations) { // Handle aliasing of Program and Game target configuration names if ((IsProgramProject && GameOrProgramConfigsAlreadyMapped.Add(SolutionConfigCombination.VCSolutionConfigAndPlatformName)) || IsProgramProject && SolutionConfigCombination.TargetConfigurationName != TargetRules.TargetType.Game.ToString() || !IsProgramProject && SolutionConfigCombination.TargetConfigurationName != TargetRules.TargetType.Program.ToString()) { var TargetConfigurationName = SolutionConfigCombination.TargetConfigurationName; if (IsProgramProject && TargetConfigurationName == TargetRules.TargetType.Game.ToString()) { TargetConfigurationName = TargetRules.TargetType.Program.ToString(); } // Now, we want to find a target in this project that maps to the current solution config combination. Only up to one target should // and every solution config combination should map to at least one target in one project (otherwise we shouldn't have added it!). ProjectTarget MatchingProjectTarget = null; foreach (var ProjectTarget in CurProject.ProjectTargets) { bool IsMatchingCombination = VCProjectFile.IsValidProjectPlatformAndConfiguration(ProjectTarget, SolutionConfigCombination.Platform, SolutionConfigCombination.Configuration); if (ProjectTarget.TargetRules != null) { if (TargetConfigurationName != ProjectTarget.TargetRules.ConfigurationName) { // Solution configuration name for this combination doesn't match this target's configuration name. It's not buildable. IsMatchingCombination = false; } } else { // UBT gets a pass because it is a dependency of every single configuration combination if (CurProject != UBTProject && !CurProject.ShouldBuildForAllSolutionTargets && TargetConfigurationName != TargetRules.TargetType.Game.ToString()) { // Can't build non-generated project in configurations except for the default (Game) IsMatchingCombination = false; } } if (IsMatchingCombination) { if (MatchingProjectTarget != null) { // Not expecting more than one target to match a single solution configuration per project! throw new BuildException("Not expecting more than one target for project " + CurProject.ProjectFilePath + " to match solution configuration " + SolutionConfigCombination.VCSolutionConfigAndPlatformName); } MatchingProjectTarget = ProjectTarget; // NOTE: For faster perf, we could "break" here and bail out early, but that would circumvent the error checking // for multiple targets within a project that may map to a single solution configuration. } } var SolutionConfiguration = SolutionConfigCombination.Configuration; var SolutionPlatform = SolutionConfigCombination.Platform; if (MatchingProjectTarget == null) { // The current configuration/platform and target configuration name doesn't map to anything our project actually supports. // We'll map it to a default config. SolutionConfiguration = UnrealTargetConfiguration.Development; // Prefer using Win64 as the default, but fall back to a platform the project file actually supports if needed. This is for // projects that can never be compiled in Windows, such as UnrealLaunchDaemon which is an iOS-only program SolutionPlatform = UnrealTargetPlatform.Win64; if (CurProject.ProjectTargets[0].TargetRules != null) { var ProjectSupportedPlatforms = new List <UnrealTargetPlatform>(); CurProject.ProjectTargets[0].TargetRules.GetSupportedPlatforms(ref ProjectSupportedPlatforms); if (!ProjectSupportedPlatforms.Contains(SolutionPlatform)) { SolutionPlatform = ProjectSupportedPlatforms[0]; } } if (IsProgramProject) { TargetConfigurationName = TargetRules.TargetType.Program.ToString(); } else { TargetConfigurationName = TargetRules.TargetType.Game.ToString(); } } // If the project wants to always build in "Development", regardless of what the solution // configuration is set to, then we'll do that here. This is used for projects like // UnrealBuildTool and ShaderCompileWorker if (MatchingProjectTarget != null) { if (MatchingProjectTarget.ForceDevelopmentConfiguration) { SolutionConfiguration = UnrealTargetConfiguration.Development; } } string ProjectConfigName; string ProjectPlatformName; CurProject.MakeProjectPlatformAndConfigurationNames(SolutionPlatform, SolutionConfiguration, TargetConfigurationName, out ProjectPlatformName, out ProjectConfigName); var ProjectConfigAndPlatformPair = ProjectConfigName.ToString() + "|" + ProjectPlatformName.ToString(); // e.g. "{4232C52C-680F-4850-8855-DC39419B5E9B}.Debug|iOS.ActiveCfg = iOS_Debug|Win32" var CurProjectGUID = CurProject.ProjectGUID.ToString("B").ToUpperInvariant(); VCSolutionFileContent.Append( " "+ CurProjectGUID + "." + SolutionConfigCombination.VCSolutionConfigAndPlatformName + ".ActiveCfg = " + ProjectConfigAndPlatformPair + ProjectFileGenerator.NewLine); // Set whether this project configuration should be built when the user initiates "build solution" if (MatchingProjectTarget != null) { VCSolutionFileContent.Append( " "+ CurProjectGUID + "." + SolutionConfigCombination.VCSolutionConfigAndPlatformName + ".Build.0 = " + ProjectConfigAndPlatformPair + ProjectFileGenerator.NewLine); var ProjGen = UEPlatformProjectGenerator.GetPlatformProjectGenerator(SolutionConfigCombination.Platform, true); if (MatchingProjectTarget.ProjectDeploys || ((ProjGen != null) && (ProjGen.GetVisualStudioDeploymentEnabled(SolutionPlatform, SolutionConfiguration) == true))) { VCSolutionFileContent.Append( " "+ CurProjectGUID + "." + SolutionConfigCombination.VCSolutionConfigAndPlatformName + ".Deploy.0 = " + ProjectConfigAndPlatformPair + ProjectFileGenerator.NewLine); } } CombinationsThatWereMatchedToProjects.Add(SolutionConfigCombination); } } } } // Check for problems foreach (var SolutionConfigCombination in SolutionConfigCombinations) { if (!CombinationsThatWereMatchedToProjects.Contains(SolutionConfigCombination)) { throw new BuildException("Unable to find a ProjectTarget that matches the solution configuration/platform mapping: " + SolutionConfigCombination.Configuration.ToString() + ", " + SolutionConfigCombination.Platform.ToString() + ", " + SolutionConfigCombination.TargetConfigurationName); } } VCSolutionFileContent.Append( " EndGlobalSection"+ ProjectFileGenerator.NewLine); } // Setup other solution properties { VCSolutionFileContent.Append( " GlobalSection(SolutionProperties) = preSolution"+ ProjectFileGenerator.NewLine); // HideSolutionNode sets whether or not the top-level solution entry is completely hidden in the UI. // We don't want that, as we need users to be able to right click on the solution tree item. VCSolutionFileContent.Append( " HideSolutionNode = FALSE"+ ProjectFileGenerator.NewLine); VCSolutionFileContent.Append( " EndGlobalSection"+ ProjectFileGenerator.NewLine); } // Solution directory hierarchy { VCSolutionFileContent.Append( " GlobalSection(NestedProjects) = preSolution"+ ProjectFileGenerator.NewLine); // Every entry in this section is in the format "Guid1 = Guid2". Guid1 is the child project (or solution // filter)'s GUID, and Guid2 is the solution filter directory to parent the child project (or solution // filter) to. This sets up the hierarchical solution explorer tree for all solution folders and projects. System.Action <StringBuilder /* VCSolutionFileContent */, List <MasterProjectFolder> /* Folders */> FolderProcessorFunction = null; FolderProcessorFunction = (LocalVCSolutionFileContent, LocalMasterProjectFolders) => { foreach (VisualStudioSolutionFolder CurFolder in LocalMasterProjectFolders) { var CurFolderGUIDString = CurFolder.FolderGUID.ToString("B").ToUpperInvariant(); foreach (MSBuildProjectFile ChildProject in CurFolder.ChildProjects) { // e.g. "{BF6FB09F-A2A6-468F-BE6F-DEBE07EAD3EA} = {C43B6BB5-3EF0-4784-B896-4099753BCDA9}" LocalVCSolutionFileContent.Append( " "+ ChildProject.ProjectGUID.ToString("B").ToUpperInvariant() + " = " + CurFolderGUIDString + ProjectFileGenerator.NewLine); } foreach (VisualStudioSolutionFolder SubFolder in CurFolder.SubFolders) { // e.g. "{BF6FB09F-A2A6-468F-BE6F-DEBE07EAD3EA} = {C43B6BB5-3EF0-4784-B896-4099753BCDA9}" LocalVCSolutionFileContent.Append( " "+ SubFolder.FolderGUID.ToString("B").ToUpperInvariant() + " = " + CurFolderGUIDString + ProjectFileGenerator.NewLine); } // Recurse into subfolders FolderProcessorFunction(LocalVCSolutionFileContent, CurFolder.SubFolders); } }; FolderProcessorFunction(VCSolutionFileContent, RootFolder.SubFolders); VCSolutionFileContent.Append( " EndGlobalSection"+ ProjectFileGenerator.NewLine); } } VCSolutionFileContent.Append( "EndGlobal" + ProjectFileGenerator.NewLine); } // Save the solution file if (bSuccess) { var SolutionFilePath = Path.Combine(MasterProjectRelativePath, SolutionFileName); bSuccess = WriteFileIfChanged(SolutionFilePath, VCSolutionFileContent.ToString()); } // Save a solution config file which selects the development editor configuration by default. if (bSuccess) { // Figure out the filename for the SUO file. VS2013 will automatically import the VS2012 options if necessary. string SolutionOptionsExtension = (ProjectFileFormat == VCProjectFileFormat.VisualStudio2012)? "v11.suo" : "v12.suo"; // Check it doesn't exist before overwriting it. Since these files store the user's preferences, it'd be bad form to overwrite them. string SolutionOptionsFileName = Path.Combine(MasterProjectRelativePath, Path.ChangeExtension(SolutionFileName, SolutionOptionsExtension)); if (!File.Exists(SolutionOptionsFileName)) { VCSolutionOptions Options = new VCSolutionOptions(); // Set the default configuration and startup project VCSolutionConfigCombination DefaultConfig = SolutionConfigCombinations.Find(x => x.Configuration == UnrealTargetConfiguration.Development && x.Platform == UnrealTargetPlatform.Win64 && x.TargetConfigurationName == "Editor"); if (DefaultConfig != null) { List <VCBinarySetting> Settings = new List <VCBinarySetting>(); Settings.Add(new VCBinarySetting("ActiveCfg", DefaultConfig.VCSolutionConfigAndPlatformName)); if (DefaultProject != null) { Settings.Add(new VCBinarySetting("StartupProject", ((MSBuildProjectFile)DefaultProject).ProjectGUID.ToString("B"))); } Options.SetConfiguration(Settings); } // Mark all the projects as closed by default, apart from the startup project VCSolutionExplorerState ExplorerState = new VCSolutionExplorerState(); foreach (ProjectFile ProjectFile in AllProjectFiles) { string ProjectName = Path.GetFileNameWithoutExtension(ProjectFile.ProjectFilePath); if (ProjectFile == DefaultProject) { ExplorerState.OpenProjects.Add(new Tuple <string, string[]>(ProjectName, new string[] { ProjectName })); } else { ExplorerState.OpenProjects.Add(new Tuple <string, string[]>(ProjectName, new string[] { })); } } if (IncludeEnginePrograms) { ExplorerState.OpenProjects.Add(new Tuple <string, string[]>("Automation", new string[0])); } Options.SetExplorerState(ExplorerState); // Write the file if (Options.Sections.Count > 0) { Options.Write(SolutionOptionsFileName); } } } return(bSuccess); }
/// <summary> /// Write the Command section for a .kdev4/$ProjectName.kdev4 file. /// </summary> /// <param name="FileContent">File content.</param> private void WriteCommandSection(ref StringBuilder FileContent) { int BuildConfigIndex = 1; var UnrealRootPath = Path.GetFullPath(ProjectFileGenerator.RootRelativePath); FileContent.Append("[CustomBuildSystem]\n"); FileContent.Append("CurrentConfiguration=BuildConfig0\n\n"); // // The Basics to get up and running with the editor, utilizing the Makefile. FileContent.Append(String.Format("[CustomBuildSystem][BuildConfig0]\nBuildDir=file://{0}\n", UnrealRootPath)); FileContent.Append("Title=BuildMeFirst\n\n"); FileContent.Append("[CustomBuildSystem][BuildConfig0][ToolBuild]\n"); FileContent.Append("Arguments=-f Makefile UE4Editor UE4Game ShaderCompileWorker UnrealLightmass UnrealPak\n"); FileContent.Append("Enabled=true\n"); FileContent.Append("Environment=\n"); FileContent.Append("Executable=make\n"); FileContent.Append("Type=0\n\n"); FileContent.Append("[CustomBuildSystem][BuildConfig0][ToolClean]\n"); FileContent.Append("Arguments=-f Makefile UE4Editor UE4Game ShaderCompileWorker UnrealLightmass UnrealPak -clean\n"); FileContent.Append("Enabled=true\n"); FileContent.Append("Environment=\n"); FileContent.Append("Executable=make\n"); FileContent.Append("Type=3\n\n"); FileContent.Append("[CustomBuildSystem][BuildConfig0][ToolConfigure]\n"); FileContent.Append("Arguments=./GenerateProjectFiles.sh\n"); FileContent.Append("Enabled=true\n"); FileContent.Append("Environment=\n"); FileContent.Append("Executable=bash\n"); FileContent.Append("Type=1\n\n"); foreach (var Project in GeneratedProjectFiles) { foreach (var TargetFile in Project.ProjectTargets) { if (TargetFile.TargetFilePath == null) { continue; } var TargetName = TargetFile.TargetFilePath.GetFileNameWithoutAnyExtensions(); // Remove both ".cs" and ". foreach (UnrealTargetConfiguration CurConfiguration in Enum.GetValues(typeof(UnrealTargetConfiguration))) { if (CurConfiguration != UnrealTargetConfiguration.Unknown && CurConfiguration != UnrealTargetConfiguration.Development) { if (UnrealBuildTool.IsValidConfiguration(CurConfiguration)) { var ConfName = Enum.GetName(typeof(UnrealTargetConfiguration), CurConfiguration); FileContent.Append(String.Format("[CustomBuildSystem][BuildConfig{0}]\nBuildDir=file://{1}\n", BuildConfigIndex, UnrealRootPath)); if (TargetName == GameProjectName) { FileContent.Append(String.Format("Title={0}-Linux-{1}\n\n", TargetName, ConfName)); WriteCommandSubSection(ref FileContent, TargetName, ConfName, BuildConfigIndex, 0); WriteCommandSubSection(ref FileContent, TargetName, ConfName, BuildConfigIndex, 1); WriteCommandSubSection(ref FileContent, TargetName, ConfName, BuildConfigIndex, 3); } else if (TargetName == (GameProjectName + "Editor")) { FileContent.Append(String.Format("Title={0}-Linux-{1}\n\n", TargetName, ConfName)); WriteCommandSubSection(ref FileContent, TargetName, ConfName, BuildConfigIndex, 0); WriteCommandSubSection(ref FileContent, TargetName, ConfName, BuildConfigIndex, 1); WriteCommandSubSection(ref FileContent, TargetName, ConfName, BuildConfigIndex, 3); } else { FileContent.Append(String.Format("Title={0}-Linux-{1}\n\n", TargetName, ConfName)); WriteCommandSubSection(ref FileContent, TargetName, ConfName, BuildConfigIndex, 0); WriteCommandSubSection(ref FileContent, TargetName, ConfName, BuildConfigIndex, 1); WriteCommandSubSection(ref FileContent, TargetName, ConfName, BuildConfigIndex, 3); } BuildConfigIndex++; } } } FileContent.Append(String.Format("[CustomBuildSystem][BuildConfig{0}]\nBuildDir=file://{1}\n", BuildConfigIndex, UnrealRootPath)); if (TargetName == GameProjectName) { FileContent.Append(String.Format("Title={0}\n\n", TargetName)); WriteCommandSubSection(ref FileContent, TargetName, "Development", BuildConfigIndex, 0); WriteCommandSubSection(ref FileContent, TargetName, "Development", BuildConfigIndex, 1); WriteCommandSubSection(ref FileContent, TargetName, "Development", BuildConfigIndex, 3); } else if (TargetName == (GameProjectName + "Editor")) { FileContent.Append(String.Format("Title={0}\n\n", TargetName)); WriteCommandSubSection(ref FileContent, TargetName, "Development", BuildConfigIndex, 0); WriteCommandSubSection(ref FileContent, TargetName, "Development", BuildConfigIndex, 1); WriteCommandSubSection(ref FileContent, TargetName, "Development", BuildConfigIndex, 3); } else { FileContent.Append(String.Format("Title={0}\n\n", TargetName)); WriteCommandSubSection(ref FileContent, TargetName, "Development", BuildConfigIndex, 0); WriteCommandSubSection(ref FileContent, TargetName, "Development", BuildConfigIndex, 1); WriteCommandSubSection(ref FileContent, TargetName, "Development", BuildConfigIndex, 3); } BuildConfigIndex++; } } }
public BranchUProject(UnrealBuildTool.UProjectInfo InfoEntry) { GameName = InfoEntry.GameName; //not sure what the heck this path is relative to FilePath = InfoEntry.FilePath; if (!CommandUtils.FileExists_NoExceptions(FilePath.FullName)) { throw new AutomationException("Could not resolve relative path corrctly {0} -> {1} which doesn't exist.", InfoEntry.FilePath, FilePath); } Properties = ProjectUtils.GetProjectProperties(FilePath); }
XGEItem XGEPrepareBuildWithUBT(string TargetName, UnrealBuildTool.UnrealTargetPlatform Platform, string Config, FileReference UprojectPath, bool ForceMonolithic = false, bool ForceNonUnity = false, bool ForceDebugInfo = false, string InAddArgs = "", bool ForceUnity = false, Dictionary<string, string> EnvVars = null) { string AddArgs = ""; if (UprojectPath != null) { AddArgs += " " + CommandUtils.MakePathSafeToUseWithCommandLine(UprojectPath.FullName); } AddArgs += " " + InAddArgs; if (ForceMonolithic) { AddArgs += " -monolithic"; } if (ForceNonUnity) { AddArgs += " -disableunity"; } if (ForceUnity) { AddArgs += " -forceunity"; } if (ForceDebugInfo) { AddArgs += " -forcedebuginfo"; } PrepareUBT(); string UBTManifest = GetUBTManifest(UprojectPath, AddArgs); DeleteFile(UBTManifest); XGEItem Result = new XGEItem(); ClearExportedXGEXML(); using(TelemetryStopwatch GenerateManifestStopwatch = new TelemetryStopwatch("GenerateXGEManifest.{0}.{1}.{2}", TargetName, Platform.ToString(), Config)) { RunUBT(CmdEnv, UBTExecutable: UBTExecutable, Project: UprojectPath, Target: TargetName, Platform: Platform.ToString(), Config: Config, AdditionalArgs: "-generatemanifest -nobuilduht -xgeexport" + AddArgs, EnvVars: EnvVars); } PrepareManifest(UBTManifest, true); Result.Platform = Platform; Result.Config = Config; Result.TargetName = TargetName; Result.UProjectPath = UprojectPath; Result.Manifest = ReadManifest(UBTManifest); Result.OutputCaption = String.Format("{0}-{1}-{2}", TargetName, Platform.ToString(), Config.ToString()); DeleteFile(UBTManifest); Result.CommandLine = UBTExecutable + " " + UBTCommandline(Project: UprojectPath, Target: TargetName, Platform: Platform.ToString(), Config: Config, AdditionalArgs: "-noxge -nobuilduht" + AddArgs); Result.XgeXmlFiles = new List<string>(); foreach (var XGEFile in FindXGEFiles()) { if (!FileExists_NoExceptions(XGEFile)) { throw new AutomationException("BUILD FAILED: Couldn't find file: {0}", XGEFile); } int FileNum = 0; string OutFile; while (true) { OutFile = CombinePaths(CmdEnv.LogFolder, String.Format("UBTExport.{0}.xge.xml", FileNum)); FileInfo ItemInfo = new FileInfo(OutFile); if (!ItemInfo.Exists) { break; } FileNum++; } CopyFile(XGEFile, OutFile); Result.XgeXmlFiles.Add(OutFile); } ClearExportedXGEXML(); return Result; }
// Look for any build options in the engine config file. public override void ParseProjectSettings() { base.ParseProjectSettings(); ConfigCacheIni Ini = new ConfigCacheIni(UnrealTargetPlatform.IOS, "Engine", UnrealBuildTool.GetUProjectPath()); string ServerName = RemoteServerName; if (Ini.GetString("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "RemoteServerName", out ServerName) && !String.IsNullOrEmpty(ServerName)) { RemoteServerName = ServerName; } bool bUseRSync = false; if (Ini.GetBool("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "bUseRSync", out bUseRSync)) { bUseRPCUtil = !bUseRSync; string UserName = RSyncUsername; if (Ini.GetString("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "RSyncUsername", out UserName)) { RSyncUsername = UserName; } if (Ini.GetString("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "DeltaCopyInstallPath", out OverrideDeltaCopyInstallPath)) { if (!string.IsNullOrEmpty(OverrideDeltaCopyInstallPath)) { SSHExe = Path.Combine(OverrideDeltaCopyInstallPath, Path.GetFileName(SSHExe)); RSyncExe = Path.Combine(OverrideDeltaCopyInstallPath, Path.GetFileName(RSyncExe)); } } string ConfigKeyPath; if (Ini.GetString("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "SSHPrivateKeyOverridePath", out ConfigKeyPath)) { if (File.Exists(ConfigKeyPath)) { SSHPrivateKeyOverridePath = ConfigKeyPath; } } } }
/// <summary> /// Adds multiple targets with the specified configuration. /// </summary> /// <param name="TargetNames">List of targets.</param> /// <param name="InPlatform">Platform</param> /// <param name="InConfiguration">Configuration</param> /// <param name="InUprojectPath">Path to optional uproject file</param> /// <param name="InAddArgs">Specifies additional arguments for UBT</param> public void AddTargets(string[] TargetNames, UnrealBuildTool.UnrealTargetPlatform InPlatform, UnrealBuildTool.UnrealTargetConfiguration InConfiguration, FileReference InUprojectPath = null, string InAddArgs = "") { // Is this platform a compilable target? if (!Platform.GetPlatform(InPlatform).CanBeCompiled()) { return; } foreach (var Target in TargetNames) { Targets.Add(new BuildTarget() { TargetName = Target, Platform = InPlatform, Config = InConfiguration, UprojectPath = InUprojectPath, UBTArgs = InAddArgs, }); } }
private static RemoteToolChainErrorCode InitializeRemoteExecution() { if (bHasBeenInitialized) { return(InitializationErrorCode); } if (BuildHostPlatform.Current.Platform != UnrealTargetPlatform.Mac) { // If we don't care which machine we're going to build on, query and // pick the one with the most free command slots available if (RemoteServerName == "best_available") { int AvailableSlots = 0; int Attempts = 0; if (!ProjectFileGenerator.bGenerateProjectFiles) { Log.TraceInformation("Picking a random Mac builder..."); } while (AvailableSlots < 2 && Attempts < 20) { RemoteServerName = PotentialServerNames.OrderBy(x => Guid.NewGuid()).FirstOrDefault(); // make sure it's ready to take commands AvailableSlots = GetAvailableCommandSlotCount(RemoteServerName); Attempts++; } // make sure it succeeded if (AvailableSlots <= 1) { throw new BuildException("Failed to find a Mac available to take commands!"); } else if (!ProjectFileGenerator.bGenerateProjectFiles) { Log.TraceInformation("Chose {0} after {1} attempts to find a Mac, with {2} slots", RemoteServerName, Attempts, AvailableSlots); } /* * this does not work right, because it pushes a lot of tasks to machines that have substantially more slots than others * Log.TraceInformation("Picking the best available Mac builder..."); * Int32 MostAvailableCount = Int32.MinValue; * foreach (string NextMacName in PotentialServerNames) * { * Int32 NextAvailableCount = GetAvailableCommandSlotCount(NextMacName); * if (NextAvailableCount > MostAvailableCount) * { * MostAvailableName = NextMacName; * MostAvailableCount = NextAvailableCount; * } * * Log.TraceVerbose("... " + NextMacName + " has " + NextAvailableCount + " slots available"); * } * Log.TraceVerbose("Picking the compile server with the most available command slots: " + MostAvailableName); * * // Finally, assign the name of the Mac we're going to use * RemoteServerName = MostAvailableName; */ } else if (!ProjectFileGenerator.bGenerateProjectFiles) { Log.TraceInformation("Picking the default remote server " + RemoteServerName); } // we need a server name! if (string.IsNullOrEmpty(RemoteServerName)) { Log.TraceError("Remote compiling requires a server name. Use the editor to set up your remote compilation settings."); return(RemoteToolChainErrorCode.ServerNameNotSpecified); } if (!bUseRPCUtil) { // Verify the Delta Copy install path ResolvedRSyncExe = ResolveString(RSyncExe, true); ResolvedSSHExe = ResolveString(SSHExe, true); if (!File.Exists(ResolvedRSyncExe) || !File.Exists(ResolvedSSHExe)) { Log.TraceError("Remote compiling requires Delta Copy to be installed."); return(RemoteToolChainErrorCode.MissingDeltaCopyInstall); } // we need the RemoteServerName and the Username to find the private key ResolvedRSyncUsername = ResolveString(RSyncUsername, false); if (string.IsNullOrEmpty(ResolvedRSyncUsername)) { Log.TraceError("Remote compiling requires a user name. Use the editor to set up your remote compilation settings."); return(RemoteToolChainErrorCode.MissingRemoteUserName); } bool bFoundOverrideSSHPrivateKey = false; // if the override path is set, just use it directly if (!string.IsNullOrEmpty(SSHPrivateKeyOverridePath)) { ResolvedSSHPrivateKey = ResolveString(SSHPrivateKeyOverridePath, true); bFoundOverrideSSHPrivateKey = File.Exists(ResolvedSSHPrivateKey); // make sure it exists if (!bFoundOverrideSSHPrivateKey) { Log.TraceWarning("An SSHKey override was specified [" + SSHPrivateKeyOverridePath + "] but it doesn't exist. Looking elsewhere..."); } } if (!bFoundOverrideSSHPrivateKey) { // all the places to look for a key string[] Locations = new string[] { Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Unreal Engine", "UnrealBuildTool"), Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), "Unreal Engine", "UnrealBuildTool"), Path.Combine(UnrealBuildTool.GetUProjectPath(), "Build", "NotForLicensees"), Path.Combine(UnrealBuildTool.GetUProjectPath(), "Build", "NoRedist"), Path.Combine(UnrealBuildTool.GetUProjectPath(), "Build"), Path.Combine(BuildConfiguration.RelativeEnginePath, "Build", "NotForLicensees"), Path.Combine(BuildConfiguration.RelativeEnginePath, "Build", "NoRedist"), Path.Combine(BuildConfiguration.RelativeEnginePath, "Build"), }; // look for a key file foreach (string Location in Locations) { string KeyPath = Path.Combine(Location, "SSHKeys", RemoteServerName, ResolvedRSyncUsername, "RemoteToolChainPrivate.key"); if (File.Exists(KeyPath)) { ResolvedSSHPrivateKey = KeyPath; bFoundOverrideSSHPrivateKey = true; break; } } } // resolve the rest of the strings ResolvedRsyncAuthentication = ResolveString(RsyncAuthentication, false); ResolvedSSHAuthentication = ResolveString(SSHAuthentication, false); } // start up remote communication and record if it succeeds InitializationErrorCode = (RemoteToolChainErrorCode)RPCUtilHelper.Initialize(RemoteServerName); if (InitializationErrorCode != RemoteToolChainErrorCode.NoError && InitializationErrorCode != RemoteToolChainErrorCode.MissingSSHKey) { Log.TraceError("Failed to initialize a connection to the Remote Server {0}", RemoteServerName); return(InitializationErrorCode); } else if (InitializationErrorCode == RemoteToolChainErrorCode.MissingSSHKey) { // Allow the user to set up a key from here. Process KeyProcess = new Process(); KeyProcess.StartInfo.WorkingDirectory = Path.GetFullPath(Path.Combine(BuildConfiguration.RelativeEnginePath, "Build", "BatchFiles")); KeyProcess.StartInfo.FileName = "MakeAndInstallSSHKey.bat"; KeyProcess.StartInfo.Arguments = string.Format( "\"{0}\" \"{1}\" {2} {3} \"{4}\" \"{5}\" \"{6}\"", ResolvedSSHExe, ResolvedRSyncExe, ResolvedRSyncUsername, RemoteServerName, Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), ConvertPathToCygwin(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)), Path.GetFullPath(BuildConfiguration.RelativeEnginePath)); KeyProcess.Start(); KeyProcess.WaitForExit(); // make sure it succeeded if we want to re-init if (KeyProcess.ExitCode == 0) { InitializationErrorCode = InitializeRemoteExecution(); } } } else { RemoteServerName = Environment.MachineName; // can't error in this case } bHasBeenInitialized = true; return(InitializationErrorCode); }
public override void ResetBuildConfiguration(UnrealTargetPlatform InPlatform, UnrealTargetConfiguration InConfiguration) { UEBuildConfiguration.bCompileICU = true; // Should we enable Windows XP support { // If it wasnt set as an XML flag, check if it is requested from the commandline. if (!SupportWindowsXPIfAvailable) { string[] CmdLine = Environment.GetCommandLineArgs(); SupportWindowsXPIfAvailable = CmdLine.Contains("-winxp", StringComparer.InvariantCultureIgnoreCase); // ...check if it was supported from a config. if (!SupportWindowsXPIfAvailable) { ConfigCacheIni Ini = new ConfigCacheIni(UnrealTargetPlatform.Win64, "Engine", UnrealBuildTool.GetUProjectPath()); string MinimumOS; if (Ini.GetString("/Script/WindowsTargetPlatform.WindowsTargetSettings", "MinimumOSVersion", out MinimumOS)) { if (string.IsNullOrEmpty(MinimumOS) == false) { SupportWindowsXPIfAvailable = MinimumOS == "MSOS_XP"; } } } } // Win32 XP is only supported at this time. SupportWindowsXP = SupportWindowsXPIfAvailable && (GetCPPTargetPlatform(InPlatform) == CPPTargetPlatform.Win32); } }
/// <summary> /// Construct an instance of the given target rules /// </summary> /// <param name="TypeName">Type name of the target rules</param> /// <param name="TargetInfo">Target configuration information to pass to the constructor</param> /// <param name="Arguments">Command line arguments for this target</param> /// <returns>Instance of the corresponding TargetRules</returns> protected TargetRules CreateTargetRulesInstance(string TypeName, TargetInfo TargetInfo, CommandLineArguments Arguments) { // The build module must define a type named '<TargetName>Target' that derives from our 'TargetRules' type. Type RulesType = CompiledAssembly.GetType(TypeName); if (RulesType == null) { throw new BuildException("Expecting to find a type to be declared in a target rules named '{0}'. This type must derive from the 'TargetRules' type defined by Unreal Build Tool.", TypeName); } // Create an instance of the module's rules object, and set some defaults before calling the constructor. TargetRules Rules = (TargetRules)FormatterServices.GetUninitializedObject(RulesType); Rules.bUseBackwardsCompatibleDefaults = bUseBackwardsCompatibleDefaults; // Find the constructor ConstructorInfo Constructor = RulesType.GetConstructor(new Type[] { typeof(TargetInfo) }); if (Constructor == null) { throw new BuildException("No constructor found on {0} which takes an argument of type TargetInfo.", RulesType.Name); } // Invoke the regular constructor try { Constructor.Invoke(Rules, new object[] { TargetInfo }); } catch (Exception Ex) { throw new BuildException(Ex, "Unable to instantiate instance of '{0}' object type from compiled assembly '{1}'. Unreal Build Tool creates an instance of your module's 'Rules' object in order to find out about your module's requirements. The CLR exception details may provide more information: {2}", TypeName, Path.GetFileNameWithoutExtension(CompiledAssembly.Location), Ex.ToString()); } // Return the target file name to the caller Rules.File = TargetNameToTargetFile[TargetInfo.Name]; // Set the default overriddes for the configured target type Rules.SetOverridesForTargetType(); // Parse any additional command-line arguments. These override default settings specified in config files or the .target.cs files. if (Arguments != null) { foreach (object ConfigurableObject in Rules.GetConfigurableObjects()) { Arguments.ApplyTo(ConfigurableObject); } } // Set the final value for the link type in the target rules if (Rules.LinkType == TargetLinkType.Default) { throw new BuildException("TargetRules.LinkType should be inferred from TargetType"); } // Set the default value for whether to use the shared build environment if (Rules.BuildEnvironment == TargetBuildEnvironment.Default) { if (Rules.Type == TargetType.Program && TargetInfo.ProjectFile != null && TargetNameToTargetFile[Rules.Name].IsUnderDirectory(TargetInfo.ProjectFile.Directory)) { Rules.BuildEnvironment = TargetBuildEnvironment.Unique; } else if (UnrealBuildTool.IsEngineInstalled() || Rules.LinkType != TargetLinkType.Monolithic) { Rules.BuildEnvironment = TargetBuildEnvironment.Shared; } else { Rules.BuildEnvironment = TargetBuildEnvironment.Unique; } } // Automatically include CoreUObject if (Rules.bCompileAgainstEngine) { Rules.bCompileAgainstCoreUObject = true; } // Must have editor only data if building the editor. if (Rules.bBuildEditor) { Rules.bBuildWithEditorOnlyData = true; } // Apply the override to force debug info to be enabled if (Rules.bForceDebugInfo) { Rules.bDisableDebugInfo = false; Rules.bOmitPCDebugInfoInDevelopment = false; } // Setup the malloc profiler if (Rules.bUseMallocProfiler) { Rules.bOmitFramePointers = false; Rules.GlobalDefinitions.Add("USE_MALLOC_PROFILER=1"); } // Set a macro if we allow using generated inis if (!Rules.bAllowGeneratedIniWhenCooked) { Rules.GlobalDefinitions.Add("DISABLE_GENERATED_INI_WHEN_COOKED=1"); } if (!Rules.bAllowNonUFSIniWhenCooked) { Rules.GlobalDefinitions.Add("DISABLE_NONUFS_INI_WHEN_COOKED=1"); } if (Rules.bDisableUnverifiedCertificates) { Rules.GlobalDefinitions.Add("DISABLE_UNVERIFIED_CERTIFICATE_LOADING=1"); } // Allow the platform to finalize the settings UEBuildPlatform Platform = UEBuildPlatform.GetBuildPlatform(Rules.Platform); Platform.ValidateTarget(Rules); // Some platforms may *require* monolithic compilation... if (Rules.LinkType != TargetLinkType.Monolithic && UEBuildPlatform.PlatformRequiresMonolithicBuilds(Rules.Platform, Rules.Configuration)) { throw new BuildException(String.Format("{0}: {1} does not support modular builds", Rules.Name, Rules.Platform)); } return(Rules); }
/** * Checks the class header files and determines if generated UObject code files are out of date in comparison. * @param UObjectModules Modules that we generate headers for * * @return True if the code files are out of date * */ private static bool AreGeneratedCodeFilesOutOfDate(List <UHTModuleInfo> UObjectModules) { bool bIsOutOfDate = false; // Get UnrealHeaderTool timestamp. If it's newer than generated headers, they need to be rebuilt too. var HeaderToolTimestamp = CheckIfUnrealHeaderToolIsUpToDate(); // Get CoreUObject.generated.cpp timestamp. If the source files are older than the CoreUObject generated code, we'll // need to regenerate code for the module DateTime?CoreGeneratedTimestamp = null; { // Find the CoreUObject module foreach (var Module in UObjectModules) { if (Module.ModuleName.Equals("CoreUObject", StringComparison.InvariantCultureIgnoreCase)) { CoreGeneratedTimestamp = GetCoreGeneratedTimestamp(Module.ModuleName, Path.GetDirectoryName(Module.GeneratedCPPFilenameBase)); break; } } if (CoreGeneratedTimestamp == null) { throw new BuildException("Could not find CoreUObject in list of all UObjectModules"); } } foreach (var Module in UObjectModules) { // In Rocket, we skip checking timestamps for modules that don't exist within the project's directory if (UnrealBuildTool.RunningRocket()) { // @todo Rocket: This could be done in a better way I'm sure if (!Utils.IsFileUnderDirectory(Module.ModuleDirectory, UnrealBuildTool.GetUProjectPath())) { // Engine or engine plugin module - Rocket does not regenerate them so don't compare their timestamps continue; } } // Make sure we have an existing folder for generated code. If not, then we definitely need to generate code! var GeneratedCodeDirectory = Path.GetDirectoryName(Module.GeneratedCPPFilenameBase); var TestDirectory = (FileSystemInfo) new DirectoryInfo(GeneratedCodeDirectory); if (TestDirectory.Exists) { // Grab our special "Timestamp" file that we saved after the last set of headers were generated. This file // actually contains the list of source files which contained UObjects, so that we can compare to see if any // UObject source files were deleted (or no longer contain UObjects), which means we need to run UHT even // if no other source files were outdated string TimestampFile = Path.Combine(GeneratedCodeDirectory, @"Timestamp"); var SavedTimestampFileInfo = (FileSystemInfo) new FileInfo(TimestampFile); if (SavedTimestampFileInfo.Exists) { // Make sure the last UHT run completed after UnrealHeaderTool.exe was compiled last, and after the CoreUObject headers were touched last. var SavedTimestamp = SavedTimestampFileInfo.LastWriteTime; if (SavedTimestamp.CompareTo(HeaderToolTimestamp) > 0 && SavedTimestamp.CompareTo(CoreGeneratedTimestamp) > 0) { // Iterate over our UObjects headers and figure out if any of them have changed var AllUObjectHeaders = new List <FileItem>(); AllUObjectHeaders.AddRange(Module.PublicUObjectClassesHeaders); AllUObjectHeaders.AddRange(Module.PublicUObjectHeaders); AllUObjectHeaders.AddRange(Module.PrivateUObjectHeaders); // Load up the old timestamp file and check to see if anything has changed { var UObjectFilesFromPreviousRun = File.ReadAllLines(TimestampFile); if (AllUObjectHeaders.Count == UObjectFilesFromPreviousRun.Length) { for (int FileIndex = 0; FileIndex < AllUObjectHeaders.Count; ++FileIndex) { if (!UObjectFilesFromPreviousRun[FileIndex].Equals(AllUObjectHeaders[FileIndex].AbsolutePath, StringComparison.InvariantCultureIgnoreCase)) { bIsOutOfDate = true; Log.TraceVerbose("UnrealHeaderTool needs to run because the set of UObject source files in module {0} has changed", Module.ModuleName); break; } } } else { bIsOutOfDate = true; Log.TraceVerbose("UnrealHeaderTool needs to run because there are a different number of UObject source files in module {0}", Module.ModuleName); } } foreach (var HeaderFile in AllUObjectHeaders) { var HeaderFileTimestamp = HeaderFile.Info.LastWriteTime; // Has the source header changed since we last generated headers successfully? if (SavedTimestamp.CompareTo(HeaderFileTimestamp) < 0) { Log.TraceVerbose("UnrealHeaderTool needs to run because SavedTimestamp is older than HeaderFileTimestamp (" + HeaderFile.AbsolutePath + ") for module {0}", Module.ModuleName); bIsOutOfDate = true; break; } } } else { // Generated code is older UnrealHeaderTool.exe or CoreUObject headers. Out of date! Log.TraceVerbose("UnrealHeaderTool needs to run because UnrealHeaderTool.exe or CoreUObject headers are newer than SavedTimestamp for module {0}", Module.ModuleName); bIsOutOfDate = true; } } else { // Timestamp file was missing (possibly deleted/cleaned), so headers are out of date Log.TraceVerbose("UnrealHeaderTool needs to run because UHT Timestamp file did not exist for module {0}", Module.ModuleName); bIsOutOfDate = true; } } else { // Generated code directory is missing entirely! Log.TraceVerbose("UnrealHeaderTool needs to run because no generated code directory was found for module {0}", Module.ModuleName); bIsOutOfDate = true; } // If even one module is out of date, we're done! UHT does them all in one fell swoop.; if (bIsOutOfDate) { break; } } return(bIsOutOfDate); }
private bool WriteQMakePro() { // Some more stuff borrowed from Mac side of things. List <string> IncludeDirectories = new List <string>(); List <string> SystemIncludeDirectories = new List <string>(); List <string> DefinesAndValues = new List <string>(); // DefineList.Add (""); string QMakeIncludesFileName = MasterProjectName + "Includes.pri"; StringBuilder QMakeIncludesPriFileContent = new StringBuilder(); string QMakeDefinesFileName = MasterProjectName + "Defines.pri"; StringBuilder QMakeDefinesPriFileContent = new StringBuilder(); string GameProjectPath = ""; string GameProjectFile = ""; string GameProjectRootPath = ""; string BuildCommand = ""; string QMakeGameProjectFile = ""; foreach (ProjectFile CurProject in GeneratedProjectFiles) { QMakefileProjectFile QMakeProject = CurProject as QMakefileProjectFile; if (QMakeProject == null) { Log.TraceInformation("QMakeProject == null"); continue; } foreach (string CurPath in QMakeProject.IntelliSenseIncludeSearchPaths) { AddIncludeDirectory(ref IncludeDirectories, CurPath, Path.GetDirectoryName(QMakeProject.ProjectFilePath.FullName)); // System.Console.WriteLine ("Not empty now? CurPath == ", CurPath); } foreach (string CurPath in QMakeProject.IntelliSenseSystemIncludeSearchPaths) { AddIncludeDirectory(ref SystemIncludeDirectories, CurPath, Path.GetDirectoryName(QMakeProject.ProjectFilePath.FullName)); } } // Remove duplicate paths from include dir and system include dir list IncludeDirectories = IncludeDirectories.Distinct().ToList(); SystemIncludeDirectories = SystemIncludeDirectories.Distinct().ToList(); // Iterate through all the defines for the projects that are generated by // UnrealBuildTool.exe // !RAKE: move to seperate function QMakeDefinesPriFileContent.Append("DEFINES += \\\n"); foreach (ProjectFile CurProject in GeneratedProjectFiles) { QMakefileProjectFile QMakeProject = CurProject as QMakefileProjectFile; if (QMakeProject == null) { Log.TraceInformation("QMakeProject == null"); continue; } foreach (string CurDefine in QMakeProject.IntelliSensePreprocessorDefinitions) { String define = ""; String value = ""; SplitDefinitionAndValue(CurDefine, out define, out value); if (!DefinesAndValues.Contains(define)) { // Log.TraceInformation (CurDefine); if (string.IsNullOrEmpty(value)) { DefinesAndValues.Add("\t"); DefinesAndValues.Add(String.Format("{0}=", define)); DefinesAndValues.Add(" \\\n"); } else { DefinesAndValues.Add("\t"); DefinesAndValues.Add(define); DefinesAndValues.Add("="); DefinesAndValues.Add(value); DefinesAndValues.Add(" \\\n"); } } } } foreach (string Def in DefinesAndValues) { QMakeDefinesPriFileContent.Append(Def); } // Iterate through all the include paths that // UnrealBuildTool.exe generates // !RAKE: Move to seperate function QMakeIncludesPriFileContent.Append("INCLUDEPATH += \\\n"); foreach (string CurPath in IncludeDirectories) { QMakeIncludesPriFileContent.Append("\t"); QMakeIncludesPriFileContent.Append(CurPath); QMakeIncludesPriFileContent.Append(" \\\n"); } foreach (string CurPath in SystemIncludeDirectories) { QMakeIncludesPriFileContent.Append("\t"); QMakeIncludesPriFileContent.Append(CurPath); QMakeIncludesPriFileContent.Append(" \\\n"); } QMakeIncludesPriFileContent.Append("\n"); if (!String.IsNullOrEmpty(GameProjectName)) { GameProjectPath = OnlyGameProject.Directory.FullName; GameProjectFile = OnlyGameProject.FullName; QMakeGameProjectFile = "gameProjectFile=" + GameProjectFile + "\n"; BuildCommand = "build=mono $$unrealRootPath/Engine/Binaries/DotNET/UnrealBuildTool.exe\n\n"; } else { BuildCommand = "build=bash $$unrealRootPath/Engine/Build/BatchFiles/Linux/Build.sh\n"; } string UnrealRootPath = UnrealBuildTool.RootDirectory.FullName; string FileName = MasterProjectName + ".pro"; string QMakeSourcePriFileName = MasterProjectName + "Source.pri"; string QMakeHeaderPriFileName = MasterProjectName + "Header.pri"; string QMakeConfigPriFileName = MasterProjectName + "Config.pri"; StringBuilder QMakeFileContent = new StringBuilder(); StringBuilder QMakeSourcePriFileContent = new StringBuilder(); StringBuilder QMakeHeaderPriFileContent = new StringBuilder(); StringBuilder QMakeConfigPriFileContent = new StringBuilder(); string QMakeSectionEnd = " \n\n"; string QMakeSourceFilesList = "SOURCES += \\ \n"; string QMakeHeaderFilesList = "HEADERS += \\ \n"; string QMakeConfigFilesList = "OTHER_FILES += \\ \n"; string QMakeTargetList = "QMAKE_EXTRA_TARGETS += \\ \n"; if (!String.IsNullOrEmpty(GameProjectName)) { GameProjectRootPath = GameProjectName + "RootPath=" + GameProjectPath + "\n\n"; } QMakeFileContent.Append( "# UnrealEngine.pro generated by QMakefileGenerator.cs\n" + "# *DO NOT EDIT*\n\n" + "TEMPLATE = aux\n" + "CONFIG += c++14\n" + "CONFIG -= console\n" + "CONFIG -= app_bundle\n" + "CONFIG -= qt\n\n" + "TARGET = UE4 \n\n" + "unrealRootPath=" + UnrealRootPath + "\n" + GameProjectRootPath + QMakeGameProjectFile + BuildCommand + "args=$(ARGS)\n\n" + "include(" + QMakeSourcePriFileName + ")\n" + "include(" + QMakeHeaderPriFileName + ")\n" + "include(" + QMakeConfigPriFileName + ")\n" + "include(" + QMakeIncludesFileName + ")\n" + "include(" + QMakeDefinesFileName + ")\n\n" ); // Create SourceFiles, HeaderFiles, and ConfigFiles sections. List <FileReference> AllModuleFiles = DiscoverModules(FindGameProjects()); foreach (FileReference CurModuleFile in AllModuleFiles) { List <FileReference> FoundFiles = SourceFileSearch.FindModuleSourceFiles(CurModuleFile); foreach (FileReference CurSourceFile in FoundFiles) { string SourceFileRelativeToRoot = CurSourceFile.MakeRelativeTo(UnrealBuildTool.EngineDirectory); // Exclude some directories that we don't compile (note that we still want Windows/Mac etc for code navigation) if (!SourceFileRelativeToRoot.Contains("Source/ThirdParty/")) { if (SourceFileRelativeToRoot.EndsWith(".cpp")) { if (!SourceFileRelativeToRoot.StartsWith("..") && !Path.IsPathRooted(SourceFileRelativeToRoot)) { QMakeSourceFilesList += ("\t\"" + "$$unrealRootPath/Engine/" + SourceFileRelativeToRoot + "\" \\\n"); } else { if (String.IsNullOrEmpty(GameProjectName)) { QMakeSourceFilesList += ("\t\"" + SourceFileRelativeToRoot.Substring(3) + "\" \\\n"); } else { QMakeSourceFilesList += ("\t\"$$" + GameProjectName + "RootPath/" + Utils.MakePathRelativeTo(CurSourceFile.FullName, GameProjectPath) + "\" \\\n"); } } } if (SourceFileRelativeToRoot.EndsWith(".h")) { if (!SourceFileRelativeToRoot.StartsWith("..") && !Path.IsPathRooted(SourceFileRelativeToRoot)) { // SourceFileRelativeToRoot = "Engine/" + SourceFileRelativeToRoot; QMakeHeaderFilesList += ("\t\"" + "$$unrealRootPath/Engine/" + SourceFileRelativeToRoot + "\" \\\n"); } else { if (String.IsNullOrEmpty(GameProjectName)) { // SourceFileRelativeToRoot = SourceFileRelativeToRoot.Substring (3); QMakeHeaderFilesList += ("\t\"" + SourceFileRelativeToRoot.Substring(3) + "\" \\\n"); } else { QMakeHeaderFilesList += ("\t\"$$" + GameProjectName + "RootPath/" + Utils.MakePathRelativeTo(CurSourceFile.FullName, GameProjectPath) + "\" \\\n"); } } } if (SourceFileRelativeToRoot.EndsWith(".cs")) { if (!SourceFileRelativeToRoot.StartsWith("..") && !Path.IsPathRooted(SourceFileRelativeToRoot)) { // SourceFileRelativeToRoot = "Engine/" + SourceFileRelativeToRoot; QMakeConfigFilesList += ("\t\"" + "$$unrealRootPath/Engine/" + SourceFileRelativeToRoot + "\" \\\n"); } else { if (String.IsNullOrEmpty(GameProjectName)) { // SourceFileRelativeToRoot = SourceFileRelativeToRoot.Substring (3); QMakeConfigFilesList += ("\t\"" + SourceFileRelativeToRoot.Substring(3) + "\" \\\n"); } else { QMakeConfigFilesList += ("\t\"$$" + GameProjectName + "RootPath/" + Utils.MakePathRelativeTo(CurSourceFile.FullName, GameProjectPath) + "\" \\\n"); } } } } } } // Add section end to section strings; QMakeSourceFilesList += QMakeSectionEnd; QMakeHeaderFilesList += QMakeSectionEnd; QMakeConfigFilesList += QMakeSectionEnd; // Append sections to the QMakeLists.txt file QMakeSourcePriFileContent.Append(QMakeSourceFilesList); QMakeHeaderPriFileContent.Append(QMakeHeaderFilesList); QMakeConfigPriFileContent.Append(QMakeConfigFilesList); string QMakeProjectCmdArg = ""; foreach (ProjectFile Project in GeneratedProjectFiles) { foreach (ProjectTarget TargetFile in Project.ProjectTargets) { if (TargetFile.TargetFilePath == null) { continue; } string TargetName = TargetFile.TargetFilePath.GetFileNameWithoutAnyExtensions(); // Remove both ".cs" and ". foreach (UnrealTargetConfiguration CurConfiguration in Enum.GetValues(typeof(UnrealTargetConfiguration))) { if (CurConfiguration != UnrealTargetConfiguration.Unknown && CurConfiguration != UnrealTargetConfiguration.Development) { if (UnrealBuildTool.IsValidConfiguration(CurConfiguration)) { if (TargetName == GameProjectName || TargetName == (GameProjectName + "Editor")) { QMakeProjectCmdArg = " -project=\"\\\"$$gameProjectFile\\\"\""; } string ConfName = Enum.GetName(typeof(UnrealTargetConfiguration), CurConfiguration); QMakeFileContent.Append(String.Format("{0}-Linux-{1}.commands = $$build {0} Linux {1} {2} $$args\n", TargetName, ConfName, QMakeProjectCmdArg)); QMakeTargetList += "\t" + TargetName + "-Linux-" + ConfName + " \\\n"; // , TargetName, ConfName); } } } if (TargetName == GameProjectName || TargetName == (GameProjectName + "Editor")) { QMakeProjectCmdArg = " -project=\"\\\"$$gameProjectFile\\\"\""; } QMakeFileContent.Append(String.Format("{0}.commands = $$build {0} Linux Development {1} $$args\n\n", TargetName, QMakeProjectCmdArg)); QMakeTargetList += "\t" + TargetName + " \\\n"; } } QMakeFileContent.Append(QMakeTargetList.TrimEnd('\\')); string FullFileName = Path.Combine(MasterProjectPath.FullName, FileName); string FullQMakeDefinesFileName = Path.Combine(MasterProjectPath.FullName, QMakeDefinesFileName); string FullQMakeIncludesFileName = Path.Combine(MasterProjectPath.FullName, QMakeIncludesFileName); string FullQMakeSourcePriFileName = Path.Combine(MasterProjectPath.FullName, QMakeSourcePriFileName); string FullQMakeHeaderPriFileName = Path.Combine(MasterProjectPath.FullName, QMakeHeaderPriFileName); string FullQMakeConfigPriFileName = Path.Combine(MasterProjectPath.FullName, QMakeConfigPriFileName); WriteFileIfChanged(FullQMakeDefinesFileName, QMakeDefinesPriFileContent.ToString()); WriteFileIfChanged(FullQMakeIncludesFileName, QMakeIncludesPriFileContent.ToString()); WriteFileIfChanged(FullQMakeSourcePriFileName, QMakeSourcePriFileContent.ToString()); WriteFileIfChanged(FullQMakeHeaderPriFileName, QMakeHeaderPriFileContent.ToString()); WriteFileIfChanged(FullQMakeConfigPriFileName, QMakeConfigPriFileContent.ToString()); return(WriteFileIfChanged(FullFileName, QMakeFileContent.ToString())); }
public static bool GenerateIOSPList(string ProjectDirectory, bool bIsUE4Game, string GameName, string ProjectName, string InEngineDir, string AppDirectory, UEDeployIOS InThis = null) { // generate the Info.plist for future use string BuildDirectory = ProjectDirectory + "/Build/IOS"; bool bSkipDefaultPNGs = false; string IntermediateDirectory = (bIsUE4Game ? InEngineDir : ProjectDirectory) + "/Intermediate/IOS"; string PListFile = IntermediateDirectory + "/" + GameName + "-Info.plist"; ProjectName = !String.IsNullOrEmpty(ProjectName) ? ProjectName : GameName; VersionUtilities.BuildDirectory = BuildDirectory; VersionUtilities.GameName = GameName; // read the old file string OldPListData = File.Exists(PListFile) ? File.ReadAllText(PListFile) : ""; // determine if there is a launch.xib string LaunchXib = InEngineDir + "/Build/IOS/Resources/Interface/LaunchScreen.xib"; if (File.Exists(BuildDirectory + "/Resources/Interface/LaunchScreen.xib")) { LaunchXib = BuildDirectory + "/Resources/Interface/LaunchScreen.xib"; } // get the settings from the ini file // plist replacements DirectoryReference DirRef = bIsUE4Game ? (!string.IsNullOrEmpty(UnrealBuildTool.GetRemoteIniPath()) ? new DirectoryReference(UnrealBuildTool.GetRemoteIniPath()) : null) : new DirectoryReference(ProjectDirectory); ConfigCacheIni Ini = ConfigCacheIni.CreateConfigCacheIni(UnrealTargetPlatform.IOS, "Engine", DirRef); // orientations string SupportedOrientations = ""; bool bSupported = true; Ini.GetBool("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "bSupportsPortraitOrientation", out bSupported); SupportedOrientations += bSupported ? "\t\t<string>UIInterfaceOrientationPortrait</string>\n" : ""; Ini.GetBool("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "bSupportsUpsideDownOrientation", out bSupported); SupportedOrientations += bSupported ? "\t\t<string>UIInterfaceOrientationPortraitUpsideDown</string>\n" : ""; Ini.GetBool("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "bSupportsLandscapeLeftOrientation", out bSupported); SupportedOrientations += bSupported ? "\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n" : ""; Ini.GetBool("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "bSupportsLandscapeRightOrientation", out bSupported); SupportedOrientations += bSupported ? "\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n" : ""; // bundle display name string BundleDisplayName; Ini.GetString("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "BundleDisplayName", out BundleDisplayName); // bundle identifier string BundleIdentifier; Ini.GetString("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "BundleIdentifier", out BundleIdentifier); // bundle name string BundleName; Ini.GetString("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "BundleName", out BundleName); // disable https requirement bool bDisableHTTPS; Ini.GetBool("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "bDisableHTTPS", out bDisableHTTPS); // short version string string BundleShortVersion; Ini.GetString("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "VersionInfo", out BundleShortVersion); // required capabilities string RequiredCaps = ""; if (InThis != null) { // required capabilities RequiredCaps += InThis.IOSPlatformContext.GetRequiredCapabilities(); } Ini.GetBool("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "bSupportsOpenGLES2", out bSupported); RequiredCaps += bSupported ? "\t\t<string>opengles-2</string>\n" : ""; if (!bSupported) { Ini.GetBool("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "bSupportsMetal", out bSupported); RequiredCaps += bSupported ? "\t\t<string>metal</string>\n" : ""; } // minimum iOS version string MinVersion; if (Ini.GetString("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "MinimumiOSVersion", out MinVersion)) { switch (MinVersion) { case "IOS_61": Log.TraceWarning("IOS 6 is no longer supported in UE4 as 4.11"); MinVersion = "7.0"; break; case "IOS_7": MinVersion = "7.0"; break; case "IOS_8": MinVersion = "8.0"; break; case "IOS_9": MinVersion = "9.0"; break; } } else { MinVersion = "7.0"; } // Get Facebook Support details bool bEnableFacebookSupport = true; Ini.GetBool("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "bEnableFacebookSupport", out bEnableFacebookSupport); // Write the Facebook App ID if we need it. string FacebookAppID = ""; Ini.GetString("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "FacebookAppID", out FacebookAppID); bEnableFacebookSupport = bEnableFacebookSupport && !string.IsNullOrWhiteSpace(FacebookAppID); // extra plist data string ExtraData = ""; Ini.GetString("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "AdditionalPlistData", out ExtraData); // generate the plist file StringBuilder Text = new StringBuilder(); Text.AppendLine("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); Text.AppendLine("<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">"); Text.AppendLine("<plist version=\"1.0\">"); Text.AppendLine("<dict>"); Text.AppendLine("\t<key>CFBundleURLTypes</key>"); Text.AppendLine("\t<array>"); Text.AppendLine("\t\t<dict>"); Text.AppendLine("\t\t\t<key>CFBundleURLName</key>"); Text.AppendLine("\t\t\t<string>com.Epic.Unreal</string>"); Text.AppendLine("\t\t\t<key>CFBundleURLSchemes</key>"); Text.AppendLine("\t\t\t<array>"); Text.AppendLine(string.Format("\t\t\t\t<string>{0}</string>", bIsUE4Game ? "UE4Game" : GameName)); if (bEnableFacebookSupport) { // This is needed for facebook login to redirect back to the app after completion. Text.AppendLine(string.Format("\t\t\t\t<string>fb{0}</string>", FacebookAppID)); } Text.AppendLine("\t\t\t</array>"); Text.AppendLine("\t\t</dict>"); Text.AppendLine("\t</array>"); Text.AppendLine("\t<key>CFBundleDevelopmentRegion</key>"); Text.AppendLine("\t<string>English</string>"); Text.AppendLine("\t<key>CFBundleDisplayName</key>"); Text.AppendLine(string.Format("\t<string>{0}</string>", EncodeBundleName(BundleDisplayName, ProjectName))); Text.AppendLine("\t<key>CFBundleExecutable</key>"); Text.AppendLine(string.Format("\t<string>{0}</string>", bIsUE4Game ? "UE4Game" : GameName)); Text.AppendLine("\t<key>CFBundleIdentifier</key>"); Text.AppendLine(string.Format("\t<string>{0}</string>", BundleIdentifier.Replace("[PROJECT_NAME]", ProjectName).Replace("_", ""))); Text.AppendLine("\t<key>CFBundleInfoDictionaryVersion</key>"); Text.AppendLine("\t<string>6.0</string>"); Text.AppendLine("\t<key>CFBundleName</key>"); Text.AppendLine(string.Format("\t<string>{0}</string>", EncodeBundleName(BundleName, ProjectName))); Text.AppendLine("\t<key>CFBundlePackageType</key>"); Text.AppendLine("\t<string>APPL</string>"); Text.AppendLine("\t<key>CFBundleSignature</key>"); Text.AppendLine("\t<string>????</string>"); Text.AppendLine("\t<key>CFBundleVersion</key>"); Text.AppendLine(string.Format("\t<string>{0}</string>", VersionUtilities.UpdateBundleVersion(OldPListData))); Text.AppendLine("\t<key>CFBundleShortVersionString</key>"); Text.AppendLine(string.Format("\t<string>{0}</string>", BundleShortVersion)); Text.AppendLine("\t<key>LSRequiresIPhoneOS</key>"); Text.AppendLine("\t<true/>"); Text.AppendLine("\t<key>UIStatusBarHidden</key>"); Text.AppendLine("\t<true/>"); Text.AppendLine("\t<key>UIRequiresFullScreen</key>"); Text.AppendLine("\t<true/>"); Text.AppendLine("\t<key>UIViewControllerBasedStatusBarAppearance</key>"); Text.AppendLine("\t<false/>"); Text.AppendLine("\t<key>UISupportedInterfaceOrientations</key>"); Text.AppendLine("\t<array>"); foreach (string Line in SupportedOrientations.Split("\r\n".ToCharArray())) { if (!string.IsNullOrWhiteSpace(Line)) { Text.AppendLine(Line); } } Text.AppendLine("\t</array>"); Text.AppendLine("\t<key>UIRequiredDeviceCapabilities</key>"); Text.AppendLine("\t<array>"); foreach (string Line in RequiredCaps.Split("\r\n".ToCharArray())) { if (!string.IsNullOrWhiteSpace(Line)) { Text.AppendLine(Line); } } Text.AppendLine("\t</array>"); Text.AppendLine("\t<key>CFBundleIcons</key>"); Text.AppendLine("\t<dict>"); Text.AppendLine("\t\t<key>CFBundlePrimaryIcon</key>"); Text.AppendLine("\t\t<dict>"); Text.AppendLine("\t\t\t<key>CFBundleIconFiles</key>"); Text.AppendLine("\t\t\t<array>"); Text.AppendLine("\t\t\t\t<string>Icon29.png</string>"); Text.AppendLine("\t\t\t\t<string>[email protected]</string>"); Text.AppendLine("\t\t\t\t<string>Icon40.png</string>"); Text.AppendLine("\t\t\t\t<string>[email protected]</string>"); Text.AppendLine("\t\t\t\t<string>Icon57.png</string>"); Text.AppendLine("\t\t\t\t<string>[email protected]</string>"); Text.AppendLine("\t\t\t\t<string>[email protected]</string>"); Text.AppendLine("\t\t\t\t<string>[email protected]</string>"); Text.AppendLine("\t\t\t</array>"); Text.AppendLine("\t\t\t<key>UIPrerenderedIcon</key>"); Text.AppendLine("\t\t\t<true/>"); Text.AppendLine("\t\t</dict>"); Text.AppendLine("\t</dict>"); Text.AppendLine("\t<key>CFBundleIcons~ipad</key>"); Text.AppendLine("\t<dict>"); Text.AppendLine("\t\t<key>CFBundlePrimaryIcon</key>"); Text.AppendLine("\t\t<dict>"); Text.AppendLine("\t\t\t<key>CFBundleIconFiles</key>"); Text.AppendLine("\t\t\t<array>"); Text.AppendLine("\t\t\t\t<string>Icon29.png</string>"); Text.AppendLine("\t\t\t\t<string>[email protected]</string>"); Text.AppendLine("\t\t\t\t<string>Icon40.png</string>"); Text.AppendLine("\t\t\t\t<string>[email protected]</string>"); Text.AppendLine("\t\t\t\t<string>Icon50.png</string>"); Text.AppendLine("\t\t\t\t<string>[email protected]</string>"); Text.AppendLine("\t\t\t\t<string>Icon72.png</string>"); Text.AppendLine("\t\t\t\t<string>[email protected]</string>"); Text.AppendLine("\t\t\t\t<string>Icon76.png</string>"); Text.AppendLine("\t\t\t\t<string>[email protected]</string>"); Text.AppendLine("\t\t\t\t<string>[email protected]</string>"); Text.AppendLine("\t\t\t</array>"); Text.AppendLine("\t\t\t<key>UIPrerenderedIcon</key>"); Text.AppendLine("\t\t\t<true/>"); Text.AppendLine("\t\t</dict>"); Text.AppendLine("\t</dict>"); if (File.Exists(LaunchXib)) { // TODO: compile the xib via remote tool Text.AppendLine("\t<key>UILaunchStoryboardName</key>"); Text.AppendLine("\t<string>LaunchScreen</string>"); bSkipDefaultPNGs = true; } else { // this is a temp way to inject the iphone 6 images without needing to upgrade everyone's plist // eventually we want to generate this based on what the user has set in the project settings string[] IPhoneConfigs = { "Default-IPhone6-Landscape", "Landscape", "{375, 667}", "8.0", "Default-IPhone6", "Portrait", "{375, 667}", "8.0", "Default-IPhone6Plus-Landscape", "Landscape", "{414, 736}", "8.0", "Default-IPhone6Plus-Portrait", "Portrait", "{414, 736}", "8.0", "Default", "Landscape", "{320, 480}", "7.0", "Default", "Portrait", "{320, 480}", "7.0", "Default-568h", "Landscape", "{320, 568}", "7.0", "Default-568h", "Portrait", "{320, 568}", "7.0", }; Text.AppendLine("\t<key>UILaunchImages~iphone</key>"); Text.AppendLine("\t<array>"); for (int ConfigIndex = 0; ConfigIndex < IPhoneConfigs.Length; ConfigIndex += 4) { Text.AppendLine("\t\t<dict>"); Text.AppendLine("\t\t\t<key>UILaunchImageMinimumOSVersion</key>"); Text.AppendLine(string.Format("\t\t\t<string>{0}</string>", IPhoneConfigs[ConfigIndex + 3])); Text.AppendLine("\t\t\t<key>UILaunchImageName</key>"); Text.AppendLine(string.Format("\t\t\t<string>{0}</string>", IPhoneConfigs[ConfigIndex + 0])); Text.AppendLine("\t\t\t<key>UILaunchImageOrientation</key>"); Text.AppendLine(string.Format("\t\t\t<string>{0}</string>", IPhoneConfigs[ConfigIndex + 1])); Text.AppendLine("\t\t\t<key>UILaunchImageSize</key>"); Text.AppendLine(string.Format("\t\t\t<string>{0}</string>", IPhoneConfigs[ConfigIndex + 2])); Text.AppendLine("\t\t</dict>"); } // close it out Text.AppendLine("\t</array>"); // this is a temp way to inject the iPad Pro without needing to upgrade everyone's plist // eventually we want to generate this based on what the user has set in the project settings string[] IPadConfigs = { "Default-Landscape", "Landscape", "{768, 1024}", "7.0", "Default-Portrait", "Portrait", "{768, 1024}", "7.0", "Default-Landscape-1336", "Landscape", "{1024, 1366}", "9.0", "Default-Portrait-1336", "Portrait", "{1024, 1366}", "9.0", }; Text.AppendLine("\t<key>UILaunchImages~ipad</key>"); Text.AppendLine("\t<array>"); for (int ConfigIndex = 0; ConfigIndex < IPadConfigs.Length; ConfigIndex += 4) { Text.AppendLine("\t\t<dict>"); Text.AppendLine("\t\t\t<key>UILaunchImageMinimumOSVersion</key>"); Text.AppendLine(string.Format("\t\t\t<string>{0}</string>", IPhoneConfigs[ConfigIndex + 3])); Text.AppendLine("\t\t\t<key>UILaunchImageName</key>"); Text.AppendLine(string.Format("\t\t\t<string>{0}</string>", IPhoneConfigs[ConfigIndex + 0])); Text.AppendLine("\t\t\t<key>UILaunchImageOrientation</key>"); Text.AppendLine(string.Format("\t\t\t<string>{0}</string>", IPhoneConfigs[ConfigIndex + 1])); Text.AppendLine("\t\t\t<key>UILaunchImageSize</key>"); Text.AppendLine(string.Format("\t\t\t<string>{0}</string>", IPhoneConfigs[ConfigIndex + 2])); Text.AppendLine("\t\t</dict>"); } Text.AppendLine("\t</array>"); } Text.AppendLine("\t<key>CFBundleSupportedPlatforms</key>"); Text.AppendLine("\t<array>"); Text.AppendLine("\t\t<string>iPhoneOS</string>"); Text.AppendLine("\t</array>"); Text.AppendLine("\t<key>MinimumOSVersion</key>"); Text.AppendLine(string.Format("\t<string>{0}</string>", MinVersion)); // disable exempt encryption Text.AppendLine("\t<key>ITSAppUsesNonExemptEncryption</key>"); Text.AppendLine("\t<false/>"); // disable HTTPS requirement if (bDisableHTTPS) { Text.AppendLine("\t<key>NSAppTransportSecurity</key>"); Text.AppendLine("\t\t<dict>"); Text.AppendLine("\t\t\t<key>NSAllowsArbitraryLoads</key><true/>"); Text.AppendLine("\t\t</dict>"); } if (bEnableFacebookSupport) { Text.AppendLine("\t<key>FacebookAppID</key>"); Text.AppendLine(string.Format("\t<string>{0}</string>", FacebookAppID)); } if (!string.IsNullOrEmpty(ExtraData)) { ExtraData = ExtraData.Replace("\\n", "\n"); foreach (string Line in ExtraData.Split("\r\n".ToCharArray())) { if (!string.IsNullOrWhiteSpace(Line)) { Text.AppendLine("\t" + Line); } } } Text.AppendLine("</dict>"); Text.AppendLine("</plist>"); // Create the intermediate directory if needed if (!Directory.Exists(IntermediateDirectory)) { Directory.CreateDirectory(IntermediateDirectory); } if (InThis != null && InThis.UPL != null) { // Allow UPL to modify the plist here XDocument XDoc; try { XDoc = XDocument.Parse(Text.ToString()); } catch (Exception e) { throw new BuildException("plist is invalid {0}\n{1}", e, Text.ToString()); } XDoc.DocumentType.InternalSubset = ""; InThis.UPL.ProcessPluginNode("None", "iosPListUpdates", "", ref XDoc); string result = XDoc.Declaration.ToString() + "\n" + XDoc.ToString().Replace("<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\"[]>", "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">"); File.WriteAllText(PListFile, result); } else { File.WriteAllText(PListFile, Text.ToString()); } if (BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Mac) { if (!Directory.Exists(AppDirectory)) { Directory.CreateDirectory(AppDirectory); } File.WriteAllText(AppDirectory + "/Info.plist", Text.ToString()); } return(bSkipDefaultPNGs); }
static public string GetNdkApiLevel() { // ask the .ini system for what version to use ConfigCacheIni Ini = new ConfigCacheIni(UnrealTargetPlatform.Android, "Engine", UnrealBuildTool.GetUProjectPath()); string NDKLevel; Ini.GetString("/Script/AndroidPlatformEditor.AndroidSDKSettings", "NDKAPILevel", out NDKLevel); if (NDKLevel == "latest") { // get a list of NDK platforms string PlatformsDir = Environment.ExpandEnvironmentVariables("%NDKROOT%/platforms"); if (!Directory.Exists(PlatformsDir)) { throw new BuildException("No platforms found in {0}", PlatformsDir); } // return the largest of them NDKLevel = GetLargestApiLevel(Directory.GetDirectories(PlatformsDir)); } return(NDKLevel); }
/// <summary> /// Constructor /// </summary> /// <param name="ProjectFile">Project to read settings from</param> public RemoteMac(FileReference ProjectFile) { this.RsyncExe = FileReference.Combine(UnrealBuildTool.EngineDirectory, "Extras", "ThirdPartyNotUE", "DeltaCopy", "Binaries", "Rsync.exe"); this.SshExe = FileReference.Combine(UnrealBuildTool.EngineDirectory, "Extras", "ThirdPartyNotUE", "DeltaCopy", "Binaries", "Ssh.exe"); this.ProjectFile = ProjectFile; this.ProjectDirectory = DirectoryReference.FromFile(ProjectFile); // Apply settings from the XML file XmlConfig.ApplyTo(this); // Get the project config file path DirectoryReference EngineIniPath = ProjectFile != null ? ProjectFile.Directory : null; if (EngineIniPath == null && UnrealBuildTool.GetRemoteIniPath() != null) { EngineIniPath = new DirectoryReference(UnrealBuildTool.GetRemoteIniPath()); } ConfigHierarchy Ini = ConfigCache.ReadHierarchy(ConfigHierarchyType.Engine, EngineIniPath, UnrealTargetPlatform.IOS); // Read the project settings if we don't have anything in the build configuration settings if (String.IsNullOrEmpty(ServerName)) { // Read the server name string IniServerName; if (Ini.GetString("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "RemoteServerName", out IniServerName) && !String.IsNullOrEmpty(IniServerName)) { this.ServerName = IniServerName; } else { throw new BuildException("Remote compiling requires a server name. Use the editor (Project Settings > IOS) to set up your remote compilation settings."); } // Parse the username string IniUserName; if (Ini.GetString("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "RSyncUsername", out IniUserName) && !String.IsNullOrEmpty(IniUserName)) { this.UserName = IniUserName; } } // Split port out from the server name int PortIdx = ServerName.LastIndexOf(':'); if (PortIdx != -1) { string Port = ServerName.Substring(PortIdx + 1); if (!int.TryParse(Port, out ServerPort)) { throw new BuildException("Unable to parse port number from '{0}'", ServerName); } ServerName = ServerName.Substring(0, PortIdx); } // If a user name is not set, use the current user if (String.IsNullOrEmpty(UserName)) { UserName = Environment.UserName; } // Print out the server info Log.TraceInformation("[Remote] Using remote server '{0}' on port {1} (user '{2}')", ServerName, ServerPort, UserName); // Get the path to the SSH private key string OverrideSshPrivateKeyPath; if (Ini.GetString("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "SSHPrivateKeyOverridePath", out OverrideSshPrivateKeyPath) && !String.IsNullOrEmpty(OverrideSshPrivateKeyPath)) { SshPrivateKey = new FileReference(OverrideSshPrivateKeyPath); if (!FileReference.Exists(SshPrivateKey)) { throw new BuildException("SSH private key specified in config file ({0}) does not exist.", SshPrivateKey); } } // If it's not set, look in the standard locations. If that fails, spawn the batch file to generate one. if (SshPrivateKey == null && !TryGetSshPrivateKey(out SshPrivateKey)) { Log.TraceWarning("No SSH private key found for {0}@{1}. Launching SSH to generate one.", UserName, ServerName); StringBuilder CommandLine = new StringBuilder(); CommandLine.AppendFormat("/C \"\"{0}\"", FileReference.Combine(UnrealBuildTool.EngineDirectory, "Build", "BatchFiles", "MakeAndInstallSSHKey.bat")); CommandLine.AppendFormat(" \"{0}\"", SshExe); CommandLine.AppendFormat(" \"{0}\"", ServerPort); CommandLine.AppendFormat(" \"{0}\"", RsyncExe); CommandLine.AppendFormat(" \"{0}\"", UserName); CommandLine.AppendFormat(" \"{0}\"", ServerName); CommandLine.AppendFormat(" \"{0}\"", DirectoryReference.GetSpecialFolder(Environment.SpecialFolder.MyDocuments)); CommandLine.AppendFormat(" \"{0}\"", GetLocalCygwinPath(DirectoryReference.GetSpecialFolder(Environment.SpecialFolder.MyDocuments))); CommandLine.AppendFormat(" \"{0}\"", UnrealBuildTool.EngineDirectory); CommandLine.Append("\""); using (Process ChildProcess = Process.Start("C:\\Windows\\System32\\Cmd.exe", CommandLine.ToString())) { ChildProcess.WaitForExit(); } if (!TryGetSshPrivateKey(out SshPrivateKey)) { throw new BuildException("Failed to generate SSH private key for {0}@{1}.", UserName, ServerName); } } // resolve the rest of the strings RsyncAuthentication = ExpandVariables(RsyncAuthentication); SshAuthentication = ExpandVariables(SshAuthentication); // Build a list of arguments for SSH CommonSshArguments = new List <string>(); CommonSshArguments.Add("-o BatchMode=yes"); CommonSshArguments.Add(SshAuthentication); CommonSshArguments.Add(String.Format("-p {0}", ServerPort)); CommonSshArguments.Add(String.Format("\"{0}@{1}\"", UserName, ServerName)); // Build a list of arguments for Rsync BasicRsyncArguments = new List <string>(); BasicRsyncArguments.Add("--compress"); BasicRsyncArguments.Add("--verbose"); BasicRsyncArguments.Add(String.Format("--rsh=\"{0} -p {1}\"", RsyncAuthentication, ServerPort)); BasicRsyncArguments.Add("--chmod=ug=rwX,o=rxX"); // Build a list of arguments for Rsync filters CommonRsyncArguments = new List <string>(BasicRsyncArguments); CommonRsyncArguments.Add("--recursive"); CommonRsyncArguments.Add("--delete"); // Delete anything not in the source directory CommonRsyncArguments.Add("--delete-excluded"); // Delete anything not in the source directory CommonRsyncArguments.Add("--times"); // Preserve modification times CommonRsyncArguments.Add("--prune-empty-dirs"); // Remove empty directories from the file list // Get the remote base directory StringBuilder Output; if (ExecuteAndCaptureOutput("'echo ~'", out Output) != 0) { throw new BuildException("Unable to determine home directory for remote user"); } RemoteBaseDir = String.Format("{0}/UE4/Builds/{1}", Output.ToString().Trim().TrimEnd('/'), Environment.MachineName); Log.TraceInformation("[Remote] Using base directory '{0}'", RemoteBaseDir); // Build the list of directory mappings between the local and remote machines Mappings = new List <RemoteMapping>(); Mappings.Add(new RemoteMapping(UnrealBuildTool.EngineDirectory, GetRemotePath(UnrealBuildTool.EngineDirectory))); if (ProjectFile != null && !ProjectFile.IsUnderDirectory(UnrealBuildTool.EngineDirectory)) { Mappings.Add(new RemoteMapping(ProjectFile.Directory, GetRemotePath(ProjectFile.Directory))); } }
public static void ParseArchitectures() { // look in ini settings for what platforms to compile for ConfigCacheIni Ini = new ConfigCacheIni(UnrealTargetPlatform.Android, "Engine", UnrealBuildTool.GetUProjectPath()); List <string> ProjectArches = new List <string>(); bool bBuild = true; if (Ini.GetBool("/Script/AndroidRuntimeSettings.AndroidRuntimeSettings", "bBuildForArmV7", out bBuild) && bBuild) { ProjectArches.Add("-armv7"); } if (Ini.GetBool("/Script/AndroidRuntimeSettings.AndroidRuntimeSettings", "bBuildForArm64", out bBuild) && bBuild) { ProjectArches.Add("-arm64"); } if (Ini.GetBool("/Script/AndroidRuntimeSettings.AndroidRuntimeSettings", "bBuildForx86", out bBuild) && bBuild) { ProjectArches.Add("-x86"); } if (Ini.GetBool("/Script/AndroidRuntimeSettings.AndroidRuntimeSettings", "bBuildForx8664", out bBuild) && bBuild) { ProjectArches.Add("-x64"); } // force armv7 if something went wrong if (ProjectArches.Count == 0) { ProjectArches.Add("-armv7"); } Arches = ProjectArches.ToArray(); // Parse selected GPU architectures List <string> ProjectGPUArches = new List <string>(); if (Ini.GetBool("/Script/AndroidRuntimeSettings.AndroidRuntimeSettings", "bBuildForES2", out bBuild) && bBuild) { ProjectGPUArches.Add("-es2"); } if (Ini.GetBool("/Script/AndroidRuntimeSettings.AndroidRuntimeSettings", "bBuildForES31", out bBuild) && bBuild) { ProjectGPUArches.Add("-es31"); } if (Ini.GetBool("/Script/AndroidRuntimeSettings.AndroidRuntimeSettings", "bBuildForGL4", out bBuild) && bBuild) { ProjectGPUArches.Add("-gl4"); } if (ProjectGPUArches.Count == 0) { ProjectGPUArches.Add("-es2"); } GPUArchitectures = ProjectGPUArches.ToArray(); List <string> FullArchCombinations = new List <string>(); foreach (string Arch in Arches) { foreach (string GPUArch in GPUArchitectures) { FullArchCombinations.Add(Arch + GPUArch); } } AllComboNames = FullArchCombinations.ToArray(); }
/** * Checks the class header files and determines if generated UObject code files are out of date in comparison. * @param UObjectModules Modules that we generate headers for * * @return True if the code files are out of date * */ private static bool AreGeneratedCodeFilesOutOfDate(List <UHTModuleInfo> UObjectModules) { bool bIsOutOfDate = false; // Get UnrealHeaderTool timestamp. If it's newer than generated headers, they need to be rebuilt too. var HeaderToolTimestamp = CheckIfUnrealHeaderToolIsUpToDate(); // Get CoreUObject.generated.cpp timestamp. If the source files are older than the CoreUObject generated code, we'll // need to regenerate code for the module DateTime?CoreGeneratedTimestamp = null; { // Find the CoreUObject module foreach (var Module in UObjectModules) { if (Module.ModuleName.Equals("CoreUObject", StringComparison.InvariantCultureIgnoreCase)) { CoreGeneratedTimestamp = GetCoreGeneratedTimestamp(Module.ModuleName, Path.GetDirectoryName(Module.GeneratedCPPFilenameBase)); break; } } if (CoreGeneratedTimestamp == null) { throw new BuildException("Could not find CoreUObject in list of all UObjectModules"); } } foreach (var Module in UObjectModules) { // In Rocket, we skip checking timestamps for modules that don't exist within the project's directory if (UnrealBuildTool.RunningRocket()) { // @todo Rocket: This could be done in a better way I'm sure if (!Utils.IsFileUnderDirectory(Module.ModuleDirectory, UnrealBuildTool.GetUProjectPath())) { // Engine or engine plugin module - Rocket does not regenerate them so don't compare their timestamps continue; } } // Make sure we have an existing folder for generated code. If not, then we definitely need to generate code! var GeneratedCodeDirectory = Path.GetDirectoryName(Module.GeneratedCPPFilenameBase); var TestDirectory = (FileSystemInfo) new DirectoryInfo(GeneratedCodeDirectory); if (TestDirectory.Exists) { // Grab our special "Timestamp" file that we saved after the last set of headers were generated string TimestampFile = Path.Combine(GeneratedCodeDirectory, @"Timestamp"); var SavedTimestampFileInfo = (FileSystemInfo) new FileInfo(TimestampFile); if (SavedTimestampFileInfo.Exists) { // Make sure the last UHT run completed after UnrealHeaderTool.exe was compiled last, and after the CoreUObject headers were touched last. var SavedTimestamp = SavedTimestampFileInfo.LastWriteTime; if (SavedTimestamp.CompareTo(HeaderToolTimestamp) > 0 && SavedTimestamp.CompareTo(CoreGeneratedTimestamp) > 0) { // Iterate over our UObjects headers and figure out if any of them have changed var AllUObjectHeaders = new List <FileItem>(); AllUObjectHeaders.AddRange(Module.PublicUObjectClassesHeaders); AllUObjectHeaders.AddRange(Module.PublicUObjectHeaders); AllUObjectHeaders.AddRange(Module.PrivateUObjectHeaders); foreach (var HeaderFile in AllUObjectHeaders) { var HeaderFileTimestamp = HeaderFile.Info.LastWriteTime; // Has the source header changed since we last generated headers successfully? if (SavedTimestamp.CompareTo(HeaderFileTimestamp) < 0) { bIsOutOfDate = true; break; } // When we're running in assembler mode, outdatedness cannot be inferred by checking the directory timestamp // of the source headers. We don't care if source files were added or removed in this mode, because we're only // able to process the known UObject headers that are in the Makefile. If UObject header files are added/removed, // we expect the user to re-run GenerateProjectFiles which will force UBTMakefile outdatedness. // @todo ubtmake: Possibly, we should never be doing this check these days. if (UnrealBuildTool.IsGatheringBuild || !UnrealBuildTool.IsAssemblingBuild) { // Also check the timestamp on the directory the source file is in. If the directory timestamp has // changed, new source files may have been added or deleted. We don't know whether the new/deleted // files were actually UObject headers, but because we don't know all of the files we processed // in the previous run, we need to assume our generated code is out of date if the directory timestamp // is newer. var HeaderDirectoryTimestamp = new DirectoryInfo(Path.GetDirectoryName(HeaderFile.AbsolutePath)).LastWriteTime; if (SavedTimestamp.CompareTo(HeaderDirectoryTimestamp) < 0) { bIsOutOfDate = true; break; } } } } else { // Generated code is older UnrealHeaderTool.exe or CoreUObject headers. Out of date! bIsOutOfDate = true; } } else { // Timestamp file was missing (possibly deleted/cleaned), so headers are out of date bIsOutOfDate = true; } } else { // Generated code directory is missing entirely! bIsOutOfDate = true; } // If even one module is out of date, we're done! UHT does them all in one fell swoop.; if (bIsOutOfDate) { break; } } return(bIsOutOfDate); }
private bool WriteQMakePro() { string GameProjectPath = ""; string GameProjectFile = ""; string GameProjectRootPath = ""; string BuildCommand = ""; string QMakeGameProjectFile = ""; if (!String.IsNullOrEmpty(GameProjectName)) { GameProjectPath = UnrealBuildTool.GetUProjectPath(); GameProjectFile = UnrealBuildTool.GetUProjectFile(); QMakeGameProjectFile = "gameProjectFile=" + GameProjectFile + "\n"; BuildCommand = "build=mono $$unrealRootPath/Engine/Binaries/DotNET/UnrealBuildTool.exe\n\n"; } else { BuildCommand = "build=bash $$unrealRootPath/Engine/Build/BatchFiles/Linux/Build.sh\n"; } var UnrealRootPath = Path.GetFullPath(ProjectFileGenerator.RootRelativePath); var FileName = MasterProjectName + ".pro"; var QMakeFileContent = new StringBuilder(); var QMakeSectionEnd = " \n\n"; var QMakeSourceFilesList = "SOURCES += \\ \n"; var QMakeHeaderFilesList = "HEADERS += \\ \n"; var QMakeConfigFilesList = "OTHER_FILES += \\ \n"; var QMakeTargetList = "QMAKE_EXTRA_TARGETS += \\ \n"; if (!String.IsNullOrEmpty(GameProjectName)) { GameProjectRootPath = GameProjectName + "RootPath=" + GameProjectPath + "\n\n"; } QMakeFileContent.Append( "# UnrealEngine.pro generated by QMakefileGenerator.cs\n" + "# *DO NOT EDIT*\n\n" + "TEMPLATE = aux\n" + "CONFIG -= console\n" + "CONFIG -= app_bundle\n" + "CONFIG -= qt\n\n" + "TARGET = UE4 \n\n" + "unrealRootPath=" + UnrealRootPath + "\n" + GameProjectRootPath + QMakeGameProjectFile + BuildCommand + "args=$(ARGS)\n\n" ); // Create SourceFiles, HeaderFiles, and ConfigFiles sections. var AllModuleFiles = DiscoverModules(); foreach (string CurModuleFile in AllModuleFiles) { var FoundFiles = SourceFileSearch.FindModuleSourceFiles(CurModuleFile, ExcludeNoRedistFiles: bExcludeNoRedistFiles); foreach (string CurSourceFile in FoundFiles) { string SourceFileRelativeToRoot = Utils.MakePathRelativeTo(CurSourceFile, Path.Combine(EngineRelativePath)); // Exclude Windows, Mac, and iOS only files/folders. // This got ugly quick. if (!SourceFileRelativeToRoot.Contains("Source/ThirdParty/") && !SourceFileRelativeToRoot.Contains("/Windows/") && !SourceFileRelativeToRoot.Contains("/Mac/") && !SourceFileRelativeToRoot.Contains("/IOS/") && !SourceFileRelativeToRoot.Contains("/iOS/") && !SourceFileRelativeToRoot.Contains("/VisualStudioSourceCodeAccess/") && !SourceFileRelativeToRoot.Contains("/XCodeSourceCodeAccess/") && !SourceFileRelativeToRoot.Contains("/WmfMedia/") && !SourceFileRelativeToRoot.Contains("/IOSDeviceProfileSelector/") && !SourceFileRelativeToRoot.Contains("/WindowsDeviceProfileSelector/") && !SourceFileRelativeToRoot.Contains("/WindowsMoviePlayer/") && !SourceFileRelativeToRoot.Contains("/AppleMoviePlayer/") && !SourceFileRelativeToRoot.Contains("/MacGraphicsSwitching/") && !SourceFileRelativeToRoot.Contains("/Apple/") && !SourceFileRelativeToRoot.Contains("/WinRT/") ) { if (SourceFileRelativeToRoot.EndsWith(".cpp")) { if (!SourceFileRelativeToRoot.StartsWith("..") && !Path.IsPathRooted(SourceFileRelativeToRoot)) { // SourceFileRelativeToRoot = "Engine/" + SourceFileRelativeToRoot; QMakeSourceFilesList += ("\t\"" + "$$unrealRootPath/Engine/" + SourceFileRelativeToRoot + "\" \\\n"); } else { if (String.IsNullOrEmpty(GameProjectName)) { // SourceFileRelativeToRoot = SourceFileRelativeToRoot.Substring (3); QMakeSourceFilesList += ("\t\"" + SourceFileRelativeToRoot.Substring(3) + "\" \\\n"); } else if (!String.IsNullOrEmpty(GameProjectName)) { QMakeSourceFilesList += ("\t\"$$" + GameProjectName + "RootPath/" + Utils.MakePathRelativeTo(CurSourceFile, GameProjectPath) + "\" \\\n"); } else { System.Console.WriteLine("Error!, you should not be here."); } } } if (SourceFileRelativeToRoot.EndsWith(".h")) { if (!SourceFileRelativeToRoot.StartsWith("..") && !Path.IsPathRooted(SourceFileRelativeToRoot)) { // SourceFileRelativeToRoot = "Engine/" + SourceFileRelativeToRoot; QMakeHeaderFilesList += ("\t\"" + "$$unrealRootPath/Engine/" + SourceFileRelativeToRoot + "\" \\\n"); } else { if (String.IsNullOrEmpty(GameProjectName)) { // SourceFileRelativeToRoot = SourceFileRelativeToRoot.Substring (3); QMakeHeaderFilesList += ("\t\"" + SourceFileRelativeToRoot.Substring(3) + "\" \\\n"); } else if (!String.IsNullOrEmpty(GameProjectName)) { QMakeHeaderFilesList += ("\t\"$$" + GameProjectName + "RootPath/" + Utils.MakePathRelativeTo(CurSourceFile, GameProjectPath) + "\" \\\n"); } else { System.Console.WriteLine("Error!, you should not be here."); } } } if (SourceFileRelativeToRoot.EndsWith(".cs")) { if (!SourceFileRelativeToRoot.StartsWith("..") && !Path.IsPathRooted(SourceFileRelativeToRoot)) { // SourceFileRelativeToRoot = "Engine/" + SourceFileRelativeToRoot; QMakeConfigFilesList += ("\t\"" + "$$unrealRootPath/Engine/" + SourceFileRelativeToRoot + "\" \\\n"); } else { if (String.IsNullOrEmpty(GameProjectName)) { // SourceFileRelativeToRoot = SourceFileRelativeToRoot.Substring (3); QMakeConfigFilesList += ("\t\"" + SourceFileRelativeToRoot.Substring(3) + "\" \\\n"); } else if (!String.IsNullOrEmpty(GameProjectName)) { QMakeConfigFilesList += ("\t\"$$" + GameProjectName + "RootPath/" + Utils.MakePathRelativeTo(CurSourceFile, GameProjectPath) + "\" \\\n"); } else { System.Console.WriteLine("Error!, you should not be here."); } } } } } } // Add section end to section strings; QMakeSourceFilesList += QMakeSectionEnd; QMakeHeaderFilesList += QMakeSectionEnd; QMakeConfigFilesList += QMakeSectionEnd; // Append sections to the QMakeLists.txt file QMakeFileContent.Append(QMakeSourceFilesList); QMakeFileContent.Append(QMakeHeaderFilesList); QMakeFileContent.Append(QMakeConfigFilesList); string QMakeProjectCmdArg = ""; foreach (string TargetFilePath in DiscoverTargets()) { var TargetName = Utils.GetFilenameWithoutAnyExtensions(TargetFilePath); // Remove both ".cs" and ". foreach (UnrealTargetConfiguration CurConfiguration in Enum.GetValues(typeof(UnrealTargetConfiguration))) { if (CurConfiguration != UnrealTargetConfiguration.Unknown && CurConfiguration != UnrealTargetConfiguration.Development) { if (UnrealBuildTool.IsValidConfiguration(CurConfiguration)) { if (TargetName == GameProjectName || TargetName == (GameProjectName + "Editor")) { QMakeProjectCmdArg = " -project=\"\\\"$$gameProjectFile\\\"\""; } var ConfName = Enum.GetName(typeof(UnrealTargetConfiguration), CurConfiguration); QMakeFileContent.Append(String.Format("{0}-Linux-{1}.commands = $$build {2} {0} Linux {1} $$args\n", TargetName, ConfName, QMakeProjectCmdArg)); QMakeTargetList += "\t" + TargetName + "-Linux-" + ConfName + " \\\n"; // , TargetName, ConfName); } } } if (TargetName == GameProjectName || TargetName == (GameProjectName + "Editor")) { QMakeProjectCmdArg = " -project=\"\\\"$$gameProjectFile\\\"\""; } QMakeFileContent.Append(String.Format("{0}.commands = $$build {1} {0} Linux Development $$args\n\n", TargetName, QMakeProjectCmdArg)); QMakeTargetList += "\t" + TargetName + " \\\n"; } QMakeFileContent.Append(QMakeTargetList.TrimEnd('\\')); var FullFileName = Path.Combine(MasterProjectRelativePath, FileName); return(WriteFileIfChanged(FullFileName, QMakeFileContent.ToString())); }
public static UE4Build.BuildAgenda MakeAgenda(UnrealBuildTool.UnrealTargetPlatform[] Platforms, List<string> ExtraBuildProducts) { // Create the build agenda UE4Build.BuildAgenda Agenda = new UE4Build.BuildAgenda(); // C# binaries Agenda.SwarmProject = @"Engine\Source\Programs\UnrealSwarm\UnrealSwarm.sln"; Agenda.DotNetProjects.Add(@"Engine/Source/Editor/SwarmInterface/DotNET/SwarmInterface.csproj"); Agenda.DotNetProjects.Add(@"Engine/Source/Programs/DotNETCommon/DotNETUtilities/DotNETUtilities.csproj"); Agenda.DotNetProjects.Add(@"Engine/Source/Programs/RPCUtility/RPCUtility.csproj"); Agenda.DotNetProjects.Add(@"Engine/Source/Programs/UnrealControls/UnrealControls.csproj"); // Windows binaries if(Platforms.Contains(UnrealBuildTool.UnrealTargetPlatform.Win64)) { Agenda.AddTarget("CrashReportClient", UnrealBuildTool.UnrealTargetPlatform.Win64, UnrealBuildTool.UnrealTargetConfiguration.Development); Agenda.AddTarget("CrashReportClient", UnrealBuildTool.UnrealTargetPlatform.Win32, UnrealBuildTool.UnrealTargetConfiguration.Development); Agenda.AddTarget("UnrealHeaderTool", UnrealBuildTool.UnrealTargetPlatform.Win64, UnrealBuildTool.UnrealTargetConfiguration.Development); Agenda.AddTarget("UnrealPak", UnrealBuildTool.UnrealTargetPlatform.Win64, UnrealBuildTool.UnrealTargetConfiguration.Development); Agenda.AddTarget("UnrealLightmass", UnrealBuildTool.UnrealTargetPlatform.Win64, UnrealBuildTool.UnrealTargetConfiguration.Development); Agenda.AddTarget("ShaderCompileWorker", UnrealBuildTool.UnrealTargetPlatform.Win64, UnrealBuildTool.UnrealTargetConfiguration.Development); Agenda.AddTarget("UnrealVersionSelector", UnrealBuildTool.UnrealTargetPlatform.Win64, UnrealBuildTool.UnrealTargetConfiguration.Shipping); Agenda.AddTarget("BootstrapPackagedGame", UnrealBuildTool.UnrealTargetPlatform.Win64, UnrealBuildTool.UnrealTargetConfiguration.Shipping); Agenda.AddTarget("BootstrapPackagedGame", UnrealBuildTool.UnrealTargetPlatform.Win32, UnrealBuildTool.UnrealTargetConfiguration.Shipping); Agenda.AddTarget("UnrealCEFSubProcess", UnrealBuildTool.UnrealTargetPlatform.Win64, UnrealBuildTool.UnrealTargetConfiguration.Development); Agenda.AddTarget("UnrealCEFSubProcess", UnrealBuildTool.UnrealTargetPlatform.Win32, UnrealBuildTool.UnrealTargetConfiguration.Development); } // Mac binaries if(Platforms.Contains(UnrealBuildTool.UnrealTargetPlatform.Mac)) { Agenda.AddTarget("CrashReportClient", UnrealBuildTool.UnrealTargetPlatform.Mac, UnrealBuildTool.UnrealTargetConfiguration.Development, InAddArgs: "-CopyAppBundleBackToDevice"); Agenda.AddTarget("UnrealPak", UnrealBuildTool.UnrealTargetPlatform.Mac, UnrealBuildTool.UnrealTargetConfiguration.Development, InAddArgs: "-CopyAppBundleBackToDevice"); Agenda.AddTarget("UnrealLightmass", UnrealBuildTool.UnrealTargetPlatform.Mac, UnrealBuildTool.UnrealTargetConfiguration.Development, InAddArgs: "-CopyAppBundleBackToDevice"); Agenda.AddTarget("ShaderCompileWorker", UnrealBuildTool.UnrealTargetPlatform.Mac, UnrealBuildTool.UnrealTargetConfiguration.Development, InAddArgs: "-CopyAppBundleBackToDevice"); Agenda.AddTarget("UE4EditorServices", UnrealBuildTool.UnrealTargetPlatform.Mac, UnrealBuildTool.UnrealTargetConfiguration.Development, InAddArgs: "-CopyAppBundleBackToDevice"); Agenda.AddTarget("UnrealCEFSubProcess", UnrealBuildTool.UnrealTargetPlatform.Mac, UnrealBuildTool.UnrealTargetConfiguration.Development, InAddArgs: "-CopyAppBundleBackToDevice"); } // iOS binaries if(Platforms.Contains(UnrealBuildTool.UnrealTargetPlatform.IOS)) { Agenda.DotNetProjects.Add(@"Engine/Source/Programs/iOS/iPhonePackager/iPhonePackager.csproj"); ExtraBuildProducts.Add(CommandUtils.CombinePaths(CommandUtils.CmdEnv.LocalRoot, @"Engine/Binaries/DotNET/iOS/iPhonePackager.exe")); Agenda.DotNetProjects.Add(@"Engine/Source/Programs/iOS/DeploymentServer/DeploymentServer.csproj"); ExtraBuildProducts.Add(CommandUtils.CombinePaths(CommandUtils.CmdEnv.LocalRoot, @"Engine/Binaries/DotNET/iOS/DeploymentServer.exe")); Agenda.DotNetProjects.Add(@"Engine/Source/Programs/iOS/DeploymentInterface/DeploymentInterface.csproj"); ExtraBuildProducts.Add(CommandUtils.CombinePaths(CommandUtils.CmdEnv.LocalRoot, @"Engine/Binaries/DotNET/iOS/DeploymentInterface.dll")); Agenda.DotNetProjects.Add(@"Engine/Source/Programs/iOS/MobileDeviceInterface/MobileDeviceInterface.csproj"); ExtraBuildProducts.Add(CommandUtils.CombinePaths(CommandUtils.CmdEnv.LocalRoot, @"Engine/Binaries/DotNET/iOS/MobileDeviceInterface.dll")); } // PS4 binaries if(Platforms.Contains(UnrealBuildTool.UnrealTargetPlatform.PS4)) { Agenda.AddTarget("PS4MapFileUtil", UnrealBuildTool.UnrealTargetPlatform.Win64, UnrealBuildTool.UnrealTargetConfiguration.Development); Agenda.DotNetProjects.Add(CommandUtils.CombinePaths(CommandUtils.CmdEnv.LocalRoot, @"Engine/Source/Programs/PS4/PS4DevKitUtil/PS4DevKitUtil.csproj")); ExtraBuildProducts.Add(CommandUtils.CombinePaths(CommandUtils.CmdEnv.LocalRoot, @"Engine/Binaries/DotNET/PS4/PS4DevKitUtil.exe")); } // Xbox One binaries if(Platforms.Contains(UnrealBuildTool.UnrealTargetPlatform.XboxOne)) { Agenda.AddTarget("XboxOnePDBFileUtil", UnrealBuildTool.UnrealTargetPlatform.Win64, UnrealBuildTool.UnrealTargetConfiguration.Development); } // HTML5 binaries if (Platforms.Contains(UnrealBuildTool.UnrealTargetPlatform.HTML5)) { Agenda.DotNetProjects.Add(@"Engine/Source/Programs/HTML5/HTML5LaunchHelper/HTML5LaunchHelper.csproj"); ExtraBuildProducts.Add(CommandUtils.CombinePaths(CommandUtils.CmdEnv.LocalRoot, @"Engine/Binaries/DotNET/HTML5LaunchHelper.exe")); } return Agenda; }
/// <summary> /// Loads a makefile from disk /// </summary> /// <param name="MakefilePath">Path to the makefile to load</param> /// <param name="ProjectFile">Path to the project file</param> /// <param name="Platform">Platform for this makefile</param> /// <param name="Arguments">Command line arguments for this target</param> /// <param name="ReasonNotLoaded">If the function returns null, this string will contain the reason why</param> /// <returns>The loaded makefile, or null if it failed for some reason. On failure, the 'ReasonNotLoaded' variable will contain information about why</returns> public static TargetMakefile Load(FileReference MakefilePath, FileReference ProjectFile, UnrealTargetPlatform Platform, string[] Arguments, out string ReasonNotLoaded) { FileInfo MakefileInfo; using (Timeline.ScopeEvent("Checking dependent timestamps")) { // Check the directory timestamp on the project files directory. If the user has generated project files more recently than the makefile, then we need to consider the file to be out of date MakefileInfo = new FileInfo(MakefilePath.FullName); if (!MakefileInfo.Exists) { // Makefile doesn't even exist, so we won't bother loading it ReasonNotLoaded = "no existing makefile"; return(null); } // Check the build version FileInfo BuildVersionFileInfo = new FileInfo(BuildVersion.GetDefaultFileName().FullName); if (BuildVersionFileInfo.Exists && MakefileInfo.LastWriteTime.CompareTo(BuildVersionFileInfo.LastWriteTime) < 0) { Log.TraceLog("Existing makefile is older than Build.version, ignoring it"); ReasonNotLoaded = "Build.version is newer"; return(null); } // @todo ubtmake: This will only work if the directory timestamp actually changes with every single GPF. Force delete existing files before creating new ones? Eh... really we probably just want to delete + create a file in that folder // -> UPDATE: Seems to work OK right now though on Windows platform, maybe due to GUID changes // @todo ubtmake: Some platforms may not save any files into this folder. We should delete + generate a "touch" file to force the directory timestamp to be updated (or just check the timestamp file itself. We could put it ANYWHERE, actually) // Installed Build doesn't need to check engine projects for outdatedness if (!UnrealBuildTool.IsEngineInstalled()) { if (DirectoryReference.Exists(ProjectFileGenerator.IntermediateProjectFilesPath)) { DateTime EngineProjectFilesLastUpdateTime = new FileInfo(ProjectFileGenerator.ProjectTimestampFile).LastWriteTime; if (MakefileInfo.LastWriteTime.CompareTo(EngineProjectFilesLastUpdateTime) < 0) { // Engine project files are newer than makefile Log.TraceLog("Existing makefile is older than generated engine project files, ignoring it"); ReasonNotLoaded = "project files are newer"; return(null); } } } // Check the game project directory too if (ProjectFile != null) { string ProjectFilename = ProjectFile.FullName; FileInfo ProjectFileInfo = new FileInfo(ProjectFilename); if (!ProjectFileInfo.Exists || MakefileInfo.LastWriteTime.CompareTo(ProjectFileInfo.LastWriteTime) < 0) { // .uproject file is newer than makefile Log.TraceLog("Makefile is older than .uproject file, ignoring it"); ReasonNotLoaded = ".uproject file is newer"; return(null); } DirectoryReference MasterProjectRelativePath = ProjectFile.Directory; string GameIntermediateProjectFilesPath = Path.Combine(MasterProjectRelativePath.FullName, "Intermediate", "ProjectFiles"); if (Directory.Exists(GameIntermediateProjectFilesPath)) { DateTime GameProjectFilesLastUpdateTime = new DirectoryInfo(GameIntermediateProjectFilesPath).LastWriteTime; if (MakefileInfo.LastWriteTime.CompareTo(GameProjectFilesLastUpdateTime) < 0) { // Game project files are newer than makefile Log.TraceLog("Makefile is older than generated game project files, ignoring it"); ReasonNotLoaded = "game project files are newer"; return(null); } } } // Check to see if UnrealBuildTool.exe was compiled more recently than the makefile DateTime UnrealBuildToolTimestamp = new FileInfo(Assembly.GetExecutingAssembly().Location).LastWriteTime; if (MakefileInfo.LastWriteTime.CompareTo(UnrealBuildToolTimestamp) < 0) { // UnrealBuildTool.exe was compiled more recently than the makefile Log.TraceLog("Makefile is older than UnrealBuildTool.exe, ignoring it"); ReasonNotLoaded = "UnrealBuildTool.exe is newer"; return(null); } // Check to see if any BuildConfiguration files have changed since the last build List <XmlConfig.InputFile> InputFiles = XmlConfig.FindInputFiles(); foreach (XmlConfig.InputFile InputFile in InputFiles) { FileInfo InputFileInfo = new FileInfo(InputFile.Location.FullName); if (InputFileInfo.LastWriteTime > MakefileInfo.LastWriteTime) { Log.TraceLog("Makefile is older than BuildConfiguration.xml, ignoring it"); ReasonNotLoaded = "BuildConfiguration.xml is newer"; return(null); } } } TargetMakefile Makefile; using (Timeline.ScopeEvent("Loading makefile")) { try { using (BinaryArchiveReader Reader = new BinaryArchiveReader(MakefilePath)) { int Version = Reader.ReadInt(); if (Version != CurrentVersion) { ReasonNotLoaded = "makefile version does not match"; return(null); } Makefile = new TargetMakefile(Reader, MakefileInfo.LastWriteTimeUtc); } } catch (Exception Ex) { Log.TraceWarning("Failed to read makefile: {0}", Ex.Message); Log.TraceLog("Exception: {0}", Ex.ToString()); ReasonNotLoaded = "couldn't read existing makefile"; return(null); } } using (Timeline.ScopeEvent("Checking makefile validity")) { // Check if the arguments are different if (!Enumerable.SequenceEqual(Makefile.AdditionalArguments, Arguments)) { ReasonNotLoaded = "command line arguments changed"; return(null); } // Check if any config settings have changed. Ini files contain build settings too. if (!Makefile.ConfigValueTracker.IsValid()) { ReasonNotLoaded = "config setting changed"; return(null); } // Get the current build metadata from the platform string CurrentExternalMetadata = UEBuildPlatform.GetBuildPlatform(Platform).GetExternalBuildMetadata(ProjectFile); if (String.Compare(CurrentExternalMetadata, Makefile.ExternalMetadata, StringComparison.Ordinal) != 0) { Log.TraceLog("Old metadata:\n", Makefile.ExternalMetadata); Log.TraceLog("New metadata:\n", CurrentExternalMetadata); ReasonNotLoaded = "build metadata has changed"; return(null); } } // The makefile is ok ReasonNotLoaded = null; return(Makefile); }
public bool CanUseXGE(UnrealBuildTool.UnrealTargetPlatform Platform) { if (!UnrealBuildTool.UEBuildPlatform.IsPlatformAvailable(Platform)) { return false; } return UnrealBuildTool.UEBuildPlatform.GetBuildPlatform(Platform).CanUseXGE(); }
/// <summary> /// Given a set of C++ files, generates another set of C++ files that #include all the original /// files, the goal being to compile the same code in fewer translation units. /// The "unity" files are written to the CompileEnvironment's OutputDirectory. /// </summary> /// <param name="Target">The target we're building</param> /// <param name="CPPFiles">The C++ files to #include.</param> /// <param name="CompileEnvironment">The environment that is used to compile the C++ files.</param> /// <param name="BaseName">Base name to use for the Unity files</param> /// <returns>The "unity" C++ files.</returns> public static List <FileItem> GenerateUnityCPPs( UEToolChain ToolChain, UEBuildTarget Target, List <FileItem> CPPFiles, CPPEnvironment CompileEnvironment, string BaseName ) { List <FileItem> NewCPPFiles = new List <FileItem>(); UEBuildPlatform BuildPlatform = UEBuildPlatform.GetBuildPlatformForCPPTargetPlatform(CompileEnvironment.Config.Platform); // Figure out size of all input files combined. We use this to determine whether to use larger unity threshold or not. long TotalBytesInCPPFiles = CPPFiles.Sum(F => F.Info.Length); // We have an increased threshold for unity file size if, and only if, all files fit into the same unity file. This // is beneficial when dealing with PCH files. The default PCH creation limit is X unity files so if we generate < X // this could be fairly slow and we'd rather bump the limit a bit to group them all into the same unity file. // When enabled, UnrealBuildTool will try to determine source files that you are actively iteratively changing, and break those files // out of their unity blobs so that you can compile them as individual translation units, much faster than recompiling the entire // unity blob each time. bool bUseAdaptiveUnityBuild = BuildConfiguration.bUseAdaptiveUnityBuild && !BuildConfiguration.bStressTestUnity; // Optimization only makes sense if PCH files are enabled. bool bForceIntoSingleUnityFile = BuildConfiguration.bStressTestUnity || (TotalBytesInCPPFiles < BuildConfiguration.NumIncludedBytesPerUnityCPP * 2 && CompileEnvironment.ShouldUsePCHs()); // Build the list of unity files. List <FileCollection> AllUnityFiles; { // Sort the incoming file paths alphabetically, so there will be consistency in unity blobs across multiple machines. // Note that we're relying on this not only sorting files within each directory, but also the directories // themselves, so the whole list of file paths is the same across computers. List <FileItem> SortedCPPFiles = CPPFiles.GetRange(0, CPPFiles.Count); { // Case-insensitive file path compare, because you never know what is going on with local file systems Comparison <FileItem> FileItemComparer = (FileA, FileB) => { return(FileA.AbsolutePath.ToLowerInvariant().CompareTo(FileB.AbsolutePath.ToLowerInvariant())); }; SortedCPPFiles.Sort(FileItemComparer); } // Figure out whether we REALLY want to use adaptive unity for this module. If nearly every file in the module appears in the working // set, we'll just go ahead and let unity build do its thing. if (bUseAdaptiveUnityBuild) { int CandidateWorkingSetSourceFileCount = 0; int WorkingSetSourceFileCount = 0; foreach (FileItem CPPFile in SortedCPPFiles) { // Don't include writable source files into unity blobs if (!CPPFile.Reference.IsUnderDirectory(Target.EngineIntermediateDirectory) && !CPPFile.Reference.IsUnderDirectory(Target.ProjectIntermediateDirectory)) { ++CandidateWorkingSetSourceFileCount; if (UnrealBuildTool.ShouldSourceFileBePartOfWorkingSet(CPPFile.AbsolutePath)) { ++WorkingSetSourceFileCount; // Mark this file as part of the working set. This will be saved into the UBT Makefile so that // the assembler can automatically invalidate the Makefile when the working set changes (allowing this // code to run again, to build up new unity blobs.) SourceFileWorkingSet.Add(CPPFile); } } } if (WorkingSetSourceFileCount >= CandidateWorkingSetSourceFileCount) { // Every single file in the module appears in the working set, so don't bother using adaptive unity for this // module. Otherwise it would make full builds really slow. bUseAdaptiveUnityBuild = false; } } UnityFileBuilder CPPUnityFileBuilder = new UnityFileBuilder(bForceIntoSingleUnityFile ? -1 : BuildConfiguration.NumIncludedBytesPerUnityCPP); StringBuilder AdaptiveUnityBuildInfoString = new StringBuilder(); foreach (FileItem CPPFile in SortedCPPFiles) { if (!bForceIntoSingleUnityFile && CPPFile.AbsolutePath.IndexOf(".GeneratedWrapper.", StringComparison.InvariantCultureIgnoreCase) != -1) { NewCPPFiles.Add(CPPFile); } // When adaptive unity is enabled, go ahead and exclude any source files that we're actively working with if (bUseAdaptiveUnityBuild && SourceFileWorkingSet.Contains(CPPFile)) { // Just compile this file normally, not as part of the unity blob NewCPPFiles.Add(CPPFile); // Let the unity file builder know about the file, so that we can retain the existing size of the unity blobs. // This won't actually make the source file part of the unity blob, but it will keep track of how big the // file is so that other existing unity blobs from the same module won't be invalidated. This prevents much // longer compile times the first time you build after your working file set changes. CPPUnityFileBuilder.AddVirtualFile(CPPFile); string CPPFileName = Path.GetFileName(CPPFile.AbsolutePath); if (AdaptiveUnityBuildInfoString.Length == 0) { AdaptiveUnityBuildInfoString.Append(String.Format("[Adaptive unity build] Excluded from {0} unity file: {1}", BaseName, CPPFileName)); } else { AdaptiveUnityBuildInfoString.Append(", " + CPPFileName); } } else { // If adaptive unity build is enabled for this module, add this source file to the set that will invalidate the makefile if (bUseAdaptiveUnityBuild) { CandidateSourceFilesForWorkingSet.Add(CPPFile); } // Compile this file as part of the unity blob CPPUnityFileBuilder.AddFile(CPPFile); // Now that the CPPFile is part of this unity file, we will no longer need to treat it like a root level prerequisite for our // dependency cache, as it is now an "indirect include" from the unity file. We'll clear out the compile environment // attached to this file. This prevents us from having to cache all of the indirect includes from these files inside our // dependency cache, which speeds up iterative builds a lot! CPPFile.CachedCPPIncludeInfo = null; } } if (AdaptiveUnityBuildInfoString.Length > 0) { Log.TraceInformation(AdaptiveUnityBuildInfoString.ToString()); } AllUnityFiles = CPPUnityFileBuilder.GetUnityFiles(); } // Create a set of CPP files that combine smaller CPP files into larger compilation units, along with the corresponding // actions to compile them. int CurrentUnityFileCount = 0; foreach (FileCollection UnityFile in AllUnityFiles) { ++CurrentUnityFileCount; StringWriter OutputUnityCPPWriter = new StringWriter(); StringWriter OutputUnityCPPWriterExtra = null; // add an extra file for UBT to get the #include dependencies from if (BuildPlatform.RequiresExtraUnityCPPWriter() == true) { OutputUnityCPPWriterExtra = new StringWriter(); } OutputUnityCPPWriter.WriteLine("// This file is automatically generated at compile-time to include some subset of the user-created cpp files."); // Add source files to the unity file foreach (FileItem CPPFile in UnityFile.Files) { OutputUnityCPPWriter.WriteLine("#include \"{0}\"", ToolChain.ConvertPath(CPPFile.AbsolutePath)); if (OutputUnityCPPWriterExtra != null) { OutputUnityCPPWriterExtra.WriteLine("#include \"{0}\"", CPPFile.AbsolutePath); } } // Determine unity file path name string UnityCPPFileName; if (AllUnityFiles.Count > 1) { UnityCPPFileName = string.Format("{0}{1}.{2}_of_{3}.cpp", ModulePrefix, BaseName, CurrentUnityFileCount, AllUnityFiles.Count); } else { UnityCPPFileName = string.Format("{0}{1}.cpp", ModulePrefix, BaseName); } FileReference UnityCPPFilePath = FileReference.Combine(CompileEnvironment.Config.OutputDirectory, UnityCPPFileName); // Write the unity file to the intermediate folder. FileItem UnityCPPFile = FileItem.CreateIntermediateTextFile(UnityCPPFilePath, OutputUnityCPPWriter.ToString()); if (OutputUnityCPPWriterExtra != null) { FileItem.CreateIntermediateTextFile(UnityCPPFilePath + ".ex", OutputUnityCPPWriterExtra.ToString()); } UnityCPPFile.RelativeCost = UnityFile.TotalLength; NewCPPFiles.Add(UnityCPPFile); // Cache information about the unity .cpp dependencies // @todo ubtmake urgent: Fails when building remotely for Mac because unity .cpp has an include for a PCH on the REMOTE machine UEBuildModuleCPP.CachePCHUsageForModuleSourceFile(CompileEnvironment, UnityCPPFile); } return(NewCPPFiles); }
void XGEDeleteBuildProducts(UnrealBuildTool.BuildManifest Manifest) { foreach (string Item in Manifest.BuildProducts) { DeleteFile(Item); } }
public BranchUProject(UnrealBuildTool.UProjectInfo InfoEntry) { GameName = InfoEntry.GameName; //not sure what the heck this path is relative to FilePath = Path.GetFullPath(CommandUtils.CombinePaths(CommandUtils.CmdEnv.LocalRoot, "Engine", "Binaries", InfoEntry.FilePath)); if (!CommandUtils.FileExists_NoExceptions(FilePath)) { throw new AutomationException("Could not resolve relative path corrctly {0} -> {1} which doesn't exist.", InfoEntry.FilePath, FilePath); } Properties = ProjectUtils.GetProjectProperties(Path.GetFullPath(FilePath)); }
BuildManifest BuildWithUBT(string TargetName, UnrealBuildTool.UnrealTargetPlatform TargetPlatform, string Config, FileReference UprojectPath, bool ForceMonolithic = false, bool ForceNonUnity = false, bool ForceDebugInfo = false, bool ForceFlushMac = false, bool DisableXGE = false, string InAddArgs = "", bool ForceUnity = false, Dictionary<string, string> EnvVars = null) { string AddArgs = ""; if (UprojectPath != null) { AddArgs += " " + CommandUtils.MakePathSafeToUseWithCommandLine(UprojectPath.FullName); } AddArgs += " " + InAddArgs; if (ForceMonolithic) { AddArgs += " -monolithic"; } if (ForceNonUnity) { AddArgs += " -disableunity"; } if (ForceUnity) { AddArgs += " -forceunity"; } if (ForceDebugInfo) { AddArgs += " -forcedebuginfo"; } if (ForceFlushMac) { AddArgs += " -flushmac"; } if (DisableXGE) { AddArgs += " -noxge"; } PrepareUBT(); // let the platform determine when to use the manifest bool UseManifest = Platform.GetPlatform(TargetPlatform).ShouldUseManifestForUBTBuilds(AddArgs); BuildManifest Manifest = null; if (UseManifest) { string UBTManifest = GetUBTManifest(UprojectPath, AddArgs); DeleteFile(UBTManifest); using(TelemetryStopwatch PrepareManifestStopwatch = new TelemetryStopwatch("PrepareUBTManifest.{0}.{1}.{2}", TargetName, TargetPlatform.ToString(), Config)) { RunUBT(CmdEnv, UBTExecutable: UBTExecutable, Project: UprojectPath, Target: TargetName, Platform: TargetPlatform.ToString(), Config: Config, AdditionalArgs: AddArgs + " -generatemanifest", EnvVars: EnvVars); } Manifest = PrepareManifest(UBTManifest, false); } using(TelemetryStopwatch CompileStopwatch = new TelemetryStopwatch("Compile.{0}.{1}.{2}", TargetName, TargetPlatform.ToString(), Config)) { RunUBT(CmdEnv, UBTExecutable: UBTExecutable, Project: UprojectPath, Target: TargetName, Platform: TargetPlatform.ToString(), Config: Config, AdditionalArgs: AddArgs, EnvVars: EnvVars); } if(!AddArgs.Contains("-nolink")) { // allow the platform to perform any actions after building a target (seems almost like this should be done in UBT) Platform.GetPlatform(TargetPlatform).PostBuildTarget(this, UprojectPath, TargetName, Config); } if (UseManifest) { string UBTManifest = GetUBTManifest(UprojectPath, AddArgs); AddBuildProductsFromManifest(UBTManifest); DeleteFile(UBTManifest); } return Manifest; }
/// <summary> /// Returns the default path of the editor executable to use for running commandlets. /// </summary> /// <param name="BuildRoot">Root directory for the build</param> /// <param name="HostPlatform">Platform to get the executable for</param> /// <returns>Path to the editor executable</returns> public static string GetEditorCommandletExe(string BuildRoot, UnrealBuildTool.UnrealTargetPlatform HostPlatform) { switch(HostPlatform) { case UnrealBuildTool.UnrealTargetPlatform.Mac: return CommandUtils.CombinePaths(BuildRoot, "Engine/Binaries/Mac/UE4Editor.app/Contents/MacOS/UE4Editor"); case UnrealBuildTool.UnrealTargetPlatform.Win64: return CommandUtils.CombinePaths(BuildRoot, "Engine/Binaries/Win64/UE4Editor-Cmd.exe"); case UnrealBuildTool.UnrealTargetPlatform.Linux: return CommandUtils.CombinePaths(BuildRoot, "Engine/Binaries/Linux/UE4Editor"); default: throw new AutomationException("EditorCommandlet is not supported for platform {0}", HostPlatform); } }
/// <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.Name == "LiveCodingConsole") && 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, WriteOutdatedActionsFile); } } } finally { // Save all the caches SourceFileMetadataCache.SaveAll(); CppDependencyCache.SaveAll(); } return(0); }
/// <summary> /// Creates a rules assembly with the given parameters. /// </summary> /// <param name="ProjectFileName">The project file to create rules for. Null for the engine.</param> /// <param name="bUsePrecompiled">Whether to use a precompiled engine</param> /// <param name="bSkipCompile">Whether to skip compilation for this assembly</param> /// <returns>New rules assembly</returns> public static RulesAssembly CreateProjectRulesAssembly(FileReference ProjectFileName, bool bUsePrecompiled, bool bSkipCompile) { // Check if there's an existing assembly for this project RulesAssembly ProjectRulesAssembly; if (!LoadedAssemblyMap.TryGetValue(ProjectFileName, out ProjectRulesAssembly)) { ProjectDescriptor Project = ProjectDescriptor.FromFile(ProjectFileName); // Create the parent assembly RulesAssembly Parent; if (Project.IsEnterpriseProject) { Parent = CreateEnterpriseRulesAssembly(bUsePrecompiled, bSkipCompile); } else { Parent = CreateEngineRulesAssembly(bUsePrecompiled, bSkipCompile); } DirectoryReference MainProjectDirectory = ProjectFileName.Directory; //DirectoryReference MainProjectSourceDirectory = DirectoryReference.Combine(MainProjectDirectory, "Source"); // Create a scope for things in this assembly RulesScope Scope = new RulesScope("Project", Parent.Scope); // Create a new context for modules created by this assembly ModuleRulesContext DefaultModuleContext = new ModuleRulesContext(Scope, MainProjectDirectory); DefaultModuleContext.bCanBuildDebugGame = true; DefaultModuleContext.bCanHotReload = true; DefaultModuleContext.bClassifyAsGameModuleForUHT = true; DefaultModuleContext.bCanUseForSharedPCH = false; // gather modules from project and platforms Dictionary <FileReference, ModuleRulesContext> ModuleFiles = new Dictionary <FileReference, ModuleRulesContext>(); List <FileReference> TargetFiles = new List <FileReference>(); // Find all the project directories List <DirectoryReference> ProjectDirectories = new List <DirectoryReference>(UnrealBuildTool.GetAllProjectDirectories(ProjectFileName)); if (Project.AdditionalRootDirectories != null) { ProjectDirectories.AddRange(Project.AdditionalRootDirectories); } // Find all the rules/plugins under the project source directories foreach (DirectoryReference ProjectDirectory in ProjectDirectories) { DirectoryReference ProjectSourceDirectory = DirectoryReference.Combine(ProjectDirectory, "Source"); AddModuleRulesWithContext(ProjectSourceDirectory, DefaultModuleContext, ModuleFiles); TargetFiles.AddRange(FindAllRulesFiles(ProjectSourceDirectory, RulesFileType.Target)); } // Find all the project plugins List <PluginInfo> ProjectPlugins = new List <PluginInfo>(); ProjectPlugins.AddRange(Plugins.ReadProjectPlugins(MainProjectDirectory)); // Add the project's additional plugin directories plugins too if (Project.AdditionalPluginDirectories != null) { foreach (DirectoryReference AdditionalPluginDirectory in Project.AdditionalPluginDirectories) { ProjectPlugins.AddRange(Plugins.ReadAdditionalPlugins(AdditionalPluginDirectory)); } } // Find all the plugin module rules FindModuleRulesForPlugins(ProjectPlugins, DefaultModuleContext, ModuleFiles); // Add the games project's intermediate source folder DirectoryReference ProjectIntermediateSourceDirectory = DirectoryReference.Combine(MainProjectDirectory, "Intermediate", "Source"); if (DirectoryReference.Exists(ProjectIntermediateSourceDirectory)) { AddModuleRulesWithContext(ProjectIntermediateSourceDirectory, DefaultModuleContext, ModuleFiles); TargetFiles.AddRange(FindAllRulesFiles(ProjectIntermediateSourceDirectory, RulesFileType.Target)); } // Compile the assembly. If there are no module or target files, just use the parent assembly. FileReference AssemblyFileName = FileReference.Combine(MainProjectDirectory, "Intermediate", "Build", "BuildRules", ProjectFileName.GetFileNameWithoutExtension() + "ModuleRules" + FrameworkAssemblyExtension); if (ModuleFiles.Count == 0 && TargetFiles.Count == 0) { ProjectRulesAssembly = Parent; } else { ProjectRulesAssembly = new RulesAssembly(Scope, new List <DirectoryReference> { MainProjectDirectory }, ProjectPlugins, ModuleFiles, TargetFiles, AssemblyFileName, bContainsEngineModules: false, DefaultBuildSettings: null, bReadOnly: UnrealBuildTool.IsProjectInstalled(), bSkipCompile: bSkipCompile, Parent: Parent); } LoadedAssemblyMap.Add(ProjectFileName, ProjectRulesAssembly); } return(ProjectRulesAssembly); }
public virtual UnrealBuildTool.UEDeployIOS GetDeployHandler(FileReference InProject, UnrealBuildTool.IOSPlatformContext inPlatformContext) { Console.WriteLine("Getting IOS Deploy()"); return new UnrealBuildTool.UEDeployIOS(InProject, inPlatformContext); }
/// <summary> /// Discover and fill in the project info /// </summary> public static void FillProjectInfo() { DateTime StartTime = DateTime.Now; List <string> DirectoriesToSearch = new List <string>(); // Find all the .uprojectdirs files contained in the root folder and add their entries to the search array string RootDirectory = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetOriginalLocation()), "..", "..", ".."); string EngineSourceDirectory = Path.GetFullPath(Path.Combine(RootDirectory, "Engine", "Source")); foreach (var File in Directory.EnumerateFiles(RootDirectory, "*.uprojectdirs", SearchOption.TopDirectoryOnly)) { string FilePath = Path.GetFullPath(File); Log.TraceVerbose("\tFound uprojectdirs file {0}", FilePath); using (StreamReader Reader = new StreamReader(FilePath)) { string LineRead; while ((LineRead = Reader.ReadLine()) != null) { string ProjDirEntry = LineRead.Trim(); if (String.IsNullOrEmpty(ProjDirEntry) == false) { if (ProjDirEntry.StartsWith(";")) { // Commented out line... skip it continue; } else { string DirPath = Path.GetFullPath(Path.Combine(RootDirectory, ProjDirEntry)); DirectoriesToSearch.Add(DirPath); } } } } } Log.TraceVerbose("\tFound {0} directories to search", DirectoriesToSearch.Count); foreach (string DirToSearch in DirectoriesToSearch) { Log.TraceVerbose("\t\tSearching {0}", DirToSearch); if (Directory.Exists(DirToSearch)) { foreach (string SubDir in Directory.EnumerateDirectories(DirToSearch, "*", SearchOption.TopDirectoryOnly)) { Log.TraceVerbose("\t\t\tFound subdir {0}", SubDir); string[] SubDirFiles = Directory.GetFiles(SubDir, "*.uproject", SearchOption.TopDirectoryOnly); foreach (string UProjFile in SubDirFiles) { Log.TraceVerbose("\t\t\t\t{0}", UProjFile); AddProject(new FileReference(UProjFile)); } } } else { Log.TraceVerbose("ProjectInfo: Skipping directory {0} from .uprojectdirs file as it doesn't exist.", DirToSearch); } } DateTime StopTime = DateTime.Now; if (BuildConfiguration.bPrintPerformanceInfo) { TimeSpan TotalProjectInfoTime = StopTime - StartTime; Log.TraceInformation("FillProjectInfo took {0} milliseconds", TotalProjectInfoTime.Milliseconds); } if (UnrealBuildTool.CommandLineContains("-dumpprojectinfo")) { UProjectInfo.DumpProjectInfo(); } }
private static void MakeFreshDirectoryIfRequired(UnrealBuildTool.DirectoryReference Directory) { if (!Directory.Exists()) { Directory.CreateDirectory(); } else { InternalUtils.SafeDeleteDirectory(Directory.FullName); Directory.CreateDirectory(); } }
public static bool GenerateTVOSPList(string ProjectDirectory, bool bIsUE4Game, string GameName, string ProjectName, string InEngineDir, string AppDirectory, UEDeployTVOS InThis = null) { // @todo tvos: THIS! // generate the Info.plist for future use string BuildDirectory = ProjectDirectory + "/Build/TVOS"; bool bSkipDefaultPNGs = false; string IntermediateDirectory = (bIsUE4Game ? InEngineDir : ProjectDirectory) + "/Intermediate/TVOS"; string PListFile = IntermediateDirectory + "/" + GameName + "-Info.plist"; // @todo tvos: This is really nasty - both IOS and TVOS are setting static vars VersionUtilities.BuildDirectory = BuildDirectory; VersionUtilities.GameName = GameName; // read the old file string OldPListData = File.Exists(PListFile) ? File.ReadAllText(PListFile) : ""; // determine if there is a launch.xib string LaunchXib = InEngineDir + "/Build/IOS/Resources/Interface/LaunchScreen.xib"; if (File.Exists(BuildDirectory + "/Resources/Interface/LaunchScreen.xib")) { LaunchXib = BuildDirectory + "/Resources/Interface/LaunchScreen.xib"; } // get the settings from the ini file // plist replacements // @todo tvos: Are we going to make TVOS specific .ini files? DirectoryReference DirRef = bIsUE4Game ? (!string.IsNullOrEmpty(UnrealBuildTool.GetRemoteIniPath()) ? new DirectoryReference(UnrealBuildTool.GetRemoteIniPath()) : null) : new DirectoryReference(ProjectDirectory); ConfigHierarchy Ini = ConfigCache.ReadHierarchy(ConfigHierarchyType.Engine, DirRef, UnrealTargetPlatform.IOS); // bundle display name string BundleDisplayName; Ini.GetString("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "BundleDisplayName", out BundleDisplayName); // bundle identifier string BundleIdentifier; Ini.GetString("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "BundleIdentifier", out BundleIdentifier); // bundle name string BundleName; Ini.GetString("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "BundleName", out BundleName); // short version string string BundleShortVersion; Ini.GetString("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "VersionInfo", out BundleShortVersion); // required capabilities string RequiredCaps = "\t\t<string>arm64</string>\n"; // minimum iOS version string MinVersion; if (Ini.GetString("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "MinimumTVOSVersion", out MinVersion)) { switch (MinVersion) { case "TVOS_9": MinVersion = "9.0"; break; } } else { MinVersion = "9.0"; } // extra plist data string ExtraData = ""; Ini.GetString("/Script/IOSRuntimeSettings.IOSRuntimeSettings", "AdditionalPlistData", out ExtraData); // create the final display name, including converting all entities for XML use string FinalDisplayName = BundleDisplayName.Replace("[PROJECT_NAME]", ProjectName).Replace("_", ""); FinalDisplayName = FinalDisplayName.Replace("&", "&"); FinalDisplayName = FinalDisplayName.Replace("\"", """); FinalDisplayName = FinalDisplayName.Replace("\'", "'"); FinalDisplayName = FinalDisplayName.Replace("<", "<"); FinalDisplayName = FinalDisplayName.Replace(">", ">"); // generate the plist file StringBuilder Text = new StringBuilder(); Text.AppendLine("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); Text.AppendLine("<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">"); Text.AppendLine("<plist version=\"1.0\">"); Text.AppendLine("<dict>"); Text.AppendLine("\t<key>CFBundleDevelopmentRegion</key>"); Text.AppendLine("\t<string>en</string>"); Text.AppendLine("\t<key>CFBundleDisplayName</key>"); Text.AppendLine(string.Format("\t<string>{0}</string>", EncodeBundleName(BundleDisplayName, ProjectName))); Text.AppendLine("\t<key>CFBundleExecutable</key>"); Text.AppendLine(string.Format("\t<string>{0}</string>", bIsUE4Game ? "UE4Game" : GameName)); Text.AppendLine("\t<key>CFBundleIdentifier</key>"); Text.AppendLine(string.Format("\t<string>{0}</string>", BundleIdentifier.Replace("[PROJECT_NAME]", ProjectName).Replace("_", ""))); Text.AppendLine("\t<key>CFBundleInfoDictionaryVersion</key>"); Text.AppendLine("\t<string>6.0</string>"); Text.AppendLine("\t<key>CFBundleName</key>"); Text.AppendLine(string.Format("\t<string>{0}</string>", EncodeBundleName(BundleName, ProjectName))); Text.AppendLine("\t<key>CFBundlePackageType</key>"); Text.AppendLine("\t<string>APPL</string>"); Text.AppendLine("\t<key>CFBundleSignature</key>"); Text.AppendLine("\t<string>????</string>"); Text.AppendLine("\t<key>CFBundleVersion</key>"); Text.AppendLine(string.Format("\t<string>{0}</string>", VersionUtilities.UpdateBundleVersion(OldPListData, InEngineDir))); Text.AppendLine("\t<key>CFBundleShortVersionString</key>"); Text.AppendLine(string.Format("\t<string>{0}</string>", BundleShortVersion)); Text.AppendLine("\t<key>LSRequiresIPhoneOS</key>"); Text.AppendLine("\t<true/>"); Text.AppendLine("\t<key>UIRequiredDeviceCapabilities</key>"); Text.AppendLine("\t<array>"); foreach (string Line in RequiredCaps.Split("\r\n".ToCharArray())) { if (!string.IsNullOrWhiteSpace(Line)) { Text.AppendLine(Line); } } Text.AppendLine("\t</array>"); Text.AppendLine("\t<key>TVTopShelfImage</key>"); Text.AppendLine("\t<dict>"); Text.AppendLine("\t\t<key>TVTopShelfPrimaryImage</key>"); Text.AppendLine("\t\t<string>TopShelf</string>"); Text.AppendLine("\t</dict>"); Text.AppendLine("\t<key>UILaunchImages</key>"); Text.AppendLine("\t<array>"); Text.AppendLine("\t\t<dict>"); Text.AppendLine("\t\t\t<key>UILaunchImageSize</key>"); Text.AppendLine("\t\t\t<string>{1920, 1080}</string>"); Text.AppendLine("\t\t\t<key>UILaunchImageName</key>"); Text.AppendLine("\t\t\t<string>LaunchImage</string>"); Text.AppendLine("\t\t\t<key>UILaunchImageMinimumOSVersion</key>"); Text.AppendLine("\t\t\t<string>9.0</string>"); Text.AppendLine("\t\t\t<key>UILaunchImageOrientation</key>"); Text.AppendLine("\t\t\t<string>Landscape</string>"); Text.AppendLine("\t\t</dict>"); Text.AppendLine("\t</array>"); Text.AppendLine("\t<key>CFBundleIcons</key>"); Text.AppendLine("\t<dict>"); Text.AppendLine("\t\t<key>CFBundlePrimaryIcon</key>"); Text.AppendLine("\t\t<string>AppIconSmall</string>"); Text.AppendLine("\t</dict>"); /* Text.AppendLine("\t<key>CFBundleIcons</key>"); * Text.AppendLine("\t<dict>"); * Text.AppendLine("\t\t<key>CFBundlePrimaryIcon</key>"); * Text.AppendLine("\t\t<dict>"); * Text.AppendLine("\t\t\t<key>CFBundleIconFiles</key>"); * Text.AppendLine("\t\t\t<array>"); * Text.AppendLine("\t\t\t\t<string>Icon29.png</string>"); * Text.AppendLine("\t\t\t\t<string>[email protected]</string>"); * Text.AppendLine("\t\t\t\t<string>Icon40.png</string>"); * Text.AppendLine("\t\t\t\t<string>[email protected]</string>"); * Text.AppendLine("\t\t\t\t<string>Icon57.png</string>"); * Text.AppendLine("\t\t\t\t<string>[email protected]</string>"); * Text.AppendLine("\t\t\t\t<string>[email protected]</string>"); * Text.AppendLine("\t\t\t</array>"); * Text.AppendLine("\t\t\t<key>UIPrerenderedIcon</key>"); * Text.AppendLine("\t\t\t<true/>"); * Text.AppendLine("\t\t</dict>"); * Text.AppendLine("\t</dict>"); * Text.AppendLine("\t<key>CFBundleIcons~ipad</key>"); * Text.AppendLine("\t<dict>"); * Text.AppendLine("\t\t<key>CFBundlePrimaryIcon</key>"); * Text.AppendLine("\t\t<dict>"); * Text.AppendLine("\t\t\t<key>CFBundleIconFiles</key>"); * Text.AppendLine("\t\t\t<array>"); * Text.AppendLine("\t\t\t\t<string>Icon29.png</string>"); * Text.AppendLine("\t\t\t\t<string>[email protected]</string>"); * Text.AppendLine("\t\t\t\t<string>Icon40.png</string>"); * Text.AppendLine("\t\t\t\t<string>[email protected]</string>"); * Text.AppendLine("\t\t\t\t<string>Icon50.png</string>"); * Text.AppendLine("\t\t\t\t<string>[email protected]</string>"); * Text.AppendLine("\t\t\t\t<string>Icon72.png</string>"); * Text.AppendLine("\t\t\t\t<string>[email protected]</string>"); * Text.AppendLine("\t\t\t\t<string>Icon76.png</string>"); * Text.AppendLine("\t\t\t\t<string>[email protected]</string>"); * Text.AppendLine("\t\t\t</array>"); * Text.AppendLine("\t\t\t<key>UIPrerenderedIcon</key>"); * Text.AppendLine("\t\t\t<true/>"); * Text.AppendLine("\t\t</dict>"); * Text.AppendLine("\t</dict>"); * if (File.Exists(LaunchXib)) * { * // TODO: compile the xib via remote tool * Text.AppendLine("\t<key>UILaunchStoryboardName</key>"); * Text.AppendLine("\t<string>LaunchScreen</string>"); * bSkipDefaultPNGs = true; * } * else * { * // this is a temp way to inject the iphone 6 images without needing to upgrade everyone's plist * // eventually we want to generate this based on what the user has set in the project settings * string[] IPhoneConfigs = * { * "Default-IPhone6", "Landscape", "{375, 667}", * "Default-IPhone6", "Portrait", "{375, 667}", * "Default-IPhone6Plus-Landscape", "Landscape", "{414, 736}", * "Default-IPhone6Plus-Portrait", "Portrait", "{414, 736}", * "Default", "Landscape", "{320, 480}", * "Default", "Portrait", "{320, 480}", * "Default-568h", "Landscape", "{320, 568}", * "Default-568h", "Portrait", "{320, 568}", * }; * * Text.AppendLine("\t<key>UILaunchImages~iphone</key>"); * Text.AppendLine("\t<array>"); * for (int ConfigIndex = 0; ConfigIndex < IPhoneConfigs.Length; ConfigIndex += 3) * { * Text.AppendLine("\t\t<dict>"); * Text.AppendLine("\t\t\t<key>UILaunchImageMinimumOSVersion</key>"); * Text.AppendLine("\t\t\t<string>8.0</string>"); * Text.AppendLine("\t\t\t<key>UILaunchImageName</key>"); * Text.AppendLine(string.Format("\t\t\t<string>{0}</string>", IPhoneConfigs[ConfigIndex + 0])); * Text.AppendLine("\t\t\t<key>UILaunchImageOrientation</key>"); * Text.AppendLine(string.Format("\t\t\t<string>{0}</string>", IPhoneConfigs[ConfigIndex + 1])); * Text.AppendLine("\t\t\t<key>UILaunchImageSize</key>"); * Text.AppendLine(string.Format("\t\t\t<string>{0}</string>", IPhoneConfigs[ConfigIndex + 2])); * Text.AppendLine("\t\t</dict>"); * } * * // close it out * Text.AppendLine("\t</array>"); * } * Text.AppendLine("\t<key>UILaunchImages~ipad</key>"); * Text.AppendLine("\t<array>"); * Text.AppendLine("\t\t<dict>"); * Text.AppendLine("\t\t\t<key>UILaunchImageMinimumOSVersion</key>"); * Text.AppendLine("\t\t\t<string>7.0</string>"); * Text.AppendLine("\t\t\t<key>UILaunchImageName</key>"); * Text.AppendLine("\t\t\t<string>Default-Landscape</string>"); * Text.AppendLine("\t\t\t<key>UILaunchImageOrientation</key>"); * Text.AppendLine("\t\t\t<string>Landscape</string>"); * Text.AppendLine("\t\t\t<key>UILaunchImageSize</key>"); * Text.AppendLine("\t\t\t<string>{768, 1024}</string>"); * Text.AppendLine("\t\t</dict>"); * Text.AppendLine("\t\t<dict>"); * Text.AppendLine("\t\t\t<key>UILaunchImageMinimumOSVersion</key>"); * Text.AppendLine("\t\t\t<string>7.0</string>"); * Text.AppendLine("\t\t\t<key>UILaunchImageName</key>"); * Text.AppendLine("\t\t\t<string>Default-Portrait</string>"); * Text.AppendLine("\t\t\t<key>UILaunchImageOrientation</key>"); * Text.AppendLine("\t\t\t<string>Portrait</string>"); * Text.AppendLine("\t\t\t<key>UILaunchImageSize</key>"); * Text.AppendLine("\t\t\t<string>{768, 1024}</string>"); * Text.AppendLine("\t\t</dict>"); * Text.AppendLine("\t</array>”); * Text.AppendLine("\t<key>CFBundleSupportedPlatforms</key>"); * Text.AppendLine("\t<array>"); * Text.AppendLine("\t\t<string>iPhoneOS</string>"); * Text.AppendLine("\t</array>"); * Text.AppendLine("\t<key>MinimumOSVersion</key>"); * Text.AppendLine(string.Format("\t<string>{0}</string>", MinVersion)); * if (!string.IsNullOrEmpty(ExtraData)) * { * ExtraData = ExtraData.Replace("\\n", "\n"); * foreach (string Line in ExtraData.Split("\r\n".ToCharArray())) * { * if (!string.IsNullOrWhiteSpace(Line)) * { * Text.AppendLine("\t" + Line); * } * } * }*/ Text.AppendLine("</dict>"); Text.AppendLine("</plist>"); // Create the intermediate directory if needed if (!Directory.Exists(IntermediateDirectory)) { Directory.CreateDirectory(IntermediateDirectory); } if (InThis != null && InThis.UPL != null) { // Allow UPL to modify the plist here XDocument XDoc; try { XDoc = XDocument.Parse(Text.ToString()); } catch (Exception e) { throw new BuildException("plist is invalid {0}\n{1}", e, Text.ToString()); } XDoc.DocumentType.InternalSubset = ""; InThis.UPL.ProcessPluginNode("None", "iosPListUpdates", "", ref XDoc); string result = XDoc.Declaration.ToString() + "\n" + XDoc.ToString().Replace("<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\"[]>", "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">"); File.WriteAllText(PListFile, result); } else { File.WriteAllText(PListFile, Text.ToString()); } if (BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Mac) { if (!Directory.Exists(AppDirectory)) { Directory.CreateDirectory(AppDirectory); } File.WriteAllText(AppDirectory + "/Info.plist", Text.ToString()); } return(bSkipDefaultPNGs); }