[Coverage] Reland disappeared changes.

In a previous CL, I screwed up while trying to rebase and accidentally
reverted all the changes that was meant to be landed. Please see:

https://chromium-review.googlesource.com/c/chromium/src/+/935195/10..11/tools/code_coverage/coverage.py

So, in this CL re-land those changes, sorry about the inconvenience!

Bug: 814608

Change-Id: I5cb7a87d851aa4d97b05fde9a243677c07eb13d8
Reviewed-on: https://chromium-review.googlesource.com/945422
Reviewed-by: Abhishek Arya <[email protected]>
Commit-Queue: Abhishek Arya <[email protected]>
Cr-Commit-Position: refs/heads/master@{#540546}
diff --git a/tools/code_coverage/coverage.py b/tools/code_coverage/coverage.py
index 69fc11e..bdd1000 100755
--- a/tools/code_coverage/coverage.py
+++ b/tools/code_coverage/coverage.py
@@ -64,6 +64,8 @@
 import json
 import logging
 import os
+import re
+import shlex
 import subprocess
 import urllib2
 
@@ -316,7 +318,7 @@
     return 'mac'
 
 
-def _IsTargetOsIos():
+def _IsIOS():
   """Returns true if the target_os specified in args.gn file is ios"""
   build_args = _ParseArgsGnFile()
   return 'target_os' in build_args and build_args['target_os'] == '"ios"'
@@ -422,11 +424,7 @@
   ]
   subprocess_cmd.extend(
       ['-object=' + binary_path for binary_path in binary_paths[1:]])
-  if _IsTargetOsIos():
-    # iOS binaries are universal binaries, and it requires specifying the
-    # architecture to use.
-    subprocess_cmd.append('-arch=x86_64')
-
+  _AddArchArgumentForIOSIfNeeded(subprocess_cmd, len(binary_paths))
   subprocess_cmd.extend(filters)
   subprocess.check_call(subprocess_cmd)
   logging.debug('Finished running "llvm-cov show" command')
@@ -771,11 +769,11 @@
     logging.info('Running command: "%s", the output is redirected to "%s"',
                  command, output_file_path)
 
-    if _IsIosCommand(command):
+    if _IsIOSCommand(command):
       # On iOS platform, due to lack of write permissions, profraw files are
       # generated outside of the OUTPUT_DIR, and the exact paths are contained
       # in the output of the command execution.
-      output = _ExecuteIosCommand(target, command)
+      output = _ExecuteIOSCommand(target, command)
       profraw_file_paths.append(_GetProfrawDataFileByParsingOutput(output))
     else:
       # On other platforms, profraw files are generated inside the OUTPUT_DIR.
@@ -786,7 +784,7 @@
 
   logging.debug('Finished executing the test commands')
 
-  if _IsTargetOsIos():
+  if _IsIOS():
     return profraw_file_paths
 
   for file_or_dir in os.listdir(OUTPUT_DIR):
@@ -825,7 +823,8 @@
 
   try:
     output = subprocess.check_output(
-        command.split(), env={'LLVM_PROFILE_FILE': expected_profraw_file_path})
+        shlex.split(command),
+        env={'LLVM_PROFILE_FILE': expected_profraw_file_path})
   except subprocess.CalledProcessError as e:
     output = e.output
     logging.warning('Command: "%s" exited with non-zero return code', command)
@@ -833,7 +832,7 @@
   return output
 
 
-def _ExecuteIosCommand(target, command):
+def _ExecuteIOSCommand(target, command):
   """Runs a single iOS command and generates a profraw data file.
 
   iOS application doesn't have write access to folders outside of the app, so
@@ -842,10 +841,18 @@
   application's Documents folder, and the full path can be obtained by parsing
   the output.
   """
-  assert _IsIosCommand(command)
+  assert _IsIOSCommand(command)
+
+  # After running tests, iossim generates a profraw data file, it won't be
+  # needed anyway, so dump it into the OUTPUT_DIR to avoid polluting the
+  # checkout.
+  iossim_profraw_file_path = os.path.join(
+      OUTPUT_DIR, os.extsep.join(['iossim', PROFRAW_FILE_EXTENSION]))
 
   try:
