Skip to content

Commit

Permalink
Update build script stuff from Reggie
Browse files Browse the repository at this point in the history
  • Loading branch information
RoadrunnerWMC committed Aug 8, 2022
1 parent ce0894e commit 8686582
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 167 deletions.
106 changes: 12 additions & 94 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,135 +2,53 @@ name: Build

on: [push]

# macOS dark mode stuff is based on
# https://github.com/pyinstaller/pyinstaller/issues/4627#issuecomment-609982803
# and the comment linked from there

jobs:
build:

strategy:
matrix:
include:
- build-name: 'Windows 8.1+ 64-bit'
python-version: 3.9
python-version: "3.10"
platform: windows-latest
arch: 'x64'
- build-name: 'Windows 8.1+ 32-bit'
python-version: 3.9
platform: windows-latest
arch: 'x86'
- build-name: 'Windows 7'
- build-name: 'Windows 7 or 32-bit'
python-version: 3.8
platform: windows-latest
arch: 'x86'
- build-name: 'macOS'
python-version: 3.9
python-version: "3.10"
platform: macos-latest
arch: 'x64'
- build-name: 'Ubuntu'
python-version: 3.9
python-version: "3.10"
platform: ubuntu-latest
arch: 'x64'

name: ${{ matrix.build-name }}

runs-on: ${{ matrix.platform }}

env:
manual-python-version: 3.9 # Don't forget to also change the matrix "python-version" number above!
manual-python-version-full: 3.9.1

