midori/waf-modules/wafadmin/Tools/python.py
2013-10-23 20:26:27 -07:00

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)