-    output = subprocess.check_output(command.split())
+    output = subprocess.check_output(
+        shlex.split(command),
+        env={'LLVM_PROFILE_FILE': iossim_profraw_file_path})
   except subprocess.CalledProcessError as e:
     # iossim emits non-zero return code even if tests run successfully, so
     # ignore the return code.
@@ -861,15 +868,15 @@
   have a single line containing the path to the generated profraw data file.
   NOTE: This should only be called when target os is iOS.
   """
-  assert _IsTargetOsIos()
+  assert _IsIOS()
 
-  output_by_lines = ''.join(output).split('\n')
-  profraw_file_identifier = 'Coverage data at '
+  output_by_lines = ''.join(output).splitlines()
+  profraw_file_pattern = re.compile('.*Coverage data at (.*coverage\.profraw).')
 
   for line in output_by_lines:
-    if profraw_file_identifier in line:
-      profraw_file_path = line.split(profraw_file_identifier)[1][:-1]
-      return profraw_file_path
+    result = profraw_file_pattern.match(line)
+    if result:
+      return result.group(1)
 
   assert False, ('No profraw data file was generated, did you call '
                  'coverage_util::ConfigureCoverageReportPath() in test setup? '
@@ -922,10 +929,7 @@
   ]
   subprocess_cmd.extend(
       ['-object=' + binary_path for binary_path in binary_paths[1:]])
-  if _IsTargetOsIos():
-    # iOS binaries are universal binaries, and it requires specifying the
-    # architecture to use.
-    subprocess_cmd.append('-arch=x86_64')
+  _AddArchArgumentForIOSIfNeeded(subprocess_cmd, len(binary_paths))
 
   subprocess_cmd.extend(filters)
 
@@ -953,6 +957,16 @@
   return per_file_coverage_summary
 
 
+def _AddArchArgumentForIOSIfNeeded(cmd_list, num_archs):
+  """Appends -arch arguments to the command list if it's ios platform.
+
+  iOS binaries are universal binaries, and require specifying the architecture
+  to use, and one architecture needs to be specified for each binary.
+  """
+  if _IsIOS():
+    cmd_list.extend(['-arch=x86_64'] * num_archs)
+
+
 def _GetBinaryPath(command):
   """Returns a relative path to the binary to be run by the command.
 
@@ -973,7 +987,7 @@
   """
   xvfb_script_name = os.extsep.join(['xvfb', 'py'])
 
-  command_parts = command.split()
+  command_parts = shlex.split(command)
   if os.path.basename(command_parts[0]) == 'python':
     assert os.path.basename(command_parts[1]) == xvfb_script_name, (
         'This tool doesn\'t understand the command: "%s"' % command)
@@ -982,19 +996,19 @@
   if os.path.basename(command_parts[0]) == xvfb_script_name:
     return command_parts[1]
 
-  if _IsIosCommand(command):
+  if _IsIOSCommand(command):
     # For a given application bundle, the binary resides in the bundle and has
     # the same name with the application without the .app extension.
     app_path = command_parts[1]
     app_name = os.path.splitext(os.path.basename(app_path))[0]
     return os.path.join(app_path, app_name)
 
-  return command.split()[0]
+  return command_parts[0]
 
 
-def _IsIosCommand(command):
+def _IsIOSCommand(command):
   """Returns true if command is used to run tests on iOS platform."""
-  return os.path.basename(command.split()[0]) == 'iossim'
+  return os.path.basename(shlex.split(command)[0]) == 'iossim'
 
 
 def _VerifyTargetExecutablesAreInBuildDirectory(commands):
@@ -1144,7 +1158,7 @@
   """Execute tool commands."""
   assert _GetPlatform() in [
       'linux', 'mac'
-  ], ('Coverage is only supported on linux and mac platforms.')
+  ], ('This script is only supported on linux and mac platforms.')
   assert os.path.abspath(os.getcwd()) == SRC_ROOT_PATH, ('This script must be '
                                                          'called from the root '
                                                          'of checkout.')