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

[flutter_tools] maintain file manifest for create (#59706)

First pass at fixing #57985 and implementing #59602

This doesn't have enough metadata to be useful for IDEs yet, but it prevents the issue from getting worse while we iterate on it.
parent 2a7ee930
......@@ -278,6 +278,29 @@ class CreateCommand extends FlutterCommand {
return template;
}
Set<Uri> get templateManifest => _templateManifest ??= _computeTemplateManifest();
Set<Uri> _templateManifest;
Set<Uri> _computeTemplateManifest() {
final String flutterToolsAbsolutePath = globals.fs.path.join(
Cache.flutterRoot,
'packages',
'flutter_tools',
);
final String manifestPath = globals.fs.path.join(
flutterToolsAbsolutePath,
'templates',
'template_manifest.json',
);
final Map<String, Object> manifest = json.decode(
globals.fs.file(manifestPath).readAsStringSync(),
) as Map<String, Object>;
return Set<Uri>.from(
(manifest['files'] as List<Object>)
.cast<String>()
.map<Uri>((String path) => Uri.file(globals.fs.path.join(flutterToolsAbsolutePath, path))),
);
}
@override
Future<FlutterCommandResult> runCommand() async {
if (argResults['list-samples'] != null) {
......@@ -750,7 +773,11 @@ https://flutter.dev/docs/development/packages-and-plugins/developing-packages#pl
}
Future<int> _renderTemplate(String templateName, Directory directory, Map<String, dynamic> context, { bool overwrite = false }) async {
final Template template = await Template.fromName(templateName, fileSystem: globals.fs);
final Template template = await Template.fromName(
templateName,
fileSystem: globals.fs,
templateManifest: templateManifest,
);
return template.render(directory, context, overwriteExisting: overwrite);
}
......
......@@ -247,7 +247,13 @@ class IdeConfigCommand extends FlutterCommand {
}
int _renderTemplate(String templateName, String dirPath, Map<String, dynamic> context) {
final Template template = Template(_templateDirectory, _templateDirectory, null, fileSystem: globals.fs);
final Template template = Template(
_templateDirectory,
_templateDirectory,
null,
fileSystem: globals.fs,
templateManifest: null,
);
return template.render(
globals.fs.directory(dirPath),
context,
......
......@@ -647,7 +647,7 @@ class IosProject extends FlutterProjectPlatform implements XcodeBasedProject {
}
Future<void> _overwriteFromTemplate(String path, Directory target) async {
final Template template = await Template.fromName(path, fileSystem: globals.fs);
final Template template = await Template.fromName(path, fileSystem: globals.fs, templateManifest: null);
template.render(
target,
<String, dynamic>{
......@@ -797,7 +797,7 @@ class AndroidProject extends FlutterProjectPlatform {
}
Future<void> _overwriteFromTemplate(String path, Directory target) async {
final Template template = await Template.fromName(path, fileSystem: globals.fs);
final Template template = await Template.fromName(path, fileSystem: globals.fs, templateManifest: null);
template.render(
target,
<String, dynamic>{
......
......@@ -32,7 +32,9 @@ import 'globals.dart' as globals hide fs;
class Template {
Template(Directory templateSource, Directory baseDir, this.imageSourceDir, {
@required FileSystem fileSystem,
}) : _fileSystem = fileSystem {
@required Set<Uri> templateManifest,
}) : _fileSystem = fileSystem,
_templateManifest = templateManifest {
_templateFilePaths = <String, String>{};
if (!templateSource.existsSync()) {
......@@ -46,10 +48,14 @@ class Template {
// We are only interesting in template *file* URIs.
continue;
}
if (_templateManifest != null && !_templateManifest.contains(Uri.file(entity.absolute.path))) {
globals.logger.printTrace('Skipping ${entity.absolute.path}, missing from the template manifest.');
// Skip stale files in the flutter_tools directory.
continue;
}
final String relativePath = fileSystem.path.relative(entity.path,
from: baseDir.absolute.path);
if (relativePath.contains(templateExtension)) {
// If '.tmpl' appears anywhere within the path of this entity, it is
// is a candidate for rendering. This catches cases where the folder
......@@ -59,14 +65,23 @@ class Template {
}
}
static Future<Template> fromName(String name, { @required FileSystem fileSystem }) async {
static Future<Template> fromName(String name, {
@required FileSystem fileSystem,
@required Set<Uri> templateManifest,
}) async {
// All named templates are placed in the 'templates' directory
final Directory templateDir = _templateDirectoryInPackage(name, fileSystem);
final Directory imageDir = await _templateImageDirectory(name, fileSystem);
return Template(templateDir, templateDir, imageDir, fileSystem: fileSystem);
return Template(
templateDir,
templateDir, imageDir,
fileSystem: fileSystem,
templateManifest: templateManifest,
);
}
final FileSystem _fileSystem;
final Set<Uri> _templateManifest;
static const String templateExtension = '.tmpl';
static const String copyTemplateExtension = '.copy.tmpl';
static const String imageTemplateExtension = '.img.tmpl';
......
This diff is collapsed.
......@@ -39,10 +39,14 @@ void main() {
// Set up enough of the packages to satisfy the templating code.
final File packagesFile = globals.fs.file(
globals.fs.path.join('flutter', 'packages', 'flutter_tools', '.packages'));
final File flutterManifest = globals.fs.file(
globals.fs.path.join('flutter', 'packages', 'flutter_tools', 'templates', 'template_manifest.json'))
..createSync(recursive: true);
final Directory templateImagesDirectory = globals.fs.directory('flutter_template_images');
templateImagesDirectory.createSync(recursive: true);
packagesFile.createSync(recursive: true);
packagesFile.writeAsStringSync('flutter_template_images:file:///${templateImagesDirectory.uri}');
flutterManifest.writeAsStringSync('{"files":[]}');
}, overrides: <Type, Generator>{
DoctorValidatorsProvider: () => FakeDoctorValidatorsProvider(),
});
......
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:io'; // ignore: dart_io_import
import 'package:path/path.dart' as path; // ignore: package_path_import
import 'package:flutter_tools/src/convert.dart';
import '../src/common.dart';
/// Checks that all active template files are defined in the template_manifest.json
void main() {
test('Check template manifest is up to date', () {
final Map<String, Object> manifest = json.decode(
File('templates/template_manifest.json').readAsStringSync(),
) as Map<String, Object>;
final Set<Uri> declaredFileList = Set<Uri>.from(
(manifest['files'] as List<Object>).cast<String>().map<Uri>(path.toUri));
final Set<Uri> activeTemplateList = Directory('templates')
.listSync(recursive: true)
.whereType<File>()
.where((File file) => path.basename(file.path) != 'template_manifest.json' &&
path.basename(file.path) != '.DS_Store')
.map((File file) => file.uri)
.toSet();
final Set<Uri> difference = activeTemplateList.difference(declaredFileList);
expect(difference, isEmpty, reason: 'manifest and template directory should be in-sync');
});
}
......@@ -21,7 +21,13 @@ void main() {
});
test('Template.render throws ToolExit when FileSystem exception is raised', () => testbed.run(() {
final Template template = Template(globals.fs.directory('examples'), globals.fs.currentDirectory, null, fileSystem: globals.fs);
final Template template = Template(
globals.fs.directory('examples'),
globals.fs.currentDirectory,
null,
fileSystem: globals.fs,
templateManifest: null,
);
final MockDirectory mockDirectory = MockDirectory();
when(mockDirectory.createSync(recursive: true)).thenThrow(const FileSystemException());
......@@ -40,7 +46,13 @@ void main() {
sourceImage.createSync(recursive: true);
sourceImage.writeAsStringSync('Ceci n\'est pas une pipe');
final Template template = Template(templateDir, templateDir, imageSourceDir, fileSystem: fileSystem);
final Template template = Template(
templateDir,
templateDir,
imageSourceDir,
fileSystem: fileSystem,
templateManifest: null,
);
template.render(destination, <String, Object>{});
final File destinationImage = destination.childFile(imageName);
......@@ -52,7 +64,7 @@ void main() {
final MemoryFileSystem fileSystem = MemoryFileSystem();
// Attempting to run pub in a test throws.
await expectLater(Template.fromName('app', fileSystem: fileSystem),
await expectLater(Template.fromName('app', fileSystem: fileSystem, templateManifest: null),
throwsUnsupportedError);
}));
......@@ -66,7 +78,7 @@ void main() {
packagesFile.createSync(recursive: true);
// Attempting to run pub in a test throws.
await expectLater(Template.fromName('app', fileSystem: fileSystem),
await expectLater(Template.fromName('app', fileSystem: fileSystem, templateManifest: null),
throwsUnsupportedError);
}));
......@@ -88,7 +100,7 @@ void main() {
packagesFile.writeAsStringSync('flutter_template_images:file:///flutter_template_images');
});
await Template.fromName('app', fileSystem: fileSystem);
await Template.fromName('app', fileSystem: fileSystem, templateManifest: null);
}, overrides: <Type, Generator>{
Pub: () => MockPub(),
}));
......
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