diff --git a/.github/workflows/autobuild.yml b/.github/workflows/autobuild.yml index c48fc926..6208a8ab 100644 --- a/.github/workflows/autobuild.yml +++ b/.github/workflows/autobuild.yml @@ -304,6 +304,11 @@ jobs: name: Libretro phase 1 runs-on: ubuntu-24.04 steps: + - id: short-sha + uses: benjlevesque/short-sha@v2.2 + with: + length: 7 + - name: Checkout repository uses: actions/checkout@v4 @@ -336,7 +341,7 @@ jobs: - uses: actions/upload-artifact@v4 with: - name: retro-phase1 + name: retro-phase1.${{ github.event_name == 'pull_request' && format('PR{0}', github.event.number) || github.ref_name }}-${{ steps.short-sha.outputs.sha }} path: retro/build/retro-phase1 build-retro-phase2-windows: @@ -344,12 +349,17 @@ jobs: name: Libretro Windows runs-on: windows-latest steps: + - id: short-sha + uses: benjlevesque/short-sha@v2.2 + with: + length: 7 + - name: Checkout repository uses: actions/checkout@v4 - uses: actions/download-artifact@v4 with: - name: retro-phase1 + name: retro-phase1.${{ github.event_name == 'pull_request' && format('PR{0}', github.event.number) || github.ref_name }}-${{ steps.short-sha.outputs.sha }} path: retro/build/retro-phase1 - uses: msys2/setup-msys2@v2 @@ -371,7 +381,7 @@ jobs: - uses: actions/upload-artifact@v4 with: - name: libretro-mkxp-z.windows + name: libretro-mkxp-z.windows.${{ github.event_name == 'pull_request' && format('PR{0}', github.event.number) || github.ref_name }}-${{ steps.short-sha.outputs.sha }} path: ${{ runner.temp }}/retro-phase2 build-retro-phase2-linux: @@ -410,12 +420,17 @@ jobs: arch_debian: riscv64 arch_gcc: riscv64-linux-gnu steps: + - id: short-sha + uses: benjlevesque/short-sha@v2.2 + with: + length: 7 + - name: Checkout repository uses: actions/checkout@v4 - uses: actions/download-artifact@v4 with: - name: retro-phase1 + name: retro-phase1.${{ github.event_name == 'pull_request' && format('PR{0}', github.event.number) || github.ref_name }}-${{ steps.short-sha.outputs.sha }} path: retro/build/retro-phase1 - name: Install apt dependencies @@ -461,20 +476,39 @@ jobs: - uses: actions/upload-artifact@v4 with: - name: libretro-mkxp-z.linux.${{ matrix.arch_mkxpz }} + name: libretro-mkxp-z.linux.${{ matrix.arch_mkxpz }}.${{ github.event_name == 'pull_request' && format('PR{0}', github.event.number) || github.ref_name }}-${{ steps.short-sha.outputs.sha }} path: ${{ runner.temp }}/retro-phase2 build-retro-phase2-macos: needs: build-retro-phase1 - name: Libretro macOS + name: Libretro macOS ${{ matrix.arch_mkxpz }} runs-on: macos-latest + strategy: + fail-fast: false + matrix: + include: + - arch_mkxpz: arm64 + arch_llvm: aarch64-apple-darwin + cpu_family: aarch64 + cpu: generic-armv8-a + endian: little + - arch_mkxpz: x86_64 + arch_llvm: x86_64-apple-darwin + cpu_family: x86_64 + cpu: x86_64 + endian: little steps: + - id: short-sha + uses: benjlevesque/short-sha@v2.2 + with: + length: 7 + - name: Checkout repository uses: actions/checkout@v4 - uses: actions/download-artifact@v4 with: - name: retro-phase1 + name: retro-phase1.${{ github.event_name == 'pull_request' && format('PR{0}', github.event.number) || github.ref_name }}-${{ steps.short-sha.outputs.sha }} path: retro/build/retro-phase1 - name: Install dependencies @@ -483,9 +517,27 @@ jobs: - name: Build phase 2 run: | + echo '#!/bin/sh'$'\n''exec clang --target=${{ matrix.arch_llvm }} "$@"' > ${{ runner.temp }}/cc + echo '#!/bin/sh'$'\n''exec clang++ --target=${{ matrix.arch_llvm }} "$@"' > ${{ runner.temp }}/c++ + chmod +x ${{ runner.temp }}/cc + chmod +x ${{ runner.temp }}/c++ + + echo "[binaries]" | tee -a ${{ runner.temp }}/cross.txt + echo "c = '${{ runner.temp }}/cc'" | tee -a ${{ runner.temp }}/cross.txt + echo "cpp = '${{ runner.temp }}/c++'" | tee -a ${{ runner.temp }}/cross.txt + echo "ar = 'ar'" | tee -a ${{ runner.temp }}/cross.txt + echo "strip = 'strip'" | tee -a ${{ runner.temp }}/cross.txt + echo "pkgconfig = 'pkg-config'" | tee -a ${{ runner.temp }}/cross.txt + echo "cmake = 'cmake'" | tee -a ${{ runner.temp }}/cross.txt + echo "[host_machine]" | tee -a ${{ runner.temp }}/cross.txt + echo "system = 'darwin'" | tee -a ${{ runner.temp }}/cross.txt + echo "cpu_family = '${{ matrix.cpu_family }}'" | tee -a ${{ runner.temp }}/cross.txt + echo "cpu = '${{ matrix.cpu }}'" | tee -a ${{ runner.temp }}/cross.txt + echo "endian = '${{ matrix.endian }}'" | tee -a ${{ runner.temp }}/cross.txt + mkdir ${{ runner.temp }}/retro-phase2 cp retro/core.info ${{ runner.temp }}/retro-phase2/libretro-mkxp-z.info - PATH="$HOMEBREW_PREFIX/opt/gpatch/libexec/gnubin:$PATH" meson setup build --buildtype release -Db_lto=true -Dretro=true -Dretro_phase1_path=retro/build/retro-phase1 + PATH="$HOMEBREW_PREFIX/opt/gpatch/libexec/gnubin:$PATH" meson setup build --cross-file ${{ runner.temp }}/cross.txt --buildtype release -Db_lto=true -Dretro=true -Dretro_phase1_path=retro/build/retro-phase1 cd build ninja -v strip -x libretro-mkxp-z.dylib @@ -493,5 +545,5 @@ jobs: - uses: actions/upload-artifact@v4 with: - name: libretro-mkxp-z.macos + name: libretro-mkxp-z.macos.${{ matrix.arch_mkxpz }}.${{ github.event_name == 'pull_request' && format('PR{0}', github.event.number) || github.ref_name }}-${{ steps.short-sha.outputs.sha }} path: ${{ runner.temp }}/retro-phase2 diff --git a/linux/meson-x86_64.txt b/linux/meson-x86_64.txt new file mode 100644 index 00000000..b4ae7593 --- /dev/null +++ b/linux/meson-x86_64.txt @@ -0,0 +1,13 @@ +[binaries] +c = 'x86_64-linux-gnu-gcc' +cpp = 'x86_64-linux-gnu-g++' +ar = 'x86_64-linux-gnu-ar' +strip = 'x86_64-linux-gnu-strip' +pkgconfig = 'pkg-config' +cmake = 'cmake' + +[host_machine] +system = 'linux' +cpu_family = 'x86_64' +cpu = 'x86_64' +endian = 'little' diff --git a/meson.build b/meson.build index ad9f4341..5d0057ab 100644 --- a/meson.build +++ b/meson.build @@ -51,6 +51,7 @@ if get_option('retro') == true bzip2_options = cmake.subproject_options() bzip2_options.add_cmake_defines({ 'CMAKE_POSITION_INDEPENDENT_CODE': true, + 'CMAKE_BUILD_TYPE': 'None', 'ENABLE_STATIC_LIB': true, 'ENABLE_LIB_ONLY': true, }) @@ -104,11 +105,17 @@ if get_option('retro') == true endif retro_link_args = [] + + # We need to statically link the C++ standard library (libstdc++/libc++), the compiler runtime library (libgcc/compiler-rt) and libpthread in MSYS2 builds for Windows if host_system == 'windows' or host_system == 'cygwin' - # We need to statically link the C++ standard library (libstdc++/libc++), the compiler runtime library (libgcc/compiler-rt) and libpthread in MSYS2 builds for Windows retro_link_args += '-static' endif + # If possible, stop the linker from reexporting the symbols from the static libraries we use (e.g. zlib) + if compilers['cpp'].has_link_argument('-Wl,--version-script,' + join_paths(meson.current_source_dir(), 'retro/link.T')) # Only works with GNU linker and LLVM linker + retro_link_args += '-Wl,--version-script,' + join_paths(meson.current_source_dir(), 'retro/link.T') + endif + library( 'retro-' + meson.project_name(), dependencies: [ diff --git a/retro/Makefile b/retro/Makefile index fa43d7c6..28c637e5 100644 --- a/retro/Makefile +++ b/retro/Makefile @@ -21,6 +21,12 @@ CXX ?= c++ LD ?= ld AR ?= ar RANLIB ?= ranlib +CFLAGS ?= +CXXFLAGS ?= +LDFLAGS ?= +WASI_CFLAGS ?= -O3 +WASI_CXXFLAGS ?= -O3 +WASI_LDFLAGS ?= BUILD_PREFIX := ${PWD}/build OUTDIR := $(BUILD_PREFIX)/retro-phase1 @@ -72,15 +78,15 @@ $(OUTDIR)/wasm2c/wasm-rt.h $(OUTDIR)/wasm2c/wasm-rt-impl.c $(OUTDIR)/wasm2c/wasm $(CLONE) $(GITHUB)/WebAssembly/wabt $(DOWNLOADS)/wabt -b $(WASM_RT_VERSION) cp -r $(DOWNLOADS)/wabt/wasm2c $(OUTDIR) -# Cross ruby +# Cross Ruby (targets WASI) $(OUTDIR)/mkxp-retro-ruby/mkxp-retro-ruby.h $(OUTDIR)/mkxp-retro-ruby/mkxp-retro-ruby-impl.h $(OUTDIR)/mkxp-retro-ruby/mkxp-retro-ruby_0.c $(OUTDIR)/mkxp-retro-ruby/mkxp-retro-ruby_1.c $(OUTDIR)/mkxp-retro-ruby/mkxp-retro-ruby_2.c $(OUTDIR)/mkxp-retro-ruby/mkxp-retro-ruby_3.c $(OUTDIR)/mkxp-retro-ruby/mkxp-retro-ruby_4.c $(OUTDIR)/mkxp-retro-ruby/mkxp-retro-ruby_5.c $(OUTDIR)/mkxp-retro-ruby/mkxp-retro-ruby_6.c $(OUTDIR)/mkxp-retro-ruby/mkxp-retro-ruby_7.c &: $(LIBDIR)/ruby.wasm mkdir -p $(OUTDIR)/mkxp-retro-ruby $(WASM2C) $(LIBDIR)/ruby.wasm -n ruby --num-outputs=8 -o $(OUTDIR)/mkxp-retro-ruby/mkxp-retro-ruby.c -$(LIBDIR)/ruby.wasm $(OUTDIR)/mkxp-retro-dist.zip.c &: $(DOWNLOADS)/ruby/Makefile extra-ruby-bindings.h +$(LIBDIR)/ruby.wasm $(OUTDIR)/mkxp-retro-dist.zip.c &: $(DOWNLOADS)/crossruby/Makefile extra-ruby-bindings.h mkdir -p $(OUTDIR) - cd $(DOWNLOADS)/ruby && $(MAKE) install DESTDIR=$(OUTDIR) + cd $(DOWNLOADS)/crossruby && $(MAKE) install DESTDIR=$(OUTDIR) mv $(OUTDIR)/mkxp-retro-dist/bin/ruby $(LIBDIR)/ruby.wasm rm $(OUTDIR)/mkxp-retro-dist/lib/libruby-static.a cd $(OUTDIR)/mkxp-retro-dist && $(ZIP) -r $(OUTDIR)/mkxp-retro-dist.zip * @@ -93,12 +99,12 @@ $(OUTDIR)/mkxp-sandbox-bindgen.cpp $(OUTDIR)/mkxp-sandbox-bindgen.h &: sandbox-b mv $(LIBDIR)/mkxp-sandbox-bindgen.h $(OUTDIR) mv $(LIBDIR)/mkxp-sandbox-bindgen.cpp $(OUTDIR) -$(LIBDIR)/tags: $(DOWNLOADS)/ruby/.ext/include/$(TARGET)/ruby/config.h - echo '#include ' | $(WASI_CC) -E -I$(DOWNLOADS)/ruby/include -I$(DOWNLOADS)/ruby/.ext/include/$(TARGET) -o $(LIBDIR)/tags.c - - $(CTAGS) -R --fields=S --kinds-c=p --kinds-c++=p -o $(LIBDIR)/tags $(LIBDIR)/tags.c +$(LIBDIR)/tags: $(DOWNLOADS)/crossruby/.ext/include/$(TARGET)/ruby/config.h + echo '#include ' | $(WASI_CC) -E -I$(DOWNLOADS)/crossruby/include -I$(DOWNLOADS)/crossruby/.ext/include/$(TARGET) -o $(LIBDIR)/tags.c - + $(CTAGS) -R --fields=S --kinds-c=p -o $(LIBDIR)/tags $(LIBDIR)/tags.c -$(DOWNLOADS)/ruby/Makefile $(DOWNLOADS)/ruby/.ext/include/$(TARGET)/ruby/config.h &: $(DOWNLOADS)/ruby/configure $(RUBY) $(LIBDIR)/usr/local/lib/libyaml.a $(LIBDIR)/usr/local/lib/libz.a $(LIBDIR)/usr/local/lib/libssl.a - cd $(DOWNLOADS)/ruby && ./configure \ +$(DOWNLOADS)/crossruby/Makefile $(DOWNLOADS)/crossruby/.ext/include/$(TARGET)/ruby/config.h &: $(DOWNLOADS)/crossruby/configure $(RUBY) $(LIBDIR)/usr/local/lib/libyaml.a $(LIBDIR)/usr/local/lib/libz.a $(LIBDIR)/usr/local/lib/libssl.a + cd $(DOWNLOADS)/crossruby && ./configure \ --prefix=/mkxp-retro-dist \ --host $(TARGET) \ --build $(shell $(RUBY) -e 'puts(RbConfig::CONFIG["host"])') \ @@ -111,31 +117,31 @@ $(DOWNLOADS)/ruby/Makefile $(DOWNLOADS)/ruby/.ext/include/$(TARGET)/ruby/config. WASMOPT=$(WASM_OPT) \ WASI_SDK_PATH=$(WASI_SDK) \ $(WASI_TOOLCHAIN) \ - LDFLAGS='-Wl,--export-dynamic,--stack-first,-z,stack-size=16777216' \ - XCFLAGS='-DWASM_SETJMP_STACK_BUFFER_SIZE=24576 -DWASM_FIBER_STACK_BUFFER_SIZE=24576 -DWASM_SCAN_STACK_BUFFER_SIZE=24576' \ + LDFLAGS='-Wl,--export-dynamic,--stack-first,-z,stack-size=16777216 $(WASI_LDFLAGS)' \ + XCFLAGS='-DWASM_SETJMP_STACK_BUFFER_SIZE=24576 -DWASM_FIBER_STACK_BUFFER_SIZE=24576 -DWASM_SCAN_STACK_BUFFER_SIZE=24576 $(WASI_CFLAGS)' \ debugflags=-g \ - cppflags= \ + cppflags='$(WASI_CXXFLAGS)' \ wasmoptflags='-O4 -g --pass-arg=asyncify-ignore-imports' \ $(shell $(RUBY) -e 'puts "ac_cv_func_dlopen=no" if RUBY_VERSION.split(".")[..1].join(".").to_f < 3.3') \ --disable-install-doc -$(DOWNLOADS)/ruby/configure: $(DOWNLOADS)/ruby/configure.ac $(RUBY) - cd $(DOWNLOADS)/ruby && $(RUBY) tool/downloader.rb -d tool -e gnu config.guess config.sub - cd $(DOWNLOADS)/ruby && $(AUTORECONF) -i +$(DOWNLOADS)/crossruby/configure: $(DOWNLOADS)/crossruby/configure.ac $(RUBY) + cd $(DOWNLOADS)/crossruby && $(RUBY) tool/downloader.rb -d tool -e gnu config.guess config.sub + cd $(DOWNLOADS)/crossruby && $(AUTORECONF) -i -$(DOWNLOADS)/ruby/configure.ac: +$(DOWNLOADS)/crossruby/configure.ac: mkdir -p $(DOWNLOADS) - $(CLONE) $(GITHUB)/ruby/ruby $(DOWNLOADS)/ruby -b ruby_$(shell echo $(RUBY_VERSION) | sed -e 's/\./_/g') - echo '#include "${PWD}/extra-ruby-bindings.h"' >> $(DOWNLOADS)/ruby/main.c + $(CLONE) $(GITHUB)/ruby/ruby $(DOWNLOADS)/crossruby -b ruby_$(shell echo $(RUBY_VERSION) | sed -e 's/\./_/g') + echo '#include "${PWD}/extra-ruby-bindings.h"' >> $(DOWNLOADS)/crossruby/main.c -# Base ruby +# Base Ruby (targets the build machine) $(RUBY): $(DOWNLOADS)/baseruby/Makefile cd $(DOWNLOADS)/baseruby && $(MAKE) install if ! $(RUBY) -e 'exit 1 if RUBY_VERSION.split(".")[..1].join(".").to_f < 3.2'; then echo -e '\e[91m[ERROR] Ruby version must be at least 3.2 because earlier versions do not have WebAssembly support\e[0m'; rm $(RUBY); exit 1; fi; $(DOWNLOADS)/baseruby/Makefile: $(DOWNLOADS)/baseruby/configure - cd $(DOWNLOADS)/baseruby && ./configure --prefix=$(LIBDIR) $(NATIVE_TOOLCHAIN) --disable-install-doc + cd $(DOWNLOADS)/baseruby && ./configure --prefix=$(LIBDIR) $(NATIVE_TOOLCHAIN) LDFLAGS='$(LDFLAGS)' XCFLAGS='$(CFLAGS)' cppflags='$(CXXFLAGS)' --disable-install-doc $(DOWNLOADS)/baseruby/configure: $(DOWNLOADS)/baseruby/configure.ac cd $(DOWNLOADS)/baseruby && $(AUTORECONF) -i @@ -150,7 +156,7 @@ $(LIBDIR)/usr/local/lib/libyaml.a: $(DOWNLOADS)/libyaml/Makefile cd $(DOWNLOADS)/libyaml && $(MAKE) install DESTDIR=$(LIBDIR) $(DOWNLOADS)/libyaml/Makefile: $(DOWNLOADS)/libyaml/configure - cd $(DOWNLOADS)/libyaml && ./configure --host $(TARGET) $(WASI_TOOLCHAIN) + cd $(DOWNLOADS)/libyaml && ./configure --host $(TARGET) $(WASI_TOOLCHAIN) CFLAGS='$(WASI_CFLAGS)' CXXFLAGS='$(WASI_CXXFLAGS)' LDFLAGS='$(WASI_LDFLAGS)' $(DOWNLOADS)/libyaml/configure: $(DOWNLOADS)/libyaml/configure.ac cd $(DOWNLOADS)/libyaml && $(AUTORECONF) -i @@ -165,7 +171,7 @@ $(LIBDIR)/usr/local/lib/libz.a: $(DOWNLOADS)/zlib/Makefile cd $(DOWNLOADS)/zlib && $(MAKE) install DESTDIR=$(LIBDIR) $(DOWNLOADS)/zlib/Makefile: $(DOWNLOADS)/zlib/configure - cd $(DOWNLOADS)/zlib && env CHOST=linux $(WASI_TOOLCHAIN) ./configure --static + cd $(DOWNLOADS)/zlib && CHOST=linux $(WASI_TOOLCHAIN) CFLAGS='$(WASI_CFLAGS)' CXXFLAGS='$(WASI_CXXFLAGS)' LDFLAGS='$(WASI_LDFLAGS)' ./configure --static $(DOWNLOADS)/zlib/configure: mkdir -p $(DOWNLOADS) @@ -177,7 +183,7 @@ $(LIBDIR)/usr/local/lib/libssl.a: $(DOWNLOADS)/openssl/Makefile cd $(DOWNLOADS)/openssl && $(MAKE) install_dev DESTDIR=$(LIBDIR) $(DOWNLOADS)/openssl/Makefile: $(DOWNLOADS)/openssl/Configure - cd $(DOWNLOADS)/openssl && ./Configure \ + cd $(DOWNLOADS)/openssl && CFLAGS='$(WASI_CFLAGS)' CXXFLAGS='$(WASI_CXXFLAGS)' LDFLAGS='$(WASI_LDFLAGS)' ./Configure \ gcc \ -static \ -no-asm \ diff --git a/retro/link.T b/retro/link.T new file mode 100644 index 00000000..d0465c4c --- /dev/null +++ b/retro/link.T @@ -0,0 +1,4 @@ +{ + global: retro_*; + local: *; +}; diff --git a/retro/sandbox-bindgen.rb b/retro/sandbox-bindgen.rb index 1fe6cd8b..81b5779e 100644 --- a/retro/sandbox-bindgen.rb +++ b/retro/sandbox-bindgen.rb @@ -34,6 +34,10 @@ MALLOC_FUNC = 'mkxp_sandbox_malloc' # The name of the `free()` binding defined in extra-ruby-bindings.h FREE_FUNC = 'mkxp_sandbox_free' +FIBER_ENTRY_POINT_FUNC = 'mkxp_sandbox_fiber_entry_point' +FIBER_ARG0_FUNC = 'mkxp_sandbox_fiber_arg0' +FIBER_ARG1_FUNC = 'mkxp_sandbox_fiber_arg1' + ################################################################################ IGNORED_FUNCTIONS = Set[ @@ -184,14 +188,14 @@ HEADER_START = <<~HEREDOC }; wasm_ptr_t next_func_ptr; - std::shared_ptr instance; + std::shared_ptr instance; std::unordered_map> fibers; wasm_ptr_t sbindgen_malloc(wasm_ptr_t); wasm_ptr_t sbindgen_create_func_ptr(); public: - bindings(std::shared_ptr); + bindings(std::shared_ptr); template struct stack_frame { friend struct bindings; @@ -204,9 +208,9 @@ HEADER_START = <<~HEREDOC static inline struct fiber &init_fiber(struct bindings &bind) { key_t key = { - w2c_ruby_mkxp_sandbox_fiber_entry_point(bind.instance.get()), - w2c_ruby_mkxp_sandbox_fiber_arg0(bind.instance.get()), - w2c_ruby_mkxp_sandbox_fiber_arg1(bind.instance.get()), + w2c_#{MODULE_NAME}_#{FIBER_ENTRY_POINT_FUNC}(bind.instance.get()), + w2c_#{MODULE_NAME}_#{FIBER_ARG0_FUNC}(bind.instance.get()), + w2c_#{MODULE_NAME}_#{FIBER_ARG1_FUNC}(bind.instance.get()), }; if (bind.fibers.count(key) == 0) { bind.fibers[key] = (struct fiber){.key = key};