-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
20c1bf1
commit cbf0013
Showing
8 changed files
with
253 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
analyzer: | ||
enable-experiment: | ||
- enhanced-parts | ||
- macros |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,3 +6,5 @@ import 'package:_test_macros/declare_x_macro.dart'; | |
|
||
@DeclareX() | ||
class Foo {} | ||
|
||
void main() => print(Foo().x); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
// Copyright (c) 2024, 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'; | ||
|
||
import 'package:_macro_tool/macro_tool.dart'; | ||
import 'package:args/args.dart'; | ||
import 'package:path/path.dart' as p; | ||
|
||
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'); | ||
|
||
Future<void> main(List<String> arguments) async { | ||
final args = argParser.parse(arguments); | ||
final workspace = args['workspace'] as String?; | ||
final packageConfig = args['packageConfig'] as String?; | ||
final script = args['script'] as String?; | ||
|
||
if (workspace == null || packageConfig == null || script == null) { | ||
print(''' | ||
Runs a Dart script with `dart_model` macros. | ||
${argParser.usage}'''); | ||
exit(1); | ||
} | ||
|
||
final tool = MacroTool( | ||
workspacePath: p.canonicalize(workspace), | ||
packageConfigPath: p.canonicalize(packageConfig), | ||
scriptPath: p.canonicalize(script), | ||
skipCleanup: args['skip-cleanup'] as bool); | ||
await tool.run(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
// Copyright (c) 2024, 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'; | ||
|
||
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/src/summary2/macro_injected_impl.dart' as injected; | ||
import 'package:macro_service/macro_service.dart'; | ||
import 'package:path/path.dart' as p; | ||
|
||
class MacroTool { | ||
String workspacePath; | ||
String packageConfigPath; | ||
String scriptPath; | ||
bool skipCleanup; | ||
|
||
MacroTool( | ||
{required this.workspacePath, | ||
required this.packageConfigPath, | ||
required this.scriptPath, | ||
required this.skipCleanup}); | ||
|
||
Future<void> run() async { | ||
print('Running ${p.basename(scriptPath)} with macros.'); | ||
print('~~~'); | ||
print('Package config: $packageConfigPath'); | ||
print('Workspace: $workspacePath'); | ||
print('Script: $scriptPath'); | ||
|
||
// TODO(davidmorgan): make it an option to run with the CFE instead. | ||
if (!await _augmentUsingAnalyzer()) { | ||
print('No augmentation was generated, nothing to do, exiting.'); | ||
exit(0); | ||
} | ||
|
||
_addImportAugment(); | ||
|
||
try { | ||
print('~~~ running, output follows'); | ||
final result = Process.runSync( | ||
Platform.resolvedExecutable, | ||
[ | ||
'run', | ||
'--enable-experiment=macros', | ||
'--enable-experiment=enhanced-parts', | ||
'--packages=$packageConfigPath', | ||
scriptPath | ||
], | ||
workingDirectory: workspacePath, | ||
); | ||
stdout.write(result.stdout); | ||
stderr.write(result.stderr); | ||
exitCode = result.exitCode; | ||
} finally { | ||
if (skipCleanup) { | ||
print('~~~ done running, skipping cleanup because --skip-cleanup'); | ||
} else { | ||
print('~~~ done running, cleanup follows'); | ||
_removeImportAugment(); | ||
_removeAugmentations(); | ||
} | ||
} | ||
|
||
// The analyzer seems to prevent exit. | ||
exit(0); | ||
} | ||
|
||
/// The path where macro-generated augmentations will be written. | ||
String get _augmentationFilePath => '$scriptPath.macro_tool_output'; | ||
|
||
/// Runs macros in [scriptFile]. | ||
/// | ||
/// Writes any augmentation to [_augmentationFilePath]. | ||
/// | ||
/// Returns whether an augmentation file was written. | ||
Future<bool> _augmentUsingAnalyzer() async { | ||
final scriptUri = Directory.current.uri.resolve(scriptPath); | ||
final contextCollection = | ||
AnalysisContextCollection(includedPaths: [workspacePath]); | ||
final analysisContext = contextCollection.contexts.first; | ||
injected.macroImplementation = await AnalyzerMacroImplementation.start( | ||
protocol: Protocol( | ||
encoding: ProtocolEncoding.binary, | ||
version: ProtocolVersion.macros1), | ||
packageConfig: File(packageConfigPath).uri); | ||
|
||
final resolvedLibrary = (await analysisContext.currentSession | ||
.getResolvedLibrary(scriptUri.toFilePath())) as ResolvedLibraryResult; | ||
|
||
final errors = (await analysisContext.currentSession | ||
.getErrors(scriptUri.toFilePath())) as ErrorsResult; | ||
if (errors.errors.isNotEmpty) { | ||
print('Errors: ${errors.errors}'); | ||
} | ||
|
||
final augmentationUnits = | ||
resolvedLibrary.units.where((u) => u.isMacroAugmentation).toList(); | ||
if (augmentationUnits.isEmpty) { | ||
return false; | ||
} | ||
|
||
print('Macro output (patched to use augment library): ' | ||
'$_augmentationFilePath'); | ||
File(_augmentationFilePath).writeAsStringSync(augmentationUnits | ||
.first.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; | ||
} | ||
|
||
/// Deletes the augmentation file created by this tool. | ||
void _removeAugmentations() { | ||
final scriptUri = Directory.current.uri.resolve(scriptPath); | ||
final out = scriptUri.toFilePath() + '.macro_tool_output'; | ||
print('Deleting: $out'); | ||
File(out).deleteSync(); | ||
} | ||
|
||
/// The path where the script file will be backed up before modification. | ||
String get _backupScriptPath => '$scriptPath.backup'; | ||
|
||
/// Adds `import augment` of the augmentation file. | ||
/// | ||
/// When macros run in the analyzer or CFE this inclusion of the augmentation | ||
/// output is automatic, but for `macro_tool` it has to be patched in. | ||
void _addImportAugment() { | ||
print('Patching to import augmentations: $scriptPath'); | ||
final file = File(scriptPath); | ||
// Back up the unmodified file for [_removeImportAugment]. | ||
file.copySync(_backupScriptPath); | ||
|
||
final partName = p.basename(_augmentationFilePath); | ||
final line = "import augment '$partName'; // added by macro_tool"; | ||
final lines = file.readAsLinesSync(); | ||
final lastImport = lines.lastIndexWhere((l) => l.startsWith('import')); | ||
|
||
final insertAt = lastImport == -1 ? 0 : lastImport + 1; | ||
lines.insert(insertAt, line); | ||
file.writeAsStringSync(lines.map((l) => l + '\n').join()); | ||
} | ||
|
||
/// Reverts the script file. | ||
void _removeImportAugment() { | ||
print('Reverting: $scriptPath'); | ||
File(_backupScriptPath).renameSync(scriptPath); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
sdk: | ||
- pubspec | ||
- dev | ||
|
||
stages: | ||
- analyze_and_format: | ||
- analyze: --fatal-infos . | ||
- format: | ||
sdk: | ||
- dev |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
name: _macro_tool | ||
publish-to: none | ||
description: >- | ||
CLI for running macros and for building code with macros applied. | ||
resolution: workspace | ||
environment: | ||
sdk: ^3.6.0-198.0.dev | ||
|
||
dependencies: | ||
_analyzer_macros: any | ||
|
||
dev_dependencies: | ||
dart_flutter_team_lints: ^3.0.0 | ||
test: ^1.25.7 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters