Fix flutter upgrade and flutter build ios (#4564)

* Fix `flutter upgrade`

* Fix builds on iOS
diff --git a/packages/flutter_tools/lib/src/base/process.dart b/packages/flutter_tools/lib/src/base/process.dart
index 4cf0565..ffe0f66 100644
--- a/packages/flutter_tools/lib/src/base/process.dart
+++ b/packages/flutter_tools/lib/src/base/process.dart
@@ -10,16 +10,26 @@
 
 typedef String StringConverter(String string);
 
+// TODO(ianh): We have way too many ways to run subprocesses in this project.
+
+Map<String, String> _environment(bool allowReentrantFlutter) {
+  return allowReentrantFlutter ? <String, String>{ 'FLUTTER_ALREADY_LOCKED': 'true' } : null;
+}
+
 /// This runs the command in the background from the specified working
 /// directory. Completes when the process has been started.
-Future<Process> runCommand(List<String> cmd, { String workingDirectory }) async {
+Future<Process> runCommand(List<String> cmd, {
+  String workingDirectory,
+  bool allowReentrantFlutter: false
+}) async {
   printTrace(cmd.join(' '));
   String executable = cmd[0];
   List<String> arguments = cmd.length > 1 ? cmd.sublist(1) : <String>[];
   Process process = await Process.start(
     executable,
     arguments,
-    workingDirectory: workingDirectory
+    workingDirectory: workingDirectory,
+    environment: _environment(allowReentrantFlutter)
   );
   return process;
 }
@@ -28,12 +38,17 @@
 /// this process' stdout/stderr. Completes with the process's exit code.
 Future<int> runCommandAndStreamOutput(List<String> cmd, {
   String workingDirectory,
+  bool allowReentrantFlutter: false,
   String prefix: '',
   bool trace: false,
   RegExp filter,
   StringConverter mapFunction
 }) async {
-  Process process = await runCommand(cmd, workingDirectory: workingDirectory);
+  Process process = await runCommand(
+    cmd,
+    workingDirectory: workingDirectory,
+    allowReentrantFlutter: allowReentrantFlutter
+  );
   process.stdout
     .transform(UTF8.decoder)
     .transform(const LineSplitter())
@@ -73,42 +88,30 @@
 Future<Process> runDetached(List<String> cmd) {
   printTrace(cmd.join(' '));
   Future<Process> proc = Process.start(
-      cmd[0], cmd.getRange(1, cmd.length).toList(),
-      mode: ProcessStartMode.DETACHED);
+    cmd[0], cmd.getRange(1, cmd.length).toList(),
+    mode: ProcessStartMode.DETACHED
+  );
   return proc;
 }
 
-/// Run cmd and return stdout.
-///
-/// Throws an error if cmd exits with a non-zero value.
-String runCheckedSync(List<String> cmd, {
-  String workingDirectory, bool truncateCommand: false
-}) {
-  return _runWithLoggingSync(
-    cmd, workingDirectory: workingDirectory, checked: true, noisyErrors: true, truncateCommand: truncateCommand
-  );
-}
-
-Future<RunResult> runAsync(List<String> cmd, { String workingDirectory }) async {
+Future<RunResult> runAsync(List<String> cmd, {
+  String workingDirectory,
+  bool allowReentrantFlutter: false
+}) async {
   printTrace(cmd.join(' '));
   ProcessResult results = await Process.run(
     cmd[0],
     cmd.getRange(1, cmd.length).toList(),
-    workingDirectory: workingDirectory
+    workingDirectory: workingDirectory,
+    environment: _environment(allowReentrantFlutter)
   );
   RunResult runResults = new RunResult(results);
   printTrace(runResults.toString());
   return runResults;
 }
 
-/// Run cmd and return stdout.
-String runSync(List<String> cmd, { String workingDirectory }) {
-  return _runWithLoggingSync(cmd, workingDirectory: workingDirectory);
-}
-
 bool exitsHappy(List<String> cli) {
   printTrace(cli.join(' '));
-
   try {
     return Process.runSync(cli.first, cli.sublist(1)).exitCode == 0;
   } catch (error) {
@@ -116,18 +119,53 @@
   }
 }
 
+/// Run cmd and return stdout.
+///
+/// Throws an error if cmd exits with a non-zero value.
+String runCheckedSync(List<String> cmd, {
+  String workingDirectory,
+  bool allowReentrantFlutter: false,
+  bool truncateCommand: false
+}) {
+  return _runWithLoggingSync(
+    cmd,
+    workingDirectory: workingDirectory,
+    allowReentrantFlutter: allowReentrantFlutter,
+    checked: true,
+    noisyErrors: true,
+    truncateCommand: truncateCommand
+  );
+}
+
+/// Run cmd and return stdout.
+String runSync(List<String> cmd, {
+  String workingDirectory,
+  bool allowReentrantFlutter: false
+}) {
+  return _runWithLoggingSync(
+    cmd,
+    workingDirectory: workingDirectory,
+    allowReentrantFlutter: allowReentrantFlutter
+  );
+}
+
 String _runWithLoggingSync(List<String> cmd, {
   bool checked: false,
   bool noisyErrors: false,
   String workingDirectory,
+  bool allowReentrantFlutter: false,
   bool truncateCommand: false
 }) {
   String cmdText = cmd.join(' ');
   if (truncateCommand && cmdText.length > 160)
     cmdText = cmdText.substring(0, 160) + '…';
   printTrace(cmdText);
-  ProcessResult results =
-      Process.runSync(cmd[0], cmd.getRange(1, cmd.length).toList(), workingDirectory: workingDirectory);
+  ProcessResult results = Process.runSync(
+    cmd[0],
+    cmd.getRange(1, cmd.length).toList(),
+    workingDirectory: workingDirectory,
+    environment: _environment(allowReentrantFlutter)
+  );
 
   printTrace('Exit code ${results.exitCode} from: ${cmd.join(' ')}');
 
diff --git a/packages/flutter_tools/lib/src/commands/upgrade.dart b/packages/flutter_tools/lib/src/commands/upgrade.dart
index 1d40a29..f0a48fe 100644
--- a/packages/flutter_tools/lib/src/commands/upgrade.dart
+++ b/packages/flutter_tools/lib/src/commands/upgrade.dart
@@ -50,9 +50,13 @@
     // if necessary.
     printStatus('');
     printStatus('Upgrading engine...');
-    code = await runCommandAndStreamOutput(<String>[
-      'bin/flutter', '--no-lock', '--no-color', 'precache'
-    ], workingDirectory: Cache.flutterRoot);
+    code = await runCommandAndStreamOutput(
+      <String>[
+        'bin/flutter', '--no-color', 'precache'
+      ],
+      workingDirectory: Cache.flutterRoot,
+      allowReentrantFlutter: true
+    );
 
     printStatus('');
     printStatus(FlutterVersion.getVersion(Cache.flutterRoot).toString());
diff --git a/packages/flutter_tools/lib/src/ios/mac.dart b/packages/flutter_tools/lib/src/ios/mac.dart
index fd2a452..ca09efe 100644
--- a/packages/flutter_tools/lib/src/ios/mac.dart
+++ b/packages/flutter_tools/lib/src/ios/mac.dart
@@ -150,7 +150,11 @@
     commands.addAll(<String>['-sdk', 'iphonesimulator', '-arch', 'x86_64']);
   }
 
-  RunResult result = await runAsync(commands, workingDirectory: app.rootPath);
+  RunResult result = await runAsync(
+    commands,
+    workingDirectory: app.rootPath,
+    allowReentrantFlutter: true
+  );
 
   if (result.exitCode != 0) {
     if (result.stderr.isNotEmpty)
diff --git a/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart b/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart
index 81e0472..6ac7a37 100644
--- a/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart
+++ b/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart
@@ -48,11 +48,6 @@
         negatable: true,
         hide: !verboseHelp,
         help: 'Whether to use terminal colors.');
-    argParser.addFlag('lock',
-        negatable: true,
-        hide: !verboseHelp,
-        help: 'Whether to lock the Flutter binary artifacts directory while running. (This prevents multiple simultaneous Flutter instances from colliding.)',
-        defaultsTo: true);
     argParser.addFlag('suppress-analytics',
         negatable: false,
         hide: !verboseHelp,
@@ -147,7 +142,7 @@
     // enginePath's initialiser uses it).
     Cache.flutterRoot = path.normalize(path.absolute(globalResults['flutter-root']));
 
-    if (globalResults['lock']);
+    if (Platform.environment['FLUTTER_ALREADY_LOCKED'] != 'true')
       await Cache.lock();
 
     if (globalResults['suppress-analytics'])