From db9d81ba90b8a41f2b52292c80d55df53d66a648 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Sun, 4 Sep 2016 19:21:09 -0700 Subject: [PATCH] Migrate from PyInstaller to cx_Freeze for OSX --- BUILD.md | 10 ++- install/Info.plist | 18 +++++ install/build_osx.sh | 26 ++++--- install/pyinstaller.spec | 62 ----------------- onionshare/helpers.py | 10 +-- setup.py | 147 +++++++++++++++++++++------------------ 6 files changed, 123 insertions(+), 150 deletions(-) create mode 100644 install/Info.plist delete mode 100644 install/pyinstaller.spec diff --git a/BUILD.md b/BUILD.md index 87274958..05122859 100644 --- a/BUILD.md +++ b/BUILD.md @@ -62,9 +62,15 @@ brew install python3 pyqt5 qt5 Install some dependencies using pip3: ```sh -sudo pip3 install pyinstaller flask stem +sudo pip3 install flask stem ``` +Install the latest development version of cx_Freeze: + +* Download a [snapshot](https://bitbucket.org/anthony_tuininga/cx_freeze/downloads) of the latest development version of cx_Freeze, extract it, and cd into the folder you extracted it to +* Build the package: `python3 setup.py bdist_wheel` +* Install it with pip: `sudo pip3 install dist/cx_Freeze-5.0-cp35-cp35m-macosx_10_11_x86_64.whl` + Get the source code: ```sh @@ -83,7 +89,7 @@ Now you should have `dist/OnionShare.app`. To codesign and build a .pkg for distribution: ```sh -install/build_osx.sh --sign +install/build_osx.sh --release ``` Now you should have `dist/OnionShare.pkg`. diff --git a/install/Info.plist b/install/Info.plist new file mode 100644 index 00000000..0125c1d5 --- /dev/null +++ b/install/Info.plist @@ -0,0 +1,18 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + onionshare-gui + CFBundleIdentifier + com.micahflee.onionshare + NSHighResolutionCapable + + CFBundleShortVersionString + {VERSION} + CFBundleIconFile + icon.icns + + diff --git a/install/build_osx.sh b/install/build_osx.sh index 22d6cc4d..2f2314f5 100755 --- a/install/build_osx.sh +++ b/install/build_osx.sh @@ -5,19 +5,27 @@ cd $ROOT # deleting dist echo Deleting dist folder -rm -rf $ROOT/dist &>/dev/null 2>&1 +rm -rf $ROOT/build $ROOT/dist &>/dev/null 2>&1 # build the .app echo Building OnionShare.app -pyinstaller install/pyinstaller.spec +python3 setup.py bdist_mac -if [ "$1" = "--sign" ]; then - SIGNING_IDENTITY_APP="3rd Party Mac Developer Application: Micah Lee" - SIGNING_IDENTITY_INSTALLER="3rd Party Mac Developer Installer: Micah Lee" +if [ "$1" = "--release" ]; then + mkdir -p dist + APP_PATH="build/OnionShare.app" + PKG_PATH="dist/OnionShare.pkg" + IDENTITY_NAME_APPLICATION="Developer ID Application: Micah Lee" + IDENTITY_NAME_INSTALLER="Developer ID Installer: Micah Lee" - # codesign the .app - codesign -vvvv --deep -s "$SIGNING_IDENTITY_APP" dist/OnionShare.app + echo "Codesigning the app bundle" + codesign --deep -s "$IDENTITY_NAME_APPLICATION" "$APP_PATH" - # build .pkg - productbuild --component dist/OnionShare.app /Applications dist/OnionShare.pkg --sign "$SIGNING_IDENTITY_INSTALLER" + echo "Creating an installer" + productbuild --sign "$IDENTITY_NAME_INSTALLER" --component "$APP_PATH" /Applications "$PKG_PATH" + + echo "Cleaning up" + rm -rf "$APP_PATH" + + echo "All done, your installer is in: $PKG_PATH" fi diff --git a/install/pyinstaller.spec b/install/pyinstaller.spec deleted file mode 100644 index 817c22c2..00000000 --- a/install/pyinstaller.spec +++ /dev/null @@ -1,62 +0,0 @@ -# -*- mode: python -*- - -import platform -system = platform.system() - -version = open('resources/version.txt').read().strip() - -block_cipher = None - -a = Analysis( - ['scripts/onionshare-gui'], - pathex=['.'], - binaries=None, - datas=[ - ('../resources/images/*', 'images'), - ('../resources/locale/*', 'locale'), - ('../resources/html/*', 'html'), - ('../resources/version.txt', '.'), - ('../resources/wordlist.txt', '.') - ], - hiddenimports=[], - hookspath=[], - runtime_hooks=[], - excludes=[], - win_no_prefer_redirects=False, - win_private_assemblies=False, - cipher=block_cipher) - -pyz = PYZ( - a.pure, a.zipped_data, - cipher=block_cipher) - -exe = EXE( - pyz, - a.scripts, - exclude_binaries=True, - name='onionshare', - debug=False, - strip=False, - upx=True, - console=False) - -coll = COLLECT( - exe, - a.binaries, - a.zipfiles, - a.datas, - strip=False, - upx=True, - name='onionshare') - -if system == 'Darwin': - app = BUNDLE( - coll, - name='OnionShare.app', - icon='install/onionshare.icns', - bundle_identifier='com.micahflee.onionshare', - info_plist={ - 'CFBundleShortVersionString': version, - 'NSHighResolutionCapable': 'True' - } - ) diff --git a/onionshare/helpers.py b/onionshare/helpers.py index d80122df..aaa1c999 100644 --- a/onionshare/helpers.py +++ b/onionshare/helpers.py @@ -37,15 +37,9 @@ def get_resource_path(filename): if p == 'Linux' and sys.argv and sys.argv[0].startswith('/usr/bin/onionshare'): # OnionShare is installed systemwide in Linux resources_dir = os.path.join(sys.prefix, 'share/onionshare') - elif getattr(sys, 'frozen', False): # Check if app is "frozen" with pyinstaller, cx_Freeze - # https://pythonhosted.org/PyInstaller/#run-time-information + elif getattr(sys, 'frozen', False): # Check if app is "frozen" with cx_Freeze # http://cx-freeze.readthedocs.io/en/latest/faq.html#using-data-files - if p == 'Windows': - # Windows is using cx_Freeze - resources_dir = os.path.join(os.path.dirname(sys.executable), 'resources') - else: - # OS X is using PyInstaller - resources_dir = sys._MEIPASS + resources_dir = os.path.join(os.path.dirname(sys.executable), 'resources') else: # Look for resources directory relative to python file resources_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))), 'resources') diff --git a/setup.py b/setup.py index a3516a88..b37f3598 100644 --- a/setup.py +++ b/setup.py @@ -19,7 +19,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . """ -import os, sys, platform +import os, sys, platform, tempfile def file_list(path): files = [] @@ -42,84 +42,69 @@ long_description = description + " " + ( author = 'Micah Lee' author_email = 'micah@micahflee.com' url = 'https://github.com/micahflee/onionshare' -license = "GPL v3" +license = 'GPL v3' keywords = 'onion, share, onionshare, tor, anonymous, web server' os = platform.system() -if os == 'Windows': +# Windows and Mac +if os == 'Windows' or os == 'Darwin': from cx_Freeze import setup, Executable + + if os == 'Windows': + executables = [ + Executable('install/scripts/onionshare', + icon='install/onionshare.ico', + base=None), + Executable('install/scripts/onionshare-gui', + icon='install/onionshare.ico', + shortcutName='OnionShare', + shortcutDir='ProgramMenuFolder', + base='Win32GUI') + ] + custom_info_plist = '' + + elif os == 'Darwin': + executables = [ + Executable('install/scripts/onionshare-gui'), + Executable('install/scripts/onionshare') + ] + + # Write the correct version into Info.plist + f = tempfile.NamedTemporaryFile(mode='w') + custom_info_plist = f.name + f.write(open('install/Info.plist').read().replace('{VERSION}', str(version))) + f.flush() + setup( - name="OnionShare", - version=version, - description=description, - long_description=long_description, - author=author, - author_email=author_email, - url=url, - license=license, - keywords=keywords, + name='OnionShare', version=version, + description=description, long_description=long_description, + author=author, author_email=author_email, + url=url, license=license, keywords=keywords, options={ - "build_exe": { - "packages": [], - "excludes": [], - "include_files": ['resources'] + 'build_exe': { + 'packages': ['jinja2.ext'], + 'excludes': [], + 'include_files': ['resources'] + }, + 'bdist_mac': { + 'iconfile': 'install/onionshare.icns', + 'bundle_name': 'OnionShare', + 'qt_menu_nib': '/usr/local/Cellar/qt5/5.6.1-1/plugins/platforms', + 'custom_info_plist': custom_info_plist } }, - executables=[ - Executable("install/scripts/onionshare", - icon="install/onionshare.ico", - base=None), - Executable("install/scripts/onionshare-gui", - icon="install/onionshare.ico", - shortcutName="OnionShare", - shortcutDir="ProgramMenuFolder", - base="Win32GUI") - ] + executables=executables ) - + +# Linux else: - images = [ - 'resources/images/logo.png', - 'resources/images/drop_files.png', - 'resources/images/server_stopped.png', - 'resources/images/server_started.png', - 'resources/images/server_working.png' - ] - - locale = [ - 'resources/locale/cs.json', - 'resources/locale/de.json', - 'resources/locale/en.json', - 'resources/locale/eo.json', - 'resources/locale/es.json', - 'resources/locale/fi.json', - 'resources/locale/fr.json', - 'resources/locale/it.json', - 'resources/locale/nl.json', - 'resources/locale/no.json', - 'resources/locale/pt.json', - 'resources/locale/ru.json', - 'resources/locale/tr.json' - ] - - html = [ - 'resources/html/index.html', - 'resources/html/denied.html', - 'resources/html/404.html' - ] - from setuptools import setup setup( - name='onionshare', - version=version, - description=description, - long_description=long_description, - author=author, - author_email=author_email, - url=url, - license=license, - keywords=keywords, + name='onionshare', version=version, + description=description, long_description=long_description, + author=author, author_email=author_email, + url=url, license=license, keywords=keywords, packages=['onionshare', 'onionshare_gui'], include_package_data=True, scripts=['install/scripts/onionshare', 'install/scripts/onionshare-gui'], @@ -131,9 +116,33 @@ else: 'resources/version.txt', 'resources/wordlist.txt' ]), - (os.path.join(sys.prefix, 'share/onionshare/images'), images), - (os.path.join(sys.prefix, 'share/onionshare/locale'), locale), - (os.path.join(sys.prefix, 'share/onionshare/html'), html), + (os.path.join(sys.prefix, 'share/onionshare/images'), [ + 'resources/images/logo.png', + 'resources/images/drop_files.png', + 'resources/images/server_stopped.png', + 'resources/images/server_started.png', + 'resources/images/server_working.png' + ]), + (os.path.join(sys.prefix, 'share/onionshare/locale'), [ + 'resources/locale/cs.json', + 'resources/locale/de.json', + 'resources/locale/en.json', + 'resources/locale/eo.json', + 'resources/locale/es.json', + 'resources/locale/fi.json', + 'resources/locale/fr.json', + 'resources/locale/it.json', + 'resources/locale/nl.json', + 'resources/locale/no.json', + 'resources/locale/pt.json', + 'resources/locale/ru.json', + 'resources/locale/tr.json' + ]), + (os.path.join(sys.prefix, 'share/onionshare/html'), [ + 'resources/html/index.html', + 'resources/html/denied.html', + 'resources/html/404.html' + ]), ('/usr/share/nautilus-python/extensions/', ['install/scripts/onionshare-nautilus.py']), ] )