diff --git a/docs/BUILDING.md b/docs/BUILDING.md index cece7684..49683e99 100644 --- a/docs/BUILDING.md +++ b/docs/BUILDING.md @@ -168,7 +168,8 @@ that basic build as reproduction of the full experimental Public Build. Run `./Setup-macOS.sh` to install and check prerequisites, or `./Setup-macOS.sh --skip-deps` to check without installing. Setup and Build's -preparation share the same checks: full Xcode with Metal, GNU Make 4+ (`gmake`), +preparation share the same checks: the Command Line Tools (`xcode-select --install`; +full Xcode is not needed, RT64's Metal shaders compile at runtime), GNU Make 4+ (`gmake`), CMake, Ninja, Python, SDL2, PCRE2, and a `sha1sum` supporting `-c --quiet` (Homebrew `coreutils`). The verifier is checked before ROM extraction. @@ -188,7 +189,7 @@ Set `DKR_MAC_BUNDLE_VERSION=N[.N[.N]]` for an explicit release/CI build number Packaging checks the version metadata copied beside the executable after linking, so a new package label cannot silently rename a build of a different version. -With Xcode setup and Homebrew installed: +With the Command Line Tools and Homebrew installed: ```bash ./Build-macOS.sh --rom-v77 "/path/to/US 1.0.z64" --rom-v80 "/path/to/US Rev A.z64" diff --git a/patches/manifest.json b/patches/manifest.json index 5cfd7e29..537e46d8 100644 --- a/patches/manifest.json +++ b/patches/manifest.json @@ -132,6 +132,10 @@ { "path": "patches/n64recomp/0002-name-recomp-context-struct.patch", "sha256": "6cf31eebf6a5f0a9a31c2912f562f8ef977c0a43dfffd76531f3ceb94ae2bee7" + }, + { + "path": "patches/n64recomp/0003-null-statement-before-instruction-hooks.patch", + "sha256": "f864ffd285ff4b09f2a3f4e453e5f0e5b2c39e345bc05dd88f47a46b01d3dac7" } ] }, @@ -226,6 +230,10 @@ { "path": "patches/rt64/0020-ios-imgui-pixel-size.patch", "sha256": "d346ecb854a422b3c9b0349a72b8217a08e4b0b5dc86e22f5b1ab995d25a7ca5" + }, + { + "path": "patches/rt64/0021-macos-runtime-msl-shaders.patch", + "sha256": "441fe5f1dc8edf646d8f4ea3a76d25a65e86fc836aa8ae9cea637c9bde22128b" } ] }, @@ -309,6 +317,10 @@ { "path": "patches/plume/0002-ios-metal-device-os27-upstream.patch", "sha256": "a1c559877eaa59a5cf8878c4e0879f15aa5ffbd0455b8e5a5d593289348709ce" + }, + { + "path": "patches/plume/0003-compile-msl-source-shaders.patch", + "sha256": "80947b240d7a3bd5e3f9434df7501fc54d130d4e06942e36705f2238d8fb3565" } ] }, diff --git a/patches/n64recomp/0003-null-statement-before-instruction-hooks.patch b/patches/n64recomp/0003-null-statement-before-instruction-hooks.patch new file mode 100644 index 00000000..23bf1459 --- /dev/null +++ b/patches/n64recomp/0003-null-statement-before-instruction-hooks.patch @@ -0,0 +1,16 @@ +diff --git a/src/recompilation.cpp b/src/recompilation.cpp +index c228433..afdfe63 100644 +--- a/src/recompilation.cpp ++++ b/src/recompilation.cpp +@@ -128,7 +128,10 @@ bool process_instruction(GeneratorType& generator, const N64Recomp::Context& con + + auto hook_find = func.function_hooks.find(instr_index); + if (hook_find != func.function_hooks.end()) { +- fmt::print(output_file, " {}\n", hook_find->second); ++ // The leading null statement lets a hook that declares something follow a ++ // branch label under C17 (Clang before 18 rejects it). It is not braced: ++ // hooks declare locals that later hooks in the function read. ++ fmt::print(output_file, " ; {}\n", hook_find->second); + if (indent) { + print_indent(); + } diff --git a/patches/plume/0003-compile-msl-source-shaders.patch b/patches/plume/0003-compile-msl-source-shaders.patch new file mode 100644 index 00000000..131ff0c8 --- /dev/null +++ b/patches/plume/0003-compile-msl-source-shaders.patch @@ -0,0 +1,23 @@ +diff --git a/plume_metal.cpp b/plume_metal.cpp +index b670ed3..f0ca614 100644 +--- a/plume_metal.cpp ++++ b/plume_metal.cpp +@@ -1342,8 +1342,16 @@ namespace plume { + functionName->retain(); + + NS::Error *error = nullptr; +- const dispatch_data_t dispatchData = dispatch_data_create(data, size, dispatch_get_main_queue(), ^{}); +- library = device->mtl->newLibrary(dispatchData, &error); ++ // Precompiled libraries start with the "MTLB" magic. Anything else is MSL source, ++ // compiled by the OS so macOS builds don't need Xcode's offline Metal compiler. ++ if ((size >= 4) && (memcmp(data, "MTLB", 4) == 0)) { ++ const dispatch_data_t dispatchData = dispatch_data_create(data, size, dispatch_get_main_queue(), ^{}); ++ library = device->mtl->newLibrary(dispatchData, &error); ++ } ++ else { ++ const std::string source(static_cast(data), size); ++ library = device->mtl->newLibrary(NS::String::string(source.c_str(), NS::UTF8StringEncoding), nullptr, &error); ++ } + + if (error != nullptr) { + fprintf(stderr, "MTLDevice newLibraryWithSource: failed with error %s.\n", error->localizedDescription()->utf8String()); diff --git a/patches/rt64/0021-macos-runtime-msl-shaders.patch b/patches/rt64/0021-macos-runtime-msl-shaders.patch new file mode 100644 index 00000000..d00be920 --- /dev/null +++ b/patches/rt64/0021-macos-runtime-msl-shaders.patch @@ -0,0 +1,28 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 17563c1..d86dfec 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -163,17 +163,15 @@ function(build_shader_msl_impl TARGETOBJ FILENAME TARGET_NAME OUTNAME) + add_custom_command(OUTPUT ${OUTNAME}.metallib + COMMAND xcrun -sdk ${_dkr_ios_metal_sdk} metallib ${OUTNAME}.ir -o ${OUTNAME}.metallib + DEPENDS ${OUTNAME}.ir) ++ set(_dkr_msl_blob ${OUTNAME}.metallib) + else() +- add_custom_command(OUTPUT ${OUTNAME}.ir +- COMMAND xcrun -sdk macosx metal -o ${OUTNAME}.ir -c ${OUTNAME}.metal $<$:-frecord-sources> +- DEPENDS ${OUTNAME}.metal) +- add_custom_command(OUTPUT ${OUTNAME}.metallib +- COMMAND xcrun -sdk macosx metallib ${OUTNAME}.ir -o ${OUTNAME}.metallib +- DEPENDS ${OUTNAME}.ir) ++ # macOS embeds the MSL source; plume compiles it at runtime, so the ++ # build needs no Xcode Metal toolchain. ++ set(_dkr_msl_blob ${OUTNAME}.metal) + endif() + add_custom_command(OUTPUT ${OUTNAME}.metal.c +- COMMAND file_to_c ${OUTNAME}.metallib ${TARGET_NAME}BlobMSL ${OUTNAME}.metal.c ${OUTNAME}.metal.h +- DEPENDS ${OUTNAME}.metallib file_to_c ++ COMMAND file_to_c ${_dkr_msl_blob} ${TARGET_NAME}BlobMSL ${OUTNAME}.metal.c ${OUTNAME}.metal.h ++ DEPENDS ${_dkr_msl_blob} file_to_c + BYPRODUCTS ${OUTNAME}.metal.h) + target_sources(${TARGETOBJ} PRIVATE ${OUTNAME}.metal.c) + endfunction() diff --git a/runtime-recomp/tests/build_tooling_tests.py b/runtime-recomp/tests/build_tooling_tests.py index 76528a33..df9c23cc 100644 --- a/runtime-recomp/tests/build_tooling_tests.py +++ b/runtime-recomp/tests/build_tooling_tests.py @@ -915,7 +915,7 @@ def executable(name, body="exit 0"): p.chmod(0o755) for name in ( - "xcodebuild", + "xcrun", "git", "python3", "cmake", diff --git a/scripts/lib/apple-build.sh b/scripts/lib/apple-build.sh index 079f89f1..d1817fd6 100644 --- a/scripts/lib/apple-build.sh +++ b/scripts/lib/apple-build.sh @@ -3,7 +3,7 @@ dkr_require_macos() { [[ "$(uname -s)" == Darwin ]] || { - echo 'Apple builds require macOS with the full Xcode application installed.' >&2 + echo 'Apple builds require macOS.' >&2 return 1 } } diff --git a/scripts/macos-host-tools.sh b/scripts/macos-host-tools.sh index 1ed56b7e..dbae23d1 100644 --- a/scripts/macos-host-tools.sh +++ b/scripts/macos-host-tools.sh @@ -4,13 +4,27 @@ source "${BASH_SOURCE[0]%/*}/lib/apple-build.sh" dkr_macos_host_tools() { local skip_deps="$1" make_version probe dkr_require_macos || return 1 - dkr_require_xcode || return 1 + # Command Line Tools are enough: RT64's Metal shaders compile at runtime. + xcrun --sdk macosx --show-sdk-path >/dev/null 2>&1 || { + echo 'Install the Command Line Tools: xcode-select --install' >&2 + return 1 + } if [[ "$skip_deps" -eq 0 ]]; then command -v brew >/dev/null || { echo 'Install Homebrew: https://brew.sh/' >&2 return 1 } - HOMEBREW_NO_AUTO_UPDATE=1 HOMEBREW_NO_INSTALL_UPGRADE=1 brew install --quiet cmake ninja make pcre2 python sdl2 coreutils || return 1 + # Install only what is missing: on macOS releases Homebrew no longer + # bottles, reinstalling a present tool (python) means a failing source build. + local pair missing=() + for pair in cmake:cmake ninja:ninja make:gmake pcre2:pcre2-config sdl2:sdl2-config coreutils:sha1sum; do + command -v "${pair#*:}" >/dev/null || missing+=("${pair%%:*}") + done + # tomllib needs 3.11; the Command Line Tools ship 3.9. + python3 -c 'import sys; sys.exit(sys.version_info < (3, 11))' 2>/dev/null || missing+=(python) + if [[ "${#missing[@]}" -gt 0 ]]; then + HOMEBREW_NO_AUTO_UPDATE=1 HOMEBREW_NO_INSTALL_UPGRADE=1 brew install --quiet "${missing[@]}" || return 1 + fi fi dkr_require_tools git python3 cmake ninja c++ shasum sha1sum pcre2-config sdl2-config || { echo 'Run ./Setup-macOS.sh (sha1sum is supplied by brew install coreutils).' >&2 @@ -39,11 +53,4 @@ dkr_macos_host_tools() { return 1 fi rm -rf -- "$probe" - if ! xcrun metal -v >/dev/null 2>&1; then - if [[ "$skip_deps" -eq 1 ]]; then - echo 'Install Metal: xcodebuild -downloadComponent MetalToolchain' >&2 - return 1 - fi - xcodebuild -downloadComponent MetalToolchain || return 1 - fi }