steps:
- name: Set environment variables (Windows/Linux)
if: ${{ runner.os == 'Windows' || runner.os == 'Linux' }}
shell: bash
run: echo "PYTHON_CMD=python" >> $GITHUB_ENV
- name: Set environment variables (macOS)
if: ${{ runner.os == 'macOS' }}
run: echo "PYTHON_CMD=./python3" >> $GITHUB_ENV
- name: Checkout repository
uses: actions/checkout@v2
- name: Cache Python build (macOS)
id: caching
if: ${{ runner.os == 'macOS' }}
uses: actions/cache@v2
with:
path: |
./python
./python-framework
~/Library/Python/${{ env.manual-python-version }}
key: macos-python-${{ env.manual-python-version-full }}
- name: Download Python (macOS, cache miss)
if: ${{ runner.os == 'macOS' && !steps.caching.outputs.cache-hit }}
run: |
# Do all this in a "python" subfolder
mkdir python && cd python
# Download Python source and unzip into /src subfolder
curl -o Python.tar.xz https://www.python.org/ftp/python/${{ env.manual-python-version-full }}/Python-${{ env.manual-python-version-full }}.tar.xz
mkdir src
tar -xf Python.tar.xz -C ./src
# All the source code is annoyingly in e.g. /src/Python-3.9.0/ or similar, so let's
# move it all up by two directories in a subfolder-name-agnostic way
cd src
cd *
mv ./* ../../
- name: Build Python (macOS, cache miss)
if: ${{ runner.os == 'macOS' && !steps.caching.outputs.cache-hit }}
run: |
cd python
mkdir build
brew install openssl
export MACOSX_DEPLOYMENT_TARGET=10.13
./configure --enable-optimizations --enable-framework --with-openssl=$(brew --prefix openssl) "--prefix=$(pwd)/build"
make -j2
sudo make install
cd ..
- name: Copy Python for caching (macOS, cache miss)
if: ${{ runner.os == 'macOS' && !steps.caching.outputs.cache-hit }}
run: cp -r /Library/Frameworks/Python.framework/Versions/${{ env.manual-python-version }} ./python-framework
- name: Un-copy Python from cache (macOS, cache hit)
if: ${{ runner.os == 'macOS' && steps.caching.outputs.cache-hit }}
run: |
sudo mkdir -p /Library/Frameworks/Python.framework
sudo mkdir -p /Library/Frameworks/Python.framework/Versions
sudo cp -r ./python-framework /Library/Frameworks/Python.framework/Versions/${{ env.manual-python-version }}
- name: Make Python symlink (macOS)
if: ${{ runner.os == 'macOS' }}
run: ln -s ./python/build/bin/python3 ./python3
- name: Set up Python (Windows/Linux)
if: ${{ runner.os == 'Windows' || runner.os == 'Linux' }}
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
architecture: ${{ matrix.arch }}
- name: Install dependencies other than PyInstaller
- name: Install dependencies
shell: bash
run: |
$PYTHON_CMD -m pip install --upgrade pip
$PYTHON_CMD -m pip install wheel
$PYTHON_CMD -m pip install PyQt5==5.15.3 nsmblib
- name: Install PyInstaller (Windows/Linux)
if: ${{ runner.os == 'Windows' || runner.os == 'Linux' }}
python -m pip install --upgrade pip
python -m pip install wheel
python -m pip install PyQt5 nsmblib PyInstaller
- name: Configure PyQt5 build
shell: bash
run: |
$PYTHON_CMD -m pip install PyInstaller
- name: Build and install PyInstaller (macOS, cache miss)
if: ${{ runner.os == 'macOS' && !steps.caching.outputs.cache-hit }}
run: |
git clone https://github.com/pyinstaller/pyinstaller
cd pyinstaller/bootloader
export MACOSX_DEPLOYMENT_TARGET=10.13
export CFLAGS=-mmacosx-version-min=10.13
export CPPFLAGS=-mmacosx-version-min=10.13
export LDFLAGS=-mmacosx-version-min=10.13
export LINKFLAGS=-mmacosx-version-min=10.13
../.././python3 ./waf all
cd ..
.././python3 -m pip install .
run: echo "PYQT_VERSION=PyQt5" >> $GITHUB_ENV
- name: Build
shell: bash
run: $PYTHON_CMD -OO build_release.py
run: python -OO build_release.py
- name: Tar (macOS/Linux)
if: ${{ runner.os == 'macOS' || runner.os == 'Linux' }}
run: |
Expand Down
140 changes: 69 additions & 71 deletions build_release.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# Koopatlas

import json
import os, os.path
import shutil
Expand All @@ -11,7 +9,6 @@
import PyInstaller.__main__



########################################################################
############################### Constants ##############################
########################################################################
Expand Down Expand Up @@ -64,39 +61,28 @@ def run_pyinstaller(args):
print('>>')

# Python optimization level
if sys.flags.optimize == 2:
print('>> [X] Python optimization level is -OO')
if sys.flags.optimize >= 1:
print('>> [X] Python optimization level is -O')
else:
print('>> [ ] Python optimization level is -OO')
print('>> [ ] Python optimization level is -O')

# NSMBLib being installed
try:
import nsmblib
print('>> [X] NSMBLib is installed')
except ImportError:
nsmblib = None
print('>> [ ] NSMBLib is installed')

# UPX being installed

# There seems to be no reliable way to determine in this script if
# PyInstaller will detect UPX or not. PyInstaller itself provides no
# public API for this, and doing horrible things with its private API
# didn't ultimately work well enough to be useful.
# So all we can do is show this generic message, unfortunately.
print('>> [?] UPX is installed')
if config.USE_NSMBLIB:
try:
import nsmblib
print('>> [X] NSMBLib is installed')
except ImportError:
nsmblib = None
print('>> [ ] NSMBLib is installed')


# Now show big warning messages if any of those failed
if sys.flags.optimize < 2:
msg = 'without' if sys.flags.optimize == 0 else 'with only one level of'
print_emphasis('>> WARNING: Python is being run ' + msg + ' optimizations enabled! Please consider building with -OO.')
if sys.flags.optimize < 1:
print_emphasis('>> WARNING: Python is being run without optimizations enabled! Please consider building with -O.')

if nsmblib is None:
if config.USE_NSMBLIB and nsmblib is None:
print_emphasis('>> WARNING: NSMBLib does not seem to be installed! Please consider installing it prior to building.')

print_emphasis('>> NOTE: If the PyInstaller output below says "INFO: UPX is not available.", you should install UPX!')


########################################################################
######################### Excludes and Includes ########################
Expand All @@ -107,40 +93,39 @@ def run_pyinstaller(args):

# Excludes
excludes = ['calendar', 'datetime', 'difflib', 'doctest', 'inspect',
'locale', 'multiprocessing', 'optpath', 'os2emxpath', 'pdb',
'socket', 'ssl', 'unittest',
'multiprocessing', 'optpath', 'os2emxpath', 'pdb', 'socket', 'ssl',
'unittest',
'FixTk', 'tcl', 'tk', '_tkinter', 'tkinter', 'Tkinter']

if config.EXCLUDE_SELECT:
excludes.append('select')
if config.EXCLUDE_THREADING:
excludes.append('threading')
if config.EXCLUDE_HASHLIB:
excludes.append('hashlib')
if config.EXCLUDE_LOCALE:
excludes.append('locale')

if sys.platform == 'nt':
excludes.append('posixpath')

# Add excludes for other Qt modules
if config.USE_PYQT:
unneededQtModules = ['Designer', 'Network', 'OpenGL', 'Qml', 'Script', 'Sql', 'Test', 'WebKit', 'Xml']
neededQtModules = ['Core', 'Gui', 'Widgets']

unneededQtModules = ['Designer', 'Network', 'OpenGL', 'Qml', 'Script', 'Sql', 'Test', 'WebKit', 'Xml']
neededQtModules = ['Core', 'Gui', 'Widgets']
targetQtVer = 5 if os.environ.get('PYQT_VERSION') == 'PyQt5' else 6
targetQt = f'PyQt{targetQtVer}'
print('>> Targeting ' + targetQt)

targetQt = 'PyQt' + str(4 if sys.version_info.major < 3 else 5)
print('>> Targeting ' + targetQt)

for qt in ['PySide2', 'PyQt4', 'PyQt5']:
# Exclude all the stuff we don't use
for m in unneededQtModules:
excludes.append(qt + '.Qt' + m)
for qt in ['PySide2', 'PySide6', 'PyQt4', 'PyQt5', 'PyQt6']:
# Exclude all the stuff we don't use
for m in unneededQtModules:
excludes.append(qt + '.Qt' + m)

if qt != targetQt:
# Since we're not using this copy of Qt, exclude it
excludes.append(qt)
if qt != targetQt:
# Since we're not using this copy of Qt, exclude it
excludes.append(qt)

# As well as its QtCore/QtGui/etc
for m in neededQtModules:
excludes.append(qt + '.Qt' + m)
# As well as its QtCore/QtGui/etc
for m in neededQtModules:
excludes.append(qt + '.Qt' + m)

# Includes
includes = ['pkgutil']
Expand All @@ -149,16 +134,18 @@ def run_pyinstaller(args):
excludes_binaries = []
if sys.platform == 'win32':
excludes_binaries = [
# Qt stuff
'Qt5Network.dll', 'Qt5Qml.dll', 'Qt5QmlModels.dll',
'Qt5Quick.dll', 'Qt5WebSockets.dll',
# Other stuff
'opengl32sw.dll',
'd3dcompiler_', # currently (2020-09-25) "d3dcompiler_47.dll",
# but that'll probably change eventually, so we
# just exclude anything that starts with this
# substring
]
if config.USE_PYQT:
excludes_binaries.extend([
f'Qt{targetQtVer}Network.dll', f'Qt{targetQtVer}Qml.dll',
f'Qt{targetQtVer}QmlModels.dll', f'Qt{targetQtVer}Quick.dll',
f'Qt{targetQtVer}WebSockets.dll',
])

elif sys.platform == 'darwin':
# Sadly, we can't exclude anything on macOS -- it just crashes. :(
Expand All @@ -172,15 +159,17 @@ def run_pyinstaller(args):

elif sys.platform == 'linux':
excludes_binaries = [
# Qt stuff
# Currently (2020-09-25) these all end with ".so.5", but that
# may change, so we exclude anything that starts with these
# substrings
'libQt5Network.so', 'libQt5Qml.so', 'libQt5QmlModels.so',
'libQt5Quick.so', 'libQt5WebSockets.so',
# Other stuff
'libgtk-3.so',
]
if config.USE_PYQT:
excludes_binaries.extend([
# Currently (2020-09-25) these all end with ".so.5", but that
# may change, so we exclude anything that starts with these
# substrings
f'libQt{targetQtVer}Network.so', f'libQt{targetQtVer}Qml.so',
f'libQt{targetQtVer}QmlModels.so', f'libQt{targetQtVer}Quick.so',
f'libQt{targetQtVer}WebSockets.so',
])


print('>> Will use the following excludes list: ' + ', '.join(excludes))
Expand All @@ -197,18 +186,23 @@ def run_pyinstaller(args):
# build...

args = [
'--windowed',
'--onefile',
'--distpath=' + DIR,
'--workpath=' + WORKPATH,
]

if sys.platform == 'win32':
if config.WIN_ICON:
args.append('--icon=' + os.path.abspath(config.WIN_ICON))
elif sys.platform == 'darwin':
if config.MAC_ICON:
args.append('--icon=' + os.path.abspath(config.MAC_ICON))
if config.USE_PYQT:
args.append('--windowed')

if sys.platform == 'win32':
if config.WIN_ICON:
args.append('--icon=' + os.path.abspath(config.WIN_ICON))

elif sys.platform == 'darwin':
if config.MAC_ICON:
args.append('--icon=' + os.path.abspath(config.MAC_ICON))

if sys.platform == 'darwin':
args.append('--osx-bundle-identifier=' + config.MAC_BUNDLE_IDENTIFIER)

for p in config.EXTRA_IMPORT_PATHS:
Expand Down Expand Up @@ -247,6 +241,7 @@ def run_pyinstaller(args):

# Iterate over its lines, and potentially add new ones
new_lines = []
found_bundle_line = False
for line in lines:
if 'PYZ(' in line and excludes_binaries:
new_lines.append('EXCLUDES = ' + repr(excludes_binaries))
Expand All @@ -260,10 +255,13 @@ def run_pyinstaller(args):
new_lines.append(' new_binaries.append((x, y, z))')
new_lines.append('a.binaries = new_binaries')

new_lines.append(line)
if 'BUNDLE(' in line:
found_bundle_line = True
if found_bundle_line and sys.platform == 'darwin' and line.strip() == ')':
new_lines.append(' info_plist=' + json.dumps(info_plist) + ',')
found_bundle_line = False

if sys.platform == 'darwin' and 'BUNDLE(' in line:
new_lines.append('info_plist=' + json.dumps(info_plist) + ',')
new_lines.append(line)

# Save new specfile
with open(SPECFILE, 'w', encoding='utf-8') as f:
Expand All @@ -279,12 +277,12 @@ def run_pyinstaller(args):
# run with minimal arguments this time.

args = [
'--windowed',
'--distpath=' + DIR,
'--workpath=' + WORKPATH,
SPECFILE,
]

args.append(SPECFILE)

run_pyinstaller(args)

shutil.rmtree(WORKPATH)
Expand Down
Loading

0 comments on commit 8686582

Please sign in to comment.