• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3# Copyright (c) 2016 The Chromium Authors. All rights reserved.
4
5# Use of this source code is governed by a BSD-style license that can be
6# found in the LICENSE file.
7
8"""Helper functions for gcc_toolchain.gni wrappers."""
9
10import gzip
11import os
12import subprocess
13import shutil
14import threading
15
16_BAT_PREFIX = 'cmd /c call '
17
18
19def _gzip_then_delete(src_path, dest_path):
20    """ Results for ohos map file with GCC on a z620:
21    Uncompressed: 207MB
22    gzip -9: 16.4MB, takes 8.7 seconds.
23    gzip -1: 21.8MB, takes 2.0 seconds.
24    Piping directly from the linker via -print-map (or via -Map with a fifo)
25    adds a whopping 30-45 seconds!
26    """
27    with open(src_path, 'rb') as f_in, gzip.GzipFile(dest_path,
28                                                     'wb',
29                                                     1) as f_out:
30        shutil.copyfileobj(f_in, f_out)
31    os.unlink(src_path)
32
33
34def command_to_run(command):
35    """Generates commands compatible with Windows.
36
37    When running on a Windows host and using a toolchain whose tools are
38    actually wrapper scripts (i.e. .bat files on Windows) rather than binary
39    executables, the |command| to run has to be prefixed with this magic.
40    The GN toolchain definitions take care of that for when GN/Ninja is
41    running the tool directly.  When that command is passed in to this
42    script, it appears as a unitary string but needs to be split up so that
43    just 'cmd' is the actual command given to Python's subprocess module.
44
45    Args:
46      command: List containing the UNIX style |command|.
47
48    Returns:
49      A list containing the Windows version of the |command|.
50    """
51    if command[0].startswith(_BAT_PREFIX):
52        command = command[0].split(None, 3) + command[1:]
53    return command
54
55
56def run_link_with_optional_map_file(command, env=None, map_file=None):
57    """Runs the given command, adding in -Wl,-Map when |map_file| is given.
58
59    Also takes care of gzipping when |map_file| ends with .gz.
60
61    Args:
62      command: List of arguments comprising the command.
63      env: Environment variables.
64      map_file: Path to output map_file.
65
66    Returns:
67      The exit code of running |command|.
68    """
69    tmp_map_path = None
70    if map_file and map_file.endswith('.gz'):
71        tmp_map_path = map_file + '.tmp'
72        command.append('-Wl,-Map,' + tmp_map_path)
73    elif map_file:
74        command.append('-Wl,-Map,' + map_file)
75
76    result = subprocess.call(command, env=env)
77
78    if tmp_map_path and result == 0:
79        threading.Thread(
80            target=lambda: _gzip_then_delete(tmp_map_path, map_file)).start()
81    elif tmp_map_path and os.path.exists(tmp_map_path):
82        os.unlink(tmp_map_path)
83
84    return result
85
86
87def capture_command_stderr(command, env=None):
88    """Returns the stderr of a command.
89
90    Args:
91      command: A list containing the command and arguments.
92      env: Environment variables for the new process.
93    """
94    child = subprocess.Popen(command, stderr=subprocess.PIPE, env=env)
95    _, stderr = child.communicate()
96    return child.returncode, stderr
97