Use output dir instead of specific paths in assemble rules (#39274)

diff --git a/packages/flutter_tools/bin/macos_assemble.sh b/packages/flutter_tools/bin/macos_assemble.sh
index 6b10f86..23128cc 100755
--- a/packages/flutter_tools/bin/macos_assemble.sh
+++ b/packages/flutter_tools/bin/macos_assemble.sh
@@ -72,4 +72,5 @@
     -dBuildMode="${build_mode}"                                             \
     --build-inputs="${build_inputs_path}"                                   \
     --build-outputs="${build_outputs_path}"                                 \
+    --output="${ephemeral_dir}"                                             \
    "${build_mode}_macos_bundle_flutter_assets"
diff --git a/packages/flutter_tools/lib/src/build_system/build_system.dart b/packages/flutter_tools/lib/src/build_system/build_system.dart
index e60c444..c97ce86 100644
--- a/packages/flutter_tools/lib/src/build_system/build_system.dart
+++ b/packages/flutter_tools/lib/src/build_system/build_system.dart
@@ -349,6 +349,7 @@
   /// defaults based on it.
   factory Environment({
     @required Directory projectDir,
+    @required Directory outputDir,
     Directory buildDir,
     Map<String, String> defines = const <String, String>{},
   }) {
@@ -371,6 +372,7 @@
     final Directory rootBuildDir = buildDir ?? projectDir.childDirectory('build');
     final Directory buildDirectory = rootBuildDir.childDirectory(buildPrefix);
     return Environment._(
+      outputDir: outputDir,
       projectDir: projectDir,
       buildDir: buildDirectory,
       rootBuildDir: rootBuildDir,
@@ -381,6 +383,7 @@
   }
 
   Environment._({
+    @required this.outputDir,
     @required this.projectDir,
     @required this.buildDir,
     @required this.rootBuildDir,
@@ -401,6 +404,9 @@
   /// The [Source] value which is substituted with a path to the flutter root.
   static const String kFlutterRootDirectory = '{FLUTTER_ROOT}';
 
+  /// The [Source] value which is substituted with a path to [outputDir].
+  static const String kOutputDirectory = '{OUTPUT_DIR}';
+
   /// The `PROJECT_DIR` environment variable.
   ///
   /// This should be root of the flutter project where a pubspec and dart files
@@ -424,6 +430,11 @@
   /// Defaults to to the value of [Cache.flutterRoot].
   final Directory flutterRootDir;
 
+  /// The `OUTPUT_DIR` environment variable.
+  ///
+  /// Must be provided to configure the output location for the final artifacts.
+  final Directory outputDir;
+
   /// Additional configuration passed to the build targets.
   ///
   /// Setting values here forces a unique build directory to be chosen
@@ -464,6 +475,7 @@
     { BuildSystemConfig buildSystemConfig = const BuildSystemConfig() }
   ) async {
     environment.buildDir.createSync(recursive: true);
+    environment.outputDir.createSync(recursive: true);
 
     // Load file hash store from previous builds.
     final FileHashStore fileCache = FileHashStore(environment)
@@ -482,7 +494,7 @@
     }
     // TODO(jonahwilliams): this is a bit of a hack, due to various parts of
     // the flutter tool writing these files unconditionally. Since Xcode uses
-    // timestamps to track files, this leads to unecessary rebuilds if they
+    // timestamps to track files, this leads to unnecessary rebuilds if they
     // are included. Once all the places that write these files have been
     // tracked down and moved into assemble, these checks should be removable.
     // We also remove files under .dart_tool, since these are intermediaries
diff --git a/packages/flutter_tools/lib/src/build_system/source.dart b/packages/flutter_tools/lib/src/build_system/source.dart
index 6aeda2c..2d43129 100644
--- a/packages/flutter_tools/lib/src/build_system/source.dart
+++ b/packages/flutter_tools/lib/src/build_system/source.dart
@@ -71,6 +71,10 @@
         segments.addAll(
             fs.path.split(environment.flutterRootDir.absolute.path));
         break;
+      case Environment.kOutputDirectory:
+        segments.addAll(
+            fs.path.split(environment.outputDir.resolveSymbolicLinksSync()));
+        break;
       default:
         throw InvalidPatternException(pattern);
     }
diff --git a/packages/flutter_tools/lib/src/build_system/targets/macos.dart b/packages/flutter_tools/lib/src/build_system/targets/macos.dart
index 78c0709..70e91c3 100644
--- a/packages/flutter_tools/lib/src/build_system/targets/macos.dart
+++ b/packages/flutter_tools/lib/src/build_system/targets/macos.dart
@@ -15,12 +15,11 @@
 import '../../devfs.dart';
 import '../../globals.dart';
 import '../../macos/xcode.dart';
-import '../../project.dart';
 import '../build_system.dart';
 import '../exceptions.dart';
 import 'dart.dart';
 
-const String _kOutputPrefix = '{PROJECT_DIR}/macos/Flutter/ephemeral/FlutterMacOS.framework';
+const String _kOutputPrefix = '{OUTPUT_DIR}/FlutterMacOS.framework';
 
 /// The copying logic for flutter assets in macOS.
 // TODO(jonahwilliams): remove once build planning lands.
@@ -51,8 +50,7 @@
       manifestPath: environment.projectDir.childFile('pubspec.yaml').path,
       packagesPath: environment.projectDir.childFile('.packages').path,
     );
-    final FlutterProject flutterProject = FlutterProject.fromDirectory(environment.projectDir);
-    final String prefix = fs.path.join(flutterProject.macos.ephemeralDirectory.path,
+    final String prefix = fs.path.join(environment.outputDir.path,
         'App.framework', 'Versions', 'A', 'Resources', 'flutter_assets');
     final List<File> results = <File>[];
     for (String key in assetBundle.entries.keys) {
@@ -65,7 +63,7 @@
 
 /// Copy the macOS framework to the correct copy dir by invoking 'cp -R'.
 ///
-/// This class is abstract to share logic between the three conrete
+/// This class is abstract to share logic between the three concrete
 /// implementations. The shelling out is done to avoid complications with
 /// preserving special files (e.g., symbolic links) in the framework structure.
 ///
@@ -121,9 +119,8 @@
     }
     final BuildMode buildMode = getBuildModeForName(environment.defines[kBuildMode]);
     final String basePath = artifacts.getArtifactPath(Artifact.flutterMacOSFramework, mode: buildMode);
-    final FlutterProject flutterProject = FlutterProject.fromDirectory(environment.projectDir);
-    final Directory targetDirectory = flutterProject.macos
-      .ephemeralDirectory
+    final Directory targetDirectory = environment
+      .outputDir
       .childDirectory('FlutterMacOS.framework');
     if (targetDirectory.existsSync()) {
       targetDirectory.deleteSync(recursive: true);
@@ -247,7 +244,6 @@
     if (buildMode == BuildMode.debug) {
       throw Exception('precompiled macOS framework only supported in release/profile builds.');
     }
-    final FlutterProject flutterProject = FlutterProject.fromDirectory(environment.projectDir);
     final int result = await AOTSnapshotter(reportTimings: false).build(
       bitcode: false,
       buildMode: buildMode,
@@ -255,7 +251,7 @@
       outputPath: environment.buildDir.path,
       platform: TargetPlatform.darwin_x64,
       darwinArch: DarwinArch.x86_64,
-      packagesPath: flutterProject.packagesFile.path
+      packagesPath: environment.projectDir.childFile('.packages').path,
     );
     if (result != 0) {
       throw Exception('gen shapshot failed.');
@@ -298,11 +294,11 @@
   @override
   List<Source> get outputs => const <Source>[
     Source.behavior(MacOSAssetBehavior()),
-    Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/Versions/A/App'),
-    Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/Versions/A/Resources/Info.plist'),
-    Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/Versions/A/Resources/flutter_assets/AssetManifest.json'),
-    Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/Versions/A/Resources/flutter_assets/FontManifest.json'),
-    Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/Versions/A/Resources/flutter_assets/LICENSE'),
+    Source.pattern('{OUTPUT_DIR}/App.framework/Versions/A/App'),
+    Source.pattern('{OUTPUT_DIR}/App.framework/Versions/A/Resources/Info.plist'),
+    Source.pattern('{OUTPUT_DIR}/App.framework/Versions/A/Resources/flutter_assets/AssetManifest.json'),
+    Source.pattern('{OUTPUT_DIR}/App.framework/Versions/A/Resources/flutter_assets/FontManifest.json'),
+    Source.pattern('{OUTPUT_DIR}/App.framework/Versions/A/Resources/flutter_assets/LICENSE'),
   ];
 
   @override
@@ -311,9 +307,8 @@
       throw MissingDefineException(kBuildMode, 'compile_macos_framework');
     }
     final BuildMode buildMode = getBuildModeForName(environment.defines[kBuildMode]);
-    final FlutterProject flutterProject = FlutterProject.fromDirectory(environment.projectDir);
-    final Directory frameworkRootDirectory = flutterProject.macos
-        .ephemeralDirectory
+    final Directory frameworkRootDirectory = environment
+        .outputDir
         .childDirectory('App.framework');
     final Directory outputDirectory = frameworkRootDirectory
         .childDirectory('Versions')
@@ -420,7 +415,6 @@
       if (!currentVersion.existsSync()) {
         final String linkPath = fs.path.relative(outputDirectory.path,
             from: outputDirectory.parent.path);
-         print(linkPath);
         currentVersion.createSync('$linkPath${fs.path.separator}');
       }
       // Create symlink to current resources.
@@ -429,7 +423,6 @@
       if (!currentResources.existsSync()) {
         final String linkPath = fs.path.relative(fs.path.join(currentVersion.path, 'Resources'),
             from: frameworkRootDirectory.path);
-        print(linkPath);
         currentResources.createSync(linkPath);
       }
       // Create symlink to current binary.
@@ -438,12 +431,11 @@
       if (!currentFramework.existsSync()) {
         final String linkPath = fs.path.relative(fs.path.join(currentVersion.path, 'App'),
             from: frameworkRootDirectory.path);
-         print(linkPath);
         currentFramework.createSync(linkPath);
       }
     } on FileSystemException {
       throw Exception('Failed to create symlinks for framework. try removing '
-        'the "${flutterProject.macos.ephemeralDirectory.path}" directory and rerunning');
+        'the "${environment.outputDir.path}" directory and rerunning');
     }
   }
 }
