GN: Improve bootstrap for Windows

Improve bootstrap for Windows and allow bootstrap to work in specified
target directory. Additionally, some fixes made to project generation,
and for Linux.

R=dpranke@chromium.org

Change-Id: Ied7f99d3cb83559fbde597cb707b46fcb3e98dc4
Reviewed-on: https://chromium-review.googlesource.com/941214
Commit-Queue: Yngve Pettersen <yngve@vivaldi.com>
Reviewed-by: Dirk Pranke <dpranke@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#547867}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: 73b7854e6d4c7a186047809aacb9ce1478277c7a
diff --git a/tools/gn/bootstrap/bootstrap.py b/tools/gn/bootstrap/bootstrap.py
index 83f5838..8f32161 100755
--- a/tools/gn/bootstrap/bootstrap.py
+++ b/tools/gn/bootstrap/bootstrap.py
@@ -39,12 +39,13 @@
 def check_call(cmd, **kwargs):
   logging.debug('Running: %s', ' '.join(cmd))
 
-  # With shell=False, subprocess expects an executable on Windows
-  if is_win and cmd and cmd[0].endswith('.py'):
-    cmd.insert(0, sys.executable)
-
   subprocess.check_call(cmd, cwd=GN_ROOT, **kwargs)
 
+def check_output(cmd, cwd=GN_ROOT, **kwargs):
+  logging.debug('Running: %s', ' '.join(cmd))
+
+  return subprocess.check_output(cmd, cwd=cwd, **kwargs)
+
 def mkdir_p(path):
   try:
     os.makedirs(path)
@@ -71,8 +72,13 @@
     build_rel = os.path.join('out', 'Release')
   build_root = os.path.join(SRC_ROOT, build_rel)
 
+  windows_x64_toolchain = None
+  if is_win:
+    windows_x64_toolchain = windows_prepare_toolchain(tempdir)
+    os.environ["PATH"] = windows_x64_toolchain["paths"]
+
   print 'Building gn manually in a temporary directory for bootstrapping...'
-  build_gn_with_ninja_manually(tempdir, options)
+  build_gn_with_ninja_manually(tempdir, options, windows_x64_toolchain)
   temp_gn = os.path.join(tempdir, 'gn')
   out_gn = os.path.join(build_root, 'gn')
 
@@ -99,6 +105,37 @@
     if platform.machine().lower() in ['x86_64', 'amd64']: return 'x64'
     return 'x86'
 
+def windows_prepare_toolchain(tempdir):
+
+  def CallPythonScopeScript(command, **kwargs):
+    response = check_output(command, **kwargs)
+
+    _globals = {"__builtins__":None}
+    _locals = {}
+    exec(response, _globals, _locals)
+
+    return _locals
+
+  toolchain_paths = CallPythonScopeScript(
+      [sys.executable,
+       os.path.join(SRC_ROOT, "build", "vs_toolchain.py"),
+      "get_toolchain_dir"],
+      cwd=tempdir)
+
+  windows_x64_toolchain =  CallPythonScopeScript(
+      [sys.executable,
+       os.path.join(SRC_ROOT, "build", "toolchain",
+                    "win", "setup_toolchain.py"),
+       toolchain_paths["vs_path"],
+       toolchain_paths["sdk_path"],
+       toolchain_paths["runtime_dirs"],
+       "x64",
+       "true"
+      ],
+      cwd=tempdir)
+
+  return windows_x64_toolchain
+
 def main(argv):
   parser = optparse.OptionParser(description=sys.modules[__name__].__doc__)
   parser.add_option('-d', '--debug', action='store_true',
@@ -112,7 +149,9 @@
                          'temporary location each time')
   parser.add_option('--gn-gen-args', help='Args to pass to gn gen --args')
   parser.add_option('--build-path', help='The directory in which to build gn, '
-                    'relative to the src directory. (eg. out/Release)')
+                    'relative to the src directory. (eg. out/Release)'
+                    'In the no-clean mode an absolute path will also force '
+                    'the out_bootstrap to be located in the parent directory')
   parser.add_option('-v', '--verbose', action='store_true',
                     help='Log more details')
   options, args = parser.parse_args(argv)
