Merge pull request #271 from niphlod/enhancement/windows_build
fix building on windows. New handler for gevented webserver
This commit is contained in:
@@ -21,4 +21,7 @@ zip_filename = web2py_win
|
|||||||
|
|
||||||
#should the build, deposit & dist directories used by py2exe be removed?
|
#should the build, deposit & dist directories used by py2exe be removed?
|
||||||
#if you created a zip file you likely don't need these directories anymore
|
#if you created a zip file you likely don't need these directories anymore
|
||||||
remove_build_files = Yes
|
remove_build_files = Yes
|
||||||
|
|
||||||
|
#should the build include the gevented webserver (needs gevent)
|
||||||
|
include_gevent = Yes
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
#Adapted from http://bazaar.launchpad.net/~flavour/sahana-eden/trunk/view/head:/static/scripts/tools/standalone_exe.py
|
#Adapted from http://bazaar.launchpad.net/~flavour/sahana-eden/trunk/view/head:/static/scripts/tools/standalone_exe.py
|
||||||
|
|
||||||
USAGE = """
|
USAGE = """
|
||||||
Usage:
|
Usage:
|
||||||
Copy this and setup_exe.conf to web2py root folder
|
Copy this and setup_exe.conf to web2py root folder
|
||||||
@@ -13,7 +13,7 @@ Usage:
|
|||||||
Install bbfreeze: https://pypi.python.org/pypi/bbfreeze/
|
Install bbfreeze: https://pypi.python.org/pypi/bbfreeze/
|
||||||
run python setup_exe.py bbfreeze
|
run python setup_exe.py bbfreeze
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from distutils.core import setup
|
from distutils.core import setup
|
||||||
from gluon.import_all import base_modules, contributed_modules
|
from gluon.import_all import base_modules, contributed_modules
|
||||||
from gluon.fileutils import readlines_file
|
from gluon.fileutils import readlines_file
|
||||||
@@ -24,7 +24,7 @@ import shutil
|
|||||||
import sys
|
import sys
|
||||||
import re
|
import re
|
||||||
import zipfile
|
import zipfile
|
||||||
|
|
||||||
if len(sys.argv) != 2 or not os.path.isfile('web2py.py'):
|
if len(sys.argv) != 2 or not os.path.isfile('web2py.py'):
|
||||||
print USAGE
|
print USAGE
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
@@ -32,11 +32,11 @@ BUILD_MODE = sys.argv[1]
|
|||||||
if not BUILD_MODE in ('py2exe', 'bbfreeze'):
|
if not BUILD_MODE in ('py2exe', 'bbfreeze'):
|
||||||
print USAGE
|
print USAGE
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
def unzip(source_filename, dest_dir):
|
def unzip(source_filename, dest_dir):
|
||||||
with zipfile.ZipFile(source_filename) as zf:
|
with zipfile.ZipFile(source_filename) as zf:
|
||||||
zf.extractall(dest_dir)
|
zf.extractall(dest_dir)
|
||||||
|
|
||||||
#borrowed from http://bytes.com/topic/python/answers/851018-how-zip-directory-python-using-zipfile
|
#borrowed from http://bytes.com/topic/python/answers/851018-how-zip-directory-python-using-zipfile
|
||||||
def recursive_zip(zipf, directory, folder=""):
|
def recursive_zip(zipf, directory, folder=""):
|
||||||
for item in os.listdir(directory):
|
for item in os.listdir(directory):
|
||||||
@@ -45,14 +45,14 @@ def recursive_zip(zipf, directory, folder=""):
|
|||||||
elif os.path.isdir(os.path.join(directory, item)):
|
elif os.path.isdir(os.path.join(directory, item)):
|
||||||
recursive_zip(
|
recursive_zip(
|
||||||
zipf, os.path.join(directory, item), folder + os.sep + item)
|
zipf, os.path.join(directory, item), folder + os.sep + item)
|
||||||
|
|
||||||
|
|
||||||
#read web2py version from VERSION file
|
#read web2py version from VERSION file
|
||||||
web2py_version_line = readlines_file('VERSION')[0]
|
web2py_version_line = readlines_file('VERSION')[0]
|
||||||
#use regular expression to get just the version number
|
#use regular expression to get just the version number
|
||||||
v_re = re.compile('[0-9]+\.[0-9]+\.[0-9]+')
|
v_re = re.compile('[0-9]+\.[0-9]+\.[0-9]+')
|
||||||
web2py_version = v_re.search(web2py_version_line).group(0)
|
web2py_version = v_re.search(web2py_version_line).group(0)
|
||||||
|
|
||||||
#pull in preferences from config file
|
#pull in preferences from config file
|
||||||
import ConfigParser
|
import ConfigParser
|
||||||
Config = ConfigParser.ConfigParser()
|
Config = ConfigParser.ConfigParser()
|
||||||
@@ -64,20 +64,16 @@ copy_scripts = Config.getboolean("Setup", "copy_scripts")
|
|||||||
make_zip = Config.getboolean("Setup", "make_zip")
|
make_zip = Config.getboolean("Setup", "make_zip")
|
||||||
zip_filename = Config.get("Setup", "zip_filename")
|
zip_filename = Config.get("Setup", "zip_filename")
|
||||||
remove_build_files = Config.getboolean("Setup", "remove_build_files")
|
remove_build_files = Config.getboolean("Setup", "remove_build_files")
|
||||||
|
include_gevent = Config.getboolean("Setup", "include_gevent")
|
||||||
|
|
||||||
# Python base version
|
# Python base version
|
||||||
python_version = sys.version_info[:3]
|
python_version = sys.version_info[:3]
|
||||||
|
|
||||||
if python_version > (2,5):
|
|
||||||
# Python26 compatibility: http://www.py2exe.org/index.cgi/Tutorial#Step52
|
|
||||||
try:
|
|
||||||
shutil.copytree('C:\Bin\Microsoft.VC90.CRT', 'dist/')
|
|
||||||
except:
|
|
||||||
print "You MUST copy Microsoft.VC90.CRT folder into the archive"
|
|
||||||
|
|
||||||
if BUILD_MODE == 'py2exe':
|
if BUILD_MODE == 'py2exe':
|
||||||
import py2exe
|
import py2exe
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
console=[{'script':'web2py.py',
|
console=[{'script':'web2py.py',
|
||||||
'icon_resources': [(0, 'extras/icons/web2py.ico')]
|
'icon_resources': [(0, 'extras/icons/web2py.ico')]
|
||||||
@@ -112,35 +108,49 @@ if BUILD_MODE == 'py2exe':
|
|||||||
zipl.close()
|
zipl.close()
|
||||||
shutil.rmtree(library_temp_dir)
|
shutil.rmtree(library_temp_dir)
|
||||||
print "web2py binary successfully built"
|
print "web2py binary successfully built"
|
||||||
|
|
||||||
elif BUILD_MODE == 'bbfreeze':
|
elif BUILD_MODE == 'bbfreeze':
|
||||||
modules = base_modules + contributed_modules
|
modules = base_modules + contributed_modules
|
||||||
from bbfreeze import Freezer
|
from bbfreeze import Freezer
|
||||||
f = Freezer(distdir="dist", includes=(modules))
|
f = Freezer(distdir="dist", includes=(modules))
|
||||||
#f.addScript("web2py_gevent.py")
|
|
||||||
f.addScript("web2py.py")
|
f.addScript("web2py.py")
|
||||||
#to make executable without GUI we need this trick
|
#to make executable without GUI we need this trick
|
||||||
shutil.copy("web2py.py", "web2py_no_console.py")
|
shutil.copy("web2py.py", "web2py_no_console.py")
|
||||||
f.addScript("web2py_no_console.py", gui_only=True)
|
f.addScript("web2py_no_console.py", gui_only=True)
|
||||||
|
if include_gevent:
|
||||||
|
#fetch the gevented webserver script and copy to root
|
||||||
|
gevented_webserver = os.path.join("handlers", "web2py_on_gevent.py")
|
||||||
|
shutil.copy(gevented_webserver, "web2py_on_gevent.py")
|
||||||
|
f.addScript("web2py_on_gevent.py")
|
||||||
f.setIcon('extras/icons/web2py.ico')
|
f.setIcon('extras/icons/web2py.ico')
|
||||||
f() # starts the freezing process
|
f() # starts the freezing process
|
||||||
os.unlink("web2py_no_console.py")
|
os.unlink("web2py_no_console.py")
|
||||||
|
if include_gevent:
|
||||||
|
os.unlink("web2py_on_gevent.py")
|
||||||
#add data_files
|
#add data_files
|
||||||
for req in ['ABOUT', 'LICENSE', 'VERSION']:
|
for req in ['ABOUT', 'LICENSE', 'VERSION']:
|
||||||
shutil.copy(req, os.path.join('dist', req))
|
shutil.copy(req, os.path.join('dist', req))
|
||||||
print "web2py binary successfully built"
|
print "web2py binary successfully built"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
os.unlink('storage.sqlite')
|
os.unlink('storage.sqlite')
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
#This need to happen after bbfreeze is run because Freezer() deletes distdir before starting!
|
||||||
|
if python_version > (2,5):
|
||||||
|
# Python26 compatibility: http://www.py2exe.org/index.cgi/Tutorial#Step52
|
||||||
|
try:
|
||||||
|
shutil.copytree('C:\Bin\Microsoft.VC90.CRT', 'dist/Microsoft.VC90.CRT/')
|
||||||
|
except:
|
||||||
|
print "You MUST copy Microsoft.VC90.CRT folder into the archive"
|
||||||
|
|
||||||
def copy_folders(source, destination):
|
def copy_folders(source, destination):
|
||||||
"""Copy files & folders from source to destination (within dist/)"""
|
"""Copy files & folders from source to destination (within dist/)"""
|
||||||
if os.path.exists(os.path.join('dist', destination)):
|
if os.path.exists(os.path.join('dist', destination)):
|
||||||
shutil.rmtree(os.path.join('dist', destination))
|
shutil.rmtree(os.path.join('dist', destination))
|
||||||
shutil.copytree(os.path.join(source), os.path.join('dist', destination))
|
shutil.copytree(os.path.join(source), os.path.join('dist', destination))
|
||||||
|
|
||||||
#should we remove Windows OS dlls user is unlikely to be able to distribute
|
#should we remove Windows OS dlls user is unlikely to be able to distribute
|
||||||
if remove_msft_dlls:
|
if remove_msft_dlls:
|
||||||
print "Deleted Microsoft files not licensed for open source distribution"
|
print "Deleted Microsoft files not licensed for open source distribution"
|
||||||
@@ -156,7 +166,7 @@ if remove_msft_dlls:
|
|||||||
os.unlink(os.path.join('dist', f))
|
os.unlink(os.path.join('dist', f))
|
||||||
except:
|
except:
|
||||||
print "unable to delete dist/" + f
|
print "unable to delete dist/" + f
|
||||||
|
|
||||||
#Should we include applications?
|
#Should we include applications?
|
||||||
if copy_apps:
|
if copy_apps:
|
||||||
copy_folders('applications', 'applications')
|
copy_folders('applications', 'applications')
|
||||||
@@ -167,12 +177,12 @@ else:
|
|||||||
copy_folders('applications/welcome', 'applications/welcome')
|
copy_folders('applications/welcome', 'applications/welcome')
|
||||||
copy_folders('applications/examples', 'applications/examples')
|
copy_folders('applications/examples', 'applications/examples')
|
||||||
print "Only web2py's admin, examples & welcome applications have been added"
|
print "Only web2py's admin, examples & welcome applications have been added"
|
||||||
|
|
||||||
copy_folders('extras', 'extras')
|
copy_folders('extras', 'extras')
|
||||||
copy_folders('examples', 'examples')
|
copy_folders('examples', 'examples')
|
||||||
copy_folders('handlers', 'handlers')
|
copy_folders('handlers', 'handlers')
|
||||||
|
|
||||||
|
|
||||||
#should we copy project's site-packages into dist/site-packages
|
#should we copy project's site-packages into dist/site-packages
|
||||||
if copy_site_packages:
|
if copy_site_packages:
|
||||||
#copy site-packages
|
#copy site-packages
|
||||||
@@ -180,7 +190,7 @@ if copy_site_packages:
|
|||||||
else:
|
else:
|
||||||
#no worries, web2py will create the (empty) folder first run
|
#no worries, web2py will create the (empty) folder first run
|
||||||
print "Skipping site-packages"
|
print "Skipping site-packages"
|
||||||
|
|
||||||
#should we copy project's scripts into dist/scripts
|
#should we copy project's scripts into dist/scripts
|
||||||
if copy_scripts:
|
if copy_scripts:
|
||||||
#copy scripts
|
#copy scripts
|
||||||
@@ -188,7 +198,7 @@ if copy_scripts:
|
|||||||
else:
|
else:
|
||||||
#no worries, web2py will create the (empty) folder first run
|
#no worries, web2py will create the (empty) folder first run
|
||||||
print "Skipping scripts"
|
print "Skipping scripts"
|
||||||
|
|
||||||
#should we create a zip file of the build?
|
#should we create a zip file of the build?
|
||||||
if make_zip:
|
if make_zip:
|
||||||
#create a web2py folder & copy dist's files into it
|
#create a web2py folder & copy dist's files into it
|
||||||
@@ -205,7 +215,7 @@ if make_zip:
|
|||||||
print "Your Windows binary version of web2py can be found in " + \
|
print "Your Windows binary version of web2py can be found in " + \
|
||||||
zip_filename + ".zip"
|
zip_filename + ".zip"
|
||||||
print "You may extract the archive anywhere and then run web2py/web2py.exe"
|
print "You may extract the archive anywhere and then run web2py/web2py.exe"
|
||||||
|
|
||||||
#should py2exe build files be removed?
|
#should py2exe build files be removed?
|
||||||
if remove_build_files:
|
if remove_build_files:
|
||||||
if BUILD_MODE == 'py2exe':
|
if BUILD_MODE == 'py2exe':
|
||||||
@@ -213,10 +223,10 @@ if remove_build_files:
|
|||||||
shutil.rmtree('deposit')
|
shutil.rmtree('deposit')
|
||||||
shutil.rmtree('dist')
|
shutil.rmtree('dist')
|
||||||
print "build files removed"
|
print "build files removed"
|
||||||
|
|
||||||
#final info
|
#final info
|
||||||
if not make_zip and not remove_build_files:
|
if not make_zip and not remove_build_files:
|
||||||
print "Your Windows binary & associated files can also be found in /dist"
|
print "Your Windows binary & associated files can also be found in /dist"
|
||||||
|
|
||||||
print "Finished!"
|
print "Finished!"
|
||||||
print "Enjoy web2py " + web2py_version_line
|
print "Enjoy web2py " + web2py_version_line
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import optparse
|
||||||
|
|
||||||
|
if '__file__' in globals():
|
||||||
|
path = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
elif hasattr(sys, 'frozen'):
|
||||||
|
path = os.path.dirname(os.path.abspath(sys.executable))
|
||||||
|
else:
|
||||||
|
path = os.getcwd()
|
||||||
|
os.chdir(path)
|
||||||
|
|
||||||
|
sys.path = [path] + [p for p in sys.path if not p == path]
|
||||||
|
|
||||||
|
from gevent import pywsgi
|
||||||
|
from gevent.pool import Pool
|
||||||
|
from gevent import monkey
|
||||||
|
monkey.patch_all()
|
||||||
|
|
||||||
|
def run(options):
|
||||||
|
import gluon.main
|
||||||
|
if options.password != '<recycle>':
|
||||||
|
gluon.main.save_password(options.password, int(options.port))
|
||||||
|
if options.logging:
|
||||||
|
application = gluon.main.appfactory(wsgiapp=gluon.main.wsgibase,
|
||||||
|
logfilename='httpserver.log',
|
||||||
|
profiler_dir=profiler)
|
||||||
|
else:
|
||||||
|
application = gluon.main.wsgibase
|
||||||
|
address = (options.ip, int(options.port))
|
||||||
|
workers = options.workers
|
||||||
|
spawn = workers and Pool(int(options.workers)) or 'default'
|
||||||
|
ssl_args = dict()
|
||||||
|
if options.ssl_private_key:
|
||||||
|
ssl_args['keyfile'] = options.ssl_private_key
|
||||||
|
if options.ssl_certificate:
|
||||||
|
ssl_args['certfile'] = options.ssl_certificate
|
||||||
|
server = pywsgi.WSGIServer(
|
||||||
|
address, application,
|
||||||
|
spawn=spawn, log=None,
|
||||||
|
**ssl_args
|
||||||
|
)
|
||||||
|
server.serve_forever()
|
||||||
|
|
||||||
|
def main():
|
||||||
|
usage = 'python web2py_gevent.py -i 127.0.0.1 -p 8000 -a "<recycle>"'
|
||||||
|
try:
|
||||||
|
version = open('VERSION','r')
|
||||||
|
except IOError:
|
||||||
|
version = ''
|
||||||
|
parser = optparse.OptionParser(usage, None, optparse.Option, version)
|
||||||
|
msg = ('password to be used for administration '
|
||||||
|
'(use -a "<recycle>" to reuse the last password))')
|
||||||
|
parser.add_option('-a',
|
||||||
|
'--password',
|
||||||
|
default='<recycle>',
|
||||||
|
dest='password',
|
||||||
|
help=msg)
|
||||||
|
|
||||||
|
parser.add_option('-c',
|
||||||
|
'--ssl_certificate',
|
||||||
|
default='',
|
||||||
|
dest='ssl_certificate',
|
||||||
|
help='file that contains ssl certificate')
|
||||||
|
|
||||||
|
parser.add_option('-k',
|
||||||
|
'--ssl_private_key',
|
||||||
|
default='',
|
||||||
|
dest='ssl_private_key',
|
||||||
|
help='file that contains ssl private key')
|
||||||
|
|
||||||
|
parser.add_option('-l',
|
||||||
|
'--logging',
|
||||||
|
action='store_true',
|
||||||
|
default=False,
|
||||||
|
dest='logging',
|
||||||
|
help='log into httpserver.log')
|
||||||
|
|
||||||
|
parser.add_option('-F',
|
||||||
|
'--profiler',
|
||||||
|
dest='profiler_dir',
|
||||||
|
default=None,
|
||||||
|
help='profiler dir')
|
||||||
|
|
||||||
|
parser.add_option('-i',
|
||||||
|
'--ip',
|
||||||
|
default='127.0.0.1',
|
||||||
|
dest='ip',
|
||||||
|
help='ip address')
|
||||||
|
|
||||||
|
parser.add_option('-p',
|
||||||
|
'--port',
|
||||||
|
default='8000',
|
||||||
|
dest='port',
|
||||||
|
help='port number')
|
||||||
|
|
||||||
|
parser.add_option('-w',
|
||||||
|
'--workers',
|
||||||
|
default=None,
|
||||||
|
dest='workers',
|
||||||
|
help='number of workers')
|
||||||
|
|
||||||
|
(options, args) = parser.parse_args()
|
||||||
|
print 'starting on %s:%s...' % (
|
||||||
|
options.ip, options.port)
|
||||||
|
run(options)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
|
|
||||||
Reference in New Issue
Block a user