| # 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 shlex |
| import shutil |
| import subprocess |
| 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 |