Avoid null manifests in FlutterProject (#20332)
diff --git a/packages/flutter_tools/lib/src/project.dart b/packages/flutter_tools/lib/src/project.dart index b10192c..3c8743e 100644 --- a/packages/flutter_tools/lib/src/project.dart +++ b/packages/flutter_tools/lib/src/project.dart
@@ -8,6 +8,7 @@ import 'package:meta/meta.dart'; import 'android/gradle.dart' as gradle; +import 'base/common.dart'; import 'base/file_system.dart'; import 'build_info.dart'; import 'bundle.dart' as bundle; @@ -28,34 +29,39 @@ /// cached. class FlutterProject { @visibleForTesting - FlutterProject(this.directory, this.manifest, this._exampleManifest); + FlutterProject(this.directory, this.manifest, this._exampleManifest) + : assert(directory != null), + assert(manifest != null), + assert(_exampleManifest != null); - /// Returns a future that completes with a FlutterProject view of the given directory. + /// Returns a future that completes with a [FlutterProject] view of the given directory + /// or a ToolExit error, if `pubspec.yaml` or `example/pubspec.yaml` is invalid. static Future<FlutterProject> fromDirectory(Directory directory) async { - final FlutterManifest manifest = await FlutterManifest.createFromPath( + assert(directory != null); + final FlutterManifest manifest = await _readManifest( directory.childFile(bundle.defaultManifestPath).path, ); - final Directory exampleDirectory = _exampleDirectory(directory); - final FlutterManifest exampleManifest = await FlutterManifest.createFromPath( - exampleDirectory.childFile(bundle.defaultManifestPath).path, + final FlutterManifest exampleManifest = await _readManifest( + _exampleDirectory(directory).childFile(bundle.defaultManifestPath).path, ); return new FlutterProject(directory, manifest, exampleManifest); } - /// Returns a future that completes with a FlutterProject view of the current directory. + /// Returns a future that completes with a [FlutterProject] view of the current directory. + /// or a ToolExit error, if `pubspec.yaml` or `example/pubspec.yaml` is invalid. static Future<FlutterProject> current() => fromDirectory(fs.currentDirectory); - /// Returns a future that completes with a FlutterProject view of the given directory. + /// Returns a future that completes with a [FlutterProject] view of the given directory. + /// or a ToolExit error, if `pubspec.yaml` or `example/pubspec.yaml` is invalid. static Future<FlutterProject> fromPath(String path) => fromDirectory(fs.directory(path)); /// The location of this project. final Directory directory; - /// The manifest of this project, or null, if `pubspec.yaml` is invalid. + /// The manifest of this project. final FlutterManifest manifest; - /// The manifest of the example sub-project of this project, or null, if - /// `example/pubspec.yaml` is invalid. + /// The manifest of the example sub-project of this project. final FlutterManifest _exampleManifest; /// Asynchronously returns the organization names found in this project as @@ -96,7 +102,8 @@ FlutterManifest.empty(), ); - bool get isModule => manifest != null && manifest.isModule; + /// True, if this project is a Flutter module. + bool get isModule => manifest.isModule; /// True, if this project has an example application. bool get hasExampleApp => _exampleDirectory(directory).existsSync(); @@ -104,6 +111,18 @@ /// The directory that will contain the example if an example exists. static Directory _exampleDirectory(Directory directory) => directory.childDirectory('example'); + /// Reads and validates the `pubspec.yaml` file at [path], asynchronously + /// returning a [FlutterManifest] representation of the contents. + /// + /// Completes with an empty [FlutterManifest], if the file does not exist. + /// Completes with a ToolExit on validation error. + static Future<FlutterManifest> _readManifest(String path) async { + final FlutterManifest manifest = await FlutterManifest.createFromPath(path); + if (manifest == null) + throwToolExit('Please correct the pubspec.yaml file at $path'); + return manifest; + } + /// Generates project files necessary to make Gradle builds work on Android /// and CocoaPods+Xcode work on iOS, for app and module projects only. Future<void> ensureReadyForPlatformSpecificTooling() async {