3 # Thomas Nagy, 2007 (ita)
4 # Gustavo Carneiro (gjc), 2007
9 import TaskGen
, Utils
, Utils
, Runner
, Options
, Build
10 from Logs
import debug
, warn
, info
11 from TaskGen
import extension
, taskgen
, before
, after
, feature
12 from Configure
import conf
20 void Py_Initialize(void);
21 void Py_Finalize(void);
34 @before('apply_incpaths', 'apply_lib_vars', 'apply_type_vars', 'apply_bundle')
35 @after('vars_target_cshlib')
37 self
.default_install_path
= '${PYTHONARCHDIR}'
38 self
.uselib
= self
.to_list(getattr(self
, 'uselib', ''))
39 if not 'PYEXT' in self
.uselib
:
40 self
.uselib
.append('PYEXT')
41 self
.env
['MACBUNDLE'] = True
43 @before('apply_link', 'apply_lib_vars', 'apply_type_vars')
44 @after('apply_bundle')
46 def pyext_shlib_ext(self
):
47 # override shlib_PATTERN set by the osx module
48 self
.env
['shlib_PATTERN'] = self
.env
['pyext_PATTERN']
50 @before('apply_incpaths', 'apply_lib_vars', 'apply_type_vars')
52 def init_pyembed(self
):
53 self
.uselib
= self
.to_list(getattr(self
, 'uselib', ''))
54 if not 'PYEMBED' in self
.uselib
:
55 self
.uselib
.append('PYEMBED')
58 def process_py(self
, node
):
59 if not (self
.bld
.is_install
and self
.install_path
):
62 install_pyfile(self
, node
)
63 self
.bld
.add_post_fun(inst_py
)
65 def install_pyfile(self
, node
):
66 path
= self
.bld
.get_install_path(self
.install_path
+ os
.sep
+ node
.name
, self
.env
)
68 self
.bld
.install_files(self
.install_path
, [node
], self
.env
, self
.chmod
, postpone
=False)
69 if self
.bld
.is_install
< 0:
70 info("* removing byte compiled python files")
77 if self
.bld
.is_install
> 0:
78 if self
.env
['PYC'] or self
.env
['PYO']:
79 info("* byte compiling %r" % path
)
83 import sys, py_compile
84 for pyfile in sys.argv[1:]:
85 py_compile.compile(pyfile, pyfile + 'c')
87 argv
= [self
.env
['PYTHON'], '-c', program
, path
]
88 ret
= Utils
.pproc
.Popen(argv
).wait()
90 raise Utils
.WafError('bytecode compilation failed %r' % path
)
94 import sys, py_compile
95 for pyfile in sys.argv[1:]:
96 py_compile.compile(pyfile, pyfile + 'o')
98 argv
= [self
.env
['PYTHON'], self
.env
['PYFLAGS_OPT'], '-c', program
, path
]
99 ret
= Utils
.pproc
.Popen(argv
).wait()
101 raise Utils
.WafError('bytecode compilation failed %r' % path
)
104 class py_taskgen(TaskGen
.task_gen
):
105 def __init__(self
, *k
, **kw
):
106 TaskGen
.task_gen
.__init
__(self
, *k
, **kw
)
108 @before('apply_core')
109 @after('vars_target_cprogram', 'vars_target_cshlib')
112 self
.default_install_path
= '${PYTHONDIR}'
114 def _get_python_variables(python_exe
, variables
, imports
=['import sys']):
115 """Run a python interpreter and print some variables"""
116 program
= list(imports
)
119 program
.append("print(repr(%s))" % v
)
120 os_env
= dict(os
.environ
)
122 del os_env
['MACOSX_DEPLOYMENT_TARGET'] # see comments in the OSX tool
125 proc
= Utils
.pproc
.Popen([python_exe
, "-c", '\n'.join(program
)], stdout
=Utils
.pproc
.PIPE
, env
=os_env
)
126 output
= proc
.communicate()[0].split("\n") # do not touch, python3
128 if Options
.options
.verbose
:
129 warn("Python program to extract python configuration variables failed:\n%s"
130 % '\n'.join(["line %03i: %s" % (lineno
+1, line
) for lineno
, line
in enumerate(program
)]))
138 return_values
.append(None)
139 elif s
[0] == "'" and s
[-1] == "'":
140 return_values
.append(s
[1:-1])
142 return_values
.append(int(s
))
147 def check_python_headers(conf
, mandatory
=True):
148 """Check for headers and libraries necessary to extend or embed python.
150 On success the environment variables xxx_PYEXT and xxx_PYEMBED are added for uselib
152 PYEXT: for compiling python extensions
153 PYEMBED: for embedding a python interpreter"""
155 if not conf
.env
['CC_NAME'] and not conf
.env
['CXX_NAME']:
156 conf
.fatal('load a compiler first (gcc, g++, ..)')
158 if not conf
.env
['PYTHON_VERSION']:
159 conf
.check_python_version()
162 python
= env
['PYTHON']
164 conf
.fatal('could not find the python executable')
166 ## On Mac OSX we need to use mac bundles for python plugins
167 if Options
.platform
== 'darwin':
168 conf
.check_tool('osx')
171 # Get some python configuration variables using distutils
172 v
= 'prefix SO SYSLIBS LDFLAGS SHLIBS LIBDIR LIBPL INCLUDEPY Py_ENABLE_SHARED MACOSX_DEPLOYMENT_TARGET'.split()
173 (python_prefix
, python_SO
, python_SYSLIBS
, python_LDFLAGS
, python_SHLIBS
,
174 python_LIBDIR
, python_LIBPL
, INCLUDEPY
, Py_ENABLE_SHARED
,
175 python_MACOSX_DEPLOYMENT_TARGET
) = \
176 _get_python_variables(python
, ["get_config_var('%s')" % x
for x
in v
],
177 ['from distutils.sysconfig import get_config_var'])
179 conf
.fatal("Python development headers not found (-v for details).")
181 conf
.log
.write("""Configuration returned from %r:
190 Py_ENABLE_SHARED = %r
191 MACOSX_DEPLOYMENT_TARGET = %r
192 """ % (python
, python_prefix
, python_SO
, python_SYSLIBS
, python_LDFLAGS
, python_SHLIBS
,
193 python_LIBDIR
, python_LIBPL
, INCLUDEPY
, Py_ENABLE_SHARED
, python_MACOSX_DEPLOYMENT_TARGET
))
195 if python_MACOSX_DEPLOYMENT_TARGET
:
196 conf
.env
['MACOSX_DEPLOYMENT_TARGET'] = python_MACOSX_DEPLOYMENT_TARGET
197 conf
.environ
['MACOSX_DEPLOYMENT_TARGET'] = python_MACOSX_DEPLOYMENT_TARGET
199 env
['pyext_PATTERN'] = '%s'+python_SO
201 # Check for python libraries for embedding
202 if python_SYSLIBS
is not None:
203 for lib
in python_SYSLIBS
.split():
204 if lib
.startswith('-l'):
205 lib
= lib
[2:] # strip '-l'
206 env
.append_value('LIB_PYEMBED', lib
)
208 if python_SHLIBS
is not None:
209 for lib
in python_SHLIBS
.split():
210 if lib
.startswith('-l'):
211 env
.append_value('LIB_PYEMBED', lib
[2:]) # strip '-l'
213 env
.append_value('LINKFLAGS_PYEMBED', lib
)
215 if Options
.platform
!= 'darwin' and python_LDFLAGS
:
216 env
.append_value('LINKFLAGS_PYEMBED', python_LDFLAGS
.split())
219 name
= 'python' + env
['PYTHON_VERSION']
221 if python_LIBDIR
is not None:
222 path
= [python_LIBDIR
]
223 conf
.log
.write("\n\n# Trying LIBDIR: %r\n" % path
)
224 result
= conf
.check(lib
=name
, uselib
='PYEMBED', libpath
=path
)
226 if not result
and python_LIBPL
is not None:
227 conf
.log
.write("\n\n# try again with -L$python_LIBPL (some systems don't install the python library in $prefix/lib)\n")
228 path
= [python_LIBPL
]
229 result
= conf
.check(lib
=name
, uselib
='PYEMBED', libpath
=path
)
232 conf
.log
.write("\n\n# try again with -L$prefix/libs, and pythonXY name rather than pythonX.Y (win32)\n")
233 path
= [os
.path
.join(python_prefix
, "libs")]
234 name
= 'python' + env
['PYTHON_VERSION'].replace('.', '')
235 result
= conf
.check(lib
=name
, uselib
='PYEMBED', libpath
=path
)
238 env
['LIBPATH_PYEMBED'] = path
239 env
.append_value('LIB_PYEMBED', name
)
241 conf
.log
.write("\n\n### LIB NOT FOUND\n")
243 # under certain conditions, python extensions must link to
244 # python libraries, not just python embedding programs.
245 if (sys
.platform
== 'win32' or sys
.platform
.startswith('os2')
246 or sys
.platform
== 'darwin' or Py_ENABLE_SHARED
):
247 env
['LIBPATH_PYEXT'] = env
['LIBPATH_PYEMBED']
248 env
['LIB_PYEXT'] = env
['LIB_PYEMBED']
250 # We check that pythonX.Y-config exists, and if it exists we
251 # use it to get only the includes, else fall back to distutils.
252 python_config
= conf
.find_program(
253 'python%s-config' % ('.'.join(env
['PYTHON_VERSION'].split('.')[:2])),
255 if not python_config
:
256 python_config
= conf
.find_program(
257 'python-config-%s' % ('.'.join(env
['PYTHON_VERSION'].split('.')[:2])),
262 for incstr
in Utils
.cmd_output("%s %s --includes" % (python
, python_config
)).strip().split():
264 if (incstr
.startswith('-I')
265 or incstr
.startswith('/I')):
267 # append include path, unless already given
268 if incstr
not in includes
:
269 includes
.append(incstr
)
270 conf
.log
.write("Include path for Python extensions "
271 "(found via python-config --includes): %r\n" % (includes
,))
272 env
['CPPPATH_PYEXT'] = includes
273 env
['CPPPATH_PYEMBED'] = includes
275 conf
.log
.write("Include path for Python extensions "
276 "(found via distutils module): %r\n" % (INCLUDEPY
,))
277 env
['CPPPATH_PYEXT'] = [INCLUDEPY
]
278 env
['CPPPATH_PYEMBED'] = [INCLUDEPY
]
280 # Code using the Python API needs to be compiled with -fno-strict-aliasing
281 if env
['CC_NAME'] == 'gcc':
282 env
.append_value('CCFLAGS_PYEMBED', '-fno-strict-aliasing')
283 env
.append_value('CCFLAGS_PYEXT', '-fno-strict-aliasing')
284 if env
['CXX_NAME'] == 'gcc':
285 env
.append_value('CXXFLAGS_PYEMBED', '-fno-strict-aliasing')
286 env
.append_value('CXXFLAGS_PYEXT', '-fno-strict-aliasing')
289 conf
.check(define_name
='HAVE_PYTHON_H',
290 uselib
='PYEMBED', fragment
=FRAG_2
,
291 errmsg
='Could not find the python development headers', mandatory
=mandatory
)
294 def check_python_version(conf
, minver
=None):
296 Check if the python interpreter is found matching a given minimum version.
297 minver should be a tuple, eg. to check for python >= 2.4.2 pass (2,4,2) as minver.
299 If successful, PYTHON_VERSION is defined as 'MAJOR.MINOR'
300 (eg. '2.4') of the actual python version found, and PYTHONDIR is
301 defined, pointing to the site-packages directory appropriate for
302 this python version, where modules/packages/extensions should be
305 assert minver
is None or isinstance(minver
, tuple)
306 python
= conf
.env
['PYTHON']
308 conf
.fatal('could not find the python executable')
310 # Get python version string
311 cmd
= [python
, "-c", "import sys\nfor x in sys.version_info: print(str(x))"]
312 debug('python: Running python command %r' % cmd
)
313 proc
= Utils
.pproc
.Popen(cmd
, stdout
=Utils
.pproc
.PIPE
)
314 lines
= proc
.communicate()[0].split()
315 assert len(lines
) == 5, "found %i lines, expected 5: %r" % (len(lines
), lines
)
316 pyver_tuple
= (int(lines
[0]), int(lines
[1]), int(lines
[2]), lines
[3], int(lines
[4]))
318 # compare python version with the minimum required
319 result
= (minver
is None) or (pyver_tuple
>= minver
)
322 # define useful environment variables
323 pyver
= '.'.join([str(x
) for x
in pyver_tuple
[:2]])
324 conf
.env
['PYTHON_VERSION'] = pyver
326 if 'PYTHONDIR' in conf
.environ
:
327 pydir
= conf
.environ
['PYTHONDIR']
329 if sys
.platform
== 'win32':
330 (python_LIBDEST
, pydir
) = \
331 _get_python_variables(python
,
332 ["get_config_var('LIBDEST')",
333 "get_python_lib(standard_lib=0, prefix=%r)" % conf
.env
['PREFIX']],
334 ['from distutils.sysconfig import get_config_var, get_python_lib'])
336 python_LIBDEST
= None
338 _get_python_variables(python
,
339 ["get_python_lib(standard_lib=0, prefix=%r)" % conf
.env
['PREFIX']],
340 ['from distutils.sysconfig import get_config_var, get_python_lib'])
341 if python_LIBDEST
is None:
342 if conf
.env
['LIBDIR']:
343 python_LIBDEST
= os
.path
.join(conf
.env
['LIBDIR'], "python" + pyver
)
345 python_LIBDEST
= os
.path
.join(conf
.env
['PREFIX'], "lib", "python" + pyver
)
347 if 'PYTHONARCHDIR' in conf
.environ
:
348 pyarchdir
= conf
.environ
['PYTHONARCHDIR']
350 (pyarchdir
,) = _get_python_variables(python
,
351 ["get_python_lib(plat_specific=1, standard_lib=0, prefix=%r)" % conf
.env
['PREFIX']],
352 ['from distutils.sysconfig import get_config_var, get_python_lib'])
356 if hasattr(conf
, 'define'): # conf.define is added by the C tool, so may not exist
357 conf
.define('PYTHONDIR', pydir
)
358 conf
.define('PYTHONARCHDIR', pyarchdir
)
360 conf
.env
['PYTHONDIR'] = pydir
363 pyver_full
= '.'.join(map(str, pyver_tuple
[:3]))
365 conf
.check_message_custom('Python version', '', pyver_full
)
367 minver_str
= '.'.join(map(str, minver
))
368 conf
.check_message('Python version', ">= %s" % minver_str
, result
, option
=pyver_full
)
371 conf
.fatal('The python version is too old (%r)' % pyver_full
)
374 def check_python_module(conf
, module_name
):
376 Check if the selected python interpreter can import the given python module.
378 result
= not Utils
.pproc
.Popen([conf
.env
['PYTHON'], "-c", "import %s" % module_name
],
379 stderr
=Utils
.pproc
.PIPE
, stdout
=Utils
.pproc
.PIPE
).wait()
380 conf
.check_message('Python module', module_name
, result
)
382 conf
.fatal('Could not find the python module %r' % module_name
)
386 if not conf
.env
.PYTHON
:
387 conf
.env
.PYTHON
= sys
.executable
389 python
= conf
.find_program('python', var
='PYTHON')
391 conf
.fatal('Could not find the path of the python executable')
395 v
['PYCMD'] = '"import sys, py_compile;py_compile.compile(sys.argv[1], sys.argv[2])"'
397 v
['PYFLAGS_OPT'] = '-O'
399 v
['PYC'] = getattr(Options
.options
, 'pyc', 1)
400 v
['PYO'] = getattr(Options
.options
, 'pyo', 1)
402 def set_options(opt
):
403 opt
.add_option('--nopyc',
404 action
='store_false',
406 help = 'Do not install bytecode compiled .pyc files (configuration) [Default:install]',
408 opt
.add_option('--nopyo',
409 action
='store_false',
411 help='Do not install optimised compiled .pyo files (configuration) [Default:install]',