| // Copyright (c) 2016, 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 'dart:collection'; | |
| import 'package:analyzer/file_system/file_system.dart'; | |
| import 'package:analyzer/source/file_source.dart'; | |
| import 'package:analyzer/source/source.dart'; | |
| import 'package:analyzer/src/context/packages.dart'; | |
| import 'package:analyzer/src/file_system/file_system.dart'; | |
| import 'package:analyzer/src/generated/sdk.dart'; | |
| import 'package:analyzer/src/generated/source.dart'; | |
| import 'package:analyzer/src/summary/package_bundle_reader.dart'; | |
| import 'package:analyzer/src/util/file_paths.dart' as file_paths; | |
| import 'package:analyzer/src/util/uri.dart'; | |
| import 'package:analyzer/src/utilities/uri_cache.dart'; | |
| import 'package:analyzer/src/workspace/blaze_watcher.dart'; | |
| import 'package:analyzer/src/workspace/workspace.dart'; | |
| import 'package:path/path.dart' as path; | |
| import 'package:pub_semver/pub_semver.dart'; | |
| /// Instances of the class `BlazeFileUriResolver` resolve `file` URI's by first | |
| /// resolving file uri's in the expected way, and then by looking in the | |
| /// corresponding generated directories. | |
| class BlazeFileUriResolver extends ResourceUriResolver { | |
| final BlazeWorkspace workspace; | |
| BlazeFileUriResolver(this.workspace) : super(workspace.provider); | |
| @override | |
| Uri pathToUri(String path) { | |
| var pathContext = workspace.provider.pathContext; | |
| for (var genRoot in [...workspace.binPaths, workspace.genfiles]) { | |
| if (pathContext.isWithin(genRoot, path)) { | |
| String relative = pathContext.relative(path, from: genRoot); | |
| var writablePath = pathContext.join(workspace.root, relative); | |
| return pathContext.toUri(writablePath); | |
| } | |
| } | |
| return workspace.provider.pathContext.toUri(path); | |
| } | |
| @override | |
| Source? resolveAbsolute(Uri uri) { | |
| if (!ResourceUriResolver.isFileUri(uri)) { | |
| return null; | |
| } | |
| String filePath = fileUriToNormalizedPath(provider.pathContext, uri); | |
| var file = workspace.findFile(filePath); | |
| if (file != null) { | |
| return FileSource(file, uri); | |
| } | |
| return null; | |
| } | |
| } | |
| /// The [UriResolver] that can resolve `package` URIs in [BlazeWorkspace]. | |
| class BlazePackageUriResolver extends UriResolver { | |
| final BlazeWorkspace _workspace; | |
| final path.Context _context; | |
| /// The cache of absolute [Uri]s to [Source]s mappings. | |
| final Map<Uri, Source?> _sourceCache = HashMap(); | |
| BlazePackageUriResolver(BlazeWorkspace workspace) | |
| : _workspace = workspace, | |
| _context = workspace.provider.pathContext; | |
| @override | |
| Uri? pathToUri(String path) { | |
| // Search in each root. | |
| for (var root in [ | |
| ..._workspace.binPaths, | |
| _workspace.genfiles, | |
| _workspace.root, | |
| ]) { | |
| var uriParts = _restoreUriParts(root, path); | |
| if (uriParts != null) { | |
| return uriCache.parse('package:${uriParts[0]}/${uriParts[1]}'); | |
| } | |
| } | |
| return null; | |
| } | |
| @override | |
| Source? resolveAbsolute(Uri uri) { | |
| if (_sourceCache.containsKey(uri)) { | |
| return _sourceCache[uri]; | |
| } | |
| Source? source; | |
| var fullFilePath = resolveAbsolutePath(uri); | |
| if (fullFilePath != null) { | |
| var file = _workspace.findFile(fullFilePath); | |
| if (file != null) { | |
| return FileSource(file, uri); | |
| } | |
| } | |
| _sourceCache[uri] = source; | |
| return source; | |
| } | |
| String? resolveAbsolutePath(Uri uri) { | |
| if (uri.isScheme('file')) { | |
| var path = fileUriToNormalizedPath(_context, uri); | |
| var pathRelativeToRoot = _workspace._relativeToRoot(path); | |
| if (pathRelativeToRoot == null) return null; | |
| return _context.join(_workspace.root, pathRelativeToRoot); | |
| } | |
| if (!uri.isScheme('package')) { | |
| return null; | |
| } | |
| String uriPath = Uri.decodeComponent(uri.path); | |
| int slash = uriPath.indexOf('/'); | |
| // If the path either starts with a slash or has no slash, it is invalid. | |
| if (slash < 1) { | |
| return null; | |
| } | |
| if (uriPath.contains('//') || uriPath.contains('..')) { | |
| return null; | |
| } | |
| String packageName = uriPath.substring(0, slash); | |
| String fileUriPart = uriPath.substring(slash + 1); | |
| if (fileUriPart.isEmpty) { | |
| return null; | |
| } | |
| String filePath = fileUriPart.replaceAll('/', _context.separator); | |
| if (!packageName.contains('.')) { | |
| return _context.join( | |
| _workspace.root, | |
| 'third_party', | |
| 'dart', | |
| packageName, | |
| 'lib', | |
| filePath, | |
| ); | |
| } else { | |
| String packagePath = packageName.replaceAll('.', _context.separator); | |
| return _context.join(_workspace.root, packagePath, 'lib', filePath); | |
| } | |
| } | |
| /// Restore [filePath] to its 'package:' URI parts. | |
| /// | |
| /// Returns `null` if [root] is null or if [filePath] is not within [root]. | |
| List<String>? _restoreUriParts(String? root, String filePath) { | |
| path.Context context = _workspace.provider.pathContext; | |
| if (root != null && context.isWithin(root, filePath)) { | |
| String relative = context.relative(filePath, from: root); | |
| List<String> components = context.split(relative); | |
| if (components.length > 4 && | |
| components[0] == 'third_party' && | |
| components[1] == 'dart' && | |
| components[3] == 'lib') { | |
| String packageName = components[2]; | |
| String pathInLib = components.skip(4).join('/'); | |
| return [packageName, pathInLib]; | |
| } else { | |
| for (int i = components.length - 2; i >= 2; i--) { | |
| String component = components[i]; | |
| if (component == 'lib') { | |
| String packageName = components.getRange(0, i).join('.'); | |
| String pathInLib = components.skip(i + 1).join('/'); | |
| return [packageName, pathInLib]; | |
| } | |
| } | |
| } | |
| } | |
| return null; | |
| } | |
| } | |
| /// Information about a Blaze workspace. | |
| class BlazeWorkspace extends Workspace | |
| implements WorkspaceWithDefaultAnalysisOptions { | |
| /// The name of the file that identifies a set of Blaze Targets. | |
| /// | |
| /// For Dart package purposes, a BUILD file identifies a package. | |
| static const String _buildFileName = 'BUILD'; | |
| final ResourceProvider provider; | |
| /// The absolute workspace root path. | |
| /// | |
| /// It contains the `WORKSPACE` file or its parent contains the `READONLY` | |
| /// folder. | |
| @override | |
| final String root; | |
| /// The absolute paths to all `blaze-bin` folders. | |
| /// | |
| /// In practice, there is usually one "bin" path, and sometimes there are two, | |
| /// on distributed build systems. It is very rare to have more than two. | |
| final List<String> binPaths; | |
| /// The absolute path to the `blaze-genfiles` folder. | |
| final String genfiles; | |
| /// Sometimes `BUILD` files are not preserved, and `xyz.packages` files | |
| /// are created instead. But looking for them is expensive, so we want | |
| /// to avoid this in cases when `BUILD` files are always available. | |
| final bool _lookForBuildFileSubstitutes; | |
| /// If `true`, the language version for packages will be provided. | |
| /// | |
| /// If `false`, [BlazeWorkspacePackage.languageVersion] will return `null`, | |
| /// so that [Packages] will be used to get the language versions. | |
| /// | |
| /// See https://buganizer.corp.google.com/issues/279909837 | |
| final bool _provideLanguageVersion; | |
| /// The language version for this workspace, `null` if cannot be read. | |
| final Version? _languageVersion; | |
| /// The cache of packages. The key is the directory path, the value is | |
| /// the corresponding package. | |
| final Map<String, BlazeWorkspacePackage> _directoryToPackage = {}; | |
| final _blazeCandidateFiles = StreamController<BlazeSearchInfo>.broadcast(); | |
| BlazeWorkspace._( | |
| this.provider, | |
| this.root, | |
| this.binPaths, | |
| this.genfiles, { | |
| required bool lookForBuildFileSubstitutes, | |
| required bool provideLanguageVersion, | |
| }) : _lookForBuildFileSubstitutes = lookForBuildFileSubstitutes, | |
| _provideLanguageVersion = provideLanguageVersion, | |
| _languageVersion = provideLanguageVersion | |
| ? _readLanguageVersion(provider, root) | |
| : null; | |
| /// Stream of files that we tried to find along with their potential or actual | |
| /// paths. | |
| Stream<BlazeSearchInfo> get blazeCandidateFiles => | |
| _blazeCandidateFiles.stream; | |
| @override | |
| bool get isBlaze => true; | |
| @override | |
| // TODO(srawlins): Implement this by looking in the BUILD file for 'deps' | |
| // lists (same TODO as in `BlazeWorkspacePackage.packagesAvailableTo`). | |
| Packages get packages => Packages.empty; | |
| @override | |
| BlazePackageUriResolver get packageUriResolver => | |
| BlazePackageUriResolver(this); | |
| @override | |
| SourceFactory createSourceFactory( | |
| DartSdk? sdk, | |
| SummaryDataStore? summaryData, | |
| ) { | |
| List<UriResolver> resolvers = <UriResolver>[]; | |
| if (sdk != null) { | |
| resolvers.add(DartUriResolver(sdk)); | |
| } | |
| resolvers.add(packageUriResolver); | |
| resolvers.add(BlazeFileUriResolver(this)); | |
| if (summaryData != null) { | |
| resolvers.add(InSummaryUriResolver(summaryData)); | |
| } | |
| return SourceFactory(resolvers); | |
| } | |
| /// Return the file with the given [absolutePath], looking first into | |
| /// directories for generated files: `blaze-bin` and `blaze-genfiles`, and | |
| /// then into the workspace root. The file in the workspace root is returned | |
| /// even if it does not exist. Return `null` if the given [absolutePath] is | |
| /// not in the workspace [root]. | |
| File? findFile(String absolutePath) { | |
| path.Context context = provider.pathContext; | |
| try { | |
| String relative = context.relative(absolutePath, from: root); | |
| if (relative == '.') { | |
| return null; | |
| } | |
| // First check genfiles and bin directories. Note that we always use the | |
| // symlinks and not the [binPaths] or [genfiles] to make sure we use the | |
| // files corresponding to the most recent build configuration and get | |
| // consistent view of all the generated files. | |
| var generatedCandidates = [ | |
| 'blaze-genfiles', | |
| 'blaze-bin', | |
| ].map((prefix) => context.join(root, context.join(prefix, relative))); | |
| for (var path in generatedCandidates) { | |
| File file = provider.getFile(path); | |
| if (file.exists) { | |
| _blazeCandidateFiles.add( | |
| BlazeSearchInfo(relative, generatedCandidates.toList()), | |
| ); | |
| return file; | |
| } | |
| } | |
| // Writable | |
| File writableFile = provider.getFile(absolutePath); | |
| if (writableFile.exists) { | |
| return writableFile; | |
| } | |
| // If we couldn't find the file, assume that it has not yet been | |
| // generated, so send an event with all the paths that we tried. | |
| _blazeCandidateFiles.add( | |
| BlazeSearchInfo(relative, generatedCandidates.toList()), | |
| ); | |
| // Not generated, return the default one. | |
| return writableFile; | |
| } catch (_) { | |
| return null; | |
| } | |
| } | |
| @override | |
| BlazeWorkspacePackage? findPackageFor(String filePath) { | |
| path.Context context = provider.pathContext; | |
| var directoryPath = context.dirname(filePath); | |
| var cachedPackage = _directoryToPackage[directoryPath]; | |
| if (cachedPackage != null) { | |
| return cachedPackage; | |
| } | |
| if (!context.isWithin(root, directoryPath)) { | |
| return null; | |
| } | |
| // Handle files which are given with their location in "blaze-bin", etc. | |
| // This does not typically happen during usual analysis, but it still could, | |
| // and it can come up in tests. | |
| for (var binPath in [genfiles, ...binPaths]) { | |
| if (context.isWithin(binPath, directoryPath)) { | |
| return findPackageFor( | |
| context.join(root, context.relative(filePath, from: binPath)), | |
| ); | |
| } | |
| } | |
| /// Return the package rooted at [folder]. | |
| BlazeWorkspacePackage? packageRootedAt(Folder folder) { | |
| var uriParts = packageUriResolver._restoreUriParts( | |
| root, | |
| '${folder.path}/lib/__fake__.dart', | |
| ); | |
| String? packageName; | |
| if (uriParts != null && uriParts.isNotEmpty) { | |
| packageName = uriParts[0]; | |
| } | |
| // TODO(srawlins): If [packageName] could not be derived from [uriParts], | |
| // I imagine this should throw. | |
| if (packageName == null) { | |
| return null; | |
| } | |
| var package = BlazeWorkspacePackage(packageName, folder, this); | |
| _directoryToPackage[directoryPath] = package; | |
| return package; | |
| } | |
| var startFolder = provider.getFolder(directoryPath); | |
| for (var folder in startFolder.withAncestors) { | |
| if (folder.path.length < root.length) { | |
| // We've walked up outside of [root], so [path] is definitely not | |
| // defined in any package in this workspace. | |
| return null; | |
| } | |
| if (folder.getFile(_buildFileName).exists) { | |
| // Found the BUILD file, denoting a Dart package. | |
| return packageRootedAt(folder); | |
| } | |
| if (_hasBuildFileSubstitute(folder)) { | |
| return packageRootedAt(folder); | |
| } | |
| } | |
| return null; | |
| } | |
| /// In some distributed build environments, BUILD files are not preserved. | |
| /// We can still look for a ".packages" file in order to determine a | |
| /// package's root. A ".packages" file found in [folder]'s sister path | |
| /// under a "bin" path among [binPaths] denotes a Dart package. | |
| /// | |
| /// For example, if the [root] of this BlazeWorkspace is | |
| /// "/build/work/abc123/workspace" with two "bin" folders, | |
| /// "/build/work/abc123/workspace/blaze-out/host/bin/" and | |
| /// "/build/work/abc123/workspace/blaze-out/k8-opt/bin/", and [folder] | |
| /// is at "/build/work/abc123/workspace/foo/bar", then we must look for a | |
| /// file ending in ".packages" in the folders | |
| /// "/build/work/abc123/workspace/blaze-out/host/bin/foo/bar" and | |
| /// "/build/work/abc123/workspace/blaze-out/k8-opt/bin/foo/bar". | |
| bool _hasBuildFileSubstitute(Folder folder) { | |
| if (!_lookForBuildFileSubstitutes) { | |
| return false; | |
| } | |
| path.Context context = provider.pathContext; | |
| // [folder]'s path, relative to [root]. For example, "foo/bar". | |
| String relative = context.relative(folder.path, from: root); | |
| for (String bin in binPaths) { | |
| Folder binChild = provider.getFolder( | |
| context.normalize(context.join(bin, relative)), | |
| ); | |
| if (binChild.exists && | |
| binChild.getChildren().any((c) => c.path.endsWith('.packages'))) { | |
| // [folder]'s sister folder within [bin] contains a ".packages" file. | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| String? _relativeToRoot(String p) { | |
| path.Context context = provider.pathContext; | |
| // genfiles | |
| if (context.isWithin(genfiles, p)) { | |
| return context.relative(p, from: genfiles); | |
| } | |
| // bin | |
| for (String bin in binPaths) { | |
| if (context.isWithin(bin, p)) { | |
| return context.relative(p, from: bin); | |
| } | |
| } | |
| // Not generated | |
| if (context.isWithin(root, p)) { | |
| return context.relative(p, from: root); | |
| } | |
| // Failed reverse lookup | |
| return null; | |
| } | |
| /// Find the Blaze workspace that contains the given [filePath]. | |
| /// | |
| /// This method walks up the file system from [filePath], looking for various | |
| /// "marker" files which indicate a Blaze workspace. | |
| /// | |
| /// At each folder _f_ with parent _p_, starting with [filePath]: | |
| /// | |
| /// * If _f_ has a sibling folder named "READONLY", and that folder has a | |
| /// child folder with the same name as _f_, then a [BlazeWorkspace] rooted at | |
| /// _f_ is returned. | |
| /// * If _f_ has a child folder named "blaze-out", then a [BlazeWorkspace] | |
| /// rooted at _f_ is returned. | |
| /// * If _f_ has a child file named [file_paths.blazeWorkspaceMarker], then | |
| /// a [BlazeWorkspace] rooted at _f_ is returned. | |
| static BlazeWorkspace? find( | |
| ResourceProvider provider, | |
| String filePath, { | |
| bool lookForBuildFileSubstitutes = true, | |
| bool provideLanguageVersion = true, | |
| }) { | |
| var context = provider.pathContext; | |
| var startFolder = provider.getFolder(filePath); | |
| for (var folder in startFolder.withAncestors) { | |
| var parent = folder.parent; | |
| var blazeOutFolder = parent.getFolder('blaze-out'); | |
| if (blazeOutFolder.exists) { | |
| // Found the "out" folder; must be a Blaze workspace. | |
| String root = parent.path; | |
| var binPaths = _findBinFolderPaths(parent); | |
| binPaths = binPaths..add(context.join(root, 'blaze-bin')); | |
| return BlazeWorkspace._( | |
| provider, | |
| root, | |
| binPaths, | |
| context.join(root, 'blaze-genfiles'), | |
| lookForBuildFileSubstitutes: lookForBuildFileSubstitutes, | |
| provideLanguageVersion: provideLanguageVersion, | |
| ); | |
| } | |
| // Found the WORKSPACE file, must be a non-git workspace. | |
| if (folder.getFile(file_paths.blazeWorkspaceMarker).exists) { | |
| String root = folder.path; | |
| var binPaths = _findBinFolderPaths(folder); | |
| binPaths = binPaths..add(context.join(root, 'blaze-bin')); | |
| return BlazeWorkspace._( | |
| provider, | |
| root, | |
| binPaths, | |
| context.join(root, 'blaze-genfiles'), | |
| lookForBuildFileSubstitutes: lookForBuildFileSubstitutes, | |
| provideLanguageVersion: provideLanguageVersion, | |
| ); | |
| } | |
| } | |
| return null; | |
| } | |
| /// Create the Blaze workspace with the given [root]. | |
| /// | |
| /// This method should be used during build in google3, when we know for | |
| /// sure the workspace root, but [file_paths.blazeWorkspaceMarker] is not | |
| /// available. | |
| static BlazeWorkspace forBuild({required Folder root}) { | |
| var provider = root.provider; | |
| var blazeBin = root.getFolder('blaze-bin'); | |
| var blazeGenfiles = root.getFolder('blaze-genfiles'); | |
| var binPaths = _findBinFolderPaths(root); | |
| binPaths.add(blazeBin.path); | |
| return BlazeWorkspace._( | |
| provider, | |
| root.path, | |
| binPaths, | |
| blazeGenfiles.path, | |
| lookForBuildFileSubstitutes: true, | |
| provideLanguageVersion: true, | |
| ); | |
| } | |
| /// Find the "bin" folder path, by searching for it. | |
| /// | |
| /// Depending on the environment we're working in (source code tree, build | |
| /// environment subtree of sources, local workspace, blaze), the "bin" | |
| /// folder may be available at a symlink found at `$root/blaze-bin/`. If that | |
| /// symlink is not available, then we must search the immediate folders found | |
| /// in `$root/blaze-out/` for folders named "bin". | |
| /// | |
| /// If no "bin" folder is found in any of those locations, empty list is | |
| /// returned. | |
| static List<String> _findBinFolderPaths(Folder root) { | |
| var out = root.getFolder('blaze-out'); | |
| if (!out.exists) { | |
| return []; | |
| } | |
| List<String> binPaths = []; | |
| for (var child in out.getChildren().whereType<Folder>()) { | |
| // Children are folders denoting architectures and build flags, like | |
| // 'k8-opt', 'k8-fastbuild', perhaps 'host'. | |
| Folder possibleBin = child.getFolder('bin'); | |
| if (possibleBin.exists) { | |
| binPaths.add(possibleBin.path); | |
| } | |
| } | |
| return binPaths; | |
| } | |
| /// Return the default language version of the workspace. | |
| /// | |
| /// Return `null` if cannot be read, for example because the file does not | |
| /// exist, or is not available in this build configuration (batch analysis). | |
| static Version? _readLanguageVersion( | |
| ResourceProvider resourceProvider, | |
| String rootPath, | |
| ) { | |
| var file = resourceProvider.getFile( | |
| resourceProvider.pathContext.joinAll([ | |
| rootPath, | |
| 'dart', | |
| 'build_defs', | |
| 'bzl', | |
| 'language.bzl', | |
| ]), | |
| ); | |
| String content; | |
| try { | |
| content = file.readAsStringSync(); | |
| } on FileSystemException { | |
| return null; | |
| } | |
| var pattern = RegExp(r'(^|\s+)_version\s*=\s*"(\d+\.\d+)"'); | |
| for (var match in pattern.allMatches(content)) { | |
| return Version.parse('${match[2]}.0'); | |
| } | |
| return null; | |
| } | |
| } | |
| /// Information about a package defined in a [BlazeWorkspace]. | |
| /// | |
| /// Separate from [Packages] or package maps, this class is designed to simply | |
| /// understand whether arbitrary file paths represent libraries declared within | |
| /// a given package in a [BlazeWorkspace]. | |
| class BlazeWorkspacePackage extends WorkspacePackageImpl { | |
| /// A prefix for any URI of a path in this package. | |
| final String _uriPrefix; | |
| @override | |
| final Folder root; | |
| @override | |
| final BlazeWorkspace workspace; | |
| List<String>? _enabledExperiments; | |
| Version? _languageVersion; | |
| BlazeWorkspacePackage(String packageName, this.root, this.workspace) | |
| : _uriPrefix = 'package:$packageName/'; | |
| @override | |
| List<String>? get enabledExperiments { | |
| return _enabledExperiments; | |
| } | |
| @override | |
| Version? get languageVersion { | |
| if (!workspace._provideLanguageVersion) { | |
| return null; | |
| } | |
| return _languageVersion ?? workspace._languageVersion; | |
| } | |
| @override | |
| bool contains(Source source) { | |
| var uri = source.uri; | |
| if (uri.isScheme('package')) { | |
| return uri.toString().startsWith(_uriPrefix); | |
| } | |
| var path = source.fullName; | |
| return workspace.findPackageFor(path)?.root == root; | |
| } | |
| @override | |
| bool isInTestDirectory(File file) { | |
| // If the package itself is a "testing" package, then [file] counts as being | |
| // "in a test directory." | |
| var pathContext = workspace.provider.pathContext; | |
| var relativeRoot = pathContext.relative(root.path, from: workspace.root); | |
| var relativeRootSegments = pathContext.split(relativeRoot); | |
| if (root.shortName == 'testing' || | |
| relativeRootSegments.contains('testing') || | |
| relativeRootSegments.contains('integration_test') || | |
| relativeRootSegments.contains('test_driver') || | |
| relativeRootSegments.contains('test')) { | |
| return true; | |
| } | |
| var libFolder = root.getFolder('lib'); | |
| var libSrcFolder = libFolder.getFolder('src'); | |
| var libSrcTestingFolder = libSrcFolder.getFolder('testing'); | |
| if (libSrcTestingFolder.contains(file.path)) { | |
| return true; | |
| } | |
| var libTestDriverFolder = libFolder.getFolder('test_driver'); | |
| if (libTestDriverFolder.contains(file.path)) { | |
| return true; | |
| } | |
| for (var binPath in workspace.binPaths) { | |
| var binFolder = workspace.provider.getFolder(binPath); | |
| var genPackageRoot = binFolder.getFolder(relativeRoot); | |
| if (isInTestDirectoryUnder(genPackageRoot, file)) { | |
| return true; | |
| } | |
| var genLibSrcTestingFolder = genPackageRoot | |
| .getFolder('lib') | |
| .getFolder('src') | |
| .getFolder('testing'); | |
| if (genLibSrcTestingFolder.contains(file.path)) { | |
| return true; | |
| } | |
| var genLibTestDriverFolder = genPackageRoot | |
| .getFolder('lib') | |
| .getFolder('test_driver'); | |
| if (genLibTestDriverFolder.contains(file.path)) { | |
| return true; | |
| } | |
| } | |
| var genfilesFolder = workspace.provider.getFolder(workspace.genfiles); | |
| var genPackageRoot = genfilesFolder.getFolder(relativeRoot); | |
| if (isInTestDirectoryUnder(genPackageRoot, file)) { | |
| return true; | |
| } | |
| var genLibSrcTestingFolder = genPackageRoot | |
| .getFolder('lib') | |
| .getFolder('src') | |
| .getFolder('testing'); | |
| if (genLibSrcTestingFolder.contains(file.path)) { | |
| return true; | |
| } | |
| var genLibTestDriverFolder = genPackageRoot | |
| .getFolder('lib') | |
| .getFolder('test_driver'); | |
| if (genLibTestDriverFolder.contains(file.path)) { | |
| return true; | |
| } | |
| return super.isInTestDirectory(file); | |
| } | |
| @override | |
| // TODO(brianwilkerson): Implement this by looking in the BUILD file for 'deps' | |
| // lists. | |
| Packages packagesAvailableTo(String libraryPath) => Packages.empty; | |
| @override | |
| bool sourceIsInPublicApi(Source source) { | |
| var filePath = filePathFromSource(source); | |
| if (filePath == null) return false; | |
| var libFolder = root.getFolder('lib'); | |
| if (libFolder.contains(filePath)) { | |
| // A file in "$root/lib" is public iff it is not in "$root/lib/src". | |
| var libSrcFolder = libFolder.getFolder('src'); | |
| return !libSrcFolder.contains(filePath); | |
| } | |
| var relativeRoot = workspace.provider.pathContext.relative( | |
| root.path, | |
| from: workspace.root, | |
| ); | |
| for (var binPath in workspace.binPaths) { | |
| Folder bin = workspace.provider.getFolder(binPath); | |
| libFolder = bin.getFolder(relativeRoot).getFolder('lib'); | |
| if (libFolder.contains(filePath)) { | |
| // A file in "$bin/lib" is public iff it is not in "$bin/lib/src". | |
| var libSrcFolder = libFolder.getFolder('src'); | |
| return !libSrcFolder.contains(filePath); | |
| } | |
| } | |
| libFolder = workspace.provider | |
| .getFolder(workspace.genfiles) | |
| .getFolder(relativeRoot) | |
| .getFolder('lib'); | |
| if (libFolder.contains(filePath)) { | |
| // A file in "$genfiles/lib" is public iff it is not in | |
| // "$genfiles/lib/src". | |
| var libSrcFolder = libFolder.getFolder('src'); | |
| return !libSrcFolder.contains(filePath); | |
| } | |
| return false; | |
| } | |
| } |