diff --git a/BUILD.md b/BUILD.md index 69cd7d8b..6762fd9d 100644 --- a/BUILD.md +++ b/BUILD.md @@ -34,35 +34,23 @@ For ArchLinux: There is a PKBUILD available [here](https://aur.archlinux.org/pac Install Xcode from the Mac App Store. Once it's installed, run it for the first time to set it up. -If you don't already have it installed, install [Homebrew](http://brew.sh/). +Download and install Python 3.5.2 from https://www.python.org/downloads/release/python-352/ (note that a pyinstaller bug prevents you from using Python 3.6). I downloaded `python-3.5.2-macosx10.6.pkg`. -Install some dependencies using Homebrew: +Download and install Qt 5.7.1 for macOS offline installer from https://www.qt.io/download-open-source/. I downloaded `qt-opensource-mac-x64-clang-5.7.1.dmg`. (You can skip making an account in the installer.) + +Now install some python dependencies with pip (note, there's issues building a .app if you install this in a virtualenv): ```sh -brew install python3 pyqt5 qt5 +sudo pip3 install -r install/requirements.txt ``` -Install some dependencies using pip3: - -```sh -sudo pip3 install flask stem -``` - -After that you can try both the CLI and the GUI version of OnionShare: +You can run both the CLI and GUI versions of OnionShare without building an bundle: ```sh ./dev_scripts/onionshare ./dev_scripts/onionshare-gui ``` -If you want to build a Mac OS X app bundle: - -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` - To build the app bundle: ```sh @@ -83,9 +71,13 @@ Now you should have `dist/OnionShare.pkg`. ### Setting up your dev environment -Download the latest Python 3.6.x, 32-bit (x86) from https://www.python.org/downloads/. I downloaded `python-3.6.0.exe`. When installing it, make sure to check the "Add Python 3.6 to PATH" checkbox on the first page of the installer. +Download the latest Python 3.5.2, 32-bit (x86) from https://www.python.org/downloads/release/python-352/ (note that there's a pyinstaller/pywin32 bug that prevents 3.6.x from working). I downloaded `python-3.5.2.exe`. When installing it, make sure to check the "Add Python 3.5 to PATH" checkbox on the first page of the installer. -Open a command prompt and install dependencies with pip: `pip install flask stem PyQt5` +Open a command prompt, cd to the onionshare folder, and install dependencies with pip: + +```cmd +pip3 install -r install\requirements-windows.txt +``` Download and install Qt5 from https://www.qt.io/download-open-source/. I downloaded `qt-unified-windows-x86-2.0.4-online.exe`. There's no need to login to a Qt account during installation. Make sure you install the latest Qt 5.x. I installed Qt 5.7. @@ -96,19 +88,17 @@ python dev_scripts\onionshare python dev_scripts\onionshare-gui ``` -If you want to build an .exe: +If you want to build a .exe: These instructions include adding folders to the path in Windows. To do this, go to Start and type "advanced system settings", and open "View advanced system settings" in the Control Panel. Click Environment Variables. Under "System variables" double-click on Path. From there you can add and remove folders that are available in the PATH. -Download and install the [Microsoft Visual C++ 2008 Redistributable Package (x86)](http://www.microsoft.com/en-us/download/details.aspx?id=29). +Download and install the 32-bit [Visual C++ Redistributable for Visual Studio 2015](https://www.microsoft.com/en-US/download/details.aspx?id=48145). I downloaded `vc_redist.x86.exe`. -Installing cx_Freeze with support for Python 3.5 is annoying. Here are the steps (thanks https://github.com/sekrause/cx_Freeze-Wheels): +Download and install the standalone [Windows 10 SDK](https://dev.windows.com/en-us/downloads/windows-10-sdk). Note that you may not need this if you already have Visual Studio. Add the following directories to the path: -* Download and install the Visual C++ Build Tools 2005 from http://go.microsoft.com/fwlink/?LinkId=691126. I downloaded `visualcppbuildtools_full.exe`. -* Install the python wheel package: `pip install wheel` -* 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: `python setup.py bdist_wheel` -* Install it with pip: `pip install dist\cx_Freeze-5.0-cp35-cp35m-win32.whl` +* `C:\Program Files (x86)\Windows Kits\10\bin\x86` +* `C:\Program Files (x86)\Windows Kits\10\Redist\ucrt\DLLs\x86` +* `C:\Users\user\AppData\Local\Programs\Python\Python35-32\Lib\site-packages\PyQt5\Qt\bin` If you want to build the installer: @@ -119,17 +109,12 @@ If you want to sign binaries with Authenticode: * You'll need a code signing certificate. I roughly followed [this guide](http://blog.assarbad.net/20110513/startssl-code-signing-certificate/) to make one using my StartSSL account. * Once you get a code signing key and certificate and covert it to a pfx file, import it into your certificate store. -* Windows 7: - * Go to http://msdn.microsoft.com/en-us/vstudio/aa496123 and install the latest .NET Framework. I installed `.NET Framework 4.6`. - * Go to http://www.microsoft.com/en-us/download/confirmation.aspx?id=8279 and install the Windows SDK. - * Add `C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin` to the path. -* Windows 10: - * Go to https://dev.windows.com/en-us/downloads/windows-10-sdk and install the standalone Windows 10 SDK. Note that you may not need this if you already have Visual Studio. - * Add `C:\Program Files (x86)\Windows Kits\10\bin\x86` to the path. ### To make a .exe: -* Open a command prompt, cd into the onionshare directory, and type: `python setup.py build`. `onionshare.exe`, `onionshare-gui.exe`, and all of their supporting files will get created inside the `build\exe.win32-3.5` folder. +For PyInstaller to work, you might need to edit `Scripts\pyinstaller-script.py` in your Python 3.5 folder, to work around [this bug](https://stackoverflow.com/questions/31808180/installing-pyinstaller-via-pip-leads-to-failed-to-create-process) in pip. + +* Open a command prompt, cd into the onionshare directory, and type: `pyinstaller install\pyinstaller.spec`. `onionshare.exe`, `onionshare-gui.exe`, and all of their supporting files will get created inside the `build` folder. ### To build the installer: diff --git a/install/build_deb.sh b/install/build_deb.sh index 8c4d487f..05a37822 100755 --- a/install/build_deb.sh +++ b/install/build_deb.sh @@ -3,7 +3,7 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && cd .. && pwd )" cd $DIR -VERSION=`cat resources/version.txt` +VERSION=`cat share/version.txt` # clean up from last build rm -r deb_dist >/dev/null 2>&1 diff --git a/install/build_exe.bat b/install/build_exe.bat index e0a2094e..bfbb998e 100644 --- a/install/build_exe.bat +++ b/install/build_exe.bat @@ -1,5 +1,5 @@ REM build onionshare.exe, onionshare-gui.exe -python setup.py build +pyinstaller install\pyinstaller.spec -y REM sign onionshare.exe, onionshare-gui.exe signtool.exe sign /v /d "OnionShare" /a /tr http://timestamp.globalsign.com/scripts/timstamp.dll /fd sha256 build\exe.win32-3.5\onionshare.exe diff --git a/install/build_osx.sh b/install/build_osx.sh index 2f2314f5..a8259a43 100755 --- a/install/build_osx.sh +++ b/install/build_osx.sh @@ -9,7 +9,7 @@ rm -rf $ROOT/build $ROOT/dist &>/dev/null 2>&1 # build the .app echo Building OnionShare.app -python3 setup.py bdist_mac +pyinstaller install/pyinstaller.spec if [ "$1" = "--release" ]; then mkdir -p dist diff --git a/install/build_rpm.sh b/install/build_rpm.sh index 7be77bb1..89b85586 100755 --- a/install/build_rpm.sh +++ b/install/build_rpm.sh @@ -3,7 +3,7 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && cd .. && pwd )" cd $DIR -VERSION=`cat resources/version.txt` +VERSION=`cat share/version.txt` # clean up from last build rm -r build dist >/dev/null 2>&1 diff --git a/install/ppa_release.sh b/install/ppa_release.sh index e64fb866..cb1a4cdb 100755 --- a/install/ppa_release.sh +++ b/install/ppa_release.sh @@ -7,7 +7,7 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && cd .. && pwd )" cd $DIR -VERSION=`cat resources/version.txt` +VERSION=`cat share/version.txt` rm -rf deb_dist >/dev/null 2>&1 python3 setup.py --command-packages=stdeb.command sdist_dsc diff --git a/install/pyinstaller.spec b/install/pyinstaller.spec new file mode 100644 index 00000000..de88dd09 --- /dev/null +++ b/install/pyinstaller.spec @@ -0,0 +1,61 @@ +# -*- mode: python -*- + +import platform +p = platform.system() + +version = open('share/version.txt').read().strip() + +a = Analysis( + ['scripts/onionshare-gui'], + pathex=['.'], + binaries=None, + datas=[ + ('../share/license.txt', 'share'), + ('../share/version.txt', 'share'), + ('../share/wordlist.txt', 'share'), + ('../share/images/*', 'share/images'), + ('../share/locale/*', 'share/locale'), + ('../share/html/*', 'share/html') + ], + hiddenimports=[], + hookspath=[], + runtime_hooks=[], + excludes=['jinja2.asyncsupport'], + win_no_prefer_redirects=False, + win_private_assemblies=False, + cipher=None) + +pyz = PYZ( + a.pure, a.zipped_data, + cipher=None) + +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 p == '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/install/requirements-windows.txt b/install/requirements-windows.txt new file mode 100644 index 00000000..37b77e0f --- /dev/null +++ b/install/requirements-windows.txt @@ -0,0 +1,13 @@ +click==6.7 +Flask==0.12 +future==0.16.0 +itsdangerous==0.24 +Jinja2==2.9.5 +MarkupSafe==0.23 +pefile==2016.3.28 +PyInstaller==3.2.1 +pypiwin32==219 +PyQt5==5.8 +sip==4.19.1 +stem==1.5.4 +Werkzeug==0.11.15 diff --git a/install/requirements.txt b/install/requirements.txt new file mode 100644 index 00000000..bfc7f935 --- /dev/null +++ b/install/requirements.txt @@ -0,0 +1,10 @@ +click==6.7 +Flask==0.12 +itsdangerous==0.24 +Jinja2==2.9.5 +MarkupSafe==0.23 +PyInstaller==3.2.1 +PyQt5==5.7.1 +sip==4.19 +stem==1.5.4 +Werkzeug==0.11.15 diff --git a/onionshare/helpers.py b/onionshare/helpers.py index 832f2f38..a7873942 100644 --- a/onionshare/helpers.py +++ b/onionshare/helpers.py @@ -37,16 +37,21 @@ def get_resource_path(filename): if getattr(sys, 'onionshare_dev_mode', False): # 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') + prefix = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))), 'share') elif p == 'Linux' and sys.argv and sys.argv[0].startswith(sys.prefix): # 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 cx_Freeze - # http://cx-freeze.readthedocs.io/en/latest/faq.html#using-data-files - resources_dir = os.path.join(os.path.dirname(sys.executable), 'resources') + prefix = os.path.join(sys.prefix, 'share/onionshare') - return os.path.join(resources_dir, filename) + elif getattr(sys, 'frozen', False): + # Check if app is "frozen" + # https://pythonhosted.org/PyInstaller/#run-time-information + if p == 'Darwin': + prefix = os.path.join(sys._MEIPASS, 'share') + elif p == 'Windows': + prefix = os.path.join(os.path.dirname(sys.executable), 'share') + + return os.path.join(prefix, filename) def get_version(): diff --git a/setup.py b/setup.py index 097fb035..dcbe42fb 100644 --- a/setup.py +++ b/setup.py @@ -20,6 +20,7 @@ along with this program. If not, see . """ import os, sys, platform, tempfile +from distutils.core import setup def file_list(path): files = [] @@ -28,7 +29,7 @@ def file_list(path): files.append(os.path.join(path, filename)) return files -version = open('resources/version.txt').read().strip() +version = open('share/version.txt').read().strip() description = ( """OnionShare lets you securely and anonymously share a file of any size with someone. """ """It works by starting a web server, making it accessible as a Tor hidden service, """ @@ -45,104 +46,22 @@ url = 'https://github.com/micahflee/onionshare' license = 'GPL v3' keywords = 'onion, share, onionshare, tor, anonymous, web server' -p = platform.system() - -# Windows and Mac -if p == 'Windows' or p == 'Darwin': - from cx_Freeze import setup, Executable - - if p == '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 p == '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, - options={ - '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=executables - ) - -# Linux -else: - 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, - packages=['onionshare', 'onionshare_gui'], - include_package_data=True, - scripts=['install/scripts/onionshare', 'install/scripts/onionshare-gui'], - data_files=[ - (os.path.join(sys.prefix, 'share/applications'), ['install/onionshare.desktop']), - (os.path.join(sys.prefix, 'share/appdata'), ['install/onionshare.appdata.xml']), - (os.path.join(sys.prefix, 'share/pixmaps'), ['install/onionshare80.xpm']), - (os.path.join(sys.prefix, 'share/onionshare'), [ - 'resources/version.txt', - 'resources/wordlist.txt' - ]), - (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']), - ] - ) +setup( + 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'], + data_files=[ + (os.path.join(sys.prefix, 'share/applications'), ['install/onionshare.desktop']), + (os.path.join(sys.prefix, 'share/appdata'), ['install/onionshare.appdata.xml']), + (os.path.join(sys.prefix, 'share/pixmaps'), ['install/onionshare80.xpm']), + (os.path.join(sys.prefix, 'share/onionshare'), file_list('share')), + (os.path.join(sys.prefix, 'share/onionshare/images'), file_list('share/images')), + (os.path.join(sys.prefix, 'share/onionshare/locale'), file_list('share/locale')), + (os.path.join(sys.prefix, 'share/onionshare/html'), file_list('share/html')), + ('/usr/share/nautilus-python/extensions/', ['install/scripts/onionshare-nautilus.py']) + ] +) diff --git a/resources/html/404.html b/share/html/404.html similarity index 100% rename from resources/html/404.html rename to share/html/404.html diff --git a/resources/html/denied.html b/share/html/denied.html similarity index 100% rename from resources/html/denied.html rename to share/html/denied.html diff --git a/resources/html/index.html b/share/html/index.html similarity index 100% rename from resources/html/index.html rename to share/html/index.html diff --git a/resources/images/drop_files.png b/share/images/drop_files.png similarity index 100% rename from resources/images/drop_files.png rename to share/images/drop_files.png diff --git a/resources/images/logo.png b/share/images/logo.png similarity index 100% rename from resources/images/logo.png rename to share/images/logo.png diff --git a/resources/images/server_started.png b/share/images/server_started.png similarity index 100% rename from resources/images/server_started.png rename to share/images/server_started.png diff --git a/resources/images/server_stopped.png b/share/images/server_stopped.png similarity index 100% rename from resources/images/server_stopped.png rename to share/images/server_stopped.png diff --git a/resources/images/server_working.png b/share/images/server_working.png similarity index 100% rename from resources/images/server_working.png rename to share/images/server_working.png diff --git a/resources/license.txt b/share/license.txt similarity index 100% rename from resources/license.txt rename to share/license.txt diff --git a/resources/locale/cs.json b/share/locale/cs.json similarity index 100% rename from resources/locale/cs.json rename to share/locale/cs.json diff --git a/resources/locale/de.json b/share/locale/de.json similarity index 100% rename from resources/locale/de.json rename to share/locale/de.json diff --git a/resources/locale/en.json b/share/locale/en.json similarity index 100% rename from resources/locale/en.json rename to share/locale/en.json diff --git a/resources/locale/eo.json b/share/locale/eo.json similarity index 100% rename from resources/locale/eo.json rename to share/locale/eo.json diff --git a/resources/locale/es.json b/share/locale/es.json similarity index 100% rename from resources/locale/es.json rename to share/locale/es.json diff --git a/resources/locale/fi.json b/share/locale/fi.json similarity index 100% rename from resources/locale/fi.json rename to share/locale/fi.json diff --git a/resources/locale/fr.json b/share/locale/fr.json similarity index 100% rename from resources/locale/fr.json rename to share/locale/fr.json diff --git a/resources/locale/it.json b/share/locale/it.json similarity index 100% rename from resources/locale/it.json rename to share/locale/it.json diff --git a/resources/locale/nl.json b/share/locale/nl.json similarity index 100% rename from resources/locale/nl.json rename to share/locale/nl.json diff --git a/resources/locale/no.json b/share/locale/no.json similarity index 100% rename from resources/locale/no.json rename to share/locale/no.json diff --git a/resources/locale/pt.json b/share/locale/pt.json similarity index 100% rename from resources/locale/pt.json rename to share/locale/pt.json diff --git a/resources/locale/ru.json b/share/locale/ru.json similarity index 100% rename from resources/locale/ru.json rename to share/locale/ru.json diff --git a/resources/locale/tr.json b/share/locale/tr.json similarity index 100% rename from resources/locale/tr.json rename to share/locale/tr.json diff --git a/resources/version.txt b/share/version.txt similarity index 100% rename from resources/version.txt rename to share/version.txt diff --git a/resources/wordlist.txt b/share/wordlist.txt similarity index 100% rename from resources/wordlist.txt rename to share/wordlist.txt diff --git a/test/onionshare_strings_test.py b/test/onionshare_strings_test.py index 0a48e8ca..dc20a271 100644 --- a/test/onionshare_strings_test.py +++ b/test/onionshare_strings_test.py @@ -22,7 +22,7 @@ from onionshare import helpers, strings # Stub get_resource_path so it finds the correct path while running tests def get_resource_path(filename): - resources_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'resources') + resources_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'share') path = os.path.join(resources_dir, filename) return path helpers.get_resource_path = get_resource_path