1#!/usr/bin/env python 2 3# Copyright (c) 2012 Google Inc. All rights reserved. 4# Use of this source code is governed by a BSD-style license that can be 5# found in the LICENSE file. 6 7"""Utility functions for Windows builds. 8 9These functions are executed via gyp-win-tool when using the ninja generator. 10""" 11 12import os 13import shutil 14import subprocess 15import sys 16 17BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 18 19 20def main(args): 21 executor = WinTool() 22 exit_code = executor.Dispatch(args) 23 if exit_code is not None: 24 sys.exit(exit_code) 25 26 27class WinTool(object): 28 """This class performs all the Windows tooling steps. The methods can either 29 be executed directly, or dispatched from an argument list.""" 30 31 def Dispatch(self, args): 32 """Dispatches a string command to a method.""" 33 if len(args) < 1: 34 raise Exception("Not enough arguments") 35 36 method = "Exec%s" % self._CommandifyName(args[0]) 37 return getattr(self, method)(*args[1:]) 38 39 def _CommandifyName(self, name_string): 40 """Transforms a tool name like recursive-mirror to RecursiveMirror.""" 41 return name_string.title().replace('-', '') 42 43 def _GetEnv(self, arch): 44 """Gets the saved environment from a file for a given architecture.""" 45 # The environment is saved as an "environment block" (see CreateProcess 46 # and msvs_emulation for details). We convert to a dict here. 47 # Drop last 2 NULs, one for list terminator, one for trailing vs. separator. 48 pairs = open(arch).read()[:-2].split('\0') 49 kvs = [item.split('=', 1) for item in pairs] 50 return dict(kvs) 51 52 def ExecStamp(self, path): 53 """Simple stamp command.""" 54 open(path, 'w').close() 55 56 def ExecRecursiveMirror(self, source, dest): 57 """Emulation of rm -rf out && cp -af in out.""" 58 if os.path.exists(dest): 59 if os.path.isdir(dest): 60 shutil.rmtree(dest) 61 else: 62 os.unlink(dest) 63 if os.path.isdir(source): 64 shutil.copytree(source, dest) 65 else: 66 shutil.copy2(source, dest) 67 68 def ExecLinkWrapper(self, arch, *args): 69 """Filter diagnostic output from link that looks like: 70 ' Creating library ui.dll.lib and object ui.dll.exp' 71 This happens when there are exports from the dll or exe. 72 """ 73 env = self._GetEnv(arch) 74 popen = subprocess.Popen(args, shell=True, env=env, 75 stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 76 out, _ = popen.communicate() 77 for line in out.splitlines(): 78 if not line.startswith(' Creating library '): 79 print line 80 return popen.returncode 81 82 def ExecManifestWrapper(self, arch, *args): 83 """Run manifest tool with environment set. Strip out undesirable warning 84 (some XML blocks are recognized by the OS loader, but not the manifest 85 tool).""" 86 env = self._GetEnv(arch) 87 popen = subprocess.Popen(args, shell=True, env=env, 88 stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 89 out, _ = popen.communicate() 90 for line in out.splitlines(): 91 if line and 'manifest authoring warning 81010002' not in line: 92 print line 93 return popen.returncode 94 95 def ExecManifestToRc(self, arch, *args): 96 """Creates a resource file pointing a SxS assembly manifest. 97 |args| is tuple containing path to resource file, path to manifest file 98 and resource name which can be "1" (for executables) or "2" (for DLLs).""" 99 manifest_path, resource_path, resource_name = args 100 with open(resource_path, 'wb') as output: 101 output.write('#include <windows.h>\n%s RT_MANIFEST "%s"' % ( 102 resource_name, 103 os.path.abspath(manifest_path).replace('\\', '/'))) 104 105 def ExecMidlWrapper(self, arch, outdir, tlb, h, dlldata, iid, proxy, idl, 106 *flags): 107 """Filter noisy filenames output from MIDL compile step that isn't 108 quietable via command line flags. 109 """ 110 args = ['midl', '/nologo'] + list(flags) + [ 111 '/out', outdir, 112 '/tlb', tlb, 113 '/h', h, 114 '/dlldata', dlldata, 115 '/iid', iid, 116 '/proxy', proxy, 117 idl] 118 env = self._GetEnv(arch) 119 popen = subprocess.Popen(args, shell=True, env=env, 120 stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 121 out, _ = popen.communicate() 122 # Filter junk out of stdout, and write filtered versions. Output we want 123 # to filter is pairs of lines that look like this: 124 # Processing C:\Program Files (x86)\Microsoft SDKs\...\include\objidl.idl 125 # objidl.idl 126 lines = out.splitlines() 127 prefix = 'Processing ' 128 processing = set(os.path.basename(x) for x in lines if x.startswith(prefix)) 129 for line in lines: 130 if not line.startswith(prefix) and line not in processing: 131 print line 132 return popen.returncode 133 134 def ExecAsmWrapper(self, arch, *args): 135 """Filter logo banner from invocations of asm.exe.""" 136 env = self._GetEnv(arch) 137 # MSVS doesn't assemble x64 asm files. 138 if arch == 'environment.x64': 139 return 0 140 popen = subprocess.Popen(args, shell=True, env=env, 141 stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 142 out, _ = popen.communicate() 143 for line in out.splitlines(): 144 if (not line.startswith('Copyright (C) Microsoft Corporation') and 145 not line.startswith('Microsoft (R) Macro Assembler') and 146 not line.startswith(' Assembling: ') and 147 line): 148 print line 149 return popen.returncode 150 151 def ExecRcWrapper(self, arch, *args): 152 """Filter logo banner from invocations of rc.exe. Older versions of RC 153 don't support the /nologo flag.""" 154 env = self._GetEnv(arch) 155 popen = subprocess.Popen(args, shell=True, env=env, 156 stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 157 out, _ = popen.communicate() 158 for line in out.splitlines(): 159 if (not line.startswith('Microsoft (R) Windows (R) Resource Compiler') and 160 not line.startswith('Copyright (C) Microsoft Corporation') and 161 line): 162 print line 163 return popen.returncode 164 165 def ExecActionWrapper(self, arch, rspfile, *dir): 166 """Runs an action command line from a response file using the environment 167 for |arch|. If |dir| is supplied, use that as the working directory.""" 168 env = self._GetEnv(arch) 169 args = open(rspfile).read() 170 dir = dir[0] if dir else None 171 popen = subprocess.Popen(args, shell=True, env=env, cwd=dir) 172 popen.wait() 173 return popen.returncode 174 175if __name__ == '__main__': 176 sys.exit(main(sys.argv[1:])) 177