xcode.dart 4.49 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4 5 6
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:async';

7
import '../base/common.dart';
8 9 10
import '../base/context.dart';
import '../base/file_system.dart';
import '../base/io.dart';
11
import '../base/platform.dart';
12 13 14
import '../base/process.dart';
import '../ios/xcodeproj.dart';

15 16
const int kXcodeRequiredVersionMajor = 10;
const int kXcodeRequiredVersionMinor = 2;
17 18 19

Xcode get xcode => context.get<Xcode>();

20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
enum SdkType {
  iPhone,
  iPhoneSimulator,
  macOS,
}

/// SDK name passed to `xcrun --sdk`. Corresponds to undocumented Xcode
/// SUPPORTED_PLATFORMS values.
///
/// Usage: xcrun [options] <tool name> ... arguments ...
/// ...
/// --sdk <sdk name>            find the tool for the given SDK name
String getNameForSdk(SdkType sdk) {
  switch (sdk) {
    case SdkType.iPhone:
      return 'iphoneos';
    case SdkType.iPhoneSimulator:
      return 'iphonesimulator';
    case SdkType.macOS:
      return 'macosx';
  }
  assert(false);
  return null;
}

45
class Xcode {
46
  bool get isInstalledAndMeetsVersionCheck => platform.isMacOS && isInstalled && isVersionSatisfactory;
47 48 49 50 51

  String _xcodeSelectPath;
  String get xcodeSelectPath {
    if (_xcodeSelectPath == null) {
      try {
52 53 54
        _xcodeSelectPath = processUtils.runSync(
          <String>['/usr/bin/xcode-select', '--print-path'],
        ).stdout.trim();
55 56
      } on ProcessException {
        // Ignored, return null below.
57 58
      } on ArgumentError {
        // Ignored, return null below.
59 60 61 62 63 64
      }
    }
    return _xcodeSelectPath;
  }

  bool get isInstalled {
65
    if (xcodeSelectPath == null || xcodeSelectPath.isEmpty) {
66
      return false;
67
    }
68 69 70 71 72 73 74 75 76 77 78 79 80 81
    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 {
82 83 84 85
        final RunResult result = processUtils.runSync(
          <String>['/usr/bin/xcrun', 'clang'],
        );
        if (result.stdout != null && result.stdout.contains('license')) {
86
          _eulaSigned = false;
87
        } else if (result.stderr != null && result.stderr.contains('license')) {
88
          _eulaSigned = false;
89
        } else {
90
          _eulaSigned = true;
91
        }
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
      } 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.
107 108 109
        final RunResult result = processUtils.runSync(
          <String>['/usr/bin/xcrun', 'simctl', 'list'],
        );
110 111 112 113 114 115 116 117 118
        _isSimctlInstalled = result.stderr == null || result.stderr == '';
      } on ProcessException {
        _isSimctlInstalled = false;
      }
    }
    return _isSimctlInstalled;
  }

  bool get isVersionSatisfactory {
119
    if (!xcodeProjectInterpreter.isInstalled) {
120
      return false;
121 122
    }
    if (majorVersion > kXcodeRequiredVersionMajor) {
123
      return true;
124 125
    }
    if (majorVersion == kXcodeRequiredVersionMajor) {
126
      return minorVersion >= kXcodeRequiredVersionMinor;
127
    }
128 129 130 131
    return false;
  }

  Future<RunResult> cc(List<String> args) {
132 133 134 135
    return processUtils.run(
      <String>['xcrun', 'cc', ...args],
      throwOnError: true,
    );
136 137 138
  }

  Future<RunResult> clang(List<String> args) {
139 140 141 142
    return processUtils.run(
      <String>['xcrun', 'clang', ...args],
      throwOnError: true,
    );
143 144
  }

145 146
  Future<String> sdkLocation(SdkType sdk) async {
    assert(sdk != null);
147
    final RunResult runResult = await processUtils.run(
148
      <String>['xcrun', '--sdk', getNameForSdk(sdk), '--show-sdk-path'],
149
      throwOnError: true,
150 151 152 153 154
    );
    if (runResult.exitCode != 0) {
      throwToolExit('Could not find iPhone SDK location: ${runResult.stderr}');
    }
    return runResult.stdout.trim();
155 156
  }

157
  String getSimulatorPath() {
158
    if (xcodeSelectPath == null) {
159
      return null;
160
    }
161 162 163 164 165 166 167 168 169
    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,
    );
  }
}