Skip to content

Commit ffcb1d7

Browse files
Merge branch 'release/4.4.4'
2 parents bc36a27 + bdf81bb commit ffcb1d7

File tree

8 files changed

+297
-31
lines changed

8 files changed

+297
-31
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,4 @@ apxs
2323
libtool
2424
docs/_build
2525
newrelic.ini
26+
src/packages

README.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ If running Linux, any corresponding developer variant of the specific
4141
Apache package you are using also needs to be installed. This is required
4242
in order to be able to compile mod_wsgi from source code.
4343

44-
For example, on Ubuntu Linux, if you were using the Apache prefork MPM
45-
you would need both:
44+
For example, on Ubuntu Linux with Apache 2.2, if you were using the Apache
45+
prefork MPM you would need both:
4646

4747
* apache2-mpm-prefork
4848
* apache2-prefork-dev

docs/release-notes/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Release Notes
55
.. toctree::
66
:maxdepth: 2
77

8+
version-4.4.4.rst
89
version-4.4.3.rst
910
version-4.4.2.rst
1011
version-4.4.1.rst

docs/release-notes/version-4.4.4.rst

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
=============
2+
Version 4.4.4
3+
=============
4+
5+
Version 4.4.4 of mod_wsgi can be obtained from:
6+
7+
https://codeload.github.com/GrahamDumpleton/mod_wsgi/tar.gz/4.4.4
8+
9+
Known Issues
10+
------------
11+
12+
1. Although the makefiles for building mod_wsgi on Windows have now been
13+
updated for the new source code layout, some issues are being seen with
14+
mod_wsgi on Apache 2.4. These issues are still being investigated. As
15+
most new changes in 4.X relate to mod_wsgi daemon mode, which is not
16+
supported under Windows, you should keep using the last available binary
17+
for version 3.X on Windows instead. Binaries compiled by a third party
18+
can be obtained from:
19+
20+
* http://www.lfd.uci.edu/~gohlke/pythonlibs/#mod_wsgi
21+
22+
New Features
23+
------------
24+
25+
1. The ``mod_wsgi-express`` command will now output to ``stdout`` the
26+
number of daemon processes and threads being used.
27+
28+
2. Add automatic installation of precompiled Apache binaries when deploying
29+
``mod_wsgi-express`` to Heroku or OpenShift. These binaries will be pulled
30+
down from S3 and installed as part of the mod_wsgi package.

setup.py

Lines changed: 236 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,127 @@
44
import sys
55
import fnmatch
66
import subprocess
7+
import tarfile
8+
import shutil
9+
import stat
710
import re
811

12+
try:
13+
from urllib.request import urlretrieve
14+
except ImportError:
15+
from urllib import urlretrieve
16+
917
from setuptools import setup
1018
from distutils.core import Extension
1119
from distutils.sysconfig import get_config_var as get_python_config
1220
from distutils.sysconfig import get_python_lib
1321

