Skip to content

Commit 9ae8a6b

Browse files
authored
Reland #128236 "Improve build output for all platforms" (#145495)
Reland #128236, reverted in flutter/flutter#143125, flutter/flutter#145261, and flutter/flutter#145487. The errors were raised in post-submit tests on Windows. I have finally obtained a Windows machine to reproduce the errors locally and adjust the test (remove size expectation and rename output `.exe`). ## Original description Improves the build output: 1. Gives confirmation that the build succeeded, in green 1. Gives the path to the built executable, without a trailing period to make it slightly easier to cmd/ctrl+open 1. Gives the size of the built executable (when the built executable is self contained) ### `apk`, `appbundle` <img width="607" alt="image" src="https://github.com/flutter/flutter/assets/6655696/ecc52abe-cd2e-4116-b22a-8385ae3e980d"> <img width="634" alt="image" src="https://github.com/flutter/flutter/assets/6655696/8af8bd33-c0bd-4215-9a06-9652ee019436"> ### `macos`, `ios`, `ipa` Build executables are self-contained and use a newly introduced `OperatingSystemUtils.getDirectorySize`. <img width="514" alt="image" src="https://github.com/flutter/flutter/assets/6655696/b5918a69-3959-4417-9205-4f501d185257"> <img width="581" alt="image" src="https://github.com/flutter/flutter/assets/6655696/d72fd420-18cf-4470-9e4b-b6ac10fbcd50"> <img width="616" alt="image" src="https://github.com/flutter/flutter/assets/6655696/5f235ce1-252a-4c13-898f-139f6c7bc698"> ### `windows`, `linux`, and `web` Build executables aren't self-contained, and folder size can sometimes overestimate distribution size, therefore their size isn't mentioned (see discussion below). <img width="647" alt="image" src="https://github.com/flutter/flutter/assets/6655696/7179e771-1eb7-48f6-b770-975bc073437b"> <img width="658" alt="image" src="https://github.com/flutter/flutter/assets/6655696/a6801cab-7b5a-4975-a406-f4c9fa44d7a2"> <img width="608" alt="image" src="https://github.com/flutter/flutter/assets/6655696/ee7c4125-a273-4a65-95d7-ab441edf8ac5"> ### Size reporting When applicable, the printed size matches the OS reported size. - macOS <img width="391" alt="image" src="https://github.com/flutter/flutter/assets/6655696/881cbfb1-d355-444b-ab44-c1a6343190ce"> - Windows <img width="338" alt="image" src="https://github.com/flutter/flutter/assets/6655696/3b806def-3d15-48a9-8a25-df200d6feef7"> - Linux <img width="320" alt="image" src="https://github.com/flutter/flutter/assets/6655696/89a4aa3d-2148-4f3b-b231-f93a057fee2b"> ## Related issues Part of #120127 Fixes flutter/flutter#121401
1 parent 09b1afb commit 9ae8a6b

File tree

20 files changed

+264
-43
lines changed

20 files changed

+264
-43
lines changed

dev/devicelab/lib/tasks/run_tests.dart

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ class AndroidRunOutputTest extends RunOutputTask {
135135
_findNextMatcherInList(
136136
stdout,
137137
(String line) => line.contains('Built build/app/outputs/flutter-apk/$apk') &&
138-
(!release || line.contains('MB).')),
138+
(!release || line.contains('MB)')),
139139
'Built build/app/outputs/flutter-apk/$apk',
140140
);
141141

@@ -177,7 +177,7 @@ class WindowsRunOutputTest extends DesktopRunOutputTest {
177177
multiLine: true,
178178
);
179179
static final RegExp _builtOutput = RegExp(
180-
r'Built build\\windows\\(x64|arm64)\\runner\\(Debug|Release)\\\w+\.exe( \(\d+(\.\d+)?MB\))?\.',
180+
r'Built build\\windows\\(x64|arm64)\\runner\\(Debug|Release)\\\w+\.exe( \(\d+(\.\d+)?MB\))?',
181181
);
182182

183183
@override
@@ -196,15 +196,9 @@ class WindowsRunOutputTest extends DesktopRunOutputTest {
196196
return false;
197197
}
198198

199-
// Size information is only included in release builds.
200-
final bool hasSize = line.contains('MB).');
201-
if (release != hasSize) {
202-
return false;
203-
}
204-
205199
return true;
206200
},
207-
'Built build\\windows\\$arch\\runner\\$buildMode\\app.exe',
201+
'Built build\\windows\\$arch\\runner\\$buildMode\\ui.exe',
208202
);
209203
}
210204
}

