16d528ed9Sopenharmony_ci# Copyright 2018 The Chromium Authors. All rights reserved.
26d528ed9Sopenharmony_ci# Use of this source code is governed by a BSD-style license that can be
36d528ed9Sopenharmony_ci# found in the LICENSE file.
46d528ed9Sopenharmony_ci
56d528ed9Sopenharmony_cifrom contextlib import contextmanager
66d528ed9Sopenharmony_ci
76d528ed9Sopenharmony_cifrom recipe_engine import recipe_api
86d528ed9Sopenharmony_ci
96d528ed9Sopenharmony_ci
106d528ed9Sopenharmony_ciclass WindowsSDKApi(recipe_api.RecipeApi):
116d528ed9Sopenharmony_ci  """API for using Windows SDK distributed via CIPD."""
126d528ed9Sopenharmony_ci
136d528ed9Sopenharmony_ci  def __init__(self, sdk_properties, *args, **kwargs):
146d528ed9Sopenharmony_ci    super(WindowsSDKApi, self).__init__(*args, **kwargs)
156d528ed9Sopenharmony_ci
166d528ed9Sopenharmony_ci    self._sdk_package = sdk_properties['sdk_package']
176d528ed9Sopenharmony_ci    self._sdk_version = sdk_properties['sdk_version']
186d528ed9Sopenharmony_ci
196d528ed9Sopenharmony_ci  @contextmanager
206d528ed9Sopenharmony_ci  def __call__(self):
216d528ed9Sopenharmony_ci    """Setups the Windows SDK environment.
226d528ed9Sopenharmony_ci
236d528ed9Sopenharmony_ci    This call is a no-op on non-Windows platforms.
246d528ed9Sopenharmony_ci
256d528ed9Sopenharmony_ci    Raises:
266d528ed9Sopenharmony_ci        StepFailure or InfraFailure.
276d528ed9Sopenharmony_ci    """
286d528ed9Sopenharmony_ci    if not self.m.platform.is_win:
296d528ed9Sopenharmony_ci      yield
306d528ed9Sopenharmony_ci      return
316d528ed9Sopenharmony_ci
326d528ed9Sopenharmony_ci    with self.m.context(infra_steps=True):
336d528ed9Sopenharmony_ci      sdk_dir = self._ensure_sdk()
346d528ed9Sopenharmony_ci
356d528ed9Sopenharmony_ci    with self.m.context(**self._sdk_env(sdk_dir)):
366d528ed9Sopenharmony_ci      try:
376d528ed9Sopenharmony_ci        yield
386d528ed9Sopenharmony_ci      finally:
396d528ed9Sopenharmony_ci        # cl.exe automatically starts background mspdbsrv.exe daemon which
406d528ed9Sopenharmony_ci        # needs to be manually stopped so Swarming can tidy up after itself.
416d528ed9Sopenharmony_ci        self.m.step('taskkill mspdbsrv',
426d528ed9Sopenharmony_ci                    ['taskkill.exe', '/f', '/t', '/im', 'mspdbsrv.exe'])
436d528ed9Sopenharmony_ci
446d528ed9Sopenharmony_ci  def _ensure_sdk(self):
456d528ed9Sopenharmony_ci    """Ensures the Windows SDK CIPD package is installed.
466d528ed9Sopenharmony_ci
476d528ed9Sopenharmony_ci    Returns the directory where the SDK package has been installed.
486d528ed9Sopenharmony_ci
496d528ed9Sopenharmony_ci    Args:
506d528ed9Sopenharmony_ci      path (path): Path to a directory.
516d528ed9Sopenharmony_ci      version (str): CIPD instance ID, tag or ref.
526d528ed9Sopenharmony_ci    """
536d528ed9Sopenharmony_ci    sdk_dir = self.m.path['cache'].join('windows_sdk')
546d528ed9Sopenharmony_ci    pkgs = self.m.cipd.EnsureFile()
556d528ed9Sopenharmony_ci    pkgs.add_package(self._sdk_package, self._sdk_version)
566d528ed9Sopenharmony_ci    self.m.cipd.ensure(sdk_dir, pkgs)
576d528ed9Sopenharmony_ci    return sdk_dir
586d528ed9Sopenharmony_ci
596d528ed9Sopenharmony_ci  def _sdk_env(self, sdk_dir):
606d528ed9Sopenharmony_ci    """Constructs the environment for the SDK.
616d528ed9Sopenharmony_ci
626d528ed9Sopenharmony_ci    Returns environment and environment prefixes.
636d528ed9Sopenharmony_ci
646d528ed9Sopenharmony_ci    Args:
656d528ed9Sopenharmony_ci      sdk_dir (path): Path to a directory containing the SDK.
666d528ed9Sopenharmony_ci    """
676d528ed9Sopenharmony_ci    env = {}
686d528ed9Sopenharmony_ci    env_prefixes = {}
696d528ed9Sopenharmony_ci
706d528ed9Sopenharmony_ci    # Load .../Windows Kits/10/bin/SetEnv.${arch}.json to extract the required
716d528ed9Sopenharmony_ci    # environment. It contains a dict that looks like this:
726d528ed9Sopenharmony_ci    # {
736d528ed9Sopenharmony_ci    #   "env": {
746d528ed9Sopenharmony_ci    #     "VAR": [["x"], ["y"]],
756d528ed9Sopenharmony_ci    #     ...
766d528ed9Sopenharmony_ci    #   }
776d528ed9Sopenharmony_ci    # }
786d528ed9Sopenharmony_ci    # All these environment variables need to be added to the environment
796d528ed9Sopenharmony_ci    # for the compiler and linker to work.
806d528ed9Sopenharmony_ci    filename = 'SetEnv.%s.json' % {32: 'x86', 64: 'x64'}[self.m.platform.bits]
816d528ed9Sopenharmony_ci    step_result = self.m.json.read(
826d528ed9Sopenharmony_ci        'read %s' % filename,
836d528ed9Sopenharmony_ci        sdk_dir.join('Windows Kits', '10', 'bin', filename),
846d528ed9Sopenharmony_ci        step_test_data=lambda: self.m.json.test_api.output({
856d528ed9Sopenharmony_ci            'env': {
866d528ed9Sopenharmony_ci                'PATH': [['Windows Kits', '10', 'bin', '10.0.19041.0', 'x64']],
876d528ed9Sopenharmony_ci                'VSINSTALLDIR': [['.\\']],
886d528ed9Sopenharmony_ci            },
896d528ed9Sopenharmony_ci        }))
906d528ed9Sopenharmony_ci    data = step_result.json.output.get('env')
916d528ed9Sopenharmony_ci    for key in data:
926d528ed9Sopenharmony_ci      results = ['%s' % sdk_dir.join(*e) for e in data[key]]
936d528ed9Sopenharmony_ci
946d528ed9Sopenharmony_ci      # PATH is special-cased because we don't want to overwrite other things
956d528ed9Sopenharmony_ci      # like C:\Windows\System32. Others are replacements because prepending
966d528ed9Sopenharmony_ci      # doesn't necessarily makes sense, like VSINSTALLDIR.
976d528ed9Sopenharmony_ci      if key.lower() == 'path':
986d528ed9Sopenharmony_ci        env_prefixes[key] = results
996d528ed9Sopenharmony_ci      else:
1006d528ed9Sopenharmony_ci        env[key] = ';'.join(results)
1016d528ed9Sopenharmony_ci
1026d528ed9Sopenharmony_ci    return {'env': env, 'env_prefixes': env_prefixes}
103