Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
1e479a4
test: does current master branch build
culix-7 Jan 8, 2026
4ca0d33
Build: Fix `ui` build target
culix-7 Jan 8, 2026
610c228
Build: Add `zip` to linux build steps
culix-7 Jan 8, 2026
6726018
Build: move linux + mac PreBuild steps to script
culix-7 Jan 8, 2026
0b4be4a
Build: Update `macos` image from `13` to `14`
culix-7 Jan 8, 2026
c8303a2
Build: ensure `no-self-contained` flag is parsed correctly
culix-7 Jan 8, 2026
9126c0a
Build: Don't run signing steps on PRs
culix-7 Jan 8, 2026
a836774
Build: Try to save time on mac by not updating homebrew
culix-7 Jan 8, 2026
3410140
Build: use `find` to get mac app path for publishing
culix-7 Jan 8, 2026
99d85ac
Build: Add `test-env` target for Make
culix-7 Jan 8, 2026
0e6de94
Build: Refactor make lines to test the `UNAME` output
culix-7 Jan 8, 2026
11ed17c
Build: add one env test target for devs, and one for CI
culix-7 Jan 8, 2026
6b60f3c
Build: Add steps to verify the env works correctly before building
culix-7 Jan 8, 2026
6e3021a
Build: Add new failing tests for the cloud CI builds that are failing
culix-7 Jan 8, 2026
942f325
Build: Refactor verify-env to accept the build matrix value
culix-7 Jan 8, 2026
5707c8a
Build: Update tests to pass what the build script passes
culix-7 Jan 8, 2026
7aa1e3f
Build: Fix architecture detection
culix-7 Jan 8, 2026
827a512
Build: move `ARCH` arg to the end in tests
culix-7 Jan 8, 2026
7f69138
Build: Add more tests for command currently failing on mac
culix-7 Jan 8, 2026
3905663
Build: Improve arch platform detection
culix-7 Jan 8, 2026
293278b
Build: Change zip step to only search in the main bin folder
culix-7 Jan 8, 2026
c4fe530
Build: Add `compiler` to mac artifact name
culix-7 Jan 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 35 additions & 5 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
name: Build Mesen

on: [push]
on:
push:
branches: [ master ]
pull_request:

env:
DOTNET_CLI_TELEMETRY_OPTOUT: 1
Expand Down Expand Up @@ -90,13 +93,17 @@ jobs:
- name: Install dependencies
run: |
sudo apt-get update -qy
sudo apt-get install -qy libsdl2-dev ccache # The compilers are already installed on GitHub's runners.
sudo apt-get install -qy libsdl2-dev ccache zip # The compilers are already installed on GitHub's runners.

- name: Setup CCache
uses: ./.github/actions/setup-ccache-action

- name: Write commit SHA1 to file
uses: ./.github/actions/build-sha1-action

# Verify the Makefile correctly detects this specific Linux environment
- name: Verify Environment
run: make verify-env ${{ matrix.make_flags }} REQ_PLAT=linux-${{ matrix.platform.path }} ARCH=${{ matrix.platform.path }}

# stderr is not detected as a TTY, so diagnostics are by default printed without colours;
# forcing colours makes the log a little nicer to read.
Expand Down Expand Up @@ -157,7 +164,7 @@ jobs:
matrix:
compiler: [clang, clang_aot]
platform: [
{os: macos-13, arch: x64},
{os: macos-14, arch: x64},
{os: macos-14, arch: arm64}
]
include:
Expand All @@ -167,6 +174,10 @@ jobs:
make_flags: "USE_AOT=true"
fail-fast: false
runs-on: ${{ matrix.platform.os }}
env:
# try to save time by preventing brew updates
HOMEBREW_NO_AUTO_UPDATE: 1
HOMEBREW_NO_INSTALL_CLEANUP: 1

steps:
- name: Checkout repo
Expand Down Expand Up @@ -195,13 +206,18 @@ jobs:
- name: Write commit SHA1 to file
uses: ./.github/actions/build-sha1-action

