Skip to content

Commit e6c9fce

Browse files
authored
Add hidden --no-implicit-pubspec-resolution flag for one stable release. (#157635)
Closes flutter/flutter#157532. Work towards flutter/flutter#157819. Because this flag is checked in `FlutterCommand.verifyAndRun`, it seemed cleaner to just make it a global flag - particularly because this flag's longevity is unlikely to be longer than a single stable release.
1 parent e10d1de commit e6c9fce

File tree

9 files changed

+133
-51
lines changed

9 files changed

+133
-51
lines changed

packages/flutter_tools/lib/src/commands/create.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import '../macos/swift_packages.dart';
2626
import '../project.dart';
2727
import '../reporting/reporting.dart';
2828
import '../runner/flutter_command.dart';
29+
import '../runner/flutter_command_runner.dart';
2930
import 'create_base.dart';
3031

3132
const String kPlatformHelp =
@@ -458,6 +459,7 @@ class CreateCommand extends CreateBase {
458459
macOSPlatform: includeMacos,
459460
windowsPlatform: includeWindows,
460461
webPlatform: includeWeb,
462+
writeLegacyPluginsList: boolArg(FlutterGlobalOptions.kImplicitPubspecResolution, global: true),
461463
);
462464
}
463465
}

packages/flutter_tools/lib/src/commands/packages.dart

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import '../plugins.dart';
2121
import '../project.dart';
2222
import '../reporting/reporting.dart';
2323
import '../runner/flutter_command.dart';
24+
import '../runner/flutter_command_runner.dart';
2425

