278 lines
11 KiB
Python
278 lines
11 KiB
Python
#! /usr/bin/env python
|
|
# encoding: utf-8
|
|
|
|
import os,sys
|
|
import TaskGen,Utils,Utils,Runner,Options,Build
|
|
from Logs import debug,warn,info
|
|
from TaskGen import extension,taskgen,before,after,feature
|
|
from Configure import conf
|
|
EXT_PY=['.py']
|
|
FRAG_2='''
|
|
#include "Python.h"
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
void Py_Initialize(void);
|
|
void Py_Finalize(void);
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
int main()
|
|
{
|
|
Py_Initialize();
|
|
Py_Finalize();
|
|
return 0;
|
|
}
|
|
'''
|
|
def init_pyext(self):
|
|
self.default_install_path='${PYTHONDIR}'
|
|
self.uselib=self.to_list(getattr(self,'uselib',''))
|
|
if not'PYEXT'in self.uselib:
|
|
self.uselib.append('PYEXT')
|
|
self.env['MACBUNDLE']=True
|
|
def pyext_shlib_ext(self):
|
|
self.env['shlib_PATTERN']=self.env['pyext_PATTERN']
|
|
def init_pyembed(self):
|
|
self.uselib=self.to_list(getattr(self,'uselib',''))
|
|
if not'PYEMBED'in self.uselib:
|
|
self.uselib.append('PYEMBED')
|
|
def process_py(self,node):
|
|
if not(self.bld.is_install and self.install_path):
|
|
return
|
|
def inst_py(ctx):
|
|
install_pyfile(self,node)
|
|
self.bld.add_post_fun(inst_py)
|
|
def install_pyfile(self,node):
|
|
path=self.bld.get_install_path(self.install_path+os.sep+node.name,self.env)
|
|
self.bld.install_files(self.install_path,[node],self.env,self.chmod,postpone=False)
|
|
if self.bld.is_install<0:
|
|
info("* removing byte compiled python files")
|
|
for x in'co':
|
|
try:
|
|
os.remove(path+x)
|
|
except OSError:
|
|
pass
|
|
if self.bld.is_install>0:
|
|
if self.env['PYC']or self.env['PYO']:
|
|
info("* byte compiling %r"%path)
|
|
if self.env['PYC']:
|
|
program=("""
|
|
import sys, py_compile
|
|
for pyfile in sys.argv[1:]:
|
|
py_compile.compile(pyfile, pyfile + 'c')
|
|
""")
|
|
argv=[self.env['PYTHON'],'-c',program,path]
|
|
ret=Utils.pproc.Popen(argv).wait()
|
|
if ret:
|
|
raise Utils.WafError('bytecode compilation failed %r'%path)
|
|
if self.env['PYO']:
|
|
program=("""
|
|
import sys, py_compile
|
|
for pyfile in sys.argv[1:]:
|
|
py_compile.compile(pyfile, pyfile + 'o')
|
|
""")
|
|
argv=[self.env['PYTHON'],self.env['PYFLAGS_OPT'],'-c',program,path]
|
|
ret=Utils.pproc.Popen(argv).wait()
|
|
if ret:
|
|
raise Utils.WafError('bytecode compilation failed %r'%path)
|
|
class py_taskgen(TaskGen.task_gen):
|
|
def __init__(self,*k,**kw):
|
|
TaskGen.task_gen.__init__(self,*k,**kw)
|
|
def init_py(self):
|
|
self.default_install_path='${PYTHONDIR}'
|
|
def _get_python_variables(python_exe,variables,imports=['import sys']):
|
|
program=list(imports)
|
|
program.append('')
|
|
for v in variables:
|
|
program.append("print(repr(%s))"%v)
|
|
os_env=dict(os.environ)
|
|
try:
|
|
del os_env['MACOSX_DEPLOYMENT_TARGET']
|
|
except KeyError:
|
|
pass
|
|
proc=Utils.pproc.Popen([python_exe,"-c",'\n'.join(program)],stdout=Utils.pproc.PIPE,env=os_env)
|
|
output=proc.communicate()[0].split("\n")
|
|
if proc.returncode:
|
|
if Options.options.verbose:
|
|
warn("Python program to extract python configuration variables failed:\n%s"%'\n'.join(["line %03i: %s"%(lineno+1,line)for lineno,line in enumerate(program)]))
|
|
raise RuntimeError
|
|
return_values=[]
|
|
for s in output:
|
|
s=s.strip()
|
|
if not s:
|
|
continue
|
|
if s=='None':
|
|
return_values.append(None)
|
|
elif s[0]=="'"and s[-1]=="'":
|
|
return_values.append(s[1:-1])
|
|
elif s[0].isdigit():
|
|
return_values.append(int(s))
|
|
else:break
|
|
return return_values
|
|
def check_python_headers(conf,mandatory=True):
|
|
if not conf.env['CC_NAME']and not conf.env['CXX_NAME']:
|
|
conf.fatal('load a compiler first (gcc, g++, ..)')
|
|
if not conf.env['PYTHON_VERSION']:
|
|
conf.check_python_version()
|
|
env=conf.env
|
|
python=env['PYTHON']
|
|
if not python:
|
|
conf.fatal('could not find the python executable')
|
|
if Options.platform=='darwin':
|
|
conf.check_tool('osx')
|
|
try:
|
|
v='prefix SO SYSLIBS LDFLAGS SHLIBS LIBDIR LIBPL INCLUDEPY Py_ENABLE_SHARED MACOSX_DEPLOYMENT_TARGET'.split()
|
|
(python_prefix,python_SO,python_SYSLIBS,python_LDFLAGS,python_SHLIBS,python_LIBDIR,python_LIBPL,INCLUDEPY,Py_ENABLE_SHARED,python_MACOSX_DEPLOYMENT_TARGET)=_get_python_variables(python,["get_config_var('%s')"%x for x in v],['from distutils.sysconfig import get_config_var'])
|
|
except RuntimeError:
|
|
conf.fatal("Python development headers not found (-v for details).")
|
|
conf.log.write("""Configuration returned from %r:
|
|
python_prefix = %r
|
|
python_SO = %r
|
|
python_SYSLIBS = %r
|
|
python_LDFLAGS = %r
|
|
python_SHLIBS = %r
|
|
python_LIBDIR = %r
|
|
python_LIBPL = %r
|
|
INCLUDEPY = %r
|
|
Py_ENABLE_SHARED = %r
|
|
MACOSX_DEPLOYMENT_TARGET = %r
|
|
"""%(python,python_prefix,python_SO,python_SYSLIBS,python_LDFLAGS,python_SHLIBS,python_LIBDIR,python_LIBPL,INCLUDEPY,Py_ENABLE_SHARED,python_MACOSX_DEPLOYMENT_TARGET))
|
|
if python_MACOSX_DEPLOYMENT_TARGET:
|
|
conf.env['MACOSX_DEPLOYMENT_TARGET']=python_MACOSX_DEPLOYMENT_TARGET
|
|
conf.environ['MACOSX_DEPLOYMENT_TARGET']=python_MACOSX_DEPLOYMENT_TARGET
|
|
env['pyext_PATTERN']='%s'+python_SO
|
|
if python_SYSLIBS is not None:
|
|
for lib in python_SYSLIBS.split():
|
|
if lib.startswith('-l'):
|
|
lib=lib[2:]
|
|
env.append_value('LIB_PYEMBED',lib)
|
|
if python_SHLIBS is not None:
|
|
for lib in python_SHLIBS.split():
|
|
if lib.startswith('-l'):
|
|
env.append_value('LIB_PYEMBED',lib[2:])
|
|
else:
|
|
env.append_value('LINKFLAGS_PYEMBED',lib)
|
|
if Options.platform!='darwin'and python_LDFLAGS:
|
|
env.append_value('LINKFLAGS_PYEMBED',python_LDFLAGS.split())
|
|
result=False
|
|
name='python'+env['PYTHON_VERSION']
|
|
if python_LIBDIR is not None:
|
|
path=[python_LIBDIR]
|
|
conf.log.write("\n\n# Trying LIBDIR: %r\n"%path)
|
|
result=conf.check(lib=name,uselib='PYEMBED',libpath=path)
|
|
if not result and python_LIBPL is not None:
|
|
conf.log.write("\n\n# try again with -L$python_LIBPL (some systems don't install the python library in $prefix/lib)\n")
|
|
path=[python_LIBPL]
|
|
result=conf.check(lib=name,uselib='PYEMBED',libpath=path)
|
|
if not result:
|
|
conf.log.write("\n\n# try again with -L$prefix/libs, and pythonXY name rather than pythonX.Y (win32)\n")
|
|
path=[os.path.join(python_prefix,"libs")]
|
|
name='python'+env['PYTHON_VERSION'].replace('.','')
|
|
result=conf.check(lib=name,uselib='PYEMBED',libpath=path)
|
|
if result:
|
|
env['LIBPATH_PYEMBED']=path
|
|
env.append_value('LIB_PYEMBED',name)
|
|
else:
|
|
conf.log.write("\n\n### LIB NOT FOUND\n")
|
|
if(sys.platform=='win32'or sys.platform.startswith('os2')or sys.platform=='darwin'or Py_ENABLE_SHARED):
|
|
env['LIBPATH_PYEXT']=env['LIBPATH_PYEMBED']
|
|
env['LIB_PYEXT']=env['LIB_PYEMBED']
|
|
python_config=conf.find_program('python%s-config'%('.'.join(env['PYTHON_VERSION'].split('.')[:2])),var='PYTHON_CONFIG')
|
|
if not python_config:
|
|
python_config=conf.find_program('python-config-%s'%('.'.join(env['PYTHON_VERSION'].split('.')[:2])),var='PYTHON_CONFIG')
|
|
includes=[]
|
|
if python_config:
|
|
for incstr in Utils.cmd_output("%s %s --includes"%(python,python_config)).strip().split():
|
|
if(incstr.startswith('-I')or incstr.startswith('/I')):
|
|
incstr=incstr[2:]
|
|
if incstr not in includes:
|
|
includes.append(incstr)
|
|
conf.log.write("Include path for Python extensions ""(found via python-config --includes): %r\n"%(includes,))
|
|
env['CPPPATH_PYEXT']=includes
|
|
env['CPPPATH_PYEMBED']=includes
|
|
else:
|
|
conf.log.write("Include path for Python extensions ""(found via distutils module): %r\n"%(INCLUDEPY,))
|
|
env['CPPPATH_PYEXT']=[INCLUDEPY]
|
|
env['CPPPATH_PYEMBED']=[INCLUDEPY]
|
|
if env['CC_NAME']=='gcc':
|
|
env.append_value('CCFLAGS_PYEMBED','-fno-strict-aliasing')
|
|
env.append_value('CCFLAGS_PYEXT','-fno-strict-aliasing')
|
|
if env['CXX_NAME']=='gcc':
|
|
env.append_value('CXXFLAGS_PYEMBED','-fno-strict-aliasing')
|
|
env.append_value('CXXFLAGS_PYEXT','-fno-strict-aliasing')
|
|
conf.check(define_name='HAVE_PYTHON_H',uselib='PYEMBED',fragment=FRAG_2,errmsg='Could not find the python development headers',mandatory=mandatory)
|
|
def check_python_version(conf,minver=None):
|
|
assert minver is None or isinstance(minver,tuple)
|
|
python=conf.env['PYTHON']
|
|
if not python:
|
|
conf.fatal('could not find the python executable')
|
|
cmd=[python,"-c","import sys\nfor x in sys.version_info: print(str(x))"]
|
|
debug('python: Running python command %r'%cmd)
|
|
proc=Utils.pproc.Popen(cmd,stdout=Utils.pproc.PIPE)
|
|
lines=proc.communicate()[0].split()
|
|
assert len(lines)==5,"found %i lines, expected 5: %r"%(len(lines),lines)
|
|
pyver_tuple=(int(lines[0]),int(lines[1]),int(lines[2]),lines[3],int(lines[4]))
|
|
result=(minver is None)or(pyver_tuple>=minver)
|
|
if result:
|
|
pyver='.'.join([str(x)for x in pyver_tuple[:2]])
|
|
conf.env['PYTHON_VERSION']=pyver
|
|
if'PYTHONDIR'in conf.environ:
|
|
pydir=conf.environ['PYTHONDIR']
|
|
else:
|
|
if sys.platform=='win32':
|
|
(python_LIBDEST,pydir)=_get_python_variables(python,["get_config_var('LIBDEST')","get_python_lib(standard_lib=0, prefix=%r)"%conf.env['PREFIX']],['from distutils.sysconfig import get_config_var, get_python_lib'])
|
|
else:
|
|
python_LIBDEST=None
|
|
(pydir,)=_get_python_variables(python,["get_python_lib(standard_lib=0, prefix=%r)"%conf.env['PREFIX']],['from distutils.sysconfig import get_config_var, get_python_lib'])
|
|
if python_LIBDEST is None:
|
|
if conf.env['LIBDIR']:
|
|
python_LIBDEST=os.path.join(conf.env['LIBDIR'],"python"+pyver)
|
|
else:
|
|
python_LIBDEST=os.path.join(conf.env['PREFIX'],"lib","python"+pyver)
|
|
if hasattr(conf,'define'):
|
|
conf.define('PYTHONDIR',pydir)
|
|
conf.env['PYTHONDIR']=pydir
|
|
pyver_full='.'.join(map(str,pyver_tuple[:3]))
|
|
if minver is None:
|
|
conf.check_message_custom('Python version','',pyver_full)
|
|
else:
|
|
minver_str='.'.join(map(str,minver))
|
|
conf.check_message('Python version',">= %s"%minver_str,result,option=pyver_full)
|
|
if not result:
|
|
conf.fatal('The python version is too old (%r)'%pyver_full)
|
|
def check_python_module(conf,module_name):
|
|
result=not Utils.pproc.Popen([conf.env['PYTHON'],"-c","import %s"%module_name],stderr=Utils.pproc.PIPE,stdout=Utils.pproc.PIPE).wait()
|
|
conf.check_message('Python module',module_name,result)
|
|
if not result:
|
|
conf.fatal('Could not find the python module %r'%module_name)
|
|
def detect(conf):
|
|
if not conf.env.PYTHON:
|
|
conf.env.PYTHON=sys.executable
|
|
python=conf.find_program('python',var='PYTHON')
|
|
if not python:
|
|
conf.fatal('Could not find the path of the python executable')
|
|
v=conf.env
|
|
v['PYCMD']='"import sys, py_compile;py_compile.compile(sys.argv[1], sys.argv[2])"'
|
|
v['PYFLAGS']=''
|
|
v['PYFLAGS_OPT']='-O'
|
|
v['PYC']=getattr(Options.options,'pyc',1)
|
|
v['PYO']=getattr(Options.options,'pyo',1)
|
|
def set_options(opt):
|
|
opt.add_option('--nopyc',action='store_false',default=1,help='Do not install bytecode compiled .pyc files (configuration) [Default:install]',dest='pyc')
|
|
opt.add_option('--nopyo',action='store_false',default=1,help='Do not install optimised compiled .pyo files (configuration) [Default:install]',dest='pyo')
|
|
|
|
before('apply_incpaths','apply_lib_vars','apply_type_vars')(init_pyext)
|
|
feature('pyext')(init_pyext)
|
|
before('apply_bundle')(init_pyext)
|
|
before('apply_link','apply_lib_vars','apply_type_vars')(pyext_shlib_ext)
|
|
after('apply_bundle')(pyext_shlib_ext)
|
|
feature('pyext')(pyext_shlib_ext)
|
|
before('apply_incpaths','apply_lib_vars','apply_type_vars')(init_pyembed)
|
|
feature('pyembed')(init_pyembed)
|
|
extension(EXT_PY)(process_py)
|
|
before('apply_core')(init_py)
|
|
after('vars_target_cprogram','vars_target_cshlib')(init_py)
|
|
feature('py')(init_py)
|
|
conf(check_python_headers)
|
|
conf(check_python_version)
|
|
conf(check_python_module)
|