packages/flutter_tools/lib/src/android/gradle.dart

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -549,14 +549,15 @@ class AndroidGradleBuilder implements AndroidBuilder {
549549
final File bundleFile = findBundleFile(project, buildInfo, _logger, _usage, _analytics);
550550
final String appSize = (buildInfo.mode == BuildMode.debug)
551551
? '' // Don't display the size when building a debug variant.
552-
: ' (${getSizeAsMB(bundleFile.lengthSync())})';
552+
: ' (${getSizeAsPlatformMB(bundleFile.lengthSync())})';
553553

554554
if (buildInfo.codeSizeDirectory != null) {
555555
await _performCodeSizeAnalysis('aab', bundleFile, androidBuildInfo);
556556
}
557557

558558
_logger.printStatus(
559-
'${_logger.terminal.successMark} Built ${_fileSystem.path.relative(bundleFile.path)}$appSize.',
559+
'${_logger.terminal.successMark} '
560+
'Built ${_fileSystem.path.relative(bundleFile.path)}$appSize',
560561
color: TerminalColor.green,
561562
);
562563
return;
@@ -586,9 +587,10 @@ class AndroidGradleBuilder implements AndroidBuilder {
586587

587588
final String appSize = (buildInfo.mode == BuildMode.debug)
588589
? '' // Don't display the size when building a debug variant.
589-
: ' (${getSizeAsMB(apkFile.lengthSync())})';
590+
: ' (${getSizeAsPlatformMB(apkFile.lengthSync())})';
590591
_logger.printStatus(
591-
'${_logger.terminal.successMark} Built ${_fileSystem.path.relative(apkFile.path)}$appSize.',
592+
'${_logger.terminal.successMark} '
593+
'Built ${_fileSystem.path.relative(apkFile.path)}$appSize',
592594
color: TerminalColor.green,
593595
);
594596

@@ -780,7 +782,8 @@ class AndroidGradleBuilder implements AndroidBuilder {
780782
);
781783
}
782784
_logger.printStatus(
783-
'${_logger.terminal.successMark} Built ${_fileSystem.path.relative(repoDirectory.path)}.',
785+
'${_logger.terminal.successMark} '
786+
'Built ${_fileSystem.path.relative(repoDirectory.path)}',
784787
color: TerminalColor.green,
785788
);
786789
}

