diff --git a/build_resolvers/lib/src/build_asset_uri_resolver.dart b/build_resolvers/lib/src/build_asset_uri_resolver.dart index b351a0c28..80c3d489b 100644 --- a/build_resolvers/lib/src/build_asset_uri_resolver.dart +++ b/build_resolvers/lib/src/build_asset_uri_resolver.dart @@ -4,6 +4,7 @@ import 'dart:async'; import 'dart:collection'; +import 'dart:isolate'; import 'package:analyzer/dart/analysis/utilities.dart'; import 'package:analyzer/dart/ast/ast.dart'; @@ -218,6 +219,11 @@ class BuildAssetUriResolver extends UriResolver { String assetPath(AssetId assetId) => p.posix.join('/${assetId.package}', assetId.path); +Future packagePath(String package) async { + var libRoot = await Isolate.resolvePackageUri(Uri.parse('package:$package/')); + return p.dirname(p.fromUri(libRoot)); +} + /// Returns all the directives from a Dart library that can be resolved to an /// [AssetId]. Set _parseDirectives(String content, AssetId from) => HashSet.of( diff --git a/build_resolvers/lib/src/resolver.dart b/build_resolvers/lib/src/resolver.dart index 75572a8bf..813114389 100644 --- a/build_resolvers/lib/src/resolver.dart +++ b/build_resolvers/lib/src/resolver.dart @@ -12,16 +12,13 @@ import 'package:analyzer/dart/analysis/features.dart'; import 'package:analyzer/dart/analysis/results.dart'; import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/dart/element/element.dart'; -import 'package:analyzer/dart/sdk/build_sdk_summary.dart'; import 'package:analyzer/error/error.dart'; -import 'package:analyzer/file_system/physical_file_system.dart'; // ignore: implementation_imports import 'package:analyzer/src/clients/build_resolvers/build_resolvers.dart'; import 'package:async/async.dart'; import 'package:build/build.dart'; import 'package:build/experiments.dart'; import 'package:collection/collection.dart' show IterableExtension; -import 'package:logging/logging.dart'; import 'package:package_config/package_config.dart'; import 'package:path/path.dart' as p; import 'package:pool/pool.dart'; @@ -29,14 +26,7 @@ import 'package:yaml/yaml.dart'; import 'analysis_driver.dart'; import 'build_asset_uri_resolver.dart'; -import 'human_readable_duration.dart'; - -final _logger = Logger('build_resolvers'); - -Future _packagePath(String package) async { - var libRoot = await Isolate.resolvePackageUri(Uri.parse('package:$package/')); - return p.dirname(p.fromUri(libRoot)); -} +import 'sdk_summary.dart'; /// Implements [Resolver.libraries] and [Resolver.findLibraryByName] by crawling /// down from entrypoints. @@ -365,7 +355,7 @@ class AnalyzerResolvers implements Resolvers { /// A function that returns the path to the SDK summary when invoked. /// - /// Defaults to [_defaultSdkSummaryGenerator]. + /// Defaults to [defaultSdkSummaryGenerator]. final Future Function() _sdkSummaryGenerator; // Lazy, all access must be preceded by a call to `_ensureInitialized`. @@ -400,7 +390,7 @@ class AnalyzerResolvers implements Resolvers { ..contextFeatures = _featureSet(enableExperiments: enabledExperiments)), _sdkSummaryGenerator = - sdkSummaryGenerator ?? _defaultSdkSummaryGenerator; + sdkSummaryGenerator ?? defaultSdkSummaryGenerator; /// Create a Resolvers backed by an `AnalysisContext` using options /// [_analysisOptions]. @@ -430,89 +420,6 @@ class AnalyzerResolvers implements Resolvers { } } -/// Lazily creates a summary of the users SDK and caches it under -/// `.dart_tool/build_resolvers`. -/// -/// This is only intended for use in typical dart packages, which must -/// have an already existing `.dart_tool` directory (this is how we -/// validate we are running under a typical dart package and not a custom -/// environment). -Future _defaultSdkSummaryGenerator() async { - var dartToolPath = '.dart_tool'; - if (!await Directory(dartToolPath).exists()) { - throw StateError( - 'The default analyzer resolver can only be used when the current ' - 'working directory is a standard pub package.'); - } - - var cacheDir = p.join(dartToolPath, 'build_resolvers'); - var summaryPath = p.join(cacheDir, 'sdk.sum'); - var depsFile = File('$summaryPath.deps'); - var summaryFile = File(summaryPath); - - var currentDeps = { - 'sdk': Platform.version, - for (var package in _packageDepsToCheck) - package: await _packagePath(package), - }; - - // Invalidate existing summary/version/analyzer files if present. - if (await depsFile.exists()) { - if (!await _checkDeps(depsFile, currentDeps)) { - await depsFile.delete(); - if (await summaryFile.exists()) await summaryFile.delete(); - } - } else if (await summaryFile.exists()) { - // Fallback for cases where we could not do a proper version check. - await summaryFile.delete(); - } - - // Generate the summary and version files if necessary. - if (!await summaryFile.exists()) { - var watch = Stopwatch()..start(); - _logger.info('Generating SDK summary...'); - await summaryFile.create(recursive: true); - final embedderYamlPath = - isFlutter ? p.join(_dartUiPath, '_embedder.yaml') : null; - await summaryFile.writeAsBytes( - await buildSdkSummary( - sdkPath: _runningDartSdkPath, - resourceProvider: PhysicalResourceProvider.INSTANCE, - embedderYamlPath: embedderYamlPath, - ), - ); - - await _createDepsFile(depsFile, currentDeps); - watch.stop(); - _logger.info('Generating SDK summary completed, took ' - '${humanReadable(watch.elapsed)}\n'); - } - - return p.absolute(summaryPath); -} - -final _packageDepsToCheck = ['analyzer', 'build_resolvers']; - -Future _checkDeps( - File versionsFile, Map currentDeps) async { - var previous = - jsonDecode(await versionsFile.readAsString()) as Map; - - if (previous.keys.length != currentDeps.keys.length) return false; - - for (var entry in previous.entries) { - if (entry.value != currentDeps[entry.key]) return false; - } - - return true; -} - -Future _createDepsFile( - File depsFile, Map currentDeps) async { - await depsFile.create(recursive: true); - await depsFile.writeAsString(jsonEncode(currentDeps)); -} - /// Checks that the current analyzer version supports the current language /// version. void _warnOnLanguageVersionMismatch() async { @@ -528,7 +435,7 @@ void _warnOnLanguageVersionMismatch() async { var json = jsonDecode(content.toString()); var latestAnalyzer = json['latest']['version']; var analyzerPubspecPath = - p.join(await _packagePath('analyzer'), 'pubspec.yaml'); + p.join(await packagePath('analyzer'), 'pubspec.yaml'); var currentAnalyzer = loadYaml(await File(analyzerPubspecPath).readAsString())['version']; @@ -580,11 +487,6 @@ https://pub.dev/packages/analyzer. } } -/// Path where the dart:ui package will be found, if executing via the dart -/// binary provided by the Flutter SDK. -final _dartUiPath = - p.normalize(p.join(_runningDartSdkPath, '..', 'pkg', 'sky_engine', 'lib')); - /// The current feature set based on the current sdk version and enabled /// experiments. FeatureSet _featureSet({List enableExperiments = const []}) { @@ -609,10 +511,3 @@ current version by running `pub deps`. return FeatureSet.fromEnableFlags2( sdkLanguageVersion: sdkLanguageVersion, flags: enableExperiments); } - -/// Path to the running dart's SDK root. -final _runningDartSdkPath = p.dirname(p.dirname(Platform.resolvedExecutable)); - -/// `true` if the currently running dart was provided by the Flutter SDK. -final isFlutter = - Platform.version.contains('flutter') || Directory(_dartUiPath).existsSync(); diff --git a/build_resolvers/lib/src/sdk_summary.dart b/build_resolvers/lib/src/sdk_summary.dart new file mode 100644 index 000000000..223761471 --- /dev/null +++ b/build_resolvers/lib/src/sdk_summary.dart @@ -0,0 +1,111 @@ +// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file +// for details. 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:convert'; +import 'dart:io'; + +import 'package:analyzer/dart/sdk/build_sdk_summary.dart'; +import 'package:analyzer/file_system/physical_file_system.dart'; +import 'package:logging/logging.dart'; +import 'package:path/path.dart' as p; + +import 'build_asset_uri_resolver.dart' show packagePath; +import 'human_readable_duration.dart'; + +final _logger = Logger('build_resolvers'); + +/// `true` if the currently running dart was provided by the Flutter SDK. +final isFlutter = + Platform.version.contains('flutter') || Directory(_dartUiPath).existsSync(); + +/// Path to the running dart's SDK root. +final _runningDartSdkPath = p.dirname(p.dirname(Platform.resolvedExecutable)); + +/// Path where the dart:ui package will be found, if executing via the dart +/// binary provided by the Flutter SDK. +final _dartUiPath = + p.normalize(p.join(_runningDartSdkPath, '..', 'pkg', 'sky_engine', 'lib')); + +/// Lazily creates a summary of the users SDK and caches it under +/// `.dart_tool/build_resolvers`. +/// +/// This is only intended for use in typical dart packages, which must +/// have an already existing `.dart_tool` directory (this is how we +/// validate we are running under a typical dart package and not a custom +/// environment). +Future defaultSdkSummaryGenerator() async { + var dartToolPath = '.dart_tool'; + if (!await Directory(dartToolPath).exists()) { + throw StateError( + 'The default analyzer resolver can only be used when the current ' + 'working directory is a standard pub package.'); + } + + var cacheDir = p.join(dartToolPath, 'build_resolvers'); + var summaryPath = p.join(cacheDir, 'sdk.sum'); + var depsFile = File('$summaryPath.deps'); + var summaryFile = File(summaryPath); + + var currentDeps = { + 'sdk': Platform.version, + for (var package in _packageDepsToCheck) + package: await packagePath(package), + }; + + // Invalidate existing summary/version/analyzer files if present. + if (await depsFile.exists()) { + if (!await _checkDeps(depsFile, currentDeps)) { + await depsFile.delete(); + if (await summaryFile.exists()) await summaryFile.delete(); + } + } else if (await summaryFile.exists()) { + // Fallback for cases where we could not do a proper version check. + await summaryFile.delete(); + } + + // Generate the summary and version files if necessary. + if (!await summaryFile.exists()) { + var watch = Stopwatch()..start(); + _logger.info('Generating SDK summary...'); + await summaryFile.create(recursive: true); + final embedderYamlPath = + isFlutter ? p.join(_dartUiPath, '_embedder.yaml') : null; + await summaryFile.writeAsBytes( + await buildSdkSummary( + sdkPath: _runningDartSdkPath, + resourceProvider: PhysicalResourceProvider.INSTANCE, + embedderYamlPath: embedderYamlPath, + ), + ); + + await _createDepsFile(depsFile, currentDeps); + watch.stop(); + _logger.info('Generating SDK summary completed, took ' + '${humanReadable(watch.elapsed)}\n'); + } + + return p.absolute(summaryPath); +} + +final _packageDepsToCheck = ['analyzer', 'build_resolvers']; + +Future _checkDeps( + File versionsFile, Map currentDeps) async { + var previous = + jsonDecode(await versionsFile.readAsString()) as Map; + + if (previous.keys.length != currentDeps.keys.length) return false; + + for (var entry in previous.entries) { + if (entry.value != currentDeps[entry.key]) return false; + } + + return true; +} + +Future _createDepsFile( + File depsFile, Map currentDeps) async { + await depsFile.create(recursive: true); + await depsFile.writeAsString(jsonEncode(currentDeps)); +} diff --git a/build_resolvers/test/resolver_test.dart b/build_resolvers/test/resolver_test.dart index 500b3be6d..837d5d184 100644 --- a/build_resolvers/test/resolver_test.dart +++ b/build_resolvers/test/resolver_test.dart @@ -13,6 +13,7 @@ import 'package:build/build.dart'; import 'package:build/experiments.dart'; import 'package:build_resolvers/src/analysis_driver.dart'; import 'package:build_resolvers/src/resolver.dart'; +import 'package:build_resolvers/src/sdk_summary.dart'; import 'package:build_test/build_test.dart'; import 'package:logging/logging.dart'; import 'package:package_config/package_config.dart';