@@ -124,7 +163,10 @@
 
   try:
     if options.no_clean:
-      build_dir = os.path.join(SRC_ROOT, 'out_bootstrap')
+      out_bootstrap_dir = SRC_ROOT
+      if options.build_path and os.path.isabs(options.build_path):
+        out_bootstrap_dir = os.path.dirname(options.build_path)
+      build_dir = os.path.join(out_bootstrap_dir, 'out_bootstrap')
       if not os.path.exists(build_dir):
         os.makedirs(build_dir)
       return run_build(build_dir, options)
@@ -159,6 +201,7 @@
       f.write(' ' + name + '=' + value)
 
   check_call([
+      sys.executable,
       os.path.join(SRC_ROOT, 'build', 'write_buildflag_header.py'),
       '--output', header,
       '--gen-dir', root_gen_dir,
@@ -169,12 +212,13 @@
 
 def write_build_date_header(root_gen_dir):
   check_call([
+       sys.executable,
        os.path.join(SRC_ROOT, 'build', 'write_build_date_header.py'),
        os.path.join(root_gen_dir, 'base/generated_build_date.h'),
        'default',
   ])
 
-def build_gn_with_ninja_manually(tempdir, options):
+def build_gn_with_ninja_manually(tempdir, options, windows_x64_toolchain):
   root_gen_dir = os.path.join(tempdir, 'gen')
   mkdir_p(root_gen_dir)
 
@@ -215,6 +259,7 @@
     # and this file is only included for Mac builds.
     mkdir_p(os.path.join(root_gen_dir, 'base'))
     check_call([
+        sys.executable,
         os.path.join(SRC_ROOT, 'build', 'write_build_date_header.py'),
         os.path.join(root_gen_dir, 'base', 'generated_build_date.h'),
         'default'
@@ -233,7 +278,7 @@
       {'USE_LLD': 'false', 'SUPPORTS_CODE_ORDERING': 'false'})
 
   write_gn_ninja(os.path.join(tempdir, 'build.ninja'),
-                 root_gen_dir, options)
+                 root_gen_dir, options, windows_x64_toolchain)
   cmd = ['ninja', '-C', tempdir, '-w', 'dupbuild=err']
   if options.verbose:
     cmd.append('-v')
@@ -332,12 +377,14 @@
     f.write(ninja_template)
     f.write('\n'.join(ninja_lines))
 
-def write_gn_ninja(path, root_gen_dir, options):
+def write_gn_ninja(path, root_gen_dir, options, windows_x64_toolchain):
   if is_win:
-    cc = os.environ.get('CC', 'cl.exe')
-    cxx = os.environ.get('CXX', 'cl.exe')
-    ld = os.environ.get('LD', 'link.exe')
-    ar = os.environ.get('AR', 'lib.exe')
+    CCPATH = windows_x64_toolchain["vc_bin_dir"]
+
+    cc = os.environ.get('CC', os.path.join(CCPATH, 'cl.exe'))
+    cxx = os.environ.get('CXX', os.path.join(CCPATH, 'cl.exe'))
+    ld = os.environ.get('LD', os.path.join(CCPATH, 'link.exe'))
+    ar = os.environ.get('AR', os.path.join(CCPATH, 'lib.exe'))
   elif is_aix:
     cc = os.environ.get('CC', 'gcc')
     cxx = os.environ.get('CXX', 'c++')
@@ -835,6 +882,9 @@
 
   if is_win:
     static_libraries['base']['sources'].extend([
+        "base/allocator/partition_allocator/address_space_randomization.cc",
+        'base/allocator/partition_allocator/page_allocator.cc',
+        "base/allocator/partition_allocator/spin_lock.cc",
         'base/base_paths_win.cc',
         'base/cpu.cc',
         'base/debug/close_handle_hook_win.cc',
@@ -871,17 +921,16 @@
         'base/sync_socket_win.cc',
         'base/synchronization/condition_variable_win.cc',
         'base/synchronization/lock_impl_win.cc',
-        'base/synchronization/read_write_lock_win.cc',
         'base/synchronization/waitable_event_watcher_win.cc',
         'base/synchronization/waitable_event_win.cc',
         'base/sys_info_win.cc',
         'base/threading/platform_thread_win.cc',
         'base/threading/thread_local_storage_win.cc',
-        'base/threading/worker_pool_win.cc',
         'base/time/time_win.cc',
         'base/timer/hi_res_timer_manager_win.cc',
         'base/trace_event/heap_profiler_allocation_register_win.cc',
         'base/trace_event/trace_event_etw_export_win.cc',
+        'base/win/core_winrt_util.cc',
         'base/win/enum_variant.cc',
         'base/win/event_trace_controller.cc',
         'base/win/event_trace_provider.cc',
@@ -895,9 +944,12 @@
         'base/win/registry.cc',
         'base/win/resource_util.cc',
         'base/win/scoped_bstr.cc',
+        'base/win/scoped_com_initializer.cc',
         'base/win/scoped_handle.cc',
+        'base/win/scoped_handle_verifier.cc',
         'base/win/scoped_process_information.cc',
         'base/win/scoped_variant.cc',
+        'base/win/scoped_winrt_initializer.cc',
         'base/win/shortcut.cc',
         'base/win/startup_information.cc',
         'base/win/wait_chain.cc',
@@ -930,7 +982,9 @@
   gn_gen_args = options.gn_gen_args or ''
   if not options.debug:
     gn_gen_args += ' is_debug=false'
-  cmd = [temp_gn, 'gen', build_dir, '--args=%s' % gn_gen_args]
+  cmd = [temp_gn, 'gen', build_dir, '--args=%s' % gn_gen_args,
+          "--root="+SRC_ROOT
+         ]
   check_call(cmd)
 
   cmd = ['ninja', '-C', build_dir, '-w', 'dupbuild=err']
diff --git a/tools/gn/bootstrap/build_vs.ninja.template b/tools/gn/bootstrap/build_vs.ninja.template
index 6e98575..88bba37 100644
--- a/tools/gn/bootstrap/build_vs.ninja.template
+++ b/tools/gn/bootstrap/build_vs.ninja.template
@@ -1,25 +1,28 @@
+
+arch = environment.x64
+
 rule cc
-  command = $cc /nologo /showIncludes /FC @${out}.rsp /c ${in} /Fo${out}
+  command = ninja -t msvc -e $arch -- $cc /nologo /showIncludes /FC @${out}.rsp /c ${in} /Fo${out}
   description = CC ${out}
   rspfile = ${out}.rsp
   rspfile_content = ${defines} ${includes} ${cflags} ${cflags_c}
   deps = msvc
 
 rule cxx
-  command = $cxx /nologo /showIncludes /FC @${out}.rsp /c ${in} /Fo${out}
+  command = ninja -t msvc -e $arch -- $cxx /nologo /showIncludes /FC @${out}.rsp /c ${in} /Fo${out}
   description = CXX ${out}
   rspfile = ${out}.rsp
   rspfile_content = ${defines} ${includes} ${cflags} ${cflags_cc}
   deps = msvc
 
 rule alink_thin
-  command = $ar /nologo /ignore:4221 /OUT:${out} @${out}.rsp
+  command = ninja -t msvc -e $arch -- $ar /nologo /ignore:4221 /OUT:${out} @${out}.rsp
   description = LIB ${out}
   rspfile = ${out}.rsp
   rspfile_content = ${in_newline}
 
 rule link
-  command = $ld /nologo /OUT:${out} /PDB:${out}.pdb @${out}.rsp
+  command = ninja -t msvc -e $arch -- $ld /nologo /OUT:${out} /PDB:${out}.pdb @${out}.rsp
   description = LINK ${out}
   rspfile = ${out}.rsp
   rspfile_content = ${in_newline} ${libs} ${solibs} ${ldflags}