Reland "win toolchain: Prepare downloader for windows sdk dir switch"

This is a reland of e72789f5b4e6e96a6c114133eb7d147da6be656b

Original change's description:
> win toolchain: Prepare downloader for windows sdk dir switch
>
> crrev.com/c/2655836 tries to move the Windows SDK from
> "win_sdk" to "Windows Kits/10".
>
> get_toolchain_if_necessary.py (in depot_tools) saves the path to the SDK to
> third_party/depot_tools/win_toolchain/data.json which then gets copied
> by a script in the chromium repo to build/win_toolchain.json.
> For the SDK move to work, chromium's pinned depot_tools
> must write the new SDK path when rolling in the new toolchain package.
> This change makes depot_tools handle win packages that have the
> windows sdk either win "win_sdk" or in "Windows Kits\10".
>
> The plan is:
>
> 1. Land this change, which can handle both path styles
> 2. Wait for depot_tools in chromium to update
> 3. Then roll to a win toolchain package with the new layout
>
> In a few years, when we no longer need the old layout,
> we can remove this detection code again and assume the new layout.
>
> Bug: 1173176
> Change-Id: Iaefc5c16685d3dbfff87a3e50a7b20b457366e44
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/tools/depot_tools/+/2666429
> Commit-Queue: Nico Weber <thakis@chromium.org>
> Auto-Submit: Nico Weber <thakis@chromium.org>
> Reviewed-by: Bruce Dawson <brucedawson@chromium.org>

Bug: 1173176,1173393
Change-Id: Ic706f694f8f0260208fa637864e62d7cc4f7ce93
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/tools/depot_tools/+/2668127
Commit-Queue: Nico Weber <thakis@chromium.org>
Auto-Submit: Nico Weber <thakis@chromium.org>
Reviewed-by: Takuto Ikuta <tikuta@chromium.org>
diff --git a/win_toolchain/get_toolchain_if_necessary.py b/win_toolchain/get_toolchain_if_necessary.py
index 261f6cd..241411b 100755
--- a/win_toolchain/get_toolchain_if_necessary.py
+++ b/win_toolchain/get_toolchain_if_necessary.py
@@ -65,7 +65,7 @@
   pass
 
 
-def GetFileList(root):
+def GetFileList(root, win_sdk_in_windows_kits):
   """Gets a normalized list of files under |root|."""
   assert not os.path.isabs(root)
   assert os.path.normpath(root) == root
@@ -79,11 +79,12 @@
   # Note: These files are only created on a Windows host, so the
   # ignored_directories list isn't relevant on non-Windows hosts.
 
+  win_sdk = 'Windows Kits\\10' if win_sdk_in_windows_kits else 'win_sdk'
   ignored_directories = ['wer\\reportqueue',
-                         'win_sdk\\debuggers\\x86\\sym\\',
-                         'win_sdk\\debuggers\\x64\\sym\\',
-                         'win_sdk\\debuggers\\x86\\src\\',
-                         'win_sdk\\debuggers\\x64\\src\\']
+                         win_sdk + '\\debuggers\\x86\\sym\\',
+                         win_sdk + '\\debuggers\\x64\\sym\\',
+                         win_sdk + '\\debuggers\\x86\\src\\',
+                         win_sdk + '\\debuggers\\x64\\src\\']
   for base, _, files in os.walk(root):
     paths = [os.path.join(base, f) for f in files]
     for p in paths:
@@ -97,7 +98,7 @@
   return os.path.join(root, os.pardir, '%s.timestamps' % sha1)
 
 
