[tool] Allow using archiveName in android bundle build (#162390)

fixes #126971 

Also, for some reason, fixes #147261 , which shouldn't have been ever
broken since this case tested here:

b007899d3a/packages/flutter_tools/test/general.shard/android/gradle_find_bundle_test.dart (L153-L173)

<!--
Thanks for filing a pull request!
Reviewers are typically assigned within a week of filing a request.
To learn more about code review, see our documentation on Tree Hygiene:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
-->

Key change here is replacing the optimistic approach of guessing the
filename based on buildInfo parameters with the actual discovery of
built artifacts on the filesystem. This change is needed because there
is no way the build can determine the archiveName from Gradle, which
differentiates this part from other parameters such as buildType and
flavor. Flutter build contains information about them, but not about the
base name.

<img width="673" alt="Screenshot 2025-01-29 at 22 42 29"
src="https://github.com/user-attachments/assets/ab530a00-031f-48e2-962c-ed010b4009c9"
/>


## 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].

<!-- Links -->
[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

---------

Co-authored-by: Reid Baker <hamilton.reid.baker@gmail.com>
Co-authored-by: Gray Mackall <34871572+gmackall@users.noreply.github.com>
Co-authored-by: Reid Baker <reidbaker@google.com>
This commit is contained in:
Mikhail Novoseltsev 2025-02-28 19:57:20 +05:00 committed by GitHub
parent 556ae54358
commit b8902e6a17
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 60 additions and 50 deletions

View File

@ -1149,58 +1149,47 @@ File findBundleFile(
Logger logger,
Analytics analytics,
) {
final List<File> fileCandidates = <File>[
getBundleDirectory(project).childDirectory(camelCase(buildInfo.modeName)).childFile('app.aab'),
getBundleDirectory(
project,
).childDirectory(camelCase(buildInfo.modeName)).childFile('app-${buildInfo.modeName}.aab'),
];
if (buildInfo.flavor != null) {
// The Android Gradle plugin 3.0.0 adds the flavor name to the path.
// For example: In release mode, if the flavor name is `foo_bar`, then
// the directory name is `foo_barRelease`.
fileCandidates.add(
getBundleDirectory(project)
.childDirectory('${buildInfo.lowerCasedFlavor}${camelCase('_${buildInfo.modeName}')}')
.childFile('app.aab'),
);
// The Android Gradle plugin 3.5.0 adds the flavor name to file name.
// For example: In release mode, if the flavor name is `foo_bar`, then
// the file name is `app-foo_bar-release.aab`.
fileCandidates.add(
getBundleDirectory(project)
.childDirectory('${buildInfo.lowerCasedFlavor}${camelCase('_${buildInfo.modeName}')}')
.childFile('app-${buildInfo.lowerCasedFlavor}-${buildInfo.modeName}.aab'),
);
// The Android Gradle plugin 4.1.0 does only lowercase the first character of flavor name.
fileCandidates.add(
getBundleDirectory(project)
.childDirectory('${buildInfo.uncapitalizedFlavor}${camelCase('_${buildInfo.modeName}')}')
.childFile('app-${buildInfo.uncapitalizedFlavor}-${buildInfo.modeName}.aab'),
);
// The Android Gradle plugin uses kebab-case and lowercases the first character of the flavor name
// when multiple flavor dimensions are used:
// e.g.
// flavorDimensions "dimension1","dimension2"
// productFlavors {
// foo {
// dimension "dimension1"
// }
// bar {
// dimension "dimension2"
// }
// }
fileCandidates.add(
getBundleDirectory(project)
.childDirectory('${buildInfo.uncapitalizedFlavor}${camelCase('_${buildInfo.modeName}')}')
.childFile('app-${kebabCase(buildInfo.uncapitalizedFlavor!)}-${buildInfo.modeName}.aab'),
final Directory bundleDir = getBundleDirectory(project);
if (!bundleDir.existsSync()) {
_exitWithExpectedFileNotFound(
project: project,
fileExtension: '.aab',
logger: logger,
analytics: analytics,
);
}
for (final File bundleFile in fileCandidates) {
if (bundleFile.existsSync()) {
final Iterable<File> allBundleFiles = bundleDir.listSync(recursive: true).whereType<File>().where(
(File file) {
return file.path.endsWith('.aab');
},
);
for (final File bundleFile in allBundleFiles) {
// Use lowercase bundle parent directory name to handle varying cases from Android Gradle Plugin
final String bundleParentDir = bundleFile.parent.basename.toLowerCase();
if (buildInfo.flavor != null) {
// Handle flavor builds (e.g., 'build/app/outputs/bundle/foo_barRelease/app-foo_bar-release.aab')
if (bundleParentDir.contains(
'${buildInfo.lowerCasedFlavor}${buildInfo.modeName.toLowerCase()}',
) &&
bundleFile.basename.endsWith('${buildInfo.modeName}.aab')) {
return bundleFile;
}
// Support legacy Android Gradle Plugin versions that don't include flavor in AAB filename
if (bundleParentDir.contains('${buildInfo.lowerCasedFlavor}${buildInfo.modeName}')) {
return bundleFile;
}
}
// Handle non-flavor builds (e.g., 'build/app/outputs/bundle/release/app-release.aab')
if (bundleParentDir == buildInfo.modeName &&
bundleFile.basename.endsWith('${buildInfo.modeName}.aab')) {
return bundleFile;
}
// Support legacy Android Gradle Plugin versions without build mode in AAB filename
if (bundleParentDir == buildInfo.modeName && bundleFile.basename.endsWith('aab')) {
return bundleFile;
}
}

View File

@ -621,6 +621,27 @@ void main() {
),
);
});
testWithoutContext(
'Finds app bundle when archiveName / archiveBaseName is not standard "app"',
() {
final FlutterProject project = generateFakeAppBundle('debug', 'foo-debug.aab', fileSystem);
final File bundle = findBundleFile(
project,
const BuildInfo(
BuildMode.debug,
null,
treeShakeIcons: false,
packageConfigPath: '.dart_tool/package_config.json',
),
BufferLogger.test(),
fakeAnalytics,
);
expect(bundle, isNotNull);
expect(bundle.path, '/build/app/outputs/bundle/debug/foo-debug.aab');
},
);
}
/// Generates a fake app bundle at the location [directoryName]/[fileName].