# Verify the Makefile correctly detects this specific macOS environment
- name: Verify Environment
run: make verify-env ${{ matrix.make_flags }} REQ_PLAT=osx-${{ matrix.platform.arch }} ARCH=${{ matrix.platform.arch }}

# stderr is not detected as a TTY, so diagnostics are by default printed without colours;
# forcing colours makes the log a little nicer to read.
- name: Build Mesen
run: |
${{ matrix.make_flags }} make -j$(sysctl -n hw.logicalcpu)

- name: Sign binary
if: github.event_name != 'pull_request' && env.CERT_DATA != ''
env:
APP_NAME: bin/osx-${{ matrix.platform.arch }}/Release/osx-${{ matrix.platform.arch }}/publish/Mesen.app
CERT_DATA: ${{ secrets.MACOS_CERTIFICATE }}
Expand All @@ -228,10 +244,24 @@ jobs:

- name: Zip Mesen.app
run: |
ditto -c -k --sequesterRsrc --keepParent bin/osx-${{ matrix.platform.arch }}/Release/osx-${{ matrix.platform.arch }}/publish/Mesen.app bin/osx-${{ matrix.platform.arch }}/Release/Mesen.app.zip
# Find the Mesen.app folder regardless of exactly which subfolder it landed in
APP_PATH=$(find bin -name "Mesen.app" -type d | head -n 1)

if [ -z "$APP_PATH" ]; then
echo "::error::Could not find Mesen.app in bin directory!"
exit 1
fi

echo "Found app at: $APP_PATH"

# Create the destination directory if it doesn't exist
mkdir -p bin/osx-${{ matrix.platform.arch }}/Release/

# Zip it up
ditto -c -k --sequesterRsrc --keepParent "$APP_PATH" bin/osx-${{ matrix.platform.arch }}/Release/Mesen.app.zip

- name: Upload Mesen
uses: actions/upload-artifact@v4
with:
name: Mesen (macOS - ${{ matrix.platform.os }} - ${{ matrix.compiler }})
name: Mesen (macOS - ${{ matrix.platform.os }} - ${{ matrix.platform.arch }} - ${{ matrix.compiler }})
path: bin/osx-${{ matrix.platform.arch }}/Release/Mesen.app.zip
11 changes: 6 additions & 5 deletions UI/UI.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -653,12 +653,13 @@
<Exec Command="cd $(OutDir)&#xD;&#xA;rd Dependencies /s /q&#xD;&#xA;md Dependencies&#xD;&#xA;xcopy /s $(ProjectDir)Dependencies\* Dependencies&#xD;&#xA;copy libHarfBuzzSharp.dll Dependencies&#xD;&#xA;copy libSkiaSharp.dll Dependencies&#xD;&#xA;copy MesenCore.dll Dependencies&#xD;&#xA;cd Dependencies&#xD;&#xA;del ..\Dependencies.zip&#xD;&#xA;powershell Compress-Archive -Path * -DestinationPath '..\Dependencies.zip' -Force&#xD;&#xA;copy ..\Dependencies.zip $(ProjectDir)" />
</Target>

<Target Name="PreBuildLinux" BeforeTargets="PreBuildEvent" Condition="'$(RuntimeIdentifier)'=='linux-x64' Or '$(RuntimeIdentifier)'=='linux-arm64'">
<Exec Command="cd $(OutDir)&#xD;&#xA;rm -rf Dependencies&#xD;&#xA;mkdir Dependencies&#xD;&#xA;cp -R $(ProjectDir)/Dependencies/* Dependencies&#xD;&#xA;cp libHarfBuzzSharp.so Dependencies&#xD;&#xA;cp libSkiaSharp.so Dependencies&#xD;&#xA;cp MesenCore.so Dependencies&#xD;&#xA;cd Dependencies&#xD;&#xA;rm ../Dependencies.zip&#xD;&#xA;zip -r ../Dependencies.zip *&#xD;&#xA;cp ../Dependencies.zip $(ProjectDir)" />
<Target Name="PreBuildLinux" BeforeTargets="PreBuildEvent" Condition="$(RuntimeIdentifier.StartsWith('linux-'))">
<Exec Command="/bin/bash ./prebuild_linux_mac.sh linux '../bin/$(RuntimeIdentifier)/$(Configuration)/' '$(ProjectDir)'" WorkingDirectory="$(ProjectDir)" />
</Target>

