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.
7 """Utility functions for Windows builds.
9 These functions are executed via gyp-win-tool when using the ninja generator.
17 BASE_DIR = os.path.dirname(os.path.abspath(__file__))
22 exit_code = executor.Dispatch(args)
23 if exit_code is not None:
27 class 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."""
31 def Dispatch(self, args):
32 """Dispatches a string command to a method."""
34 raise Exception("Not enough arguments")
36 method = "Exec%s" % self._CommandifyName(args[0])
37 return getattr(self, method)(*args[1:])
39 def _CommandifyName(self, name_string):
40 """Transforms a tool name like recursive-mirror to RecursiveMirror."""
41 return name_string.title().replace('-', '')
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]
52 def ExecStamp(self, path):
53 """Simple stamp command."""
54 open(path, 'w').close()
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):
63 if os.path.isdir(source):
64 shutil.copytree(source, dest)
66 shutil.copy2(source, dest)
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.
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 '):
80 return popen.returncode
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
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:
93 return popen.returncode
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"' % (
103 os.path.abspath(manifest_path).replace('\\', '/')))
105 def ExecMidlWrapper(self, arch, outdir, tlb, h, dlldata, iid, proxy, idl,
107 """Filter noisy filenames output from MIDL compile step that isn't
108 quietable via command line flags.
110 args = ['midl', '/nologo'] + list(flags) + [
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
126 lines = out.splitlines()
127 prefix = 'Processing '
128 processing = set(os.path.basename(x) for x in lines if x.startswith(prefix))
130 if not line.startswith(prefix) and line not in processing:
132 return popen.returncode
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':
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
149 return popen.returncode
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
163 return popen.returncode
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)
173 return popen.returncode
175 if __name__ == '__main__':
176 sys.exit(main(sys.argv[1:]))