"Fossies" - the Fresh Open Source Software Archive

Member "Atom/resources/app/apm/node_modules/node-gyp/gyp/pylib/gyp/win_tool.py" (7 Feb 2017, 12751 Bytes) of archive /windows/misc/atom-windows.zip:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file.

    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[0].replace('/', '\\')] + list(args[1:]),
  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 ') and
  127           not line.startswith('Generating code') and
  128           not line.startswith('Finished generating code')):
  129         print line
  130     return link.returncode
  131 
  132   def ExecLinkWithManifests(self, arch, embed_manifest, out, ldcmd, resname,
  133                             mt, rc, intermediate_manifest, *manifests):
  134     """A wrapper for handling creating a manifest resource and then executing
  135     a link command."""
  136     # The 'normal' way to do manifests is to have link generate a manifest
  137     # based on gathering dependencies from the object files, then merge that
  138     # manifest with other manifests supplied as sources, convert the merged
  139     # manifest to a resource, and then *relink*, including the compiled
  140     # version of the manifest resource. This breaks incremental linking, and
  141     # is generally overly complicated. Instead, we merge all the manifests
  142     # provided (along with one that includes what would normally be in the
  143     # linker-generated one, see msvs_emulation.py), and include that into the
  144     # first and only link. We still tell link to generate a manifest, but we
  145     # only use that to assert that our simpler process did not miss anything.
  146     variables = {
  147       'python': sys.executable,
  148       'arch': arch,
  149       'out': out,
  150       'ldcmd': ldcmd,
  151       'resname': resname,
  152       'mt': mt,
  153       'rc': rc,
  154       'intermediate_manifest': intermediate_manifest,
  155       'manifests': ' '.join(manifests),
  156     }
  157     add_to_ld = ''
  158     if manifests:
  159       subprocess.check_call(
  160           '%(python)s gyp-win-tool manifest-wrapper %(arch)s %(mt)s -nologo '
  161           '-manifest %(manifests)s -out:%(out)s.manifest' % variables)
  162       if embed_manifest == 'True':
  163         subprocess.check_call(
  164             '%(python)s gyp-win-tool manifest-to-rc %(arch)s %(out)s.manifest'
  165           ' %(out)s.manifest.rc %(resname)s' % variables)
  166         subprocess.check_call(
  167             '%(python)s gyp-win-tool rc-wrapper %(arch)s %(rc)s '
  168             '%(out)s.manifest.rc' % variables)
  169         add_to_ld = ' %(out)s.manifest.res' % variables
  170     subprocess.check_call(ldcmd + add_to_ld)
  171 
  172     # Run mt.exe on the theoretically complete manifest we generated, merging
  173     # it with the one the linker generated to confirm that the linker
  174     # generated one does not add anything. This is strictly unnecessary for
  175     # correctness, it's only to verify that e.g. /MANIFESTDEPENDENCY was not
  176     # used in a #pragma comment.
  177     if manifests:
  178       # Merge the intermediate one with ours to .assert.manifest, then check
  179       # that .assert.manifest is identical to ours.
  180       subprocess.check_call(
  181           '%(python)s gyp-win-tool manifest-wrapper %(arch)s %(mt)s -nologo '
  182           '-manifest %(out)s.manifest %(intermediate_manifest)s '
  183           '-out:%(out)s.assert.manifest' % variables)
  184       assert_manifest = '%(out)s.assert.manifest' % variables
  185       our_manifest = '%(out)s.manifest' % variables
  186       # Load and normalize the manifests. mt.exe sometimes removes whitespace,
  187       # and sometimes doesn't unfortunately.
  188       with open(our_manifest, 'rb') as our_f:
  189         with open(assert_manifest, 'rb') as assert_f:
  190           our_data = our_f.read().translate(None, string.whitespace)
  191           assert_data = assert_f.read().translate(None, string.whitespace)
  192       if our_data != assert_data:
  193         os.unlink(out)
  194         def dump(filename):
  195           sys.stderr.write('%s\n-----\n' % filename)
  196           with open(filename, 'rb') as f:
  197             sys.stderr.write(f.read() + '\n-----\n')
  198         dump(intermediate_manifest)
  199         dump(our_manifest)
  200         dump(assert_manifest)
  201         sys.stderr.write(
  202             'Linker generated manifest "%s" added to final manifest "%s" '
  203             '(result in "%s"). '
  204             'Were /MANIFEST switches used in #pragma statements? ' % (
  205               intermediate_manifest, our_manifest, assert_manifest))
  206         return 1
  207 
  208   def ExecManifestWrapper(self, arch, *args):
  209     """Run manifest tool with environment set. Strip out undesirable warning
  210     (some XML blocks are recognized by the OS loader, but not the manifest
  211     tool)."""
  212     env = self._GetEnv(arch)
  213     popen = subprocess.Popen(args, shell=True, env=env,
  214                              stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
  215     out, _ = popen.communicate()
  216     for line in out.splitlines():
  217       if line and 'manifest authoring warning 81010002' not in line:
  218         print line
  219     return popen.returncode
  220 
  221   def ExecManifestToRc(self, arch, *args):
  222     """Creates a resource file pointing a SxS assembly manifest.
  223     |args| is tuple containing path to resource file, path to manifest file
  224     and resource name which can be "1" (for executables) or "2" (for DLLs)."""
  225     manifest_path, resource_path, resource_name = args
  226     with open(resource_path, 'wb') as output:
  227       output.write('#include <windows.h>\n%s RT_MANIFEST "%s"' % (
  228         resource_name,
  229         os.path.abspath(manifest_path).replace('\\', '/')))
  230 
  231   def ExecMidlWrapper(self, arch, outdir, tlb, h, dlldata, iid, proxy, idl,
  232                       *flags):
  233     """Filter noisy filenames output from MIDL compile step that isn't
  234     quietable via command line flags.
  235     """
  236     args = ['midl', '/nologo'] + list(flags) + [
  237         '/out', outdir,
  238         '/tlb', tlb,
  239         '/h', h,
  240         '/dlldata', dlldata,
  241         '/iid', iid,
  242         '/proxy', proxy,
  243         idl]
  244     env = self._GetEnv(arch)
  245     popen = subprocess.Popen(args, shell=True, env=env,
  246                              stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
  247     out, _ = popen.communicate()
  248     # Filter junk out of stdout, and write filtered versions. Output we want
  249     # to filter is pairs of lines that look like this:
  250     # Processing C:\Program Files (x86)\Microsoft SDKs\...\include\objidl.idl
  251     # objidl.idl
  252     lines = out.splitlines()
  253     prefixes = ('Processing ', '64 bit Processing ')
  254     processing = set(os.path.basename(x)
  255                      for x in lines if x.startswith(prefixes))
  256     for line in lines:
  257       if not line.startswith(prefixes) and line not in processing:
  258         print line
  259     return popen.returncode
  260 
  261   def ExecAsmWrapper(self, arch, *args):
  262     """Filter logo banner from invocations of asm.exe."""
  263     env = self._GetEnv(arch)
  264     popen = subprocess.Popen(args, shell=True, env=env,
  265                              stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
  266     out, _ = popen.communicate()
  267     for line in out.splitlines():
  268       if (not line.startswith('Copyright (C) Microsoft Corporation') and
  269           not line.startswith('Microsoft (R) Macro Assembler') and
  270           not line.startswith(' Assembling: ') and
  271           line):
  272         print line
  273     return popen.returncode
  274 
  275   def ExecRcWrapper(self, arch, *args):
  276     """Filter logo banner from invocations of rc.exe. Older versions of RC
  277     don't support the /nologo flag."""
  278     env = self._GetEnv(arch)
  279     popen = subprocess.Popen(args, shell=True, env=env,
  280                              stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
  281     out, _ = popen.communicate()
  282     for line in out.splitlines():
  283       if (not line.startswith('Microsoft (R) Windows (R) Resource Compiler') and
  284           not line.startswith('Copyright (C) Microsoft Corporation') and
  285           line):
  286         print line
  287     return popen.returncode
  288 
  289   def ExecActionWrapper(self, arch, rspfile, *dir):
  290     """Runs an action command line from a response file using the environment
  291     for |arch|. If |dir| is supplied, use that as the working directory."""
  292     env = self._GetEnv(arch)
  293     # TODO(scottmg): This is a temporary hack to get some specific variables
  294     # through to actions that are set after gyp-time. http://crbug.com/333738.
  295     for k, v in os.environ.iteritems():
  296       if k not in env:
  297         env[k] = v
  298     args = open(rspfile).read()
  299     dir = dir[0] if dir else None
  300     return subprocess.call(args, shell=True, env=env, cwd=dir)
  301 
  302   def ExecClCompile(self, project_dir, selected_files):
  303     """Executed by msvs-ninja projects when the 'ClCompile' target is used to
  304     build selected C/C++ files."""
  305     project_dir = os.path.relpath(project_dir, BASE_DIR)
  306     selected_files = selected_files.split(';')
  307     ninja_targets = [os.path.join(project_dir, filename) + '^^'
  308         for filename in selected_files]
  309     cmd = ['ninja.exe']
  310     cmd.extend(ninja_targets)
  311     return subprocess.call(cmd, shell=True, cwd=BASE_DIR)
  312 
  313 if __name__ == '__main__':
  314   sys.exit(main(sys.argv[1:]))