build/gen.py: Add --link-lib=LINK_LIB option.

This adds one new command-line option to the build/gen.py
script to allow linking the final executable(s) with one or
more extra libraries.

This is mostly useful to link a custom malloc implementation
or a cpu profiling library, like libtcmalloc.a or libprofiler.a
provided by the the gperftools project [1].

For the record, using:

  build/gen.py --use-icf --use-lto --link-lib=/path/to/libtcmalloc.a

Results in a noticeably faster GN executable for both Chromium
(21% faster) and the Fuchsia build (43% faster!)

It is also possible to link against a more recent release of TCMalloc
from [2], but measurements show no noticeable performance difference
with the older release for GN workloads.

  Chromium:
    BEFORE:  Done. Made 13124 targets from 2289 files in 3593ms
    AFTER:   Done. Made 13124 targets from 2289 files in 4358ms

  Fuchsia:
    BEFORE: [...] Done. Made 47263 targets from 3819 files in 14263ms
    AFTER:  [...] Done. Made 47263 targets from 3819 files in 9923ms

[1] https://github.com/gperftools/gperftools
[2] https://github.com/google/tcmalloc

Change-Id: If21911f13a886eeae09797099a078ed1d4f789dd
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/8360
Reviewed-by: Scott Graham <scottmg@chromium.org>
Commit-Queue: David Turner <digit@google.com>
diff --git a/build/gen.py b/build/gen.py
index 8083aac..a00558b 100755
--- a/build/gen.py
+++ b/build/gen.py
@@ -15,6 +15,11 @@
 import sys
 import tempfile
 
+try:  # py3
+    from shlex import quote as shell_quote
+except ImportError:  # py2
+    from pipes import quote as shell_quote
+
 SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
 REPO_ROOT = os.path.dirname(SCRIPT_DIR)
 
@@ -102,6 +107,16 @@
   parser.add_option('--no-static-libstdc++', action='store_true',
                     default=False, dest='no_static_libstdcpp',
                     help='Don\'t link libstdc++ statically')
+  parser.add_option('--link-lib',
+                    action='append',
+                    metavar='LINK_LIB',
+                    default=[],
+                    dest='link_libs',
+                    help=('Add a library to the final executable link. ' +
+                          'LINK_LIB must be the path to a static or shared ' +
+                          'library, or \'-l<name>\' on POSIX systems. Can be ' +
+                          'used multiple times. Useful to link custom malloc ' +
+                          'or cpu profiling libraries.'))
   options, args = parser.parse_args(argv)
 
   if args:
@@ -159,14 +174,17 @@
                       cxx, ar, ld, platform, host, options,
                       cflags=[], ldflags=[], libflags=[],
                       include_dirs=[], solibs=[]):
+  args = ' -d' if options.debug else ''
+  for link_lib in options.link_libs:
+    args +=  ' --link-lib=' + shell_quote(link_lib)
+
   ninja_header_lines = [
     'cxx = ' + cxx,
     'ar = ' + ar,
     'ld = ' + ld,
     '',
     'rule regen',
-    '  command = %s ../build/gen.py%s' % (
-        sys.executable, ' -d' if options.debug else ''),
+    '  command = %s ../build/gen.py%s' % (sys.executable, args),
     '  description = Regenerating ninja files',
     '',
     'build build.ninja: regen',
@@ -734,6 +752,8 @@
       ])
 
 
+  libs.extend(options.link_libs)
+
   # we just build static libraries that GN needs
   executables['gn']['libs'].extend(static_libraries.keys())
   executables['gn_unittests']['libs'].extend(static_libraries.keys())