Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add native ARM macOS build #707

Merged
merged 2 commits into from
Feb 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
89 changes: 85 additions & 4 deletions .github/workflows/build-test-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,8 @@ jobs:
installer/skytemple-*-install-*.exe

package-mac:
runs-on: macos-11
name: Build and package for Mac OS
runs-on: macos-12
name: Build and package for Mac OS (Intel)
steps:
- name: Checkout
uses: actions/checkout@v4
Expand Down Expand Up @@ -269,7 +269,7 @@ jobs:
- name: Upload .app
uses: actions/upload-artifact@v3
with:
name: skytemple-mac-app
name: skytemple-mac-app-x86_64
path: |
installer/skytemple-mac.zip

Expand All @@ -288,7 +288,88 @@ jobs:
- name: Upload .dmg
uses: actions/upload-artifact@v3
with:
name: skytemple-mac-dmg
name: skytemple-mac-dmg-x86_64
path: |
installer/SkyTemple*.dmg

package-mac-arm64:
runs-on: macos-14
name: Build and package for Mac OS (ARM)
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Rewrite version for dev if not tag
if: "!startsWith(github.ref, 'refs/tags/')"
run: |
perl -i -pe "s/version\s*=\s*\"(.*?)(\.rc.*|\.a.*|\.post.*)?\"/version=\"\1.dev0+${GITHUB_SHA::8}\"/" pyproject.toml
echo "IS_DEV_BUILD=1" >> $GITHUB_ENV
- name: Note version
run: |
python3 -m venv .yq-venv
. .yq-venv/bin/activate
pip install yq
echo "PACKAGE_VERSION=$(tomlq '.project.version' pyproject.toml -r)" >> $GITHUB_ENV
- name: Install and package
run: |
# git is already installed.
brew install enchant pygobject3 gtk+3 [email protected] gtksourceview4 adwaita-icon-theme sdl12-compat sdl2 meson cmake
pip3 install -U certifi
export PATH=/usr/local/opt/[email protected]/bin:/usr/local/bin:/opt/homebrew/bin:$PATH

# Install other dependencies and SkyTemple itself
pip3 install py-desmume 'pyinstaller~=5.0'
if [ -n "$IS_DEV_BUILD" ]; then
IS_MACOS=1 installer/install-skytemple-rust.sh x86_64
fi
pip3 install -r requirements-mac-windows.txt
# Generate MO localization files
installer/generate-mo.sh
pip3 install '.[eventserver]'
if [ -n "$IS_DEV_BUILD" ]; then
installer/install-skytemple-components-from-git.sh
fi

cd installer

# Install themes
curl https://skytemple.org/build_deps/Arc.zip -O
unzip Arc.zip > /dev/null
curl https://skytemple.org/build_deps/ZorinBlue.zip -O
unzip ZorinBlue.zip > /dev/null

# Download armips
curl https://skytemple.org/build_deps/mac/armips -O
chmod +x armips

# Package
./build-mac-arm.sh $PACKAGE_VERSION

# Creating a zip makes the artifact upload much faster since there are so many files
zip -r skytemple-mac.zip dist/SkyTemple.app > /dev/null

- name: Upload .app
uses: actions/upload-artifact@v3
with:
name: skytemple-mac-app-arm64
path: |
installer/skytemple-mac.zip

- name: Create installer
run: |
export PATH=/usr/local/opt/[email protected]/bin:/usr/local/bin:/opt/homebrew/bin:$PATH

# See https://github.com/sindresorhus/create-dmg
# create-dmg automatically generates an installer icon if imagemagick is installed
brew install graphicsmagick imagemagick
npm -g install create-dmg

# This tool returns exit code 2 if the DMG was created but code signing failed for some reason
npx create-dmg --dmg-title=SkyTemple installer/dist/SkyTemple.app installer || true

- name: Upload .dmg
uses: actions/upload-artifact@v3
with:
name: skytemple-mac-dmg-arm64
path: |
installer/SkyTemple*.dmg

Expand Down
41 changes: 41 additions & 0 deletions installer/build-mac-arm.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/bin/bash

# Call with "PACKAGE_VERSION=[version number] ./build-mac.sh"
# The version from the current pip install of SkyTemple is used if no version number is set.
set -e

generate_version_file() {
location=$(pip3 show $1 | grep Location | cut -d":" -f 2 | cut -c2-)
pip3 show $1 | grep Version | cut -d":" -f 2 | cut -c2- > $location/$1/VERSION
}

# The VERSION files for a few dependencies are missing for some reason, so generate them from 'pip show' commands
generate_version_file cssselect2
generate_version_file tinycss2
generate_version_file cairosvg
generate_version_file cairocffi

# Create the icon
# https://www.codingforentrepreneurs.com/blog/create-icns-icons-for-macos-apps
mkdir skytemple.iconset
icons_path=../skytemple/data/icons/hicolor
cp $icons_path/16x16/apps/skytemple.png skytemple.iconset/icon_16x16.png
cp $icons_path/32x32/apps/skytemple.png skytemple.iconset/[email protected]
cp $icons_path/32x32/apps/skytemple.png skytemple.iconset/icon_32x32.png
cp $icons_path/64x64/apps/skytemple.png skytemple.iconset/[email protected]
cp $icons_path/128x128/apps/skytemple.png skytemple.iconset/icon_128x128.png
cp $icons_path/256x256/apps/skytemple.png skytemple.iconset/[email protected]
cp $icons_path/256x256/apps/skytemple.png skytemple.iconset/icon_256x256.png
cp $icons_path/512x512/apps/skytemple.png skytemple.iconset/[email protected]
cp $icons_path/512x512/apps/skytemple.png skytemple.iconset/icon_512x512.png

