1
2
3
4
5
6 "Python support"
7
8 import os, sys
9 import TaskGen, Utils, Utils, Runner, Options, Build
10 from Logs import debug, warn
11 from TaskGen import extension, taskgen, before, after, feature
12 from Configure import conf
13 import pproc
14
15 EXT_PY = ['.py']
16
17 @taskgen
18 @before('apply_incpaths')
19 @feature('pyext')
20 @before('apply_bundle')
21 -def init_pyext(self):
22 self.default_install_path = '${PYTHONDIR}'
23 self.uselib = self.to_list(self.uselib)
24 if not 'PYEXT' in self.uselib:
25 self.uselib.append('PYEXT')
26 self.env['MACBUNDLE'] = True
27
28 @taskgen
29 @before('apply_link')
30 @before('apply_lib_vars')
31 @after('apply_bundle')
32 @feature('pyext')
33 -def pyext_shlib_ext(self):
34
35 self.env['shlib_PATTERN'] = self.env['pyext_PATTERN']
36
37
38 @taskgen
39 @before('apply_incpaths')
40 @feature('pyembed')
41 -def init_pyembed(self):
42 self.uselib = self.to_list(self.uselib)
43 if not 'PYEMBED' in self.uselib:
44 self.uselib.append('PYEMBED')
45
49
57
59 files_to_install = []
60 for filename in self.to_list(self.source):
61 node = self.path.find_resource(filename)
62 if node is not None:
63 files_to_install.append(node.abspath())
64 else:
65 node = self.path.find_or_declare(filename)
66 if node is None:
67 raise Utils.WafError("Cannot install file %s: not found in %s"
68 % (filename, self.path))
69 else:
70 files_to_install.append(node.abspath(self.env))
71
72 installed_files = Build.bld.install_files(self.install_path, files_to_install, self.env, self.chmod)
73
74 if not installed_files:
75 return
76
77 if Options.commands['uninstall']:
78 print "* removing byte compiled python files"
79 for fname in installed_files:
80 try:
81 os.remove(fname + 'c')
82 except OSError:
83 pass
84 try:
85 os.remove(fname + 'o')
86 except OSError:
87 pass
88 else:
89 if self.env['PYC'] or self.env['PYO']:
90 print "* byte compiling python files"
91
92 if self.env['PYC']:
93 program = ("""
94 import sys, py_compile
95 for pyfile in sys.argv[1:]:
96 py_compile.compile(pyfile, pyfile + 'c')
97 """)
98 argv = [self.env['PYTHON'], "-c", program ]
99 argv.extend(installed_files)
100 retval = pproc.Popen(argv).wait()
101 if retval:
102 raise Utils.WafError("bytecode compilation failed")
103
104
105 if self.env['PYO']:
106 program = ("""
107 import sys, py_compile
108 for pyfile in sys.argv[1:]:
109 py_compile.compile(pyfile, pyfile + 'o')
110 """)
111 argv = [self.env['PYTHON'], self.env['PYFLAGS_OPT'], "-c", program ]
112 argv.extend(installed_files)
113 retval = pproc.Popen(argv).wait()
114 if retval:
115 raise Utils.WafError("bytecode compilation failed")
116
118 """Run a python interpreter and print some variables"""
119 program = list(imports)
120 program.append('')
121 for v in variables:
122 program.append("print repr(%s)" % v)
123 proc = pproc.Popen([python_exe, "-c", '\n'.join(program)], stdout=pproc.PIPE)
124 output = proc.communicate()[0].split("\n")
125 if proc.returncode:
126 if Logs.verbose:
127 warn("Python program to extract python configuration variables failed:\n%s"
128 % '\n'.join(["line %03i: %s" % (lineno+1, line) for lineno, line in enumerate(program)]))
129 raise ValueError
130 return_values = []
131 for s in output:
132 s = s.strip()
133 if not s:
134 continue
135 if s == 'None':
136 return_values.append(None)
137 elif s[0] == "'" and s[-1] == "'":
138 return_values.append(s[1:-1])
139 elif s[0].isdigit():
140 return_values.append(int(s))
141 else: break
142 return return_values
143
146 """Check for headers and libraries necessary to extend or embed python.
147
148 If successful, xxx_PYEXT and xxx_PYEMBED variables are defined in the
149 environment (for uselib). PYEXT should be used for compiling
150 python extensions, while PYEMBED should be used by programs that
151 need to embed a python interpreter.
152
153 Note: this test requires that check_python_version was previously
154 executed and successful."""
155
156 env = conf.env
157 python = env['PYTHON']
158 assert python, ("python is %r !" % (python,))
159
160
161 import checks
162 if checks.detect_platform(None) == 'darwin':
163 conf.check_tool('osx')
164
165 try:
166
167 v = 'prefix SO SYSLIBS SHLIBS LIBDIR LIBPL INCLUDEPY Py_ENABLE_SHARED'.split()
168 (python_prefix, python_SO, python_SYSLIBS, python_SHLIBS,
169 python_LIBDIR, python_LIBPL, INCLUDEPY, Py_ENABLE_SHARED) = \
170 _get_python_variables(python, ["get_config_var('%s')" % x for x in v],
171 ['from distutils.sysconfig import get_config_var'])
172 except ValueError:
173 conf.fatal("Python development headers not found (-v for details).")
174
175 Runner.print_log("""Configuration returned from %r:
176 python_prefix = %r
177 python_SO = %r
178 python_SYSLIBS = %r
179 python_SHLIBS = %r
180 python_LIBDIR = %r
181 python_LIBPL = %r
182 INCLUDEPY = %r
183 Py_ENABLE_SHARED = %r
184 """ % (python, python_prefix, python_SO, python_SYSLIBS, python_SHLIBS,
185 python_LIBDIR, python_LIBPL, INCLUDEPY, Py_ENABLE_SHARED))
186
187 env['pyext_PATTERN'] = '%s'+python_SO
188
189
190 if python_SYSLIBS is not None:
191 for lib in python_SYSLIBS.split():
192 if lib.startswith('-l'):
193 lib = lib[2:]
194 env.append_value('LIB_PYEMBED', lib)
195 if python_SHLIBS is not None:
196 for lib in python_SHLIBS.split():
197 if lib.startswith('-l'):
198 lib = lib[2:]
199 env.append_value('LIB_PYEMBED', lib)
200 lib = conf.create_library_configurator()
201 lib.name = 'python' + env['PYTHON_VERSION']
202 lib.uselib = 'PYEMBED'
203 lib.code = '''
204 #ifdef __cplusplus
205 extern "C" {
206 #endif
207 void Py_Initialize(void);
208 void Py_Finalize(void);
209 #ifdef __cplusplus
210 }
211 #endif
212 int main(int argc, char *argv[]) { Py_Initialize(); Py_Finalize(); return 0; }
213 '''
214 if python_LIBDIR is not None:
215 lib.path = [python_LIBDIR]
216 result = lib.run()
217 else:
218 result = 0
219
220
221 if not result:
222 if python_LIBPL is not None:
223 lib.path = [python_LIBPL]
224 result = lib.run()
225 else:
226 result = 0
227
228
229 if not result:
230 lib.path = [os.path.join(python_prefix, "libs")]
231 lib.name = 'python' + env['PYTHON_VERSION'].replace('.', '')
232 result = lib.run()
233
234 if result:
235 env['LIBPATH_PYEMBED'] = lib.path
236 env.append_value('LIB_PYEMBED', lib.name)
237
238
239
240 if (sys.platform == 'win32' or sys.platform.startswith('os2')
241 or sys.platform == 'darwin' or Py_ENABLE_SHARED):
242 env['LIBPATH_PYEXT'] = env['LIBPATH_PYEMBED']
243 env['LIB_PYEXT'] = env['LIB_PYEMBED']
244
245
246
247 python_config = conf.find_program(
248 'python%s-config' % ('.'.join(env['PYTHON_VERSION'].split('.')[:2])),
249 var='PYTHON_CONFIG')
250 if python_config:
251 includes = []
252 for incstr in os.popen("%s %s --includes" % (python, python_config)).readline().strip().split():
253
254 if (incstr.startswith('-I')
255 or incstr.startswith('/I')):
256 incstr = incstr[2:]
257
258 if incstr not in includes:
259 includes.append(incstr)
260 env['CPPPATH_PYEXT'] = list(includes)
261 env['CPPPATH_PYEMBED'] = list(includes)
262 else:
263 env['CPPPATH_PYEXT'] = [INCLUDEPY]
264 env['CPPPATH_PYEMBED'] = [INCLUDEPY]
265
266
267 if env['CC']:
268 version = os.popen("%s --version" % env['CC']).readline()
269 if '(GCC)' in version:
270 env.append_value('CCFLAGS_PYEMBED', '-fno-strict-aliasing')
271 env.append_value('CCFLAGS_PYEXT', '-fno-strict-aliasing')
272 if env['CXX']:
273 version = os.popen("%s --version" % env['CXX']).readline()
274 if '(GCC)' in version:
275 env.append_value('CXXFLAGS_PYEMBED', '-fno-strict-aliasing')
276 env.append_value('CXXFLAGS_PYEXT', '-fno-strict-aliasing')
277
278
279 header = conf.create_header_configurator()
280 header.name = 'Python.h'
281 header.define = 'HAVE_PYTHON_H'
282 header.uselib = 'PYEXT'
283 header.code = "#include <Python.h>\nint main(int argc, char *argv[]) { Py_Initialize(); Py_Finalize(); return 0; }"
284 result = header.run()
285 if not result:
286 conf.fatal("Python development headers not found.")
287
290 """
291 Check if the python interpreter is found matching a given minimum version.
292 minver should be a tuple, eg. to check for python >= 2.4.2 pass (2,4,2) as minver.
293
294 If successful, PYTHON_VERSION is defined as 'MAJOR.MINOR'
295 (eg. '2.4') of the actual python version found, and PYTHONDIR is
296 defined, pointing to the site-packages directory appropriate for
297 this python version, where modules/packages/extensions should be
298 installed.
299 """
300 assert minver is None or isinstance(minver, tuple)
301 python = conf.env['PYTHON']
302 assert python, ("python is %r !" % (python,))
303
304
305 cmd = [python, "-c", "import sys\nfor x in sys.version_info: print str(x)"]
306 debug('python: Running python command %r' % cmd)
307 proc = pproc.Popen(cmd, stdout=pproc.PIPE)
308 lines = proc.communicate()[0].split()
309 assert len(lines) == 5, "found %i lines, expected 5: %r" % (len(lines), lines)
310 pyver_tuple = (int(lines[0]), int(lines[1]), int(lines[2]), lines[3], int(lines[4]))
311
312
313 result = (minver is None) or (pyver_tuple >= minver)
314
315 if result:
316
317 pyver = '.'.join([str(x) for x in pyver_tuple[:2]])
318 conf.env['PYTHON_VERSION'] = pyver
319
320 if 'PYTHONDIR' in os.environ:
321 pydir = os.environ['PYTHONDIR']
322 else:
323 if sys.platform == 'win32':
324 (python_LIBDEST,) = \
325 _get_python_variables(python, ["get_config_var('LIBDEST')"],
326 ['from distutils.sysconfig import get_config_var'])
327 else:
328 python_LIBDEST = None
329 if python_LIBDEST is None:
330 if conf.env['LIBDIR']:
331 python_LIBDEST = os.path.join(conf.env['LIBDIR'], "python" + pyver)
332 else:
333 python_LIBDEST = os.path.join(conf.env['PREFIX'], "lib", "python" + pyver)
334 pydir = os.path.join(python_LIBDEST, "site-packages")
335
336 if hasattr(conf, 'define'):