<Target Name="PreBuildOsx" BeforeTargets="PreBuildEvent" Condition="'$(RuntimeIdentifier)'=='osx-x64' Or '$(RuntimeIdentifier)'=='osx-arm64'">
<Exec Command="cp ./Assets/MesenIcon.icns $(OutDir)&#xD;&#xA;cd $(OutDir)&#xD;&#xA;rm -R Dependencies&#xD;&#xA;mkdir Dependencies&#xD;&#xA;cp -R $(ProjectDir)/Dependencies/* Dependencies&#xD;&#xA;cp libHarfBuzzSharp.dylib Dependencies&#xD;&#xA;cp libSkiaSharp.dylib Dependencies&#xD;&#xA;cp MesenCore.dylib Dependencies&#xD;&#xA;cd Dependencies&#xD;&#xA;rm ../Dependencies.zip&#xD;&#xA;zip -r ../Dependencies.zip *&#xD;&#xA;cp ../Dependencies.zip $(ProjectDir)" />

<Target Name="PreBuildOsx" BeforeTargets="PreBuildEvent" Condition="$(RuntimeIdentifier.StartsWith('osx-'))">
<Exec Command="cp &quot;$(ProjectDir)Assets/MesenIcon.icns&quot; &quot;$(OutDir)&quot;" />
<Exec Command="/bin/bash ./prebuild_linux_mac.sh osx '../bin/$(RuntimeIdentifier)/$(Configuration)/' '$(ProjectDir)'" WorkingDirectory="$(ProjectDir)" />
</Target>

</Project>
39 changes: 39 additions & 0 deletions UI/prebuild_linux_mac.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#!/bin/bash

set -e # Exit immediately if a command fails

PLATFORM=$1 # 'linux' or 'osx'
OUTDIR="${2%/}" # remove trailing slash if present
PROJECTDIR="${3%/}"
EXT="so"
if [ "$PLATFORM" == "osx" ]; then EXT="dylib"; fi

echo "Packaging dependencies for $PLATFORM..."

mkdir -p "$OUTDIR"
cd "$OUTDIR"
rm -rf Dependencies
mkdir -p Dependencies

# 1. Copy static assets
if [ -d "$PROJECTDIR/Dependencies" ]; then
cp -R "$PROJECTDIR/Dependencies/." Dependencies/
fi

# 2. Copy native libs (with a fallback check)
# NuGet puts these in runtimes/[rid]/native/ - we look there if not in root
LIBS=("libHarfBuzzSharp.$EXT" "libSkiaSharp.$EXT" "MesenCore.$EXT")

for LIB in "${LIBS[@]}"; do
FOUND_LIB=$(find . -name "$LIB" -print -quit)
if [ -n "$FOUND_LIB" ]; then
cp "$FOUND_LIB" Dependencies/
fi
done

# 3. Zip it up
cd Dependencies
rm -f ../Dependencies.zip
zip -r ../Dependencies.zip .

mv ../Dependencies.zip "$PROJECTDIR/"
99 changes: 92 additions & 7 deletions makefile
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,19 @@ LINKCHECKUNRESOLVED := -Wl,-z,defs

LINKOPTIONS :=
MESENOS :=
UNAME_S := $(shell uname -s)

# Use ?= so we can override these during testing
UNAME_S ?= $(shell uname -s)
MACHINE ?= $(shell uname -m)

ifneq ($(ARCH),)
ifneq (,$(findstring x64,$(ARCH))$(findstring x86_64,$(ARCH)))
override MACHINE := x86_64
endif
ifneq (,$(findstring arm64,$(ARCH))$(findstring aarch64,$(ARCH)))
override MACHINE := aarch64
endif
endif

ifeq ($(UNAME_S),Linux)
MESENOS := linux
Expand All @@ -42,22 +54,20 @@ endif

