// Copyright 2016 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import 'dart:async'; import '../base/common.dart'; import '../base/context.dart'; import '../base/file_system.dart'; import '../base/io.dart'; import '../base/platform.dart'; import '../base/process.dart'; import '../ios/xcodeproj.dart'; const int kXcodeRequiredVersionMajor = 9; const int kXcodeRequiredVersionMinor = 0; Xcode get xcode => context.get<Xcode>(); class Xcode { bool get isInstalledAndMeetsVersionCheck => platform.isMacOS && isInstalled && isVersionSatisfactory; String _xcodeSelectPath; String get xcodeSelectPath { if (_xcodeSelectPath == null) { try { _xcodeSelectPath = processUtils.runSync( <String>['/usr/bin/xcode-select', '--print-path'], ).stdout.trim(); } on ProcessException { // Ignored, return null below. } on ArgumentError { // Ignored, return null below. } } return _xcodeSelectPath; } bool get isInstalled { if (xcodeSelectPath == null || xcodeSelectPath.isEmpty) { return false; } return xcodeProjectInterpreter.isInstalled; } int get majorVersion => xcodeProjectInterpreter.majorVersion; int get minorVersion => xcodeProjectInterpreter.minorVersion; String get versionText => xcodeProjectInterpreter.versionText; bool _eulaSigned; /// Has the EULA been signed? bool get eulaSigned { if (_eulaSigned == null) { try { final RunResult result = processUtils.runSync( <String>['/usr/bin/xcrun', 'clang'], ); if (result.stdout != null && result.stdout.contains('license')) { _eulaSigned = false; } else if (result.stderr != null && result.stderr.contains('license')) { _eulaSigned = false; } else { _eulaSigned = true; } } on ProcessException { _eulaSigned = false; } } return _eulaSigned; } bool _isSimctlInstalled; /// Verifies that simctl is installed by trying to run it. bool get isSimctlInstalled { if (_isSimctlInstalled == null) { try { // This command will error if additional components need to be installed in // xcode 9.2 and above. final RunResult result = processUtils.runSync( <String>['/usr/bin/xcrun', 'simctl', 'list'], ); _isSimctlInstalled = result.stderr == null || result.stderr == ''; } on ProcessException { _isSimctlInstalled = false; } } return _isSimctlInstalled; } bool get isVersionSatisfactory { if (!xcodeProjectInterpreter.isInstalled) { return false; } if (majorVersion > kXcodeRequiredVersionMajor) { return true; } if (majorVersion == kXcodeRequiredVersionMajor) { return minorVersion >= kXcodeRequiredVersionMinor; } return false; } Future<RunResult> cc(List<String> args) { return processUtils.run( <String>['xcrun', 'cc', ...args], throwOnError: true, ); } Future<RunResult> clang(List<String> args) { return processUtils.run( <String>['xcrun', 'clang', ...args], throwOnError: true, ); } Future<String> iPhoneSdkLocation() async { final RunResult runResult = await processUtils.run( <String>['xcrun', '--sdk', 'iphoneos', '--show-sdk-path'], throwOnError: true, ); if (runResult.exitCode != 0) { throwToolExit('Could not find iPhone SDK location: ${runResult.stderr}'); } return runResult.stdout.trim(); } String getSimulatorPath() { if (xcodeSelectPath == null) { return null; } final List<String> searchPaths = <String>[ fs.path.join(xcodeSelectPath, 'Applications', 'Simulator.app'), ]; return searchPaths.where((String p) => p != null).firstWhere( (String p) => fs.directory(p).existsSync(), orElse: () => null, ); } }