Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 38 additions & 10 deletions packages/flutter_tools/bin/macos_assemble.sh
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,28 @@ BuildApp() {
if [[ -n "$LOCAL_ENGINE_HOST" ]]; then
flutter_args+=("--local-engine-host=${LOCAL_ENGINE_HOST}")
fi

local architectures="${ARCHS}"
if [[ -n "$1" && "$1" == "prepare" ]]; then
# The "prepare" command runs in a pre-action script, which doesn't always
# filter the "ARCHS" build setting to only the active arch. To workaround,
# if "ONLY_ACTIVE_ARCH" is true and the "NATIVE_ARCH" is arm, assume the
# active arch is also arm to improve caching. If this assumption is
# incorrect, it will later be corrected by the "build" command.
if [[ -n "$ONLY_ACTIVE_ARCH" && "$ONLY_ACTIVE_ARCH" == "YES" && -n "$NATIVE_ARCH" ]]; then
if [[ "$NATIVE_ARCH" == *"arm"* ]]; then
architectures="arm64"
else
architectures="x86_64"
fi
fi
fi

flutter_args+=(
"assemble"
"--no-version-check"
"-dTargetPlatform=darwin"
"-dDarwinArchs=${ARCHS}"
"-dDarwinArchs=${architectures}"
"-dTargetFile=${target_path}"
"-dBuildMode=${build_mode}"
"-dTreeShakeIcons=${TREE_SHAKE_ICONS}"
Expand All @@ -132,6 +149,19 @@ BuildApp() {
"--output=${BUILT_PRODUCTS_DIR}"
)

local target="${build_mode}_macos_bundle_flutter_assets";
if [[ -n "$1" && "$1" == "prepare" ]]; then
# The "prepare" command only targets the UnpackMacOS target, which copies the
# FlutterMacOS framework to the BUILT_PRODUCTS_DIR.
target="${build_mode}_unpack_macos"

# Use the PreBuildAction define flag to force the tool to use a different
# filecache file for the "prepare" command. This will make the environment
# buildPrefix for the "prepare" command unique from the "build" command.
# This will improve caching since the "build" command has more target dependencies.
flutter_args+=("-dPreBuildAction=PrepareFramework")
fi

if [[ -n "$FLAVOR" ]]; then
flutter_args+=("-dFlavor=${FLAVOR}")
fi
Expand All @@ -145,20 +175,18 @@ BuildApp() {
flutter_args+=("-dCodeSizeDirectory=${CODE_SIZE_DIRECTORY}")
fi

# Run flutter assemble with the build mode specific target that was passed in.
# If no target was passed it, default to build mode specific
# macos_bundle_flutter_assets target.
if [[ -n "$1" ]]; then
flutter_args+=("${build_mode}$1")
else
flutter_args+=("${build_mode}_macos_bundle_flutter_assets")
fi
flutter_args+=("${target}")

RunCommand "${flutter_args[@]}"
}

PrepareFramework() {
BuildApp "_unpack_macos"
# The "prepare" command runs in a pre-action script, which also runs when
# using the Xcode/xcodebuild clean command. Skip if cleaning.
if [[ $ACTION == "clean" ]]; then
exit 0
fi
BuildApp "prepare"
}

# Adds the App.framework as an embedded binary, the flutter_assets as
Expand Down
51 changes: 47 additions & 4 deletions packages/flutter_tools/bin/xcode_backend.dart
Original file line number Diff line number Diff line change
Expand Up @@ -354,14 +354,25 @@ class Context {
}

void prepare() {
// The "prepare" command runs in a pre-action script, which also runs when
// using the Xcode/xcodebuild clean command. Skip if cleaning.
if (environment['ACTION'] == 'clean') {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh good call...

return;
}
final bool verbose = (environment['VERBOSE_SCRIPT_LOGGING'] ?? '').isNotEmpty;
final String sourceRoot = environment['SOURCE_ROOT'] ?? '';
final String projectPath = environment['FLUTTER_APPLICATION_PATH'] ?? '$sourceRoot/..';

final String buildMode = parseFlutterBuildMode();

final List<String> flutterArgs = _generateFlutterArgsForAssemble(buildMode, verbose);
final List<String> flutterArgs = _generateFlutterArgsForAssemble(
'prepare',
buildMode,
verbose,
);

// The "prepare" command only targets the UnpackIOS target, which copies the
// Flutter framework to the BUILT_PRODUCTS_DIR.
flutterArgs.add('${buildMode}_unpack_ios');

final ProcessResult result = runSync(
Expand All @@ -385,7 +396,11 @@ class Context {

final String buildMode = parseFlutterBuildMode();

final List<String> flutterArgs = _generateFlutterArgsForAssemble(buildMode, verbose);
final List<String> flutterArgs = _generateFlutterArgsForAssemble(
'build',
buildMode,
verbose,
);

flutterArgs.add('${buildMode}_ios_bundle_flutter_assets');

Expand All @@ -408,7 +423,11 @@ class Context {
echo('Project $projectPath built and packaged successfully.');
}

List<String> _generateFlutterArgsForAssemble(String buildMode, bool verbose) {
List<String> _generateFlutterArgsForAssemble(
String command,
String buildMode,
bool verbose,
) {
String targetPath = 'lib/main.dart';
if (environment['FLUTTER_TARGET'] != null) {
targetPath = environment['FLUTTER_TARGET']!;
Expand Down Expand Up @@ -442,6 +461,22 @@ class Context {
flutterArgs.add('--local-engine-host=${environment['LOCAL_ENGINE_HOST']}');
}

String architectures = environment['ARCHS'] ?? '';
if (command == 'prepare') {
// The "prepare" command runs in a pre-action script, which doesn't always
// filter the "ARCHS" build setting to only the active arch. To workaround,
// if "ONLY_ACTIVE_ARCH" is true and the "NATIVE_ARCH" is arm, assume the
// active arch is also arm to improve caching. If this assumption is
// incorrect, it will later be corrected by the "build" command.
if (environment['ONLY_ACTIVE_ARCH'] == 'YES' && environment['NATIVE_ARCH'] != null) {
if (environment['NATIVE_ARCH']!.contains('arm')) {
architectures = 'arm64';
} else {
architectures = 'x86_64';
}
}
}

flutterArgs.addAll(<String>[
'assemble',
'--no-version-check',
Expand All @@ -450,7 +485,7 @@ class Context {
'-dTargetFile=$targetPath',
'-dBuildMode=$buildMode',
if (environment['FLAVOR'] != null) '-dFlavor=${environment['FLAVOR']}',
'-dIosArchs=${environment['ARCHS'] ?? ''}',
'-dIosArchs=$architectures',
'-dSdkRoot=${environment['SDKROOT'] ?? ''}',
'-dSplitDebugInfo=${environment['SPLIT_DEBUG_INFO'] ?? ''}',
'-dTreeShakeIcons=${environment['TREE_SHAKE_ICONS'] ?? ''}',
Expand All @@ -463,6 +498,14 @@ class Context {
'--ExtraFrontEndOptions=${environment['EXTRA_FRONT_END_OPTIONS'] ?? ''}',
]);

if (command == 'prepare') {
// Use the PreBuildAction define flag to force the tool to use a different
// filecache file for the "prepare" command. This will make the environment
// buildPrefix for the "prepare" command unique from the "build" command.
// This will improve caching since the "build" command has more target dependencies.
flutterArgs.add('-dPreBuildAction=PrepareFramework');
}

if (environment['PERFORMANCE_MEASUREMENT_FILE'] != null && environment['PERFORMANCE_MEASUREMENT_FILE']!.isNotEmpty) {
flutterArgs.add('--performance-measurement-file=${environment['PERFORMANCE_MEASUREMENT_FILE']}');
}
Expand Down
6 changes: 6 additions & 0 deletions packages/flutter_tools/lib/src/build_info.dart
Original file line number Diff line number Diff line change
Expand Up @@ -958,6 +958,12 @@ const String kBuildNumber = 'BuildNumber';
/// Will be "build" when building and "install" when archiving.
const String kXcodeAction = 'Action';

/// The define of the Xcode build Pre-action.
///
/// Will be "PrepareFramework" when copying the Flutter/FlutterMacOS framework
/// to the BUILT_PRODUCTS_DIR prior to the build.
const String kXcodePreAction = 'PreBuildAction';

final Converter<String, String> _defineEncoder = utf8.encoder.fuse(base64.encoder);
final Converter<String, String> _defineDecoder = base64.decoder.fuse(utf8.decoder);

Expand Down
8 changes: 8 additions & 0 deletions packages/flutter_tools/lib/src/build_system/build_system.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import '../base/file_system.dart';
import '../base/logger.dart';
import '../base/platform.dart';
import '../base/utils.dart';
import '../build_info.dart';
import '../cache.dart';
import '../convert.dart';
import '../reporting/reporting.dart';
Expand Down Expand Up @@ -735,6 +736,13 @@ class FlutterBuildSystem extends BuildSystem {
FileSystem fileSystem,
Map<String, File> currentOutputs,
) {
if (environment.defines[kXcodePreAction] == 'PrepareFramework') {
// If the current build is the PrepareFramework Xcode pre-action, skip
// updating the last build identifier and cleaning up the previous build
// since this build is not a complete build.
return;
}

final String currentBuildId = fileSystem.path.basename(environment.buildDir.path);
final File lastBuildIdFile = environment.outputDir.childFile('.last_build_id');
if (!lastBuildIdFile.existsSync()) {
Expand Down
104 changes: 104 additions & 0 deletions packages/flutter_tools/test/general.shard/xcode_backend_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ void main() {
'--ExtraGenSnapshotOptions=',
'--DartDefines=',
'--ExtraFrontEndOptions=',
'-dPreBuildAction=PrepareFramework',
'debug_unpack_ios',
],
),
Expand Down Expand Up @@ -315,6 +316,7 @@ void main() {
'--ExtraGenSnapshotOptions=',
'--DartDefines=',
'--ExtraFrontEndOptions=',
'-dPreBuildAction=PrepareFramework',
'debug_unpack_ios',
],
),
Expand Down Expand Up @@ -386,6 +388,7 @@ void main() {
'--ExtraGenSnapshotOptions=$extraGenSnapshotOptions',
'--DartDefines=$dartDefines',
'--ExtraFrontEndOptions=$extraFrontEndOptions',
'-dPreBuildAction=PrepareFramework',
'-dCodesignIdentity=$expandedCodeSignIdentity',
'release_unpack_ios',
],
Expand All @@ -395,6 +398,107 @@ void main() {
)..run();
expect(context.stderr, isEmpty);
});

test('assumes ARCHS based on NATIVE_ARCH if ONLY_ACTIVE_ARCH is YES', () {
final Directory buildDir = fileSystem.directory('/path/to/builds')
..createSync(recursive: true);
final Directory flutterRoot = fileSystem.directory('/path/to/flutter')
..createSync(recursive: true);
final File pipe = fileSystem.file('/tmp/pipe')
..createSync(recursive: true);
const String buildMode = 'Debug';
final TestContext context = TestContext(
<String>['prepare'],
<String, String>{
'BUILT_PRODUCTS_DIR': buildDir.path,
'CONFIGURATION': buildMode,
'FLUTTER_ROOT': flutterRoot.path,
'INFOPLIST_PATH': 'Info.plist',
'ARCHS': 'arm64 x86_64',
'ONLY_ACTIVE_ARCH': 'YES',
'NATIVE_ARCH': 'arm64e'
},
commands: <FakeCommand>[
FakeCommand(
command: <String>[
'${flutterRoot.path}/bin/flutter',
'assemble',
'--no-version-check',
'--output=${buildDir.path}/',
'-dTargetPlatform=ios',
'-dTargetFile=lib/main.dart',
'-dBuildMode=${buildMode.toLowerCase()}',
'-dIosArchs=arm64',
'-dSdkRoot=',
'-dSplitDebugInfo=',
'-dTreeShakeIcons=',
'-dTrackWidgetCreation=',
'-dDartObfuscation=',
'-dAction=',
'-dFrontendServerStarterPath=',
'--ExtraGenSnapshotOptions=',
'--DartDefines=',
'--ExtraFrontEndOptions=',
'-dPreBuildAction=PrepareFramework',
'debug_unpack_ios',
],
),
],
fileSystem: fileSystem,
scriptOutputStreamFile: pipe,
)..run();
expect(context.stderr, isEmpty);
});

test('does not assumes ARCHS if ONLY_ACTIVE_ARCH is not YES', () {
final Directory buildDir = fileSystem.directory('/path/to/builds')
..createSync(recursive: true);
final Directory flutterRoot = fileSystem.directory('/path/to/flutter')
..createSync(recursive: true);
final File pipe = fileSystem.file('/tmp/pipe')
..createSync(recursive: true);
const String buildMode = 'Debug';
final TestContext context = TestContext(
<String>['prepare'],
<String, String>{
'BUILT_PRODUCTS_DIR': buildDir.path,
'CONFIGURATION': buildMode,
'FLUTTER_ROOT': flutterRoot.path,
'INFOPLIST_PATH': 'Info.plist',
'ARCHS': 'arm64 x86_64',
'NATIVE_ARCH': 'arm64e'
},
commands: <FakeCommand>[
FakeCommand(
command: <String>[
'${flutterRoot.path}/bin/flutter',
'assemble',
'--no-version-check',
'--output=${buildDir.path}/',
'-dTargetPlatform=ios',
'-dTargetFile=lib/main.dart',
'-dBuildMode=${buildMode.toLowerCase()}',
'-dIosArchs=arm64 x86_64',
'-dSdkRoot=',
'-dSplitDebugInfo=',
'-dTreeShakeIcons=',
'-dTrackWidgetCreation=',
'-dDartObfuscation=',
'-dAction=',
'-dFrontendServerStarterPath=',
'--ExtraGenSnapshotOptions=',
'--DartDefines=',
'--ExtraFrontEndOptions=',
'-dPreBuildAction=PrepareFramework',
'debug_unpack_ios',
],
),
],
fileSystem: fileSystem,
scriptOutputStreamFile: pipe,
)..run();
expect(context.stderr, isEmpty);
});
});
}

Expand Down
Loading