2 # dsextras.py - Extra classes and utilities for distutils, adding
6 from distutils
.command
.build_ext
import build_ext
7 from distutils
.command
.install_lib
import install_lib
8 from distutils
.command
.install_data
import install_data
9 from distutils
.extension
import Extension
10 import distutils
.dep_util
20 def get_m4_define(varname
):
21 """Return the value of a m4_define variable as set in configure.in."""
22 pattern
= re
.compile("m4_define\(" + varname
+ "\,\s*(.+)\)")
23 if os
.path
.exists('configure.ac'):
24 fname
= 'configure.ac'
25 elif os
.path
.exists('configure.in'):
26 fname
= 'configure.in'
28 raise SystemExit('could not find configure file')
30 for line
in open(fname
).readlines():
31 match_obj
= pattern
.match(line
)
33 return match_obj
.group(1)
38 """Return output (stdout or stderr) of executing cmd in a shell."""
39 return getstatusoutput(cmd
)[1]
41 def getstatusoutput(cmd
):
42 """Return (status, output) of executing cmd in a shell."""
43 if sys
.platform
== 'win32':
44 pipe
= os
.popen(cmd
, 'r')
46 sts
= pipe
.close() or 0
51 from commands
import getstatusoutput
52 return getstatusoutput(cmd
)
55 """Checks for the existence of pkg-config"""
56 if (sys
.platform
== 'win32' and
57 os
.system('pkg-config --version > NUL') == 0):
60 if getstatusoutput('pkg-config')[0] == 256:
64 """List all files in a dir, with filename match support:
65 for example: glade/*.glade will return all files in the glade directory
66 that matches *.glade. It also looks up the full path"""
67 if dir.find(os
.sep
) != -1:
68 parts
= dir.split(os
.sep
)
69 dir = string
.join(parts
[:-1], os
.sep
)
75 dir = os
.path
.abspath(dir)
77 for file in os
.listdir(dir):
78 if fnmatch
.fnmatch(file, pattern
):
79 retval
.append(os
.path
.join(dir, file))
82 def pkgc_version_check(name
, req_version
):
83 """Check the existence and version number of a package:
84 returns 0 if not installed or too old, 1 otherwise."""
85 is_installed
= not os
.system('pkg-config --exists %s' % name
)
89 orig_version
= getoutput('pkg-config --modversion %s' % name
)
90 version
= map(int, orig_version
.split('.'))
91 pkc_version
= map(int, req_version
.split('.'))
93 if version
>= pkc_version
:
98 class BuildExt(build_ext
):
99 def init_extra_compile_args(self
):
100 self
.extra_compile_args
= []
101 if sys
.platform
== 'win32' and \
102 self
.compiler
.compiler_type
== 'mingw32':
103 # MSVC compatible struct packing is required.
104 # Note gcc2 uses -fnative-struct while gcc3
105 # uses -mms-bitfields. Based on the version
106 # the proper flag is used below.
107 msnative_struct
= { '2' : '-fnative-struct',
108 '3' : '-mms-bitfields' }
109 gcc_version
= getoutput('gcc -dumpversion')
110 print 'using MinGW GCC version %s with %s option' % \
111 (gcc_version
, msnative_struct
[gcc_version
[0]])
112 self
.extra_compile_args
.append(msnative_struct
[gcc_version
[0]])
114 def modify_compiler(self
):
115 if sys
.platform
== 'win32' and \
116 self
.compiler
.compiler_type
== 'mingw32':
117 # Remove '-static' linker option to prevent MinGW ld
118 # from trying to link with MSVC import libraries.
119 if self
.compiler
.linker_so
.count('-static'):
120 self
.compiler
.linker_so
.remove('-static')
122 def build_extensions(self
):
123 # Init self.extra_compile_args
124 self
.init_extra_compile_args()
125 # Modify default compiler settings
126 self
.modify_compiler()
127 # Invoke base build_extensions()
128 build_ext
.build_extensions(self
)
130 def build_extension(self
, ext
):
131 # Add self.extra_compile_args to ext.extra_compile_args
132 ext
.extra_compile_args
+= self
.extra_compile_args
133 # Generate eventual templates before building
134 if hasattr(ext
, 'generate'):
136 # Filter out 'c' and 'm' libs when compilic w/ msvc
137 if sys
.platform
== 'win32' and self
.compiler
.compiler_type
== 'msvc':
138 save_libs
= ext
.libraries
139 ext
.libraries
= [lib
for lib
in ext
.libraries
140 if lib
not in ['c', 'm']]
142 save_libs
= ext
.libraries
143 # Invoke base build_extension()
144 build_ext
.build_extension(self
, ext
)
145 if save_libs
!= None and save_libs
!= ext
.libraries
:
146 ext
.libraries
= save_libs
148 class InstallLib(install_lib
):
153 def set_install_dir(self
, install_dir
):
154 self
.install_dir
= install_dir
156 def get_outputs(self
):
157 return install_lib
.get_outputs(self
) + self
.local_outputs
159 def get_inputs(self
):
160 return install_lib
.get_inputs(self
) + self
.local_inputs
162 class InstallData(install_data
):
166 template_options
= {}
170 self
.prefix
= os
.sep
.join(self
.install_dir
.split(os
.sep
)[:-3])
172 # default: os.name == "posix"
173 self
.prefix
= os
.sep
.join(self
.install_dir
.split(os
.sep
)[:-4])
175 self
.exec_prefix
= '${prefix}/bin'
176 self
.includedir
= '${prefix}/include'
177 self
.libdir
= '${prefix}/lib'
178 self
.datarootdir
= '${prefix}/share'
179 self
.datadir
= '${prefix}/share'
181 self
.add_template_option('prefix', self
.prefix
)
182 self
.add_template_option('exec_prefix', self
.exec_prefix
)
183 self
.add_template_option('includedir', self
.includedir
)
184 self
.add_template_option('libdir', self
.libdir
)
185 self
.add_template_option('datarootdir', self
.datarootdir
)
186 self
.add_template_option('datadir', self
.datadir
)
187 self
.add_template_option('PYTHON', sys
.executable
)
188 self
.add_template_option('THREADING_CFLAGS', '')
190 def set_install_dir(self
, install_dir
):
191 self
.install_dir
= install_dir
193 def add_template_option(self
, name
, value
):
194 self
.template_options
['@%s@' % name
] = value
196 def install_template(self
, filename
, install_dir
):
197 """Install template filename into target directory install_dir."""
198 output_file
= os
.path
.split(filename
)[-1][:-3]
200 template
= open(filename
).read()
201 for key
, value
in self
.template_options
.items():
202 template
= template
.replace(key
, value
)
204 output
= os
.path
.join(install_dir
, output_file
)
205 self
.mkpath(install_dir
)
206 open(output
, 'w').write(template
)
207 self
.local_inputs
.append(filename
)
208 self
.local_outputs
.append(output
)
211 def get_outputs(self
):
212 return install_data
.get_outputs(self
) + self
.local_outputs
214 def get_inputs(self
):
215 return install_data
.get_inputs(self
) + self
.local_inputs
217 class PkgConfigExtension(Extension
):
218 # Name of pygobject package extension depends on, can be None
219 pygobject_pkc
= 'pygobject-2.0'
221 def __init__(self
, **kwargs
):
222 name
= kwargs
['pkc_name']
223 kwargs
['include_dirs'] = self
.get_include_dirs(name
) + GLOBAL_INC
224 kwargs
['define_macros'] = GLOBAL_MACROS
225 kwargs
['libraries'] = self
.get_libraries(name
)
226 kwargs
['library_dirs'] = self
.get_library_dirs(name
)
227 if 'pygobject_pkc' in kwargs
:
228 self
.pygobject_pkc
= kwargs
.pop('pygobject_pkc')
229 if self
.pygobject_pkc
:
230 kwargs
['include_dirs'] += self
.get_include_dirs(self
.pygobject_pkc
)
231 kwargs
['libraries'] += self
.get_libraries(self
.pygobject_pkc
)
232 kwargs
['library_dirs'] += self
.get_library_dirs(self
.pygobject_pkc
)
233 self
.name
= kwargs
['name']
234 self
.pkc_name
= kwargs
['pkc_name']
235 self
.pkc_version
= kwargs
['pkc_version']
236 del kwargs
['pkc_name'], kwargs
['pkc_version']
237 Extension
.__init
__(self
, **kwargs
)
239 def get_include_dirs(self
, names
):
240 if type(names
) != tuple:
244 output
= getoutput('pkg-config --cflags-only-I %s' % name
)
245 retval
.extend(output
.replace('-I', '').split())
248 def get_libraries(self
, names
):
249 if type(names
) != tuple:
253 output
= getoutput('pkg-config --libs-only-l %s' % name
)
254 retval
.extend(output
.replace('-l', '').split())
257 def get_library_dirs(self
, names
):
258 if type(names
) != tuple:
262 output
= getoutput('pkg-config --libs-only-L %s' % name
)
263 retval
.extend(output
.replace('-L', '').split())
267 """If the pkg-config version found is good enough"""
268 if self
.can_build_ok
!= None:
269 return self
.can_build_ok
271 if type(self
.pkc_name
) != tuple:
272 reqs
= [(self
.pkc_name
, self
.pkc_version
)]
274 reqs
= zip(self
.pkc_name
, self
.pkc_version
)
276 for package
, version
in reqs
:
277 retval
= os
.system('pkg-config --exists %s' % package
)
279 print ("* %s.pc could not be found, bindings for %s"
280 " will not be built." % (package
, self
.name
))
281 self
.can_build_ok
= 0
284 orig_version
= getoutput('pkg-config --modversion %s' %
286 if (map(int, orig_version
.split('.')) >=
287 map(int, version
.split('.'))):
288 self
.can_build_ok
= 1
291 print "Warning: Too old version of %s" % self
.pkc_name
292 print " Need %s, but %s is installed" % \
293 (package
, orig_version
)
294 self
.can_build_ok
= 0
300 # The Template and TemplateExtension classes require codegen which is
301 # currently part of the pygtk distribution. While codegen might ultimately
302 # be moved to pygobject, it was decided (bug #353849) to keep the Template
303 # and TemplateExtension code in dsextras. In the meantime, we check for the
304 # availability of codegen and redirect the user to the pygtk installer if
305 # he/she wants to get access to Template and TemplateExtension.
307 template_classes_enabled
=True
308 codegen_error_message
="""
309 ***************************************************************************
310 Codegen could not be found on your system and is required by the
311 dsextras.Template and dsextras.TemplateExtension classes. codegen is part
312 of PyGTK. To use either Template or TemplateExtension, you should also
314 ***************************************************************************
317 from codegen
.override
import Overrides
318 from codegen
.defsparser
import DefsParser
319 from codegen
.codegen
import register_types
, SourceWriter
, \
321 import codegen
.createdefs
322 except ImportError, e
:
323 template_classes_enabled
=False
325 class Template(object):
326 def __new__(cls
,*args
, **kwds
):
327 if not template_classes_enabled
:
328 raise NameError("'%s' is not defined\n" % cls
.__name
__
329 + codegen_error_message
)
330 return object.__new
__(cls
,*args
, **kwds
)
332 def __init__(self
, override
, output
, defs
, prefix
,
333 register
=[], load_types
=None, py_ssize_t_clean
=False):
335 self
.override
= override
338 self
.load_types
= load_types
339 self
.py_ssize_t_clean
= py_ssize_t_clean
342 if isinstance(defs
,tuple):
344 self
.built_defs
.append(defs
)
350 if isinstance(r
,tuple):
351 self
.register
.append(r
[0])
352 self
.built_defs
.append(r
)
354 self
.register
.append(r
)
356 def check_dates(self
):
357 # Return True if files are up-to-date
358 files
=self
.register
[:]
359 files
.append(self
.override
)
360 files
.append(self
.defs
)
362 return not distutils
.dep_util
.newer_group(files
,self
.output
)
364 def generate_defs(self
):
365 for (target
,sources
) in self
.built_defs
:
366 if distutils
.dep_util
.newer_group(sources
,target
):
367 # createdefs is mostly called from the CLI !
368 args
=['dummy',target
]+sources
369 codegen
.createdefs
.main(args
)
373 # Generate defs files if necessary
375 # ... then check the file timestamps
376 if self
.check_dates():
379 for item
in self
.register
:
380 dp
= DefsParser(item
,dict(GLOBAL_MACROS
))
386 execfile(self
.load_types
, globals)
388 dp
= DefsParser(self
.defs
,dict(GLOBAL_MACROS
))
392 fd
= open(self
.output
, 'w')
393 sw
= SourceWriter(dp
,Overrides(self
.override
),
394 self
.prefix
,FileOutput(fd
,self
.output
))
395 sw
.write(self
.py_ssize_t_clean
)
398 class TemplateExtension(PkgConfigExtension
):
399 def __new__(cls
,*args
, **kwds
):
400 if not template_classes_enabled
:
401 raise NameError("'%s' is not defined\n" % cls
.__name
__
402 + codegen_error_message
)
403 return PkgConfigExtension
.__new
__(cls
,*args
, **kwds
)
405 def __init__(self
, **kwargs
):
406 name
= kwargs
['name']
407 defs
= kwargs
['defs']
408 if isinstance(defs
,tuple):
409 output
= defs
[0][:-5] + '.c'
411 output
= defs
[:-5] + '.c'
412 override
= kwargs
['override']
413 load_types
= kwargs
.get('load_types')
414 py_ssize_t_clean
= kwargs
.pop('py_ssize_t_clean',False)
416 self
.templates
.append(Template(override
, output
, defs
, 'py' + name
,
417 kwargs
['register'], load_types
,
420 del kwargs
['register'], kwargs
['override'], kwargs
['defs']
422 del kwargs
['load_types']
424 if kwargs
.has_key('output'):
425 kwargs
['name'] = kwargs
['output']
428 PkgConfigExtension
.__init
__(self
, **kwargs
)
431 map(lambda x
: x
.generate(), self
.templates
)