@@ -473,9 +465,9 @@
   @override
   List<Source> get outputs => <Source>[
     ...super.outputs,
-    const Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/Versions/A/Resources/flutter_assets/kernel_blob.bin'),
-    const Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/Versions/A/Resources/flutter_assets/vm_snapshot_data'),
-    const Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/Versions/A/Resources/flutter_assets/isolate_snapshot_data'),
+    const Source.pattern('{OUTPUT_DIR}/App.framework/Versions/A/Resources/flutter_assets/kernel_blob.bin'),
+    const Source.pattern('{OUTPUT_DIR}/App.framework/Versions/A/Resources/flutter_assets/vm_snapshot_data'),
+    const Source.pattern('{OUTPUT_DIR}/App.framework/Versions/A/Resources/flutter_assets/isolate_snapshot_data'),
   ];
 }
 
diff --git a/packages/flutter_tools/lib/src/commands/assemble.dart b/packages/flutter_tools/lib/src/commands/assemble.dart
index dc8a8ae..db0df03 100644
--- a/packages/flutter_tools/lib/src/commands/assemble.dart
+++ b/packages/flutter_tools/lib/src/commands/assemble.dart
@@ -54,6 +54,10 @@
         'separated file containing all outputs used will be written after a build.'
         ' This file is not included as a build input or output. This file is not'
         ' written if the build fails for any reason.');
