4
4
import sys
5
5
import fnmatch
6
6
import subprocess
7
+ import tarfile
8
+ import shutil
9
+ import stat
7
10
import re
8
11
12
+ try :
13
+ from urllib .request import urlretrieve
14
+ except ImportError :
15
+ from urllib import urlretrieve
16
+
9
17
from setuptools import setup
10
18
from distutils .core import Extension
11
19
from distutils .sysconfig import get_config_var as get_python_config
12
20
from distutils .sysconfig import get_python_lib
13
21
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.
15
119
16
120
source_files = [os .path .join ('src/server' , name ) for name in
17
121
os .listdir (os .path .join (os .path .dirname (os .path .abspath (__file__ )),
18
122
'src/server' )) if fnmatch .fnmatch (name , '*.c' )]
19
123
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.
21
128
22
129
def find_program (names , default = None , paths = []):
23
130
for name in names :
@@ -34,19 +141,102 @@ def find_program(names, default=None, paths=[]):
34
141
elif not os .path .isabs (APXS ):
35
142
APXS = find_program ([APXS ], APXS , ['/usr/sbin' , os .getcwd ()])
36
143
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.
42
222
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 ()
50
240
51
241
INCLUDEDIR = get_apxs_config ('INCLUDEDIR' )
52
242
CPPFLAGS = get_apxs_config ('CPPFLAGS' ).split ()
@@ -56,8 +246,12 @@ def get_apxs_config(query):
56
246
EXTRA_CPPFLAGS = get_apxs_config ('EXTRA_CPPFLAGS' ).split ()
57
247
EXTRA_CFLAGS = get_apxs_config ('EXTRA_CFLAGS' ).split ()
58
248
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.
61
255
62
256
BINDIR = get_apxs_config ('BINDIR' )
63
257
SBINDIR = get_apxs_config ('SBINDIR' )
@@ -68,6 +262,26 @@ def get_apxs_config(query):
68
262
LIBEXECDIR = get_apxs_config ('LIBEXECDIR' )
69
263
SHLIBPATH_VAR = get_apxs_config ('SHLIBPATH_VAR' )
70
264
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
+
71
285
if os.path.exists(os.path.join(SBINDIR, PROGNAME)):
72
286
HTTPD = os.path.join(SBINDIR, PROGNAME)
73
287
elif os.path.exists(os.path.join(BINDIR, PROGNAME)):
@@ -81,17 +295,14 @@ def get_apxs_config(query):
81
295
ROTATELOGS = os.path.join(BINDIR, 'rotatelogs')
82
296
else:
83
297
ROTATELOGS = 'rotatelogs'
298
+ """
84
299
85
300
with open (os .path .join (os .path .dirname (__file__ ),
86
301
'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 )
95
306
96
307
# Work out location of Python library and how to link it.
97
308
@@ -235,5 +446,5 @@ def _version():
235
446
ext_modules = [extension ],
236
447
entry_points = { 'console_scripts' :
237
448
['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' ],
239
450
)
0 commit comments