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