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'.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
) = \
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
193 """ % (python
, python_prefix
, python_SO
, python_SYSLIBS
, python_LDFLAGS
, python_SHLIBS
,
194 python_LIBDIR
, python_LIBPL
, INCLUDEPY
, Py_ENABLE_SHARED
, python_MACOSX_DEPLOYMENT_TARGET
))
196 # Allow some python overrides from env vars for cross-compiling
197 os_env
= dict(os
.environ
)
199 override_python_LDFLAGS
= os_env
.get('python_LDFLAGS', None)
200 if override_python_LDFLAGS
is not None:
201 conf
.log
.write("python_LDFLAGS override from environment = %r\n" % (override_python_LDFLAGS
))
202 python_LDFLAGS
= override_python_LDFLAGS
204 override_python_LIBDIR
= os_env
.get('python_LIBDIR', None)
205 if override_python_LIBDIR
is not None:
206 conf
.log
.write("python_LIBDIR override from environment = %r\n" % (override_python_LIBDIR
))
207 python_LIBDIR
= override_python_LIBDIR
209 if python_MACOSX_DEPLOYMENT_TARGET
:
210 conf
.env
['MACOSX_DEPLOYMENT_TARGET'] = python_MACOSX_DEPLOYMENT_TARGET
211 conf
.environ
['MACOSX_DEPLOYMENT_TARGET'] = python_MACOSX_DEPLOYMENT_TARGET
213 env
['pyext_PATTERN'] = '%s'+python_SO
215 # Check for python libraries for embedding
216 if python_SYSLIBS
is not None:
217 for lib
in python_SYSLIBS
.split():
218 if lib
.startswith('-l'):
219 lib
= lib
[2:] # strip '-l'
220 env
.append_value('LIB_PYEMBED', lib
)
222 if python_SHLIBS
is not None:
223 for lib
in python_SHLIBS
.split():
224 if lib
.startswith('-l'):
225 env
.append_value('LIB_PYEMBED', lib
[2:]) # strip '-l'
227 env
.append_value('LINKFLAGS_PYEMBED', lib
)
229 if Options
.platform
!= 'darwin' and python_LDFLAGS
:
230 parse_flags(python_LDFLAGS
, 'PYEMBED', env
)
233 name
= 'python' + env
['PYTHON_VERSION']
235 if python_LIBDIR
is not None:
236 path
= [python_LIBDIR
]
237 conf
.log
.write("\n\n# Trying LIBDIR: %r\n" % path
)
238 result
= conf
.check(lib
=name
, uselib
='PYEMBED', libpath
=path
)
240 if not result
and python_LIBPL
is not None:
241 conf
.log
.write("\n\n# try again with -L$python_LIBPL (some systems don't install the python library in $prefix/lib)\n")
242 path
= [python_LIBPL
]
243 result
= conf
.check(lib
=name
, uselib
='PYEMBED', libpath
=path
)
246 conf
.log
.write("\n\n# try again with -L$prefix/libs, and pythonXY name rather than pythonX.Y (win32)\n")
247 path
= [os
.path
.join(python_prefix
, "libs")]
248 name
= 'python' + env
['PYTHON_VERSION'].replace('.', '')
249 result
= conf
.check(lib
=name
, uselib
='PYEMBED', libpath
=path
)
252 env
['LIBPATH_PYEMBED'] = path
253 env
.append_value('LIB_PYEMBED', name
)
255 conf
.log
.write("\n\n### LIB NOT FOUND\n")
257 # under certain conditions, python extensions must link to
258 # python libraries, not just python embedding programs.
259 if (sys
.platform
== 'win32' or sys
.platform
.startswith('os2')
260 or sys
.platform
== 'darwin' or Py_ENABLE_SHARED
):
261 env
['LIBPATH_PYEXT'] = env
['LIBPATH_PYEMBED']
262 env
['LIB_PYEXT'] = env
['LIB_PYEMBED']
264 # We check that pythonX.Y-config exists, and if it exists we
265 # use it to get only the includes, else fall back to distutils.
266 python_config
= conf
.find_program(
267 'python%s-config' % ('.'.join(env
['PYTHON_VERSION'].split('.')[:2])),
269 if not python_config
:
270 python_config
= conf
.find_program(
271 'python-config-%s' % ('.'.join(env
['PYTHON_VERSION'].split('.')[:2])),
276 for incstr
in Utils
.cmd_output("%s --includes" % (python_config
,)).strip().split():
278 if (incstr
.startswith('-I')
279 or incstr
.startswith('/I')):
281 # append include path, unless already given
282 if incstr
not in includes
:
283 includes
.append(incstr
)
284 conf
.log
.write("Include path for Python extensions "
285 "(found via python-config --includes): %r\n" % (includes
,))
286 env
['CPPPATH_PYEXT'] = includes
287 env
['CPPPATH_PYEMBED'] = includes
289 conf
.log
.write("Include path for Python extensions "
290 "(found via distutils module): %r\n" % (INCLUDEPY
,))
291 env
['CPPPATH_PYEXT'] = [INCLUDEPY
]
292 env
['CPPPATH_PYEMBED'] = [INCLUDEPY
]
294 # Code using the Python API needs to be compiled with -fno-strict-aliasing
295 if env
['CC_NAME'] == 'gcc':
296 env
.append_value('CCFLAGS_PYEMBED', '-fno-strict-aliasing')
297 env
.append_value('CCFLAGS_PYEXT', '-fno-strict-aliasing')
298 if env
['CXX_NAME'] == 'gcc':
299 env
.append_value('CXXFLAGS_PYEMBED', '-fno-strict-aliasing')
300 env
.append_value('CXXFLAGS_PYEXT', '-fno-strict-aliasing')
303 conf
.check(define_name
='HAVE_PYTHON_H',
304 uselib
='PYEMBED', fragment
=FRAG_2
,
305 errmsg
='Could not find the python development headers', mandatory
=mandatory
)
308 def check_python_version(conf
, minver
=None):
310 Check if the python interpreter is found matching a given minimum version.
311 minver should be a tuple, eg. to check for python >= 2.4.2 pass (2,4,2) as minver.
313 If successful, PYTHON_VERSION is defined as 'MAJOR.MINOR'
314 (eg. '2.4') of the actual python version found, and PYTHONDIR is
315 defined, pointing to the site-packages directory appropriate for
316 this python version, where modules/packages/extensions should be
319 assert minver
is None or isinstance(minver
, tuple)
320 python
= conf
.env
['PYTHON']
322 conf
.fatal('could not find the python executable')
324 # Get python version string
325 cmd
= [python
, "-c", "import sys\nfor x in sys.version_info: print(str(x))"]
326 debug('python: Running python command %r' % cmd
)
327 proc
= Utils
.pproc
.Popen(cmd
, stdout
=Utils
.pproc
.PIPE
, shell
=False)
328 lines
= proc
.communicate()[0].split()
329 assert len(lines
) == 5, "found %i lines, expected 5: %r" % (len(lines
), lines
)
330 pyver_tuple
= (int(lines
[0]), int(lines
[1]), int(lines
[2]), lines
[3], int(lines
[4]))
332 # compare python version with the minimum required
333 result
= (minver
is None) or (pyver_tuple
>= minver
)
336 # define useful environment variables
337 pyver
= '.'.join([str(x
) for x
in pyver_tuple
[:2]])
338 conf
.env
['PYTHON_VERSION'] = pyver
340 if 'PYTHONDIR' in conf
.environ
:
341 pydir
= conf
.environ
['PYTHONDIR']
343 if sys
.platform
== 'win32':
344 (python_LIBDEST
, pydir
) = \
345 _get_python_variables(python
,
346 ["get_config_var('LIBDEST') or ''",
347 "get_python_lib(standard_lib=0, prefix=%r) or ''" % conf
.env
['PREFIX']],
348 ['from distutils.sysconfig import get_config_var, get_python_lib'])
350 python_LIBDEST
= None
352 _get_python_variables(python
,
353 ["get_python_lib(standard_lib=0, prefix=%r) or ''" % conf
.env
['PREFIX']],
354 ['from distutils.sysconfig import get_config_var, get_python_lib'])
355 if python_LIBDEST
is None:
356 if conf
.env
['LIBDIR']:
357 python_LIBDEST
= os
.path
.join(conf
.env
['LIBDIR'], "python" + pyver
)
359 python_LIBDEST
= os
.path
.join(conf
.env
['PREFIX'], "lib", "python" + pyver
)
361 if 'PYTHONARCHDIR' in conf
.environ
:
362 pyarchdir
= conf
.environ
['PYTHONARCHDIR']
364 (pyarchdir
,) = _get_python_variables(python
,
365 ["get_python_lib(plat_specific=1, standard_lib=0, prefix=%r) or ''" % conf
.env
['PREFIX']],
366 ['from distutils.sysconfig import get_config_var, get_python_lib'])
370 if hasattr(conf
, 'define'): # conf.define is added by the C tool, so may not exist
371 conf
.define('PYTHONDIR', pydir
)
372 conf
.define('PYTHONARCHDIR', pyarchdir
)
374 conf
.env
['PYTHONDIR'] = pydir
377 pyver_full
= '.'.join(map(str, pyver_tuple
[:3]))
379 conf
.check_message_custom('Python version', '', pyver_full
)
381 minver_str
= '.'.join(map(str, minver
))
382 conf
.check_message('Python version', ">= %s" % minver_str
, result
, option
=pyver_full
)
385 conf
.fatal('The python version is too old (%r)' % pyver_full
)
388 def check_python_module(conf
, module_name
):
390 Check if the selected python interpreter can import the given python module.
392 result
= not Utils
.pproc
.Popen([conf
.env
['PYTHON'], "-c", "import %s" % module_name
],
393 stderr
=Utils
.pproc
.PIPE
, stdout
=Utils
.pproc
.PIPE
).wait()
394 conf
.check_message('Python module', module_name
, result
)
396 conf
.fatal('Could not find the python module %r' % module_name
)
400 if not conf
.env
.PYTHON
:
401 conf
.env
.PYTHON
= sys
.executable
403 python
= conf
.find_program('python', var
='PYTHON')
405 conf
.fatal('Could not find the path of the python executable')
407 if conf
.env
.PYTHON
!= sys
.executable
:
408 warn("python executable '%s' different from sys.executable '%s'" % (conf
.env
.PYTHON
, sys
.executable
))
411 v
['PYCMD'] = '"import sys, py_compile;py_compile.compile(sys.argv[1], sys.argv[2])"'
413 v
['PYFLAGS_OPT'] = '-O'
415 v
['PYC'] = getattr(Options
.options
, 'pyc', 1)
416 v
['PYO'] = getattr(Options
.options
, 'pyo', 1)
418 def set_options(opt
):
419 opt
.add_option('--nopyc',
420 action
='store_false',
422 help = 'Do not install bytecode compiled .pyc files (configuration) [Default:install]',
424 opt
.add_option('--nopyo',
425 action
='store_false',
427 help='Do not install optimised compiled .pyo files (configuration) [Default:install]',