// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.Text; using System.Diagnostics; using System.IO; using System.Linq; namespace UnrealBuildTool { abstract class UEBuildPlatform { private static Dictionary BuildPlatformDictionary = new Dictionary(); // a mapping of a group to the platforms in the group (ie, Microsoft contains Win32 and Win64) static Dictionary> PlatformGroupDictionary = new Dictionary>(); /// /// The corresponding target platform enum /// public readonly UnrealTargetPlatform Platform; /// /// The default C++ target platform to use /// public readonly CppPlatform DefaultCppPlatform; /// /// Cached copy of the list of folders to exclude for this platform /// private FileSystemName[] CachedExcludedFolderNames; /// /// List of all confidential folder names /// public static readonly string[] RestrictedFolderNames = { "EpicInternal", "CarefullyRedist", "NotForLicensees", "NoRedist", "PS4", "XboxOne", "Switch", "Wolf", "WolfPlat", }; /// /// Constructor. /// /// The enum value for this platform /// The default C++ platform for this platform public UEBuildPlatform(UnrealTargetPlatform InPlatform, CppPlatform InDefaultCPPPlatform) { Platform = InPlatform; DefaultCppPlatform = InDefaultCPPPlatform; } /// /// Finds a list of folder names to exclude when building for this platform /// public FileSystemName[] GetExcludedFolderNames() { if(CachedExcludedFolderNames == null) { // Find all the platform folders to exclude from the list of precompiled modules List Names = new List(); foreach (UnrealTargetPlatform TargetPlatform in Enum.GetValues(typeof(UnrealTargetPlatform))) { if (TargetPlatform != UnrealTargetPlatform.Unknown && TargetPlatform != Platform) { Names.Add(new FileSystemName(TargetPlatform.ToString())); } } // Also exclude all the platform groups that this platform is not a part of List IncludePlatformGroups = new List(UEBuildPlatform.GetPlatformGroups(Platform)); foreach (UnrealPlatformGroup PlatformGroup in Enum.GetValues(typeof(UnrealPlatformGroup))) { if (!IncludePlatformGroups.Contains(PlatformGroup)) { Names.Add(new FileSystemName(PlatformGroup.ToString())); } } // Save off the list as an array CachedExcludedFolderNames = Names.ToArray(); } return CachedExcludedFolderNames; } /// /// Whether the required external SDKs are installed for this platform. Could be either a manual install or an AutoSDK. /// public abstract SDKStatus HasRequiredSDKsInstalled(); /// /// Gets all the registered platforms /// /// Sequence of registered platforms public static IEnumerable GetRegisteredPlatforms() { return BuildPlatformDictionary.Keys; } /// /// Get the default architecture for a project. This may be overriden on the command line to UBT. /// /// Optional project to read settings from public virtual string GetDefaultArchitecture(FileReference ProjectFile) { // by default, use an empty architecture (which is really just a modifer to the platform for some paths/names) return ""; } /// /// Get name for architecture-specific directories (can be shorter than architecture name itself) /// public virtual string GetFolderNameForArchitecture(string Architecture) { // by default, use the architecture name return Architecture; } public virtual void PreBuildSync() { } public virtual void PostBuildSync(UEBuildTarget Target) { } /// /// Called immediately after UnrealHeaderTool is executed to generated code for all UObjects modules. Only is called if UnrealHeaderTool was actually run in this session. /// /// List of UObject modules we generated code for. public virtual void PostCodeGeneration(UHTManifest Manifest) { } /// /// Converts the passed in path from UBT host to compiler native format. /// /// The path to convert /// The path in native format for the toolchain public virtual string ConvertPath(string OriginalPath) { return OriginalPath; } /// /// Attempt to convert a string to an UnrealTargetPlatform enum entry /// /// UnrealTargetPlatform.Unknown on failure (the platform didn't match the enum) public static UnrealTargetPlatform ConvertStringToPlatform(string InPlatformName) { // special case x64, to not break anything // @todo: Is it possible to remove this hack? if (InPlatformName.Equals("X64", StringComparison.InvariantCultureIgnoreCase)) { return UnrealTargetPlatform.Win64; } // we can't parse the string into an enum because Enum.Parse is case sensitive, so we loop over the enum // looking for matches foreach (string PlatformName in Enum.GetNames(typeof(UnrealTargetPlatform))) { if (InPlatformName.Equals(PlatformName, StringComparison.InvariantCultureIgnoreCase)) { // convert the known good enum string back to the enum value return (UnrealTargetPlatform)Enum.Parse(typeof(UnrealTargetPlatform), PlatformName); } } return UnrealTargetPlatform.Unknown; } /// /// Determines whether a given platform is available /// /// The platform to check for /// True if it's available, false otherwise public static bool IsPlatformAvailable(UnrealTargetPlatform Platform) { return BuildPlatformDictionary.ContainsKey(Platform); } /// /// Register the given platforms UEBuildPlatform instance /// /// The UEBuildPlatform instance to use for the InPlatform public static void RegisterBuildPlatform(UEBuildPlatform InBuildPlatform) { if (BuildPlatformDictionary.ContainsKey(InBuildPlatform.Platform) == true) { Log.TraceWarning("RegisterBuildPlatform Warning: Registering build platform {0} for {1} when it is already set to {2}", InBuildPlatform.ToString(), InBuildPlatform.Platform.ToString(), BuildPlatformDictionary[InBuildPlatform.Platform].ToString()); BuildPlatformDictionary[InBuildPlatform.Platform] = InBuildPlatform; } else { BuildPlatformDictionary.Add(InBuildPlatform.Platform, InBuildPlatform); } } /// /// Assign a platform as a member of the given group /// public static void RegisterPlatformWithGroup(UnrealTargetPlatform InPlatform, UnrealPlatformGroup InGroup) { // find or add the list of groups for this platform List Platforms; if(!PlatformGroupDictionary.TryGetValue(InGroup, out Platforms)) { Platforms = new List(); PlatformGroupDictionary.Add(InGroup, Platforms); } Platforms.Add(InPlatform); } /// /// Retrieve the list of platforms in this group (if any) /// public static List GetPlatformsInGroup(UnrealPlatformGroup InGroup) { List PlatformList; PlatformGroupDictionary.TryGetValue(InGroup, out PlatformList); return PlatformList; } /// /// Enumerates all the platform groups for a given platform /// /// The platform to look for /// List of platform groups that this platform is a member of public static IEnumerable GetPlatformGroups(UnrealTargetPlatform Platform) { return PlatformGroupDictionary.Where(x => x.Value.Contains(Platform)).Select(x => x.Key); } /// /// Retrieve the IUEBuildPlatform instance for the given TargetPlatform /// /// The UnrealTargetPlatform being built /// If true, do not throw an exception and return null /// UEBuildPlatform The instance of the build platform public static UEBuildPlatform GetBuildPlatform(UnrealTargetPlatform InPlatform, bool bInAllowFailure = false) { if (BuildPlatformDictionary.ContainsKey(InPlatform) == true) { return BuildPlatformDictionary[InPlatform]; } if (bInAllowFailure == true) { return null; } throw new BuildException("GetBuildPlatform: No BuildPlatform found for {0}", InPlatform.ToString()); } /// /// Gets the UnrealTargetPlatform matching a given CPPTargetPlatform /// /// The compile platform /// The target platform public static UnrealTargetPlatform CPPTargetPlatformToUnrealTargetPlatform(CppPlatform InCPPPlatform) { switch (InCPPPlatform) { case CppPlatform.Win32: return UnrealTargetPlatform.Win32; case CppPlatform.Win64: return UnrealTargetPlatform.Win64; case CppPlatform.Mac: return UnrealTargetPlatform.Mac; case CppPlatform.XboxOne: return UnrealTargetPlatform.XboxOne; case CppPlatform.PS4: return UnrealTargetPlatform.PS4; case CppPlatform.Android: return UnrealTargetPlatform.Android; case CppPlatform.IOS: return UnrealTargetPlatform.IOS; case CppPlatform.HTML5: return UnrealTargetPlatform.HTML5; case CppPlatform.Linux: return UnrealTargetPlatform.Linux; case CppPlatform.TVOS: return UnrealTargetPlatform.TVOS; case CppPlatform.Switch: return UnrealTargetPlatform.Switch; } throw new BuildException("CPPTargetPlatformToUnrealTargetPlatform: Unknown CPPTargetPlatform {0}", InCPPPlatform.ToString()); } /// /// Retrieve the IUEBuildPlatform instance for the given CPPTargetPlatform /// /// The CPPTargetPlatform being built /// If true, do not throw an exception and return null /// UEBuildPlatform The instance of the build platform public static UEBuildPlatform GetBuildPlatformForCPPTargetPlatform(CppPlatform InPlatform, bool bInAllowFailure = false) { UnrealTargetPlatform UTPlatform = CPPTargetPlatformToUnrealTargetPlatform(InPlatform); if (BuildPlatformDictionary.ContainsKey(UTPlatform) == true) { return BuildPlatformDictionary[UTPlatform]; } if (bInAllowFailure == true) { return null; } throw new BuildException("UEBuildPlatform::GetBuildPlatformForCPPTargetPlatform: No BuildPlatform found for {0}", InPlatform.ToString()); } /// /// Allow all registered build platforms to modify the newly created module /// passed in for the given platform. /// This is not required - but allows for hiding details of a particular platform. /// /// The name of the module /// The module rules /// The target being build public static void PlatformModifyHostModuleRules(string ModuleName, ModuleRules Rules, ReadOnlyTargetRules Target) { foreach (KeyValuePair PlatformEntry in BuildPlatformDictionary) { PlatformEntry.Value.ModifyModuleRulesForOtherPlatform(ModuleName, Rules, Target); } } /// /// Returns the delimiter used to separate paths in the PATH environment variable for the platform we are executing on. /// public static String GetPathVarDelimiter() { switch (BuildHostPlatform.Current.Platform) { case UnrealTargetPlatform.Linux: case UnrealTargetPlatform.Mac: return ":"; case UnrealTargetPlatform.Win32: case UnrealTargetPlatform.Win64: return ";"; default: Log.TraceWarning("PATH var delimiter unknown for platform " + BuildHostPlatform.Current.Platform.ToString() + " using ';'"); return ";"; } } /// /// Returns the name that should be returned in the output when doing -validateplatforms /// public virtual string GetPlatformValidationName() { return Platform.ToString(); } /// /// If this platform can be compiled with XGE /// public virtual bool CanUseXGE() { return true; } /// /// If this platform can be compiled with DMUCS/Distcc /// public virtual bool CanUseDistcc() { return false; } /// /// If this platform can be compiled with SN-DBS /// public virtual bool CanUseSNDBS() { return false; } /// /// Set all the platform-specific defaults for a new target /// public virtual void ResetTarget(TargetRules Target) { } /// /// Validate a target's settings /// public virtual void ValidateTarget(TargetRules Target) { } /// /// Return whether the given platform requires a monolithic build /// /// The platform of interest /// The configuration of interest /// public static bool PlatformRequiresMonolithicBuilds(UnrealTargetPlatform InPlatform, UnrealTargetConfiguration InConfiguration) { // Some platforms require monolithic builds... UEBuildPlatform BuildPlatform = GetBuildPlatform(InPlatform, true); if (BuildPlatform != null) { return BuildPlatform.ShouldCompileMonolithicBinary(InPlatform); } // We assume it does not return false; } /// /// Get the extension to use for the given binary type /// /// The binary type being built /// string The binary extension (i.e. 'exe' or 'dll') public virtual string GetBinaryExtension(UEBuildBinaryType InBinaryType) { throw new BuildException("GetBinaryExtensiton for {0} not handled in {1}", InBinaryType.ToString(), this.ToString()); } /// /// Get the extension to use for debug info for the given binary type /// /// Options for the target being built /// The binary type being built /// string The debug info extension (i.e. 'pdb') public virtual string GetDebugInfoExtension(ReadOnlyTargetRules InTarget, UEBuildBinaryType InBinaryType) { throw new BuildException("GetDebugInfoExtension for {0} not handled in {1}", InBinaryType.ToString(), this.ToString()); } /// /// Whether the editor should be built for this platform or not /// /// The UnrealTargetPlatform being built /// The UnrealTargetConfiguration being built /// bool true if the editor should be built, false if not public virtual bool ShouldNotBuildEditor(UnrealTargetPlatform InPlatform, UnrealTargetConfiguration InConfiguration) { return false; } /// /// Whether this build should support ONLY cooked data or not /// /// The UnrealTargetPlatform being built /// The UnrealTargetConfiguration being built /// bool true if the editor should be built, false if not public virtual bool BuildRequiresCookedData(UnrealTargetPlatform InPlatform, UnrealTargetConfiguration InConfiguration) { return false; } /// /// Whether this platform requires the use of absolute paths in Unity files. The compiler will try to combine paths in /// each #include directive with the standard include paths, and unity files in intermediate directories can result in the /// maximum path length being exceeded on Windows. On the other hand, remote compilation requires relative paths so /// dependency checking works correctly on the local machine as well as on the remote machine. /// /// bool true if it is required, false if not public virtual bool UseAbsolutePathsInUnityFiles() { return true; } /// /// Whether this platform should build a monolithic binary /// public virtual bool ShouldCompileMonolithicBinary(UnrealTargetPlatform InPlatform) { return false; } /// /// Modify the rules for a newly created module, where the target is a different host platform. /// This is not required - but allows for hiding details of a particular platform. /// /// The name of the module /// The module rules /// The target being build public virtual void ModifyModuleRulesForOtherPlatform(string ModuleName, ModuleRules Rules, ReadOnlyTargetRules Target) { } /// /// Allow the platform to override the NMake output name /// public virtual FileReference ModifyNMakeOutput(FileReference ExeName) { // by default, use original return ExeName; } /// /// Allows the platform to override whether the architecture name should be appended to the name of binaries. /// /// True if the architecture name should be appended to the binary public virtual bool RequiresArchitectureSuffix() { return true; } /// /// For platforms that need to output multiple files per binary (ie Android "fat" binaries) /// this will emit multiple paths. By default, it simply makes an array from the input /// public virtual List FinalizeBinaryPaths(FileReference BinaryName, FileReference ProjectFile, ReadOnlyTargetRules Target) { List TempList = new List() { BinaryName }; return TempList; } /// /// Return whether this platform has uniquely named binaries across multiple games /// public virtual bool HasUniqueBinaries() { return true; } /// /// Return whether we wish to have this platform's binaries in our builds /// public virtual bool IsBuildRequired() { return true; } /// /// Return whether we wish to have this platform's binaries in our CIS tests /// public virtual bool IsCISRequired() { return true; } /// /// Whether the build platform requires deployment prep /// /// public virtual bool RequiresDeployPrepAfterCompile() { return false; } /// /// Return all valid configurations for this platform /// Typically, this is always Debug, Development, and Shipping - but Test is a likely future addition for some platforms /// public virtual List GetConfigurations(UnrealTargetPlatform InUnrealTargetPlatform, bool bIncludeDebug) { List Configurations = new List() { UnrealTargetConfiguration.Development, }; if (bIncludeDebug) { Configurations.Insert(0, UnrealTargetConfiguration.Debug); } return Configurations; } protected static bool DoProjectSettingsMatchDefault(UnrealTargetPlatform Platform, DirectoryReference ProjectDirectoryName, string Section, string[] BoolKeys, string[] IntKeys, string[] StringKeys) { ConfigHierarchy ProjIni = ConfigCache.ReadHierarchy(ConfigHierarchyType.Engine, ProjectDirectoryName, Platform); ConfigHierarchy DefaultIni = ConfigCache.ReadHierarchy(ConfigHierarchyType.Engine, (DirectoryReference)null, Platform); // look at all bool values if (BoolKeys != null) foreach (string Key in BoolKeys) { bool Default = false, Project = false; DefaultIni.GetBool(Section, Key, out Default); ProjIni.GetBool(Section, Key, out Project); if (Default != Project) { Log.TraceInformationOnce(Key + " is not set to default. (" + Default + " vs. " + Project + ")"); return false; } } // look at all int values if (IntKeys != null) foreach (string Key in IntKeys) { int Default = 0, Project = 0; DefaultIni.GetInt32(Section, Key, out Default); ProjIni.GetInt32(Section, Key, out Project); if (Default != Project) { Log.TraceInformationOnce(Key + " is not set to default. (" + Default + " vs. " + Project + ")"); return false; } } // look for all string values if (StringKeys != null) foreach (string Key in StringKeys) { string Default = "", Project = ""; DefaultIni.GetString(Section, Key, out Default); ProjIni.GetString(Section, Key, out Project); if (Default != Project) { Log.TraceInformationOnce(Key + " is not set to default. (" + Default + " vs. " + Project + ")"); return false; } } // if we get here, we match all important settings return true; } /// /// Check for the default configuration /// return true if the project uses the default build config /// public virtual bool HasDefaultBuildConfig(UnrealTargetPlatform Platform, DirectoryReference ProjectDirectoryName) { string[] BoolKeys = new string[] { "bCompileApex", "bCompileBox2D", "bCompileICU", "bCompileSimplygon", "bCompileSimplygonSSF", "bCompileLeanAndMeanUE", "bIncludeADO", "bCompileRecast", "bCompileSpeedTree", "bCompileWithPluginSupport", "bCompilePhysXVehicle", "bCompileFreeType", "bCompileForSize", "bCompileCEF3" }; return DoProjectSettingsMatchDefault(Platform, ProjectDirectoryName, "/Script/BuildSettings.BuildSettings", BoolKeys, null, null); } /// /// Get a list of extra modules the platform requires. /// This is to allow undisclosed platforms to add modules they need without exposing information about the platform. /// /// The target being build /// List of extra modules the platform needs to add to the target public virtual void AddExtraModules(ReadOnlyTargetRules Target, List ExtraModuleNames) { } /// /// Modify the rules for a newly created module, in a target that's being built for this platform. /// This is not required - but allows for hiding details of a particular platform. /// /// The name of the module /// The module rules /// The target being build public virtual void ModifyModuleRulesForActivePlatform(string ModuleName, ModuleRules Rules, ReadOnlyTargetRules Target) { } /// /// Setup the target environment for building /// /// Settings for the target being compiled /// The compile environment for this target /// The link environment for this target public abstract void SetUpEnvironment(ReadOnlyTargetRules Target, CppCompileEnvironment CompileEnvironment, LinkEnvironment LinkEnvironment); /// /// Setup the configuration environment for building /// /// The target being built /// The global compile environment /// The global link environment public virtual void SetUpConfigurationEnvironment(ReadOnlyTargetRules Target, CppCompileEnvironment GlobalCompileEnvironment, LinkEnvironment GlobalLinkEnvironment) { if (GlobalCompileEnvironment.bUseDebugCRT) { GlobalCompileEnvironment.Definitions.Add("_DEBUG=1"); // the engine doesn't use this, but lots of 3rd party stuff does } else { GlobalCompileEnvironment.Definitions.Add("NDEBUG=1"); // the engine doesn't use this, but lots of 3rd party stuff does } switch (Target.Configuration) { default: case UnrealTargetConfiguration.Debug: GlobalCompileEnvironment.Definitions.Add("UE_BUILD_DEBUG=1"); break; case UnrealTargetConfiguration.DebugGame: // Individual game modules can be switched to be compiled in debug as necessary. By default, everything is compiled in development. case UnrealTargetConfiguration.Development: GlobalCompileEnvironment.Definitions.Add("UE_BUILD_DEVELOPMENT=1"); break; case UnrealTargetConfiguration.Shipping: GlobalCompileEnvironment.Definitions.Add("UE_BUILD_SHIPPING=1"); break; case UnrealTargetConfiguration.Test: GlobalCompileEnvironment.Definitions.Add("UE_BUILD_TEST=1"); break; } // Create debug info based on the heuristics specified by the user. GlobalCompileEnvironment.bCreateDebugInfo = !Target.bDisableDebugInfo && ShouldCreateDebugInfo(Target); GlobalLinkEnvironment.bCreateDebugInfo = GlobalCompileEnvironment.bCreateDebugInfo; } /// /// Whether this platform should create debug information or not /// /// The target being built /// bool true if debug info should be generated, false if not public abstract bool ShouldCreateDebugInfo(ReadOnlyTargetRules Target); /// /// Creates a toolchain instance for the given platform. There should be a single toolchain instance per-target, as their may be /// state data and configuration cached between calls. /// /// The platform to create a toolchain for /// The target being built /// New toolchain instance. public abstract UEToolChain CreateToolChain(CppPlatform CppPlatform, ReadOnlyTargetRules Target); /// /// Deploys the given target /// /// Information about the target being deployed public abstract void Deploy(UEBuildDeployTarget Target); } }