Support for macOS release mode (1 of 3) (#37425)
diff --git a/packages/flutter_tools/bin/macos_build_flutter_assets.sh b/packages/flutter_tools/bin/macos_build_flutter_assets.sh index d576a87..3ee697c 100755 --- a/packages/flutter_tools/bin/macos_build_flutter_assets.sh +++ b/packages/flutter_tools/bin/macos_build_flutter_assets.sh
@@ -33,18 +33,6 @@ target_path="${FLUTTER_TARGET}" fi -# Set the track widget creation flag. -track_widget_creation_flag="" -if [[ -n "$TRACK_WIDGET_CREATION" ]]; then - track_widget_creation_flag="--track-widget-creation" -fi - -# Copy the framework and handle local engine builds. -framework_name="FlutterMacOS.framework" -ephemeral_dir="${SOURCE_ROOT}/Flutter/ephemeral" -framework_path="${FLUTTER_ROOT}/bin/cache/artifacts/engine/darwin-x64" -flutter_framework="${framework_path}/${framework_name}" - if [[ -n "$FLUTTER_ENGINE" ]]; then flutter_engine_flag="--local-engine-src-path=${FLUTTER_ENGINE}" fi @@ -63,22 +51,29 @@ exit -1 fi local_engine_flag="--local-engine=${LOCAL_ENGINE}" - flutter_framework="${FLUTTER_ENGINE}/out/${LOCAL_ENGINE}/${framework_name}" fi -RunCommand mkdir -p -- "$ephemeral_dir" -RunCommand rm -rf -- "${ephemeral_dir}/${framework_name}" -RunCommand cp -Rp -- "${flutter_framework}" "${ephemeral_dir}" - # Set the build mode build_mode="$(echo "${FLUTTER_BUILD_MODE:-${CONFIGURATION}}" | tr "[:upper:]" "[:lower:]")" +# The path where the input/output xcfilelists are stored. These are used by xcode +# to conditionally skip this script phase if neither have changed. +ephemeral_dir="${SOURCE_ROOT}/Flutter/ephemeral" +build_inputs_path="${ephemeral_dir}/FlutterInputs.xcfilelist" +build_outputs_path="${ephemeral_dir}/FlutterOutputs.xcfilelist" + +# TODO(jonahwilliams): connect AOT rules once engine artifacts are published. +# The build mode is currently hard-coded to debug only. Since this does not yet +# support AOT, we need to ensure that we compile the kernel file in debug so that +# the VM can load it. RunCommand "${FLUTTER_ROOT}/bin/flutter" --suppress-analytics \ ${verbose_flag} \ - build bundle \ - --target-platform=darwin-x64 \ - --target="${target_path}" \ - --${build_mode} \ - ${track_widget_creation_flag} \ ${flutter_engine_flag} \ - ${local_engine_flag} + ${local_engine_flag} \ + assemble \ + -dTargetPlatform=darwin-x64 \ + -dTargetFile="${target_path}" \ + -dBuildMode=debug \ + --build-inputs="${build_inputs_path}" \ + --build-outputs="${build_outputs_path}" \ + debug_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 c3cf8cd..e60c444 100644 --- a/packages/flutter_tools/lib/src/build_system/build_system.dart +++ b/packages/flutter_tools/lib/src/build_system/build_system.dart
@@ -485,16 +485,18 @@ // timestamps to track files, this leads to unecessary 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 + // and don't need to be tracked by external systems. { buildInstance.inputFiles.removeWhere((String path, File file) { - return path.contains('pubspec.yaml') || - path.contains('.flutter-plugins') || - path.contains('xcconfig'); + return path.contains('.flutter-plugins') || + path.contains('xcconfig') || + path.contains('.dart_tool'); }); buildInstance.outputFiles.removeWhere((String path, File file) { - return path.contains('pubspec.yaml') || - path.contains('.flutter-plugins') || - path.contains('xcconfig'); + return path.contains('.flutter-plugins') || + path.contains('xcconfig') || + path.contains('.dart_tool'); }); } return BuildResult( @@ -509,6 +511,7 @@ } } + /// An active instance of a build. class _BuildInstance { _BuildInstance(this.environment, this.fileCache, this.buildSystemConfig)
diff --git a/packages/flutter_tools/lib/src/build_system/targets/dart.dart b/packages/flutter_tools/lib/src/build_system/targets/dart.dart index b2b66ff..76d2ab0 100644 --- a/packages/flutter_tools/lib/src/build_system/targets/dart.dart +++ b/packages/flutter_tools/lib/src/build_system/targets/dart.dart
@@ -64,6 +64,7 @@ @override List<Source> get inputs => const <Source>[ + Source.pattern('{PROJECT_DIR}/.packages'), Source.pattern('{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/dart.dart'), Source.function(listDartSources), // <- every dart file under {PROJECT_DIR}/lib and in .packages Source.artifact(Artifact.platformKernelDill),
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 53c082d..bc31ad3 100644 --- a/packages/flutter_tools/lib/src/build_system/targets/macos.dart +++ b/packages/flutter_tools/lib/src/build_system/targets/macos.dart
@@ -2,21 +2,65 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:pool/pool.dart'; + import '../../artifacts.dart'; +import '../../asset.dart'; import '../../base/file_system.dart'; import '../../base/io.dart'; +import '../../base/process.dart'; import '../../base/process_manager.dart'; import '../../build_info.dart'; +import '../../devfs.dart'; import '../../globals.dart'; -import '../../macos/cocoapods.dart'; +import '../../macos/xcode.dart'; import '../../project.dart'; import '../build_system.dart'; -import '../exceptions.dart'; -import 'assets.dart'; import 'dart.dart'; const String _kOutputPrefix = '{PROJECT_DIR}/macos/Flutter/ephemeral/FlutterMacOS.framework'; +/// The copying logic for flutter assets in macOS. +// TODO(jonahwilliams): remove once build planning lands. +class MacOSAssetBehavior extends SourceBehavior { + const MacOSAssetBehavior(); + + @override + List<File> inputs(Environment environment) { + final AssetBundle assetBundle = AssetBundleFactory.instance.createBundle(); + assetBundle.build( + manifestPath: environment.projectDir.childFile('pubspec.yaml').path, + packagesPath: environment.projectDir.childFile('.packages').path, + ); + // Filter the file type to remove the files that are generated by this + // command as inputs. + final List<File> results = <File>[]; + final Iterable<DevFSFileContent> files = assetBundle.entries.values.whereType<DevFSFileContent>(); + for (DevFSFileContent devFsContent in files) { + results.add(fs.file(devFsContent.file.path)); + } + return results; + } + + @override + List<File> outputs(Environment environment) { + final AssetBundle assetBundle = AssetBundleFactory.instance.createBundle(); + assetBundle.build( + 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, + 'App.framework', 'flutter_assets'); + final List<File> results = <File>[]; + for (String key in assetBundle.entries.keys) { + final File file = fs.file(fs.path.join(prefix, key)); + results.add(file); + } + return results; + } +} + /// Copy the macOS framework to the correct copy dir by invoking 'cp -R'. /// /// The shelling out is done to avoid complications with preserving special @@ -41,18 +85,21 @@ List<Source> get outputs => const <Source>[ Source.pattern('$_kOutputPrefix/FlutterMacOS'), // Headers - Source.pattern('$_kOutputPrefix/Headers/FLEViewController.h'), + Source.pattern('$_kOutputPrefix/Headers/FlutterDartProject.h'), + Source.pattern('$_kOutputPrefix/Headers/FlutterEngine.h'), + Source.pattern('$_kOutputPrefix/Headers/FlutterViewController.h'), Source.pattern('$_kOutputPrefix/Headers/FlutterBinaryMessenger.h'), Source.pattern('$_kOutputPrefix/Headers/FlutterChannels.h'), Source.pattern('$_kOutputPrefix/Headers/FlutterCodecs.h'), - Source.pattern('$_kOutputPrefix/Headers/FlutterMacOS.h'), + Source.pattern('$_kOutputPrefix/Headers/FlutterMacros.h'), Source.pattern('$_kOutputPrefix/Headers/FlutterPluginMacOS.h'), Source.pattern('$_kOutputPrefix/Headers/FlutterPluginRegistrarMacOS.h'), + Source.pattern('$_kOutputPrefix/Headers/FlutterMacOS.h'), // Modules Source.pattern('$_kOutputPrefix/Modules/module.modulemap'), // Resources Source.pattern('$_kOutputPrefix/Resources/icudtl.dat'), - Source.pattern('$_kOutputPrefix/Resources/info.plist'), + Source.pattern('$_kOutputPrefix/Resources/Info.plist'), // Ignore Versions folder for now ]; @@ -62,11 +109,9 @@ @override Future<void> build(List<File> inputFiles, Environment environment) async { final String basePath = artifacts.getArtifactPath(Artifact.flutterMacOSFramework); - final Directory targetDirectory = environment - .projectDir - .childDirectory('macos') - .childDirectory('Flutter') - .childDirectory('ephemeral') + final FlutterProject flutterProject = FlutterProject.fromDirectory(environment.projectDir); + final Directory targetDirectory = flutterProject.macos + .ephemeralDirectory .childDirectory('FlutterMacOS.framework'); if (targetDirectory.existsSync()) { targetDirectory.deleteSync(recursive: true); @@ -83,111 +128,151 @@ } } -/// Tell cocoapods to re-fetch dependencies. -class DebugMacOSPodInstall extends Target { - const DebugMacOSPodInstall(); +/// Create an App.framework for debug macOS targets. +/// +/// This framework needs to exist for the Xcode project to link/bundle, +/// but it isn't actually executed. To generate something valid, we compile a trivial +/// constant. +class DebugMacOSFramework extends Target { + const DebugMacOSFramework(); @override - String get name => 'debug_macos_pod_install'; + String get name => 'debug_macos_framework'; + + @override + Future<void> build(List<File> inputFiles, Environment environment) async { + final FlutterProject flutterProject = FlutterProject.fromDirectory(environment.projectDir); + final File outputFile = fs.file(fs.path.join( + flutterProject.macos.ephemeralDirectory.path, 'App.framework', 'App')); + outputFile.createSync(recursive: true); + final File debugApp = environment.buildDir.childFile('debug_app.cc') + ..writeAsStringSync(r''' +static const int Moo = 88; +'''); + final RunResult result = await xcode.clang(<String>[ + '-x', + 'c', + debugApp.path, + '-arch', 'x86_64', + '-dynamiclib', + '-Xlinker', '-rpath', '-Xlinker', '@executable_path/Frameworks', + '-Xlinker', '-rpath', '-Xlinker', '@loader_path/Frameworks', + '-install_name', '@rpath/App.framework/App', + '-o', 'macos/Flutter/ephemeral/App.framework/App', + ]); + if (result.exitCode != 0) { + throw Exception('Failed to compile debug App.framework'); + } + } + + @override + List<Target> get dependencies => const <Target>[]; @override List<Source> get inputs => const <Source>[ - Source.artifact(Artifact.flutterMacOSPodspec, - platform: TargetPlatform.darwin_x64, - mode: BuildMode.debug - ), - Source.pattern('{PROJECT_DIR}/macos/Podfile', optional: true), - Source.pattern('{PROJECT_DIR}/macos/Runner.xcodeproj/project.pbxproj'), - Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/Flutter-Generated.xcconfig'), + Source.pattern('{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/macos.dart'), ]; @override List<Source> get outputs => const <Source>[ - // TODO(jonahwilliams): introduce configuration/planning phase to build. - // No outputs because Cocoapods is fully responsible for tracking. plus there - // is no concept of an optional output. Instead we will need a build config - // phase to conditionally add this rule so that it can be written properly. + Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/App'), ]; +} + +/// Bundle the flutter assets, app.dill, and precompiled runtimes into the App.framework. +class DebugBundleFlutterAssets extends Target { + const DebugBundleFlutterAssets(); @override - List<Target> get dependencies => const <Target>[ - UnpackMacOS(), - FlutterPlugins(), - ]; + String get name => 'debug_bundle_flutter_assets'; @override Future<void> build(List<File> inputFiles, Environment environment) async { - if (environment.defines[kBuildMode] == null) { - throw MissingDefineException(kBuildMode, 'debug_macos_pod_install'); + final FlutterProject flutterProject = FlutterProject.fromDirectory(environment.projectDir); + final Directory outputDirectory = flutterProject.macos + .ephemeralDirectory.childDirectory('App.framework'); + if (!outputDirectory.existsSync()) { + throw Exception('App.framework must exist to bundle assets.'); } - // If there is no podfile do not perform any pods actions. - if (!environment.projectDir.childDirectory('macos') - .childFile('Podfile').existsSync()) { - return; + // Copy assets into asset directory. + final Directory assetDirectory = outputDirectory.childDirectory('flutter_assets'); + // We're not smart enough to only remove assets that are removed. If + // anything changes blow away the whole directory. + if (assetDirectory.existsSync()) { + assetDirectory.deleteSync(recursive: true); } - final BuildMode buildMode = getBuildModeForName(environment.defines[kBuildMode]); - final FlutterProject project = FlutterProject.fromDirectory(environment.projectDir); - final String enginePath = artifacts.getArtifactPath(Artifact.flutterMacOSPodspec, - mode: buildMode, platform: TargetPlatform.darwin_x64); - - await cocoaPods.processPods( - xcodeProject: project.macos, - engineDir: enginePath, - isSwift: true, - dependenciesChanged: true, + assetDirectory.createSync(); + final AssetBundle assetBundle = AssetBundleFactory.instance.createBundle(); + final int result = await assetBundle.build( + manifestPath: environment.projectDir.childFile('pubspec.yaml').path, + packagesPath: environment.projectDir.childFile('.packages').path, ); - } -} - -/// Build all of the artifacts for a debug macOS application. -class DebugMacOSApplication extends Target { - const DebugMacOSApplication(); - - @override - Future<void> build(List<File> inputFiles, Environment environment) async { - final File sourceFile = environment.buildDir.childFile('app.dill'); - final File destinationFile = environment.buildDir - .childDirectory('flutter_assets') - .childFile('kernel_blob.bin'); - if (!destinationFile.parent.existsSync()) { - destinationFile.parent.createSync(recursive: true); + if (result != 0) { + throw Exception('Failed to create asset bundle: $result'); } - sourceFile.copySync(destinationFile.path); + // Limit number of open files to avoid running out of file descriptors. + try { + final Pool pool = Pool(64); + await Future.wait<void>( + assetBundle.entries.entries.map<Future<void>>((MapEntry<String, DevFSContent> entry) async { + final PoolResource resource = await pool.request(); + try { + final File file = fs.file(fs.path.join(assetDirectory.path, entry.key)); + file.parent.createSync(recursive: true); + await file.writeAsBytes(await entry.value.contentsAsBytes()); + } finally { + resource.release(); + } + })); + } catch (err, st){ + throw Exception('Failed to copy assets: $st'); + } + // Copy dill file. + try { + final File sourceFile = environment.buildDir.childFile('app.dill'); + sourceFile.copySync(assetDirectory.childFile('kernel_blob.bin').path); + } catch (err) { + throw Exception('Failed to copy app.dill: $err'); + } + // Copy precompiled runtimes. + try { + final String vmSnapshotData = artifacts.getArtifactPath(Artifact.vmSnapshotData, + platform: TargetPlatform.darwin_x64, mode: BuildMode.debug); + final String isolateSnapshotData = artifacts.getArtifactPath(Artifact.isolateSnapshotData, + platform: TargetPlatform.darwin_x64, mode: BuildMode.debug); + fs.file(vmSnapshotData).copySync( + assetDirectory.childFile('vm_snapshot_data').path); + fs.file(isolateSnapshotData).copySync( + assetDirectory.childFile('isolate_snapshot_data').path); + } catch (err) { + throw Exception('Failed to copy precompiled runtimes: $err'); + } } @override List<Target> get dependencies => const <Target>[ - FlutterPlugins(), - UnpackMacOS(), KernelSnapshot(), - CopyAssets(), - DebugMacOSPodInstall(), + DebugMacOSFramework(), + UnpackMacOS(), ]; @override List<Source> get inputs => const <Source>[ - Source.pattern('{BUILD_DIR}/app.dill') + Source.pattern('{PROJECT_DIR}/pubspec.yaml'), + Source.behavior(MacOSAssetBehavior()), + Source.pattern('{BUILD_DIR}/app.dill'), + Source.artifact(Artifact.isolateSnapshotData, platform: TargetPlatform.darwin_x64, mode: BuildMode.debug), + Source.artifact(Artifact.vmSnapshotData, platform: TargetPlatform.darwin_x64, mode: BuildMode.debug), ]; @override - String get name => 'debug_macos_application'; - - @override List<Source> get outputs => const <Source>[ - Source.pattern('{BUILD_DIR}/flutter_assets/kernel_blob.bin'), + Source.behavior(MacOSAssetBehavior()), + Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/flutter_assets/AssetManifest.json'), + Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/flutter_assets/FontManifest.json'), + Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/flutter_assets/LICENSE'), + Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/flutter_assets/kernel_blob.bin'), + Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/flutter_assets/vm_snapshot_data'), + Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/flutter_assets/isolate_snapshot_data'), ]; } - -// TODO(jonahwilliams): real AOT implementation. -class ReleaseMacOSApplication extends DebugMacOSApplication { - const ReleaseMacOSApplication(); - - @override - String get name => 'release_macos_application'; -} -class ProfileMacOSApplication extends DebugMacOSApplication { - const ProfileMacOSApplication(); - - @override - String get name => 'profile_macos_application'; -}
diff --git a/packages/flutter_tools/lib/src/commands/assemble.dart b/packages/flutter_tools/lib/src/commands/assemble.dart index 476d788..85c9efe 100644 --- a/packages/flutter_tools/lib/src/commands/assemble.dart +++ b/packages/flutter_tools/lib/src/commands/assemble.dart
@@ -32,9 +32,8 @@ AotElfRelease(), AotAssemblyProfile(), AotAssemblyRelease(), - DebugMacOSApplication(), - ProfileMacOSApplication(), - ReleaseMacOSApplication(), + DebugMacOSFramework(), + DebugBundleFlutterAssets(), ]; /// Assemble provides a low level API to interact with the flutter tool build
diff --git a/packages/flutter_tools/lib/src/desktop.dart b/packages/flutter_tools/lib/src/desktop.dart index 26773a2..69a9bf8 100644 --- a/packages/flutter_tools/lib/src/desktop.dart +++ b/packages/flutter_tools/lib/src/desktop.dart
@@ -11,6 +11,9 @@ /// Kills a process on linux or macOS. Future<bool> killProcess(String executable) async { + if (executable == null) { + return false; + } final RegExp whitespace = RegExp(r'\s+'); bool succeeded = true; try {
diff --git a/packages/flutter_tools/lib/src/macos/application_package.dart b/packages/flutter_tools/lib/src/macos/application_package.dart index cfa4abc..278122a 100644 --- a/packages/flutter_tools/lib/src/macos/application_package.dart +++ b/packages/flutter_tools/lib/src/macos/application_package.dart
@@ -141,7 +141,7 @@ return null; } final _ExecutableAndId executableAndId = MacOSApp._executableFromBundle(fs.directory(directory)); - return executableAndId.executable; + return executableAndId?.executable; } }
diff --git a/packages/flutter_tools/lib/src/macos/build_macos.dart b/packages/flutter_tools/lib/src/macos/build_macos.dart index 95fa007..9fa7513 100644 --- a/packages/flutter_tools/lib/src/macos/build_macos.dart +++ b/packages/flutter_tools/lib/src/macos/build_macos.dart
@@ -36,6 +36,13 @@ setSymroot: false, ); await processPodsIfNeeded(flutterProject.macos, getMacOSBuildDirectory(), buildInfo.mode); + // If the xcfilelists do not exist, create empty version. + if (!flutterProject.macos.inputFileList.existsSync()) { + flutterProject.macos.inputFileList.createSync(recursive: true); + } + if (!flutterProject.macos.outputFileList.existsSync()) { + flutterProject.macos.outputFileList.createSync(recursive: true); + } // Set debug or release mode. String config = 'Debug';
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 3ce30f1..27a67b6 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
@@ -2,22 +2,48 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:flutter_tools/src/base/build.dart'; import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/io.dart'; import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/base/process_manager.dart'; import 'package:flutter_tools/src/build_system/build_system.dart'; -import 'package:flutter_tools/src/build_system/exceptions.dart'; import 'package:flutter_tools/src/build_system/targets/dart.dart'; import 'package:flutter_tools/src/build_system/targets/macos.dart'; import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/macos/cocoapods.dart'; +import 'package:flutter_tools/src/macos/xcode.dart'; import 'package:mockito/mockito.dart'; import 'package:process/process.dart'; import '../../../src/common.dart'; import '../../../src/testbed.dart'; +const String _kInputPrefix = 'bin/cache/artifacts/engine/darwin-x64/FlutterMacOS.framework'; +const String _kOutputPrefix = 'macos/Flutter/ephemeral/FlutterMacOS.framework'; + +final List<File> inputs = <File>[ + fs.file('$_kInputPrefix/FlutterMacOS'), + // Headers + fs.file('$_kInputPrefix/Headers/FlutterDartProject.h'), + fs.file('$_kInputPrefix/Headers/FlutterEngine.h'), + fs.file('$_kInputPrefix/Headers/FlutterViewController.h'), + fs.file('$_kInputPrefix/Headers/FlutterBinaryMessenger.h'), + fs.file('$_kInputPrefix/Headers/FlutterChannels.h'), + fs.file('$_kInputPrefix/Headers/FlutterCodecs.h'), + fs.file('$_kInputPrefix/Headers/FlutterMacros.h'), + fs.file('$_kInputPrefix/Headers/FlutterPluginMacOS.h'), + fs.file('$_kInputPrefix/Headers/FlutterPluginRegistrarMacOS.h'), + fs.file('$_kInputPrefix/Headers/FlutterMacOS.h'), + // Modules + fs.file('$_kInputPrefix/Modules/module.modulemap'), + // Resources + fs.file('$_kInputPrefix/Resources/icudtl.dat'), + fs.file('$_kInputPrefix/Resources/Info.plist'), + // Ignore Versions folder for now + fs.file('packages/flutter_tools/lib/src/build_system/targets/macos.dart'), +]; + void main() { Testbed testbed; Environment environment; @@ -37,44 +63,11 @@ testbed = Testbed(setup: () { environment = Environment( projectDir: fs.currentDirectory, - ); - final List<File> inputs = <File>[ - fs.file('bin/cache/artifacts/engine/darwin-x64/FlutterMacOS.framework/FlutterMacOS'), - fs.file('bin/cache/artifacts/engine/darwin-x64/FlutterMacOS.framework/Headers/FLEOpenGLContextHandling.h'), - fs.file('bin/cache/artifacts/engine/darwin-x64/FlutterMacOS.framework/Headers/FLEReshapeListener.h'), - fs.file('bin/cache/artifacts/engine/darwin-x64/FlutterMacOS.framework/Headers/FLEView.h'), - fs.file('bin/cache/artifacts/engine/darwin-x64/FlutterMacOS.framework/Headers/FLEViewController.h'), - fs.file('bin/cache/artifacts/engine/darwin-x64/FlutterMacOS.framework/Headers/FlutterBinaryMessenger.h'), - fs.file('bin/cache/artifacts/engine/darwin-x64/FlutterMacOS.framework/Headers/FlutterChannels.h'), - fs.file('bin/cache/artifacts/engine/darwin-x64/FlutterMacOS.framework/Headers/FlutterCodecs.h'), - fs.file('bin/cache/artifacts/engine/darwin-x64/FlutterMacOS.framework/Headers/FlutterMacOS.h'), - fs.file('bin/cache/artifacts/engine/darwin-x64/FlutterMacOS.framework/Headers/FlutterPluginMacOS.h'), - fs.file('bin/cache/artifacts/engine/darwin-x64/FlutterMacOS.framework/Headers/FlutterPluginRegisrarMacOS.h'), - fs.file('bin/cache/artifacts/engine/darwin-x64/FlutterMacOS.framework/Modules/module.modulemap'), - fs.file('bin/cache/artifacts/engine/darwin-x64/FlutterMacOS.framework/Resources/icudtl.dat'), - fs.file('bin/cache/artifacts/engine/darwin-x64/FlutterMacOS.framework/Resources/info.plist'), - fs.file('packages/flutter_tools/lib/src/build_system/targets/macos.dart'), - ]; - for (File input in inputs) { - input.createSync(recursive: true); - } - when(processManager.run(any)).thenAnswer((Invocation invocation) async { - final List<String> arguments = invocation.positionalArguments.first; - final Directory source = fs.directory(arguments[arguments.length - 2]); - final Directory target = fs.directory(arguments.last) - ..createSync(recursive: true); - for (FileSystemEntity entity in source.listSync(recursive: true)) { - if (entity is File) { - final String relative = fs.path.relative(entity.path, from: source.path); - final String destination = fs.path.join(target.path, relative); - if (!fs.file(destination).parent.existsSync()) { - fs.file(destination).parent.createSync(); - } - entity.copySync(destination); - } + defines: <String, String>{ + kBuildMode: 'debug', + kTargetPlatform: 'darwin-x64', } - return FakeProcessResult()..exitCode = 0; - }); + ); }, overrides: <Type, Generator>{ ProcessManager: () => MockProcessManager(), Platform: () => mockPlatform, @@ -82,115 +75,69 @@ }); test('Copies files to correct cache directory', () => testbed.run(() async { + for (File input in inputs) { + input.createSync(recursive: true); + } + when(processManager.run(any)).thenAnswer((Invocation invocation) async { + final List<String> arguments = invocation.positionalArguments.first; + final Directory source = fs.directory(arguments[arguments.length - 2]); + final Directory target = fs.directory(arguments.last) + ..createSync(recursive: true); + for (FileSystemEntity entity in source.listSync(recursive: true)) { + if (entity is File) { + final String relative = fs.path.relative(entity.path, from: source.path); + final String destination = fs.path.join(target.path, relative); + if (!fs.file(destination).parent.existsSync()) { + fs.file(destination).parent.createSync(); + } + entity.copySync(destination); + } + } + return FakeProcessResult()..exitCode = 0; + }); await const UnpackMacOS().build(<File>[], environment); - expect(fs.directory('macos/Flutter/ephemeral/FlutterMacOS.framework').existsSync(), true); - expect(fs.file('macos/Flutter/ephemeral/FlutterMacOS.framework/FlutterMacOS').existsSync(), true); - expect(fs.file('macos/Flutter/ephemeral/FlutterMacOS.framework/Headers/FLEViewController.h').existsSync(), true); - expect(fs.file('macos/Flutter/ephemeral/FlutterMacOS.framework/Headers/FlutterBinaryMessenger.h').existsSync(), true); - expect(fs.file('macos/Flutter/ephemeral/FlutterMacOS.framework/Headers/FlutterChannels.h').existsSync(), true); - expect(fs.file('macos/Flutter/ephemeral/FlutterMacOS.framework/Headers/FlutterCodecs.h').existsSync(), true); - expect(fs.file('macos/Flutter/ephemeral/FlutterMacOS.framework/Headers/FlutterMacOS.h').existsSync(), true); - expect(fs.file('macos/Flutter/ephemeral/FlutterMacOS.framework/Headers/FlutterPluginMacOS.h').existsSync(), true); - expect(fs.file('macos/Flutter/ephemeral/FlutterMacOS.framework/Headers/FlutterPluginRegisrarMacOS.h').existsSync(), true); - expect(fs.file('macos/Flutter/ephemeral/FlutterMacOS.framework/Modules/module.modulemap').existsSync(), true); - expect(fs.file('macos/Flutter/ephemeral/FlutterMacOS.framework/Resources/icudtl.dat').existsSync(), true); - expect(fs.file('macos/Flutter/ephemeral/FlutterMacOS.framework/Resources/info.plist').existsSync(), true); + expect(fs.directory('$_kOutputPrefix').existsSync(), true); + for (File file in inputs) { + expect(fs.file(file.path.replaceFirst(_kInputPrefix, _kOutputPrefix)).existsSync(), true); + } + })); + + test('debug macOS application fails if App.framework missing', () => testbed.run(() async { + final String inputKernel = fs.path.join(environment.buildDir.path, 'app.dill'); + fs.file(inputKernel) + ..createSync(recursive: true) + ..writeAsStringSync('testing'); + + expect(() async => await const DebugBundleFlutterAssets().build(<File>[], environment), + throwsA(isInstanceOf<Exception>())); })); test('debug macOS application copies kernel blob', () => testbed.run(() async { + fs.file(fs.path.join('bin', 'cache', 'artifacts', 'engine', 'darwin-x64', + 'vm_isolate_snapshot.bin')).createSync(recursive: true); + fs.file(fs.path.join('bin', 'cache', 'artifacts', 'engine', 'darwin-x64', + 'isolate_snapshot.bin')).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'); - final String outputKernel = fs.path.join(environment.buildDir.path, 'flutter_assets', 'kernel_blob.bin'); + fs.directory(frameworkPath).createSync(recursive: true); + final String outputKernel = fs.path.join(frameworkPath, 'flutter_assets', 'kernel_blob.bin'); fs.file(inputKernel) ..createSync(recursive: true) ..writeAsStringSync('testing'); - await const DebugMacOSApplication().build(<File>[], environment); + await const DebugBundleFlutterAssets().build(<File>[], environment); expect(fs.file(outputKernel).readAsStringSync(), 'testing'); })); - - test('profile macOS application copies kernel blob', () => testbed.run(() async { - final String inputKernel = fs.path.join(environment.buildDir.path, 'app.dill'); - final String outputKernel = fs.path.join(environment.buildDir.path, 'flutter_assets', 'kernel_blob.bin'); - fs.file(inputKernel) - ..createSync(recursive: true) - ..writeAsStringSync('testing'); - - await const ProfileMacOSApplication().build(<File>[], environment); - - expect(fs.file(outputKernel).readAsStringSync(), 'testing'); - })); - - test('release macOS application copies kernel blob', () => testbed.run(() async { - final String inputKernel = fs.path.join(environment.buildDir.path, 'app.dill'); - final String outputKernel = fs.path.join(environment.buildDir.path, 'flutter_assets', 'kernel_blob.bin'); - fs.file(inputKernel) - ..createSync(recursive: true) - ..writeAsStringSync('testing'); - - await const ReleaseMacOSApplication().build(<File>[], environment); - - expect(fs.file(outputKernel).readAsStringSync(), 'testing'); - })); - - // Changing target names will require a corresponding update in flutter_tools/bin/macos_build_flutter_assets.sh. - test('Target names match those expected by bin scripts', () => testbed.run(() async { - expect(const DebugMacOSApplication().name, 'debug_macos_application'); - expect(const ProfileMacOSApplication().name, 'profile_macos_application'); - expect(const ReleaseMacOSApplication().name, 'release_macos_application'); - })); - - - test('DebugMacOSPodInstall throws if missing build mode', () => testbed.run(() async { - expect(() => const DebugMacOSPodInstall().build(<File>[], environment), - throwsA(isInstanceOf<MissingDefineException>())); - })); - - test('DebugMacOSPodInstall skips if podfile does not exist', () => testbed.run(() async { - await const DebugMacOSPodInstall().build(<File>[], Environment( - projectDir: fs.currentDirectory, - defines: <String, String>{ - kBuildMode: 'debug' - } - )); - - verifyNever(cocoaPods.processPods( - xcodeProject: anyNamed('xcodeProject'), - engineDir: anyNamed('engineDir'), - isSwift: true, - dependenciesChanged: true)); - }, overrides: <Type, Generator>{ - CocoaPods: () => MockCocoaPods(), - })); - - test('DebugMacOSPodInstall invokes processPods with podfile', () => testbed.run(() async { - fs.file(fs.path.join('macos', 'Podfile')).createSync(recursive: true); - await const DebugMacOSPodInstall().build(<File>[], Environment( - projectDir: fs.currentDirectory, - defines: <String, String>{ - kBuildMode: 'debug' - } - )); - - verify(cocoaPods.processPods( - xcodeProject: anyNamed('xcodeProject'), - engineDir: anyNamed('engineDir'), - isSwift: true, - dependenciesChanged: true)).called(1); - }, overrides: <Type, Generator>{ - CocoaPods: () => MockCocoaPods(), - })); - - test('b', () => testbed.run(() async { - - })); - } class MockPlatform extends Mock implements Platform {} class MockCocoaPods extends Mock implements CocoaPods {} class MockProcessManager extends Mock implements ProcessManager {} +class MockGenSnapshot extends Mock implements GenSnapshot {} +class MockXCode extends Mock implements Xcode {} class FakeProcessResult implements ProcessResult { @override int exitCode; @@ -204,5 +151,3 @@ @override String stdout = ''; } - -
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 638582b..afc5f7d 100644 --- a/packages/flutter_tools/test/general.shard/commands/assemble_test.dart +++ b/packages/flutter_tools/test/general.shard/commands/assemble_test.dart
@@ -73,7 +73,7 @@ return BuildResult( success: true, inputFiles: <File>[fs.file('foo'), fs.file('fizz')..createSync()], - outputFiles: <File>[fs.file('bar')]); + 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', 'unpack_macos']);