+    argParser.addOption('output', abbr: 'o', help: 'A directory where output '
+        'files will be written. Must be either absolute or relative from the '
+        'root of the current Flutter project.',
+    );
     argParser.addOption(
       'resource-pool-size',
       help: 'The maximum number of concurrent tasks the build system will run.'
@@ -83,7 +87,16 @@
   /// The environmental configuration for a build invocation.
   Environment get environment {
     final FlutterProject flutterProject = FlutterProject.current();
+    String output = argResults['output'];
+    if (output == null) {
+      throwToolExit('--output directory is required for assemble.');
+    }
+    // If path is relative, make it absolute from flutter project.
+    if (fs.path.isRelative(output)) {
+      output = fs.path.join(flutterProject.directory.path, output);
+    }
     final Environment result = Environment(
+      outputDir: fs.directory(output),
       buildDir: flutterProject.directory
           .childDirectory('.dart_tool')
           .childDirectory('flutter_build'),
diff --git a/packages/flutter_tools/test/general.shard/build_system/build_system_test.dart b/packages/flutter_tools/test/general.shard/build_system/build_system_test.dart
index e806251..1b2017e 100644
--- a/packages/flutter_tools/test/general.shard/build_system/build_system_test.dart
+++ b/packages/flutter_tools/test/general.shard/build_system/build_system_test.dart
@@ -92,6 +92,7 @@
     testbed = Testbed(
       setup: () {
         environment = Environment(
+          outputDir: fs.currentDirectory,
           projectDir: fs.currentDirectory,
         );
         fs.file('foo.dart')
diff --git a/packages/flutter_tools/test/general.shard/build_system/filecache_test.dart b/packages/flutter_tools/test/general.shard/build_system/filecache_test.dart
index 4f74de4..15c7871 100644
--- a/packages/flutter_tools/test/general.shard/build_system/filecache_test.dart
+++ b/packages/flutter_tools/test/general.shard/build_system/filecache_test.dart
@@ -18,6 +18,7 @@
     testbed = Testbed(setup: () {
       fs.directory('build').createSync();
       environment = Environment(
+        outputDir: fs.currentDirectory,
         projectDir: fs.currentDirectory,
       );
       environment.buildDir.createSync(recursive: true);
diff --git a/packages/flutter_tools/test/general.shard/build_system/source_test.dart b/packages/flutter_tools/test/general.shard/build_system/source_test.dart
index 8d0c2a4..e3d7d4e 100644
--- a/packages/flutter_tools/test/general.shard/build_system/source_test.dart
+++ b/packages/flutter_tools/test/general.shard/build_system/source_test.dart
@@ -21,7 +21,10 @@
   setUp(() {
     testbed = Testbed(setup: () {
       fs.directory('cache').createSync();
+      final Directory outputs = fs.directory('outputs')
+          ..createSync();
       environment = Environment(
+        outputDir: outputs,
         projectDir: fs.currentDirectory,
         buildDir: fs.directory('build'),
       );
@@ -45,6 +48,15 @@
     expect(visitor.sources.single.path, fs.path.absolute('foo'));
   }));
 
+  test('can substitute {OUTPUT_DIR}/foo', () => testbed.run(() {
+    fs.file('foo').createSync();
+    const Source fooSource = Source.pattern('{OUTPUT_DIR}/foo');
+    fooSource.accept(visitor);
+
+    expect(visitor.sources.single.path, fs.path.absolute(fs.path.join('outputs', 'foo')));
+  }));
+
+
   test('can substitute {BUILD_DIR}/bar', () => testbed.run(() {
     final String path = fs.path.join(environment.buildDir.path, 'bar');
     fs.file(path).createSync();
diff --git a/packages/flutter_tools/test/general.shard/build_system/targets/assets_test.dart b/packages/flutter_tools/test/general.shard/build_system/targets/assets_test.dart
index f4fbb50..7badd19 100644
--- a/packages/flutter_tools/test/general.shard/build_system/targets/assets_test.dart
+++ b/packages/flutter_tools/test/general.shard/build_system/targets/assets_test.dart
@@ -17,6 +17,7 @@
   setUp(() {
     testbed = Testbed(setup: () {
       environment = Environment(
+        outputDir: fs.currentDirectory,
         projectDir: fs.currentDirectory,
       );
       fs.file(fs.path.join('packages', 'flutter_tools', 'lib', 'src',
@@ -71,6 +72,7 @@
       ..writeAsStringSync('name: foo\ndependencies:\n  foo: any\n');
 
     await const FlutterPlugins().build(<File>[], Environment(
+      outputDir: fs.currentDirectory,
       projectDir: fs.currentDirectory,
     ));
 
diff --git a/packages/flutter_tools/test/general.shard/build_system/targets/dart_test.dart b/packages/flutter_tools/test/general.shard/build_system/targets/dart_test.dart
index caa5352..7b18e6f 100644
--- a/packages/flutter_tools/test/general.shard/build_system/targets/dart_test.dart
+++ b/packages/flutter_tools/test/general.shard/build_system/targets/dart_test.dart
@@ -39,6 +39,7 @@
     mockProcessManager = MockProcessManager();
     testbed = Testbed(setup: () {
       androidEnvironment = Environment(
+        outputDir: fs.currentDirectory,
         projectDir: fs.currentDirectory,
         defines: <String, String>{
           kBuildMode: getNameForBuildMode(BuildMode.profile),
@@ -46,6 +47,7 @@
         }
       );
       iosEnvironment = Environment(
+        outputDir: fs.currentDirectory,
         projectDir: fs.currentDirectory,
         defines: <String, String>{
           kBuildMode: getNameForBuildMode(BuildMode.profile),
@@ -158,6 +160,7 @@
     });
 
     await const KernelSnapshot().build(<File>[], Environment(
+        outputDir: fs.currentDirectory,
         projectDir: fs.currentDirectory,
         defines: <String, String>{
       kBuildMode: 'debug',
diff --git a/packages/flutter_tools/test/general.shard/build_system/targets/linux_test.dart b/packages/flutter_tools/test/general.shard/build_system/targets/linux_test.dart
index 78f387a..6f6bcbf 100644
--- a/packages/flutter_tools/test/general.shard/build_system/targets/linux_test.dart
+++ b/packages/flutter_tools/test/general.shard/build_system/targets/linux_test.dart
@@ -31,6 +31,7 @@
     testbed = Testbed(setup: () {
       Cache.flutterRoot = '';
       environment = Environment(
+        outputDir: fs.currentDirectory,
         projectDir: fs.currentDirectory,
       );
       fs.file('bin/cache/artifacts/engine/linux-x64/libflutter_linux_glfw.so').createSync(recursive: true);
diff --git a/packages/flutter_tools/test/general.shard/build_system/targets/macos_test.dart b/packages/flutter_tools/test/general.shard/build_system/targets/macos_test.dart
index 4b14f40..344bbc1 100644
--- a/packages/flutter_tools/test/general.shard/build_system/targets/macos_test.dart
+++ b/packages/flutter_tools/test/general.shard/build_system/targets/macos_test.dart
@@ -21,7 +21,7 @@
 import '../../../src/testbed.dart';
 
 const String _kInputPrefix = 'bin/cache/artifacts/engine/darwin-x64/FlutterMacOS.framework';
-const String _kOutputPrefix = 'macos/Flutter/ephemeral/FlutterMacOS.framework';
+const String _kOutputPrefix = 'FlutterMacOS.framework';
 
 final List<File> inputs = <File>[
   fs.file('$_kInputPrefix/FlutterMacOS'),
@@ -68,6 +68,7 @@
           'vmservice_io.dart')).createSync(recursive: true);
 
       environment = Environment(
+        outputDir: fs.currentDirectory,
         projectDir: fs.currentDirectory,
         defines: <String, String>{
           kBuildMode: 'debug',
@@ -126,13 +127,11 @@
         'isolate_snapshot.bin')).createSync(recursive: true);
     fs.file(fs.path.join(environment.buildDir.path, 'App.framework', 'App'))
         ..createSync(recursive: true);
-    final String frameworkPath = fs.path.join(environment.projectDir.path,
-        'macos', 'Flutter', 'ephemeral', 'App.framework');
+
     final String inputKernel = fs.path.join(environment.buildDir.path, 'app.dill');
-    fs.directory(frameworkPath).createSync(recursive: true);
-    final String outputKernel = fs.path.join(frameworkPath, 'Versions', 'A', 'Resources',
+    final String outputKernel = fs.path.join('App.framework', 'Versions', 'A', 'Resources',
         'flutter_assets', 'kernel_blob.bin');
-    final String outputPlist = fs.path.join(frameworkPath, 'Versions', 'A', 'Resources',
+    final String outputPlist = fs.path.join('App.framework', 'Versions', 'A', 'Resources',
         'Info.plist');
     fs.file(inputKernel)
       ..createSync(recursive: true)
@@ -151,14 +150,11 @@
         'isolate_snapshot.bin')).createSync(recursive: true);
     fs.file(fs.path.join(environment.buildDir.path, 'App.framework', 'App'))
         ..createSync(recursive: true);
-    final String frameworkPath = fs.path.join(environment.projectDir.path,
-        'macos', 'Flutter', 'ephemeral', 'App.framework');
-    fs.directory(frameworkPath).createSync(recursive: true);
-    final String outputKernel = fs.path.join(frameworkPath, 'Resources',
+    final String outputKernel = fs.path.join('App.framework', 'Resources',
         'flutter_assets', 'kernel_blob.bin');
-    final String precompiledVm = fs.path.join(frameworkPath, 'Resources',
+    final String precompiledVm = fs.path.join('App.framework', 'Resources',
         'flutter_assets', 'vm_snapshot_data');
-    final String precompiledIsolate = fs.path.join(frameworkPath, 'Resources',
+    final String precompiledIsolate = fs.path.join('App.framework', 'Resources',
         'flutter_assets', 'isolate_snapshot_data');
     await const ProfileMacOSBundleFlutterAssets().build(<File>[], environment..defines[kBuildMode] = 'profile');
 
diff --git a/packages/flutter_tools/test/general.shard/build_system/targets/windows_test.dart b/packages/flutter_tools/test/general.shard/build_system/targets/windows_test.dart
index 9bfbe48..539c3de 100644
--- a/packages/flutter_tools/test/general.shard/build_system/targets/windows_test.dart
+++ b/packages/flutter_tools/test/general.shard/build_system/targets/windows_test.dart
@@ -32,6 +32,7 @@
     when(platform.pathSeparator).thenReturn(r'\');
     testbed = Testbed(setup: () {
       environment = Environment(
+        outputDir: fs.currentDirectory,
         projectDir: fs.currentDirectory,
       );
       fs.file(r'C:\bin\cache\artifacts\engine\windows-x64\flutter_export.h').createSync(recursive: true);
diff --git a/packages/flutter_tools/test/general.shard/commands/assemble_test.dart b/packages/flutter_tools/test/general.shard/commands/assemble_test.dart
index 2c227af..dc247d3 100644
--- a/packages/flutter_tools/test/general.shard/commands/assemble_test.dart
+++ b/packages/flutter_tools/test/general.shard/commands/assemble_test.dart
@@ -36,12 +36,22 @@
       return BuildResult(success: true);
     });
     final CommandRunner<void> commandRunner = createTestCommandRunner(AssembleCommand());
-    await commandRunner.run(<String>['assemble', 'debug_macos_bundle_flutter_assets']);
+    await commandRunner.run(<String>['assemble', '-o Output', 'debug_macos_bundle_flutter_assets']);
     final BufferLogger bufferLogger = logger;
 
     expect(bufferLogger.statusText.trim(), 'build succeeded.');
   }));
 
+  test('Throws ToolExit if not provided with output', () => testbed.run(() async {
+    when(mockBuildSystem.build(any, any, buildSystemConfig: anyNamed('buildSystemConfig')))
+        .thenAnswer((Invocation invocation) async {
+      return BuildResult(success: true);
+    });
+    final CommandRunner<void> commandRunner = createTestCommandRunner(AssembleCommand());
+
+    expect(commandRunner.run(<String>['assemble', 'debug_macos_bundle_flutter_assets']), throwsA(isInstanceOf<ToolExit>()));
+  }));
+
   test('Throws ToolExit if called with non-existent rule', () => testbed.run(() async {
     when(mockBuildSystem.build(any, any, buildSystemConfig: anyNamed('buildSystemConfig')))
         .thenAnswer((Invocation invocation) async {
@@ -49,8 +59,9 @@
     });
     final CommandRunner<void> commandRunner = createTestCommandRunner(AssembleCommand());
 
-    expect(commandRunner.run(<String>['assemble', 'undefined']), throwsA(isInstanceOf<ToolExit>()));
+    expect(commandRunner.run(<String>['assemble', '-o Output', 'undefined']), throwsA(isInstanceOf<ToolExit>()));
   }));
+
   test('Only writes input and output files when the values change', () => testbed.run(() async {
     when(mockBuildSystem.build(any, any, buildSystemConfig: anyNamed('buildSystemConfig')))
         .thenAnswer((Invocation invocation) async {
@@ -62,7 +73,7 @@
     });
 
     final CommandRunner<void> commandRunner = createTestCommandRunner(AssembleCommand());
-    await commandRunner.run(<String>['assemble', '--build-outputs=outputs', '--build-inputs=inputs', 'debug_macos_bundle_flutter_assets']);
+    await commandRunner.run(<String>['assemble', '-o Output', '--build-outputs=outputs', '--build-inputs=inputs', 'debug_macos_bundle_flutter_assets']);
 
     final File inputs = fs.file('inputs');
     final File outputs = fs.file('outputs');
@@ -72,7 +83,7 @@
     final DateTime theDistantPast = DateTime(1991, 8, 23);
     inputs.setLastModifiedSync(theDistantPast);
     outputs.setLastModifiedSync(theDistantPast);
-    await commandRunner.run(<String>['assemble', '--build-outputs=outputs', '--build-inputs=inputs', 'debug_macos_bundle_flutter_assets']);
+    await commandRunner.run(<String>['assemble', '-o Output', '--build-outputs=outputs', '--build-inputs=inputs', 'debug_macos_bundle_flutter_assets']);
 
     expect(inputs.lastModifiedSync(), theDistantPast);
     expect(outputs.lastModifiedSync(), theDistantPast);
@@ -85,7 +96,7 @@
         inputFiles: <File>[fs.file('foo'), fs.file('fizz')..createSync()],
         outputFiles: <File>[fs.file('bar'), fs.file(fs.path.join('.dart_tool', 'fizz2'))..createSync(recursive: true)]);
     });
-    await commandRunner.run(<String>['assemble', '--build-outputs=outputs', '--build-inputs=inputs', 'debug_macos_bundle_flutter_assets']);
+    await commandRunner.run(<String>['assemble', '-o Output', '--build-outputs=outputs', '--build-inputs=inputs', 'debug_macos_bundle_flutter_assets']);
 
     expect(inputs.readAsStringSync(), contains('foo'));
     expect(inputs.readAsStringSync(), contains('fizz'));