Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(342)

Side by Side Diff: build/win/win_tool.py

Issue 694353003: Get `gn gen` to succeed on Windows (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: remove GYP_DEFINES code Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « build/vs_toolchain.py ('k') | net/BUILD.gn » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
9 These functions are executed via gyp-win-tool when using the ninja generator.
10 """
11
12 import os
13 import re
14 import shutil
15 import subprocess
16 import stat
17 import string
18 import sys
19
20 BASE_DIR = os.path.dirname(os.path.abspath(__file__))
21
22 # A regex matching an argument corresponding to the output filename passed to
23 # link.exe.
24 _LINK_EXE_OUT_ARG = re.compile('/OUT:(?P<out>.+)$', re.IGNORECASE)
25
26 def main(args):
27 executor = WinTool()
28 exit_code = executor.Dispatch(args)
29 if exit_code is not None:
30 sys.exit(exit_code)
31
32
33 class WinTool(object):
34 """This class performs all the Windows tooling steps. The methods can either
35 be executed directly, or dispatched from an argument list."""
36
37 def _UseSeparateMspdbsrv(self, env, args):
38 """Allows to use a unique instance of mspdbsrv.exe per linker instead of a
39 shared one."""
40 if len(args) < 1:
41 raise Exception("Not enough arguments")
42
43 if args[0] != 'link.exe':
44 return
45
46 # Use the output filename passed to the linker to generate an endpoint name
47 # for mspdbsrv.exe.
48 endpoint_name = None
49 for arg in args:
50 m = _LINK_EXE_OUT_ARG.match(arg)
51 if m:
52 endpoint_name = re.sub(r'\W+', '',
53 '%s_%d' % (m.group('out'), os.getpid()))
54 break
55
56 if endpoint_name is None:
57 return
58
59 # Adds the appropriate environment variable. This will be read by link.exe
60 # to know which instance of mspdbsrv.exe it should connect to (if it's
61 # not set then the default endpoint is used).
62 env['_MSPDBSRV_ENDPOINT_'] = endpoint_name
63
64 def Dispatch(self, args):
65 """Dispatches a string command to a method."""
66 if len(args) < 1:
67 raise Exception("Not enough arguments")
68
69 method = "Exec%s" % self._CommandifyName(args[0])
70 return getattr(self, method)(*args[1:])
71
72 def _CommandifyName(self, name_string):
73 """Transforms a tool name like recursive-mirror to RecursiveMirror."""
74 return name_string.title().replace('-', '')
75
76 def _GetEnv(self, arch):
77 """Gets the saved environment from a file for a given architecture."""
78 # The environment is saved as an "environment block" (see CreateProcess
79 # and msvs_emulation for details). We convert to a dict here.
80 # Drop last 2 NULs, one for list terminator, one for trailing vs. separator.
81 pairs = open(arch).read()[:-2].split('\0')
82 kvs = [item.split('=', 1) for item in pairs]
83 return dict(kvs)
84
85 def ExecStamp(self, path):
86 """Simple stamp command."""
87 open(path, 'w').close()
88
89 def ExecRecursiveMirror(self, source, dest):
90 """Emulation of rm -rf out && cp -af in out."""
91 if os.path.exists(dest):
92 if os.path.isdir(dest):
93 def _on_error(fn, path, excinfo):
94 # The operation failed, possibly because the file is set to
95 # read-only. If that's why, make it writable and try the op again.
96 if not os.access(path, os.W_OK):
97 os.chmod(path, stat.S_IWRITE)
98 fn(path)
99 shutil.rmtree(dest, onerror=_on_error)
100 else:
101 if not os.access(dest, os.W_OK):
102 # Attempt to make the file writable before deleting it.
103 os.chmod(dest, stat.S_IWRITE)
104 os.unlink(dest)
105
106 if os.path.isdir(source):
107 shutil.copytree(source, dest)
108 else:
109 shutil.copy2(source, dest)
110
111 def ExecLinkWrapper(self, arch, use_separate_mspdbsrv, *args):
112 """Filter diagnostic output from link that looks like:
113 ' Creating library ui.dll.lib and object ui.dll.exp'
114 This happens when there are exports from the dll or exe.
115 """
116 env = self._GetEnv(arch)
117 if use_separate_mspdbsrv == 'True':
118 self._UseSeparateMspdbsrv(env, args)
119 link = subprocess.Popen(args,
120 shell=True,
121 env=env,
122 stdout=subprocess.PIPE,
123 stderr=subprocess.STDOUT)
124 out, _ = link.communicate()
125 for line in out.splitlines():
126 if not line.startswith(' Creating library '):
127 print line
128 return link.returncode
129
130 def ExecLinkWithManifests(self, arch, embed_manifest, out, ldcmd, resname,
131 mt, rc, intermediate_manifest, *manifests):
132 """A wrapper for handling creating a manifest resource and then executing
133 a link command."""
134 # The 'normal' way to do manifests is to have link generate a manifest
135 # based on gathering dependencies from the object files, then merge that
136 # manifest with other manifests supplied as sources, convert the merged
137 # manifest to a resource, and then *relink*, including the compiled
138 # version of the manifest resource. This breaks incremental linking, and
139 # is generally overly complicated. Instead, we merge all the manifests
140 # provided (along with one that includes what would normally be in the
141 # linker-generated one, see msvs_emulation.py), and include that into the
142 # first and only link. We still tell link to generate a manifest, but we
143 # only use that to assert that our simpler process did not miss anything.
144 variables = {
145 'python': sys.executable,
146 'arch': arch,
147 'out': out,
148 'ldcmd': ldcmd,
149 'resname': resname,
150 'mt': mt,
151 'rc': rc,
152 'intermediate_manifest': intermediate_manifest,
153 'manifests': ' '.join(manifests),
154 }
155 add_to_ld = ''
156 if manifests:
157 subprocess.check_call(
158 '%(python)s gyp-win-tool manifest-wrapper %(arch)s %(mt)s -nologo '
159 '-manifest %(manifests)s -out:%(out)s.manifest' % variables)
160 if embed_manifest == 'True':
161 subprocess.check_call(
162 '%(python)s gyp-win-tool manifest-to-rc %(arch)s %(out)s.manifest'
163 ' %(out)s.manifest.rc %(resname)s' % variables)
164 subprocess.check_call(
165 '%(python)s gyp-win-tool rc-wrapper %(arch)s %(rc)s '
166 '%(out)s.manifest.rc' % variables)
167 add_to_ld = ' %(out)s.manifest.res' % variables
168 subprocess.check_call(ldcmd + add_to_ld)
169
170 # Run mt.exe on the theoretically complete manifest we generated, merging
171 # it with the one the linker generated to confirm that the linker
172 # generated one does not add anything. This is strictly unnecessary for
173 # correctness, it's only to verify that e.g. /MANIFESTDEPENDENCY was not
174 # used in a #pragma comment.
175 if manifests:
176 # Merge the intermediate one with ours to .assert.manifest, then check
177 # that .assert.manifest is identical to ours.
178 subprocess.check_call(
179 '%(python)s gyp-win-tool manifest-wrapper %(arch)s %(mt)s -nologo '
180 '-manifest %(out)s.manifest %(intermediate_manifest)s '
181 '-out:%(out)s.assert.manifest' % variables)
182 assert_manifest = '%(out)s.assert.manifest' % variables
183 our_manifest = '%(out)s.manifest' % variables
184 # Load and normalize the manifests. mt.exe sometimes removes whitespace,
185 # and sometimes doesn't unfortunately.
186 with open(our_manifest, 'rb') as our_f:
187 with open(assert_manifest, 'rb') as assert_f:
188 our_data = our_f.read().translate(None, string.whitespace)
189 assert_data = assert_f.read().translate(None, string.whitespace)
190 if our_data != assert_data:
191 os.unlink(out)
192 def dump(filename):
193 sys.stderr.write('%s\n-----\n' % filename)
194 with open(filename, 'rb') as f:
195 sys.stderr.write(f.read() + '\n-----\n')
196 dump(intermediate_manifest)
197 dump(our_manifest)
198 dump(assert_manifest)
199 sys.stderr.write(
200 'Linker generated manifest "%s" added to final manifest "%s" '
201 '(result in "%s"). '
202 'Were /MANIFEST switches used in #pragma statements? ' % (
203 intermediate_manifest, our_manifest, assert_manifest))
204 return 1
205
206 def ExecManifestWrapper(self, arch, *args):
207 """Run manifest tool with environment set. Strip out undesirable warning
208 (some XML blocks are recognized by the OS loader, but not the manifest
209 tool)."""
210 env = self._GetEnv(arch)
211 popen = subprocess.Popen(args, shell=True, env=env,
212 stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
213 out, _ = popen.communicate()
214 for line in out.splitlines():
215 if line and 'manifest authoring warning 81010002' not in line:
216 print line
217 return popen.returncode
218
219 def ExecManifestToRc(self, arch, *args):
220 """Creates a resource file pointing a SxS assembly manifest.
221 |args| is tuple containing path to resource file, path to manifest file
222 and resource name which can be "1" (for executables) or "2" (for DLLs)."""
223 manifest_path, resource_path, resource_name = args
224 with open(resource_path, 'wb') as output:
225 output.write('#include <windows.h>\n%s RT_MANIFEST "%s"' % (
226 resource_name,
227 os.path.abspath(manifest_path).replace('\\', '/')))
228
229 def ExecMidlWrapper(self, arch, outdir, tlb, h, dlldata, iid, proxy, idl,
230 *flags):
231 """Filter noisy filenames output from MIDL compile step that isn't
232 quietable via command line flags.
233 """
234 args = ['midl', '/nologo'] + list(flags) + [
235 '/out', outdir,
236 '/tlb', tlb,
237 '/h', h,
238 '/dlldata', dlldata,
239 '/iid', iid,
240 '/proxy', proxy,
241 idl]
242 env = self._GetEnv(arch)
243 popen = subprocess.Popen(args, shell=True, env=env,
244 stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
245 out, _ = popen.communicate()
246 # Filter junk out of stdout, and write filtered versions. Output we want
247 # to filter is pairs of lines that look like this:
248 # Processing C:\Program Files (x86)\Microsoft SDKs\...\include\objidl.idl
249 # objidl.idl
250 lines = out.splitlines()
251 prefixes = ('Processing ', '64 bit Processing ')
252 processing = set(os.path.basename(x)
253 for x in lines if x.startswith(prefixes))
254 for line in lines:
255 if not line.startswith(prefixes) and line not in processing:
256 print line
257 return popen.returncode
258
259 def ExecAsmWrapper(self, arch, *args):
260 """Filter logo banner from invocations of asm.exe."""
261 env = self._GetEnv(arch)
262 # MSVS doesn't assemble x64 asm files.
263 if arch == 'environment.x64':
264 return 0
265 popen = subprocess.Popen(args, shell=True, env=env,
266 stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
267 out, _ = popen.communicate()
268 for line in out.splitlines():
269 if (not line.startswith('Copyright (C) Microsoft Corporation') and
270 not line.startswith('Microsoft (R) Macro Assembler') and
271 not line.startswith(' Assembling: ') and
272 line):
273 print line
274 return popen.returncode
275
276 def ExecRcWrapper(self, arch, *args):
277 """Filter logo banner from invocations of rc.exe. Older versions of RC
278 don't support the /nologo flag."""
279 env = self._GetEnv(arch)
280 popen = subprocess.Popen(args, shell=True, env=env,
281 stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
282 out, _ = popen.communicate()
283 for line in out.splitlines():
284 if (not line.startswith('Microsoft (R) Windows (R) Resource Compiler') and
285 not line.startswith('Copyright (C) Microsoft Corporation') and
286 line):
287 print line
288 return popen.returncode
289
290 def ExecActionWrapper(self, arch, rspfile, *dir):
291 """Runs an action command line from a response file using the environment
292 for |arch|. If |dir| is supplied, use that as the working directory."""
293 env = self._GetEnv(arch)
294 # TODO(scottmg): This is a temporary hack to get some specific variables
295 # through to actions that are set after gyp-time. http://crbug.com/333738.
296 for k, v in os.environ.iteritems():
297 if k not in env:
298 env[k] = v
299 args = open(rspfile).read()
300 dir = dir[0] if dir else None
301 return subprocess.call(args, shell=True, env=env, cwd=dir)
302
303 def ExecClCompile(self, project_dir, selected_files):
304 """Executed by msvs-ninja projects when the 'ClCompile' target is used to
305 build selected C/C++ files."""
306 project_dir = os.path.relpath(project_dir, BASE_DIR)
307 selected_files = selected_files.split(';')
308 ninja_targets = [os.path.join(project_dir, filename) + '^^'
309 for filename in selected_files]
310 cmd = ['ninja.exe']
311 cmd.extend(ninja_targets)
312 return subprocess.call(cmd, shell=True, cwd=BASE_DIR)
313
314 if __name__ == '__main__':
315 sys.exit(main(sys.argv[1:]))
OLDNEW
« no previous file with comments | « build/vs_toolchain.py ('k') | net/BUILD.gn » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698