PageRenderTime 51ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/ctypes_configure/cbuild.py

https://bitbucket.org/kkris/pypy
Python | 456 lines | 375 code | 36 blank | 45 comment | 57 complexity | 9f5df1ff50163481cfe5feeea9885ddd MD5 | raw file
  1. import os, sys, inspect, re, imp, py
  2. from ctypes_configure import stdoutcapture
  3. import distutils
  4. debug = 0
  5. configdir = py.path.local.make_numbered_dir(prefix='ctypes_configure-')
  6. class ExternalCompilationInfo(object):
  7. _ATTRIBUTES = ['pre_include_lines', 'includes', 'include_dirs',
  8. 'post_include_lines', 'libraries', 'library_dirs',
  9. 'separate_module_sources', 'separate_module_files']
  10. _AVOID_DUPLICATES = ['separate_module_files', 'libraries', 'includes',
  11. 'include_dirs', 'library_dirs', 'separate_module_sources']
  12. def __init__(self,
  13. pre_include_lines = [],
  14. includes = [],
  15. include_dirs = [],
  16. post_include_lines = [],
  17. libraries = [],
  18. library_dirs = [],
  19. separate_module_sources = [],
  20. separate_module_files = []):
  21. """
  22. pre_include_lines: list of lines that should be put at the top
  23. of the generated .c files, before any #include. They shouldn't
  24. contain an #include themselves.
  25. includes: list of .h file names to be #include'd from the
  26. generated .c files.
  27. include_dirs: list of dir names that is passed to the C compiler
  28. post_include_lines: list of lines that should be put at the top
  29. of the generated .c files, after the #includes.
  30. libraries: list of library names that is passed to the linker
  31. library_dirs: list of dir names that is passed to the linker
  32. separate_module_sources: list of multiline strings that are
  33. each written to a .c file and compiled separately and linked
  34. later on. (If function prototypes are needed for other .c files
  35. to access this, they can be put in post_include_lines.)
  36. separate_module_files: list of .c file names that are compiled
  37. separately and linked later on. (If an .h file is needed for
  38. other .c files to access this, it can be put in includes.)
  39. """
  40. for name in self._ATTRIBUTES:
  41. value = locals()[name]
  42. assert isinstance(value, (list, tuple))
  43. setattr(self, name, tuple(value))
  44. def _value(self):
  45. return tuple([getattr(self, x) for x in self._ATTRIBUTES])
  46. def __hash__(self):
  47. return hash(self._value())
  48. def __eq__(self, other):
  49. return self.__class__ is other.__class__ and \
  50. self._value() == other._value()
  51. def __ne__(self, other):
  52. return not self == other
  53. def __repr__(self):
  54. info = []
  55. for attr in self._ATTRIBUTES:
  56. val = getattr(self, attr)
  57. info.append("%s=%s" % (attr, repr(val)))
  58. return "<ExternalCompilationInfo (%s)>" % ", ".join(info)
  59. def merge(self, *others):
  60. others = list(others)
  61. attrs = {}
  62. for name in self._ATTRIBUTES:
  63. if name not in self._AVOID_DUPLICATES:
  64. s = []
  65. for i in [self] + others:
  66. s += getattr(i, name)
  67. attrs[name] = s
  68. else:
  69. s = set()
  70. attr = []
  71. for one in [self] + others:
  72. for elem in getattr(one, name):
  73. if elem not in s:
  74. s.add(elem)
  75. attr.append(elem)
  76. attrs[name] = attr
  77. return ExternalCompilationInfo(**attrs)
  78. def write_c_header(self, fileobj):
  79. for line in self.pre_include_lines:
  80. print >> fileobj, line
  81. for path in self.includes:
  82. print >> fileobj, '#include <%s>' % (path,)
  83. for line in self.post_include_lines:
  84. print >> fileobj, line
  85. def _copy_attributes(self):
  86. d = {}
  87. for attr in self._ATTRIBUTES:
  88. d[attr] = getattr(self, attr)
  89. return d
  90. def convert_sources_to_files(self, cache_dir=None, being_main=False):
  91. if not self.separate_module_sources:
  92. return self
  93. if cache_dir is None:
  94. cache_dir = configdir.join('module_cache').ensure(dir=1)
  95. num = 0
  96. files = []
  97. for source in self.separate_module_sources:
  98. while 1:
  99. filename = cache_dir.join('module_%d.c' % num)
  100. num += 1
  101. if not filename.check():
  102. break
  103. f = filename.open("w")
  104. if being_main:
  105. f.write("#define PYPY_NOT_MAIN_FILE\n")
  106. self.write_c_header(f)
  107. source = str(source)
  108. f.write(source)
  109. if not source.endswith('\n'):
  110. f.write('\n')
  111. f.close()
  112. files.append(str(filename))
  113. d = self._copy_attributes()
  114. d['separate_module_sources'] = ()
  115. d['separate_module_files'] += tuple(files)
  116. return ExternalCompilationInfo(**d)
  117. def compile_shared_lib(self):
  118. self = self.convert_sources_to_files()
  119. if not self.separate_module_files:
  120. return self
  121. lib = compile_c_module([], 'externmod', self)
  122. d = self._copy_attributes()
  123. d['libraries'] += (lib,)
  124. d['separate_module_files'] = ()
  125. d['separate_module_sources'] = ()
  126. return ExternalCompilationInfo(**d)
  127. if sys.platform == 'win32':
  128. so_ext = '.dll'
  129. else:
  130. so_ext = '.so'
  131. def compiler_command():
  132. # e.g. for tcc, you might set this to
  133. # "tcc -shared -o %s.so %s.c"
  134. return os.getenv('PYPY_CC')
  135. def enable_fast_compilation():
  136. if sys.platform == 'win32':
  137. dash = '/'
  138. else:
  139. dash = '-'
  140. from distutils import sysconfig
  141. gcv = sysconfig.get_config_vars()
  142. opt = gcv.get('OPT') # not always existent
  143. if opt:
  144. opt = re.sub('%sO\d+' % dash, '%sO0' % dash, opt)
  145. else:
  146. opt = '%sO0' % dash
  147. gcv['OPT'] = opt
  148. def ensure_correct_math():
  149. if sys.platform != 'win32':
  150. return # so far
  151. from distutils import sysconfig
  152. gcv = sysconfig.get_config_vars()
  153. opt = gcv.get('OPT') # not always existent
  154. if opt and '/Op' not in opt:
  155. opt += '/Op'
  156. gcv['OPT'] = opt
  157. def try_compile(c_files, eci):
  158. try:
  159. build_executable(c_files, eci)
  160. result = True
  161. except (distutils.errors.CompileError,
  162. distutils.errors.LinkError):
  163. result = False
  164. return result
  165. def compile_c_module(cfiles, modbasename, eci, tmpdir=None):
  166. #try:
  167. # from distutils.log import set_threshold
  168. # set_threshold(10000)
  169. #except ImportError:
  170. # print "ERROR IMPORTING"
  171. # pass
  172. cfiles = [py.path.local(f) for f in cfiles]
  173. if tmpdir is None:
  174. tmpdir = configdir.join("module_cache").ensure(dir=1)
  175. num = 0
  176. cfiles += eci.separate_module_files
  177. include_dirs = list(eci.include_dirs)
  178. library_dirs = list(eci.library_dirs)
  179. if (sys.platform == 'darwin' or # support Fink & Darwinports
  180. sys.platform.startswith('freebsd')):
  181. for s in ('/sw/', '/opt/local/', '/usr/local/'):
  182. if s + 'include' not in include_dirs and \
  183. os.path.exists(s + 'include'):
  184. include_dirs.append(s + 'include')
  185. if s + 'lib' not in library_dirs and \
  186. os.path.exists(s + 'lib'):
  187. library_dirs.append(s + 'lib')
  188. num = 0
  189. modname = modbasename
  190. while 1:
  191. if not tmpdir.join(modname + so_ext).check():
  192. break
  193. num += 1
  194. modname = '%s_%d' % (modbasename, num)
  195. lastdir = tmpdir.chdir()
  196. libraries = eci.libraries
  197. ensure_correct_math()
  198. try:
  199. if debug: print "modname", modname
  200. c = stdoutcapture.Capture(mixed_out_err = True)
  201. try:
  202. try:
  203. if compiler_command():
  204. # GCC-ish options only
  205. from distutils import sysconfig
  206. gcv = sysconfig.get_config_vars()
  207. cmd = compiler_command().replace('%s',
  208. str(tmpdir.join(modname)))
  209. for dir in [gcv['INCLUDEPY']] + list(include_dirs):
  210. cmd += ' -I%s' % dir
  211. for dir in library_dirs:
  212. cmd += ' -L%s' % dir
  213. os.system(cmd)
  214. else:
  215. from distutils.dist import Distribution
  216. from distutils.extension import Extension
  217. from distutils.ccompiler import get_default_compiler
  218. saved_environ = os.environ.items()
  219. try:
  220. # distutils.core.setup() is really meant for end-user
  221. # interactive usage, because it eats most exceptions and
  222. # turn them into SystemExits. Instead, we directly
  223. # instantiate a Distribution, which also allows us to
  224. # ignore unwanted features like config files.
  225. extra_compile_args = []
  226. # ensure correct math on windows
  227. if sys.platform == 'win32':
  228. extra_compile_args.append('/Op') # get extra precision
  229. if get_default_compiler() == 'unix':
  230. old_version = False
  231. try:
  232. g = os.popen('gcc --version', 'r')
  233. verinfo = g.read()
  234. g.close()
  235. except (OSError, IOError):
  236. pass
  237. else:
  238. old_version = verinfo.startswith('2')
  239. if not old_version:
  240. extra_compile_args.extend(["-Wno-unused-label",
  241. "-Wno-unused-variable"])
  242. attrs = {
  243. 'name': "testmodule",
  244. 'ext_modules': [
  245. Extension(modname, [str(cfile) for cfile in cfiles],
  246. include_dirs=include_dirs,
  247. library_dirs=library_dirs,
  248. extra_compile_args=extra_compile_args,
  249. libraries=list(libraries),)
  250. ],
  251. 'script_name': 'setup.py',
  252. 'script_args': ['-q', 'build_ext', '--inplace', '--force'],
  253. }
  254. dist = Distribution(attrs)
  255. if not dist.parse_command_line():
  256. raise ValueError, "distutils cmdline parse error"
  257. dist.run_commands()
  258. finally:
  259. for key, value in saved_environ:
  260. if os.environ.get(key) != value:
  261. os.environ[key] = value
  262. finally:
  263. foutput, foutput = c.done()
  264. data = foutput.read()
  265. if data:
  266. fdump = open("%s.errors" % modname, "w")
  267. fdump.write(data)
  268. fdump.close()
  269. # XXX do we need to do some check on fout/ferr?
  270. # XXX not a nice way to import a module
  271. except:
  272. print >>sys.stderr, data
  273. raise
  274. finally:
  275. lastdir.chdir()
  276. return str(tmpdir.join(modname) + so_ext)
  277. def make_module_from_c(cfile, eci):
  278. cfile = py.path.local(cfile)
  279. modname = cfile.purebasename
  280. compile_c_module([cfile], modname, eci)
  281. return import_module_from_directory(cfile.dirpath(), modname)
  282. def import_module_from_directory(dir, modname):
  283. file, pathname, description = imp.find_module(modname, [str(dir)])
  284. try:
  285. mod = imp.load_module(modname, file, pathname, description)
  286. finally:
  287. if file:
  288. file.close()
  289. return mod
  290. def log_spawned_cmd(spawn):
  291. def spawn_and_log(cmd, *args, **kwds):
  292. if debug:
  293. print ' '.join(cmd)
  294. return spawn(cmd, *args, **kwds)
  295. return spawn_and_log
  296. class ProfOpt(object):
  297. #XXX assuming gcc style flags for now
  298. name = "profopt"
  299. def __init__(self, compiler):
  300. self.compiler = compiler
  301. def first(self):
  302. self.build('-fprofile-generate')
  303. def probe(self, exe, args):
  304. # 'args' is a single string typically containing spaces
  305. # and quotes, which represents several arguments.
  306. os.system("'%s' %s" % (exe, args))
  307. def after(self):
  308. self.build('-fprofile-use')
  309. def build(self, option):
  310. compiler = self.compiler
  311. compiler.compile_extra.append(option)
  312. compiler.link_extra.append(option)
  313. try:
  314. compiler._build()
  315. finally:
  316. compiler.compile_extra.pop()
  317. compiler.link_extra.pop()
  318. class CCompiler:
  319. def __init__(self, cfilenames, eci, outputfilename=None,
  320. compiler_exe=None, profbased=None):
  321. self.cfilenames = cfilenames
  322. ext = ''
  323. self.compile_extra = []
  324. self.link_extra = []
  325. self.libraries = list(eci.libraries)
  326. self.include_dirs = list(eci.include_dirs)
  327. self.library_dirs = list(eci.library_dirs)
  328. self.compiler_exe = compiler_exe
  329. self.profbased = profbased
  330. if not sys.platform in ('win32', 'darwin', 'cygwin'): # xxx
  331. if 'm' not in self.libraries:
  332. self.libraries.append('m')
  333. if 'pthread' not in self.libraries:
  334. self.libraries.append('pthread')
  335. self.compile_extra += ['-O3', '-fomit-frame-pointer', '-pthread']
  336. self.link_extra += ['-pthread']
  337. if sys.platform == 'win32':
  338. self.link_extra += ['/DEBUG'] # generate .pdb file
  339. if (sys.platform == 'darwin' or # support Fink & Darwinports
  340. sys.platform.startswith('freebsd')):
  341. for s in ('/sw/', '/opt/local/', '/usr/local/'):
  342. if s + 'include' not in self.include_dirs and \
  343. os.path.exists(s + 'include'):
  344. self.include_dirs.append(s + 'include')
  345. if s + 'lib' not in self.library_dirs and \
  346. os.path.exists(s + 'lib'):
  347. self.library_dirs.append(s + 'lib')
  348. self.compile_extra += ['-O3', '-fomit-frame-pointer']
  349. if outputfilename is None:
  350. self.outputfilename = py.path.local(cfilenames[0]).new(ext=ext)
  351. else:
  352. self.outputfilename = py.path.local(outputfilename)
  353. def build(self, noerr=False):
  354. basename = self.outputfilename.new(ext='')
  355. data = ''
  356. try:
  357. saved_environ = os.environ.copy()
  358. c = stdoutcapture.Capture(mixed_out_err = True)
  359. try:
  360. self._build()
  361. finally:
  362. # workaround for a distutils bugs where some env vars can
  363. # become longer and longer every time it is used
  364. for key, value in saved_environ.items():
  365. if os.environ.get(key) != value:
  366. os.environ[key] = value
  367. foutput, foutput = c.done()
  368. data = foutput.read()
  369. if data:
  370. fdump = basename.new(ext='errors').open("w")
  371. fdump.write(data)
  372. fdump.close()
  373. except:
  374. if not noerr:
  375. print >>sys.stderr, data
  376. raise
  377. def _build(self):
  378. from distutils.ccompiler import new_compiler
  379. compiler = new_compiler(force=1)
  380. if self.compiler_exe is not None:
  381. for c in '''compiler compiler_so compiler_cxx
  382. linker_exe linker_so'''.split():
  383. compiler.executables[c][0] = self.compiler_exe
  384. compiler.spawn = log_spawned_cmd(compiler.spawn)
  385. objects = []
  386. for cfile in self.cfilenames:
  387. cfile = py.path.local(cfile)
  388. old = cfile.dirpath().chdir()
  389. try:
  390. res = compiler.compile([cfile.basename],
  391. include_dirs=self.include_dirs,
  392. extra_preargs=self.compile_extra)
  393. assert len(res) == 1
  394. cobjfile = py.path.local(res[0])
  395. assert cobjfile.check()
  396. objects.append(str(cobjfile))
  397. finally:
  398. old.chdir()
  399. compiler.link_executable(objects, str(self.outputfilename),
  400. libraries=self.libraries,
  401. extra_preargs=self.link_extra,
  402. library_dirs=self.library_dirs)
  403. def build_executable(*args, **kwds):
  404. noerr = kwds.pop('noerr', False)
  405. compiler = CCompiler(*args, **kwds)
  406. compiler.build(noerr=noerr)
  407. return str(compiler.outputfilename)