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