mirror of
https://github.com/dart-lang/sdk
synced 2024-09-18 20:51:19 +00:00
Implement analysis options modification.
Change-Id: I899067652bf00b3273559b59969d6b7c30883900 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/135343 Commit-Queue: Janice Collins <jcollins@google.com> Reviewed-by: Mike Fairhurst <mfairhurst@google.com>
This commit is contained in:
parent
7ce78365bf
commit
e0e438bf8b
|
@ -14,6 +14,7 @@ import 'package:args/args.dart';
|
|||
final parser = ArgParser()
|
||||
..addMultiOption('extra-packages', abbr: 'e', splitCommas: true)
|
||||
..addOption('package-name', abbr: 'p')
|
||||
..addFlag('allow-update', defaultsTo: false, negatable: true)
|
||||
..addFlag('analysis-options-hack', defaultsTo: true, negatable: true)
|
||||
..addFlag('strip-sdk-constraint-hack', defaultsTo: true, negatable: true)
|
||||
..addFlag('force-migrate-deps', defaultsTo: true, negatable: true)
|
||||
|
@ -45,11 +46,17 @@ Future<void> main(List<String> args) async {
|
|||
assert(extraPackages != null);
|
||||
|
||||
FantasyWorkspace workspace = await buildFantasyLand(
|
||||
packageName, extraPackages, path.canonicalize(results.rest.first));
|
||||
packageName,
|
||||
extraPackages,
|
||||
path.canonicalize(results.rest.first),
|
||||
results['allow-update'] as bool);
|
||||
workspace.makeAllSymlinks();
|
||||
|
||||
if (results['analysis-options-hack'] as bool) {
|
||||
stderr.writeln('warning: analysis options hack not implemented');
|
||||
await Future.wait([
|
||||
for (FantasySubPackage p in workspace.subPackages.values)
|
||||
p.enableExperimentHack()
|
||||
]);
|
||||
}
|
||||
|
||||
if (results['strip-sdk-constraint-hack'] as bool) {
|
||||
|
|
|
@ -110,11 +110,11 @@ abstract class FantasyRepo {
|
|||
Folder get repoRoot;
|
||||
|
||||
static Future<FantasyRepo> buildGitRepoFrom(
|
||||
FantasyRepoSettings repoSettings, String repoRootPath,
|
||||
FantasyRepoSettings repoSettings, String repoRootPath, bool allowUpdate,
|
||||
{FantasyRepoDependencies fantasyRepoDependencies}) async {
|
||||
FantasyRepoGitImpl newRepo = FantasyRepoGitImpl(repoSettings, repoRootPath,
|
||||
fantasyRepoDependencies: fantasyRepoDependencies);
|
||||
await newRepo.init();
|
||||
await newRepo.init(allowUpdate);
|
||||
return newRepo;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,12 +55,12 @@ class FantasyRepoGitImpl extends FantasyRepo {
|
|||
///
|
||||
/// May throw [FantasyRepoException] in the event of problems and does
|
||||
/// not clean up filesystem state.
|
||||
Future<void> init() async {
|
||||
Future<void> init([bool allowUpdate = true]) async {
|
||||
assert(_isInitialized == false);
|
||||
if (repoRoot.exists) {
|
||||
if (repoRoot.exists && allowUpdate) {
|
||||
await _update(_external.launcher);
|
||||
// TODO(jcollins-g): handle "update" of pinned revision edge case
|
||||
} else {
|
||||
} else if (!repoRoot.exists) {
|
||||
await _clone(_external.launcher);
|
||||
}
|
||||
_isInitialized = true;
|
||||
|
|
|
@ -2,12 +2,17 @@
|
|||
// 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_plugin/protocol/protocol_common.dart';
|
||||
import 'package:analyzer/file_system/file_system.dart';
|
||||
import 'package:analyzer/file_system/physical_file_system.dart';
|
||||
import 'package:analyzer/src/dart/analysis/experiments.dart';
|
||||
import 'package:analyzer/src/lint/pub.dart';
|
||||
import 'package:analyzer/src/task/options.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;
|
||||
import 'package:source_span/source_span.dart';
|
||||
import 'package:yaml/yaml.dart';
|
||||
|
||||
final Map<String, FantasySubPackageSettings> _subPackageTable = {
|
||||
'_fe_analyzer_shared': FantasySubPackageSettings(
|
||||
|
@ -278,4 +283,120 @@ class FantasySubPackage {
|
|||
await _acceptPubspecVisitor(visitor);
|
||||
return visitor.results;
|
||||
}
|
||||
|
||||
/// Delete any `pub get` output that interferes with a workspace.
|
||||
Future<void> cleanUp() async {
|
||||
File pubspecLock = packageRoot.getChildAssumingFile('pubspec.lock');
|
||||
File dotPackages = packageRoot.getChildAssumingFile('.packages');
|
||||
Folder dartTool = packageRoot.getChildAssumingFolder('.dart_tool');
|
||||
File packageConfigJson =
|
||||
dartTool.getChildAssumingFile('package_config.json');
|
||||
for (File f in [pubspecLock, dotPackages, packageConfigJson]) {
|
||||
if (f.exists) f.delete();
|
||||
}
|
||||
}
|
||||
|
||||
void processYamlException(String operation, path, exception) {
|
||||
// TODO(jcollins-g): implement
|
||||
}
|
||||
|
||||
/// Modify all analysis_options.yaml file to include the nullability
|
||||
/// experiment.
|
||||
Future<void> enableExperimentHack() async {
|
||||
// This is completely bonkers, cut and paste from non_nullable_fix.dart.
|
||||
// But it is temporary, right?
|
||||
// TODO(jcollins-g): Remove this hack once no longer needed.
|
||||
File optionsFile =
|
||||
packageRoot.getChildAssumingFile('analysis_options.yaml');
|
||||
SourceChange sourceChange = SourceChange('fantasy_sub_package-$name');
|
||||
String optionsContent;
|
||||
YamlNode optionsMap;
|
||||
if (optionsFile.exists) {
|
||||
try {
|
||||
optionsContent = optionsFile.readAsStringSync();
|
||||
} on FileSystemException catch (e) {
|
||||
processYamlException('read', optionsFile.path, e);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
optionsMap = loadYaml(optionsContent) as YamlNode;
|
||||
} on YamlException catch (e) {
|
||||
processYamlException('parse', optionsFile.path, e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SourceSpan parentSpan;
|
||||
String content;
|
||||
YamlNode analyzerOptions;
|
||||
if (optionsMap is YamlMap) {
|
||||
analyzerOptions = optionsMap.nodes[AnalyzerOptions.analyzer];
|
||||
}
|
||||
if (analyzerOptions == null) {
|
||||
var start = SourceLocation(0, line: 0, column: 0);
|
||||
parentSpan = SourceSpan(start, start, '');
|
||||
content = '''
|
||||
analyzer:
|
||||
enable-experiment:
|
||||
- non-nullable
|
||||
|
||||
''';
|
||||
} else if (analyzerOptions is YamlMap) {
|
||||
YamlNode experiments =
|
||||
analyzerOptions.nodes[AnalyzerOptions.enableExperiment];
|
||||
if (experiments == null) {
|
||||
parentSpan = analyzerOptions.span;
|
||||
content = '''
|
||||
|
||||
enable-experiment:
|
||||
- non-nullable''';
|
||||
} else if (experiments is YamlList) {
|
||||
experiments.nodes.firstWhere(
|
||||
(node) => node.span.text == EnableString.non_nullable,
|
||||
orElse: () {
|
||||
parentSpan = experiments.span;
|
||||
content = '''
|
||||
|
||||
- non-nullable''';
|
||||
return null;
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (parentSpan != null) {
|
||||
final space = ' '.codeUnitAt(0);
|
||||
final cr = '\r'.codeUnitAt(0);
|
||||
final lf = '\n'.codeUnitAt(0);
|
||||
|
||||
int offset = parentSpan.end.offset;
|
||||
while (offset > 0) {
|
||||
int ch = optionsContent.codeUnitAt(offset - 1);
|
||||
if (ch == space || ch == cr) {
|
||||
--offset;
|
||||
} else if (ch == lf) {
|
||||
--offset;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
SourceFileEdit fileEdit = SourceFileEdit(optionsFile.path, 0,
|
||||
edits: [SourceEdit(offset, 0, content)]);
|
||||
for (SourceEdit sourceEdit in fileEdit.edits) {
|
||||
sourceChange.addEdit(fileEdit.file, fileEdit.fileStamp, sourceEdit);
|
||||
}
|
||||
}
|
||||
_applyEdits(sourceChange);
|
||||
}
|
||||
|
||||
void _applyEdits(SourceChange sourceChange) {
|
||||
for (var fileEdit in sourceChange.edits) {
|
||||
File toEdit = packageRoot.getChildAssumingFile(fileEdit.file);
|
||||
String contents = toEdit.exists ? toEdit.readAsStringSync() : '';
|
||||
for (SourceEdit edit in fileEdit.edits) {
|
||||
contents = edit.apply(contents);
|
||||
}
|
||||
toEdit.writeAsStringSync(contents);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,14 +17,16 @@ abstract class FantasyWorkspace {
|
|||
/// Add a package to the workspace, given [packageSettings].
|
||||
///
|
||||
/// Completes when the repository and subPackage is added.
|
||||
/// If allowUpdate is true, the repository may be updated to the latest
|
||||
/// version.
|
||||
Future<FantasySubPackage> addPackageToWorkspace(
|
||||
FantasySubPackageSettings packageSettings);
|
||||
FantasySubPackageSettings packageSettings, bool allowUpdate);
|
||||
}
|
||||
|
||||
/// Build a "fantasyland"-style repository structure suitable for applying
|
||||
/// a migration to.
|
||||
Future<FantasyWorkspace> buildFantasyLand(
|
||||
String topLevelPackage, List<String> extraPackages, String fantasyLandDir) {
|
||||
Future<FantasyWorkspace> buildFantasyLand(String topLevelPackage,
|
||||
List<String> extraPackages, String fantasyLandDir, bool allowUpdate) {
|
||||
return FantasyWorkspaceTopLevelDevDepsImpl.buildFor(
|
||||
topLevelPackage, extraPackages, fantasyLandDir);
|
||||
topLevelPackage, extraPackages, fantasyLandDir, allowUpdate);
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ class FantasyWorkspaceError extends Error {
|
|||
|
||||
// TODO(jcollins-g): consider refactor that makes resourceProvider required.
|
||||
class FantasyWorkspaceDependencies {
|
||||
final Future<FantasyRepo> Function(FantasyRepoSettings, String,
|
||||
final Future<FantasyRepo> Function(FantasyRepoSettings, String, bool,
|
||||
{FantasyRepoDependencies fantasyRepoDependencies}) buildGitRepoFrom;
|
||||
final ResourceProvider resourceProvider;
|
||||
final SubprocessLauncher launcher;
|
||||
|
@ -30,7 +30,7 @@ class FantasyWorkspaceDependencies {
|
|||
FantasyWorkspaceDependencies(
|
||||
{ResourceProvider resourceProvider,
|
||||
SubprocessLauncher launcher,
|
||||
Future<FantasyRepo> Function(FantasyRepoSettings, String,
|
||||
Future<FantasyRepo> Function(FantasyRepoSettings, String, bool,
|
||||
{FantasyRepoDependencies fantasyRepoDependencies})
|
||||
buildGitRepoFrom})
|
||||
: resourceProvider =
|
||||
|
@ -85,16 +85,17 @@ abstract class FantasyWorkspaceBase extends FantasyWorkspace {
|
|||
/// dependencies.
|
||||
///
|
||||
/// Which dependencies are automatically added is implementation dependent.
|
||||
Future<void> addPackageNameToWorkspace(String packageName);
|
||||
Future<void> addPackageNameToWorkspace(String packageName, bool allowUpdate);
|
||||
|
||||
Future<FantasySubPackage> addPackageToWorkspace(
|
||||
FantasySubPackageSettings packageSettings) async {
|
||||
FantasySubPackageSettings packageSettings, bool allowUpdate) async {
|
||||
FantasyRepo containingRepo =
|
||||
await addRepoToWorkspace(packageSettings.repoSettings);
|
||||
await addRepoToWorkspace(packageSettings.repoSettings, allowUpdate);
|
||||
FantasySubPackage fantasySubPackage =
|
||||
FantasySubPackage(packageSettings, containingRepo);
|
||||
// TODO(jcollins-g): throw if double add
|
||||
subPackages[packageSettings] = fantasySubPackage;
|
||||
fantasySubPackage.cleanUp();
|
||||
return fantasySubPackage;
|
||||
}
|
||||
|
||||
|
@ -102,15 +103,19 @@ abstract class FantasyWorkspaceBase extends FantasyWorkspace {
|
|||
|
||||
/// Add one repository to the workspace.
|
||||
///
|
||||
/// If allowUpdate is true, the repository will be pulled before being
|
||||
/// synced.
|
||||
///
|
||||
/// The returned [Future] completes when the repository is synced and cloned.
|
||||
Future<FantasyRepo> addRepoToWorkspace(FantasyRepoSettings repoSettings) {
|
||||
Future<FantasyRepo> addRepoToWorkspace(
|
||||
FantasyRepoSettings repoSettings, bool allowUpdate) {
|
||||
if (_repos.containsKey(repoSettings.name)) return _repos[repoSettings.name];
|
||||
Folder repoRoot = _external.resourceProvider.getFolder(_external
|
||||
.resourceProvider.pathContext
|
||||
.canonicalize(_external.resourceProvider.pathContext
|
||||
.join(workspaceRootPath, _repoSubDir, repoSettings.name)));
|
||||
_repos[repoSettings.name] = _external.buildGitRepoFrom(
|
||||
repoSettings, repoRoot.path,
|
||||
repoSettings, repoRoot.path, allowUpdate,
|
||||
fantasyRepoDependencies:
|
||||
FantasyRepoDependencies.fromWorkspaceDependencies(_external));
|
||||
return _repos[repoSettings.name];
|
||||
|
@ -152,41 +157,46 @@ class FantasyWorkspaceTopLevelDevDepsImpl extends FantasyWorkspaceBase {
|
|||
: super._(workspaceRootPath,
|
||||
workspaceDependencies: workspaceDependencies);
|
||||
|
||||
static Future<FantasyWorkspace> buildFor(String topLevelPackage,
|
||||
List<String> extraPackageNames, String workspaceRootPath,
|
||||
static Future<FantasyWorkspace> buildFor(
|
||||
String topLevelPackage,
|
||||
List<String> extraPackageNames,
|
||||
String workspaceRootPath,
|
||||
bool allowUpdate,
|
||||
{FantasyWorkspaceDependencies workspaceDependencies}) async {
|
||||
var workspace = FantasyWorkspaceTopLevelDevDepsImpl._(
|
||||
topLevelPackage, workspaceRootPath,
|
||||
workspaceDependencies: workspaceDependencies);
|
||||
await Future.wait([
|
||||
for (var n in [topLevelPackage, ...extraPackageNames])
|
||||
workspace.addPackageNameToWorkspace(n)
|
||||
workspace.addPackageNameToWorkspace(n, allowUpdate)
|
||||
]);
|
||||
return workspace;
|
||||
}
|
||||
|
||||
Future<FantasySubPackage> addPackageNameToWorkspace(
|
||||
String packageName) async {
|
||||
String packageName, bool allowUpdate) async {
|
||||
FantasySubPackageSettings packageSettings =
|
||||
FantasySubPackageSettings.fromName(packageName);
|
||||
return await addPackageToWorkspace(packageSettings);
|
||||
return await addPackageToWorkspace(packageSettings, allowUpdate);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<FantasySubPackage> addPackageToWorkspace(
|
||||
FantasySubPackageSettings packageSettings,
|
||||
FantasySubPackageSettings packageSettings, bool allowUpdate,
|
||||
{Set<FantasySubPackageSettings> seenPackages}) async {
|
||||
seenPackages ??= {};
|
||||
if (seenPackages.contains(packageSettings)) return null;
|
||||
seenPackages.add(packageSettings);
|
||||
await _addPackageToWorkspace(packageSettings, seenPackages);
|
||||
return await _addPackageToWorkspace(
|
||||
packageSettings, allowUpdate, seenPackages);
|
||||
}
|
||||
|
||||
Future<FantasySubPackage> _addPackageToWorkspace(
|
||||
FantasySubPackageSettings packageSettings,
|
||||
bool allowUpdate,
|
||||
Set<FantasySubPackageSettings> seenPackages) async {
|
||||
FantasySubPackage fantasySubPackage =
|
||||
await super.addPackageToWorkspace(packageSettings);
|
||||
await super.addPackageToWorkspace(packageSettings, allowUpdate);
|
||||
String packageName = packageSettings.name;
|
||||
|
||||
await rewritePackageConfigWith(fantasySubPackage);
|
||||
|
@ -200,7 +210,8 @@ class FantasyWorkspaceTopLevelDevDepsImpl extends FantasyWorkspaceBase {
|
|||
|
||||
await Future.wait([
|
||||
for (var subPackageSettings in dependencies)
|
||||
addPackageToWorkspace(subPackageSettings, seenPackages: seenPackages)
|
||||
addPackageToWorkspace(subPackageSettings, allowUpdate,
|
||||
seenPackages: seenPackages)
|
||||
]);
|
||||
return fantasySubPackage;
|
||||
}
|
||||
|
|
|
@ -5,7 +5,9 @@ environment:
|
|||
dependencies:
|
||||
_fe_analyzer_shared: 1.0.0
|
||||
analyzer: ^0.37.0
|
||||
analyzer_plugin: ^0.2.2
|
||||
path: ^1.6.2
|
||||
yaml: any
|
||||
dev_dependencies:
|
||||
args: ^1.5.2
|
||||
mockito: any
|
||||
|
|
|
@ -114,7 +114,9 @@ class FantasyRepoE2ETest {
|
|||
// checked out.
|
||||
io.Directory repoRoot = io.Directory(path.join(tempDir.path, 'repoRoot'));
|
||||
await FantasyRepo.buildGitRepoFrom(
|
||||
FantasyRepoSettings('repoE2Etest', origRepoDir.path), repoRoot.path,
|
||||
FantasyRepoSettings('repoE2Etest', origRepoDir.path),
|
||||
repoRoot.path,
|
||||
true,
|
||||
fantasyRepoDependencies: fantasyRepoDependencies);
|
||||
|
||||
dotPackages = io.File(path.join(repoRoot.path, '.packages'));
|
||||
|
@ -141,7 +143,9 @@ class FantasyRepoE2ETest {
|
|||
// Finally, use the repoBuilder to update a repository from head and verify
|
||||
// we did it right.
|
||||
await FantasyRepo.buildGitRepoFrom(
|
||||
FantasyRepoSettings('repoE2Etest', origRepoDir.path), repoRoot.path,
|
||||
FantasyRepoSettings('repoE2Etest', origRepoDir.path),
|
||||
repoRoot.path,
|
||||
true,
|
||||
fantasyRepoDependencies: fantasyRepoDependencies);
|
||||
|
||||
aNewFile = io.File(path.join(repoRoot.path, 'hello_new_file_here'));
|
||||
|
@ -172,7 +176,7 @@ class FantasyRepoTest extends FilesystemTestBase {
|
|||
FantasyRepoSettings settings = FantasyRepoSettings.fromName(repoName);
|
||||
FantasyRepoDependencies fantasyRepoDependencies = FantasyRepoDependencies(
|
||||
launcher: mockLauncher, resourceProvider: resourceProvider);
|
||||
await FantasyRepo.buildGitRepoFrom(settings, repoPath,
|
||||
await FantasyRepo.buildGitRepoFrom(settings, repoPath, true,
|
||||
fantasyRepoDependencies: fantasyRepoDependencies);
|
||||
}
|
||||
|
||||
|
|
|
@ -69,6 +69,7 @@ class FantasySubPackageTest extends FilesystemTestBase {
|
|||
FantasySubPackage fantasySubPackage;
|
||||
Folder repoRoot;
|
||||
File pubspecYaml;
|
||||
File analysisOptionsYaml;
|
||||
|
||||
setUp() {
|
||||
super.setUp();
|
||||
|
@ -85,6 +86,8 @@ class FantasySubPackageTest extends FilesystemTestBase {
|
|||
// the pubspecYaml.
|
||||
pubspecYaml = resourceProvider
|
||||
.getFile(join(repoRootPath, 'unreal_package_dir', 'pubspec.yaml'));
|
||||
analysisOptionsYaml = resourceProvider.getFile(
|
||||
join(repoRootPath, 'unreal_package_dir', 'analysis_options.yaml'));
|
||||
}
|
||||
|
||||
test_recognizeAllDependencies() async {
|
||||
|
@ -149,4 +152,18 @@ class FantasySubPackageTest extends FilesystemTestBase {
|
|||
''');
|
||||
await fantasySubPackage.getPackageAllDependencies();
|
||||
}
|
||||
|
||||
test_analysisOptionsCreate() async {
|
||||
pubspecYaml.writeAsStringSync('''
|
||||
name: unreal_package
|
||||
''');
|
||||
await fantasySubPackage.enableExperimentHack();
|
||||
expect(analysisOptionsYaml.exists, isTrue);
|
||||
expect(
|
||||
analysisOptionsYaml.readAsStringSync(),
|
||||
'analyzer:\n'
|
||||
' enable-experiment:\n'
|
||||
' - non-nullable\n'
|
||||
'\n');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
// 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';
|
||||
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:nnbd_migration/src/fantasyland/fantasy_workspace.dart';
|
||||
import 'package:nnbd_migration/src/fantasyland/fantasy_workspace_impl.dart';
|
||||
|
@ -40,6 +38,7 @@ class FantasyWorkspaceIntegrationTest extends FilesystemTestBase {
|
|||
'test_package',
|
||||
['extra_package_1', 'extra_package_2'],
|
||||
convertPath('/fantasyland'),
|
||||
true,
|
||||
workspaceDependencies: workspaceDependencies);
|
||||
expect(getFolder('/fantasyland').exists, isTrue);
|
||||
for (var n in ['test_package', 'extra_package_1', 'extra_package_2']) {
|
||||
|
|
|
@ -91,8 +91,8 @@ class FantasyLandPackage extends Package {
|
|||
String name, Playground playground) async {
|
||||
return FantasyLandPackage._(
|
||||
name,
|
||||
await buildFantasyLand(
|
||||
name, [], path.join(playground.playgroundPath, '${name}__flat')));
|
||||
await buildFantasyLand(name, [],
|
||||
path.join(playground.playgroundPath, '${name}__flat'), true));
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
Loading…
Reference in a new issue