14-
# Compile all available source files.
22+
# Before anything else, this setup.py uses various tricks to install
23+
# precompiled Apache binaries for the Heroku and OpenShift environments.
24+
# Once they are installed, then the installation of the mod_wsgi package
25+
# itself will be triggered, ensuring that it can be built against the
26+
# precompiled Apache binaries which were installed.
27+
#
28+
# We therefore first need to work out whether we are actually running on
29+
# either Heroku of OpenShift. If we are, then we identify the set of
30+
# precompiled binaries we are to use and copy it into the Python
31+
# installation.
32+
33+
PREFIX = 'https://s3.amazonaws.com'
34+
BUCKET = os.environ.get('MOD_WSGI_REMOTE_S3_BUCKET_NAME', 'modwsgi.org')
35+
36+
REMOTE_TARBALL_NAME = os.environ.get('MOD_WSGI_REMOTE_PACKAGES_NAME')
37+
38+
TGZ_OPENSHIFT='mod_wsgi-packages-openshift-centos6-apache-2.4.10-1.tar.gz'
39+
TGZ_HEROKU='mod_wsgi-packages-heroku-cedar14-apache-2.4.10-1.tar.gz'
40+
41+
if not REMOTE_TARBALL_NAME:
42+
if os.environ.get('OPENSHIFT_HOMEDIR'):
43+
REMOTE_TARBALL_NAME = TGZ_OPENSHIFT
44+
elif os.path.isdir('/app/.heroku'):
45+
REMOTE_TARBALL_NAME = TGZ_HEROKU
46+
47+
LOCAL_TARBALL_FILE = os.environ.get('MOD_WSGI_LOCAL_PACKAGES_FILE')
48+
49+
REMOTE_TARBALL_URL = None
50+
51+
if LOCAL_TARBALL_FILE is None and REMOTE_TARBALL_NAME:
52+
REMOTE_TARBALL_URL = '%s/%s/%s' % (PREFIX, BUCKET, REMOTE_TARBALL_NAME)
53+
54+
# Work out if we are actually performing an install as we don't want to
55+
# download any binaries or try and install them if we aren't. To
56+
# determine this, we need to scan through the arguments, skipping any
57+
# global options being passed to distutils and then look for 'install'.
58+
# Note that older versions of pip appear to use the 'egg_info' command
59+
# and not the 'install' command to somehow trigger installations.
60+
61+
WITH_PACKAGES = False
62+
SETUP_COMMAND = None
63+
64+
for arg in sys.argv[1:]:
65+
if arg.startswith('-'):
66+
continue
67+
68+
SETUP_COMMAND = arg
69+
70+
break
71+
72+
if REMOTE_TARBALL_URL or LOCAL_TARBALL_FILE:
73+
if arg in ('install', 'egg_info'):
74+
WITH_PACKAGES = True
75+
76+
# If we are doing an install, download the tarball and unpack it into
77+
# the 'packages' subdirectory. We will then add everything in that
78+
# directory as package data so that it will be installed into the Python
79+
# installation.
80+
81+
if WITH_PACKAGES:
82+
if REMOTE_TARBALL_URL:
83+
if not os.path.isfile(REMOTE_TARBALL_NAME):
84+
print('Downloading', REMOTE_TARBALL_URL)
85+
urlretrieve(REMOTE_TARBALL_URL, REMOTE_TARBALL_NAME+'.download')
86+
os.rename(REMOTE_TARBALL_NAME+'.download', REMOTE_TARBALL_NAME)
87+
LOCAL_TARBALL_FILE = REMOTE_TARBALL_NAME
88+
89+
shutil.rmtree('src/packages', ignore_errors=True)
90+
91+
tar = tarfile.open(LOCAL_TARBALL_FILE)
92+
tar.extractall('src/packages')
93+
tar.close()
94+
95+
open('src/packages/__init__.py', 'a').close()
96+
97+
package_files = []
98+
99+
for root, dirs, files in os.walk('src/packages', topdown=False):
100+
for name in files:
101+
path = os.path.join(root, name).split('/', 1)[1]
102+
package_files.append(path)
103+
print('adding ', path)
104+
105+
print('Running setup for Apache')
106+
107+
setup(name = 'mod_wsgi-packages',
108+
version = '1.0.0',
109+
packages = ['mod_wsgi', 'mod_wsgi.packages'],
110+
package_dir = {'mod_wsgi': 'src'},
111+
package_data = {'mod_wsgi': package_files},
112+
entry_points = { 'console_scripts':
113+
['mod_wsgi-apxs = mod_wsgi.packages.apxs:main'],},
114+
)
115+
116+
# From this point on we will now actually install mod_wsgi. First we need
117+
# to work out what all the available source code files are that should be
118+
# compiled.
15119

16120
source_files = [os.path.join('src/server', name) for name in
17121
os.listdir(os.path.join(os.path.dirname(os.path.abspath(__file__)),
18122
'src/server')) if fnmatch.fnmatch(name, '*.c')]
19123

20-
# Work out all the Apache specific compilation flags.
124+
# Work out all the Apache specific compilation flags. This is done using
125+
# the standard Apache apxs command unless we are installing our own build
126+
# of Apache. In that case we use Python code to do the equivalent of apxs
127+
# as apxs will not work due to paths not matching where it was installed.
21128

22129
def find_program(names, default=None, paths=[]):
23130
for name in names:
@@ -34,19 +141,102 @@ def find_program(names, default=None, paths=[]):
34141
elif not os.path.isabs(APXS):
35142
APXS = find_program([APXS], APXS, ['/usr/sbin', os.getcwd()])
36143

37-
if not os.path.isabs(APXS) or not os.access(APXS, os.X_OK):
38-
raise RuntimeError('The %r command appears not to be installed or is '
39-
'not executable. Please check the list of prerequisites in the '
40-
'documentation for this package and install any missing '
41-
'Apache httpd server packages.' % APXS)
144+
if not WITH_PACKAGES:
145+
if not os.path.isabs(APXS) or not os.access(APXS, os.X_OK):
146+
raise RuntimeError('The %r command appears not to be installed or '
147+
'is not executable. Please check the list of prerequisites '
148+
'in the documentation for this package and install any '
149+
'missing Apache httpd server packages.' % APXS)
150+
151+
if WITH_PACKAGES:
152+
SCRIPT_DIR = os.path.join(os.path.dirname(__file__), 'src', 'packages')
153+
154+
CONFIG_FILE = os.path.join(SCRIPT_DIR, 'apache/build/config_vars.mk')
155+
156+
CONFIG = {}
157+
158+
with open(CONFIG_FILE) as fp:
159+
for line in fp.readlines():
160+
name, value = line.split('=', 1)
161+
name = name.strip()
162+
value = value.strip()
163+
CONFIG[name] = value
164+
165+
_varprog = re.compile(r'\$(\w+|(?:\{[^}]*\}|\([^)]*\)))')
166+
167+
def expand_vars(value):
168+
if '$' not in value:
169+
return value
170+
171+
i = 0
172+
while True:
173+
m = _varprog.search(value, i)
174+
if not m:
175+
break
176+
i, j = m.span(0)
177+
name = m.group(1)
178+
if name.startswith('{') and name.endswith('}'):
179+
name = name[1:-1]
180+
elif name.startswith('(') and name.endswith(')'):
181+
name = name[1:-1]
182+
if name in CONFIG:
183+
tail = value[j:]
184+
value = value[:i] + CONFIG.get(name, '')
185+
i = len(value)
186+
value += tail
187+
else:
188+
i = j
189+
190+
return value
191+
192+
def get_apxs_config(name):
193+
value = CONFIG.get(name, '')
194+
sub_value = expand_vars(value)
195+
while value != sub_value:
196+
value = sub_value
197+
sub_value = expand_vars(value)
198+
return sub_value.replace('/mod_wsgi-packages/', SCRIPT_DIR+'/')
199+
200+
CONFIG['PREFIX'] = get_apxs_config('prefix')
201+
CONFIG['TARGET'] = get_apxs_config('target')
202+
CONFIG['SYSCONFDIR'] = get_apxs_config('sysconfdir')
203+
CONFIG['INCLUDEDIR'] = get_apxs_config('includedir')
204+
CONFIG['LIBEXECDIR'] = get_apxs_config('libexecdir')
205+
CONFIG['BINDIR'] = get_apxs_config('bindir')
206+
CONFIG['SBINDIR'] = get_apxs_config('sbindir')
207+
CONFIG['PROGNAME'] = get_apxs_config('progname')
208+
209+
_CFLAGS_NAMES = ['SHLTCFLAGS', 'CFLAGS', 'NOTEST_CPPFLAGS',
210+
'EXTRA_CPPFLAGS', 'EXTRA_CFLAGS']
211+
212+
_CFLAGS_VALUES = []
213+
214+
for name in _CFLAGS_NAMES:
215+
value = get_apxs_config(name)
216+
217+
# Heroku doesn't appear to run the same version of gcc
218+
# that a standard Ubuntu installation does and which was
219+
# used to originally build the Apache binaries. We need
220+
# therefore to strip out flags that the Heroku gcc may
221+
# not understand.
42222

43-
def get_apxs_config(query):
44-
p = subprocess.Popen([APXS, '-q', query],
45-
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
46-
out, err = p.communicate()
47-
if isinstance(out, bytes):
48-
out = out.decode('UTF-8')
49-
return out.strip()
223+
if value:
224+
if os.path.isdir('/app/.heroku'):
225+
value = value.replace('-prefer-pic', '')
226+
227+
if value:
228+
_CFLAGS_VALUES.append(value)
229+
230+
CONFIG['CFLAGS'] = ' '.join(_CFLAGS_VALUES)
231+
232+
else:
233+
def get_apxs_config(query):
234+
p = subprocess.Popen([APXS, '-q', query],
235+
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
236+
out, err = p.communicate()
237+
if isinstance(out, bytes):
238+
out = out.decode('UTF-8')
239+
return out.strip()
50240

51241
INCLUDEDIR = get_apxs_config('INCLUDEDIR')
52242
CPPFLAGS = get_apxs_config('CPPFLAGS').split()
@@ -56,8 +246,12 @@ def get_apxs_config(query):
56246
EXTRA_CPPFLAGS = get_apxs_config('EXTRA_CPPFLAGS').split()
57247
EXTRA_CFLAGS = get_apxs_config('EXTRA_CFLAGS').split()
58248

59-
# Write out apxs_config.py which caches various configuration
60-
# related to Apache.
249+
# Write out apxs_config.py which caches various configuration related to
250+
# Apache. For the case of using our own Apache build, this needs to
251+
# calculate values dynamically based on where binaries were installed.
252+
# This is necessary as on OpenShift the virtual environment gets copied
253+
# for each gear to a different path. We can't therefore rely on a hard
254+
# coded path.
61255

62256
BINDIR = get_apxs_config('BINDIR')
63257
SBINDIR = get_apxs_config('SBINDIR')
@@ -68,6 +262,26 @@ def get_apxs_config(query):
68262
LIBEXECDIR = get_apxs_config('LIBEXECDIR')
69263
SHLIBPATH_VAR = get_apxs_config('SHLIBPATH_VAR')
70264

265+
APXS_CONFIG_TEMPLATE = """
266+
import os
267+
268+
WITH_PACKAGES = %(WITH_PACKAGES)r
269+
270+
if WITH_PACKAGES:
271+
import mod_wsgi.packages
272+
PACKAGES = os.path.join(os.path.dirname(mod_wsgi.packages.__file__))
273+
BINDIR = os.path.join(PACKAGES, 'apache', 'bin')
274+
SBINDIR = BINDIR
275+
LIBEXECDIR = os.path.join(PACKAGES, 'apache', 'modules')
276+
else:
277+
BINDIR = '%(BINDIR)s'
278+
SBINDIR = '%(SBINDIR)s'
279+
LIBEXECDIR = '%(LIBEXECDIR)s'
280+
281+
MPM_NAME = '%(MPM_NAME)s'
282+
PROGNAME = '%(PROGNAME)s'
283+
SHLIBPATH_VAR = '%(SHLIBPATH_VAR)s'
284+
71285
if os.path.exists(os.path.join(SBINDIR, PROGNAME)):
72286
HTTPD = os.path.join(SBINDIR, PROGNAME)
73287
elif os.path.exists(os.path.join(BINDIR, PROGNAME)):
@@ -81,17 +295,14 @@ def get_apxs_config(query):
81295
ROTATELOGS = os.path.join(BINDIR, 'rotatelogs')
82296
else:
83297
ROTATELOGS = 'rotatelogs'
298+
"""
84299

85300
with open(os.path.join(os.path.dirname(__file__),
86301
'src/server/apxs_config.py'), 'w') as fp:
87-
print('HTTPD = "%s"' % HTTPD, file=fp)
88-
print('ROTATELOGS = "%s"' % ROTATELOGS, file=fp)
89-
print('BINDIR = "%s"' % BINDIR, file=fp)
90-
print('SBINDIR = "%s"' % SBINDIR, file=fp)
91-
print('PROGNAME = "%s"' % PROGNAME, file=fp)
92-
print('MPM_NAME = "%s"' % MPM_NAME, file=fp)
93-
print('LIBEXECDIR = "%s"' % LIBEXECDIR, file=fp)
94-
print('SHLIBPATH_VAR = "%s"' % SHLIBPATH_VAR, file=fp)
302+
print(APXS_CONFIG_TEMPLATE % dict(WITH_PACKAGES=WITH_PACKAGES,
303+
BINDIR=BINDIR, SBINDIR=SBINDIR, LIBEXECDIR=LIBEXECDIR,
304+
MPM_NAME=MPM_NAME, PROGNAME=PROGNAME,
305+
SHLIBPATH_VAR=SHLIBPATH_VAR), file=fp)
95306

96307
# Work out location of Python library and how to link it.
97308

@@ -235,5 +446,5 @@ def _version():
235446
ext_modules = [extension],
236447
entry_points = { 'console_scripts':
237448
['mod_wsgi-express = mod_wsgi.server:main'],},
238-
install_requires=['mod_wsgi-metrics >= 1.0.0'],
449+
install_requires = ['mod_wsgi-metrics >= 1.0.0'],
239450
)

0 commit comments

Comments
 (0)