Avoid null manifests in FlutterProject (#20332)
diff --git a/packages/flutter_tools/lib/src/android/gradle.dart b/packages/flutter_tools/lib/src/android/gradle.dart index 0ef82d0..2473479 100644 --- a/packages/flutter_tools/lib/src/android/gradle.dart +++ b/packages/flutter_tools/lib/src/android/gradle.dart
@@ -203,18 +203,13 @@ /// Overwrite local.properties in the specified Flutter project's Android /// sub-project, if needed. /// -/// Throws tool exit, if `pubspec.yaml` is invalid. -/// -/// If [requireSdk] is `true` this will fail with a tool exit if no Android Sdk -/// is found. +/// If [requireAndroidSdk] is true (the default) and no Android SDK is found, +/// this will fail with a [ToolExit]. Future<void> updateLocalProperties({ @required FlutterProject project, BuildInfo buildInfo, bool requireAndroidSdk = true, }) async { - if (project.manifest == null) { - throwToolExit('Invalid `pubspec.yaml`'); - } if (requireAndroidSdk && androidSdk == null) { throwToolExit('Unable to locate Android SDK. Please run `flutter doctor` for more details.'); }
diff --git a/packages/flutter_tools/lib/src/flutter_manifest.dart b/packages/flutter_tools/lib/src/flutter_manifest.dart index 3cfb1e7..81a1e64 100644 --- a/packages/flutter_tools/lib/src/flutter_manifest.dart +++ b/packages/flutter_tools/lib/src/flutter_manifest.dart
@@ -28,14 +28,6 @@ return manifest; } - /// Returns a mock manifest with the given contents. - static FlutterManifest mock(Map<String, dynamic> contents) { - final FlutterManifest manifest = new FlutterManifest._(); - manifest._descriptor = contents ?? const <String, dynamic>{}; - manifest._flutterDescriptor = manifest._descriptor['flutter'] ?? const <String, dynamic>{}; - return manifest; - } - /// Returns null on invalid manifest. Returns empty manifest on missing file. static Future<FlutterManifest> createFromPath(String path) async { if (path == null || !fs.isFileSync(path)) @@ -78,8 +70,10 @@ /// A map representation of the `flutter` section in the `pubspec.yaml` file. Map<String, dynamic> _flutterDescriptor; + /// True if the `pubspec.yaml` file does not exist. bool get isEmpty => _descriptor.isEmpty; + /// The string value of the top-level `name` property in the `pubspec.yaml` file. String get appName => _descriptor['name'] ?? ''; /// The version String from the `pubspec.yaml` file.
diff --git a/packages/flutter_tools/lib/src/ios/xcodeproj.dart b/packages/flutter_tools/lib/src/ios/xcodeproj.dart index 18b629b..c2aeaca 100644 --- a/packages/flutter_tools/lib/src/ios/xcodeproj.dart +++ b/packages/flutter_tools/lib/src/ios/xcodeproj.dart
@@ -16,7 +16,6 @@ import '../base/utils.dart'; import '../build_info.dart'; import '../cache.dart'; -import '../flutter_manifest.dart'; import '../globals.dart'; import '../project.dart'; @@ -59,20 +58,19 @@ localsBuffer.writeln('SYMROOT=\${SOURCE_ROOT}/../${getIosBuildDirectory()}'); - final FlutterManifest manifest = project.manifest; - if (!manifest.isModule) { + if (!project.isModule) { // For module projects we do not want to write the FLUTTER_FRAMEWORK_DIR // explicitly. Rather we rely on the xcode backend script and the Podfile // logic to derive it from FLUTTER_ROOT and FLUTTER_BUILD_MODE. localsBuffer.writeln('FLUTTER_FRAMEWORK_DIR=${flutterFrameworkDir(buildInfo.mode)}'); } - final String buildName = buildInfo?.buildName ?? manifest.buildName; + final String buildName = buildInfo?.buildName ?? project.manifest.buildName; if (buildName != null) { localsBuffer.writeln('FLUTTER_BUILD_NAME=$buildName'); } - final int buildNumber = buildInfo?.buildNumber ?? manifest.buildNumber; + final int buildNumber = buildInfo?.buildNumber ?? project.manifest.buildNumber; if (buildNumber != null) { localsBuffer.writeln('FLUTTER_BUILD_NUMBER=$buildNumber'); }
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 {