• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"""distutils.msvccompiler
2
3Contains MSVCCompiler, an implementation of the abstract CCompiler class
4for the Microsoft Visual Studio.
5"""
6
7# Written by Perry Stoll
8# hacked by Robin Becker and Thomas Heller to do a better job of
9#   finding DevStudio (through the registry)
10
11import sys, os
12from distutils.errors import DistutilsPlatformError
13from distutils.ccompiler import CCompiler
14from distutils import log
15
16_can_read_reg = False
17try:
18    import winreg
19
20    _can_read_reg = True
21    hkey_mod = winreg
22
23    RegOpenKeyEx = winreg.OpenKeyEx
24    RegEnumKey = winreg.EnumKey
25    RegEnumValue = winreg.EnumValue
26    RegError = winreg.error
27
28except ImportError:
29    try:
30        import win32api
31        import win32con
32        _can_read_reg = True
33        hkey_mod = win32con
34
35        RegOpenKeyEx = win32api.RegOpenKeyEx
36        RegEnumKey = win32api.RegEnumKey
37        RegEnumValue = win32api.RegEnumValue
38        RegError = win32api.error
39    except ImportError:
40        log.info("Warning: Can't read registry to find the "
41                 "necessary compiler setting\n"
42                 "Make sure that Python modules winreg, "
43                 "win32api or win32con are installed.")
44
45if _can_read_reg:
46    HKEYS = (hkey_mod.HKEY_USERS,
47             hkey_mod.HKEY_CURRENT_USER,
48             hkey_mod.HKEY_LOCAL_MACHINE,
49             hkey_mod.HKEY_CLASSES_ROOT)
50
51def read_keys(base, key):
52    """Return list of registry keys."""
53    try:
54        handle = RegOpenKeyEx(base, key)
55    except RegError:
56        return None
57    L = []
58    i = 0
59    while True:
60        try:
61            k = RegEnumKey(handle, i)
62        except RegError:
63            break
64        L.append(k)
65        i += 1
66    return L
67
68def read_values(base, key):
69    """Return dict of registry keys and values.
70
71    All names are converted to lowercase.
72    """
73    try:
74        handle = RegOpenKeyEx(base, key)
75    except RegError:
76        return None
77    d = {}
78    i = 0
79    while True:
80        try:
81            name, value, type = RegEnumValue(handle, i)
82        except RegError:
83            break
84        name = name.lower()
85        d[convert_mbcs(name)] = convert_mbcs(value)
86        i += 1
87    return d
88
89def convert_mbcs(s):
90    dec = getattr(s, "decode", None)
91    if dec is not None:
92        try:
93            s = dec("mbcs")
94        except UnicodeError:
95            pass
96    return s
97
98class MacroExpander:
99    def __init__(self, version):
100        self.macros = {}
101        self.load_macros(version)
102
103    def set_macro(self, macro, path, key):
104        for base in HKEYS:
105            d = read_values(base, path)
106            if d:
107                self.macros["$(%s)" % macro] = d[key]
108                break
109
110    def load_macros(self, version):
111        vsbase = r"Software\Microsoft\VisualStudio\%0.1f" % version
112        self.set_macro("VCInstallDir", vsbase + r"\Setup\VC", "productdir")
113        self.set_macro("VSInstallDir", vsbase + r"\Setup\VS", "productdir")
114        net = r"Software\Microsoft\.NETFramework"
115        self.set_macro("FrameworkDir", net, "installroot")
116        try:
117            if version > 7.0:
118                self.set_macro("FrameworkSDKDir", net, "sdkinstallrootv1.1")
119            else:
120                self.set_macro("FrameworkSDKDir", net, "sdkinstallroot")
121        except KeyError as exc: #
122            raise DistutilsPlatformError(
123            """Python was built with Visual Studio 2003;
124extensions must be built with a compiler than can generate compatible binaries.
125Visual Studio 2003 was not found on this system. If you have Cygwin installed,
126you can try compiling with MingW32, by passing "-c mingw32" to setup.py.""")
127
128        p = r"Software\Microsoft\NET Framework Setup\Product"
129        for base in HKEYS:
130            try:
131                h = RegOpenKeyEx(base, p)
132            except RegError:
133                continue
134            key = RegEnumKey(h, 0)
135            d = read_values(base, r"%s\%s" % (p, key))
136            self.macros["$(FrameworkVersion)"] = d["version"]
137
138    def sub(self, s):
139        for k, v in self.macros.items():
140            s = s.replace(k, v)
141        return s
142
143def get_build_version():
144    """Return the version of MSVC that was used to build Python.
145
146    For Python 2.3 and up, the version number is included in
147    sys.version.  For earlier versions, assume the compiler is MSVC 6.
148    """
149    prefix = "MSC v."
150    i = sys.version.find(prefix)
151    if i == -1:
152        return 6
153    i = i + len(prefix)
154    s, rest = sys.version[i:].split(" ", 1)
155    majorVersion = int(s[:-2]) - 6
156    if majorVersion >= 13:
157        # v13 was skipped and should be v14
158        majorVersion += 1
159    minorVersion = int(s[2:3]) / 10.0
160    # I don't think paths are affected by minor version in version 6
161    if majorVersion == 6:
162        minorVersion = 0
163    if majorVersion >= 6:
164        return majorVersion + minorVersion
165    # else we don't know what version of the compiler this is
166    return None
167
168def get_build_architecture():
169    """Return the processor architecture.
170
171    Possible results are "Intel" or "AMD64".
172    """
173
174    prefix = " bit ("
175    i = sys.version.find(prefix)
176    if i == -1:
177        return "Intel"
178    j = sys.version.find(")", i)
179    return sys.version[i+len(prefix):j]
180
181def normalize_and_reduce_paths(paths):
182    """Return a list of normalized paths with duplicates removed.
183
184    The current order of paths is maintained.
185    """
186    # Paths are normalized so things like:  /a and /a/ aren't both preserved.
187    reduced_paths = []
188    for p in paths:
189        np = os.path.normpath(p)
190        # XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set.
191        if np not in reduced_paths:
192            reduced_paths.append(np)
193    return reduced_paths
194
195
196class MSVCCompiler(CCompiler) :
197    """Concrete class that implements an interface to Microsoft Visual C++,
198       as defined by the CCompiler abstract class."""
199
200    compiler_type = 'msvc'
201
202    # Just set this so CCompiler's constructor doesn't barf.  We currently
203    # don't use the 'set_executables()' bureaucracy provided by CCompiler,
204    # as it really isn't necessary for this sort of single-compiler class.
205    # Would be nice to have a consistent interface with UnixCCompiler,
206    # though, so it's worth thinking about.
207    executables = {}
208
209    # Private class data (need to distinguish C from C++ source for compiler)
210    _c_extensions = ['.c']
211    _cpp_extensions = ['.cc', '.cpp', '.cxx']
212    _rc_extensions = ['.rc']
213    _mc_extensions = ['.mc']
214
215    # Needed for the filename generation methods provided by the
216    # base class, CCompiler.
217    src_extensions = (_c_extensions + _cpp_extensions +
218                      _rc_extensions + _mc_extensions)
219    res_extension = '.res'
220    obj_extension = '.obj'
221    static_lib_extension = '.lib'
222    shared_lib_extension = '.dll'
223    static_lib_format = shared_lib_format = '%s%s'
224    exe_extension = '.exe'
225
226    def __init__(self, verbose=0, dry_run=0, force=0):
227        CCompiler.__init__ (self, verbose, dry_run, force)
228        self.__version = get_build_version()
229        self.__arch = get_build_architecture()
230        if self.__arch == "Intel":
231            # x86
232            if self.__version >= 7:
233                self.__root = r"Software\Microsoft\VisualStudio"
234                self.__macros = MacroExpander(self.__version)
235            else:
236                self.__root = r"Software\Microsoft\Devstudio"
237            self.__product = "Visual Studio version %s" % self.__version
238        else:
239            # Win64. Assume this was built with the platform SDK
240            self.__product = "Microsoft SDK compiler %s" % (self.__version + 6)
241
242        self.initialized = False
243
244
245    # -- Miscellaneous methods -----------------------------------------
246
247    # Helper methods for using the MSVC registry settings
248
249    def find_exe(self, exe):
250        """Return path to an MSVC executable program.
251
252        Tries to find the program in several places: first, one of the
253        MSVC program search paths from the registry; next, the directories
254        in the PATH environment variable.  If any of those work, return an
255        absolute path that is known to exist.  If none of them work, just
256        return the original program name, 'exe'.
257        """
258        for p in self.__paths:
259            fn = os.path.join(os.path.abspath(p), exe)
260            if os.path.isfile(fn):
261                return fn
262
263        # didn't find it; try existing path
264        for p in os.environ['Path'].split(';'):
265            fn = os.path.join(os.path.abspath(p),exe)
266            if os.path.isfile(fn):
267                return fn
268
269        return exe
270
271    def get_msvc_paths(self, path, platform='x86'):
272        """Get a list of devstudio directories (include, lib or path).
273
274        Return a list of strings.  The list will be empty if unable to
275        access the registry or appropriate registry keys not found.
276        """
277        if not _can_read_reg:
278            return []
279
280        path = path + " dirs"
281        if self.__version >= 7:
282            key = (r"%s\%0.1f\VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories"
283                   % (self.__root, self.__version))
284        else:
285            key = (r"%s\6.0\Build System\Components\Platforms"
286                   r"\Win32 (%s)\Directories" % (self.__root, platform))
287
288        for base in HKEYS:
289            d = read_values(base, key)
290            if d:
291                if self.__version >= 7:
292                    return self.__macros.sub(d[path]).split(";")
293                else:
294                    return d[path].split(";")
295        # MSVC 6 seems to create the registry entries we need only when
296        # the GUI is run.
297        if self.__version == 6:
298            for base in HKEYS:
299                if read_values(base, r"%s\6.0" % self.__root) is not None:
300                    self.warn("It seems you have Visual Studio 6 installed, "
301                        "but the expected registry settings are not present.\n"
302                        "You must at least run the Visual Studio GUI once "
303                        "so that these entries are created.")
304                    break
305        return []
306
307    def set_path_env_var(self, name):
308        """Set environment variable 'name' to an MSVC path type value.
309
310        This is equivalent to a SET command prior to execution of spawned
311        commands.
312        """
313
314        if name == "lib":
315            p = self.get_msvc_paths("library")
316        else:
317            p = self.get_msvc_paths(name)
318        if p:
319            os.environ[name] = ';'.join(p)
320
321
322if get_build_version() >= 8.0:
323    log.debug("Importing new compiler from distutils.msvc9compiler")
324    OldMSVCCompiler = MSVCCompiler
325    from distutils.msvc9compiler import MSVCCompiler
326    # get_build_architecture not really relevant now we support cross-compile
327    from distutils.msvc9compiler import MacroExpander
328