diff --git a/extras/build_web2py/build_web2py.py b/extras/build_web2py/build_web2py.py new file mode 100644 index 00000000..1734ab2c --- /dev/null +++ b/extras/build_web2py/build_web2py.py @@ -0,0 +1,145 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# up to 2019, we have used py2applet, py2exe and bbfreeze for building web2py binaries +# The original scripts can be found on GitHub for web2py up to version 2.18.4 +# See also Niphlod's work on http://www.web2pyslices.com/slice/show/1726/build-windows-binaries +# Then we switched to Pyinstaller in order to fully support Python 3 + +from distutils.core import setup +from gluon.import_all import base_modules, contributed_modules +from gluon.fileutils import readlines_file +from glob import glob +import os +import shutil +import sys +import re +import zipfile +import subprocess +import platform + + +USAGE = """ +build_web2py - make web2py Windows and MacOS binaries with pyinstaller + +Usage: + Install pyinstaller program, copy this file to web2py root folder and run: + + python build_py3.py + +""" + +if len(sys.argv) != 1 or not os.path.isfile('web2py.py'): + print(USAGE) + sys.exit(1) +os_version = platform.system() +if os_version not in ('Windows', 'Darwin'): + print('Unsupported system: %s' % os_version) + sys.exit(1) + + +def unzip(source_filename, dest_dir): + with zipfile.ZipFile(source_filename) as zf: + zf.extractall(dest_dir) + +# borrowed from http://bytes.com/topic/python/answers/851018-how-zip-directory-python-using-zipfile + + +def recursive_zip(zipf, directory, folder=""): + for item in os.listdir(directory): + if os.path.isfile(os.path.join(directory, item)): + zipf.write(os.path.join(directory, item), folder + os.sep + item) + elif os.path.isdir(os.path.join(directory, item)): + recursive_zip( + zipf, os.path.join(directory, item), folder + os.sep + item) + + +# read web2py version from VERSION file +web2py_version_line = readlines_file('VERSION')[0] +# use regular expression to get just the version number +v_re = re.compile('[0-9]+\.[0-9]+\.[0-9]+') +web2py_version = v_re.search(web2py_version_line).group(0) + +# Python base version +python_version = sys.version_info[:3] + + +if os_version == 'Windows': + print("\nBuilding binary web2py for Windows\n") + # to make executable without GUI we need this trick + shutil.copy("web2py.py", "web2py_no_console.py") + + subprocess.run('pyinstaller --clean --icon=extras/icons/web2py.ico --hidden-import=gluon.packages.dal.pydal \ + --hidden-import=gluon.packages.yatl.yatl --hidden-import=site-packages web2py.py') + subprocess.run('pyinstaller -w --clean --icon=extras/icons/web2py.ico --hidden-import=gluon.packages.dal.pydal \ + --hidden-import=gluon.packages.yatl.yatl --hidden-import=site-packages web2py_no_console.py') + + # cleanup + move binary files to dist folder + os.unlink('web2py_no_console.py') + os.unlink('web2py_no_console.spec') + source = 'dist/web2py/' + for files in os.listdir(source): + shutil.move(os.path.join(source, files), 'dist') + source2 = 'dist/web2py_no_console/' + files = 'web2py_no_console.exe' + shutil.move(os.path.join(source2, files), 'dist') + shutil.rmtree(source) + shutil.rmtree(source2) + os.unlink('dist/web2py.exe.manifest') + + zip_filename = 'web2py_win' + bin_folder = 'dist' + + +elif os_version == 'Darwin': + print("\nBuilding binary web2py for MacOS\n") + + import subprocess + subprocess.call("pyinstaller --clean --windowed --icon=extras/icons/web2py.icns --hidden-import=gluon.packages.dal.pydal --hidden-import=gluon.packages.yatl.yatl \ + --hidden-import=site-packages --add-binary='/System/Library/Frameworks/Tk.framework/Tk':'tk' \ + --add-binary='/System/Library/Frameworks/Tcl.framework/Tcl':'tcl' web2py.py", shell=True) + + # cleanup + move binary files to dist folder + shutil.rmtree(os.path.join('dist', 'web2py')) + shutil.rmtree('build') + + zip_filename = 'web2py_osx' + bin_folder = (os.path.join('dist', 'web2py.app/Contents/MacOS')) + +print("\nWeb2py binary successfully built!\n") + + +# add data_files +for req in ['CHANGELOG', 'LICENSE', 'VERSION']: + shutil.copy(req, os.path.join(bin_folder, req)) +# cleanup unuseful binary cache +for dirpath, dirnames, files in os.walk('.'): + if dirpath.endswith('__pycache__'): + print('Deleting cached binary directory : %s' % dirpath) + shutil.rmtree(dirpath) + +print("\nPreparing package ...") +# misc +for folders in ['gluon', 'extras', 'site-packages', 'scripts', 'applications', 'examples', 'handlers']: + shutil.copytree(folders, os.path.join(bin_folder, folders)) +os.mkdir(os.path.join(bin_folder, 'logs')) +os.unlink('web2py.spec') + + +# create a web2py folder & copy dist's files into it +shutil.copytree('dist', 'zip_temp/web2py') +# create zip file +zipf = zipfile.ZipFile(zip_filename + ".zip", + "w", compression=zipfile.ZIP_DEFLATED) +# just temp so the web2py directory is included in our zip file +path = 'zip_temp' +# leave the first folder as None, as path is root. +recursive_zip(zipf, path) +zipf.close() +shutil.rmtree('zip_temp') +shutil.rmtree('dist') + +print("Your binary version of web2py can be found in " + \ + zip_filename + ".zip") +print("You may extract the archive anywhere and then run web2py without worrying about dependency") +print("\nEnjoy binary web2py " + web2py_version_line + "\n with embedded Python " + sys.version + "\n")