]>
Commit | Line | Data |
---|---|---|
1 | """distutils.msvc9compiler\r | |
2 | \r | |
3 | Contains MSVCCompiler, an implementation of the abstract CCompiler class\r | |
4 | for the Microsoft Visual Studio 2008.\r | |
5 | \r | |
6 | The module is compatible with VS 2005 and VS 2008. You can find legacy support\r | |
7 | for older versions of VS in distutils.msvccompiler.\r | |
8 | """\r | |
9 | \r | |
10 | # Written by Perry Stoll\r | |
11 | # hacked by Robin Becker and Thomas Heller to do a better job of\r | |
12 | # finding DevStudio (through the registry)\r | |
13 | # ported to VS2005 and VS 2008 by Christian Heimes\r | |
14 | \r | |
15 | __revision__ = "$Id$"\r | |
16 | \r | |
17 | import os\r | |
18 | import subprocess\r | |
19 | import sys\r | |
20 | import re\r | |
21 | \r | |
22 | from distutils.errors import (DistutilsExecError, DistutilsPlatformError,\r | |
23 | CompileError, LibError, LinkError)\r | |
24 | from distutils.ccompiler import CCompiler, gen_lib_options\r | |
25 | from distutils import log\r | |
26 | from distutils.util import get_platform\r | |
27 | \r | |
28 | import _winreg\r | |
29 | \r | |
30 | RegOpenKeyEx = _winreg.OpenKeyEx\r | |
31 | RegEnumKey = _winreg.EnumKey\r | |
32 | RegEnumValue = _winreg.EnumValue\r | |
33 | RegError = _winreg.error\r | |
34 | \r | |
35 | HKEYS = (_winreg.HKEY_USERS,\r | |
36 | _winreg.HKEY_CURRENT_USER,\r | |
37 | _winreg.HKEY_LOCAL_MACHINE,\r | |
38 | _winreg.HKEY_CLASSES_ROOT)\r | |
39 | \r | |
40 | NATIVE_WIN64 = (sys.platform == 'win32' and sys.maxsize > 2**32)\r | |
41 | if NATIVE_WIN64:\r | |
42 | # Visual C++ is a 32-bit application, so we need to look in\r | |
43 | # the corresponding registry branch, if we're running a\r | |
44 | # 64-bit Python on Win64\r | |
45 | VS_BASE = r"Software\Wow6432Node\Microsoft\VisualStudio\%0.1f"\r | |
46 | VSEXPRESS_BASE = r"Software\Wow6432Node\Microsoft\VCExpress\%0.1f"\r | |
47 | WINSDK_BASE = r"Software\Wow6432Node\Microsoft\Microsoft SDKs\Windows"\r | |
48 | NET_BASE = r"Software\Wow6432Node\Microsoft\.NETFramework"\r | |
49 | else:\r | |
50 | VS_BASE = r"Software\Microsoft\VisualStudio\%0.1f"\r | |
51 | VSEXPRESS_BASE = r"Software\Microsoft\VCExpress\%0.1f"\r | |
52 | WINSDK_BASE = r"Software\Microsoft\Microsoft SDKs\Windows"\r | |
53 | NET_BASE = r"Software\Microsoft\.NETFramework"\r | |
54 | \r | |
55 | # A map keyed by get_platform() return values to values accepted by\r | |
56 | # 'vcvarsall.bat'. Note a cross-compile may combine these (eg, 'x86_amd64' is\r | |
57 | # the param to cross-compile on x86 targetting amd64.)\r | |
58 | PLAT_TO_VCVARS = {\r | |
59 | 'win32' : 'x86',\r | |
60 | 'win-amd64' : 'amd64',\r | |
61 | 'win-ia64' : 'ia64',\r | |
62 | }\r | |
63 | \r | |
64 | class Reg:\r | |
65 | """Helper class to read values from the registry\r | |
66 | """\r | |
67 | \r | |
68 | def get_value(cls, path, key):\r | |
69 | for base in HKEYS:\r | |
70 | d = cls.read_values(base, path)\r | |
71 | if d and key in d:\r | |
72 | return d[key]\r | |
73 | raise KeyError(key)\r | |
74 | get_value = classmethod(get_value)\r | |
75 | \r | |
76 | def read_keys(cls, base, key):\r | |
77 | """Return list of registry keys."""\r | |
78 | try:\r | |
79 | handle = RegOpenKeyEx(base, key)\r | |
80 | except RegError:\r | |
81 | return None\r | |
82 | L = []\r | |
83 | i = 0\r | |
84 | while True:\r | |
85 | try:\r | |
86 | k = RegEnumKey(handle, i)\r | |
87 | except RegError:\r | |
88 | break\r | |
89 | L.append(k)\r | |
90 | i += 1\r | |
91 | return L\r | |
92 | read_keys = classmethod(read_keys)\r | |
93 | \r | |
94 | def read_values(cls, base, key):\r | |
95 | """Return dict of registry keys and values.\r | |
96 | \r | |
97 | All names are converted to lowercase.\r | |
98 | """\r | |
99 | try:\r | |
100 | handle = RegOpenKeyEx(base, key)\r | |
101 | except RegError:\r | |
102 | return None\r | |
103 | d = {}\r | |
104 | i = 0\r | |
105 | while True:\r | |
106 | try:\r | |
107 | name, value, type = RegEnumValue(handle, i)\r | |
108 | except RegError:\r | |
109 | break\r | |
110 | name = name.lower()\r | |
111 | d[cls.convert_mbcs(name)] = cls.convert_mbcs(value)\r | |
112 | i += 1\r | |
113 | return d\r | |
114 | read_values = classmethod(read_values)\r | |
115 | \r | |
116 | def convert_mbcs(s):\r | |
117 | dec = getattr(s, "decode", None)\r | |
118 | if dec is not None:\r | |
119 | try:\r | |
120 | s = dec("mbcs")\r | |
121 | except UnicodeError:\r | |
122 | pass\r | |
123 | return s\r | |
124 | convert_mbcs = staticmethod(convert_mbcs)\r | |
125 | \r | |
126 | class MacroExpander:\r | |
127 | \r | |
128 | def __init__(self, version):\r | |
129 | self.macros = {}\r | |
130 | self.vsbase = VS_BASE % version\r | |
131 | self.load_macros(version)\r | |
132 | \r | |
133 | def set_macro(self, macro, path, key):\r | |
134 | self.macros["$(%s)" % macro] = Reg.get_value(path, key)\r | |
135 | \r | |
136 | def load_macros(self, version):\r | |
137 | self.set_macro("VCInstallDir", self.vsbase + r"\Setup\VC", "productdir")\r | |
138 | self.set_macro("VSInstallDir", self.vsbase + r"\Setup\VS", "productdir")\r | |
139 | self.set_macro("FrameworkDir", NET_BASE, "installroot")\r | |
140 | try:\r | |
141 | if version >= 8.0:\r | |
142 | self.set_macro("FrameworkSDKDir", NET_BASE,\r | |
143 | "sdkinstallrootv2.0")\r | |
144 | else:\r | |
145 | raise KeyError("sdkinstallrootv2.0")\r | |
146 | except KeyError:\r | |
147 | raise DistutilsPlatformError(\r | |
148 | """Python was built with Visual Studio 2008;\r | |
149 | extensions must be built with a compiler than can generate compatible binaries.\r | |
150 | Visual Studio 2008 was not found on this system. If you have Cygwin installed,\r | |
151 | you can try compiling with MingW32, by passing "-c mingw32" to setup.py.""")\r | |
152 | \r | |
153 | if version >= 9.0:\r | |
154 | self.set_macro("FrameworkVersion", self.vsbase, "clr version")\r | |
155 | self.set_macro("WindowsSdkDir", WINSDK_BASE, "currentinstallfolder")\r | |
156 | else:\r | |
157 | p = r"Software\Microsoft\NET Framework Setup\Product"\r | |
158 | for base in HKEYS:\r | |
159 | try:\r | |
160 | h = RegOpenKeyEx(base, p)\r | |
161 | except RegError:\r | |
162 | continue\r | |
163 | key = RegEnumKey(h, 0)\r | |
164 | d = Reg.get_value(base, r"%s\%s" % (p, key))\r | |
165 | self.macros["$(FrameworkVersion)"] = d["version"]\r | |
166 | \r | |
167 | def sub(self, s):\r | |
168 | for k, v in self.macros.items():\r | |
169 | s = s.replace(k, v)\r | |
170 | return s\r | |
171 | \r | |
172 | def get_build_version():\r | |
173 | """Return the version of MSVC that was used to build Python.\r | |
174 | \r | |
175 | For Python 2.3 and up, the version number is included in\r | |
176 | sys.version. For earlier versions, assume the compiler is MSVC 6.\r | |
177 | """\r | |
178 | prefix = "MSC v."\r | |
179 | i = sys.version.find(prefix)\r | |
180 | if i == -1:\r | |
181 | return 6\r | |
182 | i = i + len(prefix)\r | |
183 | s, rest = sys.version[i:].split(" ", 1)\r | |
184 | majorVersion = int(s[:-2]) - 6\r | |
185 | minorVersion = int(s[2:3]) / 10.0\r | |
186 | # I don't think paths are affected by minor version in version 6\r | |
187 | if majorVersion == 6:\r | |
188 | minorVersion = 0\r | |
189 | if majorVersion >= 6:\r | |
190 | return majorVersion + minorVersion\r | |
191 | # else we don't know what version of the compiler this is\r | |
192 | return None\r | |
193 | \r | |
194 | def normalize_and_reduce_paths(paths):\r | |
195 | """Return a list of normalized paths with duplicates removed.\r | |
196 | \r | |
197 | The current order of paths is maintained.\r | |
198 | """\r | |
199 | # Paths are normalized so things like: /a and /a/ aren't both preserved.\r | |
200 | reduced_paths = []\r | |
201 | for p in paths:\r | |
202 | np = os.path.normpath(p)\r | |
203 | # XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set.\r | |
204 | if np not in reduced_paths:\r | |
205 | reduced_paths.append(np)\r | |
206 | return reduced_paths\r | |
207 | \r | |
208 | def removeDuplicates(variable):\r | |
209 | """Remove duplicate values of an environment variable.\r | |
210 | """\r | |
211 | oldList = variable.split(os.pathsep)\r | |
212 | newList = []\r | |
213 | for i in oldList:\r | |
214 | if i not in newList:\r | |
215 | newList.append(i)\r | |
216 | newVariable = os.pathsep.join(newList)\r | |
217 | return newVariable\r | |
218 | \r | |
219 | def find_vcvarsall(version):\r | |
220 | """Find the vcvarsall.bat file\r | |
221 | \r | |
222 | At first it tries to find the productdir of VS 2008 in the registry. If\r | |
223 | that fails it falls back to the VS90COMNTOOLS env var.\r | |
224 | """\r | |
225 | vsbase = VS_BASE % version\r | |
226 | try:\r | |
227 | productdir = Reg.get_value(r"%s\Setup\VC" % vsbase,\r | |
228 | "productdir")\r | |
229 | except KeyError:\r | |
230 | productdir = None\r | |
231 | \r | |
232 | # trying Express edition\r | |
233 | if productdir is None:\r | |
234 | vsbase = VSEXPRESS_BASE % version\r | |
235 | try:\r | |
236 | productdir = Reg.get_value(r"%s\Setup\VC" % vsbase,\r | |
237 | "productdir")\r | |
238 | except KeyError:\r | |
239 | productdir = None\r | |
240 | log.debug("Unable to find productdir in registry")\r | |
241 | \r | |
242 | if not productdir or not os.path.isdir(productdir):\r | |
243 | toolskey = "VS%0.f0COMNTOOLS" % version\r | |
244 | toolsdir = os.environ.get(toolskey, None)\r | |
245 | \r | |
246 | if toolsdir and os.path.isdir(toolsdir):\r | |
247 | productdir = os.path.join(toolsdir, os.pardir, os.pardir, "VC")\r | |
248 | productdir = os.path.abspath(productdir)\r | |
249 | if not os.path.isdir(productdir):\r | |
250 | log.debug("%s is not a valid directory" % productdir)\r | |
251 | return None\r | |
252 | else:\r | |
253 | log.debug("Env var %s is not set or invalid" % toolskey)\r | |
254 | if not productdir:\r | |
255 | log.debug("No productdir found")\r | |
256 | return None\r | |
257 | vcvarsall = os.path.join(productdir, "vcvarsall.bat")\r | |
258 | if os.path.isfile(vcvarsall):\r | |
259 | return vcvarsall\r | |
260 | log.debug("Unable to find vcvarsall.bat")\r | |
261 | return None\r | |
262 | \r | |
263 | def query_vcvarsall(version, arch="x86"):\r | |
264 | """Launch vcvarsall.bat and read the settings from its environment\r | |
265 | """\r | |
266 | vcvarsall = find_vcvarsall(version)\r | |
267 | interesting = set(("include", "lib", "libpath", "path"))\r | |
268 | result = {}\r | |
269 | \r | |
270 | if vcvarsall is None:\r | |
271 | raise DistutilsPlatformError("Unable to find vcvarsall.bat")\r | |
272 | log.debug("Calling 'vcvarsall.bat %s' (version=%s)", arch, version)\r | |
273 | popen = subprocess.Popen('"%s" %s & set' % (vcvarsall, arch),\r | |
274 | stdout=subprocess.PIPE,\r | |
275 | stderr=subprocess.PIPE)\r | |
276 | try:\r | |
277 | stdout, stderr = popen.communicate()\r | |
278 | if popen.wait() != 0:\r | |
279 | raise DistutilsPlatformError(stderr.decode("mbcs"))\r | |
280 | \r | |
281 | stdout = stdout.decode("mbcs")\r | |
282 | for line in stdout.split("\n"):\r | |
283 | line = Reg.convert_mbcs(line)\r | |
284 | if '=' not in line:\r | |
285 | continue\r | |
286 | line = line.strip()\r | |
287 | key, value = line.split('=', 1)\r | |
288 | key = key.lower()\r | |
289 | if key in interesting:\r | |
290 | if value.endswith(os.pathsep):\r | |
291 | value = value[:-1]\r | |
292 | result[key] = removeDuplicates(value)\r | |
293 | \r | |
294 | finally:\r | |
295 | popen.stdout.close()\r | |
296 | popen.stderr.close()\r | |
297 | \r | |
298 | if len(result) != len(interesting):\r | |
299 | raise ValueError(str(list(result.keys())))\r | |
300 | \r | |
301 | return result\r | |
302 | \r | |
303 | # More globals\r | |
304 | VERSION = get_build_version()\r | |
305 | if VERSION < 8.0:\r | |
306 | raise DistutilsPlatformError("VC %0.1f is not supported by this module" % VERSION)\r | |
307 | # MACROS = MacroExpander(VERSION)\r | |
308 | \r | |
309 | class MSVCCompiler(CCompiler) :\r | |
310 | """Concrete class that implements an interface to Microsoft Visual C++,\r | |
311 | as defined by the CCompiler abstract class."""\r | |
312 | \r | |
313 | compiler_type = 'msvc'\r | |
314 | \r | |
315 | # Just set this so CCompiler's constructor doesn't barf. We currently\r | |
316 | # don't use the 'set_executables()' bureaucracy provided by CCompiler,\r | |
317 | # as it really isn't necessary for this sort of single-compiler class.\r | |
318 | # Would be nice to have a consistent interface with UnixCCompiler,\r | |
319 | # though, so it's worth thinking about.\r | |
320 | executables = {}\r | |
321 | \r | |
322 | # Private class data (need to distinguish C from C++ source for compiler)\r | |
323 | _c_extensions = ['.c']\r | |
324 | _cpp_extensions = ['.cc', '.cpp', '.cxx']\r | |
325 | _rc_extensions = ['.rc']\r | |
326 | _mc_extensions = ['.mc']\r | |
327 | \r | |
328 | # Needed for the filename generation methods provided by the\r | |
329 | # base class, CCompiler.\r | |
330 | src_extensions = (_c_extensions + _cpp_extensions +\r | |
331 | _rc_extensions + _mc_extensions)\r | |
332 | res_extension = '.res'\r | |
333 | obj_extension = '.obj'\r | |
334 | static_lib_extension = '.lib'\r | |
335 | shared_lib_extension = '.dll'\r | |
336 | static_lib_format = shared_lib_format = '%s%s'\r | |
337 | exe_extension = '.exe'\r | |
338 | \r | |
339 | def __init__(self, verbose=0, dry_run=0, force=0):\r | |
340 | CCompiler.__init__ (self, verbose, dry_run, force)\r | |
341 | self.__version = VERSION\r | |
342 | self.__root = r"Software\Microsoft\VisualStudio"\r | |
343 | # self.__macros = MACROS\r | |
344 | self.__paths = []\r | |
345 | # target platform (.plat_name is consistent with 'bdist')\r | |
346 | self.plat_name = None\r | |
347 | self.__arch = None # deprecated name\r | |
348 | self.initialized = False\r | |
349 | \r | |
350 | def initialize(self, plat_name=None):\r | |
351 | # multi-init means we would need to check platform same each time...\r | |
352 | assert not self.initialized, "don't init multiple times"\r | |
353 | if plat_name is None:\r | |
354 | plat_name = get_platform()\r | |
355 | # sanity check for platforms to prevent obscure errors later.\r | |
356 | ok_plats = 'win32', 'win-amd64', 'win-ia64'\r | |
357 | if plat_name not in ok_plats:\r | |
358 | raise DistutilsPlatformError("--plat-name must be one of %s" %\r | |
359 | (ok_plats,))\r | |
360 | \r | |
361 | if "DISTUTILS_USE_SDK" in os.environ and "MSSdk" in os.environ and self.find_exe("cl.exe"):\r | |
362 | # Assume that the SDK set up everything alright; don't try to be\r | |
363 | # smarter\r | |
364 | self.cc = "cl.exe"\r | |
365 | self.linker = "link.exe"\r | |
366 | self.lib = "lib.exe"\r | |
367 | self.rc = "rc.exe"\r | |
368 | self.mc = "mc.exe"\r | |
369 | else:\r | |
370 | # On x86, 'vcvars32.bat amd64' creates an env that doesn't work;\r | |
371 | # to cross compile, you use 'x86_amd64'.\r | |
372 | # On AMD64, 'vcvars32.bat amd64' is a native build env; to cross\r | |
373 | # compile use 'x86' (ie, it runs the x86 compiler directly)\r | |
374 | # No idea how itanium handles this, if at all.\r | |
375 | if plat_name == get_platform() or plat_name == 'win32':\r | |
376 | # native build or cross-compile to win32\r | |
377 | plat_spec = PLAT_TO_VCVARS[plat_name]\r | |
378 | else:\r | |
379 | # cross compile from win32 -> some 64bit\r | |
380 | plat_spec = PLAT_TO_VCVARS[get_platform()] + '_' + \\r | |
381 | PLAT_TO_VCVARS[plat_name]\r | |
382 | \r | |
383 | vc_env = query_vcvarsall(VERSION, plat_spec)\r | |
384 | \r | |
385 | # take care to only use strings in the environment.\r | |
386 | self.__paths = vc_env['path'].encode('mbcs').split(os.pathsep)\r | |
387 | os.environ['lib'] = vc_env['lib'].encode('mbcs')\r | |
388 | os.environ['include'] = vc_env['include'].encode('mbcs')\r | |
389 | \r | |
390 | if len(self.__paths) == 0:\r | |
391 | raise DistutilsPlatformError("Python was built with %s, "\r | |
392 | "and extensions need to be built with the same "\r | |
393 | "version of the compiler, but it isn't installed."\r | |
394 | % self.__product)\r | |
395 | \r | |
396 | self.cc = self.find_exe("cl.exe")\r | |
397 | self.linker = self.find_exe("link.exe")\r | |
398 | self.lib = self.find_exe("lib.exe")\r | |
399 | self.rc = self.find_exe("rc.exe") # resource compiler\r | |
400 | self.mc = self.find_exe("mc.exe") # message compiler\r | |
401 | #self.set_path_env_var('lib')\r | |
402 | #self.set_path_env_var('include')\r | |
403 | \r | |
404 | # extend the MSVC path with the current path\r | |
405 | try:\r | |
406 | for p in os.environ['path'].split(';'):\r | |
407 | self.__paths.append(p)\r | |
408 | except KeyError:\r | |
409 | pass\r | |
410 | self.__paths = normalize_and_reduce_paths(self.__paths)\r | |
411 | os.environ['path'] = ";".join(self.__paths)\r | |
412 | \r | |
413 | self.preprocess_options = None\r | |
414 | if self.__arch == "x86":\r | |
415 | self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3',\r | |
416 | '/DNDEBUG']\r | |
417 | self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3',\r | |
418 | '/Z7', '/D_DEBUG']\r | |
419 | else:\r | |
420 | # Win64\r | |
421 | self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GS-' ,\r | |
422 | '/DNDEBUG']\r | |
423 | self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GS-',\r | |
424 | '/Z7', '/D_DEBUG']\r | |
425 | \r | |
426 | self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO']\r | |
427 | if self.__version >= 7:\r | |
428 | self.ldflags_shared_debug = [\r | |
429 | '/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG', '/pdb:None'\r | |
430 | ]\r | |
431 | self.ldflags_static = [ '/nologo']\r | |
432 | \r | |
433 | self.initialized = True\r | |
434 | \r | |
435 | # -- Worker methods ------------------------------------------------\r | |
436 | \r | |
437 | def object_filenames(self,\r | |
438 | source_filenames,\r | |
439 | strip_dir=0,\r | |
440 | output_dir=''):\r | |
441 | # Copied from ccompiler.py, extended to return .res as 'object'-file\r | |
442 | # for .rc input file\r | |
443 | if output_dir is None: output_dir = ''\r | |
444 | obj_names = []\r | |
445 | for src_name in source_filenames:\r | |
446 | (base, ext) = os.path.splitext (src_name)\r | |
447 | base = os.path.splitdrive(base)[1] # Chop off the drive\r | |
448 | base = base[os.path.isabs(base):] # If abs, chop off leading /\r | |
449 | if ext not in self.src_extensions:\r | |
450 | # Better to raise an exception instead of silently continuing\r | |
451 | # and later complain about sources and targets having\r | |
452 | # different lengths\r | |
453 | raise CompileError ("Don't know how to compile %s" % src_name)\r | |
454 | if strip_dir:\r | |
455 | base = os.path.basename (base)\r | |
456 | if ext in self._rc_extensions:\r | |
457 | obj_names.append (os.path.join (output_dir,\r | |
458 | base + self.res_extension))\r | |
459 | elif ext in self._mc_extensions:\r | |
460 | obj_names.append (os.path.join (output_dir,\r | |
461 | base + self.res_extension))\r | |
462 | else:\r | |
463 | obj_names.append (os.path.join (output_dir,\r | |
464 | base + self.obj_extension))\r | |
465 | return obj_names\r | |
466 | \r | |
467 | \r | |
468 | def compile(self, sources,\r | |
469 | output_dir=None, macros=None, include_dirs=None, debug=0,\r | |
470 | extra_preargs=None, extra_postargs=None, depends=None):\r | |
471 | \r | |
472 | if not self.initialized:\r | |
473 | self.initialize()\r | |
474 | compile_info = self._setup_compile(output_dir, macros, include_dirs,\r | |
475 | sources, depends, extra_postargs)\r | |
476 | macros, objects, extra_postargs, pp_opts, build = compile_info\r | |
477 | \r | |
478 | compile_opts = extra_preargs or []\r | |
479 | compile_opts.append ('/c')\r | |
480 | if debug:\r | |
481 | compile_opts.extend(self.compile_options_debug)\r | |
482 | else:\r | |
483 | compile_opts.extend(self.compile_options)\r | |
484 | \r | |
485 | for obj in objects:\r | |
486 | try:\r | |
487 | src, ext = build[obj]\r | |
488 | except KeyError:\r | |
489 | continue\r | |
490 | if debug:\r | |
491 | # pass the full pathname to MSVC in debug mode,\r | |
492 | # this allows the debugger to find the source file\r | |
493 | # without asking the user to browse for it\r | |
494 | src = os.path.abspath(src)\r | |
495 | \r | |
496 | if ext in self._c_extensions:\r | |
497 | input_opt = "/Tc" + src\r | |
498 | elif ext in self._cpp_extensions:\r | |
499 | input_opt = "/Tp" + src\r | |
500 | elif ext in self._rc_extensions:\r | |
501 | # compile .RC to .RES file\r | |
502 | input_opt = src\r | |
503 | output_opt = "/fo" + obj\r | |
504 | try:\r | |
505 | self.spawn([self.rc] + pp_opts +\r | |
506 | [output_opt] + [input_opt])\r | |
507 | except DistutilsExecError, msg:\r | |
508 | raise CompileError(msg)\r | |
509 | continue\r | |
510 | elif ext in self._mc_extensions:\r | |
511 | # Compile .MC to .RC file to .RES file.\r | |
512 | # * '-h dir' specifies the directory for the\r | |
513 | # generated include file\r | |
514 | # * '-r dir' specifies the target directory of the\r | |
515 | # generated RC file and the binary message resource\r | |
516 | # it includes\r | |
517 | #\r | |
518 | # For now (since there are no options to change this),\r | |
519 | # we use the source-directory for the include file and\r | |
520 | # the build directory for the RC file and message\r | |
521 | # resources. This works at least for win32all.\r | |
522 | h_dir = os.path.dirname(src)\r | |
523 | rc_dir = os.path.dirname(obj)\r | |
524 | try:\r | |
525 | # first compile .MC to .RC and .H file\r | |
526 | self.spawn([self.mc] +\r | |
527 | ['-h', h_dir, '-r', rc_dir] + [src])\r | |
528 | base, _ = os.path.splitext (os.path.basename (src))\r | |
529 | rc_file = os.path.join (rc_dir, base + '.rc')\r | |
530 | # then compile .RC to .RES file\r | |
531 | self.spawn([self.rc] +\r | |
532 | ["/fo" + obj] + [rc_file])\r | |
533 | \r | |
534 | except DistutilsExecError, msg:\r | |
535 | raise CompileError(msg)\r | |
536 | continue\r | |
537 | else:\r | |
538 | # how to handle this file?\r | |
539 | raise CompileError("Don't know how to compile %s to %s"\r | |
540 | % (src, obj))\r | |
541 | \r | |
542 | output_opt = "/Fo" + obj\r | |
543 | try:\r | |
544 | self.spawn([self.cc] + compile_opts + pp_opts +\r | |
545 | [input_opt, output_opt] +\r | |
546 | extra_postargs)\r | |
547 | except DistutilsExecError, msg:\r | |
548 | raise CompileError(msg)\r | |
549 | \r | |
550 | return objects\r | |
551 | \r | |
552 | \r | |
553 | def create_static_lib(self,\r | |
554 | objects,\r | |
555 | output_libname,\r | |
556 | output_dir=None,\r | |
557 | debug=0,\r | |
558 | target_lang=None):\r | |
559 | \r | |
560 | if not self.initialized:\r | |
561 | self.initialize()\r | |
562 | (objects, output_dir) = self._fix_object_args(objects, output_dir)\r | |
563 | output_filename = self.library_filename(output_libname,\r | |
564 | output_dir=output_dir)\r | |
565 | \r | |
566 | if self._need_link(objects, output_filename):\r | |
567 | lib_args = objects + ['/OUT:' + output_filename]\r | |
568 | if debug:\r | |
569 | pass # XXX what goes here?\r | |
570 | try:\r | |
571 | self.spawn([self.lib] + lib_args)\r | |
572 | except DistutilsExecError, msg:\r | |
573 | raise LibError(msg)\r | |
574 | else:\r | |
575 | log.debug("skipping %s (up-to-date)", output_filename)\r | |
576 | \r | |
577 | \r | |
578 | def link(self,\r | |
579 | target_desc,\r | |
580 | objects,\r | |
581 | output_filename,\r | |
582 | output_dir=None,\r | |
583 | libraries=None,\r | |
584 | library_dirs=None,\r | |
585 | runtime_library_dirs=None,\r | |
586 | export_symbols=None,\r | |
587 | debug=0,\r | |
588 | extra_preargs=None,\r | |
589 | extra_postargs=None,\r | |
590 | build_temp=None,\r | |
591 | target_lang=None):\r | |
592 | \r | |
593 | if not self.initialized:\r | |
594 | self.initialize()\r | |
595 | (objects, output_dir) = self._fix_object_args(objects, output_dir)\r | |
596 | fixed_args = self._fix_lib_args(libraries, library_dirs,\r | |
597 | runtime_library_dirs)\r | |
598 | (libraries, library_dirs, runtime_library_dirs) = fixed_args\r | |
599 | \r | |
600 | if runtime_library_dirs:\r | |
601 | self.warn ("I don't know what to do with 'runtime_library_dirs': "\r | |
602 | + str (runtime_library_dirs))\r | |
603 | \r | |
604 | lib_opts = gen_lib_options(self,\r | |
605 | library_dirs, runtime_library_dirs,\r | |
606 | libraries)\r | |
607 | if output_dir is not None:\r | |
608 | output_filename = os.path.join(output_dir, output_filename)\r | |
609 | \r | |
610 | if self._need_link(objects, output_filename):\r | |
611 | if target_desc == CCompiler.EXECUTABLE:\r | |
612 | if debug:\r | |
613 | ldflags = self.ldflags_shared_debug[1:]\r | |
614 | else:\r | |
615 | ldflags = self.ldflags_shared[1:]\r | |
616 | else:\r | |
617 | if debug:\r | |
618 | ldflags = self.ldflags_shared_debug\r | |
619 | else:\r | |
620 | ldflags = self.ldflags_shared\r | |
621 | \r | |
622 | export_opts = []\r | |
623 | for sym in (export_symbols or []):\r | |
624 | export_opts.append("/EXPORT:" + sym)\r | |
625 | \r | |
626 | ld_args = (ldflags + lib_opts + export_opts +\r | |
627 | objects + ['/OUT:' + output_filename])\r | |
628 | \r | |
629 | # The MSVC linker generates .lib and .exp files, which cannot be\r | |
630 | # suppressed by any linker switches. The .lib files may even be\r | |
631 | # needed! Make sure they are generated in the temporary build\r | |
632 | # directory. Since they have different names for debug and release\r | |
633 | # builds, they can go into the same directory.\r | |
634 | build_temp = os.path.dirname(objects[0])\r | |
635 | if export_symbols is not None:\r | |
636 | (dll_name, dll_ext) = os.path.splitext(\r | |
637 | os.path.basename(output_filename))\r | |
638 | implib_file = os.path.join(\r | |
639 | build_temp,\r | |
640 | self.library_filename(dll_name))\r | |
641 | ld_args.append ('/IMPLIB:' + implib_file)\r | |
642 | \r | |
643 | # Embedded manifests are recommended - see MSDN article titled\r | |
644 | # "How to: Embed a Manifest Inside a C/C++ Application"\r | |
645 | # (currently at http://msdn2.microsoft.com/en-us/library/ms235591(VS.80).aspx)\r | |
646 | # Ask the linker to generate the manifest in the temp dir, so\r | |
647 | # we can embed it later.\r | |
648 | temp_manifest = os.path.join(\r | |
649 | build_temp,\r | |
650 | os.path.basename(output_filename) + ".manifest")\r | |
651 | ld_args.append('/MANIFESTFILE:' + temp_manifest)\r | |
652 | \r | |
653 | if extra_preargs:\r | |
654 | ld_args[:0] = extra_preargs\r | |
655 | if extra_postargs:\r | |
656 | ld_args.extend(extra_postargs)\r | |
657 | \r | |
658 | self.mkpath(os.path.dirname(output_filename))\r | |
659 | try:\r | |
660 | self.spawn([self.linker] + ld_args)\r | |
661 | except DistutilsExecError, msg:\r | |
662 | raise LinkError(msg)\r | |
663 | \r | |
664 | # embed the manifest\r | |
665 | # XXX - this is somewhat fragile - if mt.exe fails, distutils\r | |
666 | # will still consider the DLL up-to-date, but it will not have a\r | |
667 | # manifest. Maybe we should link to a temp file? OTOH, that\r | |
668 | # implies a build environment error that shouldn't go undetected.\r | |
669 | if target_desc == CCompiler.EXECUTABLE:\r | |
670 | mfid = 1\r | |
671 | else:\r | |
672 | mfid = 2\r | |
673 | self._remove_visual_c_ref(temp_manifest)\r | |
674 | out_arg = '-outputresource:%s;%s' % (output_filename, mfid)\r | |
675 | try:\r | |
676 | self.spawn(['mt.exe', '-nologo', '-manifest',\r | |
677 | temp_manifest, out_arg])\r | |
678 | except DistutilsExecError, msg:\r | |
679 | raise LinkError(msg)\r | |
680 | else:\r | |
681 | log.debug("skipping %s (up-to-date)", output_filename)\r | |
682 | \r | |
683 | def _remove_visual_c_ref(self, manifest_file):\r | |
684 | try:\r | |
685 | # Remove references to the Visual C runtime, so they will\r | |
686 | # fall through to the Visual C dependency of Python.exe.\r | |
687 | # This way, when installed for a restricted user (e.g.\r | |
688 | # runtimes are not in WinSxS folder, but in Python's own\r | |
689 | # folder), the runtimes do not need to be in every folder\r | |
690 | # with .pyd's.\r | |
691 | manifest_f = open(manifest_file)\r | |
692 | try:\r | |
693 | manifest_buf = manifest_f.read()\r | |
694 | finally:\r | |
695 | manifest_f.close()\r | |
696 | pattern = re.compile(\r | |
697 | r"""<assemblyIdentity.*?name=("|')Microsoft\."""\\r | |
698 | r"""VC\d{2}\.CRT("|').*?(/>|</assemblyIdentity>)""",\r | |
699 | re.DOTALL)\r | |
700 | manifest_buf = re.sub(pattern, "", manifest_buf)\r | |
701 | pattern = "<dependentAssembly>\s*</dependentAssembly>"\r | |
702 | manifest_buf = re.sub(pattern, "", manifest_buf)\r | |
703 | manifest_f = open(manifest_file, 'w')\r | |
704 | try:\r | |
705 | manifest_f.write(manifest_buf)\r | |
706 | finally:\r | |
707 | manifest_f.close()\r | |
708 | except IOError:\r | |
709 | pass\r | |
710 | \r | |
711 | # -- Miscellaneous methods -----------------------------------------\r | |
712 | # These are all used by the 'gen_lib_options() function, in\r | |
713 | # ccompiler.py.\r | |
714 | \r | |
715 | def library_dir_option(self, dir):\r | |
716 | return "/LIBPATH:" + dir\r | |
717 | \r | |
718 | def runtime_library_dir_option(self, dir):\r | |
719 | raise DistutilsPlatformError(\r | |
720 | "don't know how to set runtime library search path for MSVC++")\r | |
721 | \r | |
722 | def library_option(self, lib):\r | |
723 | return self.library_filename(lib)\r | |
724 | \r | |
725 | \r | |
726 | def find_library_file(self, dirs, lib, debug=0):\r | |
727 | # Prefer a debugging library if found (and requested), but deal\r | |
728 | # with it if we don't have one.\r | |
729 | if debug:\r | |
730 | try_names = [lib + "_d", lib]\r | |
731 | else:\r | |
732 | try_names = [lib]\r | |
733 | for dir in dirs:\r | |
734 | for name in try_names:\r | |
735 | libfile = os.path.join(dir, self.library_filename (name))\r | |
736 | if os.path.exists(libfile):\r | |
737 | return libfile\r | |
738 | else:\r | |
739 | # Oops, didn't find it in *any* of 'dirs'\r | |
740 | return None\r | |
741 | \r | |
742 | # Helper methods for using the MSVC registry settings\r | |
743 | \r | |
744 | def find_exe(self, exe):\r | |
745 | """Return path to an MSVC executable program.\r | |
746 | \r | |
747 | Tries to find the program in several places: first, one of the\r | |
748 | MSVC program search paths from the registry; next, the directories\r | |
749 | in the PATH environment variable. If any of those work, return an\r | |
750 | absolute path that is known to exist. If none of them work, just\r | |
751 | return the original program name, 'exe'.\r | |
752 | """\r | |
753 | for p in self.__paths:\r | |
754 | fn = os.path.join(os.path.abspath(p), exe)\r | |
755 | if os.path.isfile(fn):\r | |
756 | return fn\r | |
757 | \r | |
758 | # didn't find it; try existing path\r | |
759 | for p in os.environ['Path'].split(';'):\r | |
760 | fn = os.path.join(os.path.abspath(p),exe)\r | |
761 | if os.path.isfile(fn):\r | |
762 | return fn\r | |
763 | \r | |
764 | return exe\r |