diff --git a/.github/workflows/default.yml b/.github/workflows/default.yml index c3675a2..e8e86fb 100644 --- a/.github/workflows/default.yml +++ b/.github/workflows/default.yml @@ -210,17 +210,19 @@ jobs: - name: Build run: | source venv/bin/activate - # python3 buildPy2app.py py2app - pyinstaller -w --noconfirm --hidden-import PySide6 \ - --add-data "${{ env.PROJ_DIR }}/resources:." \ - --icon "${{ env.PROJ_DIR }}/resources/icon.icns" \ - --target-architecture universal2 \ - --osx-bundle-identifier "${{ env.BUNDLE_ID }}" \ - --hidden-import pkg_resources \ - --distpath "${{ env.BUILD_DIR }}/ProjectMac" \ - --onefile -n "${{ env.PROJECT_NAME }}" pyinstaller.py - ls -al "${{ env.BUILD_DIR }}/ProjectMac" - cp -r "${{ env.BUILD_DIR }}/ProjectMac/${{ env.PROJECT_NAME }}.app" "${{ env.PROJECT_NAME }}.app" + # py2app works better + python3 buildPy2app.py py2app + # pyinstaller -w --noconfirm --hidden-import PySide6 \ + # --add-data "${{ env.PROJ_DIR }}/resources:." \ + # --icon "${{ env.PROJ_DIR }}/resources/icon.icns" \ + # --target-architecture universal2 \ + # --osx-bundle-identifier "${{ env.BUNDLE_ID }}" \ + # --hidden-import pkg_resources \ + # --distpath "${{ env.BUILD_DIR }}/ProjectMac" \ + # --onefile -n "${{ env.PROJECT_NAME }}" pyinstaller.py + ls -al "dist/" + mv "dist/${{ env.PROJECT_NAME }}.app" "${{ env.PROJECT_NAME }}.app" + python3 ci/cleandist.py "${{ env.PROJECT_NAME }}.app" python3 ci/codesign.py "${{ env.PROJECT_NAME }}.app" zip -r "${{ env.PROJECT_NAME }}-${{ env.VER }}.zip" "${{ env.PROJECT_NAME }}.app" diff --git a/buildPy2App.py b/buildPy2App.py index ef9b3c1..dcb9499 100644 --- a/buildPy2App.py +++ b/buildPy2App.py @@ -2,31 +2,25 @@ from setuptools import setup from glob import glob from datetime import datetime -module_name = "project" -name = "Project" -version = "" -main_file = "main.py" -bundle_identifier = "com.project.Project" +from project.info import main_module, name, version, main_file, bundle_identifier, module_name -# Overrides the above -exec(open("info.py").read()) - - -APP = [f'{module_name}/{main_file}'] +APP = [f'pyinstaller.py'] DATA_FILES = [ - ('resources', glob(module_name + '/resources/*.png') + glob(module_name + '/resources/*.rtf') + glob(module_name + '/resources/*.txt')), + ('resources', glob(module_name + '/resources/*')), ] OPTIONS = { + 'arch': 'universal2', + 'optimize': 2, 'iconfile': module_name + '/resources/icon.icns', - 'extra_scripts': module_name + '/info.py', - 'includes': {'PySide6.QtCore', 'PySide6.QtUiTools', 'PySide6.QtGui', 'PySide6.QtWidgets', 'certifi', 'cffi', 'pem'}, - 'excludes': {'tkinter'}, + 'includes': {'PySide6.QtCore', 'PySide6.QtUiTools', 'PySide6.QtGui', 'PySide6.QtWidgets', 'certifi', }, + 'excludes': {'tkinter', "unittest"}, 'qt_plugins': [ 'platforms/libqcocoa.dylib', 'platforms/libqminimal.dylib', 'platforms/libqoffscreen.dylib', 'styles/libqmacstyle.dylib' ], + 'argv_emulation': True, 'plist': { 'CFBundleName': name, 'CFBundleShortVersionString': version, diff --git a/ci/cleandist.py b/ci/cleandist.py new file mode 100644 index 0000000..0c03248 --- /dev/null +++ b/ci/cleandist.py @@ -0,0 +1,122 @@ +import sys +import os +import platform +import shutil + +from glob import glob + +pyver = platform.python_version_tuple()[0] + '.' + platform.python_version_tuple()[1] + +# Clean resources +def clean(glob_path: str = "", to_be_kept=None, to_be_deleted=None): + to_be_kept = to_be_kept or [] + to_be_deleted = to_be_deleted or [] + + if to_be_kept and glob_path: + for f in glob(glob_path): + if not any({k in f for k in to_be_kept}): + to_be_deleted.append(f) + + for p in to_be_deleted: + if os.path.exists(p): + if os.path.isdir(p): + shutil.rmtree(p, ignore_errors=True) + else: + os.remove(p) + + +def clean_resources(app_path: str): + PATH = '{app_path}/Contents/Resources/' + + to_be_kept = [] + to_be_deleted = [] + + clean(f'{PATH}/qt*', to_be_deleted=to_be_deleted, to_be_kept=to_be_kept) + + +def clean_pyside6(app_path: str): + # Clean PySide6 folder + + PATH = f'{app_path}/Contents/Resources/lib/python{pyver}/PySide6' + + shutil.rmtree(f'{PATH}/examples', ignore_errors=True) + shutil.rmtree(f'{PATH}/include', ignore_errors=True) + shutil.rmtree(f'{PATH}/Qt/libexec', ignore_errors=True) + + to_be_kept = ['QtCore', 'QtGui', 'QtWidgets'] + to_be_deleted = [] + + for f in glob(f'{PATH}/Qt*'): + if not any({k in f for k in to_be_kept}): + to_be_deleted.append(f) + + for a in glob(f'{PATH}/*.app'): + to_be_deleted.append(a) + + to_be_deleted.remove(f'{PATH}/Qt') + to_be_deleted.extend([ + f'{PATH}/lupdate', + f'{PATH}/qmllint', + f'{PATH}/lrelease', + f'{PATH}/qmlformat', + f'{PATH}/qmlls', + ]) + clean(to_be_deleted=to_be_deleted) + + +def clean_qt(app_path: str): + # Clean PySide6/Qt folder + + PATH = f'{app_path}/Contents/Resources/lib/python{pyver}/PySide6/Qt' + + to_be_deleted = [f'{PATH}/qml', f'{PATH}/translations'] + + clean(to_be_deleted=to_be_deleted) + + +def clean_lib(app_path: str): + # Clean PySide6/Qt/lib folder + + PATH = f'{app_path}/Contents/Resources/lib/python{pyver}/PySide6/Qt/lib' + + to_be_kept = ['QtCore', 'QtDBus', 'QtGui', 'QtWidgets'] + to_be_deleted = [f'{PATH}/metatypes'] + + clean(f'{PATH}/Qt*', to_be_kept=to_be_kept, to_be_deleted=to_be_deleted) + + +def clean_plugins(app_path: str): + # Clean PySide6/Qt/plugins folder + PATH = f'{app_path}/Contents/Resources/lib/python{pyver}/PySide6/Qt/plugins' + + to_be_kept = ['platforms', 'styles'] + to_be_deleted = [] + + clean(f'{PATH}/*', to_be_kept=to_be_kept, to_be_deleted=to_be_deleted) + + +def symlink_shiboken(app_path: str): + # symlink .so from shiboken6 to PySide6 folder + cwd = os.getcwd() + + FROM = f'{app_path}/Contents/Resources/lib/python{pyver}/shiboken6' + TO = f'{app_path}/Contents/Resources/lib/python{pyver}/PySide6' + + fn = os.path.basename(glob(f'{FROM}/libshiboken6*.dylib')[0]) + + os.chdir(TO) + os.symlink(f'../shiboken6/{fn}', f'./{fn}') + os.chdir(cwd) + + +def main(): + app_path = sys.argv[1] + + clean_resources(app_path) + clean_pyside6(app_path) + clean_qt(app_path) + clean_lib(app_path) + clean_plugins(app_path) + symlink_shiboken(app_path) + +main()