PageRenderTime 54ms CodeModel.GetById 28ms RepoModel.GetById 1ms app.codeStats 0ms

/pypy/translator/platform/windows.py

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