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

/rpython/translator/platform/__init__.py

https://bitbucket.org/pypy/pypy/
Python | 358 lines | 314 code | 37 blank | 7 comment | 53 complexity | 3531a1f7252d479dff9a2cb72447f732 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. """Platform-specific support for compiling/executing C sources."""
  2. import py, os, sys
  3. from rpython.tool.runsubprocess import run_subprocess as _run_subprocess
  4. from rpython.tool.udir import udir
  5. from rpython.tool.version import rpythonroot
  6. from rpython.tool.ansi_print import AnsiLogger
  7. log = AnsiLogger("platform")
  8. class CompilationError(Exception):
  9. def __init__(self, out, err):
  10. self.out = out.replace('\r\n', '\n')
  11. self.err = err.replace('\r\n', '\n')
  12. def __repr__(self):
  13. if self.err:
  14. attr = 'err'
  15. else:
  16. attr = 'out'
  17. text = getattr(self, attr).replace('\n', '\n\t')
  18. return 'CompilationError(%s="""\n\t%s""")' % (attr, text)
  19. __str__ = __repr__
  20. class ExecutionResult(object):
  21. def __init__(self, returncode, out, err):
  22. self.returncode = returncode
  23. self.out = out.replace('\r\n', '\n')
  24. self.err = err.replace('\r\n', '\n')
  25. def __repr__(self):
  26. return "<ExecutionResult retcode=%d>" % (self.returncode,)
  27. class Platform(object):
  28. name = "abstract platform"
  29. c_environ = None
  30. relevant_environ = ()
  31. log_errors = True
  32. so_prefixes = ('',)
  33. extra_libs = ()
  34. def __init__(self, cc):
  35. if self.__class__ is Platform:
  36. raise TypeError("You should not instantiate Platform class directly")
  37. self.cc = cc
  38. def compile(self, cfiles, eci, outputfilename=None, standalone=True):
  39. ofiles = self._compile_o_files(cfiles, eci, standalone)
  40. return self._finish_linking(ofiles, eci, outputfilename, standalone)
  41. def _all_cfiles(self, cfiles, eci):
  42. seen = set()
  43. result = []
  44. for cfile in list(cfiles) + list(eci.separate_module_files):
  45. cfile = py.path.local(cfile)
  46. if cfile not in seen:
  47. seen.add(cfile)
  48. result.append(cfile)
  49. return result
  50. def _compile_o_files(self, cfiles, eci, standalone=True):
  51. cfiles = self._all_cfiles(cfiles, eci)
  52. compile_args = self._compile_args_from_eci(eci, standalone)
  53. ofiles = []
  54. for cfile in cfiles:
  55. # Windows hack: use masm for files ending in .asm
  56. if str(cfile).lower().endswith('.asm'):
  57. ofiles.append(self._compile_c_file(self.masm, cfile, []))
  58. else:
  59. ofiles.append(self._compile_c_file(self.cc, cfile, compile_args))
  60. return ofiles
  61. def execute(self, executable, args=None, env=None, compilation_info=None):
  62. if env is None:
  63. env = os.environ.copy()
  64. else:
  65. env = env.copy()
  66. # On Windows, %SystemRoot% must be present for most programs to start
  67. if (os.name == 'nt' and
  68. "SystemRoot" not in env and
  69. "SystemRoot" in os.environ):
  70. env["SystemRoot"] = os.environ["SystemRoot"]
  71. # Set LD_LIBRARY_PATH on posix platforms
  72. if os.name == 'posix' and compilation_info is not None:
  73. library_path = ':'.join([str(i) for i in compilation_info.library_dirs])
  74. if sys.platform == 'darwin':
  75. env['DYLD_LIBRARY_PATH'] = library_path
  76. else:
  77. env['LD_LIBRARY_PATH'] = library_path
  78. returncode, stdout, stderr = _run_subprocess(str(executable), args,
  79. env)
  80. return ExecutionResult(returncode, stdout, stderr)
  81. def gen_makefile(self, cfiles, eci, exe_name=None, path=None,
  82. shared=False, headers_to_precompile=[],
  83. no_precompile_cfiles = [], icon=None):
  84. raise NotImplementedError("Pure abstract baseclass")
  85. def __repr__(self):
  86. return '<%s cc=%s>' % (self.__class__.__name__, self.cc)
  87. def __hash__(self):
  88. return hash(self.__class__.__name__)
  89. def __ne__(self, other):
  90. return not self == other
  91. def __eq__(self, other):
  92. return (self.__class__ is other.__class__ and
  93. self.__dict__ == other.__dict__)
  94. def key(self):
  95. bits = [self.__class__.__name__, 'cc=%r' % self.cc]
  96. for varname in self.relevant_environ:
  97. bits.append('%s=%r' % (varname, os.environ.get(varname)))
  98. # adding sys.maxint to disambiguate windows
  99. bits.append('%s=%r' % ('sys.maxint', sys.maxint))
  100. return ' '.join(bits)
  101. # some helpers which seem to be cross-platform enough
  102. def _execute_c_compiler(self, cc, args, outname, cwd=None):
  103. log.execute(cc + ' ' + ' '.join(args))
  104. # 'cc' can also contain some options for the C compiler;
  105. # e.g. it can be "gcc -m32". We handle it by splitting on ' '.
  106. cclist = cc.split()
  107. cc = cclist[0]
  108. args = cclist[1:] + args
  109. returncode, stdout, stderr = _run_subprocess(cc, args, self.c_environ,
  110. cwd)
  111. self._handle_error(returncode, stdout, stderr, outname)
  112. def _handle_error(self, returncode, stdout, stderr, outname):
  113. if returncode != 0:
  114. errorfile = outname.new(ext='errors')
  115. errorfile.write(stderr, 'wb')
  116. if self.log_errors:
  117. stderrlines = stderr.splitlines()
  118. for line in stderrlines:
  119. log.Error(line)
  120. # ^^^ don't use ERROR, because it might actually be fine.
  121. # Also, ERROR confuses lib-python/conftest.py.
  122. raise CompilationError(stdout, stderr)
  123. else:
  124. for line in stderr.splitlines():
  125. log.WARNING(line)
  126. def _make_o_file(self, cfile, ext):
  127. """Create an object file name under the udir for a .c file"""
  128. ofile = cfile.new(ext=ext)
  129. if ofile.relto(udir):
  130. return ofile
  131. assert ofile.relto(rpythonroot), (
  132. "%r should be relative to either %r or %r" % (
  133. ofile, rpythonroot, udir))
  134. ofile = udir.join(ofile.relto(rpythonroot))
  135. ofile.dirpath().ensure(dir=True)
  136. return ofile
  137. def preprocess_include_dirs(self, include_dirs):
  138. if 'PYPY_LOCALBASE' in os.environ:
  139. dirs = list(self._preprocess_include_dirs(include_dirs))
  140. return [os.environ['PYPY_LOCALBASE'] + '/include'] + dirs
  141. return self._preprocess_include_dirs(include_dirs)
  142. def _preprocess_include_dirs(self, include_dirs):
  143. return include_dirs
  144. def _compile_args_from_eci(self, eci, standalone):
  145. include_dirs = self.preprocess_include_dirs(eci.include_dirs)
  146. args = self._includedirs(include_dirs)
  147. if standalone:
  148. extra = self.standalone_only
  149. else:
  150. extra = self.get_shared_only_compile_flags()
  151. cflags = list(self.cflags) + list(extra)
  152. return (cflags + list(eci.compile_extra) + args)
  153. def get_shared_only_compile_flags(self):
  154. return tuple(self.shared_only)
  155. def preprocess_library_dirs(self, library_dirs):
  156. if 'PYPY_LOCALBASE' in os.environ:
  157. dirs = list(self._preprocess_library_dirs(library_dirs))
  158. return [os.environ['PYPY_LOCALBASE'] + '/lib'] + dirs
  159. return self._preprocess_library_dirs(library_dirs)
  160. def _preprocess_library_dirs(self, library_dirs):
  161. return library_dirs
  162. def _link_args_from_eci(self, eci, standalone):
  163. library_dirs = self.preprocess_library_dirs(eci.library_dirs)
  164. library_dirs = self._libdirs(library_dirs)
  165. libraries = self._libs(eci.libraries)
  166. link_files = self._linkfiles(eci.link_files)
  167. export_flags = self._exportsymbols_link_flags()
  168. return (library_dirs + list(self.link_flags) + export_flags +
  169. link_files + list(eci.link_extra) + libraries +
  170. list(self.extra_libs))
  171. def _exportsymbols_link_flags(self):
  172. return []
  173. def _finish_linking(self, ofiles, eci, outputfilename, standalone):
  174. if outputfilename is None:
  175. outputfilename = ofiles[0].purebasename
  176. if ofiles:
  177. dirname = ofiles[0].dirpath()
  178. else:
  179. dirname = udir.join('module_cache')
  180. exe_name = dirname.join(outputfilename, abs=True)
  181. if standalone:
  182. if self.exe_ext:
  183. exe_name += '.' + self.exe_ext
  184. else:
  185. exe_name += '.' + self.so_ext
  186. if eci.use_cpp_linker:
  187. cc_link = 'g++' # XXX hard-coded so far
  188. else:
  189. cc_link = self.cc
  190. largs = self._link_args_from_eci(eci, standalone)
  191. return self._link(cc_link, ofiles, largs, standalone, exe_name)
  192. # below are some detailed information for platforms
  193. def include_dirs_for_libffi(self):
  194. dirs = self._include_dirs_for_libffi()
  195. if 'PYPY_LOCALBASE' in os.environ:
  196. return [os.environ['PYPY_LOCALBASE'] + '/include'] + dirs
  197. return dirs
  198. def library_dirs_for_libffi(self):
  199. dirs = self._library_dirs_for_libffi()
  200. if 'PYPY_LOCALBASE' in os.environ:
  201. return [os.environ['PYPY_LOCALBASE'] + '/lib'] + dirs
  202. return dirs
  203. def _include_dirs_for_libffi(self):
  204. raise NotImplementedError("Needs to be overwritten")
  205. def _library_dirs_for_libffi(self):
  206. raise NotImplementedError("Needs to be overwritten")
  207. def check___thread(self):
  208. return True
  209. if sys.platform.startswith('linux'):
  210. from rpython.translator.platform.linux import Linux, LinuxPIC
  211. import platform
  212. # Only required on armhf and mips{,el}, not armel. But there's no way to
  213. # detect armhf without shelling out
  214. if (platform.architecture()[0] == '64bit'
  215. or platform.machine().startswith(('arm', 'mips', 'ppc'))):
  216. host_factory = LinuxPIC
  217. else:
  218. host_factory = Linux
  219. elif sys.platform == 'darwin':
  220. from rpython.translator.platform.darwin import Darwin_i386, Darwin_x86_64, Darwin_PowerPC
  221. import platform
  222. assert platform.machine() in ('Power Macintosh', 'i386', 'x86_64')
  223. if platform.machine() == 'Power Macintosh':
  224. host_factory = Darwin_PowerPC
  225. elif sys.maxint <= 2147483647:
  226. host_factory = Darwin_i386
  227. else:
  228. host_factory = Darwin_x86_64
  229. elif "gnukfreebsd" in sys.platform:
  230. from rpython.translator.platform.freebsd import GNUkFreebsd, GNUkFreebsd_64
  231. import platform
  232. if platform.architecture()[0] == '32bit':
  233. host_factory = GNUkFreebsd
  234. else:
  235. host_factory = GNUkFreebsd_64
  236. elif "freebsd" in sys.platform:
  237. from rpython.translator.platform.freebsd import Freebsd, Freebsd_64
  238. import platform
  239. if platform.architecture()[0] == '32bit':
  240. host_factory = Freebsd
  241. else:
  242. host_factory = Freebsd_64
  243. elif sys.platform.startswith('netbsd'):
  244. from rpython.translator.platform.netbsd import Netbsd, Netbsd_64
  245. import platform
  246. if platform.architecture()[0] == '32bit':
  247. host_factory = Netbsd
  248. else:
  249. host_factory = Netbsd_64
  250. elif "openbsd" in sys.platform:
  251. from rpython.translator.platform.openbsd import OpenBSD, OpenBSD_64
  252. import platform
  253. if platform.architecture()[0] == '32bit':
  254. host_factory = OpenBSD
  255. else:
  256. host_factory = OpenBSD_64
  257. elif os.name == 'nt':
  258. from rpython.translator.platform.windows import Windows, Windows_x64
  259. import platform
  260. if platform.architecture()[0] == '32bit':
  261. host_factory = Windows
  262. else:
  263. host_factory = Windows_x64
  264. elif sys.platform == 'cygwin':
  265. from rpython.translator.platform.cygwin import Cygwin, Cygwin64
  266. import platform
  267. if platform.architecture()[0] == '32bit':
  268. host_factory = Cygwin
  269. else:
  270. host_factory = Cygwin64
  271. else:
  272. # pray
  273. from rpython.translator.platform.distutils_platform import DistutilsPlatform
  274. host_factory = DistutilsPlatform
  275. platform = host = host_factory()
  276. def pick_platform(new_platform, cc):
  277. if new_platform == 'host':
  278. return host_factory(cc)
  279. elif new_platform == 'maemo':
  280. from rpython.translator.platform.maemo import Maemo
  281. return Maemo(cc)
  282. elif new_platform == 'arm':
  283. from rpython.translator.platform.arm import ARM
  284. return ARM(cc)
  285. elif new_platform == 'distutils':
  286. from rpython.translator.platform.distutils_platform import DistutilsPlatform
  287. return DistutilsPlatform()
  288. else:
  289. raise ValueError("platform = %s" % (new_platform,))
  290. def set_platform(new_platform, cc):
  291. global platform
  292. platform = pick_platform(new_platform, cc)
  293. if not platform:
  294. raise ValueError("pick_platform(%r, %s) failed"%(new_platform, cc))
  295. log.msg("Set platform with %r cc=%s, using cc=%r, version=%r" % (new_platform, cc,
  296. getattr(platform, 'cc','Unknown'),
  297. getattr(platform, 'version','Unknown'),
  298. ))
  299. if new_platform == 'host':
  300. global host
  301. host = platform
  302. def is_host_build():
  303. return host == platform