xref: /aosp_15_r20/external/angle/build/toolchain/win/tool_wrapper.py (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1*8975f5c5SAndroid Build Coastguard Worker# Copyright 2012 The Chromium Authors
2*8975f5c5SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be
3*8975f5c5SAndroid Build Coastguard Worker# found in the LICENSE file.
4*8975f5c5SAndroid Build Coastguard Worker
5*8975f5c5SAndroid Build Coastguard Worker"""Utility functions for Windows builds.
6*8975f5c5SAndroid Build Coastguard Worker
7*8975f5c5SAndroid Build Coastguard WorkerThis file is copied to the build directory as part of toolchain setup and
8*8975f5c5SAndroid Build Coastguard Workeris used to set up calls to tools used by the build that need wrappers.
9*8975f5c5SAndroid Build Coastguard Worker"""
10*8975f5c5SAndroid Build Coastguard Worker
11*8975f5c5SAndroid Build Coastguard Worker
12*8975f5c5SAndroid Build Coastguard Workerimport os
13*8975f5c5SAndroid Build Coastguard Workerimport re
14*8975f5c5SAndroid Build Coastguard Workerimport shutil
15*8975f5c5SAndroid Build Coastguard Workerimport subprocess
16*8975f5c5SAndroid Build Coastguard Workerimport stat
17*8975f5c5SAndroid Build Coastguard Workerimport sys
18*8975f5c5SAndroid Build Coastguard Worker
19*8975f5c5SAndroid Build Coastguard Worker
20*8975f5c5SAndroid Build Coastguard WorkerBASE_DIR = os.path.dirname(os.path.abspath(__file__))
21*8975f5c5SAndroid Build Coastguard Worker
22*8975f5c5SAndroid Build Coastguard Worker# A regex matching an argument corresponding to the output filename passed to
23*8975f5c5SAndroid Build Coastguard Worker# link.exe.
24*8975f5c5SAndroid Build Coastguard Worker_LINK_EXE_OUT_ARG = re.compile('/OUT:(?P<out>.+)$', re.IGNORECASE)
25*8975f5c5SAndroid Build Coastguard Worker
26*8975f5c5SAndroid Build Coastguard Workerdef main(args):
27*8975f5c5SAndroid Build Coastguard Worker  exit_code = WinTool().Dispatch(args)
28*8975f5c5SAndroid Build Coastguard Worker  if exit_code is not None:
29*8975f5c5SAndroid Build Coastguard Worker    sys.exit(exit_code)
30*8975f5c5SAndroid Build Coastguard Worker
31*8975f5c5SAndroid Build Coastguard Worker
32*8975f5c5SAndroid Build Coastguard Workerclass WinTool(object):
33*8975f5c5SAndroid Build Coastguard Worker  """This class performs all the Windows tooling steps. The methods can either
34*8975f5c5SAndroid Build Coastguard Worker  be executed directly, or dispatched from an argument list."""
35*8975f5c5SAndroid Build Coastguard Worker
36*8975f5c5SAndroid Build Coastguard Worker  def _UseSeparateMspdbsrv(self, env, args):
37*8975f5c5SAndroid Build Coastguard Worker    """Allows to use a unique instance of mspdbsrv.exe per linker instead of a
38*8975f5c5SAndroid Build Coastguard Worker    shared one."""
39*8975f5c5SAndroid Build Coastguard Worker    if len(args) < 1:
40*8975f5c5SAndroid Build Coastguard Worker      raise Exception("Not enough arguments")
41*8975f5c5SAndroid Build Coastguard Worker
42*8975f5c5SAndroid Build Coastguard Worker    if args[0] != 'link.exe':
43*8975f5c5SAndroid Build Coastguard Worker      return
44*8975f5c5SAndroid Build Coastguard Worker
45*8975f5c5SAndroid Build Coastguard Worker    # Use the output filename passed to the linker to generate an endpoint name
46*8975f5c5SAndroid Build Coastguard Worker    # for mspdbsrv.exe.
47*8975f5c5SAndroid Build Coastguard Worker    endpoint_name = None
48*8975f5c5SAndroid Build Coastguard Worker    for arg in args:
49*8975f5c5SAndroid Build Coastguard Worker      m = _LINK_EXE_OUT_ARG.match(arg)
50*8975f5c5SAndroid Build Coastguard Worker      if m:
51*8975f5c5SAndroid Build Coastguard Worker        endpoint_name = re.sub(r'\W+', '',
52*8975f5c5SAndroid Build Coastguard Worker            '%s_%d' % (m.group('out'), os.getpid()))
53*8975f5c5SAndroid Build Coastguard Worker        break
54*8975f5c5SAndroid Build Coastguard Worker
55*8975f5c5SAndroid Build Coastguard Worker    if endpoint_name is None:
56*8975f5c5SAndroid Build Coastguard Worker      return
57*8975f5c5SAndroid Build Coastguard Worker
58*8975f5c5SAndroid Build Coastguard Worker    # Adds the appropriate environment variable. This will be read by link.exe
59*8975f5c5SAndroid Build Coastguard Worker    # to know which instance of mspdbsrv.exe it should connect to (if it's
60*8975f5c5SAndroid Build Coastguard Worker    # not set then the default endpoint is used).
61*8975f5c5SAndroid Build Coastguard Worker    env['_MSPDBSRV_ENDPOINT_'] = endpoint_name
62*8975f5c5SAndroid Build Coastguard Worker
63*8975f5c5SAndroid Build Coastguard Worker  def Dispatch(self, args):
64*8975f5c5SAndroid Build Coastguard Worker    """Dispatches a string command to a method."""
65*8975f5c5SAndroid Build Coastguard Worker    if len(args) < 1:
66*8975f5c5SAndroid Build Coastguard Worker      raise Exception("Not enough arguments")
67*8975f5c5SAndroid Build Coastguard Worker
68*8975f5c5SAndroid Build Coastguard Worker    method = "Exec%s" % self._CommandifyName(args[0])
69*8975f5c5SAndroid Build Coastguard Worker    return getattr(self, method)(*args[1:])
70*8975f5c5SAndroid Build Coastguard Worker
71*8975f5c5SAndroid Build Coastguard Worker  def _CommandifyName(self, name_string):
72*8975f5c5SAndroid Build Coastguard Worker    """Transforms a tool name like recursive-mirror to RecursiveMirror."""
73*8975f5c5SAndroid Build Coastguard Worker    return name_string.title().replace('-', '')
74*8975f5c5SAndroid Build Coastguard Worker
75*8975f5c5SAndroid Build Coastguard Worker  def _GetEnv(self, arch):
76*8975f5c5SAndroid Build Coastguard Worker    """Gets the saved environment from a file for a given architecture."""
77*8975f5c5SAndroid Build Coastguard Worker    # The environment is saved as an "environment block" (see CreateProcess
78*8975f5c5SAndroid Build Coastguard Worker    # and msvs_emulation for details). We convert to a dict here.
79*8975f5c5SAndroid Build Coastguard Worker    # Drop last 2 NULs, one for list terminator, one for trailing vs. separator.
80*8975f5c5SAndroid Build Coastguard Worker    pairs = open(arch).read()[:-2].split('\0')
81*8975f5c5SAndroid Build Coastguard Worker    kvs = [item.split('=', 1) for item in pairs]
82*8975f5c5SAndroid Build Coastguard Worker    return dict(kvs)
83*8975f5c5SAndroid Build Coastguard Worker
84*8975f5c5SAndroid Build Coastguard Worker  def ExecDeleteFile(self, path):
85*8975f5c5SAndroid Build Coastguard Worker    """Simple file delete command."""
86*8975f5c5SAndroid Build Coastguard Worker    if os.path.exists(path):
87*8975f5c5SAndroid Build Coastguard Worker      os.unlink(path)
88*8975f5c5SAndroid Build Coastguard Worker
89*8975f5c5SAndroid Build Coastguard Worker  def ExecRecursiveMirror(self, source, dest):
90*8975f5c5SAndroid Build Coastguard Worker    """Emulation of rm -rf out && cp -af in out."""
91*8975f5c5SAndroid Build Coastguard Worker    if os.path.exists(dest):
92*8975f5c5SAndroid Build Coastguard Worker      if os.path.isdir(dest):
93*8975f5c5SAndroid Build Coastguard Worker        def _on_error(fn, path, dummy_excinfo):
94*8975f5c5SAndroid Build Coastguard Worker          # The operation failed, possibly because the file is set to
95*8975f5c5SAndroid Build Coastguard Worker          # read-only. If that's why, make it writable and try the op again.
96*8975f5c5SAndroid Build Coastguard Worker          if not os.access(path, os.W_OK):
97*8975f5c5SAndroid Build Coastguard Worker            os.chmod(path, stat.S_IWRITE)
98*8975f5c5SAndroid Build Coastguard Worker          fn(path)
99*8975f5c5SAndroid Build Coastguard Worker        shutil.rmtree(dest, onerror=_on_error)
100*8975f5c5SAndroid Build Coastguard Worker      else:
101*8975f5c5SAndroid Build Coastguard Worker        if not os.access(dest, os.W_OK):
102*8975f5c5SAndroid Build Coastguard Worker          # Attempt to make the file writable before deleting it.
103*8975f5c5SAndroid Build Coastguard Worker          os.chmod(dest, stat.S_IWRITE)
104*8975f5c5SAndroid Build Coastguard Worker        os.unlink(dest)
105*8975f5c5SAndroid Build Coastguard Worker
106*8975f5c5SAndroid Build Coastguard Worker    if os.path.isdir(source):
107*8975f5c5SAndroid Build Coastguard Worker      shutil.copytree(source, dest)
108*8975f5c5SAndroid Build Coastguard Worker    else:
109*8975f5c5SAndroid Build Coastguard Worker      shutil.copy2(source, dest)
110*8975f5c5SAndroid Build Coastguard Worker      # Try to diagnose crbug.com/741603
111*8975f5c5SAndroid Build Coastguard Worker      if not os.path.exists(dest):
112*8975f5c5SAndroid Build Coastguard Worker        raise Exception("Copying of %s to %s failed" % (source, dest))
113*8975f5c5SAndroid Build Coastguard Worker
114*8975f5c5SAndroid Build Coastguard Worker  def ExecLinkWrapper(self, arch, use_separate_mspdbsrv, *args):
115*8975f5c5SAndroid Build Coastguard Worker    """Filter diagnostic output from link that looks like:
116*8975f5c5SAndroid Build Coastguard Worker    '   Creating library ui.dll.lib and object ui.dll.exp'
117*8975f5c5SAndroid Build Coastguard Worker    This happens when there are exports from the dll or exe.
118*8975f5c5SAndroid Build Coastguard Worker    """
119*8975f5c5SAndroid Build Coastguard Worker    env = self._GetEnv(arch)
120*8975f5c5SAndroid Build Coastguard Worker    if use_separate_mspdbsrv == 'True':
121*8975f5c5SAndroid Build Coastguard Worker      self._UseSeparateMspdbsrv(env, args)
122*8975f5c5SAndroid Build Coastguard Worker    if sys.platform == 'win32':
123*8975f5c5SAndroid Build Coastguard Worker      args = list(args)  # *args is a tuple by default, which is read-only.
124*8975f5c5SAndroid Build Coastguard Worker      args[0] = args[0].replace('/', '\\')
125*8975f5c5SAndroid Build Coastguard Worker    # https://docs.python.org/2/library/subprocess.html:
126*8975f5c5SAndroid Build Coastguard Worker    # "On Unix with shell=True [...] if args is a sequence, the first item
127*8975f5c5SAndroid Build Coastguard Worker    # specifies the command string, and any additional items will be treated as
128*8975f5c5SAndroid Build Coastguard Worker    # additional arguments to the shell itself.  That is to say, Popen does the
129*8975f5c5SAndroid Build Coastguard Worker    # equivalent of:
130*8975f5c5SAndroid Build Coastguard Worker    #   Popen(['/bin/sh', '-c', args[0], args[1], ...])"
131*8975f5c5SAndroid Build Coastguard Worker    # For that reason, since going through the shell doesn't seem necessary on
132*8975f5c5SAndroid Build Coastguard Worker    # non-Windows don't do that there.
133*8975f5c5SAndroid Build Coastguard Worker    pe_name = None
134*8975f5c5SAndroid Build Coastguard Worker    for arg in args:
135*8975f5c5SAndroid Build Coastguard Worker      m = _LINK_EXE_OUT_ARG.match(arg)
136*8975f5c5SAndroid Build Coastguard Worker      if m:
137*8975f5c5SAndroid Build Coastguard Worker        pe_name = m.group('out')
138*8975f5c5SAndroid Build Coastguard Worker    link = subprocess.Popen(args, shell=sys.platform == 'win32', env=env,
139*8975f5c5SAndroid Build Coastguard Worker                            stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
140*8975f5c5SAndroid Build Coastguard Worker    # Read output one line at a time as it shows up to avoid OOM failures when
141*8975f5c5SAndroid Build Coastguard Worker    # GBs of output is produced.
142*8975f5c5SAndroid Build Coastguard Worker    for line in link.stdout:
143*8975f5c5SAndroid Build Coastguard Worker      line = line.decode('utf8')
144*8975f5c5SAndroid Build Coastguard Worker      if (not line.startswith('   Creating library ')
145*8975f5c5SAndroid Build Coastguard Worker          and not line.startswith('Generating code')
146*8975f5c5SAndroid Build Coastguard Worker          and not line.startswith('Finished generating code')):
147*8975f5c5SAndroid Build Coastguard Worker        print(line.rstrip())
148*8975f5c5SAndroid Build Coastguard Worker    return link.wait()
149*8975f5c5SAndroid Build Coastguard Worker
150*8975f5c5SAndroid Build Coastguard Worker  def ExecAsmWrapper(self, arch, *args):
151*8975f5c5SAndroid Build Coastguard Worker    """Filter logo banner from invocations of asm.exe."""
152*8975f5c5SAndroid Build Coastguard Worker    env = self._GetEnv(arch)
153*8975f5c5SAndroid Build Coastguard Worker    if sys.platform == 'win32':
154*8975f5c5SAndroid Build Coastguard Worker      # Windows ARM64 uses clang-cl as assembler which has '/' as path
155*8975f5c5SAndroid Build Coastguard Worker      # separator, convert it to '\\' when running on Windows.
156*8975f5c5SAndroid Build Coastguard Worker      args = list(args) # *args is a tuple by default, which is read-only
157*8975f5c5SAndroid Build Coastguard Worker      args[0] = args[0].replace('/', '\\')
158*8975f5c5SAndroid Build Coastguard Worker    # See comment in ExecLinkWrapper() for why shell=False on non-win.
159*8975f5c5SAndroid Build Coastguard Worker    popen = subprocess.Popen(args, shell=sys.platform == 'win32', env=env,
160*8975f5c5SAndroid Build Coastguard Worker                             stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
161*8975f5c5SAndroid Build Coastguard Worker    out, _ = popen.communicate()
162*8975f5c5SAndroid Build Coastguard Worker    for line in out.decode('utf8').splitlines():
163*8975f5c5SAndroid Build Coastguard Worker      if not line.startswith(' Assembling: '):
164*8975f5c5SAndroid Build Coastguard Worker        print(line)
165*8975f5c5SAndroid Build Coastguard Worker    return popen.returncode
166*8975f5c5SAndroid Build Coastguard Worker
167*8975f5c5SAndroid Build Coastguard Worker  def ExecRcWrapper(self, arch, *args):
168*8975f5c5SAndroid Build Coastguard Worker    """Converts .rc files to .res files."""
169*8975f5c5SAndroid Build Coastguard Worker    env = self._GetEnv(arch)
170*8975f5c5SAndroid Build Coastguard Worker    args = list(args)
171*8975f5c5SAndroid Build Coastguard Worker    rcpy_args = args[:]
172*8975f5c5SAndroid Build Coastguard Worker    rcpy_args[0:1] = [sys.executable, os.path.join(BASE_DIR, 'rc', 'rc.py')]
173*8975f5c5SAndroid Build Coastguard Worker    rcpy_args.append('/showIncludes')
174*8975f5c5SAndroid Build Coastguard Worker    return subprocess.call(rcpy_args, env=env)
175*8975f5c5SAndroid Build Coastguard Worker
176*8975f5c5SAndroid Build Coastguard Worker  def ExecActionWrapper(self, arch, rspfile, *dirname):
177*8975f5c5SAndroid Build Coastguard Worker    """Runs an action command line from a response file using the environment
178*8975f5c5SAndroid Build Coastguard Worker    for |arch|. If |dirname| is supplied, use that as the working directory."""
179*8975f5c5SAndroid Build Coastguard Worker    env = self._GetEnv(arch)
180*8975f5c5SAndroid Build Coastguard Worker    # TODO(scottmg): This is a temporary hack to get some specific variables
181*8975f5c5SAndroid Build Coastguard Worker    # through to actions that are set after GN-time. http://crbug.com/333738.
182*8975f5c5SAndroid Build Coastguard Worker    for k, v in os.environ.items():
183*8975f5c5SAndroid Build Coastguard Worker      if k not in env:
184*8975f5c5SAndroid Build Coastguard Worker        env[k] = v
185*8975f5c5SAndroid Build Coastguard Worker    args = open(rspfile).read()
186*8975f5c5SAndroid Build Coastguard Worker    dirname = dirname[0] if dirname else None
187*8975f5c5SAndroid Build Coastguard Worker    return subprocess.call(args, shell=True, env=env, cwd=dirname)
188*8975f5c5SAndroid Build Coastguard Worker
189*8975f5c5SAndroid Build Coastguard Worker
190*8975f5c5SAndroid Build Coastguard Workerif __name__ == '__main__':
191*8975f5c5SAndroid Build Coastguard Worker  sys.exit(main(sys.argv[1:]))
192