mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Build xcarchive command (#67598)
This commit is contained in:
parent
98d1ad0195
commit
98aeef2d98
@ -166,6 +166,29 @@ Future<void> main() async {
|
||||
if (!await localNetworkUsageFound(outputAppPath)) {
|
||||
throw TaskResult.failure('Debug bundle is missing NSLocalNetworkUsageDescription');
|
||||
}
|
||||
|
||||
section('Clean build');
|
||||
|
||||
await inDirectory(flutterProject.rootPath, () async {
|
||||
await flutter('clean');
|
||||
});
|
||||
|
||||
section('Archive');
|
||||
|
||||
await inDirectory(flutterProject.rootPath, () async {
|
||||
await flutter('build', options: <String>[
|
||||
'xcarchive',
|
||||
]);
|
||||
});
|
||||
|
||||
checkDirectoryExists(path.join(
|
||||
flutterProject.rootPath,
|
||||
'build',
|
||||
'ios',
|
||||
'archive',
|
||||
'Runner.xcarchive',
|
||||
'Products',
|
||||
));
|
||||
});
|
||||
|
||||
return TaskResult.success(null);
|
||||
|
@ -379,6 +379,12 @@ class BuildableIOSApp extends IOSApp {
|
||||
@override
|
||||
String get deviceBundlePath => _buildAppPath('iphoneos');
|
||||
|
||||
// Xcode uses this path for the final archive bundle location,
|
||||
// not a top-level output directory.
|
||||
// Specifying `build/ios/archive/Runner` will result in `build/ios/archive/Runner.xcarchive`.
|
||||
String get archiveBundlePath
|
||||
=> globals.fs.path.join(getIosBuildDirectory(), 'archive', globals.fs.path.withoutExtension(_hostAppBundleName));
|
||||
|
||||
String _buildAppPath(String type) {
|
||||
return globals.fs.path.join(getIosBuildDirectory(), type, _hostAppBundleName);
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ class BuildCommand extends FlutterCommand {
|
||||
buildSystem: globals.buildSystem,
|
||||
verboseHelp: verboseHelp,
|
||||
));
|
||||
addSubcommand(BuildIOSArchiveCommand(verboseHelp: verboseHelp));
|
||||
addSubcommand(BuildBundleCommand(verboseHelp: verboseHelp));
|
||||
addSubcommand(BuildWebCommand(verboseHelp: verboseHelp));
|
||||
addSubcommand(BuildMacosCommand(verboseHelp: verboseHelp));
|
||||
|
@ -19,24 +19,8 @@ import 'build.dart';
|
||||
/// Builds an .app for an iOS app to be used for local testing on an iOS device
|
||||
/// or simulator. Can only be run on a macOS host. For producing deployment
|
||||
/// .ipas, see https://flutter.dev/docs/deployment/ios.
|
||||
class BuildIOSCommand extends BuildSubCommand {
|
||||
BuildIOSCommand({ @required bool verboseHelp }) {
|
||||
addTreeShakeIconsFlag();
|
||||
addSplitDebugInfoOption();
|
||||
addBuildModeFlags(defaultToRelease: true);
|
||||
usesTargetOption();
|
||||
usesFlavorOption();
|
||||
usesPubOption();
|
||||
usesBuildNumberOption();
|
||||
usesBuildNameOption();
|
||||
addDartObfuscationOption();
|
||||
usesDartDefineOption();
|
||||
usesExtraFrontendOptions();
|
||||
addEnableExperimentation(hide: !verboseHelp);
|
||||
addBuildPerformanceFile(hide: !verboseHelp);
|
||||
addBundleSkSLPathOption(hide: !verboseHelp);
|
||||
addNullSafetyModeOptions(hide: !verboseHelp);
|
||||
usesAnalyzeSizeFlag();
|
||||
class BuildIOSCommand extends _BuildIOSSubCommand {
|
||||
BuildIOSCommand({ @required bool verboseHelp }) : super(verboseHelp: verboseHelp) {
|
||||
argParser
|
||||
..addFlag('config-only',
|
||||
help: 'Update the project configuration without performing a build. '
|
||||
@ -59,16 +43,76 @@ class BuildIOSCommand extends BuildSubCommand {
|
||||
@override
|
||||
final String description = 'Build an iOS application bundle (Mac OS X host only).';
|
||||
|
||||
@override
|
||||
final XcodeBuildAction xcodeBuildAction = XcodeBuildAction.build;
|
||||
|
||||
@override
|
||||
bool get forSimulator => boolArg('simulator');
|
||||
|
||||
@override
|
||||
bool get configOnly => boolArg('config-only');
|
||||
|
||||
@override
|
||||
bool get shouldCodesign => boolArg('codesign');
|
||||
}
|
||||
|
||||
/// Builds an .xcarchive for an iOS app to be generated for App Store submission.
|
||||
/// Can only be run on a macOS host.
|
||||
/// For producing deployment .ipas, see https://flutter.dev/docs/deployment/ios.
|
||||
class BuildIOSArchiveCommand extends _BuildIOSSubCommand {
|
||||
BuildIOSArchiveCommand({ @required bool verboseHelp }) : super(verboseHelp: verboseHelp);
|
||||
|
||||
@override
|
||||
final String name = 'xcarchive';
|
||||
|
||||
@override
|
||||
final String description = 'Build an iOS archive bundle (Mac OS X host only).';
|
||||
|
||||
@override
|
||||
final XcodeBuildAction xcodeBuildAction = XcodeBuildAction.archive;
|
||||
|
||||
@override
|
||||
final bool forSimulator = false;
|
||||
|
||||
@override
|
||||
final bool configOnly = false;
|
||||
|
||||
@override
|
||||
final bool shouldCodesign = true;
|
||||
}
|
||||
|
||||
abstract class _BuildIOSSubCommand extends BuildSubCommand {
|
||||
_BuildIOSSubCommand({ @required bool verboseHelp }) {
|
||||
addTreeShakeIconsFlag();
|
||||
addSplitDebugInfoOption();
|
||||
addBuildModeFlags(defaultToRelease: true);
|
||||
usesTargetOption();
|
||||
usesFlavorOption();
|
||||
usesPubOption();
|
||||
usesBuildNumberOption();
|
||||
usesBuildNameOption();
|
||||
addDartObfuscationOption();
|
||||
usesDartDefineOption();
|
||||
usesExtraFrontendOptions();
|
||||
addEnableExperimentation(hide: !verboseHelp);
|
||||
addBuildPerformanceFile(hide: !verboseHelp);
|
||||
addBundleSkSLPathOption(hide: !verboseHelp);
|
||||
addNullSafetyModeOptions(hide: !verboseHelp);
|
||||
usesAnalyzeSizeFlag();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Set<DevelopmentArtifact>> get requiredArtifacts async => const <DevelopmentArtifact>{
|
||||
DevelopmentArtifact.iOS,
|
||||
};
|
||||
|
||||
XcodeBuildAction get xcodeBuildAction;
|
||||
bool get forSimulator;
|
||||
bool get configOnly;
|
||||
bool get shouldCodesign;
|
||||
|
||||
@override
|
||||
Future<FlutterCommandResult> runCommand() async {
|
||||
final bool forSimulator = boolArg('simulator');
|
||||
final bool configOnly = boolArg('config-only');
|
||||
final bool shouldCodesign = boolArg('codesign');
|
||||
defaultBuildMode = forSimulator ? BuildMode.debug : BuildMode.release;
|
||||
final BuildInfo buildInfo = getBuildInfo();
|
||||
|
||||
@ -99,7 +143,11 @@ class BuildIOSCommand extends BuildSubCommand {
|
||||
|
||||
final String logTarget = forSimulator ? 'simulator' : 'device';
|
||||
final String typeName = globals.artifacts.getEngineType(TargetPlatform.ios, buildInfo.mode);
|
||||
globals.printStatus('Building $app for $logTarget ($typeName)...');
|
||||
if (xcodeBuildAction == XcodeBuildAction.build) {
|
||||
globals.printStatus('Building $app for $logTarget ($typeName)...');
|
||||
} else {
|
||||
globals.printStatus('Archiving $app...');
|
||||
}
|
||||
final XcodeBuildResult result = await buildXcodeProject(
|
||||
app: app,
|
||||
buildInfo: buildInfo,
|
||||
@ -107,11 +155,12 @@ class BuildIOSCommand extends BuildSubCommand {
|
||||
buildForDevice: !forSimulator,
|
||||
codesign: shouldCodesign,
|
||||
configOnly: configOnly,
|
||||
buildAction: xcodeBuildAction,
|
||||
);
|
||||
|
||||
if (!result.success) {
|
||||
await diagnoseXcodeBuildFailure(result, globals.flutterUsage, globals.logger);
|
||||
throwToolExit('Encountered error while building for $logTarget.');
|
||||
throwToolExit('Encountered error while ${xcodeBuildAction.name}ing for $logTarget.');
|
||||
}
|
||||
|
||||
if (buildInfo.codeSizeDirectory != null) {
|
||||
|
@ -96,6 +96,7 @@ Future<XcodeBuildResult> buildXcodeProject({
|
||||
bool codesign = true,
|
||||
String deviceID,
|
||||
bool configOnly = false,
|
||||
XcodeBuildAction buildAction = XcodeBuildAction.build,
|
||||
}) async {
|
||||
if (!upgradePbxProjWithFlutterAssets(app.project, globals.logger)) {
|
||||
return XcodeBuildResult(success: false);
|
||||
@ -321,6 +322,14 @@ Future<XcodeBuildResult> buildXcodeProject({
|
||||
buildCommands.add('COMPILER_INDEX_STORE_ENABLE=NO');
|
||||
buildCommands.addAll(environmentVariablesAsXcodeBuildSettings(globals.platform));
|
||||
|
||||
if (buildAction == XcodeBuildAction.archive) {
|
||||
buildCommands.addAll(<String>[
|
||||
'-archivePath',
|
||||
globals.fs.path.absolute(app.archiveBundlePath),
|
||||
'archive',
|
||||
]);
|
||||
}
|
||||
|
||||
final Stopwatch sw = Stopwatch()..start();
|
||||
initialBuildStatus = globals.logger.startProgress('Running Xcode build...', timeout: timeoutConfiguration.slowOperation);
|
||||
|
||||
@ -333,13 +342,13 @@ Future<XcodeBuildResult> buildXcodeProject({
|
||||
initialBuildStatus?.cancel();
|
||||
initialBuildStatus = null;
|
||||
globals.printStatus(
|
||||
'Xcode build done.'.padRight(kDefaultStatusPadding + 1)
|
||||
'Xcode ${buildAction.name} done.'.padRight(kDefaultStatusPadding + 1)
|
||||
+ getElapsedAsSeconds(sw.elapsed).padLeft(5),
|
||||
);
|
||||
globals.flutterUsage.sendTiming('build', 'xcode-ios', Duration(milliseconds: sw.elapsedMilliseconds));
|
||||
globals.flutterUsage.sendTiming(buildAction.name, 'xcode-ios', Duration(milliseconds: sw.elapsedMilliseconds));
|
||||
|
||||
// Run -showBuildSettings again but with the exact same parameters as the
|
||||
// build. showBuildSettings is reported to ocassionally timeout. Here, we give
|
||||
// build. showBuildSettings is reported to occasionally timeout. Here, we give
|
||||
// it a lot of wiggle room (locally on Flutter Gallery, this takes ~1s).
|
||||
// When there is a timeout, we retry once. See issue #35988.
|
||||
final List<String> showBuildSettingsCommand = (List<String>
|
||||
@ -398,36 +407,42 @@ Future<XcodeBuildResult> buildXcodeProject({
|
||||
),
|
||||
);
|
||||
} else {
|
||||
// If the app contains a watch companion target, the sdk argument of xcodebuild has to be omitted.
|
||||
// For some reason this leads to TARGET_BUILD_DIR always ending in 'iphoneos' even though the
|
||||
// actual directory will end with 'iphonesimulator' for simulator builds.
|
||||
// The value of TARGET_BUILD_DIR is adjusted to accommodate for this effect.
|
||||
String targetBuildDir = buildSettings['TARGET_BUILD_DIR'];
|
||||
if (hasWatchCompanion && !buildForDevice) {
|
||||
globals.printTrace('Replacing iphoneos with iphonesimulator in TARGET_BUILD_DIR.');
|
||||
targetBuildDir = targetBuildDir.replaceFirst('iphoneos', 'iphonesimulator');
|
||||
}
|
||||
final String expectedOutputDirectory = globals.fs.path.join(
|
||||
targetBuildDir,
|
||||
buildSettings['WRAPPER_NAME'],
|
||||
);
|
||||
|
||||
String outputDir;
|
||||
if (globals.fs.isDirectorySync(expectedOutputDirectory)) {
|
||||
// Copy app folder to a place where other tools can find it without knowing
|
||||
// the BuildInfo.
|
||||
outputDir = expectedOutputDirectory.replaceFirst('/$configuration-', '/');
|
||||
if (globals.fs.isDirectorySync(outputDir)) {
|
||||
// Previous output directory might have incompatible artifacts
|
||||
// (for example, kernel binary files produced from previous run).
|
||||
globals.fs.directory(outputDir).deleteSync(recursive: true);
|
||||
if (buildAction == XcodeBuildAction.build) {
|
||||
// If the app contains a watch companion target, the sdk argument of xcodebuild has to be omitted.
|
||||
// For some reason this leads to TARGET_BUILD_DIR always ending in 'iphoneos' even though the
|
||||
// actual directory will end with 'iphonesimulator' for simulator builds.
|
||||
// The value of TARGET_BUILD_DIR is adjusted to accommodate for this effect.
|
||||
String targetBuildDir = buildSettings['TARGET_BUILD_DIR'];
|
||||
if (hasWatchCompanion && !buildForDevice) {
|
||||
globals.printTrace('Replacing iphoneos with iphonesimulator in TARGET_BUILD_DIR.');
|
||||
targetBuildDir = targetBuildDir.replaceFirst('iphoneos', 'iphonesimulator');
|
||||
}
|
||||
globals.fsUtils.copyDirectorySync(
|
||||
globals.fs.directory(expectedOutputDirectory),
|
||||
globals.fs.directory(outputDir),
|
||||
final String expectedOutputDirectory = globals.fs.path.join(
|
||||
targetBuildDir,
|
||||
buildSettings['WRAPPER_NAME'],
|
||||
);
|
||||
if (globals.fs.isDirectorySync(expectedOutputDirectory)) {
|
||||
// Copy app folder to a place where other tools can find it without knowing
|
||||
// the BuildInfo.
|
||||
outputDir = expectedOutputDirectory.replaceFirst('/$configuration-', '/');
|
||||
if (globals.fs.isDirectorySync(outputDir)) {
|
||||
// Previous output directory might have incompatible artifacts
|
||||
// (for example, kernel binary files produced from previous run).
|
||||
globals.fs.directory(outputDir).deleteSync(recursive: true);
|
||||
}
|
||||
globals.fsUtils.copyDirectorySync(
|
||||
globals.fs.directory(expectedOutputDirectory),
|
||||
globals.fs.directory(outputDir),
|
||||
);
|
||||
} else {
|
||||
globals.printError('Build succeeded but the expected app at $expectedOutputDirectory not found');
|
||||
}
|
||||
} else {
|
||||
globals.printError('Build succeeded but the expected app at $expectedOutputDirectory not found');
|
||||
outputDir = '${globals.fs.path.absolute(app.archiveBundlePath)}.xcarchive';
|
||||
if (!globals.fs.isDirectorySync(outputDir)) {
|
||||
globals.printError('Archive succeeded but the expected xcarchive at $outputDir not found');
|
||||
}
|
||||
}
|
||||
return XcodeBuildResult(
|
||||
success: true,
|
||||
@ -568,6 +583,24 @@ Future<void> diagnoseXcodeBuildFailure(XcodeBuildResult result, Usage flutterUsa
|
||||
}
|
||||
}
|
||||
|
||||
/// xcodebuild <buildaction> parameter (see man xcodebuild for details).
|
||||
///
|
||||
/// `clean`, `test`, `analyze`, and `install` are not supported.
|
||||
enum XcodeBuildAction { build, archive }
|
||||
|
||||
extension XcodeBuildActionExtension on XcodeBuildAction {
|
||||
String get name {
|
||||
switch (this) {
|
||||
case XcodeBuildAction.build:
|
||||
return 'build';
|
||||
case XcodeBuildAction.archive:
|
||||
return 'archive';
|
||||
default:
|
||||
throw UnsupportedError('Unknown Xcode build action');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class XcodeBuildResult {
|
||||
XcodeBuildResult({
|
||||
@required this.success,
|
||||
|
@ -0,0 +1,216 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:file/memory.dart';
|
||||
import 'package:flutter_tools/src/base/file_system.dart';
|
||||
import 'package:flutter_tools/src/base/platform.dart';
|
||||
import 'package:flutter_tools/src/cache.dart';
|
||||
import 'package:flutter_tools/src/commands/build.dart';
|
||||
import 'package:flutter_tools/src/ios/xcodeproj.dart';
|
||||
import 'package:flutter_tools/src/reporting/reporting.dart';
|
||||
import 'package:process/process.dart';
|
||||
|
||||
import '../../src/common.dart';
|
||||
import '../../src/context.dart';
|
||||
import '../../src/testbed.dart';
|
||||
|
||||
class FakeXcodeProjectInterpreterWithBuildSettings extends FakeXcodeProjectInterpreter {
|
||||
@override
|
||||
Future<Map<String, String>> getBuildSettings(
|
||||
String projectPath, {
|
||||
String scheme,
|
||||
Duration timeout = const Duration(minutes: 1),
|
||||
}) async {
|
||||
return <String, String>{
|
||||
'PRODUCT_BUNDLE_IDENTIFIER': 'io.flutter.someProject',
|
||||
'DEVELOPMENT_TEAM': 'abc',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
final Platform macosPlatform = FakePlatform(
|
||||
operatingSystem: 'macos',
|
||||
environment: <String, String>{
|
||||
'FLUTTER_ROOT': '/',
|
||||
}
|
||||
);
|
||||
final Platform notMacosPlatform = FakePlatform(
|
||||
operatingSystem: 'linux',
|
||||
environment: <String, String>{
|
||||
'FLUTTER_ROOT': '/',
|
||||
}
|
||||
);
|
||||
|
||||
void main() {
|
||||
FileSystem fileSystem;
|
||||
Usage usage;
|
||||
|
||||
setUpAll(() {
|
||||
Cache.disableLocking();
|
||||
});
|
||||
|
||||
setUp(() {
|
||||
fileSystem = MemoryFileSystem.test();
|
||||
usage = Usage.test();
|
||||
});
|
||||
|
||||
// Sets up the minimal mock project files necessary to look like a Flutter project.
|
||||
void createCoreMockProjectFiles() {
|
||||
fileSystem.file('pubspec.yaml').createSync();
|
||||
fileSystem.file('.packages').createSync();
|
||||
fileSystem.file(fileSystem.path.join('lib', 'main.dart')).createSync(recursive: true);
|
||||
}
|
||||
|
||||
// Sets up the minimal mock project files necessary for iOS builds to succeed.
|
||||
void createMinimalMockProjectFiles() {
|
||||
fileSystem.directory(fileSystem.path.join('ios', 'Runner.xcodeproj')).createSync(recursive: true);
|
||||
fileSystem.directory(fileSystem.path.join('ios', 'Runner.xcworkspace')).createSync(recursive: true);
|
||||
fileSystem.file(fileSystem.path.join('ios', 'Runner.xcodeproj', 'project.pbxproj')).createSync();
|
||||
createCoreMockProjectFiles();
|
||||
}
|
||||
|
||||
const FakeCommand xattrCommand = FakeCommand(command: <String>[
|
||||
'xattr', '-r', '-d', 'com.apple.FinderInfo', '/ios'
|
||||
]);
|
||||
|
||||
// Creates a FakeCommand for the xcodebuild call to build the app
|
||||
// in the given configuration.
|
||||
FakeCommand setUpMockXcodeBuildHandler({ bool verbose = false, bool showBuildSettings = false, void Function() onRun }) {
|
||||
return FakeCommand(
|
||||
command: <String>[
|
||||
'/usr/bin/env',
|
||||
'xcrun',
|
||||
'xcodebuild',
|
||||
'-configuration', 'Release',
|
||||
if (verbose)
|
||||
'VERBOSE_SCRIPT_LOGGING=YES'
|
||||
else
|
||||
'-quiet',
|
||||
'-workspace', 'Runner.xcworkspace',
|
||||
'-scheme', 'Runner',
|
||||
'BUILD_DIR=/build/ios',
|
||||
'-sdk', 'iphoneos',
|
||||
'FLUTTER_SUPPRESS_ANALYTICS=true',
|
||||
'COMPILER_INDEX_STORE_ENABLE=NO',
|
||||
'-archivePath', '/build/ios/archive/Runner',
|
||||
'archive',
|
||||
if (showBuildSettings)
|
||||
'-showBuildSettings',
|
||||
],
|
||||
stdout: 'STDOUT STUFF',
|
||||
onRun: onRun,
|
||||
);
|
||||
}
|
||||
|
||||
testUsingContext('xcarchive build fails when there is no ios project', () async {
|
||||
final BuildCommand command = BuildCommand();
|
||||
createCoreMockProjectFiles();
|
||||
|
||||
expect(createTestCommandRunner(command).run(
|
||||
const <String>['build', 'xcarchive', '--no-pub']
|
||||
), throwsToolExit(message: 'Application not configured for iOS'));
|
||||
}, overrides: <Type, Generator>{
|
||||
Platform: () => macosPlatform,
|
||||
FileSystem: () => fileSystem,
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
XcodeProjectInterpreter: () => FakeXcodeProjectInterpreterWithBuildSettings(),
|
||||
});
|
||||
|
||||
testUsingContext('xcarchive build fails on non-macOS platform', () async {
|
||||
final BuildCommand command = BuildCommand();
|
||||
fileSystem.file('pubspec.yaml').createSync();
|
||||
fileSystem.file('.packages').createSync();
|
||||
fileSystem.file(fileSystem.path.join('lib', 'main.dart'))
|
||||
.createSync(recursive: true);
|
||||
|
||||
expect(createTestCommandRunner(command).run(
|
||||
const <String>['build', 'xcarchive', '--no-pub']
|
||||
), throwsToolExit());
|
||||
}, overrides: <Type, Generator>{
|
||||
Platform: () => notMacosPlatform,
|
||||
FileSystem: () => fileSystem,
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
XcodeProjectInterpreter: () => FakeXcodeProjectInterpreterWithBuildSettings(),
|
||||
});
|
||||
|
||||
testUsingContext('xcarchive build invokes xcode build', () async {
|
||||
final BuildCommand command = BuildCommand();
|
||||
createMinimalMockProjectFiles();
|
||||
|
||||
await createTestCommandRunner(command).run(
|
||||
const <String>['build', 'xcarchive', '--no-pub']
|
||||
);
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fileSystem,
|
||||
ProcessManager: () => FakeProcessManager.list(<FakeCommand>[
|
||||
xattrCommand,
|
||||
setUpMockXcodeBuildHandler(),
|
||||
setUpMockXcodeBuildHandler(showBuildSettings: true),
|
||||
]),
|
||||
Platform: () => macosPlatform,
|
||||
XcodeProjectInterpreter: () => FakeXcodeProjectInterpreterWithBuildSettings(),
|
||||
});
|
||||
|
||||
testUsingContext('xcarchive build invokes xcode build with verbosity', () async {
|
||||
final BuildCommand command = BuildCommand();
|
||||
createMinimalMockProjectFiles();
|
||||
|
||||
await createTestCommandRunner(command).run(
|
||||
const <String>['build', 'xcarchive', '--no-pub', '-v']
|
||||
);
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fileSystem,
|
||||
ProcessManager: () => FakeProcessManager.list(<FakeCommand>[
|
||||
xattrCommand,
|
||||
setUpMockXcodeBuildHandler(verbose: true),
|
||||
setUpMockXcodeBuildHandler(verbose: true, showBuildSettings: true),
|
||||
]),
|
||||
Platform: () => macosPlatform,
|
||||
XcodeProjectInterpreter: () => FakeXcodeProjectInterpreterWithBuildSettings(),
|
||||
});
|
||||
|
||||
testUsingContext('Performs code size analysis and sends analytics', () async {
|
||||
final BuildCommand command = BuildCommand();
|
||||
createMinimalMockProjectFiles();
|
||||
|
||||
fileSystem.file('build/ios/Release-iphoneos/Runner.app/Frameworks/App.framework/App')
|
||||
..createSync(recursive: true)
|
||||
..writeAsBytesSync(List<int>.generate(10000, (int index) => 0));
|
||||
|
||||
// Capture Usage.test() events.
|
||||
final StringBuffer buffer = await capturedConsolePrint(() =>
|
||||
createTestCommandRunner(command).run(
|
||||
const <String>['build', 'xcarchive', '--no-pub', '--analyze-size']
|
||||
)
|
||||
);
|
||||
|
||||
expect(testLogger.statusText, contains('A summary of your iOS bundle analysis can be found at'));
|
||||
expect(buffer.toString(), contains('event {category: code-size-analysis, action: ios, label: null, value: null, cd33: '));
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fileSystem,
|
||||
ProcessManager: () => FakeProcessManager.list(<FakeCommand>[
|
||||
xattrCommand,
|
||||
setUpMockXcodeBuildHandler(onRun: () {
|
||||
fileSystem.file('build/flutter_size_01/snapshot.arm64.json')
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync('''[
|
||||
{
|
||||
"l": "dart:_internal",
|
||||
"c": "SubListIterable",
|
||||
"n": "[Optimized] skip",
|
||||
"s": 2400
|
||||
}
|
||||
]''');
|
||||
fileSystem.file('build/flutter_size_01/trace.arm64.json')
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync('{}');
|
||||
}),
|
||||
setUpMockXcodeBuildHandler(showBuildSettings: true),
|
||||
]),
|
||||
Platform: () => macosPlatform,
|
||||
FileSystemUtils: () => FileSystemUtils(fileSystem: fileSystem, platform: macosPlatform),
|
||||
Usage: () => usage,
|
||||
XcodeProjectInterpreter: () => FakeXcodeProjectInterpreterWithBuildSettings(),
|
||||
});
|
||||
}
|
@ -29,6 +29,7 @@ void main() {
|
||||
BuildWebCommand(verboseHelp: false),
|
||||
BuildApkCommand(verboseHelp: false),
|
||||
BuildIOSCommand(verboseHelp: false),
|
||||
BuildIOSArchiveCommand(verboseHelp: false),
|
||||
BuildAppBundleCommand(verboseHelp: false),
|
||||
BuildFuchsiaCommand(verboseHelp: false),
|
||||
BuildAarCommand(verboseHelp: false),
|
||||
|
Loading…
Reference in New Issue
Block a user