#! /usr/bin/env python # encoding: utf-8 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file import os,sys from waflib import Errors,Logs,Node,Options,Task,Utils from waflib.TaskGen import extension,before_method,after_method,feature from waflib.Configure import conf FRAG=''' #include #ifdef __cplusplus extern "C" { #endif void Py_Initialize(void); void Py_Finalize(void); #ifdef __cplusplus } #endif int main(int argc, char **argv) { (void)argc; (void)argv; Py_Initialize(); Py_Finalize(); return 0; } ''' INST=''' import sys, py_compile py_compile.compile(sys.argv[1], sys.argv[2], sys.argv[3], True) ''' DISTUTILS_IMP=['from distutils.sysconfig import get_config_var, get_python_lib'] @before_method('process_source') @feature('py') def feature_py(self): self.install_path=getattr(self,'install_path','${PYTHONDIR}') install_from=getattr(self,'install_from',None) if install_from and not isinstance(install_from,Node.Node): install_from=self.path.find_dir(install_from) self.install_from=install_from ver=self.env.PYTHON_VERSION if not ver: self.bld.fatal('Installing python files requires PYTHON_VERSION, try conf.check_python_version') if int(ver.replace('.',''))>31: self.install_32=True @extension('.py') def process_py(self,node): assert(hasattr(self,'install_path')),'add features="py"' if self.install_path: if self.install_from: self.add_install_files(install_to=self.install_path,install_from=node,cwd=self.install_from,relative_trick=True) else: self.add_install_files(install_to=self.install_path,install_from=node,relative_trick=True) lst=[] if self.env.PYC: lst.append('pyc') if self.env.PYO: lst.append('pyo') if self.install_path: if self.install_from: pyd=Utils.subst_vars("%s/%s"%(self.install_path,node.path_from(self.install_from)),self.env) else: pyd=Utils.subst_vars("%s/%s"%(self.install_path,node.path_from(self.path)),self.env) else: pyd=node.abspath() for ext in lst: if self.env.PYTAG and not self.env.NOPYCACHE: name=node.name[:-3] pyobj=node.parent.get_bld().make_node('__pycache__').make_node("%s.%s.%s"%(name,self.env.PYTAG,ext)) pyobj.parent.mkdir() else: pyobj=node.change_ext(".%s"%ext) tsk=self.create_task(ext,node,pyobj) tsk.pyd=pyd if self.install_path: self.add_install_files(install_to=os.path.dirname(pyd),install_from=pyobj,cwd=node.parent.get_bld(),relative_trick=True) class pyc(Task.Task): color='PINK' def __str__(self): node=self.outputs[0] return node.path_from(node.ctx.launch_node()) def run(self): cmd=[Utils.subst_vars('${PYTHON}',self.env),'-c',INST,self.inputs[0].abspath(),self.outputs[0].abspath(),self.pyd] ret=self.generator.bld.exec_command(cmd) return ret class pyo(Task.Task): color='PINK' def __str__(self): node=self.outputs[0] return node.path_from(node.ctx.launch_node()) def run(self): cmd=[Utils.subst_vars('${PYTHON}',self.env),Utils.subst_vars('${PYFLAGS_OPT}',self.env),'-c',INST,self.inputs[0].abspath(),self.outputs[0].abspath(),self.pyd] ret=self.generator.bld.exec_command(cmd) return ret @feature('pyext') @before_method('propagate_uselib_vars','apply_link') @after_method('apply_bundle') def init_pyext(self): self.uselib=self.to_list(getattr(self,'uselib',[])) if not'PYEXT'in self.uselib: self.uselib.append('PYEXT') self.env.cshlib_PATTERN=self.env.cxxshlib_PATTERN=self.env.macbundle_PATTERN=self.env.pyext_PATTERN self.env.fcshlib_PATTERN=self.env.dshlib_PATTERN=self.env.pyext_PATTERN try: if not self.install_path: return except AttributeError: self.install_path='${PYTHONARCHDIR}' @feature('pyext') @before_method('apply_link','apply_bundle') def set_bundle(self): if Utils.unversioned_sys_platform()=='darwin': self.mac_bundle=True @before_method('propagate_uselib_vars') @feature('pyembed') def init_pyembed(self): self.uselib=self.to_list(getattr(self,'uselib',[])) if not'PYEMBED'in self.uselib: self.uselib.append('PYEMBED') @conf def get_python_variables(self,variables,imports=None): if not imports: try: imports=self.python_imports except AttributeError: imports=DISTUTILS_IMP 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 try: out=self.cmd_and_log(self.env.PYTHON+['-c','\n'.join(program)],env=os_env) except Errors.WafError: self.fatal('The distutils module is unusable: install "python-devel"?') self.to_log(out) return_values=[] for s in out.splitlines(): s=s.strip() if not s: continue if s=='None': return_values.append(None) elif(s[0]=="'"and s[-1]=="'")or(s[0]=='"'and s[-1]=='"'): return_values.append(eval(s)) elif s[0].isdigit(): return_values.append(int(s)) else:break return return_values @conf def test_pyembed(self,mode,msg='Testing pyembed configuration'): self.check(header_name='Python.h',define_name='HAVE_PYEMBED',msg=msg,fragment=FRAG,errmsg='Could not build a python embedded interpreter',features='%s %sprogram pyembed'%(mode,mode)) @conf def test_pyext(self,mode,msg='Testing pyext configuration'): self.check(header_name='Python.h',define_name='HAVE_PYEXT',msg=msg,fragment=FRAG,errmsg='Could not build python extensions',features='%s %sshlib pyext'%(mode,mode)) @conf def python_cross_compile(self,features='pyembed pyext'): features=Utils.to_list(features) if not('PYTHON_LDFLAGS'in self.environ or'PYTHON_PYEXT_LDFLAGS'in self.environ or'PYTHON_PYEMBED_LDFLAGS'in self.environ): return False for x in'PYTHON_VERSION PYTAG pyext_PATTERN'.split(): if not x in self.environ: self.fatal('Please set %s in the os environment'%x) else: self.env[x]=self.environ[x] xx=self.env.CXX_NAME and'cxx'or'c' if'pyext'in features: flags=self.environ.get('PYTHON_PYEXT_LDFLAGS',self.environ.get('PYTHON_LDFLAGS')) if flags is None: self.fatal('No flags provided through PYTHON_PYEXT_LDFLAGS as required') else: self.parse_flags(flags,'PYEXT') self.test_pyext(xx) if'pyembed'in features: flags=self.environ.get('PYTHON_PYEMBED_LDFLAGS',self.environ.get('PYTHON_LDFLAGS')) if flags is None: self.fatal('No flags provided through PYTHON_PYEMBED_LDFLAGS as required') else: self.parse_flags(flags,'PYEMBED') self.test_pyembed(xx) return True @conf def check_python_headers(conf,features='pyembed pyext'): features=Utils.to_list(features) assert('pyembed'in features)or('pyext'in features),"check_python_headers features must include 'pyembed' and/or 'pyext'" env=conf.env if not env.CC_NAME and not env.CXX_NAME: conf.fatal('load a compiler first (gcc, g++, ..)') if conf.python_cross_compile(features): return if not env.PYTHON_VERSION: conf.check_python_version() pybin=env.PYTHON if not pybin: conf.fatal('Could not find the python executable') v='prefix SO LDFLAGS LIBDIR LIBPL INCLUDEPY Py_ENABLE_SHARED MACOSX_DEPLOYMENT_TARGET LDSHARED CFLAGS LDVERSION'.split() try: lst=conf.get_python_variables(["get_config_var('%s') or ''"%x for x in v]) except RuntimeError: conf.fatal("Python development headers not found (-v for details).") vals=['%s = %r'%(x,y)for(x,y)in zip(v,lst)] conf.to_log("Configuration returned from %r:\n%s\n"%(pybin,'\n'.join(vals))) dct=dict(zip(v,lst)) x='MACOSX_DEPLOYMENT_TARGET' if dct[x]: env[x]=conf.environ[x]=dct[x] env.pyext_PATTERN='%s'+dct['SO'] num='.'.join(env.PYTHON_VERSION.split('.')[:2]) conf.find_program([''.join(pybin)+'-config','python%s-config'%num,'python-config-%s'%num,'python%sm-config'%num],var='PYTHON_CONFIG',msg="python-config",mandatory=False) if env.PYTHON_CONFIG: all_flags=[['--cflags','--libs','--ldflags']] if sys.hexversion<0x2070000: all_flags=[[k]for k in all_flags[0]] xx=env.CXX_NAME and'cxx'or'c' if'pyembed'in features: for flags in all_flags: conf.check_cfg(msg='Asking python-config for pyembed %r flags'%' '.join(flags),path=env.PYTHON_CONFIG,package='',uselib_store='PYEMBED',args=flags) try: conf.test_pyembed(xx) except conf.errors.ConfigurationError: if dct['Py_ENABLE_SHARED']and dct['LIBDIR']: env.append_unique('LIBPATH_PYEMBED',[dct['LIBDIR']]) conf.test_pyembed(xx) else: raise if'pyext'in features: for flags in all_flags: conf.check_cfg(msg='Asking python-config for pyext %r flags'%' '.join(flags),path=env.PYTHON_CONFIG,package='',uselib_store='PYEXT',args=flags) try: conf.test_pyext(xx) except conf.errors.ConfigurationError: if dct['Py_ENABLE_SHARED']and dct['LIBDIR']: env.append_unique('LIBPATH_PYEXT',[dct['LIBDIR']]) conf.test_pyext(xx) else: raise conf.define('HAVE_PYTHON_H',1) return all_flags=dct['LDFLAGS']+' '+dct['CFLAGS'] conf.parse_flags(all_flags,'PYEMBED') all_flags=dct['LDFLAGS']+' '+dct['LDSHARED']+' '+dct['CFLAGS'] conf.parse_flags(all_flags,'PYEXT') result=None if not dct["LDVERSION"]: dct["LDVERSION"]=env.PYTHON_VERSION for name in('python'+dct['LDVERSION'],'python'+env.PYTHON_VERSION+'m','python'+env.PYTHON_VERSION.replace('.','')): if not result and env.LIBPATH_PYEMBED: path=env.LIBPATH_PYEMBED conf.to_log("\n\n# Trying default LIBPATH_PYEMBED: %r\n"%path) result=conf.check(lib=name,uselib='PYEMBED',libpath=path,mandatory=False,msg='Checking for library %s in LIBPATH_PYEMBED'%name) if not result and dct['LIBDIR']: path=[dct['LIBDIR']] conf.to_log("\n\n# try again with -L$python_LIBDIR: %r\n"%path) result=conf.check(lib=name,uselib='PYEMBED',libpath=path,mandatory=False,msg='Checking for library %s in LIBDIR'%name) if not result and dct['LIBPL']: path=[dct['LIBPL']] conf.to_log("\n\n# try again with -L$python_LIBPL (some systems don't install the python library in $prefix/lib)\n") result=conf.check(lib=name,uselib='PYEMBED',libpath=path,mandatory=False,msg='Checking for library %s in python_LIBPL'%name) if not result: path=[os.path.join(dct['prefix'],"libs")] conf.to_log("\n\n# try again with -L$prefix/libs, and pythonXY name rather than pythonX.Y (win32)\n") result=conf.check(lib=name,uselib='PYEMBED',libpath=path,mandatory=False,msg='Checking for library %s in $prefix/libs'%name) if result: break if result: env.LIBPATH_PYEMBED=path env.append_value('LIB_PYEMBED',[name]) else: conf.to_log("\n\n### LIB NOT FOUND\n") if Utils.is_win32 or dct['Py_ENABLE_SHARED']: env.LIBPATH_PYEXT=env.LIBPATH_PYEMBED env.LIB_PYEXT=env.LIB_PYEMBED conf.to_log("Include path for Python extensions (found via distutils module): %r\n"%(dct['INCLUDEPY'],)) env.INCLUDES_PYEXT=[dct['INCLUDEPY']] env.INCLUDES_PYEMBED=[dct['INCLUDEPY']] if env.CC_NAME=='gcc': env.append_value('CFLAGS_PYEMBED',['-fno-strict-aliasing']) env.append_value('CFLAGS_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']) if env.CC_NAME=="msvc": from distutils.msvccompiler import MSVCCompiler dist_compiler=MSVCCompiler() dist_compiler.initialize() env.append_value('CFLAGS_PYEXT',dist_compiler.compile_options) env.append_value('CXXFLAGS_PYEXT',dist_compiler.compile_options) env.append_value('LINKFLAGS_PYEXT',dist_compiler.ldflags_shared) conf.check(header_name='Python.h',define_name='HAVE_PYTHON_H',uselib='PYEMBED',fragment=FRAG,errmsg='Distutils not installed? Broken python installation? Get python-config now!') @conf def check_python_version(conf,minver=None): assert minver is None or isinstance(minver,tuple) pybin=conf.env.PYTHON if not pybin: conf.fatal('could not find the python executable') cmd=pybin+['-c','import sys\nfor x in sys.version_info: print(str(x))'] Logs.debug('python: Running python command %r',cmd) lines=conf.cmd_and_log(cmd).split() assert len(lines)==5,"found %r 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.env: pydir=conf.env.PYTHONDIR elif'PYTHONDIR'in conf.environ: pydir=conf.environ['PYTHONDIR'] else: if Utils.is_win32: (python_LIBDEST,pydir)=conf.get_python_variables(["get_config_var('LIBDEST') or ''","get_python_lib(standard_lib=0) or ''"]) else: python_LIBDEST=None (pydir,)=conf.get_python_variables(["get_python_lib(standard_lib=0, prefix=%r) or ''"%conf.env.PREFIX]) 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'PYTHONARCHDIR'in conf.env: pyarchdir=conf.env.PYTHONARCHDIR elif'PYTHONARCHDIR'in conf.environ: pyarchdir=conf.environ['PYTHONARCHDIR'] else: (pyarchdir,)=conf.get_python_variables(["get_python_lib(plat_specific=1, standard_lib=0, prefix=%r) or ''"%conf.env.PREFIX]) if not pyarchdir: pyarchdir=pydir if hasattr(conf,'define'): conf.define('PYTHONDIR',pydir) conf.define('PYTHONARCHDIR',pyarchdir) conf.env.PYTHONDIR=pydir conf.env.PYTHONARCHDIR=pyarchdir pyver_full='.'.join(map(str,pyver_tuple[:3])) if minver is None: conf.msg('Checking for python version',pyver_full) else: minver_str='.'.join(map(str,minver)) conf.msg('Checking for python version >= %s'%(minver_str,),pyver_full,color=result and'GREEN'or'YELLOW') if not result: conf.fatal('The python version is too old, expecting %r'%(minver,)) PYTHON_MODULE_TEMPLATE=''' import %s as current_module version = getattr(current_module, '__version__', None) if version is not None: print(str(version)) else: print('unknown version') ''' @conf def check_python_module(conf,module_name,condition=''): msg="Checking for python module %r"%module_name if condition: msg='%s (%s)'%(msg,condition) conf.start_msg(msg) try: ret=conf.cmd_and_log(conf.env.PYTHON+['-c',PYTHON_MODULE_TEMPLATE%module_name]) except Errors.WafError: conf.end_msg(False) conf.fatal('Could not find the python module %r'%module_name) ret=ret.strip() if condition: conf.end_msg(ret) if ret=='unknown version': conf.fatal('Could not check the %s version'%module_name) from distutils.version import LooseVersion def num(*k): if isinstance(k[0],int): return LooseVersion('.'.join([str(x)for x in k])) else: return LooseVersion(k[0]) d={'num':num,'ver':LooseVersion(ret)} ev=eval(condition,{},d) if not ev: conf.fatal('The %s version does not satisfy the requirements'%module_name) else: if ret=='unknown version': conf.end_msg(True) else: conf.end_msg(ret) def configure(conf): v=conf.env if getattr(Options.options,'pythondir',None): v.PYTHONDIR=Options.options.pythondir if getattr(Options.options,'pythonarchdir',None): v.PYTHONARCHDIR=Options.options.pythonarchdir if getattr(Options.options,'nopycache',None): v.NOPYCACHE=Options.options.nopycache if not v.PYTHON: v.PYTHON=[getattr(Options.options,'python',None)or sys.executable] v.PYTHON=Utils.to_list(v.PYTHON) conf.find_program('python',var='PYTHON') v.PYFLAGS='' v.PYFLAGS_OPT='-O' v.PYC=getattr(Options.options,'pyc',1) v.PYO=getattr(Options.options,'pyo',1) try: v.PYTAG=conf.cmd_and_log(conf.env.PYTHON+['-c',"import imp;print(imp.get_tag())"]).strip() except Errors.WafError: pass def options(opt): pyopt=opt.add_option_group("Python Options") pyopt.add_option('--nopyc',dest='pyc',action='store_false',default=1,help='Do not install bytecode compiled .pyc files (configuration) [Default:install]') pyopt.add_option('--nopyo',dest='pyo',action='store_false',default=1,help='Do not install optimised compiled .pyo files (configuration) [Default:install]') pyopt.add_option('--nopycache',dest='nopycache',action='store_true',help='Do not use __pycache__ directory to install objects [Default:auto]') pyopt.add_option('--python',dest="python",help='python binary to be used [Default: %s]'%sys.executable) pyopt.add_option('--pythondir',dest='pythondir',help='Installation path for python modules (py, platform-independent .py and .pyc files)') pyopt.add_option('--pythonarchdir',dest='pythonarchdir',help='Installation path for python extension (pyext, platform-dependent .so or .dylib files)')