4b04ff2270
Change-Id: I032a004497c0add825b24d4f989d8f5851d157d0 Reviewed-on: https://dart-review.googlesource.com/c/88164 Reviewed-by: Konstantin Shcheglov <scheglov@google.com> Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
120 lines
5.0 KiB
Dart
120 lines
5.0 KiB
Dart
// Copyright (c) 2018, 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 'package:analyzer/file_system/file_system.dart';
|
|
import 'package:analyzer/src/context/builder.dart';
|
|
import 'package:analyzer/src/generated/engine.dart';
|
|
import 'package:analyzer_cli/src/options.dart';
|
|
import 'package:path/path.dart' as path;
|
|
|
|
/// Cache of [AnalysisOptionsImpl] objects that correspond to directories
|
|
/// with analyzed files, used to reduce searching for `analysis_options.yaml`
|
|
/// files.
|
|
class ContextCache {
|
|
final ResourceProvider resourceProvider;
|
|
final CommandLineOptions clOptions;
|
|
final void Function(String) verbosePrint;
|
|
|
|
/// A mapping from normalized paths (currently, directories) to cache entries
|
|
/// which include builder, analysis_options paths, and [AnalysisOptionImpl]s.
|
|
final Map<String, ContextCacheEntry> _byDirectory = {};
|
|
|
|
ContextCache(this.resourceProvider, this.clOptions, this.verbosePrint);
|
|
|
|
/// Look up info about a context from the cache. You can pass in any [path],
|
|
/// and it will try to provide an existing [ContextCacheEntry] that is
|
|
/// suitable for that [path] if one exists.
|
|
ContextCacheEntry forSource(String path) {
|
|
path = _normalizeSourcePath(path);
|
|
return _byDirectory.putIfAbsent(path, () {
|
|
final builder = new ContextBuilder(resourceProvider, null, null,
|
|
options: clOptions.contextBuilderOptions);
|
|
return new ContextCacheEntry(builder, path, clOptions, verbosePrint);
|
|
});
|
|
}
|
|
|
|
/// Cheaply normalize source paths so we can increase cache performance.
|
|
/// Getting the location of an analysis_options.yaml file for a given source
|
|
/// can be expensive, so we want to reduce extra lookups where possible. We
|
|
/// know that two files in the same directory share an analysis options file,
|
|
/// so that's the normalization we perform currently. However, this could be
|
|
/// any form of performance-increasing cache key normalization.
|
|
String _normalizeSourcePath(String sourcePath) {
|
|
if (!resourceProvider.pathContext.isAbsolute(sourcePath)) {
|
|
// TODO(mfairhurst) Use resourceProvider.pathContext.absolute(). For the
|
|
// moment, we get an issues where pathContext.current is `.`, which causes
|
|
// pathContext.absolute() to produce `./foo` instead of `/absolute/foo`.
|
|
sourcePath = path.absolute(sourcePath);
|
|
}
|
|
|
|
sourcePath = resourceProvider.pathContext.normalize(sourcePath);
|
|
|
|
// Prepare the directory which is, or contains, the context root.
|
|
if (resourceProvider.getFolder(sourcePath).exists) {
|
|
return sourcePath;
|
|
}
|
|
|
|
return resourceProvider.pathContext.dirname(sourcePath);
|
|
}
|
|
}
|
|
|
|
/// Each entry of the [ContextCache] caches three things: the [ContextBuilder],
|
|
/// the analysis_options.yaml path of the context, and the [AnalysisOptionsImpl]
|
|
/// of the context.
|
|
class ContextCacheEntry {
|
|
final CommandLineOptions clOptions;
|
|
final ContextBuilder builder;
|
|
final String requestedSourceDirectory;
|
|
final void Function(String) verbosePrint;
|
|
|
|
AnalysisOptionsImpl _analysisOptions;
|
|
String _analysisRoot;
|
|
|
|
ContextCacheEntry(this.builder, this.requestedSourceDirectory, this.clOptions,
|
|
this.verbosePrint);
|
|
|
|
/// Get the fully parsed [AnalysisOptionsImpl] for this entry.
|
|
AnalysisOptionsImpl get analysisOptions =>
|
|
_analysisOptions ??= _getAnalysisOptions();
|
|
|
|
/// Find the root path from which excludes should be considered due to where
|
|
/// the analysis_options.yaml was defined.
|
|
String get analysisRoot => _analysisRoot ??= _getAnalysisRoot();
|
|
|
|
/// The actual calculation to get the [AnalysisOptionsImpl], with no caching.
|
|
/// This should not be used except behind the getter which caches this result
|
|
/// automatically.
|
|
AnalysisOptionsImpl _getAnalysisOptions() {
|
|
AnalysisOptionsImpl contextOptions = builder.getAnalysisOptions(
|
|
requestedSourceDirectory,
|
|
verbosePrint: clOptions.verbose ? verbosePrint : null)
|
|
as AnalysisOptionsImpl;
|
|
|
|
contextOptions.trackCacheDependencies = false;
|
|
contextOptions.disableCacheFlushing = clOptions.disableCacheFlushing;
|
|
contextOptions.enabledExperiments = clOptions.enabledExperiments;
|
|
contextOptions.hint = !clOptions.disableHints;
|
|
contextOptions.generateImplicitErrors = clOptions.showPackageWarnings;
|
|
contextOptions.generateSdkErrors = clOptions.showSdkWarnings;
|
|
contextOptions.useFastaParser = clOptions.useFastaParser;
|
|
return contextOptions;
|
|
}
|
|
|
|
/// The actual calculation to get the analysis root, with no caching. This
|
|
/// should not be used except behind the getter which caches this result
|
|
/// automatically.
|
|
String _getAnalysisRoot() {
|
|
// The analysis yaml defines the root, if it exists.
|
|
var analysisOptionsPath = builder
|
|
.getOptionsFile(requestedSourceDirectory, forceSearch: true)
|
|
?.path;
|
|
|
|
if (analysisOptionsPath == null) {
|
|
return requestedSourceDirectory;
|
|
}
|
|
|
|
return path.dirname(analysisOptionsPath);
|
|
}
|
|
}
|