flutter build aar regenerates tooling between each build-mode step (#162705)

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

I still need to do a similar change for `ios|macos`-`framework` in
https://github.com/flutter/flutter/issues/162704.

I added two unit tests, as well as opted in an integration test to the
flag (it will become the default in
https://github.com/flutter/flutter/pull/160289).

/cc @reidbaker @gmackall.
This commit is contained in:
Matan Lurey 2025-02-06 09:03:02 -08:00 committed by GitHub
parent f219bbac55
commit 0e59f0f64c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 363 additions and 89 deletions

View File

@ -21,6 +21,11 @@ Future<void> main() async {
}
print('\nUsing JAVA_HOME=$javaHome');
// TODO(matanlurey): Remove after default.
// https://github.com/flutter/flutter/issues/160257
section('Opt-in to --explicit-package-dependencies');
await flutter('config', options: <String>['--explicit-package-dependencies']);
final Directory tempDir = Directory.systemTemp.createTempSync('flutter_module_test.');
final Directory projectDir = Directory(path.join(tempDir.path, 'hello'));
try {

View File

@ -19,6 +19,7 @@ abstract class AndroidBuilder {
required FlutterProject project,
required Set<AndroidBuildInfo> androidBuildInfo,
required String target,
required Future<void> Function(FlutterProject, {required bool releaseMode}) generateTooling,
String? outputDirectoryPath,
required String buildNumber,
});

View File

@ -191,6 +191,7 @@ class AndroidGradleBuilder implements AndroidBuilder {
required FlutterProject project,
required Set<AndroidBuildInfo> androidBuildInfo,
required String target,
required Future<void> Function(FlutterProject, {required bool releaseMode}) generateTooling,
String? outputDirectoryPath,
required String buildNumber,
}) async {
@ -208,6 +209,7 @@ class AndroidGradleBuilder implements AndroidBuilder {
_logger.printWarning(androidX86DeprecationWarning);
}
for (final AndroidBuildInfo androidBuildInfo in androidBuildInfo) {
await generateTooling(project, releaseMode: androidBuildInfo.buildInfo.isRelease);
await buildGradleAar(
project: project,
androidBuildInfo: androidBuildInfo,

View File

@ -111,6 +111,9 @@ class BuildAarCommand extends BuildSubCommand {
await super.validateCommand();
}
@override
bool get regeneratePlatformSpecificToolingDurifyVerify => false;
@override
Future<FlutterCommandResult> runCommand() async {
if (_androidSdk == null) {
@ -149,10 +152,12 @@ class BuildAarCommand extends BuildSubCommand {
}
displayNullSafetyMode(androidBuildInfo.first.buildInfo);
await androidBuilder?.buildAar(
project: project,
target: targetFile.path,
androidBuildInfo: androidBuildInfo,
generateTooling: regeneratePlatformSpecificToolingIfApplicable,
outputDirectoryPath: stringArg('output'),
buildNumber: buildNumber,
);

View File

@ -1893,22 +1893,15 @@ Run 'flutter -h' (or 'flutter <command> -h') for available flutter commands and
buildSystem: globals.buildSystem,
buildTargets: globals.buildTargets,
);
// null implicitly means all plugins are allowed
List<String>? allowedPlugins;
if (stringArg(FlutterGlobalOptions.kDeviceIdOption, global: true) == 'preview') {
// The preview device does not currently support any plugins.
allowedPlugins = PreviewDevice.supportedPubPlugins;
}
await project.regeneratePlatformSpecificTooling(
allowedPlugins: allowedPlugins,
releaseMode: featureFlags.isExplicitPackageDependenciesEnabled && getBuildMode().isRelease,
);
if (reportNullSafety) {
await _sendNullSafetyAnalyticsEvents(project);
}
}
if (regeneratePlatformSpecificToolingDurifyVerify) {
await regeneratePlatformSpecificToolingIfApplicable(project);
}
setupApplicationPackages();
if (commandPath != null) {
@ -1918,6 +1911,66 @@ Run 'flutter -h' (or 'flutter <command> -h') for available flutter commands and
return runCommand();
}
/// Whether to run [regeneratePlatformSpecificTooling] in [verifyThenRunCommand].
///
/// By default `true`, but sub-commands that do _meta_ builds (make multiple different
/// builds sequentially in one-go) may choose to override this and provide `false`, instead
/// calling [regeneratePlatformSpecificTooling] manually when applicable.
@visibleForOverriding
bool get regeneratePlatformSpecificToolingDurifyVerify => true;
/// 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) {
/// await regeneratePlatformSpecificTooling(project);
/// }
/// ```
///
/// See also:
///
/// - <https://github.com/flutter/flutter/issues/162649>.
@protected
@nonVirtual
Future<void> regeneratePlatformSpecificToolingIfApplicable(
FlutterProject project, {
bool? releaseMode,
}) async {
if (!shouldRunPub) {
return;
}
// TODO(matanlurey): Determine if PreviewDevice should be kept.
// https://github.com/flutter/flutter/issues/162693
final List<String>? allowedPlugins;
if (stringArg(FlutterGlobalOptions.kDeviceIdOption, global: true) == 'preview') {
// The preview device does not currently support any plugins.
allowedPlugins = PreviewDevice.supportedPubPlugins;
} else {
// null means all plugins are allowed
allowedPlugins = null;
}
await project.regeneratePlatformSpecificTooling(
allowedPlugins: allowedPlugins,
// TODO(matanlurey): Move this up, i.e. releaseMode ??= getBuildMode().release.
//
// As it stands, this is a breaking change until https://github.com/flutter/flutter/issues/162704 is
// implemented, as the build_ios_framework command (and similar) will start querying
// for getBuildMode(), causing an error (meta-build commands like build ios-framework do not have
// a single build mode). Once ios-framework and macos-framework are migrated, then this can be
// cleaned up.
releaseMode:
featureFlags.isExplicitPackageDependenciesEnabled &&
(releaseMode ?? getBuildMode().isRelease),
);
}
Future<void> _sendNullSafetyAnalyticsEvents(FlutterProject project) async {
final BuildInfo buildInfo = await getBuildInfo();
NullSafetyAnalysisEvent(

View File

@ -13,6 +13,7 @@ import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/build_aar.dart';
import 'package:flutter_tools/src/dart/pub.dart';
import 'package:flutter_tools/src/features.dart';
import 'package:flutter_tools/src/globals.dart' as globals;
import 'package:flutter_tools/src/project.dart';
@ -24,24 +25,42 @@ import '../../src/android_common.dart';
import '../../src/common.dart';
import '../../src/context.dart';
import '../../src/fake_process_manager.dart';
import '../../src/fake_pub_deps.dart';
import '../../src/fakes.dart' hide FakeFlutterProjectFactory;
import '../../src/test_flutter_command_runner.dart';
void main() {
Cache.disableLocking();
Future<BuildAarCommand> runCommandIn(String target, {List<String>? arguments}) async {
/// Runs the equivalent of `flutter build aar`.
///
/// If [arguments] are provided, they are appended to the end, i.e.:
/// ```sh
/// flutter build aar [arguments]
/// ```
///
/// If [androidSdk] is provided, it is used, otherwise defaults to [FakeAndroidSdk].
Future<BuildAarCommand> runBuildAar(
String target, {
AndroidSdk? androidSdk = const _FakeAndroidSdk(),
List<String>? arguments,
}) async {
final BuildAarCommand command = BuildAarCommand(
androidSdk: FakeAndroidSdk(),
androidSdk: androidSdk,
fileSystem: globals.fs,
logger: BufferLogger.test(),
verboseHelp: false,
);
final CommandRunner<void> runner = createTestCommandRunner(command);
await runner.run(<String>['aar', '--no-pub', ...?arguments, target]);
await runner.run(<String>['aar', ...?arguments, target]);
return command;
}
// TODO(matanlurey): Remove after `explicit-package-dependencies` is enabled by default.
FeatureFlags enableExplicitPackageDependencies() {
return TestFeatureFlags(isExplicitPackageDependenciesEnabled: true);
}
group('Usage', () {
late Directory tempDir;
late FakeAnalytics analytics;
@ -66,7 +85,7 @@ void main() {
arguments: <String>['--no-pub', '--template=module'],
);
await runCommandIn(projectPath);
await runBuildAar(projectPath, arguments: <String>['--no-pub']);
expect(
analytics.sentEvents,
contains(
@ -80,7 +99,7 @@ void main() {
);
},
overrides: <Type, Generator>{
AndroidBuilder: () => FakeAndroidBuilder(),
AndroidBuilder: () => _CapturingFakeAndroidBuilder(),
Analytics: () => analytics,
},
);
@ -93,7 +112,10 @@ void main() {
arguments: <String>['--no-pub', '--template=module'],
);
await runCommandIn(projectPath, arguments: <String>['--target-platform=android-arm']);
await runBuildAar(
projectPath,
arguments: <String>['--no-pub', '--target-platform=android-arm'],
);
expect(
analytics.sentEvents,
contains(
@ -107,11 +129,36 @@ void main() {
);
},
overrides: <Type, Generator>{
AndroidBuilder: () => FakeAndroidBuilder(),
AndroidBuilder: () => _CapturingFakeAndroidBuilder(),
Analytics: () => analytics,
},
);
// Regression test for https://github.com/flutter/flutter/issues/162649.
testUsingContext(
'triggers builds even with --pub',
() async {
final String projectPath = await createProject(
tempDir,
arguments: <String>['--no-pub', '--template=module'],
);
await runBuildAar(
projectPath,
arguments: <String>['--target-platform=android-arm'],
// If we use --no-pub, it bypasses validation that occurs only on a
// build with --pub, which as a consequence means that we aren't
// testing every code branch.
);
},
overrides: <Type, Generator>{
AndroidBuilder: () => _CapturingFakeAndroidBuilder(),
Analytics: () => analytics,
FeatureFlags: enableExplicitPackageDependencies,
Pub: () => FakePubWithPrimedDeps(allowGet: true),
},
);
testUsingContext(
'logs success',
() async {
@ -120,7 +167,10 @@ void main() {
arguments: <String>['--no-pub', '--template=module'],
);
await runCommandIn(projectPath, arguments: <String>['--target-platform=android-arm']);
await runBuildAar(
projectPath,
arguments: <String>['--no-pub', '--target-platform=android-arm'],
);
final Iterable<Event> successEvent = analytics.sentEvents.where(
(Event e) =>
@ -131,7 +181,7 @@ void main() {
expect(successEvent, isNotEmpty, reason: 'Tool should send create success event');
},
overrides: <Type, Generator>{
AndroidBuilder: () => FakeAndroidBuilder(),
AndroidBuilder: () => _CapturingFakeAndroidBuilder(),
Analytics: () => analytics,
},
);
@ -139,10 +189,10 @@ void main() {
group('flag parsing', () {
late Directory tempDir;
late FakeAndroidBuilder fakeAndroidBuilder;
late _CapturingFakeAndroidBuilder fakeAndroidBuilder;
setUp(() {
fakeAndroidBuilder = FakeAndroidBuilder();
fakeAndroidBuilder = _CapturingFakeAndroidBuilder();
tempDir = globals.fs.systemTempDirectory.createTempSync('flutter_tools_build_aar_test.');
});
@ -155,13 +205,19 @@ void main() {
tempDir,
arguments: <String>['--no-pub', '--template=module'],
);
await runCommandIn(projectPath);
await runBuildAar(projectPath, arguments: <String>['--no-pub']);
expect(fakeAndroidBuilder.buildNumber, '1.0');
expect(fakeAndroidBuilder.androidBuildInfo.length, 3);
expect(
fakeAndroidBuilder.capturedBuildAarCalls,
hasLength(1),
reason: 'A single call to buildAar was expected.',
);
final Invocation buildAarCall = fakeAndroidBuilder.capturedBuildAarCalls.single;
expect(buildAarCall.namedArguments[#buildNumber], '1.0');
final List<BuildMode> buildModes = <BuildMode>[];
for (final AndroidBuildInfo androidBuildInfo in fakeAndroidBuilder.androidBuildInfo) {
for (final AndroidBuildInfo androidBuildInfo
in buildAarCall.namedArguments[#androidBuildInfo] as Set<AndroidBuildInfo>) {
final BuildInfo buildInfo = androidBuildInfo.buildInfo;
buildModes.add(buildInfo.mode);
if (buildInfo.mode.isPrecompiled) {
@ -180,7 +236,7 @@ void main() {
AndroidArch.x86_64,
]);
}
expect(buildModes.length, 3);
expect(buildModes, hasLength(3));
expect(
buildModes,
containsAll(<BuildMode>[BuildMode.debug, BuildMode.profile, BuildMode.release]),
@ -192,9 +248,10 @@ void main() {
tempDir,
arguments: <String>['--no-pub', '--template=module'],
);
await runCommandIn(
await runBuildAar(
projectPath,
arguments: <String>[
'--no-pub',
'--no-debug',
'--no-profile',
'--target-platform',
@ -211,9 +268,16 @@ void main() {
],
);
expect(fakeAndroidBuilder.buildNumber, '200');
expect(
fakeAndroidBuilder.capturedBuildAarCalls,
hasLength(1),
reason: 'A single call to buildAar was expected.',
);
final Invocation buildAarCall = fakeAndroidBuilder.capturedBuildAarCalls.single;
expect(buildAarCall.namedArguments[#buildNumber], '200');
final AndroidBuildInfo androidBuildInfo = fakeAndroidBuilder.androidBuildInfo.single;
final AndroidBuildInfo androidBuildInfo =
(buildAarCall.namedArguments[#androidBuildInfo] as Set<AndroidBuildInfo>).single;
expect(androidBuildInfo.targetArchs, <AndroidArch>[AndroidArch.x86]);
final BuildInfo buildInfo = androidBuildInfo.buildInfo;
@ -229,7 +293,6 @@ void main() {
group('Gradle', () {
late Directory tempDir;
late AndroidSdk mockAndroidSdk;
late String gradlew;
late FakeProcessManager processManager;
late String flutterRoot;
@ -241,7 +304,6 @@ void main() {
fs: MemoryFileSystem.test(),
fakeFlutterVersion: FakeFlutterVersion(),
);
mockAndroidSdk = FakeAndroidSdk();
gradlew = globals.fs.path.join(
tempDir.path,
'flutter_project',
@ -267,7 +329,7 @@ void main() {
await expectLater(
() async {
await runBuildAarCommand(projectPath, null, arguments: <String>['--no-pub']);
await runBuildAar(projectPath, androidSdk: null, arguments: <String>['--no-pub']);
},
throwsToolExit(
message: 'No Android SDK found. Try setting the ANDROID_HOME environment variable',
@ -284,17 +346,19 @@ void main() {
group('throws ToolExit', () {
testUsingContext('main.dart not found', () async {
await expectLater(() async {
await runBuildAarCommand(
await runBuildAar(
'missing_project',
mockAndroidSdk,
arguments: <String>['--no-pub'],
arguments: <String>[
'--no-pub',
globals.fs.path.join('missing_project', 'lib', 'main.dart'),
],
);
}, throwsToolExit(message: 'main.dart does not exist'));
});
testUsingContext('flutter project not valid', () async {
await expectLater(() async {
await runCommandIn(tempDir.path, arguments: <String>['--no-pub']);
await runBuildAar(tempDir.path, arguments: <String>['--no-pub']);
}, throwsToolExit(message: 'is not a valid flutter project'));
});
});
@ -330,10 +394,10 @@ void main() {
);
await expectLater(
() => runBuildAarCommand(
() => runBuildAar(
projectPath,
mockAndroidSdk,
arguments: <String>[
'--no-pub',
'--no-debug',
'--no-profile',
'--extra-front-end-options=foo',
@ -349,7 +413,7 @@ void main() {
Java: () => null,
ProcessManager: () => processManager,
FeatureFlags: () => TestFeatureFlags(isIOSEnabled: false),
AndroidStudio: () => FakeAndroidStudio(),
AndroidStudio: () => _FakeAndroidStudio(),
},
);
@ -393,7 +457,7 @@ void main() {
arguments: <String>['--no-pub', '--template=module'],
);
await runBuildAarCommand(projectPath, mockAndroidSdk);
await runBuildAar(projectPath, arguments: <String>['--no-pub']);
expect(
fakeAnalytics.sentEvents,
@ -404,7 +468,7 @@ void main() {
},
overrides: <Type, Generator>{
Analytics: () => fakeAnalytics,
AndroidBuilder: () => FakeAndroidBuilder(),
AndroidBuilder: () => _CapturingFakeAndroidBuilder(),
FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir),
},
);
@ -427,7 +491,7 @@ void main() {
value: 'true',
);
await runBuildAarCommand(projectPath, mockAndroidSdk);
await runBuildAar(projectPath, arguments: <String>['--no-pub']);
expect(
fakeAnalytics.sentEvents,
@ -438,7 +502,7 @@ void main() {
},
overrides: <Type, Generator>{
Analytics: () => fakeAnalytics,
AndroidBuilder: () => FakeAndroidBuilder(),
AndroidBuilder: () => _CapturingFakeAndroidBuilder(),
FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir),
},
);
@ -461,7 +525,7 @@ void main() {
value: 'false',
);
await runBuildAarCommand(projectPath, mockAndroidSdk);
await runBuildAar(projectPath, arguments: <String>['--no-pub']);
expect(
fakeAnalytics.sentEvents,
@ -472,7 +536,7 @@ void main() {
},
overrides: <Type, Generator>{
Analytics: () => fakeAnalytics,
AndroidBuilder: () => FakeAndroidBuilder(),
AndroidBuilder: () => _CapturingFakeAndroidBuilder(),
FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir),
},
);
@ -480,53 +544,27 @@ void main() {
});
}
Future<BuildAarCommand> runBuildAarCommand(
String target,
AndroidSdk? androidSdk, {
List<String>? arguments,
}) async {
final BuildAarCommand command = BuildAarCommand(
androidSdk: androidSdk,
fileSystem: globals.fs,
logger: BufferLogger.test(),
verboseHelp: false,
);
final CommandRunner<void> runner = createTestCommandRunner(command);
await runner.run(<String>[
'aar',
'--no-pub',
...?arguments,
globals.fs.path.join(target, 'lib', 'main.dart'),
]);
return command;
}
class FakeAndroidBuilder extends Fake implements AndroidBuilder {
late FlutterProject project;
late Set<AndroidBuildInfo> androidBuildInfo;
late String target;
String? outputDirectoryPath;
late String buildNumber;
/// A fake implementation of [AndroidBuilder] that allows [buildAar] calls.
///
/// Calls to [buildAar] are stored as [capturedBuildAarCalls], other calls are rejected.
final class _CapturingFakeAndroidBuilder extends Fake implements AndroidBuilder {
final List<Invocation> capturedBuildAarCalls = <Invocation>[];
@override
Future<void> buildAar({
required FlutterProject project,
required Set<AndroidBuildInfo> androidBuildInfo,
required String target,
String? outputDirectoryPath,
required String buildNumber,
}) async {
this.project = project;
this.androidBuildInfo = androidBuildInfo;
this.target = target;
this.outputDirectoryPath = outputDirectoryPath;
this.buildNumber = buildNumber;
Object? noSuchMethod(Invocation invocation) {
if (invocation.memberName != #buildAar) {
return super.noSuchMethod(invocation);
}
capturedBuildAarCalls.add(invocation);
return Future<void>.value();
}
}
class FakeAndroidSdk extends Fake implements AndroidSdk {}
final class _FakeAndroidSdk with Fake implements AndroidSdk {
const _FakeAndroidSdk();
}
class FakeAndroidStudio extends Fake implements AndroidStudio {
final class _FakeAndroidStudio extends Fake implements AndroidStudio {
@override
String get javaPath => 'java';
}

View File

@ -20,6 +20,7 @@ import 'package:flutter_tools/src/base/process.dart';
import 'package:flutter_tools/src/base/user_messages.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/features.dart';
import 'package:flutter_tools/src/project.dart';
import 'package:test/fake.dart';
import 'package:unified_analytics/unified_analytics.dart';
@ -41,6 +42,11 @@ const String minimalV2EmbeddingManifest = r'''
''';
void main() {
// TODO(matanlurey): Remove after `explicit-package-dependencies` is enabled by default.
FeatureFlags enableExplicitPackageDependencies() {
return TestFeatureFlags(isExplicitPackageDependenciesEnabled: true);
}
group('gradle build', () {
late BufferLogger logger;
late FakeAnalytics fakeAnalytics;
@ -1566,6 +1572,141 @@ Gradle Crashed
},
);
// Regression test for https://github.com/flutter/flutter/issues/162649.
testUsingContext(
'buildAar generates tooling for each sub-build for AARs',
() async {
addTearDown(() {
printOnFailure(logger.statusText);
printOnFailure(logger.errorText);
});
final AndroidGradleBuilder builder = AndroidGradleBuilder(
java: FakeJava(),
logger: logger,
processManager: processManager,
fileSystem: fileSystem,
artifacts: Artifacts.test(),
analytics: fakeAnalytics,
gradleUtils: FakeGradleUtils(),
platform: FakePlatform(),
androidStudio: FakeAndroidStudio(),
);
processManager.addCommands(const <FakeCommand>[
FakeCommand(
command: <String>[
'gradlew',
'-I=/packages/flutter_tools/gradle/aar_init_script.gradle',
'-Pflutter-root=/',
'-Poutput-dir=/build/host',
'-Pis-plugin=false',
'-PbuildNumber=1.0',
'-q',
'-Pdart-obfuscation=false',
'-Ptrack-widget-creation=false',
'-Ptree-shake-icons=false',
'-Ptarget-platform=android-arm,android-arm64,android-x64',
'assembleAarDebug',
],
),
FakeCommand(
command: <String>[
'gradlew',
'-I=/packages/flutter_tools/gradle/aar_init_script.gradle',
'-Pflutter-root=/',
'-Poutput-dir=/build/host',
'-Pis-plugin=false',
'-PbuildNumber=1.0',
'-q',
'-Pdart-obfuscation=false',
'-Ptrack-widget-creation=false',
'-Ptree-shake-icons=false',
'-Ptarget-platform=android-arm,android-arm64,android-x64',
'assembleAarProfile',
],
),
FakeCommand(
command: <String>[
'gradlew',
'-I=/packages/flutter_tools/gradle/aar_init_script.gradle',
'-Pflutter-root=/',
'-Poutput-dir=/build/host',
'-Pis-plugin=false',
'-PbuildNumber=1.0',
'-q',
'-Pdart-obfuscation=false',
'-Ptrack-widget-creation=false',
'-Ptree-shake-icons=false',
'-Ptarget-platform=android-arm,android-arm64,android-x64',
'assembleAarRelease',
],
),
]);
final File manifestFile = fileSystem.file('pubspec.yaml');
manifestFile.createSync(recursive: true);
manifestFile.writeAsStringSync('''
flutter:
module:
androidPackage: com.example.test
''');
fileSystem.file('.android/gradlew').createSync(recursive: true);
fileSystem.file('.android/gradle.properties').writeAsStringSync('irrelevant');
fileSystem.file('.android/build.gradle').createSync(recursive: true);
fileSystem.directory('build/host/outputs/repo').createSync(recursive: true);
final List<(FlutterProject, bool)> generateToolingCalls = <(FlutterProject, bool)>[];
await builder.buildAar(
project: FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
androidBuildInfo: const <AndroidBuildInfo>{
AndroidBuildInfo(
BuildInfo(
BuildMode.debug,
null,
treeShakeIcons: false,
packageConfigPath: '.dart_tool/package_config.json',
),
),
AndroidBuildInfo(
BuildInfo(
BuildMode.profile,
null,
treeShakeIcons: false,
packageConfigPath: '.dart_tool/package_config.json',
),
),
AndroidBuildInfo(
BuildInfo(
BuildMode.release,
null,
treeShakeIcons: false,
packageConfigPath: '.dart_tool/package_config.json',
),
),
},
target: '',
buildNumber: '1.0',
generateTooling: (FlutterProject project, {required bool releaseMode}) async {
generateToolingCalls.add((project, releaseMode));
},
);
expect(processManager, hasNoRemainingExpectations);
// Ideally, this should be checked before each invocation to the process,
// but instead we'll assume it was invoked in the same order as the calls
// to gradle to keep the scope of this test light.
expect(generateToolingCalls, hasLength(3));
expect(
generateToolingCalls.map(((FlutterProject, bool) call) {
return call.$2;
}),
<bool>[false, false, true],
reason: 'generateTooling should omit debug metadata for release builds',
);
},
overrides: <Type, Generator>{FeatureFlags: enableExplicitPackageDependencies},
);
testUsingContext(
'Verbose mode for AARs includes Gradle stacktrace and sets debug log level',
() async {

View File

@ -20,6 +20,7 @@ class FakeAndroidBuilder implements AndroidBuilder {
required FlutterProject project,
required Set<AndroidBuildInfo> androidBuildInfo,
required String target,
required Future<void> Function(FlutterProject, {required bool releaseMode}) generateTooling,
String? outputDirectoryPath,
required String buildNumber,
}) async {}

View File

@ -16,10 +16,15 @@ final class FakePubWithPrimedDeps implements Pub {
/// dev-dependencies ([dependencies]) of any package to a set of any other
/// packages. A resulting valid `dart pub deps --json` response is implicitly
/// created.
///
/// If [allowGet] is `true`, [Pub.get] can be invoked (all the parameters are
/// ignored and it is considered a success); otherwise an error is thrown to
/// reject an unexpected call.
factory FakePubWithPrimedDeps({
String rootPackageName = 'app_name',
Set<String> devDependencies = const <String>{},
Map<String, Set<String>> dependencies = const <String, Set<String>>{},
bool allowGet = false,
}) {
// Start the packages: [ ... ] list with the root package.
final List<Object?> packages = <Object?>[
@ -56,11 +61,34 @@ final class FakePubWithPrimedDeps implements Pub {
return FakePubWithPrimedDeps._(<String, Object?>{
'root': rootPackageName,
'packages': packages,
});
}, allowGetToSucceed: allowGet);
}
const FakePubWithPrimedDeps._(this._deps);
const FakePubWithPrimedDeps._(this._deps, {required bool allowGetToSucceed})
: _allowGetToSucceed = allowGetToSucceed;
final Map<String, Object?> _deps;
final bool _allowGetToSucceed;
@override
Future<void> get({
required PubContext context,
required FlutterProject project,
bool upgrade = false,
bool offline = false,
String? flutterRootOverride,
bool checkUpToDate = false,
bool shouldSkipThirdPartyGenerator = true,
PubOutputMode outputMode = PubOutputMode.all,
}) async {
if (_allowGetToSucceed) {
return;
}
throw UnsupportedError(
'Instance did not expect <Pub>.get to be invoked. If this was intentional, '
'change the constructor of FakePubWithPrimeDeps to include the parameter '
'allowGet: true.',
);
}
@override
Future<Map<String, Object?>> deps(FlutterProject project) async => _deps;