1// Copyright 2016 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 5import 'dart:async'; 6 7import '../base/context.dart'; 8import '../base/file_system.dart'; 9import '../base/io.dart'; 10import '../base/process.dart'; 11import '../base/process_manager.dart'; 12import '../ios/xcodeproj.dart'; 13 14const int kXcodeRequiredVersionMajor = 9; 15const int kXcodeRequiredVersionMinor = 0; 16 17Xcode get xcode => context.get<Xcode>(); 18 19class Xcode { 20 bool get isInstalledAndMeetsVersionCheck => isInstalled && isVersionSatisfactory; 21 22 String _xcodeSelectPath; 23 String get xcodeSelectPath { 24 if (_xcodeSelectPath == null) { 25 try { 26 _xcodeSelectPath = processManager.runSync(<String>['/usr/bin/xcode-select', '--print-path']).stdout.trim(); 27 } on ProcessException { 28 // Ignored, return null below. 29 } on ArgumentError { 30 // Ignored, return null below. 31 } 32 } 33 return _xcodeSelectPath; 34 } 35 36 bool get isInstalled { 37 if (xcodeSelectPath == null || xcodeSelectPath.isEmpty) 38 return false; 39 return xcodeProjectInterpreter.isInstalled; 40 } 41 42 int get majorVersion => xcodeProjectInterpreter.majorVersion; 43 44 int get minorVersion => xcodeProjectInterpreter.minorVersion; 45 46 String get versionText => xcodeProjectInterpreter.versionText; 47 48 bool _eulaSigned; 49 /// Has the EULA been signed? 50 bool get eulaSigned { 51 if (_eulaSigned == null) { 52 try { 53 final ProcessResult result = processManager.runSync(<String>['/usr/bin/xcrun', 'clang']); 54 if (result.stdout != null && result.stdout.contains('license')) 55 _eulaSigned = false; 56 else if (result.stderr != null && result.stderr.contains('license')) 57 _eulaSigned = false; 58 else 59 _eulaSigned = true; 60 } on ProcessException { 61 _eulaSigned = false; 62 } 63 } 64 return _eulaSigned; 65 } 66 67 bool _isSimctlInstalled; 68 69 /// Verifies that simctl is installed by trying to run it. 70 bool get isSimctlInstalled { 71 if (_isSimctlInstalled == null) { 72 try { 73 // This command will error if additional components need to be installed in 74 // xcode 9.2 and above. 75 final ProcessResult result = processManager.runSync(<String>['/usr/bin/xcrun', 'simctl', 'list']); 76 _isSimctlInstalled = result.stderr == null || result.stderr == ''; 77 } on ProcessException { 78 _isSimctlInstalled = false; 79 } 80 } 81 return _isSimctlInstalled; 82 } 83 84 bool get isVersionSatisfactory { 85 if (!xcodeProjectInterpreter.isInstalled) 86 return false; 87 if (majorVersion > kXcodeRequiredVersionMajor) 88 return true; 89 if (majorVersion == kXcodeRequiredVersionMajor) 90 return minorVersion >= kXcodeRequiredVersionMinor; 91 return false; 92 } 93 94 Future<RunResult> cc(List<String> args) { 95 return runCheckedAsync(<String>['xcrun', 'cc', ...args]); 96 } 97 98 Future<RunResult> clang(List<String> args) { 99 return runCheckedAsync(<String>['xcrun', 'clang', ...args]); 100 } 101 102 Future<RunResult> dsymutil(List<String> args) { 103 return runCheckedAsync(<String>['xcrun', 'dsymutil', ...args]); 104 } 105 106 Future<RunResult> strip(List<String> args) { 107 return runCheckedAsync(<String>['xcrun', 'strip', ...args]); 108 } 109 110 Future<RunResult> otool(List<String> args) { 111 return runCheckedAsync(<String>['xcrun', 'otool', ...args]); 112 } 113 114 String getSimulatorPath() { 115 if (xcodeSelectPath == null) 116 return null; 117 final List<String> searchPaths = <String>[ 118 fs.path.join(xcodeSelectPath, 'Applications', 'Simulator.app'), 119 ]; 120 return searchPaths.where((String p) => p != null).firstWhere( 121 (String p) => fs.directory(p).existsSync(), 122 orElse: () => null, 123 ); 124 } 125} 126