| # Copyright (c) 2016 The Chromium Authors. All rights reserved. | 
 | # Use of this source code is governed by a BSD-style license that can be | 
 | # found in the LICENSE file. | 
 |  | 
 | """Helper functions for gcc_toolchain.gni wrappers.""" | 
 |  | 
 | import gzip | 
 | import os | 
 | import re | 
 | import subprocess | 
 | import shlex | 
 | import shutil | 
 | import sys | 
 | import threading | 
 |  | 
 | _BAT_PREFIX = 'cmd /c call ' | 
 | _WHITELIST_RE = re.compile('whitelisted_resource_(?P<resource_id>[0-9]+)') | 
 |  | 
 |  | 
 | def _GzipThenDelete(src_path, dest_path): | 
 |   # Results for Android map file with GCC on a z620: | 
 |   # Uncompressed: 207MB | 
 |   # gzip -9: 16.4MB, takes 8.7 seconds. | 
 |   # gzip -1: 21.8MB, takes 2.0 seconds. | 
 |   # Piping directly from the linker via -print-map (or via -Map with a fifo) | 
 |   # adds a whopping 30-45 seconds! | 
 |   with open(src_path, 'rb') as f_in, gzip.GzipFile(dest_path, 'wb', 1) as f_out: | 
 |     shutil.copyfileobj(f_in, f_out) | 
 |   os.unlink(src_path) | 
 |  | 
 |  | 
 | def CommandToRun(command): | 
 |   """Generates commands compatible with Windows. | 
 |  | 
 |   When running on a Windows host and using a toolchain whose tools are | 
 |   actually wrapper scripts (i.e. .bat files on Windows) rather than binary | 
 |   executables, the |command| to run has to be prefixed with this magic. | 
 |   The GN toolchain definitions take care of that for when GN/Ninja is | 
 |   running the tool directly.  When that command is passed in to this | 
 |   script, it appears as a unitary string but needs to be split up so that | 
 |   just 'cmd' is the actual command given to Python's subprocess module. | 
 |  | 
 |   Args: | 
 |     command: List containing the UNIX style |command|. | 
 |  | 
 |   Returns: | 
 |     A list containing the Windows version of the |command|. | 
 |   """ | 
 |   if command[0].startswith(_BAT_PREFIX): | 
 |     command = command[0].split(None, 3) + command[1:] | 
 |   return command | 
 |  | 
 |  | 
 | def RunLinkWithOptionalMapFile(command, env=None, map_file=None): | 
 |   """Runs the given command, adding in -Wl,-Map when |map_file| is given. | 
 |  | 
 |   Also takes care of gzipping when |map_file| ends with .gz. | 
 |  | 
 |   Args: | 
 |     command: List of arguments comprising the command. | 
 |     env: Environment variables. | 
 |     map_file: Path to output map_file. | 
 |  | 
 |   Returns: | 
 |     The exit code of running |command|. | 
 |   """ | 
 |   tmp_map_path = None | 
 |   if map_file and map_file.endswith('.gz'): | 
 |     tmp_map_path = map_file + '.tmp' | 
 |     command.append('-Wl,-Map,' + tmp_map_path) | 
 |   elif map_file: | 
 |     command.append('-Wl,-Map,' + map_file) | 
 |  | 
 |   result = subprocess.call(command, env=env) | 
 |  | 
 |   if tmp_map_path and result == 0: | 
 |     threading.Thread( | 
 |         target=lambda: _GzipThenDelete(tmp_map_path, map_file)).start() | 
 |   elif tmp_map_path and os.path.exists(tmp_map_path): | 
 |     os.unlink(tmp_map_path) | 
 |  | 
 |   return result | 
 |  | 
 |  | 
 | def ResolveRspLinks(inputs): | 
 |   """Return a list of files contained in a response file. | 
 |  | 
 |   Args: | 
 |     inputs: A command containing rsp files. | 
 |  | 
 |   Returns: | 
 |     A set containing the rsp file content.""" | 
 |   rspfiles = [a[1:] for a in inputs if a.startswith('@')] | 
 |   resolved = set() | 
 |   for rspfile in rspfiles: | 
 |     with open(rspfile, 'r') as f: | 
 |       resolved.update(shlex.split(f.read())) | 
 |  | 
 |   return resolved | 
 |  | 
 |  | 
 | def CombineResourceWhitelists(whitelist_candidates, outfile): | 
 |   """Combines all whitelists for a resource file into a single whitelist. | 
 |  | 
 |   Args: | 
 |     whitelist_candidates: List of paths to rsp files containing all targets. | 
 |     outfile: Path to save the combined whitelist. | 
 |   """ | 
 |   whitelists = ('%s.whitelist' % candidate for candidate in whitelist_candidates | 
 |                 if os.path.exists('%s.whitelist' % candidate)) | 
 |  | 
 |   resources = set() | 
 |   for whitelist in whitelists: | 
 |     with open(whitelist, 'r') as f: | 
 |       resources.update(f.readlines()) | 
 |  | 
 |   with open(outfile, 'w') as f: | 
 |     f.writelines(resources) | 
 |  | 
 |  | 
 | def ExtractResourceIdsFromPragmaWarnings(text): | 
 |   """Returns set of resource IDs that are inside unknown pragma warnings. | 
 |  | 
 |   Args: | 
 |     text: The text that will be scanned for unknown pragma warnings. | 
 |  | 
 |   Returns: | 
 |     A set containing integers representing resource IDs. | 
 |   """ | 
 |   used_resources = set() | 
 |   lines = text.splitlines() | 
 |   for ln in lines: | 
 |     match = _WHITELIST_RE.search(ln) | 
 |     if match: | 
 |       resource_id = int(match.group('resource_id')) | 
 |       used_resources.add(resource_id) | 
 |  | 
 |   return used_resources | 
 |  | 
 |  | 
 | def CaptureCommandStderr(command, env=None): | 
 |   """Returns the stderr of a command. | 
 |  | 
 |   Args: | 
 |     command: A list containing the command and arguments. | 
 |     env: Environment variables for the new process. | 
 |   """ | 
 |   child = subprocess.Popen(command, stderr=subprocess.PIPE, env=env) | 
 |   _, stderr = child.communicate() | 
 |   return child.returncode, stderr |