Use CIPD Windows SDK to generate WinRT headers (#448)

Rather than relying on the Windows SDK that happens to be on the host
machine, we now pull down the Windows SDK from CIPD into
third_party/windows_sdk and use it directly.

cppwinrt relies on a registry entry to determine the SDK location and
doesn't currently support a mechanism for specifying this location
explicitly via an argument. Instead, we do what cppwinrt itself does and
parse out the Platform.XML file to determine the set of .winmd files to
take as input, then pass them to the tool directly.
diff --git a/build/win/generate_winrt_headers.py b/build/win/generate_winrt_headers.py
index 9b4f063..81ada4c 100644
--- a/build/win/generate_winrt_headers.py
+++ b/build/win/generate_winrt_headers.py
@@ -1,53 +1,74 @@
-#!/usr/bin/env python

-# Copyright 2014 The Chromium Authors. All rights reserved.

-# Use of this source code is governed by a BSD-style license that can be

-# found in the LICENSE file.

-

-import os

-import shutil

-import subprocess

-import sys

-import winreg

-

-def clean(output_dir):

-  if os.path.exists(output_dir):

-        shutil.rmtree(output_dir, ignore_errors=True)

-  return

-

-

-def generate_headers(output_dir):

-  """Run cppwinrt.exe on the installed Windows SDK version and generate

-  cppwinrt headers in the output directory.

-  """

-  

-  cppwinrt_exe = os.path.join(

-  __file__,

-  '..\\..\\..\\third_party\\cppwinrt\\bin\\cppwinrt.exe')

-

-  args = [cppwinrt_exe, '-in', 'sdk',

-      '-out', '%s' % output_dir]

-

-  cppwinrt_sdk_result = subprocess.run(args)

-  if cppwinrt_sdk_result.returncode != 0:

-    print('Retrying with alternate location for References')

-    # Try to point to References folder under sdk directly. It was observed

-    # that in some cases that is where References folder is placed.

-    r = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE)

-    k = winreg.OpenKey(r, r"SOFTWARE\WOW6432Node\Microsoft\Windows Kits\Installed Roots")

-    sdk_path = winreg.QueryValueEx(k, "KitsRoot10")[0]

-    subprocess.check_output([cppwinrt_exe,

-        '-in', os.path.join(sdk_path, "References"),  '-out', '%s' % output_dir])

-

-  print('All done')

-  return 0

-

-

-def main(argv):

-  generated_dir = os.path.join(

-  __file__,

-  '..\\..\\..\\third_party\\cppwinrt\\generated')

-  clean(generated_dir)

-  return generate_headers(generated_dir)

-

-if __name__ == "__main__":

-  sys.exit(main(sys.argv[1:]))

+#!/usr/bin/env python
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import shutil
+import subprocess
+import sys
+
+from xml.dom import minidom
+
+# The SDK version downloaded from CIPD.
+# TODO(cbracken): pass this as an argument to this script.
+SDK_VERSION = '10.0.19041.0'
+SDK_PATH = r'third_party\windows_sdk\Windows Kits\10'
+
+def clean(output_dir):
+  if os.path.exists(output_dir):
+        shutil.rmtree(output_dir, ignore_errors=True)
+  return
+
+
+def to_abs_path(relative_path):
+  """Returns a canonical path for the specified path relative to the script
+  directory.
+  """
+
+  script_dir = os.path.dirname(os.path.realpath(__file__))
+  return os.path.realpath(os.path.join(script_dir, relative_path))
+
+
+def get_inputs(sdk_path, sdk_version):
+  """Parses the SDK's Platform.xml file and generates the set of .winmd files
+  to pass as input to cppwinrt.
+  """
+
+  platform_xml = r'%s\Platforms\UAP\%s\Platform.xml' % (sdk_path, sdk_version)
+  reference_dir = r'%s\References\%s' % (sdk_path, sdk_version)
+
+  inputs = []
+  doc = minidom.parse(platform_xml)
+  for contract in doc.getElementsByTagName('ApiContract'):
+    name = contract.getAttribute('name')
+    version = contract.getAttribute('version')
+    winmd_path = os.path.join(reference_dir, name, version, '%s.winmd' % name)
+    inputs.append(winmd_path)
+  return inputs
+
+
+def generate_headers(input_files, output_dir):
+  """Run cppwinrt.exe on the installed Windows SDK version and generate
+  cppwinrt headers in the output directory.
+  """
+
+  args = [to_abs_path(r'..\..\third_party\cppwinrt\bin\cppwinrt.exe')]
+  for winmd_path in input_files:
+    args += ['-in', winmd_path]
+  args += ['-out', output_dir]
+  subprocess.check_output(args)
+  return 0
+
+
+def main(argv):
+  generated_dir = to_abs_path(r'..\..\third_party\cppwinrt\generated')
+  clean(generated_dir)
+
+  abs_sdk_path = to_abs_path(r'..\..\%s' % SDK_PATH)
+  input_files = get_inputs(abs_sdk_path, SDK_VERSION)
+  return generate_headers(input_files, generated_dir)
+
+
+if __name__ == "__main__":
+  sys.exit(main(sys.argv[1:]))