• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2018 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4"""The `macos_sdk` module provides safe functions to access a semi-hermetic
5XCode installation.
6
7Available only to Google-run bots."""
8
9from contextlib import contextmanager
10
11from recipe_engine import recipe_api
12
13
14class MacOSSDKApi(recipe_api.RecipeApi):
15  """API for using OS X SDK distributed via CIPD."""
16
17  def __init__(self, sdk_properties, *args, **kwargs):
18    super(MacOSSDKApi, self).__init__(*args, **kwargs)
19
20    self._sdk_dir = None
21    self._sdk_version = sdk_properties['sdk_version'].lower()
22    self._tool_package = sdk_properties['tool_package']
23    self._tool_version = sdk_properties['tool_version']
24
25  @property
26  def sdk_dir(self):
27    assert self._sdk_dir
28    return self._sdk_dir
29
30  @contextmanager
31  def __call__(self):
32    """Sets up the XCode SDK environment.
33
34    This call is a no-op on non-Mac platforms.
35
36    This will deploy the helper tool and the XCode.app bundle at
37    `[START_DIR]/cache/macos_sdk`.
38
39    To avoid machines rebuilding these on every run, set up a named cache in
40    your cr-buildbucket.cfg file like:
41
42        caches: {
43          # Cache for mac_toolchain tool and XCode.app
44          name: "macos_sdk"
45          path: "macos_sdk"
46        }
47
48    If you have builders which e.g. use a non-current SDK, you can give them
49    a uniqely named cache:
50
51        caches: {
52          # Cache for N-1 version mac_toolchain tool and XCode.app
53          name: "macos_sdk_old"
54          path: "macos_sdk"
55        }
56
57    Usage:
58      with api.macos_sdk():
59        # sdk with mac build bits
60
61    Raises:
62        StepFailure or InfraFailure.
63    """
64    if not self.m.platform.is_mac:
65      yield
66      return
67
68    try:
69      with self.m.context(infra_steps=True):
70        self._sdk_dir = self._ensure_sdk()
71        self.m.step('select XCode',
72                    ['sudo', 'xcode-select', '--switch', self._sdk_dir])
73      yield
74    finally:
75      with self.m.context(infra_steps=True):
76        self.m.step('reset XCode', ['sudo', 'xcode-select', '--reset'])
77
78  def _ensure_sdk(self):
79    """Ensures the mac_toolchain tool and MacOS SDK packages are installed.
80
81    Returns Path to the installed sdk app bundle."""
82    cache_dir = self.m.path['cache'].join('macos_sdk')
83    pkgs = self.m.cipd.EnsureFile()
84    pkgs.add_package(self._tool_package, self._tool_version)
85    self.m.cipd.ensure(cache_dir, pkgs)
86
87    sdk_dir = cache_dir.join('XCode.app')
88    self.m.step('install xcode', [
89        cache_dir.join('mac_toolchain'),
90        'install',
91        '-kind',
92        'mac',
93        '-xcode-version',
94        self._sdk_version,
95        '-output-dir',
96        sdk_dir,
97    ])
98    return sdk_dir
99