Skip to content

Commit

Permalink
Add --watch to _macro_tool for analyzer. (#99)
Browse files Browse the repository at this point in the history
  • Loading branch information
davidmorgan authored Oct 11, 2024
1 parent 23d034c commit a482288
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 28 deletions.
7 changes: 5 additions & 2 deletions pkgs/_macro_tool/bin/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ final argParser = ArgParser()
..addOption('workspace', help: 'Path to workspace.')
..addOption('packageConfig', help: 'Path to package config.')
..addOption('script', help: 'Path to script.')
..addFlag('skip-cleanup');
..addFlag('skip-cleanup',
help: 'Whether to skip delete of augmentations and revert of script.')
..addFlag('watch', help: 'Whether to watch for changes.');

Future<void> main(List<String> arguments) async {
final args = argParser.parse(arguments);
Expand All @@ -40,6 +42,7 @@ ${argParser.usage}''');
workspacePath: p.canonicalize(workspace),
packageConfigPath: p.canonicalize(packageConfig),
scriptPath: p.canonicalize(script),
skipCleanup: args['skip-cleanup'] as bool);
skipCleanup: args['skip-cleanup'] as bool,
watch: args['watch'] as bool);
await tool.run();
}
61 changes: 40 additions & 21 deletions pkgs/_macro_tool/lib/analyzer_macro_tool.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'dart:io';
import 'package:_analyzer_macros/macro_implementation.dart';
import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/diagnostic/diagnostic.dart';
import 'package:analyzer/src/summary2/macro_injected_impl.dart'
as injected_analyzer;
import 'package:macro_service/macro_service.dart';
Expand All @@ -18,7 +19,8 @@ class AnalyzerMacroTool extends MacroTool {
{required super.workspacePath,
required super.packageConfigPath,
required super.scriptPath,
required super.skipCleanup})
required super.skipCleanup,
required super.watch})
: super.internal();

/// Runs macros in [scriptFile] on the analyzer.
Expand All @@ -38,30 +40,47 @@ class AnalyzerMacroTool extends MacroTool {
version: ProtocolVersion.macros1),
packageConfig: Uri.file(packageConfigPath));

final resolvedLibrary = (await analysisContext.currentSession
.getResolvedLibrary(scriptPath)) as ResolvedLibraryResult;
ResolvedLibraryResult resolvedLibrary;
// `asBroadcastStream` so repeated use of `first` below waits for the next
// change.
var events = File(scriptPath).watch().asBroadcastStream();
var stopwatch = Stopwatch()..start();
while (true) {
resolvedLibrary = (await analysisContext.currentSession
.getResolvedLibrary(scriptPath)) as ResolvedLibraryResult;
print('Resolved in ${stopwatch.elapsedMilliseconds}ms.');

final errors = (await analysisContext.currentSession.getErrors(scriptPath))
as ErrorsResult;
if (errors.errors.isNotEmpty) {
print('Errors: ${errors.errors}');
}
final errors = (await analysisContext.currentSession
.getErrors(scriptPath)) as ErrorsResult;
final actualErrors =
errors.errors.where((e) => e.severity == Severity.error).toList();
if (actualErrors.isNotEmpty) {
print('Errors: $actualErrors');
}

final augmentationUnits =
resolvedLibrary.units.where((u) => u.isMacroPart).toList();
if (augmentationUnits.isEmpty) {
return false;
}
final augmentationUnits =
resolvedLibrary.units.where((u) => u.isMacroPart).toList();
if (augmentationUnits.isEmpty) {
return false;
}

print('Macro output (patched to use augment library): '
'$augmentationFilePath');
File(augmentationFilePath).writeAsStringSync(augmentationUnits
.single.content
// The analyzer produces augmentations in parts, but the CFE still
// wants them in augmentation libraries. Adjust the output accordingly.
.replaceAll('part of', 'augment library'));
print('Macro output (patched to use augment library): '
'$augmentationFilePath');
File(augmentationFilePath).writeAsStringSync(augmentationUnits
.single.content
// The analyzer produces augmentations in parts, but the CFE still
// wants them in augmentation libraries. Adjust the output accordingly.
.replaceAll('part of', 'augment library'));

return true;
if (!watch) return true;

print('Running with --watch, waiting for next change to script.');
await events.first;
print('Script changed, rerunning macro.');
stopwatch.reset();
analysisContext.changeFile(scriptPath);
await analysisContext.applyPendingFileChanges();
}
}

@override
Expand Down
5 changes: 4 additions & 1 deletion pkgs/_macro_tool/lib/cfe_macro_tool.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ class CfeMacroTool extends MacroTool {
{required super.workspacePath,
required super.packageConfigPath,
required super.scriptPath,
required super.skipCleanup})
required super.skipCleanup,
required super.watch})
: super.internal();

/// Runs macros in [scriptFile] using the CFE.
Expand All @@ -27,6 +28,8 @@ class CfeMacroTool extends MacroTool {
/// Returns whether an augmentation file was written.
@override
Future<bool> augment() async {
if (watch) throw UnimplementedError('--watch not implemented for CFE.');

// TODO(davidmorgan): this dill comes from the Dart SDK running the test,
// but `package:frontend_server` and `package:front_end` are used as a
// library, so we will see version skew breakage. Find a better way.
Expand Down
13 changes: 9 additions & 4 deletions pkgs/_macro_tool/lib/macro_tool.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,30 +15,35 @@ abstract class MacroTool {
String packageConfigPath;
String scriptPath;
bool skipCleanup;
bool watch;

MacroTool.internal(
{required this.workspacePath,
required this.packageConfigPath,
required this.scriptPath,
required this.skipCleanup});
required this.skipCleanup,
required this.watch});

factory MacroTool(
{required HostOption host,
required String workspacePath,
required String packageConfigPath,
required String scriptPath,
required bool skipCleanup}) =>
required bool skipCleanup,
required bool watch}) =>
host == HostOption.analyzer
? AnalyzerMacroTool(
workspacePath: workspacePath,
packageConfigPath: packageConfigPath,
scriptPath: scriptPath,
skipCleanup: skipCleanup)
skipCleanup: skipCleanup,
watch: watch)
: CfeMacroTool(
workspacePath: workspacePath,
packageConfigPath: packageConfigPath,
scriptPath: scriptPath,
skipCleanup: skipCleanup);
skipCleanup: skipCleanup,
watch: watch);

Future<void> run() async {
print('Running ${p.basename(scriptPath)} with macros on $this.');
Expand Down

0 comments on commit a482288

Please sign in to comment.