From 6f66cc39a7b03f995201214ddd032a5be8bd509b Mon Sep 17 00:00:00 2001
From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com>
Date: Wed, 1 Apr 2026 20:42:50 -0400
Subject: [PATCH 1/2] build: refactor build to host-first CMake with nxdk child
Restructure the build to perform a host-native configure/build while driving the Xbox target via an internal nxdk child configure. Added CMake helper modules (cmake/msys2.cmake, cmake/nxdk.cmake, cmake/host-mingw64-clang.cmake, run-child-build.cmake, sources.cmake) and a CLion run configuration (.run/moonlight_xbox.run.xml). Updated CMakeLists to detect/build host tests and optionally add the Xbox child build, and simplified CMakePresets.json for MinGW host builds.
Tooling and scripts updated: add MSYS2 detection helpers (scripts/find-msys2.*), remove legacy nxdk wrapper BATs and other outdated helpers, and make build scripts default to cmake-build-release (build.sh, build-mingw64.*). CI workflow adjusted to use a configurable CMAKE_BUILD dir and to point artifact, test and coverage paths at that build output. README updated to reflect the new workflow and usage.
---
.github/workflows/ci.yml | 30 +--
.gitignore | 4 +-
.run/Run Moonlight ISO in xemu.run.xml | 17 --
.run/moonlight_xbox.run.xml | 7 +
CMakeLists.txt | 152 ++++--------
CMakePresets.json | 58 +----
README.md | 62 ++---
build-mingw64.bat | 8 +-
build-mingw64.sh | 3 +-
build.sh | 40 +---
cmake/host-mingw64-clang.cmake | 26 +++
cmake/make.bat | 22 --
cmake/msys2.cmake | 174 ++++++++++++++
cmake/nxdk-cc.bat | 21 --
cmake/nxdk-cxx.bat | 21 --
cmake/nxdk-link.bat | 21 --
cmake/nxdk-pkg-config.bat | 21 --
cmake/nxdk.cmake | 311 +++++++++++++++++++++++++
cmake/run-child-build.cmake | 56 +++++
cmake/run-nxdk-tool.sh | 22 --
cmake/sources.cmake | 21 ++
cmake/toolchain-nxdk-clion.cmake | 7 -
cmake/xbox-build.cmake | 98 ++++++++
scripts/find-msys2.cmd | 71 ++++++
scripts/find-msys2.sh | 65 ++++++
scripts/run-xemu.cmd | 40 +++-
scripts/run-xemu.sh | 180 +++++++++++++-
scripts/setup-xemu.cmd | 8 +-
tests/CMakeLists.txt | 35 ++-
29 files changed, 1166 insertions(+), 435 deletions(-)
delete mode 100644 .run/Run Moonlight ISO in xemu.run.xml
create mode 100644 .run/moonlight_xbox.run.xml
create mode 100644 cmake/host-mingw64-clang.cmake
delete mode 100644 cmake/make.bat
create mode 100644 cmake/msys2.cmake
delete mode 100644 cmake/nxdk-cc.bat
delete mode 100644 cmake/nxdk-cxx.bat
delete mode 100644 cmake/nxdk-link.bat
delete mode 100644 cmake/nxdk-pkg-config.bat
create mode 100644 cmake/nxdk.cmake
create mode 100644 cmake/run-child-build.cmake
delete mode 100644 cmake/run-nxdk-tool.sh
create mode 100644 cmake/sources.cmake
delete mode 100644 cmake/toolchain-nxdk-clion.cmake
create mode 100644 cmake/xbox-build.cmake
create mode 100644 scripts/find-msys2.cmd
create mode 100644 scripts/find-msys2.sh
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 61ebcd3..6f5712f 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -55,7 +55,7 @@ jobs:
run:
shell: ${{ matrix.shell }}
env:
- CMAKE_TEST: cmake-build-host-tests
+ CMAKE_BUILD: build
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -135,15 +135,15 @@ jobs:
- name: Build
run: |
chmod +x build.sh
- ./build.sh
+ ./build.sh --build-dir ${CMAKE_BUILD}
# recursively list all files in build directory
- ls -R build
+ ls -R ${CMAKE_BUILD}
# move artifacts
mkdir -p artifacts
- tar -czf ./artifacts/Moonlight.tar.gz ./build/xbe
- mv ./Moonlight.iso ./artifacts
+ tar -czf ./artifacts/Moonlight.tar.gz ./${CMAKE_BUILD}/xbox/xbe
+ cp ./${CMAKE_BUILD}/xbox/Moonlight.iso ./artifacts
- name: Upload Artifacts
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
@@ -151,16 +151,10 @@ jobs:
name: moonlight-${{ runner.os }}
path: artifacts/
- - name: Configure host tests
- run: cmake -S . -B ${CMAKE_TEST} -DBUILD_TESTS=ON -DBUILD_DOCS=OFF
-
- - name: Build host tests
- run: cmake --build ${CMAKE_TEST} --target test_moonlight
-
- name: Run tests
id: test
- working-directory: ${{ env.CMAKE_TEST }}
- run: ./tests/test_moonlight --gtest_color=yes --gtest_output=xml:tests/test_results.xml
+ working-directory: ${{ env.CMAKE_BUILD }}/tests
+ run: ./test_moonlight --gtest_color=yes --gtest_output=xml:test_results.xml
- name: Generate gcov report
id: test_report
@@ -170,20 +164,20 @@ jobs:
(steps.test.outcome == 'success' || steps.test.outcome == 'failure')
run: |
${{ steps.python-path.outputs.python-path }} -m pip install ".[test]"
- ${{ steps.python-path.outputs.python-path }} -m gcovr ${CMAKE_TEST} -r . \
+ ${{ steps.python-path.outputs.python-path }} -m gcovr ${CMAKE_BUILD} -r . \
--filter src \
--exclude-noncode-lines \
--exclude-throw-branches \
--exclude-unreachable-branches \
--verbose \
--xml-pretty \
- -o "${CMAKE_TEST}/coverage.xml"
+ -o "${CMAKE_BUILD}/coverage.xml"
- name: Debug coverage file
if: >-
always() &&
steps.test_report.outcome == 'success'
- run: cat "${CMAKE_TEST}/coverage.xml"
+ run: cat "${CMAKE_BUILD}/coverage.xml"
- name: Upload coverage
# any except canceled or skipped
@@ -195,7 +189,7 @@ jobs:
with:
disable_search: true
fail_ci_if_error: true
- files: ./${{ env.CMAKE_TEST }}/coverage.xml
+ files: ./${{ env.CMAKE_BUILD }}/coverage.xml
flags: ${{ runner.os }}
report_type: coverage
token: ${{ secrets.CODECOV_TOKEN }}
@@ -211,7 +205,7 @@ jobs:
with:
disable_search: true
fail_ci_if_error: true
- files: ./${{ env.CMAKE_TEST }}/tests/test_results.xml
+ files: ./${{ env.CMAKE_BUILD }}/tests/test_results.xml
flags: ${{ runner.os }}
handle_no_reports_found: true
report_type: test_results
diff --git a/.gitignore b/.gitignore
index 49485fe..a7fe280 100644
--- a/.gitignore
+++ b/.gitignore
@@ -43,5 +43,5 @@ cmake-*/
.local/
docs/doxyconfig*
-# iso file is created in project root
-*.iso
+# Temporary files
+*.cmd~
diff --git a/.run/Run Moonlight ISO in xemu.run.xml b/.run/Run Moonlight ISO in xemu.run.xml
deleted file mode 100644
index 6045dbf..0000000
--- a/.run/Run Moonlight ISO in xemu.run.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/.run/moonlight_xbox.run.xml b/.run/moonlight_xbox.run.xml
new file mode 100644
index 0000000..fffada2
--- /dev/null
+++ b/.run/moonlight_xbox.run.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/CMakeLists.txt b/CMakeLists.txt
index fb1f4d7..6238a79 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,139 +1,69 @@
+# Configure the host-native top-level build, bootstrap vendored nxdk prerequisites,
+# and delegate the Xbox target to an internal child configure that uses the stock nxdk toolchain.
+
cmake_minimum_required(VERSION 3.18)
-# much of this file is borrowed from https://github.com/Ryzee119/Xenium-Tools/blob/master/CMakeLists.txt
-# and https://github.com/abaire/nxdk_pgraph_tests/blob/main/CMakeLists.txt
# Allow third-party subdirectories that use cmake_minimum_required < 3.5 (removed in CMake 4.x)
set(CMAKE_POLICY_VERSION_MINIMUM 3.5 CACHE STRING "")
-project(Moonlight C CXX)
+set(MOONLIGHT_BUILD_KIND "HOST" CACHE STRING "Internal Moonlight build mode")
+set_property(CACHE MOONLIGHT_BUILD_KIND PROPERTY STRINGS HOST XBOX)
-#
-# metadata
-#
-set(XBE_TITLE ${CMAKE_PROJECT_NAME})
-set(NXDK_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third-party/nxdk")
-set(XBOX_XBE_DIR "${CMAKE_CURRENT_BINARY_DIR}/xbe")
-set(XBOX_ISO "${CMAKE_PROJECT_NAME}.iso")
-
-#
-# Options
-#
-option(BUILD_DOCS "Build documentation" ON)
-option(BUILD_TESTS "Build tests" OFF)
+if(MOONLIGHT_BUILD_KIND STREQUAL "XBOX")
+ project(Moonlight C CXX)
-# add custom modules
-set(CMAKE_MODULE_PATH
- ${CMAKE_MODULE_PATH}
- "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/"
-)
+ set(CMAKE_MODULE_PATH
+ ${CMAKE_MODULE_PATH}
+ "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/"
+ )
-set(CMAKE_CXX_STANDARD 17)
+ set(CMAKE_CXX_STANDARD 17)
+ set(CMAKE_CXX_STANDARD_REQUIRED ON)
-find_package(NXDK REQUIRED)
-find_package(NXDK_SDL2 REQUIRED)
-find_package(NXDK_SDL2_Image REQUIRED)
+ option(BUILD_DOCS "Build documentation" OFF)
+ option(MOONLIGHT_SKIP_NXDK_PREP "Skip nxdk bootstrap during the internal Xbox child configure" OFF)
-# add the automount_d_drive symbol to the linker flags, this is automatic with nxdk when using the Makefile option
-# if this is not used, we must add some code to the main function to automount the D drive
-# e.g. https://github.com/abaire/nxdk_pgraph_tests/blob/4b7934e6d612a6d17f9ec229a2d72601a5caefc4/src/main.cpp#L118-L122
-set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -include:_automount_d_drive")
+ include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/xbox-build.cmake")
+ return()
+endif()
-# create the xbe directory if it doesn't exist
-file(MAKE_DIRECTORY ${XBOX_XBE_DIR})
+project(Moonlight C CXX)
-add_custom_target(sync_xbe_assets ALL
- COMMAND "${CMAKE_COMMAND}" -E copy_directory
- "${CMAKE_CURRENT_SOURCE_DIR}/xbe"
- "${XBOX_XBE_DIR}"
- COMMENT "Sync XBE assets"
+set(CMAKE_MODULE_PATH
+ ${CMAKE_MODULE_PATH}
+ "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/"
)
-include(FindPkgConfig)
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
-if(NOT CMAKE_BUILD_TYPE)
- set(CMAKE_BUILD_TYPE Release)
+if(CMAKE_HOST_WIN32 AND CMAKE_GENERATOR MATCHES "Makefiles")
+ set(CMAKE_DEPENDS_USE_COMPILER FALSE CACHE BOOL "Use CMake depfile scanning with Windows makefile generators" FORCE)
endif()
-set(CMAKE_CXX_FLAGS_RELEASE "-O2")
-set(CMAKE_C_FLAGS_RELEASE "-O2")
-file(GLOB_RECURSE MOONLIGHT_SOURCES
- "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp"
-)
-set(MOONLIGHT_EXTERNAL_LIBRARIES
- NXDK::NXDK
- NXDK::NXDK_CXX
- NXDK::SDL2
- NXDK::SDL2_Image
-)
-set(MOONLIGHT_INCLUDE_DIRS
-)
-set(MOONLIGHT_COMPILE_OPTIONS "-Wno-builtin-macro-redefined")
-
-# submodules
-# moonlight common library
-# find_package(OpenSSL REQUIRED)
-include(GetOpenSSL REQUIRED)
-set(ENET_NO_INSTALL ON CACHE BOOL "Don't install any libraries built for enet")
-set(BUILD_SHARED_LIBS OFF)
-add_subdirectory("${CMAKE_SOURCE_DIR}/third-party/moonlight-common-c")
-if(TARGET moonlight-common-c AND TARGET openssl_external)
- add_dependencies(moonlight-common-c openssl_external)
-endif()
-target_link_libraries(enet PUBLIC NXDK::NXDK NXDK::Net NXDK::ws2_32)
-if(TARGET moonlight-common-c)
- target_link_libraries(moonlight-common-c PRIVATE NXDK::ws2_32)
-endif()
+option(BUILD_DOCS "Build documentation" ON)
+option(BUILD_TESTS "Build host-native unit tests" ON)
+option(BUILD_XBOX "Build the Xbox target through an internal child configure" ON)
+option(MOONLIGHT_FORCE_NXDK_DISTCLEAN "Force a fresh nxdk distclean during configure" OFF)
-add_executable(${CMAKE_PROJECT_NAME}
- ${MOONLIGHT_SOURCES}
-)
-include_directories(SYSTEM
- ${CMAKE_CURRENT_SOURCE_DIR}
- ${MOONLIGHT_INCLUDE_DIRS})
-target_link_libraries(${CMAKE_PROJECT_NAME} PUBLIC
- ${MOONLIGHT_EXTERNAL_LIBRARIES}
-)
-target_compile_options(${CMAKE_PROJECT_NAME} PRIVATE ${MOONLIGHT_COMPILE_OPTIONS})
-target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE XBOX NXDK)
-add_dependencies(${CMAKE_PROJECT_NAME} moonlight-common-c)
+include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/sources.cmake")
-#
-# documentation
-#
if(BUILD_DOCS)
add_subdirectory(third-party/doxyconfig docs)
endif()
-#
-# tests
-#
if(BUILD_TESTS)
enable_testing()
- add_subdirectory(tests EXCLUDE_FROM_ALL)
+ add_subdirectory(tests)
endif()
-# Post-build exe to xbe conversion
-add_custom_target(cxbe_convert ALL
- VERBATIM COMMAND "${CMAKE_COMMAND}" -E env
- ./tools/cxbe/cxbe
- -OUT:${XBOX_XBE_DIR}/default.xbe
- -TITLE:${XBE_TITLE}
- ${CMAKE_CURRENT_BINARY_DIR}/${XBE_TITLE}.exe
- WORKING_DIRECTORY ${NXDK_DIR}
- COMMENT "CXBE Conversion: [EXE -> XBE]"
-)
-add_dependencies(cxbe_convert ${CMAKE_PROJECT_NAME})
-add_dependencies(cxbe_convert sync_xbe_assets)
-
-# Post-build xbe to xiso conversion
-add_custom_target(xbe_iso ALL
- VERBATIM COMMAND "${CMAKE_COMMAND}" -E env
- ${NXDK_DIR}/tools/extract-xiso/build/extract-xiso
- -c ${XBOX_XBE_DIR} ${XBOX_ISO}
- WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
- COMMENT "CXBE Conversion: [XBE -> XISO]"
-)
-add_dependencies(xbe_iso cxbe_convert)
+if(BUILD_XBOX)
+ include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/nxdk.cmake")
-set_target_properties(cxbe_convert PROPERTIES OUTPUT_QUIET ON)
-set_target_properties(xbe_iso PROPERTIES OUTPUT_QUIET ON)
+ moonlight_resolve_nxdk_dir(MOONLIGHT_NXDK_DIR)
+ moonlight_prepare_nxdk("${MOONLIGHT_NXDK_DIR}" "${CMAKE_BINARY_DIR}/nxdk-bootstrap")
+ moonlight_add_xbox_build(
+ NXDK_DIR "${MOONLIGHT_NXDK_DIR}"
+ BINARY_DIR "${CMAKE_BINARY_DIR}/xbox"
+ )
+endif()
diff --git a/CMakePresets.json b/CMakePresets.json
index cb0db9a..0aa380b 100644
--- a/CMakePresets.json
+++ b/CMakePresets.json
@@ -8,50 +8,32 @@
"configurePresets": [
{
"name": "nxdk-release (mingw64)",
- "displayName": "nxdk Release (mingw64)",
- "description": "Build for Original Xbox using the nxdk toolchain",
- "generator": "MSYS Makefiles",
- "cmakeExecutable": "C:/msys64/usr/bin/cmake",
- "toolchainFile": "${sourceDir}/cmake/toolchain-nxdk-clion.cmake",
- "binaryDir": "${sourceDir}/cmake-build-nxdk-release",
+ "displayName": "Release (mingw64)",
+ "description": "Build host tests natively with MinGW Makefiles and drive the Xbox target through an internal nxdk child configure",
+ "generator": "MinGW Makefiles",
+ "toolchainFile": "${sourceDir}/cmake/host-mingw64-clang.cmake",
+ "binaryDir": "${sourceDir}/cmake-build-release",
"environment": {
"CHERE_INVOKING": "1",
- "MSYSTEM": "MINGW64",
- "NXDK_DIR": "${sourceDir}/third-party/nxdk",
- "PATH": "${sourceDir}/cmake;${sourceDir}/third-party/nxdk/bin;C:/msys64/mingw64/bin;C:/msys64/usr/bin;$penv{PATH}"
+ "MSYSTEM": "MINGW64"
},
"cacheVariables": {
- "CMAKE_BUILD_TYPE": "Release",
- "CMAKE_CXX_COMPILER": "${sourceDir}/cmake/nxdk-cxx.bat",
- "CMAKE_C_COMPILER": "${sourceDir}/cmake/nxdk-cc.bat",
+ "BUILD_DOCS": "ON",
+ "BUILD_TESTS": "ON",
+ "BUILD_XBOX": "ON",
"CMAKE_DEPENDS_USE_COMPILER": "FALSE",
- "CMAKE_MAKE_PROGRAM": "${sourceDir}/cmake/make.bat",
- "CMAKE_TRY_COMPILE_TARGET_TYPE": "STATIC_LIBRARY",
- "PKG_CONFIG_EXECUTABLE": "${sourceDir}/cmake/nxdk-pkg-config.bat"
+ "CMAKE_BUILD_TYPE": "Release"
}
},
{
"name": "nxdk-debug (mingw64)",
- "displayName": "nxdk Debug (mingw64)",
- "description": "Debug build for Original Xbox using the nxdk toolchain",
+ "displayName": "Debug (mingw64)",
+ "description": "Debug host build with the internal nxdk Xbox child configure enabled",
"inherits": "nxdk-release (mingw64)",
"binaryDir": "${sourceDir}/cmake-build-nxdk-debug",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug"
}
- },
- {
- "name": "host-tests (mingw64)",
- "displayName": "Host Tests (mingw64)",
- "description": "Build host-native googletest targets under MSYS2/mingw64",
- "generator": "Unix Makefiles",
- "cmakeExecutable": "C:/msys64/usr/bin/cmake",
- "binaryDir": "${sourceDir}/cmake-build-host-tests",
- "cacheVariables": {
- "BUILD_DOCS": "OFF",
- "BUILD_TESTS": "ON",
- "CMAKE_BUILD_TYPE": "Debug"
- }
}
],
"buildPresets": [
@@ -62,22 +44,6 @@
{
"name": "nxdk-debug (mingw64)",
"configurePreset": "nxdk-debug (mingw64)"
- },
- {
- "name": "host-tests (mingw64)",
- "configurePreset": "host-tests (mingw64)",
- "targets": [
- "test_moonlight"
- ]
- }
- ],
- "testPresets": [
- {
- "name": "host-tests (mingw64)",
- "configurePreset": "host-tests (mingw64)",
- "output": {
- "outputOnFailure": true
- }
}
]
}
diff --git a/README.md b/README.md
index b6e03c0..b47918d 100644
--- a/README.md
+++ b/README.md
@@ -91,36 +91,18 @@ dependencies=("${nxdk_dependencies[@]}" "${moonlight_dependencies[@]}")
brew install "${dependencies[@]}"
```
-### Pre-Build
+### Configure
-1. Run the following from mingw64 or bash shell:
+Configure the top-level project with the normal host toolchain. CMake uses the vendored `third-party/nxdk` checkout, bootstraps the required `nxdk` outputs, builds the host-native tests with the standard compiler/linker, and drives the Xbox build through an internal child configure that reuses the stock `nxdk` toolchain.
```bash
-export NXDK_DIR="$(pwd)/third-party/nxdk"
-eval "$(${NXDK_DIR}/bin/activate -s)"
-cd "${NXDK_DIR}"
-make NXDK_ONLY=y
-make tools
+cmake -S . -B cmake-build-release -DBUILD_DOCS=OFF -DCMAKE_BUILD_TYPE=Release
```
-### Configure
-
-1. Create build directory
-
- ```bash
- mkdir -p build
- ```
-
-2. Configure the project
-
- ```bash
- cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE="${NXDK_DIR}/share/toolchain-nxdk.cmake"
- ```
-
### Build
```bash
-cmake --build build
+cmake --build cmake-build-release
```
### Combined script
@@ -131,12 +113,12 @@ This script takes care of everything, except installing the prerequisites.
./build.sh
```
-The default build directory is `build`. You can override it or force a clean build:
+The default build directory is `cmake-build-release`. You can override it or force a clean build:
```bash
-./build.sh --build-dir cmake-build-nxdk-release
+./build.sh --build-dir cmake-build-debug
./build.sh --clean
-./build.sh build clean
+./build.sh cmake-build-custom clean
```
To launch the same build from shells outside MSYS2 on Windows, use one of these wrappers:
@@ -151,28 +133,28 @@ build-mingw64.bat
### Host-native unit tests
-The Xbox executable cannot run directly on Windows, macOS, or Linux, so unit tests are built as a separate host-native target. Keep Xbox runtime code thin and move logic you want to test into platform-neutral sources that can be linked into `test_moonlight`.
+The Xbox executable cannot run directly on Windows, macOS, or Linux, so the top-level project builds `test_moonlight` natively while the Xbox binary is built by an internal child configure that uses the `nxdk` toolchain. Keep Xbox runtime code thin and move logic you want to test into platform-neutral sources that can be linked into `test_moonlight`.
#### Windows via MSYS2/mingw64
-From `cmd.exe`, configure, build, and run the host tests with:
+From `cmd.exe`, configure, build, and run the host tests after the helper locates your local MSYS2 installation:
```bat
-C:\msys64\msys2_shell.cmd -defterm -here -no-start -mingw64 -c "cd /c/Users/%USERNAME%/Dev/git/Moonlight-XboxOG && cmake --preset \"host-tests (mingw64)\" && cmake --build --preset \"host-tests (mingw64)\" && ctest --preset \"host-tests (mingw64)\""
+call scripts\find-msys2.cmd && "%MOONLIGHT_MSYS2_SHELL%" -defterm -here -no-start -mingw64 -c "cd /c/Users/%USERNAME%/Dev/git/Moonlight-XboxOG && cmake -S . -B cmake-build-host-tests -DBUILD_DOCS=OFF -DBUILD_TESTS=ON -DBUILD_XBOX=OFF -DCMAKE_BUILD_TYPE=Debug && cmake --build cmake-build-host-tests --target test_moonlight && ctest --test-dir cmake-build-host-tests --output-on-failure"
```
If you are already inside a `mingw64` shell, the equivalent commands are:
```bash
-cmake --preset "host-tests (mingw64)"
-cmake --build --preset "host-tests (mingw64)"
-ctest --preset "host-tests (mingw64)"
+cmake -S . -B cmake-build-host-tests -DBUILD_DOCS=OFF -DBUILD_TESTS=ON -DBUILD_XBOX=OFF -DCMAKE_BUILD_TYPE=Debug
+cmake --build cmake-build-host-tests --target test_moonlight
+ctest --test-dir cmake-build-host-tests --output-on-failure
```
#### Linux or macOS
```bash
-cmake -S . -B cmake-build-host-tests -DBUILD_TESTS=ON -DBUILD_DOCS=OFF
+cmake -S . -B cmake-build-host-tests -DBUILD_TESTS=ON -DBUILD_XBOX=OFF -DBUILD_DOCS=OFF
cmake --build cmake-build-host-tests --target test_moonlight
ctest --test-dir cmake-build-host-tests --output-on-failure
```
@@ -181,12 +163,12 @@ Coverage should come from this host-native test build instead of the cross-compi
### CLion on Windows
-The repository now includes CLion-friendly nxdk wrapper scripts in `cmake/` plus shared run configurations in `.run/`.
+The Windows preset in `CMakePresets.json` uses `MinGW Makefiles` for the host-native CLion build, auto-detects the local MSYS2 installation through `cmake/host-mingw64-clang.cmake`, and delegates the Xbox child build through a dedicated CMake driver that enters the vendored `nxdk` environment only for the child configure and build steps.
-1. Open the project in CLion and let it import the `nxdk` preset from `CMakePresets.json`.
-2. If CLion cached an older failed configure, reload the CMake project or remove `cmake-build-nxdk-release/CMakeCache.txt` once.
-3. Use the normal build button with the `nxdk` profile selected.
-4. Build `Moonlight.iso` first, then use the shared `Run Moonlight ISO in xemu` run configuration to launch it in xemu.
+1. Open the project in CLion and import the `nxdk-release (mingw64)` preset from `CMakePresets.json`.
+2. Use the normal build button with that profile selected. The top-level build will compile `test_moonlight` natively and configure the Xbox child build automatically.
+3. When you use CLion's **Reset Cache and Reload Project**, the next configure will clean the `nxdk` build outputs and rebuild the required `nxdk` libraries and tools.
+4. Build the `moonlight_xbox` target or the default `all` target. The generated ISO now lives at `cmake-build-release/xbox/Moonlight.iso`.
For the first xemu launch, you can either run the shared `Setup portable xemu` configuration or run the Windows wrapper manually:
@@ -194,9 +176,11 @@ For the first xemu launch, you can either run the shared `Setup portable xemu` c
scripts\setup-xemu.cmd
```
-The shared CLion run configurations now call `scripts\setup-xemu.cmd` and `scripts\run-xemu.cmd` directly through `C:\Windows\System32\cmd.exe`. Those wrappers start MSYS2 with the expected `mingw64` environment and then launch the corresponding `.sh` scripts.
+The repository now includes `.run/Run xemu.run.xml`, which launches `scripts\run-xemu.cmd` through `C:\Windows\System32\cmd.exe` without extra arguments and lets the launcher auto-discover a built Moonlight ISO.
+
+If you create a local CLion run configuration that sets the working directory to a build output such as `$CMakeCurrentBuildDir$/xbox`, the Windows wrapper also treats that caller working directory as the xemu target path when no explicit launcher arguments or `MOONLIGHT_XEMU_*` overrides are provided.
-The setup script downloads xemu and the emulator support files into `.local/xemu`, then refreshes launcher manifests used by `scripts/run-xemu.sh`.
+The setup script downloads xemu and the emulator support files into `.local/xemu`, then refreshes launcher manifests used by `scripts/run-xemu.sh`. The launcher accepts `MOONLIGHT_XEMU_BUILD_DIR`, `MOONLIGHT_XEMU_ISO_PATH`, `--build-dir `, `--iso `, or a single positional path that can point at either a build directory or an ISO file. If you do not pass a path, it falls back across available `cmake-build-*` outputs and prefers the newest built ISO.
If you only want the emulator without the ROM/HDD support bundle, run:
diff --git a/build-mingw64.bat b/build-mingw64.bat
index 3f7c43b..96120b5 100644
--- a/build-mingw64.bat
+++ b/build-mingw64.bat
@@ -2,15 +2,13 @@
setlocal
set "SCRIPT_DIR=%~dp0"
-set "MSYS2_SHELL=C:\msys64\msys2_shell.cmd"
-
-if not exist "%MSYS2_SHELL%" (
- echo MSYS2 shell not found at %MSYS2_SHELL%
+call "%SCRIPT_DIR%scripts\find-msys2.cmd"
+if errorlevel 1 (
exit /b 1
)
pushd "%SCRIPT_DIR%" >nul
-call "%MSYS2_SHELL%" -defterm -here -no-start -mingw64 -c "./build.sh %*"
+call "%MOONLIGHT_MSYS2_SHELL%" -defterm -here -no-start -mingw64 -c "./build.sh %*"
set "EXIT_CODE=%ERRORLEVEL%"
popd >nul
diff --git a/build-mingw64.sh b/build-mingw64.sh
index 5213835..b7aec73 100644
--- a/build-mingw64.sh
+++ b/build-mingw64.sh
@@ -3,7 +3,8 @@
set -eu
SCRIPT_DIR="$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd)"
-MSYS2_SHELL="/c/msys64/msys2_shell.cmd"
+MSYS2_ROOT="$(sh "${SCRIPT_DIR}/scripts/find-msys2.sh")"
+MSYS2_SHELL="${MSYS2_ROOT}/msys2_shell.cmd"
if [ ! -f "${MSYS2_SHELL}" ]; then
echo "MSYS2 shell not found at ${MSYS2_SHELL}"
diff --git a/build.sh b/build.sh
index 490437a..9331d26 100644
--- a/build.sh
+++ b/build.sh
@@ -9,9 +9,8 @@ usage() {
}
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
-BUILD_DIR="build"
+BUILD_DIR="cmake-build-release"
CLEAN_BUILD=0
-DISTCLEAN_BUILD=0
POSITIONAL_BUILD_DIR_SET=0
while [[ "$#" -gt 0 ]]; do
@@ -30,7 +29,6 @@ while [[ "$#" -gt 0 ]]; do
shift
;;
--distclean|distclean)
- DISTCLEAN_BUILD=1
CLEAN_BUILD=1
shift
;;
@@ -61,43 +59,17 @@ case "${BUILD_DIR}" in
;;
esac
-# Set the NXDK_DIR environment variable
-export NXDK_DIR="${NXDK_DIR:-${PROJECT_ROOT}/third-party/nxdk}"
-
-if [[ ! -d "${NXDK_DIR}" ]]; then
- echo "NXDK directory not found: ${NXDK_DIR}"
- echo "Run: git submodule update --init --recursive"
- exit 1
-fi
-
-# Activate the nxdk environment
-eval "$("${NXDK_DIR}/bin/activate" -s)"
-
-# Navigate to the nxdk directory
-cd "${NXDK_DIR}"
-
-if [[ "${DISTCLEAN_BUILD}" -eq 1 ]]; then
- make distclean
-fi
-
-# Build nxdk with the specified options
-make NXDK_ONLY=y
-make tools
-
-cd "${PROJECT_ROOT}"
-
if [[ "${CLEAN_BUILD}" -eq 1 ]]; then
rm -rf "${BUILD_DIR_PATH}"
fi
-# Configure the project
cmake \
- -G "Unix Makefiles" \
- -B "${BUILD_DIR_PATH}" \
-S . \
+ -B "${BUILD_DIR_PATH}" \
-DBUILD_DOCS=OFF \
- -DCMAKE_TOOLCHAIN_FILE="${NXDK_DIR}/share/toolchain-nxdk.cmake" \
- -DCMAKE_DEPENDS_USE_COMPILER=FALSE
+ -DBUILD_TESTS=ON \
+ -DBUILD_XBOX=ON \
+ -DCMAKE_DEPENDS_USE_COMPILER=FALSE \
+ -DCMAKE_BUILD_TYPE=Release
-# Build the project
cmake --build "${BUILD_DIR_PATH}"
diff --git a/cmake/host-mingw64-clang.cmake b/cmake/host-mingw64-clang.cmake
new file mode 100644
index 0000000..fe05b3f
--- /dev/null
+++ b/cmake/host-mingw64-clang.cmake
@@ -0,0 +1,26 @@
+# Detect the local MSYS2 installation and configure the host-native Windows build
+# to use the mingw64 clang toolchain and MinGW Makefiles.
+
+if(NOT WIN32)
+ return()
+endif()
+
+include("${CMAKE_CURRENT_LIST_DIR}/msys2.cmake")
+
+moonlight_detect_windows_msys2_root(_msys2_root)
+moonlight_get_windows_msys2_msystem_bin(_msys2_mingw_bin mingw64)
+moonlight_get_windows_msys2_usr_bin(_msys2_usr_bin)
+moonlight_prepend_windows_msys2_path(_msys2_path mingw64)
+
+set(ENV{MSYSTEM} "MINGW64")
+set(ENV{PATH} "${_msys2_path}")
+
+find_program(_clang_executable NAMES clang.exe HINTS "${_msys2_mingw_bin}" NO_DEFAULT_PATH REQUIRED)
+find_program(_clangxx_executable NAMES clang++.exe HINTS "${_msys2_mingw_bin}" NO_DEFAULT_PATH REQUIRED)
+find_program(_mingw_make_executable NAMES mingw32-make.exe HINTS "${_msys2_mingw_bin}" NO_DEFAULT_PATH REQUIRED)
+
+set(MOONLIGHT_HOST_TOOL_DIR "${_msys2_mingw_bin}" CACHE PATH "Path to the detected host-native mingw64 tool directory" FORCE)
+set(MOONLIGHT_MSYS2_ROOT "${_msys2_root}" CACHE PATH "Path to the detected MSYS2 installation" FORCE)
+set(CMAKE_C_COMPILER "${_clang_executable}" CACHE FILEPATH "Host C compiler for the Windows CLion build" FORCE)
+set(CMAKE_CXX_COMPILER "${_clangxx_executable}" CACHE FILEPATH "Host C++ compiler for the Windows CLion build" FORCE)
+set(CMAKE_MAKE_PROGRAM "${_mingw_make_executable}" CACHE FILEPATH "Make program for the Windows CLion build" FORCE)
diff --git a/cmake/make.bat b/cmake/make.bat
deleted file mode 100644
index 6950055..0000000
--- a/cmake/make.bat
+++ /dev/null
@@ -1,22 +0,0 @@
-@echo off
-setlocal EnableExtensions
-
-set "MSYS2_SHELL=C:\msys64\msys2_shell.cmd"
-
-if not exist "%MSYS2_SHELL%" (
- echo MSYS2 shell not found at %MSYS2_SHELL%>&2
- exit /b 1
-)
-
-set "MAKE_ARGS="
-:collect_args
-if "%~1"=="" goto run_make
-set "ARG=%~1"
-set "ARG=%ARG:'='\''%"
-set "MAKE_ARGS=%MAKE_ARGS% '%ARG%'"
-shift
-goto collect_args
-
-:run_make
-call "%MSYS2_SHELL%" -defterm -here -no-start -mingw64 -c "make%MAKE_ARGS%"
-exit /b %ERRORLEVEL%
diff --git a/cmake/msys2.cmake b/cmake/msys2.cmake
new file mode 100644
index 0000000..451d2df
--- /dev/null
+++ b/cmake/msys2.cmake
@@ -0,0 +1,174 @@
+# Detect the Windows MSYS2 installation and expose the paths needed by host and nxdk builds.
+
+include_guard(GLOBAL)
+
+function(_moonlight_normalize_windows_path out_var input_path)
+ if("${input_path}" STREQUAL "")
+ set(${out_var} "" PARENT_SCOPE)
+ return()
+ endif()
+
+ file(TO_CMAKE_PATH "${input_path}" _normalized_path)
+ string(REGEX REPLACE "/$" "" _normalized_path "${_normalized_path}")
+ set(${out_var} "${_normalized_path}" PARENT_SCOPE)
+endfunction()
+
+function(_moonlight_set_msys2_root_if_valid out_var candidate_root)
+ _moonlight_normalize_windows_path(_normalized_root "${candidate_root}")
+
+ if(NOT _normalized_root STREQUAL "" AND EXISTS "${_normalized_root}/msys2_shell.cmd")
+ set(${out_var} "${_normalized_root}" PARENT_SCOPE)
+ else()
+ set(${out_var} "" PARENT_SCOPE)
+ endif()
+endfunction()
+
+function(_moonlight_try_msys2_root_from_tool out_var tool_path)
+ if("${tool_path}" STREQUAL "")
+ set(${out_var} "" PARENT_SCOPE)
+ return()
+ endif()
+
+ _moonlight_normalize_windows_path(_tool_path "${tool_path}")
+ get_filename_component(_tool_dir "${_tool_path}" DIRECTORY)
+ get_filename_component(_tool_name "${_tool_path}" NAME)
+
+ set(_candidate_roots)
+ if(_tool_name STREQUAL "msys2_shell.cmd")
+ list(APPEND _candidate_roots "${_tool_dir}")
+ endif()
+
+ get_filename_component(_one_up "${_tool_dir}/.." ABSOLUTE)
+ get_filename_component(_two_up "${_tool_dir}/../.." ABSOLUTE)
+ list(APPEND _candidate_roots "${_one_up}" "${_two_up}")
+
+ foreach(_candidate_root IN LISTS _candidate_roots)
+ _moonlight_set_msys2_root_if_valid(_resolved_root "${_candidate_root}")
+ if(NOT _resolved_root STREQUAL "")
+ set(${out_var} "${_resolved_root}" PARENT_SCOPE)
+ return()
+ endif()
+ endforeach()
+
+ set(${out_var} "" PARENT_SCOPE)
+endfunction()
+
+function(moonlight_detect_windows_msys2_root out_var)
+ if(NOT WIN32)
+ message(FATAL_ERROR "moonlight_detect_windows_msys2_root is only available on Windows hosts")
+ endif()
+
+ set(_candidate_roots)
+ if(DEFINED MOONLIGHT_MSYS2_ROOT AND NOT MOONLIGHT_MSYS2_ROOT STREQUAL "")
+ list(APPEND _candidate_roots "${MOONLIGHT_MSYS2_ROOT}")
+ endif()
+ if(DEFINED ENV{MSYS2_ROOT} AND NOT "$ENV{MSYS2_ROOT}" STREQUAL "")
+ list(APPEND _candidate_roots "$ENV{MSYS2_ROOT}")
+ endif()
+ if(DEFINED ENV{SystemDrive} AND NOT "$ENV{SystemDrive}" STREQUAL "")
+ list(APPEND _candidate_roots "$ENV{SystemDrive}/msys64")
+ endif()
+ list(APPEND _candidate_roots
+ "C:/msys64"
+ "C:/tools/msys64"
+ )
+
+ foreach(_candidate_root IN LISTS _candidate_roots)
+ _moonlight_set_msys2_root_if_valid(_resolved_root "${_candidate_root}")
+ if(NOT _resolved_root STREQUAL "")
+ set(MOONLIGHT_MSYS2_ROOT "${_resolved_root}" CACHE PATH "Path to the detected MSYS2 installation" FORCE)
+ set(${out_var} "${_resolved_root}" PARENT_SCOPE)
+ return()
+ endif()
+ endforeach()
+
+ set(_program_hints ${_candidate_roots})
+
+ find_program(_msys2_shell_path
+ NAMES msys2_shell.cmd
+ HINTS ${_program_hints}
+ PATH_SUFFIXES .
+ )
+ _moonlight_try_msys2_root_from_tool(_resolved_root "${_msys2_shell_path}")
+ if(NOT _resolved_root STREQUAL "")
+ set(MOONLIGHT_MSYS2_ROOT "${_resolved_root}" CACHE PATH "Path to the detected MSYS2 installation" FORCE)
+ set(${out_var} "${_resolved_root}" PARENT_SCOPE)
+ return()
+ endif()
+
+ foreach(_tool_name bash.exe mingw32-make.exe clang++.exe clang.exe)
+ find_program(_tool_path
+ NAMES ${_tool_name}
+ HINTS ${_program_hints}
+ PATH_SUFFIXES usr/bin mingw64/bin ucrt64/bin clang64/bin clangarm64/bin mingw32/bin
+ )
+ _moonlight_try_msys2_root_from_tool(_resolved_root "${_tool_path}")
+ if(NOT _resolved_root STREQUAL "")
+ set(MOONLIGHT_MSYS2_ROOT "${_resolved_root}" CACHE PATH "Path to the detected MSYS2 installation" FORCE)
+ set(${out_var} "${_resolved_root}" PARENT_SCOPE)
+ return()
+ endif()
+ endforeach()
+
+ message(FATAL_ERROR
+ "Could not find an MSYS2 installation. Set the MSYS2_ROOT environment variable or add the MSYS2 tools to PATH.")
+endfunction()
+
+function(moonlight_get_windows_msys2_shell out_var)
+ moonlight_detect_windows_msys2_root(_msys2_root)
+ set(_msys2_shell "${_msys2_root}/msys2_shell.cmd")
+ set(${out_var} "${_msys2_shell}" PARENT_SCOPE)
+endfunction()
+
+function(moonlight_get_windows_msys2_usr_bin out_var)
+ moonlight_detect_windows_msys2_root(_msys2_root)
+ set(_msys2_usr_bin "${_msys2_root}/usr/bin")
+ set(${out_var} "${_msys2_usr_bin}" PARENT_SCOPE)
+endfunction()
+
+function(moonlight_get_windows_msys2_bash out_var)
+ moonlight_get_windows_msys2_usr_bin(_msys2_usr_bin)
+ set(_bash_executable "${_msys2_usr_bin}/bash.exe")
+
+ if(NOT EXISTS "${_bash_executable}")
+ message(FATAL_ERROR "MSYS2 bash not found at ${_bash_executable}")
+ endif()
+
+ set(${out_var} "${_bash_executable}" PARENT_SCOPE)
+endfunction()
+
+function(moonlight_get_windows_msys2_msystem_bin out_var)
+ set(_msystem_dir "mingw64")
+ if(ARGC GREATER 1 AND NOT "${ARGV1}" STREQUAL "")
+ set(_msystem_dir "${ARGV1}")
+ endif()
+
+ moonlight_detect_windows_msys2_root(_msys2_root)
+ set(_msystem_bin "${_msys2_root}/${_msystem_dir}/bin")
+
+ if(NOT EXISTS "${_msystem_bin}")
+ message(FATAL_ERROR "MSYS2 ${_msystem_dir} bin directory not found at ${_msystem_bin}")
+ endif()
+
+ set(${out_var} "${_msystem_bin}" PARENT_SCOPE)
+endfunction()
+
+function(moonlight_prepend_windows_msys2_path out_var)
+ set(_msystem_dir "mingw64")
+ if(ARGC GREATER 1 AND NOT "${ARGV1}" STREQUAL "")
+ set(_msystem_dir "${ARGV1}")
+ endif()
+
+ moonlight_get_windows_msys2_msystem_bin(_msystem_bin "${_msystem_dir}")
+ moonlight_get_windows_msys2_usr_bin(_msys2_usr_bin)
+
+ set(_path_entries "${_msystem_bin}" "${_msys2_usr_bin}")
+ file(TO_CMAKE_PATH "$ENV{PATH}" _existing_path)
+ if(NOT _existing_path STREQUAL "")
+ list(APPEND _path_entries ${_existing_path})
+ endif()
+
+ list(REMOVE_DUPLICATES _path_entries)
+ list(JOIN _path_entries ";" _resolved_path)
+ set(${out_var} "${_resolved_path}" PARENT_SCOPE)
+endfunction()
diff --git a/cmake/nxdk-cc.bat b/cmake/nxdk-cc.bat
deleted file mode 100644
index b84b968..0000000
--- a/cmake/nxdk-cc.bat
+++ /dev/null
@@ -1,21 +0,0 @@
-@echo off
-setlocal
-
-set "SCRIPT_DIR=%~dp0"
-set "PROJECT_ROOT=%SCRIPT_DIR%.."
-set "NXDK_DIR=%PROJECT_ROOT%\third-party\nxdk"
-set "BASH_EXE=C:\msys64\usr\bin\bash.exe"
-set "HELPER_SCRIPT=%SCRIPT_DIR%run-nxdk-tool.sh"
-
-if not exist "%BASH_EXE%" (
- echo MSYS2 bash not found at %BASH_EXE%>&2
- exit /b 1
-)
-
-if not exist "%NXDK_DIR%\bin\nxdk-cc" (
- echo nxdk compiler wrapper not found at %NXDK_DIR%\bin\nxdk-cc>&2
- exit /b 1
-)
-
-"%BASH_EXE%" "%HELPER_SCRIPT%" "%NXDK_DIR%" nxdk-cc %*
-exit /b %ERRORLEVEL%
diff --git a/cmake/nxdk-cxx.bat b/cmake/nxdk-cxx.bat
deleted file mode 100644
index a9c2219..0000000
--- a/cmake/nxdk-cxx.bat
+++ /dev/null
@@ -1,21 +0,0 @@
-@echo off
-setlocal
-
-set "SCRIPT_DIR=%~dp0"
-set "PROJECT_ROOT=%SCRIPT_DIR%.."
-set "NXDK_DIR=%PROJECT_ROOT%\third-party\nxdk"
-set "BASH_EXE=C:\msys64\usr\bin\bash.exe"
-set "HELPER_SCRIPT=%SCRIPT_DIR%run-nxdk-tool.sh"
-
-if not exist "%BASH_EXE%" (
- echo MSYS2 bash not found at %BASH_EXE%>&2
- exit /b 1
-)
-
-if not exist "%NXDK_DIR%\bin\nxdk-cxx" (
- echo nxdk compiler wrapper not found at %NXDK_DIR%\bin\nxdk-cxx>&2
- exit /b 1
-)
-
-"%BASH_EXE%" "%HELPER_SCRIPT%" "%NXDK_DIR%" nxdk-cxx %*
-exit /b %ERRORLEVEL%
diff --git a/cmake/nxdk-link.bat b/cmake/nxdk-link.bat
deleted file mode 100644
index 3ff5970..0000000
--- a/cmake/nxdk-link.bat
+++ /dev/null
@@ -1,21 +0,0 @@
-@echo off
-setlocal
-
-set "SCRIPT_DIR=%~dp0"
-set "PROJECT_ROOT=%SCRIPT_DIR%.."
-set "NXDK_DIR=%PROJECT_ROOT%\third-party\nxdk"
-set "BASH_EXE=C:\msys64\usr\bin\bash.exe"
-set "HELPER_SCRIPT=%SCRIPT_DIR%run-nxdk-tool.sh"
-
-if not exist "%BASH_EXE%" (
- echo MSYS2 bash not found at %BASH_EXE%>&2
- exit /b 1
-)
-
-if not exist "%NXDK_DIR%\bin\nxdk-link" (
- echo nxdk linker wrapper not found at %NXDK_DIR%\bin\nxdk-link>&2
- exit /b 1
-)
-
-"%BASH_EXE%" "%HELPER_SCRIPT%" "%NXDK_DIR%" nxdk-link %*
-exit /b %ERRORLEVEL%
diff --git a/cmake/nxdk-pkg-config.bat b/cmake/nxdk-pkg-config.bat
deleted file mode 100644
index 807495b..0000000
--- a/cmake/nxdk-pkg-config.bat
+++ /dev/null
@@ -1,21 +0,0 @@
-@echo off
-setlocal
-
-set "SCRIPT_DIR=%~dp0"
-set "PROJECT_ROOT=%SCRIPT_DIR%.."
-set "NXDK_DIR=%PROJECT_ROOT%\third-party\nxdk"
-set "BASH_EXE=C:\msys64\usr\bin\bash.exe"
-set "HELPER_SCRIPT=%SCRIPT_DIR%run-nxdk-tool.sh"
-
-if not exist "%BASH_EXE%" (
- echo MSYS2 bash not found at %BASH_EXE%>&2
- exit /b 1
-)
-
-if not exist "%NXDK_DIR%\bin\nxdk-pkg-config" (
- echo nxdk pkg-config wrapper not found at %NXDK_DIR%\bin\nxdk-pkg-config>&2
- exit /b 1
-)
-
-"%BASH_EXE%" "%HELPER_SCRIPT%" "%NXDK_DIR%" nxdk-pkg-config %*
-exit /b %ERRORLEVEL%
diff --git a/cmake/nxdk.cmake b/cmake/nxdk.cmake
new file mode 100644
index 0000000..328c5f2
--- /dev/null
+++ b/cmake/nxdk.cmake
@@ -0,0 +1,311 @@
+# Resolve the vendored nxdk checkout, bootstrap its prerequisites, and drive the internal Xbox child build.
+
+include_guard(GLOBAL)
+
+include(CMakeParseArguments)
+include("${CMAKE_CURRENT_LIST_DIR}/msys2.cmake")
+
+set(MOONLIGHT_NXDK_DIR "${CMAKE_SOURCE_DIR}/third-party/nxdk" CACHE PATH "Path to the vendored nxdk checkout")
+cmake_path(ABSOLUTE_PATH MOONLIGHT_NXDK_DIR BASE_DIRECTORY "${CMAKE_SOURCE_DIR}" NORMALIZE)
+
+function(moonlight_resolve_nxdk_dir out_var)
+ set(_nxdk_dir "${MOONLIGHT_NXDK_DIR}")
+ cmake_path(ABSOLUTE_PATH _nxdk_dir BASE_DIRECTORY "${CMAKE_SOURCE_DIR}" NORMALIZE)
+
+ if(NOT EXISTS "${_nxdk_dir}/bin/activate")
+ message(FATAL_ERROR
+ "NXDK directory not found: ${_nxdk_dir}\n"
+ "Run: git submodule update --init --recursive")
+ endif()
+
+ set(${out_var} "${_nxdk_dir}" PARENT_SCOPE)
+endfunction()
+
+function(_moonlight_get_windows_msys2_shell out_var)
+ moonlight_get_windows_msys2_shell(_msys2_shell)
+ set(${out_var} "${_msys2_shell}" PARENT_SCOPE)
+endfunction()
+
+function(_moonlight_get_xbox_make_program out_var)
+ if(WIN32)
+ moonlight_get_windows_msys2_usr_bin(_msys2_usr_bin)
+ set(_make_program "${_msys2_usr_bin}/make.exe")
+ if(NOT EXISTS "${_make_program}")
+ message(FATAL_ERROR "MSYS2 make not found at ${_make_program}")
+ endif()
+ else()
+ find_program(_make_program NAMES make REQUIRED)
+ endif()
+
+ set(${out_var} "${_make_program}" PARENT_SCOPE)
+endfunction()
+
+function(_moonlight_to_msys_path out_var path)
+ file(TO_CMAKE_PATH "${path}" _normalized_path)
+
+ if(_normalized_path MATCHES "^([A-Za-z]):/(.*)$")
+ string(TOLOWER "${CMAKE_MATCH_1}" _drive)
+ set(_normalized_path "/${_drive}/${CMAKE_MATCH_2}")
+ endif()
+
+ set(${out_var} "${_normalized_path}" PARENT_SCOPE)
+endfunction()
+
+function(_moonlight_shell_quote out_var value)
+ string(REPLACE "'" "'\"'\"'" _escaped_value "${value}")
+ set(${out_var} "'${_escaped_value}'" PARENT_SCOPE)
+endfunction()
+
+function(_moonlight_join_shell_command out_var)
+ set(_quoted_args)
+
+ foreach(_arg IN LISTS ARGN)
+ _moonlight_shell_quote(_quoted_arg "${_arg}")
+ list(APPEND _quoted_args "${_quoted_arg}")
+ endforeach()
+
+ list(JOIN _quoted_args " " _command)
+ set(${out_var} "${_command}" PARENT_SCOPE)
+endfunction()
+
+function(_moonlight_get_nxdk_path out_var nxdk_dir)
+ set(_path_entries "${nxdk_dir}/bin")
+
+ if(APPLE)
+ foreach(_llvm_path "/opt/homebrew/opt/llvm/bin" "/usr/local/opt/llvm/bin")
+ if(EXISTS "${_llvm_path}/clang")
+ list(APPEND _path_entries "${_llvm_path}")
+ endif()
+ endforeach()
+ endif()
+
+ if(WIN32)
+ moonlight_get_windows_msys2_msystem_bin(_msys2_mingw_bin mingw64)
+ moonlight_get_windows_msys2_usr_bin(_msys2_usr_bin)
+ list(APPEND _path_entries "${_msys2_mingw_bin}" "${_msys2_usr_bin}")
+ file(TO_CMAKE_PATH "$ENV{PATH}" _existing_path)
+ if(NOT _existing_path STREQUAL "")
+ list(APPEND _path_entries ${_existing_path})
+ endif()
+ set(_path_separator ";")
+ else()
+ if(NOT "$ENV{PATH}" STREQUAL "")
+ string(REPLACE ":" ";" _existing_path "$ENV{PATH}")
+ list(APPEND _path_entries ${_existing_path})
+ endif()
+ set(_path_separator ":")
+ endif()
+
+ list(JOIN _path_entries "${_path_separator}" _path)
+ set(${out_var} "${_path}" PARENT_SCOPE)
+endfunction()
+
+function(moonlight_run_nxdk_command description nxdk_dir working_directory)
+ if(WIN32)
+ _moonlight_get_windows_msys2_shell(_msys2_shell)
+ _moonlight_to_msys_path(_msys_nxdk_dir "${nxdk_dir}")
+ _moonlight_to_msys_path(_msys_working_directory "${working_directory}")
+ _moonlight_join_shell_command(_shell_command ${ARGN})
+ _moonlight_shell_quote(_quoted_nxdk_dir "${_msys_nxdk_dir}")
+ _moonlight_shell_quote(_quoted_working_directory "${_msys_working_directory}")
+
+ set(_shell_script
+ "unset MAKEFLAGS MFLAGS GNUMAKEFLAGS MAKELEVEL; export NXDK_DIR=${_quoted_nxdk_dir}; export PATH=\"$NXDK_DIR/bin:$PATH\"; cd ${_quoted_working_directory}; exec ${_shell_command}")
+
+ execute_process(
+ COMMAND "${_msys2_shell}" -defterm -here -no-start -mingw64 -c "${_shell_script}"
+ RESULT_VARIABLE _moonlight_command_result
+ )
+ else()
+ _moonlight_get_nxdk_path(_moonlight_nxdk_path "${nxdk_dir}")
+
+ execute_process(
+ COMMAND "${CMAKE_COMMAND}" -E env
+ "NXDK_DIR=${nxdk_dir}"
+ "PATH=${_moonlight_nxdk_path}"
+ ${ARGN}
+ WORKING_DIRECTORY "${working_directory}"
+ RESULT_VARIABLE _moonlight_command_result
+ )
+ endif()
+
+ if(NOT _moonlight_command_result EQUAL 0)
+ message(FATAL_ERROR "${description} failed with exit code ${_moonlight_command_result}")
+ endif()
+endfunction()
+
+function(_moonlight_run_nxdk_make nxdk_dir description)
+ if(WIN32)
+ set(_make_program make)
+ else()
+ _moonlight_get_xbox_make_program(_make_program)
+ endif()
+
+ moonlight_run_nxdk_command("${description}" "${nxdk_dir}" "${nxdk_dir}" "${_make_program}" ${ARGN})
+endfunction()
+
+function(moonlight_prepare_nxdk nxdk_dir state_dir)
+ file(MAKE_DIRECTORY "${state_dir}")
+
+ set(_signature_inputs
+ "NXDK_DIR=${nxdk_dir}"
+ "HOST_SYSTEM=${CMAKE_HOST_SYSTEM_NAME}"
+ "NXDK_TOOLCHAIN=${nxdk_dir}/share/toolchain-nxdk.cmake"
+ "NXDK_ENV_MODE=cmake-driver"
+ )
+ list(JOIN _signature_inputs "\n" _signature_text)
+ string(SHA256 _signature "${_signature_text}")
+
+ set(_signature_file "${state_dir}/bootstrap.signature")
+ set(_cxbe_path "${nxdk_dir}/tools/cxbe/cxbe")
+ set(_extract_xiso_source_dir "${nxdk_dir}/tools/extract-xiso")
+ set(_extract_xiso_build_dir "${_extract_xiso_source_dir}/build")
+ set(_extract_xiso_path "${_extract_xiso_build_dir}/extract-xiso")
+ set(_required_libraries
+ "${nxdk_dir}/lib/libnxdk.lib"
+ "${nxdk_dir}/lib/libc++.lib"
+ "${nxdk_dir}/lib/libSDL2.lib"
+ "${nxdk_dir}/lib/libSDL2_image.lib"
+ )
+ set(_required_tools
+ "${_cxbe_path}"
+ "${_extract_xiso_path}"
+ )
+
+ if(MOONLIGHT_FORCE_NXDK_DISTCLEAN)
+ set(_need_distclean TRUE)
+ else()
+ set(_need_distclean FALSE)
+ endif()
+ set(_need_prepare_libraries FALSE)
+ set(_need_prepare_tools FALSE)
+
+ if(NOT EXISTS "${_signature_file}")
+ set(_need_distclean TRUE)
+ else()
+ file(READ "${_signature_file}" _saved_signature)
+ string(STRIP "${_saved_signature}" _saved_signature)
+ if(NOT _saved_signature STREQUAL _signature)
+ set(_need_distclean TRUE)
+ endif()
+ endif()
+
+ foreach(_required_output IN LISTS _required_libraries)
+ if(NOT EXISTS "${_required_output}")
+ set(_need_prepare_libraries TRUE)
+ break()
+ endif()
+ endforeach()
+
+ foreach(_required_output IN LISTS _required_tools)
+ if(NOT EXISTS "${_required_output}")
+ set(_need_prepare_tools TRUE)
+ break()
+ endif()
+ endforeach()
+
+ if(_need_distclean)
+ message(STATUS "Cleaning nxdk build tree at ${nxdk_dir}")
+ _moonlight_run_nxdk_make("${nxdk_dir}" "nxdk clean" clean)
+ set(_need_prepare_libraries TRUE)
+ endif()
+
+ if(_need_prepare_libraries)
+ message(STATUS "Preparing nxdk libraries at ${nxdk_dir}")
+ _moonlight_run_nxdk_make("${nxdk_dir}" "nxdk bootstrap" NXDK_ONLY=y)
+ else()
+ message(STATUS "Using existing nxdk library outputs from ${nxdk_dir}")
+ endif()
+
+ if(_need_prepare_tools)
+ if(NOT EXISTS "${_cxbe_path}")
+ message(STATUS "Preparing cxbe at ${nxdk_dir}")
+ if(WIN32)
+ moonlight_run_nxdk_command("cxbe build" "${nxdk_dir}" "${nxdk_dir}/tools/cxbe" make)
+ else()
+ _moonlight_get_xbox_make_program(_host_make_program)
+ moonlight_run_nxdk_command("cxbe build" "${nxdk_dir}" "${nxdk_dir}/tools/cxbe" "${_host_make_program}")
+ endif()
+ endif()
+
+ if(NOT EXISTS "${_extract_xiso_path}")
+ message(STATUS "Preparing extract-xiso at ${nxdk_dir}")
+ file(REMOVE_RECURSE "${_extract_xiso_build_dir}")
+ file(MAKE_DIRECTORY "${_extract_xiso_build_dir}")
+ moonlight_run_nxdk_command(
+ "extract-xiso configure"
+ "${nxdk_dir}"
+ "${nxdk_dir}"
+ "${CMAKE_COMMAND}"
+ -S "${_extract_xiso_source_dir}"
+ -B "${_extract_xiso_build_dir}"
+ -G "Unix Makefiles"
+ )
+ moonlight_run_nxdk_command(
+ "extract-xiso build"
+ "${nxdk_dir}"
+ "${nxdk_dir}"
+ "${CMAKE_COMMAND}"
+ --build "${_extract_xiso_build_dir}"
+ )
+ endif()
+ else()
+ message(STATUS "Using existing nxdk tool outputs from ${nxdk_dir}")
+ endif()
+
+ file(WRITE "${_signature_file}" "${_signature}\n")
+endfunction()
+
+function(moonlight_add_xbox_build)
+ set(options)
+ set(oneValueArgs BINARY_DIR NXDK_DIR)
+ cmake_parse_arguments(MOONLIGHT_XBOX "${options}" "${oneValueArgs}" "" ${ARGN})
+
+ if(NOT MOONLIGHT_XBOX_BINARY_DIR)
+ message(FATAL_ERROR "moonlight_add_xbox_build requires BINARY_DIR")
+ endif()
+
+ if(NOT MOONLIGHT_XBOX_NXDK_DIR)
+ message(FATAL_ERROR "moonlight_add_xbox_build requires NXDK_DIR")
+ endif()
+
+ _moonlight_get_xbox_make_program(_xbox_make_program)
+
+ set(_toolchain_file "${MOONLIGHT_XBOX_NXDK_DIR}/share/toolchain-nxdk.cmake")
+ set(_driver_script "${CMAKE_SOURCE_DIR}/cmake/run-child-build.cmake")
+ set(_configure_command
+ "${CMAKE_COMMAND}"
+ -DMOONLIGHT_COMMAND_MODE:STRING=configure
+ -DMOONLIGHT_CMAKE_COMMAND:FILEPATH=${CMAKE_COMMAND}
+ -DMOONLIGHT_DESCRIPTION:STRING=Configure Xbox child build
+ -DMOONLIGHT_NXDK_DIR:PATH=${MOONLIGHT_XBOX_NXDK_DIR}
+ -DMOONLIGHT_SOURCE_DIR:PATH=${CMAKE_SOURCE_DIR}
+ -DMOONLIGHT_BINARY_DIR:PATH=${MOONLIGHT_XBOX_BINARY_DIR}
+ -DMOONLIGHT_WORKING_DIRECTORY:PATH=${CMAKE_SOURCE_DIR}
+ -DMOONLIGHT_MAKE_PROGRAM:FILEPATH=${_xbox_make_program}
+ -DMOONLIGHT_TOOLCHAIN_FILE:FILEPATH=${_toolchain_file}
+ -DMOONLIGHT_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}
+ -P "${_driver_script}"
+ )
+ set(_build_command
+ "${CMAKE_COMMAND}"
+ -DMOONLIGHT_COMMAND_MODE:STRING=build
+ -DMOONLIGHT_CMAKE_COMMAND:FILEPATH=${CMAKE_COMMAND}
+ -DMOONLIGHT_DESCRIPTION:STRING=Build Xbox child target
+ -DMOONLIGHT_NXDK_DIR:PATH=${MOONLIGHT_XBOX_NXDK_DIR}
+ -DMOONLIGHT_BINARY_DIR:PATH=${MOONLIGHT_XBOX_BINARY_DIR}
+ -DMOONLIGHT_WORKING_DIRECTORY:PATH=${MOONLIGHT_XBOX_BINARY_DIR}
+ -P "${_driver_script}"
+ )
+
+ add_custom_target(moonlight_xbox ALL
+ COMMAND ${_configure_command}
+ COMMAND ${_build_command}
+ BYPRODUCTS
+ "${MOONLIGHT_XBOX_BINARY_DIR}/Moonlight.iso"
+ "${MOONLIGHT_XBOX_BINARY_DIR}/xbe/default.xbe"
+ COMMENT "Configure and build the Xbox target in ${MOONLIGHT_XBOX_BINARY_DIR}"
+ USES_TERMINAL
+ VERBATIM
+ )
+endfunction()
diff --git a/cmake/run-child-build.cmake b/cmake/run-child-build.cmake
new file mode 100644
index 0000000..9df8346
--- /dev/null
+++ b/cmake/run-child-build.cmake
@@ -0,0 +1,56 @@
+# Configure or build the internal Xbox child project inside the vendored nxdk environment.
+
+foreach(_required_var
+ MOONLIGHT_COMMAND_MODE
+ MOONLIGHT_CMAKE_COMMAND
+ MOONLIGHT_DESCRIPTION
+ MOONLIGHT_NXDK_DIR
+ MOONLIGHT_BINARY_DIR
+ MOONLIGHT_WORKING_DIRECTORY)
+ if(NOT DEFINED ${_required_var} OR "${${_required_var}}" STREQUAL "")
+ message(FATAL_ERROR "${_required_var} must be defined when running run-child-build.cmake")
+ endif()
+endforeach()
+
+include("${CMAKE_CURRENT_LIST_DIR}/nxdk.cmake")
+
+if(MOONLIGHT_COMMAND_MODE STREQUAL "configure")
+ foreach(_required_var MOONLIGHT_SOURCE_DIR MOONLIGHT_MAKE_PROGRAM MOONLIGHT_TOOLCHAIN_FILE)
+ if(NOT DEFINED ${_required_var} OR "${${_required_var}}" STREQUAL "")
+ message(FATAL_ERROR "${_required_var} must be defined for configure mode")
+ endif()
+ endforeach()
+
+ set(_command
+ "${MOONLIGHT_CMAKE_COMMAND}"
+ -G "Unix Makefiles"
+ "-DCMAKE_MAKE_PROGRAM:FILEPATH=${MOONLIGHT_MAKE_PROGRAM}"
+ -S "${MOONLIGHT_SOURCE_DIR}"
+ -B "${MOONLIGHT_BINARY_DIR}"
+ -DMOONLIGHT_BUILD_KIND:STRING=XBOX
+ -DMOONLIGHT_SKIP_NXDK_PREP:BOOL=ON
+ "-DNXDK_DIR:PATH=${MOONLIGHT_NXDK_DIR}"
+ -DBUILD_DOCS:BOOL=OFF
+ "-DCMAKE_TOOLCHAIN_FILE:FILEPATH=${MOONLIGHT_TOOLCHAIN_FILE}"
+ -DCMAKE_DEPENDS_USE_COMPILER:BOOL=FALSE
+ -DCMAKE_TRY_COMPILE_TARGET_TYPE:STRING=STATIC_LIBRARY
+ )
+
+ if(DEFINED MOONLIGHT_BUILD_TYPE AND NOT MOONLIGHT_BUILD_TYPE STREQUAL "")
+ list(APPEND _command "-DCMAKE_BUILD_TYPE:STRING=${MOONLIGHT_BUILD_TYPE}")
+ endif()
+elseif(MOONLIGHT_COMMAND_MODE STREQUAL "build")
+ set(_command
+ "${MOONLIGHT_CMAKE_COMMAND}"
+ --build "${MOONLIGHT_BINARY_DIR}"
+ )
+else()
+ message(FATAL_ERROR "Unsupported MOONLIGHT_COMMAND_MODE: ${MOONLIGHT_COMMAND_MODE}")
+endif()
+
+moonlight_run_nxdk_command(
+ "${MOONLIGHT_DESCRIPTION}"
+ "${MOONLIGHT_NXDK_DIR}"
+ "${MOONLIGHT_WORKING_DIRECTORY}"
+ ${_command}
+)
diff --git a/cmake/run-nxdk-tool.sh b/cmake/run-nxdk-tool.sh
deleted file mode 100644
index 9660eb0..0000000
--- a/cmake/run-nxdk-tool.sh
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/usr/bin/env bash
-
-set -euo pipefail
-
-if [[ $# -lt 2 ]]; then
- printf 'Usage: %s [args...]\n' "$0" >&2
- exit 2
-fi
-
-export PATH="/mingw64/bin:/usr/bin:${PATH}"
-
-WRAPPER_DIR="$(cd "$(dirname "$0")" && pwd)"
-NXDK_DIR="$(cygpath -u "$1")"
-TOOL_NAME="$2"
-shift 2
-
-export MSYSTEM="MINGW64"
-export CHERE_INVOKING="1"
-export NXDK_DIR
-export PATH="${WRAPPER_DIR}:${NXDK_DIR}/bin:${PATH}"
-
-exec "${NXDK_DIR}/bin/${TOOL_NAME}" "$@"
diff --git a/cmake/sources.cmake b/cmake/sources.cmake
new file mode 100644
index 0000000..23a8380
--- /dev/null
+++ b/cmake/sources.cmake
@@ -0,0 +1,21 @@
+# Define the shared Moonlight source lists used by both the host-native test build
+# and the Xbox child build, including the sources that must stay out of host tests.
+
+include_guard(GLOBAL)
+
+set(MOONLIGHT_SOURCE_ROOT "${CMAKE_SOURCE_DIR}")
+
+file(GLOB_RECURSE MOONLIGHT_SOURCES CONFIGURE_DEPENDS
+ "${MOONLIGHT_SOURCE_ROOT}/src/*.cpp"
+)
+
+set(MOONLIGHT_TEST_EXCLUDED_SOURCES
+ "${MOONLIGHT_SOURCE_ROOT}/src/main.cpp"
+ "${MOONLIGHT_SOURCE_ROOT}/src/splash/splash_screen.cpp"
+ "${MOONLIGHT_SOURCE_ROOT}/src/startup/memory_stats.cpp"
+)
+
+set(MOONLIGHT_HOST_TESTABLE_SOURCES ${MOONLIGHT_SOURCES})
+list(REMOVE_ITEM MOONLIGHT_HOST_TESTABLE_SOURCES ${MOONLIGHT_TEST_EXCLUDED_SOURCES})
+
+set(MOONLIGHT_COMPILE_OPTIONS -Wno-builtin-macro-redefined)
diff --git a/cmake/toolchain-nxdk-clion.cmake b/cmake/toolchain-nxdk-clion.cmake
deleted file mode 100644
index a848642..0000000
--- a/cmake/toolchain-nxdk-clion.cmake
+++ /dev/null
@@ -1,7 +0,0 @@
-include("${CMAKE_CURRENT_LIST_DIR}/../third-party/nxdk/share/toolchain-nxdk.cmake")
-
-if(CMAKE_HOST_WIN32)
- set(CMAKE_C_COMPILER "${CMAKE_CURRENT_LIST_DIR}/nxdk-cc.bat" CACHE FILEPATH "" FORCE)
- set(CMAKE_CXX_COMPILER "${CMAKE_CURRENT_LIST_DIR}/nxdk-cxx.bat" CACHE FILEPATH "" FORCE)
- set(PKG_CONFIG_EXECUTABLE "${CMAKE_CURRENT_LIST_DIR}/nxdk-pkg-config.bat" CACHE STRING "Path to pkg-config" FORCE)
-endif()
diff --git a/cmake/xbox-build.cmake b/cmake/xbox-build.cmake
new file mode 100644
index 0000000..42ca951
--- /dev/null
+++ b/cmake/xbox-build.cmake
@@ -0,0 +1,98 @@
+# Build the Xbox child project with the stock nxdk toolchain, then package the
+# resulting executable into the XBE and ISO artifacts consumed by xemu and CI.
+
+include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/sources.cmake")
+include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/nxdk.cmake")
+
+set(XBE_TITLE ${CMAKE_PROJECT_NAME})
+set(XBOX_XBE_DIR "${CMAKE_CURRENT_BINARY_DIR}/xbe")
+set(XBOX_ISO_NAME "${CMAKE_PROJECT_NAME}.iso")
+set(XBOX_ISO "${CMAKE_CURRENT_BINARY_DIR}/${XBOX_ISO_NAME}")
+
+moonlight_resolve_nxdk_dir(NXDK_DIR)
+set(ENV{NXDK_DIR} "${NXDK_DIR}")
+
+if(NOT MOONLIGHT_SKIP_NXDK_PREP)
+ moonlight_prepare_nxdk("${NXDK_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/nxdk-bootstrap")
+endif()
+
+find_package(NXDK REQUIRED)
+find_package(NXDK_SDL2 REQUIRED)
+find_package(NXDK_SDL2_Image REQUIRED)
+
+set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -include:_automount_d_drive")
+
+file(MAKE_DIRECTORY "${XBOX_XBE_DIR}")
+
+add_custom_target(sync_xbe_assets ALL
+ COMMAND "${CMAKE_COMMAND}" -E copy_directory
+ "${CMAKE_CURRENT_SOURCE_DIR}/xbe"
+ "${XBOX_XBE_DIR}"
+ COMMENT "Sync XBE assets"
+)
+
+if(NOT CMAKE_BUILD_TYPE)
+ set(CMAKE_BUILD_TYPE Release)
+endif()
+set(CMAKE_CXX_FLAGS_RELEASE "-O2")
+set(CMAKE_C_FLAGS_RELEASE "-O2")
+
+include(GetOpenSSL REQUIRED)
+set(ENET_NO_INSTALL ON CACHE BOOL "Do not install libraries built for enet" FORCE)
+set(BUILD_SHARED_LIBS OFF)
+add_subdirectory("${CMAKE_SOURCE_DIR}/third-party/moonlight-common-c")
+if(TARGET moonlight-common-c AND TARGET openssl_external)
+ add_dependencies(moonlight-common-c openssl_external)
+endif()
+target_link_libraries(enet PUBLIC NXDK::NXDK NXDK::Net NXDK::ws2_32)
+target_compile_options(enet PRIVATE -Wno-unused-function -Wno-error=unused-function)
+if(TARGET moonlight-common-c)
+ target_compile_options(moonlight-common-c PRIVATE -Wno-unused-function -Wno-error=unused-function)
+ target_link_libraries(moonlight-common-c PRIVATE NXDK::ws2_32)
+endif()
+
+add_executable(${CMAKE_PROJECT_NAME}
+ ${MOONLIGHT_SOURCES}
+)
+target_include_directories(${CMAKE_PROJECT_NAME}
+ SYSTEM PRIVATE
+ "${CMAKE_CURRENT_SOURCE_DIR}"
+)
+target_link_libraries(${CMAKE_PROJECT_NAME}
+ PUBLIC
+ NXDK::NXDK
+ NXDK::NXDK_CXX
+ NXDK::SDL2
+ NXDK::SDL2_Image
+)
+target_compile_options(${CMAKE_PROJECT_NAME}
+ PRIVATE
+ ${MOONLIGHT_COMPILE_OPTIONS}
+ $<$:-std=gnu++17>
+)
+target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE XBOX NXDK)
+add_dependencies(${CMAKE_PROJECT_NAME} moonlight-common-c)
+
+if(BUILD_DOCS)
+ add_subdirectory(third-party/doxyconfig docs)
+endif()
+
+add_custom_target(cxbe_convert ALL
+ COMMAND "${NXDK_DIR}/tools/cxbe/cxbe"
+ -OUT:${XBOX_XBE_DIR}/default.xbe
+ -TITLE:${XBE_TITLE}
+ "${CMAKE_CURRENT_BINARY_DIR}/${XBE_TITLE}.exe"
+ COMMENT "CXBE conversion: EXE -> XBE"
+ VERBATIM
+)
+add_dependencies(cxbe_convert ${CMAKE_PROJECT_NAME})
+add_dependencies(cxbe_convert sync_xbe_assets)
+
+add_custom_target(xbe_iso ALL
+ COMMAND "${NXDK_DIR}/tools/extract-xiso/build/extract-xiso"
+ -c "${XBOX_XBE_DIR}" "${XBOX_ISO_NAME}"
+ COMMENT "CXBE conversion: XBE -> XISO"
+ WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
+ VERBATIM
+)
+add_dependencies(xbe_iso cxbe_convert)
diff --git a/scripts/find-msys2.cmd b/scripts/find-msys2.cmd
new file mode 100644
index 0000000..08881e6
--- /dev/null
+++ b/scripts/find-msys2.cmd
@@ -0,0 +1,71 @@
+@echo off
+setlocal
+
+set "MOONLIGHT_MSYS2_ROOT="
+
+call :try_candidate "%MSYS2_ROOT%"
+if defined MOONLIGHT_MSYS2_ROOT goto found
+
+if defined SystemDrive call :try_candidate "%SystemDrive%\msys64"
+if defined MOONLIGHT_MSYS2_ROOT goto found
+
+for %%D in ("C:\msys64" "C:\tools\msys64") do (
+ call :try_candidate "%%~fD"
+ if defined MOONLIGHT_MSYS2_ROOT goto found
+)
+
+for %%T in (msys2_shell.cmd bash.exe mingw32-make.exe clang++.exe clang.exe) do (
+ for /f "delims=" %%I in ('where %%T 2^>nul') do (
+ call :try_from_tool "%%~fI"
+ if defined MOONLIGHT_MSYS2_ROOT goto found
+ )
+)
+
+echo MSYS2 installation not found. Set MSYS2_ROOT or add MSYS2 tools to PATH.>&2
+exit /b 1
+
+:found
+set "MOONLIGHT_MSYS2_SHELL=%MOONLIGHT_MSYS2_ROOT%\msys2_shell.cmd"
+set "MOONLIGHT_MSYS2_USR_BIN=%MOONLIGHT_MSYS2_ROOT%\usr\bin"
+set "MOONLIGHT_MSYS2_MINGW64_BIN=%MOONLIGHT_MSYS2_ROOT%\mingw64\bin"
+set "MOONLIGHT_MSYS2_BASH=%MOONLIGHT_MSYS2_USR_BIN%\bash.exe"
+
+if not exist "%MOONLIGHT_MSYS2_SHELL%" (
+ echo MSYS2 shell not found at %MOONLIGHT_MSYS2_SHELL%>&2
+ exit /b 1
+)
+if not exist "%MOONLIGHT_MSYS2_BASH%" (
+ echo MSYS2 bash not found at %MOONLIGHT_MSYS2_BASH%>&2
+ exit /b 1
+)
+
+endlocal & (
+ set "MOONLIGHT_MSYS2_ROOT=%MOONLIGHT_MSYS2_ROOT%"
+ set "MOONLIGHT_MSYS2_SHELL=%MOONLIGHT_MSYS2_SHELL%"
+ set "MOONLIGHT_MSYS2_USR_BIN=%MOONLIGHT_MSYS2_USR_BIN%"
+ set "MOONLIGHT_MSYS2_MINGW64_BIN=%MOONLIGHT_MSYS2_MINGW64_BIN%"
+ set "MOONLIGHT_MSYS2_BASH=%MOONLIGHT_MSYS2_BASH%"
+)
+exit /b 0
+
+:try_candidate
+set "CANDIDATE=%~1"
+if not defined CANDIDATE exit /b 0
+for %%R in ("%CANDIDATE%") do set "CANDIDATE=%%~fR"
+if exist "%CANDIDATE%\msys2_shell.cmd" set "MOONLIGHT_MSYS2_ROOT=%CANDIDATE%"
+exit /b 0
+
+:try_from_tool
+set "TOOL_PATH=%~1"
+if not defined TOOL_PATH exit /b 0
+
+if /i "%~nx1"=="msys2_shell.cmd" (
+ for %%R in ("%~dp1.") do call :try_candidate "%%~fR"
+ exit /b 0
+)
+
+for %%R in ("%~dp1..\..") do call :try_candidate "%%~fR"
+if defined MOONLIGHT_MSYS2_ROOT exit /b 0
+
+for %%R in ("%~dp1..") do call :try_candidate "%%~fR"
+exit /b 0
diff --git a/scripts/find-msys2.sh b/scripts/find-msys2.sh
new file mode 100644
index 0000000..93bc033
--- /dev/null
+++ b/scripts/find-msys2.sh
@@ -0,0 +1,65 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+try_root() {
+ local candidate_root="$1"
+
+ if [[ -z "$candidate_root" ]]; then
+ return 1
+ fi
+
+ if [[ -f "$candidate_root/msys2_shell.cmd" ]]; then
+ printf '%s\n' "$candidate_root"
+ return 0
+ fi
+
+ return 1
+}
+
+try_from_tool() {
+ local tool_path="$1"
+ local tool_dir
+
+ if [[ -z "$tool_path" ]]; then
+ return 1
+ fi
+
+ tool_dir="$(cd "$(dirname "$tool_path")" && pwd)"
+
+ if [[ "$(basename "$tool_path")" == "msys2_shell.cmd" ]]; then
+ try_root "$tool_dir" && return 0
+ fi
+
+ try_root "$(cd "$tool_dir/../.." 2>/dev/null && pwd)" && return 0
+ try_root "$(cd "$tool_dir/.." 2>/dev/null && pwd)" && return 0
+ return 1
+}
+
+if try_root "${MSYS2_ROOT:-}"; then
+ exit 0
+fi
+
+if [[ -n "${SYSTEMDRIVE:-}" ]]; then
+ system_drive_letter="$(printf '%s' "$SYSTEMDRIVE" | cut -c1 | tr '[:upper:]' '[:lower:]')"
+ if try_root "/${system_drive_letter}/msys64"; then
+ exit 0
+ fi
+fi
+
+for candidate_root in /c/msys64 /c/tools/msys64; do
+ if try_root "$candidate_root"; then
+ exit 0
+ fi
+done
+
+for tool_name in msys2_shell.cmd bash.exe mingw32-make.exe clang++.exe clang.exe; do
+ if tool_path="$(command -v "$tool_name" 2>/dev/null)"; then
+ if try_from_tool "$tool_path"; then
+ exit 0
+ fi
+ fi
+done
+
+echo 'MSYS2 installation not found. Set MSYS2_ROOT or add MSYS2 tools to PATH.' >&2
+exit 1
diff --git a/scripts/run-xemu.cmd b/scripts/run-xemu.cmd
index 9f275b7..3ee1e5e 100644
--- a/scripts/run-xemu.cmd
+++ b/scripts/run-xemu.cmd
@@ -2,16 +2,46 @@
setlocal
set "SCRIPT_DIR=%~dp0"
-set "MSYS2_SHELL=C:\msys64\msys2_shell.cmd"
+for %%I in ("%SCRIPT_DIR%..") do set "PROJECT_ROOT=%%~fI"
+set "CALLER_DIR=%CD%"
-if not exist "%MSYS2_SHELL%" (
- echo MSYS2 shell not found at %MSYS2_SHELL%>&2
+call :has_explicit_target %*
+if errorlevel 1 if not defined MOONLIGHT_XEMU_BUILD_DIR if not defined MOONLIGHT_XEMU_ISO_PATH if not defined MOONLIGHT_XEMU_TARGET_PATH (
+ if /I not "%CALLER_DIR%"=="%PROJECT_ROOT%" (
+ set "MOONLIGHT_XEMU_TARGET_PATH=%CALLER_DIR%"
+ )
+)
+
+call "%SCRIPT_DIR%find-msys2.cmd"
+if errorlevel 1 (
exit /b 1
)
-pushd "%SCRIPT_DIR%.." >nul
-call "%MSYS2_SHELL%" -defterm -here -no-start -mingw64 -c "./scripts/run-xemu.sh %*"
+pushd "%PROJECT_ROOT%" >nul
+call "%MOONLIGHT_MSYS2_BASH%" -lc "cd \"$1\" && shift && ./scripts/run-xemu.sh \"$@\"" bash "%CD%" %*
set "EXIT_CODE=%ERRORLEVEL%"
popd >nul
exit /b %EXIT_CODE%
+
+:has_explicit_target
+if "%~1"=="" exit /b 1
+if /I "%~1"=="--check" (
+ shift
+ goto has_explicit_target
+)
+if /I "%~1"=="-h" exit /b 1
+if /I "%~1"=="--help" exit /b 1
+if /I "%~1"=="--build-dir" exit /b 0
+if /I "%~1"=="--iso" exit /b 0
+if /I "%~1"=="--" (
+ shift
+ if "%~1"=="" exit /b 1
+ exit /b 0
+)
+set "ARG=%~1"
+if "%ARG:~0,1%"=="-" (
+ shift
+ goto has_explicit_target
+)
+exit /b 0
diff --git a/scripts/run-xemu.sh b/scripts/run-xemu.sh
index 29a76f8..282e694 100644
--- a/scripts/run-xemu.sh
+++ b/scripts/run-xemu.sh
@@ -1,10 +1,18 @@
#!/usr/bin/env bash
+# Launch xemu against an explicit ISO path or a CMake build directory that
+# contains the generated Xbox ISO.
+
set -euo pipefail
usage() {
cat <<'EOF'
-Usage: run-xemu.sh [--check] [--iso path]
+Usage: run-xemu.sh [--check] [--build-dir dir] [--iso path] [path]
+
+Environment overrides:
+ MOONLIGHT_XEMU_BUILD_DIR
+ MOONLIGHT_XEMU_ISO_PATH
+ MOONLIGHT_XEMU_TARGET_PATH
EOF
return 0
}
@@ -16,6 +24,12 @@ is_windows() {
esac
}
+is_unresolved_ide_macro() {
+ local value="$1"
+
+ [[ "$value" =~ ^\$[A-Za-z_][A-Za-z0-9_]*\$$ ]]
+}
+
to_native_path() {
local path="$1"
@@ -71,38 +85,189 @@ require_file() {
fi
}
+normalize_cli_path() {
+ local path="$1"
+
+ if [[ -z "$path" ]]; then
+ printf '\n'
+ return 0
+ fi
+
+ if is_unresolved_ide_macro "$path"; then
+ printf '\n'
+ return 0
+ fi
+
+ if is_windows && [[ "$path" =~ ^[A-Za-z]:\\ ]]; then
+ cygpath -u "$path"
+ return 0
+ fi
+
+ printf '%s\n' "${path//\\//}"
+ return 0
+}
+
+default_iso_path() {
+ local project_root="$1"
+ local build_dir="${2:-}"
+
+ if [[ -n "$build_dir" ]]; then
+ local build_dir_iso="$build_dir/xbox/Moonlight.iso"
+ if [[ -f "$build_dir/Moonlight.iso" ]]; then
+ printf '%s\n' "$build_dir/Moonlight.iso"
+ else
+ printf '%s\n' "$build_dir_iso"
+ fi
+ return 0
+ fi
+
+ local preferred_path="$project_root/cmake-build-release/xbox/Moonlight.iso"
+ local candidate
+ local newest_candidate=""
+
+ shopt -s nullglob
+ for candidate in \
+ "$project_root"/cmake-build-*/xbox/Moonlight.iso \
+ "$project_root"/cmake-build-*/Moonlight.iso; do
+ if [[ -z "$newest_candidate" || "$candidate" -nt "$newest_candidate" ]]; then
+ newest_candidate="$candidate"
+ fi
+ done
+ shopt -u nullglob
+
+ if [[ -n "$newest_candidate" ]]; then
+ printf '%s\n' "$newest_candidate"
+ return 0
+ fi
+
+ printf '%s\n' "$preferred_path"
+ return 0
+}
+
+resolve_build_dir() {
+ local path="$1"
+
+ path="$(normalize_cli_path "$path")"
+
+ if [[ -z "$path" ]]; then
+ printf '\n'
+ return 0
+ fi
+
+ case "$path" in
+ /*)
+ printf '%s\n' "$path"
+ ;;
+ [A-Za-z]:/*)
+ printf '%s\n' "$path"
+ ;;
+ *)
+ printf '%s\n' "$project_root/$path"
+ ;;
+ esac
+
+ return 0
+}
+
+resolve_input_path() {
+ resolve_build_dir "$1"
+}
+
+apply_target_path() {
+ local target_path="$1"
+ local resolved_path
+
+ resolved_path="$(resolve_input_path "$target_path")"
+
+ if [[ -z "$resolved_path" ]]; then
+ return 0
+ fi
+
+ if [[ -d "$resolved_path" ]]; then
+ build_dir="$resolved_path"
+ iso_path="$(default_iso_path "$project_root" "$build_dir")"
+ else
+ iso_path="$resolved_path"
+ fi
+
+ return 0
+}
+
project_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
setup_script="$project_root/scripts/setup-xemu.sh"
manifest_sh="$project_root/.local/xemu/paths.sh"
-iso_path="$project_root/Moonlight.iso"
+build_dir=""
+iso_path="$(default_iso_path "$project_root")"
check_only=0
+target_path=""
+
+if [[ -n "${MOONLIGHT_XEMU_BUILD_DIR:-}" ]]; then
+ build_dir="$(resolve_build_dir "$MOONLIGHT_XEMU_BUILD_DIR")"
+ iso_path="$(default_iso_path "$project_root" "$build_dir")"
+fi
+
+if [[ -n "${MOONLIGHT_XEMU_ISO_PATH:-}" ]]; then
+ iso_path="$(resolve_input_path "$MOONLIGHT_XEMU_ISO_PATH")"
+fi
+
+if [[ -n "${MOONLIGHT_XEMU_TARGET_PATH:-}" ]]; then
+ target_path="$MOONLIGHT_XEMU_TARGET_PATH"
+fi
while [[ $# -gt 0 ]]; do
case "$1" in
--check)
check_only=1
;;
+ --build-dir)
+ shift
+ if [[ $# -eq 0 ]]; then
+ echo 'Missing value for --build-dir' >&2
+ exit 2
+ fi
+ build_dir="$(resolve_build_dir "$1")"
+ iso_path="$(default_iso_path "$project_root" "$build_dir")"
+ ;;
--iso)
shift
if [[ $# -eq 0 ]]; then
echo 'Missing value for --iso' >&2
exit 2
fi
- iso_path="$1"
+ iso_path="$(resolve_input_path "$1")"
+ ;;
+ --)
+ shift
+ if [[ $# -gt 0 ]]; then
+ target_path="$1"
+ shift
+ fi
+ if [[ $# -gt 0 ]]; then
+ echo 'Only one positional path is supported' >&2
+ exit 2
+ fi
+ break
;;
-h|--help)
usage
exit 0
;;
*)
- echo "Unknown argument: $1" >&2
- usage >&2
- exit 2
+ if [[ -z "$target_path" ]]; then
+ target_path="$1"
+ else
+ echo 'Only one positional path is supported' >&2
+ exit 2
+ fi
;;
esac
shift
done
+if [[ -n "$target_path" ]]; then
+ apply_target_path "$target_path"
+fi
+
if [[ ! -f "$manifest_sh" ]]; then
"$setup_script"
fi
@@ -152,6 +317,9 @@ require_file 'xemu hard disk image' "$hdd_path"
write_xemu_config "$xemu_config_path" "$games_dir" "$bootrom_path" "$flashrom_path" "$eeprom_path" "$hdd_path"
if [[ "$check_only" -eq 1 ]]; then
+ if [[ -n "$build_dir" ]]; then
+ printf 'BUILD_DIR=%s\n' "$build_dir"
+ fi
printf 'XEMU_EXE=%s\n' "$xemu_exe"
printf 'XEMU_CONFIG_PATH=%s\n' "$xemu_config_path"
printf 'ISO_PATH=%s\n' "$iso_path"
diff --git a/scripts/setup-xemu.cmd b/scripts/setup-xemu.cmd
index 4f5f7f6..7b1455f 100644
--- a/scripts/setup-xemu.cmd
+++ b/scripts/setup-xemu.cmd
@@ -2,15 +2,13 @@
setlocal
set "SCRIPT_DIR=%~dp0"
-set "MSYS2_SHELL=C:\msys64\msys2_shell.cmd"
-
-if not exist "%MSYS2_SHELL%" (
- echo MSYS2 shell not found at %MSYS2_SHELL%>&2
+call "%SCRIPT_DIR%find-msys2.cmd"
+if errorlevel 1 (
exit /b 1
)
pushd "%SCRIPT_DIR%.." >nul
-call "%MSYS2_SHELL%" -defterm -here -no-start -mingw64 -c "./scripts/setup-xemu.sh %*"
+call "%MOONLIGHT_MSYS2_SHELL%" -defterm -here -no-start -mingw64 -c "./scripts/setup-xemu.sh %*"
set "EXIT_CODE=%ERRORLEVEL%"
popd >nul
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 3dc7639..6edfc89 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -1,8 +1,15 @@
+# Build the host-native googletest target that exercises platform-neutral
+# Moonlight code without pulling the Xbox-only entrypoints into the test binary.
+
cmake_minimum_required(VERSION 3.13)
# https://github.com/google/oss-policies-info/blob/main/foundational-cxx-support-matrix.md#foundational-c-support
project(test_moonlight LANGUAGES CXX)
+if(NOT DEFINED MOONLIGHT_HOST_TESTABLE_SOURCES)
+ message(FATAL_ERROR "tests/CMakeLists.txt must be configured from the top-level CMake project")
+endif()
+
set(GTEST_SOURCE_DIR "${CMAKE_SOURCE_DIR}/third-party/googletest")
set(INSTALL_GTEST OFF CACHE BOOL "" FORCE)
set(INSTALL_GMOCK OFF CACHE BOOL "" FORCE)
@@ -10,7 +17,8 @@ add_subdirectory("${GTEST_SOURCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/googletest"
set(TEST_COVERAGE_COMPILE_OPTIONS)
set(TEST_COVERAGE_LINK_OPTIONS)
-if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
+if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU"
+ OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND NOT WIN32))
list(APPEND TEST_COVERAGE_COMPILE_OPTIONS -fprofile-arcs -ftest-coverage -ggdb -O0)
list(APPEND TEST_COVERAGE_LINK_OPTIONS -fprofile-arcs -ftest-coverage)
endif()
@@ -23,17 +31,9 @@ file(GLOB_RECURSE TEST_SOURCES CONFIGURE_DEPENDS
${CMAKE_SOURCE_DIR}/tests/*.h
${CMAKE_SOURCE_DIR}/tests/*.cpp)
-set(MOONLIGHT_TEST_EXCLUDED_SOURCES
- ${CMAKE_SOURCE_DIR}/src/main.cpp
- ${CMAKE_SOURCE_DIR}/src/splash/splash_screen.cpp
- ${CMAKE_SOURCE_DIR}/src/startup/memory_stats.cpp)
-
-set(MOONLIGHT_TESTABLE_SOURCES ${MOONLIGHT_SOURCES})
-list(REMOVE_ITEM MOONLIGHT_TESTABLE_SOURCES ${MOONLIGHT_TEST_EXCLUDED_SOURCES})
-
add_executable(${PROJECT_NAME}
${TEST_SOURCES}
- ${MOONLIGHT_TESTABLE_SOURCES})
+ ${MOONLIGHT_HOST_TESTABLE_SOURCES})
set_target_properties(${PROJECT_NAME} PROPERTIES CXX_STANDARD 17)
target_include_directories(${PROJECT_NAME}
@@ -55,4 +55,17 @@ target_link_options(${PROJECT_NAME}
add_dependencies(${PROJECT_NAME} gtest gtest_main gmock gmock_main)
include(GoogleTest)
-gtest_discover_tests(${PROJECT_NAME})
+set(TEST_RUNTIME_PROPERTIES)
+if(WIN32)
+ set(_test_runtime_path "${MOONLIGHT_HOST_TOOL_DIR}")
+ if(_test_runtime_path STREQUAL "")
+ get_filename_component(_test_runtime_path "${CMAKE_CXX_COMPILER}" DIRECTORY)
+ endif()
+
+ list(APPEND TEST_RUNTIME_PROPERTIES
+ ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:${_test_runtime_path}")
+endif()
+
+gtest_discover_tests(${PROJECT_NAME}
+ DISCOVERY_MODE PRE_TEST
+ PROPERTIES ${TEST_RUNTIME_PROPERTIES})
From 69d30b738e5408be94beea88b1aff7b6fe692812 Mon Sep 17 00:00:00 2001
From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com>
Date: Wed, 1 Apr 2026 22:52:40 -0400
Subject: [PATCH 2/2] style: lint/sonar fixes
---
cmake/host-mingw64-clang.cmake | 4 +-
cmake/msys2.cmake | 90 ++++++-----
cmake/nxdk.cmake | 285 +++++++++++++++++++--------------
cmake/run-child-build.cmake | 20 +--
scripts/find-msys2.sh | 6 +-
scripts/run-xemu.sh | 11 +-
6 files changed, 240 insertions(+), 176 deletions(-)
diff --git a/cmake/host-mingw64-clang.cmake b/cmake/host-mingw64-clang.cmake
index fe05b3f..0abd71c 100644
--- a/cmake/host-mingw64-clang.cmake
+++ b/cmake/host-mingw64-clang.cmake
@@ -19,7 +19,9 @@ find_program(_clang_executable NAMES clang.exe HINTS "${_msys2_mingw_bin}" NO_DE
find_program(_clangxx_executable NAMES clang++.exe HINTS "${_msys2_mingw_bin}" NO_DEFAULT_PATH REQUIRED)
find_program(_mingw_make_executable NAMES mingw32-make.exe HINTS "${_msys2_mingw_bin}" NO_DEFAULT_PATH REQUIRED)
-set(MOONLIGHT_HOST_TOOL_DIR "${_msys2_mingw_bin}" CACHE PATH "Path to the detected host-native mingw64 tool directory" FORCE)
+set(MOONLIGHT_HOST_TOOL_DIR "${_msys2_mingw_bin}"
+ CACHE PATH "Path to the detected host-native mingw64 tool directory" FORCE
+)
set(MOONLIGHT_MSYS2_ROOT "${_msys2_root}" CACHE PATH "Path to the detected MSYS2 installation" FORCE)
set(CMAKE_C_COMPILER "${_clang_executable}" CACHE FILEPATH "Host C compiler for the Windows CLion build" FORCE)
set(CMAKE_CXX_COMPILER "${_clangxx_executable}" CACHE FILEPATH "Host C++ compiler for the Windows CLion build" FORCE)
diff --git a/cmake/msys2.cmake b/cmake/msys2.cmake
index 451d2df..4065649 100644
--- a/cmake/msys2.cmake
+++ b/cmake/msys2.cmake
@@ -2,6 +2,7 @@
include_guard(GLOBAL)
+# Normalize a Windows path to cmake-style forward slashes with no trailing slash.
function(_moonlight_normalize_windows_path out_var input_path)
if("${input_path}" STREQUAL "")
set(${out_var} "" PARENT_SCOPE)
@@ -13,6 +14,7 @@ function(_moonlight_normalize_windows_path out_var input_path)
set(${out_var} "${_normalized_path}" PARENT_SCOPE)
endfunction()
+# Validate a candidate MSYS2 root path and set the output variable when valid.
function(_moonlight_set_msys2_root_if_valid out_var candidate_root)
_moonlight_normalize_windows_path(_normalized_root "${candidate_root}")
@@ -23,6 +25,7 @@ function(_moonlight_set_msys2_root_if_valid out_var candidate_root)
endif()
endfunction()
+# Attempt to derive the MSYS2 root directory by walking up from a known tool path.
function(_moonlight_try_msys2_root_from_tool out_var tool_path)
if("${tool_path}" STREQUAL "")
set(${out_var} "" PARENT_SCOPE)
@@ -33,17 +36,17 @@ function(_moonlight_try_msys2_root_from_tool out_var tool_path)
get_filename_component(_tool_dir "${_tool_path}" DIRECTORY)
get_filename_component(_tool_name "${_tool_path}" NAME)
- set(_candidate_roots)
+ set(candidate_roots)
if(_tool_name STREQUAL "msys2_shell.cmd")
- list(APPEND _candidate_roots "${_tool_dir}")
+ list(APPEND candidate_roots "${_tool_dir}")
endif()
get_filename_component(_one_up "${_tool_dir}/.." ABSOLUTE)
get_filename_component(_two_up "${_tool_dir}/../.." ABSOLUTE)
- list(APPEND _candidate_roots "${_one_up}" "${_two_up}")
+ list(APPEND candidate_roots "${_one_up}" "${_two_up}")
- foreach(_candidate_root IN LISTS _candidate_roots)
- _moonlight_set_msys2_root_if_valid(_resolved_root "${_candidate_root}")
+ foreach(candidate_root IN LISTS candidate_roots)
+ _moonlight_set_msys2_root_if_valid(_resolved_root "${candidate_root}")
if(NOT _resolved_root STREQUAL "")
set(${out_var} "${_resolved_root}" PARENT_SCOPE)
return()
@@ -53,28 +56,29 @@ function(_moonlight_try_msys2_root_from_tool out_var tool_path)
set(${out_var} "" PARENT_SCOPE)
endfunction()
+# Detect the MSYS2 installation root on Windows and cache the resolved path.
function(moonlight_detect_windows_msys2_root out_var)
if(NOT WIN32)
message(FATAL_ERROR "moonlight_detect_windows_msys2_root is only available on Windows hosts")
endif()
- set(_candidate_roots)
+ set(candidate_roots)
if(DEFINED MOONLIGHT_MSYS2_ROOT AND NOT MOONLIGHT_MSYS2_ROOT STREQUAL "")
- list(APPEND _candidate_roots "${MOONLIGHT_MSYS2_ROOT}")
+ list(APPEND candidate_roots "${MOONLIGHT_MSYS2_ROOT}")
endif()
if(DEFINED ENV{MSYS2_ROOT} AND NOT "$ENV{MSYS2_ROOT}" STREQUAL "")
- list(APPEND _candidate_roots "$ENV{MSYS2_ROOT}")
+ list(APPEND candidate_roots "$ENV{MSYS2_ROOT}")
endif()
if(DEFINED ENV{SystemDrive} AND NOT "$ENV{SystemDrive}" STREQUAL "")
- list(APPEND _candidate_roots "$ENV{SystemDrive}/msys64")
+ list(APPEND candidate_roots "$ENV{SystemDrive}/msys64")
endif()
- list(APPEND _candidate_roots
+ list(APPEND candidate_roots
"C:/msys64"
"C:/tools/msys64"
)
- foreach(_candidate_root IN LISTS _candidate_roots)
- _moonlight_set_msys2_root_if_valid(_resolved_root "${_candidate_root}")
+ foreach(candidate_root IN LISTS candidate_roots)
+ _moonlight_set_msys2_root_if_valid(_resolved_root "${candidate_root}")
if(NOT _resolved_root STREQUAL "")
set(MOONLIGHT_MSYS2_ROOT "${_resolved_root}" CACHE PATH "Path to the detected MSYS2 installation" FORCE)
set(${out_var} "${_resolved_root}" PARENT_SCOPE)
@@ -82,11 +86,11 @@ function(moonlight_detect_windows_msys2_root out_var)
endif()
endforeach()
- set(_program_hints ${_candidate_roots})
+ set(program_hints ${candidate_roots})
find_program(_msys2_shell_path
NAMES msys2_shell.cmd
- HINTS ${_program_hints}
+ HINTS ${program_hints}
PATH_SUFFIXES .
)
_moonlight_try_msys2_root_from_tool(_resolved_root "${_msys2_shell_path}")
@@ -96,10 +100,10 @@ function(moonlight_detect_windows_msys2_root out_var)
return()
endif()
- foreach(_tool_name bash.exe mingw32-make.exe clang++.exe clang.exe)
+ foreach(tool_name bash.exe mingw32-make.exe clang++.exe clang.exe)
find_program(_tool_path
- NAMES ${_tool_name}
- HINTS ${_program_hints}
+ NAMES ${tool_name}
+ HINTS ${program_hints}
PATH_SUFFIXES usr/bin mingw64/bin ucrt64/bin clang64/bin clangarm64/bin mingw32/bin
)
_moonlight_try_msys2_root_from_tool(_resolved_root "${_tool_path}")
@@ -111,64 +115,70 @@ function(moonlight_detect_windows_msys2_root out_var)
endforeach()
message(FATAL_ERROR
- "Could not find an MSYS2 installation. Set the MSYS2_ROOT environment variable or add the MSYS2 tools to PATH.")
+ "Could not find an MSYS2 installation. "
+ "Set the MSYS2_ROOT environment variable or add the MSYS2 tools to PATH.")
endfunction()
+# Get the path to the MSYS2 shell script.
function(moonlight_get_windows_msys2_shell out_var)
moonlight_detect_windows_msys2_root(_msys2_root)
- set(_msys2_shell "${_msys2_root}/msys2_shell.cmd")
- set(${out_var} "${_msys2_shell}" PARENT_SCOPE)
+ set(msys2_shell "${_msys2_root}/msys2_shell.cmd")
+ set(${out_var} "${msys2_shell}" PARENT_SCOPE)
endfunction()
+# Get the MSYS2 usr/bin directory path.
function(moonlight_get_windows_msys2_usr_bin out_var)
moonlight_detect_windows_msys2_root(_msys2_root)
- set(_msys2_usr_bin "${_msys2_root}/usr/bin")
- set(${out_var} "${_msys2_usr_bin}" PARENT_SCOPE)
+ set(msys2_usr_bin "${_msys2_root}/usr/bin")
+ set(${out_var} "${msys2_usr_bin}" PARENT_SCOPE)
endfunction()
+# Get the path to the MSYS2 bash executable, failing if it does not exist.
function(moonlight_get_windows_msys2_bash out_var)
moonlight_get_windows_msys2_usr_bin(_msys2_usr_bin)
- set(_bash_executable "${_msys2_usr_bin}/bash.exe")
+ set(bash_executable "${_msys2_usr_bin}/bash.exe")
- if(NOT EXISTS "${_bash_executable}")
- message(FATAL_ERROR "MSYS2 bash not found at ${_bash_executable}")
+ if(NOT EXISTS "${bash_executable}")
+ message(FATAL_ERROR "MSYS2 bash not found at ${bash_executable}")
endif()
- set(${out_var} "${_bash_executable}" PARENT_SCOPE)
+ set(${out_var} "${bash_executable}" PARENT_SCOPE)
endfunction()
+# Get the bin directory for the specified MSYS2 MSYSTEM environment (default: mingw64).
function(moonlight_get_windows_msys2_msystem_bin out_var)
- set(_msystem_dir "mingw64")
+ set(msystem_dir "mingw64")
if(ARGC GREATER 1 AND NOT "${ARGV1}" STREQUAL "")
- set(_msystem_dir "${ARGV1}")
+ set(msystem_dir "${ARGV1}")
endif()
moonlight_detect_windows_msys2_root(_msys2_root)
- set(_msystem_bin "${_msys2_root}/${_msystem_dir}/bin")
+ set(msystem_bin "${_msys2_root}/${msystem_dir}/bin")
- if(NOT EXISTS "${_msystem_bin}")
- message(FATAL_ERROR "MSYS2 ${_msystem_dir} bin directory not found at ${_msystem_bin}")
+ if(NOT EXISTS "${msystem_bin}")
+ message(FATAL_ERROR "MSYS2 ${msystem_dir} bin directory not found at ${msystem_bin}")
endif()
- set(${out_var} "${_msystem_bin}" PARENT_SCOPE)
+ set(${out_var} "${msystem_bin}" PARENT_SCOPE)
endfunction()
+# Build a PATH-style string prepending the MSYS2 MSYSTEM and usr/bin directories.
function(moonlight_prepend_windows_msys2_path out_var)
- set(_msystem_dir "mingw64")
+ set(msystem_dir "mingw64")
if(ARGC GREATER 1 AND NOT "${ARGV1}" STREQUAL "")
- set(_msystem_dir "${ARGV1}")
+ set(msystem_dir "${ARGV1}")
endif()
- moonlight_get_windows_msys2_msystem_bin(_msystem_bin "${_msystem_dir}")
+ moonlight_get_windows_msys2_msystem_bin(_msystem_bin "${msystem_dir}")
moonlight_get_windows_msys2_usr_bin(_msys2_usr_bin)
- set(_path_entries "${_msystem_bin}" "${_msys2_usr_bin}")
+ set(path_entries "${_msystem_bin}" "${_msys2_usr_bin}")
file(TO_CMAKE_PATH "$ENV{PATH}" _existing_path)
if(NOT _existing_path STREQUAL "")
- list(APPEND _path_entries ${_existing_path})
+ list(APPEND path_entries ${_existing_path})
endif()
- list(REMOVE_DUPLICATES _path_entries)
- list(JOIN _path_entries ";" _resolved_path)
- set(${out_var} "${_resolved_path}" PARENT_SCOPE)
+ list(REMOVE_DUPLICATES path_entries)
+ list(JOIN path_entries ";" resolved_path)
+ set(${out_var} "${resolved_path}" PARENT_SCOPE)
endfunction()
diff --git a/cmake/nxdk.cmake b/cmake/nxdk.cmake
index 328c5f2..e65655b 100644
--- a/cmake/nxdk.cmake
+++ b/cmake/nxdk.cmake
@@ -8,73 +8,80 @@ include("${CMAKE_CURRENT_LIST_DIR}/msys2.cmake")
set(MOONLIGHT_NXDK_DIR "${CMAKE_SOURCE_DIR}/third-party/nxdk" CACHE PATH "Path to the vendored nxdk checkout")
cmake_path(ABSOLUTE_PATH MOONLIGHT_NXDK_DIR BASE_DIRECTORY "${CMAKE_SOURCE_DIR}" NORMALIZE)
+# Resolve and validate the nxdk directory, returning its absolute path.
function(moonlight_resolve_nxdk_dir out_var)
- set(_nxdk_dir "${MOONLIGHT_NXDK_DIR}")
- cmake_path(ABSOLUTE_PATH _nxdk_dir BASE_DIRECTORY "${CMAKE_SOURCE_DIR}" NORMALIZE)
+ set(nxdk_dir "${MOONLIGHT_NXDK_DIR}")
+ cmake_path(ABSOLUTE_PATH nxdk_dir BASE_DIRECTORY "${CMAKE_SOURCE_DIR}" NORMALIZE)
- if(NOT EXISTS "${_nxdk_dir}/bin/activate")
+ if(NOT EXISTS "${nxdk_dir}/bin/activate")
message(FATAL_ERROR
- "NXDK directory not found: ${_nxdk_dir}\n"
+ "NXDK directory not found: ${nxdk_dir}\n"
"Run: git submodule update --init --recursive")
endif()
- set(${out_var} "${_nxdk_dir}" PARENT_SCOPE)
+ set(${out_var} "${nxdk_dir}" PARENT_SCOPE)
endfunction()
+# Get the path to the MSYS2 shell script for use in Xbox build commands.
function(_moonlight_get_windows_msys2_shell out_var)
moonlight_get_windows_msys2_shell(_msys2_shell)
set(${out_var} "${_msys2_shell}" PARENT_SCOPE)
endfunction()
+# Locate the make program appropriate for driving the Xbox nxdk build.
function(_moonlight_get_xbox_make_program out_var)
if(WIN32)
moonlight_get_windows_msys2_usr_bin(_msys2_usr_bin)
- set(_make_program "${_msys2_usr_bin}/make.exe")
- if(NOT EXISTS "${_make_program}")
- message(FATAL_ERROR "MSYS2 make not found at ${_make_program}")
+ set(make_program "${_msys2_usr_bin}/make.exe")
+ if(NOT EXISTS "${make_program}")
+ message(FATAL_ERROR "MSYS2 make not found at ${make_program}")
endif()
else()
- find_program(_make_program NAMES make REQUIRED)
+ find_program(make_program NAMES make REQUIRED)
endif()
- set(${out_var} "${_make_program}" PARENT_SCOPE)
+ set(${out_var} "${make_program}" PARENT_SCOPE)
endfunction()
+# Convert a native Windows path to an MSYS2 POSIX-style path.
function(_moonlight_to_msys_path out_var path)
- file(TO_CMAKE_PATH "${path}" _normalized_path)
+ file(TO_CMAKE_PATH "${path}" normalized_path)
- if(_normalized_path MATCHES "^([A-Za-z]):/(.*)$")
+ if(normalized_path MATCHES "^([A-Za-z]):/(.*)$")
string(TOLOWER "${CMAKE_MATCH_1}" _drive)
- set(_normalized_path "/${_drive}/${CMAKE_MATCH_2}")
+ set(normalized_path "/${_drive}/${CMAKE_MATCH_2}")
endif()
- set(${out_var} "${_normalized_path}" PARENT_SCOPE)
+ set(${out_var} "${normalized_path}" PARENT_SCOPE)
endfunction()
+# Single-quote a value for safe embedding in a POSIX shell command string.
function(_moonlight_shell_quote out_var value)
string(REPLACE "'" "'\"'\"'" _escaped_value "${value}")
set(${out_var} "'${_escaped_value}'" PARENT_SCOPE)
endfunction()
+# Join a list of arguments into a single-quoted shell command string.
function(_moonlight_join_shell_command out_var)
- set(_quoted_args)
+ set(quoted_args)
- foreach(_arg IN LISTS ARGN)
- _moonlight_shell_quote(_quoted_arg "${_arg}")
- list(APPEND _quoted_args "${_quoted_arg}")
+ foreach(arg IN LISTS ARGN)
+ _moonlight_shell_quote(_quoted_arg "${arg}")
+ list(APPEND quoted_args "${_quoted_arg}")
endforeach()
- list(JOIN _quoted_args " " _command)
- set(${out_var} "${_command}" PARENT_SCOPE)
+ list(JOIN quoted_args " " command)
+ set(${out_var} "${command}" PARENT_SCOPE)
endfunction()
+# Build the PATH string needed to run nxdk build commands on the current platform.
function(_moonlight_get_nxdk_path out_var nxdk_dir)
- set(_path_entries "${nxdk_dir}/bin")
+ set(path_entries "${nxdk_dir}/bin")
if(APPLE)
- foreach(_llvm_path "/opt/homebrew/opt/llvm/bin" "/usr/local/opt/llvm/bin")
- if(EXISTS "${_llvm_path}/clang")
- list(APPEND _path_entries "${_llvm_path}")
+ foreach(llvm_path "/opt/homebrew/opt/llvm/bin" "/usr/local/opt/llvm/bin")
+ if(EXISTS "${llvm_path}/clang")
+ list(APPEND path_entries "${llvm_path}")
endif()
endforeach()
endif()
@@ -82,24 +89,25 @@ function(_moonlight_get_nxdk_path out_var nxdk_dir)
if(WIN32)
moonlight_get_windows_msys2_msystem_bin(_msys2_mingw_bin mingw64)
moonlight_get_windows_msys2_usr_bin(_msys2_usr_bin)
- list(APPEND _path_entries "${_msys2_mingw_bin}" "${_msys2_usr_bin}")
+ list(APPEND path_entries "${_msys2_mingw_bin}" "${_msys2_usr_bin}")
file(TO_CMAKE_PATH "$ENV{PATH}" _existing_path)
if(NOT _existing_path STREQUAL "")
- list(APPEND _path_entries ${_existing_path})
+ list(APPEND path_entries ${_existing_path})
endif()
- set(_path_separator ";")
+ set(path_separator ";")
else()
if(NOT "$ENV{PATH}" STREQUAL "")
string(REPLACE ":" ";" _existing_path "$ENV{PATH}")
- list(APPEND _path_entries ${_existing_path})
+ list(APPEND path_entries ${_existing_path})
endif()
- set(_path_separator ":")
+ set(path_separator ":")
endif()
- list(JOIN _path_entries "${_path_separator}" _path)
- set(${out_var} "${_path}" PARENT_SCOPE)
+ list(JOIN path_entries "${path_separator}" nxdk_path)
+ set(${out_var} "${nxdk_path}" PARENT_SCOPE)
endfunction()
+# Execute a command inside the nxdk environment, sourcing the NXDK activation on all platforms.
function(moonlight_run_nxdk_command description nxdk_dir working_directory)
if(WIN32)
_moonlight_get_windows_msys2_shell(_msys2_shell)
@@ -109,8 +117,13 @@ function(moonlight_run_nxdk_command description nxdk_dir working_directory)
_moonlight_shell_quote(_quoted_nxdk_dir "${_msys_nxdk_dir}")
_moonlight_shell_quote(_quoted_working_directory "${_msys_working_directory}")
- set(_shell_script
- "unset MAKEFLAGS MFLAGS GNUMAKEFLAGS MAKELEVEL; export NXDK_DIR=${_quoted_nxdk_dir}; export PATH=\"$NXDK_DIR/bin:$PATH\"; cd ${_quoted_working_directory}; exec ${_shell_command}")
+ string(CONCAT _shell_script
+ "unset MAKEFLAGS MFLAGS GNUMAKEFLAGS MAKELEVEL; "
+ "export NXDK_DIR=${_quoted_nxdk_dir}; "
+ "export PATH=\"$NXDK_DIR/bin:$PATH\"; "
+ "cd ${_quoted_working_directory}; "
+ "exec ${_shell_command}"
+ )
execute_process(
COMMAND "${_msys2_shell}" -defterm -here -no-start -mingw64 -c "${_shell_script}"
@@ -134,128 +147,162 @@ function(moonlight_run_nxdk_command description nxdk_dir working_directory)
endif()
endfunction()
+# Run make inside the nxdk environment with the platform-appropriate make tool.
function(_moonlight_run_nxdk_make nxdk_dir description)
if(WIN32)
- set(_make_program make)
+ set(make_program make)
else()
- _moonlight_get_xbox_make_program(_make_program)
+ _moonlight_get_xbox_make_program(make_program)
+ endif()
+
+ moonlight_run_nxdk_command("${description}" "${nxdk_dir}" "${nxdk_dir}" "${make_program}" ${ARGN})
+endfunction()
+
+# Report whether any listed output path is missing.
+function(_moonlight_has_missing_output out_var)
+ set(missing_output FALSE)
+
+ foreach(required_output IN LISTS ARGN)
+ if(NOT EXISTS "${required_output}")
+ set(missing_output TRUE)
+ break()
+ endif()
+ endforeach()
+
+ set(${out_var} "${missing_output}" PARENT_SCOPE)
+endfunction()
+
+# Build the vendored cxbe tool when it is missing.
+function(_moonlight_prepare_cxbe nxdk_dir cxbe_path)
+ if(EXISTS "${cxbe_path}")
+ return()
+ endif()
+
+ message(STATUS "Preparing cxbe at ${nxdk_dir}")
+ if(WIN32)
+ moonlight_run_nxdk_command("cxbe build" "${nxdk_dir}" "${nxdk_dir}/tools/cxbe" make)
+ return()
+ endif()
+
+ _moonlight_get_xbox_make_program(host_make_program)
+ moonlight_run_nxdk_command("cxbe build" "${nxdk_dir}" "${nxdk_dir}/tools/cxbe" "${host_make_program}")
+endfunction()
+
+# Configure and build the vendored extract-xiso tool when it is missing.
+function(_moonlight_prepare_extract_xiso nxdk_dir source_dir build_dir output_path)
+ if(EXISTS "${output_path}")
+ return()
endif()
- moonlight_run_nxdk_command("${description}" "${nxdk_dir}" "${nxdk_dir}" "${_make_program}" ${ARGN})
+ message(STATUS "Preparing extract-xiso at ${nxdk_dir}")
+ file(REMOVE_RECURSE "${build_dir}")
+ file(MAKE_DIRECTORY "${build_dir}")
+ moonlight_run_nxdk_command(
+ "extract-xiso configure"
+ "${nxdk_dir}"
+ "${nxdk_dir}"
+ "${CMAKE_COMMAND}"
+ -S "${source_dir}"
+ -B "${build_dir}"
+ -G "Unix Makefiles"
+ )
+ moonlight_run_nxdk_command(
+ "extract-xiso build"
+ "${nxdk_dir}"
+ "${nxdk_dir}"
+ "${CMAKE_COMMAND}"
+ --build "${build_dir}"
+ )
+endfunction()
+
+# Build the vendored host-side nxdk tools required by the Moonlight Xbox build.
+function(_moonlight_prepare_nxdk_tools nxdk_dir cxbe_path source_dir build_dir output_path)
+ _moonlight_prepare_cxbe("${nxdk_dir}" "${cxbe_path}")
+ _moonlight_prepare_extract_xiso(
+ "${nxdk_dir}"
+ "${source_dir}"
+ "${build_dir}"
+ "${output_path}"
+ )
endfunction()
+# Bootstrap the vendored nxdk outputs required by the host-first Moonlight build.
function(moonlight_prepare_nxdk nxdk_dir state_dir)
file(MAKE_DIRECTORY "${state_dir}")
- set(_signature_inputs
+ set(signature_inputs
"NXDK_DIR=${nxdk_dir}"
"HOST_SYSTEM=${CMAKE_HOST_SYSTEM_NAME}"
"NXDK_TOOLCHAIN=${nxdk_dir}/share/toolchain-nxdk.cmake"
"NXDK_ENV_MODE=cmake-driver"
)
- list(JOIN _signature_inputs "\n" _signature_text)
- string(SHA256 _signature "${_signature_text}")
-
- set(_signature_file "${state_dir}/bootstrap.signature")
- set(_cxbe_path "${nxdk_dir}/tools/cxbe/cxbe")
- set(_extract_xiso_source_dir "${nxdk_dir}/tools/extract-xiso")
- set(_extract_xiso_build_dir "${_extract_xiso_source_dir}/build")
- set(_extract_xiso_path "${_extract_xiso_build_dir}/extract-xiso")
- set(_required_libraries
+ list(JOIN signature_inputs "\n" signature_text)
+ string(SHA256 signature "${signature_text}")
+
+ set(signature_file "${state_dir}/bootstrap.signature")
+ set(cxbe_path "${nxdk_dir}/tools/cxbe/cxbe")
+ set(extract_xiso_source_dir "${nxdk_dir}/tools/extract-xiso")
+ set(extract_xiso_build_dir "${extract_xiso_source_dir}/build")
+ set(extract_xiso_path "${extract_xiso_build_dir}/extract-xiso")
+ set(required_libraries
"${nxdk_dir}/lib/libnxdk.lib"
"${nxdk_dir}/lib/libc++.lib"
"${nxdk_dir}/lib/libSDL2.lib"
"${nxdk_dir}/lib/libSDL2_image.lib"
)
- set(_required_tools
- "${_cxbe_path}"
- "${_extract_xiso_path}"
+ set(required_tools
+ "${cxbe_path}"
+ "${extract_xiso_path}"
)
if(MOONLIGHT_FORCE_NXDK_DISTCLEAN)
- set(_need_distclean TRUE)
+ set(need_distclean TRUE)
else()
- set(_need_distclean FALSE)
+ set(need_distclean FALSE)
endif()
- set(_need_prepare_libraries FALSE)
- set(_need_prepare_tools FALSE)
- if(NOT EXISTS "${_signature_file}")
- set(_need_distclean TRUE)
+ if(NOT EXISTS "${signature_file}")
+ set(need_distclean TRUE)
else()
- file(READ "${_signature_file}" _saved_signature)
- string(STRIP "${_saved_signature}" _saved_signature)
- if(NOT _saved_signature STREQUAL _signature)
- set(_need_distclean TRUE)
+ file(READ "${signature_file}" saved_signature)
+ string(STRIP "${saved_signature}" saved_signature)
+ if(NOT saved_signature STREQUAL signature)
+ set(need_distclean TRUE)
endif()
endif()
- foreach(_required_output IN LISTS _required_libraries)
- if(NOT EXISTS "${_required_output}")
- set(_need_prepare_libraries TRUE)
- break()
- endif()
- endforeach()
+ _moonlight_has_missing_output(need_prepare_libraries ${required_libraries})
+ _moonlight_has_missing_output(need_prepare_tools ${required_tools})
- foreach(_required_output IN LISTS _required_tools)
- if(NOT EXISTS "${_required_output}")
- set(_need_prepare_tools TRUE)
- break()
- endif()
- endforeach()
-
- if(_need_distclean)
+ if(need_distclean)
message(STATUS "Cleaning nxdk build tree at ${nxdk_dir}")
_moonlight_run_nxdk_make("${nxdk_dir}" "nxdk clean" clean)
- set(_need_prepare_libraries TRUE)
+ set(need_prepare_libraries TRUE)
endif()
- if(_need_prepare_libraries)
+ if(need_prepare_libraries)
message(STATUS "Preparing nxdk libraries at ${nxdk_dir}")
_moonlight_run_nxdk_make("${nxdk_dir}" "nxdk bootstrap" NXDK_ONLY=y)
else()
message(STATUS "Using existing nxdk library outputs from ${nxdk_dir}")
endif()
- if(_need_prepare_tools)
- if(NOT EXISTS "${_cxbe_path}")
- message(STATUS "Preparing cxbe at ${nxdk_dir}")
- if(WIN32)
- moonlight_run_nxdk_command("cxbe build" "${nxdk_dir}" "${nxdk_dir}/tools/cxbe" make)
- else()
- _moonlight_get_xbox_make_program(_host_make_program)
- moonlight_run_nxdk_command("cxbe build" "${nxdk_dir}" "${nxdk_dir}/tools/cxbe" "${_host_make_program}")
- endif()
- endif()
-
- if(NOT EXISTS "${_extract_xiso_path}")
- message(STATUS "Preparing extract-xiso at ${nxdk_dir}")
- file(REMOVE_RECURSE "${_extract_xiso_build_dir}")
- file(MAKE_DIRECTORY "${_extract_xiso_build_dir}")
- moonlight_run_nxdk_command(
- "extract-xiso configure"
- "${nxdk_dir}"
- "${nxdk_dir}"
- "${CMAKE_COMMAND}"
- -S "${_extract_xiso_source_dir}"
- -B "${_extract_xiso_build_dir}"
- -G "Unix Makefiles"
- )
- moonlight_run_nxdk_command(
- "extract-xiso build"
- "${nxdk_dir}"
- "${nxdk_dir}"
- "${CMAKE_COMMAND}"
- --build "${_extract_xiso_build_dir}"
- )
- endif()
+ if(need_prepare_tools)
+ _moonlight_prepare_nxdk_tools(
+ "${nxdk_dir}"
+ "${cxbe_path}"
+ "${extract_xiso_source_dir}"
+ "${extract_xiso_build_dir}"
+ "${extract_xiso_path}"
+ )
else()
message(STATUS "Using existing nxdk tool outputs from ${nxdk_dir}")
endif()
- file(WRITE "${_signature_file}" "${_signature}\n")
+ file(WRITE "${signature_file}" "${signature}\n")
endfunction()
+# Configure the internal Xbox child build and expose it as the moonlight_xbox target.
function(moonlight_add_xbox_build)
set(options)
set(oneValueArgs BINARY_DIR NXDK_DIR)
@@ -269,11 +316,11 @@ function(moonlight_add_xbox_build)
message(FATAL_ERROR "moonlight_add_xbox_build requires NXDK_DIR")
endif()
- _moonlight_get_xbox_make_program(_xbox_make_program)
+ _moonlight_get_xbox_make_program(xbox_make_program)
- set(_toolchain_file "${MOONLIGHT_XBOX_NXDK_DIR}/share/toolchain-nxdk.cmake")
- set(_driver_script "${CMAKE_SOURCE_DIR}/cmake/run-child-build.cmake")
- set(_configure_command
+ set(toolchain_file "${MOONLIGHT_XBOX_NXDK_DIR}/share/toolchain-nxdk.cmake")
+ set(driver_script "${CMAKE_SOURCE_DIR}/cmake/run-child-build.cmake")
+ set(configure_command
"${CMAKE_COMMAND}"
-DMOONLIGHT_COMMAND_MODE:STRING=configure
-DMOONLIGHT_CMAKE_COMMAND:FILEPATH=${CMAKE_COMMAND}
@@ -282,12 +329,12 @@ function(moonlight_add_xbox_build)
-DMOONLIGHT_SOURCE_DIR:PATH=${CMAKE_SOURCE_DIR}
-DMOONLIGHT_BINARY_DIR:PATH=${MOONLIGHT_XBOX_BINARY_DIR}
-DMOONLIGHT_WORKING_DIRECTORY:PATH=${CMAKE_SOURCE_DIR}
- -DMOONLIGHT_MAKE_PROGRAM:FILEPATH=${_xbox_make_program}
- -DMOONLIGHT_TOOLCHAIN_FILE:FILEPATH=${_toolchain_file}
+ -DMOONLIGHT_MAKE_PROGRAM:FILEPATH=${xbox_make_program}
+ -DMOONLIGHT_TOOLCHAIN_FILE:FILEPATH=${toolchain_file}
-DMOONLIGHT_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}
- -P "${_driver_script}"
+ -P "${driver_script}"
)
- set(_build_command
+ set(build_command
"${CMAKE_COMMAND}"
-DMOONLIGHT_COMMAND_MODE:STRING=build
-DMOONLIGHT_CMAKE_COMMAND:FILEPATH=${CMAKE_COMMAND}
@@ -295,12 +342,12 @@ function(moonlight_add_xbox_build)
-DMOONLIGHT_NXDK_DIR:PATH=${MOONLIGHT_XBOX_NXDK_DIR}
-DMOONLIGHT_BINARY_DIR:PATH=${MOONLIGHT_XBOX_BINARY_DIR}
-DMOONLIGHT_WORKING_DIRECTORY:PATH=${MOONLIGHT_XBOX_BINARY_DIR}
- -P "${_driver_script}"
+ -P "${driver_script}"
)
add_custom_target(moonlight_xbox ALL
- COMMAND ${_configure_command}
- COMMAND ${_build_command}
+ COMMAND ${configure_command}
+ COMMAND ${build_command}
BYPRODUCTS
"${MOONLIGHT_XBOX_BINARY_DIR}/Moonlight.iso"
"${MOONLIGHT_XBOX_BINARY_DIR}/xbe/default.xbe"
diff --git a/cmake/run-child-build.cmake b/cmake/run-child-build.cmake
index 9df8346..f3a7a18 100644
--- a/cmake/run-child-build.cmake
+++ b/cmake/run-child-build.cmake
@@ -1,27 +1,27 @@
# Configure or build the internal Xbox child project inside the vendored nxdk environment.
-foreach(_required_var
+foreach(required_var
MOONLIGHT_COMMAND_MODE
MOONLIGHT_CMAKE_COMMAND
MOONLIGHT_DESCRIPTION
MOONLIGHT_NXDK_DIR
MOONLIGHT_BINARY_DIR
MOONLIGHT_WORKING_DIRECTORY)
- if(NOT DEFINED ${_required_var} OR "${${_required_var}}" STREQUAL "")
- message(FATAL_ERROR "${_required_var} must be defined when running run-child-build.cmake")
+ if(NOT DEFINED ${required_var} OR "${${required_var}}" STREQUAL "")
+ message(FATAL_ERROR "${required_var} must be defined when running run-child-build.cmake")
endif()
endforeach()
include("${CMAKE_CURRENT_LIST_DIR}/nxdk.cmake")
if(MOONLIGHT_COMMAND_MODE STREQUAL "configure")
- foreach(_required_var MOONLIGHT_SOURCE_DIR MOONLIGHT_MAKE_PROGRAM MOONLIGHT_TOOLCHAIN_FILE)
- if(NOT DEFINED ${_required_var} OR "${${_required_var}}" STREQUAL "")
- message(FATAL_ERROR "${_required_var} must be defined for configure mode")
+ foreach(required_var MOONLIGHT_SOURCE_DIR MOONLIGHT_MAKE_PROGRAM MOONLIGHT_TOOLCHAIN_FILE)
+ if(NOT DEFINED ${required_var} OR "${${required_var}}" STREQUAL "")
+ message(FATAL_ERROR "${required_var} must be defined for configure mode")
endif()
endforeach()
- set(_command
+ set(_command_args
"${MOONLIGHT_CMAKE_COMMAND}"
-G "Unix Makefiles"
"-DCMAKE_MAKE_PROGRAM:FILEPATH=${MOONLIGHT_MAKE_PROGRAM}"
@@ -37,10 +37,10 @@ if(MOONLIGHT_COMMAND_MODE STREQUAL "configure")
)
if(DEFINED MOONLIGHT_BUILD_TYPE AND NOT MOONLIGHT_BUILD_TYPE STREQUAL "")
- list(APPEND _command "-DCMAKE_BUILD_TYPE:STRING=${MOONLIGHT_BUILD_TYPE}")
+ list(APPEND _command_args "-DCMAKE_BUILD_TYPE:STRING=${MOONLIGHT_BUILD_TYPE}")
endif()
elseif(MOONLIGHT_COMMAND_MODE STREQUAL "build")
- set(_command
+ set(_command_args
"${MOONLIGHT_CMAKE_COMMAND}"
--build "${MOONLIGHT_BINARY_DIR}"
)
@@ -52,5 +52,5 @@ moonlight_run_nxdk_command(
"${MOONLIGHT_DESCRIPTION}"
"${MOONLIGHT_NXDK_DIR}"
"${MOONLIGHT_WORKING_DIRECTORY}"
- ${_command}
+ ${_command_args}
)
diff --git a/scripts/find-msys2.sh b/scripts/find-msys2.sh
index 93bc033..65ee141 100644
--- a/scripts/find-msys2.sh
+++ b/scripts/find-msys2.sh
@@ -54,10 +54,8 @@ for candidate_root in /c/msys64 /c/tools/msys64; do
done
for tool_name in msys2_shell.cmd bash.exe mingw32-make.exe clang++.exe clang.exe; do
- if tool_path="$(command -v "$tool_name" 2>/dev/null)"; then
- if try_from_tool "$tool_path"; then
- exit 0
- fi
+ if tool_path="$(command -v "$tool_name" 2>/dev/null)" && try_from_tool "$tool_path"; then
+ exit 0
fi
done
diff --git a/scripts/run-xemu.sh b/scripts/run-xemu.sh
index 282e694..a8a72e3 100644
--- a/scripts/run-xemu.sh
+++ b/scripts/run-xemu.sh
@@ -27,7 +27,11 @@ is_windows() {
is_unresolved_ide_macro() {
local value="$1"
- [[ "$value" =~ ^\$[A-Za-z_][A-Za-z0-9_]*\$$ ]]
+ if [[ "$value" =~ ^\$[A-Za-z_][A-Za-z0-9_]*\$$ ]]; then
+ return 0
+ fi
+
+ return 1
}
to_native_path() {
@@ -170,7 +174,10 @@ resolve_build_dir() {
}
resolve_input_path() {
- resolve_build_dir "$1"
+ local input_path="$1"
+
+ resolve_build_dir "$input_path"
+ return 0
}
apply_target_path() {