diff --git a/packages/flutter_tools/lib/src/android/gradle.dart b/packages/flutter_tools/lib/src/android/gradle.dart index 03d2148f6e..cf73383236 100644 --- a/packages/flutter_tools/lib/src/android/gradle.dart +++ b/packages/flutter_tools/lib/src/android/gradle.dart @@ -819,28 +819,36 @@ Iterable findApkFiles(GradleProject project, AndroidBuildInfo androidBuild @visibleForTesting File findBundleFile(GradleProject project, BuildInfo buildInfo) { - final String bundleFileName = project.bundleFileFor(buildInfo); - if (bundleFileName == null) { - return null; + final List fileCandidates = [ + project.bundleDirectory + .childDirectory(camelCase(buildInfo.modeName)) + .childFile('app.aab'), + project.bundleDirectory + .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( + project.bundleDirectory + .childDirectory('${buildInfo.flavor}${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 name is `app-foo_bar-release.aab`. + fileCandidates.add( + project.bundleDirectory + .childDirectory('${buildInfo.flavor}${camelCase('_' + buildInfo.modeName)}') + .childFile('app-${buildInfo.flavor}-${buildInfo.modeName}.aab')); } - File bundleFile = project.bundleDirectory - .childDirectory(camelCase(buildInfo.modeName)) - .childFile(bundleFileName); - if (bundleFile.existsSync()) { - return bundleFile; - } - if (buildInfo.flavor == null) { - return null; - } - // Android Studio Gradle plugin v3 adds the flavor to the path. For the bundle the - // folder name is the flavor plus the mode name. On Windows, filenames aren't case sensitive. - // For example: foo_barRelease where `foo_bar` is the flavor and `Release` the mode name. - final String childDirName = '${buildInfo.flavor}${camelCase('_' + buildInfo.modeName)}'; - bundleFile = project.bundleDirectory - .childDirectory(childDirName) - .childFile(bundleFileName); - if (bundleFile.existsSync()) { - return bundleFile; + for (final File bundleFile in fileCandidates) { + if (bundleFile.existsSync()) { + return bundleFile; + } } return null; } @@ -984,10 +992,4 @@ class GradleProject { return null; return 'assembleAar${toTitleCase(productFlavor)}${toTitleCase(buildType)}'; } - - String bundleFileFor(BuildInfo buildInfo) { - // For app bundle all bundle names are called as app.aab. Product flavors - // & build types are differentiated as folders, where the aab will be added. - return 'app.aab'; - } } diff --git a/packages/flutter_tools/test/general.shard/android/gradle_test.dart b/packages/flutter_tools/test/general.shard/android/gradle_test.dart index 623c0a8497..79a6a72639 100644 --- a/packages/flutter_tools/test/general.shard/android/gradle_test.dart +++ b/packages/flutter_tools/test/general.shard/android/gradle_test.dart @@ -119,14 +119,7 @@ void main() { }); testUsingContext('Finds app bundle when flavor contains underscores in release mode', () { - final GradleProject gradleProject = MockGradleProject(); - when(gradleProject.bundleDirectory).thenReturn(fs.currentDirectory); - when(gradleProject.bundleFileFor(any)).thenReturn('app.aab'); - - final Directory aabDirectory = gradleProject.bundleDirectory.childDirectory('foo_barRelease'); - fs.directory(aabDirectory).createSync(recursive: true); - fs.file(fs.path.join(aabDirectory.path, 'app.aab')).writeAsStringSync('irrelevant'); - + final GradleProject gradleProject = generateFakeAppBundle('foo_barRelease', 'app.aab'); final File bundle = findBundleFile(gradleProject, const BuildInfo(BuildMode.release, 'foo_bar')); expect(bundle, isNotNull); expect(bundle.path, '/foo_barRelease/app.aab'); @@ -135,14 +128,7 @@ void main() { }); testUsingContext('Finds app bundle when flavor doesn\'t contain underscores in release mode', () { - final GradleProject gradleProject = MockGradleProject(); - when(gradleProject.bundleDirectory).thenReturn(fs.currentDirectory); - when(gradleProject.bundleFileFor(any)).thenReturn('app.aab'); - - final Directory aabDirectory = gradleProject.bundleDirectory.childDirectory('fooRelease'); - fs.directory(aabDirectory).createSync(recursive: true); - fs.file(fs.path.join(aabDirectory.path, 'app.aab')).writeAsStringSync('irrelevant'); - + final GradleProject gradleProject = generateFakeAppBundle('fooRelease', 'app.aab'); final File bundle = findBundleFile(gradleProject, const BuildInfo(BuildMode.release, 'foo')); expect(bundle, isNotNull); expect(bundle.path, '/fooRelease/app.aab'); @@ -151,14 +137,7 @@ void main() { }); testUsingContext('Finds app bundle when no flavor is used in release mode', () { - final GradleProject gradleProject = MockGradleProject(); - when(gradleProject.bundleDirectory).thenReturn(fs.currentDirectory); - when(gradleProject.bundleFileFor(any)).thenReturn('app.aab'); - - final Directory aabDirectory = gradleProject.bundleDirectory.childDirectory('release'); - fs.directory(aabDirectory).createSync(recursive: true); - fs.file(fs.path.join(aabDirectory.path, 'app.aab')).writeAsStringSync('irrelevant'); - + final GradleProject gradleProject = generateFakeAppBundle('release', 'app.aab'); final File bundle = findBundleFile(gradleProject, const BuildInfo(BuildMode.release, null)); expect(bundle, isNotNull); expect(bundle.path, '/release/app.aab'); @@ -167,14 +146,7 @@ void main() { }); testUsingContext('Finds app bundle when flavor contains underscores in debug mode', () { - final GradleProject gradleProject = MockGradleProject(); - when(gradleProject.bundleDirectory).thenReturn(fs.currentDirectory); - when(gradleProject.bundleFileFor(any)).thenReturn('app.aab'); - - final Directory aabDirectory = gradleProject.bundleDirectory.childDirectory('foo_barDebug'); - fs.directory(aabDirectory).createSync(recursive: true); - fs.file(fs.path.join(aabDirectory.path, 'app.aab')).writeAsStringSync('irrelevant'); - + final GradleProject gradleProject = generateFakeAppBundle('foo_barDebug', 'app.aab'); final File bundle = findBundleFile(gradleProject, const BuildInfo(BuildMode.debug, 'foo_bar')); expect(bundle, isNotNull); expect(bundle.path, '/foo_barDebug/app.aab'); @@ -183,14 +155,7 @@ void main() { }); testUsingContext('Finds app bundle when flavor doesn\'t contain underscores in debug mode', () { - final GradleProject gradleProject = MockGradleProject(); - when(gradleProject.bundleDirectory).thenReturn(fs.currentDirectory); - when(gradleProject.bundleFileFor(any)).thenReturn('app.aab'); - - final Directory aabDirectory = gradleProject.bundleDirectory.childDirectory('fooDebug'); - fs.directory(aabDirectory).createSync(recursive: true); - fs.file(fs.path.join(aabDirectory.path, 'app.aab')).writeAsStringSync('irrelevant'); - + final GradleProject gradleProject = generateFakeAppBundle('fooDebug', 'app.aab'); final File bundle = findBundleFile(gradleProject, const BuildInfo(BuildMode.debug, 'foo')); expect(bundle, isNotNull); expect(bundle.path, '/fooDebug/app.aab'); @@ -199,14 +164,7 @@ void main() { }); testUsingContext('Finds app bundle when no flavor is used in debug mode', () { - final GradleProject gradleProject = MockGradleProject(); - when(gradleProject.bundleDirectory).thenReturn(fs.currentDirectory); - when(gradleProject.bundleFileFor(any)).thenReturn('app.aab'); - - final Directory aabDirectory = gradleProject.bundleDirectory.childDirectory('debug'); - fs.directory(aabDirectory).createSync(recursive: true); - fs.file(fs.path.join(aabDirectory.path, 'app.aab')).writeAsStringSync('irrelevant'); - + final GradleProject gradleProject = generateFakeAppBundle('debug', 'app.aab'); final File bundle = findBundleFile(gradleProject, const BuildInfo(BuildMode.debug, null)); expect(bundle, isNotNull); expect(bundle.path, '/debug/app.aab'); @@ -215,14 +173,7 @@ void main() { }); testUsingContext('Finds app bundle when flavor contains underscores in profile mode', () { - final GradleProject gradleProject = MockGradleProject(); - when(gradleProject.bundleDirectory).thenReturn(fs.currentDirectory); - when(gradleProject.bundleFileFor(any)).thenReturn('app.aab'); - - final Directory aabDirectory = gradleProject.bundleDirectory.childDirectory('foo_barProfile'); - fs.directory(aabDirectory).createSync(recursive: true); - fs.file(fs.path.join(aabDirectory.path, 'app.aab')).writeAsStringSync('irrelevant'); - + final GradleProject gradleProject = generateFakeAppBundle('foo_barProfile', 'app.aab'); final File bundle = findBundleFile(gradleProject, const BuildInfo(BuildMode.profile, 'foo_bar')); expect(bundle, isNotNull); expect(bundle.path, '/foo_barProfile/app.aab'); @@ -231,14 +182,7 @@ void main() { }); testUsingContext('Finds app bundle when flavor doesn\'t contain underscores in profile mode', () { - final GradleProject gradleProject = MockGradleProject(); - when(gradleProject.bundleDirectory).thenReturn(fs.currentDirectory); - when(gradleProject.bundleFileFor(any)).thenReturn('app.aab'); - - final Directory aabDirectory = gradleProject.bundleDirectory.childDirectory('fooProfile'); - fs.directory(aabDirectory).createSync(recursive: true); - fs.file(fs.path.join(aabDirectory.path, 'app.aab')).writeAsStringSync('irrelevant'); - + final GradleProject gradleProject = generateFakeAppBundle('fooProfile', 'app.aab'); final File bundle = findBundleFile(gradleProject, const BuildInfo(BuildMode.profile, 'foo')); expect(bundle, isNotNull); expect(bundle.path, '/fooProfile/app.aab'); @@ -247,20 +191,67 @@ void main() { }); testUsingContext('Finds app bundle when no flavor is used in profile mode', () { - final GradleProject gradleProject = MockGradleProject(); - when(gradleProject.bundleDirectory).thenReturn(fs.currentDirectory); - when(gradleProject.bundleFileFor(any)).thenReturn('app.aab'); - - final Directory aabDirectory = gradleProject.bundleDirectory.childDirectory('profile'); - fs.directory(aabDirectory).createSync(recursive: true); - fs.file(fs.path.join(aabDirectory.path, 'app.aab')).writeAsStringSync('irrelevant'); - + final GradleProject gradleProject = generateFakeAppBundle('profile', 'app.aab'); final File bundle = findBundleFile(gradleProject, const BuildInfo(BuildMode.profile, null)); expect(bundle, isNotNull); expect(bundle.path, '/profile/app.aab'); }, overrides: { FileSystem: () => MemoryFileSystem(), }); + + testUsingContext('Finds app bundle in release mode - Gradle 3.5', () { + final GradleProject gradleProject = generateFakeAppBundle('release', 'app-release.aab'); + final File bundle = findBundleFile(gradleProject, const BuildInfo(BuildMode.release, null)); + expect(bundle, isNotNull); + expect(bundle.path, '/release/app-release.aab'); + }, overrides: { + FileSystem: () => MemoryFileSystem(), + }); + + testUsingContext('Finds app bundle in profile mode - Gradle 3.5', () { + final GradleProject gradleProject = generateFakeAppBundle('profile', 'app-profile.aab'); + final File bundle = findBundleFile(gradleProject, const BuildInfo(BuildMode.profile, null)); + expect(bundle, isNotNull); + expect(bundle.path, '/profile/app-profile.aab'); + }, overrides: { + FileSystem: () => MemoryFileSystem(), + }); + + testUsingContext('Finds app bundle in debug mode - Gradle 3.5', () { + final GradleProject gradleProject = generateFakeAppBundle('debug', 'app-debug.aab'); + final File bundle = findBundleFile(gradleProject, const BuildInfo(BuildMode.debug, null)); + expect(bundle, isNotNull); + expect(bundle.path, '/debug/app-debug.aab'); + }, overrides: { + FileSystem: () => MemoryFileSystem(), + }); + + testUsingContext('Finds app bundle when flavor contains underscores in release mode - Gradle 3.5', () { + final GradleProject gradleProject = generateFakeAppBundle('foo_barRelease', 'app-foo_bar-release.aab'); + final File bundle = findBundleFile(gradleProject, const BuildInfo(BuildMode.release, 'foo_bar')); + expect(bundle, isNotNull); + expect(bundle.path, '/foo_barRelease/app-foo_bar-release.aab'); + }, overrides: { + FileSystem: () => MemoryFileSystem(), + }); + + testUsingContext('Finds app bundle when flavor contains underscores in profile mode - Gradle 3.5', () { + final GradleProject gradleProject = generateFakeAppBundle('foo_barProfile', 'app-foo_bar-profile.aab'); + final File bundle = findBundleFile(gradleProject, const BuildInfo(BuildMode.profile, 'foo_bar')); + expect(bundle, isNotNull); + expect(bundle.path, '/foo_barProfile/app-foo_bar-profile.aab'); + }, overrides: { + FileSystem: () => MemoryFileSystem(), + }); + + testUsingContext('Finds app bundle when flavor contains underscores in debug mode - Gradle 3.5', () { + final GradleProject gradleProject = generateFakeAppBundle('foo_barDebug', 'app-foo_bar-debug.aab'); + final File bundle = findBundleFile(gradleProject, const BuildInfo(BuildMode.debug, 'foo_bar')); + expect(bundle, isNotNull); + expect(bundle.path, '/foo_barDebug/app-foo_bar-debug.aab'); + }, overrides: { + FileSystem: () => MemoryFileSystem(), + }); }); group('gradle project', () { @@ -412,19 +403,6 @@ someOtherTask ) ).isEmpty, isTrue); }); - test('should provide bundle file name for default build types', () { - final GradleProject project = GradleProject(['debug', 'profile', 'release'], [], '/some/dir'); - expect(project.bundleFileFor(BuildInfo.debug), 'app.aab'); - expect(project.bundleFileFor(BuildInfo.profile), 'app.aab'); - expect(project.bundleFileFor(BuildInfo.release), 'app.aab'); - expect(project.bundleFileFor(const BuildInfo(BuildMode.release, 'unknown')), 'app.aab'); - }); - test('should provide bundle file name for flavored build types', () { - final GradleProject project = GradleProject(['debug', 'profile', 'release'], ['free', 'paid'], '/some/dir'); - expect(project.bundleFileFor(const BuildInfo(BuildMode.debug, 'free')), 'app.aab'); - expect(project.bundleFileFor(const BuildInfo(BuildMode.release, 'paid')), 'app.aab'); - expect(project.bundleFileFor(const BuildInfo(BuildMode.release, 'unknown')), 'app.aab'); - }); test('should provide assemble task name for default build types', () { final GradleProject project = GradleProject(['debug', 'profile', 'release'], [], '/some/dir'); expect(project.assembleTaskFor(BuildInfo.debug), 'assembleDebug'); @@ -867,6 +845,17 @@ flutter: }); } +/// Generates a fake app bundle at the location [directoryName]/[fileName]. +GradleProject generateFakeAppBundle(String directoryName, String fileName) { + final GradleProject gradleProject = MockGradleProject(); + when(gradleProject.bundleDirectory).thenReturn(fs.currentDirectory); + + final Directory aabDirectory = gradleProject.bundleDirectory.childDirectory(directoryName); + fs.directory(aabDirectory).createSync(recursive: true); + fs.file(fs.path.join(aabDirectory.path, fileName)).writeAsStringSync('irrelevant'); + return gradleProject; +} + Platform fakePlatform(String name) { return FakePlatform.fromPlatform(const LocalPlatform())..operatingSystem = name; }