Unverified Commit 0f6c093d authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

Remove Source.behavior, fix bug in depfile invalidation (#43945)

* remove Source.behavior, fix bug in depfile invalidation

* more cleanup of assets

* Add skip

* address comments

* Update build_system.dart
parent b815f762
...@@ -526,17 +526,20 @@ class _BuildInstance { ...@@ -526,17 +526,20 @@ class _BuildInstance {
await node.target.build(environment); await node.target.build(environment);
printTrace('${node.target.name}: Complete'); printTrace('${node.target.name}: Complete');
// If we were missing the depfile, resolve files after executing the node.inputs
..clear()
..addAll(node.target.resolveInputs(environment).sources);
node.outputs
..clear()
..addAll(node.target.resolveOutputs(environment).sources);
// If we were missing the depfile, resolve input files after executing the
// target so that all file hashes are up to date on the next run. // target so that all file hashes are up to date on the next run.
if (node.missingDepfile) { if (node.missingDepfile) {
node.inputs.clear();
node.outputs.clear();
node.inputs.addAll(node.target.resolveInputs(environment).sources);
node.outputs.addAll(node.target.resolveOutputs(environment).sources);
await fileCache.hashFiles(node.inputs); await fileCache.hashFiles(node.inputs);
} }
// Update hashes for output files. // Always update hashes for output files.
await fileCache.hashFiles(node.outputs); await fileCache.hashFiles(node.outputs);
node.target._writeStamp(node.inputs, node.outputs, environment); node.target._writeStamp(node.inputs, node.outputs, environment);
updateGraph(); updateGraph();
......
...@@ -56,9 +56,13 @@ class Depfile { ...@@ -56,9 +56,13 @@ class Depfile {
/// Given an [depfile] File, write the depfile contents. /// Given an [depfile] File, write the depfile contents.
/// ///
/// If either [inputs] or [outputs] is empty, does not write to the file. /// If either [inputs] or [outputs] is empty, ensures the file does not
/// exist.
void writeToFile(File depfile) { void writeToFile(File depfile) {
if (inputs.isEmpty || outputs.isEmpty) { if (inputs.isEmpty || outputs.isEmpty) {
if (depfile.existsSync()) {
depfile.deleteSync();
}
return; return;
} }
final StringBuffer buffer = StringBuffer(); final StringBuffer buffer = StringBuffer();
......
...@@ -163,15 +163,6 @@ class SourceVisitor implements ResolvedFiles { ...@@ -163,15 +163,6 @@ class SourceVisitor implements ResolvedFiles {
} }
} }
/// Visit a [Source] which contains a [SourceBehavior].
void visitBehavior(SourceBehavior sourceBehavior) {
if (inputs) {
sources.addAll(sourceBehavior.inputs(environment));
} else {
sources.addAll(sourceBehavior.outputs(environment));
}
}
/// Visit a [Source] which is defined by an [Artifact] from the flutter cache. /// Visit a [Source] which is defined by an [Artifact] from the flutter cache.
/// ///
/// If the [Artifact] points to a directory then all child files are included. /// If the [Artifact] points to a directory then all child files are included.
...@@ -194,10 +185,6 @@ abstract class Source { ...@@ -194,10 +185,6 @@ abstract class Source {
/// This source is a file-uri which contains some references to magic /// This source is a file-uri which contains some references to magic
/// environment variables. /// environment variables.
const factory Source.pattern(String pattern, { bool optional }) = _PatternSource; const factory Source.pattern(String pattern, { bool optional }) = _PatternSource;
/// This source is produced by the [SourceBehavior] class.
const factory Source.behavior(SourceBehavior behavior) = _SourceBehavior;
/// The source is provided by an [Artifact]. /// The source is provided by an [Artifact].
/// ///
/// If [artifact] points to a directory then all child files are included. /// If [artifact] points to a directory then all child files are included.
...@@ -222,34 +209,10 @@ abstract class Source { ...@@ -222,34 +209,10 @@ abstract class Source {
/// evaluated before the build. /// evaluated before the build.
/// ///
/// For example, [Source.pattern] and [Source.version] are not implicit /// For example, [Source.pattern] and [Source.version] are not implicit
/// provided they do not use any wildcards. [Source.behavior] and /// provided they do not use any wildcards.
/// [Source.function] are always implicit.
bool get implicit; bool get implicit;
} }
/// An interface for describing input and output copies together.
abstract class SourceBehavior {
const SourceBehavior();
/// The inputs for a particular target.
List<File> inputs(Environment environment);
/// The outputs for a particular target.
List<File> outputs(Environment environment);
}
class _SourceBehavior implements Source {
const _SourceBehavior(this.value);
final SourceBehavior value;
@override
void accept(SourceVisitor visitor) => visitor.visitBehavior(value);
@override
bool get implicit => true;
}
class _PatternSource implements Source { class _PatternSource implements Source {
const _PatternSource(this.value, { this.optional = false }); const _PatternSource(this.value, { this.optional = false });
......
...@@ -10,81 +10,45 @@ import '../../devfs.dart'; ...@@ -10,81 +10,45 @@ import '../../devfs.dart';
import '../../plugins.dart'; import '../../plugins.dart';
import '../../project.dart'; import '../../project.dart';
import '../build_system.dart'; import '../build_system.dart';
import '../depfile.dart';
/// The copying logic for flutter assets.
class AssetBehavior extends SourceBehavior { /// A helper function to copy an asset bundle into an [environment]'s output
const AssetBehavior(); /// directory.
///
@override /// Returns a [Depfile] containing all assets used in the build.
List<File> inputs(Environment environment) { Future<Depfile> copyAssets(Environment environment, Directory outputDirectory) async {
final AssetBundle assetBundle = AssetBundleFactory.instance.createBundle(); final File pubspecFile = environment.projectDir.childFile('pubspec.yaml');
assetBundle.build( final AssetBundle assetBundle = AssetBundleFactory.instance.createBundle();
manifestPath: environment.projectDir.childFile('pubspec.yaml').path, await assetBundle.build(
packagesPath: environment.projectDir.childFile('.packages').path, manifestPath: pubspecFile.path,
); packagesPath: environment.projectDir.childFile('.packages').path,
// Filter the file type to remove the files that are generated by this );
// command as inputs. final Pool pool = Pool(kMaxOpenFiles);
final List<File> results = <File>[]; final List<File> inputs = <File>[
final Iterable<DevFSFileContent> files = assetBundle.entries.values.whereType<DevFSFileContent>(); // An asset manifest with no assets would have zero inputs if not
for (DevFSFileContent devFsContent in files) { // for this pubspec file.
results.add(fs.file(devFsContent.file.path)); pubspecFile,
} ];
return results; final List<File> outputs = <File>[];
} await Future.wait<void>(
assetBundle.entries.entries.map<Future<void>>((MapEntry<String, DevFSContent> entry) async {
@override final PoolResource resource = await pool.request();
List<File> outputs(Environment environment) { try {
final AssetBundle assetBundle = AssetBundleFactory.instance.createBundle(); final File file = fs.file(fs.path.join(outputDirectory.path, entry.key));
assetBundle.build( outputs.add(file);
manifestPath: environment.projectDir.childFile('pubspec.yaml').path, file.parent.createSync(recursive: true);
packagesPath: environment.projectDir.childFile('.packages').path, final DevFSContent content = entry.value;
); if (content is DevFSFileContent && content.file is File) {
final List<File> results = <File>[]; inputs.add(fs.file(content.file.path));
for (String key in assetBundle.entries.keys) { await (content.file as File).copy(file.path);
final File file = fs.file(fs.path.join(environment.buildDir.path, 'flutter_assets', key)); } else {
results.add(file); await file.writeAsBytes(await entry.value.contentsAsBytes());
} }
return results; } finally {
} resource.release();
} }
}));
/// A specific asset behavior for building bundles. return Depfile(inputs, outputs);
class AssetOutputBehavior extends SourceBehavior {
const AssetOutputBehavior([this._pathSuffix = '']);
final String _pathSuffix;
@override
List<File> inputs(Environment environment) {
final AssetBundle assetBundle = AssetBundleFactory.instance.createBundle();
assetBundle.build(
manifestPath: environment.projectDir.childFile('pubspec.yaml').path,
packagesPath: environment.projectDir.childFile('.packages').path,
);
// Filter the file type to remove the files that are generated by this
// command as inputs.
final List<File> results = <File>[];
final Iterable<DevFSFileContent> files = assetBundle.entries.values.whereType<DevFSFileContent>();
for (DevFSFileContent devFsContent in files) {
results.add(fs.file(fs.path.join(_pathSuffix, devFsContent.file.path)));
}
return results;
}
@override
List<File> outputs(Environment environment) {
final AssetBundle assetBundle = AssetBundleFactory.instance.createBundle();
assetBundle.build(
manifestPath: environment.projectDir.childFile('pubspec.yaml').path,
packagesPath: environment.projectDir.childFile('.packages').path,
);
final List<File> results = <File>[];
for (String key in assetBundle.entries.keys) {
final File file = fs.file(fs.path.join(environment.outputDir.path, _pathSuffix, key));
results.add(file);
}
return results;
}
} }
/// Copy the assets defined in the flutter manifest into a build directory. /// Copy the assets defined in the flutter manifest into a build directory.
...@@ -100,16 +64,12 @@ class CopyAssets extends Target { ...@@ -100,16 +64,12 @@ class CopyAssets 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/assets.dart'), Source.pattern('{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/assets.dart'),
Source.pattern('{PROJECT_DIR}/pubspec.yaml'), Source.depfile('flutter_assets.d'),
Source.behavior(AssetBehavior()),
]; ];
@override @override
List<Source> get outputs => const <Source>[ List<Source> get outputs => const <Source>[
Source.pattern('{BUILD_DIR}/flutter_assets/AssetManifest.json'), Source.depfile('flutter_assets.d'),
Source.pattern('{BUILD_DIR}/flutter_assets/FontManifest.json'),
Source.pattern('{BUILD_DIR}/flutter_assets/LICENSE'),
Source.behavior(AssetBehavior()), // <- everything in this subdirectory.
]; ];
@override @override
...@@ -117,28 +77,9 @@ class CopyAssets extends Target { ...@@ -117,28 +77,9 @@ class CopyAssets extends Target {
final Directory output = environment final Directory output = environment
.buildDir .buildDir
.childDirectory('flutter_assets'); .childDirectory('flutter_assets');
if (output.existsSync()) {
output.deleteSync(recursive: true);
}
output.createSync(recursive: true); output.createSync(recursive: true);
final AssetBundle assetBundle = AssetBundleFactory.instance.createBundle(); final Depfile depfile = await copyAssets(environment, output);
await assetBundle.build( depfile.writeToFile(environment.buildDir.childFile('flutter_assets.d'));
manifestPath: environment.projectDir.childFile('pubspec.yaml').path,
packagesPath: environment.projectDir.childFile('.packages').path,
);
// Limit number of open files to avoid running out of file descriptors.
final Pool pool = Pool(kMaxOpenFiles);
await Future.wait<void>(
assetBundle.entries.entries.map<Future<void>>((MapEntry<String, DevFSContent> entry) async {
final PoolResource resource = await pool.request();
try {
final File file = fs.file(fs.path.join(output.path, entry.key));
file.parent.createSync(recursive: true);
await file.writeAsBytes(await entry.value.contentsAsBytes());
} finally {
resource.release();
}
}));
} }
} }
......
...@@ -2,18 +2,15 @@ ...@@ -2,18 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'package:pool/pool.dart';
import '../../artifacts.dart'; import '../../artifacts.dart';
import '../../asset.dart';
import '../../base/build.dart'; import '../../base/build.dart';
import '../../base/file_system.dart'; import '../../base/file_system.dart';
import '../../build_info.dart'; import '../../build_info.dart';
import '../../compile.dart'; import '../../compile.dart';
import '../../devfs.dart';
import '../../globals.dart'; import '../../globals.dart';
import '../../project.dart'; import '../../project.dart';
import '../build_system.dart'; import '../build_system.dart';
import '../depfile.dart';
import '../exceptions.dart'; import '../exceptions.dart';
import 'assets.dart'; import 'assets.dart';
...@@ -53,7 +50,7 @@ class CopyFlutterBundle extends Target { ...@@ -53,7 +50,7 @@ class CopyFlutterBundle extends Target {
Source.artifact(Artifact.vmSnapshotData, mode: BuildMode.debug), Source.artifact(Artifact.vmSnapshotData, mode: BuildMode.debug),
Source.artifact(Artifact.isolateSnapshotData, mode: BuildMode.debug), Source.artifact(Artifact.isolateSnapshotData, mode: BuildMode.debug),
Source.pattern('{BUILD_DIR}/app.dill'), Source.pattern('{BUILD_DIR}/app.dill'),
Source.behavior(AssetOutputBehavior()), Source.depfile('flutter_assets.d'),
]; ];
@override @override
...@@ -61,10 +58,7 @@ class CopyFlutterBundle extends Target { ...@@ -61,10 +58,7 @@ class CopyFlutterBundle extends Target {
Source.pattern('{OUTPUT_DIR}/vm_snapshot_data'), Source.pattern('{OUTPUT_DIR}/vm_snapshot_data'),
Source.pattern('{OUTPUT_DIR}/isolate_snapshot_data'), Source.pattern('{OUTPUT_DIR}/isolate_snapshot_data'),
Source.pattern('{OUTPUT_DIR}/kernel_blob.bin'), Source.pattern('{OUTPUT_DIR}/kernel_blob.bin'),
Source.pattern('{OUTPUT_DIR}/AssetManifest.json'), Source.depfile('flutter_assets.d'),
Source.pattern('{OUTPUT_DIR}/FontManifest.json'),
Source.pattern('{OUTPUT_DIR}/LICENSE'),
Source.behavior(AssetOutputBehavior()),
]; ];
@override @override
...@@ -73,12 +67,6 @@ class CopyFlutterBundle extends Target { ...@@ -73,12 +67,6 @@ class CopyFlutterBundle extends Target {
throw MissingDefineException(kBuildMode, 'copy_flutter_bundle'); throw MissingDefineException(kBuildMode, 'copy_flutter_bundle');
} }
final BuildMode buildMode = getBuildModeForName(environment.defines[kBuildMode]); final BuildMode buildMode = getBuildModeForName(environment.defines[kBuildMode]);
// We're not smart enough to only remove assets that are removed. If
// anything changes blow away the whole directory.
if (environment.outputDir.existsSync()) {
environment.outputDir.deleteSync(recursive: true);
}
environment.outputDir.createSync(recursive: true); environment.outputDir.createSync(recursive: true);
// Only copy the prebuilt runtimes and kernel blob in debug mode. // Only copy the prebuilt runtimes and kernel blob in debug mode.
...@@ -92,10 +80,8 @@ class CopyFlutterBundle extends Target { ...@@ -92,10 +80,8 @@ class CopyFlutterBundle extends Target {
fs.file(isolateSnapshotData) fs.file(isolateSnapshotData)
.copySync(environment.outputDir.childFile('isolate_snapshot_data').path); .copySync(environment.outputDir.childFile('isolate_snapshot_data').path);
} }
final Depfile assetDepfile = await copyAssets(environment, environment.outputDir);
final AssetBundle assetBundle = AssetBundleFactory.instance.createBundle(); assetDepfile.writeToFile(environment.buildDir.childFile('flutter_assets.d'));
await assetBundle.build();
await copyAssets(assetBundle, environment);
} }
@override @override
...@@ -104,28 +90,6 @@ class CopyFlutterBundle extends Target { ...@@ -104,28 +90,6 @@ class CopyFlutterBundle extends Target {
]; ];
} }
/// A helper function to copy an [assetBundle] into an [environment]'s output directory,
/// plus an optional [pathSuffix]
Future<void> copyAssets(AssetBundle assetBundle, Environment environment, [String pathSuffix = '']) async {
final Pool pool = Pool(kMaxOpenFiles);
await Future.wait<void>(
assetBundle.entries.entries.map<Future<void>>((MapEntry<String, DevFSContent> entry) async {
final PoolResource resource = await pool.request();
try {
final File file = fs.file(fs.path.join(environment.outputDir.path, pathSuffix, entry.key));
file.parent.createSync(recursive: true);
final DevFSContent content = entry.value;
if (content is DevFSFileContent && content.file is File) {
await (content.file as File).copy(file.path);
} else {
await file.writeAsBytes(await entry.value.contentsAsBytes());
}
} finally {
resource.release();
}
}));
}
/// Copies the prebuilt flutter bundle for release mode. /// Copies the prebuilt flutter bundle for release mode.
class ReleaseCopyFlutterBundle extends CopyFlutterBundle { class ReleaseCopyFlutterBundle extends CopyFlutterBundle {
const ReleaseCopyFlutterBundle(); const ReleaseCopyFlutterBundle();
...@@ -135,15 +99,12 @@ class ReleaseCopyFlutterBundle extends CopyFlutterBundle { ...@@ -135,15 +99,12 @@ class ReleaseCopyFlutterBundle extends CopyFlutterBundle {
@override @override
List<Source> get inputs => const <Source>[ List<Source> get inputs => const <Source>[
Source.behavior(AssetOutputBehavior()), Source.depfile('flutter_assets.d'),
]; ];
@override @override
List<Source> get outputs => const <Source>[ List<Source> get outputs => const <Source>[
Source.pattern('{OUTPUT_DIR}/AssetManifest.json'), Source.depfile('flutter_assets.d'),
Source.pattern('{OUTPUT_DIR}/FontManifest.json'),
Source.pattern('{OUTPUT_DIR}/LICENSE'),
Source.behavior(AssetOutputBehavior()),
]; ];
@override @override
......
...@@ -2,13 +2,9 @@ ...@@ -2,13 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'package:pool/pool.dart';
import '../../artifacts.dart'; import '../../artifacts.dart';
import '../../asset.dart';
import '../../base/file_system.dart'; import '../../base/file_system.dart';
import '../../build_info.dart'; import '../../build_info.dart';
import '../../devfs.dart';
import '../../globals.dart'; import '../../globals.dart';
import '../build_system.dart'; import '../build_system.dart';
import '../depfile.dart'; import '../depfile.dart';
...@@ -120,16 +116,14 @@ class DebugBundleLinuxAssets extends Target { ...@@ -120,16 +116,14 @@ class DebugBundleLinuxAssets extends Target {
List<Source> get inputs => const <Source>[ List<Source> get inputs => const <Source>[
Source.pattern('{BUILD_DIR}/app.dill'), Source.pattern('{BUILD_DIR}/app.dill'),
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.behavior(AssetOutputBehavior('flutter_assets')), Source.depfile('flutter_assets.d'),
Source.pattern('{PROJECT_DIR}/pubspec.yaml'),
]; ];
@override @override
List<Source> get outputs => const <Source>[ List<Source> get outputs => const <Source>[
Source.behavior(AssetOutputBehavior('flutter_assets')), Source.depfile('flutter_assets.d'),
Source.pattern('{OUTPUT_DIR}/flutter_assets/kernel_blob.bin'), Source.pattern('{OUTPUT_DIR}/flutter_assets/kernel_blob.bin'),
Source.pattern('{OUTPUT_DIR}/flutter_assets/AssetManifest.json'),
Source.pattern('{OUTPUT_DIR}/flutter_assets/FontManifest.json'),
Source.pattern('{OUTPUT_DIR}/flutter_assets/LICENSE'),
]; ];
@override @override
...@@ -149,25 +143,7 @@ class DebugBundleLinuxAssets extends Target { ...@@ -149,25 +143,7 @@ class DebugBundleLinuxAssets extends Target {
environment.buildDir.childFile('app.dill') environment.buildDir.childFile('app.dill')
.copySync(outputDirectory.childFile('kernel_blob.bin').path); .copySync(outputDirectory.childFile('kernel_blob.bin').path);
} }
final Depfile depfile = await copyAssets(environment, outputDirectory);
final AssetBundle assetBundle = AssetBundleFactory.instance.createBundle(); depfile.writeToFile(environment.buildDir.childFile('flutter_assets.d'));
await assetBundle.build();
final Pool pool = Pool(kMaxOpenFiles);
await Future.wait<void>(
assetBundle.entries.entries.map<Future<void>>((MapEntry<String, DevFSContent> entry) async {
final PoolResource resource = await pool.request();
try {
final File file = fs.file(fs.path.join(outputDirectory.path, entry.key));
file.parent.createSync(recursive: true);
final DevFSContent content = entry.value;
if (content is DevFSFileContent && content.file is File) {
await (content.file as File).copy(file.path);
} else {
await file.writeAsBytes(await entry.value.contentsAsBytes());
}
} finally {
resource.release();
}
}));
} }
} }
...@@ -2,65 +2,23 @@ ...@@ -2,65 +2,23 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'package:pool/pool.dart';
import '../../artifacts.dart'; import '../../artifacts.dart';
import '../../asset.dart';
import '../../base/build.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';
import '../../base/process_manager.dart'; import '../../base/process_manager.dart';
import '../../build_info.dart'; import '../../build_info.dart';
import '../../devfs.dart';
import '../../globals.dart'; import '../../globals.dart';
import '../../macos/xcode.dart'; import '../../macos/xcode.dart';
import '../build_system.dart'; import '../build_system.dart';
import '../depfile.dart';
import '../exceptions.dart'; import '../exceptions.dart';
import 'assets.dart';
import 'dart.dart'; import 'dart.dart';
const String _kOutputPrefix = '{OUTPUT_DIR}/FlutterMacOS.framework'; const String _kOutputPrefix = '{OUTPUT_DIR}/FlutterMacOS.framework';
/// The copying logic for flutter assets in macOS.
// TODO(jonahwilliams): remove once build planning lands.
class MacOSAssetBehavior extends SourceBehavior {
const MacOSAssetBehavior();
@override
List<File> inputs(Environment environment) {
final AssetBundle assetBundle = AssetBundleFactory.instance.createBundle();
assetBundle.build(
manifestPath: environment.projectDir.childFile('pubspec.yaml').path,
packagesPath: environment.projectDir.childFile('.packages').path,
);
// Filter the file type to remove the files that are generated by this
// command as inputs.
final List<File> results = <File>[];
final Iterable<DevFSFileContent> files = assetBundle.entries.values.whereType<DevFSFileContent>();
for (DevFSFileContent devFsContent in files) {
results.add(fs.file(devFsContent.file.path));
}
return results;
}
@override
List<File> outputs(Environment environment) {
final AssetBundle assetBundle = AssetBundleFactory.instance.createBundle();
assetBundle.build(
manifestPath: environment.projectDir.childFile('pubspec.yaml').path,
packagesPath: environment.projectDir.childFile('.packages').path,
);
final String prefix = fs.path.join(environment.outputDir.path,
'App.framework', 'Versions', 'A', 'Resources', 'flutter_assets');
final List<File> results = <File>[];
for (String key in assetBundle.entries.keys) {
final File file = fs.file(fs.path.join(prefix, key));
results.add(file);
}
return results;
}
}
/// 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'.
/// ///
/// This class is abstract to share logic between the three concrete /// This class is abstract to share logic between the three concrete
...@@ -286,19 +244,15 @@ abstract class MacOSBundleFlutterAssets extends Target { ...@@ -286,19 +244,15 @@ abstract class MacOSBundleFlutterAssets extends Target {
@override @override
List<Source> get inputs => const <Source>[ List<Source> get inputs => const <Source>[
Source.pattern('{PROJECT_DIR}/pubspec.yaml'),
Source.pattern('{BUILD_DIR}/App.framework/App'), Source.pattern('{BUILD_DIR}/App.framework/App'),
Source.behavior(MacOSAssetBehavior()), Source.depfile('flutter_assets.d'),
]; ];
@override @override
List<Source> get outputs => const <Source>[ List<Source> get outputs => const <Source>[
Source.behavior(MacOSAssetBehavior()),
Source.pattern('{OUTPUT_DIR}/App.framework/Versions/A/App'), Source.pattern('{OUTPUT_DIR}/App.framework/Versions/A/App'),
Source.pattern('{OUTPUT_DIR}/App.framework/Versions/A/Resources/Info.plist'), Source.pattern('{OUTPUT_DIR}/App.framework/Versions/A/Resources/Info.plist'),
Source.pattern('{OUTPUT_DIR}/App.framework/Versions/A/Resources/flutter_assets/AssetManifest.json'), Source.depfile('flutter_assets.d'),
Source.pattern('{OUTPUT_DIR}/App.framework/Versions/A/Resources/flutter_assets/FontManifest.json'),
Source.pattern('{OUTPUT_DIR}/App.framework/Versions/A/Resources/flutter_assets/LICENSE'),
]; ];
@override @override
...@@ -326,31 +280,9 @@ abstract class MacOSBundleFlutterAssets extends Target { ...@@ -326,31 +280,9 @@ abstract class MacOSBundleFlutterAssets extends Target {
.childDirectory('Resources') .childDirectory('Resources')
.childDirectory('flutter_assets'); .childDirectory('flutter_assets');
assetDirectory.createSync(recursive: true); assetDirectory.createSync(recursive: true);
final AssetBundle assetBundle = AssetBundleFactory.instance.createBundle(); final Depfile depfile = await copyAssets(environment, assetDirectory);
final int result = await assetBundle.build( depfile.writeToFile(environment.buildDir.childFile('flutter_assets.d'));
manifestPath: environment.projectDir.childFile('pubspec.yaml').path,
packagesPath: environment.projectDir.childFile('.packages').path,
);
if (result != 0) {
throw Exception('Failed to create asset bundle: $result');
}
// Limit number of open files to avoid running out of file descriptors.
try {
final Pool pool = Pool(kMaxOpenFiles);
await Future.wait<void>(
assetBundle.entries.entries.map<Future<void>>((MapEntry<String, DevFSContent> entry) async {
final PoolResource resource = await pool.request();
try {
final File file = fs.file(fs.path.join(assetDirectory.path, entry.key));
file.parent.createSync(recursive: true);
await file.writeAsBytes(await entry.value.contentsAsBytes());
} finally {
resource.release();
}
}));
} catch (err, st) {
throw Exception('Failed to copy assets: $st');
}
// Copy Info.plist template. // Copy Info.plist template.
assetDirectory.parent.childFile('Info.plist') assetDirectory.parent.childFile('Info.plist')
..createSync() ..createSync()
......
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
// found in the LICENSE file. // found in the LICENSE file.
import '../../artifacts.dart'; import '../../artifacts.dart';
import '../../asset.dart';
import '../../base/file_system.dart'; import '../../base/file_system.dart';
import '../../base/io.dart'; import '../../base/io.dart';
import '../../base/process_manager.dart'; import '../../base/process_manager.dart';
...@@ -183,18 +182,16 @@ class WebReleaseBundle extends Target { ...@@ -183,18 +182,16 @@ class WebReleaseBundle extends Target {
@override @override
List<Source> get inputs => const <Source>[ List<Source> get inputs => const <Source>[
Source.pattern('{BUILD_DIR}/main.dart.js'), Source.pattern('{BUILD_DIR}/main.dart.js'),
Source.behavior(AssetOutputBehavior('assets')), Source.pattern('{PROJECT_DIR}/pubspec.yaml'),
Source.pattern('{PROJECT_DIR}/web/index.html'), Source.pattern('{PROJECT_DIR}/web/index.html'),
Source.depfile('flutter_assets.d'),
]; ];
@override @override
List<Source> get outputs => const <Source>[ List<Source> get outputs => const <Source>[
Source.pattern('{OUTPUT_DIR}/main.dart.js'), Source.pattern('{OUTPUT_DIR}/main.dart.js'),
Source.pattern('{OUTPUT_DIR}/assets/AssetManifest.json'),
Source.pattern('{OUTPUT_DIR}/assets/FontManifest.json'),
Source.pattern('{OUTPUT_DIR}/assets/LICENSE'),
Source.pattern('{OUTPUT_DIR}/index.html'), Source.pattern('{OUTPUT_DIR}/index.html'),
Source.behavior(AssetOutputBehavior('assets')) Source.depfile('flutter_assets.d'),
]; ];
@override @override
...@@ -207,12 +204,13 @@ class WebReleaseBundle extends Target { ...@@ -207,12 +204,13 @@ class WebReleaseBundle extends Target {
environment.outputDir.childFile(fs.path.basename(outputFile.path)).path environment.outputDir.childFile(fs.path.basename(outputFile.path)).path
); );
} }
final Directory outputDirectory = environment.outputDir.childDirectory('assets');
outputDirectory.createSync(recursive: true);
environment.projectDir environment.projectDir
.childDirectory('web') .childDirectory('web')
.childFile('index.html') .childFile('index.html')
.copySync(fs.path.join(environment.outputDir.path, 'index.html')); .copySync(fs.path.join(environment.outputDir.path, 'index.html'));
final AssetBundle assetBundle = AssetBundleFactory.instance.createBundle(); final Depfile depfile = await copyAssets(environment, environment.outputDir.childDirectory('assets'));
await assetBundle.build(); depfile.writeToFile(environment.buildDir.childFile('flutter_assets.d'));
await copyAssets(assetBundle, environment, 'assets');
} }
} }
...@@ -339,6 +339,41 @@ void main() { ...@@ -339,6 +339,41 @@ void main() {
expect(environmentA.buildDir.path, isNot(environmentB.buildDir.path)); expect(environmentA.buildDir.path, isNot(environmentB.buildDir.path));
})); }));
test('A target with depfile dependencies can delete stale outputs on the first run', () => testbed.run(() async {
int called = 0;
final TestTarget target = TestTarget((Environment environment) async {
if (called == 0) {
environment.buildDir.childFile('example.d')
.writeAsStringSync('a.txt c.txt: b.txt');
fs.file('a.txt').writeAsStringSync('a');
fs.file('c.txt').writeAsStringSync('a');
} else {
// On second run, we no longer claim c.txt as an output.
environment.buildDir.childFile('example.d')
.writeAsStringSync('a.txt: b.txt');
fs.file('a.txt').writeAsStringSync('a');
}
called += 1;
})
..inputs = const <Source>[Source.depfile('example.d')]
..outputs = const <Source>[Source.depfile('example.d')];
fs.file('b.txt').writeAsStringSync('b');
await buildSystem.build(target, environment);
expect(fs.file('a.txt').existsSync(), true);
expect(fs.file('c.txt').existsSync(), true);
expect(called, 1);
// rewrite an input to force a rerun, espect that the old c.txt is deleted.
fs.file('b.txt').writeAsStringSync('ba');
await buildSystem.build(target, environment);
expect(fs.file('a.txt').existsSync(), true);
expect(fs.file('c.txt').existsSync(), false);
expect(called, 2);
}));
} }
class MockPlatform extends Mock implements Platform {} class MockPlatform extends Mock implements Platform {}
......
...@@ -41,7 +41,6 @@ void main() { ...@@ -41,7 +41,6 @@ void main() {
test('configures implicit vs explict correctly', () => testbed.run(() { test('configures implicit vs explict correctly', () => testbed.run(() {
expect(const Source.pattern('{PROJECT_DIR}/foo').implicit, false); expect(const Source.pattern('{PROJECT_DIR}/foo').implicit, false);
expect(const Source.pattern('{PROJECT_DIR}/*foo').implicit, true); expect(const Source.pattern('{PROJECT_DIR}/*foo').implicit, true);
expect(Source.behavior(TestBehavior()).implicit, true);
})); }));
test('can substitute {PROJECT_DIR}/foo', () => testbed.run(() { test('can substitute {PROJECT_DIR}/foo', () => testbed.run(() {
...@@ -217,17 +216,4 @@ void main() { ...@@ -217,17 +216,4 @@ void main() {
})); }));
} }
class TestBehavior extends SourceBehavior {
@override
List<File> inputs(Environment environment) {
return null;
}
@override
List<File> outputs(Environment environment) {
return null;
}
}
class MockPlatform extends Mock implements Platform {} class MockPlatform extends Mock implements Platform {}
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:io';
import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/file_system.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/assets.dart'; import 'package:flutter_tools/src/build_system/targets/assets.dart';
...@@ -65,7 +67,7 @@ flutter: ...@@ -65,7 +67,7 @@ flutter:
// See https://github.com/flutter/flutter/issues/35293 // See https://github.com/flutter/flutter/issues/35293
expect(fs.file(fs.path.join(environment.buildDir.path, 'flutter_assets', 'assets/foo/bar.png')).existsSync(), false); expect(fs.file(fs.path.join(environment.buildDir.path, 'flutter_assets', 'assets/foo/bar.png')).existsSync(), false);
})); }), skip: Platform.isWindows); // See https://github.com/google/file.dart/issues/131
test('FlutterPlugins updates required files as needed', () => testbed.run(() async { test('FlutterPlugins updates required files as needed', () => testbed.run(() async {
fs.file('pubspec.yaml') fs.file('pubspec.yaml')
......
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