-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Build macros, run them, serve a (test) service, host, connect to it #20
Conversation
0501a0f
to
3c4a927
Compare
a5287c5
to
81605eb
Compare
@@ -2,22 +2,110 @@ | |||
// 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'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we separate out an interface here, and then have an implementation which imports dart:io
? It might anyways make sense to have an abstraction here, for kernel versions or a version which uses the front end as a library (this might live in the SDK, under pkg/front_end).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For sure this will end up with multiple implementations; added a TODO.
Some of them may come from code already in the analyzer and/or CFE.
// to by `macroImplementations` then pass them to `MacroClient.run` in | ||
// `package:_macro_client`. | ||
return BuiltMacroBundle(); | ||
File packageConfig, Iterable<QualifiedName> macroImplementations) async { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the interface probably shouldn't expose any dart:io
types - I would make the package config be something provided up front and stored in a field (also not part of the interface probably, just an implementation specific thing).
Otherwise, there is no need for a class at all here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the key question here is about the scope of MacroBuilder
: is it one builder for the host, one for the workspace, one for the workspace at a particular time ... wider scope allows more optimization, by which I mean reuse of (resident?) compilers and compile artifacts across builds.
I agree that File
does not seem like the right concept, what we are really talking about here is the version solve and workspace that we need to do a build: the builder is going to have to care about both and about changes to both. There are questions of file overlays when using the analyzer and multiroot file systems.
So a lot to unpack here :) which is why I have it at the simplest+wrong thing currently. I'll add a pile of TODOs for all the above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fwiw I don't think the version solve or workspace do matter - those are both encapsulated by the package config. And the package config is the only thing the compiler or analyzer should care about. It is the only unifying concept across all our environments.
I just don't think we want File (or any dart:io type) in the public API. Even if this was just a URI I probably would be happy :).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we probably do have to end up worrying about version solve + workspace in order to handle finding prebuilt macros and changes to macros; it's not needed for now though.
Sure, Uri works; done. Thanks.
} | ||
|
||
/// Creates the entrypoint script for [macros]. | ||
static String createBootstrap(List<QualifiedName> macros) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this need to be public?
If we want to unit test it, I would put it in a different library under lib/src
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the "private" packages, starting with an underscore, I put implementation directly under lib
instead of lib/src
; my expectation is that we end up folding them entirely into some package's src
so adding lib/src
now just seemed like noise.
So I'm thinking of this entire file as private.
Happy to do something else though, as you like.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do think there is a difference here worth encapsulating.
If I understand correctly, MacroBuilder will be public (ie: accessible to the CFE/Analyzer). But this function is not intended to be used by either, and should ideally not be exposed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes sense, moved out to lib/src
as you originally suggested. Thanks :)
script.writeln("import '${macro.uri}' as m$i;"); | ||
} | ||
script.write(''' | ||
import 'dart:convert'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: you might want to prefix all these, to be safe
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, done.
Future<BuiltMacroBundle> build() async { | ||
final scriptFile = File.fromUri(workspace.uri.resolve('bin/main.dart')); | ||
scriptFile.parent.createSync(recursive: true); | ||
scriptFile.writeAsStringSync(script.toString()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: it is a bit weird to be using sync I/O within an async function like this, I would just use async
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
class MacroRunner { | ||
void run(BuiltMacroBundle macro, HostEndpoint endpoint) {} | ||
/// Runs [macroBundle] connected to [endpoint]. | ||
void run( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems a bit odd to not have this return a Future - maybe just call it start
? It also seems like it should just be a top level method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rename done.
I expect it needs to be a class for the shutdown/cleanup TODO :)
} | ||
|
||
void _handleConnections() { | ||
serverSocket.forEach(_handleConnection); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could probably be inlined above
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
@override | ||
Future<Object> handle(Object request) async { | ||
_macroStartedRequestsController.add(request as MacroStartedRequest); | ||
return MacroStartedResponse() as Object; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this cast necessary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Extension types don't implement Object
unless it's explicitly stated :)
This gets resolved properly when we add union types for requests+responses in PR #22, so we don't use Object
and use the union type.
@@ -12,7 +12,7 @@ class DeclareX { | |||
// TODO(davidmorgan): this is a placeholder; make it do something, test it. | |||
class DeclareXImplementation implements Macro { | |||
@override | |||
MacroDescription get description => MacroDescription(runsInPhases: [3]); | |||
MacroDescription get description => MacroDescription(runsInPhases: [2]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems wrong for users to be creating this description, with explicit phase numbers like this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, added a TODO on the Macro
interface that MacroDescription
should be populated (as far as possible) by macro_builder
which can figure out the information and inject it in the bootstrap script, rather than asking the user macro code to do it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please see my general reply to your email about these PRs, as to why there are a lot of TODOs open instead of addressed: tl;dr so we can figure out where to reuse code instead of writing new.
Thanks, PTAL.
script.writeln("import '${macro.uri}' as m$i;"); | ||
} | ||
script.write(''' | ||
import 'dart:convert'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, done.
/// Throws on failure to build. | ||
Future<BuiltMacroBundle> build() async { | ||
final scriptFile = File.fromUri(workspace.uri.resolve('bin/main.dart')); | ||
scriptFile.parent.createSync(recursive: true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
Future<BuiltMacroBundle> build() async { | ||
final scriptFile = File.fromUri(workspace.uri.resolve('bin/main.dart')); | ||
scriptFile.parent.createSync(recursive: true); | ||
scriptFile.writeAsStringSync(script.toString()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
// | ||
// For now just use the command line. | ||
|
||
final result = Process.runSync('dart', ['compile', 'exe', 'bin/main.dart'], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done; although the resolved executable is also not always correct, if this is used from an AOT-compiled binary. Added a TODO as well :)
// | ||
// For now just use the command line. | ||
|
||
final result = Process.runSync('dart', ['compile', 'exe', 'bin/main.dart'], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
class MacroRunner { | ||
void run(BuiltMacroBundle macro, HostEndpoint endpoint) {} | ||
/// Runs [macroBundle] connected to [endpoint]. | ||
void run( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rename done.
I expect it needs to be a class for the shutdown/cleanup TODO :)
} | ||
|
||
void _handleConnections() { | ||
serverSocket.forEach(_handleConnection); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
@override | ||
Future<Object> handle(Object request) async { | ||
_macroStartedRequestsController.add(request as MacroStartedRequest); | ||
return MacroStartedResponse() as Object; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Extension types don't implement Object
unless it's explicitly stated :)
This gets resolved properly when we add union types for requests+responses in PR #22, so we don't use Object
and use the union type.
@@ -12,7 +12,7 @@ class DeclareX { | |||
// TODO(davidmorgan): this is a placeholder; make it do something, test it. | |||
class DeclareXImplementation implements Macro { | |||
@override | |||
MacroDescription get description => MacroDescription(runsInPhases: [3]); | |||
MacroDescription get description => MacroDescription(runsInPhases: [2]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, added a TODO on the Macro
interface that MacroDescription
should be populated (as far as possible) by macro_builder
which can figure out the information and inject it in the bootstrap script, rather than asking the user macro code to do it.
} | ||
|
||
/// Creates the entrypoint script for [macros]. | ||
static String createBootstrap(List<QualifiedName> macros) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do think there is a difference here worth encapsulating.
If I understand correctly, MacroBuilder will be public (ie: accessible to the CFE/Analyzer). But this function is not intended to be used by either, and should ideally not be exposed.
final MacroServer macroServer; | ||
final ListOfServices services; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 Fine to follow up in later prs
5425428
to
54a861c
Compare
54a861c
to
be9d3d7
Compare
Flesh out much of the end to end functionality:
Next after this: add first introspection and augmentation RPCs/capability; then it will be an actual e2e macro.