Make releaseMode explicit, inform determineDevDependencies entirely on the flag (#163780)

Closes https://github.com/flutter/flutter/issues/163770.

This simplifies understanding "when I called function X, how is it
determining release mode and/or dev-dependencies?"

AFAICT, at HEAD, this is a NOP (will let integration tests run), but
would have avoided cases such as
https://github.com/flutter/flutter/issues/163706.
This commit is contained in:
Matan Lurey 2025-02-20 16:07:48 -08:00 committed by GitHub
parent 0f586af401
commit f6543c3d45
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 101 additions and 72 deletions

View File

@ -522,7 +522,16 @@ class CreateCommand extends FlutterCommand with CreateBase {
// TODO(dacoharkes): Uncouple the app and parent project platforms. https://github.com/flutter/flutter/issues/133874
// Then this if can be removed.
if (!generateFfiPackage) {
// TODO(matanlurey): https://github.com/flutter/flutter/issues/163774.
//
// `flutter packages get` inherently is neither a debug or release build,
// and since a future build (`flutter build apk`) will regenerate tooling
// anyway, we assume this is fine.
//
// It won't be if they do `flutter build --no-pub`, though.
const bool ignoreReleaseModeSinceItsNotABuildAndHopeItWorks = false;
await project.ensureReadyForPlatformSpecificTooling(
releaseMode: ignoreReleaseModeSinceItsNotABuildAndHopeItWorks,
androidPlatform: includeAndroid,
iosPlatform: includeIos,
linuxPlatform: includeLinux,

View File

@ -370,12 +370,25 @@ class PackagesGetCommand extends FlutterCommand {
}
if (rootProject != null) {
// TODO(matanlurey): https://github.com/flutter/flutter/issues/163774.
//
// `flutter packages get` inherently is neither a debug or release build,
// and since a future build (`flutter build apk`) will regenerate tooling
// anyway, we assume this is fine.
//
// It won't be if they do `flutter build --no-pub`, though.
const bool ignoreReleaseModeSinceItsNotABuildAndHopeItWorks = false;
// We need to regenerate the platform specific tooling for both the project
// itself and example(if present).
await rootProject.regeneratePlatformSpecificTooling();
await rootProject.regeneratePlatformSpecificTooling(
releaseMode: ignoreReleaseModeSinceItsNotABuildAndHopeItWorks,
);
if (example && rootProject.hasExampleApp && rootProject.example.pubspecFile.existsSync()) {
final FlutterProject exampleProject = rootProject.example;
await exampleProject.regeneratePlatformSpecificTooling();
await exampleProject.regeneratePlatformSpecificTooling(
releaseMode: ignoreReleaseModeSinceItsNotABuildAndHopeItWorks,
);
}
}

View File

@ -300,6 +300,7 @@ final class WidgetPreviewStartCommand extends WidgetPreviewSubCommandBase with C
Future<void> initialBuild({required FlutterProject widgetPreviewScaffoldProject}) async {
// TODO(bkonyi): handle error case where desktop device isn't enabled.
await widgetPreviewScaffoldProject.ensureReadyForPlatformSpecificTooling(
releaseMode: false,
linuxPlatform: platform.isLinux && !isWeb,
macOSPlatform: platform.isMacOS && !isWeb,
windowsPlatform: platform.isWindows && !isWeb,

View File

@ -89,12 +89,7 @@ Future<Plugin?> _pluginFromPackage(
/// Returns a list of all plugins to be registered with the provided [project].
///
/// If [throwOnError] is `true`, an empty package configuration is an error.
Future<List<Plugin>> findPlugins(
FlutterProject project, {
bool throwOnError = true,
bool? determineDevDependencies,
}) async {
determineDevDependencies ??= featureFlags.isExplicitPackageDependenciesEnabled;
Future<List<Plugin>> findPlugins(FlutterProject project, {bool throwOnError = true}) async {
final List<Plugin> plugins = <Plugin>[];
final FileSystem fs = project.directory.fileSystem;
final File packageConfigFile = findPackageConfigFileOrDefault(project.directory);
@ -104,7 +99,7 @@ Future<List<Plugin>> findPlugins(
throwOnError: throwOnError,
);
final Set<String> devDependencies;
if (!determineDevDependencies) {
if (!featureFlags.isExplicitPackageDependenciesEnabled) {
devDependencies = <String>{};
} else {
devDependencies = await computeExclusiveDevDependencies(
@ -1105,13 +1100,9 @@ Future<void> refreshPluginsList(
bool iosPlatform = false,
bool macOSPlatform = false,
bool forceCocoaPodsOnly = false,
bool? determineDevDependencies,
bool? generateLegacyPlugins,
}) async {
final List<Plugin> plugins = await findPlugins(
project,
determineDevDependencies: determineDevDependencies,
);
final List<Plugin> plugins = await findPlugins(project);
// Sort the plugins by name to keep ordering stable in generated files.
plugins.sort((Plugin left, Plugin right) => left.name.compareTo(right.name));
// TODO(matanlurey): Remove once migration is complete.
@ -1188,19 +1179,23 @@ Future<void> injectBuildTimePluginFilesForWebPlatform(
/// current build (temp) directory, and doesn't modify the users' working copy.
///
/// Assumes [refreshPluginsList] has been called since last change to `pubspec.yaml`.
///
/// If [releaseMode] is `true`, platform-specific tooling and metadata generated
/// may apply optimizations or changes that are only specific to release builds,
/// such as not including dev-only dependencies.
Future<void> injectPlugins(
FlutterProject project, {
required bool releaseMode,
bool androidPlatform = false,
bool iosPlatform = false,
bool linuxPlatform = false,
bool macOSPlatform = false,
bool windowsPlatform = false,
DarwinDependencyManagement? darwinDependencyManagement,
bool? releaseMode,
}) async {
List<Plugin> plugins = await findPlugins(project);
if (releaseMode ?? false) {
if (releaseMode) {
plugins = plugins.where((Plugin p) => !p.isDevDependency).toList();
}

View File

@ -5,7 +5,6 @@
import '../base/fingerprint.dart';
import '../build_info.dart';
import '../cache.dart';
import '../features.dart';
import '../flutter_plugins.dart';
import '../globals.dart' as globals;
import '../plugins.dart';
@ -36,13 +35,6 @@ Future<void> processPodsIfNeeded(
macOSPlatform: project.macos.existsSync(),
forceCocoaPodsOnly: forceCocoaPodsOnly,
// TODO(matanlurey): Ideally processPodsIfNeeded would not be used at all, and it would definitely
// not call refreshPluginsList, but until that happens (https://github.com/flutter/flutter/issues/157391)
// we have to reproduce some of the work that otherwise would be handled by underlying commands, otherwise
// this call to refreshPluginsList would overwrite the correct plugins list generated elsewhere.
determineDevDependencies:
featureFlags.isExplicitPackageDependenciesEnabled && buildMode.isRelease,
// TODO(matanlurey): As-per discussion on https://github.com/flutter/flutter/pull/157393
// we'll assume that iOS/MacOS builds do not use or rely on the `.flutter-plugins` legacy
// file being generated. A better long-term fix would be not to have a call to refreshPluginsList

View File

@ -325,9 +325,13 @@ class FlutterProject {
/// registrants for app and module projects only.
///
/// Will not create project platform directories if they do not already exist.
///
/// If [releaseMode] is `true`, platform-specific tooling and metadata generated
/// may apply optimizations or changes that are only specific to release builds,
/// such as not including dev-only dependencies.
Future<void> regeneratePlatformSpecificTooling({
DeprecationBehavior deprecationBehavior = DeprecationBehavior.none,
bool? releaseMode,
required bool releaseMode,
}) async {
return ensureReadyForPlatformSpecificTooling(
androidPlatform: android.existsSync(),
@ -345,7 +349,12 @@ class FlutterProject {
/// Applies template files and generates project files and plugin
/// registrants for app and module projects only for the specified platforms.
///
/// If [releaseMode] is `true`, platform-specific tooling and metadata generated
/// may apply optimizations or changes that are only specific to release builds,
/// such as not including dev-only dependencies.
Future<void> ensureReadyForPlatformSpecificTooling({
required bool releaseMode,
bool androidPlatform = false,
bool iosPlatform = false,
bool linuxPlatform = false,
@ -353,7 +362,6 @@ class FlutterProject {
bool windowsPlatform = false,
bool webPlatform = false,
DeprecationBehavior deprecationBehavior = DeprecationBehavior.none,
bool? releaseMode,
}) async {
if (!directory.existsSync() || isPlugin) {
return;

View File

@ -1851,7 +1851,10 @@ Run 'flutter -h' (or 'flutter <command> -h') for available flutter commands and
}
if (regeneratePlatformSpecificToolingDuringVerify) {
await regeneratePlatformSpecificToolingIfApplicable(project);
await regeneratePlatformSpecificToolingIfApplicable(
project,
releaseMode: getBuildMode().isRelease,
);
}
setupApplicationPackages();
@ -1873,10 +1876,6 @@ Run 'flutter -h' (or 'flutter <command> -h') for available flutter commands and
/// Runs [FlutterProject.regeneratePlatformSpecificTooling] for [project] with appropriate configuration.
///
/// By default, this uses [getBuildMode] to determine and provide whether a release build is being made,
/// but sub-commands (such as commands that do _meta_ builds, or builds that make multiple different builds
/// sequentially in one-go) may choose to overide this and make the call at a different point in time.
///
/// This method should only be called when [shouldRunPub] is `true`:
/// ```dart
/// if (shouldRunPub) {
@ -1891,13 +1890,11 @@ Run 'flutter -h' (or 'flutter <command> -h') for available flutter commands and
@nonVirtual
Future<void> regeneratePlatformSpecificToolingIfApplicable(
FlutterProject project, {
bool? releaseMode,
required bool releaseMode,
}) async {
if (!shouldRunPub) {
return;
}
releaseMode ??= getBuildMode().isRelease;
await project.regeneratePlatformSpecificTooling(
releaseMode: featureFlags.isExplicitPackageDependenciesEnabled && releaseMode,
);

View File

@ -410,7 +410,7 @@ void main() {
final FlutterProject project = FlutterProject.fromDirectoryTest(
fileSystem.directory('project'),
);
await injectPlugins(project, iosPlatform: true);
await injectPlugins(project, iosPlatform: true, releaseMode: false);
final String debugContents =
projectUnderTest.ios.xcodeConfigFor('Debug').readAsStringSync();

View File

@ -1012,7 +1012,7 @@ dependencies:
() async {
androidProject.embeddingVersion = AndroidEmbeddingVersion.v2;
await injectPlugins(flutterProject, androidPlatform: true);
await injectPlugins(flutterProject, androidPlatform: true, releaseMode: false);
final File registrant = flutterProject.directory
.childDirectory(
@ -1046,7 +1046,7 @@ dependencies:
await expectLater(
() async {
await injectPlugins(flutterProject, androidPlatform: true);
await injectPlugins(flutterProject, androidPlatform: true, releaseMode: false);
},
throwsToolExit(
message:
@ -1075,7 +1075,7 @@ dependencies:
createDualSupportJavaPlugin4();
await injectPlugins(flutterProject, androidPlatform: true);
await injectPlugins(flutterProject, androidPlatform: true, releaseMode: false);
final File registrant = flutterProject.directory
.childDirectory(
@ -1106,7 +1106,7 @@ dependencies:
flutterProject.isModule = true;
androidProject.embeddingVersion = AndroidEmbeddingVersion.v2;
await injectPlugins(flutterProject, androidPlatform: true);
await injectPlugins(flutterProject, androidPlatform: true, releaseMode: false);
final File registrant = flutterProject.directory
.childDirectory(
@ -1138,7 +1138,7 @@ dependencies:
createNewJavaPlugin1();
await injectPlugins(flutterProject, androidPlatform: true);
await injectPlugins(flutterProject, androidPlatform: true, releaseMode: false);
final File registrant = flutterProject.directory
.childDirectory(
@ -1169,7 +1169,7 @@ dependencies:
createDualSupportJavaPlugin4();
await injectPlugins(flutterProject, androidPlatform: true);
await injectPlugins(flutterProject, androidPlatform: true, releaseMode: false);
final File registrant = flutterProject.directory
.childDirectory(
@ -1200,7 +1200,7 @@ dependencies:
createDualSupportJavaPlugin4();
await injectPlugins(flutterProject, androidPlatform: true);
await injectPlugins(flutterProject, androidPlatform: true, releaseMode: false);
final File registrant = flutterProject.directory
.childDirectory(
@ -1228,7 +1228,7 @@ dependencies:
() async {
final File manifest = fs.file('AndroidManifest.xml');
androidProject.appManifestFile = manifest;
await injectPlugins(flutterProject, androidPlatform: true);
await injectPlugins(flutterProject, androidPlatform: true, releaseMode: false);
},
overrides: <Type, Generator>{
FileSystem: () => fs,
@ -1372,7 +1372,7 @@ flutter:
dartPluginClass: SomePlugin
''');
await injectPlugins(flutterProject, androidPlatform: true);
await injectPlugins(flutterProject, androidPlatform: true, releaseMode: false);
final File registrantFile = androidProject.pluginRegistrantHost
.childDirectory(fs.path.join('src', 'main', 'java', 'io', 'flutter', 'plugins'))
@ -1406,6 +1406,7 @@ flutter:
FakeDarwinDependencyManagement();
await injectPlugins(
flutterProject,
releaseMode: false,
iosPlatform: true,
darwinDependencyManagement: dependencyManagement,
);
@ -1440,6 +1441,7 @@ flutter:
FakeDarwinDependencyManagement();
await injectPlugins(
flutterProject,
releaseMode: false,
macOSPlatform: true,
darwinDependencyManagement: dependencyManagement,
);
@ -1477,6 +1479,7 @@ flutter:
FakeDarwinDependencyManagement();
await injectPlugins(
flutterProject,
releaseMode: false,
macOSPlatform: true,
darwinDependencyManagement: dependencyManagement,
);
@ -1510,6 +1513,7 @@ flutter:
FakeDarwinDependencyManagement();
await injectPlugins(
flutterProject,
releaseMode: false,
macOSPlatform: true,
darwinDependencyManagement: dependencyManagement,
);
@ -1533,7 +1537,7 @@ flutter:
() async {
createFakePlugin(fs);
await injectPlugins(flutterProject, linuxPlatform: true);
await injectPlugins(flutterProject, releaseMode: false, linuxPlatform: true);
final File registrantHeader = linuxProject.managedDirectory.childFile(
'generated_plugin_registrant.h',
@ -1596,7 +1600,7 @@ dependencies:
flutterProject.manifest = manifest;
await injectPlugins(flutterProject, linuxPlatform: true);
await injectPlugins(flutterProject, releaseMode: false, linuxPlatform: true);
final File registrantImpl = linuxProject.managedDirectory.childFile(
'generated_plugin_registrant.cc',
@ -1669,7 +1673,7 @@ dependencies:
flutterProject.manifest = manifest;
await injectPlugins(flutterProject, linuxPlatform: true);
await injectPlugins(flutterProject, releaseMode: false, linuxPlatform: true);
final File registrantImpl = linuxProject.managedDirectory.childFile(
'generated_plugin_registrant.cc',
@ -1706,7 +1710,7 @@ flutter:
dartPluginClass: SomePlugin
''');
await injectPlugins(flutterProject, linuxPlatform: true);
await injectPlugins(flutterProject, releaseMode: false, linuxPlatform: true);
final File registrantImpl = linuxProject.managedDirectory.childFile(
'generated_plugin_registrant.cc',
@ -1738,7 +1742,7 @@ flutter:
dartPluginClass: SomePlugin
''');
await injectPlugins(flutterProject, linuxPlatform: true);
await injectPlugins(flutterProject, releaseMode: false, linuxPlatform: true);
final File registrantImpl = linuxProject.managedDirectory.childFile(
'generated_plugin_registrant.cc',
@ -1761,7 +1765,7 @@ flutter:
() async {
createFakePlugin(fs);
await injectPlugins(flutterProject, linuxPlatform: true);
await injectPlugins(flutterProject, releaseMode: false, linuxPlatform: true);
final File pluginMakefile = linuxProject.generatedPluginCmakeFile;
@ -1799,7 +1803,7 @@ flutter:
'/local_plugins/plugin_b',
]);
await injectPlugins(flutterProject, linuxPlatform: true);
await injectPlugins(flutterProject, releaseMode: false, linuxPlatform: true);
final File pluginCmakeFile = linuxProject.generatedPluginCmakeFile;
final File pluginRegistrant = linuxProject.managedDirectory.childFile(
@ -1825,7 +1829,7 @@ flutter:
() async {
createFakePlugin(fs);
await injectPlugins(flutterProject, windowsPlatform: true);
await injectPlugins(flutterProject, releaseMode: false, windowsPlatform: true);
final File registrantHeader = windowsProject.managedDirectory.childFile(
'generated_plugin_registrant.h',
@ -1859,7 +1863,7 @@ flutter:
dartPluginClass: SomePlugin
''');
await injectPlugins(flutterProject, windowsPlatform: true);
await injectPlugins(flutterProject, releaseMode: false, windowsPlatform: true);
final File registrantImpl = windowsProject.managedDirectory.childFile(
'generated_plugin_registrant.cc',
@ -1890,7 +1894,7 @@ flutter:
dartPluginClass: SomePlugin
''');
await injectPlugins(flutterProject, windowsPlatform: true);
await injectPlugins(flutterProject, releaseMode: false, windowsPlatform: true);
final File registrantImpl = windowsProject.managedDirectory.childFile(
'generated_plugin_registrant.cc',
@ -1918,7 +1922,7 @@ flutter:
'/local_plugins/plugin_b',
]);
await injectPlugins(flutterProject, windowsPlatform: true);
await injectPlugins(flutterProject, releaseMode: false, windowsPlatform: true);
final File pluginCmakeFile = windowsProject.generatedPluginCmakeFile;
final File pluginRegistrant = windowsProject.managedDirectory.childFile(
@ -1946,7 +1950,12 @@ flutter:
setUpProject(fsWindows);
createFakePlugin(fsWindows);
await injectPlugins(flutterProject, linuxPlatform: true, windowsPlatform: true);
await injectPlugins(
flutterProject,
releaseMode: false,
linuxPlatform: true,
windowsPlatform: true,
);
for (final CmakeBasedProject? project in <CmakeBasedProject?>[
linuxProject,
@ -1974,6 +1983,7 @@ flutter:
FakeDarwinDependencyManagement();
await injectPlugins(
flutterProject,
releaseMode: false,
iosPlatform: true,
macOSPlatform: true,
darwinDependencyManagement: dependencyManagement,
@ -1996,7 +2006,11 @@ flutter:
() async {
final FakeDarwinDependencyManagement dependencyManagement =
FakeDarwinDependencyManagement();
await injectPlugins(flutterProject, darwinDependencyManagement: dependencyManagement);
await injectPlugins(
flutterProject,
releaseMode: false,
darwinDependencyManagement: dependencyManagement,
);
expect(dependencyManagement.setupPlatforms, <SupportedPlatform>[]);
},
overrides: <Type, Generator>{

View File

@ -125,12 +125,12 @@ void main() {
FlutterManifest.empty(logger: logger),
FlutterManifest.empty(logger: logger),
);
await project.regeneratePlatformSpecificTooling();
await project.regeneratePlatformSpecificTooling(releaseMode: false);
expectNotExists(project.directory);
});
_testInMemory('does nothing in plugin or package root project', () async {
final FlutterProject project = await aPluginProject();
await project.regeneratePlatformSpecificTooling();
await project.regeneratePlatformSpecificTooling(releaseMode: false);
expectNotExists(
project.ios.hostAppRoot.childDirectory('Runner').childFile('GeneratedPluginRegistrant.h'),
);
@ -148,7 +148,7 @@ void main() {
// that a project was a plugin, but shouldn't be as this creates false
// positives.
project.directory.childDirectory('example').createSync();
await project.regeneratePlatformSpecificTooling();
await project.regeneratePlatformSpecificTooling(releaseMode: false);
expectExists(
project.ios.hostAppRoot.childDirectory('Runner').childFile('GeneratedPluginRegistrant.h'),
);
@ -162,28 +162,28 @@ void main() {
});
_testInMemory('injects plugins for iOS', () async {
final FlutterProject project = await someProject();
await project.regeneratePlatformSpecificTooling();
await project.regeneratePlatformSpecificTooling(releaseMode: false);
expectExists(
project.ios.hostAppRoot.childDirectory('Runner').childFile('GeneratedPluginRegistrant.h'),
);
});
_testInMemory('generates Xcode configuration for iOS', () async {
final FlutterProject project = await someProject();
await project.regeneratePlatformSpecificTooling();
await project.regeneratePlatformSpecificTooling(releaseMode: false);
expectExists(
project.ios.hostAppRoot.childDirectory('Flutter').childFile('Generated.xcconfig'),
);
});
_testInMemory('injects plugins for Android', () async {
final FlutterProject project = await someProject();
await project.regeneratePlatformSpecificTooling();
await project.regeneratePlatformSpecificTooling(releaseMode: false);
expectExists(
androidPluginRegistrant(project.android.hostAppGradleRoot.childDirectory('app')),
);
});
_testInMemory('updates local properties for Android', () async {
final FlutterProject project = await someProject();
await project.regeneratePlatformSpecificTooling();
await project.regeneratePlatformSpecificTooling(releaseMode: false);
expectExists(project.android.hostAppGradleRoot.childFile('local.properties'));
});
_testInMemory('checkForDeprecation fails on invalid android app manifest file', () async {
@ -257,7 +257,7 @@ void main() {
final FlutterProject project = await aPluginProject();
project.example.directory.deleteSync();
await project.regeneratePlatformSpecificTooling();
await project.regeneratePlatformSpecificTooling(releaseMode: false);
expect(
testLogger.statusText,
isNot(
@ -269,7 +269,7 @@ void main() {
});
_testInMemory('updates local properties for Android', () async {
final FlutterProject project = await someProject();
await project.regeneratePlatformSpecificTooling();
await project.regeneratePlatformSpecificTooling(releaseMode: false);
expectExists(project.android.hostAppGradleRoot.childFile('local.properties'));
});
testUsingContext(
@ -277,7 +277,7 @@ void main() {
() async {
final FlutterProject project = await someProject();
project.macos.managedDirectory.createSync(recursive: true);
await project.regeneratePlatformSpecificTooling();
await project.regeneratePlatformSpecificTooling(releaseMode: false);
expectExists(project.macos.pluginRegistrantImplementation);
},
overrides: <Type, Generator>{
@ -293,7 +293,7 @@ void main() {
() async {
final FlutterProject project = await someProject();
project.macos.managedDirectory.createSync(recursive: true);
await project.regeneratePlatformSpecificTooling();
await project.regeneratePlatformSpecificTooling(releaseMode: false);
expectExists(project.macos.generatedXcodePropertiesFile);
},
overrides: <Type, Generator>{
@ -309,7 +309,7 @@ void main() {
() async {
final FlutterProject project = await someProject();
project.linux.cmakeFile.createSync(recursive: true);
await project.regeneratePlatformSpecificTooling();
await project.regeneratePlatformSpecificTooling(releaseMode: false);
expectExists(project.linux.managedDirectory.childFile('generated_plugin_registrant.h'));
expectExists(project.linux.managedDirectory.childFile('generated_plugin_registrant.cc'));
},
@ -326,7 +326,7 @@ void main() {
() async {
final FlutterProject project = await someProject();
project.windows.cmakeFile.createSync(recursive: true);
await project.regeneratePlatformSpecificTooling();
await project.regeneratePlatformSpecificTooling(releaseMode: false);
expectExists(project.windows.managedDirectory.childFile('generated_plugin_registrant.h'));
expectExists(
project.windows.managedDirectory.childFile('generated_plugin_registrant.cc'),
@ -342,7 +342,7 @@ void main() {
);
_testInMemory('creates Android library in module', () async {
final FlutterProject project = await aModuleProject();
await project.regeneratePlatformSpecificTooling();
await project.regeneratePlatformSpecificTooling(releaseMode: false);
expectExists(project.android.hostAppGradleRoot.childFile('settings.gradle'));
expectExists(project.android.hostAppGradleRoot.childFile('local.properties'));
expectExists(
@ -351,7 +351,7 @@ void main() {
});
_testInMemory('creates iOS pod in module', () async {
final FlutterProject project = await aModuleProject();
await project.regeneratePlatformSpecificTooling();
await project.regeneratePlatformSpecificTooling(releaseMode: false);
final Directory flutter = project.ios.hostAppRoot.childDirectory('Flutter');
expectExists(flutter.childFile('podhelper.rb'));
expectExists(flutter.childFile('flutter_export_environment.sh'));