MESENFLAGS += -m64

MACHINE := $(shell uname -m)
ifeq ($(MACHINE),x86_64)
MESENPLATFORM := $(MESENOS)-x64
endif
ifneq ($(filter %86,$(MACHINE)),)
MESENPLATFORM := $(MESENOS)-x64
endif
# TODO: this returns `aarch64` on one of my machines...
ifneq ($(filter arm%,$(MACHINE)),)
MESENPLATFORM := $(MESENOS)-arm64
endif
ifeq ($(MACHINE),aarch64)
MESENPLATFORM := $(MESENOS)-arm64
ifeq ($(USE_GCC),true)
#don't set -m64 on arm64 for gcc (unrecognized option)
MESENFLAGS=
MESENFLAGS := $(filter-out -m64,$(MESENFLAGS))
endif
endif

Expand Down Expand Up @@ -131,7 +141,7 @@ endif
ifeq ($(USE_AOT),true)
PUBLISHFLAGS ?= -r $(MESENPLATFORM) -p:PublishSingleFile=false -p:PublishAot=true -p:SelfContained=true
else
PUBLISHFLAGS ?= -r $(MESENPLATFORM) --no-self-contained true -p:PublishSingleFile=true
PUBLISHFLAGS ?= -r $(MESENPLATFORM) --no-self-contained -p:PublishSingleFile=true
endif


