blob: 43ea66477f0ef9944ade39f9d221a5ab62a1c843 [file] [log] [blame]
// 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 'dart:async';
import 'package:analyzer/dart/analysis/context_root.dart';
import 'package:analyzer/dart/analysis/declared_variables.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/file_system/physical_file_system.dart';
import 'package:analyzer/src/analysis_options/analysis_options_provider.dart';
import 'package:analyzer/src/context/builder.dart' show EmbedderYamlLocator;
import 'package:analyzer/src/context/packages.dart';
import 'package:analyzer/src/dart/analysis/analysis_options.dart';
import 'package:analyzer/src/dart/analysis/analysis_options_map.dart';
import 'package:analyzer/src/dart/analysis/byte_store.dart'
show ByteStore, MemoryByteStore;
import 'package:analyzer/src/dart/analysis/context_root.dart';
import 'package:analyzer/src/dart/analysis/driver.dart'
show
AnalysisDriver,
AnalysisDriverScheduler,
AnalysisDriverTestView,
OwnedFiles;
import 'package:analyzer/src/dart/analysis/driver_based_analysis_context.dart';
import 'package:analyzer/src/dart/analysis/file_content_cache.dart';
import 'package:analyzer/src/dart/analysis/library_context.dart';
import 'package:analyzer/src/dart/analysis/performance_logger.dart'
show PerformanceLog;
import 'package:analyzer/src/dart/analysis/unlinked_unit_store.dart';
import 'package:analyzer/src/dart/sdk/sdk.dart';
import 'package:analyzer/src/generated/sdk.dart' show DartSdk;
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/summary/package_bundle_reader.dart';
import 'package:analyzer/src/summary/summary_sdk.dart';
import 'package:analyzer/src/summary2/package_bundle_format.dart';
import 'package:analyzer/src/workspace/workspace.dart';
/// A utility class used to build an analysis context based on a context root.
class ContextBuilderImpl {
/// The resource provider used to access the file system.
final ResourceProvider resourceProvider;
/// Analysis options mappings shared by all contexts built by this builder.
final AnalysisOptionsMap _optionsMap = AnalysisOptionsMap();
/// Initialize a newly created context builder. If a [resourceProvider] is
/// given, then it will be used to access the file system, otherwise the
/// default resource provider will be used.
ContextBuilderImpl({ResourceProvider? resourceProvider})
: resourceProvider = resourceProvider ?? PhysicalResourceProvider.INSTANCE;
/// Return an analysis context corresponding to the given [contextRoot].
///
/// If a set of [declaredVariables] is provided, the values will be used to
/// map the variable names found in `fromEnvironment` invocations to the
/// constant value that will be returned. If none is given, then no variables
/// will be defined.
///
/// If a list of [librarySummaryPaths] is provided, then the summary files at
/// those paths will be used, when possible, when analyzing the libraries
/// contained in the summary files.
///
/// If an [sdkPath] is provided, and if it is a valid path to a directory
/// containing a valid SDK, then the SDK in the referenced directory will be
/// used when analyzing the code in the context.
///
/// If an [sdkSummaryPath] is provided, then that file will be used as the
/// summary file for the SDK.
DriverBasedAnalysisContext createContext({
ByteStore? byteStore,
required ContextRoot contextRoot,
bool definedOptionsFile = false,
DeclaredVariables? declaredVariables,
bool drainStreams = true,
bool enableIndex = false,
List<String>? librarySummaryPaths,
PerformanceLog? performanceLog,
bool retainDataForTesting = false,
AnalysisDriverScheduler? scheduler,
required String sdkPath,
String? sdkSummaryPath,
void Function({
required AnalysisOptionsImpl analysisOptions,
required DartSdk sdk,
})?
updateAnalysisOptions3,
FileContentCache? fileContentCache,
UnlinkedUnitStore? unlinkedUnitStore,
OwnedFiles? ownedFiles,
bool enableLintRuleTiming = false,
LinkedBundleProvider? linkedBundleProvider,
required bool withFineDependencies,
}) {
byteStore ??= MemoryByteStore();
performanceLog ??= PerformanceLog(null);
linkedBundleProvider ??= LinkedBundleProvider(
byteStore: byteStore,
withFineDependencies: withFineDependencies,
);
if (scheduler == null) {
scheduler = AnalysisDriverScheduler(performanceLog);
scheduler.start();
}
SummaryDataStore? summaryData;
if (librarySummaryPaths != null) {
summaryData = SummaryDataStore();
for (var summaryPath in librarySummaryPaths) {
var bytes = resourceProvider.getFile(summaryPath).readAsBytesSync();
var bundle = PackageBundleReader(bytes);
summaryData.addBundle(summaryPath, bundle);
}
}
var workspace = contextRoot.workspace;
var sdk = _createSdk(
workspace: workspace,
sdkPath: sdkPath,
sdkSummaryPath: sdkSummaryPath,
);
// TODO(scheglov): Ensure that "librarySummaryPaths" not null only
// when "sdkSummaryPath" is not null.
if (sdk is SummaryBasedDartSdk) {
summaryData?.addBundle(null, sdk.bundle);
}
var optionsFile = contextRoot.optionsFile;
var sourceFactory = workspace.createSourceFactory(sdk, summaryData);
AnalysisOptionsMap analysisOptionsMap;
// If there's an options file defined (as, e.g. passed into the
// AnalysisContextCollection), use a shared options map based on it.
if (definedOptionsFile && optionsFile != null) {
analysisOptionsMap = AnalysisOptionsMap.forSharedOptions(
_getAnalysisOptions(
contextRoot,
optionsFile,
sourceFactory,
sdk,
updateAnalysisOptions3,
),
);
} else {
// Otherwise, create one from the options file mappings stored in the
// context root.
analysisOptionsMap = _createOptionsMap(
contextRoot,
sourceFactory,
updateAnalysisOptions3,
sdk,
);
}
var analysisContext = DriverBasedAnalysisContext(
resourceProvider,
contextRoot,
);
var driver = AnalysisDriver(
scheduler: scheduler,
logger: performanceLog,
resourceProvider: resourceProvider,
byteStore: byteStore,
linkedBundleProvider: linkedBundleProvider,
sourceFactory: sourceFactory,
analysisOptionsMap: analysisOptionsMap,
packages: _createPackageMap(contextRoot: contextRoot),
analysisContext: analysisContext,
enableIndex: enableIndex,
externalSummaries: summaryData,
retainDataForTesting: retainDataForTesting,
fileContentCache: fileContentCache,
unlinkedUnitStore: unlinkedUnitStore,
declaredVariables: declaredVariables,
testView: retainDataForTesting ? AnalysisDriverTestView() : null,
ownedFiles: ownedFiles,
enableLintRuleTiming: enableLintRuleTiming,
withFineDependencies: withFineDependencies,
);
// AnalysisDriver reports results into streams.
// We need to drain these streams to avoid memory leak.
if (drainStreams) {
unawaited(driver.exceptions.drain<void>());
}
return analysisContext;
}
/// Create an [AnalysisOptionsMap] for the given [contextRoot].
AnalysisOptionsMap _createOptionsMap(
ContextRoot contextRoot,
SourceFactory sourceFactory,
void Function({
required AnalysisOptionsImpl analysisOptions,
required DartSdk sdk,
})?
updateAnalysisOptions,
DartSdk sdk,
) {
var provider = AnalysisOptionsProvider(sourceFactory);
void updateOptions(AnalysisOptionsImpl options) {
if (updateAnalysisOptions != null) {
updateAnalysisOptions(analysisOptions: options, sdk: sdk);
}
}
var optionsMappings =
(contextRoot as ContextRootImpl).optionsFileMap.entries;
for (var entry in optionsMappings) {
var file = entry.value;
var options = AnalysisOptionsImpl.fromYaml(
optionsMap: provider.getOptionsFromFile(file),
file: file,
resourceProvider: resourceProvider,
);
_optionsMap.add(entry.key, options);
}
_optionsMap.forEachOptionsObject(updateOptions);
return _optionsMap;
}
/// Return [Packages] to analyze the [contextRoot].
///
// TODO(scheglov): Get [Packages] from [Workspace]?
Packages _createPackageMap({required ContextRoot contextRoot}) {
var packagesFile = contextRoot.packagesFile;
if (packagesFile != null) {
return parsePackageConfigJsonFile(resourceProvider, packagesFile);
} else {
return Packages.empty;
}
}
/// Return the SDK that should be used to analyze code.
DartSdk _createSdk({
required Workspace workspace,
String? sdkPath,
String? sdkSummaryPath,
}) {
if (sdkSummaryPath != null) {
var file = resourceProvider.getFile(sdkSummaryPath);
var bytes = file.readAsBytesSync();
return SummaryBasedDartSdk.forBundle(PackageBundleReader(bytes));
}
var folderSdk = FolderBasedDartSdk(
resourceProvider,
resourceProvider.getFolder(sdkPath!),
);
{
// TODO(scheglov): We already had partial SourceFactory in ContextLocatorImpl.
var partialSourceFactory = workspace.createSourceFactory(null, null);
var embedderYamlSource = partialSourceFactory.forUri(
'package:sky_engine/_embedder.yaml',
);
if (embedderYamlSource != null) {
var embedderYamlPath = embedderYamlSource.fullName;
var libFolder = resourceProvider.getFile(embedderYamlPath).parent;
var locator = EmbedderYamlLocator.forLibFolder(libFolder);
var embedderMap = locator.embedderYamls;
if (embedderMap.isNotEmpty) {
return EmbedderSdk(
resourceProvider,
embedderMap,
languageVersion: folderSdk.languageVersion,
);
}
}
}
return folderSdk;
}
/// Return the analysis options that should be used to analyze code in the
/// [contextRoot].
///
// TODO(scheglov): We have already loaded it once in [ContextLocatorImpl].
AnalysisOptionsImpl _getAnalysisOptions(
ContextRoot contextRoot,
File? optionsFile,
SourceFactory sourceFactory,
DartSdk sdk,
void Function({
required AnalysisOptionsImpl analysisOptions,
required DartSdk sdk,
})?
updateAnalysisOptions,
) {
AnalysisOptionsImpl? options;
if (optionsFile != null) {
try {
var provider = AnalysisOptionsProvider(sourceFactory);
options = AnalysisOptionsImpl.fromYaml(
optionsMap: provider.getOptionsFromFile(optionsFile),
file: optionsFile,
resourceProvider: resourceProvider,
);
} catch (e) {
// Ignore exception.
}
}
options ??= AnalysisOptionsImpl(file: optionsFile);
if (updateAnalysisOptions != null) {
updateAnalysisOptions(analysisOptions: options, sdk: sdk);
}
return options;
}
}