diff --git a/.ci/legacy_project/README.md b/.ci/legacy_project/README.md
new file mode 100644
index 00000000000..c10ace0b2c2
--- /dev/null
+++ b/.ci/legacy_project/README.md
@@ -0,0 +1,37 @@
+This directory contains a partial snapshot of an old Flutter project; it is
+intended to replace the corresponding parts of a newly Flutter-created project
+to allow testing plugin builds with a legacy project.
+
+It was originally created with Flutter 2.0.6. In general the guidelines are:
+- Pieces here should be largely self-contained rather than portions of
+ major project components; for instance, it currently contains the entire
+ `android/` directory from a legacy project, rather than a subset of it
+ which would be combined with a subset of a new project's `android/`
+ directory. This is to avoid random breakage in the future due to
+ conflicts between those subsets. For instance, we could probably get
+ away with not including android/app/src/main/res for a while, and
+ instead layer in the versions from a new project, but then someday
+ if the resources were renamed, there would be dangling references to
+ the old resources in files that are included here.
+- Updates over time should be minimal. We don't expect that an unchanged
+ project will keep working forever, but this directory should simulate
+ a developer who has done the bare minimum to keep their project working
+ as they have updated Flutter.
+- Updates should be logged below.
+
+The reason for the hybrid model, rather than checking in a full legacy
+project, is to minimize unnecessary maintenance work. E.g., there's no
+need to manually keep Dart code updated for Flutter changes just to
+test legacy native Android build behaviors.
+
+## Manual changes to files
+
+The following are the changes relative to running:
+
+```bash
+flutter create -a java all_packages
+```
+
+and then deleting everything but `android/` from it:
+
+- Added license boilerplate.
diff --git a/.ci/legacy_project/all_packages/.gitignore b/.ci/legacy_project/all_packages/.gitignore
new file mode 100644
index 00000000000..0fa6b675c0a
--- /dev/null
+++ b/.ci/legacy_project/all_packages/.gitignore
@@ -0,0 +1,46 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.buildlog/
+.history
+.svn/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# The .vscode folder contains launch configuration and tasks you configure in
+# VS Code which you may wish to be included in version control, so this line
+# is commented out by default.
+#.vscode/
+
+# Flutter/Dart/Pub related
+**/doc/api/
+**/ios/Flutter/.last_build_id
+.dart_tool/
+.flutter-plugins
+.flutter-plugins-dependencies
+.packages
+.pub-cache/
+.pub/
+/build/
+
+# Web related
+lib/generated_plugin_registrant.dart
+
+# Symbolication related
+app.*.symbols
+
+# Obfuscation related
+app.*.map.json
+
+# Android Studio will place build artifacts here
+/android/app/debug
+/android/app/profile
+/android/app/release
diff --git a/.ci/legacy_project/all_packages/.metadata b/.ci/legacy_project/all_packages/.metadata
new file mode 100644
index 00000000000..d7e64d0b322
--- /dev/null
+++ b/.ci/legacy_project/all_packages/.metadata
@@ -0,0 +1,10 @@
+# This file tracks properties of this Flutter project.
+# Used by Flutter tool to assess capabilities and perform upgrades etc.
+#
+# This file should be version controlled and should not be manually edited.
+
+version:
+ revision: 1d9032c7e1d867f071f2277eb1673e8f9b0274e3
+ channel: unknown
+
+project_type: app
diff --git a/.ci/legacy_project/all_packages/android/.gitignore b/.ci/legacy_project/all_packages/android/.gitignore
new file mode 100644
index 00000000000..0a741cb43d6
--- /dev/null
+++ b/.ci/legacy_project/all_packages/android/.gitignore
@@ -0,0 +1,11 @@
+gradle-wrapper.jar
+/.gradle
+/captures/
+/gradlew
+/gradlew.bat
+/local.properties
+GeneratedPluginRegistrant.java
+
+# Remember to never publicly share your keystore.
+# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
+key.properties
diff --git a/.ci/legacy_project/all_packages/android/app/build.gradle b/.ci/legacy_project/all_packages/android/app/build.gradle
new file mode 100644
index 00000000000..b75c7b0561b
--- /dev/null
+++ b/.ci/legacy_project/all_packages/android/app/build.gradle
@@ -0,0 +1,47 @@
+def localProperties = new Properties()
+def localPropertiesFile = rootProject.file('local.properties')
+if (localPropertiesFile.exists()) {
+ localPropertiesFile.withReader('UTF-8') { reader ->
+ localProperties.load(reader)
+ }
+}
+
+def flutterRoot = localProperties.getProperty('flutter.sdk')
+if (flutterRoot == null) {
+ throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
+}
+
+def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
+if (flutterVersionCode == null) {
+ flutterVersionCode = '1'
+}
+
+def flutterVersionName = localProperties.getProperty('flutter.versionName')
+if (flutterVersionName == null) {
+ flutterVersionName = '1.0'
+}
+
+apply plugin: 'com.android.application'
+apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
+
+android {
+ compileSdkVersion 30
+
+ defaultConfig {
+ applicationId "com.example.all_packages"
+ minSdkVersion 16
+ targetSdkVersion 30
+ versionCode flutterVersionCode.toInteger()
+ versionName flutterVersionName
+ }
+
+ buildTypes {
+ release {
+ signingConfig signingConfigs.debug
+ }
+ }
+}
+
+flutter {
+ source '../..'
+}
diff --git a/.ci/legacy_project/all_packages/android/app/src/debug/AndroidManifest.xml b/.ci/legacy_project/all_packages/android/app/src/debug/AndroidManifest.xml
new file mode 100644
index 00000000000..3a38eba348d
--- /dev/null
+++ b/.ci/legacy_project/all_packages/android/app/src/debug/AndroidManifest.xml
@@ -0,0 +1,4 @@
+
+
+
diff --git a/.ci/legacy_project/all_packages/android/app/src/main/AndroidManifest.xml b/.ci/legacy_project/all_packages/android/app/src/main/AndroidManifest.xml
new file mode 100644
index 00000000000..70c010f2867
--- /dev/null
+++ b/.ci/legacy_project/all_packages/android/app/src/main/AndroidManifest.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.ci/legacy_project/all_packages/android/app/src/main/java/com/example/all_packages/MainActivity.java b/.ci/legacy_project/all_packages/android/app/src/main/java/com/example/all_packages/MainActivity.java
new file mode 100644
index 00000000000..f494afad857
--- /dev/null
+++ b/.ci/legacy_project/all_packages/android/app/src/main/java/com/example/all_packages/MainActivity.java
@@ -0,0 +1,10 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package com.example.all_packages;
+
+import io.flutter.embedding.android.FlutterActivity;
+
+public class MainActivity extends FlutterActivity {
+}
diff --git a/.ci/legacy_project/all_packages/android/app/src/main/res/drawable-v21/launch_background.xml b/.ci/legacy_project/all_packages/android/app/src/main/res/drawable-v21/launch_background.xml
new file mode 100644
index 00000000000..f74085f3f6a
--- /dev/null
+++ b/.ci/legacy_project/all_packages/android/app/src/main/res/drawable-v21/launch_background.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
diff --git a/.ci/legacy_project/all_packages/android/app/src/main/res/drawable/launch_background.xml b/.ci/legacy_project/all_packages/android/app/src/main/res/drawable/launch_background.xml
new file mode 100644
index 00000000000..304732f8842
--- /dev/null
+++ b/.ci/legacy_project/all_packages/android/app/src/main/res/drawable/launch_background.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
diff --git a/.ci/legacy_project/all_packages/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/.ci/legacy_project/all_packages/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 00000000000..db77bb4b7b0
Binary files /dev/null and b/.ci/legacy_project/all_packages/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/.ci/legacy_project/all_packages/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/.ci/legacy_project/all_packages/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 00000000000..17987b79bb8
Binary files /dev/null and b/.ci/legacy_project/all_packages/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/.ci/legacy_project/all_packages/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/.ci/legacy_project/all_packages/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 00000000000..09d4391482b
Binary files /dev/null and b/.ci/legacy_project/all_packages/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/.ci/legacy_project/all_packages/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/.ci/legacy_project/all_packages/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 00000000000..d5f1c8d34e7
Binary files /dev/null and b/.ci/legacy_project/all_packages/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/.ci/legacy_project/all_packages/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/.ci/legacy_project/all_packages/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 00000000000..4d6372eebdb
Binary files /dev/null and b/.ci/legacy_project/all_packages/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/.ci/legacy_project/all_packages/android/app/src/main/res/values-night/styles.xml b/.ci/legacy_project/all_packages/android/app/src/main/res/values-night/styles.xml
new file mode 100644
index 00000000000..449a9f93082
--- /dev/null
+++ b/.ci/legacy_project/all_packages/android/app/src/main/res/values-night/styles.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/.ci/legacy_project/all_packages/android/app/src/main/res/values/styles.xml b/.ci/legacy_project/all_packages/android/app/src/main/res/values/styles.xml
new file mode 100644
index 00000000000..d74aa35c282
--- /dev/null
+++ b/.ci/legacy_project/all_packages/android/app/src/main/res/values/styles.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/.ci/legacy_project/all_packages/android/app/src/profile/AndroidManifest.xml b/.ci/legacy_project/all_packages/android/app/src/profile/AndroidManifest.xml
new file mode 100644
index 00000000000..02ba522d3d9
--- /dev/null
+++ b/.ci/legacy_project/all_packages/android/app/src/profile/AndroidManifest.xml
@@ -0,0 +1,7 @@
+
+
+
+
diff --git a/.ci/legacy_project/all_packages/android/build.gradle b/.ci/legacy_project/all_packages/android/build.gradle
new file mode 100644
index 00000000000..c9e3db0a0f3
--- /dev/null
+++ b/.ci/legacy_project/all_packages/android/build.gradle
@@ -0,0 +1,29 @@
+buildscript {
+ repositories {
+ google()
+ jcenter()
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:4.1.0'
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ }
+}
+
+rootProject.buildDir = '../build'
+subprojects {
+ project.buildDir = "${rootProject.buildDir}/${project.name}"
+}
+subprojects {
+ project.evaluationDependsOn(':app')
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/.ci/legacy_project/all_packages/android/gradle.properties b/.ci/legacy_project/all_packages/android/gradle.properties
new file mode 100644
index 00000000000..94adc3a3f97
--- /dev/null
+++ b/.ci/legacy_project/all_packages/android/gradle.properties
@@ -0,0 +1,3 @@
+org.gradle.jvmargs=-Xmx1536M
+android.useAndroidX=true
+android.enableJetifier=true
diff --git a/.ci/legacy_project/all_packages/android/gradle/wrapper/gradle-wrapper.properties b/.ci/legacy_project/all_packages/android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 00000000000..bc6a58afdda
--- /dev/null
+++ b/.ci/legacy_project/all_packages/android/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Fri Jun 23 08:50:38 CEST 2017
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
diff --git a/.ci/legacy_project/all_packages/android/settings.gradle b/.ci/legacy_project/all_packages/android/settings.gradle
new file mode 100644
index 00000000000..44e62bcf06a
--- /dev/null
+++ b/.ci/legacy_project/all_packages/android/settings.gradle
@@ -0,0 +1,11 @@
+include ':app'
+
+def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
+def properties = new Properties()
+
+assert localPropertiesFile.exists()
+localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
+
+def flutterSdkPath = properties.getProperty("flutter.sdk")
+assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
+apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
diff --git a/.cirrus.yml b/.cirrus.yml
index e239560c968..41534365ab4 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -301,6 +301,11 @@ task:
CHANNEL: "master"
CHANNEL: "stable"
<< : *BUILD_ALL_PACKAGES_APP_TEMPLATE
+ create_all_packages_app_legacy_script:
+ - $PLUGIN_TOOL_COMMAND create-all-packages-app --legacy-source=.ci/legacy_project/all_packages --output-dir=legacy/ --exclude script/configs/exclude_all_packages_app.yaml
+ build_all_packages_legacy_script:
+ - cd legacy/all_packages
+ - flutter build $BUILD_ALL_ARGS --debug
### Web tasks ###
- name: web-platform_tests
env:
diff --git a/script/tool/lib/src/common/file_utils.dart b/script/tool/lib/src/common/file_utils.dart
index 3c2f2f18f95..968de011a21 100644
--- a/script/tool/lib/src/common/file_utils.dart
+++ b/script/tool/lib/src/common/file_utils.dart
@@ -11,10 +11,21 @@ import 'package:file/file.dart';
/// childFileWithSubcomponents(rootDir, ['foo', 'bar', 'baz.txt'])
/// creates a File representing /rootDir/foo/bar/baz.txt.
File childFileWithSubcomponents(Directory base, List components) {
- Directory dir = base;
final String basename = components.removeLast();
+ return childDirectoryWithSubcomponents(base, components).childFile(basename);
+}
+
+/// Returns a [Directory] created by appending everything in [components]
+/// to [base] as subdirectories.
+///
+/// Example:
+/// childFileWithSubcomponents(rootDir, ['foo', 'bar'])
+/// creates a File representing /rootDir/foo/bar/.
+Directory childDirectoryWithSubcomponents(
+ Directory base, List components) {
+ Directory dir = base;
for (final String directoryName in components) {
dir = dir.childDirectory(directoryName);
}
- return dir.childFile(basename);
+ return dir;
}
diff --git a/script/tool/lib/src/create_all_packages_app_command.dart b/script/tool/lib/src/create_all_packages_app_command.dart
index fb07e335e0f..01f37f3a310 100644
--- a/script/tool/lib/src/create_all_packages_app_command.dart
+++ b/script/tool/lib/src/create_all_packages_app_command.dart
@@ -2,26 +2,27 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-import 'dart:io' as io;
-
import 'package:file/file.dart';
+import 'package:meta/meta.dart';
import 'package:path/path.dart' as p;
import 'package:platform/platform.dart';
import 'package:pub_semver/pub_semver.dart';
import 'package:pubspec_parse/pubspec_parse.dart';
import 'common/core.dart';
+import 'common/file_utils.dart';
import 'common/package_command.dart';
import 'common/process_runner.dart';
import 'common/repository_package.dart';
-const String _outputDirectoryFlag = 'output-dir';
-
-const String _projectName = 'all_packages';
+/// The name of the build-all-packages project, as passed to `flutter create`.
+@visibleForTesting
+const String allPackagesProjectName = 'all_packages';
-const int _exitUpdateMacosPodfileFailed = 3;
-const int _exitUpdateMacosPbxprojFailed = 4;
-const int _exitGenNativeBuildFilesFailed = 5;
+const int _exitFlutterCreateFailed = 3;
+const int _exitGenNativeBuildFilesFailed = 4;
+const int _exitMissingFile = 5;
+const int _exitMissingLegacySource = 6;
/// A command to create an application that builds all in a single application.
class CreateAllPackagesAppCommand extends PackageCommand {
@@ -29,22 +30,29 @@ class CreateAllPackagesAppCommand extends PackageCommand {
CreateAllPackagesAppCommand(
Directory packagesDir, {
ProcessRunner processRunner = const ProcessRunner(),
- Directory? pluginsRoot,
Platform platform = const LocalPlatform(),
}) : super(packagesDir, processRunner: processRunner, platform: platform) {
- final Directory defaultDir =
- pluginsRoot ?? packagesDir.fileSystem.currentDirectory;
argParser.addOption(_outputDirectoryFlag,
- defaultsTo: defaultDir.path,
- help:
- 'The path the directory to create the "$_projectName" project in.\n'
+ defaultsTo: packagesDir.parent.path,
+ help: 'The path the directory to create the "$allPackagesProjectName" '
+ 'project in.\n'
'Defaults to the repository root.');
+ argParser.addOption(_legacySourceFlag,
+ help: 'A partial project directory to use as a source for replacing '
+ 'portions of the created app. All top-level directories in the '
+ 'source will replace the corresponding directories in the output '
+ 'directory post-create.\n\n'
+ 'The replacement will be done before any tool-driven '
+ 'modifications.');
}
+ static const String _legacySourceFlag = 'legacy-source';
+ static const String _outputDirectoryFlag = 'output-dir';
+
/// The location to create the synthesized app project.
Directory get _appDirectory => packagesDir.fileSystem
.directory(getStringArg(_outputDirectoryFlag))
- .childDirectory(_projectName);
+ .childDirectory(allPackagesProjectName);
/// The synthesized app project.
RepositoryPackage get app => RepositoryPackage(_appDirectory);
@@ -60,7 +68,15 @@ class CreateAllPackagesAppCommand extends PackageCommand {
Future run() async {
final int exitCode = await _createApp();
if (exitCode != 0) {
- throw ToolExit(exitCode);
+ printError('Failed to `flutter create`: $exitCode');
+ throw ToolExit(_exitFlutterCreateFailed);
+ }
+
+ final String? legacySource = getNullableStringArg(_legacySourceFlag);
+ if (legacySource != null) {
+ final Directory legacyDir =
+ packagesDir.fileSystem.directory(legacySource);
+ await _replaceWithLegacy(target: _appDirectory, source: legacyDir);
}
final Set excluded = getExcludedPackageNames();
@@ -89,7 +105,6 @@ class CreateAllPackagesAppCommand extends PackageCommand {
await Future.wait(>[
_updateAppGradle(),
- _updateManifest(),
_updateMacosPbxproj(),
// This step requires the native file generation triggered by
// flutter pub get above, so can't currently be run on Windows.
@@ -98,80 +113,152 @@ class CreateAllPackagesAppCommand extends PackageCommand {
}
Future _createApp() async {
- final io.ProcessResult result = io.Process.runSync(
+ return processRunner.runAndStream(
flutterCommand,
[
'create',
'--template=app',
- '--project-name=$_projectName',
- '--android-language=java',
+ '--project-name=$allPackagesProjectName',
_appDirectory.path,
],
);
-
- print(result.stdout);
- print(result.stderr);
- return result.exitCode;
}
- Future _updateAppGradle() async {
- final File gradleFile = app
- .platformDirectory(FlutterPlatform.android)
- .childDirectory('app')
- .childFile('build.gradle');
- if (!gradleFile.existsSync()) {
- throw ToolExit(64);
+ Future _replaceWithLegacy(
+ {required Directory target, required Directory source}) async {
+ if (!source.existsSync()) {
+ printError('No such legacy source directory: ${source.path}');
+ throw ToolExit(_exitMissingLegacySource);
+ }
+ for (final FileSystemEntity entity in source.listSync()) {
+ final String basename = entity.basename;
+ print('Replacing $basename with legacy version...');
+ if (entity is Directory) {
+ target.childDirectory(basename).deleteSync(recursive: true);
+ } else {
+ target.childFile(basename).deleteSync();
+ }
+ _copyDirectory(source: source, target: target);
}
+ }
- final StringBuffer newGradle = StringBuffer();
- for (final String line in gradleFile.readAsLinesSync()) {
- if (line.contains('minSdkVersion')) {
- // minSdkVersion 21 is required by camera_android.
- newGradle.writeln('minSdkVersion 21');
- } else if (line.contains('compileSdkVersion')) {
- // compileSdkVersion 33 is required by local_auth.
- newGradle.writeln('compileSdkVersion 33');
+ void _copyDirectory({required Directory target, required Directory source}) {
+ target.createSync(recursive: true);
+ for (final FileSystemEntity entity in source.listSync(recursive: true)) {
+ final List subcomponents =
+ p.split(p.relative(entity.path, from: source.path));
+ if (entity is Directory) {
+ childDirectoryWithSubcomponents(target, subcomponents)
+ .createSync(recursive: true);
+ } else if (entity is File) {
+ final File targetFile =
+ childFileWithSubcomponents(target, subcomponents);
+ targetFile.parent.createSync(recursive: true);
+ entity.copySync(targetFile.path);
} else {
- newGradle.writeln(line);
+ throw UnimplementedError('Unsupported entity: $entity');
}
- if (line.contains('defaultConfig {')) {
- newGradle.writeln(' multiDexEnabled true');
- } else if (line.contains('dependencies {')) {
- // Tests for https://github.com/flutter/flutter/issues/43383
- newGradle.writeln(
- " implementation 'androidx.lifecycle:lifecycle-runtime:2.2.0-rc01'\n",
- );
+ }
+ }
+
+ /// Rewrites [file], replacing any lines contain a key in [replacements] with
+ /// the lines in the corresponding value, and adding any lines in [additions]'
+ /// values after lines containing the key.
+ void _adjustFile(
+ File file, {
+ Map> replacements = const >{},
+ Map> additions = const >{},
+ Map> regexReplacements =
+ const >{},
+ }) {
+ if (replacements.isEmpty && additions.isEmpty) {
+ return;
+ }
+ if (!file.existsSync()) {
+ printError('Unable to find ${file.path} for updating.');
+ throw ToolExit(_exitMissingFile);
+ }
+
+ final StringBuffer output = StringBuffer();
+ for (final String line in file.readAsLinesSync()) {
+ List? replacementLines;
+ for (final MapEntry> replacement
+ in replacements.entries) {
+ if (line.contains(replacement.key)) {
+ replacementLines = replacement.value;
+ break;
+ }
+ }
+ if (replacementLines == null) {
+ for (final MapEntry> replacement
+ in regexReplacements.entries) {
+ final RegExpMatch? match = replacement.key.firstMatch(line);
+ if (match != null) {
+ replacementLines = replacement.value;
+ break;
+ }
+ }
+ }
+ (replacementLines ?? [line]).forEach(output.writeln);
+
+ for (final String targetString in additions.keys) {
+ if (line.contains(targetString)) {
+ additions[targetString]!.forEach(output.writeln);
+ }
}
}
- gradleFile.writeAsStringSync(newGradle.toString());
+ file.writeAsStringSync(output.toString());
}
- Future _updateManifest() async {
- final File manifestFile = app
+ Future _updateAppGradle() async {
+ final File gradleFile = app
.platformDirectory(FlutterPlatform.android)
.childDirectory('app')
- .childDirectory('src')
- .childDirectory('main')
- .childFile('AndroidManifest.xml');
- if (!manifestFile.existsSync()) {
- throw ToolExit(64);
- }
+ .childFile('build.gradle');
- final StringBuffer newManifest = StringBuffer();
- for (final String line in manifestFile.readAsLinesSync()) {
- if (line.contains('package="com.example.$_projectName"')) {
- newManifest
- ..writeln('package="com.example.$_projectName"')
- ..writeln('xmlns:tools="http://schemas.android.com/tools">')
- ..writeln()
- ..writeln(
- '',
- );
- } else {
- newManifest.writeln(line);
- }
+ // Ensure that there is a dependencies section, so the dependencies addition
+ // below will work.
+ final String content = gradleFile.readAsStringSync();
+ if (!content.contains('\ndependencies {')) {
+ gradleFile.writeAsStringSync('''
+$content
+dependencies {}
+''');
}
- manifestFile.writeAsStringSync(newManifest.toString());
+
+ const String lifecycleDependency =
+ " implementation 'androidx.lifecycle:lifecycle-runtime:2.2.0-rc01'";
+
+ _adjustFile(
+ gradleFile,
+ replacements: >{
+ // minSdkVersion 21 is required by camera_android.
+ 'minSdkVersion': ['minSdkVersion 21'],
+ // compileSdkVersion 33 is required by local_auth.
+ 'compileSdkVersion': ['compileSdkVersion 33'],
+ },
+ additions: >{
+ 'defaultConfig {': [' multiDexEnabled true'],
+ },
+ regexReplacements: >{
+ // Tests for https://github.com/flutter/flutter/issues/43383
+ // Handling of 'dependencies' is more complex since it hasn't been very
+ // stable across template versions.
+ // - Handle an empty, collapsed dependencies section.
+ RegExp(r'^dependencies\s+{\s*}$'): [
+ 'dependencies {',
+ lifecycleDependency,
+ '}',
+ ],
+ // - Handle a normal dependencies section.
+ RegExp(r'^dependencies\s+{$'): [
+ 'dependencies {',
+ lifecycleDependency,
+ ],
+ // - See below for handling of the case where there is no dependencies
+ // section.
+ },
+ );
}
Future _genPubspecWithAllPlugins() async {
@@ -190,7 +277,7 @@ class CreateAllPackagesAppCommand extends PackageCommand {
final Map pluginDeps =
await _getValidPathDependencies();
final Pubspec pubspec = Pubspec(
- _projectName,
+ allPackagesProjectName,
description: 'Flutter app containing all 1st party plugins.',
version: Version.parse('1.0.0+1'),
environment: {
@@ -300,23 +387,15 @@ dev_dependencies:${_pubspecMapString(pubspec.devDependencies)}
return;
}
- final File podfileFile =
+ final File podfile =
app.platformDirectory(FlutterPlatform.macos).childFile('Podfile');
- if (!podfileFile.existsSync()) {
- printError("Can't find Podfile for macOS");
- throw ToolExit(_exitUpdateMacosPodfileFailed);
- }
-
- final StringBuffer newPodfile = StringBuffer();
- for (final String line in podfileFile.readAsLinesSync()) {
- if (line.contains('platform :osx')) {
+ _adjustFile(
+ podfile,
+ replacements: >{
// macOS 10.15 is required by in_app_purchase.
- newPodfile.writeln("platform :osx, '10.15'");
- } else {
- newPodfile.writeln(line);
- }
- }
- podfileFile.writeAsStringSync(newPodfile.toString());
+ 'platform :osx': ["platform :osx, '10.15'"],
+ },
+ );
}
Future _updateMacosPbxproj() async {
@@ -324,20 +403,14 @@ dev_dependencies:${_pubspecMapString(pubspec.devDependencies)}
.platformDirectory(FlutterPlatform.macos)
.childDirectory('Runner.xcodeproj')
.childFile('project.pbxproj');
- if (!pbxprojFile.existsSync()) {
- printError("Can't find project.pbxproj for macOS");
- throw ToolExit(_exitUpdateMacosPbxprojFailed);
- }
-
- final StringBuffer newPbxproj = StringBuffer();
- for (final String line in pbxprojFile.readAsLinesSync()) {
- if (line.contains('MACOSX_DEPLOYMENT_TARGET')) {
+ _adjustFile(
+ pbxprojFile,
+ replacements: >{
// macOS 10.15 is required by in_app_purchase.
- newPbxproj.writeln(' MACOSX_DEPLOYMENT_TARGET = 10.15;');
- } else {
- newPbxproj.writeln(line);
- }
- }
- pbxprojFile.writeAsStringSync(newPbxproj.toString());
+ 'MACOSX_DEPLOYMENT_TARGET': [
+ ' MACOSX_DEPLOYMENT_TARGET = 10.15;'
+ ],
+ },
+ );
}
}
diff --git a/script/tool/test/common/file_utils_test.dart b/script/tool/test/common/file_utils_test.dart
index 79b804e31ea..4640aa8f691 100644
--- a/script/tool/test/common/file_utils_test.dart
+++ b/script/tool/test/common/file_utils_test.dart
@@ -8,25 +8,51 @@ import 'package:flutter_plugin_tools/src/common/file_utils.dart';
import 'package:test/test.dart';
void main() {
- test('works on Posix', () async {
- final FileSystem fileSystem =
- MemoryFileSystem();
+ group('childFileWithSubcomponents', () {
+ test('works on Posix', () async {
+ final FileSystem fileSystem = MemoryFileSystem();
- final Directory base = fileSystem.directory('/').childDirectory('base');
- final File file =
- childFileWithSubcomponents(base, ['foo', 'bar', 'baz.txt']);
+ final Directory base = fileSystem.directory('/').childDirectory('base');
+ final File file =
+ childFileWithSubcomponents(base, ['foo', 'bar', 'baz.txt']);
- expect(file.absolute.path, '/base/foo/bar/baz.txt');
+ expect(file.absolute.path, '/base/foo/bar/baz.txt');
+ });
+
+ test('works on Windows', () async {
+ final FileSystem fileSystem =
+ MemoryFileSystem(style: FileSystemStyle.windows);
+
+ final Directory base =
+ fileSystem.directory(r'C:\').childDirectory('base');
+ final File file =
+ childFileWithSubcomponents(base, ['foo', 'bar', 'baz.txt']);
+
+ expect(file.absolute.path, r'C:\base\foo\bar\baz.txt');
+ });
});
- test('works on Windows', () async {
- final FileSystem fileSystem =
- MemoryFileSystem(style: FileSystemStyle.windows);
+ group('childDirectoryWithSubcomponents', () {
+ test('works on Posix', () async {
+ final FileSystem fileSystem = MemoryFileSystem();
+
+ final Directory base = fileSystem.directory('/').childDirectory('base');
+ final Directory dir =
+ childDirectoryWithSubcomponents(base, ['foo', 'bar', 'baz']);
+
+ expect(dir.absolute.path, '/base/foo/bar/baz');
+ });
+
+ test('works on Windows', () async {
+ final FileSystem fileSystem =
+ MemoryFileSystem(style: FileSystemStyle.windows);
- final Directory base = fileSystem.directory(r'C:\').childDirectory('base');
- final File file =
- childFileWithSubcomponents(base, ['foo', 'bar', 'baz.txt']);
+ final Directory base =
+ fileSystem.directory(r'C:\').childDirectory('base');
+ final Directory dir =
+ childDirectoryWithSubcomponents(base, ['foo', 'bar', 'baz']);
- expect(file.absolute.path, r'C:\base\foo\bar\baz.txt');
+ expect(dir.absolute.path, r'C:\base\foo\bar\baz');
+ });
});
}
diff --git a/script/tool/test/create_all_packages_app_command_test.dart b/script/tool/test/create_all_packages_app_command_test.dart
index c545c1f3f5a..6f7ba8ead2e 100644
--- a/script/tool/test/create_all_packages_app_command_test.dart
+++ b/script/tool/test/create_all_packages_app_command_test.dart
@@ -6,7 +6,7 @@ import 'dart:io' as io;
import 'package:args/command_runner.dart';
import 'package:file/file.dart';
-import 'package:file/local.dart';
+import 'package:file/memory.dart';
import 'package:flutter_plugin_tools/src/common/core.dart';
import 'package:flutter_plugin_tools/src/create_all_packages_app_command.dart';
import 'package:platform/platform.dart';
@@ -18,16 +18,15 @@ import 'util.dart';
void main() {
late CommandRunner runner;
late CreateAllPackagesAppCommand command;
+ late Platform mockPlatform;
late FileSystem fileSystem;
late Directory testRoot;
late Directory packagesDir;
late RecordingProcessRunner processRunner;
setUp(() {
- // Since the core of this command is a call to 'flutter create', the test
- // has to use the real filesystem. Put everything possible in a unique
- // temporary to minimize effect on the host system.
- fileSystem = const LocalFileSystem();
+ mockPlatform = MockPlatform(isMacOS: true);
+ fileSystem = MemoryFileSystem();
testRoot = fileSystem.systemTempDirectory.createTempSync();
packagesDir = testRoot.childDirectory('packages');
processRunner = RecordingProcessRunner();
@@ -35,34 +34,142 @@ void main() {
command = CreateAllPackagesAppCommand(
packagesDir,
processRunner: processRunner,
- pluginsRoot: testRoot,
+ platform: mockPlatform,
);
runner = CommandRunner(
'create_all_test', 'Test for $CreateAllPackagesAppCommand');
runner.addCommand(command);
});
- tearDown(() {
- testRoot.deleteSync(recursive: true);
- });
+ /// Simulates enough of `flutter create`s output to allow the modifications
+ /// made by the command to work.
+ void writeFakeFlutterCreateOutput(
+ Directory outputDirectory, {
+ String dartSdkConstraint = '>=3.0.0 <4.0.0',
+ String? appBuildGradleDependencies,
+ bool androidOnly = false,
+ }) {
+ final RepositoryPackage package = RepositoryPackage(
+ outputDirectory.childDirectory(allPackagesProjectName));
+
+ // Android
+ final String dependencies = appBuildGradleDependencies ??
+ r'''
+dependencies {
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+}
+''';
+ package
+ .platformDirectory(FlutterPlatform.android)
+ .childDirectory('app')
+ .childFile('build.gradle')
+ ..createSync(recursive: true)
+ ..writeAsStringSync('''
+android {
+ namespace 'dev.flutter.packages.foo.example'
+ compileSdkVersion flutter.compileSdkVersion
+ sourceSets {
+ main.java.srcDirs += 'src/main/kotlin'
+ }
+ defaultConfig {
+ applicationId "dev.flutter.packages.foo.example"
+ minSdkVersion flutter.minSdkVersion
+ targetSdkVersion 32
+ }
+}
+
+$dependencies
+''');
+
+ if (androidOnly) {
+ return;
+ }
+
+ // Non-platform-specific
+ package.pubspecFile
+ ..createSync(recursive: true)
+ ..writeAsStringSync('''
+name: $allPackagesProjectName
+description: Flutter app containing all 1st party plugins.
+publish_to: none
+version: 1.0.0
+
+environment:
+ sdk: '$dartSdkConstraint'
+
+dependencies:
+ flutter:
+ sdk: flutter
+
+dev_dependencies:
+ flutter_test:
+ sdk: flutter
+###
+''');
+
+ // macOS
+ final Directory macOS = package.platformDirectory(FlutterPlatform.macos);
+ macOS.childDirectory('Runner.xcodeproj').childFile('project.pbxproj')
+ ..createSync(recursive: true)
+ ..writeAsStringSync('''
+ 97C147041CF9000F007C117D /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.14;
+ };
+ name = Release;
+ };
+''');
+ macOS.childFile('Podfile')
+ ..createSync(recursive: true)
+ ..writeAsStringSync('''
+# platform :osx, '10.14'
+
+ENV['COCOAPODS_DISABLE_STATS'] = 'true'
+
+project 'Runner', {
+ 'Debug' => :debug,
+ 'Profile' => :release,
+ 'Release' => :release,
+}
+''');
+ }
group('non-macOS host', () {
setUp(() {
+ mockPlatform = MockPlatform(isLinux: true);
command = CreateAllPackagesAppCommand(
packagesDir,
processRunner: processRunner,
- // Set isWindows or not based on the actual host, so that
- // `flutterCommand` works, since these tests actually call 'flutter'.
- // The important thing is that isMacOS always returns false.
- platform: MockPlatform(isWindows: const LocalPlatform().isWindows),
- pluginsRoot: testRoot,
+ platform: mockPlatform,
);
runner = CommandRunner(
'create_all_test', 'Test for $CreateAllPackagesAppCommand');
runner.addCommand(command);
});
+ test('calls "flutter create"', () async {
+ writeFakeFlutterCreateOutput(testRoot);
+ createFakePlugin('plugina', packagesDir);
+
+ await runCapturingPrint(runner, ['create-all-packages-app']);
+
+ expect(
+ processRunner.recordedCalls,
+ contains(ProcessCall(
+ getFlutterCommand(mockPlatform),
+ [
+ 'create',
+ '--template=app',
+ '--project-name=$allPackagesProjectName',
+ testRoot.childDirectory(allPackagesProjectName).path,
+ ],
+ null)));
+ });
+
test('pubspec includes all plugins', () async {
+ writeFakeFlutterCreateOutput(testRoot);
createFakePlugin('plugina', packagesDir);
createFakePlugin('pluginb', packagesDir);
createFakePlugin('pluginc', packagesDir);
@@ -80,6 +187,7 @@ void main() {
});
test('pubspec has overrides for all plugins', () async {
+ writeFakeFlutterCreateOutput(testRoot);
createFakePlugin('plugina', packagesDir);
createFakePlugin('pluginb', packagesDir);
createFakePlugin('pluginc', packagesDir);
@@ -97,33 +205,186 @@ void main() {
]));
});
- test('pubspec preserves existing Dart SDK version', () async {
- const String baselineProjectName = 'baseline';
- final Directory baselineProjectDirectory =
- testRoot.childDirectory(baselineProjectName);
- io.Process.runSync(
- getFlutterCommand(const LocalPlatform()),
- [
- 'create',
- '--template=app',
- '--project-name=$baselineProjectName',
- baselineProjectDirectory.path,
- ],
- );
- final Pubspec baselinePubspec =
- RepositoryPackage(baselineProjectDirectory).parsePubspec();
+ test('legacy files are copied when requested', () async {
+ writeFakeFlutterCreateOutput(testRoot);
+ createFakePlugin('plugina', packagesDir);
+ // Make a fake legacy source with all the necessary files, replacing one
+ // of them.
+ final Directory legacyDir = testRoot.childDirectory('legacy');
+ final RepositoryPackage legacySource =
+ RepositoryPackage(legacyDir.childDirectory(allPackagesProjectName));
+ writeFakeFlutterCreateOutput(legacyDir, androidOnly: true);
+ const String legacyAppBuildGradleContents = 'Fake legacy content';
+ final File legacyGradleFile = legacySource
+ .platformDirectory(FlutterPlatform.android)
+ .childFile('build.gradle');
+ legacyGradleFile.writeAsStringSync(legacyAppBuildGradleContents);
+
+ await runCapturingPrint(runner, [
+ 'create-all-packages-app',
+ '--legacy-source=${legacySource.path}',
+ ]);
+ final File buildGradle = command.app
+ .platformDirectory(FlutterPlatform.android)
+ .childFile('build.gradle');
+
+ expect(buildGradle.readAsStringSync(), legacyAppBuildGradleContents);
+ });
+
+ test('legacy directory replaces, rather than overlaying', () async {
+ writeFakeFlutterCreateOutput(testRoot);
+ createFakePlugin('plugina', packagesDir);
+ final File extraFile =
+ RepositoryPackage(testRoot.childDirectory(allPackagesProjectName))
+ .platformDirectory(FlutterPlatform.android)
+ .childFile('extra_file');
+ extraFile.createSync(recursive: true);
+ // Make a fake legacy source with all the necessary files, but not
+ // including the extra file.
+ final Directory legacyDir = testRoot.childDirectory('legacy');
+ final RepositoryPackage legacySource =
+ RepositoryPackage(legacyDir.childDirectory(allPackagesProjectName));
+ writeFakeFlutterCreateOutput(legacyDir, androidOnly: true);
+
+ await runCapturingPrint(runner, [
+ 'create-all-packages-app',
+ '--legacy-source=${legacySource.path}',
+ ]);
+
+ expect(extraFile.existsSync(), false);
+ });
+
+ test('legacy files are modified as needed by the tool', () async {
+ writeFakeFlutterCreateOutput(testRoot);
+ createFakePlugin('plugina', packagesDir);
+ // Make a fake legacy source with all the necessary files, replacing one
+ // of them.
+ final Directory legacyDir = testRoot.childDirectory('legacy');
+ final RepositoryPackage legacySource =
+ RepositoryPackage(legacyDir.childDirectory(allPackagesProjectName));
+ writeFakeFlutterCreateOutput(legacyDir, androidOnly: true);
+ const String legacyAppBuildGradleContents = '''
+# This is the legacy file
+android {
+ compileSdkVersion flutter.compileSdkVersion
+ defaultConfig {
+ minSdkVersion flutter.minSdkVersion
+ }
+}
+''';
+ final File legacyGradleFile = legacySource
+ .platformDirectory(FlutterPlatform.android)
+ .childDirectory('app')
+ .childFile('build.gradle');
+ legacyGradleFile.writeAsStringSync(legacyAppBuildGradleContents);
+
+ await runCapturingPrint(runner, [
+ 'create-all-packages-app',
+ '--legacy-source=${legacySource.path}',
+ ]);
+
+ final List buildGradle = command.app
+ .platformDirectory(FlutterPlatform.android)
+ .childDirectory('app')
+ .childFile('build.gradle')
+ .readAsLinesSync();
+
+ expect(
+ buildGradle,
+ containsAll([
+ contains('This is the legacy file'),
+ contains('minSdkVersion 21'),
+ contains('compileSdkVersion 33'),
+ ]));
+ });
+
+ test('pubspec preserves existing Dart SDK version', () async {
+ const String existingSdkConstraint = '>=1.0.0 <99.0.0';
+ writeFakeFlutterCreateOutput(testRoot,
+ dartSdkConstraint: existingSdkConstraint);
createFakePlugin('plugina', packagesDir);
await runCapturingPrint(runner, ['create-all-packages-app']);
final Pubspec generatedPubspec = command.app.parsePubspec();
const String dartSdkKey = 'sdk';
- expect(generatedPubspec.environment?[dartSdkKey],
- baselinePubspec.environment?[dartSdkKey]);
+ expect(generatedPubspec.environment?[dartSdkKey].toString(),
+ existingSdkConstraint);
+ });
+
+ test('Android app gradle is modified as expected', () async {
+ writeFakeFlutterCreateOutput(testRoot);
+ createFakePlugin('plugina', packagesDir);
+
+ await runCapturingPrint(runner, ['create-all-packages-app']);
+
+ final List buildGradle = command.app
+ .platformDirectory(FlutterPlatform.android)
+ .childDirectory('app')
+ .childFile('build.gradle')
+ .readAsLinesSync();
+
+ expect(
+ buildGradle,
+ containsAll([
+ contains('minSdkVersion 21'),
+ contains('compileSdkVersion 33'),
+ contains('multiDexEnabled true'),
+ contains('androidx.lifecycle:lifecycle-runtime'),
+ ]));
+ });
+
+ // The template's app/build.gradle does not always have a dependencies
+ // section; ensure that the dependency is added if there is not one.
+ test('Android lifecyle dependency is added with no dependencies', () async {
+ writeFakeFlutterCreateOutput(testRoot, appBuildGradleDependencies: '');
+ createFakePlugin('plugina', packagesDir);
+
+ await runCapturingPrint(runner, ['create-all-packages-app']);
+
+ final List buildGradle = command.app
+ .platformDirectory(FlutterPlatform.android)
+ .childDirectory('app')
+ .childFile('build.gradle')
+ .readAsLinesSync();
+
+ expect(
+ buildGradle,
+ containsAllInOrder([
+ equals('dependencies {'),
+ contains('androidx.lifecycle:lifecycle-runtime'),
+ equals('}'),
+ ]));
+ });
+
+ // Some versions of the template's app/build.gradle has an empty
+ // dependencies section; ensure that the dependency is added in that case.
+ test('Android lifecyle dependency is added with empty dependencies',
+ () async {
+ writeFakeFlutterCreateOutput(testRoot,
+ appBuildGradleDependencies: 'dependencies {}');
+ createFakePlugin('plugina', packagesDir);
+
+ await runCapturingPrint(runner, ['create-all-packages-app']);
+
+ final List buildGradle = command.app
+ .platformDirectory(FlutterPlatform.android)
+ .childDirectory('app')
+ .childFile('build.gradle')
+ .readAsLinesSync();
+
+ expect(
+ buildGradle,
+ containsAllInOrder([
+ equals('dependencies {'),
+ contains('androidx.lifecycle:lifecycle-runtime'),
+ equals('}'),
+ ]));
});
test('macOS deployment target is modified in pbxproj', () async {
+ writeFakeFlutterCreateOutput(testRoot);
createFakePlugin('plugina', packagesDir);
await runCapturingPrint(runner, ['create-all-packages-app']);
@@ -141,27 +402,50 @@ void main() {
});
test('calls flutter pub get', () async {
+ writeFakeFlutterCreateOutput(testRoot);
createFakePlugin('plugina', packagesDir);
await runCapturingPrint(runner, ['create-all-packages-app']);
expect(
processRunner.recordedCalls,
- orderedEquals([
- ProcessCall(
- getFlutterCommand(const LocalPlatform()),
- const ['pub', 'get'],
- testRoot.childDirectory('all_packages').path),
+ contains(ProcessCall(
+ getFlutterCommand(mockPlatform),
+ const ['pub', 'get'],
+ testRoot.childDirectory(allPackagesProjectName).path)));
+ });
+
+ test('fails if flutter create fails', () async {
+ writeFakeFlutterCreateOutput(testRoot);
+ createFakePlugin('plugina', packagesDir);
+
+ processRunner
+ .mockProcessesForExecutable[getFlutterCommand(mockPlatform)] =
+ [
+ FakeProcessInfo(MockProcess(exitCode: 1), ['create'])
+ ];
+ Error? commandError;
+ final List output = await runCapturingPrint(
+ runner, ['create-all-packages-app'], errorHandler: (Error e) {
+ commandError = e;
+ });
+
+ expect(commandError, isA());
+ expect(
+ output,
+ containsAllInOrder([
+ contains('Failed to `flutter create`'),
]));
- },
- // See comment about Windows in create_all_packages_app_command.dart
- skip: io.Platform.isWindows);
+ });
test('fails if flutter pub get fails', () async {
+ writeFakeFlutterCreateOutput(testRoot);
createFakePlugin('plugina', packagesDir);
- processRunner.mockProcessesForExecutable[
- getFlutterCommand(const LocalPlatform())] = [
+ processRunner
+ .mockProcessesForExecutable[getFlutterCommand(mockPlatform)] =
+ [
+ FakeProcessInfo(MockProcess(), ['create']),
FakeProcessInfo(MockProcess(exitCode: 1), ['pub', 'get'])
];
Error? commandError;
@@ -182,20 +466,22 @@ void main() {
skip: io.Platform.isWindows);
test('handles --output-dir', () async {
- createFakePlugin('plugina', packagesDir);
-
final Directory customOutputDir =
fileSystem.systemTempDirectory.createTempSync();
+ writeFakeFlutterCreateOutput(customOutputDir);
+ createFakePlugin('plugina', packagesDir);
+
await runCapturingPrint(runner, [
'create-all-packages-app',
'--output-dir=${customOutputDir.path}'
]);
expect(command.app.path,
- customOutputDir.childDirectory('all_packages').path);
+ customOutputDir.childDirectory(allPackagesProjectName).path);
});
test('logs exclusions', () async {
+ writeFakeFlutterCreateOutput(testRoot);
createFakePlugin('plugina', packagesDir);
createFakePlugin('pluginb', packagesDir);
createFakePlugin('pluginc', packagesDir);
@@ -219,7 +505,6 @@ void main() {
packagesDir,
processRunner: processRunner,
platform: MockPlatform(isMacOS: true),
- pluginsRoot: testRoot,
);
runner = CommandRunner(
'create_all_test', 'Test for $CreateAllPackagesAppCommand');
@@ -227,10 +512,11 @@ void main() {
});
test('macOS deployment target is modified in Podfile', () async {
+ writeFakeFlutterCreateOutput(testRoot);
createFakePlugin('plugina', packagesDir);
final File podfileFile = RepositoryPackage(
- command.packagesDir.parent.childDirectory('all_packages'))
+ command.packagesDir.parent.childDirectory(allPackagesProjectName))
.platformDirectory(FlutterPlatform.macos)
.childFile('Podfile');
podfileFile.createSync(recursive: true);