From 57c4051ecaa2fd09d54b63bec21181a6188df6ca Mon Sep 17 00:00:00 2001 From: "Herman S." <429230+has207@users.noreply.github.com> Date: Fri, 27 Mar 2026 02:28:34 +0900 Subject: [PATCH] [macOS] Build infrastructure and portability fixes Build system: - Enable ObjC++, macOS deployment target, framework linking - Fix AppleClang detection - macOS platform name for output directories - Skip Vulkan on macOS, null graphics without Vulkan dependency - macOS SDL2 framework detection and discord-rpc macOS sources Portability fixes: - mach_absolute_time for macOS tick counting - _NSGetExecutablePath for macOS - guard MAP_FIXED_NOREPLACE, ftruncate64->ftruncate - guard Vulkan include/usage, fix data root for all POSIX --- CMakeLists.txt | 30 +++++++++++++++++++--- src/xenia/app/xenia_main.cc | 11 ++++---- src/xenia/base/clock_posix.cc | 14 ++++++++++ src/xenia/base/filesystem_posix.cc | 26 ++++++++++++++++--- src/xenia/base/memory_posix.cc | 6 ++++- src/xenia/gpu/null/CMakeLists.txt | 12 ++++++--- src/xenia/gpu/null/null_graphics_system.cc | 6 +++++ third_party/CMakeLists.txt | 6 +++++ 8 files changed, 95 insertions(+), 16 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a0c5b07cc..115ffab45 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,11 @@ cmake_minimum_required(VERSION 3.20) -project(xenia LANGUAGES C CXX) +if(APPLE) + project(xenia LANGUAGES C CXX OBJCXX) + set(CMAKE_OBJCXX_STANDARD 20) + set(CMAKE_OBJCXX_STANDARD_REQUIRED ON) +else() + project(xenia LANGUAGES C CXX) +endif() set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -52,6 +58,8 @@ set(CMAKE_STATIC_LINKER_FLAGS_CHECKED "${CMAKE_STATIC_LINKER_FLAGS_DEBUG}" CACHE # Platform name for output paths if(WIN32) set(XE_PLATFORM_NAME "Windows") +elseif(APPLE) + set(XE_PLATFORM_NAME "macOS") else() set(XE_PLATFORM_NAME "Linux") endif() @@ -178,7 +186,7 @@ else() ) # Clang-specific C++ warnings - if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") add_compile_options( $<$:-Wno-deprecated-register> $<$:-Wno-deprecated-volatile> @@ -216,7 +224,23 @@ else() $<$:-Wno-implicit-function-declaration> ) - if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + if(APPLE) + # macOS-specific settings + set(CMAKE_OSX_DEPLOYMENT_TARGET "15.0" CACHE STRING "Minimum macOS version") + add_compile_options( + $<$:-Wno-shorten-64-to-32> + -Wno-deprecated-declarations + -Wno-character-conversion + ) + add_link_options(-Wl,-no_warn_duplicate_libraries) + link_libraries( + "-framework CoreFoundation" + "-framework Foundation" + "-framework IOKit" + dl + pthread + ) + elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") # GTK3 via pkg-config find_package(PkgConfig REQUIRED) pkg_check_modules(GTK3 REQUIRED gtk+-x11-3.0) diff --git a/src/xenia/app/xenia_main.cc b/src/xenia/app/xenia_main.cc index ceff37570..a18ffd7cd 100644 --- a/src/xenia/app/xenia_main.cc +++ b/src/xenia/app/xenia_main.cc @@ -48,7 +48,9 @@ // Available graphics systems: #include "xenia/gpu/null/null_graphics_system.h" +#if !XE_PLATFORM_MAC #include "xenia/gpu/vulkan/vulkan_graphics_system.h" +#endif #if XE_PLATFORM_WIN32 #include "xenia/gpu/d3d12/d3d12_graphics_system.h" #endif // XE_PLATFORM_WIN32 @@ -401,7 +403,9 @@ std::unique_ptr EmulatorApp::CreateGraphicsSystem() { #if XE_PLATFORM_WIN32 factory.Add("d3d12"); #endif // XE_PLATFORM_WIN32 +#if !XE_PLATFORM_MAC factory.Add("vulkan"); +#endif std::unique_ptr gpu_implementation = factory.Create(gpu_implementation_name); if (!gpu_implementation) { @@ -475,12 +479,9 @@ bool EmulatorApp::OnInitialize() { if (!cvars::portable && !std::filesystem::exists(storage_root / "portable.txt")) { storage_root = xe::filesystem::GetUserFolder(); -#if defined(XE_PLATFORM_WIN32) || defined(XE_PLATFORM_LINUX) - storage_root = storage_root / "Xenia"; +#if XE_PLATFORM_ANDROID + // TODO(Triang3l): Point to the app's external storage "files" directory. #else - // TODO(Triang3l): Point to the app's external storage "files" directory - // on Android. -#warning Unhandled platform for the data root. storage_root = storage_root / "Xenia"; #endif } diff --git a/src/xenia/base/clock_posix.cc b/src/xenia/base/clock_posix.cc index 1dd155f8f..cbe0fff85 100644 --- a/src/xenia/base/clock_posix.cc +++ b/src/xenia/base/clock_posix.cc @@ -8,6 +8,9 @@ */ #include +#ifdef __APPLE__ +#include +#endif #include "xenia/base/assert.h" #include "xenia/base/clock.h" @@ -15,6 +18,12 @@ namespace xe { uint64_t Clock::host_tick_frequency_platform() { +#ifdef __APPLE__ + mach_timebase_info_data_t info; + mach_timebase_info(&info); + return (uint64_t)((1000000000ull * (uint64_t)info.denom) / + (uint64_t)info.numer); +#else timespec res; int error = clock_getres(CLOCK_MONOTONIC_RAW, &res); assert_zero(error); @@ -22,14 +31,19 @@ uint64_t Clock::host_tick_frequency_platform() { // Convert nano seconds to hertz. Resolution is 1ns on most systems. return 1000000000ull / res.tv_nsec; +#endif } uint64_t Clock::host_tick_count_platform() { +#ifdef __APPLE__ + return mach_absolute_time(); +#else timespec tp; int error = clock_gettime(CLOCK_MONOTONIC_RAW, &tp); assert_zero(error); return tp.tv_nsec + tp.tv_sec * 1000000000ull; +#endif } uint64_t Clock::QueryHostSystemTime() { diff --git a/src/xenia/base/filesystem_posix.cc b/src/xenia/base/filesystem_posix.cc index 2cd71f328..0a759f1f4 100644 --- a/src/xenia/base/filesystem_posix.cc +++ b/src/xenia/base/filesystem_posix.cc @@ -23,6 +23,10 @@ #include #include #include +#if XE_PLATFORM_MAC +#include +#include +#endif namespace xe { @@ -43,10 +47,26 @@ std::filesystem::path to_path(const std::u16string_view source) { namespace filesystem { std::filesystem::path GetExecutablePath() { +#if XE_PLATFORM_MAC + char path[PATH_MAX]; + uint32_t size = sizeof(path); + if (_NSGetExecutablePath(path, &size) == 0) { + char real_path[PATH_MAX]; + if (realpath(path, real_path)) { + return std::string(real_path); + } + return std::string(path); + } + return std::string(); +#else char buff[FILENAME_MAX] = ""; - readlink("/proc/self/exe", buff, FILENAME_MAX); - std::string s(buff); - return s; + ssize_t len = readlink("/proc/self/exe", buff, sizeof(buff) - 1); + if (len != -1) { + buff[len] = '\0'; + return std::string(buff); + } + return std::string(); +#endif } std::filesystem::path GetExecutableFolder() { diff --git a/src/xenia/base/memory_posix.cc b/src/xenia/base/memory_posix.cc index 15a9ab858..212dec661 100644 --- a/src/xenia/base/memory_posix.cc +++ b/src/xenia/base/memory_posix.cc @@ -146,7 +146,9 @@ void* AllocFixed(void* base_address, size_t length, } return nullptr; } +#ifdef MAP_FIXED_NOREPLACE flags |= MAP_FIXED_NOREPLACE; +#endif } void* result = mmap(base_address, length, prot, flags, -1, 0); @@ -296,7 +298,7 @@ FileMappingHandle CreateFileMappingHandle(const std::filesystem::path& path, if (ret < 0) { return kFileMappingHandleInvalid; } - if (ftruncate64(ret, length) < 0) { + if (ftruncate(ret, length) < 0) { close(ret); shm_unlink(full_path.c_str()); return kFileMappingHandleInvalid; @@ -335,7 +337,9 @@ void* MapFileView(FileMappingHandle handle, void* base_address, size_t length, int flags = MAP_SHARED; if (base_address != nullptr) { +#ifdef MAP_FIXED_NOREPLACE flags |= MAP_FIXED_NOREPLACE; +#endif } void* result = mmap(base_address, length, prot, flags, handle, file_offset); diff --git a/src/xenia/gpu/null/CMakeLists.txt b/src/xenia/gpu/null/CMakeLists.txt index d295b4ce9..0cd9672e2 100644 --- a/src/xenia/gpu/null/CMakeLists.txt +++ b/src/xenia/gpu/null/CMakeLists.txt @@ -1,7 +1,11 @@ add_library(xenia-gpu-null STATIC) xe_platform_sources(xenia-gpu-null ${CMAKE_CURRENT_SOURCE_DIR}) -target_include_directories(xenia-gpu-null PRIVATE - ${PROJECT_SOURCE_DIR}/third_party/Vulkan-Headers/include -) -target_link_libraries(xenia-gpu-null PUBLIC xenia-base xenia-gpu xenia-ui xenia-ui-vulkan xxhash) +if(APPLE) + target_link_libraries(xenia-gpu-null PUBLIC xenia-base xenia-gpu xenia-ui xxhash) +else() + target_include_directories(xenia-gpu-null PRIVATE + ${PROJECT_SOURCE_DIR}/third_party/Vulkan-Headers/include + ) + target_link_libraries(xenia-gpu-null PUBLIC xenia-base xenia-gpu xenia-ui xenia-ui-vulkan xxhash) +endif() xe_target_defaults(xenia-gpu-null) diff --git a/src/xenia/gpu/null/null_graphics_system.cc b/src/xenia/gpu/null/null_graphics_system.cc index 85b6324da..b5059db1c 100644 --- a/src/xenia/gpu/null/null_graphics_system.cc +++ b/src/xenia/gpu/null/null_graphics_system.cc @@ -10,7 +10,9 @@ #include "xenia/gpu/null/null_graphics_system.h" #include "xenia/gpu/null//null_command_processor.h" +#if !XE_PLATFORM_MAC #include "xenia/ui/vulkan/vulkan_provider.h" +#endif #include "xenia/xbox.h" namespace xe { @@ -25,9 +27,13 @@ X_STATUS NullGraphicsSystem::Setup(cpu::Processor* processor, kernel::KernelState* kernel_state, ui::WindowedAppContext* app_context, bool with_presentation) { +#if XE_PLATFORM_MAC + provider_ = nullptr; +#else // This is a null graphics system, but we still setup vulkan because UI needs // it through us :| provider_ = xe::ui::vulkan::VulkanProvider::Create(false, with_presentation); +#endif return GraphicsSystem::Setup(processor, kernel_state, app_context, with_presentation); } diff --git a/third_party/CMakeLists.txt b/third_party/CMakeLists.txt index 27d517ef0..14f187844 100644 --- a/third_party/CMakeLists.txt +++ b/third_party/CMakeLists.txt @@ -121,6 +121,12 @@ if(WIN32) discord-rpc/src/connection_win.cpp discord-rpc/src/discord_register_win.cpp ) +elseif(APPLE) + target_sources(discord-rpc PRIVATE + discord-rpc/src/connection_unix.cpp + discord-rpc/src/discord_register_osx.m + ) + target_link_libraries(discord-rpc PRIVATE "-framework AppKit") elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") target_sources(discord-rpc PRIVATE discord-rpc/src/connection_unix.cpp