3 # Thomas Nagy, 2007 (ita)
4 # Gustavo Carneiro (gjc), 2007
9 import TaskGen, Utils, Options
10 from Logs import debug, warn, info
11 from TaskGen import extension, before, after, feature
12 from Configure import conf
13 from config_c import parse_flags
21 void Py_Initialize(void);
22 void Py_Finalize(void);
35 @before('apply_incpaths', 'apply_lib_vars', 'apply_type_vars', 'apply_bundle')
36 @after('vars_target_cshlib')
38 self.default_install_path = '${PYTHONARCHDIR}'
39 self.uselib = self.to_list(getattr(self, 'uselib', ''))
40 if not 'PYEXT' in self.uselib:
41 self.uselib.append('PYEXT')
42 self.env['MACBUNDLE'] = True
44 @before('apply_link', 'apply_lib_vars', 'apply_type_vars')
45 @after('apply_bundle')
47 def pyext_shlib_ext(self):
48 # override shlib_PATTERN set by the osx module
49 self.env['shlib_PATTERN'] = self.env['pyext_PATTERN']
51 @before('apply_incpaths', 'apply_lib_vars', 'apply_type_vars')
53 def init_pyembed(self):
54 self.uselib = self.to_list(getattr(self, 'uselib', ''))
55 if not 'PYEMBED' in self.uselib:
56 self.uselib.append('PYEMBED')
59 def process_py(self, node):
60 if not (self.bld.is_install and self.install_path):
63 install_pyfile(self, node)
64 self.bld.add_post_fun(inst_py)
66 def install_pyfile(self, node):
67 path = self.bld.get_install_path(self.install_path + os.sep + node.name, self.env)
69 self.bld.install_files(self.install_path, [node], self.env, self.chmod, postpone=False)
70 if self.bld.is_install < 0:
71 info("* removing byte compiled python files")
78 if self.bld.is_install > 0:
79 if self.env['PYC'] or self.env['PYO']:
80 info("* byte compiling %r" % path)
84 import sys, py_compile
85 for pyfile in sys.argv[1:]:
86 py_compile.compile(pyfile, pyfile + 'c')
88 argv = [self.env['PYTHON'], '-c', program, path]
89 ret = Utils.pproc.Popen(argv).wait()
91 raise Utils.WafError('bytecode compilation failed %r' % path)
95 import sys, py_compile
96 for pyfile in sys.argv[1:]:
97 py_compile.compile(pyfile, pyfile + 'o')
99 argv = [self.env['PYTHON'], self.env['PYFLAGS_OPT'], '-c', program, path]
100 ret = Utils.pproc.Popen(argv).wait()
102 raise Utils.WafError('bytecode compilation failed %r' % path)
105 class py_taskgen(TaskGen.task_gen):
106 def __init__(self, *k, **kw):
107 TaskGen.task_gen.__init__(self, *k, **kw)
109 @before('apply_core')
110 @after('vars_target_cprogram', 'vars_target_cshlib')
113 self.default_install_path = '${PYTHONDIR}'
115 def _get_python_variables(python_exe, variables, imports=['import sys']):
116 """Run a python interpreter and print some variables"""
117 program = list(imports)
120 program.append("print(repr(%s))" % v)
121 os_env = dict(os.environ)
123 del os_env['MACOSX_DEPLOYMENT_TARGET'] # see comments in the OSX tool
126 proc = Utils.pproc.Popen([python_exe, "-c", '\n'.join(program)], stdout=Utils.pproc.PIPE, env=os_env)
127 output = proc.communicate()[0].split("\n") # do not touch, python3
129 if Options.options.verbose:
130 warn("Python program to extract python configuration variables failed:\n%s"
131 % '\n'.join(["line %03i: %s" % (lineno+1, line) for lineno, line in enumerate(program)]))
139 return_values.append(None)
140 elif (s[0] == "'" and s[-1] == "'") or (s[0] == '"' and s[-1] == '"'):
141 return_values.append(eval(s))
143 return_values.append(int(s))
148 def check_python_headers(conf, mandatory=True):
149 """Check for headers and libraries necessary to extend or embed python.
151 On success the environment variables xxx_PYEXT and xxx_PYEMBED are added for uselib
153 PYEXT: for compiling python extensions
154 PYEMBED: for embedding a python interpreter"""
156 if not conf.env['CC_NAME'] and not conf.env['CXX_NAME']:
157 conf.fatal('load a compiler first (gcc, g++, ..)')
159 if not conf.env['PYTHON_VERSION']:
160 conf.check_python_version()
163 python = env['PYTHON']
165 conf.fatal('could not find the python executable')
167 ## On Mac OSX we need to use mac bundles for python plugins
168 if Options.platform == 'darwin':
169 conf.check_tool('osx')
172 # Get some python configuration variables using distutils
173 v = 'prefix SO SYSLIBS LDFLAGS SHLIBS LIBDIR LIBPL INCLUDEPY Py_ENABLE_SHARED MACOSX_DEPLOYMENT_TARGET LDVERSION'.split()
174 (python_prefix, python_SO, python_SYSLIBS, python_LDFLAGS, python_SHLIBS,
175 python_LIBDIR, python_LIBPL, INCLUDEPY, Py_ENABLE_SHARED,
176 python_MACOSX_DEPLOYMENT_TARGET, python_LDVERSION) = \
177 _get_python_variables(python, ["get_config_var('%s') or ''" % x for x in v],
178 ['from distutils.sysconfig import get_config_var'])
180 conf.fatal("Python development headers not found (-v for details).")
182 conf.log.write("""Configuration returned from %r:
191 Py_ENABLE_SHARED = %r
192 MACOSX_DEPLOYMENT_TARGET = %r
194 """ % (python, python_prefix, python_SO, python_SYSLIBS, python_LDFLAGS, python_SHLIBS,
195 python_LIBDIR, python_LIBPL, INCLUDEPY, Py_ENABLE_SHARED, python_MACOSX_DEPLOYMENT_TARGET,
198 if python_MACOSX_DEPLOYMENT_TARGET:
199 conf.env['MACOSX_DEPLOYMENT_TARGET'] = python_MACOSX_DEPLOYMENT_TARGET
200 conf.environ['MACOSX_DEPLOYMENT_TARGET'] = python_MACOSX_DEPLOYMENT_TARGET
202 env['pyext_PATTERN'] = '%s'+python_SO
204 # Check for python libraries for embedding
205 if python_SYSLIBS is not None:
206 for lib in python_SYSLIBS.split():
207 if lib.startswith('-l'):
208 lib = lib[2:] # strip '-l'
209 env.append_value('LIB_PYEMBED', lib)
211 if python_SHLIBS is not None:
212 for lib in python_SHLIBS.split():
213 if lib.startswith('-l'):
214 env.append_value('LIB_PYEMBED', lib[2:]) # strip '-l'
216 env.append_value('LINKFLAGS_PYEMBED', lib)
218 if Options.platform != 'darwin' and python_LDFLAGS:
219 parse_flags(python_LDFLAGS, 'PYEMBED', env)
222 if not python_LDVERSION:
223 python_LDVERSION = env['PYTHON_VERSION']
224 name = 'python' + python_LDVERSION
226 if python_LIBDIR is not None:
227 path = [python_LIBDIR]
228 conf.log.write("\n\n# Trying LIBDIR: %r\n" % path)
229 result = conf.check(lib=name, uselib='PYEMBED', libpath=path)
231 if not result and python_LIBPL is not None:
232 conf.log.write("\n\n# try again with -L$python_LIBPL (some systems don't install the python library in $prefix/lib)\n")
233 path = [python_LIBPL]
234 result = conf.check(lib=name, uselib='PYEMBED', libpath=path)
237 conf.log.write("\n\n# try again with -L$prefix/libs, and pythonXY name rather than pythonX.Y (win32)\n")
238 path = [os.path.join(python_prefix, "libs")]
239 name = 'python' + python_LDVERSION.replace('.', '')
240 result = conf.check(lib=name, uselib='PYEMBED', libpath=path)
243 env['LIBPATH_PYEMBED'] = path
244 env.append_value('LIB_PYEMBED', name)
246 conf.log.write("\n\n### LIB NOT FOUND\n")
248 # under certain conditions, python extensions must link to
249 # python libraries, not just python embedding programs.
250 if (sys.platform == 'win32' or sys.platform.startswith('os2')
251 or sys.platform == 'darwin' or Py_ENABLE_SHARED):
252 env['LIBPATH_PYEXT'] = env['LIBPATH_PYEMBED']
253 env['LIB_PYEXT'] = env['LIB_PYEMBED']
255 # We check that pythonX.Y-config exists, and if it exists we
256 # use it to get only the includes, else fall back to distutils.
257 python_config = conf.find_program(
258 'python%s-config' % ('.'.join(env['PYTHON_VERSION'].split('.')[:2])),
260 if not python_config:
261 python_config = conf.find_program(
262 'python-config-%s' % ('.'.join(env['PYTHON_VERSION'].split('.')[:2])),
267 for incstr in Utils.cmd_output("%s %s --includes" % (python, python_config)).strip().split():
269 if (incstr.startswith('-I')
270 or incstr.startswith('/I')):
272 # append include path, unless already given
273 if incstr not in includes:
274 includes.append(incstr)
275 conf.log.write("Include path for Python extensions "
276 "(found via python-config --includes): %r\n" % (includes,))
277 env['CPPPATH_PYEXT'] = includes
278 env['CPPPATH_PYEMBED'] = includes
280 conf.log.write("Include path for Python extensions "
281 "(found via distutils module): %r\n" % (INCLUDEPY,))
282 env['CPPPATH_PYEXT'] = [INCLUDEPY]
283 env['CPPPATH_PYEMBED'] = [INCLUDEPY]
285 # Code using the Python API needs to be compiled with -fno-strict-aliasing
286 if env['CC_NAME'] == 'gcc':
287 env.append_value('CCFLAGS_PYEMBED', '-fno-strict-aliasing')
288 env.append_value('CCFLAGS_PYEXT', '-fno-strict-aliasing')
289 if env['CXX_NAME'] == 'gcc':
290 env.append_value('CXXFLAGS_PYEMBED', '-fno-strict-aliasing')
291 env.append_value('CXXFLAGS_PYEXT', '-fno-strict-aliasing')
294 conf.check(define_name='HAVE_PYTHON_H',
295 uselib='PYEMBED', fragment=FRAG_2,
296 errmsg='Could not find the python development headers', mandatory=mandatory)
299 def check_python_version(conf, minver=None):
301 Check if the python interpreter is found matching a given minimum version.
302 minver should be a tuple, eg. to check for python >= 2.4.2 pass (2,4,2) as minver.
304 If successful, PYTHON_VERSION is defined as 'MAJOR.MINOR'
305 (eg. '2.4') of the actual python version found, and PYTHONDIR is
306 defined, pointing to the site-packages directory appropriate for
307 this python version, where modules/packages/extensions should be
310 assert minver is None or isinstance(minver, tuple)
311 python = conf.env['PYTHON']
313 conf.fatal('could not find the python executable')
315 # Get python version string
316 cmd = [python, "-c", "import sys\nfor x in sys.version_info: print(str(x))"]
317 debug('python: Running python command %r' % cmd)
318 proc = Utils.pproc.Popen(cmd, stdout=Utils.pproc.PIPE, shell=False)
319 lines = proc.communicate()[0].split()
320 assert len(lines) == 5, "found %i lines, expected 5: %r" % (len(lines), lines)
321 pyver_tuple = (int(lines[0]), int(lines[1]), int(lines[2]), lines[3], int(lines[4]))
323 # compare python version with the minimum required
324 result = (minver is None) or (pyver_tuple >= minver)
327 # define useful environment variables
328 pyver = '.'.join([str(x) for x in pyver_tuple[:2]])
329 conf.env['PYTHON_VERSION'] = pyver
331 if 'PYTHONDIR' in conf.environ:
332 pydir = conf.environ['PYTHONDIR']
334 if sys.platform == 'win32':
335 (python_LIBDEST, pydir) = \
336 _get_python_variables(python,
337 ["get_config_var('LIBDEST') or ''",
338 "get_python_lib(standard_lib=0, prefix=%r) or ''" % conf.env['PREFIX']],
339 ['from distutils.sysconfig import get_config_var, get_python_lib'])
341 python_LIBDEST = None
343 _get_python_variables(python,
344 ["get_python_lib(standard_lib=0, prefix=%r) or ''" % conf.env['PREFIX']],
345 ['from distutils.sysconfig import get_config_var, get_python_lib'])
346 if python_LIBDEST is None:
347 if conf.env['LIBDIR']:
348 python_LIBDEST = os.path.join(conf.env['LIBDIR'], "python" + pyver)
350 python_LIBDEST = os.path.join(conf.env['PREFIX'], "lib", "python" + pyver)
352 if 'PYTHONARCHDIR' in conf.environ:
353 pyarchdir = conf.environ['PYTHONARCHDIR']
355 (pyarchdir,) = _get_python_variables(python,
356 ["get_python_lib(plat_specific=1, standard_lib=0, prefix=%r) or ''" % conf.env['PREFIX']],
357 ['from distutils.sysconfig import get_config_var, get_python_lib'])
361 if hasattr(conf, 'define'): # conf.define is added by the C tool, so may not exist
362 conf.define('PYTHONDIR', pydir)
363 conf.define('PYTHONARCHDIR', pyarchdir)
365 conf.env['PYTHONDIR'] = pydir
368 pyver_full = '.'.join(map(str, pyver_tuple[:3]))
370 conf.check_message_custom('Python version', '', pyver_full)
372 minver_str = '.'.join(map(str, minver))
373 conf.check_message('Python version', ">= %s" % minver_str, result, option=pyver_full)
376 conf.fatal('The python version is too old (%r)' % pyver_full)
379 def check_python_module(conf, module_name):
381 Check if the selected python interpreter can import the given python module.
383 result = not Utils.pproc.Popen([conf.env['PYTHON'], "-c", "import %s" % module_name],
384 stderr=Utils.pproc.PIPE, stdout=Utils.pproc.PIPE).wait()
385 conf.check_message('Python module', module_name, result)
387 conf.fatal('Could not find the python module %r' % module_name)
391 if not conf.env.PYTHON:
392 conf.env.PYTHON = sys.executable
394 python = conf.find_program('python', var='PYTHON')
396 conf.fatal('Could not find the path of the python executable')
398 if conf.env.PYTHON != sys.executable:
399 warn("python executable '%s' different from sys.executable '%s'" % (conf.env.PYTHON, sys.executable))
402 v['PYCMD'] = '"import sys, py_compile;py_compile.compile(sys.argv[1], sys.argv[2])"'
404 v['PYFLAGS_OPT'] = '-O'
406 v['PYC'] = getattr(Options.options, 'pyc', 1)
407 v['PYO'] = getattr(Options.options, 'pyo', 1)
409 def set_options(opt):
410 opt.add_option('--nopyc',
411 action='store_false',
413 help = 'Do not install bytecode compiled .pyc files (configuration) [Default:install]',
415 opt.add_option('--nopyo',
416 action='store_false',
418 help='Do not install optimised compiled .pyo files (configuration) [Default:install]',