Unverified Commit 7bf74c34 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[flutter_tools] handle unsafe build outputs (#53601)

parent e71cf1cd
......@@ -276,6 +276,13 @@ abstract class IosAssetBundle extends Target {
final Directory frameworkDirectory = environment.outputDir.childDirectory('App.framework');
final Directory assetDirectory = frameworkDirectory.childDirectory('flutter_assets');
frameworkDirectory.createSync(recursive: true);
// This is necessary because multiple different build configurations will
// output different files here. Build cleaning only works when the files
// change within a build configuration.
if (assetDirectory.existsSync()) {
assetDirectory.deleteSync(recursive: true);
}
assetDirectory.createSync();
// Only copy the prebuilt runtimes and kernel blob in debug mode.
......
......@@ -16,8 +16,6 @@ import 'assets.dart';
import 'dart.dart';
import 'icon_tree_shaker.dart';
const String _kOutputPrefix = '{OUTPUT_DIR}/FlutterMacOS.framework';
/// Copy the macOS framework to the correct copy dir by invoking 'cp -R'.
///
/// This class is abstract to share logic between the three concrete
......@@ -31,11 +29,6 @@ const String _kOutputPrefix = '{OUTPUT_DIR}/FlutterMacOS.framework';
/// * [DebugUnpackMacOS]
/// * [ProfileUnpackMacOS]
/// * [ReleaseUnpackMacOS]
///
// TODO(jonahwilliams): remove shell out.
// 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();
......@@ -45,30 +38,14 @@ abstract class UnpackMacOS extends Target {
];
@override
List<Source> get outputs => const <Source>[
Source.pattern('$_kOutputPrefix/FlutterMacOS'),
// Headers
Source.pattern('$_kOutputPrefix/Headers/FlutterDartProject.h'),
Source.pattern('$_kOutputPrefix/Headers/FlutterEngine.h'),
Source.pattern('$_kOutputPrefix/Headers/FlutterViewController.h'),
Source.pattern('$_kOutputPrefix/Headers/FlutterBinaryMessenger.h'),
Source.pattern('$_kOutputPrefix/Headers/FlutterChannels.h'),
Source.pattern('$_kOutputPrefix/Headers/FlutterCodecs.h'),
Source.pattern('$_kOutputPrefix/Headers/FlutterMacros.h'),
Source.pattern('$_kOutputPrefix/Headers/FlutterPluginMacOS.h'),
Source.pattern('$_kOutputPrefix/Headers/FlutterPluginRegistrarMacOS.h'),
Source.pattern('$_kOutputPrefix/Headers/FlutterMacOS.h'),
// Modules
Source.pattern('$_kOutputPrefix/Modules/module.modulemap'),
// Resources
Source.pattern('$_kOutputPrefix/Resources/icudtl.dat'),
Source.pattern('$_kOutputPrefix/Resources/Info.plist'),
// Ignore Versions folder for now
];
List<Source> get outputs => const <Source>[];
@override
List<Target> get dependencies => <Target>[];
@override
List<String> get depfiles => const <String>['unpack_macos.d'];
@override
Future<void> build(Environment environment) async {
if (environment.defines[kBuildMode] == null) {
......@@ -79,9 +56,20 @@ abstract class UnpackMacOS extends Target {
final Directory targetDirectory = environment
.outputDir
.childDirectory('FlutterMacOS.framework');
// This is necessary because multiple different build configurations will
// output different files here. Build cleaning only works when the files
// change within a build configuration.
if (targetDirectory.existsSync()) {
targetDirectory.deleteSync(recursive: true);
}
final List<File> inputs = globals.fs.directory(basePath)
.listSync(recursive: true)
.whereType<File>()
.toList();
final List<File> outputs = inputs.map((File file) {
final String relativePath = globals.fs.path.relative(file.path, from: basePath);
return globals.fs.file(globals.fs.path.join(targetDirectory.path, relativePath));
}).toList();
final ProcessResult result = await globals.processManager
.run(<String>['cp', '-R', basePath, targetDirectory.path]);
if (result.exitCode != 0) {
......@@ -90,6 +78,15 @@ abstract class UnpackMacOS extends Target {
'${result.stdout}\n---\n${result.stderr}',
);
}
final DepfileService depfileService = DepfileService(
logger: globals.logger,
fileSystem: globals.fs,
platform: globals.platform,
);
depfileService.writeToFile(
Depfile(inputs, outputs),
environment.buildDir.childFile('unpack_macos.d'),
);
}
}
......@@ -294,6 +291,12 @@ abstract class MacOSBundleFlutterAssets extends Target {
final Directory assetDirectory = outputDirectory
.childDirectory('Resources')
.childDirectory('flutter_assets');
// This is necessary because multiple different build configurations will
// output different files here. Build cleaning only works when the files
// change within a build configuration.
if (assetDirectory.existsSync()) {
assetDirectory.deleteSync(recursive: true);
}
assetDirectory.createSync(recursive: true);
final Depfile depfile = await copyAssets(environment, assetDirectory);
final DepfileService depfileService = DepfileService(
......
......@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:file_testing/file_testing.dart';
import 'package:flutter_tools/src/artifacts.dart';
import 'package:flutter_tools/src/base/build.dart';
import 'package:flutter_tools/src/base/file_system.dart';
......@@ -67,17 +68,18 @@ void main() {
globals.fs.file(globals.fs.path.join('bin', 'cache', 'pkg', 'sky_engine', 'sdk_ext',
'vmservice_io.dart')).createSync(recursive: true);
environment = Environment.test(
globals.fs.currentDirectory,
defines: <String, String>{
kBuildMode: 'debug',
kTargetPlatform: 'darwin-x64',
},
artifacts: MockArtifacts(),
processManager: FakeProcessManager.any(),
logger: globals.logger,
fileSystem: globals.fs,
);
environment = Environment.test(
globals.fs.currentDirectory,
defines: <String, String>{
kBuildMode: 'debug',
kTargetPlatform: 'darwin-x64',
},
artifacts: MockArtifacts(),
processManager: FakeProcessManager.any(),
logger: globals.logger,
fileSystem: globals.fs,
);
environment.buildDir.createSync(recursive: true);
}, overrides: <Type, Generator>{
ProcessManager: () => MockProcessManager(),
Platform: () => mockPlatform,
......@@ -119,7 +121,7 @@ void main() {
expect(globals.fs.directory(_kOutputPrefix).existsSync(), true);
for (final File file in inputs) {
expect(globals.fs.file(file.path.replaceFirst(_kInputPrefix, _kOutputPrefix)).existsSync(), true);
expect(globals.fs.file(file.path.replaceFirst(_kInputPrefix, _kOutputPrefix)), exists);
}
}));
......@@ -171,9 +173,57 @@ void main() {
'flutter_assets', 'isolate_snapshot_data');
await const ProfileMacOSBundleFlutterAssets().build(environment..defines[kBuildMode] = 'profile');
expect(globals.fs.file(outputKernel).existsSync(), false);
expect(globals.fs.file(precompiledVm).existsSync(), false);
expect(globals.fs.file(precompiledIsolate).existsSync(), false);
expect(globals.fs.file(outputKernel), isNot(exists));
expect(globals.fs.file(precompiledVm), isNot(exists));
expect(globals.fs.file(precompiledIsolate), isNot(exists));
}));
test('release/profile macOS application has no blob or precompiled runtime when '
'run ontop of different configuration', () => testbed.run(() async {
globals.fs.file(globals.fs.path.join('bin', 'cache', 'artifacts', 'engine', 'darwin-x64',
'vm_isolate_snapshot.bin')).createSync(recursive: true);
globals.fs.file(globals.fs.path.join('bin', 'cache', 'artifacts', 'engine', 'darwin-x64',
'isolate_snapshot.bin')).createSync(recursive: true);
globals.fs.file(globals.fs.path.join(environment.buildDir.path, 'App.framework', 'App'))
.createSync(recursive: true);
final String inputKernel = globals.fs.path.join(environment.buildDir.path, 'app.dill');
final String outputKernel = globals.fs.path.join('App.framework', 'Versions', 'A', 'Resources',
'flutter_assets', 'kernel_blob.bin');
globals.fs.file(inputKernel)
..createSync(recursive: true)
..writeAsStringSync('testing');
await const DebugMacOSBundleFlutterAssets().build(environment);
globals.fs.file(globals.fs.path.join('bin', 'cache', 'artifacts', 'engine', 'darwin-x64',
'vm_isolate_snapshot.bin')).createSync(recursive: true);
globals.fs.file(globals.fs.path.join('bin', 'cache', 'artifacts', 'engine', 'darwin-x64',
'isolate_snapshot.bin')).createSync(recursive: true);
final Environment testEnvironment = Environment.test(
globals.fs.currentDirectory,
defines: <String, String>{
kBuildMode: 'profile',
kTargetPlatform: 'darwin-x64',
},
artifacts: MockArtifacts(),
processManager: FakeProcessManager.any(),
logger: globals.logger,
fileSystem: globals.fs,
);
testEnvironment.buildDir.createSync(recursive: true);
globals.fs.file(globals.fs.path.join(testEnvironment.buildDir.path, 'App.framework', 'App'))
.createSync(recursive: true);
final String precompiledVm = globals.fs.path.join('App.framework', 'Resources',
'flutter_assets', 'vm_snapshot_data');
final String precompiledIsolate = globals.fs.path.join('App.framework', 'Resources',
'flutter_assets', 'isolate_snapshot_data');
await const ProfileMacOSBundleFlutterAssets().build(testEnvironment);
expect(globals.fs.file(outputKernel), isNot(exists));
expect(globals.fs.file(precompiledVm), isNot(exists));
expect(globals.fs.file(precompiledIsolate), isNot(exists));
}));
test('release/profile macOS application updates when App.framework updates', () => testbed.run(() async {
......
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