-
Notifications
You must be signed in to change notification settings - Fork 112
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
feat: support binding to generated package #285
feat: support binding to generated package #285
Conversation
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.
Cool, thanks for figuring this out! I'm very happy with how little code it turned out to be, and the tests are very convincing that it works.
One question I have is whether we should allow any valid relative path in the genqlient.yaml
, i.e. you could say ./mypkg/subpkg
if that's what you want. That seems like it could be quite convenient rather than writing out the name of the current package a bunch of times. In that case we'd probably want to say .
means the directory with the genqlient.yaml
. Although, maybe it's better to say you have to write it out, like you do in Go import blocks. In fact I think these changes would solve the problem without allowing special .
syntax at all -- you would just do what you did in the first two test cases. I think I'd prefer to either fully support the usual, or stick to what we have, since config syntax is always confusing and can be more annoying to change later.
Beyond that I think my questions are about whether running this in the real world could hit weird cases we don't really see in a test. I put some comments inline. But probably if we can't figure those out we can just see if anyone complains.
if err != nil { | ||
return fmt.Errorf("loading generated package: %w", err) | ||
} |
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 wonder if this is safe. Like would this break things if the directory where you're putting the output is empty (as it would be if your output is in a separate package and this is the first run of genqlient)? On the other hand swallowing the error seems dangerous, and it's not clear how easy it is to know if we actually need packagePath
. Maybe instead we should just look at baseDir
and then add the suffix ourselves? (Is that safe?)
(For that matter, does this require that baseDir
is a valid Go package? Or at least one with at least one Go file with valid package clause? Maybe that's fine; if you have some kind of monorepo where all the Go is in a subdirectory you can just put genqlient.yaml
in that directory.)
If we don't know the answer, it's probably not a huge deal; it doesn't really affect the API surface we are committing to so if we break someone's workflow we can fix it then.
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.
would this break things if the directory where you're putting the output is empty
Yes, it would break, but that would be the same as trying to bind to an empty package, right?
does this require that baseDir is a valid Go package?
It shouldn't: it's just used as a base for the package search.
@@ -203,6 +223,7 @@ func (c *Config) ValidateAndFillDefaults(baseDir string) error { | |||
|
|||
mode := packages.NeedDeps | packages.NeedTypes | |||
pkgs, err := packages.Load(&packages.Config{ | |||
Dir: c.baseDir, |
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.
Thinking out loud: this should be ok for third-party packages because it's just the directory where we run the build tool, so if binding.Package
is absolute then it'll still return the right package (and, if relevant, will now respect any versions/replaces in the module, if they differ from those in the current directory, which is probably actually better than current behavior, in the rare case where it matters).
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, exactly. This is just needed to support the "relative import" hack we use above, which isn't usual, but is supported.
@@ -21,24 +21,41 @@ const ( | |||
// buildGoFile returns an error if the given Go code is not valid. | |||
// | |||
// namePrefix is used for the temp-file, and is just for debugging. | |||
func buildGoFile(namePrefix string, content []byte) error { | |||
func buildGoFile(namePrefix string, content []byte, extraFiles ...string) error { |
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.
nice!
# | ||
# Using "." is a shorthand for the package containing the generated file. | ||
# Both allow the generated code to live in the same package as its types. |
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 would have some concerns about actually using the generated package here. I worry it would end up being circular in ways that are hard to understand -- in that now genqlient depends on genqlient-generated types. I don't think we need to ban it, but maybe we should say in the comment here that it's not a good idea? Or does it actually work fine?
Also, whatever we settle on for the syntax, can you add it to the docs for bindings
above too?
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.
now genqlient depends on genqlient-generated types
Yes, a warning might be appropriate, but it should work as long as the user avoids name conflicts.
I've added a "double generate" test to try to cover this case and made it more explicit in the comment.
can you add it to the docs for bindings above too
The shorthand is intentionally only supported in the "whole package" mode. Otherwise one would end up with ..ID
or some other weird syntax to reference the types inside the package. Just didn't feel like it was worth it. WDYT?
This allows one to generate code in an existing package without causing import loops.
ae9f207
to
18cb77e
Compare
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 for the feedback and sorry for the late reply! Vacation got in the way!
PTAL again! 🙏
# | ||
# Using "." is a shorthand for the package containing the generated file. | ||
# Both allow the generated code to live in the same package as its types. |
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.
now genqlient depends on genqlient-generated types
Yes, a warning might be appropriate, but it should work as long as the user avoids name conflicts.
I've added a "double generate" test to try to cover this case and made it more explicit in the comment.
can you add it to the docs for bindings above too
The shorthand is intentionally only supported in the "whole package" mode. Otherwise one would end up with ..ID
or some other weird syntax to reference the types inside the package. Just didn't feel like it was worth it. WDYT?
if err != nil { | ||
return fmt.Errorf("loading generated package: %w", err) | ||
} |
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.
would this break things if the directory where you're putting the output is empty
Yes, it would break, but that would be the same as trying to bind to an empty package, right?
does this require that baseDir is a valid Go package?
It shouldn't: it's just used as a base for the package search.
@@ -203,6 +223,7 @@ func (c *Config) ValidateAndFillDefaults(baseDir string) error { | |||
|
|||
mode := packages.NeedDeps | packages.NeedTypes | |||
pkgs, err := packages.Load(&packages.Config{ | |||
Dir: c.baseDir, |
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, exactly. This is just needed to support the "relative import" hack we use above, which isn't usual, but is supported.
Closing in favor of #316 which makes this as a narrower change to support existing syntax. If you're interested, you could revisit adding the shorthand ( |
This allows one to generate code in an existing package without causing import loops.
It is now possible to use something like the following (assuming we're in module
github.com/foo/bar
):or (shorthand variant of the example above)
or
The last example assumes some file in
generated/
defines the typeBar
.Closes #283