diff --git a/pkg/analyzer/lib/dart/analysis/context_builder.dart b/pkg/analyzer/lib/dart/analysis/context_builder.dart new file mode 100644 index 00000000000..c949c3c9fd1 --- /dev/null +++ b/pkg/analyzer/lib/dart/analysis/context_builder.dart @@ -0,0 +1,42 @@ +// 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/context/declared_variables.dart'; +import 'package:analyzer/dart/analysis/analysis_context.dart'; +import 'package:analyzer/dart/analysis/context_root.dart'; +import 'package:analyzer/file_system/file_system.dart'; +import 'package:analyzer/src/dart/analysis/context_builder.dart'; +import 'package:meta/meta.dart'; + +/** + * A utility class used to build an analysis context based on a context root. + * + * Clients may not extend, implement or mix-in this class. + */ +abstract class ContextBuilder { + /** + * 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. + */ + factory ContextBuilder({ResourceProvider resourceProvider}) = + ContextBuilderImpl; + + /** + * Return an analysis context corresponding to the given [contextRoot]. + * + * If a set of [declaredVariables] is provided, the values will be used to map + * the 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 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. + */ + AnalysisContext createContext( + {@required ContextRoot contextRoot, + DeclaredVariables declaredVariables, + String sdkPath}); +} diff --git a/pkg/analyzer/lib/src/dart/analysis/context_builder.dart b/pkg/analyzer/lib/src/dart/analysis/context_builder.dart new file mode 100644 index 00000000000..06115dbfe95 --- /dev/null +++ b/pkg/analyzer/lib/src/dart/analysis/context_builder.dart @@ -0,0 +1,101 @@ +// 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/context/context_root.dart' as old; +import 'package:analyzer/context/declared_variables.dart'; +import 'package:analyzer/dart/analysis/analysis_context.dart'; +import 'package:analyzer/dart/analysis/context_builder.dart'; +import 'package:analyzer/dart/analysis/context_root.dart'; +import 'package:analyzer/file_system/file_system.dart'; +import 'package:analyzer/file_system/physical_file_system.dart'; +import 'package:analyzer/src/context/builder.dart' as old + show ContextBuilder, ContextBuilderOptions; +import 'package:analyzer/src/dart/analysis/driver.dart' + show AnalysisDriver, AnalysisDriverScheduler; +import 'package:analyzer/src/dart/analysis/driver_based_analysis_context.dart'; +import 'package:analyzer/src/dart/analysis/file_state.dart' + show FileContentOverlay; +import 'package:analyzer/src/dart/sdk/sdk.dart'; +import 'package:analyzer/src/generated/sdk.dart' show DartSdkManager; +import 'package:analyzer/src/generated/source.dart' show ContentCache; +import 'package:front_end/src/base/performance_logger.dart' show PerformanceLog; +import 'package:front_end/src/byte_store/byte_store.dart' show MemoryByteStore; +import 'package:meta/meta.dart'; + +/** + * An implementation of a context builder. + */ +class ContextBuilderImpl implements ContextBuilder { + /** + * The resource provider used to access the file system. + */ + final ResourceProvider resourceProvider; + + /** + * 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 the path to the default location of the SDK, or `null` if the sdk + * cannot be found. + */ + String get _defaultSdkPath => + FolderBasedDartSdk.defaultSdkDirectory(resourceProvider)?.path; + + @override + AnalysisContext createContext( + {@required ContextRoot contextRoot, + DeclaredVariables declaredVariables, + String sdkPath}) { + PerformanceLog performanceLog = new PerformanceLog(new StringBuffer()); + AnalysisDriverScheduler scheduler = + new AnalysisDriverScheduler(performanceLog); + sdkPath ??= _defaultSdkPath; + if (sdkPath == null) { + throw new ArgumentError('Cannot find path to the SDK'); + } + DartSdkManager sdkManager = new DartSdkManager(sdkPath, true); + scheduler.start(); + + // TODO(brianwilkerson) Move the required implementation from the old + // ContextBuilder to this class and remove the old class. + old.ContextBuilderOptions options = new old.ContextBuilderOptions(); + if (declaredVariables != null) { + options.declaredVariables = _toMap(declaredVariables); + } + options.defaultPackageFilePath = contextRoot.packagesFile?.path; + + old.ContextBuilder builder = new old.ContextBuilder( + resourceProvider, sdkManager, new ContentCache(), + options: options); + builder.analysisDriverScheduler = scheduler; + builder.byteStore = new MemoryByteStore(); + builder.fileContentOverlay = new FileContentOverlay(); + builder.performanceLog = performanceLog; + + old.ContextRoot oldContextRoot = new old.ContextRoot( + contextRoot.root.path, contextRoot.excludedPaths.toList()); + AnalysisDriver driver = builder.buildDriver(oldContextRoot); + DriverBasedAnalysisContext context = + new DriverBasedAnalysisContext(resourceProvider, contextRoot, driver); + return context; + } + + /** + * Convert the [declaredVariables] into a map for use with the old context + * builder. + */ + Map _toMap(DeclaredVariables declaredVariables) { + Map map = {}; + for (String name in declaredVariables.variableNames) { + map[name] = declaredVariables.get(name); + } + return map; + } +} diff --git a/pkg/analyzer/test/src/dart/analysis/context_builder_test.dart b/pkg/analyzer/test/src/dart/analysis/context_builder_test.dart new file mode 100644 index 00000000000..d59025e859d --- /dev/null +++ b/pkg/analyzer/test/src/dart/analysis/context_builder_test.dart @@ -0,0 +1,91 @@ +// 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:io' as io; + +import 'package:analyzer/context/declared_variables.dart'; +import 'package:analyzer/dart/analysis/analysis_context.dart'; +import 'package:analyzer/dart/analysis/context_root.dart'; +import 'package:analyzer/file_system/file_system.dart'; +import 'package:analyzer/src/dart/analysis/context_builder.dart'; +import 'package:analyzer/src/dart/analysis/context_root.dart'; +import 'package:analyzer/src/dart/analysis/driver_based_analysis_context.dart'; +import 'package:analyzer/src/test_utilities/resource_provider_mixin.dart'; +import 'package:test/test.dart'; +import 'package:test_reflective_loader/test_reflective_loader.dart'; + +import '../../context/mock_sdk.dart'; + +main() { + defineReflectiveSuite(() { + defineReflectiveTests(ContextBuilderImplTest); + }); +} + +@reflectiveTest +class ContextBuilderImplTest extends Object with ResourceProviderMixin { + ContextBuilderImpl contextBuilder; + ContextRoot contextRoot; + + void assertEquals(DeclaredVariables actual, DeclaredVariables expected) { + Iterable actualNames = actual.variableNames; + Iterable expectedNames = expected.variableNames; + expect(actualNames, expectedNames); + for (String name in expectedNames) { + expect(actual.get(name), expected.get(name)); + } + } + + void setUp() { + resourceProvider.newFolder(resourceProvider.pathContext.dirname( + resourceProvider.pathContext.dirname(io.Platform.resolvedExecutable))); + contextBuilder = new ContextBuilderImpl(resourceProvider: resourceProvider); + String path = resourceProvider.convertPath('/temp/root'); + Folder folder = resourceProvider.newFolder(path); + contextRoot = new ContextRootImpl(resourceProvider, folder); + } + + test_createContext_declaredVariables() { + DeclaredVariables declaredVariables = new DeclaredVariables(); + declaredVariables.define('foo', 'true'); + DriverBasedAnalysisContext context = contextBuilder.createContext( + contextRoot: contextRoot, declaredVariables: declaredVariables); + expect(context.analysisOptions, isNotNull); + expect(context.contextRoot, contextRoot); + assertEquals(context.driver.declaredVariables, declaredVariables); + } + + test_createContext_declaredVariables_sdkPath() { + DeclaredVariables declaredVariables = new DeclaredVariables(); + declaredVariables.define('bar', 'true'); + MockSdk sdk = new MockSdk(resourceProvider: resourceProvider); + DriverBasedAnalysisContext context = contextBuilder.createContext( + contextRoot: contextRoot, + declaredVariables: declaredVariables, + sdkPath: sdkRoot); + expect(context.analysisOptions, isNotNull); + expect(context.contextRoot, contextRoot); + assertEquals(context.driver.declaredVariables, declaredVariables); + expect(context.driver.sourceFactory.dartSdk.mapDartUri('dart:core'), + sdk.mapDartUri('dart:core')); + } + + test_createContext_defaults() { + AnalysisContext context = + contextBuilder.createContext(contextRoot: contextRoot); + expect(context.analysisOptions, isNotNull); + expect(context.contextRoot, contextRoot); + } + + test_createContext_sdkPath() { + String sdkPath = resourceProvider.convertPath('/path/to/sdk'); + resourceProvider.newFolder(sdkPath); + AnalysisContext context = contextBuilder.createContext( + contextRoot: contextRoot, sdkPath: sdkPath); + expect(context.analysisOptions, isNotNull); + expect(context.contextRoot, contextRoot); + // TODO(brianwilkerson) We don't currently have a way to test whether the + // sdkPath is being handled correctly. + } +} diff --git a/pkg/analyzer/test/src/dart/analysis/test_all.dart b/pkg/analyzer/test/src/dart/analysis/test_all.dart index 527afbf1816..f452edef2ce 100644 --- a/pkg/analyzer/test/src/dart/analysis/test_all.dart +++ b/pkg/analyzer/test/src/dart/analysis/test_all.dart @@ -4,6 +4,7 @@ import 'package:test_reflective_loader/test_reflective_loader.dart'; +import 'context_builder_test.dart' as context_builder_test; import 'context_locator_test.dart' as context_locator_test; import 'context_root_test.dart' as context_root_test; import 'defined_names_test.dart' as defined_names_test; @@ -21,6 +22,7 @@ import 'session_test.dart' as session_test; main() { defineReflectiveSuite(() { + context_builder_test.main(); context_locator_test.main(); context_root_test.main(); defined_names_test.main();