Fix wrapper shell scripts and symlinks for invoking clang++

In a typical installation, clang++ symlinks to clang, which symlinks to the
elf executable. The executable distinguishes between clang and clang++ based
on argv[0].

When invoked through the LdsoWrapper, argv[0] always contains the path to the
executable elf file, making clang/clang++ invocations indistinguishable.

This CL adjusts the symlinks and wrappers so clang++ invocation works correctly.

BUG=chromium:628524
TEST='cros_setup_toolchains --create-packages -t x86_64-cros-linux-gnu'
TEST=Used x86_64-cros-linux-gnu.tar.xz to successfully build chrome for panther (simple-chrome).
TEST='cros_setup_toolchains --create-packages -t i686-pc-linux-gnu'
TEST=Used i686-pc-linux-gnu.tar.xz to successfully build chrome for x86-zgb (simple-chrome).
TEST='cros_setup_toolchains --create-packages -t armv7a-cros-linux-gnueabi'
TEST=Used armv7a-cros-linux-gnueabi.tar.xz to successfully build chrome for daisy (simple-chrome).

Change-Id: Id2ecb65b99226b65a3da4197c09eda28c5fdefca
Reviewed-on: https://chromium-review.googlesource.com/362682
Commit-Ready: Luis Lozano <llozano@chromium.org>
Tested-by: Luis Lozano <llozano@chromium.org>
Reviewed-by: Luis Lozano <llozano@chromium.org>
diff --git a/scripts/cros_setup_toolchains.py b/scripts/cros_setup_toolchains.py
index 4590831..2bbf21c 100644
--- a/scripts/cros_setup_toolchains.py
+++ b/scripts/cros_setup_toolchains.py
@@ -10,6 +10,7 @@
 import glob
 import json
 import os
+import re
 
 from chromite.cbuildbot import constants
 from chromite.lib import commandline
@@ -700,6 +701,48 @@
   os.chmod(root_wrapper, 0o755)
 
 
+def FixClangXXWrapper(root, path):
+  """Fix wrapper shell scripts and symlinks for invoking clang++
+
+  In a typical installation, clang++ symlinks to clang, which symlinks to the
+  elf executable. The executable distinguishes between clang and clang++ based
+  on argv[0].
+
+  When invoked through the LdsoWrapper, argv[0] always contains the path to the
+  executable elf file, making clang/clang++ invocations indistinguishable.
+
+  This function detects if the elf executable being wrapped is clang-X.Y, and
+  fixes wrappers/symlinks as necessary so that clang++ will work correctly.
+
+  The calling sequence now becomes:
+  -) clang++ invocation turns into clang++-3.9 (which is a copy of clang-3.9,
+     the Ldsowrapper).
+  -) clang++-3.9 uses the Ldso to invoke clang++-3.9.elf, which is a symlink
+     to the original clang-3.9 elf.
+  -) The difference this time is that inside the elf file execution, $0 is
+     set as .../usr/bin/clang++-3.9.elf, which contains 'clang++' in the name.
+
+  Args:
+    root: The root tree to generate scripts / symlinks inside of
+    path: The target elf for which LdsoWrapper was created
+  """
+  if re.match(r'/usr/bin/clang-\d+\.\d+$', path):
+    logging.info('fixing clang++ invocation for %s', path)
+    clangdir = os.path.dirname(root + path)
+    clang = os.path.basename(path)
+    clangxx = clang.replace('clang', 'clang++')
+
+    # Create a symlink clang++-X.Y.elf to point to clang-X.Y.elf
+    os.symlink(clang + '.elf', os.path.join(clangdir, clangxx + '.elf'))
+
+    # Create a hardlink clang++-X.Y pointing to clang-X.Y
+    os.link(os.path.join(clangdir, clang), os.path.join(clangdir, clangxx))
+
+    # Adjust the clang++ symlink to point to clang++-X.Y
+    os.unlink(os.path.join(clangdir, 'clang++'))
+    os.symlink(clangxx, os.path.join(clangdir, 'clang++'))
+
+
 def FileIsCrosSdkElf(elf):
   """Determine if |elf| is an ELF that we execute in the cros_sdk
 
@@ -877,6 +920,7 @@
       interp = os.path.join('/lib', os.path.basename(interp))
       lddtree.GenerateLdsoWrapper(output_dir, path_rewrite_func(elf), interp,
                                   libpaths=e['rpath'] + e['runpath'])
+      FixClangXXWrapper(output_dir, path_rewrite_func(elf))
 
     for lib, lib_data in e['libs'].iteritems():
       if lib in donelibs: