blob: e9afbfa1a7c7385f82b76b34b9877049d19e1936 [file] [log] [blame]
// Copyright (c) 2020, 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/file_system/physical_file_system.dart';
import 'package:analyzer/src/lint/pub.dart';
import 'package:nnbd_migration/src/fantasyland/fantasy_repo.dart';
import 'package:nnbd_migration/src/fantasyland/fantasy_workspace_impl.dart';
import 'package:path/path.dart' as path;
final Map<String, FantasySubPackageSettings> _subPackageTable = {
'_fe_analyzer_shared': FantasySubPackageSettings(
'_fe_analyzer_shared', FantasyRepoSettings.fromName('sdk'),
subDir: path.join('pkg', '_fe_analyzer_shared')),
'analysis_tool': FantasySubPackageSettings(
'analysis_tool', FantasyRepoSettings.fromName('sdk'),
subDir: path.join('pkg', 'analysis_tool')),
'analyzer': FantasySubPackageSettings(
'analyzer', FantasyRepoSettings.fromName('sdk'),
subDir: path.join('pkg', 'analyzer')),
'build': FantasySubPackageSettings(
'build', FantasyRepoSettings.fromName('build'),
subDir: 'build'),
'build_config': FantasySubPackageSettings(
'build_config', FantasyRepoSettings.fromName('build'),
subDir: 'build_config'),
'build_daemon': FantasySubPackageSettings(
'build_daemon', FantasyRepoSettings.fromName('build'),
subDir: 'build_daemon'),
'build_integration': FantasySubPackageSettings(
'build_integration', FantasyRepoSettings.fromName('sdk'),
subDir: path.join('pkg', 'build_integration')),
'build_modules': FantasySubPackageSettings(
'build_modules', FantasyRepoSettings.fromName('build'),
subDir: 'build_modules'),
'build_node_compilers': FantasySubPackageSettings(
'build_node_compilers', FantasyRepoSettings.fromName('node-interop'),
subDir: 'build_node_compilers'),
'build_resolvers': FantasySubPackageSettings(
'build_resolvers', FantasyRepoSettings.fromName('build'),
subDir: 'build_resolvers'),
'build_runner': FantasySubPackageSettings(
'build_runner', FantasyRepoSettings.fromName('build'),
subDir: 'build_runner'),
'build_runner_core': FantasySubPackageSettings(
'build_runner_core', FantasyRepoSettings.fromName('build'),
subDir: 'build_runner_core'),
'build_test': FantasySubPackageSettings(
'build_test', FantasyRepoSettings.fromName('build'),
subDir: 'build_test'),
'build_vm_compilers': FantasySubPackageSettings(
'build_vm_compilers', FantasyRepoSettings.fromName('build'),
subDir: 'build_vm_compilers'),
'build_web_compilers': FantasySubPackageSettings(
'build_web_compilers', FantasyRepoSettings.fromName('build'),
subDir: 'build_web_compilers'),
'built_collection': FantasySubPackageSettings('built_collection',
FantasyRepoSettings.fromName('built_collection.dart')),
'built_value': FantasySubPackageSettings(
'built_value', FantasyRepoSettings.fromName('built_value.dart'),
subDir: 'built_value'),
'built_value_generator': FantasySubPackageSettings(
'built_value_generator', FantasyRepoSettings.fromName('built_value.dart'),
subDir: 'built_value_generator'),
'checked_yaml': FantasySubPackageSettings(
'checked_yaml', FantasyRepoSettings.fromName('json_serializable'),
subDir: 'checked_yaml'),
'expect': FantasySubPackageSettings(
'expect', FantasyRepoSettings.fromName('sdk'),
subDir: path.join('pkg', 'expect')),
'front_end': FantasySubPackageSettings(
'front_end', FantasyRepoSettings.fromName('sdk'),
subDir: path.join('pkg', 'front_end')),
'grinder': FantasySubPackageSettings(
'grinder', FantasyRepoSettings.fromName('grinder.dart')),
'kernel': FantasySubPackageSettings(
'kernel', FantasyRepoSettings.fromName('sdk'),
subDir: path.join('pkg', 'kernel')),
'meta': FantasySubPackageSettings('meta', FantasyRepoSettings.fromName('sdk'),
subDir: path.join('pkg', 'meta')),
'node_interop': FantasySubPackageSettings(
'node_interop', FantasyRepoSettings.fromName('node-interop'),
subDir: 'node_interop'),
'node_io': FantasySubPackageSettings(
'node_io', FantasyRepoSettings.fromName('node-interop'),
subDir: 'node_io'),
'js': FantasySubPackageSettings('js', FantasyRepoSettings.fromName('sdk'),
subDir: path.join('pkg', 'js')),
'json_annotation': FantasySubPackageSettings(
'json_annotation', FantasyRepoSettings.fromName('json_serializable'),
subDir: 'json_annotation'),
'json_serializable': FantasySubPackageSettings(
'json_serializable', FantasyRepoSettings.fromName('json_serializable'),
subDir: 'json_serializable'),
'package_config': FantasySubPackageSettings(
'package_config', FantasyRepoSettings.fromName('package_config')),
'protobuf': FantasySubPackageSettings(
'protobuf', FantasyRepoSettings.fromName('protobuf'),
subDir: 'protobuf'),
'scratch_space': FantasySubPackageSettings(
'scratch_space', FantasyRepoSettings.fromName('build'),
subDir: 'scratch_space'),
'source_gen': FantasySubPackageSettings(
'source_gen', FantasyRepoSettings.fromName('source_gen'),
subDir: 'source_gen'),
'source_gen_test': FantasySubPackageSettings(
'source_gen_test', FantasyRepoSettings.fromName('source_gen_test')),
'test': FantasySubPackageSettings(
'test', FantasyRepoSettings.fromName('test'),
subDir: path.join('pkgs', 'test')),
'test_api': FantasySubPackageSettings(
'test_api', FantasyRepoSettings.fromName('test'),
subDir: path.join('pkgs', 'test_api')),
'test_core': FantasySubPackageSettings(
'test_core', FantasyRepoSettings.fromName('test'),
subDir: path.join('pkgs', 'test_core')),
'testing': FantasySubPackageSettings(
'testing', FantasyRepoSettings.fromName('sdk'),
subDir: path.join('pkg', 'testing')),
'vm_service': FantasySubPackageSettings(
'vm_service', FantasyRepoSettings.fromName('sdk'),
subDir: path.join('pkg', 'vm_service')),
'quiver': FantasySubPackageSettings(
'quiver', FantasyRepoSettings.fromName('quiver-dart')),
'webkit_inspection_protocol': FantasySubPackageSettings(
'webkit_inspection_protocol',
FantasyRepoSettings.fromName('webkit_inspection_protocol.dart')),
};
/// Data class containing settings for a package within a [FantasyWorkspaceImpl].
class FantasySubPackageSettings {
final String name;
final FantasyRepoSettings repoSettings;
final String subDir;
FantasySubPackageSettings(this.name, this.repoSettings, {this.subDir: '.'});
/// Build settings just from a name in a mostly-hardcoded table.
factory FantasySubPackageSettings.fromName(String name) {
if (_subPackageTable.containsKey(name)) {
return _subPackageTable[name];
}
return FantasySubPackageSettings(name, FantasyRepoSettings.fromName(name));
}
/// Build [FantasySubPackageSettings] from the dependency of a given subPackage.
factory FantasySubPackageSettings.fromDependency(
FantasySubPackage subPackage, PSDependency dependency) {
if (dependency.host != null)
throw UnimplementedError(
'fromDependency: contains a host field: $dependency');
if (dependency.git != null)
throw UnimplementedError(
'fromDependency: contains a git field: $dependency');
if (dependency.path != null) {
return FantasySubPackageSettings.fromNested(
dependency.name.text, subPackage, dependency.path.value.text);
}
// Hopefully, a version is declared at least... but if not proceed onward
// and hope building from name works if we aren't running with asserts.
assert(dependency.version != null);
return FantasySubPackageSettings.fromName(dependency.name.text);
}
/// Build settings for a nested package based on the repository settings
/// of an existing package.
///
/// [subDir] is resolved relative to [parent.packageRoot].
factory FantasySubPackageSettings.fromNested(
String name, FantasySubPackage parent, String subDir) {
var pathContext = parent.resourceProvider.pathContext;
String nestedSubdir;
if (pathContext.isRelative(subDir)) {
nestedSubdir = pathContext
.normalize(pathContext.join(parent.packageRoot.path, subDir));
} else {
nestedSubdir = pathContext.normalize(subDir);
}
assert(pathContext.isWithin(parent.packageRoot.path, nestedSubdir));
return FantasySubPackageSettings(name, parent.containingRepo.repoSettings,
subDir: pathContext.relative(nestedSubdir,
from: parent.containingRepo.repoRoot.path));
}
@override
int get hashCode => toString().hashCode;
@override
bool operator ==(other) {
return other is FantasySubPackageSettings &&
(other.name == name &&
other.repoSettings == repoSettings &&
other.subDir == subDir);
}
@override
String toString() =>
'FantasySubPackageSettings("$name", ${repoSettings.toString()}, "$subDir")';
}
class _AccumulateDependenciesVisitor<T> extends PubspecVisitor {
final T Function(PSDependency) transformDependency;
final List<T> results = [];
_AccumulateDependenciesVisitor(this.transformDependency);
void visitPackageDependency(PSDependency dependency) =>
results.add(transformDependency(dependency));
}
class _AccumulateAllDependenciesVisitor<T>
extends _AccumulateDependenciesVisitor<T> {
_AccumulateAllDependenciesVisitor(
T Function(PSDependency) transformDependency)
: super(transformDependency);
void visitPackageDevDependency(PSDependency dependency) =>
results.add(transformDependency(dependency));
}
class FantasySubPackageDependencies {
final ResourceProvider resourceProvider;
File Function(String) get fileBuilder => resourceProvider.getFile;
FantasySubPackageDependencies({ResourceProvider resourceProvider})
: resourceProvider =
resourceProvider ?? PhysicalResourceProvider.INSTANCE;
factory FantasySubPackageDependencies.fromWorkspaceDependencies(
FantasyWorkspaceDependencies workspaceDependencies) {
return FantasySubPackageDependencies(
resourceProvider: workspaceDependencies.resourceProvider);
}
}
/// Represents one package within a [FantasyWorkspaceImpl].
///
/// A `FantasySubPackage` differs from a normal package in that Dart code within
/// it depends on a global .packages file to resolve symbols.
class FantasySubPackage {
final FantasyRepo containingRepo;
final String name;
final FantasySubPackageSettings packageSettings;
final ResourceProvider resourceProvider;
FantasySubPackage(this.packageSettings, this.containingRepo,
{ResourceProvider resourceProvider})
: name = packageSettings.name,
resourceProvider =
resourceProvider ?? PhysicalResourceProvider.INSTANCE;
Folder _packageRoot;
Folder get packageRoot => _packageRoot ??= resourceProvider.getFolder(
resourceProvider.pathContext.normalize(resourceProvider.pathContext
.join(containingRepo.repoRoot.path, packageSettings.subDir)));
Future<void> _acceptPubspecVisitor<T>(
PubspecVisitor<T> pubspecVisitor) async {
File pubspecYaml = resourceProvider.getFile(
resourceProvider.pathContext.join(packageRoot.path, 'pubspec.yaml'));
if (!pubspecYaml.exists) return;
Pubspec pubspec = Pubspec.parse(pubspecYaml.readAsStringSync(),
sourceUrl: resourceProvider.pathContext.toUri(pubspecYaml.path));
pubspec.accept(pubspecVisitor);
}
Future<List<FantasySubPackageSettings>> getPackageDependencies() async {
var visitor = _AccumulateDependenciesVisitor(
(d) => FantasySubPackageSettings.fromDependency(this, d));
await _acceptPubspecVisitor(visitor);
return visitor.results;
}
Future<List<FantasySubPackageSettings>> getPackageAllDependencies() async {
var visitor = _AccumulateAllDependenciesVisitor(
(d) => FantasySubPackageSettings.fromDependency(this, d));
await _acceptPubspecVisitor(visitor);
return visitor.results;
}
}