1# Copyright (C) 2021 The Android Open Source Project 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14"""The `macos_sdk` module provides safe functions to access a semi-hermetic 15XCode installation. 16 17Available only to Google-run bots.""" 18 19from contextlib import contextmanager 20 21from recipe_engine import recipe_api 22 23 24class MacOSSDKApi(recipe_api.RecipeApi): 25 """API for using OS X SDK distributed via CIPD.""" 26 27 def __init__(self, sdk_properties, *args, **kwargs): 28 super(MacOSSDKApi, self).__init__(*args, **kwargs) 29 30 self._sdk_dir = None 31 self._sdk_version = sdk_properties['sdk_version'].lower() 32 self._tool_package = sdk_properties['tool_package'] 33 self._tool_version = sdk_properties['tool_version'] 34 35 @property 36 def sdk_dir(self): 37 assert self._sdk_dir 38 return self._sdk_dir 39 40 @contextmanager 41 def __call__(self): 42 """Sets up the XCode SDK environment. 43 44 This call is a no-op on non-Mac platforms. 45 46 This will deploy the helper tool and the XCode.app bundle at 47 `[START_DIR]/cache/macos_sdk`. 48 49 To avoid machines rebuilding these on every run, set up a named cache in 50 your cr-buildbucket.cfg file like: 51 52 caches: { 53 # Cache for mac_toolchain tool and XCode.app 54 name: "macos_sdk" 55 path: "macos_sdk" 56 } 57 58 If you have builders which e.g. use a non-current SDK, you can give them 59 a uniqely named cache: 60 61 caches: { 62 # Cache for N-1 version mac_toolchain tool and XCode.app 63 name: "macos_sdk_old" 64 path: "macos_sdk" 65 } 66 67 Usage: 68 with api.macos_sdk(): 69 # sdk with mac build bits 70 71 Raises: 72 StepFailure or InfraFailure. 73 """ 74 if not self.m.platform.is_mac: 75 yield 76 return 77 78 try: 79 with self.m.context(infra_steps=True): 80 self._sdk_dir = self._ensure_sdk() 81 self.m.step('select XCode', 82 ['sudo', 'xcode-select', '--switch', self._sdk_dir]) 83 yield 84 finally: 85 with self.m.context(infra_steps=True): 86 self.m.step('reset XCode', ['sudo', 'xcode-select', '--reset']) 87 88 def _ensure_sdk(self): 89 """Ensures the mac_toolchain tool and MacOS SDK packages are installed. 90 91 Returns Path to the installed sdk app bundle.""" 92 cache_dir = self.m.path['cache'].join('macos_sdk') 93 pkgs = self.m.cipd.EnsureFile() 94 pkgs.add_package(self._tool_package, self._tool_version) 95 self.m.cipd.ensure(cache_dir, pkgs) 96 97 sdk_dir = cache_dir.join('XCode.app') 98 self.m.step('install xcode', [ 99 cache_dir.join('mac_toolchain'), 100 'install', 101 '-kind', 102 'mac', 103 '-xcode-version', 104 self._sdk_version, 105 '-output-dir', 106 sdk_dir, 107 ]) 108 return sdk_dir 109