PageRenderTime 59ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/rpython/translator/platform/windows.py

https://bitbucket.org/kkris/pypy
Python | 439 lines | 434 code | 4 blank | 1 comment | 1 complexity | 35f1d9b5a35c6db2a58d698befec565d MD5 | raw file
  1. """Support for Windows."""
  2. import py, os, sys, re
  3. from rpython.translator.platform import CompilationError
  4. from rpython.translator.platform import log, _run_subprocess
  5. from rpython.translator.platform import Platform, posix
  6. import rpython
  7. rpydir = str(py.path.local(rpython.__file__).join('..'))
  8. def _get_compiler_type(cc, x64_flag):
  9. import subprocess
  10. if not cc:
  11. cc = os.environ.get('CC','')
  12. if not cc:
  13. return MsvcPlatform(cc=cc, x64=x64_flag)
  14. elif cc.startswith('mingw') or cc == 'gcc':
  15. return MingwPlatform(cc)
  16. try:
  17. subprocess.check_output([cc, '--version'])
  18. except:
  19. raise ValueError,"Could not find compiler specified by cc option" + \
  20. " '%s', it must be a valid exe file on your path"%cc
  21. return MingwPlatform(cc)
  22. def Windows(cc=None):
  23. return _get_compiler_type(cc, False)
  24. def Windows_x64(cc=None):
  25. return _get_compiler_type(cc, True)
  26. def _get_msvc_env(vsver, x64flag):
  27. try:
  28. toolsdir = os.environ['VS%sCOMNTOOLS' % vsver]
  29. except KeyError:
  30. return None
  31. if x64flag:
  32. vsinstalldir = os.path.abspath(os.path.join(toolsdir, '..', '..'))
  33. vcinstalldir = os.path.join(vsinstalldir, 'VC')
  34. vcbindir = os.path.join(vcinstalldir, 'BIN')
  35. vcvars = os.path.join(vcbindir, 'amd64', 'vcvarsamd64.bat')
  36. else:
  37. vcvars = os.path.join(toolsdir, 'vsvars32.bat')
  38. import subprocess
  39. try:
  40. popen = subprocess.Popen('"%s" & set' % (vcvars,),
  41. stdout=subprocess.PIPE,
  42. stderr=subprocess.PIPE)
  43. stdout, stderr = popen.communicate()
  44. if popen.wait() != 0:
  45. return None
  46. except:
  47. return None
  48. env = {}
  49. stdout = stdout.replace("\r\n", "\n")
  50. for line in stdout.split("\n"):
  51. if '=' not in line:
  52. continue
  53. key, value = line.split('=', 1)
  54. if key.upper() in ['PATH', 'INCLUDE', 'LIB']:
  55. env[key.upper()] = value
  56. ## log.msg("Updated environment with %s" % (vcvars,))
  57. return env
  58. def find_msvc_env(x64flag=False):
  59. # First, try to get the compiler which served to compile python
  60. msc_pos = sys.version.find('MSC v.')
  61. if msc_pos != -1:
  62. msc_ver = int(sys.version[msc_pos+6:msc_pos+10])
  63. # 1300 -> 70, 1310 -> 71, 1400 -> 80, 1500 -> 90
  64. vsver = (msc_ver / 10) - 60
  65. env = _get_msvc_env(vsver, x64flag)
  66. if env is not None:
  67. return env
  68. # Then, try any other version
  69. for vsver in (100, 90, 80, 71, 70): # All the versions I know
  70. env = _get_msvc_env(vsver, x64flag)
  71. if env is not None:
  72. return env
  73. log.error("Could not find a Microsoft Compiler")
  74. # Assume that the compiler is already part of the environment
  75. class MsvcPlatform(Platform):
  76. name = "msvc"
  77. so_ext = 'dll'
  78. exe_ext = 'exe'
  79. relevant_environ = ('PATH', 'INCLUDE', 'LIB')
  80. cc = 'cl.exe'
  81. link = 'link.exe'
  82. cflags = ('/MD', '/O2')
  83. link_flags = ()
  84. standalone_only = ()
  85. shared_only = ()
  86. environ = None
  87. def __init__(self, cc=None, x64=False):
  88. self.x64 = x64
  89. msvc_compiler_environ = find_msvc_env(x64)
  90. Platform.__init__(self, 'cl.exe')
  91. if msvc_compiler_environ:
  92. self.c_environ = os.environ.copy()
  93. self.c_environ.update(msvc_compiler_environ)
  94. # XXX passing an environment to subprocess is not enough. Why?
  95. os.environ.update(msvc_compiler_environ)
  96. # detect version of current compiler
  97. returncode, stdout, stderr = _run_subprocess(self.cc, '',
  98. env=self.c_environ)
  99. r = re.match(r'Microsoft.+C/C\+\+.+\s([0-9]+)\.([0-9]+).*', stderr)
  100. if r is not None:
  101. self.version = int(''.join(r.groups())) / 10 - 60
  102. else:
  103. # Probably not a msvc compiler...
  104. self.version = 0
  105. # Try to find a masm assembler
  106. returncode, stdout, stderr = _run_subprocess('ml.exe', '',
  107. env=self.c_environ)
  108. r = re.search('Macro Assembler', stderr)
  109. if r is None and os.path.exists('c:/masm32/bin/ml.exe'):
  110. masm32 = 'c:/masm32/bin/ml.exe'
  111. masm64 = 'c:/masm64/bin/ml64.exe'
  112. else:
  113. masm32 = 'ml.exe'
  114. masm64 = 'ml64.exe'
  115. if x64:
  116. self.masm = masm64
  117. else:
  118. self.masm = masm32
  119. # Install debug options only when interpreter is in debug mode
  120. if sys.executable.lower().endswith('_d.exe'):
  121. self.cflags = ['/MDd', '/Z7', '/Od']
  122. self.link_flags = ['/debug']
  123. # Increase stack size, for the linker and the stack check code.
  124. stack_size = 8 << 20 # 8 Mb
  125. self.link_flags.append('/STACK:%d' % stack_size)
  126. # The following symbol is used in c/src/stack.h
  127. self.cflags.append('/DMAX_STACK_SIZE=%d' % (stack_size - 1024))
  128. def _includedirs(self, include_dirs):
  129. return ['/I%s' % (idir,) for idir in include_dirs]
  130. def _libs(self, libraries):
  131. libs = []
  132. for lib in libraries:
  133. lib = str(lib)
  134. if lib.endswith('.dll'):
  135. lib = lib[:-4]
  136. libs.append('%s.lib' % (lib,))
  137. return libs
  138. def _libdirs(self, library_dirs):
  139. return ['/LIBPATH:%s' % (ldir,) for ldir in library_dirs]
  140. def _linkfiles(self, link_files):
  141. return list(link_files)
  142. def _args_for_shared(self, args):
  143. return ['/dll'] + args
  144. def check___thread(self):
  145. # __declspec(thread) does not seem to work when using assembler.
  146. # Returning False will cause the program to use TlsAlloc functions.
  147. # see src/thread_nt.h
  148. return False
  149. def _link_args_from_eci(self, eci, standalone):
  150. # Windows needs to resolve all symbols even for DLLs
  151. return super(MsvcPlatform, self)._link_args_from_eci(eci, standalone=True)
  152. def _exportsymbols_link_flags(self, eci, relto=None):
  153. if not eci.export_symbols:
  154. return []
  155. response_file = self._make_response_file("exported_symbols_")
  156. f = response_file.open("w")
  157. for sym in eci.export_symbols:
  158. f.write("/EXPORT:%s\n" % (sym,))
  159. f.close()
  160. if relto:
  161. response_file = relto.bestrelpath(response_file)
  162. return ["@%s" % (response_file,)]
  163. def _compile_c_file(self, cc, cfile, compile_args):
  164. oname = cfile.new(ext='obj')
  165. # notabene: (tismer)
  166. # This function may be called for .c but also .asm files.
  167. # The c compiler accepts any order of arguments, while
  168. # the assembler still has the old behavior that all options
  169. # must come first, and after the file name all options are ignored.
  170. # So please be careful with the order of parameters! ;-)
  171. args = ['/nologo', '/c'] + compile_args + ['/Fo%s' % (oname,), str(cfile)]
  172. self._execute_c_compiler(cc, args, oname)
  173. return oname
  174. def _link(self, cc, ofiles, link_args, standalone, exe_name):
  175. args = ['/nologo'] + [str(ofile) for ofile in ofiles] + link_args
  176. args += ['/out:%s' % (exe_name,), '/incremental:no']
  177. if not standalone:
  178. args = self._args_for_shared(args)
  179. if self.version >= 80:
  180. # Tell the linker to generate a manifest file
  181. temp_manifest = exe_name.dirpath().join(
  182. exe_name.purebasename + '.manifest')
  183. args += ["/MANIFEST", "/MANIFESTFILE:%s" % (temp_manifest,)]
  184. self._execute_c_compiler(self.link, args, exe_name)
  185. if self.version >= 80:
  186. # Now, embed the manifest into the program
  187. if standalone:
  188. mfid = 1
  189. else:
  190. mfid = 2
  191. out_arg = '-outputresource:%s;%s' % (exe_name, mfid)
  192. args = ['-nologo', '-manifest', str(temp_manifest), out_arg]
  193. self._execute_c_compiler('mt.exe', args, exe_name)
  194. return exe_name
  195. def _handle_error(self, returncode, stdout, stderr, outname):
  196. if returncode != 0:
  197. # Microsoft compilers write compilation errors to stdout
  198. stderr = stdout + stderr
  199. errorfile = outname.new(ext='errors')
  200. errorfile.write(stderr, mode='wb')
  201. stderrlines = stderr.splitlines()
  202. for line in stderrlines:
  203. log.ERROR(line)
  204. raise CompilationError(stdout, stderr)
  205. def gen_makefile(self, cfiles, eci, exe_name=None, path=None,
  206. shared=False):
  207. cfiles = [py.path.local(f) for f in cfiles]
  208. cfiles += [py.path.local(f) for f in eci.separate_module_files]
  209. if path is None:
  210. path = cfiles[0].dirpath()
  211. rpypath = py.path.local(rpydir)
  212. if exe_name is None:
  213. exe_name = cfiles[0].new(ext=self.exe_ext)
  214. else:
  215. exe_name = exe_name.new(ext=self.exe_ext)
  216. m = NMakefile(path)
  217. m.exe_name = exe_name
  218. m.eci = eci
  219. linkflags = list(self.link_flags)
  220. if shared:
  221. linkflags = self._args_for_shared(linkflags) + [
  222. '/EXPORT:$(PYPY_MAIN_FUNCTION)']
  223. linkflags += self._exportsymbols_link_flags(eci, relto=path)
  224. # Make sure different functions end up at different addresses!
  225. # This is required for the JIT.
  226. linkflags.append('/opt:noicf')
  227. if shared:
  228. so_name = exe_name.new(purebasename='lib' + exe_name.purebasename,
  229. ext=self.so_ext)
  230. target_name = so_name.basename
  231. else:
  232. target_name = exe_name.basename
  233. def rpyrel(fpath):
  234. rel = py.path.local(fpath).relto(rpypath)
  235. if rel:
  236. return os.path.join('$(RPYDIR)', rel)
  237. else:
  238. return fpath
  239. rel_cfiles = [m.pathrel(cfile) for cfile in cfiles]
  240. rel_ofiles = [rel_cfile[:rel_cfile.rfind('.')]+'.obj' for rel_cfile in rel_cfiles]
  241. m.cfiles = rel_cfiles
  242. rel_includedirs = [rpyrel(incldir) for incldir in eci.include_dirs]
  243. m.comment('automatically generated makefile')
  244. definitions = [
  245. ('RPYDIR', rpydir),
  246. ('TARGET', target_name),
  247. ('DEFAULT_TARGET', exe_name.basename),
  248. ('SOURCES', rel_cfiles),
  249. ('OBJECTS', rel_ofiles),
  250. ('LIBS', self._libs(eci.libraries)),
  251. ('LIBDIRS', self._libdirs(eci.library_dirs)),
  252. ('INCLUDEDIRS', self._includedirs(rel_includedirs)),
  253. ('CFLAGS', self.cflags),
  254. ('CFLAGSEXTRA', list(eci.compile_extra)),
  255. ('LDFLAGS', linkflags),
  256. ('LDFLAGSEXTRA', list(eci.link_extra)),
  257. ('CC', self.cc),
  258. ('CC_LINK', self.link),
  259. ('LINKFILES', eci.link_files),
  260. ('MASM', self.masm),
  261. ('_WIN32', '1'),
  262. ]
  263. if self.x64:
  264. definitions.append(('_WIN64', '1'))
  265. for args in definitions:
  266. m.definition(*args)
  267. rules = [
  268. ('all', '$(DEFAULT_TARGET)', []),
  269. ('.c.obj', '', '$(CC) /nologo $(CFLAGS) $(CFLAGSEXTRA) /Fo$@ /c $< $(INCLUDEDIRS)'),
  270. ('.asm.obj', '', '$(MASM) /nologo /Fo$@ /c $< $(INCLUDEDIRS)'),
  271. ]
  272. for rule in rules:
  273. m.rule(*rule)
  274. objects = ' $(OBJECTS)'
  275. create_obj_response_file = []
  276. if len(' '.join(rel_ofiles)) > 4000:
  277. # cmd.exe has a limit of ~4000 characters before a command line is too long.
  278. # Use a response file instead, at the cost of making the Makefile very ugly.
  279. for i in range(len(rel_ofiles) - 1):
  280. create_obj_response_file.append('echo %s >> obj_names.rsp' % \
  281. rel_ofiles[i])
  282. # use cmd /c for the last one so that the file is flushed
  283. create_obj_response_file.append('cmd /c echo %s >> obj_names.rsp' % \
  284. rel_ofiles[-1])
  285. objects = ' @obj_names.rsp'
  286. if self.version < 80:
  287. m.rule('$(TARGET)', '$(OBJECTS)',
  288. create_obj_response_file + [\
  289. '$(CC_LINK) /nologo $(LDFLAGS) $(LDFLAGSEXTRA)' + objects + ' /out:$@ $(LIBDIRS) $(LIBS)',
  290. ])
  291. else:
  292. m.rule('$(TARGET)', '$(OBJECTS)',
  293. create_obj_response_file + [\
  294. '$(CC_LINK) /nologo $(LDFLAGS) $(LDFLAGSEXTRA)' + objects + ' $(LINKFILES) /out:$@ $(LIBDIRS) $(LIBS) /MANIFEST /MANIFESTFILE:$*.manifest',
  295. 'mt.exe -nologo -manifest $*.manifest -outputresource:$@;1',
  296. ])
  297. m.rule('debugmode_$(TARGET)', '$(OBJECTS)',
  298. create_obj_response_file + [\
  299. '$(CC_LINK) /nologo /DEBUG $(LDFLAGS) $(LDFLAGSEXTRA)' + objects + ' $(LINKFILES) /out:$@ $(LIBDIRS) $(LIBS)',
  300. ])
  301. if shared:
  302. m.definition('SHARED_IMPORT_LIB', so_name.new(ext='lib').basename)
  303. m.definition('PYPY_MAIN_FUNCTION', "pypy_main_startup")
  304. m.rule('main.c', '',
  305. 'echo '
  306. 'int $(PYPY_MAIN_FUNCTION)(int, char*[]); '
  307. 'int main(int argc, char* argv[]) '
  308. '{ return $(PYPY_MAIN_FUNCTION)(argc, argv); } > $@')
  309. m.rule('$(DEFAULT_TARGET)', ['$(TARGET)', 'main.obj'],
  310. ['$(CC_LINK) /nologo main.obj $(SHARED_IMPORT_LIB) /out:$@ /MANIFEST /MANIFESTFILE:$*.manifest',
  311. 'mt.exe -nologo -manifest $*.manifest -outputresource:$@;1',
  312. ])
  313. m.rule('debugmode_$(DEFAULT_TARGET)', ['debugmode_$(TARGET)', 'main.obj'],
  314. ['$(CC_LINK) /nologo /DEBUG main.obj $(SHARED_IMPORT_LIB) /out:$@'
  315. ])
  316. return m
  317. def execute_makefile(self, path_to_makefile, extra_opts=[]):
  318. if isinstance(path_to_makefile, NMakefile):
  319. path = path_to_makefile.makefile_dir
  320. else:
  321. path = path_to_makefile
  322. log.execute('make %s in %s' % (" ".join(extra_opts), path))
  323. oldcwd = path.chdir()
  324. try:
  325. returncode, stdout, stderr = _run_subprocess(
  326. 'nmake',
  327. ['/nologo', '/f', str(path.join('Makefile'))] + extra_opts)
  328. finally:
  329. oldcwd.chdir()
  330. self._handle_error(returncode, stdout, stderr, path.join('make'))
  331. class NMakefile(posix.GnuMakefile):
  332. def write(self, out=None):
  333. # nmake expands macros when it parses rules.
  334. # Write all macros before the rules.
  335. if out is None:
  336. f = self.makefile_dir.join('Makefile').open('w')
  337. else:
  338. f = out
  339. for line in self.lines:
  340. if not isinstance(line, posix.Rule):
  341. line.write(f)
  342. for line in self.lines:
  343. if isinstance(line, posix.Rule):
  344. line.write(f)
  345. f.flush()
  346. if out is None:
  347. f.close()
  348. class MingwPlatform(posix.BasePosix):
  349. name = 'mingw32'
  350. standalone_only = ()
  351. shared_only = ()
  352. cflags = ('-O3',)
  353. link_flags = ()
  354. exe_ext = 'exe'
  355. so_ext = 'dll'
  356. def __init__(self, cc=None):
  357. if not cc:
  358. cc = 'gcc'
  359. Platform.__init__(self, cc)
  360. def _args_for_shared(self, args):
  361. return ['-shared'] + args
  362. def _include_dirs_for_libffi(self):
  363. return []
  364. def _library_dirs_for_libffi(self):
  365. return []
  366. def _handle_error(self, returncode, stdout, stderr, outname):
  367. # Mingw tools write compilation errors to stdout
  368. super(MingwPlatform, self)._handle_error(
  369. returncode, '', stderr + stdout, outname)