wrapper_utils.py 2.8 KB

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