-def CalculateHash(root, expected_hash):
+def CalculateHash(root, expected_hash, win_sdk_in_windows_kits):
   """Calculates the sha1 of the paths to all files in the given |root| and the
   contents of those files, and returns as a hex string.
 
@@ -108,7 +109,7 @@
     full_root_path = os.path.join(root, expected_hash)
   else:
     full_root_path = root
-  file_list = GetFileList(full_root_path)
+  file_list = GetFileList(full_root_path, win_sdk_in_windows_kits)
   # Check whether we previously saved timestamps in $root/../{sha1}.timestamps.
   # If we didn't, or they don't match, then do the full calculation, otherwise
   # return the saved value.
@@ -178,20 +179,21 @@
   # The expected hash may be shorter, to reduce path lengths, in which case just
   # compare that many characters.
   if expected_hash and digest.hexdigest()[:len(expected_hash)] == expected_hash:
-    SaveTimestampsAndHash(root, digest.hexdigest())
+    SaveTimestampsAndHash(root, digest.hexdigest(), win_sdk_in_windows_kits)
     # Return the (potentially truncated) expected_hash.
     return expected_hash
   return digest.hexdigest()
 
 
-def CalculateToolchainHashes(root, remove_corrupt_toolchains):
+def CalculateToolchainHashes(
+        root, remove_corrupt_toolchains, win_sdk_in_windows_kits):
   """Calculate the hash of the different toolchains installed in the |root|
   directory."""
   hashes = []
   dir_list = [
       d for d in os.listdir(root) if os.path.isdir(os.path.join(root, d))]
   for d in dir_list:
-    toolchain_hash = CalculateHash(root, d)
+    toolchain_hash = CalculateHash(root, d, win_sdk_in_windows_kits)
     if toolchain_hash != d:
       print('The hash of a version of the toolchain has an unexpected value ('
              '%s instead of %s)%s.' % (toolchain_hash, d,
@@ -203,10 +205,10 @@
   return hashes
 
 
-def SaveTimestampsAndHash(root, sha1):
+def SaveTimestampsAndHash(root, sha1, win_sdk_in_windows_kits):
   """Saves timestamps and the final hash to be able to early-out more quickly
   next time."""
-  file_list = GetFileList(os.path.join(root, sha1))
+  file_list = GetFileList(os.path.join(root, sha1), win_sdk_in_windows_kits)
   timestamps_data = {
     'files': [[f, os.path.getmtime(f)] for f in file_list],
     'sha1': sha1,
@@ -487,13 +489,19 @@
 
   abs_toolchain_target_dir = os.path.abspath(toolchain_target_dir)
 
+  # The Windows SDK is either in `win_sdk` or in `Windows Kits\10`. This
+  # script must work with both layouts, so check which one it is.
+  win_sdk_in_windows_kits = os.path.isdir(
+          os.path.join(abs_toolchain_target_dir, 'Windows Kits', '10'))
+
   got_new_toolchain = False
 
   # If the current hash doesn't match what we want in the file, nuke and pave.
   # Typically this script is only run when the .sha1 one file is updated, but
   # directly calling "gclient runhooks" will also run it, so we cache
   # based on timestamps to make that case fast.
-  current_hashes = CalculateToolchainHashes(target_dir, True)
+  current_hashes = CalculateToolchainHashes(
+          target_dir, True, win_sdk_in_windows_kits)
   if desired_hash not in current_hashes:
     if options.no_download:
       raise SystemExit('Toolchain is out of date. Run "gclient runhooks" to '
@@ -536,7 +544,10 @@
 
     got_new_toolchain = True
 
-  win_sdk = os.path.join(abs_toolchain_target_dir, 'win_sdk')
+  if win_sdk_in_windows_kits:
+    win_sdk = os.path.join(abs_toolchain_target_dir, 'Windows Kits', '10')
+  else:
+    win_sdk = os.path.join(abs_toolchain_target_dir, 'win_sdk')
   try:
     version_file = os.path.join(toolchain_target_dir, 'VS_VERSION')
     vc_dir = os.path.join(toolchain_target_dir, 'VC')
@@ -568,14 +579,15 @@
     json.dump(data, f)
 
   if got_new_toolchain:
-    current_hashes = CalculateToolchainHashes(target_dir, False)
+    current_hashes = CalculateToolchainHashes(
+            target_dir, False, win_sdk_in_windows_kits)
     if desired_hash not in current_hashes:
       print(
           'Got wrong hash after pulling a new toolchain. '
           'Wanted \'%s\', got one of \'%s\'.' % (
               desired_hash, ', '.join(current_hashes)), file=sys.stderr)
       return 1
-    SaveTimestampsAndHash(target_dir, desired_hash)
+    SaveTimestampsAndHash(target_dir, desired_hash, win_sdk_in_windows_kits)
 
   if options.output_json:
     shutil.copyfile(os.path.join(target_dir, '..', 'data.json'),