Expand Down Expand Up @@ -202,10 +212,11 @@ ui: InteropDLL/$(OBJFOLDER)/$(SHAREDLIB)
mkdir -p $(OUTFOLDER)/Dependencies
rm -fr $(OUTFOLDER)/Dependencies/*
cp InteropDLL/$(OBJFOLDER)/$(SHAREDLIB) $(OUTFOLDER)/$(SHAREDLIB)
chmod +x UI/prebuild_linux_mac.sh
#Called twice because the first call copies native libraries to the bin folder which need to be included in Dependencies.zip
#Don't run with AOT flags the first time to reduce build duration
cd UI && dotnet publish -c $(BUILD_TYPE) $(OPTIMIZEUI) -r $(MESENPLATFORM)
cd UI && dotnet publish -c $(BUILD_TYPE) $(OPTIMIZEUI) $(PUBLISHFLAGS)
dotnet publish UI/UI.csproj -c $(BUILD_TYPE) $(OPTIMIZEUI) -r $(MESENPLATFORM)
dotnet publish UI/UI.csproj -c $(BUILD_TYPE) $(OPTIMIZEUI) $(PUBLISHFLAGS)

core: InteropDLL/$(OBJFOLDER)/$(SHAREDLIB)

Expand Down Expand Up @@ -243,3 +254,77 @@ clean:
rm -r -f $(LUAOBJ)
rm -r -f $(MACOSOBJ)
rm -r -f $(DLLOBJ)


# --- Environment Reporting & Validation ---

# Shared format for the table rows
PRINT_FORMAT := "%-15s | %-13s %-11s | %-13s %-11s | %-10s\n"

.PHONY: verify-env verify-all-env test-env-row

# Hide the "make[1]: Entering directory..." noise
.SILENT: test-env-row

# Target for CI: verify the current machine's environment
verify-env:
@echo "Checking Build Environment..."
@printf $(PRINT_FORMAT) "INPUT" "MATRIX_REQ" "" "ACTUAL" "" "RESULT"
# compare what the matrix requested (REQ_PLAT) vs what the Makefile found (MESENPLATFORM)
@$(MAKE) --no-print-directory test-env-row \
E_PLAT="$(if $(REQ_PLAT),$(REQ_PLAT),$(MESENPLATFORM))" \
E_FLAGS="$(filter -m64,$(MESENFLAGS))"

verify-all-env:
@rm -f .test_failed
@echo "Validating Makefile Architecture Detection Logic:"
@echo "----------------------------------------------------------------------------------------------------------"
@printf $(PRINT_FORMAT) "INPUT" "EXPECTED (PLAT/FLAGS)" "" "ACTUAL (PLAT/FLAGS)" "" "RESULT"
@echo "----------------------------------------------------------------------------------------------------------"
@# --- TEST GROUP 1: Auto-Detection (No ARCH param) ---
@# These prove the Makefile works out-of-the-box on different hardware
@$(MAKE) --no-print-directory test-env-row UNAME_S=Linux MACHINE=x86_64 E_PLAT=linux-x64 E_FLAGS="-m64" || touch .test_failed
@$(MAKE) --no-print-directory test-env-row UNAME_S=Linux MACHINE=aarch64 E_PLAT=linux-arm64 E_FLAGS="" USE_GCC=true || touch .test_failed
@$(MAKE) --no-print-directory test-env-row UNAME_S=Darwin MACHINE=x86_64 E_PLAT=osx-x64 E_FLAGS="-m64" || touch .test_failed
@$(MAKE) --no-print-directory test-env-row UNAME_S=Darwin MACHINE=arm64 E_PLAT=osx-arm64 E_FLAGS="-m64" || touch .test_failed

@# --- TEST GROUP 2: Explicit Overrides (With ARCH param) ---
@# These prove the Makefile correctly ignores the hardware when told to
@# Case: On x86_64 hardware, but ARCH says arm64
@$(MAKE) --no-print-directory test-env-row UNAME_S=Linux MACHINE=x86_64 E_PLAT=linux-arm64 E_FLAGS="" ARCH=arm64 USE_GCC=true || touch .test_failed

@# Case: On ARM64 hardware, but ARCH says x64
@$(MAKE) --no-print-directory test-env-row UNAME_S=Darwin MACHINE=arm64 E_PLAT=osx-x64 E_FLAGS="-m64" ARCH=x64 || touch .test_failed

@# Case: Using alternate naming (aarch64) in the ARCH param
@$(MAKE) --no-print-directory test-env-row UNAME_S=Linux MACHINE=x86_64 E_PLAT=linux-arm64 E_FLAGS="" ARCH=aarch64 USE_GCC=true || touch .test_failed

@# --- TEST GROUP 3: Resilient Naming (Fuzzy ARCH matching) ---
@# Test: ARCH contains the full platform name (common in CI)
@$(MAKE) --no-print-directory test-env-row UNAME_S=Darwin MACHINE=arm64 ARCH=osx-x64 E_PLAT=osx-x64 E_FLAGS="-m64" || touch .test_failed

@# Test: ARCH contains extra spaces or different casing (handled by findstring)
@$(MAKE) --no-print-directory test-env-row UNAME_S=Linux MACHINE=x86_64 ARCH=linux-arm64 E_PLAT=linux-arm64 E_FLAGS="" USE_GCC=true || touch .test_failed
@echo "----------------------------------------------------------------------------------------------------------"
@if [ -f .test_failed ]; then \
rm .test_failed; \
echo "Verification FAILED!"; \
exit 1; \
else \
echo "Verification Complete. All tests PASSED."; \
fi

test-env-row:
$(eval ACTUAL_FLAGS := $(strip $(filter -m64,$(MESENFLAGS))))
$(eval EXP_FLAGS := $(strip $(E_FLAGS)))
$(eval PLAT_MATCH := $(if $(filter $(E_PLAT),$(MESENPLATFORM)),OK,FAIL))
$(eval FLAG_MATCH := $(if $(subst $(EXP_FLAGS),,$(ACTUAL_FLAGS))$(subst $(ACTUAL_FLAGS),,$(EXP_FLAGS)),FAIL,OK))
$(eval PASS := $(if $(filter OKOK,$(PLAT_MATCH)$(FLAG_MATCH)),PASS,FAIL))
@printf $(PRINT_FORMAT) \
"$(UNAME_S)-$(MACHINE)" \
"$(E_PLAT)" "$(EXP_FLAGS)" \
"$(MESENPLATFORM)" "$(ACTUAL_FLAGS)" \
"$(PASS)"
@# Return a non-zero exit code ONLY so verify-all-env can catch it with ||
@if [ "$(PASS)" = "FAIL" ]; then exit 1; fi