// 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/context.dart'; import '../base/file_system.dart'; import '../base/io.dart'; import '../base/process.dart'; import '../base/process_manager.dart'; import '../ios/xcodeproj.dart'; const int kXcodeRequiredVersionMajor = 9; const int kXcodeRequiredVersionMinor = 0; Xcode get xcode => context.get(); class Xcode { bool get isInstalledAndMeetsVersionCheck => isInstalled && isVersionSatisfactory; String _xcodeSelectPath; String get xcodeSelectPath { if (_xcodeSelectPath == null) { try { _xcodeSelectPath = processManager.runSync(['/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 ProcessResult result = processManager.runSync(['/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 ProcessResult result = processManager.runSync(['/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 cc(List args) { return runCheckedAsync(['xcrun', 'cc', ...args]); } Future clang(List args) { return runCheckedAsync(['xcrun', 'clang', ...args]); } Future dsymutil(List args) { return runCheckedAsync(['xcrun', 'dsymutil', ...args]); } Future strip(List args) { return runCheckedAsync(['xcrun', 'strip', ...args]); } Future otool(List args) { return runCheckedAsync(['xcrun', 'otool', ...args]); } String getSimulatorPath() { if (xcodeSelectPath == null) return null; final List searchPaths = [ fs.path.join(xcodeSelectPath, 'Applications', 'Simulator.app'), ]; return searchPaths.where((String p) => p != null).firstWhere( (String p) => fs.directory(p).existsSync(), orElse: () => null, ); } }