Unverified Commit 2504a416 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

Create correctly structured framework for macOS (#38748)

parent 700020fc
...@@ -51,7 +51,7 @@ class MacOSAssetBehavior extends SourceBehavior { ...@@ -51,7 +51,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', 'flutter_assets'); 'App.framework', '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));
...@@ -141,9 +141,8 @@ class DebugMacOSFramework extends Target { ...@@ -141,9 +141,8 @@ class DebugMacOSFramework extends Target {
@override @override
Future<void> build(List<File> inputFiles, Environment environment) async { Future<void> build(List<File> inputFiles, Environment environment) async {
final FlutterProject flutterProject = FlutterProject.fromDirectory(environment.projectDir);
final File outputFile = fs.file(fs.path.join( final File outputFile = fs.file(fs.path.join(
flutterProject.macos.ephemeralDirectory.path, 'App.framework', 'App')); environment.buildDir.path, 'App.framework', 'App'));
outputFile.createSync(recursive: true); outputFile.createSync(recursive: true);
final File debugApp = environment.buildDir.childFile('debug_app.cc') final File debugApp = environment.buildDir.childFile('debug_app.cc')
..writeAsStringSync(r''' ..writeAsStringSync(r'''
...@@ -158,7 +157,7 @@ static const int Moo = 88; ...@@ -158,7 +157,7 @@ static const int Moo = 88;
'-Xlinker', '-rpath', '-Xlinker', '@executable_path/Frameworks', '-Xlinker', '-rpath', '-Xlinker', '@executable_path/Frameworks',
'-Xlinker', '-rpath', '-Xlinker', '@loader_path/Frameworks', '-Xlinker', '-rpath', '-Xlinker', '@loader_path/Frameworks',
'-install_name', '@rpath/App.framework/App', '-install_name', '@rpath/App.framework/App',
'-o', 'macos/Flutter/ephemeral/App.framework/App', '-o', outputFile.path,
]); ]);
if (result.exitCode != 0) { if (result.exitCode != 0) {
throw Exception('Failed to compile debug App.framework'); throw Exception('Failed to compile debug App.framework');
...@@ -175,11 +174,14 @@ static const int Moo = 88; ...@@ -175,11 +174,14 @@ static const int Moo = 88;
@override @override
List<Source> get outputs => const <Source>[ List<Source> get outputs => const <Source>[
Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/App'), Source.pattern('{BUILD_DIR}/App.framework/App'),
]; ];
} }
/// Bundle the flutter assets, app.dill, and precompiled runtimes into the App.framework. /// Bundle the flutter assets, app.dill, and precompiled runtimes into the App.framework.
///
/// See https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPFrameworks/Concepts/FrameworkAnatomy.html
/// for more information on Framework structure.
class DebugBundleFlutterAssets extends Target { class DebugBundleFlutterAssets extends Target {
const DebugBundleFlutterAssets(); const DebugBundleFlutterAssets();
...@@ -189,19 +191,30 @@ class DebugBundleFlutterAssets extends Target { ...@@ -189,19 +191,30 @@ class DebugBundleFlutterAssets extends Target {
@override @override
Future<void> build(List<File> inputFiles, Environment environment) async { Future<void> build(List<File> inputFiles, Environment environment) async {
final FlutterProject flutterProject = FlutterProject.fromDirectory(environment.projectDir); final FlutterProject flutterProject = FlutterProject.fromDirectory(environment.projectDir);
final Directory outputDirectory = flutterProject.macos final Directory frameworkRootDirectory = flutterProject.macos
.ephemeralDirectory.childDirectory('App.framework'); .ephemeralDirectory
if (!outputDirectory.existsSync()) { .childDirectory('App.framework');
throw Exception('App.framework must exist to bundle assets.'); final Directory outputDirectory = frameworkRootDirectory
} .childDirectory('Versions')
.childDirectory('A')
..createSync(recursive: true);
// Copy App into framework directory.
environment.buildDir
.childDirectory('App.framework')
.childFile('App')
.copySync(outputDirectory.childFile('App').path);
// Copy assets into asset directory. // Copy assets into asset directory.
final Directory assetDirectory = outputDirectory.childDirectory('flutter_assets'); final Directory assetDirectory = outputDirectory
.childDirectory('Resources')
.childDirectory('flutter_assets');
// We're not smart enough to only remove assets that are removed. If // We're not smart enough to only remove assets that are removed. If
// anything changes blow away the whole directory. // anything changes blow away the whole directory.
if (assetDirectory.existsSync()) { if (assetDirectory.existsSync()) {
assetDirectory.deleteSync(recursive: true); assetDirectory.deleteSync(recursive: true);
} }
assetDirectory.createSync(); assetDirectory.createSync(recursive: true);
final AssetBundle assetBundle = AssetBundleFactory.instance.createBundle(); final AssetBundle assetBundle = AssetBundleFactory.instance.createBundle();
final int result = await assetBundle.build( final int result = await assetBundle.build(
manifestPath: environment.projectDir.childFile('pubspec.yaml').path, manifestPath: environment.projectDir.childFile('pubspec.yaml').path,
...@@ -224,9 +237,38 @@ class DebugBundleFlutterAssets extends Target { ...@@ -224,9 +237,38 @@ class DebugBundleFlutterAssets extends Target {
resource.release(); resource.release();
} }
})); }));
} catch (err, st){ } catch (err, st) {
throw Exception('Failed to copy assets: $st'); throw Exception('Failed to copy assets: $st');
} }
// Copy Info.plist template.
assetDirectory.parent.childFile('Info.plist')
..createSync()
..writeAsStringSync(r'''
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>App</string>
<key>CFBundleIdentifier</key>
<string>io.flutter.flutter.app</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>App</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1.0</string>
</dict>
</plist>
''');
// Copy dill file. // Copy dill file.
try { try {
final File sourceFile = environment.buildDir.childFile('app.dill'); final File sourceFile = environment.buildDir.childFile('app.dill');
...@@ -247,6 +289,29 @@ class DebugBundleFlutterAssets extends Target { ...@@ -247,6 +289,29 @@ class DebugBundleFlutterAssets extends Target {
} 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.
try {
final Link currentVersion = outputDirectory.parent
.childLink('Current');
if (!currentVersion.existsSync()) {
currentVersion.createSync(outputDirectory.path);
}
// Create symlink to current resources.
final Link currentResources = frameworkRootDirectory
.childLink('Resources');
if (!currentResources.existsSync()) {
currentResources.createSync(fs.path.join(currentVersion.path, 'Resources'));
}
// Create symlink to current binary.
final Link currentFramework = frameworkRootDirectory
.childLink('App');
if (!currentFramework.existsSync()) {
currentFramework.createSync(fs.path.join(currentVersion.path, 'App'));
}
} on FileSystemException {
throw Exception('Failed to create symlinks for framework. try removing '
'the "${flutterProject.macos.ephemeralDirectory.path}" directory and rerunning');
}
} }
@override @override
...@@ -268,11 +333,13 @@ class DebugBundleFlutterAssets extends Target { ...@@ -268,11 +333,13 @@ class DebugBundleFlutterAssets extends Target {
@override @override
List<Source> get outputs => const <Source>[ List<Source> get outputs => const <Source>[
Source.behavior(MacOSAssetBehavior()), Source.behavior(MacOSAssetBehavior()),
Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/flutter_assets/AssetManifest.json'), Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/Versions/A/App'),
Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/flutter_assets/FontManifest.json'), Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/Versions/A/Resources/Info.plist'),
Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/flutter_assets/LICENSE'), 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/flutter_assets/kernel_blob.bin'), 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/flutter_assets/vm_snapshot_data'), Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/Versions/A/Resources/flutter_assets/LICENSE'),
Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/flutter_assets/isolate_snapshot_data'), 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'),
Source.pattern('{PROJECT_DIR}/macos/Flutter/ephemeral/App.framework/Versions/A/Resources/flutter_assets/isolate_snapshot_data'),
]; ];
} }
...@@ -118,11 +118,15 @@ void main() { ...@@ -118,11 +118,15 @@ void main() {
'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',
'isolate_snapshot.bin')).createSync(recursive: true); '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, final String frameworkPath = fs.path.join(environment.projectDir.path,
'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, 'flutter_assets', 'kernel_blob.bin'); final String outputKernel = fs.path.join(frameworkPath, 'Resources',
'flutter_assets', 'kernel_blob.bin');
final String outputPlist = fs.path.join(frameworkPath, 'Resources', 'Info.plist');
fs.file(inputKernel) fs.file(inputKernel)
..createSync(recursive: true) ..createSync(recursive: true)
..writeAsStringSync('testing'); ..writeAsStringSync('testing');
...@@ -130,6 +134,7 @@ void main() { ...@@ -130,6 +134,7 @@ void main() {
await const DebugBundleFlutterAssets().build(<File>[], environment); await const DebugBundleFlutterAssets().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'));
})); }));
} }
......
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