mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
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:
parent
f219bbac55
commit
0e59f0f64c
@ -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 {
|
||||
|
@ -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,
|
||||
});
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
);
|
||||
|
@ -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(
|
||||
|
@ -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';
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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 {}
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user