packages/flutter_tools/lib/src/base/os.dart

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,18 @@ abstract class OperatingSystemUtils {
105105
/// Return the File representing a new pipe.
106106
File makePipe(String path);
107107

108+
/// Return a directory's total size in bytes.
109+
int? getDirectorySize(Directory directory) {
110+
int? size;
111+
for (final FileSystemEntity entity in directory.listSync(recursive: true, followLinks: false)) {
112+
if (entity is File) {
113+
size ??= 0;
114+
size += entity.lengthSync();
115+
}
116+
}
117+
return size;
118+
}
119+
108120
void unzip(File file, Directory targetDirectory);
109121

110122
void unpack(File gzippedTarFile, Directory targetDirectory);

packages/flutter_tools/lib/src/base/utils.dart

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@ import 'dart:math' as math;
77

88
import 'package:file/file.dart';
99
import 'package:intl/intl.dart';
10+
import 'package:meta/meta.dart';
1011
import 'package:path/path.dart' as path; // flutter_ignore: package_path_import
1112

1213
import '../convert.dart';
14+
import 'platform.dart';
1315

1416
/// A path jointer for URL paths.
1517
final path.Context urlContext = path.url;
@@ -88,9 +90,14 @@ String getElapsedAsMilliseconds(Duration duration) {
8890
return '${kMillisecondsFormat.format(duration.inMilliseconds)}ms';
8991
}
9092

91-
/// Return a String - with units - for the size in MB of the given number of bytes.
92-
String getSizeAsMB(int bytesLength) {
93-
return '${(bytesLength / (1024 * 1024)).toStringAsFixed(1)}MB';
93+
/// Return a platform-appropriate [String] representing the size of the given number of bytes.
94+
String getSizeAsPlatformMB(int bytesLength, {
95+
@visibleForTesting Platform platform = const LocalPlatform()
96+
}) {
97+
// Because Windows displays 'MB' but actually reports MiB, we calculate MiB
98+
// accordingly on Windows.
99+
final int bytesInPlatformMB = platform.isWindows ? 1024 * 1024 : 1000 * 1000;
100+
return '${(bytesLength / bytesInPlatformMB).toStringAsFixed(1)}MB';
94101
}
95102

96103
/// A class to maintain a list of items, fire events when items are added or

packages/flutter_tools/lib/src/commands/build_ios.dart

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,16 @@
55
import 'dart:typed_data';
66

77
import 'package:crypto/crypto.dart';
8-
import 'package:file/file.dart';
98
import 'package:meta/meta.dart';
109
import 'package:unified_analytics/unified_analytics.dart';
1110

1211
import '../base/analyze_size.dart';
1312
import '../base/common.dart';
1413
import '../base/error_handling_io.dart';
14+
import '../base/file_system.dart';
1515
import '../base/logger.dart';
1616
import '../base/process.dart';
17+
import '../base/terminal.dart';
1718
import '../base/utils.dart';
1819
import '../build_info.dart';
1920
import '../convert.dart';
@@ -523,7 +524,17 @@ class BuildIOSArchiveCommand extends _BuildIOSSubCommand {
523524
return FlutterCommandResult.success();
524525
}
525526

526-
globals.printStatus('Built IPA to $absoluteOutputPath.');
527+
final Directory outputDirectory = globals.fs.directory(absoluteOutputPath);
528+
final int? directorySize = globals.os.getDirectorySize(outputDirectory);
529+
final String appSize = (buildInfo.mode == BuildMode.debug || directorySize == null)
530+
? '' // Don't display the size when building a debug variant.
531+
: ' (${getSizeAsPlatformMB(directorySize)})';
532+
533+
globals.printStatus(
534+
'${globals.terminal.successMark} '
535+
'Built IPA to ${globals.fs.path.relative(outputDirectory.path)}$appSize',
536+
color: TerminalColor.green,
537+
);
527538

528539
if (isAppStoreUpload) {
529540
globals.printStatus('To upload to the App Store either:');
@@ -737,7 +748,17 @@ abstract class _BuildIOSSubCommand extends BuildSubCommand {
737748
}
738749

739750
if (result.output != null) {
740-
globals.printStatus('Built ${result.output}.');
751+
final Directory outputDirectory = globals.fs.directory(result.output);
752+
final int? directorySize = globals.os.getDirectorySize(outputDirectory);
753+
final String appSize = (buildInfo.mode == BuildMode.debug || directorySize == null)
754+
? '' // Don't display the size when building a debug variant.
755+
: ' (${getSizeAsPlatformMB(directorySize)})';
756+
757+
globals.printStatus(
758+
'${globals.terminal.successMark} '
759+
'Built ${globals.fs.path.relative(outputDirectory.path)}$appSize',
760+
color: TerminalColor.green,
761+
);
741762

742763
// When an app is successfully built, record to analytics whether Impeller
743764
// is enabled or disabled.

packages/flutter_tools/lib/src/isolated/resident_web_runner.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -577,7 +577,7 @@ Please provide a valid TCP port (an integer between 0 and 65535, inclusive).
577577
shaderCompiler: device!.developmentShaderCompiler,
578578
);
579579
devFSStatus.stop();
580-
_logger.printTrace('Synced ${getSizeAsMB(report.syncedBytes)}.');
580+
_logger.printTrace('Synced ${getSizeAsPlatformMB(report.syncedBytes)}.');
581581
return report;
582582
}
583583

packages/flutter_tools/lib/src/linux/build_linux.dart

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import '../base/common.dart';
1010
import '../base/file_system.dart';
1111
import '../base/logger.dart';
1212
import '../base/project_migrator.dart';
13+
import '../base/terminal.dart';
1314
import '../base/utils.dart';
1415
import '../build_info.dart';
1516
import '../cache.dart';
@@ -73,16 +74,31 @@ Future<void> buildLinux(
7374
final Status status = logger.startProgress(
7475
'Building Linux application...',
7576
);
77+
final String buildModeName = buildInfo.mode.cliName;
78+
final Directory platformBuildDirectory = globals.fs.directory(getLinuxBuildDirectory(targetPlatform));
79+
final Directory buildDirectory = platformBuildDirectory.childDirectory(buildModeName);
7680
try {
77-
final String buildModeName = buildInfo.mode.cliName;
78-
final Directory buildDirectory =
79-
globals.fs.directory(getLinuxBuildDirectory(targetPlatform)).childDirectory(buildModeName);
8081
await _runCmake(buildModeName, linuxProject.cmakeFile.parent, buildDirectory,
8182
needCrossBuild, targetPlatform, targetSysroot);
8283
await _runBuild(buildDirectory);
8384
} finally {
8485
status.cancel();
8586
}
87+
88+
final String? binaryName = getCmakeExecutableName(linuxProject);
89+
final File binaryFile = buildDirectory
90+
.childDirectory('bundle')
91+
.childFile('$binaryName');
92+
final FileSystemEntity buildOutput = binaryFile.existsSync() ? binaryFile : binaryFile.parent;
93+
// We don't print a size because the output directory can contain
94+
// optional files not needed by the user and because the binary is not
95+
// self-contained.
96+
globals.printStatus(
97+
'${globals.terminal.successMark} '
98+
'Built ${globals.fs.path.relative(buildOutput.path)}',
99+
color: TerminalColor.green,
100+
);
101+
86102
if (buildInfo.codeSizeDirectory != null && sizeAnalyzer != null) {
87103
final String arch = getNameForTargetPlatform(targetPlatform);
88104
final File codeSizeFile = globals.fs.directory(buildInfo.codeSizeDirectory)

packages/flutter_tools/lib/src/macos/build_macos.dart

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import '../base/common.dart';
99
import '../base/file_system.dart';
1010
import '../base/logger.dart';
1111
import '../base/project_migrator.dart';
12+
import '../base/terminal.dart';
13+
import '../base/utils.dart';
1214
import '../build_info.dart';
1315
import '../convert.dart';
1416
import '../globals.dart' as globals;
@@ -18,6 +20,7 @@ import '../migrations/xcode_project_object_version_migration.dart';
1820
import '../migrations/xcode_script_build_phase_migration.dart';
1921
import '../migrations/xcode_thin_binary_build_phase_input_paths_migration.dart';
2022
import '../project.dart';
23+
import 'application_package.dart';
2124
import 'cocoapod_utils.dart';
2225
import 'migrations/flutter_application_migration.dart';
2326
import 'migrations/macos_deployment_target_migration.dart';
@@ -158,9 +161,24 @@ Future<void> buildMacOS({
158161
} finally {
159162
status.cancel();
160163
}
164+
161165
if (result != 0) {
162166
throwToolExit('Build process failed');
163167
}
168+
final String? applicationBundle = MacOSApp.fromMacOSProject(flutterProject.macos).applicationBundle(buildInfo);
169+
if (applicationBundle != null) {
170+
final Directory outputDirectory = globals.fs.directory(applicationBundle);
171+
// This output directory is the .app folder itself.
172+
final int? directorySize = globals.os.getDirectorySize(outputDirectory);
173+
final String appSize = (buildInfo.mode == BuildMode.debug || directorySize == null)
174+
? '' // Don't display the size when building a debug variant.
175+
: ' (${getSizeAsPlatformMB(directorySize)})';
176+
globals.printStatus(
177+
'${globals.terminal.successMark} '
178+
'Built ${globals.fs.path.relative(outputDirectory.path)}$appSize',
179+
color: TerminalColor.green,
180+
);
181+
}
164182
await _writeCodeSizeAnalysis(buildInfo, sizeAnalyzer);
165183
final Duration elapsedDuration = sw.elapsed;
166184
globals.flutterUsage.sendTiming('build', 'xcode-macos', elapsedDuration);

packages/flutter_tools/lib/src/resident_runner.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -597,7 +597,7 @@ class FlutterDevice {
597597
return UpdateFSReport();
598598
}
599599
devFSStatus.stop();
600-
globals.printTrace('Synced ${getSizeAsMB(report.syncedBytes)}.');
600+
globals.printTrace('Synced ${getSizeAsPlatformMB(report.syncedBytes)}.');
601601
return report;
602602
}
603603

packages/flutter_tools/lib/src/web/compile.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import '../base/common.dart';
1010
import '../base/file_system.dart';
1111
import '../base/logger.dart';
1212
import '../base/project_migrator.dart';
13+
import '../base/terminal.dart';
1314
import '../base/utils.dart';
1415
import '../build_info.dart';
1516
import '../build_system/build_system.dart';
@@ -130,6 +131,14 @@ class WebBuilder {
130131
status.stop();
131132
}
132133

134+
// We don't print a size because the output directory can contain
135+
// optional files not needed by the user.
136+
globals.printStatus(
137+
'${globals.terminal.successMark} '
138+
'Built ${globals.fs.path.relative(outputDirectory.path)}',
139+
color: TerminalColor.green,
140+
);
141+
133142
final String buildSettingsString = _buildEventAnalyticsSettings(
134143
configs: compilerConfigs,
135144
);

0 commit comments

Comments
 (0)