From e361ab811a57da02ad1c73401fa7f9930b74632e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 29 Dec 2021 19:43:53 +0100 Subject: [PATCH] Mac support progress --- Source/Engine/Platform/Mac/MacFileSystem.cpp | 26 +- Source/Engine/Platform/Mac/MacPlatform.cpp | 242 +++++++++++++++--- Source/Engine/Platform/Mac/MacPlatform.h | 5 +- Source/ThirdParty/PhysX/PhysX.Build.cs | 1 + Source/Tools/Flax.Build/Build/EngineTarget.cs | 2 +- .../Deps/Dependencies/NewtonsoftJson.cs | 2 + .../Flax.Build/Deps/Dependencies/PhysX.cs | 45 +++- .../Flax.Build/Deps/Dependencies/freetype.cs | 22 +- .../Tools/Flax.Build/Deps/Dependencies/ogg.cs | 19 +- .../Flax.Build/Deps/Dependencies/vorbis.cs | 38 ++- Source/Tools/Flax.Build/Deps/Dependency.cs | 6 +- Source/Tools/Flax.Build/Deps/Downloader.cs | 2 + .../Flax.Build/Platforms/Mac/MacToolchain.cs | 86 ++++++- .../VisualStudioCodeProjectGenerator.cs | 27 +- .../Tools/Flax.Build/Utilities/Utilities.cs | 5 +- 15 files changed, 465 insertions(+), 63 deletions(-) diff --git a/Source/Engine/Platform/Mac/MacFileSystem.cpp b/Source/Engine/Platform/Mac/MacFileSystem.cpp index a64da4d0e..7aeb1fcef 100644 --- a/Source/Engine/Platform/Mac/MacFileSystem.cpp +++ b/Source/Engine/Platform/Mac/MacFileSystem.cpp @@ -502,8 +502,30 @@ DateTime MacFileSystem::GetFileLastEditTime(const StringView& path) void MacFileSystem::GetSpecialFolderPath(const SpecialFolder type, String& result) { - MISSING_CODE("MacFileSystem::GetSpecialFolderPath"); - return; // TODO: filesystem on Mac + String home; + Platform::GetEnvironmentVariable(TEXT("HOME"), home); + switch (type) + { + case SpecialFolder::Desktop: + result = home / TEXT("/Desktop"); + break; + case SpecialFolder::Documents: + result = home / TEXT("/Documents"); + break; + case SpecialFolder::Pictures: + result = home / TEXT("/Pictures"); + break; + case SpecialFolder::AppData: + case SpecialFolder::LocalAppData: + result = home / TEXT("/Library/Caches"); + break; + case SpecialFolder::ProgramData: + result = home / TEXT("/Library/Application Support"); + break; + case SpecialFolder::Temporary: + Platform::GetEnvironmentVariable(TEXT("TMPDIR"), result); + break; + } } #endif diff --git a/Source/Engine/Platform/Mac/MacPlatform.cpp b/Source/Engine/Platform/Mac/MacPlatform.cpp index baa9ea739..c2e903a69 100644 --- a/Source/Engine/Platform/Mac/MacPlatform.cpp +++ b/Source/Engine/Platform/Mac/MacPlatform.cpp @@ -32,15 +32,37 @@ #include #include #include +#include #include #include +#include +#include +#include +#include +#include +#include +#if CRASH_LOG_ENABLE +#include +#endif CPUInfo MacCpu; Guid DeviceId; String UserLocale, ComputerName; -byte MacAddress[6]; double SecondsPerCycle; +String ToString(CFStringRef str) +{ + String result; + const int32 length = CFStringGetLength(str); + if (length > 0) + { + CFRange range = CFRangeMake(0, length); + result.ReserveSpace(length); + CFStringGetBytes(str, range, kCFStringEncodingUTF16LE, '?', false, (uint8*)result.Get(), length * sizeof(Char), nullptr); + } + return result; +} + DialogResult MessageBox::Show(Window* parent, const StringView& text, const StringView& caption, MessageBoxButtons buttons, MessageBoxIcon icon) { if (CommandLine::Options.Headless) @@ -97,25 +119,39 @@ int32 MacPlatform::GetCacheLineSize() MemoryStats MacPlatform::GetMemoryStats() { - MISSING_CODE("MacPlatform::GetMemoryStats"); - return MemoryStats(); // TODO: platform stats on Mac + MemoryStats result; + int64 value64; + size_t value64Size = sizeof(value64); + if (sysctlbyname("hw.memsize", &value64, &value64Size, nullptr, 0) != 0) + value64 = 1024 * 1024; + result.TotalPhysicalMemory = value64; + int id[] = { CTL_HW, HW_MEMSIZE }; + if (sysctl(id, 2, &value64, &value64Size, nullptr, 0) != 0) + value64Size = 1024; + result.UsedPhysicalMemory = value64Size; + xsw_usage swapusage; + size_t swapusageSize = sizeof(swapusage); + result.TotalVirtualMemory = result.TotalPhysicalMemory; + result.UsedVirtualMemory = result.UsedPhysicalMemory; + if (sysctlbyname("vm.swapusage", &swapusage, &swapusageSize, nullptr, 0) == 0) + { + result.TotalVirtualMemory += swapusage.xsu_total; + result.UsedVirtualMemory += swapusage.xsu_used; + } + return result; } ProcessMemoryStats MacPlatform::GetProcessMemoryStats() { - MISSING_CODE("MacPlatform::GetProcessMemoryStats"); - return ProcessMemoryStats(); // TODO: platform stats on Mac -} - -uint64 MacPlatform::GetCurrentProcessId() -{ - return getpid(); + ProcessMemoryStats result; + result.UsedPhysicalMemory = 1024; + result.UsedVirtualMemory = 1024; + return result; } uint64 MacPlatform::GetCurrentThreadID() { - MISSING_CODE("MacPlatform::GetCurrentThreadID"); - return 0; // TODO: threading on Mac + return (uint64)pthread_mach_thread_np(pthread_self()); } void MacPlatform::SetThreadPriority(ThreadPriority priority) @@ -151,7 +187,7 @@ uint64 MacPlatform::GetClockFrequency() void MacPlatform::GetSystemTime(int32& year, int32& month, int32& dayOfWeek, int32& day, int32& hour, int32& minute, int32& second, int32& millisecond) { - // Query for calendar time + // Query for calendar time struct timeval time; gettimeofday(&time, nullptr); @@ -203,16 +239,75 @@ bool MacPlatform::Init() SecondsPerCycle = 1e-9 * (double)info.numer / (double)info.denom; } - // TODO: get MacCpu + // Get CPU info + int32 value32; + int64 value64; + size_t value32Size = sizeof(value32), value64Size = sizeof(value64); + if (sysctlbyname("hw.packages", &value32, &value32Size, nullptr, 0) != 0) + value32 = 1; + MacCpu.ProcessorPackageCount = value32; + if (sysctlbyname("hw.physicalcpu", &value32, &value32Size, nullptr, 0) != 0) + value32 = 1; + MacCpu.ProcessorCoreCount = value32; + if (sysctlbyname("hw.logicalcpu", &value32, &value32Size, nullptr, 0) != 0) + value32 = 1; + MacCpu.LogicalProcessorCount = value32; + if (sysctlbyname("hw.l1icachesize", &value32, &value32Size, nullptr, 0) != 0) + value32 = 0; + MacCpu.L1CacheSize = value32; + if (sysctlbyname("hw.l2cachesize", &value32, &value32Size, nullptr, 0) != 0) + value32 = 0; + MacCpu.L2CacheSize = value32; + if (sysctlbyname("hw.l3cachesize", &value32, &value32Size, nullptr, 0) != 0) + value32 = 0; + MacCpu.L3CacheSize = value32; + if (sysctlbyname("hw.pagesize", &value32, &value32Size, nullptr, 0) != 0) + value32 = vm_page_size; + MacCpu.PageSize = value32; + if (sysctlbyname("hw.cpufrequency_max", &value64, &value64Size, nullptr, 0) != 0) + value64 = GetClockFrequency(); + MacCpu.ClockSpeed = value64; + if (sysctlbyname("hw.cachelinesize", &value32, &value32Size, nullptr, 0) != 0) + value32 = PLATFORM_CACHE_LINE_SIZE; + MacCpu.CacheLineSize = value32; - // TODO: get MacAddress - // TODO: get DeviceId + // Get device id + { + io_registry_entry_t ioRegistryRoot = IORegistryEntryFromPath(kIOMasterPortDefault, "IOService:/"); + CFStringRef deviceUuid = (CFStringRef)IORegistryEntryCreateCFProperty(ioRegistryRoot, CFSTR(kIOPlatformUUIDKey), kCFAllocatorDefault, 0); + IOObjectRelease(ioRegistryRoot); + String uuidStr = ToString(deviceUuid); + Guid::Parse(uuidStr, DeviceId); + CFRelease(deviceUuid); + } - // TODO: get username - OnPlatformUserAdd(New(TEXT("User"))); + // Get locale + { + CFLocaleRef locale = CFLocaleCopyCurrent(); + CFStringRef localeLang = (CFStringRef)CFLocaleGetValue(locale, kCFLocaleLanguageCode); + CFStringRef localeCountry = (CFStringRef)CFLocaleGetValue(locale, kCFLocaleCountryCode); + UserLocale = ToString(localeLang); + String localeCountryStr = ToString(localeCountry); + if (localeCountryStr.HasChars()) + UserLocale += TEXT("-") + localeCountryStr; + CFRelease(locale); + CFRelease(localeLang); + CFRelease(localeCountry); + } - // TODO: get UserLocale - // TODO: get ComputerName + // Get computer name + { + CFStringRef computerName = SCDynamicStoreCopyComputerName(nullptr, nullptr); + ComputerName = ToString(computerName); + CFRelease(computerName); + } + + // Init user + { + String username; + GetEnvironmentVariable(TEXT("USER"), username); + OnPlatformUserAdd(New(username)); + } Input::Mouse = New(); Input::Keyboard = New(); @@ -220,13 +315,27 @@ bool MacPlatform::Init() return false; } +void MacPlatform::LogInfo() +{ + UnixPlatform::LogInfo(); + + char str[250]; + size_t strSize = sizeof(str); + if (sysctlbyname("kern.osrelease", str, &strSize, nullptr, 0) != 0) + str[0] = 0; + String osRelease(str); + if (sysctlbyname("kern.osproductversion", str, &strSize, nullptr, 0) != 0) + str[0] = 0; + String osProductVer(str); + LOG(Info, "macOS {1} (kernel {0})", osRelease, osProductVer); +} + void MacPlatform::BeforeRun() { } void MacPlatform::Tick() { - // TODO: app events } void MacPlatform::BeforeExit() @@ -239,8 +348,11 @@ void MacPlatform::Exit() int32 MacPlatform::GetDpi() { - // TODO: Screen DPI on Mac - return 96; + CGDirectDisplayID mainDisplay = CGMainDisplayID(); + CGSize size = CGDisplayScreenSize(mainDisplay); + float wide = (float)CGDisplayPixelsWide(mainDisplay); + float dpi = (wide * 25.4f) / size.width; + return Math::Max(dpi, 72.0f); } String MacPlatform::GetUserLocaleName() @@ -267,6 +379,17 @@ bool MacPlatform::GetHasFocus() return WindowsManager::Windows.IsEmpty(); } +void MacPlatform::CreateGuid(Guid& result) +{ + uuid_t uuid; + uuid_generate(uuid); + auto ptr = (uint32*)&uuid; + result.A = ptr[0]; + result.B = ptr[1]; + result.C = ptr[2]; + result.D = ptr[3]; +} + bool MacPlatform::CanOpenUrl(const StringView& url) { return false; @@ -290,32 +413,55 @@ void MacPlatform::SetMousePosition(const Vector2& pos) Vector2 MacPlatform::GetDesktopSize() { - MISSING_CODE("MacPlatform::GetDesktopSize"); - return Vector2(0, 0); // TODO: desktop size on Mac + CGDirectDisplayID mainDisplay = CGMainDisplayID(); + return Vector2((float)CGDisplayPixelsWide(mainDisplay), (float)CGDisplayPixelsHigh(mainDisplay)); +} + +Rectangle GetDisplayBounds(CGDirectDisplayID display) +{ + CGRect rect = CGDisplayBounds(display); + return Rectangle(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height); } Rectangle MacPlatform::GetMonitorBounds(const Vector2& screenPos) { - // TODO: do it in a proper way + CGPoint point; + point.x = screenPos.X; + point.y = screenPos.Y; + CGDirectDisplayID display; + uint32_t count = 0; + CGGetDisplaysWithPoint(point, 1, &display, &count); + if (count == 1) + return GetDisplayBounds(display); return Rectangle(Vector2::Zero, GetDesktopSize()); } Rectangle MacPlatform::GetVirtualDesktopBounds() { - // TODO: do it in a proper way - return Rectangle(Vector2::Zero, GetDesktopSize()); + CGDirectDisplayID displays[16]; + uint32_t count = 0; + CGGetOnlineDisplayList(ARRAY_COUNT(displays), displays, &count); + if (count == 0) + return Rectangle(Vector2::Zero, GetDesktopSize()); + Rectangle result = GetDisplayBounds(displays[0]); + for (uint32_t i = 1; i < count; i++) + result = Rectangle::Union(result, GetDisplayBounds(displays[i])); + return result; } String MacPlatform::GetMainDirectory() { - MISSING_CODE("MacPlatform::GetMainDirectory"); - return TEXT("/"); // TODO: GetMainDirectory + return StringUtils::GetDirectoryName(GetExecutableFilePath()); } String MacPlatform::GetExecutableFilePath() { - MISSING_CODE("MacPlatform::GetExecutableFilePath"); - return TEXT("/"); // TODO: GetMainDirectory + char buf[PATH_MAX]; + uint32 size = PATH_MAX; + String result; + if (_NSGetExecutablePath(buf, &size) == 0) + result.SetUTF8(buf, size); + return result; } Guid MacPlatform::GetUniqueDeviceId() @@ -374,4 +520,36 @@ void* MacPlatform::GetProcAddress(void* handle, const char* symbol) return nullptr; // TODO: dynamic libs on Mac } +Array MacPlatform::GetStackFrames(int32 skipCount, int32 maxDepth, void* context) +{ + Array result; +#if CRASH_LOG_ENABLE + void* callstack[120]; + skipCount = Math::Min(skipCount, ARRAY_COUNT(callstack)); + int32 maxCount = Math::Min(ARRAY_COUNT(callstack), skipCount + maxDepth); + int32 count = backtrace(callstack, maxCount); + int32 useCount = count - skipCount; + if (useCount > 0) + { + char** names = backtrace_symbols(callstack + skipCount, useCount); + result.Resize(useCount); + for (int32 i = 0; i < useCount; i++) + { + char* name = names[i]; + StackFrame& frame = result[i]; + frame.ProgramCounter = callstack[skipCount + i]; + frame.ModuleName[0] = 0; + frame.FileName[0] = 0; + frame.LineNumber = 0; + int32 nameLen = Math::Min(StringUtils::Length(name), ARRAY_COUNT(frame.FunctionName) - 1); + Platform::MemoryCopy(frame.FunctionName, name, nameLen); + frame.FunctionName[nameLen] = 0; + + } + free(names); + } +#endif + return result; +} + #endif diff --git a/Source/Engine/Platform/Mac/MacPlatform.h b/Source/Engine/Platform/Mac/MacPlatform.h index 86bb07e63..b0315fb71 100644 --- a/Source/Engine/Platform/Mac/MacPlatform.h +++ b/Source/Engine/Platform/Mac/MacPlatform.h @@ -4,7 +4,7 @@ #if PLATFORM_MAC -#include "../Base/PlatformBase.h" +#include "../Unix/UnixPlatform.h" /// /// The Mac platform implementation and application management utilities. @@ -81,6 +81,7 @@ public: static void GetSystemTime(int32& year, int32& month, int32& dayOfWeek, int32& day, int32& hour, int32& minute, int32& second, int32& millisecond); static void GetUTCTime(int32& year, int32& month, int32& dayOfWeek, int32& day, int32& hour, int32& minute, int32& second, int32& millisecond); static bool Init(); + static void LogInfo(); static void BeforeRun(); static void Tick(); static void BeforeExit(); @@ -89,6 +90,7 @@ public: static String GetUserLocaleName(); static String GetComputerName(); static bool GetHasFocus(); + static void CreateGuid(Guid& result); static bool CanOpenUrl(const StringView& url); static void OpenUrl(const StringView& url); static Vector2 GetMousePosition(); @@ -107,6 +109,7 @@ public: static void* LoadLibrary(const Char* filename); static void FreeLibrary(void* handle); static void* GetProcAddress(void* handle, const char* symbol); + static Array GetStackFrames(int32 skipCount = 0, int32 maxDepth = 60, void* context = nullptr); }; #endif diff --git a/Source/ThirdParty/PhysX/PhysX.Build.cs b/Source/ThirdParty/PhysX/PhysX.Build.cs index 3571896bd..888893f59 100644 --- a/Source/ThirdParty/PhysX/PhysX.Build.cs +++ b/Source/ThirdParty/PhysX/PhysX.Build.cs @@ -56,6 +56,7 @@ public class PhysX : DepsModule case TargetPlatform.Linux: case TargetPlatform.XboxOne: case TargetPlatform.XboxScarlett: + case TargetPlatform.Mac: switch (options.Architecture) { case TargetArchitecture.x86: diff --git a/Source/Tools/Flax.Build/Build/EngineTarget.cs b/Source/Tools/Flax.Build/Build/EngineTarget.cs index 76191943c..4633542e9 100644 --- a/Source/Tools/Flax.Build/Build/EngineTarget.cs +++ b/Source/Tools/Flax.Build/Build/EngineTarget.cs @@ -47,7 +47,7 @@ namespace Flax.Build public override string GetOutputFilePath(BuildOptions options, TargetOutputType? outputType) { // If building engine executable for platform doesn't support referencing it when linking game shared libraries - if (UseSymbolsExports && (outputType ?? OutputType) == TargetOutputType.Executable && !options.Platform.HasExecutableFileReferenceSupport) + if (outputType == null && UseSymbolsExports && OutputType == TargetOutputType.Executable && !options.Platform.HasExecutableFileReferenceSupport) { // Build into shared library outputType = TargetOutputType.Library; diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/NewtonsoftJson.cs b/Source/Tools/Flax.Build/Deps/Dependencies/NewtonsoftJson.cs index 154c0b86b..c444557e2 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/NewtonsoftJson.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/NewtonsoftJson.cs @@ -29,6 +29,7 @@ namespace Flax.Deps.Dependencies TargetPlatform.PS4, TargetPlatform.PS5, TargetPlatform.Switch, + TargetPlatform.Mac, }; default: return new TargetPlatform[0]; } @@ -62,6 +63,7 @@ namespace Flax.Deps.Dependencies { case TargetPlatform.Windows: case TargetPlatform.Linux: + case TargetPlatform.Mac: { foreach (var file in outputFileNames) { diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/PhysX.cs b/Source/Tools/Flax.Build/Deps/Dependencies/PhysX.cs index f3aa8ac9e..2995c61a7 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/PhysX.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/PhysX.cs @@ -41,6 +41,11 @@ namespace Flax.Deps.Dependencies { TargetPlatform.Linux, }; + case TargetPlatform.Mac: + return new[] + { + TargetPlatform.Mac, + }; default: return new TargetPlatform[0]; } } @@ -104,6 +109,7 @@ namespace Flax.Deps.Dependencies string buildPlatform; bool suppressBitsPostfix = false; string binariesPrefix = string.Empty; + var envVars = new Dictionary(); switch (architecture) { case TargetArchitecture.x86: @@ -180,11 +186,15 @@ namespace Flax.Deps.Dependencies suppressBitsPostfix = true; binariesPrefix = "lib"; break; + case TargetPlatform.Mac: + binariesSubDir = "mac.x86_64"; + binariesPrefix = "lib"; + envVars.Add("MACOSX_DEPLOYMENT_TARGET", Configuration.MacOSXMinVer); + break; default: throw new InvalidPlatformException(targetPlatform); } // Setup build environment variables for PhysX build system - var envVars = new Dictionary(); switch (BuildPlatform) { case TargetPlatform.Windows: @@ -197,11 +207,11 @@ namespace Flax.Deps.Dependencies break; } case TargetPlatform.Linux: - { envVars.Add("CC", "clang-7"); envVars.Add("CC_FOR_BUILD", "clang-7"); break; - } + case TargetPlatform.Mac: + break; default: throw new InvalidPlatformException(BuildPlatform); } if (AndroidNdk.Instance.IsValid) @@ -276,6 +286,9 @@ namespace Flax.Deps.Dependencies case TargetPlatform.Linux: Utilities.Run("make", null, null, Path.Combine(projectGenDir, "compiler", "linux-" + configuration), Utilities.RunOptions.None); break; + case TargetPlatform.Mac: + Utilities.Run("xcodebuild", "-project PhysXSDK.xcodeproj -alltargets -configuration " + configuration, null, Path.Combine(projectGenDir, "compiler", preset), Utilities.RunOptions.None); + break; default: throw new InvalidPlatformException(BuildPlatform); } @@ -291,6 +304,16 @@ namespace Flax.Deps.Dependencies var filenamePdb = Path.ChangeExtension(filename, "pdb"); if (File.Exists(Path.Combine(srcBinaries, filenamePdb))) Utilities.FileCopy(Path.Combine(srcBinaries, filenamePdb), Path.Combine(dstBinaries, filenamePdb)); + + // Strip debug symbols to reduce binaries size + switch (targetPlatform) + { + case TargetPlatform.Linux: + case TargetPlatform.Mac: + case TargetPlatform.Android: + Utilities.Run("strip", "\"" + filename + "\"", null, dstBinaries, Utilities.RunOptions.None); + break; + } } srcBinaries = Path.Combine(root, "physx", "compiler", preset, "sdk_source_bin", configuration); var additionalPhysXLibs = new[] @@ -318,13 +341,16 @@ namespace Flax.Deps.Dependencies root = options.IntermediateFolder; projectGenDir = Path.Combine(root, "physx"); solutionFilesRoot = Path.Combine(root, "physx", "compiler"); - if (BuildPlatform == TargetPlatform.Windows) + switch (BuildPlatform) { + case TargetPlatform.Windows: projectGenPath = Path.Combine(projectGenDir, "generate_projects.bat"); - } - else if (BuildPlatform == TargetPlatform.Linux) - { + break; + case TargetPlatform.Linux: + case TargetPlatform.Mac: projectGenPath = Path.Combine(projectGenDir, "generate_projects.sh"); + break; + default: throw new InvalidPlatformException(BuildPlatform); } // Get the source @@ -382,6 +408,11 @@ namespace Flax.Deps.Dependencies Build(options, "switch64", platform, TargetArchitecture.ARM64); break; } + case TargetPlatform.Mac: + { + Build(options, "mac64", platform, TargetArchitecture.x64); + break; + } } } diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/freetype.cs b/Source/Tools/Flax.Build/Deps/Dependencies/freetype.cs index 826800764..c1f49abce 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/freetype.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/freetype.cs @@ -39,6 +39,11 @@ namespace Flax.Deps.Dependencies { TargetPlatform.Linux, }; + case TargetPlatform.Mac: + return new[] + { + TargetPlatform.Mac, + }; default: return new TargetPlatform[0]; } } @@ -63,10 +68,12 @@ namespace Flax.Deps.Dependencies }; // Get the source - Downloader.DownloadFileFromUrlToPath("https://sourceforge.net/projects/freetype/files/freetype2/2.10.0/ft2100.zip/download", packagePath); + if (!File.Exists(packagePath)) + Downloader.DownloadFileFromUrlToPath("https://sourceforge.net/projects/freetype/files/freetype2/2.10.0/ft2100.zip/download", packagePath); using (ZipArchive archive = ZipFile.Open(packagePath, ZipArchiveMode.Read)) { - archive.ExtractToDirectory(root); + if (!Directory.Exists(root)) + archive.ExtractToDirectory(root); root = Path.Combine(root, archive.Entries.First().FullName); } @@ -238,6 +245,17 @@ namespace Flax.Deps.Dependencies Utilities.FileCopy(Path.Combine(buildDir, libraryFileName), Path.Combine(depsFolder, libraryFileName)); break; } + case TargetPlatform.Mac: + { + // Build for Mac + var buildDir = Path.Combine(root, "build"); + SetupDirectory(buildDir, true); + RunCmake(buildDir, platform, TargetArchitecture.x64, ".. -DCMAKE_BUILD_TYPE=Release"); + Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.None); + var depsFolder = GetThirdPartyFolder(options, platform, TargetArchitecture.x64); + Utilities.FileCopy(Path.Combine(buildDir, libraryFileName), Path.Combine(depsFolder, libraryFileName)); + break; + } } } diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/ogg.cs b/Source/Tools/Flax.Build/Deps/Dependencies/ogg.cs index 6056cad48..eeb2908ae 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/ogg.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/ogg.cs @@ -38,6 +38,11 @@ namespace Flax.Deps.Dependencies { TargetPlatform.Linux, }; + case TargetPlatform.Mac: + return new[] + { + TargetPlatform.Mac, + }; default: return new TargetPlatform[0]; } } @@ -191,7 +196,7 @@ namespace Flax.Deps.Dependencies // Build for Android SetupDirectory(buildDir, true); - RunCmake(buildDir, TargetPlatform.Android, TargetArchitecture.ARM64, ".. -DCMAKE_BUILD_TYPE=Release"); + RunCmake(buildDir, platform, TargetArchitecture.ARM64, ".. -DCMAKE_BUILD_TYPE=Release"); Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.None); var depsFolder = GetThirdPartyFolder(options, platform, TargetArchitecture.ARM64); Utilities.FileCopy(Path.Combine(buildDir, libraryFileName), Path.Combine(depsFolder, libraryFileName)); @@ -211,6 +216,18 @@ namespace Flax.Deps.Dependencies Utilities.FileCopy(Path.Combine(buildDir, libraryFileName), Path.Combine(depsFolder, libraryFileName)); break; } + case TargetPlatform.Mac: + { + var buildDir = Path.Combine(root, "build"); + + // Build for Mac + SetupDirectory(buildDir, true); + RunCmake(buildDir, platform, TargetArchitecture.x64, ".. -DCMAKE_BUILD_TYPE=Release"); + Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.None); + var depsFolder = GetThirdPartyFolder(options, platform, TargetArchitecture.x64); + Utilities.FileCopy(Path.Combine(buildDir, libraryFileName), Path.Combine(depsFolder, libraryFileName)); + break; + } } } diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/vorbis.cs b/Source/Tools/Flax.Build/Deps/Dependencies/vorbis.cs index 510feacb9..c00ade2ae 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/vorbis.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/vorbis.cs @@ -39,6 +39,11 @@ namespace Flax.Deps.Dependencies { TargetPlatform.Linux, }; + case TargetPlatform.Mac: + return new[] + { + TargetPlatform.Mac, + }; default: return new TargetPlatform[0]; } } @@ -283,12 +288,12 @@ namespace Flax.Deps.Dependencies Utilities.Run(Path.Combine(root, "autogen.sh"), null, null, root, Utilities.RunOptions.Default, envVars); // Build for Linux - var toolchain = UnixToolchain.GetToolchainName(TargetPlatform.Linux, TargetArchitecture.x64); + var toolchain = UnixToolchain.GetToolchainName(platform, TargetArchitecture.x64); Utilities.Run(Path.Combine(root, "configure"), string.Format("--host={0}", toolchain), null, root, Utilities.RunOptions.Default, envVars); SetupDirectory(buildDir, true); Utilities.Run("cmake", "-G \"Unix Makefiles\" -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_BUILD_TYPE=Release ..", null, buildDir, Utilities.RunOptions.None, envVars); Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.None, envVars); - var depsFolder = GetThirdPartyFolder(options, TargetPlatform.Linux, TargetArchitecture.x64); + var depsFolder = GetThirdPartyFolder(options, platform, TargetArchitecture.x64); foreach (var file in binariesToCopyUnix) Utilities.FileCopy(Path.Combine(buildDir, file.SrcFolder, file.Filename), Path.Combine(depsFolder, file.Filename)); break; @@ -321,12 +326,12 @@ namespace Flax.Deps.Dependencies // Build for Android SetupDirectory(oggBuildDir, true); - RunCmake(oggBuildDir, TargetPlatform.Android, TargetArchitecture.ARM64, ".. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=\"../install\""); + RunCmake(oggBuildDir, platform, TargetArchitecture.ARM64, ".. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=\"../install\""); Utilities.Run("cmake", "--build . --target install", null, oggBuildDir, Utilities.RunOptions.None); SetupDirectory(buildDir, true); - RunCmake(buildDir, TargetPlatform.Android, TargetArchitecture.ARM64, string.Format(".. -DCMAKE_BUILD_TYPE=Release -DOGG_INCLUDE_DIR=\"{0}/install/include\" -DOGG_LIBRARY=\"{0}/install/lib\"", oggRoot)); + RunCmake(buildDir, platform, TargetArchitecture.ARM64, string.Format(".. -DCMAKE_BUILD_TYPE=Release -DOGG_INCLUDE_DIR=\"{0}/install/include\" -DOGG_LIBRARY=\"{0}/install/lib\"", oggRoot)); Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.None); - var depsFolder = GetThirdPartyFolder(options, TargetPlatform.Android, TargetArchitecture.ARM64); + var depsFolder = GetThirdPartyFolder(options, platform, TargetArchitecture.ARM64); foreach (var file in binariesToCopyUnix) Utilities.FileCopy(Path.Combine(buildDir, file.SrcFolder, file.Filename), Path.Combine(depsFolder, file.Filename)); break; @@ -358,6 +363,29 @@ namespace Flax.Deps.Dependencies Utilities.FileCopy(Path.Combine(buildDir, file.SrcFolder, file.Filename), Path.Combine(depsFolder, file.Filename)); break; } + case TargetPlatform.Mac: + { + var oggRoot = Path.Combine(root, "ogg"); + var oggBuildDir = Path.Combine(oggRoot, "build"); + var buildDir = Path.Combine(root, "build"); + + // Get the source + CloneGitRepoFast(root, "https://github.com/xiph/vorbis.git"); + CloneGitRepo(oggRoot, "https://github.com/xiph/ogg.git"); + GitCheckout(oggRoot, "master", "4380566a44b8d5e85ad511c9c17eb04197863ec5"); + + // Build for Android + SetupDirectory(oggBuildDir, true); + RunCmake(oggBuildDir, platform, TargetArchitecture.x64, ".. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=\"../install\""); + Utilities.Run("cmake", "--build . --target install", null, oggBuildDir, Utilities.RunOptions.None); + SetupDirectory(buildDir, true); + RunCmake(buildDir, platform, TargetArchitecture.x64, string.Format(".. -DCMAKE_BUILD_TYPE=Release -DOGG_INCLUDE_DIR=\"{0}/install/include\" -DOGG_LIBRARY=\"{0}/install/lib\"", oggRoot)); + Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.None); + var depsFolder = GetThirdPartyFolder(options, platform, TargetArchitecture.x64); + foreach (var file in binariesToCopyUnix) + Utilities.FileCopy(Path.Combine(buildDir, file.SrcFolder, file.Filename), Path.Combine(depsFolder, file.Filename)); + break; + } } } diff --git a/Source/Tools/Flax.Build/Deps/Dependency.cs b/Source/Tools/Flax.Build/Deps/Dependency.cs index 3ace8dd8d..5c5de42da 100644 --- a/Source/Tools/Flax.Build/Deps/Dependency.cs +++ b/Source/Tools/Flax.Build/Deps/Dependency.cs @@ -239,7 +239,6 @@ namespace Flax.Deps case TargetPlatform.Linux: case TargetPlatform.PS4: case TargetPlatform.PS5: - case TargetPlatform.Mac: { cmdLine = "CMakeLists.txt"; break; @@ -257,6 +256,11 @@ namespace Flax.Deps cmdLine = string.Format("-DCMAKE_TOOLCHAIN_FILE=\"{0}/build/cmake/android.toolchain.cmake\" -DANDROID_NDK=\"{0}\" -DANDROID_STL=c++_shared -DANDROID_ABI={1} -DANDROID_PLATFORM=android-{2} -G \"MinGW Makefiles\" -DCMAKE_MAKE_PROGRAM=\"{0}/prebuilt/{3}/bin/make.exe\"", ndk, abi, Configuration.AndroidPlatformApi, hostName); break; } + case TargetPlatform.Mac: + { + cmdLine = string.Format("CMakeLists.txt -DCMAKE_OSX_DEPLOYMENT_TARGET=\"{0}\"", Configuration.MacOSXMinVer); + break; + } default: throw new InvalidPlatformException(platform); } diff --git a/Source/Tools/Flax.Build/Deps/Downloader.cs b/Source/Tools/Flax.Build/Deps/Downloader.cs index b8eeb0bea..4d4189bc7 100644 --- a/Source/Tools/Flax.Build/Deps/Downloader.cs +++ b/Source/Tools/Flax.Build/Deps/Downloader.cs @@ -61,6 +61,8 @@ namespace Flax.Deps public static FileInfo DownloadFileFromUrlToPath(string url, string path) { Log.Verbose(string.Format("Downloading {0} to {1}", url, path)); + if (File.Exists(path)) + File.Delete(path); if (url.StartsWith(GoogleDriveDomain) || url.StartsWith(GoogleDriveDomain2)) return DownloadGoogleDriveFileFromUrlToPath(url, path); diff --git a/Source/Tools/Flax.Build/Platforms/Mac/MacToolchain.cs b/Source/Tools/Flax.Build/Platforms/Mac/MacToolchain.cs index 9682f6f7b..62054721a 100644 --- a/Source/Tools/Flax.Build/Platforms/Mac/MacToolchain.cs +++ b/Source/Tools/Flax.Build/Platforms/Mac/MacToolchain.cs @@ -6,6 +6,18 @@ using System.Collections.Generic; using Flax.Build.Graph; using Flax.Build.NativeCpp; +namespace Flax.Build +{ + partial class Configuration + { + /// + /// Specifies the minimum Mac OSX version to use (eg. 10.14). + /// + [CommandLine("macOSXMinVer", "", "Specifies the minimum Mac OSX version to use (eg. 10.14).")] + public static string MacOSXMinVer = "10.14"; + } +} + namespace Flax.Build.Platforms { /// @@ -14,8 +26,6 @@ namespace Flax.Build.Platforms /// public sealed class MacToolchain : UnixToolchain { - private string MinMacOSXVer = "10.14"; - public string ToolchainPath; public string SdkPath; public string LinkerPath; @@ -95,6 +105,14 @@ namespace Flax.Build.Platforms base.SetupEnvironment(options); options.CompileEnv.PreprocessorDefinitions.Add("PLATFORM_MAC"); + + options.LinkEnv.InputLibraries.Add("z"); + options.LinkEnv.InputLibraries.Add("bz2"); + + options.LinkEnv.InputLibraries.Add("CoreFoundation.framework"); + options.LinkEnv.InputLibraries.Add("CoreGraphics.framework"); + options.LinkEnv.InputLibraries.Add("SystemConfiguration.framework"); + options.LinkEnv.InputLibraries.Add("IOKit.framework"); } /// @@ -113,15 +131,26 @@ namespace Flax.Build.Platforms commonArgs.Add("c++"); commonArgs.Add("-std=c++14"); commonArgs.Add("-stdlib=libc++"); - commonArgs.Add("-mmacosx-version-min=" + MinMacOSXVer); + AddArgsCommon(options, commonArgs); + + switch (Architecture) + { + case TargetArchitecture.x64: + commonArgs.Add("-msse2"); + break; + } commonArgs.Add("-Wdelete-non-virtual-dtor"); commonArgs.Add("-fno-math-errno"); commonArgs.Add("-fasm-blocks"); + commonArgs.Add("-fpascal-strings"); commonArgs.Add("-fdiagnostics-format=msvc"); commonArgs.Add("-Wno-absolute-value"); commonArgs.Add("-Wno-nullability-completeness"); + commonArgs.Add("-Wno-undef-prefix"); + commonArgs.Add("-Wno-expansion-to-defined"); + commonArgs.Add("-Wno-non-virtual-dtor"); // Hide all symbols by default commonArgs.Add("-fvisibility-inlines-hidden"); @@ -222,7 +251,7 @@ namespace Flax.Build.Platforms var args = new List(); { args.Add(string.Format("-o \"{0}\"", outputFilePath)); - args.Add("-mmacosx-version-min=" + MinMacOSXVer); + AddArgsCommon(options, args); if (!options.LinkEnv.DebugInformation) args.Add("-Wl,--strip-debug"); @@ -230,12 +259,15 @@ namespace Flax.Build.Platforms switch (linkEnvironment.Output) { case LinkerOutput.Executable: + args.Add("-dead_strip"); break; case LinkerOutput.SharedLibrary: args.Add("-dynamiclib"); + args.Add("-dead_strip"); break; case LinkerOutput.StaticLibrary: case LinkerOutput.ImportLibrary: + args.Add("-static"); break; default: throw new ArgumentOutOfRangeException(); } @@ -247,7 +279,11 @@ namespace Flax.Build.Platforms { var dir = Path.GetDirectoryName(library); var ext = Path.GetExtension(library); - if (string.IsNullOrEmpty(dir)) + if (ext == ".framework") + { + args.Add(string.Format("-framework {0}", library.Substring(0, library.Length - ext.Length))); + } + else if (string.IsNullOrEmpty(dir)) { args.Add(string.Format("\"-l{0}\"", library)); } @@ -260,12 +296,13 @@ namespace Flax.Build.Platforms // Link against dynamic library task.PrerequisiteFiles.Add(library); libraryPaths.Add(dir); - args.Add(string.Format("\"-l{0}\"", UnixToolchain.GetLibName(library))); + //args.Add(string.Format("\"-l{0}\"", GetLibName(library))); + args.Add(string.Format("\"{0}\"", library)); } else { task.PrerequisiteFiles.Add(library); - args.Add(string.Format("\"{0}\"", UnixToolchain.GetLibName(library))); + args.Add(string.Format("\"{0}\"", GetLibName(library))); } } foreach (var library in options.Libraries) @@ -285,12 +322,13 @@ namespace Flax.Build.Platforms // Link against dynamic library task.PrerequisiteFiles.Add(library); libraryPaths.Add(dir); - args.Add(string.Format("\"-l{0}\"", UnixToolchain.GetLibName(library))); + //args.Add(string.Format("\"-l{0}\"", GetLibName(library))); + args.Add(string.Format("\"{0}\"", library)); } else { task.PrerequisiteFiles.Add(library); - args.Add(string.Format("\"{0}\"", UnixToolchain.GetLibName(library))); + args.Add(string.Format("\"{0}\"", GetLibName(library))); } } @@ -336,6 +374,36 @@ namespace Flax.Build.Platforms task.InfoMessage = "Linking " + outputFilePath; task.Cost = task.PrerequisiteFiles.Count; task.ProducedFiles.Add(outputFilePath); + + if (!options.LinkEnv.DebugInformation) + { + // Strip debug symbols + var stripTask = graph.Add(); + stripTask.ProducedFiles.Add(outputFilePath); + stripTask.WorkingDirectory = options.WorkingDirectory; + stripTask.CommandPath = "strip"; + stripTask.CommandArguments = string.Format("\"{0}\" -S", outputFilePath); + stripTask.InfoMessage = "Striping " + outputFilePath; + stripTask.Cost = 1; + stripTask.DisableCache = true; + stripTask.DependentTasks = new HashSet(); + stripTask.DependentTasks.Add(task); + } + } + + private void AddArgsCommon(BuildOptions options, List args) + { + args.Add("-mmacosx-version-min=" + Configuration.MacOSXMinVer); + args.Add("-isysroot \"" + SdkPath + "\""); + switch (Architecture) + { + case TargetArchitecture.x64: + args.Add("-arch x86_64"); + break; + case TargetArchitecture.ARM64: + args.Add("-arch arm64"); + break; + } } } } diff --git a/Source/Tools/Flax.Build/Projects/VisualStudioCode/VisualStudioCodeProjectGenerator.cs b/Source/Tools/Flax.Build/Projects/VisualStudioCode/VisualStudioCodeProjectGenerator.cs index 37d05066e..f2a90f401 100644 --- a/Source/Tools/Flax.Build/Projects/VisualStudioCode/VisualStudioCodeProjectGenerator.cs +++ b/Source/Tools/Flax.Build/Projects/VisualStudioCode/VisualStudioCodeProjectGenerator.cs @@ -304,7 +304,7 @@ namespace Flax.Build.Projects.VisualStudioCode var target = configuration.Target; var outputType = project.OutputType ?? target.OutputType; - var outputTargetFilePath = target.GetOutputFilePath(configuration.TargetBuildOptions, project.OutputType); + var outputTargetFilePath = target.GetOutputFilePath(configuration.TargetBuildOptions, TargetOutputType.Executable); json.BeginObject(); { @@ -387,6 +387,31 @@ namespace Flax.Build.Projects.VisualStudioCode json.EndArray(); } break; + case TargetPlatform.Mac: + if (configuration.Platform == TargetPlatform.Mac && (outputType != TargetOutputType.Executable || project.Name == "Flax") && configuration.Name.StartsWith("Editor.")) + { + json.AddField("program", Path.Combine(Globals.EngineRoot, "Binaries", "Editor", "Mac", configuration.ConfigurationName, "FlaxEditor")); + } + else + { + json.AddField("program", outputTargetFilePath); + } + if (configuration.Platform == TargetPlatform.Mac) + { + json.AddField("MIMode", "lldb"); + json.BeginArray("args"); + { + json.AddUnnamedField("--std"); + if (outputType != TargetOutputType.Executable && configuration.Name.StartsWith("Editor.")) + { + json.AddUnnamedField("--project"); + json.AddUnnamedField(buildToolWorkspace); + json.AddUnnamedField("--skipCompile"); + } + } + json.EndArray(); + } + break; } switch (configuration.Platform) { diff --git a/Source/Tools/Flax.Build/Utilities/Utilities.cs b/Source/Tools/Flax.Build/Utilities/Utilities.cs index faf22221b..3528a5474 100644 --- a/Source/Tools/Flax.Build/Utilities/Utilities.cs +++ b/Source/Tools/Flax.Build/Utilities/Utilities.cs @@ -319,7 +319,10 @@ namespace Flax.Build /// The exit code of the program. public static int Run(string app, string commandLine = null, string input = null, string workspace = null, RunOptions options = RunOptions.Default, Dictionary envVars = null) { - // Check if the application exists, including the PATH directories. + if (string.IsNullOrEmpty(app)) + throw new ArgumentNullException(nameof(app), "Missing app to run."); + + // Check if the application exists, including the PATH directories if (options.HasFlag(RunOptions.AppMustExist) && !File.Exists(app)) { bool existsInPath = false;