Ian Hickson | 449f4a6 | 2019-11-27 15:04:02 -0800 | [diff] [blame] | 1 | // Copyright 2014 The Flutter Authors. All rights reserved. |
Mikkel Nygaard Ravn | 4353297 | 2018-01-18 09:21:24 +0100 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
Mikkel Nygaard Ravn | 3c83c52 | 2018-08-07 23:43:15 +0200 | [diff] [blame] | 5 | import 'package:meta/meta.dart'; |
Dan Field | 33aa457 | 2020-07-07 16:40:33 -0700 | [diff] [blame] | 6 | import 'package:xml/xml.dart'; |
Jonah Williams | b06a709 | 2019-02-05 18:35:20 -0800 | [diff] [blame] | 7 | import 'package:yaml/yaml.dart'; |
Mikkel Nygaard Ravn | 3c83c52 | 2018-08-07 23:43:15 +0200 | [diff] [blame] | 8 | |
Balvinder Singh Gambhir | 3dde3e0 | 2020-09-15 23:51:54 +0530 | [diff] [blame] | 9 | import '../src/convert.dart'; |
Emmanuel Garcia | 175b372 | 2019-10-31 13:19:15 -0700 | [diff] [blame] | 10 | import 'android/gradle_utils.dart' as gradle; |
Jonah Williams | 523ac7b | 2019-12-09 21:18:09 -0800 | [diff] [blame] | 11 | import 'artifacts.dart'; |
Mikkel Nygaard Ravn | a737c86 | 2018-08-10 21:49:24 +0200 | [diff] [blame] | 12 | import 'base/common.dart'; |
Mikkel Nygaard Ravn | d89a6b5 | 2018-06-22 18:19:37 +0200 | [diff] [blame] | 13 | import 'base/file_system.dart'; |
Jonah Williams | 8ed40dd | 2020-04-13 18:55:01 -0700 | [diff] [blame] | 14 | import 'base/logger.dart'; |
Mikkel Nygaard Ravn | 3c83c52 | 2018-08-07 23:43:15 +0200 | [diff] [blame] | 15 | import 'build_info.dart'; |
Sigurd Meldgaard | 1e8ef60 | 2018-06-28 14:35:00 +0200 | [diff] [blame] | 16 | import 'bundle.dart' as bundle; |
Jonah Williams | 3fedb8c | 2019-07-22 15:34:03 -0700 | [diff] [blame] | 17 | import 'features.dart'; |
Mikkel Nygaard Ravn | d89a6b5 | 2018-06-22 18:19:37 +0200 | [diff] [blame] | 18 | import 'flutter_manifest.dart'; |
Jonah Williams | ee7a37f | 2020-01-06 11:04:20 -0800 | [diff] [blame] | 19 | import 'globals.dart' as globals; |
Todd Volkert | c22ce95 | 2019-08-16 17:10:07 -0700 | [diff] [blame] | 20 | import 'ios/plist_parser.dart'; |
Mikkel Nygaard Ravn | d89a6b5 | 2018-06-22 18:19:37 +0200 | [diff] [blame] | 21 | import 'ios/xcodeproj.dart' as xcode; |
Jenn Magder | e110ca7 | 2020-07-09 16:56:02 -0700 | [diff] [blame] | 22 | import 'ios/xcodeproj.dart'; |
Francisco Magdaleno | fcf341e | 2020-01-17 14:43:34 -0800 | [diff] [blame] | 23 | import 'platform_plugins.dart'; |
Mikkel Nygaard Ravn | d89a6b5 | 2018-06-22 18:19:37 +0200 | [diff] [blame] | 24 | import 'plugins.dart'; |
| 25 | import 'template.dart'; |
Mikkel Nygaard Ravn | 4353297 | 2018-01-18 09:21:24 +0100 | [diff] [blame] | 26 | |
Lau Ching Jun | cfe4d0d | 2019-06-26 13:29:53 -0700 | [diff] [blame] | 27 | class FlutterProjectFactory { |
Jonah Williams | 8ed40dd | 2020-04-13 18:55:01 -0700 | [diff] [blame] | 28 | FlutterProjectFactory({ |
| 29 | @required Logger logger, |
| 30 | @required FileSystem fileSystem, |
| 31 | }) : _logger = logger, |
| 32 | _fileSystem = fileSystem; |
| 33 | |
| 34 | final Logger _logger; |
| 35 | final FileSystem _fileSystem; |
Zachary Anderson | b7c714e | 2019-08-28 10:03:53 -0700 | [diff] [blame] | 36 | |
Emmanuel Garcia | a15a81b | 2019-12-17 14:10:36 -0800 | [diff] [blame] | 37 | @visibleForTesting |
| 38 | final Map<String, FlutterProject> projects = |
Zachary Anderson | b7c714e | 2019-08-28 10:03:53 -0700 | [diff] [blame] | 39 | <String, FlutterProject>{}; |
Lau Ching Jun | cfe4d0d | 2019-06-26 13:29:53 -0700 | [diff] [blame] | 40 | |
| 41 | /// Returns a [FlutterProject] view of the given directory or a ToolExit error, |
| 42 | /// if `pubspec.yaml` or `example/pubspec.yaml` is invalid. |
| 43 | FlutterProject fromDirectory(Directory directory) { |
| 44 | assert(directory != null); |
Jonah Williams | 8ed40dd | 2020-04-13 18:55:01 -0700 | [diff] [blame] | 45 | return projects.putIfAbsent(directory.path, () { |
Zachary Anderson | b7c714e | 2019-08-28 10:03:53 -0700 | [diff] [blame] | 46 | final FlutterManifest manifest = FlutterProject._readManifest( |
| 47 | directory.childFile(bundle.defaultManifestPath).path, |
Jonah Williams | 8ed40dd | 2020-04-13 18:55:01 -0700 | [diff] [blame] | 48 | logger: _logger, |
| 49 | fileSystem: _fileSystem, |
Zachary Anderson | b7c714e | 2019-08-28 10:03:53 -0700 | [diff] [blame] | 50 | ); |
| 51 | final FlutterManifest exampleManifest = FlutterProject._readManifest( |
| 52 | FlutterProject._exampleDirectory(directory) |
| 53 | .childFile(bundle.defaultManifestPath) |
| 54 | .path, |
Jonah Williams | 8ed40dd | 2020-04-13 18:55:01 -0700 | [diff] [blame] | 55 | logger: _logger, |
| 56 | fileSystem: _fileSystem, |
Zachary Anderson | b7c714e | 2019-08-28 10:03:53 -0700 | [diff] [blame] | 57 | ); |
| 58 | return FlutterProject(directory, manifest, exampleManifest); |
| 59 | }); |
Lau Ching Jun | cfe4d0d | 2019-06-26 13:29:53 -0700 | [diff] [blame] | 60 | } |
| 61 | } |
| 62 | |
Mikkel Nygaard Ravn | 4353297 | 2018-01-18 09:21:24 +0100 | [diff] [blame] | 63 | /// Represents the contents of a Flutter project at the specified [directory]. |
Mikkel Nygaard Ravn | b280074 | 2018-08-02 14:12:25 +0200 | [diff] [blame] | 64 | /// |
Mikkel Nygaard Ravn | 3c83c52 | 2018-08-07 23:43:15 +0200 | [diff] [blame] | 65 | /// [FlutterManifest] information is read from `pubspec.yaml` and |
| 66 | /// `example/pubspec.yaml` files on construction of a [FlutterProject] instance. |
| 67 | /// The constructed instance carries an immutable snapshot representation of the |
| 68 | /// presence and content of those files. Accordingly, [FlutterProject] instances |
| 69 | /// should be discarded upon changes to the `pubspec.yaml` files, but can be |
| 70 | /// used across changes to other files, as no other file-level information is |
| 71 | /// cached. |
Mikkel Nygaard Ravn | 4353297 | 2018-01-18 09:21:24 +0100 | [diff] [blame] | 72 | class FlutterProject { |
Mikkel Nygaard Ravn | 3c83c52 | 2018-08-07 23:43:15 +0200 | [diff] [blame] | 73 | @visibleForTesting |
Mikkel Nygaard Ravn | a737c86 | 2018-08-10 21:49:24 +0200 | [diff] [blame] | 74 | FlutterProject(this.directory, this.manifest, this._exampleManifest) |
Alexandre Ardhuin | ef276ff | 2019-01-29 21:47:16 +0100 | [diff] [blame] | 75 | : assert(directory != null), |
| 76 | assert(manifest != null), |
| 77 | assert(_exampleManifest != null); |
Sigurd Meldgaard | 1e8ef60 | 2018-06-28 14:35:00 +0200 | [diff] [blame] | 78 | |
Lau Ching Jun | cfe4d0d | 2019-06-26 13:29:53 -0700 | [diff] [blame] | 79 | /// Returns a [FlutterProject] view of the given directory or a ToolExit error, |
| 80 | /// if `pubspec.yaml` or `example/pubspec.yaml` is invalid. |
Jenn Magder | ce40fba | 2020-03-27 16:21:45 -0700 | [diff] [blame] | 81 | static FlutterProject fromDirectory(Directory directory) => globals.projectFactory.fromDirectory(directory); |
Mikkel Nygaard Ravn | b280074 | 2018-08-02 14:12:25 +0200 | [diff] [blame] | 82 | |
Lau Ching Jun | cfe4d0d | 2019-06-26 13:29:53 -0700 | [diff] [blame] | 83 | /// Returns a [FlutterProject] view of the current directory or a ToolExit error, |
| 84 | /// if `pubspec.yaml` or `example/pubspec.yaml` is invalid. |
Shi-Hao Hong | fd22fc3 | 2020-08-31 13:19:41 +0800 | [diff] [blame] | 85 | static FlutterProject current() => globals.projectFactory.fromDirectory(globals.fs.currentDirectory); |
Mikkel Nygaard Ravn | b280074 | 2018-08-02 14:12:25 +0200 | [diff] [blame] | 86 | |
Lau Ching Jun | cfe4d0d | 2019-06-26 13:29:53 -0700 | [diff] [blame] | 87 | /// Returns a [FlutterProject] view of the given directory or a ToolExit error, |
| 88 | /// if `pubspec.yaml` or `example/pubspec.yaml` is invalid. |
Shi-Hao Hong | fd22fc3 | 2020-08-31 13:19:41 +0800 | [diff] [blame] | 89 | static FlutterProject fromPath(String path) => globals.projectFactory.fromDirectory(globals.fs.directory(path)); |
Mikkel Nygaard Ravn | 4353297 | 2018-01-18 09:21:24 +0100 | [diff] [blame] | 90 | |
| 91 | /// The location of this project. |
| 92 | final Directory directory; |
| 93 | |
Mikkel Nygaard Ravn | a737c86 | 2018-08-10 21:49:24 +0200 | [diff] [blame] | 94 | /// The manifest of this project. |
Mikkel Nygaard Ravn | b280074 | 2018-08-02 14:12:25 +0200 | [diff] [blame] | 95 | final FlutterManifest manifest; |
| 96 | |
Mikkel Nygaard Ravn | a737c86 | 2018-08-10 21:49:24 +0200 | [diff] [blame] | 97 | /// The manifest of the example sub-project of this project. |
Mikkel Nygaard Ravn | b280074 | 2018-08-02 14:12:25 +0200 | [diff] [blame] | 98 | final FlutterManifest _exampleManifest; |
Sigurd Meldgaard | 1e8ef60 | 2018-06-28 14:35:00 +0200 | [diff] [blame] | 99 | |
Mikkel Nygaard Ravn | 22832d3 | 2018-08-30 16:18:44 +0200 | [diff] [blame] | 100 | /// The set of organization names found in this project as |
Mikkel Nygaard Ravn | 4353297 | 2018-01-18 09:21:24 +0100 | [diff] [blame] | 101 | /// part of iOS product bundle identifier, Android application ID, or |
| 102 | /// Gradle group ID. |
Zachary Anderson | 8a33d24 | 2019-09-16 07:51:50 -0700 | [diff] [blame] | 103 | Future<Set<String>> get organizationNames async { |
Mikkel Nygaard Ravn | 22832d3 | 2018-08-30 16:18:44 +0200 | [diff] [blame] | 104 | final List<String> candidates = <String>[ |
Jenn Magder | 9c87b32 | 2020-05-11 11:56:44 -0700 | [diff] [blame] | 105 | // Don't require iOS build info, this method is only |
| 106 | // used during create as best-effort, use the |
| 107 | // default target bundle identifier. |
Jenn Magder | e110ca7 | 2020-07-09 16:56:02 -0700 | [diff] [blame] | 108 | if (ios.existsSync()) |
| 109 | await ios.productBundleIdentifier(null), |
| 110 | if (android.existsSync()) ...<String>[ |
| 111 | android.applicationId, |
| 112 | android.group, |
| 113 | ], |
| 114 | if (example.android.existsSync()) |
| 115 | example.android.applicationId, |
| 116 | if (example.ios.existsSync()) |
| 117 | await example.ios.productBundleIdentifier(null), |
Mikkel Nygaard Ravn | 22832d3 | 2018-08-30 16:18:44 +0200 | [diff] [blame] | 118 | ]; |
James D. Lin | 566c1d1 | 2020-04-21 22:09:50 -0700 | [diff] [blame] | 119 | return Set<String>.of(candidates |
Alexandre Ardhuin | f62afdc | 2018-10-01 21:29:08 +0200 | [diff] [blame] | 120 | .map<String>(_organizationNameFromPackageName) |
Mikkel Nygaard Ravn | b280074 | 2018-08-02 14:12:25 +0200 | [diff] [blame] | 121 | .where((String name) => name != null)); |
Mikkel Nygaard Ravn | 4353297 | 2018-01-18 09:21:24 +0100 | [diff] [blame] | 122 | } |
| 123 | |
| 124 | String _organizationNameFromPackageName(String packageName) { |
Zachary Anderson | e2340c6 | 2019-09-13 14:51:35 -0700 | [diff] [blame] | 125 | if (packageName != null && 0 <= packageName.lastIndexOf('.')) { |
Mikkel Nygaard Ravn | 4353297 | 2018-01-18 09:21:24 +0100 | [diff] [blame] | 126 | return packageName.substring(0, packageName.lastIndexOf('.')); |
Zachary Anderson | e2340c6 | 2019-09-13 14:51:35 -0700 | [diff] [blame] | 127 | } |
| 128 | return null; |
Mikkel Nygaard Ravn | 4353297 | 2018-01-18 09:21:24 +0100 | [diff] [blame] | 129 | } |
| 130 | |
| 131 | /// The iOS sub project of this project. |
Zachary Anderson | 8841afe | 2019-05-14 10:59:23 -0700 | [diff] [blame] | 132 | IosProject _ios; |
| 133 | IosProject get ios => _ios ??= IosProject.fromFlutter(this); |
Mikkel Nygaard Ravn | 4353297 | 2018-01-18 09:21:24 +0100 | [diff] [blame] | 134 | |
| 135 | /// The Android sub project of this project. |
Zachary Anderson | 8841afe | 2019-05-14 10:59:23 -0700 | [diff] [blame] | 136 | AndroidProject _android; |
| 137 | AndroidProject get android => _android ??= AndroidProject._(this); |
Sigurd Meldgaard | 2d3a5c7 | 2018-07-20 08:00:30 +0200 | [diff] [blame] | 138 | |
Jonah Williams | 6f5f037 | 2019-02-26 16:58:24 -0800 | [diff] [blame] | 139 | /// The web sub project of this project. |
Zachary Anderson | 8841afe | 2019-05-14 10:59:23 -0700 | [diff] [blame] | 140 | WebProject _web; |
| 141 | WebProject get web => _web ??= WebProject._(this); |
Jonah Williams | 6f5f037 | 2019-02-26 16:58:24 -0800 | [diff] [blame] | 142 | |
Zachary Anderson | 8841afe | 2019-05-14 10:59:23 -0700 | [diff] [blame] | 143 | /// The MacOS sub project of this project. |
| 144 | MacOSProject _macos; |
| 145 | MacOSProject get macos => _macos ??= MacOSProject._(this); |
Jonah Williams | 86c938b | 2019-04-17 12:16:55 -0700 | [diff] [blame] | 146 | |
Zachary Anderson | 8841afe | 2019-05-14 10:59:23 -0700 | [diff] [blame] | 147 | /// The Linux sub project of this project. |
| 148 | LinuxProject _linux; |
| 149 | LinuxProject get linux => _linux ??= LinuxProject._(this); |
Jonah Williams | 86c938b | 2019-04-17 12:16:55 -0700 | [diff] [blame] | 150 | |
Zachary Anderson | 8841afe | 2019-05-14 10:59:23 -0700 | [diff] [blame] | 151 | /// The Windows sub project of this project. |
| 152 | WindowsProject _windows; |
| 153 | WindowsProject get windows => _windows ??= WindowsProject._(this); |
| 154 | |
| 155 | /// The Fuchsia sub project of this project. |
| 156 | FuchsiaProject _fuchsia; |
| 157 | FuchsiaProject get fuchsia => _fuchsia ??= FuchsiaProject._(this); |
Jonah Williams | 86c938b | 2019-04-17 12:16:55 -0700 | [diff] [blame] | 158 | |
Mikkel Nygaard Ravn | 22832d3 | 2018-08-30 16:18:44 +0200 | [diff] [blame] | 159 | /// The `pubspec.yaml` file of this project. |
| 160 | File get pubspecFile => directory.childFile('pubspec.yaml'); |
| 161 | |
| 162 | /// The `.packages` file of this project. |
| 163 | File get packagesFile => directory.childFile('.packages'); |
| 164 | |
Jenn Magder | ce40fba | 2020-03-27 16:21:45 -0700 | [diff] [blame] | 165 | /// The `.metadata` file of this project. |
| 166 | File get metadataFile => directory.childFile('.metadata'); |
| 167 | |
Mikkel Nygaard Ravn | 22832d3 | 2018-08-30 16:18:44 +0200 | [diff] [blame] | 168 | /// The `.flutter-plugins` file of this project. |
Mikkel Nygaard Ravn | b280074 | 2018-08-02 14:12:25 +0200 | [diff] [blame] | 169 | File get flutterPluginsFile => directory.childFile('.flutter-plugins'); |
| 170 | |
Emmanuel Garcia | b6e9200 | 2019-11-22 15:02:20 -0800 | [diff] [blame] | 171 | /// The `.flutter-plugins-dependencies` file of this project, |
| 172 | /// which contains the dependencies each plugin depends on. |
| 173 | File get flutterPluginsDependenciesFile => directory.childFile('.flutter-plugins-dependencies'); |
| 174 | |
Jonah Williams | 1237ee8 | 2019-01-24 15:59:55 -0800 | [diff] [blame] | 175 | /// The `.dart-tool` directory of this project. |
| 176 | Directory get dartTool => directory.childDirectory('.dart_tool'); |
| 177 | |
Jonah Williams | 67cf215 | 2019-02-14 23:17:16 -0800 | [diff] [blame] | 178 | /// The directory containing the generated code for this project. |
| 179 | Directory get generated => directory |
Jonah Williams | a2d349c | 2019-03-07 11:02:42 -0800 | [diff] [blame] | 180 | .absolute |
Jonah Williams | 67cf215 | 2019-02-14 23:17:16 -0800 | [diff] [blame] | 181 | .childDirectory('.dart_tool') |
| 182 | .childDirectory('build') |
| 183 | .childDirectory('generated') |
| 184 | .childDirectory(manifest.appName); |
| 185 | |
Mikkel Nygaard Ravn | b280074 | 2018-08-02 14:12:25 +0200 | [diff] [blame] | 186 | /// The example sub-project of this project. |
Alexandre Ardhuin | d927c93 | 2018-09-12 08:29:29 +0200 | [diff] [blame] | 187 | FlutterProject get example => FlutterProject( |
Mikkel Nygaard Ravn | 3c83c52 | 2018-08-07 23:43:15 +0200 | [diff] [blame] | 188 | _exampleDirectory(directory), |
| 189 | _exampleManifest, |
Jonah Williams | 8ed40dd | 2020-04-13 18:55:01 -0700 | [diff] [blame] | 190 | FlutterManifest.empty(logger: globals.logger), |
Mikkel Nygaard Ravn | 3c83c52 | 2018-08-07 23:43:15 +0200 | [diff] [blame] | 191 | ); |
Sigurd Meldgaard | 2d3a5c7 | 2018-07-20 08:00:30 +0200 | [diff] [blame] | 192 | |
Greg Spencer | 0ff9e8a | 2018-10-10 11:01:40 -0700 | [diff] [blame] | 193 | /// True if this project is a Flutter module project. |
| 194 | bool get isModule => manifest.isModule; |
Mikkel Nygaard Ravn | 3c83c52 | 2018-08-07 23:43:15 +0200 | [diff] [blame] | 195 | |
Nolan Scobie | 43c1b34 | 2020-08-06 19:18:52 -0400 | [diff] [blame] | 196 | /// True if the Flutter project is using the AndroidX support library. |
Josh Burton | d0e45a2 | 2019-06-01 13:33:02 +1200 | [diff] [blame] | 197 | bool get usesAndroidX => manifest.usesAndroidX; |
| 198 | |
Greg Spencer | 0ff9e8a | 2018-10-10 11:01:40 -0700 | [diff] [blame] | 199 | /// True if this project has an example application. |
Mikkel Nygaard Ravn | 3c83c52 | 2018-08-07 23:43:15 +0200 | [diff] [blame] | 200 | bool get hasExampleApp => _exampleDirectory(directory).existsSync(); |
Mikkel Nygaard Ravn | 2000435 | 2018-02-16 10:17:28 +0100 | [diff] [blame] | 201 | |
Sigurd Meldgaard | 1e8ef60 | 2018-06-28 14:35:00 +0200 | [diff] [blame] | 202 | /// The directory that will contain the example if an example exists. |
Mikkel Nygaard Ravn | 3c83c52 | 2018-08-07 23:43:15 +0200 | [diff] [blame] | 203 | static Directory _exampleDirectory(Directory directory) => directory.childDirectory('example'); |
Mikkel Nygaard Ravn | 2000435 | 2018-02-16 10:17:28 +0100 | [diff] [blame] | 204 | |
Mikkel Nygaard Ravn | a737c86 | 2018-08-10 21:49:24 +0200 | [diff] [blame] | 205 | /// Reads and validates the `pubspec.yaml` file at [path], asynchronously |
| 206 | /// returning a [FlutterManifest] representation of the contents. |
| 207 | /// |
| 208 | /// Completes with an empty [FlutterManifest], if the file does not exist. |
| 209 | /// Completes with a ToolExit on validation error. |
Jonah Williams | 8ed40dd | 2020-04-13 18:55:01 -0700 | [diff] [blame] | 210 | static FlutterManifest _readManifest(String path, { |
| 211 | @required Logger logger, |
| 212 | @required FileSystem fileSystem, |
| 213 | }) { |
Zachary Anderson | 13382f4 | 2019-07-12 23:24:04 -0700 | [diff] [blame] | 214 | FlutterManifest manifest; |
| 215 | try { |
Jonah Williams | 8ed40dd | 2020-04-13 18:55:01 -0700 | [diff] [blame] | 216 | manifest = FlutterManifest.createFromPath( |
| 217 | path, |
| 218 | logger: logger, |
| 219 | fileSystem: fileSystem, |
| 220 | ); |
Zachary Anderson | 13382f4 | 2019-07-12 23:24:04 -0700 | [diff] [blame] | 221 | } on YamlException catch (e) { |
Jonah Williams | 8ed40dd | 2020-04-13 18:55:01 -0700 | [diff] [blame] | 222 | logger.printStatus('Error detected in pubspec.yaml:', emphasis: true); |
| 223 | logger.printError('$e'); |
Zachary Anderson | 13382f4 | 2019-07-12 23:24:04 -0700 | [diff] [blame] | 224 | } |
| 225 | if (manifest == null) { |
Mikkel Nygaard Ravn | a737c86 | 2018-08-10 21:49:24 +0200 | [diff] [blame] | 226 | throwToolExit('Please correct the pubspec.yaml file at $path'); |
Zachary Anderson | 13382f4 | 2019-07-12 23:24:04 -0700 | [diff] [blame] | 227 | } |
Mikkel Nygaard Ravn | a737c86 | 2018-08-10 21:49:24 +0200 | [diff] [blame] | 228 | return manifest; |
| 229 | } |
| 230 | |
Jenn Magder | 5e17a24 | 2020-10-19 14:17:43 -0700 | [diff] [blame] | 231 | /// Reapplies template files and regenerates project files and plugin |
| 232 | /// registrants for app and module projects only. |
| 233 | /// |
| 234 | /// Will not create project platform directories if they do not already exist. |
| 235 | Future<void> regeneratePlatformSpecificTooling() async { |
| 236 | return ensureReadyForPlatformSpecificTooling( |
| 237 | androidPlatform: android.existsSync(), |
| 238 | iosPlatform: ios.existsSync(), |
| 239 | // TODO(stuartmorgan): Revisit the conditions here once the plans for handling |
| 240 | // desktop in existing projects are in place. |
| 241 | linuxPlatform: featureFlags.isLinuxEnabled && linux.existsSync(), |
| 242 | macOSPlatform: featureFlags.isMacOSEnabled && macos.existsSync(), |
| 243 | windowsPlatform: featureFlags.isWindowsEnabled && windows.existsSync(), |
| 244 | webPlatform: featureFlags.isWebEnabled && web.existsSync(), |
| 245 | ); |
| 246 | } |
| 247 | |
| 248 | /// Applies template files and generates project files and plugin |
| 249 | /// registrants for app and module projects only for the specified platforms. |
| 250 | Future<void> ensureReadyForPlatformSpecificTooling({ |
| 251 | bool androidPlatform = false, |
| 252 | bool iosPlatform = false, |
| 253 | bool linuxPlatform = false, |
| 254 | bool macOSPlatform = false, |
| 255 | bool windowsPlatform = false, |
| 256 | bool webPlatform = false, |
| 257 | }) async { |
Jonah Williams | a476a08 | 2019-04-22 15:18:15 -0700 | [diff] [blame] | 258 | if (!directory.existsSync() || hasExampleApp) { |
Mikkel Nygaard Ravn | b280074 | 2018-08-02 14:12:25 +0200 | [diff] [blame] | 259 | return; |
Jonah Williams | a476a08 | 2019-04-22 15:18:15 -0700 | [diff] [blame] | 260 | } |
Jenn Magder | 5e17a24 | 2020-10-19 14:17:43 -0700 | [diff] [blame] | 261 | await refreshPluginsList(this, iosPlatform: iosPlatform, macOSPlatform: macOSPlatform); |
| 262 | if (androidPlatform) { |
Jonah Williams | a476a08 | 2019-04-22 15:18:15 -0700 | [diff] [blame] | 263 | await android.ensureReadyForPlatformSpecificTooling(); |
| 264 | } |
Jenn Magder | 5e17a24 | 2020-10-19 14:17:43 -0700 | [diff] [blame] | 265 | if (iosPlatform) { |
Jonah Williams | a476a08 | 2019-04-22 15:18:15 -0700 | [diff] [blame] | 266 | await ios.ensureReadyForPlatformSpecificTooling(); |
| 267 | } |
Jenn Magder | 5e17a24 | 2020-10-19 14:17:43 -0700 | [diff] [blame] | 268 | if (linuxPlatform) { |
stuartmorgan | 56d68a9 | 2019-09-26 14:01:57 -0700 | [diff] [blame] | 269 | await linux.ensureReadyForPlatformSpecificTooling(); |
| 270 | } |
Jenn Magder | 5e17a24 | 2020-10-19 14:17:43 -0700 | [diff] [blame] | 271 | if (macOSPlatform) { |
stuartmorgan | 3ebebeb | 2019-05-31 13:19:44 -0700 | [diff] [blame] | 272 | await macos.ensureReadyForPlatformSpecificTooling(); |
| 273 | } |
Jenn Magder | 5e17a24 | 2020-10-19 14:17:43 -0700 | [diff] [blame] | 274 | if (windowsPlatform) { |
stuartmorgan | 56d68a9 | 2019-09-26 14:01:57 -0700 | [diff] [blame] | 275 | await windows.ensureReadyForPlatformSpecificTooling(); |
| 276 | } |
Jenn Magder | 5e17a24 | 2020-10-19 14:17:43 -0700 | [diff] [blame] | 277 | if (webPlatform) { |
Jonah Williams | 6f5f037 | 2019-02-26 16:58:24 -0800 | [diff] [blame] | 278 | await web.ensureReadyForPlatformSpecificTooling(); |
| 279 | } |
Jenn Magder | 5e17a24 | 2020-10-19 14:17:43 -0700 | [diff] [blame] | 280 | await injectPlugins( |
| 281 | this, |
| 282 | androidPlatform: androidPlatform, |
| 283 | iosPlatform: iosPlatform, |
| 284 | linuxPlatform: linuxPlatform, |
| 285 | macOSPlatform: macOSPlatform, |
| 286 | windowsPlatform: windowsPlatform, |
| 287 | webPlatform: webPlatform, |
| 288 | ); |
Mikkel Nygaard Ravn | 2000435 | 2018-02-16 10:17:28 +0100 | [diff] [blame] | 289 | } |
Balvinder Singh Gambhir | 3dde3e0 | 2020-09-15 23:51:54 +0530 | [diff] [blame] | 290 | |
| 291 | /// Returns a json encoded string containing the [appName], [version], and [buildNumber] that is used to generate version.json |
| 292 | String getVersionInfo() { |
| 293 | final Map<String, String> versionFileJson = <String, String>{ |
| 294 | 'app_name': manifest.appName, |
| 295 | 'version': manifest.buildName, |
| 296 | 'build_number': manifest.buildNumber |
| 297 | }; |
| 298 | return jsonEncode(versionFileJson); |
| 299 | } |
Mikkel Nygaard Ravn | 4353297 | 2018-01-18 09:21:24 +0100 | [diff] [blame] | 300 | } |
| 301 | |
Francisco Magdaleno | fcf341e | 2020-01-17 14:43:34 -0800 | [diff] [blame] | 302 | /// Base class for projects per platform. |
| 303 | abstract class FlutterProjectPlatform { |
| 304 | |
| 305 | /// Plugin's platform config key, e.g., "macos", "ios". |
| 306 | String get pluginConfigKey; |
| 307 | |
| 308 | /// Whether the platform exists in the project. |
| 309 | bool existsSync(); |
| 310 | } |
| 311 | |
stuartmorgan | 3ebebeb | 2019-05-31 13:19:44 -0700 | [diff] [blame] | 312 | /// Represents an Xcode-based sub-project. |
| 313 | /// |
| 314 | /// This defines interfaces common to iOS and macOS projects. |
| 315 | abstract class XcodeBasedProject { |
| 316 | /// The parent of this project. |
| 317 | FlutterProject get parent; |
| 318 | |
| 319 | /// Whether the subproject (either iOS or macOS) exists in the Flutter project. |
| 320 | bool existsSync(); |
| 321 | |
| 322 | /// The Xcode project (.xcodeproj directory) of the host app. |
| 323 | Directory get xcodeProject; |
| 324 | |
| 325 | /// The 'project.pbxproj' file of [xcodeProject]. |
| 326 | File get xcodeProjectInfoFile; |
| 327 | |
| 328 | /// The Xcode workspace (.xcworkspace directory) of the host app. |
| 329 | Directory get xcodeWorkspace; |
| 330 | |
| 331 | /// Contains definitions for FLUTTER_ROOT, LOCAL_ENGINE, and more flags for |
| 332 | /// the Xcode build. |
| 333 | File get generatedXcodePropertiesFile; |
| 334 | |
| 335 | /// The Flutter-managed Xcode config file for [mode]. |
| 336 | File xcodeConfigFor(String mode); |
| 337 | |
Jenn Magder | bd47a31 | 2019-07-30 09:58:39 -0700 | [diff] [blame] | 338 | /// The script that exports environment variables needed for Flutter tools. |
| 339 | /// Can be run first in a Xcode Script build phase to make FLUTTER_ROOT, |
| 340 | /// LOCAL_ENGINE, and other Flutter variables available to any flutter |
| 341 | /// tooling (`flutter build`, etc) to convert into flags. |
| 342 | File get generatedEnvironmentVariableExportScript; |
| 343 | |
stuartmorgan | 3ebebeb | 2019-05-31 13:19:44 -0700 | [diff] [blame] | 344 | /// The CocoaPods 'Podfile'. |
| 345 | File get podfile; |
| 346 | |
| 347 | /// The CocoaPods 'Podfile.lock'. |
| 348 | File get podfileLock; |
| 349 | |
| 350 | /// The CocoaPods 'Manifest.lock'. |
| 351 | File get podManifestLock; |
stuartmorgan | 3ebebeb | 2019-05-31 13:19:44 -0700 | [diff] [blame] | 352 | } |
| 353 | |
stuartmorgan | 4b12050 | 2020-07-06 12:59:16 -0700 | [diff] [blame] | 354 | /// Represents a CMake-based sub-project. |
| 355 | /// |
| 356 | /// This defines interfaces common to Windows and Linux projects. |
| 357 | abstract class CmakeBasedProject { |
| 358 | /// The parent of this project. |
| 359 | FlutterProject get parent; |
| 360 | |
| 361 | /// Whether the subproject (either Windows or Linux) exists in the Flutter project. |
| 362 | bool existsSync(); |
| 363 | |
| 364 | /// The native project CMake specification. |
| 365 | File get cmakeFile; |
| 366 | |
J-P Nurmi | f2536da | 2020-12-22 01:04:04 +0100 | [diff] [blame] | 367 | /// Contains definitions for the Flutter library and the tool. |
| 368 | File get managedCmakeFile; |
| 369 | |
stuartmorgan | 4b12050 | 2020-07-06 12:59:16 -0700 | [diff] [blame] | 370 | /// Contains definitions for FLUTTER_ROOT, LOCAL_ENGINE, and more flags for |
| 371 | /// the build. |
| 372 | File get generatedCmakeConfigFile; |
| 373 | |
Jonah Williams | 08576cb | 2020-10-12 09:31:02 -0700 | [diff] [blame] | 374 | /// Included CMake with rules and variables for plugin builds. |
stuartmorgan | 4b12050 | 2020-07-06 12:59:16 -0700 | [diff] [blame] | 375 | File get generatedPluginCmakeFile; |
stuartmorgan | 6d1c244 | 2020-09-09 19:08:19 -0400 | [diff] [blame] | 376 | |
| 377 | /// The directory to write plugin symlinks. |
| 378 | Directory get pluginSymlinkDirectory; |
stuartmorgan | 4b12050 | 2020-07-06 12:59:16 -0700 | [diff] [blame] | 379 | } |
| 380 | |
Mikkel Nygaard Ravn | 3c83c52 | 2018-08-07 23:43:15 +0200 | [diff] [blame] | 381 | /// Represents the iOS sub-project of a Flutter project. |
| 382 | /// |
| 383 | /// Instances will reflect the contents of the `ios/` sub-folder of |
Greg Spencer | 0ff9e8a | 2018-10-10 11:01:40 -0700 | [diff] [blame] | 384 | /// Flutter applications and the `.ios/` sub-folder of Flutter module projects. |
Francisco Magdaleno | fcf341e | 2020-01-17 14:43:34 -0800 | [diff] [blame] | 385 | class IosProject extends FlutterProjectPlatform implements XcodeBasedProject { |
KyleWong | 60aa49e | 2019-01-30 12:33:16 +0800 | [diff] [blame] | 386 | IosProject.fromFlutter(this.parent); |
Mikkel Nygaard Ravn | 3c83c52 | 2018-08-07 23:43:15 +0200 | [diff] [blame] | 387 | |
stuartmorgan | 3ebebeb | 2019-05-31 13:19:44 -0700 | [diff] [blame] | 388 | @override |
Mikkel Nygaard Ravn | 3c83c52 | 2018-08-07 23:43:15 +0200 | [diff] [blame] | 389 | final FlutterProject parent; |
| 390 | |
Francisco Magdaleno | fcf341e | 2020-01-17 14:43:34 -0800 | [diff] [blame] | 391 | @override |
| 392 | String get pluginConfigKey => IOSPlugin.kConfigKey; |
| 393 | |
Todd Volkert | c8c935c | 2019-01-30 07:32:05 -0800 | [diff] [blame] | 394 | static final RegExp _productBundleIdPattern = RegExp(r'''^\s*PRODUCT_BUNDLE_IDENTIFIER\s*=\s*(["']?)(.*?)\1;\s*$'''); |
Alexandre Ardhuin | 2ea1d81 | 2018-10-04 07:28:07 +0200 | [diff] [blame] | 395 | static const String _productBundleIdVariable = r'$(PRODUCT_BUNDLE_IDENTIFIER)'; |
Jenn Magder | c8c55b4 | 2020-05-04 11:31:08 -0700 | [diff] [blame] | 396 | static const String _hostAppProjectName = 'Runner'; |
Alexandre Ardhuin | 2ea1d81 | 2018-10-04 07:28:07 +0200 | [diff] [blame] | 397 | |
jmagman | 0a58610 | 2019-08-09 17:26:51 -0700 | [diff] [blame] | 398 | Directory get ephemeralDirectory => parent.directory.childDirectory('.ios'); |
Mikkel Nygaard Ravn | a600fe7 | 2018-09-25 21:21:13 +0200 | [diff] [blame] | 399 | Directory get _editableDirectory => parent.directory.childDirectory('ios'); |
Mikkel Nygaard Ravn | 3c83c52 | 2018-08-07 23:43:15 +0200 | [diff] [blame] | 400 | |
Mikkel Nygaard Ravn | a600fe7 | 2018-09-25 21:21:13 +0200 | [diff] [blame] | 401 | /// This parent folder of `Runner.xcodeproj`. |
| 402 | Directory get hostAppRoot { |
Zachary Anderson | e2340c6 | 2019-09-13 14:51:35 -0700 | [diff] [blame] | 403 | if (!isModule || _editableDirectory.existsSync()) { |
Mikkel Nygaard Ravn | a600fe7 | 2018-09-25 21:21:13 +0200 | [diff] [blame] | 404 | return _editableDirectory; |
Zachary Anderson | e2340c6 | 2019-09-13 14:51:35 -0700 | [diff] [blame] | 405 | } |
jmagman | 0a58610 | 2019-08-09 17:26:51 -0700 | [diff] [blame] | 406 | return ephemeralDirectory; |
Mikkel Nygaard Ravn | a600fe7 | 2018-09-25 21:21:13 +0200 | [diff] [blame] | 407 | } |
| 408 | |
| 409 | /// The root directory of the iOS wrapping of Flutter and plugins. This is the |
| 410 | /// parent of the `Flutter/` folder into which Flutter artifacts are written |
| 411 | /// during build. |
| 412 | /// |
| 413 | /// This is the same as [hostAppRoot] except when the project is |
Greg Spencer | 0ff9e8a | 2018-10-10 11:01:40 -0700 | [diff] [blame] | 414 | /// a Flutter module with an editable host app. |
jmagman | 0a58610 | 2019-08-09 17:26:51 -0700 | [diff] [blame] | 415 | Directory get _flutterLibRoot => isModule ? ephemeralDirectory : _editableDirectory; |
Mikkel Nygaard Ravn | a600fe7 | 2018-09-25 21:21:13 +0200 | [diff] [blame] | 416 | |
Greg Spencer | 0ff9e8a | 2018-10-10 11:01:40 -0700 | [diff] [blame] | 417 | /// True, if the parent Flutter project is a module project. |
| 418 | bool get isModule => parent.isModule; |
Mikkel Nygaard Ravn | 4353297 | 2018-01-18 09:21:24 +0100 | [diff] [blame] | 419 | |
Jonah Williams | 15f271e | 2019-04-23 09:49:49 -0700 | [diff] [blame] | 420 | /// Whether the flutter application has an iOS project. |
| 421 | bool get exists => hostAppRoot.existsSync(); |
| 422 | |
stuartmorgan | 3ebebeb | 2019-05-31 13:19:44 -0700 | [diff] [blame] | 423 | @override |
Mikkel Nygaard Ravn | a600fe7 | 2018-09-25 21:21:13 +0200 | [diff] [blame] | 424 | File xcodeConfigFor(String mode) => _flutterLibRoot.childDirectory('Flutter').childFile('$mode.xcconfig'); |
Sigurd Meldgaard | 2d3a5c7 | 2018-07-20 08:00:30 +0200 | [diff] [blame] | 425 | |
stuartmorgan | 3ebebeb | 2019-05-31 13:19:44 -0700 | [diff] [blame] | 426 | @override |
Jenn Magder | bd47a31 | 2019-07-30 09:58:39 -0700 | [diff] [blame] | 427 | File get generatedEnvironmentVariableExportScript => _flutterLibRoot.childDirectory('Flutter').childFile('flutter_export_environment.sh'); |
| 428 | |
| 429 | @override |
Mikkel Nygaard Ravn | a600fe7 | 2018-09-25 21:21:13 +0200 | [diff] [blame] | 430 | File get podfile => hostAppRoot.childFile('Podfile'); |
Sigurd Meldgaard | 2d3a5c7 | 2018-07-20 08:00:30 +0200 | [diff] [blame] | 431 | |
stuartmorgan | 3ebebeb | 2019-05-31 13:19:44 -0700 | [diff] [blame] | 432 | @override |
Mikkel Nygaard Ravn | a600fe7 | 2018-09-25 21:21:13 +0200 | [diff] [blame] | 433 | File get podfileLock => hostAppRoot.childFile('Podfile.lock'); |
Sigurd Meldgaard | 2d3a5c7 | 2018-07-20 08:00:30 +0200 | [diff] [blame] | 434 | |
stuartmorgan | 3ebebeb | 2019-05-31 13:19:44 -0700 | [diff] [blame] | 435 | @override |
Mikkel Nygaard Ravn | a600fe7 | 2018-09-25 21:21:13 +0200 | [diff] [blame] | 436 | File get podManifestLock => hostAppRoot.childDirectory('Pods').childFile('Manifest.lock'); |
Sigurd Meldgaard | 2d3a5c7 | 2018-07-20 08:00:30 +0200 | [diff] [blame] | 437 | |
Jenn Magder | a6c3ffe | 2020-01-02 12:03:02 -0800 | [diff] [blame] | 438 | /// The default 'Info.plist' file of the host app. The developer can change this location in Xcode. |
Jenn Magder | c8c55b4 | 2020-05-04 11:31:08 -0700 | [diff] [blame] | 439 | File get defaultHostInfoPlist => hostAppRoot.childDirectory(_hostAppProjectName).childFile('Info.plist'); |
Mikkel Nygaard Ravn | 6cc8008 | 2018-08-31 11:07:15 +0200 | [diff] [blame] | 440 | |
Jenn Magder | 357d02c | 2019-10-15 15:33:55 -0700 | [diff] [blame] | 441 | Directory get symlinks => _flutterLibRoot.childDirectory('.symlinks'); |
| 442 | |
| 443 | @override |
Jenn Magder | c8c55b4 | 2020-05-04 11:31:08 -0700 | [diff] [blame] | 444 | Directory get xcodeProject => hostAppRoot.childDirectory('$_hostAppProjectName.xcodeproj'); |
Mikkel Nygaard Ravn | 22832d3 | 2018-08-30 16:18:44 +0200 | [diff] [blame] | 445 | |
stuartmorgan | 3ebebeb | 2019-05-31 13:19:44 -0700 | [diff] [blame] | 446 | @override |
Mikkel Nygaard Ravn | 22832d3 | 2018-08-30 16:18:44 +0200 | [diff] [blame] | 447 | File get xcodeProjectInfoFile => xcodeProject.childFile('project.pbxproj'); |
| 448 | |
stuartmorgan | 3ebebeb | 2019-05-31 13:19:44 -0700 | [diff] [blame] | 449 | @override |
Jenn Magder | c8c55b4 | 2020-05-04 11:31:08 -0700 | [diff] [blame] | 450 | Directory get xcodeWorkspace => hostAppRoot.childDirectory('$_hostAppProjectName.xcworkspace'); |
Chris Bracken | f8c50ea | 2018-09-18 10:05:46 -0700 | [diff] [blame] | 451 | |
| 452 | /// Xcode workspace shared data directory for the host app. |
| 453 | Directory get xcodeWorkspaceSharedData => xcodeWorkspace.childDirectory('xcshareddata'); |
| 454 | |
| 455 | /// Xcode workspace shared workspace settings file for the host app. |
| 456 | File get xcodeWorkspaceSharedSettings => xcodeWorkspaceSharedData.childFile('WorkspaceSettings.xcsettings'); |
| 457 | |
stuartmorgan | 3ebebeb | 2019-05-31 13:19:44 -0700 | [diff] [blame] | 458 | @override |
Jonah Williams | 6b19184 | 2019-04-25 12:25:12 -0700 | [diff] [blame] | 459 | bool existsSync() { |
| 460 | return parent.isModule || _editableDirectory.existsSync(); |
| 461 | } |
| 462 | |
Mikkel Nygaard Ravn | 6cc8008 | 2018-08-31 11:07:15 +0200 | [diff] [blame] | 463 | /// The product bundle identifier of the host app, or null if not set or if |
| 464 | /// iOS tooling needed to read it is not installed. |
Jenn Magder | e110ca7 | 2020-07-09 16:56:02 -0700 | [diff] [blame] | 465 | Future<String> productBundleIdentifier(BuildInfo buildInfo) async { |
| 466 | if (!existsSync()) { |
| 467 | return null; |
| 468 | } |
| 469 | return _productBundleIdentifier ??= await _parseProductBundleIdentifier(buildInfo); |
| 470 | } |
Jenn Magder | c8c55b4 | 2020-05-04 11:31:08 -0700 | [diff] [blame] | 471 | String _productBundleIdentifier; |
| 472 | |
Jenn Magder | 9c87b32 | 2020-05-11 11:56:44 -0700 | [diff] [blame] | 473 | Future<String> _parseProductBundleIdentifier(BuildInfo buildInfo) async { |
Todd Volkert | c22ce95 | 2019-08-16 17:10:07 -0700 | [diff] [blame] | 474 | String fromPlist; |
Jenn Magder | a6c3ffe | 2020-01-02 12:03:02 -0800 | [diff] [blame] | 475 | final File defaultInfoPlist = defaultHostInfoPlist; |
| 476 | // Users can change the location of the Info.plist. |
| 477 | // Try parsing the default, first. |
| 478 | if (defaultInfoPlist.existsSync()) { |
| 479 | try { |
Jonah Williams | 80619f1 | 2020-02-26 18:31:42 -0800 | [diff] [blame] | 480 | fromPlist = globals.plistParser.getValueFromFile( |
Jenn Magder | a6c3ffe | 2020-01-02 12:03:02 -0800 | [diff] [blame] | 481 | defaultHostInfoPlist.path, |
| 482 | PlistParser.kCFBundleIdentifierKey, |
| 483 | ); |
| 484 | } on FileNotFoundException { |
| 485 | // iOS tooling not found; likely not running OSX; let [fromPlist] be null |
| 486 | } |
Alexandre Ardhuin | b953c3e | 2020-02-07 16:35:55 +0100 | [diff] [blame] | 487 | if (fromPlist != null && !fromPlist.contains(r'$')) { |
Jenn Magder | a6c3ffe | 2020-01-02 12:03:02 -0800 | [diff] [blame] | 488 | // Info.plist has no build variables in product bundle ID. |
| 489 | return fromPlist; |
| 490 | } |
Todd Volkert | c22ce95 | 2019-08-16 17:10:07 -0700 | [diff] [blame] | 491 | } |
Jenn Magder | 9c87b32 | 2020-05-11 11:56:44 -0700 | [diff] [blame] | 492 | final Map<String, String> allBuildSettings = await buildSettingsForBuildInfo(buildInfo); |
Jenn Magder | a6c3ffe | 2020-01-02 12:03:02 -0800 | [diff] [blame] | 493 | if (allBuildSettings != null) { |
| 494 | if (fromPlist != null) { |
| 495 | // Perform variable substitution using build settings. |
| 496 | return xcode.substituteXcodeVariables(fromPlist, allBuildSettings); |
| 497 | } |
| 498 | return allBuildSettings['PRODUCT_BUNDLE_IDENTIFIER']; |
Mikkel Nygaard Ravn | 6cc8008 | 2018-08-31 11:07:15 +0200 | [diff] [blame] | 499 | } |
Jenn Magder | a6c3ffe | 2020-01-02 12:03:02 -0800 | [diff] [blame] | 500 | |
| 501 | // On non-macOS platforms, parse the first PRODUCT_BUNDLE_IDENTIFIER from |
| 502 | // the project file. This can return the wrong bundle identifier if additional |
| 503 | // bundles have been added to the project and are found first, like frameworks |
| 504 | // or companion watchOS projects. However, on non-macOS platforms this is |
| 505 | // only used for display purposes and to regenerate organization names, so |
| 506 | // best-effort is probably fine. |
Todd Volkert | c8c935c | 2019-01-30 07:32:05 -0800 | [diff] [blame] | 507 | final String fromPbxproj = _firstMatchInFile(xcodeProjectInfoFile, _productBundleIdPattern)?.group(2); |
Mikkel Nygaard Ravn | 6cc8008 | 2018-08-31 11:07:15 +0200 | [diff] [blame] | 508 | if (fromPbxproj != null && (fromPlist == null || fromPlist == _productBundleIdVariable)) { |
Mikkel Nygaard Ravn | 6cc8008 | 2018-08-31 11:07:15 +0200 | [diff] [blame] | 509 | return fromPbxproj; |
| 510 | } |
Jenn Magder | a6c3ffe | 2020-01-02 12:03:02 -0800 | [diff] [blame] | 511 | |
Mikkel Nygaard Ravn | 6cc8008 | 2018-08-31 11:07:15 +0200 | [diff] [blame] | 512 | return null; |
Mikkel Nygaard Ravn | 22832d3 | 2018-08-30 16:18:44 +0200 | [diff] [blame] | 513 | } |
| 514 | |
Jenn Magder | c8c55b4 | 2020-05-04 11:31:08 -0700 | [diff] [blame] | 515 | /// The bundle name of the host app, `My App.app`. |
Jenn Magder | e110ca7 | 2020-07-09 16:56:02 -0700 | [diff] [blame] | 516 | Future<String> hostAppBundleName(BuildInfo buildInfo) async { |
| 517 | if (!existsSync()) { |
| 518 | return null; |
| 519 | } |
| 520 | return _hostAppBundleName ??= await _parseHostAppBundleName(buildInfo); |
| 521 | } |
Jenn Magder | c8c55b4 | 2020-05-04 11:31:08 -0700 | [diff] [blame] | 522 | String _hostAppBundleName; |
| 523 | |
Jenn Magder | 9c87b32 | 2020-05-11 11:56:44 -0700 | [diff] [blame] | 524 | Future<String> _parseHostAppBundleName(BuildInfo buildInfo) async { |
Jenn Magder | c8c55b4 | 2020-05-04 11:31:08 -0700 | [diff] [blame] | 525 | // The product name and bundle name are derived from the display name, which the user |
| 526 | // is instructed to change in Xcode as part of deploying to the App Store. |
| 527 | // https://flutter.dev/docs/deployment/ios#review-xcode-project-settings |
| 528 | // The only source of truth for the name is Xcode's interpretation of the build settings. |
| 529 | String productName; |
| 530 | if (globals.xcodeProjectInterpreter.isInstalled) { |
Jenn Magder | 9c87b32 | 2020-05-11 11:56:44 -0700 | [diff] [blame] | 531 | final Map<String, String> xcodeBuildSettings = await buildSettingsForBuildInfo(buildInfo); |
Jenn Magder | c8c55b4 | 2020-05-04 11:31:08 -0700 | [diff] [blame] | 532 | if (xcodeBuildSettings != null) { |
| 533 | productName = xcodeBuildSettings['FULL_PRODUCT_NAME']; |
| 534 | } |
| 535 | } |
| 536 | if (productName == null) { |
| 537 | globals.printTrace('FULL_PRODUCT_NAME not present, defaulting to $_hostAppProjectName'); |
| 538 | } |
| 539 | return productName ?? '$_hostAppProjectName.app'; |
| 540 | } |
| 541 | |
Mikkel Nygaard Ravn | 22832d3 | 2018-08-30 16:18:44 +0200 | [diff] [blame] | 542 | /// The build settings for the host app of this project, as a detached map. |
Mikkel Nygaard Ravn | 6cc8008 | 2018-08-31 11:07:15 +0200 | [diff] [blame] | 543 | /// |
| 544 | /// Returns null, if iOS tooling is unavailable. |
Jenn Magder | 9c87b32 | 2020-05-11 11:56:44 -0700 | [diff] [blame] | 545 | Future<Map<String, String>> buildSettingsForBuildInfo(BuildInfo buildInfo) async { |
Jenn Magder | e110ca7 | 2020-07-09 16:56:02 -0700 | [diff] [blame] | 546 | if (!existsSync()) { |
| 547 | return null; |
| 548 | } |
Jenn Magder | 9c87b32 | 2020-05-11 11:56:44 -0700 | [diff] [blame] | 549 | _buildSettingsByScheme ??= <String, Map<String, String>>{}; |
Jenn Magder | e110ca7 | 2020-07-09 16:56:02 -0700 | [diff] [blame] | 550 | final XcodeProjectInfo info = await projectInfo(); |
| 551 | if (info == null) { |
| 552 | return null; |
| 553 | } |
| 554 | |
| 555 | final String scheme = info.schemeFor(buildInfo); |
| 556 | if (scheme == null) { |
| 557 | info.reportFlavorNotFoundAndExit(); |
| 558 | } |
| 559 | |
Jenn Magder | 9c87b32 | 2020-05-11 11:56:44 -0700 | [diff] [blame] | 560 | return _buildSettingsByScheme[scheme] ??= await _xcodeProjectBuildSettings(scheme); |
| 561 | } |
| 562 | Map<String, Map<String, String>> _buildSettingsByScheme; |
Jenn Magder | c8c55b4 | 2020-05-04 11:31:08 -0700 | [diff] [blame] | 563 | |
Jenn Magder | e110ca7 | 2020-07-09 16:56:02 -0700 | [diff] [blame] | 564 | Future<XcodeProjectInfo> projectInfo() async { |
Jenn Magder | fe670c0 | 2020-07-22 18:22:38 -0700 | [diff] [blame] | 565 | if (!xcodeProject.existsSync() || !globals.xcodeProjectInterpreter.isInstalled) { |
Jenn Magder | e110ca7 | 2020-07-09 16:56:02 -0700 | [diff] [blame] | 566 | return null; |
| 567 | } |
| 568 | return _projectInfo ??= await globals.xcodeProjectInterpreter.getInfo(hostAppRoot.path); |
| 569 | } |
| 570 | XcodeProjectInfo _projectInfo; |
| 571 | |
Jenn Magder | 9c87b32 | 2020-05-11 11:56:44 -0700 | [diff] [blame] | 572 | Future<Map<String, String>> _xcodeProjectBuildSettings(String scheme) async { |
Jenn Magder | ee84525 | 2020-03-18 15:55:09 -0700 | [diff] [blame] | 573 | if (!globals.xcodeProjectInterpreter.isInstalled) { |
Mikkel Nygaard Ravn | 6cc8008 | 2018-08-31 11:07:15 +0200 | [diff] [blame] | 574 | return null; |
Zachary Anderson | e2340c6 | 2019-09-13 14:51:35 -0700 | [diff] [blame] | 575 | } |
Jenn Magder | c8c55b4 | 2020-05-04 11:31:08 -0700 | [diff] [blame] | 576 | final Map<String, String> buildSettings = await globals.xcodeProjectInterpreter.getBuildSettings( |
Zachary Anderson | 8a33d24 | 2019-09-16 07:51:50 -0700 | [diff] [blame] | 577 | xcodeProject.path, |
Jenn Magder | 9c87b32 | 2020-05-11 11:56:44 -0700 | [diff] [blame] | 578 | scheme: scheme, |
Zachary Anderson | 8a33d24 | 2019-09-16 07:51:50 -0700 | [diff] [blame] | 579 | ); |
Jenn Magder | a6c3ffe | 2020-01-02 12:03:02 -0800 | [diff] [blame] | 580 | if (buildSettings != null && buildSettings.isNotEmpty) { |
| 581 | // No timeouts, flakes, or errors. |
Jenn Magder | a6c3ffe | 2020-01-02 12:03:02 -0800 | [diff] [blame] | 582 | return buildSettings; |
| 583 | } |
| 584 | return null; |
Mikkel Nygaard Ravn | 4353297 | 2018-01-18 09:21:24 +0100 | [diff] [blame] | 585 | } |
Sigurd Meldgaard | 1e8ef60 | 2018-06-28 14:35:00 +0200 | [diff] [blame] | 586 | |
Sigurd Meldgaard | 2d3a5c7 | 2018-07-20 08:00:30 +0200 | [diff] [blame] | 587 | Future<void> ensureReadyForPlatformSpecificTooling() async { |
stuartmorgan | 685e9d1 | 2020-03-23 10:42:26 -0700 | [diff] [blame] | 588 | await _regenerateFromTemplateIfNeeded(); |
Zachary Anderson | e2340c6 | 2019-09-13 14:51:35 -0700 | [diff] [blame] | 589 | if (!_flutterLibRoot.existsSync()) { |
Mikkel Nygaard Ravn | 3c83c52 | 2018-08-07 23:43:15 +0200 | [diff] [blame] | 590 | return; |
Zachary Anderson | e2340c6 | 2019-09-13 14:51:35 -0700 | [diff] [blame] | 591 | } |
Mikkel Nygaard Ravn | a600fe7 | 2018-09-25 21:21:13 +0200 | [diff] [blame] | 592 | await _updateGeneratedXcodeConfigIfNeeded(); |
| 593 | } |
| 594 | |
tauu | 56b7c93 | 2020-04-15 21:13:06 +0200 | [diff] [blame] | 595 | /// Check if one the [targets] of the project is a watchOS companion app target. |
Jenn Magder | 9c87b32 | 2020-05-11 11:56:44 -0700 | [diff] [blame] | 596 | Future<bool> containsWatchCompanion(List<String> targets, BuildInfo buildInfo) async { |
| 597 | final String bundleIdentifier = await productBundleIdentifier(buildInfo); |
tauu | 56b7c93 | 2020-04-15 21:13:06 +0200 | [diff] [blame] | 598 | // A bundle identifier is required for a companion app. |
| 599 | if (bundleIdentifier == null) { |
| 600 | return false; |
| 601 | } |
| 602 | for (final String target in targets) { |
| 603 | // Create Info.plist file of the target. |
| 604 | final File infoFile = hostAppRoot.childDirectory(target).childFile('Info.plist'); |
| 605 | // The Info.plist file of a target contains the key WKCompanionAppBundleIdentifier, |
| 606 | // if it is a watchOS companion app. |
| 607 | if (infoFile.existsSync() && globals.plistParser.getValueFromFile(infoFile.path, 'WKCompanionAppBundleIdentifier') == bundleIdentifier) { |
| 608 | return true; |
| 609 | } |
| 610 | } |
| 611 | return false; |
| 612 | } |
| 613 | |
Mikkel Nygaard Ravn | a600fe7 | 2018-09-25 21:21:13 +0200 | [diff] [blame] | 614 | Future<void> _updateGeneratedXcodeConfigIfNeeded() async { |
Jonah Williams | ee7a37f | 2020-01-06 11:04:20 -0800 | [diff] [blame] | 615 | if (globals.cache.isOlderThanToolsStamp(generatedXcodePropertiesFile)) { |
Mikkel Nygaard Ravn | 3c83c52 | 2018-08-07 23:43:15 +0200 | [diff] [blame] | 616 | await xcode.updateGeneratedXcodeProperties( |
| 617 | project: parent, |
| 618 | buildInfo: BuildInfo.debug, |
| 619 | targetOverride: bundle.defaultMainPath, |
Mikkel Nygaard Ravn | 3c83c52 | 2018-08-07 23:43:15 +0200 | [diff] [blame] | 620 | ); |
| 621 | } |
Sigurd Meldgaard | 1e8ef60 | 2018-06-28 14:35:00 +0200 | [diff] [blame] | 622 | } |
| 623 | |
stuartmorgan | 685e9d1 | 2020-03-23 10:42:26 -0700 | [diff] [blame] | 624 | Future<void> _regenerateFromTemplateIfNeeded() async { |
Zachary Anderson | e2340c6 | 2019-09-13 14:51:35 -0700 | [diff] [blame] | 625 | if (!isModule) { |
Mikkel Nygaard Ravn | 22832d3 | 2018-08-30 16:18:44 +0200 | [diff] [blame] | 626 | return; |
Zachary Anderson | e2340c6 | 2019-09-13 14:51:35 -0700 | [diff] [blame] | 627 | } |
Zachary Anderson | 2c51efe | 2020-01-31 12:51:20 -0800 | [diff] [blame] | 628 | final bool pubspecChanged = globals.fsUtils.isOlderThanReference( |
Zachary Anderson | 2949088 | 2020-01-14 09:40:39 -0800 | [diff] [blame] | 629 | entity: ephemeralDirectory, |
| 630 | referenceFile: parent.pubspecFile, |
| 631 | ); |
Jonah Williams | ee7a37f | 2020-01-06 11:04:20 -0800 | [diff] [blame] | 632 | final bool toolingChanged = globals.cache.isOlderThanToolsStamp(ephemeralDirectory); |
Zachary Anderson | e2340c6 | 2019-09-13 14:51:35 -0700 | [diff] [blame] | 633 | if (!pubspecChanged && !toolingChanged) { |
Mikkel Nygaard Ravn | 22832d3 | 2018-08-30 16:18:44 +0200 | [diff] [blame] | 634 | return; |
Zachary Anderson | e2340c6 | 2019-09-13 14:51:35 -0700 | [diff] [blame] | 635 | } |
Jonah Williams | 523ac7b | 2019-12-09 21:18:09 -0800 | [diff] [blame] | 636 | |
jmagman | 0a58610 | 2019-08-09 17:26:51 -0700 | [diff] [blame] | 637 | _deleteIfExistsSync(ephemeralDirectory); |
stuartmorgan | 685e9d1 | 2020-03-23 10:42:26 -0700 | [diff] [blame] | 638 | await _overwriteFromTemplate( |
Zachary Anderson | 2949088 | 2020-01-14 09:40:39 -0800 | [diff] [blame] | 639 | globals.fs.path.join('module', 'ios', 'library'), |
| 640 | ephemeralDirectory, |
| 641 | ); |
Mikkel Nygaard Ravn | a600fe7 | 2018-09-25 21:21:13 +0200 | [diff] [blame] | 642 | // Add ephemeral host app, if a editable host app does not already exist. |
| 643 | if (!_editableDirectory.existsSync()) { |
stuartmorgan | 685e9d1 | 2020-03-23 10:42:26 -0700 | [diff] [blame] | 644 | await _overwriteFromTemplate( |
Zachary Anderson | 2949088 | 2020-01-14 09:40:39 -0800 | [diff] [blame] | 645 | globals.fs.path.join('module', 'ios', 'host_app_ephemeral'), |
| 646 | ephemeralDirectory, |
| 647 | ); |
Mikkel Nygaard Ravn | a600fe7 | 2018-09-25 21:21:13 +0200 | [diff] [blame] | 648 | if (hasPlugins(parent)) { |
stuartmorgan | 685e9d1 | 2020-03-23 10:42:26 -0700 | [diff] [blame] | 649 | await _overwriteFromTemplate( |
Zachary Anderson | 2949088 | 2020-01-14 09:40:39 -0800 | [diff] [blame] | 650 | globals.fs.path.join('module', 'ios', 'host_app_ephemeral_cocoapods'), |
| 651 | ephemeralDirectory, |
| 652 | ); |
Mikkel Nygaard Ravn | a600fe7 | 2018-09-25 21:21:13 +0200 | [diff] [blame] | 653 | } |
Jenn Magder | a5ee7f7 | 2020-12-01 10:00:16 -0800 | [diff] [blame] | 654 | copyEngineArtifactToProject(BuildMode.debug, EnvironmentType.physical); |
Jenn Magder | 2a7d577 | 2020-01-21 16:38:03 -0800 | [diff] [blame] | 655 | } |
| 656 | } |
| 657 | |
Jenn Magder | a5ee7f7 | 2020-12-01 10:00:16 -0800 | [diff] [blame] | 658 | void copyEngineArtifactToProject(BuildMode mode, EnvironmentType environmentType) { |
Jenn Magder | 9bbc217 | 2020-11-24 10:16:09 -0800 | [diff] [blame] | 659 | // Copy framework from engine cache. The actual build mode |
Jenn Magder | 2a7d577 | 2020-01-21 16:38:03 -0800 | [diff] [blame] | 660 | // doesn't actually matter as it will be overwritten by xcode_backend.sh. |
| 661 | // However, cocoapods will run before that script and requires something |
| 662 | // to be in this location. |
| 663 | final Directory framework = globals.fs.directory( |
| 664 | globals.artifacts.getArtifactPath( |
Jenn Magder | d4150d3 | 2020-12-04 13:57:11 -0800 | [diff] [blame] | 665 | Artifact.flutterXcframework, |
Zachary Anderson | 2949088 | 2020-01-14 09:40:39 -0800 | [diff] [blame] | 666 | platform: TargetPlatform.ios, |
Jenn Magder | 2a7d577 | 2020-01-21 16:38:03 -0800 | [diff] [blame] | 667 | mode: mode, |
Jenn Magder | a5ee7f7 | 2020-12-01 10:00:16 -0800 | [diff] [blame] | 668 | environmentType: environmentType, |
Jonah Williams | 42c9c22 | 2020-11-18 17:05:33 -0800 | [diff] [blame] | 669 | ) |
| 670 | ); |
Jenn Magder | 2a7d577 | 2020-01-21 16:38:03 -0800 | [diff] [blame] | 671 | if (framework.existsSync()) { |
Zachary Anderson | 2c51efe | 2020-01-31 12:51:20 -0800 | [diff] [blame] | 672 | globals.fsUtils.copyDirectorySync( |
Jenn Magder | 2a7d577 | 2020-01-21 16:38:03 -0800 | [diff] [blame] | 673 | framework, |
Jenn Magder | d4150d3 | 2020-12-04 13:57:11 -0800 | [diff] [blame] | 674 | engineCopyDirectory.childDirectory('Flutter.xcframework'), |
Jenn Magder | 2a7d577 | 2020-01-21 16:38:03 -0800 | [diff] [blame] | 675 | ); |
Mikkel Nygaard Ravn | 22832d3 | 2018-08-30 16:18:44 +0200 | [diff] [blame] | 676 | } |
Mikkel Nygaard Ravn | d4e5e1e | 2018-08-16 13:21:55 +0200 | [diff] [blame] | 677 | } |
| 678 | |
stuartmorgan | 3ebebeb | 2019-05-31 13:19:44 -0700 | [diff] [blame] | 679 | @override |
Zachary Anderson | 2949088 | 2020-01-14 09:40:39 -0800 | [diff] [blame] | 680 | File get generatedXcodePropertiesFile => _flutterLibRoot |
| 681 | .childDirectory('Flutter') |
| 682 | .childFile('Generated.xcconfig'); |
Mikkel Nygaard Ravn | 3c83c52 | 2018-08-07 23:43:15 +0200 | [diff] [blame] | 683 | |
Jenn Magder | 91b8234 | 2020-11-03 12:48:25 -0800 | [diff] [blame] | 684 | /// No longer compiled to this location. |
| 685 | /// |
| 686 | /// Used only for "flutter clean" to remove old references. |
| 687 | Directory get deprecatedCompiledDartFramework => _flutterLibRoot |
Jenn Magder | 4fb9ce8 | 2020-02-27 12:18:06 -0800 | [diff] [blame] | 688 | .childDirectory('Flutter') |
| 689 | .childDirectory('App.framework'); |
| 690 | |
Jenn Magder | 2529e35 | 2020-11-17 10:09:29 -0800 | [diff] [blame] | 691 | /// No longer copied to this location. |
| 692 | /// |
| 693 | /// Used only for "flutter clean" to remove old references. |
| 694 | Directory get deprecatedProjectFlutterFramework => _flutterLibRoot |
| 695 | .childDirectory('Flutter') |
| 696 | .childDirectory('Flutter.framework'); |
| 697 | |
Jenn Magder | 617c57b | 2020-12-14 19:13:03 -0800 | [diff] [blame] | 698 | /// Used only for "flutter clean" to remove old references. |
| 699 | File get flutterPodspec => _flutterLibRoot |
| 700 | .childDirectory('Flutter') |
| 701 | .childFile('Flutter.podspec'); |
| 702 | |
Mikkel Nygaard Ravn | 3c83c52 | 2018-08-07 23:43:15 +0200 | [diff] [blame] | 703 | Directory get pluginRegistrantHost { |
Greg Spencer | 0ff9e8a | 2018-10-10 11:01:40 -0700 | [diff] [blame] | 704 | return isModule |
Zachary Anderson | 2949088 | 2020-01-14 09:40:39 -0800 | [diff] [blame] | 705 | ? _flutterLibRoot |
| 706 | .childDirectory('Flutter') |
| 707 | .childDirectory('FlutterPluginRegistrant') |
Jenn Magder | c8c55b4 | 2020-05-04 11:31:08 -0700 | [diff] [blame] | 708 | : hostAppRoot.childDirectory(_hostAppProjectName); |
Mikkel Nygaard Ravn | 22832d3 | 2018-08-30 16:18:44 +0200 | [diff] [blame] | 709 | } |
| 710 | |
Dan Field | be2e7bb | 2020-11-17 22:19:50 -0800 | [diff] [blame] | 711 | File get pluginRegistrantHeader { |
| 712 | final Directory registryDirectory = isModule ? pluginRegistrantHost.childDirectory('Classes') : pluginRegistrantHost; |
| 713 | return registryDirectory.childFile('GeneratedPluginRegistrant.h'); |
| 714 | } |
| 715 | |
| 716 | File get pluginRegistrantImplementation { |
| 717 | final Directory registryDirectory = isModule ? pluginRegistrantHost.childDirectory('Classes') : pluginRegistrantHost; |
| 718 | return registryDirectory.childFile('GeneratedPluginRegistrant.m'); |
| 719 | } |
| 720 | |
Jonah Williams | 42c9c22 | 2020-11-18 17:05:33 -0800 | [diff] [blame] | 721 | Directory get engineCopyDirectory { |
| 722 | return isModule |
| 723 | ? ephemeralDirectory.childDirectory('Flutter').childDirectory('engine') |
| 724 | : hostAppRoot.childDirectory('Flutter'); |
| 725 | } |
| 726 | |
stuartmorgan | 685e9d1 | 2020-03-23 10:42:26 -0700 | [diff] [blame] | 727 | Future<void> _overwriteFromTemplate(String path, Directory target) async { |
Jonah Williams | ee12d7c | 2020-08-21 11:11:04 -0700 | [diff] [blame] | 728 | final Template template = await Template.fromName( |
| 729 | path, |
| 730 | fileSystem: globals.fs, |
| 731 | templateManifest: null, |
| 732 | logger: globals.logger, |
| 733 | templateRenderer: globals.templateRenderer, |
Jonah Williams | ee12d7c | 2020-08-21 11:11:04 -0700 | [diff] [blame] | 734 | ); |
Mikkel Nygaard Ravn | 22832d3 | 2018-08-30 16:18:44 +0200 | [diff] [blame] | 735 | template.render( |
| 736 | target, |
| 737 | <String, dynamic>{ |
Chris Yang | 2e63b7d | 2020-06-23 17:38:03 -0700 | [diff] [blame] | 738 | 'ios': true, |
Mikkel Nygaard Ravn | 22832d3 | 2018-08-30 16:18:44 +0200 | [diff] [blame] | 739 | 'projectName': parent.manifest.appName, |
Alexandre Ardhuin | 387f885 | 2019-03-01 08:17:55 +0100 | [diff] [blame] | 740 | 'iosIdentifier': parent.manifest.iosBundleIdentifier, |
Mikkel Nygaard Ravn | 22832d3 | 2018-08-30 16:18:44 +0200 | [diff] [blame] | 741 | }, |
| 742 | printStatusWhenWriting: false, |
| 743 | overwriteExisting: true, |
| 744 | ); |
Mikkel Nygaard Ravn | 3c83c52 | 2018-08-07 23:43:15 +0200 | [diff] [blame] | 745 | } |
Sigurd Meldgaard | 1e8ef60 | 2018-06-28 14:35:00 +0200 | [diff] [blame] | 746 | } |
| 747 | |
Mikkel Nygaard Ravn | 3c83c52 | 2018-08-07 23:43:15 +0200 | [diff] [blame] | 748 | /// Represents the Android sub-project of a Flutter project. |
| 749 | /// |
| 750 | /// Instances will reflect the contents of the `android/` sub-folder of |
Greg Spencer | 0ff9e8a | 2018-10-10 11:01:40 -0700 | [diff] [blame] | 751 | /// Flutter applications and the `.android/` sub-folder of Flutter module projects. |
Francisco Magdaleno | fcf341e | 2020-01-17 14:43:34 -0800 | [diff] [blame] | 752 | class AndroidProject extends FlutterProjectPlatform { |
Mikkel Nygaard Ravn | 3c83c52 | 2018-08-07 23:43:15 +0200 | [diff] [blame] | 753 | AndroidProject._(this.parent); |
| 754 | |
| 755 | /// The parent of this project. |
| 756 | final FlutterProject parent; |
| 757 | |
Francisco Magdaleno | fcf341e | 2020-01-17 14:43:34 -0800 | [diff] [blame] | 758 | @override |
| 759 | String get pluginConfigKey => AndroidPlugin.kConfigKey; |
| 760 | |
Alexandre Ardhuin | 94c7e82 | 2020-02-05 08:00:32 +0100 | [diff] [blame] | 761 | static final RegExp _applicationIdPattern = RegExp('^\\s*applicationId\\s+[\'"](.*)[\'"]\\s*\$'); |
Alexandre Ardhuin | 5c28e3e | 2020-02-07 06:35:50 +0100 | [diff] [blame] | 762 | static final RegExp _kotlinPluginPattern = RegExp('^\\s*apply plugin\\:\\s+[\'"]kotlin-android[\'"]\\s*\$'); |
Alexandre Ardhuin | 94c7e82 | 2020-02-05 08:00:32 +0100 | [diff] [blame] | 763 | static final RegExp _groupPattern = RegExp('^\\s*group\\s+[\'"](.*)[\'"]\\s*\$'); |
Alexandre Ardhuin | 2ea1d81 | 2018-10-04 07:28:07 +0200 | [diff] [blame] | 764 | |
Mikkel Nygaard Ravn | d4e5e1e | 2018-08-16 13:21:55 +0200 | [diff] [blame] | 765 | /// The Gradle root directory of the Android host app. This is the directory |
| 766 | /// containing the `app/` subdirectory and the `settings.gradle` file that |
| 767 | /// includes it in the overall Gradle project. |
| 768 | Directory get hostAppGradleRoot { |
Zachary Anderson | e2340c6 | 2019-09-13 14:51:35 -0700 | [diff] [blame] | 769 | if (!isModule || _editableHostAppDirectory.existsSync()) { |
matthew-carroll | 18d5b9d | 2018-09-18 17:58:20 -0700 | [diff] [blame] | 770 | return _editableHostAppDirectory; |
Zachary Anderson | e2340c6 | 2019-09-13 14:51:35 -0700 | [diff] [blame] | 771 | } |
jmagman | 0a58610 | 2019-08-09 17:26:51 -0700 | [diff] [blame] | 772 | return ephemeralDirectory; |
Mikkel Nygaard Ravn | d4e5e1e | 2018-08-16 13:21:55 +0200 | [diff] [blame] | 773 | } |
| 774 | |
| 775 | /// The Gradle root directory of the Android wrapping of Flutter and plugins. |
| 776 | /// This is the same as [hostAppGradleRoot] except when the project is |
Greg Spencer | 0ff9e8a | 2018-10-10 11:01:40 -0700 | [diff] [blame] | 777 | /// a Flutter module with an editable host app. |
jmagman | 0a58610 | 2019-08-09 17:26:51 -0700 | [diff] [blame] | 778 | Directory get _flutterLibGradleRoot => isModule ? ephemeralDirectory : _editableHostAppDirectory; |
Mikkel Nygaard Ravn | d4e5e1e | 2018-08-16 13:21:55 +0200 | [diff] [blame] | 779 | |
jmagman | 0a58610 | 2019-08-09 17:26:51 -0700 | [diff] [blame] | 780 | Directory get ephemeralDirectory => parent.directory.childDirectory('.android'); |
matthew-carroll | 18d5b9d | 2018-09-18 17:58:20 -0700 | [diff] [blame] | 781 | Directory get _editableHostAppDirectory => parent.directory.childDirectory('android'); |
Mikkel Nygaard Ravn | 3c83c52 | 2018-08-07 23:43:15 +0200 | [diff] [blame] | 782 | |
Greg Spencer | 0ff9e8a | 2018-10-10 11:01:40 -0700 | [diff] [blame] | 783 | /// True if the parent Flutter project is a module. |
| 784 | bool get isModule => parent.isModule; |
Mikkel Nygaard Ravn | 4353297 | 2018-01-18 09:21:24 +0100 | [diff] [blame] | 785 | |
Nolan Scobie | 43c1b34 | 2020-08-06 19:18:52 -0400 | [diff] [blame] | 786 | /// True if the Flutter project is using the AndroidX support library. |
Josh Burton | d0e45a2 | 2019-06-01 13:33:02 +1200 | [diff] [blame] | 787 | bool get usesAndroidX => parent.usesAndroidX; |
| 788 | |
Emmanuel Garcia | 3bbdf01 | 2019-05-29 20:56:28 -0700 | [diff] [blame] | 789 | /// True, if the app project is using Kotlin. |
| 790 | bool get isKotlin { |
| 791 | final File gradleFile = hostAppGradleRoot.childDirectory('app').childFile('build.gradle'); |
| 792 | return _firstMatchInFile(gradleFile, _kotlinPluginPattern) != null; |
| 793 | } |
| 794 | |
Mikkel Nygaard Ravn | d4e5e1e | 2018-08-16 13:21:55 +0200 | [diff] [blame] | 795 | File get appManifestFile { |
Mikkel Nygaard Ravn | 22832d3 | 2018-08-30 16:18:44 +0200 | [diff] [blame] | 796 | return isUsingGradle |
Jonah Williams | ee7a37f | 2020-01-06 11:04:20 -0800 | [diff] [blame] | 797 | ? globals.fs.file(globals.fs.path.join(hostAppGradleRoot.path, 'app', 'src', 'main', 'AndroidManifest.xml')) |
Mikkel Nygaard Ravn | d4e5e1e | 2018-08-16 13:21:55 +0200 | [diff] [blame] | 798 | : hostAppGradleRoot.childFile('AndroidManifest.xml'); |
Sigurd Meldgaard | 2d3a5c7 | 2018-07-20 08:00:30 +0200 | [diff] [blame] | 799 | } |
Sigurd Meldgaard | 2d3a5c7 | 2018-07-20 08:00:30 +0200 | [diff] [blame] | 800 | |
Mikkel Nygaard Ravn | b280074 | 2018-08-02 14:12:25 +0200 | [diff] [blame] | 801 | File get gradleAppOutV1File => gradleAppOutV1Directory.childFile('app-debug.apk'); |
Sigurd Meldgaard | 2d3a5c7 | 2018-07-20 08:00:30 +0200 | [diff] [blame] | 802 | |
| 803 | Directory get gradleAppOutV1Directory { |
Jonah Williams | ee7a37f | 2020-01-06 11:04:20 -0800 | [diff] [blame] | 804 | return globals.fs.directory(globals.fs.path.join(hostAppGradleRoot.path, 'app', 'build', 'outputs', 'apk')); |
Sigurd Meldgaard | 2d3a5c7 | 2018-07-20 08:00:30 +0200 | [diff] [blame] | 805 | } |
Sigurd Meldgaard | 2d3a5c7 | 2018-07-20 08:00:30 +0200 | [diff] [blame] | 806 | |
Jonah Williams | 6b19184 | 2019-04-25 12:25:12 -0700 | [diff] [blame] | 807 | /// Whether the current flutter project has an Android sub-project. |
Francisco Magdaleno | fcf341e | 2020-01-17 14:43:34 -0800 | [diff] [blame] | 808 | @override |
Jonah Williams | 6b19184 | 2019-04-25 12:25:12 -0700 | [diff] [blame] | 809 | bool existsSync() { |
| 810 | return parent.isModule || _editableHostAppDirectory.existsSync(); |
| 811 | } |
| 812 | |
Mikkel Nygaard Ravn | 22832d3 | 2018-08-30 16:18:44 +0200 | [diff] [blame] | 813 | bool get isUsingGradle { |
Mikkel Nygaard Ravn | d4e5e1e | 2018-08-16 13:21:55 +0200 | [diff] [blame] | 814 | return hostAppGradleRoot.childFile('build.gradle').existsSync(); |
Sigurd Meldgaard | 2d3a5c7 | 2018-07-20 08:00:30 +0200 | [diff] [blame] | 815 | } |
Alexandre Ardhuin | 2701835 | 2018-07-23 08:31:48 +0200 | [diff] [blame] | 816 | |
Mikkel Nygaard Ravn | 22832d3 | 2018-08-30 16:18:44 +0200 | [diff] [blame] | 817 | String get applicationId { |
Mikkel Nygaard Ravn | d4e5e1e | 2018-08-16 13:21:55 +0200 | [diff] [blame] | 818 | final File gradleFile = hostAppGradleRoot.childDirectory('app').childFile('build.gradle'); |
Mikkel Nygaard Ravn | 22832d3 | 2018-08-30 16:18:44 +0200 | [diff] [blame] | 819 | return _firstMatchInFile(gradleFile, _applicationIdPattern)?.group(1); |
Mikkel Nygaard Ravn | 4353297 | 2018-01-18 09:21:24 +0100 | [diff] [blame] | 820 | } |
| 821 | |
Mikkel Nygaard Ravn | 22832d3 | 2018-08-30 16:18:44 +0200 | [diff] [blame] | 822 | String get group { |
Mikkel Nygaard Ravn | d4e5e1e | 2018-08-16 13:21:55 +0200 | [diff] [blame] | 823 | final File gradleFile = hostAppGradleRoot.childFile('build.gradle'); |
Mikkel Nygaard Ravn | 22832d3 | 2018-08-30 16:18:44 +0200 | [diff] [blame] | 824 | return _firstMatchInFile(gradleFile, _groupPattern)?.group(1); |
Mikkel Nygaard Ravn | 4353297 | 2018-01-18 09:21:24 +0100 | [diff] [blame] | 825 | } |
Mikkel Nygaard Ravn | 4353297 | 2018-01-18 09:21:24 +0100 | [diff] [blame] | 826 | |
Emmanuel Garcia | 175b372 | 2019-10-31 13:19:15 -0700 | [diff] [blame] | 827 | /// The build directory where the Android artifacts are placed. |
| 828 | Directory get buildDirectory { |
| 829 | return parent.directory.childDirectory('build'); |
| 830 | } |
| 831 | |
Mikkel Nygaard Ravn | 3c83c52 | 2018-08-07 23:43:15 +0200 | [diff] [blame] | 832 | Future<void> ensureReadyForPlatformSpecificTooling() async { |
xster | e1ae4df | 2020-08-19 22:32:08 -0700 | [diff] [blame] | 833 | if (getEmbeddingVersion() == AndroidEmbeddingVersion.v1) { |
| 834 | globals.printStatus( |
| 835 | """ |
| 836 | ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ |
| 837 | Warning |
| 838 | ────────────────────────────────────────────────────────────────────────────── |
| 839 | Your Flutter application is created using an older version of the Android |
| 840 | embedding. It's being deprecated in favor of Android embedding v2. Follow the |
| 841 | steps at |
| 842 | |
| 843 | https://flutter.dev/go/android-project-migration |
| 844 | |
| 845 | to migrate your project. |
| 846 | ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ |
| 847 | """ |
| 848 | ); |
| 849 | } |
Greg Spencer | 0ff9e8a | 2018-10-10 11:01:40 -0700 | [diff] [blame] | 850 | if (isModule && _shouldRegenerateFromTemplate()) { |
stuartmorgan | 685e9d1 | 2020-03-23 10:42:26 -0700 | [diff] [blame] | 851 | await _regenerateLibrary(); |
matthew-carroll | 18d5b9d | 2018-09-18 17:58:20 -0700 | [diff] [blame] | 852 | // Add ephemeral host app, if an editable host app does not already exist. |
| 853 | if (!_editableHostAppDirectory.existsSync()) { |
stuartmorgan | 685e9d1 | 2020-03-23 10:42:26 -0700 | [diff] [blame] | 854 | await _overwriteFromTemplate(globals.fs.path.join('module', 'android', 'host_app_common'), ephemeralDirectory); |
| 855 | await _overwriteFromTemplate(globals.fs.path.join('module', 'android', 'host_app_ephemeral'), ephemeralDirectory); |
Mikkel Nygaard Ravn | d4e5e1e | 2018-08-16 13:21:55 +0200 | [diff] [blame] | 856 | } |
Mikkel Nygaard Ravn | d89a6b5 | 2018-06-22 18:19:37 +0200 | [diff] [blame] | 857 | } |
Mikkel Nygaard Ravn | d4e5e1e | 2018-08-16 13:21:55 +0200 | [diff] [blame] | 858 | if (!hostAppGradleRoot.existsSync()) { |
Mikkel Nygaard Ravn | 3c83c52 | 2018-08-07 23:43:15 +0200 | [diff] [blame] | 859 | return; |
Mikkel Nygaard Ravn | d4e5e1e | 2018-08-16 13:21:55 +0200 | [diff] [blame] | 860 | } |
| 861 | gradle.updateLocalProperties(project: parent, requireAndroidSdk: false); |
Mikkel Nygaard Ravn | d89a6b5 | 2018-06-22 18:19:37 +0200 | [diff] [blame] | 862 | } |
| 863 | |
Mikkel Nygaard Ravn | 3c83c52 | 2018-08-07 23:43:15 +0200 | [diff] [blame] | 864 | bool _shouldRegenerateFromTemplate() { |
Zachary Anderson | 2c51efe | 2020-01-31 12:51:20 -0800 | [diff] [blame] | 865 | return globals.fsUtils.isOlderThanReference( |
Zachary Anderson | 2949088 | 2020-01-14 09:40:39 -0800 | [diff] [blame] | 866 | entity: ephemeralDirectory, |
| 867 | referenceFile: parent.pubspecFile, |
| 868 | ) || globals.cache.isOlderThanToolsStamp(ephemeralDirectory); |
Mikkel Nygaard Ravn | d89a6b5 | 2018-06-22 18:19:37 +0200 | [diff] [blame] | 869 | } |
Mikkel Nygaard Ravn | 3c83c52 | 2018-08-07 23:43:15 +0200 | [diff] [blame] | 870 | |
Mikkel Nygaard Ravn | d4e5e1e | 2018-08-16 13:21:55 +0200 | [diff] [blame] | 871 | File get localPropertiesFile => _flutterLibGradleRoot.childFile('local.properties'); |
| 872 | |
Greg Spencer | 0ff9e8a | 2018-10-10 11:01:40 -0700 | [diff] [blame] | 873 | Directory get pluginRegistrantHost => _flutterLibGradleRoot.childDirectory(isModule ? 'Flutter' : 'app'); |
Mikkel Nygaard Ravn | d4e5e1e | 2018-08-16 13:21:55 +0200 | [diff] [blame] | 874 | |
stuartmorgan | 685e9d1 | 2020-03-23 10:42:26 -0700 | [diff] [blame] | 875 | Future<void> _regenerateLibrary() async { |
jmagman | 0a58610 | 2019-08-09 17:26:51 -0700 | [diff] [blame] | 876 | _deleteIfExistsSync(ephemeralDirectory); |
stuartmorgan | 685e9d1 | 2020-03-23 10:42:26 -0700 | [diff] [blame] | 877 | await _overwriteFromTemplate(globals.fs.path.join( |
Emmanuel Garcia | 5961bcc | 2019-10-04 06:23:03 -0700 | [diff] [blame] | 878 | 'module', |
| 879 | 'android', |
xster | 8bd2e65 | 2020-07-11 12:15:19 -0700 | [diff] [blame] | 880 | 'library_new_embedding', |
Emmanuel Garcia | 5961bcc | 2019-10-04 06:23:03 -0700 | [diff] [blame] | 881 | ), ephemeralDirectory); |
stuartmorgan | 685e9d1 | 2020-03-23 10:42:26 -0700 | [diff] [blame] | 882 | await _overwriteFromTemplate(globals.fs.path.join('module', 'android', 'gradle'), ephemeralDirectory); |
Emmanuel Garcia | 354f80b | 2019-12-11 16:36:03 -0800 | [diff] [blame] | 883 | gradle.gradleUtils.injectGradleWrapperIfNeeded(ephemeralDirectory); |
Mikkel Nygaard Ravn | d4e5e1e | 2018-08-16 13:21:55 +0200 | [diff] [blame] | 884 | } |
| 885 | |
stuartmorgan | 685e9d1 | 2020-03-23 10:42:26 -0700 | [diff] [blame] | 886 | Future<void> _overwriteFromTemplate(String path, Directory target) async { |
Jonah Williams | ee12d7c | 2020-08-21 11:11:04 -0700 | [diff] [blame] | 887 | final Template template = await Template.fromName( |
| 888 | path, |
| 889 | fileSystem: globals.fs, |
| 890 | templateManifest: null, |
| 891 | logger: globals.logger, |
| 892 | templateRenderer: globals.templateRenderer, |
Jonah Williams | ee12d7c | 2020-08-21 11:11:04 -0700 | [diff] [blame] | 893 | ); |
Mikkel Nygaard Ravn | d4e5e1e | 2018-08-16 13:21:55 +0200 | [diff] [blame] | 894 | template.render( |
| 895 | target, |
| 896 | <String, dynamic>{ |
Chris Yang | 2e63b7d | 2020-06-23 17:38:03 -0700 | [diff] [blame] | 897 | 'android': true, |
Mikkel Nygaard Ravn | d4e5e1e | 2018-08-16 13:21:55 +0200 | [diff] [blame] | 898 | 'projectName': parent.manifest.appName, |
| 899 | 'androidIdentifier': parent.manifest.androidPackage, |
Josh Burton | d0e45a2 | 2019-06-01 13:33:02 +1200 | [diff] [blame] | 900 | 'androidX': usesAndroidX, |
Mikkel Nygaard Ravn | d4e5e1e | 2018-08-16 13:21:55 +0200 | [diff] [blame] | 901 | }, |
| 902 | printStatusWhenWriting: false, |
| 903 | overwriteExisting: true, |
| 904 | ); |
| 905 | } |
xster | ba26f92 | 2019-11-05 12:38:42 -0800 | [diff] [blame] | 906 | |
| 907 | AndroidEmbeddingVersion getEmbeddingVersion() { |
xster | 9e0df25 | 2019-11-11 15:56:43 -0800 | [diff] [blame] | 908 | if (isModule) { |
| 909 | // A module type's Android project is used in add-to-app scenarios and |
| 910 | // only supports the V2 embedding. |
| 911 | return AndroidEmbeddingVersion.v2; |
| 912 | } |
xster | ba26f92 | 2019-11-05 12:38:42 -0800 | [diff] [blame] | 913 | if (appManifestFile == null || !appManifestFile.existsSync()) { |
| 914 | return AndroidEmbeddingVersion.v1; |
| 915 | } |
Dan Field | 33aa457 | 2020-07-07 16:40:33 -0700 | [diff] [blame] | 916 | XmlDocument document; |
xster | ba26f92 | 2019-11-05 12:38:42 -0800 | [diff] [blame] | 917 | try { |
Dan Field | 33aa457 | 2020-07-07 16:40:33 -0700 | [diff] [blame] | 918 | document = XmlDocument.parse(appManifestFile.readAsStringSync()); |
| 919 | } on XmlParserException { |
xster | ba26f92 | 2019-11-05 12:38:42 -0800 | [diff] [blame] | 920 | throwToolExit('Error parsing $appManifestFile ' |
| 921 | 'Please ensure that the android manifest is a valid XML document and try again.'); |
| 922 | } on FileSystemException { |
| 923 | throwToolExit('Error reading $appManifestFile even though it exists. ' |
| 924 | 'Please ensure that you have read permission to this file and try again.'); |
| 925 | } |
Dan Field | 33aa457 | 2020-07-07 16:40:33 -0700 | [diff] [blame] | 926 | for (final XmlElement metaData in document.findAllElements('meta-data')) { |
xster | ba26f92 | 2019-11-05 12:38:42 -0800 | [diff] [blame] | 927 | final String name = metaData.getAttribute('android:name'); |
| 928 | if (name == 'flutterEmbedding') { |
| 929 | final String embeddingVersionString = metaData.getAttribute('android:value'); |
| 930 | if (embeddingVersionString == '1') { |
| 931 | return AndroidEmbeddingVersion.v1; |
| 932 | } |
| 933 | if (embeddingVersionString == '2') { |
| 934 | return AndroidEmbeddingVersion.v2; |
| 935 | } |
| 936 | } |
| 937 | } |
| 938 | return AndroidEmbeddingVersion.v1; |
| 939 | } |
| 940 | } |
| 941 | |
| 942 | /// Iteration of the embedding Java API in the engine used by the Android project. |
| 943 | enum AndroidEmbeddingVersion { |
| 944 | /// V1 APIs based on io.flutter.app.FlutterActivity. |
| 945 | v1, |
| 946 | /// V2 APIs based on io.flutter.embedding.android.FlutterActivity. |
| 947 | v2, |
Mikkel Nygaard Ravn | d89a6b5 | 2018-06-22 18:19:37 +0200 | [diff] [blame] | 948 | } |
| 949 | |
Jonah Williams | 6f5f037 | 2019-02-26 16:58:24 -0800 | [diff] [blame] | 950 | /// Represents the web sub-project of a Flutter project. |
Francisco Magdaleno | fcf341e | 2020-01-17 14:43:34 -0800 | [diff] [blame] | 951 | class WebProject extends FlutterProjectPlatform { |
Jonah Williams | 6f5f037 | 2019-02-26 16:58:24 -0800 | [diff] [blame] | 952 | WebProject._(this.parent); |
| 953 | |
| 954 | final FlutterProject parent; |
| 955 | |
Francisco Magdaleno | fcf341e | 2020-01-17 14:43:34 -0800 | [diff] [blame] | 956 | @override |
| 957 | String get pluginConfigKey => WebPlugin.kConfigKey; |
| 958 | |
Jonah Williams | 6b19184 | 2019-04-25 12:25:12 -0700 | [diff] [blame] | 959 | /// Whether this flutter project has a web sub-project. |
Francisco Magdaleno | fcf341e | 2020-01-17 14:43:34 -0800 | [diff] [blame] | 960 | @override |
Jonah Williams | 6b19184 | 2019-04-25 12:25:12 -0700 | [diff] [blame] | 961 | bool existsSync() { |
Jonah Williams | f530b80 | 2019-06-10 15:37:23 -0700 | [diff] [blame] | 962 | return parent.directory.childDirectory('web').existsSync() |
| 963 | && indexFile.existsSync(); |
Jonah Williams | 6b19184 | 2019-04-25 12:25:12 -0700 | [diff] [blame] | 964 | } |
Jonah Williams | a476a08 | 2019-04-22 15:18:15 -0700 | [diff] [blame] | 965 | |
Harry Terkelsen | d33cf11 | 2019-09-03 10:37:34 -0700 | [diff] [blame] | 966 | /// The 'lib' directory for the application. |
| 967 | Directory get libDirectory => parent.directory.childDirectory('lib'); |
| 968 | |
Jonah Williams | 0980fa2 | 2019-09-26 13:24:05 -0700 | [diff] [blame] | 969 | /// The directory containing additional files for the application. |
| 970 | Directory get directory => parent.directory.childDirectory('web'); |
| 971 | |
Jonah Williams | c91b657 | 2019-06-06 21:05:55 -0700 | [diff] [blame] | 972 | /// The html file used to host the flutter web application. |
Jonah Williams | f530b80 | 2019-06-10 15:37:23 -0700 | [diff] [blame] | 973 | File get indexFile => parent.directory |
| 974 | .childDirectory('web') |
| 975 | .childFile('index.html'); |
Jonah Williams | c91b657 | 2019-06-06 21:05:55 -0700 | [diff] [blame] | 976 | |
Jonah Williams | bd413bf | 2019-06-07 13:15:38 -0700 | [diff] [blame] | 977 | Future<void> ensureReadyForPlatformSpecificTooling() async {} |
Jonah Williams | 6f5f037 | 2019-02-26 16:58:24 -0800 | [diff] [blame] | 978 | } |
| 979 | |
Mikkel Nygaard Ravn | 22832d3 | 2018-08-30 16:18:44 +0200 | [diff] [blame] | 980 | /// Deletes [directory] with all content. |
| 981 | void _deleteIfExistsSync(Directory directory) { |
Zachary Anderson | e2340c6 | 2019-09-13 14:51:35 -0700 | [diff] [blame] | 982 | if (directory.existsSync()) { |
Mikkel Nygaard Ravn | 22832d3 | 2018-08-30 16:18:44 +0200 | [diff] [blame] | 983 | directory.deleteSync(recursive: true); |
Zachary Anderson | e2340c6 | 2019-09-13 14:51:35 -0700 | [diff] [blame] | 984 | } |
Mikkel Nygaard Ravn | 22832d3 | 2018-08-30 16:18:44 +0200 | [diff] [blame] | 985 | } |
| 986 | |
| 987 | |
| 988 | /// Returns the first line-based match for [regExp] in [file]. |
Mikkel Nygaard Ravn | 4353297 | 2018-01-18 09:21:24 +0100 | [diff] [blame] | 989 | /// |
| 990 | /// Assumes UTF8 encoding. |
Mikkel Nygaard Ravn | 22832d3 | 2018-08-30 16:18:44 +0200 | [diff] [blame] | 991 | Match _firstMatchInFile(File file, RegExp regExp) { |
| 992 | if (!file.existsSync()) { |
Mikkel Nygaard Ravn | 4353297 | 2018-01-18 09:21:24 +0100 | [diff] [blame] | 993 | return null; |
| 994 | } |
Alexandre Ardhuin | 4f9b6cf | 2020-01-07 16:32:04 +0100 | [diff] [blame] | 995 | for (final String line in file.readAsLinesSync()) { |
Mikkel Nygaard Ravn | 22832d3 | 2018-08-30 16:18:44 +0200 | [diff] [blame] | 996 | final Match match = regExp.firstMatch(line); |
| 997 | if (match != null) { |
| 998 | return match; |
| 999 | } |
| 1000 | } |
| 1001 | return null; |
Mikkel Nygaard Ravn | 4353297 | 2018-01-18 09:21:24 +0100 | [diff] [blame] | 1002 | } |
Jonah Williams | 86c938b | 2019-04-17 12:16:55 -0700 | [diff] [blame] | 1003 | |
| 1004 | /// The macOS sub project. |
Francisco Magdaleno | fcf341e | 2020-01-17 14:43:34 -0800 | [diff] [blame] | 1005 | class MacOSProject extends FlutterProjectPlatform implements XcodeBasedProject { |
stuartmorgan | 3ebebeb | 2019-05-31 13:19:44 -0700 | [diff] [blame] | 1006 | MacOSProject._(this.parent); |
Jonah Williams | 86c938b | 2019-04-17 12:16:55 -0700 | [diff] [blame] | 1007 | |
stuartmorgan | 3ebebeb | 2019-05-31 13:19:44 -0700 | [diff] [blame] | 1008 | @override |
| 1009 | final FlutterProject parent; |
Jonah Williams | 86c938b | 2019-04-17 12:16:55 -0700 | [diff] [blame] | 1010 | |
Francisco Magdaleno | fcf341e | 2020-01-17 14:43:34 -0800 | [diff] [blame] | 1011 | @override |
| 1012 | String get pluginConfigKey => MacOSPlugin.kConfigKey; |
| 1013 | |
Jenn Magder | c8c55b4 | 2020-05-04 11:31:08 -0700 | [diff] [blame] | 1014 | static const String _hostAppProjectName = 'Runner'; |
stuartmorgan | ef9866b | 2019-05-22 19:58:29 -0400 | [diff] [blame] | 1015 | |
stuartmorgan | 3ebebeb | 2019-05-31 13:19:44 -0700 | [diff] [blame] | 1016 | @override |
stuartmorgan | 932200c | 2019-05-30 13:43:31 -0700 | [diff] [blame] | 1017 | bool existsSync() => _macOSDirectory.existsSync(); |
Jonah Williams | 86c938b | 2019-04-17 12:16:55 -0700 | [diff] [blame] | 1018 | |
stuartmorgan | 3ebebeb | 2019-05-31 13:19:44 -0700 | [diff] [blame] | 1019 | Directory get _macOSDirectory => parent.directory.childDirectory('macos'); |
Jonah Williams | 7f959d8 | 2019-04-22 13:51:00 -0700 | [diff] [blame] | 1020 | |
stuartmorgan | 932200c | 2019-05-30 13:43:31 -0700 | [diff] [blame] | 1021 | /// The directory in the project that is managed by Flutter. As much as |
| 1022 | /// possible, files that are edited by Flutter tooling after initial project |
| 1023 | /// creation should live here. |
| 1024 | Directory get managedDirectory => _macOSDirectory.childDirectory('Flutter'); |
| 1025 | |
| 1026 | /// The subdirectory of [managedDirectory] that contains files that are |
| 1027 | /// generated on the fly. All generated files that are not intended to be |
| 1028 | /// checked in should live here. |
| 1029 | Directory get ephemeralDirectory => managedDirectory.childDirectory('ephemeral'); |
stuartmorgan | f8b07e2 | 2019-05-16 11:00:05 -0400 | [diff] [blame] | 1030 | |
Jonah Williams | 2ab4699 | 2019-07-31 16:19:22 -0700 | [diff] [blame] | 1031 | /// The xcfilelist used to track the inputs for the Flutter script phase in |
| 1032 | /// the Xcode build. |
| 1033 | File get inputFileList => ephemeralDirectory.childFile('FlutterInputs.xcfilelist'); |
| 1034 | |
| 1035 | /// The xcfilelist used to track the outputs for the Flutter script phase in |
| 1036 | /// the Xcode build. |
| 1037 | File get outputFileList => ephemeralDirectory.childFile('FlutterOutputs.xcfilelist'); |
| 1038 | |
stuartmorgan | 3ebebeb | 2019-05-31 13:19:44 -0700 | [diff] [blame] | 1039 | @override |
stuartmorgan | 932200c | 2019-05-30 13:43:31 -0700 | [diff] [blame] | 1040 | File get generatedXcodePropertiesFile => ephemeralDirectory.childFile('Flutter-Generated.xcconfig'); |
| 1041 | |
stuartmorgan | 3ebebeb | 2019-05-31 13:19:44 -0700 | [diff] [blame] | 1042 | @override |
stuartmorgan | 932200c | 2019-05-30 13:43:31 -0700 | [diff] [blame] | 1043 | File xcodeConfigFor(String mode) => managedDirectory.childFile('Flutter-$mode.xcconfig'); |
Jonah Williams | 7f959d8 | 2019-04-22 13:51:00 -0700 | [diff] [blame] | 1044 | |
stuartmorgan | 3ebebeb | 2019-05-31 13:19:44 -0700 | [diff] [blame] | 1045 | @override |
stuartmorgan | 018d45d | 2019-08-02 16:44:04 -0700 | [diff] [blame] | 1046 | File get generatedEnvironmentVariableExportScript => ephemeralDirectory.childFile('flutter_export_environment.sh'); |
Jenn Magder | bd47a31 | 2019-07-30 09:58:39 -0700 | [diff] [blame] | 1047 | |
| 1048 | @override |
stuartmorgan | 3ebebeb | 2019-05-31 13:19:44 -0700 | [diff] [blame] | 1049 | File get podfile => _macOSDirectory.childFile('Podfile'); |
| 1050 | |
| 1051 | @override |
| 1052 | File get podfileLock => _macOSDirectory.childFile('Podfile.lock'); |
| 1053 | |
| 1054 | @override |
| 1055 | File get podManifestLock => _macOSDirectory.childDirectory('Pods').childFile('Manifest.lock'); |
| 1056 | |
| 1057 | @override |
Jenn Magder | c8c55b4 | 2020-05-04 11:31:08 -0700 | [diff] [blame] | 1058 | Directory get xcodeProject => _macOSDirectory.childDirectory('$_hostAppProjectName.xcodeproj'); |
stuartmorgan | ef9866b | 2019-05-22 19:58:29 -0400 | [diff] [blame] | 1059 | |
stuartmorgan | 3ebebeb | 2019-05-31 13:19:44 -0700 | [diff] [blame] | 1060 | @override |
| 1061 | File get xcodeProjectInfoFile => xcodeProject.childFile('project.pbxproj'); |
| 1062 | |
| 1063 | @override |
Jenn Magder | c8c55b4 | 2020-05-04 11:31:08 -0700 | [diff] [blame] | 1064 | Directory get xcodeWorkspace => _macOSDirectory.childDirectory('$_hostAppProjectName.xcworkspace'); |
Jonah Williams | da92fc1 | 2019-04-17 22:40:26 -0700 | [diff] [blame] | 1065 | |
stuartmorgan | f8b07e2 | 2019-05-16 11:00:05 -0400 | [diff] [blame] | 1066 | /// The file where the Xcode build will write the name of the built app. |
| 1067 | /// |
Chris Bracken | 4d99232 | 2019-05-24 19:12:45 -0700 | [diff] [blame] | 1068 | /// Ideally this will be replaced in the future with inspection of the Runner |
stuartmorgan | f8b07e2 | 2019-05-16 11:00:05 -0400 | [diff] [blame] | 1069 | /// scheme's target. |
stuartmorgan | 932200c | 2019-05-30 13:43:31 -0700 | [diff] [blame] | 1070 | File get nameFile => ephemeralDirectory.childFile('.app_filename'); |
stuartmorgan | 3ebebeb | 2019-05-31 13:19:44 -0700 | [diff] [blame] | 1071 | |
| 1072 | Future<void> ensureReadyForPlatformSpecificTooling() async { |
| 1073 | // TODO(stuartmorgan): Add create-from-template logic here. |
| 1074 | await _updateGeneratedXcodeConfigIfNeeded(); |
| 1075 | } |
| 1076 | |
| 1077 | Future<void> _updateGeneratedXcodeConfigIfNeeded() async { |
Jonah Williams | ee7a37f | 2020-01-06 11:04:20 -0800 | [diff] [blame] | 1078 | if (globals.cache.isOlderThanToolsStamp(generatedXcodePropertiesFile)) { |
stuartmorgan | 3ebebeb | 2019-05-31 13:19:44 -0700 | [diff] [blame] | 1079 | await xcode.updateGeneratedXcodeProperties( |
| 1080 | project: parent, |
| 1081 | buildInfo: BuildInfo.debug, |
| 1082 | useMacOSConfig: true, |
| 1083 | setSymroot: false, |
| 1084 | ); |
| 1085 | } |
| 1086 | } |
Jonah Williams | 86c938b | 2019-04-17 12:16:55 -0700 | [diff] [blame] | 1087 | } |
| 1088 | |
Nolan Scobie | 43c1b34 | 2020-08-06 19:18:52 -0400 | [diff] [blame] | 1089 | /// The Windows sub project. |
stuartmorgan | 4b12050 | 2020-07-06 12:59:16 -0700 | [diff] [blame] | 1090 | class WindowsProject extends FlutterProjectPlatform implements CmakeBasedProject { |
| 1091 | WindowsProject._(this.parent); |
Jonah Williams | 86c938b | 2019-04-17 12:16:55 -0700 | [diff] [blame] | 1092 | |
stuartmorgan | 4b12050 | 2020-07-06 12:59:16 -0700 | [diff] [blame] | 1093 | @override |
| 1094 | final FlutterProject parent; |
Jonah Williams | 86c938b | 2019-04-17 12:16:55 -0700 | [diff] [blame] | 1095 | |
Francisco Magdaleno | fcf341e | 2020-01-17 14:43:34 -0800 | [diff] [blame] | 1096 | @override |
| 1097 | String get pluginConfigKey => WindowsPlugin.kConfigKey; |
| 1098 | |
| 1099 | @override |
stuartmorgan | 4b12050 | 2020-07-06 12:59:16 -0700 | [diff] [blame] | 1100 | bool existsSync() => _editableDirectory.existsSync() && cmakeFile.existsSync(); |
Jonah Williams | 86c938b | 2019-04-17 12:16:55 -0700 | [diff] [blame] | 1101 | |
stuartmorgan | 4b12050 | 2020-07-06 12:59:16 -0700 | [diff] [blame] | 1102 | @override |
| 1103 | File get cmakeFile => _editableDirectory.childFile('CMakeLists.txt'); |
| 1104 | |
| 1105 | @override |
J-P Nurmi | f2536da | 2020-12-22 01:04:04 +0100 | [diff] [blame] | 1106 | File get managedCmakeFile => managedDirectory.childFile('CMakeLists.txt'); |
| 1107 | |
| 1108 | @override |
stuartmorgan | 4b12050 | 2020-07-06 12:59:16 -0700 | [diff] [blame] | 1109 | File get generatedCmakeConfigFile => ephemeralDirectory.childFile('generated_config.cmake'); |
| 1110 | |
| 1111 | @override |
| 1112 | File get generatedPluginCmakeFile => managedDirectory.childFile('generated_plugins.cmake'); |
| 1113 | |
stuartmorgan | 6d1c244 | 2020-09-09 19:08:19 -0400 | [diff] [blame] | 1114 | @override |
| 1115 | Directory get pluginSymlinkDirectory => ephemeralDirectory.childDirectory('.plugin_symlinks'); |
| 1116 | |
stuartmorgan | 4b12050 | 2020-07-06 12:59:16 -0700 | [diff] [blame] | 1117 | Directory get _editableDirectory => parent.directory.childDirectory('windows'); |
stuartmorgan | 6722fb4 | 2019-05-14 19:24:40 -0400 | [diff] [blame] | 1118 | |
stuartmorgan | e6ae95c | 2019-09-11 07:46:57 -0700 | [diff] [blame] | 1119 | /// The directory in the project that is managed by Flutter. As much as |
| 1120 | /// possible, files that are edited by Flutter tooling after initial project |
| 1121 | /// creation should live here. |
| 1122 | Directory get managedDirectory => _editableDirectory.childDirectory('flutter'); |
| 1123 | |
| 1124 | /// The subdirectory of [managedDirectory] that contains files that are |
| 1125 | /// generated on the fly. All generated files that are not intended to be |
| 1126 | /// checked in should live here. |
| 1127 | Directory get ephemeralDirectory => managedDirectory.childDirectory('ephemeral'); |
stuartmorgan | 4e1bfca | 2019-05-15 19:32:47 -0400 | [diff] [blame] | 1128 | |
stuartmorgan | 56d68a9 | 2019-09-26 14:01:57 -0700 | [diff] [blame] | 1129 | Future<void> ensureReadyForPlatformSpecificTooling() async {} |
Jonah Williams | 86c938b | 2019-04-17 12:16:55 -0700 | [diff] [blame] | 1130 | } |
| 1131 | |
| 1132 | /// The Linux sub project. |
stuartmorgan | 4b12050 | 2020-07-06 12:59:16 -0700 | [diff] [blame] | 1133 | class LinuxProject extends FlutterProjectPlatform implements CmakeBasedProject { |
| 1134 | LinuxProject._(this.parent); |
Jonah Williams | 86c938b | 2019-04-17 12:16:55 -0700 | [diff] [blame] | 1135 | |
stuartmorgan | 4b12050 | 2020-07-06 12:59:16 -0700 | [diff] [blame] | 1136 | @override |
| 1137 | final FlutterProject parent; |
Jonah Williams | 86c938b | 2019-04-17 12:16:55 -0700 | [diff] [blame] | 1138 | |
Francisco Magdaleno | fcf341e | 2020-01-17 14:43:34 -0800 | [diff] [blame] | 1139 | @override |
| 1140 | String get pluginConfigKey => LinuxPlugin.kConfigKey; |
| 1141 | |
Robert Ancell | 392e25c | 2020-07-30 08:46:29 +1200 | [diff] [blame] | 1142 | static final RegExp _applicationIdPattern = RegExp(r'''^\s*set\s*\(\s*APPLICATION_ID\s*"(.*)"\s*\)\s*$'''); |
| 1143 | |
stuartmorgan | 4b12050 | 2020-07-06 12:59:16 -0700 | [diff] [blame] | 1144 | Directory get _editableDirectory => parent.directory.childDirectory('linux'); |
Jonah Williams | 86c938b | 2019-04-17 12:16:55 -0700 | [diff] [blame] | 1145 | |
stuartmorgan | d03aeca | 2019-09-16 16:04:55 -0700 | [diff] [blame] | 1146 | /// The directory in the project that is managed by Flutter. As much as |
| 1147 | /// possible, files that are edited by Flutter tooling after initial project |
| 1148 | /// creation should live here. |
| 1149 | Directory get managedDirectory => _editableDirectory.childDirectory('flutter'); |
Jonah Williams | 8b0243f | 2019-05-11 00:08:29 -0700 | [diff] [blame] | 1150 | |
stuartmorgan | d03aeca | 2019-09-16 16:04:55 -0700 | [diff] [blame] | 1151 | /// The subdirectory of [managedDirectory] that contains files that are |
| 1152 | /// generated on the fly. All generated files that are not intended to be |
| 1153 | /// checked in should live here. |
| 1154 | Directory get ephemeralDirectory => managedDirectory.childDirectory('ephemeral'); |
| 1155 | |
Francisco Magdaleno | fcf341e | 2020-01-17 14:43:34 -0800 | [diff] [blame] | 1156 | @override |
stuartmorgan | d03aeca | 2019-09-16 16:04:55 -0700 | [diff] [blame] | 1157 | bool existsSync() => _editableDirectory.existsSync(); |
Jonah Williams | da92fc1 | 2019-04-17 22:40:26 -0700 | [diff] [blame] | 1158 | |
stuartmorgan | 4b12050 | 2020-07-06 12:59:16 -0700 | [diff] [blame] | 1159 | @override |
stuartmorgan | 8abf0a6 | 2020-05-16 15:07:34 -0700 | [diff] [blame] | 1160 | File get cmakeFile => _editableDirectory.childFile('CMakeLists.txt'); |
stuartmorgan | d03aeca | 2019-09-16 16:04:55 -0700 | [diff] [blame] | 1161 | |
stuartmorgan | 4b12050 | 2020-07-06 12:59:16 -0700 | [diff] [blame] | 1162 | @override |
J-P Nurmi | f2536da | 2020-12-22 01:04:04 +0100 | [diff] [blame] | 1163 | File get managedCmakeFile => managedDirectory.childFile('CMakeLists.txt'); |
| 1164 | |
| 1165 | @override |
stuartmorgan | 8abf0a6 | 2020-05-16 15:07:34 -0700 | [diff] [blame] | 1166 | File get generatedCmakeConfigFile => ephemeralDirectory.childFile('generated_config.cmake'); |
stuartmorgan | 56d68a9 | 2019-09-26 14:01:57 -0700 | [diff] [blame] | 1167 | |
stuartmorgan | 4b12050 | 2020-07-06 12:59:16 -0700 | [diff] [blame] | 1168 | @override |
stuartmorgan | 8abf0a6 | 2020-05-16 15:07:34 -0700 | [diff] [blame] | 1169 | File get generatedPluginCmakeFile => managedDirectory.childFile('generated_plugins.cmake'); |
stuartmorgan | dd2756c | 2020-02-27 09:45:22 -0800 | [diff] [blame] | 1170 | |
stuartmorgan | 6d1c244 | 2020-09-09 19:08:19 -0400 | [diff] [blame] | 1171 | @override |
stuartmorgan | 7bdd475 | 2020-02-12 16:23:27 -0800 | [diff] [blame] | 1172 | Directory get pluginSymlinkDirectory => ephemeralDirectory.childDirectory('.plugin_symlinks'); |
| 1173 | |
stuartmorgan | 56d68a9 | 2019-09-26 14:01:57 -0700 | [diff] [blame] | 1174 | Future<void> ensureReadyForPlatformSpecificTooling() async {} |
Robert Ancell | 392e25c | 2020-07-30 08:46:29 +1200 | [diff] [blame] | 1175 | |
| 1176 | String get applicationId { |
| 1177 | return _firstMatchInFile(cmakeFile, _applicationIdPattern)?.group(1); |
| 1178 | } |
Jonah Williams | 6b19184 | 2019-04-25 12:25:12 -0700 | [diff] [blame] | 1179 | } |
Zachary Anderson | 8841afe | 2019-05-14 10:59:23 -0700 | [diff] [blame] | 1180 | |
Nolan Scobie | 43c1b34 | 2020-08-06 19:18:52 -0400 | [diff] [blame] | 1181 | /// The Fuchsia sub project. |
Zachary Anderson | 8841afe | 2019-05-14 10:59:23 -0700 | [diff] [blame] | 1182 | class FuchsiaProject { |
| 1183 | FuchsiaProject._(this.project); |
| 1184 | |
| 1185 | final FlutterProject project; |
| 1186 | |
| 1187 | Directory _editableHostAppDirectory; |
| 1188 | Directory get editableHostAppDirectory => |
| 1189 | _editableHostAppDirectory ??= project.directory.childDirectory('fuchsia'); |
| 1190 | |
| 1191 | bool existsSync() => editableHostAppDirectory.existsSync(); |
| 1192 | |
| 1193 | Directory _meta; |
| 1194 | Directory get meta => |
| 1195 | _meta ??= editableHostAppDirectory.childDirectory('meta'); |
| 1196 | } |