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