diff --git a/.gitattributes b/.gitattributes index 31fda4bf1c862..1f9dafd2b3939 100644 --- a/.gitattributes +++ b/.gitattributes @@ -15,8 +15,17 @@ *.yaml text # Make sure that these Windows files always have CRLF line endings in checkout -*.bat text eol=crlf -*.ps1 text eol=crlf +*.bat text eol=crlf +*.ps1 text eol=crlf +*.rc text eol=crlf +*.sln text eol=crlf +*.props text eol=crlf +*.vcxproj text eol=crlf +*.vcxproj.filters text eol=crlf +# Including templatized versions. +*.sln.tmpl text eol=crlf +*.props.tmpl text eol=crlf +*.vcxproj.tmpl text eol=crlf # Never perform LF normalization on these files *.ico binary diff --git a/dev/bots/analyze.dart b/dev/bots/analyze.dart index 20f65e93dcffb..7a852d96b7d6b 100644 --- a/dev/bots/analyze.dart +++ b/dev/bots/analyze.dart @@ -603,6 +603,7 @@ Future verifyNoTrailingSpaces(String workingDirectory, { int minimumMatche .where((File file) => path.extension(file.path) != '.snapshot') .where((File file) => path.extension(file.path) != '.png') .where((File file) => path.extension(file.path) != '.jpg') + .where((File file) => path.extension(file.path) != '.ico') .where((File file) => path.extension(file.path) != '.jar') .toList(); final List problems = []; @@ -685,7 +686,9 @@ class Hash256 { // DO NOT ADD ANY ENTRIES TO THIS LIST. // We have a policy of not checking in binaries into this repository. -// If you have binaries to add, please consult Hixie for advice. +// If you are adding/changing template images, use the flutter_template_images +// package and a .img.tmpl placeholder instead. +// If you have other binaries to add, please consult Hixie for advice. final Set _grandfatheredBinaries = { // DEFAULT ICON IMAGES @@ -1044,7 +1047,9 @@ final Set _grandfatheredBinaries = { Future verifyNoBinaries(String workingDirectory, { Set grandfatheredBinaries }) async { // Please do not add anything to the _grandfatheredBinaries set above. // We have a policy of not checking in binaries into this repository. - // If you have binaries to add, please consult Hixie for advice. + // If you are adding/changing template images, use the flutter_template_images + // package and a .img.tmpl placeholder instead. + // If you have other binaries to add, please consult Hixie for advice. assert( _grandfatheredBinaries .expand((Hash256 hash) => [hash.a, hash.b, hash.c, hash.d]) diff --git a/packages/flutter_tools/lib/src/asset.dart b/packages/flutter_tools/lib/src/asset.dart index e0e28ed79ccd4..d8db4e086062d 100644 --- a/packages/flutter_tools/lib/src/asset.dart +++ b/packages/flutter_tools/lib/src/asset.dart @@ -148,7 +148,7 @@ class _ManifestAssetBundle implements AssetBundle { final String assetBasePath = globals.fs.path.dirname(globals.fs.path.absolute(manifestPath)); - final PackageMap packageMap = PackageMap(packagesPath); + final PackageMap packageMap = PackageMap(packagesPath, fileSystem: globals.fs); final List wildcardDirectories = []; // The _assetVariants map contains an entry for each asset listed diff --git a/packages/flutter_tools/lib/src/commands/create.dart b/packages/flutter_tools/lib/src/commands/create.dart index c5ad2c3e7d1c0..81e117a4e2b39 100644 --- a/packages/flutter_tools/lib/src/commands/create.dart +++ b/packages/flutter_tools/lib/src/commands/create.dart @@ -407,6 +407,7 @@ class CreateCommand extends FlutterCommand { web: featureFlags.isWebEnabled, linux: featureFlags.isLinuxEnabled, macos: featureFlags.isMacOSEnabled, + windows: featureFlags.isWindowsEnabled, ); final String relativeDirPath = globals.fs.path.relative(projectDirPath); @@ -507,6 +508,12 @@ To edit platform code in an IDE see https://flutter.dev/developing-packages/#edi 'You will likely need to re-create the "linux" directory after future ' 'Flutter updates.'); } + if (featureFlags.isWindowsEnabled) { + globals.printStatus(''); + globals.printStatus('WARNING: The Windows tooling and APIs are not yet stable. ' + 'You will likely need to re-create the "windows" directory after future ' + 'Flutter updates.'); + } } return FlutterCommandResult.success(); } @@ -517,7 +524,7 @@ To edit platform code in an IDE see https://flutter.dev/developing-packages/#edi ? stringArg('description') : 'A new flutter module project.'; templateContext['description'] = description; - generatedCount += _renderTemplate(globals.fs.path.join('module', 'common'), directory, templateContext, overwrite: overwrite); + generatedCount += await _renderTemplate(globals.fs.path.join('module', 'common'), directory, templateContext, overwrite: overwrite); if (boolArg('pub')) { await pub.get( context: PubContext.create, @@ -536,7 +543,7 @@ To edit platform code in an IDE see https://flutter.dev/developing-packages/#edi ? stringArg('description') : 'A new Flutter package project.'; templateContext['description'] = description; - generatedCount += _renderTemplate('package', directory, templateContext, overwrite: overwrite); + generatedCount += await _renderTemplate('package', directory, templateContext, overwrite: overwrite); if (boolArg('pub')) { await pub.get( context: PubContext.createPackage, @@ -553,7 +560,7 @@ To edit platform code in an IDE see https://flutter.dev/developing-packages/#edi ? stringArg('description') : 'A new flutter plugin project.'; templateContext['description'] = description; - generatedCount += _renderTemplate('plugin', directory, templateContext, overwrite: overwrite); + generatedCount += await _renderTemplate('plugin', directory, templateContext, overwrite: overwrite); if (boolArg('pub')) { await pub.get( context: PubContext.createPlugin, @@ -581,13 +588,13 @@ To edit platform code in an IDE see https://flutter.dev/developing-packages/#edi Future _generateApp(Directory directory, Map templateContext, { bool overwrite = false }) async { int generatedCount = 0; - generatedCount += _renderTemplate('app', directory, templateContext, overwrite: overwrite); + generatedCount += await _renderTemplate('app', directory, templateContext, overwrite: overwrite); final FlutterProject project = FlutterProject.fromDirectory(directory); generatedCount += _injectGradleWrapper(project); if (boolArg('with-driver-test')) { final Directory testDirectory = directory.childDirectory('test_driver'); - generatedCount += _renderTemplate('driver', testDirectory, templateContext, overwrite: overwrite); + generatedCount += await _renderTemplate('driver', testDirectory, templateContext, overwrite: overwrite); } if (boolArg('pub')) { @@ -626,6 +633,7 @@ To edit platform code in an IDE see https://flutter.dev/developing-packages/#edi bool web = false, bool linux = false, bool macos = false, + bool windows = false, }) { flutterRoot = globals.fs.path.normalize(flutterRoot); @@ -651,6 +659,7 @@ To edit platform code in an IDE see https://flutter.dev/developing-packages/#edi 'pluginClass': pluginClass, 'pluginDartClass': pluginDartClass, 'pluginCppHeaderGuard': projectName.toUpperCase(), + 'pluginProjectUUID': Uuid().generateV4().toUpperCase(), 'withPluginHook': withPluginHook, 'androidLanguage': androidLanguage, 'iosLanguage': iosLanguage, @@ -659,12 +668,13 @@ To edit platform code in an IDE see https://flutter.dev/developing-packages/#edi 'web': web, 'linux': linux, 'macos': macos, + 'windows': windows, 'year': DateTime.now().year, }; } - int _renderTemplate(String templateName, Directory directory, Map context, { bool overwrite = false }) { - final Template template = Template.fromName(templateName); + Future _renderTemplate(String templateName, Directory directory, Map context, { bool overwrite = false }) async { + final Template template = await Template.fromName(templateName, fileSystem: globals.fs); return template.render(directory, context, overwriteExisting: overwrite); } diff --git a/packages/flutter_tools/lib/src/commands/ide_config.dart b/packages/flutter_tools/lib/src/commands/ide_config.dart index 91b78b83712b5..ae341c7e34a19 100644 --- a/packages/flutter_tools/lib/src/commands/ide_config.dart +++ b/packages/flutter_tools/lib/src/commands/ide_config.dart @@ -247,7 +247,7 @@ class IdeConfigCommand extends FlutterCommand { } int _renderTemplate(String templateName, String dirPath, Map context) { - final Template template = Template(_templateDirectory, _templateDirectory); + final Template template = Template(_templateDirectory, _templateDirectory, null, fileSystem: globals.fs); return template.render( globals.fs.directory(dirPath), context, diff --git a/packages/flutter_tools/lib/src/compile.dart b/packages/flutter_tools/lib/src/compile.dart index a845bc5f199a6..2694ab00fa1ac 100644 --- a/packages/flutter_tools/lib/src/compile.dart +++ b/packages/flutter_tools/lib/src/compile.dart @@ -196,7 +196,7 @@ class StdoutHandler { /// Converts filesystem paths to package URIs. class PackageUriMapper { PackageUriMapper(String scriptPath, String packagesPath, String fileSystemScheme, List fileSystemRoots) { - final Map packageMap = PackageMap(globals.fs.path.absolute(packagesPath)).map; + final Map packageMap = PackageMap(globals.fs.path.absolute(packagesPath), fileSystem: globals.fs).map; final bool isWindowsPath = globals.platform.isWindows && !scriptPath.startsWith('org-dartlang-app'); final String scriptUri = Uri.file(scriptPath, windows: isWindowsPath).toString(); for (final String packageName in packageMap.keys) { diff --git a/packages/flutter_tools/lib/src/dart/package_map.dart b/packages/flutter_tools/lib/src/dart/package_map.dart index ddb072cfe7c71..316aae4b5859c 100644 --- a/packages/flutter_tools/lib/src/dart/package_map.dart +++ b/packages/flutter_tools/lib/src/dart/package_map.dart @@ -2,27 +2,35 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:meta/meta.dart'; // TODO(bkonyi): remove deprecated member usage, https://github.com/flutter/flutter/issues/51951 // ignore: deprecated_member_use import 'package:package_config/packages_file.dart' as packages_file; -import '../globals.dart' as globals; +import '../base/file_system.dart'; +import '../globals.dart' as globals hide fs; const String kPackagesFileName = '.packages'; -Map _parse(String packagesPath) { - final List source = globals.fs.file(packagesPath).readAsBytesSync(); +Map _parse(String packagesPath, FileSystem fileSystem) { + final List source = fileSystem.file(packagesPath).readAsBytesSync(); return packages_file.parse(source, Uri.file(packagesPath, windows: globals.platform.isWindows)); } class PackageMap { - PackageMap(this.packagesPath); + PackageMap(this.packagesPath, { + @required FileSystem fileSystem, + }) : _fileSystem = fileSystem; /// Create a [PackageMap] for testing. - PackageMap.test(Map input) - : packagesPath = '.packages', - _map = input; + PackageMap.test(Map input, { + @required FileSystem fileSystem, + }) : packagesPath = '.packages', + _map = input, + _fileSystem = fileSystem; + + final FileSystem _fileSystem; static String get globalPackagesPath => _globalPackagesPath ?? kPackagesFileName; @@ -38,7 +46,7 @@ class PackageMap { /// Load and parses the .packages file. void load() { - _map ??= _parse(packagesPath); + _map ??= _parse(packagesPath, _fileSystem); } Map get map { @@ -59,17 +67,17 @@ class PackageMap { if (packageBase == null) { return null; } - final String packageRelativePath = globals.fs.path.joinAll(pathSegments); - return packageBase.resolveUri(globals.fs.path.toUri(packageRelativePath)); + final String packageRelativePath = _fileSystem.path.joinAll(pathSegments); + return packageBase.resolveUri(_fileSystem.path.toUri(packageRelativePath)); } String checkValid() { - if (globals.fs.isFileSync(packagesPath)) { + if (_fileSystem.isFileSync(packagesPath)) { return null; } String message = '$packagesPath does not exist.'; - final String pubspecPath = globals.fs.path.absolute(globals.fs.path.dirname(packagesPath), 'pubspec.yaml'); - if (globals.fs.isFileSync(pubspecPath)) { + final String pubspecPath = _fileSystem.path.absolute(_fileSystem.path.dirname(packagesPath), 'pubspec.yaml'); + if (_fileSystem.isFileSync(pubspecPath)) { message += '\nDid you run "flutter pub get" in this directory?'; } else { message += '\nDid you run this command from the same directory as your pubspec.yaml file?'; diff --git a/packages/flutter_tools/lib/src/plugins.dart b/packages/flutter_tools/lib/src/plugins.dart index 2b1d41d55df12..b73b42db16d1b 100644 --- a/packages/flutter_tools/lib/src/plugins.dart +++ b/packages/flutter_tools/lib/src/plugins.dart @@ -307,7 +307,7 @@ List findPlugins(FlutterProject project) { project.directory.path, PackageMap.globalPackagesPath, ); - packages = PackageMap(packagesFile).map; + packages = PackageMap(packagesFile, fileSystem: globals.fs).map; } on FormatException catch (e) { globals.printTrace('Invalid .packages file: $e'); return plugins; diff --git a/packages/flutter_tools/lib/src/project.dart b/packages/flutter_tools/lib/src/project.dart index 9bd80fa412baa..2926a182c6f72 100644 --- a/packages/flutter_tools/lib/src/project.dart +++ b/packages/flutter_tools/lib/src/project.dart @@ -460,7 +460,7 @@ class IosProject extends FlutterProjectPlatform implements XcodeBasedProject { Map _buildSettings; Future ensureReadyForPlatformSpecificTooling() async { - _regenerateFromTemplateIfNeeded(); + await _regenerateFromTemplateIfNeeded(); if (!_flutterLibRoot.existsSync()) { return; } @@ -477,7 +477,7 @@ class IosProject extends FlutterProjectPlatform implements XcodeBasedProject { } } - void _regenerateFromTemplateIfNeeded() { + Future _regenerateFromTemplateIfNeeded() async { if (!isModule) { return; } @@ -491,18 +491,18 @@ class IosProject extends FlutterProjectPlatform implements XcodeBasedProject { } _deleteIfExistsSync(ephemeralDirectory); - _overwriteFromTemplate( + await _overwriteFromTemplate( globals.fs.path.join('module', 'ios', 'library'), ephemeralDirectory, ); // Add ephemeral host app, if a editable host app does not already exist. if (!_editableDirectory.existsSync()) { - _overwriteFromTemplate( + await _overwriteFromTemplate( globals.fs.path.join('module', 'ios', 'host_app_ephemeral'), ephemeralDirectory, ); if (hasPlugins(parent)) { - _overwriteFromTemplate( + await _overwriteFromTemplate( globals.fs.path.join('module', 'ios', 'host_app_ephemeral_cocoapods'), ephemeralDirectory, ); @@ -542,19 +542,19 @@ class IosProject extends FlutterProjectPlatform implements XcodeBasedProject { throwToolExit('iOS host app is already editable. To start fresh, delete the ios/ folder.'); } _deleteIfExistsSync(ephemeralDirectory); - _overwriteFromTemplate( + await _overwriteFromTemplate( globals.fs.path.join('module', 'ios', 'library'), ephemeralDirectory, ); - _overwriteFromTemplate( + await _overwriteFromTemplate( globals.fs.path.join('module', 'ios', 'host_app_ephemeral'), _editableDirectory, ); - _overwriteFromTemplate( + await _overwriteFromTemplate( globals.fs.path.join('module', 'ios', 'host_app_ephemeral_cocoapods'), _editableDirectory, ); - _overwriteFromTemplate( + await _overwriteFromTemplate( globals.fs.path.join('module', 'ios', 'host_app_editable_cocoapods'), _editableDirectory, ); @@ -579,8 +579,8 @@ class IosProject extends FlutterProjectPlatform implements XcodeBasedProject { : hostAppRoot.childDirectory(_hostAppBundleName); } - void _overwriteFromTemplate(String path, Directory target) { - final Template template = Template.fromName(path); + Future _overwriteFromTemplate(String path, Directory target) async { + final Template template = await Template.fromName(path, fileSystem: globals.fs); template.render( target, { @@ -679,11 +679,11 @@ class AndroidProject extends FlutterProjectPlatform { Future ensureReadyForPlatformSpecificTooling() async { if (isModule && _shouldRegenerateFromTemplate()) { - _regenerateLibrary(); + await _regenerateLibrary(); // Add ephemeral host app, if an editable host app does not already exist. if (!_editableHostAppDirectory.existsSync()) { - _overwriteFromTemplate(globals.fs.path.join('module', 'android', 'host_app_common'), ephemeralDirectory); - _overwriteFromTemplate(globals.fs.path.join('module', 'android', 'host_app_ephemeral'), ephemeralDirectory); + await _overwriteFromTemplate(globals.fs.path.join('module', 'android', 'host_app_common'), ephemeralDirectory); + await _overwriteFromTemplate(globals.fs.path.join('module', 'android', 'host_app_ephemeral'), ephemeralDirectory); } } if (!hostAppGradleRoot.existsSync()) { @@ -704,10 +704,10 @@ class AndroidProject extends FlutterProjectPlatform { if (_editableHostAppDirectory.existsSync()) { throwToolExit('Android host app is already editable. To start fresh, delete the android/ folder.'); } - _regenerateLibrary(); - _overwriteFromTemplate(globals.fs.path.join('module', 'android', 'host_app_common'), _editableHostAppDirectory); - _overwriteFromTemplate(globals.fs.path.join('module', 'android', 'host_app_editable'), _editableHostAppDirectory); - _overwriteFromTemplate(globals.fs.path.join('module', 'android', 'gradle'), _editableHostAppDirectory); + await _regenerateLibrary(); + await _overwriteFromTemplate(globals.fs.path.join('module', 'android', 'host_app_common'), _editableHostAppDirectory); + await _overwriteFromTemplate(globals.fs.path.join('module', 'android', 'host_app_editable'), _editableHostAppDirectory); + await _overwriteFromTemplate(globals.fs.path.join('module', 'android', 'gradle'), _editableHostAppDirectory); gradle.gradleUtils.injectGradleWrapperIfNeeded(_editableHostAppDirectory); gradle.writeLocalProperties(_editableHostAppDirectory.childFile('local.properties')); await injectPlugins(parent); @@ -717,19 +717,19 @@ class AndroidProject extends FlutterProjectPlatform { Directory get pluginRegistrantHost => _flutterLibGradleRoot.childDirectory(isModule ? 'Flutter' : 'app'); - void _regenerateLibrary() { + Future _regenerateLibrary() async { _deleteIfExistsSync(ephemeralDirectory); - _overwriteFromTemplate(globals.fs.path.join( + await _overwriteFromTemplate(globals.fs.path.join( 'module', 'android', featureFlags.isAndroidEmbeddingV2Enabled ? 'library_new_embedding' : 'library', ), ephemeralDirectory); - _overwriteFromTemplate(globals.fs.path.join('module', 'android', 'gradle'), ephemeralDirectory); + await _overwriteFromTemplate(globals.fs.path.join('module', 'android', 'gradle'), ephemeralDirectory); gradle.gradleUtils.injectGradleWrapperIfNeeded(ephemeralDirectory); } - void _overwriteFromTemplate(String path, Directory target) { - final Template template = Template.fromName(path); + Future _overwriteFromTemplate(String path, Directory target) async { + final Template template = await Template.fromName(path, fileSystem: globals.fs); template.render( target, { diff --git a/packages/flutter_tools/lib/src/runner/flutter_command.dart b/packages/flutter_tools/lib/src/runner/flutter_command.dart index c417200e463eb..208a34ba8b703 100644 --- a/packages/flutter_tools/lib/src/runner/flutter_command.dart +++ b/packages/flutter_tools/lib/src/runner/flutter_command.dart @@ -803,7 +803,7 @@ abstract class FlutterCommand extends Command { // Validate the current package map only if we will not be running "pub get" later. if (parent?.name != 'pub' && !(_usesPubOption && boolArg('pub'))) { - final String error = PackageMap(PackageMap.globalPackagesPath).checkValid(); + final String error = PackageMap(PackageMap.globalPackagesPath, fileSystem: globals.fs).checkValid(); if (error != null) { throw ToolExit(error); } diff --git a/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart b/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart index ac37ea0571af6..b3e69714fd765 100644 --- a/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart +++ b/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart @@ -354,7 +354,7 @@ class FlutterCommandRunner extends CommandRunner { if (engineSourcePath == null && globalResults['local-engine'] != null) { try { - Uri engineUri = PackageMap(PackageMap.globalPackagesPath).map[kFlutterEnginePackageName]; + Uri engineUri = PackageMap(PackageMap.globalPackagesPath, fileSystem: globals.fs).map[kFlutterEnginePackageName]; // Skip if sky_engine is the self-contained one. if (engineUri != null && globals.fs.identicalSync(globals.fs.path.join(Cache.flutterRoot, 'bin', 'cache', 'pkg', kFlutterEnginePackageName, 'lib'), engineUri.path)) { engineUri = null; @@ -491,7 +491,7 @@ class FlutterCommandRunner extends CommandRunner { // Check that the flutter running is that same as the one referenced in the pubspec. if (globals.fs.isFileSync(kPackagesFileName)) { - final PackageMap packageMap = PackageMap(kPackagesFileName); + final PackageMap packageMap = PackageMap(kPackagesFileName, fileSystem: globals.fs); Uri flutterUri; try { flutterUri = packageMap.map['flutter']; diff --git a/packages/flutter_tools/lib/src/template.dart b/packages/flutter_tools/lib/src/template.dart index b9beecc8b3e2e..499c8aba18393 100644 --- a/packages/flutter_tools/lib/src/template.dart +++ b/packages/flutter_tools/lib/src/template.dart @@ -2,27 +2,35 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:meta/meta.dart'; + import 'base/common.dart'; import 'base/file_system.dart'; import 'cache.dart'; -import 'globals.dart' as globals; +import 'dart/package_map.dart'; +import 'dart/pub.dart'; +import 'globals.dart' as globals hide fs; /// Expands templates in a directory to a destination. All files that must -/// undergo template expansion should end with the '.tmpl' extension. All other +/// undergo template expansion should end with the '.tmpl' extension. All files +/// that should be replaced with the corresponding image from +/// flutter_template_images should end with the '.img.tmpl' extension. All other /// files are ignored. In case the contents of entire directories must be copied /// as is, the directory itself can end with '.tmpl' extension. Files within -/// such a directory may also contain the '.tmpl' extension and will be -/// considered for expansion. In case certain files need to be copied but -/// without template expansion (images, data files, etc.), the '.copy.tmpl' +/// such a directory may also contain the '.tmpl' or '.img.tmpl' extensions and +/// will be considered for expansion. In case certain files need to be copied +/// but without template expansion (data files, etc.), the '.copy.tmpl' /// extension may be used. /// /// Folders with platform/language-specific content must be named /// '-.tmpl'. /// -/// Files in the destination will contain none of the '.tmpl', '.copy.tmpl' -/// or '-.tmpl' extensions. +/// Files in the destination will contain none of the '.tmpl', '.copy.tmpl', +/// 'img.tmpl', or '-.tmpl' extensions. class Template { - Template(Directory templateSource, Directory baseDir) { + Template(Directory templateSource, Directory baseDir, this.imageSourceDir, { + @required FileSystem fileSystem, + }) : _fileSystem = fileSystem { _templateFilePaths = {}; if (!templateSource.existsSync()) { @@ -37,27 +45,31 @@ class Template { continue; } - final String relativePath = globals.fs.path.relative(entity.path, + 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 // itself is a template. - _templateFilePaths[relativePath] = globals.fs.path.absolute(entity.path); + _templateFilePaths[relativePath] = fileSystem.path.absolute(entity.path); } } } - factory Template.fromName(String name) { + static Future