Skip to content

Commit

Permalink
macOS specific build tweaks (#150)
Browse files Browse the repository at this point in the history
* Merge universal2 debloat script into prep

This additionally skips building the wheel if it has already been built (e.g. for rapid development).

* Enforce x86_64 for build environment

This ensures that x86_64 is leveraged for our pure x86_64 builds even on arm64 runners.

* Ensure setup.py is removed when building

* Resolve test invocation issues

* Leverage open for test invocation

* Enforce x86_64 Python for x86_64-only build

* Enforce universal2 Python
  • Loading branch information
spotlightishere committed Jun 2, 2024
1 parent 6909643 commit 149d240
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 66 deletions.
52 changes: 29 additions & 23 deletions .github/workflows/actions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ jobs:
- pyqt5
steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: 3.11.4
Expand All @@ -34,7 +33,7 @@ jobs:
run: mv client/dist/NSO-RPC.exe client/dist/NSO-RPC-qt5.exe
- name: Upload Build
if: github.event_name != 'pull_request'
uses: softprops/action-gh-release@v2.0.4
uses: softprops/action-gh-release@v2
with:
files: |
client/dist/NSO-RPC*.exe
Expand All @@ -53,58 +52,65 @@ jobs:
continue-on-error: false
- name: Upload Build
if: github.event_name != 'pull_request'
uses: softprops/action-gh-release@v2.0.4
uses: softprops/action-gh-release@v2
with:
files: scripts/linux.sh
build-macos:
name: Build NSO-RPC - MacOS
runs-on: macos-12
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: 3.11.4
architecture: x64
# We initially use `arch -x86_64` to ensure that we use an x86_64 version
# of Python, regardless of the host architecture.
# Subsequent invocations will all use the x86_64 `python3` binary within the venv.
- name: Build
run: >
cd scripts &&
./build.sh &&
cd ../client/dist &&
bash ../../scripts/tests/macos_test.sh &&
rm output.log &&
arch -x86_64 /bin/bash ./scripts/build.sh
- name: Test
run: >
arch -x86_64 /bin/bash ./scripts/tests/macos_test.sh
- name: Create Distributions
run: >
cd ./client/dist &&
ln -s /Applications "Applications (admin)" &&
hdiutil create -fs HFS+ -srcfolder . -volname NSO-RPC mac-installer.dmg &&
zip -yr mac-portable.zip NSO-RPC.app/
- name: Upload Build
if: github.event_name != 'pull_request'
uses: softprops/action-gh-release@v2.0.4
uses: softprops/action-gh-release@v2
with:
files: |
client/dist/mac-installer.dmg
client/dist/mac-portable.zip
build-universal2:
name: Build NSO-RPC - Universal2
runs-on: macos-12
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: 3.11.4
- name: Install Python 3.11.4 and build NSO-RPC
- name: Install Universal Python 3.11.4
run: >
curl https://www.python.org/ftp/python/3.11.4/python-3.11.4-macos11.pkg -o python-3.11.4-macos11.pkg &&
sudo installer -verbose -pkg python-3.11.4-macos11.pkg -target / &&
sudo installer -verbose -pkg python-3.11.4-macos11.pkg -target /
- name: Build
run: >
alias python3=python3.11 &&
cd scripts/macos-universal2 &&
bash ./build.sh &&
cd ../../client/dist &&
bash ../../scripts/tests/macos_test.sh &&
rm output.log &&
bash ./scripts/macos-universal2/build.sh
- name: Test
run: >
bash ./scripts/tests/macos_test.sh
- name: Create Distributions
run: >
cd client/dist &&
ln -s /Applications "Applications (admin)" &&
hdiutil create -fs HFS+ -srcfolder . -volname NSO-RPC mac-universal2-installer.dmg &&
zip -yr mac-universal2-portable.zip NSO-RPC.app/
- name: Upload NSO-RPC Universal2 Build
if: github.event_name != 'pull_request'
uses: softprops/action-gh-release@v2.0.4
uses: softprops/action-gh-release@v2
with:
files: |
client/dist/mac-universal2-installer.dmg
Expand All @@ -125,6 +131,6 @@ jobs:
hash-type: sha256
file-name: checksums.txt
get-assets: true
- uses: softprops/action-gh-release@v2.0.4
- uses: softprops/action-gh-release@v2
with:
files: checksums.txt
14 changes: 13 additions & 1 deletion scripts/build.sh
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
#!/bin/bash
set -e

# Run everything relative to our script directory.
cd "$(dirname "$0")"

# Activate a virtual environment so we don't pollute the system environment.
python3 -m venv --upgrade-deps venv
source venv/bin/activate
cd ../client

# As this is an x86_64 only version, ensure `py2app` outputs x86_64.
python3 -m pip install -r requirements.txt pyqt6 py2app GitPython
python3 _version.py
rm setup.py

# Recreate our setup.py with macOS-specific options.
if [ -f setup.py ]; then
rm setup.py
fi
py2applet --make-setup app.py icon.icns "icon.png" "taskbarDark.png" "taskbarLight.png" "version.txt"
sed -i '' -e "s/)/ name='NSO-RPC')/" setup.py
python3 setup.py py2app --arch=x86_64 -O2
Expand Down
29 changes: 26 additions & 3 deletions scripts/macos-universal2/build.sh
Original file line number Diff line number Diff line change
@@ -1,17 +1,40 @@
#!/bin/bash
set -e

# Run everything relative to our script directory.
cd "$(dirname "$0")"

# Within GitHub Actions and similar, we should use the Python.org
# copy of Python 3.11 available. This permits a universal2 framework for py2app.
# (Otherwise, GitHub's default runners include a single architecture version.)
if [[ "$CI" == "true" ]]; then
shopt -s expand_aliases
alias python3=/Library/Frameworks/Python.framework/Versions/3.11/bin/python3.11
fi

# Activate a virtual environment so we don't pollute the system environment.
python3 -m venv --upgrade-deps venv
source venv/bin/activate
python3 -m pip install wheel PyQt6
python3 -m pip install wheel

# Before we install anything further, create our custom universal2 version of
# the PyQt6 frameworks (PyQt6_Qt6), and then install PyQt6 itself.
bash prep-PyQt.sh
python3 -m pip install PyQt6

# Lastly, build our client.
cd ../../client
python3 -m pip install -r requirements.txt py2app GitPython
python3 _version.py
rm setup.py

# Recreate our setup.py with macOS-specific options.
if [ -f setup.py ]; then
rm setup.py
fi
py2applet --make-setup app.py icon.icns "icon.png" "taskbarDark.png" "taskbarLight.png" "version.txt"
# build universal binary
sed -i '' -e "s/)/ name='NSO-RPC')/" setup.py
python3 setup.py py2app -O2 --arch=universal2
python3 ../scripts/macos-universal2/debloat-qt.py
# arm64 requires codesigning to run
codesign --deep --force --sign - dist/NSO-RPC.app/Contents/MacOS/*
open dist
30 changes: 0 additions & 30 deletions scripts/macos-universal2/debloat-qt.py

This file was deleted.

54 changes: 47 additions & 7 deletions scripts/macos-universal2/prep-PyQt.sh
Original file line number Diff line number Diff line change
@@ -1,18 +1,35 @@
#!/bin/bash
set -e

# Provided by @spotlightishere on Github
# https://github.com/MCMi460/NSO-RPC/pull/86#issuecomment-1605700512

# Within GitHub Actions and similar, we should use the Python.org
# copy of Python 3.11 available. This permits a universal2 framework for py2app.
# (Otherwise, GitHub's default runners include a single architecture version.)
if [[ "$CI" == "true" ]]; then
shopt -s expand_aliases
alias python3=/Library/Frameworks/Python.framework/Versions/3.11/bin/python3.11
fi

# If we already have a universal2 wheel available, install and process no further.
# https://stackoverflow.com/a/6364244
if compgen -G "./PyQt6_*universal2.whl"; then
python3 -m pip install PyQt6_*universal2.whl --force-reinstall
exit 0
fi

# Download and unpack
python3 -m pip download --only-binary=:all: --platform=macosx_13_0_x86_64 PyQt6_Qt6
python3 -m pip download --only-binary=:all: --platform=macosx_13_0_arm64 PyQt6_Qt6
python3 -m wheel unpack PyQt6_*arm64.whl --dest arm64
python3 -m wheel unpack PyQt6_*x86_64.whl --dest x86_64
python3 -m wheel unpack PyQt6_Qt6*arm64.whl --dest arm64
python3 -m wheel unpack PyQt6_Qt6*x86_64.whl --dest x86_64

# We'll use x86_64 as our basis.
# As of writing, PyQt6_Qt6 specifies a minimum of 10.14 for x86_64, and 11.0 for arm64.
# We'll reuse this tag; it should be updated if this ever changes in the future.
python3 -m wheel tags --platform-tag macosx_10_14_universal2 PyQt6_*x86_64.whl
python3 -m wheel unpack PyQt6_*universal2.whl --dest universal
python3 -m wheel tags --platform-tag macosx_10_14_universal2 PyQt6_Qt6*x86_64.whl
python3 -m wheel unpack PyQt6_Qt6*universal2.whl --dest universal

# https://stackoverflow.com/a/46020381
merge_frameworks() {
Expand All @@ -22,9 +39,32 @@ merge_frameworks() {
}
export -f merge_frameworks

# Iterate through all frameworks and libraries, and lipo together
# Iterate through all frameworks and libraries, and lipo together.
find universal -perm +111 -type f -exec sh -c 'merge_frameworks "$1"' _ {} \;
python3 -m wheel pack universal/PyQt6_*

# Finally, install our universal python3.11 -m wheel.
# We can now debloat our created universal wheel by removing
# frameworks and libraries irrelevant to NSO-RPC.
debloat_paths=(
"Qt6/lib/QtQuick3DRuntimeRender.framework"
"Qt6/lib/QtQuickParticles.framework"
"Qt6/lib/QtSpatialAudio.framework"
"Qt6/lib/QtShaderTools.framework"
"Qt6/lib/QtQuickTest.framework"
"Qt6/lib/QtBluetooth.framework"
"Qt6/lib/QtDesigner.framework"
"Qt6/lib/QtQuick.framework"
"Qt6/lib/QtHelp.framework"
"Qt6/lib/QtPdf.framework"
"Qt6/lib/QtQml.framework"
"Qt6/plugins/sqldrivers"
"Qt6/plugins/multimedia"
"Qt6/qml"
)

for debloat_path in "${debloat_paths[@]}"; do
rm -rf universal/PyQt6_Qt6*/PyQt6/$debloat_path
done

# Finally, pack and install our universal2 wheel.
python3 -m wheel pack universal/PyQt6_*
python3 -m pip install PyQt6_*universal2.whl --force-reinstall
11 changes: 9 additions & 2 deletions scripts/tests/macos_test.sh
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
#!/bin/bash
set -e
cd "$(dirname "$0")"

RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
NC='\033[0m'

# Start the application in the background
./NSO-RPC.app/Contents/MacOS/NSO-RPC > output.log 2>&1 &
APP_PID=$!
open --stdout output.log --stderr output.log ../../client/dist/NSO-RPC.app
APP_PID=$(pgrep -n NSO-RPC)

sleep 10
kill $APP_PID
Expand All @@ -21,5 +24,9 @@ if echo "$output" | grep -q "Launch error"; then
exit 1
else
echo -e "${GREEN}Test Passed!${NC}"
# TODO(spotlightishere): Resolve issues with test invocation
if [ -f output.log ]; then
rm output.log
fi
exit 0
fi

0 comments on commit 149d240

Please sign in to comment.