@@ -3,8 +3,6 @@ package generator
33import (
44 "bytes"
55 "context"
6- "crypto/sha256"
7- "encoding/hex"
86 "errors"
97 "fmt"
108 "io"
@@ -25,7 +23,6 @@ import (
2523
2624 "github.com/mercari/grpc-federation/compiler"
2725 "github.com/mercari/grpc-federation/grpc/federation/generator"
28- "github.com/mercari/grpc-federation/grpc/federation/generator/plugin"
2926 "github.com/mercari/grpc-federation/resolver"
3027 "github.com/mercari/grpc-federation/source"
3128 "github.com/mercari/grpc-federation/validator"
@@ -47,6 +44,7 @@ type Generator struct {
4744 postProcessHandler PostProcessHandler
4845 buildCacheMap BuildCacheMap
4946 absPathToRelativePath map [string ]string
47+ wasmPluginCache * wasmPluginCache
5048}
5149
5250type Option func (* Generator ) error
@@ -135,9 +133,18 @@ func New(cfg Config) *Generator {
135133 validator : validator .New (),
136134 importPaths : cfg .Imports ,
137135 absPathToRelativePath : make (map [string ]string ),
136+ wasmPluginCache : newWasmPluginCache (),
138137 }
139138}
140139
140+ // Close releases resources held by the Generator, including cached WASM plugin runtimes.
141+ func (g * Generator ) Close (ctx context.Context ) error {
142+ if g .wasmPluginCache != nil {
143+ return g .wasmPluginCache .Close (ctx )
144+ }
145+ return nil
146+ }
147+
141148func (g * Generator ) SetPostProcessHandler (postProcessHandler func (ctx context.Context , path string , result Result ) error ) {
142149 g .postProcessHandler = postProcessHandler
143150}
@@ -182,7 +189,7 @@ func (g *Generator) Generate(ctx context.Context, protoPath string, opts ...Opti
182189 }
183190 results = append (results , result )
184191 }
185- pluginResp , err := evalAllCodeGenerationPlugin (ctx , results , g .federationGeneratorOption )
192+ pluginResp , err := evalAllCodeGenerationPlugin (ctx , results , g .federationGeneratorOption , g . wasmPluginCache )
186193 if err != nil {
187194 return err
188195 }
@@ -359,7 +366,7 @@ func (g *Generator) setWatcher(w *Watcher) error {
359366 results = append (results , result )
360367 }
361368 results = append (results , g .otherResults (path )... )
362- pluginResp , err := evalAllCodeGenerationPlugin (ctx , results , g .federationGeneratorOption )
369+ pluginResp , err := evalAllCodeGenerationPlugin (ctx , results , g .federationGeneratorOption , g . wasmPluginCache )
363370 if err != nil {
364371 log .Printf ("failed to run code generator plugin: %+v" , err )
365372 }
@@ -651,7 +658,7 @@ func (g *Generator) fileNameWithoutExt(name string) string {
651658 return name [:len (name )- len (filepath .Ext (name ))]
652659}
653660
654- func CreateCodeGeneratorResponse (ctx context.Context , req * pluginpb.CodeGeneratorRequest ) (* pluginpb.CodeGeneratorResponse , error ) {
661+ func CreateCodeGeneratorResponse (ctx context.Context , req * pluginpb.CodeGeneratorRequest ) (_ * pluginpb.CodeGeneratorResponse , err error ) {
655662 opt , err := parseOptString (req .GetParameter ())
656663 if err != nil {
657664 return nil , err
@@ -665,20 +672,33 @@ func CreateCodeGeneratorResponse(ctx context.Context, req *pluginpb.CodeGenerato
665672 fmt .Fprint (os .Stderr , validator .Format (outs ))
666673 }
667674
675+ var resp pluginpb.CodeGeneratorResponse
676+ // TODO: Since we don’t currently support editions, we will comment it out.
677+ // Strictly speaking, proto3 optional is also not fully supported, but because it cannot be used together when other plugins support proto3 optional,
678+ // we have enabled it for the time being.
679+ resp .SupportedFeatures = proto .Uint64 (uint64 (pluginpb .CodeGeneratorResponse_FEATURE_PROTO3_OPTIONAL /*| pluginpb.CodeGeneratorResponse_FEATURE_SUPPORTS_EDITIONS*/ ))
680+
681+ if len (result .Files ) == 0 {
682+ return & resp , nil
683+ }
684+
685+ cache := newWasmPluginCache ()
686+ defer func () {
687+ if closeErr := cache .Close (ctx ); closeErr != nil {
688+ err = errors .Join (err , closeErr )
689+ }
690+ }()
668691 pluginResp , err := evalAllCodeGenerationPlugin (ctx , []* ProtoFileResult {
669692 {
670693 Type : ProtocAction ,
671694 ProtoPath : "" ,
672695 FederationFiles : result .Files ,
673696 },
674- }, opt )
697+ }, opt , cache )
675698 if err != nil {
676699 return nil , err
677700 }
678-
679- var resp pluginpb.CodeGeneratorResponse
680701 resp .File = append (resp .File , pluginResp .File ... )
681- resp .SupportedFeatures = proto .Uint64 (uint64 (pluginpb .CodeGeneratorResponse_FEATURE_PROTO3_OPTIONAL /*| pluginpb.CodeGeneratorResponse_FEATURE_SUPPORTS_EDITIONS*/ ))
682702 for _ , file := range result .Files {
683703 out , err := NewCodeGenerator ().Generate (file )
684704 if err != nil {
@@ -696,61 +716,38 @@ func CreateCodeGeneratorResponse(ctx context.Context, req *pluginpb.CodeGenerato
696716 return & resp , nil
697717}
698718
699- func evalAllCodeGenerationPlugin (ctx context.Context , results []* ProtoFileResult , opt * CodeGeneratorOption ) (* pluginpb.CodeGeneratorResponse , error ) {
719+ func evalAllCodeGenerationPlugin (ctx context.Context , results []* ProtoFileResult , opt * CodeGeneratorOption , cache * wasmPluginCache ) (* pluginpb.CodeGeneratorResponse , error ) {
700720 if len (results ) == 0 {
701721 return & pluginpb.CodeGeneratorResponse {}, nil
702722 }
703723 if opt == nil || len (opt .Plugins ) == 0 {
704724 return & pluginpb.CodeGeneratorResponse {}, nil
705725 }
726+
706727 var resp pluginpb.CodeGeneratorResponse
707728 for _ , result := range results {
708- pluginFiles := make ([]* plugin.ProtoCodeGeneratorResponse_File , 0 , len (result .Files ))
709- for _ , file := range result .Files {
710- fileBytes , err := proto .Marshal (file )
729+ for _ , p := range opt .Plugins {
730+ wp , err := cache .getOrCreate (ctx , p )
711731 if err != nil {
712732 return nil , err
713733 }
714- var pluginFile plugin.ProtoCodeGeneratorResponse_File
715- if err := proto .Unmarshal (fileBytes , & pluginFile ); err != nil {
716- return nil , err
717- }
718- pluginFiles = append (pluginFiles , & pluginFile )
719- }
720- genReq := generator .CreateCodeGeneratorRequest (& generator.CodeGeneratorRequestConfig {
721- Type : generator .ActionType (result .Type ),
722- ProtoPath : result .ProtoPath ,
723- Files : pluginFiles ,
724- GRPCFederationFiles : result .FederationFiles ,
725- OutputFilePathConfig : opt .Path ,
726- })
727- encodedGenReq , err := proto .Marshal (genReq )
728- if err != nil {
729- return nil , err
730- }
731- for _ , plugin := range opt .Plugins {
732- wasmFile , err := os .ReadFile (plugin .Path )
734+ genReq := generator .CreateCodeGeneratorRequest (& generator.CodeGeneratorRequestConfig {
735+ ProtoPath : result .ProtoPath ,
736+ GRPCFederationFiles : result .FederationFiles ,
737+ OutputFilePathConfig : opt .Path ,
738+ })
739+ encodedGenReq , err := proto .Marshal (genReq )
733740 if err != nil {
734- return nil , fmt .Errorf ("grpc-federation: failed to read plugin file: %s: %w" , plugin .Path , err )
735- }
736- if plugin .Sha256 != "" {
737- hash := sha256 .Sum256 (wasmFile )
738- gotHash := hex .EncodeToString (hash [:])
739- if plugin .Sha256 != gotHash {
740- return nil , fmt .Errorf (
741- `grpc-federation: expected plugin sha256 value is [%s] but got [%s]` ,
742- plugin .Sha256 ,
743- gotHash ,
744- )
745- }
741+ return nil , err
746742 }
747- pluginRes , err := evalCodeGeneratorPlugin (ctx , wasmFile , bytes .NewBuffer (encodedGenReq ))
743+ pluginRes , err := wp . Execute (ctx , bytes .NewBuffer (encodedGenReq ))
748744 if err != nil {
749745 return nil , err
750746 }
751747 resp .File = append (resp .File , pluginRes .File ... )
752748 }
753749 }
750+
754751 return & resp , nil
755752}
756753
0 commit comments