1#!/usr/bin/env python3 2# Copyright 2012 The Chromium Authors 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5r"""Prints the lowest locally available SDK version greater than or equal to a 6given minimum sdk version to standard output. 7 8If --print_sdk_path is passed, then the script will also print the SDK path. 9If --print_bin_path is passed, then the script will also print the path to the 10toolchain bin dir. 11 12Usage: 13 python find_sdk.py \ 14 [--print_sdk_path] \ 15 [--print_bin_path] \ 16 10.6 # Ignores SDKs < 10.6 17 18Sample Output: 19/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk 20/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ 2110.14 22""" 23 24 25import os 26import plistlib 27import re 28import subprocess 29import sys 30 31from optparse import OptionParser 32 33 34class SdkError(Exception): 35 def __init__(self, value): 36 self.value = value 37 def __str__(self): 38 return repr(self.value) 39 40 41def parse_version(version_str): 42 """'10.6' => [10, 6]""" 43 return [int(s) for s in re.findall(r'(\d+)', version_str)] 44 45 46def main(): 47 parser = OptionParser() 48 parser.add_option("--print_sdk_path", 49 action="store_true", dest="print_sdk_path", default=False, 50 help="Additionally print the path the SDK (appears first).") 51 parser.add_option("--print_bin_path", 52 action="store_true", dest="print_bin_path", default=False, 53 help="Additionally print the path the toolchain bin dir.") 54 parser.add_option("--print_sdk_build", 55 action="store_true", dest="print_sdk_build", default=False, 56 help="Additionally print the build version of the SDK.") 57 options, args = parser.parse_args() 58 if len(args) != 1: 59 parser.error('Please specify a minimum SDK version') 60 min_sdk_version = args[0] 61 62 63 job = subprocess.Popen(['xcode-select', '-print-path'], 64 stdout=subprocess.PIPE, 65 stderr=subprocess.STDOUT) 66 out, err = job.communicate() 67 if job.returncode != 0: 68 print(out, file=sys.stderr) 69 print(err, file=sys.stderr) 70 raise Exception('Error %d running xcode-select' % job.returncode) 71 dev_dir = out.decode('UTF-8').rstrip() 72 sdk_dir = os.path.join( 73 dev_dir, 'Platforms/MacOSX.platform/Developer/SDKs') 74 75 if not os.path.isdir(sdk_dir): 76 raise SdkError('Install Xcode, launch it, accept the license ' + 77 'agreement, and run `sudo xcode-select -s /path/to/Xcode.app` ' + 78 'to continue.') 79 sdks = [re.findall('^MacOSX(\d+\.\d+)\.sdk$', s) for s in os.listdir(sdk_dir)] 80 sdks = [s[0] for s in sdks if s] # [['10.5'], ['10.6']] => ['10.5', '10.6'] 81 sdks = [s for s in sdks # ['10.5', '10.6'] => ['10.6'] 82 if parse_version(s) >= parse_version(min_sdk_version)] 83 if not sdks: 84 raise Exception('No %s+ SDK found' % min_sdk_version) 85 best_sdk = sorted(sdks, key=parse_version)[0] 86 sdk_name = 'MacOSX' + best_sdk + '.sdk' 87 sdk_path = os.path.join(sdk_dir, sdk_name) 88 89 if options.print_sdk_path: 90 print(sdk_path) 91 92 if options.print_bin_path: 93 bin_path = 'Toolchains/XcodeDefault.xctoolchain/usr/bin/' 94 print(os.path.join(dev_dir, bin_path)) 95 96 if options.print_sdk_build: 97 system_version_plist = os.path.join(sdk_path, 98 'System/Library/CoreServices/SystemVersion.plist') 99 with open(system_version_plist, 'rb') as f: 100 system_version_info = plistlib.load(f) 101 if 'ProductBuildVersion' not in system_version_info: 102 raise Exception('Failed to determine ProductBuildVersion' + 103 'for SDK at path %s' % system_version_plist) 104 print(system_version_info['ProductBuildVersion']) 105 106 print(best_sdk) 107 108 109if __name__ == '__main__': 110 if sys.platform != 'darwin': 111 raise Exception("This script only runs on Mac") 112 sys.exit(main()) 113