From 05f09c99373308dd6729d1c5f911d986ae6679e9 Mon Sep 17 00:00:00 2001 From: Alex Medinsh Date: Mon, 28 Apr 2025 19:35:11 +0300 Subject: [PATCH] Skip writing unchanged registrant files (#167262) This PR adds logic to skip overwriting plugin registrant files if their contents are unchanged. Fixes https://github.com/flutter/flutter/issues/134899 ## Pre-launch Checklist - [X] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [X] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [X] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [X] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [X] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md --- .../lib/src/flutter_plugins.dart | 13 ++++++++++ .../test/general.shard/plugins_test.dart | 25 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/packages/flutter_tools/lib/src/flutter_plugins.dart b/packages/flutter_tools/lib/src/flutter_plugins.dart index 883cf48cc96..94d469dcc1b 100644 --- a/packages/flutter_tools/lib/src/flutter_plugins.dart +++ b/packages/flutter_tools/lib/src/flutter_plugins.dart @@ -15,6 +15,7 @@ import 'base/file_system.dart'; import 'base/os.dart'; import 'base/platform.dart'; import 'base/template.dart'; +import 'base/utils.dart'; import 'base/version.dart'; import 'cache.dart'; import 'compute_dev_dependencies.dart'; @@ -30,6 +31,14 @@ import 'platform_plugins.dart'; import 'plugins.dart'; import 'project.dart'; +Future _fileContentsUnchanged(File file, String renderedTemplate) async { + if (!await file.exists()) { + return false; + } + final List fileBytes = await file.readAsBytes(); + return listEquals(fileBytes, renderedTemplate.codeUnits); +} + Future _renderTemplateToFile( String template, Object? context, @@ -37,6 +46,10 @@ Future _renderTemplateToFile( TemplateRenderer templateRenderer, ) async { final String renderedTemplate = templateRenderer.renderString(template, context); + if (await _fileContentsUnchanged(file, renderedTemplate)) { + globals.printTrace('Skipping generating ${file.basename} because it is up-to-date.'); + return; + } await file.create(recursive: true); await file.writeAsString(renderedTemplate); } diff --git a/packages/flutter_tools/test/general.shard/plugins_test.dart b/packages/flutter_tools/test/general.shard/plugins_test.dart index 005a79c0a3a..6aecaad791c 100644 --- a/packages/flutter_tools/test/general.shard/plugins_test.dart +++ b/packages/flutter_tools/test/general.shard/plugins_test.dart @@ -1455,6 +1455,31 @@ flutter: }, ); + testUsingContext( + 'Injecting does not overwrite unchanged registrant files', + () async { + createFakePlugin(fs); + + await injectPlugins(flutterProject, releaseMode: false, linuxPlatform: true); + + final File registrantHeader = linuxProject.managedDirectory.childFile( + 'generated_plugin_registrant.h', + ); + final DateTime headerLastModified = registrantHeader.lastModifiedSync(); + + await injectPlugins(flutterProject, releaseMode: false, linuxPlatform: true); + + // Check that the last modified date is the same. + expect(registrantHeader.lastModifiedSync(), headerLastModified); + }, + overrides: { + FileSystem: () => fs, + ProcessManager: () => FakeProcessManager.any(), + FeatureFlags: enableExplicitPackageDependencies, + Pub: FakePubWithPrimedDeps.new, + }, + ); + testUsingContext( 'Injecting creates generated macos registrant, but does not include Dart-only plugins', () async {