xref: /aosp_15_r20/prebuilts/build-tools/common/py3-stdlib/distutils/_msvccompiler.py (revision cda5da8d549138a6648c5ee6d7a49cf8f4a657be)
1*cda5da8dSAndroid Build Coastguard Worker"""distutils._msvccompiler
2*cda5da8dSAndroid Build Coastguard Worker
3*cda5da8dSAndroid Build Coastguard WorkerContains MSVCCompiler, an implementation of the abstract CCompiler class
4*cda5da8dSAndroid Build Coastguard Workerfor Microsoft Visual Studio 2015.
5*cda5da8dSAndroid Build Coastguard Worker
6*cda5da8dSAndroid Build Coastguard WorkerThe module is compatible with VS 2015 and later. You can find legacy support
7*cda5da8dSAndroid Build Coastguard Workerfor older versions in distutils.msvc9compiler and distutils.msvccompiler.
8*cda5da8dSAndroid Build Coastguard Worker"""
9*cda5da8dSAndroid Build Coastguard Worker
10*cda5da8dSAndroid Build Coastguard Worker# Written by Perry Stoll
11*cda5da8dSAndroid Build Coastguard Worker# hacked by Robin Becker and Thomas Heller to do a better job of
12*cda5da8dSAndroid Build Coastguard Worker#   finding DevStudio (through the registry)
13*cda5da8dSAndroid Build Coastguard Worker# ported to VS 2005 and VS 2008 by Christian Heimes
14*cda5da8dSAndroid Build Coastguard Worker# ported to VS 2015 by Steve Dower
15*cda5da8dSAndroid Build Coastguard Worker
16*cda5da8dSAndroid Build Coastguard Workerimport os
17*cda5da8dSAndroid Build Coastguard Workerimport subprocess
18*cda5da8dSAndroid Build Coastguard Workerimport winreg
19*cda5da8dSAndroid Build Coastguard Worker
20*cda5da8dSAndroid Build Coastguard Workerfrom distutils.errors import DistutilsExecError, DistutilsPlatformError, \
21*cda5da8dSAndroid Build Coastguard Worker                             CompileError, LibError, LinkError
22*cda5da8dSAndroid Build Coastguard Workerfrom distutils.ccompiler import CCompiler, gen_lib_options
23*cda5da8dSAndroid Build Coastguard Workerfrom distutils import log
24*cda5da8dSAndroid Build Coastguard Workerfrom distutils.util import get_platform
25*cda5da8dSAndroid Build Coastguard Worker
26*cda5da8dSAndroid Build Coastguard Workerfrom itertools import count
27*cda5da8dSAndroid Build Coastguard Worker
28*cda5da8dSAndroid Build Coastguard Workerdef _find_vc2015():
29*cda5da8dSAndroid Build Coastguard Worker    try:
30*cda5da8dSAndroid Build Coastguard Worker        key = winreg.OpenKeyEx(
31*cda5da8dSAndroid Build Coastguard Worker            winreg.HKEY_LOCAL_MACHINE,
32*cda5da8dSAndroid Build Coastguard Worker            r"Software\Microsoft\VisualStudio\SxS\VC7",
33*cda5da8dSAndroid Build Coastguard Worker            access=winreg.KEY_READ | winreg.KEY_WOW64_32KEY
34*cda5da8dSAndroid Build Coastguard Worker        )
35*cda5da8dSAndroid Build Coastguard Worker    except OSError:
36*cda5da8dSAndroid Build Coastguard Worker        log.debug("Visual C++ is not registered")
37*cda5da8dSAndroid Build Coastguard Worker        return None, None
38*cda5da8dSAndroid Build Coastguard Worker
39*cda5da8dSAndroid Build Coastguard Worker    best_version = 0
40*cda5da8dSAndroid Build Coastguard Worker    best_dir = None
41*cda5da8dSAndroid Build Coastguard Worker    with key:
42*cda5da8dSAndroid Build Coastguard Worker        for i in count():
43*cda5da8dSAndroid Build Coastguard Worker            try:
44*cda5da8dSAndroid Build Coastguard Worker                v, vc_dir, vt = winreg.EnumValue(key, i)
45*cda5da8dSAndroid Build Coastguard Worker            except OSError:
46*cda5da8dSAndroid Build Coastguard Worker                break
47*cda5da8dSAndroid Build Coastguard Worker            if v and vt == winreg.REG_SZ and os.path.isdir(vc_dir):
48*cda5da8dSAndroid Build Coastguard Worker                try:
49*cda5da8dSAndroid Build Coastguard Worker                    version = int(float(v))
50*cda5da8dSAndroid Build Coastguard Worker                except (ValueError, TypeError):
51*cda5da8dSAndroid Build Coastguard Worker                    continue
52*cda5da8dSAndroid Build Coastguard Worker                if version >= 14 and version > best_version:
53*cda5da8dSAndroid Build Coastguard Worker                    best_version, best_dir = version, vc_dir
54*cda5da8dSAndroid Build Coastguard Worker    return best_version, best_dir
55*cda5da8dSAndroid Build Coastguard Worker
56*cda5da8dSAndroid Build Coastguard Workerdef _find_vc2017():
57*cda5da8dSAndroid Build Coastguard Worker    """Returns "15, path" based on the result of invoking vswhere.exe
58*cda5da8dSAndroid Build Coastguard Worker    If no install is found, returns "None, None"
59*cda5da8dSAndroid Build Coastguard Worker
60*cda5da8dSAndroid Build Coastguard Worker    The version is returned to avoid unnecessarily changing the function
61*cda5da8dSAndroid Build Coastguard Worker    result. It may be ignored when the path is not None.
62*cda5da8dSAndroid Build Coastguard Worker
63*cda5da8dSAndroid Build Coastguard Worker    If vswhere.exe is not available, by definition, VS 2017 is not
64*cda5da8dSAndroid Build Coastguard Worker    installed.
65*cda5da8dSAndroid Build Coastguard Worker    """
66*cda5da8dSAndroid Build Coastguard Worker    root = os.environ.get("ProgramFiles(x86)") or os.environ.get("ProgramFiles")
67*cda5da8dSAndroid Build Coastguard Worker    if not root:
68*cda5da8dSAndroid Build Coastguard Worker        return None, None
69*cda5da8dSAndroid Build Coastguard Worker
70*cda5da8dSAndroid Build Coastguard Worker    try:
71*cda5da8dSAndroid Build Coastguard Worker        path = subprocess.check_output([
72*cda5da8dSAndroid Build Coastguard Worker            os.path.join(root, "Microsoft Visual Studio", "Installer", "vswhere.exe"),
73*cda5da8dSAndroid Build Coastguard Worker            "-latest",
74*cda5da8dSAndroid Build Coastguard Worker            "-prerelease",
75*cda5da8dSAndroid Build Coastguard Worker            "-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
76*cda5da8dSAndroid Build Coastguard Worker            "-property", "installationPath",
77*cda5da8dSAndroid Build Coastguard Worker            "-products", "*",
78*cda5da8dSAndroid Build Coastguard Worker        ], encoding="mbcs", errors="strict").strip()
79*cda5da8dSAndroid Build Coastguard Worker    except (subprocess.CalledProcessError, OSError, UnicodeDecodeError):
80*cda5da8dSAndroid Build Coastguard Worker        return None, None
81*cda5da8dSAndroid Build Coastguard Worker
82*cda5da8dSAndroid Build Coastguard Worker    path = os.path.join(path, "VC", "Auxiliary", "Build")
83*cda5da8dSAndroid Build Coastguard Worker    if os.path.isdir(path):
84*cda5da8dSAndroid Build Coastguard Worker        return 15, path
85*cda5da8dSAndroid Build Coastguard Worker
86*cda5da8dSAndroid Build Coastguard Worker    return None, None
87*cda5da8dSAndroid Build Coastguard Worker
88*cda5da8dSAndroid Build Coastguard WorkerPLAT_SPEC_TO_RUNTIME = {
89*cda5da8dSAndroid Build Coastguard Worker    'x86' : 'x86',
90*cda5da8dSAndroid Build Coastguard Worker    'x86_amd64' : 'x64',
91*cda5da8dSAndroid Build Coastguard Worker    'x86_arm' : 'arm',
92*cda5da8dSAndroid Build Coastguard Worker    'x86_arm64' : 'arm64'
93*cda5da8dSAndroid Build Coastguard Worker}
94*cda5da8dSAndroid Build Coastguard Worker
95*cda5da8dSAndroid Build Coastguard Workerdef _find_vcvarsall(plat_spec):
96*cda5da8dSAndroid Build Coastguard Worker    # bpo-38597: Removed vcruntime return value
97*cda5da8dSAndroid Build Coastguard Worker    _, best_dir = _find_vc2017()
98*cda5da8dSAndroid Build Coastguard Worker
99*cda5da8dSAndroid Build Coastguard Worker    if not best_dir:
100*cda5da8dSAndroid Build Coastguard Worker        best_version, best_dir = _find_vc2015()
101*cda5da8dSAndroid Build Coastguard Worker
102*cda5da8dSAndroid Build Coastguard Worker    if not best_dir:
103*cda5da8dSAndroid Build Coastguard Worker        log.debug("No suitable Visual C++ version found")
104*cda5da8dSAndroid Build Coastguard Worker        return None, None
105*cda5da8dSAndroid Build Coastguard Worker
106*cda5da8dSAndroid Build Coastguard Worker    vcvarsall = os.path.join(best_dir, "vcvarsall.bat")
107*cda5da8dSAndroid Build Coastguard Worker    if not os.path.isfile(vcvarsall):
108*cda5da8dSAndroid Build Coastguard Worker        log.debug("%s cannot be found", vcvarsall)
109*cda5da8dSAndroid Build Coastguard Worker        return None, None
110*cda5da8dSAndroid Build Coastguard Worker
111*cda5da8dSAndroid Build Coastguard Worker    return vcvarsall, None
112*cda5da8dSAndroid Build Coastguard Worker
113*cda5da8dSAndroid Build Coastguard Workerdef _get_vc_env(plat_spec):
114*cda5da8dSAndroid Build Coastguard Worker    if os.getenv("DISTUTILS_USE_SDK"):
115*cda5da8dSAndroid Build Coastguard Worker        return {
116*cda5da8dSAndroid Build Coastguard Worker            key.lower(): value
117*cda5da8dSAndroid Build Coastguard Worker            for key, value in os.environ.items()
118*cda5da8dSAndroid Build Coastguard Worker        }
119*cda5da8dSAndroid Build Coastguard Worker
120*cda5da8dSAndroid Build Coastguard Worker    vcvarsall, _ = _find_vcvarsall(plat_spec)
121*cda5da8dSAndroid Build Coastguard Worker    if not vcvarsall:
122*cda5da8dSAndroid Build Coastguard Worker        raise DistutilsPlatformError("Unable to find vcvarsall.bat")
123*cda5da8dSAndroid Build Coastguard Worker
124*cda5da8dSAndroid Build Coastguard Worker    try:
125*cda5da8dSAndroid Build Coastguard Worker        out = subprocess.check_output(
126*cda5da8dSAndroid Build Coastguard Worker            'cmd /u /c "{}" {} && set'.format(vcvarsall, plat_spec),
127*cda5da8dSAndroid Build Coastguard Worker            stderr=subprocess.STDOUT,
128*cda5da8dSAndroid Build Coastguard Worker        ).decode('utf-16le', errors='replace')
129*cda5da8dSAndroid Build Coastguard Worker    except subprocess.CalledProcessError as exc:
130*cda5da8dSAndroid Build Coastguard Worker        log.error(exc.output)
131*cda5da8dSAndroid Build Coastguard Worker        raise DistutilsPlatformError("Error executing {}"
132*cda5da8dSAndroid Build Coastguard Worker                .format(exc.cmd))
133*cda5da8dSAndroid Build Coastguard Worker
134*cda5da8dSAndroid Build Coastguard Worker    env = {
135*cda5da8dSAndroid Build Coastguard Worker        key.lower(): value
136*cda5da8dSAndroid Build Coastguard Worker        for key, _, value in
137*cda5da8dSAndroid Build Coastguard Worker        (line.partition('=') for line in out.splitlines())
138*cda5da8dSAndroid Build Coastguard Worker        if key and value
139*cda5da8dSAndroid Build Coastguard Worker    }
140*cda5da8dSAndroid Build Coastguard Worker
141*cda5da8dSAndroid Build Coastguard Worker    return env
142*cda5da8dSAndroid Build Coastguard Worker
143*cda5da8dSAndroid Build Coastguard Workerdef _find_exe(exe, paths=None):
144*cda5da8dSAndroid Build Coastguard Worker    """Return path to an MSVC executable program.
145*cda5da8dSAndroid Build Coastguard Worker
146*cda5da8dSAndroid Build Coastguard Worker    Tries to find the program in several places: first, one of the
147*cda5da8dSAndroid Build Coastguard Worker    MSVC program search paths from the registry; next, the directories
148*cda5da8dSAndroid Build Coastguard Worker    in the PATH environment variable.  If any of those work, return an
149*cda5da8dSAndroid Build Coastguard Worker    absolute path that is known to exist.  If none of them work, just
150*cda5da8dSAndroid Build Coastguard Worker    return the original program name, 'exe'.
151*cda5da8dSAndroid Build Coastguard Worker    """
152*cda5da8dSAndroid Build Coastguard Worker    if not paths:
153*cda5da8dSAndroid Build Coastguard Worker        paths = os.getenv('path').split(os.pathsep)
154*cda5da8dSAndroid Build Coastguard Worker    for p in paths:
155*cda5da8dSAndroid Build Coastguard Worker        fn = os.path.join(os.path.abspath(p), exe)
156*cda5da8dSAndroid Build Coastguard Worker        if os.path.isfile(fn):
157*cda5da8dSAndroid Build Coastguard Worker            return fn
158*cda5da8dSAndroid Build Coastguard Worker    return exe
159*cda5da8dSAndroid Build Coastguard Worker
160*cda5da8dSAndroid Build Coastguard Worker# A map keyed by get_platform() return values to values accepted by
161*cda5da8dSAndroid Build Coastguard Worker# 'vcvarsall.bat'. Always cross-compile from x86 to work with the
162*cda5da8dSAndroid Build Coastguard Worker# lighter-weight MSVC installs that do not include native 64-bit tools.
163*cda5da8dSAndroid Build Coastguard WorkerPLAT_TO_VCVARS = {
164*cda5da8dSAndroid Build Coastguard Worker    'win32' : 'x86',
165*cda5da8dSAndroid Build Coastguard Worker    'win-amd64' : 'x86_amd64',
166*cda5da8dSAndroid Build Coastguard Worker    'win-arm32' : 'x86_arm',
167*cda5da8dSAndroid Build Coastguard Worker    'win-arm64' : 'x86_arm64'
168*cda5da8dSAndroid Build Coastguard Worker}
169*cda5da8dSAndroid Build Coastguard Worker
170*cda5da8dSAndroid Build Coastguard Workerclass MSVCCompiler(CCompiler) :
171*cda5da8dSAndroid Build Coastguard Worker    """Concrete class that implements an interface to Microsoft Visual C++,
172*cda5da8dSAndroid Build Coastguard Worker       as defined by the CCompiler abstract class."""
173*cda5da8dSAndroid Build Coastguard Worker
174*cda5da8dSAndroid Build Coastguard Worker    compiler_type = 'msvc'
175*cda5da8dSAndroid Build Coastguard Worker
176*cda5da8dSAndroid Build Coastguard Worker    # Just set this so CCompiler's constructor doesn't barf.  We currently
177*cda5da8dSAndroid Build Coastguard Worker    # don't use the 'set_executables()' bureaucracy provided by CCompiler,
178*cda5da8dSAndroid Build Coastguard Worker    # as it really isn't necessary for this sort of single-compiler class.
179*cda5da8dSAndroid Build Coastguard Worker    # Would be nice to have a consistent interface with UnixCCompiler,
180*cda5da8dSAndroid Build Coastguard Worker    # though, so it's worth thinking about.
181*cda5da8dSAndroid Build Coastguard Worker    executables = {}
182*cda5da8dSAndroid Build Coastguard Worker
183*cda5da8dSAndroid Build Coastguard Worker    # Private class data (need to distinguish C from C++ source for compiler)
184*cda5da8dSAndroid Build Coastguard Worker    _c_extensions = ['.c']
185*cda5da8dSAndroid Build Coastguard Worker    _cpp_extensions = ['.cc', '.cpp', '.cxx']
186*cda5da8dSAndroid Build Coastguard Worker    _rc_extensions = ['.rc']
187*cda5da8dSAndroid Build Coastguard Worker    _mc_extensions = ['.mc']
188*cda5da8dSAndroid Build Coastguard Worker
189*cda5da8dSAndroid Build Coastguard Worker    # Needed for the filename generation methods provided by the
190*cda5da8dSAndroid Build Coastguard Worker    # base class, CCompiler.
191*cda5da8dSAndroid Build Coastguard Worker    src_extensions = (_c_extensions + _cpp_extensions +
192*cda5da8dSAndroid Build Coastguard Worker                      _rc_extensions + _mc_extensions)
193*cda5da8dSAndroid Build Coastguard Worker    res_extension = '.res'
194*cda5da8dSAndroid Build Coastguard Worker    obj_extension = '.obj'
195*cda5da8dSAndroid Build Coastguard Worker    static_lib_extension = '.lib'
196*cda5da8dSAndroid Build Coastguard Worker    shared_lib_extension = '.dll'
197*cda5da8dSAndroid Build Coastguard Worker    static_lib_format = shared_lib_format = '%s%s'
198*cda5da8dSAndroid Build Coastguard Worker    exe_extension = '.exe'
199*cda5da8dSAndroid Build Coastguard Worker
200*cda5da8dSAndroid Build Coastguard Worker
201*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, verbose=0, dry_run=0, force=0):
202*cda5da8dSAndroid Build Coastguard Worker        CCompiler.__init__ (self, verbose, dry_run, force)
203*cda5da8dSAndroid Build Coastguard Worker        # target platform (.plat_name is consistent with 'bdist')
204*cda5da8dSAndroid Build Coastguard Worker        self.plat_name = None
205*cda5da8dSAndroid Build Coastguard Worker        self.initialized = False
206*cda5da8dSAndroid Build Coastguard Worker
207*cda5da8dSAndroid Build Coastguard Worker    def initialize(self, plat_name=None):
208*cda5da8dSAndroid Build Coastguard Worker        # multi-init means we would need to check platform same each time...
209*cda5da8dSAndroid Build Coastguard Worker        assert not self.initialized, "don't init multiple times"
210*cda5da8dSAndroid Build Coastguard Worker        if plat_name is None:
211*cda5da8dSAndroid Build Coastguard Worker            plat_name = get_platform()
212*cda5da8dSAndroid Build Coastguard Worker        # sanity check for platforms to prevent obscure errors later.
213*cda5da8dSAndroid Build Coastguard Worker        if plat_name not in PLAT_TO_VCVARS:
214*cda5da8dSAndroid Build Coastguard Worker            raise DistutilsPlatformError("--plat-name must be one of {}"
215*cda5da8dSAndroid Build Coastguard Worker                                         .format(tuple(PLAT_TO_VCVARS)))
216*cda5da8dSAndroid Build Coastguard Worker
217*cda5da8dSAndroid Build Coastguard Worker        # Get the vcvarsall.bat spec for the requested platform.
218*cda5da8dSAndroid Build Coastguard Worker        plat_spec = PLAT_TO_VCVARS[plat_name]
219*cda5da8dSAndroid Build Coastguard Worker
220*cda5da8dSAndroid Build Coastguard Worker        vc_env = _get_vc_env(plat_spec)
221*cda5da8dSAndroid Build Coastguard Worker        if not vc_env:
222*cda5da8dSAndroid Build Coastguard Worker            raise DistutilsPlatformError("Unable to find a compatible "
223*cda5da8dSAndroid Build Coastguard Worker                "Visual Studio installation.")
224*cda5da8dSAndroid Build Coastguard Worker
225*cda5da8dSAndroid Build Coastguard Worker        self._paths = vc_env.get('path', '')
226*cda5da8dSAndroid Build Coastguard Worker        paths = self._paths.split(os.pathsep)
227*cda5da8dSAndroid Build Coastguard Worker        self.cc = _find_exe("cl.exe", paths)
228*cda5da8dSAndroid Build Coastguard Worker        self.linker = _find_exe("link.exe", paths)
229*cda5da8dSAndroid Build Coastguard Worker        self.lib = _find_exe("lib.exe", paths)
230*cda5da8dSAndroid Build Coastguard Worker        self.rc = _find_exe("rc.exe", paths)   # resource compiler
231*cda5da8dSAndroid Build Coastguard Worker        self.mc = _find_exe("mc.exe", paths)   # message compiler
232*cda5da8dSAndroid Build Coastguard Worker        self.mt = _find_exe("mt.exe", paths)   # message compiler
233*cda5da8dSAndroid Build Coastguard Worker
234*cda5da8dSAndroid Build Coastguard Worker        for dir in vc_env.get('include', '').split(os.pathsep):
235*cda5da8dSAndroid Build Coastguard Worker            if dir:
236*cda5da8dSAndroid Build Coastguard Worker                self.add_include_dir(dir.rstrip(os.sep))
237*cda5da8dSAndroid Build Coastguard Worker
238*cda5da8dSAndroid Build Coastguard Worker        for dir in vc_env.get('lib', '').split(os.pathsep):
239*cda5da8dSAndroid Build Coastguard Worker            if dir:
240*cda5da8dSAndroid Build Coastguard Worker                self.add_library_dir(dir.rstrip(os.sep))
241*cda5da8dSAndroid Build Coastguard Worker
242*cda5da8dSAndroid Build Coastguard Worker        self.preprocess_options = None
243*cda5da8dSAndroid Build Coastguard Worker        # bpo-38597: Always compile with dynamic linking
244*cda5da8dSAndroid Build Coastguard Worker        # Future releases of Python 3.x will include all past
245*cda5da8dSAndroid Build Coastguard Worker        # versions of vcruntime*.dll for compatibility.
246*cda5da8dSAndroid Build Coastguard Worker        self.compile_options = [
247*cda5da8dSAndroid Build Coastguard Worker            '/nologo', '/Ox', '/W3', '/GL', '/DNDEBUG', '/MD'
248*cda5da8dSAndroid Build Coastguard Worker        ]
249*cda5da8dSAndroid Build Coastguard Worker
250*cda5da8dSAndroid Build Coastguard Worker        self.compile_options_debug = [
251*cda5da8dSAndroid Build Coastguard Worker            '/nologo', '/Od', '/MDd', '/Zi', '/W3', '/D_DEBUG'
252*cda5da8dSAndroid Build Coastguard Worker        ]
253*cda5da8dSAndroid Build Coastguard Worker
254*cda5da8dSAndroid Build Coastguard Worker        ldflags = [
255*cda5da8dSAndroid Build Coastguard Worker            '/nologo', '/INCREMENTAL:NO', '/LTCG'
256*cda5da8dSAndroid Build Coastguard Worker        ]
257*cda5da8dSAndroid Build Coastguard Worker
258*cda5da8dSAndroid Build Coastguard Worker        ldflags_debug = [
259*cda5da8dSAndroid Build Coastguard Worker            '/nologo', '/INCREMENTAL:NO', '/LTCG', '/DEBUG:FULL'
260*cda5da8dSAndroid Build Coastguard Worker        ]
261*cda5da8dSAndroid Build Coastguard Worker
262*cda5da8dSAndroid Build Coastguard Worker        self.ldflags_exe = [*ldflags, '/MANIFEST:EMBED,ID=1']
263*cda5da8dSAndroid Build Coastguard Worker        self.ldflags_exe_debug = [*ldflags_debug, '/MANIFEST:EMBED,ID=1']
264*cda5da8dSAndroid Build Coastguard Worker        self.ldflags_shared = [*ldflags, '/DLL', '/MANIFEST:EMBED,ID=2', '/MANIFESTUAC:NO']
265*cda5da8dSAndroid Build Coastguard Worker        self.ldflags_shared_debug = [*ldflags_debug, '/DLL', '/MANIFEST:EMBED,ID=2', '/MANIFESTUAC:NO']
266*cda5da8dSAndroid Build Coastguard Worker        self.ldflags_static = [*ldflags]
267*cda5da8dSAndroid Build Coastguard Worker        self.ldflags_static_debug = [*ldflags_debug]
268*cda5da8dSAndroid Build Coastguard Worker
269*cda5da8dSAndroid Build Coastguard Worker        self._ldflags = {
270*cda5da8dSAndroid Build Coastguard Worker            (CCompiler.EXECUTABLE, None): self.ldflags_exe,
271*cda5da8dSAndroid Build Coastguard Worker            (CCompiler.EXECUTABLE, False): self.ldflags_exe,
272*cda5da8dSAndroid Build Coastguard Worker            (CCompiler.EXECUTABLE, True): self.ldflags_exe_debug,
273*cda5da8dSAndroid Build Coastguard Worker            (CCompiler.SHARED_OBJECT, None): self.ldflags_shared,
274*cda5da8dSAndroid Build Coastguard Worker            (CCompiler.SHARED_OBJECT, False): self.ldflags_shared,
275*cda5da8dSAndroid Build Coastguard Worker            (CCompiler.SHARED_OBJECT, True): self.ldflags_shared_debug,
276*cda5da8dSAndroid Build Coastguard Worker            (CCompiler.SHARED_LIBRARY, None): self.ldflags_static,
277*cda5da8dSAndroid Build Coastguard Worker            (CCompiler.SHARED_LIBRARY, False): self.ldflags_static,
278*cda5da8dSAndroid Build Coastguard Worker            (CCompiler.SHARED_LIBRARY, True): self.ldflags_static_debug,
279*cda5da8dSAndroid Build Coastguard Worker        }
280*cda5da8dSAndroid Build Coastguard Worker
281*cda5da8dSAndroid Build Coastguard Worker        self.initialized = True
282*cda5da8dSAndroid Build Coastguard Worker
283*cda5da8dSAndroid Build Coastguard Worker    # -- Worker methods ------------------------------------------------
284*cda5da8dSAndroid Build Coastguard Worker
285*cda5da8dSAndroid Build Coastguard Worker    def object_filenames(self,
286*cda5da8dSAndroid Build Coastguard Worker                         source_filenames,
287*cda5da8dSAndroid Build Coastguard Worker                         strip_dir=0,
288*cda5da8dSAndroid Build Coastguard Worker                         output_dir=''):
289*cda5da8dSAndroid Build Coastguard Worker        ext_map = {
290*cda5da8dSAndroid Build Coastguard Worker            **{ext: self.obj_extension for ext in self.src_extensions},
291*cda5da8dSAndroid Build Coastguard Worker            **{ext: self.res_extension for ext in self._rc_extensions + self._mc_extensions},
292*cda5da8dSAndroid Build Coastguard Worker        }
293*cda5da8dSAndroid Build Coastguard Worker
294*cda5da8dSAndroid Build Coastguard Worker        output_dir = output_dir or ''
295*cda5da8dSAndroid Build Coastguard Worker
296*cda5da8dSAndroid Build Coastguard Worker        def make_out_path(p):
297*cda5da8dSAndroid Build Coastguard Worker            base, ext = os.path.splitext(p)
298*cda5da8dSAndroid Build Coastguard Worker            if strip_dir:
299*cda5da8dSAndroid Build Coastguard Worker                base = os.path.basename(base)
300*cda5da8dSAndroid Build Coastguard Worker            else:
301*cda5da8dSAndroid Build Coastguard Worker                _, base = os.path.splitdrive(base)
302*cda5da8dSAndroid Build Coastguard Worker                if base.startswith((os.path.sep, os.path.altsep)):
303*cda5da8dSAndroid Build Coastguard Worker                    base = base[1:]
304*cda5da8dSAndroid Build Coastguard Worker            try:
305*cda5da8dSAndroid Build Coastguard Worker                # XXX: This may produce absurdly long paths. We should check
306*cda5da8dSAndroid Build Coastguard Worker                # the length of the result and trim base until we fit within
307*cda5da8dSAndroid Build Coastguard Worker                # 260 characters.
308*cda5da8dSAndroid Build Coastguard Worker                return os.path.join(output_dir, base + ext_map[ext])
309*cda5da8dSAndroid Build Coastguard Worker            except LookupError:
310*cda5da8dSAndroid Build Coastguard Worker                # Better to raise an exception instead of silently continuing
311*cda5da8dSAndroid Build Coastguard Worker                # and later complain about sources and targets having
312*cda5da8dSAndroid Build Coastguard Worker                # different lengths
313*cda5da8dSAndroid Build Coastguard Worker                raise CompileError("Don't know how to compile {}".format(p))
314*cda5da8dSAndroid Build Coastguard Worker
315*cda5da8dSAndroid Build Coastguard Worker        return list(map(make_out_path, source_filenames))
316*cda5da8dSAndroid Build Coastguard Worker
317*cda5da8dSAndroid Build Coastguard Worker
318*cda5da8dSAndroid Build Coastguard Worker    def compile(self, sources,
319*cda5da8dSAndroid Build Coastguard Worker                output_dir=None, macros=None, include_dirs=None, debug=0,
320*cda5da8dSAndroid Build Coastguard Worker                extra_preargs=None, extra_postargs=None, depends=None):
321*cda5da8dSAndroid Build Coastguard Worker
322*cda5da8dSAndroid Build Coastguard Worker        if not self.initialized:
323*cda5da8dSAndroid Build Coastguard Worker            self.initialize()
324*cda5da8dSAndroid Build Coastguard Worker        compile_info = self._setup_compile(output_dir, macros, include_dirs,
325*cda5da8dSAndroid Build Coastguard Worker                                           sources, depends, extra_postargs)
326*cda5da8dSAndroid Build Coastguard Worker        macros, objects, extra_postargs, pp_opts, build = compile_info
327*cda5da8dSAndroid Build Coastguard Worker
328*cda5da8dSAndroid Build Coastguard Worker        compile_opts = extra_preargs or []
329*cda5da8dSAndroid Build Coastguard Worker        compile_opts.append('/c')
330*cda5da8dSAndroid Build Coastguard Worker        if debug:
331*cda5da8dSAndroid Build Coastguard Worker            compile_opts.extend(self.compile_options_debug)
332*cda5da8dSAndroid Build Coastguard Worker        else:
333*cda5da8dSAndroid Build Coastguard Worker            compile_opts.extend(self.compile_options)
334*cda5da8dSAndroid Build Coastguard Worker
335*cda5da8dSAndroid Build Coastguard Worker
336*cda5da8dSAndroid Build Coastguard Worker        add_cpp_opts = False
337*cda5da8dSAndroid Build Coastguard Worker
338*cda5da8dSAndroid Build Coastguard Worker        for obj in objects:
339*cda5da8dSAndroid Build Coastguard Worker            try:
340*cda5da8dSAndroid Build Coastguard Worker                src, ext = build[obj]
341*cda5da8dSAndroid Build Coastguard Worker            except KeyError:
342*cda5da8dSAndroid Build Coastguard Worker                continue
343*cda5da8dSAndroid Build Coastguard Worker            if debug:
344*cda5da8dSAndroid Build Coastguard Worker                # pass the full pathname to MSVC in debug mode,
345*cda5da8dSAndroid Build Coastguard Worker                # this allows the debugger to find the source file
346*cda5da8dSAndroid Build Coastguard Worker                # without asking the user to browse for it
347*cda5da8dSAndroid Build Coastguard Worker                src = os.path.abspath(src)
348*cda5da8dSAndroid Build Coastguard Worker
349*cda5da8dSAndroid Build Coastguard Worker            if ext in self._c_extensions:
350*cda5da8dSAndroid Build Coastguard Worker                input_opt = "/Tc" + src
351*cda5da8dSAndroid Build Coastguard Worker            elif ext in self._cpp_extensions:
352*cda5da8dSAndroid Build Coastguard Worker                input_opt = "/Tp" + src
353*cda5da8dSAndroid Build Coastguard Worker                add_cpp_opts = True
354*cda5da8dSAndroid Build Coastguard Worker            elif ext in self._rc_extensions:
355*cda5da8dSAndroid Build Coastguard Worker                # compile .RC to .RES file
356*cda5da8dSAndroid Build Coastguard Worker                input_opt = src
357*cda5da8dSAndroid Build Coastguard Worker                output_opt = "/fo" + obj
358*cda5da8dSAndroid Build Coastguard Worker                try:
359*cda5da8dSAndroid Build Coastguard Worker                    self.spawn([self.rc] + pp_opts + [output_opt, input_opt])
360*cda5da8dSAndroid Build Coastguard Worker                except DistutilsExecError as msg:
361*cda5da8dSAndroid Build Coastguard Worker                    raise CompileError(msg)
362*cda5da8dSAndroid Build Coastguard Worker                continue
363*cda5da8dSAndroid Build Coastguard Worker            elif ext in self._mc_extensions:
364*cda5da8dSAndroid Build Coastguard Worker                # Compile .MC to .RC file to .RES file.
365*cda5da8dSAndroid Build Coastguard Worker                #   * '-h dir' specifies the directory for the
366*cda5da8dSAndroid Build Coastguard Worker                #     generated include file
367*cda5da8dSAndroid Build Coastguard Worker                #   * '-r dir' specifies the target directory of the
368*cda5da8dSAndroid Build Coastguard Worker                #     generated RC file and the binary message resource
369*cda5da8dSAndroid Build Coastguard Worker                #     it includes
370*cda5da8dSAndroid Build Coastguard Worker                #
371*cda5da8dSAndroid Build Coastguard Worker                # For now (since there are no options to change this),
372*cda5da8dSAndroid Build Coastguard Worker                # we use the source-directory for the include file and
373*cda5da8dSAndroid Build Coastguard Worker                # the build directory for the RC file and message
374*cda5da8dSAndroid Build Coastguard Worker                # resources. This works at least for win32all.
375*cda5da8dSAndroid Build Coastguard Worker                h_dir = os.path.dirname(src)
376*cda5da8dSAndroid Build Coastguard Worker                rc_dir = os.path.dirname(obj)
377*cda5da8dSAndroid Build Coastguard Worker                try:
378*cda5da8dSAndroid Build Coastguard Worker                    # first compile .MC to .RC and .H file
379*cda5da8dSAndroid Build Coastguard Worker                    self.spawn([self.mc, '-h', h_dir, '-r', rc_dir, src])
380*cda5da8dSAndroid Build Coastguard Worker                    base, _ = os.path.splitext(os.path.basename (src))
381*cda5da8dSAndroid Build Coastguard Worker                    rc_file = os.path.join(rc_dir, base + '.rc')
382*cda5da8dSAndroid Build Coastguard Worker                    # then compile .RC to .RES file
383*cda5da8dSAndroid Build Coastguard Worker                    self.spawn([self.rc, "/fo" + obj, rc_file])
384*cda5da8dSAndroid Build Coastguard Worker
385*cda5da8dSAndroid Build Coastguard Worker                except DistutilsExecError as msg:
386*cda5da8dSAndroid Build Coastguard Worker                    raise CompileError(msg)
387*cda5da8dSAndroid Build Coastguard Worker                continue
388*cda5da8dSAndroid Build Coastguard Worker            else:
389*cda5da8dSAndroid Build Coastguard Worker                # how to handle this file?
390*cda5da8dSAndroid Build Coastguard Worker                raise CompileError("Don't know how to compile {} to {}"
391*cda5da8dSAndroid Build Coastguard Worker                                   .format(src, obj))
392*cda5da8dSAndroid Build Coastguard Worker
393*cda5da8dSAndroid Build Coastguard Worker            args = [self.cc] + compile_opts + pp_opts
394*cda5da8dSAndroid Build Coastguard Worker            if add_cpp_opts:
395*cda5da8dSAndroid Build Coastguard Worker                args.append('/EHsc')
396*cda5da8dSAndroid Build Coastguard Worker            args.append(input_opt)
397*cda5da8dSAndroid Build Coastguard Worker            args.append("/Fo" + obj)
398*cda5da8dSAndroid Build Coastguard Worker            args.extend(extra_postargs)
399*cda5da8dSAndroid Build Coastguard Worker
400*cda5da8dSAndroid Build Coastguard Worker            try:
401*cda5da8dSAndroid Build Coastguard Worker                self.spawn(args)
402*cda5da8dSAndroid Build Coastguard Worker            except DistutilsExecError as msg:
403*cda5da8dSAndroid Build Coastguard Worker                raise CompileError(msg)
404*cda5da8dSAndroid Build Coastguard Worker
405*cda5da8dSAndroid Build Coastguard Worker        return objects
406*cda5da8dSAndroid Build Coastguard Worker
407*cda5da8dSAndroid Build Coastguard Worker
408*cda5da8dSAndroid Build Coastguard Worker    def create_static_lib(self,
409*cda5da8dSAndroid Build Coastguard Worker                          objects,
410*cda5da8dSAndroid Build Coastguard Worker                          output_libname,
411*cda5da8dSAndroid Build Coastguard Worker                          output_dir=None,
412*cda5da8dSAndroid Build Coastguard Worker                          debug=0,
413*cda5da8dSAndroid Build Coastguard Worker                          target_lang=None):
414*cda5da8dSAndroid Build Coastguard Worker
415*cda5da8dSAndroid Build Coastguard Worker        if not self.initialized:
416*cda5da8dSAndroid Build Coastguard Worker            self.initialize()
417*cda5da8dSAndroid Build Coastguard Worker        objects, output_dir = self._fix_object_args(objects, output_dir)
418*cda5da8dSAndroid Build Coastguard Worker        output_filename = self.library_filename(output_libname,
419*cda5da8dSAndroid Build Coastguard Worker                                                output_dir=output_dir)
420*cda5da8dSAndroid Build Coastguard Worker
421*cda5da8dSAndroid Build Coastguard Worker        if self._need_link(objects, output_filename):
422*cda5da8dSAndroid Build Coastguard Worker            lib_args = objects + ['/OUT:' + output_filename]
423*cda5da8dSAndroid Build Coastguard Worker            if debug:
424*cda5da8dSAndroid Build Coastguard Worker                pass # XXX what goes here?
425*cda5da8dSAndroid Build Coastguard Worker            try:
426*cda5da8dSAndroid Build Coastguard Worker                log.debug('Executing "%s" %s', self.lib, ' '.join(lib_args))
427*cda5da8dSAndroid Build Coastguard Worker                self.spawn([self.lib] + lib_args)
428*cda5da8dSAndroid Build Coastguard Worker            except DistutilsExecError as msg:
429*cda5da8dSAndroid Build Coastguard Worker                raise LibError(msg)
430*cda5da8dSAndroid Build Coastguard Worker        else:
431*cda5da8dSAndroid Build Coastguard Worker            log.debug("skipping %s (up-to-date)", output_filename)
432*cda5da8dSAndroid Build Coastguard Worker
433*cda5da8dSAndroid Build Coastguard Worker
434*cda5da8dSAndroid Build Coastguard Worker    def link(self,
435*cda5da8dSAndroid Build Coastguard Worker             target_desc,
436*cda5da8dSAndroid Build Coastguard Worker             objects,
437*cda5da8dSAndroid Build Coastguard Worker             output_filename,
438*cda5da8dSAndroid Build Coastguard Worker             output_dir=None,
439*cda5da8dSAndroid Build Coastguard Worker             libraries=None,
440*cda5da8dSAndroid Build Coastguard Worker             library_dirs=None,
441*cda5da8dSAndroid Build Coastguard Worker             runtime_library_dirs=None,
442*cda5da8dSAndroid Build Coastguard Worker             export_symbols=None,
443*cda5da8dSAndroid Build Coastguard Worker             debug=0,
444*cda5da8dSAndroid Build Coastguard Worker             extra_preargs=None,
445*cda5da8dSAndroid Build Coastguard Worker             extra_postargs=None,
446*cda5da8dSAndroid Build Coastguard Worker             build_temp=None,
447*cda5da8dSAndroid Build Coastguard Worker             target_lang=None):
448*cda5da8dSAndroid Build Coastguard Worker
449*cda5da8dSAndroid Build Coastguard Worker        if not self.initialized:
450*cda5da8dSAndroid Build Coastguard Worker            self.initialize()
451*cda5da8dSAndroid Build Coastguard Worker        objects, output_dir = self._fix_object_args(objects, output_dir)
452*cda5da8dSAndroid Build Coastguard Worker        fixed_args = self._fix_lib_args(libraries, library_dirs,
453*cda5da8dSAndroid Build Coastguard Worker                                        runtime_library_dirs)
454*cda5da8dSAndroid Build Coastguard Worker        libraries, library_dirs, runtime_library_dirs = fixed_args
455*cda5da8dSAndroid Build Coastguard Worker
456*cda5da8dSAndroid Build Coastguard Worker        if runtime_library_dirs:
457*cda5da8dSAndroid Build Coastguard Worker            self.warn("I don't know what to do with 'runtime_library_dirs': "
458*cda5da8dSAndroid Build Coastguard Worker                       + str(runtime_library_dirs))
459*cda5da8dSAndroid Build Coastguard Worker
460*cda5da8dSAndroid Build Coastguard Worker        lib_opts = gen_lib_options(self,
461*cda5da8dSAndroid Build Coastguard Worker                                   library_dirs, runtime_library_dirs,
462*cda5da8dSAndroid Build Coastguard Worker                                   libraries)
463*cda5da8dSAndroid Build Coastguard Worker        if output_dir is not None:
464*cda5da8dSAndroid Build Coastguard Worker            output_filename = os.path.join(output_dir, output_filename)
465*cda5da8dSAndroid Build Coastguard Worker
466*cda5da8dSAndroid Build Coastguard Worker        if self._need_link(objects, output_filename):
467*cda5da8dSAndroid Build Coastguard Worker            ldflags = self._ldflags[target_desc, debug]
468*cda5da8dSAndroid Build Coastguard Worker
469*cda5da8dSAndroid Build Coastguard Worker            export_opts = ["/EXPORT:" + sym for sym in (export_symbols or [])]
470*cda5da8dSAndroid Build Coastguard Worker
471*cda5da8dSAndroid Build Coastguard Worker            ld_args = (ldflags + lib_opts + export_opts +
472*cda5da8dSAndroid Build Coastguard Worker                       objects + ['/OUT:' + output_filename])
473*cda5da8dSAndroid Build Coastguard Worker
474*cda5da8dSAndroid Build Coastguard Worker            # The MSVC linker generates .lib and .exp files, which cannot be
475*cda5da8dSAndroid Build Coastguard Worker            # suppressed by any linker switches. The .lib files may even be
476*cda5da8dSAndroid Build Coastguard Worker            # needed! Make sure they are generated in the temporary build
477*cda5da8dSAndroid Build Coastguard Worker            # directory. Since they have different names for debug and release
478*cda5da8dSAndroid Build Coastguard Worker            # builds, they can go into the same directory.
479*cda5da8dSAndroid Build Coastguard Worker            build_temp = os.path.dirname(objects[0])
480*cda5da8dSAndroid Build Coastguard Worker            if export_symbols is not None:
481*cda5da8dSAndroid Build Coastguard Worker                (dll_name, dll_ext) = os.path.splitext(
482*cda5da8dSAndroid Build Coastguard Worker                    os.path.basename(output_filename))
483*cda5da8dSAndroid Build Coastguard Worker                implib_file = os.path.join(
484*cda5da8dSAndroid Build Coastguard Worker                    build_temp,
485*cda5da8dSAndroid Build Coastguard Worker                    self.library_filename(dll_name))
486*cda5da8dSAndroid Build Coastguard Worker                ld_args.append ('/IMPLIB:' + implib_file)
487*cda5da8dSAndroid Build Coastguard Worker
488*cda5da8dSAndroid Build Coastguard Worker            if extra_preargs:
489*cda5da8dSAndroid Build Coastguard Worker                ld_args[:0] = extra_preargs
490*cda5da8dSAndroid Build Coastguard Worker            if extra_postargs:
491*cda5da8dSAndroid Build Coastguard Worker                ld_args.extend(extra_postargs)
492*cda5da8dSAndroid Build Coastguard Worker
493*cda5da8dSAndroid Build Coastguard Worker            output_dir = os.path.dirname(os.path.abspath(output_filename))
494*cda5da8dSAndroid Build Coastguard Worker            self.mkpath(output_dir)
495*cda5da8dSAndroid Build Coastguard Worker            try:
496*cda5da8dSAndroid Build Coastguard Worker                log.debug('Executing "%s" %s', self.linker, ' '.join(ld_args))
497*cda5da8dSAndroid Build Coastguard Worker                self.spawn([self.linker] + ld_args)
498*cda5da8dSAndroid Build Coastguard Worker            except DistutilsExecError as msg:
499*cda5da8dSAndroid Build Coastguard Worker                raise LinkError(msg)
500*cda5da8dSAndroid Build Coastguard Worker        else:
501*cda5da8dSAndroid Build Coastguard Worker            log.debug("skipping %s (up-to-date)", output_filename)
502*cda5da8dSAndroid Build Coastguard Worker
503*cda5da8dSAndroid Build Coastguard Worker    def spawn(self, cmd):
504*cda5da8dSAndroid Build Coastguard Worker        old_path = os.getenv('path')
505*cda5da8dSAndroid Build Coastguard Worker        try:
506*cda5da8dSAndroid Build Coastguard Worker            os.environ['path'] = self._paths
507*cda5da8dSAndroid Build Coastguard Worker            return super().spawn(cmd)
508*cda5da8dSAndroid Build Coastguard Worker        finally:
509*cda5da8dSAndroid Build Coastguard Worker            os.environ['path'] = old_path
510*cda5da8dSAndroid Build Coastguard Worker
511*cda5da8dSAndroid Build Coastguard Worker    # -- Miscellaneous methods -----------------------------------------
512*cda5da8dSAndroid Build Coastguard Worker    # These are all used by the 'gen_lib_options() function, in
513*cda5da8dSAndroid Build Coastguard Worker    # ccompiler.py.
514*cda5da8dSAndroid Build Coastguard Worker
515*cda5da8dSAndroid Build Coastguard Worker    def library_dir_option(self, dir):
516*cda5da8dSAndroid Build Coastguard Worker        return "/LIBPATH:" + dir
517*cda5da8dSAndroid Build Coastguard Worker
518*cda5da8dSAndroid Build Coastguard Worker    def runtime_library_dir_option(self, dir):
519*cda5da8dSAndroid Build Coastguard Worker        raise DistutilsPlatformError(
520*cda5da8dSAndroid Build Coastguard Worker              "don't know how to set runtime library search path for MSVC")
521*cda5da8dSAndroid Build Coastguard Worker
522*cda5da8dSAndroid Build Coastguard Worker    def library_option(self, lib):
523*cda5da8dSAndroid Build Coastguard Worker        return self.library_filename(lib)
524*cda5da8dSAndroid Build Coastguard Worker
525*cda5da8dSAndroid Build Coastguard Worker    def find_library_file(self, dirs, lib, debug=0):
526*cda5da8dSAndroid Build Coastguard Worker        # Prefer a debugging library if found (and requested), but deal
527*cda5da8dSAndroid Build Coastguard Worker        # with it if we don't have one.
528*cda5da8dSAndroid Build Coastguard Worker        if debug:
529*cda5da8dSAndroid Build Coastguard Worker            try_names = [lib + "_d", lib]
530*cda5da8dSAndroid Build Coastguard Worker        else:
531*cda5da8dSAndroid Build Coastguard Worker            try_names = [lib]
532*cda5da8dSAndroid Build Coastguard Worker        for dir in dirs:
533*cda5da8dSAndroid Build Coastguard Worker            for name in try_names:
534*cda5da8dSAndroid Build Coastguard Worker                libfile = os.path.join(dir, self.library_filename(name))
535*cda5da8dSAndroid Build Coastguard Worker                if os.path.isfile(libfile):
536*cda5da8dSAndroid Build Coastguard Worker                    return libfile
537*cda5da8dSAndroid Build Coastguard Worker        else:
538*cda5da8dSAndroid Build Coastguard Worker            # Oops, didn't find it in *any* of 'dirs'
539*cda5da8dSAndroid Build Coastguard Worker            return None
540