gyp: update to 78b26f7
[platform/upstream/nodejs.git] / tools / gyp / pylib / gyp / win_tool.py
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 sys
17
18 BASE_DIR = os.path.dirname(os.path.abspath(__file__))
19
20 # A regex matching an argument corresponding to a PDB filename passed as an
21 # argument to link.exe.
22 _LINK_EXE_PDB_ARG = re.compile('/PDB:(?P<pdb>.+\.exe\.pdb)$', re.IGNORECASE)
23
24 def main(args):
25   executor = WinTool()
26   exit_code = executor.Dispatch(args)
27   if exit_code is not None:
28     sys.exit(exit_code)
29
30
31 class WinTool(object):
32   """This class performs all the Windows tooling steps. The methods can either
33   be executed directly, or dispatched from an argument list."""
34
35   def _MaybeUseSeparateMspdbsrv(self, env, args):
36     """Allows to use a unique instance of mspdbsrv.exe for the linkers linking
37     an .exe target if GYP_USE_SEPARATE_MSPDBSRV has been set."""
38     if not os.environ.get('GYP_USE_SEPARATE_MSPDBSRV'):
39       return
40
41     if len(args) < 1:
42       raise Exception("Not enough arguments")
43
44     if args[0] != 'link.exe':
45       return
46
47     # Checks if this linker produces a PDB for an .exe target. If so use the
48     # name of this PDB to generate an endpoint name for mspdbsrv.exe.
49     endpoint_name = None
50     for arg in args:
51       m = _LINK_EXE_PDB_ARG.match(arg)
52       if m:
53         endpoint_name = '%s_%d' % (m.group('pdb'), 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         shutil.rmtree(dest)
94       else:
95         os.unlink(dest)
96     if os.path.isdir(source):
97       shutil.copytree(source, dest)
98     else:
99       shutil.copy2(source, dest)
100
101   def ExecLinkWrapper(self, arch, *args):
102     """Filter diagnostic output from link that looks like:
103     '   Creating library ui.dll.lib and object ui.dll.exp'
104     This happens when there are exports from the dll or exe.
105     """
106     env = self._GetEnv(arch)
107     self._MaybeUseSeparateMspdbsrv(env, args)
108     link = subprocess.Popen(args,
109                             shell=True,
110                             env=env,
111                             stdout=subprocess.PIPE,
112                             stderr=subprocess.STDOUT)
113     out, _ = link.communicate()
114     for line in out.splitlines():
115       if not line.startswith('   Creating library '):
116         print line
117     return link.returncode
118
119   def ExecManifestWrapper(self, arch, *args):
120     """Run manifest tool with environment set. Strip out undesirable warning
121     (some XML blocks are recognized by the OS loader, but not the manifest
122     tool)."""
123     env = self._GetEnv(arch)
124     popen = subprocess.Popen(args, shell=True, env=env,
125                              stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
126     out, _ = popen.communicate()
127     for line in out.splitlines():
128       if line and 'manifest authoring warning 81010002' not in line:
129         print line
130     return popen.returncode
131
132   def ExecManifestToRc(self, arch, *args):
133     """Creates a resource file pointing a SxS assembly manifest.
134     |args| is tuple containing path to resource file, path to manifest file
135     and resource name which can be "1" (for executables) or "2" (for DLLs)."""
136     manifest_path, resource_path, resource_name = args
137     with open(resource_path, 'wb') as output:
138       output.write('#include <windows.h>\n%s RT_MANIFEST "%s"' % (
139         resource_name,
140         os.path.abspath(manifest_path).replace('\\', '/')))
141
142   def ExecMidlWrapper(self, arch, outdir, tlb, h, dlldata, iid, proxy, idl,
143                       *flags):
144     """Filter noisy filenames output from MIDL compile step that isn't
145     quietable via command line flags.
146     """
147     args = ['midl', '/nologo'] + list(flags) + [
148         '/out', outdir,
149         '/tlb', tlb,
150         '/h', h,
151         '/dlldata', dlldata,
152         '/iid', iid,
153         '/proxy', proxy,
154         idl]
155     env = self._GetEnv(arch)
156     popen = subprocess.Popen(args, shell=True, env=env,
157                              stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
158     out, _ = popen.communicate()
159     # Filter junk out of stdout, and write filtered versions. Output we want
160     # to filter is pairs of lines that look like this:
161     # Processing C:\Program Files (x86)\Microsoft SDKs\...\include\objidl.idl
162     # objidl.idl
163     lines = out.splitlines()
164     prefix = 'Processing '
165     processing = set(os.path.basename(x) for x in lines if x.startswith(prefix))
166     for line in lines:
167       if not line.startswith(prefix) and line not in processing:
168         print line
169     return popen.returncode
170
171   def ExecAsmWrapper(self, arch, *args):
172     """Filter logo banner from invocations of asm.exe."""
173     env = self._GetEnv(arch)
174     # MSVS doesn't assemble x64 asm files.
175     if arch == 'environment.x64':
176       return 0
177     popen = subprocess.Popen(args, shell=True, env=env,
178                              stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
179     out, _ = popen.communicate()
180     for line in out.splitlines():
181       if (not line.startswith('Copyright (C) Microsoft Corporation') and
182           not line.startswith('Microsoft (R) Macro Assembler') and
183           not line.startswith(' Assembling: ') and
184           line):
185         print line
186     return popen.returncode
187
188   def ExecRcWrapper(self, arch, *args):
189     """Filter logo banner from invocations of rc.exe. Older versions of RC
190     don't support the /nologo flag."""
191     env = self._GetEnv(arch)
192     popen = subprocess.Popen(args, shell=True, env=env,
193                              stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
194     out, _ = popen.communicate()
195     for line in out.splitlines():
196       if (not line.startswith('Microsoft (R) Windows (R) Resource Compiler') and
197           not line.startswith('Copyright (C) Microsoft Corporation') and
198           line):
199         print line
200     return popen.returncode
201
202   def ExecActionWrapper(self, arch, rspfile, *dir):
203     """Runs an action command line from a response file using the environment
204     for |arch|. If |dir| is supplied, use that as the working directory."""
205     env = self._GetEnv(arch)
206     args = open(rspfile).read()
207     dir = dir[0] if dir else None
208     return subprocess.call(args, shell=True, env=env, cwd=dir)
209
210 if __name__ == '__main__':
211   sys.exit(main(sys.argv[1:]))