2526
/// The function signature of the [print] function.
2627
typedef PrintFn = void Function(Object?);
@@ -383,10 +384,15 @@ class PackagesGetCommand extends FlutterCommand {
383384
if (rootProject != null) {
384385
// We need to regenerate the platform specific tooling for both the project
385386
// itself and example(if present).
386-
await rootProject.regeneratePlatformSpecificTooling();
387+
final bool writeLegacyPluginsList = boolArg(FlutterGlobalOptions.kImplicitPubspecResolution, global: true);
388+
await rootProject.regeneratePlatformSpecificTooling(
389+
writeLegacyPluginsList: writeLegacyPluginsList,
390+
);
387391
if (example && rootProject.hasExampleApp && rootProject.example.pubspecFile.existsSync()) {
388392
final FlutterProject exampleProject = rootProject.example;
389-
await exampleProject.regeneratePlatformSpecificTooling();
393+
await exampleProject.regeneratePlatformSpecificTooling(
394+
writeLegacyPluginsList: writeLegacyPluginsList,
395+
);
390396
}
391397
}
392398

packages/flutter_tools/lib/src/flutter_plugins.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1010,7 +1010,7 @@ Future<void> refreshPluginsList(
10101010
bool iosPlatform = false,
10111011
bool macOSPlatform = false,
10121012
bool forceCocoaPodsOnly = false,
1013-
bool writeLegacyPluginsList = true,
1013+
required bool writeLegacyPluginsList,
10141014
}) async {
10151015
final List<Plugin> plugins = await findPlugins(project);
10161016
// Sort the plugins by name to keep ordering stable in generated files.

packages/flutter_tools/lib/src/project.dart

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,7 @@ class FlutterProject {
340340
Future<void> regeneratePlatformSpecificTooling({
341341
DeprecationBehavior deprecationBehavior = DeprecationBehavior.none,
342342
Iterable<String>? allowedPlugins,
343+
required bool writeLegacyPluginsList,
343344
}) async {
344345
return ensureReadyForPlatformSpecificTooling(
345346
androidPlatform: android.existsSync(),
@@ -352,6 +353,7 @@ class FlutterProject {
352353
webPlatform: featureFlags.isWebEnabled && web.existsSync(),
353354
deprecationBehavior: deprecationBehavior,
354355
allowedPlugins: allowedPlugins,
356+
writeLegacyPluginsList: writeLegacyPluginsList,
355357
);
356358
}
357359

@@ -366,11 +368,17 @@ class FlutterProject {
366368
bool webPlatform = false,
367369
DeprecationBehavior deprecationBehavior = DeprecationBehavior.none,
368370
Iterable<String>? allowedPlugins,
371+
required bool writeLegacyPluginsList,
369372
}) async {
370373
if (!directory.existsSync() || isPlugin) {
371374
return;
372375
}
373-
await refreshPluginsList(this, iosPlatform: iosPlatform, macOSPlatform: macOSPlatform);
376+
await refreshPluginsList(
377+
this,
378+
iosPlatform: iosPlatform,
379+
macOSPlatform: macOSPlatform,
380+
writeLegacyPluginsList: writeLegacyPluginsList,
381+
);
374382
if (androidPlatform) {
375383
await android.ensureReadyForPlatformSpecificTooling(deprecationBehavior: deprecationBehavior);
376384
}

packages/flutter_tools/lib/src/runner/flutter_command.dart

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1790,7 +1790,10 @@ Run 'flutter -h' (or 'flutter <command> -h') for available flutter commands and
17901790
// The preview device does not currently support any plugins.
17911791
allowedPlugins = PreviewDevice.supportedPubPlugins;
17921792
}
1793-
await project.regeneratePlatformSpecificTooling(allowedPlugins: allowedPlugins);
1793+
await project.regeneratePlatformSpecificTooling(
1794+
allowedPlugins: allowedPlugins,
1795+
writeLegacyPluginsList: boolArg(FlutterGlobalOptions.kImplicitPubspecResolution, global: true),
1796+
);
17941797
if (reportNullSafety) {
17951798
await _sendNullSafetyAnalyticsEvents(project);
17961799
}

packages/flutter_tools/lib/src/runner/flutter_command_runner.dart

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ abstract final class FlutterGlobalOptions {
3333
static const String kLocalEngineSrcPathOption = 'local-engine-src-path';
3434
static const String kLocalEngineHostOption = 'local-engine-host';
3535
static const String kLocalWebSDKOption = 'local-web-sdk';
36+
// TODO(matanlurey): Remove one stable after https://github.com/flutter/flutter/issues/157819.
37+
static const String kImplicitPubspecResolution = 'implicit-pubspec-resolution';
3638
static const String kMachineFlag = 'machine';
3739
static const String kPackagesOption = 'packages';
3840
static const String kPrefixedErrorsFlag = 'prefixed-errors';
@@ -124,6 +126,26 @@ class FlutterCommandRunner extends CommandRunner<void> {
124126
help: 'Print the address of the Dart Tooling Daemon, if one is hosted by the Flutter CLI.',
125127
hide: !verboseHelp,
126128
);
129+
130+
// TODO(matanlurey): Remove after the Q2 2025 stable release; this is intended
131+
// to give application developers a single stable release where the .flutter-plugins
132+
// file is still supported, but is deprecated, and let folks ensure (with CI or local
133+
// testing) that their workflows do not depend on the file.
134+
//
135+
// See https://github.com/flutter/flutter/issues/157532.
136+
argParser.addFlag(FlutterGlobalOptions.kImplicitPubspecResolution,
137+
defaultsTo: true,
138+
help: 'Whether to support (deprecated) implicit pubspec resolution '
139+
'features, each of which are slated for removal in a future stable '
140+
'release. By setting to "true", the following occurs:\n'
141+
' 1. The generation of ".flutter-plugins" (https://flutter.dev/to/flutter-plugins-configuration).\n'
142+
' 2. Including plugins registered as "dev_dependencies" in release mode.\n'
143+
' 3. Flutter localizations are by default output to synthetic "flutter_gen" package.\n'
144+
'\n'
145+
'This flag will become "false" by default, and then features removed.',
146+
hide: !verboseHelp,
147+
);
148+
127149
if (verboseHelp) {
128150
argParser.addSeparator('Local build selection options (not normally required):');
129151
}

packages/flutter_tools/test/commands.shard/permeable/packages_test.dart

Lines changed: 58 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,11 @@ void main() {
7272
return projectPath;
7373
}
7474

75-
Future<PackagesCommand> runCommandIn(String projectPath, String verb, { List<String>? args }) async {
75+
Future<PackagesCommand> runCommandIn(String projectPath, String verb, { List<String>? args, List<String>? globalArgs }) async {
7676
final PackagesCommand command = PackagesCommand();
7777
final CommandRunner<void> runner = createTestCommandRunner(command);
7878
await runner.run(<String>[
79+
...?globalArgs,
7980
'packages',
8081
verb,
8182
...?args,
@@ -136,15 +137,21 @@ void main() {
136137
'.android/Flutter/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java',
137138
];
138139

139-
const List<String> pluginWitnesses = <String>[
140-
'.flutter-plugins',
141-
'ios/Podfile',
142-
];
140+
List<String> pluginWitnesses({required bool includeLegacyPluginsList}) {
141+
return <String>[
142+
if (includeLegacyPluginsList) '.flutter-plugins',
143+
'.flutter-plugins-dependencies',
144+
'ios/Podfile',
145+
];
146+
}
143147

144-
const List<String> modulePluginWitnesses = <String>[
145-
'.flutter-plugins',
146-
'.ios/Podfile',
147-
];
148+
List<String> modulePluginWitnesses({required bool includeLegacyPluginsList}) {
149+
return <String>[
150+
if (includeLegacyPluginsList) '.flutter-plugins',
151+
'.flutter-plugins-dependencies',
152+
'.ios/Podfile',
153+
];
154+
}
148155

149156
const Map<String, String> pluginContentWitnesses = <String, String>{
150157
'ios/Flutter/Debug.xcconfig': '#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"',
@@ -166,31 +173,37 @@ void main() {
166173
for (final String registrant in modulePluginRegistrants) {
167174
expectExists(projectPath, registrant);
168175
}
169-
for (final String witness in pluginWitnesses) {
176+
for (final String witness in pluginWitnesses(includeLegacyPluginsList: true)) {
177+
expectNotExists(projectPath, witness);
178+
}
179+
for (final String witness in modulePluginWitnesses(includeLegacyPluginsList: true)) {
170180
expectNotExists(projectPath, witness);
171181
}
172182
modulePluginContentWitnesses.forEach((String witness, String content) {
173183
expectNotContains(projectPath, witness, content);
174184
});
175185
}
176186

177-
void expectPluginInjected(String projectPath) {
187+
void expectPluginInjected(String projectPath, {required bool includeLegacyPluginsList}) {
178188
for (final String registrant in pluginRegistrants) {
179189
expectExists(projectPath, registrant);
180190
}
181-
for (final String witness in pluginWitnesses) {
191+
for (final String witness in pluginWitnesses(includeLegacyPluginsList: includeLegacyPluginsList)) {
182192
expectExists(projectPath, witness);
183193
}
194+
if (!includeLegacyPluginsList) {
195+
expectNotExists(projectPath, '.flutter-plugins');
196+
}
184197
pluginContentWitnesses.forEach((String witness, String content) {
185198
expectContains(projectPath, witness, content);
186199
});
187200
}
188201

189-
void expectModulePluginInjected(String projectPath) {
202+
void expectModulePluginInjected(String projectPath, {required bool includeLegacyPluginsList}) {
190203
for (final String registrant in modulePluginRegistrants) {
191204
expectExists(projectPath, registrant);
192205
}
193-
for (final String witness in modulePluginWitnesses) {
206+
for (final String witness in modulePluginWitnesses(includeLegacyPluginsList: includeLegacyPluginsList)) {
194207
expectExists(projectPath, witness);
195208
}
196209
modulePluginContentWitnesses.forEach((String witness, String content) {
@@ -202,7 +215,7 @@ void main() {
202215
final Iterable<String> allFiles = <List<String>>[
203216
pubOutput,
204217
modulePluginRegistrants,
205-
pluginWitnesses,
218+
pluginWitnesses(includeLegacyPluginsList: true),
206219
].expand<String>((List<String> list) => list);
207220
for (final String path in allFiles) {
208221
final File file = globals.fs.file(globals.fs.path.join(projectPath, path));
@@ -579,7 +592,7 @@ workspace:
579592
await runCommandIn(projectPath, 'get');
580593

581594
expectDependenciesResolved(projectPath);
582-
expectModulePluginInjected(projectPath);
595+
expectModulePluginInjected(projectPath, includeLegacyPluginsList: true);
583596
}, overrides: <Type, Generator>{
584597
Stdio: () => mockStdio,
585598
Pub: () => Pub.test(
@@ -609,7 +622,35 @@ workspace:
609622
await runCommandIn(exampleProjectPath, 'get');
610623

611624
expectDependenciesResolved(exampleProjectPath);
612-
expectPluginInjected(exampleProjectPath);
625+
expectPluginInjected(exampleProjectPath, includeLegacyPluginsList: true);
626+
}, overrides: <Type, Generator>{
627+
Stdio: () => mockStdio,
628+
Pub: () => Pub.test(
629+
fileSystem: globals.fs,
630+
logger: globals.logger,
631+
processManager: globals.processManager,
632+
usage: globals.flutterUsage,
633+
botDetector: globals.botDetector,
634+
platform: globals.platform,
635+
stdio: mockStdio,
636+
),
637+
});
638+
639+
testUsingContext('get --no-implicit-pubspec-resolution omits ".flutter-plugins"', () async {
640+
final String projectPath = await createProject(
641+
tempDir,
642+
arguments: <String>['--template=plugin', '--no-pub', '--platforms=ios,android'],
643+
);
644+
final String exampleProjectPath = globals.fs.path.join(projectPath, 'example');
645+
removeGeneratedFiles(projectPath);
646+
removeGeneratedFiles(exampleProjectPath);
647+
648+
// Running flutter packages get also resolves the dependencies in the example/ project.
649+
await runCommandIn(projectPath, 'get', globalArgs: <String>['--no-implicit-pubspec-resolution']);
650+
651+
expectDependenciesResolved(projectPath);
652+
expectDependenciesResolved(exampleProjectPath);
653+
expectPluginInjected(exampleProjectPath, includeLegacyPluginsList: false);
613654
}, overrides: <Type, Generator>{
614655
Stdio: () => mockStdio,
615656
Pub: () => Pub.test(

packages/flutter_tools/test/general.shard/plugins_test.dart

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,7 @@ dependencies:
402402

403403
group('refreshPlugins', () {
404404
testUsingContext('Refreshing the plugin list is a no-op when the plugins list stays empty', () async {
405-
await refreshPluginsList(flutterProject);
405+
await refreshPluginsList(flutterProject, writeLegacyPluginsList: true);
406406

407407
expect(flutterProject.flutterPluginsFile.existsSync(), false);
408408
expect(flutterProject.flutterPluginsDependenciesFile.existsSync(), false);
@@ -415,7 +415,7 @@ dependencies:
415415
flutterProject.flutterPluginsFile.createSync();
416416
flutterProject.flutterPluginsDependenciesFile.createSync();
417417

418-
await refreshPluginsList(flutterProject);
418+
await refreshPluginsList(flutterProject, writeLegacyPluginsList: true);
419419

420420
expect(flutterProject.flutterPluginsFile.existsSync(), false);
421421
expect(flutterProject.flutterPluginsDependenciesFile.existsSync(), false);
@@ -434,7 +434,7 @@ dependencies:
434434

435435
iosProject.testExists = true;
436436

437-
await refreshPluginsList(flutterProject);
437+
await refreshPluginsList(flutterProject, writeLegacyPluginsList: true);
438438

439439
expect(flutterProject.flutterPluginsFile.existsSync(), true);
440440
expect(flutterProject.flutterPluginsDependenciesFile.existsSync(), true);
@@ -473,7 +473,7 @@ dependencies:
473473
final DateTime dateCreated = DateTime(1970);
474474
systemClock.currentTime = dateCreated;
475475

476-
await refreshPluginsList(flutterProject);
476+
await refreshPluginsList(flutterProject, writeLegacyPluginsList: true);
477477

478478
// Verify .flutter-plugins-dependencies is configured correctly.
479479
expect(flutterProject.flutterPluginsFile.existsSync(), true);
@@ -583,7 +583,7 @@ dependencies:
583583
final DateTime dateCreated = DateTime(1970);
584584
systemClock.currentTime = dateCreated;
585585

586-
await refreshPluginsList(flutterProject);
586+
await refreshPluginsList(flutterProject, writeLegacyPluginsList: true);
587587

588588
expect(flutterProject.flutterPluginsDependenciesFile.existsSync(), true);
589589
final String pluginsString = flutterProject.flutterPluginsDependenciesFile.readAsStringSync();
@@ -655,7 +655,7 @@ dependencies:
655655

656656
flutterProject.usesSwiftPackageManager = true;
657657

658-
await refreshPluginsList(flutterProject);
658+
await refreshPluginsList(flutterProject, writeLegacyPluginsList: true);
659659

660660
expect(flutterProject.flutterPluginsDependenciesFile.existsSync(), true);
661661
final String pluginsString = flutterProject.flutterPluginsDependenciesFile
@@ -692,7 +692,7 @@ dependencies:
692692

693693
flutterProject.usesSwiftPackageManager = true;
694694

695-
await refreshPluginsList(flutterProject, forceCocoaPodsOnly: true);
695+
await refreshPluginsList(flutterProject, forceCocoaPodsOnly: true, writeLegacyPluginsList: true);
696696

697697
expect(flutterProject.flutterPluginsDependenciesFile.existsSync(), true);
698698
final String pluginsString = flutterProject.flutterPluginsDependenciesFile
@@ -714,7 +714,7 @@ dependencies:
714714
iosProject.testExists = true;
715715
macosProject.exists = true;
716716

717-
await refreshPluginsList(flutterProject, iosPlatform: true, macOSPlatform: true);
717+
await refreshPluginsList(flutterProject, iosPlatform: true, macOSPlatform: true, writeLegacyPluginsList: true);
718718
expect(iosProject.podManifestLock.existsSync(), false);
719719
expect(macosProject.podManifestLock.existsSync(), false);
720720
}, overrides: <Type, Generator>{
@@ -733,11 +733,11 @@ dependencies:
733733
// Since there was no plugins list, the lock files will be invalidated.
734734
// The second call is where the plugins list is compared to the existing one, and if there is no change,
735735
// the podfiles shouldn't be invalidated.
736-
await refreshPluginsList(flutterProject, iosPlatform: true, macOSPlatform: true);
736+
await refreshPluginsList(flutterProject, iosPlatform: true, macOSPlatform: true, writeLegacyPluginsList: true);
737737
simulatePodInstallRun(iosProject);
738738
simulatePodInstallRun(macosProject);
739739

740-
await refreshPluginsList(flutterProject);
740+
await refreshPluginsList(flutterProject, writeLegacyPluginsList: true);
741741
expect(iosProject.podManifestLock.existsSync(), true);
742742
expect(macosProject.podManifestLock.existsSync(), true);
743743
}, overrides: <Type, Generator>{
@@ -1508,7 +1508,7 @@ The Flutter Preview device does not support the following plugins from your pubs
15081508
linuxProject.exists = true;
15091509
createFakePlugin(fs);
15101510
// refreshPluginsList should call createPluginSymlinks.
1511-
await refreshPluginsList(flutterProject);
1511+
await refreshPluginsList(flutterProject, writeLegacyPluginsList: true);
15121512

15131513
expect(linuxProject.pluginSymlinkDirectory.childLink('some_plugin').existsSync(), true);
15141514
}, overrides: <Type, Generator>{
@@ -1521,7 +1521,7 @@ The Flutter Preview device does not support the following plugins from your pubs
15211521
windowsProject.exists = true;
15221522
createFakePlugin(fs);
15231523
// refreshPluginsList should call createPluginSymlinks.
1524-
await refreshPluginsList(flutterProject);
1524+
await refreshPluginsList(flutterProject, writeLegacyPluginsList: true);
15251525

15261526
expect(windowsProject.pluginSymlinkDirectory.childLink('some_plugin').existsSync(), true);
15271527
}, overrides: <Type, Generator>{
@@ -1567,7 +1567,7 @@ The Flutter Preview device does not support the following plugins from your pubs
15671567

15681568
// refreshPluginsList should remove existing links and recreate on changes.
15691569
createFakePlugin(fs);
1570-
await refreshPluginsList(flutterProject);
1570+
await refreshPluginsList(flutterProject, writeLegacyPluginsList: true);
15711571

15721572
for (final File file in dummyFiles) {
15731573
expect(file.existsSync(), false);
@@ -1606,7 +1606,7 @@ The Flutter Preview device does not support the following plugins from your pubs
16061606
linuxProject.exists = true;
16071607
windowsProject.exists = true;
16081608
createFakePlugin(fs);
1609-
await refreshPluginsList(flutterProject);
1609+
await refreshPluginsList(flutterProject, writeLegacyPluginsList: true);
16101610

16111611
final List<Link> links = <Link>[
16121612
linuxProject.pluginSymlinkDirectory.childLink('some_plugin'),

0 commit comments

Comments
 (0)