diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml new file mode 100644 index 000000000..1a34c1e41 --- /dev/null +++ b/.github/workflows/build-macos.yml @@ -0,0 +1,86 @@ +name: Build KytyPS5 (macOS) + +on: + workflow_dispatch: + push: + branches: [ main, master ] + pull_request: + +jobs: + build: + runs-on: macos-15 + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Select Xcode 26 + shell: bash + run: | + xcode_path="$(ls -d /Applications/Xcode_26*.app | tail -n 1)" + test -n "$xcode_path" + sudo xcode-select --switch "$xcode_path/Contents/Developer" + xcodebuild -version + + - name: Install build dependencies + shell: bash + run: brew install glslang ninja + + - name: Install Qt 6.10.3 + uses: jurplel/install-qt-action@v4 + with: + version: "6.10.3" + host: mac + target: desktop + arch: clang_64 + cache: true + + - name: Verify toolchain + shell: bash + run: | + git --version + cmake --version + ninja --version + clang++ --version + glslangValidator --version + echo "Host architecture: $(uname -m)" + echo "QT_ROOT_DIR=$QT_ROOT_DIR" + + - name: Configure + shell: bash + run: | + cmake -S src -B _Build/macos \ + -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_OSX_ARCHITECTURES=x86_64 \ + -DCMAKE_C_COMPILER=clang \ + -DCMAKE_CXX_COMPILER=clang++ \ + -DCMAKE_PREFIX_PATH="$QT_ROOT_DIR" + + - name: Build + shell: bash + run: | + cmake --build _Build/macos --target launcher --parallel + + - name: Install + shell: bash + run: | + cmake --install _Build/macos --prefix _Build/macos/install + + - name: Verify artifacts + shell: bash + run: | + file _Build/macos/install/launcher + file _Build/macos/install/kyty_emulator + lipo _Build/macos/install/launcher -verify_arch x86_64 + lipo _Build/macos/install/kyty_emulator -verify_arch x86_64 + codesign --verify --strict _Build/macos/install/kyty_emulator + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: KytyPS5-macOS-x86_64 + path: _Build/macos/install/** + if-no-files-found: error diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e5d4f1aec..42ff4ae38 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,11 +22,31 @@ jobs: - name: Setup Ninja uses: seanmiddleditch/gha-setup-ninja@v5 - - name: Install glslang + - name: Locate vcpkg + id: vcpkg shell: pwsh run: | $vcpkgRoot = Split-Path (Get-Command vcpkg).Source - vcpkg install glslang[tools,opt]:x64-windows + $portHash = (Get-FileHash "$vcpkgRoot\ports\glslang\vcpkg.json" -Algorithm SHA256).Hash + "root=$vcpkgRoot" | + Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append + "port_hash=$($portHash.ToLowerInvariant())" | + Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append + + - name: Cache glslang + id: glslang_cache + uses: actions/cache@v4 + with: + path: ${{ steps.vcpkg.outputs.root }}\installed\x64-windows + key: glslang-${{ runner.os }}-${{ runner.arch }}-${{ steps.vcpkg.outputs.port_hash }}-tools-opt + + - name: Install glslang + shell: pwsh + run: | + $vcpkgRoot = "${{ steps.vcpkg.outputs.root }}" + if ("${{ steps.glslang_cache.outputs.cache-hit }}" -ne "true") { + vcpkg install glslang[tools,opt]:x64-windows + } "$vcpkgRoot\installed\x64-windows\tools\glslang" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append diff --git a/README.md b/README.md index 7c817bf80..34251a640 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ [![Windows Build](https://github.com/KytyPS5/KytyPS5/actions/workflows/build.yml/badge.svg)](https://github.com/KytyPS5/KytyPS5/actions/workflows/build.yml) [![Linux Build](https://github.com/KytyPS5/KytyPS5/actions/workflows/build-linux.yml/badge.svg)](https://github.com/KytyPS5/KytyPS5/actions/workflows/build-linux.yml) +[![macOS Build](https://github.com/KytyPS5/KytyPS5/actions/workflows/build-macos.yml/badge.svg)](https://github.com/KytyPS5/KytyPS5/actions/workflows/build-macos.yml) [![Platform](https://img.shields.io/badge/platform-Windows%20x64-0078D4.svg)](#system-requirements) [![Status](https://img.shields.io/badge/status-early%20development-orange.svg)](#current-status) [![License](https://img.shields.io/badge/license-GPL--2.0-blue.svg)](LICENSE) diff --git a/src/common/platform/sysLinuxDbg.cpp b/src/common/platform/sysLinuxDbg.cpp index 11186980e..2d3a2d834 100644 --- a/src/common/platform/sysLinuxDbg.cpp +++ b/src/common/platform/sysLinuxDbg.cpp @@ -11,6 +11,9 @@ #include #include #include +#if defined(__APPLE__) +#include // POSIX basename() lives here on macOS, not in +#endif void SysStackWalk(void** /*stack*/, int* depth) { *depth = 0; diff --git a/src/common/platform/sysLinuxVirtual.cpp b/src/common/platform/sysLinuxVirtual.cpp index eadcacd11..5395af8d0 100644 --- a/src/common/platform/sysLinuxVirtual.cpp +++ b/src/common/platform/sysLinuxVirtual.cpp @@ -31,8 +31,8 @@ void SysVirtualInit() { pthread_mutexattr_t attr {}; pthread_mutexattr_init(&attr); -#if KYTY_PLATFORM == KYTY_PLATFORM_LINUX - pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_FAST_NP); +#if KYTY_PLATFORM == KYTY_PLATFORM_LINUX && !defined(__APPLE__) + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_FAST_NP); // glibc-only fast mutex #else pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL); #endif diff --git a/src/kernel/memory.cpp b/src/kernel/memory.cpp index 234571eaa..eda552a35 100644 --- a/src/kernel/memory.cpp +++ b/src/kernel/memory.cpp @@ -40,6 +40,7 @@ #endif #elif KYTY_PLATFORM == KYTY_PLATFORM_LINUX #include +#include #include #include #include diff --git a/src/kernel/memoryAddressSpace.inc b/src/kernel/memoryAddressSpace.inc index a2083366e..20a59f86b 100644 --- a/src/kernel/memoryAddressSpace.inc +++ b/src/kernel/memoryAddressSpace.inc @@ -54,9 +54,21 @@ public: m_handle = nullptr; } #elif KYTY_PLATFORM == KYTY_PLATFORM_LINUX +#if defined(__APPLE__) + { + char shm_name[64]; + snprintf(shm_name, sizeof(shm_name), "/kyty-dm-%d-%llx", static_cast(getpid()), + static_cast(reinterpret_cast(this))); + m_fd = shm_open(shm_name, O_RDWR | O_CREAT | O_EXCL, 0600); + if (m_fd >= 0) { + shm_unlink(shm_name); + } + } +#else m_fd = static_cast(syscall(SYS_memfd_create, "KytyDirectMemory", 0)); +#endif if (m_fd < 0) { - LOGF_COLOR(Log::Color::Red, "\t direct-memory backing: memfd_create failed: %d\n", + LOGF_COLOR(Log::Color::Red, "\t direct-memory backing: shared fd creation failed: %d\n", errno); return; } diff --git a/src/kernel/pthread.cpp b/src/kernel/pthread.cpp index e3fb2bc37..8939ca516 100644 --- a/src/kernel/pthread.cpp +++ b/src/kernel/pthread.cpp @@ -1056,7 +1056,7 @@ static void PthreadAttrDbgPrint(const PthreadAttr* src) { "\tstack_addr = 0x%016" PRIx64 "\n" "\tstack_size = %" PRIu64 "\n", mask, state, guard_size, inherit_sched, param.sched_priority, policy, solosched, - reinterpret_cast(stack_addr), reinterpret_cast(stack_size)); + reinterpret_cast(stack_addr), static_cast(stack_size)); } static constexpr int32_t DST_NONE = 0; @@ -2640,7 +2640,12 @@ int KYTY_SYSV_ABI PthreadCondattrSetclock(PthreadCondattr* attr, KernelClockid c return KERNEL_ERROR_EINVAL; } - int result = pthread_condattr_setclock(&(*attr)->p, pclock_id); +#if defined(__APPLE__) + (void)pclock_id; + int result = 0; +#else + int result = pthread_condattr_setclock(&(*attr)->p, pclock_id); +#endif (*attr)->clock_id = clock_id; LOGF("\tcondattr setclock: clock_id = %d, native = %d, result = %d\n", clock_id, diff --git a/src/launcher/src/mainDialog.cpp b/src/launcher/src/mainDialog.cpp index 75c4f85a3..bec14b19c 100644 --- a/src/launcher/src/mainDialog.cpp +++ b/src/launcher/src/mainDialog.cpp @@ -26,7 +26,7 @@ #include "ui_main_dialog.h" -#ifndef __linux__ +#if defined(_WIN32) #include // IWYU pragma: keep #endif @@ -36,20 +36,20 @@ class QWidget; -#ifdef __linux__ -constexpr char EMULATOR_EXE[] = "kyty_emulator"; -#else +#if defined(_WIN32) constexpr char EMULATOR_EXE[] = "kyty_emulator.exe"; +#else +constexpr char EMULATOR_EXE[] = "kyty_emulator"; #endif -#ifndef __linux__ +#if defined(_WIN32) constexpr char CMD_EXE[] = "cmd.exe"; -#else +#elif defined(__linux__) constexpr char GNOME[] = "gnome-terminal"; constexpr char XTERM[] = "xterm"; constexpr char KYTY_BASH_FILE[] = "kyty_run.sh"; #endif -#ifndef __linux__ +#if defined(_WIN32) constexpr DWORD CMD_X_CHARS = 175; constexpr DWORD CMD_Y_CHARS = 1000; #endif @@ -292,7 +292,7 @@ void MainDialog::RunInterpreter(QProcess* process, const Configuration& info) { process->setProgram(GNOME); process->setArguments({"--", "bash", "-c", bash_file_name}); } -#else +#elif defined(_WIN32) { process->setProgram(CMD_EXE); QStringList process_args; @@ -300,9 +300,12 @@ void MainDialog::RunInterpreter(QProcess* process, const Configuration& info) { process_args += args; process->setArguments(process_args); } +#else + process->setProgram(interpreter); + process->setArguments(args); #endif process->setWorkingDirectory(dir.path()); -#ifndef __linux__ +#if defined(_WIN32) process->setCreateProcessArgumentsModifier([](QProcess::CreateProcessArguments* args) { args->flags |= static_cast(CREATE_NEW_CONSOLE); args->startupInfo->dwFlags &= ~static_cast(STARTF_USESTDHANDLES); diff --git a/src/libs/audio.cpp b/src/libs/audio.cpp index cb98778a9..0767945ac 100644 --- a/src/libs/audio.cpp +++ b/src/libs/audio.cpp @@ -1919,7 +1919,7 @@ int KYTY_SYSV_ABI Ngs2SystemCreateWithAllocator(const Ngs2SystemOption* optio option->sample_rate, option->max_voice_channels, reinterpret_cast(allocator->alloc_handler), reinterpret_cast(allocator->free_handler), - reinterpret_cast(allocator->user_data)); + static_cast(allocator->user_data)); Ngs2ContextBufferInfo buf {}; buf.host_buffer = nullptr; @@ -2002,7 +2002,7 @@ int KYTY_SYSV_ABI Ngs2RackCreate(uintptr_t system_handle, uint32_t rack_id, option->max_input_delay_blocks, option->max_matrices, option->max_ports, option->max_voice_channels, option->max_output_channels, reinterpret_cast(buffer_info->host_buffer), - reinterpret_cast(buffer_info->host_buffer_size)); + static_cast(buffer_info->host_buffer_size)); auto* ngs = reinterpret_cast(system_handle); auto* rack = static_cast(buffer_info->host_buffer); @@ -2106,7 +2106,7 @@ int KYTY_SYSV_ABI Ngs2RackCreateWithAllocator(uintptr_t system_handle, uint32_t option->max_voice_channels, option->max_output_channels, reinterpret_cast(allocator->alloc_handler), reinterpret_cast(allocator->free_handler), - reinterpret_cast(allocator->user_data)); + static_cast(allocator->user_data)); Ngs2ContextBufferInfo buf {}; buf.host_buffer = nullptr;