Commit ef15eac8 authored by Jonah Williams's avatar Jonah Williams Committed by Flutter GitHub Bot

[flutter_tools] Remove context from Xcode and most of Xcodeproj (#48661)

parent 7cf2ff1f
...@@ -145,8 +145,20 @@ Future<T> runInContext<T>( ...@@ -145,8 +145,20 @@ Future<T> runInContext<T>(
VisualStudioValidator: () => const VisualStudioValidator(), VisualStudioValidator: () => const VisualStudioValidator(),
WebWorkflow: () => const WebWorkflow(), WebWorkflow: () => const WebWorkflow(),
WindowsWorkflow: () => const WindowsWorkflow(), WindowsWorkflow: () => const WindowsWorkflow(),
Xcode: () => Xcode(), Xcode: () => Xcode(
XcodeProjectInterpreter: () => XcodeProjectInterpreter(), logger: globals.logger,
processManager: globals.processManager,
platform: globals.platform,
fileSystem: globals.fs,
xcodeProjectInterpreter: xcodeProjectInterpreter,
),
XcodeProjectInterpreter: () => XcodeProjectInterpreter(
logger: globals.logger,
processManager: globals.processManager,
platform: globals.platform,
fileSystem: globals.fs,
terminal: globals.terminal,
),
XcodeValidator: () => const XcodeValidator(), XcodeValidator: () => const XcodeValidator(),
}, },
); );
......
...@@ -458,7 +458,7 @@ Future<XcodeBuildResult> buildXcodeProject({ ...@@ -458,7 +458,7 @@ Future<XcodeBuildResult> buildXcodeProject({
// e.g. `flutter build bundle`. // e.g. `flutter build bundle`.
buildCommands.add('FLUTTER_SUPPRESS_ANALYTICS=true'); buildCommands.add('FLUTTER_SUPPRESS_ANALYTICS=true');
buildCommands.add('COMPILER_INDEX_STORE_ENABLE=NO'); buildCommands.add('COMPILER_INDEX_STORE_ENABLE=NO');
buildCommands.addAll(environmentVariablesAsXcodeBuildSettings()); buildCommands.addAll(environmentVariablesAsXcodeBuildSettings(globals.platform));
final Stopwatch sw = Stopwatch()..start(); final Stopwatch sw = Stopwatch()..start();
initialBuildStatus = globals.logger.startProgress('Running Xcode build...', timeout: timeoutConfiguration.fastOperation); initialBuildStatus = globals.logger.startProgress('Running Xcode build...', timeout: timeoutConfiguration.fastOperation);
......
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
import 'dart:async'; import 'dart:async';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'package:platform/platform.dart';
import 'package:process/process.dart';
import '../artifacts.dart'; import '../artifacts.dart';
import '../base/common.dart'; import '../base/common.dart';
...@@ -14,6 +16,7 @@ import '../base/io.dart'; ...@@ -14,6 +16,7 @@ import '../base/io.dart';
import '../base/logger.dart'; import '../base/logger.dart';
import '../base/os.dart'; import '../base/os.dart';
import '../base/process.dart'; import '../base/process.dart';
import '../base/terminal.dart';
import '../base/utils.dart'; import '../base/utils.dart';
import '../build_info.dart'; import '../build_info.dart';
import '../cache.dart'; import '../cache.dart';
...@@ -221,15 +224,33 @@ XcodeProjectInterpreter get xcodeProjectInterpreter => context.get<XcodeProjectI ...@@ -221,15 +224,33 @@ XcodeProjectInterpreter get xcodeProjectInterpreter => context.get<XcodeProjectI
/// Interpreter of Xcode projects. /// Interpreter of Xcode projects.
class XcodeProjectInterpreter { class XcodeProjectInterpreter {
XcodeProjectInterpreter({
@required Platform platform,
@required ProcessManager processManager,
@required Logger logger,
@required FileSystem fileSystem,
@required AnsiTerminal terminal,
}) : _platform = platform,
_fileSystem = fileSystem,
_terminal = terminal,
_logger = logger,
_processUtils = ProcessUtils(logger: logger, processManager: processManager);
final Platform _platform;
final FileSystem _fileSystem;
final ProcessUtils _processUtils;
final AnsiTerminal _terminal;
final Logger _logger;
static const String _executable = '/usr/bin/xcodebuild'; static const String _executable = '/usr/bin/xcodebuild';
static final RegExp _versionRegex = RegExp(r'Xcode ([0-9.]+)'); static final RegExp _versionRegex = RegExp(r'Xcode ([0-9.]+)');
void _updateVersion() { void _updateVersion() {
if (!globals.platform.isMacOS || !globals.fs.file(_executable).existsSync()) { if (!_platform.isMacOS || !_fileSystem.file(_executable).existsSync()) {
return; return;
} }
try { try {
final RunResult result = processUtils.runSync( final RunResult result = _processUtils.runSync(
<String>[_executable, '-version'], <String>[_executable, '-version'],
); );
if (result.exitCode != 0) { if (result.exitCode != 0) {
...@@ -283,26 +304,26 @@ class XcodeProjectInterpreter { ...@@ -283,26 +304,26 @@ class XcodeProjectInterpreter {
Duration timeout = const Duration(minutes: 1), Duration timeout = const Duration(minutes: 1),
}) async { }) async {
final Status status = Status.withSpinner( final Status status = Status.withSpinner(
timeout: timeoutConfiguration.fastOperation, timeout: const TimeoutConfiguration().fastOperation,
timeoutConfiguration: timeoutConfiguration, timeoutConfiguration: const TimeoutConfiguration(),
platform: globals.platform, platform: _platform,
stopwatch: Stopwatch(), stopwatch: Stopwatch(),
supportsColor: globals.terminal.supportsColor, supportsColor: _terminal.supportsColor,
); );
final List<String> showBuildSettingsCommand = <String>[ final List<String> showBuildSettingsCommand = <String>[
_executable, _executable,
'-project', '-project',
globals.fs.path.absolute(projectPath), _fileSystem.path.absolute(projectPath),
'-target', '-target',
target, target,
'-showBuildSettings', '-showBuildSettings',
...environmentVariablesAsXcodeBuildSettings() ...environmentVariablesAsXcodeBuildSettings(_platform)
]; ];
try { try {
// showBuildSettings is reported to occasionally timeout. Here, we give it // showBuildSettings is reported to occasionally timeout. Here, we give it
// a lot of wiggle room (locally on Flutter Gallery, this takes ~1s). // a lot of wiggle room (locally on Flutter Gallery, this takes ~1s).
// When there is a timeout, we retry once. // When there is a timeout, we retry once.
final RunResult result = await processUtils.run( final RunResult result = await _processUtils.run(
showBuildSettingsCommand, showBuildSettingsCommand,
throwOnError: true, throwOnError: true,
workingDirectory: projectPath, workingDirectory: projectPath,
...@@ -317,7 +338,7 @@ class XcodeProjectInterpreter { ...@@ -317,7 +338,7 @@ class XcodeProjectInterpreter {
command: showBuildSettingsCommand.join(' '), command: showBuildSettingsCommand.join(' '),
).send(); ).send();
} }
globals.printTrace('Unexpected failure to get the build settings: $error.'); _logger.printTrace('Unexpected failure to get the build settings: $error.');
return const <String, String>{}; return const <String, String>{};
} finally { } finally {
status.stop(); status.stop();
...@@ -325,7 +346,7 @@ class XcodeProjectInterpreter { ...@@ -325,7 +346,7 @@ class XcodeProjectInterpreter {
} }
void cleanWorkspace(String workspacePath, String scheme) { void cleanWorkspace(String workspacePath, String scheme) {
processUtils.runSync(<String>[ _processUtils.runSync(<String>[
_executable, _executable,
'-workspace', '-workspace',
workspacePath, workspacePath,
...@@ -333,8 +354,8 @@ class XcodeProjectInterpreter { ...@@ -333,8 +354,8 @@ class XcodeProjectInterpreter {
scheme, scheme,
'-quiet', '-quiet',
'clean', 'clean',
...environmentVariablesAsXcodeBuildSettings() ...environmentVariablesAsXcodeBuildSettings(_platform)
], workingDirectory: globals.fs.currentDirectory.path); ], workingDirectory: _fileSystem.currentDirectory.path);
} }
Future<XcodeProjectInfo> getInfo(String projectPath, {String projectFilename}) async { Future<XcodeProjectInfo> getInfo(String projectPath, {String projectFilename}) async {
...@@ -342,7 +363,7 @@ class XcodeProjectInterpreter { ...@@ -342,7 +363,7 @@ class XcodeProjectInterpreter {
// * -project is passed and the given project isn't there, or // * -project is passed and the given project isn't there, or
// * no -project is passed and there isn't a project. // * no -project is passed and there isn't a project.
const int missingProjectExitCode = 66; const int missingProjectExitCode = 66;
final RunResult result = await processUtils.run( final RunResult result = await _processUtils.run(
<String>[ <String>[
_executable, _executable,
'-list', '-list',
...@@ -363,9 +384,9 @@ class XcodeProjectInterpreter { ...@@ -363,9 +384,9 @@ class XcodeProjectInterpreter {
/// This allows developers to pass arbitrary build settings in without the tool needing to make a flag /// This allows developers to pass arbitrary build settings in without the tool needing to make a flag
/// for or be aware of each one. This could be used to set code signing build settings in a CI /// for or be aware of each one. This could be used to set code signing build settings in a CI
/// environment without requiring settings changes in the Xcode project. /// environment without requiring settings changes in the Xcode project.
List<String> environmentVariablesAsXcodeBuildSettings() { List<String> environmentVariablesAsXcodeBuildSettings(Platform platform) {
const String xcodeBuildSettingPrefix = 'FLUTTER_XCODE_'; const String xcodeBuildSettingPrefix = 'FLUTTER_XCODE_';
return globals.platform.environment.entries.where((MapEntry<String, String> mapEntry) { return platform.environment.entries.where((MapEntry<String, String> mapEntry) {
return mapEntry.key.startsWith(xcodeBuildSettingPrefix); return mapEntry.key.startsWith(xcodeBuildSettingPrefix);
}).expand<String>((MapEntry<String, String> mapEntry) { }).expand<String>((MapEntry<String, String> mapEntry) {
// Remove FLUTTER_XCODE_ prefix from the environment variable to get the build setting. // Remove FLUTTER_XCODE_ prefix from the environment variable to get the build setting.
......
...@@ -85,7 +85,7 @@ Future<void> buildMacOS({ ...@@ -85,7 +85,7 @@ Future<void> buildMacOS({
'OBJROOT=${globals.fs.path.join(flutterBuildDir.absolute.path, 'Build', 'Intermediates.noindex')}', 'OBJROOT=${globals.fs.path.join(flutterBuildDir.absolute.path, 'Build', 'Intermediates.noindex')}',
'SYMROOT=${globals.fs.path.join(flutterBuildDir.absolute.path, 'Build', 'Products')}', 'SYMROOT=${globals.fs.path.join(flutterBuildDir.absolute.path, 'Build', 'Products')}',
'COMPILER_INDEX_STORE_ENABLE=NO', 'COMPILER_INDEX_STORE_ENABLE=NO',
...environmentVariablesAsXcodeBuildSettings() ...environmentVariablesAsXcodeBuildSettings(globals.platform)
], trace: true); ], trace: true);
} finally { } finally {
status.cancel(); status.cancel();
......
...@@ -4,11 +4,16 @@ ...@@ -4,11 +4,16 @@
import 'dart:async'; import 'dart:async';
import 'package:meta/meta.dart';
import 'package:platform/platform.dart';
import 'package:process/process.dart';
import '../base/common.dart'; import '../base/common.dart';
import '../base/context.dart'; import '../base/context.dart';
import '../base/file_system.dart';
import '../base/io.dart'; import '../base/io.dart';
import '../base/logger.dart';
import '../base/process.dart'; import '../base/process.dart';
import '../globals.dart' as globals;
import '../ios/xcodeproj.dart'; import '../ios/xcodeproj.dart';
const int kXcodeRequiredVersionMajor = 10; const int kXcodeRequiredVersionMajor = 10;
...@@ -41,14 +46,31 @@ String getNameForSdk(SdkType sdk) { ...@@ -41,14 +46,31 @@ String getNameForSdk(SdkType sdk) {
return null; return null;
} }
/// A utility class for interacting with Xcode command line tools.
class Xcode { class Xcode {
bool get isInstalledAndMeetsVersionCheck => globals.platform.isMacOS && isInstalled && isVersionSatisfactory; Xcode({
@required Platform platform,
@required ProcessManager processManager,
@required Logger logger,
@required FileSystem fileSystem,
@required XcodeProjectInterpreter xcodeProjectInterpreter,
}) : _platform = platform,
_fileSystem = fileSystem,
_xcodeProjectInterpreter = xcodeProjectInterpreter,
_processUtils = ProcessUtils(logger: logger, processManager: processManager);
final Platform _platform;
final ProcessUtils _processUtils;
final FileSystem _fileSystem;
final XcodeProjectInterpreter _xcodeProjectInterpreter;
bool get isInstalledAndMeetsVersionCheck => _platform.isMacOS && isInstalled && isVersionSatisfactory;
String _xcodeSelectPath; String _xcodeSelectPath;
String get xcodeSelectPath { String get xcodeSelectPath {
if (_xcodeSelectPath == null) { if (_xcodeSelectPath == null) {
try { try {
_xcodeSelectPath = processUtils.runSync( _xcodeSelectPath = _processUtils.runSync(
<String>['/usr/bin/xcode-select', '--print-path'], <String>['/usr/bin/xcode-select', '--print-path'],
).stdout.trim(); ).stdout.trim();
} on ProcessException { } on ProcessException {
...@@ -64,21 +86,21 @@ class Xcode { ...@@ -64,21 +86,21 @@ class Xcode {
if (xcodeSelectPath == null || xcodeSelectPath.isEmpty) { if (xcodeSelectPath == null || xcodeSelectPath.isEmpty) {
return false; return false;
} }
return xcodeProjectInterpreter.isInstalled; return _xcodeProjectInterpreter.isInstalled;
} }
int get majorVersion => xcodeProjectInterpreter.majorVersion; int get majorVersion => _xcodeProjectInterpreter.majorVersion;
int get minorVersion => xcodeProjectInterpreter.minorVersion; int get minorVersion => _xcodeProjectInterpreter.minorVersion;
String get versionText => xcodeProjectInterpreter.versionText; String get versionText => _xcodeProjectInterpreter.versionText;
bool _eulaSigned; bool _eulaSigned;
/// Has the EULA been signed? /// Has the EULA been signed?
bool get eulaSigned { bool get eulaSigned {
if (_eulaSigned == null) { if (_eulaSigned == null) {
try { try {
final RunResult result = processUtils.runSync( final RunResult result = _processUtils.runSync(
<String>['/usr/bin/xcrun', 'clang'], <String>['/usr/bin/xcrun', 'clang'],
); );
if (result.stdout != null && result.stdout.contains('license')) { if (result.stdout != null && result.stdout.contains('license')) {
...@@ -103,7 +125,7 @@ class Xcode { ...@@ -103,7 +125,7 @@ class Xcode {
try { try {
// This command will error if additional components need to be installed in // This command will error if additional components need to be installed in
// xcode 9.2 and above. // xcode 9.2 and above.
final RunResult result = processUtils.runSync( final RunResult result = _processUtils.runSync(
<String>['/usr/bin/xcrun', 'simctl', 'list'], <String>['/usr/bin/xcrun', 'simctl', 'list'],
); );
_isSimctlInstalled = result.stderr == null || result.stderr == ''; _isSimctlInstalled = result.stderr == null || result.stderr == '';
...@@ -115,7 +137,7 @@ class Xcode { ...@@ -115,7 +137,7 @@ class Xcode {
} }
bool get isVersionSatisfactory { bool get isVersionSatisfactory {
if (!xcodeProjectInterpreter.isInstalled) { if (!_xcodeProjectInterpreter.isInstalled) {
return false; return false;
} }
if (majorVersion > kXcodeRequiredVersionMajor) { if (majorVersion > kXcodeRequiredVersionMajor) {
...@@ -128,14 +150,14 @@ class Xcode { ...@@ -128,14 +150,14 @@ class Xcode {
} }
Future<RunResult> cc(List<String> args) { Future<RunResult> cc(List<String> args) {
return processUtils.run( return _processUtils.run(
<String>['xcrun', 'cc', ...args], <String>['xcrun', 'cc', ...args],
throwOnError: true, throwOnError: true,
); );
} }
Future<RunResult> clang(List<String> args) { Future<RunResult> clang(List<String> args) {
return processUtils.run( return _processUtils.run(
<String>['xcrun', 'clang', ...args], <String>['xcrun', 'clang', ...args],
throwOnError: true, throwOnError: true,
); );
...@@ -143,7 +165,7 @@ class Xcode { ...@@ -143,7 +165,7 @@ class Xcode {
Future<String> sdkLocation(SdkType sdk) async { Future<String> sdkLocation(SdkType sdk) async {
assert(sdk != null); assert(sdk != null);
final RunResult runResult = await processUtils.run( final RunResult runResult = await _processUtils.run(
<String>['xcrun', '--sdk', getNameForSdk(sdk), '--show-sdk-path'], <String>['xcrun', '--sdk', getNameForSdk(sdk), '--show-sdk-path'],
throwOnError: true, throwOnError: true,
); );
...@@ -158,10 +180,10 @@ class Xcode { ...@@ -158,10 +180,10 @@ class Xcode {
return null; return null;
} }
final List<String> searchPaths = <String>[ final List<String> searchPaths = <String>[
globals.fs.path.join(xcodeSelectPath, 'Applications', 'Simulator.app'), _fileSystem.path.join(xcodeSelectPath, 'Applications', 'Simulator.app'),
]; ];
return searchPaths.where((String p) => p != null).firstWhere( return searchPaths.where((String p) => p != null).firstWhere(
(String p) => globals.fs.directory(p).existsSync(), (String p) => _fileSystem.directory(p).existsSync(),
orElse: () => null, orElse: () => null,
); );
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment