From 92b6187307a165d9dba1218dd61fd934f84a3511 Mon Sep 17 00:00:00 2001 From: Jake Macdonald Date: Fri, 16 Feb 2024 20:57:26 +0000 Subject: [PATCH 1/4] add macro e2e test --- _test/analysis_options.yaml | 3 ++ _test/build.dart2js.yaml | 1 + _test/build.post_process.yaml | 1 + _test/build.throws.yaml | 1 + _test/build.yaml | 1 + _test/lib/macros/debug_to_string.dart | 22 ++++++++++ _test/pkgs/provides_builder/build.yaml | 5 +++ _test/pkgs/provides_builder/lib/builders.dart | 25 +++++++++++ _test/pubspec.yaml | 41 ++++++++++++++++++- _test/test/macro_test.dart | 25 +++++++++++ _test/web/macros/index.html | 14 +++++++ _test/web/macros/main.dart | 19 +++++++++ 12 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 _test/lib/macros/debug_to_string.dart create mode 100644 _test/test/macro_test.dart create mode 100644 _test/web/macros/index.html create mode 100644 _test/web/macros/main.dart diff --git a/_test/analysis_options.yaml b/_test/analysis_options.yaml index e0815af9d..e52098ef6 100644 --- a/_test/analysis_options.yaml +++ b/_test/analysis_options.yaml @@ -1 +1,4 @@ include: ../analysis/analysis_options.yaml +analyzer: + enable-experiment: + - macros diff --git a/_test/build.dart2js.yaml b/_test/build.dart2js.yaml index fb2f7083c..bd823a9c3 100644 --- a/_test/build.dart2js.yaml +++ b/_test/build.dart2js.yaml @@ -8,6 +8,7 @@ targets: - --enable-asserts generate_for: - web/main.dart + - web/macros/main.dart - web/sub/main.dart - test/configurable_uri_test.dart - test/configurable_uri_test.dart.browser_test.dart diff --git a/_test/build.post_process.yaml b/_test/build.post_process.yaml index cd997dc8b..a8f40c732 100644 --- a/_test/build.post_process.yaml +++ b/_test/build.post_process.yaml @@ -7,6 +7,7 @@ targets: build_web_compilers:entrypoint: generate_for: - web/main.dart + - web/macros/main.dart - web/sub/main.dart - test/hello_world_test.dart - test/hello_world_test.dart.browser_test.dart diff --git a/_test/build.throws.yaml b/_test/build.throws.yaml index a6b5d8ac4..e70313743 100644 --- a/_test/build.throws.yaml +++ b/_test/build.throws.yaml @@ -7,6 +7,7 @@ targets: build_web_compilers:entrypoint: generate_for: - web/main.dart + - web/macros/main.dart - web/sub/main.dart - test/hello_world_test.dart - test/hello_world_test.dart.browser_test.dart diff --git a/_test/build.yaml b/_test/build.yaml index ce556f218..b588e38b8 100644 --- a/_test/build.yaml +++ b/_test/build.yaml @@ -4,6 +4,7 @@ targets: build_web_compilers:entrypoint: generate_for: - web/main.dart + - web/macros/main.dart - web/sub/main.dart - test/configurable_uri_test.dart.browser_test.dart - test/hello_world_test.dart.browser_test.dart diff --git a/_test/lib/macros/debug_to_string.dart b/_test/lib/macros/debug_to_string.dart new file mode 100644 index 000000000..7725fd46d --- /dev/null +++ b/_test/lib/macros/debug_to_string.dart @@ -0,0 +1,22 @@ +import 'dart:async'; + +import 'package:_fe_analyzer_shared/src/macros/api.dart'; + +macro class DebugToString implements ClassDeclarationsMacro { + const DebugToString(); + + @override + Future buildDeclarationsForClass( + ClassDeclaration clazz, MemberDeclarationBuilder builder) async { + final fields = await builder.fieldsOf(clazz); + builder.declareInType(DeclarationCode.fromParts([ + 'String debugToString() => """\n${clazz.identifier.name} {\n', + for (var field in fields) ...[ + ' ${field.identifier.name}: \${', + field.identifier, + '}\n', + ], + '}""";', + ])); + } +} diff --git a/_test/pkgs/provides_builder/build.yaml b/_test/pkgs/provides_builder/build.yaml index 21b8d8bf6..01a32739b 100644 --- a/_test/pkgs/provides_builder/build.yaml +++ b/_test/pkgs/provides_builder/build.yaml @@ -1,4 +1,9 @@ builders: + has_debug_to_string_builder: + import: "package:provides_builder/builders.dart" + builder_factories: ["hasDebugToStringBuilder"] + build_extensions: {".fail": [".hasDebugToString"]} + auto_apply: dependents some_builder: import: "package:provides_builder/builders.dart" builder_factories: ["someBuilder"] diff --git a/_test/pkgs/provides_builder/lib/builders.dart b/_test/pkgs/provides_builder/lib/builders.dart index ab5782da6..fbc5fcac7 100644 --- a/_test/pkgs/provides_builder/lib/builders.dart +++ b/_test/pkgs/provides_builder/lib/builders.dart @@ -4,6 +4,7 @@ import 'dart:async'; +import 'package:analyzer/dart/element/element.dart'; import 'package:build/build.dart'; class _SomeBuilder implements Builder { @@ -60,9 +61,33 @@ class _ThrowingBuilder extends Builder { } } +class _HasDebugToStringBuilder extends Builder { + @override + final buildExtensions = { + '.dart': ['.hasDebugToString'] + }; + + @override + Future build(BuildStep buildStep) async { + final library = await buildStep.inputLibrary; + final buffer = StringBuffer()..writeln('classes:'); + for (var element in library.topLevelElements) { + if (element is! ClassElement) continue; + var debugToString = element.getMethod('debugToString') ?? + element.augmentation?.getMethod('debugToString'); + buffer.writeln(' ${element.name}:'); + buffer.writeln(' hasDebugToString: ${debugToString != null}'); + } + await buildStep.writeAsString( + buildStep.inputId.changeExtension('.hasDebugToString'), + buffer.toString()); + } +} + Builder someBuilder(BuilderOptions options) => _SomeBuilder.fromOptions(options); Builder notApplied(BuilderOptions options) => _SomeBuilder.fromOptions(options); PostProcessBuilder somePostProcessBuilder(BuilderOptions options) => _SomePostProcessBuilder.fromOptions(options); Builder throwingBuilder(_) => _ThrowingBuilder(); +Builder hasDebugToStringBuilder(_) => _HasDebugToStringBuilder(); diff --git a/_test/pubspec.yaml b/_test/pubspec.yaml index 7ba70c14a..809e6d06e 100644 --- a/_test/pubspec.yaml +++ b/_test/pubspec.yaml @@ -2,9 +2,10 @@ name: _test publish_to: none environment: - sdk: ^3.0.0 + sdk: ^3.3.0-0 dev_dependencies: + _fe_analyzer_shared: any analyzer: any build: any build_config: any @@ -23,6 +24,14 @@ dev_dependencies: test_process: ^2.0.0 dependency_overrides: + _fe_analyzer_shared: + path: ../../dart-lang-sdk/sdk/pkg/_fe_analyzer_shared + _js_interop_checks: + path: ../../dart-lang-sdk/sdk/pkg/_js_interop_checks + analyzer: + path: ../../dart-lang-sdk/sdk/pkg/analyzer + build_integration: + path: ../../dart-lang-sdk/sdk/pkg/build_integration build: path: ../build build_config: @@ -41,5 +50,35 @@ dependency_overrides: path: ../build_test build_web_compilers: path: ../build_web_compilers + compiler: + path: ../../dart-lang-sdk/sdk/pkg/compiler + dart2js_info: + path: ../../dart-lang-sdk/sdk/pkg/dart2js_info + dart2wasm: + path: ../../dart-lang-sdk/sdk/pkg/dart2wasm + dev_compiler: + path: ../../dart-lang-sdk/sdk/pkg/dev_compiler + dart_style: + path: ../../dart-lang-sdk/sdk/third_party/pkg/dart_style + front_end: + path: ../../dart-lang-sdk/sdk/pkg/front_end + frontend_server: + path: ../../dart-lang-sdk/sdk/pkg/frontend_server + js_ast: + path: ../../dart-lang-sdk/sdk/pkg/js_ast + js_runtime: + path: ../../dart-lang-sdk/sdk/pkg/js_runtime + js_shared: + path: ../../dart-lang-sdk/sdk/pkg/js_shared + kernel: + path: ../../dart-lang-sdk/sdk/pkg/kernel + meta: + path: ../../dart-lang-sdk/sdk/pkg/meta + mmap: + path: ../../dart-lang-sdk/sdk/pkg/mmap scratch_space: path: ../scratch_space + vm: + path: ../../dart-lang-sdk/sdk/pkg/vm + wasm_builder: + path: ../../dart-lang-sdk/sdk/pkg/wasm_builder diff --git a/_test/test/macro_test.dart b/_test/test/macro_test.dart new file mode 100644 index 000000000..3810413f4 --- /dev/null +++ b/_test/test/macro_test.dart @@ -0,0 +1,25 @@ +// 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 'package:test/test.dart'; + +import 'package:_test/macros/debug_to_string.dart'; + +void main() { + test('macro stuff is generated', () { + expect(User('Jill', 25).debugToString(), equals(''' +User { + name: Jill + age: 25 +}''')); + }); +} + +@DebugToString() +class User { + final String name; + final int age; + + User(this.name, this.age); +} diff --git a/_test/web/macros/index.html b/_test/web/macros/index.html new file mode 100644 index 000000000..5de940ff5 --- /dev/null +++ b/_test/web/macros/index.html @@ -0,0 +1,14 @@ + + + + + build integration tests - macros + + + + + + + + + diff --git a/_test/web/macros/main.dart b/_test/web/macros/main.dart new file mode 100644 index 000000000..9e46dae93 --- /dev/null +++ b/_test/web/macros/main.dart @@ -0,0 +1,19 @@ +// 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:html'; + +import 'package:_test/macros/debug_to_string.dart'; + +void main() { + document.body!.text = User('Jill', 25).debugToString(); +} + +@DebugToString() +class User { + final String name; + final int age; + + User(this.name, this.age); +} From 63a7decaf93b373ee980fb7bec8ca2f035d51f06 Mon Sep 17 00:00:00 2001 From: Jake Macdonald Date: Fri, 16 Feb 2024 21:29:36 +0000 Subject: [PATCH 2/4] add copyright --- _test/lib/macros/debug_to_string.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/_test/lib/macros/debug_to_string.dart b/_test/lib/macros/debug_to_string.dart index 7725fd46d..a41600db8 100644 --- a/_test/lib/macros/debug_to_string.dart +++ b/_test/lib/macros/debug_to_string.dart @@ -1,3 +1,7 @@ +// 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:async'; import 'package:_fe_analyzer_shared/src/macros/api.dart'; From 1c895c3eb12fd2a56a00ea6f59ba1ae9bc23ba23 Mon Sep 17 00:00:00 2001 From: Jake Macdonald Date: Fri, 16 Feb 2024 21:49:33 +0000 Subject: [PATCH 3/4] fix some lints --- _test/lib/macros/debug_to_string.dart | 1 + _test/pubspec.yaml | 12 +++++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/_test/lib/macros/debug_to_string.dart b/_test/lib/macros/debug_to_string.dart index a41600db8..0101abdaa 100644 --- a/_test/lib/macros/debug_to_string.dart +++ b/_test/lib/macros/debug_to_string.dart @@ -4,6 +4,7 @@ import 'dart:async'; +// ignore: implementation_imports import 'package:_fe_analyzer_shared/src/macros/api.dart'; macro class DebugToString implements ClassDeclarationsMacro { diff --git a/_test/pubspec.yaml b/_test/pubspec.yaml index 809e6d06e..9a8a1fa7b 100644 --- a/_test/pubspec.yaml +++ b/_test/pubspec.yaml @@ -4,8 +4,10 @@ publish_to: none environment: sdk: ^3.3.0-0 -dev_dependencies: +dependencies: _fe_analyzer_shared: any + +dev_dependencies: analyzer: any build: any build_config: any @@ -30,14 +32,14 @@ dependency_overrides: path: ../../dart-lang-sdk/sdk/pkg/_js_interop_checks analyzer: path: ../../dart-lang-sdk/sdk/pkg/analyzer - build_integration: - path: ../../dart-lang-sdk/sdk/pkg/build_integration build: path: ../build build_config: path: ../build_config build_daemon: path: ../build_daemon + build_integration: + path: ../../dart-lang-sdk/sdk/pkg/build_integration build_modules: path: ../build_modules build_resolvers: @@ -56,10 +58,10 @@ dependency_overrides: path: ../../dart-lang-sdk/sdk/pkg/dart2js_info dart2wasm: path: ../../dart-lang-sdk/sdk/pkg/dart2wasm - dev_compiler: - path: ../../dart-lang-sdk/sdk/pkg/dev_compiler dart_style: path: ../../dart-lang-sdk/sdk/third_party/pkg/dart_style + dev_compiler: + path: ../../dart-lang-sdk/sdk/pkg/dev_compiler front_end: path: ../../dart-lang-sdk/sdk/pkg/front_end frontend_server: From 109769bb37755f5efc569a790b7b336c1709de82 Mon Sep 17 00:00:00 2001 From: Jake Macdonald Date: Tue, 20 Feb 2024 19:07:00 +0000 Subject: [PATCH 4/4] provide a valid package config and extra dependencies for macro compilation --- _test/test/macro_test.dart | 3 ++- build_resolvers/lib/src/analysis_driver.dart | 19 +++++++++++++++++++ .../lib/src/build_asset_uri_resolver.dart | 15 ++++++++++++++- 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/_test/test/macro_test.dart b/_test/test/macro_test.dart index 3810413f4..3dd8114f4 100644 --- a/_test/test/macro_test.dart +++ b/_test/test/macro_test.dart @@ -2,9 +2,10 @@ // 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:_test/macros/debug_to_string.dart'; + import 'package:test/test.dart'; -import 'package:_test/macros/debug_to_string.dart'; void main() { test('macro stuff is generated', () { diff --git a/build_resolvers/lib/src/analysis_driver.dart b/build_resolvers/lib/src/analysis_driver.dart index 2d5faa033..48e0ade6d 100644 --- a/build_resolvers/lib/src/analysis_driver.dart +++ b/build_resolvers/lib/src/analysis_driver.dart @@ -2,6 +2,7 @@ // 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:convert'; import 'dart:io'; import 'package:analyzer/file_system/file_system.dart' show ResourceProvider; @@ -23,6 +24,24 @@ Future analysisDriver( String sdkSummaryPath, PackageConfig packageConfig, ) async { + // TODO: Necessary for kernel compilation of macros. Should the analyzer + // provide this implicitly based on its Packages instance? + buildAssetUriResolver.resourceProvider.newFile( + '/.dart_tool/package_config.json', + jsonEncode({ + 'configVersion': 2, + 'packages': [ + for (var package in packageConfig.packages) + { + 'name': package.name, + 'rootUri': 'file:///${package.name}', + 'packageUri': 'lib/', + if (package.languageVersion != null) + 'languageVersion': '${package.languageVersion!.major}.' + '${package.languageVersion!.minor}', + }, + ], + })); return createAnalysisDriver( analysisOptions: analysisOptions, packages: _buildAnalyzerPackages( diff --git a/build_resolvers/lib/src/build_asset_uri_resolver.dart b/build_resolvers/lib/src/build_asset_uri_resolver.dart index c7a9aa5bd..de3dd8c5d 100644 --- a/build_resolvers/lib/src/build_asset_uri_resolver.dart +++ b/build_resolvers/lib/src/build_asset_uri_resolver.dart @@ -261,7 +261,20 @@ Set _parseDependencies(String content, AssetId from) => HashSet.of( .whereType() .where((uriContent) => !_ignoredSchemes.any(Uri.parse(uriContent).isScheme)) - .map((content) => AssetId.resolve(Uri.parse(content), from: from)), + .map((content) => AssetId.resolve(Uri.parse(content), from: from)) + // TODO: Something better here? We assume anything depending on the + // macro APIs is a macro, and the bootstrap program we create for + // those libraries will require the macro implementations, but there + // is no transitive dependency exposed. + .followedBy( + from == AssetId('_fe_analyzer_shared', 'lib/src/macros/api.dart') + ? [ + AssetId('_fe_analyzer_shared', + 'lib/src/macros/executor/client.dart'), + AssetId('_fe_analyzer_shared', + 'lib/src/macros/executor/serialization.dart'), + ] + : const []), ); /// Read the (potentially) cached dependencies of [id] based on parsing the