Unverified Commit b2d19d2a authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

Add support for macOS release/profile mode (3 of 3) (#38909)

parent 055c5489
...@@ -37,6 +37,9 @@ if [[ -n "$FLUTTER_ENGINE" ]]; then ...@@ -37,6 +37,9 @@ if [[ -n "$FLUTTER_ENGINE" ]]; then
flutter_engine_flag="--local-engine-src-path=${FLUTTER_ENGINE}" flutter_engine_flag="--local-engine-src-path=${FLUTTER_ENGINE}"
fi fi
# Set the build mode
build_mode="$(echo "${FLUTTER_BUILD_MODE:-${CONFIGURATION}}" | tr "[:upper:]" "[:lower:]")"
if [[ -n "$LOCAL_ENGINE" ]]; then if [[ -n "$LOCAL_ENGINE" ]]; then
if [[ $(echo "$LOCAL_ENGINE" | tr "[:upper:]" "[:lower:]") != *"$build_mode"* ]]; then if [[ $(echo "$LOCAL_ENGINE" | tr "[:upper:]" "[:lower:]") != *"$build_mode"* ]]; then
EchoError "========================================================================" EchoError "========================================================================"
...@@ -53,19 +56,12 @@ if [[ -n "$LOCAL_ENGINE" ]]; then ...@@ -53,19 +56,12 @@ if [[ -n "$LOCAL_ENGINE" ]]; then
local_engine_flag="--local-engine=${LOCAL_ENGINE}" local_engine_flag="--local-engine=${LOCAL_ENGINE}"
fi fi
# Set the build mode
build_mode="$(echo "${FLUTTER_BUILD_MODE:-${CONFIGURATION}}" | tr "[:upper:]" "[:lower:]")"
# The path where the input/output xcfilelists are stored. These are used by xcode # The path where the input/output xcfilelists are stored. These are used by xcode
# to conditionally skip this script phase if neither have changed. # to conditionally skip this script phase if neither have changed.
ephemeral_dir="${SOURCE_ROOT}/Flutter/ephemeral" ephemeral_dir="${SOURCE_ROOT}/Flutter/ephemeral"
build_inputs_path="${ephemeral_dir}/FlutterInputs.xcfilelist" build_inputs_path="${ephemeral_dir}/FlutterInputs.xcfilelist"
build_outputs_path="${ephemeral_dir}/FlutterOutputs.xcfilelist" build_outputs_path="${ephemeral_dir}/FlutterOutputs.xcfilelist"
# TODO(jonahwilliams): connect AOT rules once engine artifacts are published.
# The build mode is currently hard-coded to debug only. Since this does not yet
# support AOT, we need to ensure that we compile the kernel file in debug so that
# the VM can load it.
RunCommand "${FLUTTER_ROOT}/bin/flutter" --suppress-analytics \ RunCommand "${FLUTTER_ROOT}/bin/flutter" --suppress-analytics \
${verbose_flag} \ ${verbose_flag} \
${flutter_engine_flag} \ ${flutter_engine_flag} \
...@@ -73,7 +69,7 @@ RunCommand "${FLUTTER_ROOT}/bin/flutter" --suppress-analytics \ ...@@ -73,7 +69,7 @@ RunCommand "${FLUTTER_ROOT}/bin/flutter" --suppress-analytics \
assemble \ assemble \
-dTargetPlatform=darwin-x64 \ -dTargetPlatform=darwin-x64 \
-dTargetFile="${target_path}" \ -dTargetFile="${target_path}" \
-dBuildMode=debug \ -dBuildMode="${build_mode}" \
--build-inputs="${build_inputs_path}" \ --build-inputs="${build_inputs_path}" \
--build-outputs="${build_outputs_path}" \ --build-outputs="${build_outputs_path}" \
debug_bundle_flutter_assets "${build_mode}_macos_bundle_flutter_assets"
...@@ -160,7 +160,6 @@ class CachedArtifacts extends Artifacts { ...@@ -160,7 +160,6 @@ class CachedArtifacts extends Artifacts {
@override @override
String getArtifactPath(Artifact artifact, { TargetPlatform platform, BuildMode mode }) { String getArtifactPath(Artifact artifact, { TargetPlatform platform, BuildMode mode }) {
platform ??= _currentHostPlatform;
switch (platform) { switch (platform) {
case TargetPlatform.android_arm: case TargetPlatform.android_arm:
case TargetPlatform.android_arm64: case TargetPlatform.android_arm64:
...@@ -170,15 +169,15 @@ class CachedArtifacts extends Artifacts { ...@@ -170,15 +169,15 @@ class CachedArtifacts extends Artifacts {
case TargetPlatform.ios: case TargetPlatform.ios:
return _getIosArtifactPath(artifact, platform, mode); return _getIosArtifactPath(artifact, platform, mode);
case TargetPlatform.darwin_x64: case TargetPlatform.darwin_x64:
return _getDarwinArtifactPath(artifact, platform, mode);
case TargetPlatform.linux_x64: case TargetPlatform.linux_x64:
case TargetPlatform.windows_x64:
case TargetPlatform.fuchsia: case TargetPlatform.fuchsia:
case TargetPlatform.windows_x64:
case TargetPlatform.tester: case TargetPlatform.tester:
case TargetPlatform.web_javascript: case TargetPlatform.web_javascript:
return _getHostArtifactPath(artifact, platform, mode); default: // could be null, but that can't be specified as a case.
return _getHostArtifactPath(artifact, platform ?? _currentHostPlatform, mode);
} }
assert(false, 'Invalid platform $platform.');
return null;
} }
@override @override
...@@ -186,6 +185,16 @@ class CachedArtifacts extends Artifacts { ...@@ -186,6 +185,16 @@ class CachedArtifacts extends Artifacts {
return fs.path.basename(_getEngineArtifactsPath(platform, mode)); return fs.path.basename(_getEngineArtifactsPath(platform, mode));
} }
String _getDarwinArtifactPath(Artifact artifact, TargetPlatform platform, BuildMode mode) {
// When platform is null, a generic host platform artifact is being requested
// and not the gen_snapshot for darwin as a target platform.
if (platform != null && artifact == Artifact.genSnapshot) {
final String engineDir = _getEngineArtifactsPath(platform, mode);
return fs.path.join(engineDir, _artifactToFileName(artifact));
}
return _getHostArtifactPath(artifact, platform ?? _currentHostPlatform, mode);
}
String _getAndroidArtifactPath(Artifact artifact, TargetPlatform platform, BuildMode mode) { String _getAndroidArtifactPath(Artifact artifact, TargetPlatform platform, BuildMode mode) {
final String engineDir = _getEngineArtifactsPath(platform, mode); final String engineDir = _getEngineArtifactsPath(platform, mode);
switch (artifact) { switch (artifact) {
...@@ -240,6 +249,7 @@ class CachedArtifacts extends Artifacts { ...@@ -240,6 +249,7 @@ class CachedArtifacts extends Artifacts {
} }
String _getHostArtifactPath(Artifact artifact, TargetPlatform platform, BuildMode mode) { String _getHostArtifactPath(Artifact artifact, TargetPlatform platform, BuildMode mode) {
assert(platform != null);
switch (artifact) { switch (artifact) {
case Artifact.genSnapshot: case Artifact.genSnapshot:
// For script snapshots any gen_snapshot binary will do. Returning gen_snapshot for // For script snapshots any gen_snapshot binary will do. Returning gen_snapshot for
...@@ -274,8 +284,14 @@ class CachedArtifacts extends Artifacts { ...@@ -274,8 +284,14 @@ class CachedArtifacts extends Artifacts {
case Artifact.linuxDesktopPath: case Artifact.linuxDesktopPath:
case Artifact.windowsDesktopPath: case Artifact.windowsDesktopPath:
case Artifact.flutterMacOSPodspec: case Artifact.flutterMacOSPodspec:
// TODO(jonahwilliams): remove once debug desktop artifacts are uploaded
// under a separate directory from the host artifacts.
// https://github.com/flutter/flutter/issues/38935
String platformDirName = getNameForTargetPlatform(platform);
if (mode == BuildMode.profile || mode == BuildMode.release) {
platformDirName = '$platformDirName-${getNameForBuildMode(mode)}';
}
final String engineArtifactsPath = cache.getArtifactDirectory('engine').path; final String engineArtifactsPath = cache.getArtifactDirectory('engine').path;
final String platformDirName = getNameForTargetPlatform(platform);
return fs.path.join(engineArtifactsPath, platformDirName, _artifactToFileName(artifact, platform, mode)); return fs.path.join(engineArtifactsPath, platformDirName, _artifactToFileName(artifact, platform, mode));
case Artifact.skyEnginePath: case Artifact.skyEnginePath:
final Directory dartPackageDirectory = cache.getCacheDir('pkg'); final Directory dartPackageDirectory = cache.getCacheDir('pkg');
...@@ -293,6 +309,14 @@ class CachedArtifacts extends Artifacts { ...@@ -293,6 +309,14 @@ class CachedArtifacts extends Artifacts {
case TargetPlatform.linux_x64: case TargetPlatform.linux_x64:
case TargetPlatform.darwin_x64: case TargetPlatform.darwin_x64:
case TargetPlatform.windows_x64: case TargetPlatform.windows_x64:
// TODO(jonahwilliams): remove once debug desktop artifacts are uploaded
// under a separate directory from the host artifacts.
// https://github.com/flutter/flutter/issues/38935
if (mode == BuildMode.debug || mode == null) {
return fs.path.join(engineDir, platformName);
}
final String suffix = mode != BuildMode.debug ? '-${snakeCase(getModeName(mode), '-')}' : '';
return fs.path.join(engineDir, platformName + suffix);
case TargetPlatform.fuchsia: case TargetPlatform.fuchsia:
case TargetPlatform.tester: case TargetPlatform.tester:
case TargetPlatform.web_javascript: case TargetPlatform.web_javascript:
......
...@@ -194,7 +194,7 @@ class AOTSnapshotter { ...@@ -194,7 +194,7 @@ class AOTSnapshotter {
final String genSnapshotPath = GenSnapshot.getSnapshotterPath(snapshotType); final String genSnapshotPath = GenSnapshot.getSnapshotterPath(snapshotType);
outputDir.childFile('gen_snapshot.d').writeAsStringSync('gen_snapshot.d: $genSnapshotPath\n'); outputDir.childFile('gen_snapshot.d').writeAsStringSync('gen_snapshot.d: $genSnapshotPath\n');
// On iOS, we use Xcode to compile the snapshot into a dynamic library that the // On iOS and macOS, we use Xcode to compile the snapshot into a dynamic library that the
// end-developer can link into their app. // end-developer can link into their app.
if (platform == TargetPlatform.ios || platform == TargetPlatform.darwin_x64) { if (platform == TargetPlatform.ios || platform == TargetPlatform.darwin_x64) {
final RunResult result = await _buildFramework( final RunResult result = await _buildFramework(
......
...@@ -103,6 +103,7 @@ class KernelSnapshot extends Target { ...@@ -103,6 +103,7 @@ class KernelSnapshot extends Target {
outputFilePath: environment.buildDir.childFile('app.dill').path, outputFilePath: environment.buildDir.childFile('app.dill').path,
depFilePath: null, depFilePath: null,
packagesPath: packagesPath, packagesPath: packagesPath,
linkPlatformKernelIn: buildMode == BuildMode.release,
mainPath: packageUriMapper.map(targetFile)?.toString() ?? targetFile, mainPath: packageUriMapper.map(targetFile)?.toString() ?? targetFile,
); );
if (output.errorCount != 0) { if (output.errorCount != 0) {
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
import '../../artifacts.dart'; import '../../artifacts.dart';
import '../../base/file_system.dart'; import '../../base/file_system.dart';
import '../../build_info.dart';
import '../../globals.dart'; import '../../globals.dart';
import '../build_system.dart'; import '../build_system.dart';
...@@ -17,7 +18,7 @@ class UnpackLinux extends Target { ...@@ -17,7 +18,7 @@ class UnpackLinux extends Target {
@override @override
List<Source> get inputs => const <Source>[ List<Source> get inputs => const <Source>[
Source.pattern('{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/linux.dart'), Source.pattern('{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/linux.dart'),
Source.artifact(Artifact.linuxDesktopPath), Source.artifact(Artifact.linuxDesktopPath, mode: BuildMode.debug),
]; ];
@override @override
......
...@@ -6,6 +6,7 @@ import 'package:pool/pool.dart'; ...@@ -6,6 +6,7 @@ import 'package:pool/pool.dart';
import '../../artifacts.dart'; import '../../artifacts.dart';
import '../../asset.dart'; import '../../asset.dart';
import '../../base/build.dart';
import '../../base/file_system.dart'; import '../../base/file_system.dart';
import '../../base/io.dart'; import '../../base/io.dart';
import '../../base/process.dart'; import '../../base/process.dart';
...@@ -16,6 +17,7 @@ import '../../globals.dart'; ...@@ -16,6 +17,7 @@ import '../../globals.dart';
import '../../macos/xcode.dart'; import '../../macos/xcode.dart';
import '../../project.dart'; import '../../project.dart';
import '../build_system.dart'; import '../build_system.dart';
import '../exceptions.dart';
import 'dart.dart'; import 'dart.dart';
const String _kOutputPrefix = '{PROJECT_DIR}/macos/Flutter/ephemeral/FlutterMacOS.framework'; const String _kOutputPrefix = '{PROJECT_DIR}/macos/Flutter/ephemeral/FlutterMacOS.framework';
...@@ -51,7 +53,7 @@ class MacOSAssetBehavior extends SourceBehavior { ...@@ -51,7 +53,7 @@ class MacOSAssetBehavior extends SourceBehavior {
); );
final FlutterProject flutterProject = FlutterProject.fromDirectory(environment.projectDir); final FlutterProject flutterProject = FlutterProject.fromDirectory(environment.projectDir);
final String prefix = fs.path.join(flutterProject.macos.ephemeralDirectory.path, final String prefix = fs.path.join(flutterProject.macos.ephemeralDirectory.path,
'App.framework', 'Resources', 'flutter_assets'); 'App.framework', 'Versions', 'A', 'Resources', 'flutter_assets');
final List<File> results = <File>[]; final List<File> results = <File>[];
for (String key in assetBundle.entries.keys) { for (String key in assetBundle.entries.keys) {
final File file = fs.file(fs.path.join(prefix, key)); final File file = fs.file(fs.path.join(prefix, key));
...@@ -63,22 +65,28 @@ class MacOSAssetBehavior extends SourceBehavior { ...@@ -63,22 +65,28 @@ class MacOSAssetBehavior extends SourceBehavior {
/// Copy the macOS framework to the correct copy dir by invoking 'cp -R'. /// Copy the macOS framework to the correct copy dir by invoking 'cp -R'.
/// ///
/// The shelling out is done to avoid complications with preserving special /// This class is abstract to share logic between the three conrete
/// files (e.g., symbolic links) in the framework structure. /// implementations. The shelling out is done to avoid complications with
/// preserving special files (e.g., symbolic links) in the framework structure.
/// ///
/// Removes any previous version of the framework that already exists in the /// Removes any previous version of the framework that already exists in the
/// target directory. /// target directory.
///
/// The real implementations are:
/// * [DebugUnpackMacOS]
/// * [ProfileUnpackMacOS]
/// * [ReleaseUnpackMacOS]
///
// TODO(jonahwilliams): remove shell out. // TODO(jonahwilliams): remove shell out.
class UnpackMacOS extends Target { // TODO(jonahwilliams): the subtypes are required to specify the different
// input dependencies as a current limitation of the build system planning.
// This should be resolved after https://github.com/flutter/flutter/issues/38937.
abstract class UnpackMacOS extends Target {
const UnpackMacOS(); const UnpackMacOS();
@override
String get name => 'unpack_macos';
@override @override
List<Source> get inputs => const <Source>[ List<Source> get inputs => const <Source>[
Source.pattern('{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/macos.dart'), Source.pattern('{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/macos.dart'),
Source.artifact(Artifact.flutterMacOSFramework),
]; ];
@override @override
...@@ -108,7 +116,11 @@ class UnpackMacOS extends Target { ...@@ -108,7 +116,11 @@ class UnpackMacOS extends Target {
@override @override
Future<void> build(List<File> inputFiles, Environment environment) async { Future<void> build(List<File> inputFiles, Environment environment) async {
final String basePath = artifacts.getArtifactPath(Artifact.flutterMacOSFramework); if (environment.defines[kBuildMode] == null) {
throw MissingDefineException(kBuildMode, 'unpack_macos');
}
final BuildMode buildMode = getBuildModeForName(environment.defines[kBuildMode]);
final String basePath = artifacts.getArtifactPath(Artifact.flutterMacOSFramework, mode: buildMode);
final FlutterProject flutterProject = FlutterProject.fromDirectory(environment.projectDir); final FlutterProject flutterProject = FlutterProject.fromDirectory(environment.projectDir);
final Directory targetDirectory = flutterProject.macos final Directory targetDirectory = flutterProject.macos
.ephemeralDirectory .ephemeralDirectory
...@@ -128,6 +140,48 @@ class UnpackMacOS extends Target { ...@@ -128,6 +140,48 @@ class UnpackMacOS extends Target {
} }
} }
/// Unpack the release prebuilt engine framework.
class ReleaseUnpackMacOS extends UnpackMacOS {
const ReleaseUnpackMacOS();
@override
String get name => 'release_unpack_macos';
@override
List<Source> get inputs => <Source>[
...super.inputs,
const Source.artifact(Artifact.flutterMacOSFramework, mode: BuildMode.release),
];
}
/// Unpack the profile prebuilt engine framework.
class ProfileUnpackMacOS extends UnpackMacOS {
const ProfileUnpackMacOS();
@override
String get name => 'profile_unpack_macos';
@override
List<Source> get inputs => <Source>[
...super.inputs,
const Source.artifact(Artifact.flutterMacOSFramework, mode: BuildMode.profile),
];
}
/// Unpack the debug prebuilt engine framework.
class DebugUnpackMacOS extends UnpackMacOS {
const DebugUnpackMacOS();
@override
String get name => 'debug_unpack_macos';
@override
List<Source> get inputs => <Source>[
...super.inputs,
const Source.artifact(Artifact.flutterMacOSFramework, mode: BuildMode.debug),
];
}
/// Create an App.framework for debug macOS targets. /// Create an App.framework for debug macOS targets.
/// ///
/// This framework needs to exist for the Xcode project to link/bundle, /// This framework needs to exist for the Xcode project to link/bundle,
...@@ -178,18 +232,85 @@ static const int Moo = 88; ...@@ -178,18 +232,85 @@ static const int Moo = 88;
]; ];
} }
/// Bundle the flutter assets, app.dill, and precompiled runtimes into the App.framework. class CompileMacOSFramework extends Target {
const CompileMacOSFramework();
@override
String get name => 'compile_macos_framework';
@override
Future<void> build(List<File> inputFiles, Environment environment) async {
if (environment.defines[kBuildMode] == null) {
throw MissingDefineException(kBuildMode, 'compile_macos_framework');
}
final BuildMode buildMode = getBuildModeForName(environment.defines[kBuildMode]);
if (buildMode == BuildMode.debug) {
throw Exception('precompiled macOS framework only supported in release/profile builds.');
}
final FlutterProject flutterProject = FlutterProject.fromDirectory(environment.projectDir);
final int result = await AOTSnapshotter(reportTimings: false).build(
bitcode: false,
buildMode: buildMode,
mainPath: environment.buildDir.childFile('app.dill').path,
outputPath: environment.buildDir.path,
platform: TargetPlatform.darwin_x64,
darwinArch: DarwinArch.x86_64,
packagesPath: flutterProject.packagesFile.path
);
if (result != 0) {
throw Exception('gen shapshot failed.');
}
}
@override
List<Target> get dependencies => const <Target>[
KernelSnapshot(),
];
@override
List<Source> get inputs => const <Source>[
Source.pattern('{BUILD_DIR}/app.dill'),
Source.pattern('{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/macos.dart'),
Source.artifact(Artifact.genSnapshot, mode: BuildMode.release, platform: TargetPlatform.darwin_x64),
];
@override
List<Source> get outputs => const <Source>[
Source.pattern('{BUILD_DIR}/App.framework/App'),
];
}
/// Bundle the flutter assets into the App.framework.
///
/// In debug mode, also include the app.dill and precompiled runtimes.
/// ///
/// See https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPFrameworks/Concepts/FrameworkAnatomy.html /// See https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPFrameworks/Concepts/FrameworkAnatomy.html
/// for more information on Framework structure. /// for more information on Framework structure.
class DebugBundleFlutterAssets extends Target { abstract class MacOSBundleFlutterAssets extends Target {
const DebugBundleFlutterAssets(); const MacOSBundleFlutterAssets();
@override @override
String get name => 'debug_bundle_flutter_assets'; List<Source> get inputs => const <Source>[
Source.pattern('{PROJECT_DIR}/pubspec.yaml'),
Source.behavior(MacOSAssetBehavior())
];
@override
List<Source> get outputs => const <Source>[
Source.behavior(MacOSAssetBehavior()),
Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/Versions/A/App'),
Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/Versions/A/Resources/Info.plist'),
Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/Versions/A/Resources/flutter_assets/AssetManifest.json'),
Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/Versions/A/Resources/flutter_assets/FontManifest.json'),
Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/Versions/A/Resources/flutter_assets/LICENSE'),
];
@override @override
Future<void> build(List<File> inputFiles, Environment environment) async { Future<void> build(List<File> inputFiles, Environment environment) async {
if (environment.defines[kBuildMode] == null) {
throw MissingDefineException(kBuildMode, 'compile_macos_framework');
}
final BuildMode buildMode = getBuildModeForName(environment.defines[kBuildMode]);
final FlutterProject flutterProject = FlutterProject.fromDirectory(environment.projectDir); final FlutterProject flutterProject = FlutterProject.fromDirectory(environment.projectDir);
final Directory frameworkRootDirectory = flutterProject.macos final Directory frameworkRootDirectory = flutterProject.macos
.ephemeralDirectory .ephemeralDirectory
...@@ -268,78 +389,121 @@ class DebugBundleFlutterAssets extends Target { ...@@ -268,78 +389,121 @@ class DebugBundleFlutterAssets extends Target {
</plist> </plist>
'''); ''');
if (buildMode == BuildMode.debug) {
// Copy dill file. // Copy dill file.
try { try {
final File sourceFile = environment.buildDir.childFile('app.dill'); final File sourceFile = environment.buildDir.childFile('app.dill');
sourceFile.copySync(assetDirectory.childFile('kernel_blob.bin').path); sourceFile.copySync(assetDirectory.childFile('kernel_blob.bin').path);
} catch (err) { } catch (err) {
throw Exception('Failed to copy app.dill: $err'); throw Exception('Failed to copy app.dill: $err');
} }
// Copy precompiled runtimes. // Copy precompiled runtimes.
try { try {
final String vmSnapshotData = artifacts.getArtifactPath(Artifact.vmSnapshotData, final String vmSnapshotData = artifacts.getArtifactPath(Artifact.vmSnapshotData,
platform: TargetPlatform.darwin_x64, mode: BuildMode.debug); platform: TargetPlatform.darwin_x64, mode: BuildMode.debug);
final String isolateSnapshotData = artifacts.getArtifactPath(Artifact.isolateSnapshotData, final String isolateSnapshotData = artifacts.getArtifactPath(Artifact.isolateSnapshotData,
platform: TargetPlatform.darwin_x64, mode: BuildMode.debug); platform: TargetPlatform.darwin_x64, mode: BuildMode.debug);
fs.file(vmSnapshotData).copySync( fs.file(vmSnapshotData).copySync(
assetDirectory.childFile('vm_snapshot_data').path); assetDirectory.childFile('vm_snapshot_data').path);
fs.file(isolateSnapshotData).copySync( fs.file(isolateSnapshotData).copySync(
assetDirectory.childFile('isolate_snapshot_data').path); assetDirectory.childFile('isolate_snapshot_data').path);
} catch (err) { } catch (err) {
throw Exception('Failed to copy precompiled runtimes: $err'); throw Exception('Failed to copy precompiled runtimes: $err');
}
} }
// Create symlink to current version. // Create symlink to current version. These must be relative, from the
// framework root for Resources/App and from the versions root for
// Current.
try { try {
final Link currentVersion = outputDirectory.parent final Link currentVersion = outputDirectory.parent
.childLink('Current'); .childLink('Current');
if (!currentVersion.existsSync()) { if (!currentVersion.existsSync()) {
currentVersion.createSync(outputDirectory.path); final String linkPath = fs.path.relative(outputDirectory.path,
from: outputDirectory.parent.path);
print(linkPath);
currentVersion.createSync('$linkPath${fs.path.separator}');
} }
// Create symlink to current resources. // Create symlink to current resources.
final Link currentResources = frameworkRootDirectory final Link currentResources = frameworkRootDirectory
.childLink('Resources'); .childLink('Resources');
if (!currentResources.existsSync()) { if (!currentResources.existsSync()) {
currentResources.createSync(fs.path.join(currentVersion.path, 'Resources')); final String linkPath = fs.path.relative(fs.path.join(currentVersion.path, 'Resources'),
from: frameworkRootDirectory.path);
print(linkPath);
currentResources.createSync(linkPath);
} }
// Create symlink to current binary. // Create symlink to current binary.
final Link currentFramework = frameworkRootDirectory final Link currentFramework = frameworkRootDirectory
.childLink('App'); .childLink('App');
if (!currentFramework.existsSync()) { if (!currentFramework.existsSync()) {
currentFramework.createSync(fs.path.join(currentVersion.path, 'App')); final String linkPath = fs.path.relative(fs.path.join(currentVersion.path, 'App'),
from: frameworkRootDirectory.path);
print(linkPath);
currentFramework.createSync(linkPath);
} }
} on FileSystemException { } on FileSystemException {
throw Exception('Failed to create symlinks for framework. try removing ' throw Exception('Failed to create symlinks for framework. try removing '
'the "${flutterProject.macos.ephemeralDirectory.path}" directory and rerunning'); 'the "${flutterProject.macos.ephemeralDirectory.path}" directory and rerunning');
} }
} }
}
/// Bundle the debug flutter assets into the App.framework.
class DebugMacOSBundleFlutterAssets extends MacOSBundleFlutterAssets {
const DebugMacOSBundleFlutterAssets();
@override
String get name => 'debug_macos_bundle_flutter_assets';
@override @override
List<Target> get dependencies => const <Target>[ List<Target> get dependencies => const <Target>[
KernelSnapshot(), KernelSnapshot(),
DebugMacOSFramework(), DebugMacOSFramework(),
UnpackMacOS(), DebugUnpackMacOS(),
]; ];
@override @override
List<Source> get inputs => const <Source>[ List<Source> get inputs => <Source>[
Source.pattern('{PROJECT_DIR}/pubspec.yaml'), ...super.inputs,
Source.behavior(MacOSAssetBehavior()), const Source.pattern('{BUILD_DIR}/app.dill'),
Source.pattern('{BUILD_DIR}/app.dill'), const Source.artifact(Artifact.isolateSnapshotData, platform: TargetPlatform.darwin_x64, mode: BuildMode.debug),
Source.artifact(Artifact.isolateSnapshotData, platform: TargetPlatform.darwin_x64, mode: BuildMode.debug), const Source.artifact(Artifact.vmSnapshotData, platform: TargetPlatform.darwin_x64, mode: BuildMode.debug),
Source.artifact(Artifact.vmSnapshotData, platform: TargetPlatform.darwin_x64, mode: BuildMode.debug),
]; ];
@override @override
List<Source> get outputs => const <Source>[ List<Source> get outputs => <Source>[
Source.behavior(MacOSAssetBehavior()), ...super.outputs,
Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/Versions/A/App'), const Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/Versions/A/Resources/flutter_assets/kernel_blob.bin'),
Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/Versions/A/Resources/Info.plist'), const Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/Versions/A/Resources/flutter_assets/vm_snapshot_data'),
Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/Versions/A/Resources/flutter_assets/AssetManifest.json'), const Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/Versions/A/Resources/flutter_assets/isolate_snapshot_data'),
Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/Versions/A/Resources/flutter_assets/FontManifest.json'), ];
Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/Versions/A/Resources/flutter_assets/LICENSE'), }
Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/Versions/A/Resources/flutter_assets/kernel_blob.bin'),
Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/Versions/A/Resources/flutter_assets/vm_snapshot_data'), /// Bundle the profile flutter assets into the App.framework.
Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/Versions/A/Resources/flutter_assets/isolate_snapshot_data'), class ProfileMacOSBundleFlutterAssets extends MacOSBundleFlutterAssets {
const ProfileMacOSBundleFlutterAssets();
@override
String get name => 'profile_macos_bundle_flutter_assets';
@override
List<Target> get dependencies => const <Target>[
CompileMacOSFramework(),
ProfileUnpackMacOS(),
];
}
/// Bundle the release flutter assets into the App.framework.
class ReleaseMacOSBundleFlutterAssets extends MacOSBundleFlutterAssets {
const ReleaseMacOSBundleFlutterAssets();
@override
String get name => 'release_macos_bundle_flutter_assets';
@override
List<Target> get dependencies => const <Target>[
CompileMacOSFramework(),
ReleaseUnpackMacOS(),
]; ];
} }
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
import '../../artifacts.dart'; import '../../artifacts.dart';
import '../../base/file_system.dart'; import '../../base/file_system.dart';
import '../../build_info.dart';
import '../../globals.dart'; import '../../globals.dart';
import '../build_system.dart'; import '../build_system.dart';
...@@ -17,7 +18,7 @@ class UnpackWindows extends Target { ...@@ -17,7 +18,7 @@ class UnpackWindows extends Target {
@override @override
List<Source> get inputs => const <Source>[ List<Source> get inputs => const <Source>[
Source.pattern('{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/windows.dart'), Source.pattern('{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/windows.dart'),
Source.artifact(Artifact.windowsDesktopPath), Source.artifact(Artifact.windowsDesktopPath, mode: BuildMode.debug),
]; ];
@override @override
......
...@@ -1061,8 +1061,15 @@ const List<List<String>> _linuxDesktopBinaryDirs = <List<String>>[ ...@@ -1061,8 +1061,15 @@ const List<List<String>> _linuxDesktopBinaryDirs = <List<String>>[
<String>['linux-x64', 'linux-x64/flutter-cpp-client-wrapper-glfw.zip'], <String>['linux-x64', 'linux-x64/flutter-cpp-client-wrapper-glfw.zip'],
]; ];
// TODO(jonahwilliams): upload debug desktop artifacts to host-debug and
// remove from existing host folder.
// https://github.com/flutter/flutter/issues/38935
const List<List<String>> _macOSDesktopBinaryDirs = <List<String>>[ const List<List<String>> _macOSDesktopBinaryDirs = <List<String>>[
<String>['darwin-x64', 'darwin-x64/FlutterMacOS.framework.zip'], <String>['darwin-x64', 'darwin-x64/FlutterMacOS.framework.zip'],
<String>['darwin-x64-profile', 'darwin-x64-profile/FlutterMacOS.framework.zip'],
<String>['darwin-x64-profile', 'darwin-x64-profile/artifacts.zip'],
<String>['darwin-x64-release', 'darwin-x64-release/FlutterMacOS.framework.zip'],
<String>['darwin-x64-release', 'darwin-x64-release/artifacts.zip'],
]; ];
const List<List<String>> _osxBinaryDirs = <List<String>>[ const List<List<String>> _osxBinaryDirs = <List<String>>[
......
...@@ -23,7 +23,6 @@ BuildSystem get buildSystem => context.get<BuildSystem>(); ...@@ -23,7 +23,6 @@ BuildSystem get buildSystem => context.get<BuildSystem>();
/// All currently implemented targets. /// All currently implemented targets.
const List<Target> _kDefaultTargets = <Target>[ const List<Target> _kDefaultTargets = <Target>[
UnpackMacOS(),
UnpackLinux(), UnpackLinux(),
UnpackWindows(), UnpackWindows(),
CopyAssets(), CopyAssets(),
...@@ -33,7 +32,9 @@ const List<Target> _kDefaultTargets = <Target>[ ...@@ -33,7 +32,9 @@ const List<Target> _kDefaultTargets = <Target>[
AotAssemblyProfile(), AotAssemblyProfile(),
AotAssemblyRelease(), AotAssemblyRelease(),
DebugMacOSFramework(), DebugMacOSFramework(),
DebugBundleFlutterAssets(), DebugMacOSBundleFlutterAssets(),
ProfileMacOSBundleFlutterAssets(),
ReleaseMacOSBundleFlutterAssets(),
]; ];
/// Assemble provides a low level API to interact with the flutter tool build /// Assemble provides a low level API to interact with the flutter tool build
......
...@@ -6,6 +6,7 @@ import 'package:flutter_tools/src/base/build.dart'; ...@@ -6,6 +6,7 @@ import 'package:flutter_tools/src/base/build.dart';
import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/io.dart'; import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/base/process.dart';
import 'package:flutter_tools/src/base/process_manager.dart'; import 'package:flutter_tools/src/base/process_manager.dart';
import 'package:flutter_tools/src/build_system/build_system.dart'; import 'package:flutter_tools/src/build_system/build_system.dart';
import 'package:flutter_tools/src/build_system/targets/dart.dart'; import 'package:flutter_tools/src/build_system/targets/dart.dart';
...@@ -61,6 +62,11 @@ void main() { ...@@ -61,6 +62,11 @@ void main() {
when(mockPlatform.isLinux).thenReturn(false); when(mockPlatform.isLinux).thenReturn(false);
when(mockPlatform.environment).thenReturn(const <String, String>{}); when(mockPlatform.environment).thenReturn(const <String, String>{});
testbed = Testbed(setup: () { testbed = Testbed(setup: () {
fs.file(fs.path.join('bin', 'cache', 'pkg', 'sky_engine', 'lib', 'ui',
'ui.dart')).createSync(recursive: true);
fs.file(fs.path.join('bin', 'cache', 'pkg', 'sky_engine', 'sdk_ext',
'vmservice_io.dart')).createSync(recursive: true);
environment = Environment( environment = Environment(
projectDir: fs.currentDirectory, projectDir: fs.currentDirectory,
defines: <String, String>{ defines: <String, String>{
...@@ -95,7 +101,7 @@ void main() { ...@@ -95,7 +101,7 @@ void main() {
} }
return FakeProcessResult()..exitCode = 0; return FakeProcessResult()..exitCode = 0;
}); });
await const UnpackMacOS().build(<File>[], environment); await const DebugUnpackMacOS().build(<File>[], environment);
expect(fs.directory('$_kOutputPrefix').existsSync(), true); expect(fs.directory('$_kOutputPrefix').existsSync(), true);
for (File file in inputs) { for (File file in inputs) {
...@@ -109,11 +115,11 @@ void main() { ...@@ -109,11 +115,11 @@ void main() {
..createSync(recursive: true) ..createSync(recursive: true)
..writeAsStringSync('testing'); ..writeAsStringSync('testing');
expect(() async => await const DebugBundleFlutterAssets().build(<File>[], environment), expect(() async => await const DebugMacOSBundleFlutterAssets().build(<File>[], environment),
throwsA(isInstanceOf<Exception>())); throwsA(isInstanceOf<Exception>()));
})); }));
test('debug macOS application copies kernel blob', () => testbed.run(() async { test('debug macOS application creates correctly structured framework', () => testbed.run(() async {
fs.file(fs.path.join('bin', 'cache', 'artifacts', 'engine', 'darwin-x64', fs.file(fs.path.join('bin', 'cache', 'artifacts', 'engine', 'darwin-x64',
'vm_isolate_snapshot.bin')).createSync(recursive: true); 'vm_isolate_snapshot.bin')).createSync(recursive: true);
fs.file(fs.path.join('bin', 'cache', 'artifacts', 'engine', 'darwin-x64', fs.file(fs.path.join('bin', 'cache', 'artifacts', 'engine', 'darwin-x64',
...@@ -124,18 +130,74 @@ void main() { ...@@ -124,18 +130,74 @@ void main() {
'macos', 'Flutter', 'ephemeral', 'App.framework'); 'macos', 'Flutter', 'ephemeral', 'App.framework');
final String inputKernel = fs.path.join(environment.buildDir.path, 'app.dill'); final String inputKernel = fs.path.join(environment.buildDir.path, 'app.dill');
fs.directory(frameworkPath).createSync(recursive: true); fs.directory(frameworkPath).createSync(recursive: true);
final String outputKernel = fs.path.join(frameworkPath, 'Resources', final String outputKernel = fs.path.join(frameworkPath, 'Versions', 'A', 'Resources',
'flutter_assets', 'kernel_blob.bin'); 'flutter_assets', 'kernel_blob.bin');
final String outputPlist = fs.path.join(frameworkPath, 'Resources', 'Info.plist'); final String outputPlist = fs.path.join(frameworkPath, 'Versions', 'A', 'Resources',
'Info.plist');
fs.file(inputKernel) fs.file(inputKernel)
..createSync(recursive: true) ..createSync(recursive: true)
..writeAsStringSync('testing'); ..writeAsStringSync('testing');
await const DebugBundleFlutterAssets().build(<File>[], environment); await const DebugMacOSBundleFlutterAssets().build(<File>[], environment);
expect(fs.file(outputKernel).readAsStringSync(), 'testing'); expect(fs.file(outputKernel).readAsStringSync(), 'testing');
expect(fs.file(outputPlist).readAsStringSync(), contains('io.flutter.flutter.app')); expect(fs.file(outputPlist).readAsStringSync(), contains('io.flutter.flutter.app'));
})); }));
test('release/profile macOS application has no blob or precompiled runtime', () => testbed.run(() async {
fs.file(fs.path.join('bin', 'cache', 'artifacts', 'engine', 'darwin-x64',
'vm_isolate_snapshot.bin')).createSync(recursive: true);
fs.file(fs.path.join('bin', 'cache', 'artifacts', 'engine', 'darwin-x64',
'isolate_snapshot.bin')).createSync(recursive: true);
fs.file(fs.path.join(environment.buildDir.path, 'App.framework', 'App'))
..createSync(recursive: true);
final String frameworkPath = fs.path.join(environment.projectDir.path,
'macos', 'Flutter', 'ephemeral', 'App.framework');
fs.directory(frameworkPath).createSync(recursive: true);
final String outputKernel = fs.path.join(frameworkPath, 'Resources',
'flutter_assets', 'kernel_blob.bin');
final String precompiledVm = fs.path.join(frameworkPath, 'Resources',
'flutter_assets', 'vm_snapshot_data');
final String precompiledIsolate = fs.path.join(frameworkPath, 'Resources',
'flutter_assets', 'isolate_snapshot_data');
await const ProfileMacOSBundleFlutterAssets().build(<File>[], environment..defines[kBuildMode] = 'profile');
expect(fs.file(outputKernel).existsSync(), false);
expect(fs.file(precompiledVm).existsSync(), false);
expect(fs.file(precompiledIsolate).existsSync(), false);
}));
test('release/profile macOS compilation uses correct gen_snapshot', () => testbed.run(() async {
when(genSnapshot.run(
snapshotType: anyNamed('snapshotType'),
additionalArgs: anyNamed('additionalArgs'),
darwinArch: anyNamed('darwinArch'),
)).thenAnswer((Invocation invocation) {
environment.buildDir.childFile('snapshot_assembly.o').createSync();
environment.buildDir.childFile('snapshot_assembly.S').createSync();
return Future<int>.value(0);
});
when(xcode.cc(any)).thenAnswer((Invocation invocation) {
return Future<RunResult>.value(RunResult(FakeProcessResult()..exitCode = 0, <String>['test']));
});
when(xcode.clang(any)).thenAnswer((Invocation invocation) {
return Future<RunResult>.value(RunResult(FakeProcessResult()..exitCode = 0, <String>['test']));
});
when(xcode.dsymutil(any)).thenAnswer((Invocation invocation) {
return Future<RunResult>.value(RunResult(FakeProcessResult()..exitCode = 0, <String>['test']));
});
environment.buildDir.childFile('app.dill').createSync(recursive: true);
fs.file('.packages')
..createSync()
..writeAsStringSync('''
# Generated
sky_engine:file:///bin/cache/pkg/sky_engine/lib/
flutter_tools:lib/''');
await const CompileMacOSFramework().build(<File>[], environment..defines[kBuildMode] = 'release');
}, overrides: <Type, Generator>{
GenSnapshot: () => MockGenSnapshot(),
Xcode: () => MockXCode(),
}));
} }
class MockPlatform extends Mock implements Platform {} class MockPlatform extends Mock implements Platform {}
......
...@@ -36,7 +36,7 @@ void main() { ...@@ -36,7 +36,7 @@ void main() {
return BuildResult(success: true); return BuildResult(success: true);
}); });
final CommandRunner<void> commandRunner = createTestCommandRunner(AssembleCommand()); final CommandRunner<void> commandRunner = createTestCommandRunner(AssembleCommand());
await commandRunner.run(<String>['assemble', 'unpack_macos']); await commandRunner.run(<String>['assemble', 'debug_macos_bundle_flutter_assets']);
final BufferLogger bufferLogger = logger; final BufferLogger bufferLogger = logger;
expect(bufferLogger.statusText.trim(), 'build succeeded.'); expect(bufferLogger.statusText.trim(), 'build succeeded.');
...@@ -62,7 +62,7 @@ void main() { ...@@ -62,7 +62,7 @@ void main() {
}); });
final CommandRunner<void> commandRunner = createTestCommandRunner(AssembleCommand()); final CommandRunner<void> commandRunner = createTestCommandRunner(AssembleCommand());
await commandRunner.run(<String>['assemble', '--build-outputs=outputs', '--build-inputs=inputs', 'unpack_macos']); await commandRunner.run(<String>['assemble', '--build-outputs=outputs', '--build-inputs=inputs', 'debug_macos_bundle_flutter_assets']);
final File inputs = fs.file('inputs'); final File inputs = fs.file('inputs');
final File outputs = fs.file('outputs'); final File outputs = fs.file('outputs');
...@@ -72,7 +72,7 @@ void main() { ...@@ -72,7 +72,7 @@ void main() {
final DateTime theDistantPast = DateTime(1991, 8, 23); final DateTime theDistantPast = DateTime(1991, 8, 23);
inputs.setLastModifiedSync(theDistantPast); inputs.setLastModifiedSync(theDistantPast);
outputs.setLastModifiedSync(theDistantPast); outputs.setLastModifiedSync(theDistantPast);
await commandRunner.run(<String>['assemble', '--build-outputs=outputs', '--build-inputs=inputs', 'unpack_macos']); await commandRunner.run(<String>['assemble', '--build-outputs=outputs', '--build-inputs=inputs', 'debug_macos_bundle_flutter_assets']);
expect(inputs.lastModifiedSync(), theDistantPast); expect(inputs.lastModifiedSync(), theDistantPast);
expect(outputs.lastModifiedSync(), theDistantPast); expect(outputs.lastModifiedSync(), theDistantPast);
...@@ -85,7 +85,7 @@ void main() { ...@@ -85,7 +85,7 @@ void main() {
inputFiles: <File>[fs.file('foo'), fs.file('fizz')..createSync()], inputFiles: <File>[fs.file('foo'), fs.file('fizz')..createSync()],
outputFiles: <File>[fs.file('bar'), fs.file(fs.path.join('.dart_tool', 'fizz2'))..createSync(recursive: true)]); outputFiles: <File>[fs.file('bar'), fs.file(fs.path.join('.dart_tool', 'fizz2'))..createSync(recursive: true)]);
}); });
await commandRunner.run(<String>['assemble', '--build-outputs=outputs', '--build-inputs=inputs', 'unpack_macos']); await commandRunner.run(<String>['assemble', '--build-outputs=outputs', '--build-inputs=inputs', 'debug_macos_bundle_flutter_assets']);
expect(inputs.readAsStringSync(), contains('foo')); expect(inputs.readAsStringSync(), contains('foo'));
expect(inputs.readAsStringSync(), contains('fizz')); expect(inputs.readAsStringSync(), contains('fizz'));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment