Introduce xwalk-extensions-common
[platform/framework/web/xwalk-extensions-common.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 from ctypes import windll, wintypes
13 import os
14 import shutil
15 import subprocess
16 import sys
17
18 BASE_DIR = os.path.dirname(os.path.abspath(__file__))
19
20
21 def main(args):
22   executor = WinTool()
23   exit_code = executor.Dispatch(args)
24   if exit_code is not None:
25     sys.exit(exit_code)
26
27
28 class LinkLock(object):
29   """A flock-style lock to limit the number of concurrent links to one.
30
31   Uses a session-local mutex based on the file's directory.
32   """
33   def __enter__(self):
34     name = 'Local\\%s' % BASE_DIR.replace('\\', '_').replace(':', '_')
35     self.mutex = windll.kernel32.CreateMutexW(
36         wintypes.c_int(0),
37         wintypes.c_int(0),
38         wintypes.create_unicode_buffer(name))
39     assert self.mutex
40     result = windll.kernel32.WaitForSingleObject(
41         self.mutex, wintypes.c_int(0xFFFFFFFF))
42     # 0x80 means another process was killed without releasing the mutex, but
43     # that this process has been given ownership. This is fine for our
44     # purposes.
45     assert result in (0, 0x80), (
46         "%s, %s" % (result, windll.kernel32.GetLastError()))
47
48   def __exit__(self, type, value, traceback):
49     windll.kernel32.ReleaseMutex(self.mutex)
50     windll.kernel32.CloseHandle(self.mutex)
51
52
53 class WinTool(object):
54   """This class performs all the Windows tooling steps. The methods can either
55   be executed directly, or dispatched from an argument list."""
56
57   def Dispatch(self, args):
58     """Dispatches a string command to a method."""
59     if len(args) < 1:
60       raise Exception("Not enough arguments")
61
62     method = "Exec%s" % self._CommandifyName(args[0])
63     return getattr(self, method)(*args[1:])
64
65   def _CommandifyName(self, name_string):
66     """Transforms a tool name like recursive-mirror to RecursiveMirror."""
67     return name_string.title().replace('-', '')
68
69   def _GetEnv(self, arch):
70     """Gets the saved environment from a file for a given architecture."""
71     # The environment is saved as an "environment block" (see CreateProcess
72     # and msvs_emulation for details). We convert to a dict here.
73     # Drop last 2 NULs, one for list terminator, one for trailing vs. separator.
74     pairs = open(arch).read()[:-2].split('\0')
75     kvs = [item.split('=', 1) for item in pairs]
76     return dict(kvs)
77
78   def ExecStamp(self, path):
79     """Simple stamp command."""
80     open(path, 'w').close()
81
82   def ExecRecursiveMirror(self, source, dest):
83     """Emulation of rm -rf out && cp -af in out."""
84     if os.path.exists(dest):
85       if os.path.isdir(dest):
86         shutil.rmtree(dest)
87       else:
88         os.unlink(dest)
89     if os.path.isdir(source):
90       shutil.copytree(source, dest)
91     else:
92       shutil.copy2(source, dest)
93
94   def ExecLinkWrapper(self, arch, *args):
95     """Filter diagnostic output from link that looks like:
96     '   Creating library ui.dll.lib and object ui.dll.exp'
97     This happens when there are exports from the dll or exe.
98     """
99     with LinkLock():
100       env = self._GetEnv(arch)
101       popen = subprocess.Popen(args, shell=True, env=env,
102                                stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
103       out, _ = popen.communicate()
104       for line in out.splitlines():
105         if not line.startswith('   Creating library '):
106           print line
107       return popen.returncode
108
109   def ExecManifestWrapper(self, arch, *args):
110     """Run manifest tool with environment set. Strip out undesirable warning
111     (some XML blocks are recognized by the OS loader, but not the manifest
112     tool)."""
113     env = self._GetEnv(arch)
114     popen = subprocess.Popen(args, shell=True, env=env,
115                              stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
116     out, _ = popen.communicate()
117     for line in out.splitlines():
118       if line and 'manifest authoring warning 81010002' not in line:
119         print line
120     return popen.returncode
121
122   def ExecMidlWrapper(self, arch, outdir, tlb, h, dlldata, iid, proxy, idl,
123                       *flags):
124     """Filter noisy filenames output from MIDL compile step that isn't
125     quietable via command line flags.
126     """
127     args = ['midl', '/nologo'] + list(flags) + [
128         '/out', outdir,
129         '/tlb', tlb,
130         '/h', h,
131         '/dlldata', dlldata,
132         '/iid', iid,
133         '/proxy', proxy,
134         idl]
135     env = self._GetEnv(arch)
136     popen = subprocess.Popen(args, shell=True, env=env,
137                              stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
138     out, _ = popen.communicate()
139     # Filter junk out of stdout, and write filtered versions. Output we want
140     # to filter is pairs of lines that look like this:
141     # Processing C:\Program Files (x86)\Microsoft SDKs\...\include\objidl.idl
142     # objidl.idl
143     lines = out.splitlines()
144     prefix = 'Processing '
145     processing = set(os.path.basename(x) for x in lines if x.startswith(prefix))
146     for line in lines:
147       if not line.startswith(prefix) and line not in processing:
148         print line
149     return popen.returncode
150
151   def ExecAsmWrapper(self, arch, *args):
152     """Filter logo banner from invocations of asm.exe."""
153     env = self._GetEnv(arch)
154     # MSVS doesn't assemble x64 asm files.
155     if arch == 'environment.x64':
156       return 0
157     popen = subprocess.Popen(args, shell=True, env=env,
158                              stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
159     out, _ = popen.communicate()
160     for line in out.splitlines():
161       if (not line.startswith('Copyright (C) Microsoft Corporation') and
162           not line.startswith('Microsoft (R) Macro Assembler') and
163           not line.startswith(' Assembling: ') and
164           line):
165         print line
166     return popen.returncode
167
168   def ExecRcWrapper(self, arch, *args):
169     """Filter logo banner from invocations of rc.exe. Older versions of RC
170     don't support the /nologo flag."""
171     env = self._GetEnv(arch)
172     popen = subprocess.Popen(args, shell=True, env=env,
173                              stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
174     out, _ = popen.communicate()
175     for line in out.splitlines():
176       if (not line.startswith('Microsoft (R) Windows (R) Resource Compiler') and
177           not line.startswith('Copyright (C) Microsoft Corporation') and
178           line):
179         print line
180     return popen.returncode
181
182   def ExecActionWrapper(self, arch, rspfile, *dir):
183     """Runs an action command line from a response file using the environment
184     for |arch|. If |dir| is supplied, use that as the working directory."""
185     env = self._GetEnv(arch)
186     args = open(rspfile).read()
187     dir = dir[0] if dir else None
188     popen = subprocess.Popen(args, shell=True, env=env, cwd=dir)
189     popen.wait()
190     return popen.returncode
191
192 if __name__ == '__main__':
193   sys.exit(main(sys.argv[1:]))