// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.Text; using EpicGames.Core; using Microsoft.Extensions.Logging; // ReSharper disable once CheckNamespace namespace UnrealBuildTool { /// /// Apple-specific target settings /// public abstract class AppleTargetRules { /// /// Whether to strip symbols or not (implied by Shipping config). /// [XmlConfigFile(Category = "BuildConfiguration")] [CommandLine("-stripsymbols", Value = "true")] public bool bStripSymbols = false; /// /// Whether to create a ".stripped" file when stripping symbols or not. /// [XmlConfigFile(Category = "BuildConfiguration")] [CommandLine("-createstripflagfile", Value = "true")] public bool bCreateStripFlagFile = false; /// /// Disables clang build verification checks on static libraries /// [CommandLine("-skipclangvalidation", Value = "true")] [XmlConfigFile(Category = "BuildConfiguration")] public bool bSkipClangValidation = false; /// /// Enables address sanitizer (ASan). /// [CommandLine("-EnableASan")] [XmlConfigFile(Category = "BuildConfiguration")] public bool bEnableAddressSanitizer = false; /// /// Enables thread sanitizer (TSan). /// [CommandLine("-EnableTSan")] [XmlConfigFile(Category = "BuildConfiguration")] public bool bEnableThreadSanitizer = false; /// /// Enables undefined behavior sanitizer (UBSan). /// [CommandLine("-EnableUBSan")] [XmlConfigFile(Category = "BuildConfiguration")] public bool bEnableUndefinedBehaviorSanitizer = false; } /// /// Read-only wrapper for Apple-specific target settings /// public class ReadOnlyAppleTargetRules { /// /// The private mutable settings object /// private readonly AppleTargetRules Inner; /// /// Constructor /// /// The settings object to wrap public ReadOnlyAppleTargetRules(AppleTargetRules Inner) { this.Inner = Inner; } /// /// Accessors for fields on the inner TargetRules instance /// #region Read-only accessor properties #pragma warning disable CS1591 public bool bStripSymbols => Inner.bStripSymbols; public bool bCreateStripFlagFile => Inner.bCreateStripFlagFile; public bool bSkipClangValidation => Inner.bSkipClangValidation; public bool bEnableAddressSanitizer => Inner.bEnableAddressSanitizer; public bool bEnableThreadSanitizer => Inner.bEnableThreadSanitizer; public bool bEnableUndefinedBehaviorSanitizer => Inner.bEnableUndefinedBehaviorSanitizer; #pragma warning restore CS1591 #endregion } /// /// Architecture config for Apple platforms /// class AppleArchitectureConfig : UnrealArchitectureConfig { public AppleArchitectureConfig(IEnumerable SupportedArchitectures) : base(UnrealArchitectureMode.SingleTargetCompileSeparately, SupportedArchitectures) { } } abstract class AppleBuildPlatform : UEBuildPlatform { public AppleBuildPlatform(UnrealTargetPlatform Platform, UEBuildPlatformSDK SDK, UnrealArchitectureConfig ArchitectureConfig, ILogger Logger) : base(Platform, SDK, ArchitectureConfig, Logger) { } public abstract AppleTargetRules GetAppleTargetRules(TargetRules Target); public abstract ReadOnlyAppleTargetRules GetAppleTargetRules(ReadOnlyTargetRules Target); public override void GetExternalBuildMetadata(FileReference? ProjectFile, StringBuilder Metadata) { base.GetExternalBuildMetadata(ProjectFile, Metadata); Metadata.AppendLine("xcode-select: {0}", ApplePlatformSDK.DeveloperDir); } public override bool IsBuildProduct(string FileName, string[] NamePrefixes, string[] NameSuffixes) { return IsBuildProductName(FileName, NamePrefixes, NameSuffixes, "") || IsBuildProductName(FileName, NamePrefixes, NameSuffixes, ".dylib") || IsBuildProductName(FileName, NamePrefixes, NameSuffixes, ".dSYM"); } public override bool CanUseFASTBuild() { return true; } public override void ResetTarget(TargetRules Target) { base.ResetTarget(Target); // always strip in shipping configuration (commandline could have set it also) if (Target.Configuration == UnrealTargetConfiguration.Shipping) { GetAppleTargetRules(Target).bStripSymbols = true; } } public override void ValidateTarget(TargetRules Target) { base.ValidateTarget(Target); if (!string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("CLANG_STATIC_ANALYZER_MODE"))) { Target.StaticAnalyzer = StaticAnalyzer.Default; Target.StaticAnalyzerOutputType = (Environment.GetEnvironmentVariable("CLANG_ANALYZER_OUTPUT")?.Contains("html", StringComparison.OrdinalIgnoreCase) == true) ? StaticAnalyzerOutputType.Html : StaticAnalyzerOutputType.Text; Target.StaticAnalyzerMode = string.Equals(Environment.GetEnvironmentVariable("CLANG_STATIC_ANALYZER_MODE"), "shallow", StringComparison.OrdinalIgnoreCase) ? StaticAnalyzerMode.Shallow : StaticAnalyzerMode.Deep; } else if (Target.StaticAnalyzer == StaticAnalyzer.Clang) { Target.StaticAnalyzer = StaticAnalyzer.Default; } // Disable linking and ignore build outputs if we're using a static analyzer if (Target.StaticAnalyzer == StaticAnalyzer.Default) { Target.bDisableLinking = true; Target.bIgnoreBuildOutputs = true; // Clang static analysis requires non unity builds Target.bUseUnityBuild = false; if (Target.bStaticAnalyzerIncludeGenerated) { Target.bAlwaysUseUnityForGeneratedFiles = false; } // Disable chaining PCHs for the moment because it is crashing clang Target.bChainPCHs = false; } if (Target.bCompileAgainstEngine) { Target.GlobalDefinitions.Add("HAS_METAL=1"); Target.ExtraModuleNames.Add("MetalRHI"); } else { Target.GlobalDefinitions.Add("HAS_METAL=0"); } // Force using the ANSI allocator if ASan is enabled if (GetAppleTargetRules(Target).bEnableAddressSanitizer) { Target.StaticAllocator = StaticAllocatorType.Ansi; } } /// /// Get the extension to use for the given binary type /// /// The binary type being built /// string The binary extenstion (ie 'exe' or 'dll') public override string GetBinaryExtension(UEBuildBinaryType InBinaryType) { switch (InBinaryType) { case UEBuildBinaryType.DynamicLinkLibrary: return ".dylib"; case UEBuildBinaryType.Executable: return ""; case UEBuildBinaryType.StaticLibrary: return ".a"; } return base.GetBinaryExtension(InBinaryType); } public override void SetUpEnvironment(ReadOnlyTargetRules Target, CppCompileEnvironment CompileEnvironment, LinkEnvironment LinkEnvironment) { // shared stuff for all apple platforms CompileEnvironment.Definitions.Add("PLATFORM_APPLE=1"); AppleExports.GetSwiftIntegrationSettings(Target.ProjectFile, Target.Type, Platform, out var bUseSwiftUIMain, out _); CompileEnvironment.Definitions.Add("UE_USE_SWIFT_UI_MAIN=" + (bUseSwiftUIMain ? "1" : "0")); CompileEnvironment.Definitions.Add("WITH_TTS=0"); CompileEnvironment.Definitions.Add("WITH_SPEECH_RECOGNITION=0"); } protected ClangToolChainOptions GetToolChainOptionsForSanitizers(ReadOnlyTargetRules Target) { ReadOnlyAppleTargetRules AppleTarget = GetAppleTargetRules(Target); ClangToolChainOptions Options = ClangToolChainOptions.None; if (AppleTarget.bEnableAddressSanitizer) { Options |= ClangToolChainOptions.EnableAddressSanitizer; } if (AppleTarget.bEnableThreadSanitizer) { Options |= ClangToolChainOptions.EnableThreadSanitizer; } if (AppleTarget.bEnableUndefinedBehaviorSanitizer) { Options |= ClangToolChainOptions.EnableUndefinedBehaviorSanitizer; } return Options; } } }