iconutil -c icns skytemple.iconset
rm -rf skytemple.iconset

# Build the app
pyinstaller skytemple-mac.spec --noconfirm
rm skytemple.icns

# Since the library search path for the app is wrong, execute a shell script that sets is correctly
# and launches the app instead of launching run_skytemple directly
appdir=dist/SkyTemple.app/Contents/MacOS
9 changes: 0 additions & 9 deletions installer/build-mac.sh
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,6 @@ install_name_tool -add_rpath @executable_path/. dist/SkyTemple.app/Contents/MacO
cat > $appdir/pre_run_skytemple << EOF
#!/bin/sh

# Fix paths
export DYLD_LIBRARY_PATH="\$(dirname \$0)"
export PYENCHANT_LIBRARY_PATH="\$(dirname \$0)/libenchant-2.dylib"
export PATH="\$PATH:\$(dirname \$0)/skytemple_files/_resources"

# Disable SDL video. It's not used and would only be required with joystick/gamepad support,
# but alas it doesn't work because Cococa only supports I/O from the main thread.
export SDL_VIDEODRIVER=dummy

# Fix the language 🥲
# The output of "defaults read -g AppleLanguages" looks like this, so we need to extract
# the language code and replace "-" with "_".
Expand Down
7 changes: 6 additions & 1 deletion installer/install-skytemple-rust.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@ branch="master"
wheel_name="skytemple_rust-*-cp311-cp311-win_amd64.whl"

if [ -n "$IS_MACOS" ]; then
wheel_name="skytemple_rust-*-cp311-cp311-macosx_10_9_x86_64.whl"
# Check if we're running on Apple Silicon
if [ "$(uname -m)" = "arm64" ]; then
wheel_name="skytemple_rust-*-cp311-cp311-macosx_11_0_arm64.whl"
else
wheel_name="skytemple_rust-*-cp311-cp311-macosx_10_9_x86_64.whl"
fi
fi

url="https://nightly.link/SkyTemple/skytemple-rust/workflows/build-test-publish/$branch/wheels.zip"
Expand Down
17 changes: 11 additions & 6 deletions installer/skytemple-mac.spec
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ from PyInstaller.utils.hooks import collect_entry_point, copy_metadata
pkg_path = os.path.abspath(os.path.join("..", "skytemple"))
site_packages = next(p for p in sys.path if "site-packages" in p)

# The Homebrew lib path is different on ARM and Intel
homebrew_path = os.path.join(os.sep, "usr", "local")
if os.uname().machine == "arm64":
homebrew_path = os.path.join(os.sep, "opt", "homebrew")

additional_datas = [
(os.path.join(pkg_path, "data"), "data"),
(os.path.join(pkg_path, "*.glade"), "."),
Expand Down Expand Up @@ -72,29 +77,29 @@ additional_datas = [
additional_binaries = [
(os.path.join(site_packages, "desmume", "libdesmume.dylib"), "."),
(
os.path.join(os.sep, "usr", "local", "lib", "libSDL-1.2.0.dylib"),
os.path.join(homebrew_path, "lib", "libSDL-1.2.0.dylib"),
".",
), # Must be installed with Homebrew
(
os.path.join(os.sep, "usr", "local", "lib", "libSDL2-2.0.0.dylib"),
os.path.join(homebrew_path, "lib", "libSDL2-2.0.0.dylib"),
".",
), # Must be installed with Homebrew
(
os.path.join(os.sep, "usr", "local", "lib", "libenchant-2.dylib"),
os.path.join(homebrew_path, "lib", "libenchant-2.dylib"),
".",
), # Must be installed with Homebrew
(
os.path.join(os.sep, "usr", "local", "lib", "libaspell.15.dylib"),
os.path.join(homebrew_path, "lib", "libaspell.15.dylib"),
".",
), # Gets installed with Enchant
(
os.path.join(
os.sep, "usr", "local", "lib", "enchant-2", "enchant_applespell.so"
homebrew_path, "lib", "enchant-2", "enchant_applespell.so"
),
".",
), # Gets installed with Enchant
(
os.path.join(os.sep, "usr", "local", "opt", "cairo", "lib", "libcairo.2.dylib"),
os.path.join(homebrew_path, "opt", "cairo", "lib", "libcairo.2.dylib"),
".",
),
(os.path.join(site_packages, "libtilequant.dylib"), "."),
Expand Down
13 changes: 13 additions & 0 deletions skytemple/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,26 @@
import sys
import locale
import gettext
from pathlib import Path
from skytemple.core.ui_utils import data_dir, APP, builder_get_assert
from skytemple.core.settings import SkyTempleSettingsStore
from skytemple_files.common.impl_cfg import (
ENV_SKYTEMPLE_USE_NATIVE,
change_implementation_type,
)

# Workaround for errors on macOS when running from a bundle
if sys.platform.startswith("darwin"):
base_path = Path(__file__).parent.absolute().as_posix()
os.chdir(base_path) # Set working directory to the executable directory
os.environ["DYLD_LIBRARY_PATH"] = base_path
os.environ["PATH"] = f"{base_path}/skytemple_files/_resources:{os.environ['PATH']}"
os.environ["PYENCHANT_LIBRARY_PATH"] = f"{base_path}/libenchant-2.dylib"

# Disable SDL video. It's not used and would only be required with joystick/gamepad support,
# but alas it doesn't work because Cococa only supports I/O from the main thread.
os.environ["SDL_VIDEODRIVER"] = "dummy" # Fixes the debugger not loading

settings = SkyTempleSettingsStore()

# Setup Sentry
Expand Down
Loading