Package wafadmin :: Package Tools :: Module python
[hide private]
[frames] | no frames]

Source Code for Module wafadmin.Tools.python

  1  #!/usr/bin/env python 
  2  # encoding: utf-8 
  3  # Thomas Nagy, 2007 (ita) 
  4  # Gustavo Carneiro (gjc), 2007 
  5   
  6  "Python support" 
  7   
  8  import os, sys 
  9  import TaskGen, Utils, Utils, Runner, Options, Build 
 10  from Logs import debug, warn 
 11  from TaskGen import extension, taskgen, before, after, feature 
 12  from Configure import conf 
 13  import pproc 
 14   
 15  EXT_PY = ['.py'] 
16 17 @taskgen 18 @before('apply_incpaths') 19 @feature('pyext') 20 @before('apply_bundle') 21 -def init_pyext(self):
22 self.default_install_path = '${PYTHONDIR}' 23 self.uselib = self.to_list(self.uselib) 24 if not 'PYEXT' in self.uselib: 25 self.uselib.append('PYEXT') 26 self.env['MACBUNDLE'] = True
27
28 @taskgen 29 @before('apply_link') 30 @before('apply_lib_vars') 31 @after('apply_bundle') 32 @feature('pyext') 33 -def pyext_shlib_ext(self):
34 # override shlib_PATTERN set by the osx module 35 self.env['shlib_PATTERN'] = self.env['pyext_PATTERN']
36
37 38 @taskgen 39 @before('apply_incpaths') 40 @feature('pyembed') 41 -def init_pyembed(self):
42 self.uselib = self.to_list(self.uselib) 43 if not 'PYEMBED' in self.uselib: 44 self.uselib.append('PYEMBED')
45
46 @extension(EXT_PY) 47 -def process_py(self, node):
48 pass
49
50 # FIXME in theory, we should absolutely avoid subclasses like this 51 -class py_taskgen(TaskGen.task_gen):
52 - def __init__(self, env=None):
53 TaskGen.task_gen.__init__(self) 54 55 self.default_install_path = '${PYTHONDIR}' 56 self.chmod = 0644
57
58 - def install(self):
59 files_to_install = [] 60 for filename in self.to_list(self.source): 61 node = self.path.find_resource(filename) 62 if node is not None: 63 files_to_install.append(node.abspath()) 64 else: 65 node = self.path.find_or_declare(filename) 66 if node is None: 67 raise Utils.WafError("Cannot install file %s: not found in %s" 68 % (filename, self.path)) 69 else: 70 files_to_install.append(node.abspath(self.env)) 71 72 installed_files = Build.bld.install_files(self.install_path, files_to_install, self.env, self.chmod) 73 74 if not installed_files: 75 return 76 77 if Options.commands['uninstall']: 78 print "* removing byte compiled python files" 79 for fname in installed_files: 80 try: 81 os.remove(fname + 'c') 82 except OSError: 83 pass 84 try: 85 os.remove(fname + 'o') 86 except OSError: 87 pass 88 else: 89 if self.env['PYC'] or self.env['PYO']: 90 print "* byte compiling python files" 91 92 if self.env['PYC']: 93 program = (""" 94 import sys, py_compile 95 for pyfile in sys.argv[1:]: 96 py_compile.compile(pyfile, pyfile + 'c') 97 """) 98 argv = [self.env['PYTHON'], "-c", program ] 99 argv.extend(installed_files) 100 retval = pproc.Popen(argv).wait() 101 if retval: 102 raise Utils.WafError("bytecode compilation failed") 103 104 105 if self.env['PYO']: 106 program = (""" 107 import sys, py_compile 108 for pyfile in sys.argv[1:]: 109 py_compile.compile(pyfile, pyfile + 'o') 110 """) 111 argv = [self.env['PYTHON'], self.env['PYFLAGS_OPT'], "-c", program ] 112 argv.extend(installed_files) 113 retval = pproc.Popen(argv).wait() 114 if retval: 115 raise Utils.WafError("bytecode compilation failed")
116
117 -def _get_python_variables(python_exe, variables, imports=['import sys']):
118 """Run a python interpreter and print some variables""" 119 program = list(imports) 120 program.append('') 121 for v in variables: 122 program.append("print repr(%s)" % v) 123 proc = pproc.Popen([python_exe, "-c", '\n'.join(program)], stdout=pproc.PIPE) 124 output = proc.communicate()[0].split("\n") 125 if proc.returncode: 126 if Logs.verbose: 127 warn("Python program to extract python configuration variables failed:\n%s" 128 % '\n'.join(["line %03i: %s" % (lineno+1, line) for lineno, line in enumerate(program)])) 129 raise ValueError 130 return_values = [] 131 for s in output: 132 s = s.strip() 133 if not s: 134 continue 135 if s == 'None': 136 return_values.append(None) 137 elif s[0] == "'" and s[-1] == "'": 138 return_values.append(s[1:-1]) 139 elif s[0].isdigit(): 140 return_values.append(int(s)) 141 else: break 142 return return_values
143
144 @conf 145 -def check_python_headers(conf):
146 """Check for headers and libraries necessary to extend or embed python. 147 148 If successful, xxx_PYEXT and xxx_PYEMBED variables are defined in the 149 environment (for uselib). PYEXT should be used for compiling 150 python extensions, while PYEMBED should be used by programs that 151 need to embed a python interpreter. 152 153 Note: this test requires that check_python_version was previously 154 executed and successful.""" 155 156 env = conf.env 157 python = env['PYTHON'] 158 assert python, ("python is %r !" % (python,)) 159 160 ## On Mac OSX we need to use mac bundles for python plugins 161 import checks 162 if checks.detect_platform(None) == 'darwin': 163 conf.check_tool('osx') 164 165 try: 166 # Get some python configuration variables using distutils 167 v = 'prefix SO SYSLIBS SHLIBS LIBDIR LIBPL INCLUDEPY Py_ENABLE_SHARED'.split() 168 (python_prefix, python_SO, python_SYSLIBS, python_SHLIBS, 169 python_LIBDIR, python_LIBPL, INCLUDEPY, Py_ENABLE_SHARED) = \ 170 _get_python_variables(python, ["get_config_var('%s')" % x for x in v], 171 ['from distutils.sysconfig import get_config_var']) 172 except ValueError: 173 conf.fatal("Python development headers not found (-v for details).") 174 175 Runner.print_log("""Configuration returned from %r: 176 python_prefix = %r 177 python_SO = %r 178 python_SYSLIBS = %r 179 python_SHLIBS = %r 180 python_LIBDIR = %r 181 python_LIBPL = %r 182 INCLUDEPY = %r 183 Py_ENABLE_SHARED = %r 184 """ % (python, python_prefix, python_SO, python_SYSLIBS, python_SHLIBS, 185 python_LIBDIR, python_LIBPL, INCLUDEPY, Py_ENABLE_SHARED)) 186 187 env['pyext_PATTERN'] = '%s'+python_SO 188 189 # Check for python libraries for embedding 190 if python_SYSLIBS is not None: 191 for lib in python_SYSLIBS.split(): 192 if lib.startswith('-l'): 193 lib = lib[2:] # strip '-l' 194 env.append_value('LIB_PYEMBED', lib) 195 if python_SHLIBS is not None: 196 for lib in python_SHLIBS.split(): 197 if lib.startswith('-l'): 198 lib = lib[2:] # strip '-l' 199 env.append_value('LIB_PYEMBED', lib) 200 lib = conf.create_library_configurator() 201 lib.name = 'python' + env['PYTHON_VERSION'] 202 lib.uselib = 'PYEMBED' 203 lib.code = ''' 204 #ifdef __cplusplus 205 extern "C" { 206 #endif 207 void Py_Initialize(void); 208 void Py_Finalize(void); 209 #ifdef __cplusplus 210 } 211 #endif 212 int main(int argc, char *argv[]) { Py_Initialize(); Py_Finalize(); return 0; } 213 ''' 214 if python_LIBDIR is not None: 215 lib.path = [python_LIBDIR] 216 result = lib.run() 217 else: 218 result = 0 219 220 ## try again with -L$python_LIBPL (some systems don't install the python library in $prefix/lib) 221 if not result: 222 if python_LIBPL is not None: 223 lib.path = [python_LIBPL] 224 result = lib.run() 225 else: 226 result = 0 227 228 ## try again with -L$prefix/libs, and pythonXY name rather than pythonX.Y (win32) 229 if not result: 230 lib.path = [os.path.join(python_prefix, "libs")] 231 lib.name = 'python' + env['PYTHON_VERSION'].replace('.', '') 232 result = lib.run() 233 234 if result: 235 env['LIBPATH_PYEMBED'] = lib.path 236 env.append_value('LIB_PYEMBED', lib.name) 237 238 ## under certain conditions, python extensions must link to 239 ## python libraries, not just python embedding programs. 240 if (sys.platform == 'win32' or sys.platform.startswith('os2') 241 or sys.platform == 'darwin' or Py_ENABLE_SHARED): 242 env['LIBPATH_PYEXT'] = env['LIBPATH_PYEMBED'] 243 env['LIB_PYEXT'] = env['LIB_PYEMBED'] 244 245 # We check that pythonX.Y-config exists, and if it exists we 246 # use it to get only the includes, else fall back to distutils. 247 python_config = conf.find_program( 248 'python%s-config' % ('.'.join(env['PYTHON_VERSION'].split('.')[:2])), 249 var='PYTHON_CONFIG') 250 if python_config: 251 includes = [] 252 for incstr in os.popen("%s %s --includes" % (python, python_config)).readline().strip().split(): 253 # strip the -I or /I 254 if (incstr.startswith('-I') 255 or incstr.startswith('/I')): 256 incstr = incstr[2:] 257 # append include path, unless already given 258 if incstr not in includes: 259 includes.append(incstr) 260 env['CPPPATH_PYEXT'] = list(includes) 261 env['CPPPATH_PYEMBED'] = list(includes) 262 else: 263 env['CPPPATH_PYEXT'] = [INCLUDEPY] 264 env['CPPPATH_PYEMBED'] = [INCLUDEPY] 265 266 # Code using the Python API needs to be compiled with -fno-strict-aliasing 267 if env['CC']: 268 version = os.popen("%s --version" % env['CC']).readline() 269 if '(GCC)' in version: 270 env.append_value('CCFLAGS_PYEMBED', '-fno-strict-aliasing') 271 env.append_value('CCFLAGS_PYEXT', '-fno-strict-aliasing') 272 if env['CXX']: 273 version = os.popen("%s --version" % env['CXX']).readline() 274 if '(GCC)' in version: 275 env.append_value('CXXFLAGS_PYEMBED', '-fno-strict-aliasing') 276 env.append_value('CXXFLAGS_PYEXT', '-fno-strict-aliasing') 277 278 # Test to see if it compiles 279 header = conf.create_header_configurator() 280 header.name = 'Python.h' 281 header.define = 'HAVE_PYTHON_H' 282 header.uselib = 'PYEXT' 283 header.code = "#include <Python.h>\nint main(int argc, char *argv[]) { Py_Initialize(); Py_Finalize(); return 0; }" 284 result = header.run() 285 if not result: 286 conf.fatal("Python development headers not found.")
287
288 @conf 289 -def check_python_version(conf, minver=None):
290 """ 291 Check if the python interpreter is found matching a given minimum version. 292 minver should be a tuple, eg. to check for python >= 2.4.2 pass (2,4,2) as minver. 293 294 If successful, PYTHON_VERSION is defined as 'MAJOR.MINOR' 295 (eg. '2.4') of the actual python version found, and PYTHONDIR is 296 defined, pointing to the site-packages directory appropriate for 297 this python version, where modules/packages/extensions should be 298 installed. 299 """ 300 assert minver is None or isinstance(minver, tuple) 301 python = conf.env['PYTHON'] 302 assert python, ("python is %r !" % (python,)) 303 304 # Get python version string 305 cmd = [python, "-c", "import sys\nfor x in sys.version_info: print str(x)"] 306 debug('python: Running python command %r' % cmd) 307 proc = pproc.Popen(cmd, stdout=pproc.PIPE) 308 lines = proc.communicate()[0].split() 309 assert len(lines) == 5, "found %i lines, expected 5: %r" % (len(lines), lines) 310 pyver_tuple = (int(lines[0]), int(lines[1]), int(lines[2]), lines[3], int(lines[4])) 311 312 # compare python version with the minimum required 313 result = (minver is None) or (pyver_tuple >= minver) 314 315 if result: 316 # define useful environment variables 317 pyver = '.'.join([str(x) for x in pyver_tuple[:2]]) 318 conf.env['PYTHON_VERSION'] = pyver 319 320 if 'PYTHONDIR' in os.environ: 321 pydir = os.environ['PYTHONDIR'] 322 else: 323 if sys.platform == 'win32': 324 (python_LIBDEST,) = \ 325 _get_python_variables(python, ["get_config_var('LIBDEST')"], 326 ['from distutils.sysconfig import get_config_var']) 327 else: 328 python_LIBDEST = None 329 if python_LIBDEST is None: 330 if conf.env['LIBDIR']: 331 python_LIBDEST = os.path.join(conf.env['LIBDIR'], "python" + pyver) 332 else: 333 python_LIBDEST = os.path.join(conf.env['PREFIX'], "lib", "python" + pyver) 334 pydir = os.path.join(python_LIBDEST, "site-packages") 335 336 if hasattr(conf, 'define'): # conf.define is added by the C tool, so may not exist