Skip to content

Commit 2df5946

Browse files
committed
Support imported type aliases
Change-Id: I2e6ad9fb51e1cf55a52267720f2394e792946f7e Signed-off-by: Ondrej Fabry <[email protected]>
1 parent 8c64ee5 commit 2df5946

File tree

4 files changed

+207
-31
lines changed

4 files changed

+207
-31
lines changed

cmd/binapi-generator/generate.go

Lines changed: 72 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"bytes"
1919
"fmt"
2020
"io"
21+
"os/exec"
2122
"path/filepath"
2223
"sort"
2324
"strings"
@@ -125,27 +126,43 @@ func generatePackage(ctx *context, w io.Writer) error {
125126
// generate enums
126127
if len(ctx.packageData.Enums) > 0 {
127128
for _, enum := range ctx.packageData.Enums {
129+
if imp, ok := ctx.packageData.Imports[enum.Name]; ok {
130+
generateImportedAlias(ctx, w, enum.Name, &imp)
131+
continue
132+
}
128133
generateEnum(ctx, w, &enum)
129134
}
130135
}
131136

132137
// generate aliases
133138
if len(ctx.packageData.Aliases) > 0 {
134139
for _, alias := range ctx.packageData.Aliases {
140+
if imp, ok := ctx.packageData.Imports[alias.Name]; ok {
141+
generateImportedAlias(ctx, w, alias.Name, &imp)
142+
continue
143+
}
135144
generateAlias(ctx, w, &alias)
136145
}
137146
}
138147

139148
// generate types
140149
if len(ctx.packageData.Types) > 0 {
141150
for _, typ := range ctx.packageData.Types {
151+
if imp, ok := ctx.packageData.Imports[typ.Name]; ok {
152+
generateImportedAlias(ctx, w, typ.Name, &imp)
153+
continue
154+
}
142155
generateType(ctx, w, &typ)
143156
}
144157
}
145158

146159
// generate unions
147160
if len(ctx.packageData.Unions) > 0 {
148161
for _, union := range ctx.packageData.Unions {
162+
if imp, ok := ctx.packageData.Imports[union.Name]; ok {
163+
generateImportedAlias(ctx, w, union.Name, &imp)
164+
continue
165+
}
149166
generateUnion(ctx, w, &union)
150167
}
151168
}
@@ -224,10 +241,42 @@ func generateHeader(ctx *context, w io.Writer) {
224241
fmt.Fprintf(w, "\tio \"%s\"\n", "io")
225242
fmt.Fprintf(w, "\tstrconv \"%s\"\n", "strconv")
226243
fmt.Fprintf(w, "\tstruc \"%s\"\n", "github.com/lunixbochs/struc")
244+
if len(ctx.packageData.Imports) > 0 {
245+
fmt.Fprintln(w)
246+
for _, imp := range getImports(ctx) {
247+
impPkg := getImportPkg(filepath.Dir(ctx.outputFile), imp)
248+
fmt.Fprintf(w, "\t%s \"%s\"\n", imp, strings.TrimSpace(impPkg))
249+
}
250+
}
227251
fmt.Fprintln(w, ")")
228252
fmt.Fprintln(w)
229253
}
230254

255+
func getImportPkg(outputDir string, pkg string) string {
256+
absPath, _ := filepath.Abs(filepath.Join(outputDir, "..", pkg))
257+
cmd := exec.Command("go", "list", absPath)
258+
var errbuf, outbuf bytes.Buffer
259+
cmd.Stdout = &outbuf
260+
cmd.Stderr = &errbuf
261+
if err := cmd.Run(); err != nil {
262+
fmt.Printf("ERR: %v\n", errbuf.String())
263+
panic(err)
264+
}
265+
return outbuf.String()
266+
}
267+
268+
func getImports(ctx *context) (imports []string) {
269+
impmap := map[string]struct{}{}
270+
for _, imp := range ctx.packageData.Imports {
271+
if _, ok := impmap[imp.Package]; !ok {
272+
imports = append(imports, imp.Package)
273+
impmap[imp.Package] = struct{}{}
274+
}
275+
}
276+
sort.Strings(imports)
277+
return imports
278+
}
279+
231280
func generateFooter(ctx *context, w io.Writer) {
232281
fmt.Fprintln(w, "// This is a compile-time assertion to ensure that this generated file")
233282
fmt.Fprintln(w, "// is compatible with the GoVPP api package it is being compiled against.")
@@ -350,6 +399,14 @@ func generateEnum(ctx *context, w io.Writer, enum *Enum) {
350399
fmt.Fprintln(w)
351400
}
352401

402+
func generateImportedAlias(ctx *context, w io.Writer, tName string, imp *Import) {
403+
name := camelCaseName(tName)
404+
405+
fmt.Fprintf(w, "type %s = %s.%s\n", name, imp.Package, name)
406+
407+
fmt.Fprintln(w)
408+
}
409+
353410
func generateAlias(ctx *context, w io.Writer, alias *Alias) {
354411
name := camelCaseName(alias.Name)
355412

@@ -550,13 +607,10 @@ func generateMessage(ctx *context, w io.Writer, msg *Message) {
550607
// generate end of the struct
551608
fmt.Fprintln(w, "}")
552609

553-
// generate name getter
610+
// generate message methods
611+
generateMessageResetMethod(w, name)
554612
generateMessageNameGetter(w, name, msg.Name)
555-
556-
// generate CRC getter
557613
generateCrcGetter(w, name, msg.CRC)
558-
559-
// generate message type getter method
560614
generateMessageTypeGetter(w, name, msgType)
561615

562616
fmt.Fprintln(w)
@@ -637,40 +691,36 @@ func generateField(ctx *context, w io.Writer, fields []Field, i int) {
637691
fmt.Fprintln(w)
638692
}
639693

640-
func generateMessageNameGetter(w io.Writer, structName, msgName string) {
641-
fmt.Fprintf(w, `func (*%s) GetMessageName() string {
642-
return %q
694+
func generateMessageResetMethod(w io.Writer, structName string) {
695+
fmt.Fprintf(w, "func (m *%[1]s) Reset() { *m = %[1]s{} }\n", structName)
643696
}
644-
`, structName, msgName)
697+
698+
func generateMessageNameGetter(w io.Writer, structName, msgName string) {
699+
fmt.Fprintf(w, "func (*%s) GetMessageName() string { return %q }\n", structName, msgName)
645700
}
646701

647702
func generateTypeNameGetter(w io.Writer, structName, msgName string) {
648-
fmt.Fprintf(w, `func (*%s) GetTypeName() string {
649-
return %q
650-
}
651-
`, structName, msgName)
703+
fmt.Fprintf(w, "func (*%s) GetTypeName() string { return %q }\n", structName, msgName)
652704
}
653705

654706
func generateCrcGetter(w io.Writer, structName, crc string) {
655707
crc = strings.TrimPrefix(crc, "0x")
656-
fmt.Fprintf(w, `func (*%s) GetCrcString() string {
657-
return %q
658-
}
659-
`, structName, crc)
708+
fmt.Fprintf(w, "func (*%s) GetCrcString() string { return %q }\n", structName, crc)
660709
}
661710

662711
func generateMessageTypeGetter(w io.Writer, structName string, msgType MessageType) {
663-
fmt.Fprintln(w, "func (*"+structName+") GetMessageType() api.MessageType {")
712+
fmt.Fprintf(w, "func (*"+structName+") GetMessageType() api.MessageType {")
664713
if msgType == requestMessage {
665-
fmt.Fprintln(w, "\treturn api.RequestMessage")
714+
fmt.Fprintf(w, "\treturn api.RequestMessage")
666715
} else if msgType == replyMessage {
667-
fmt.Fprintln(w, "\treturn api.ReplyMessage")
716+
fmt.Fprintf(w, "\treturn api.ReplyMessage")
668717
} else if msgType == eventMessage {
669-
fmt.Fprintln(w, "\treturn api.EventMessage")
718+
fmt.Fprintf(w, "\treturn api.EventMessage")
670719
} else {
671-
fmt.Fprintln(w, "\treturn api.OtherMessage")
720+
fmt.Fprintf(w, "\treturn api.OtherMessage")
672721
}
673722
fmt.Fprintln(w, "}")
723+
fmt.Fprintln(w)
674724
}
675725

676726
func generateServices(ctx *context, w io.Writer, services []Service) {

cmd/binapi-generator/main.go

Lines changed: 127 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,10 @@ import (
3232
)
3333

3434
var (
35-
theInputFile = flag.String("input-file", "", "Input file with VPP API in JSON format.")
36-
theInputDir = flag.String("input-dir", "/usr/share/vpp/api", "Input directory with VPP API files in JSON format.")
37-
theOutputDir = flag.String("output-dir", ".", "Output directory where package folders will be generated.")
35+
theInputFile = flag.String("input-file", "", "Input file with VPP API in JSON format.")
36+
theInputTypes = flag.String("input-types", "", "Types input file with VPP API in JSON format. (split by comma)")
37+
theInputDir = flag.String("input-dir", "/usr/share/vpp/api", "Input directory with VPP API files in JSON format.")
38+
theOutputDir = flag.String("output-dir", ".", "Output directory where package folders will be generated.")
3839

3940
includeAPIVer = flag.Bool("include-apiver", true, "Include APIVersion constant for each module.")
4041
includeServices = flag.Bool("include-services", true, "Include RPC service api and client implementation.")
@@ -84,14 +85,23 @@ func main() {
8485
}
8586
}
8687

87-
func run(inputFile, inputDir string, outputDir string, continueErr bool) error {
88+
func run(inputFile, inputDir string, outputDir string, continueErr bool) (err error) {
8889
if inputFile == "" && inputDir == "" {
8990
return fmt.Errorf("input-file or input-dir must be specified")
9091
}
9192

93+
var typesPkgs []*context
94+
if *theInputTypes != "" {
95+
types := strings.Split(*theInputTypes, ",")
96+
typesPkgs, err = loadTypesPackages(types...)
97+
if err != nil {
98+
return fmt.Errorf("loading types input failed: %v", err)
99+
}
100+
}
101+
92102
if inputFile != "" {
93103
// process one input file
94-
if err := generateFromFile(inputFile, outputDir); err != nil {
104+
if err := generateFromFile(inputFile, outputDir, typesPkgs); err != nil {
95105
return fmt.Errorf("code generation from %s failed: %v\n", inputFile, err)
96106
}
97107
} else {
@@ -107,7 +117,7 @@ func run(inputFile, inputDir string, outputDir string, continueErr bool) error {
107117
return fmt.Errorf("no input files found in input directory: %v\n", dir)
108118
}
109119
for _, file := range files {
110-
if err := generateFromFile(file, outputDir); err != nil {
120+
if err := generateFromFile(file, outputDir, typesPkgs); err != nil {
111121
if continueErr {
112122
logrus.Warnf("code generation from %s failed: %v (error ignored)\n", file, err)
113123
continue
@@ -151,7 +161,7 @@ func parseInputJSON(inputData []byte) (*jsongo.Node, error) {
151161
}
152162

153163
// generateFromFile generates Go package from one input JSON file
154-
func generateFromFile(inputFile, outputDir string) error {
164+
func generateFromFile(inputFile, outputDir string, typesPkgs []*context) error {
155165
// create generator context
156166
ctx, err := newContext(inputFile, outputDir)
157167
if err != nil {
@@ -185,6 +195,13 @@ func generateFromFile(inputFile, outputDir string) error {
185195
return fmt.Errorf("parsing package %s failed: %v", ctx.packageName, err)
186196
}
187197

198+
if len(typesPkgs) > 0 {
199+
err = loadTypeAliases(ctx, typesPkgs)
200+
if err != nil {
201+
return fmt.Errorf("loading type aliases failed: %v", err)
202+
}
203+
}
204+
188205
// generate Go package code
189206
var buf bytes.Buffer
190207
if err := generatePackage(ctx, &buf); err != nil {
@@ -210,6 +227,109 @@ func generateFromFile(inputFile, outputDir string) error {
210227
return nil
211228
}
212229

230+
func loadTypesPackages(types ...string) ([]*context, error) {
231+
var ctxs []*context
232+
for _, inputFile := range types {
233+
// create generator context
234+
ctx, err := newContext(inputFile, "")
235+
if err != nil {
236+
return nil, err
237+
}
238+
// read API definition from input file
239+
ctx.inputData, err = ioutil.ReadFile(ctx.inputFile)
240+
if err != nil {
241+
return nil, fmt.Errorf("reading input file %s failed: %v", ctx.inputFile, err)
242+
}
243+
// parse JSON data into objects
244+
jsonRoot, err := parseInputJSON(ctx.inputData)
245+
if err != nil {
246+
return nil, fmt.Errorf("parsing JSON input failed: %v", err)
247+
}
248+
ctx.packageData, err = parsePackage(ctx, jsonRoot)
249+
if err != nil {
250+
return nil, fmt.Errorf("parsing package %s failed: %v", ctx.packageName, err)
251+
}
252+
ctxs = append(ctxs, ctx)
253+
}
254+
return ctxs, nil
255+
}
256+
257+
func loadTypeAliases(ctx *context, typesCtxs []*context) error {
258+
for _, t := range ctx.packageData.Types {
259+
for _, c := range typesCtxs {
260+
if _, ok := ctx.packageData.Imports[t.Name]; ok {
261+
break
262+
}
263+
for _, at := range c.packageData.Types {
264+
if at.Name != t.Name {
265+
continue
266+
}
267+
if len(at.Fields) != len(t.Fields) {
268+
continue
269+
}
270+
ctx.packageData.Imports[t.Name] = Import{
271+
Package: c.packageName,
272+
}
273+
}
274+
}
275+
}
276+
for _, t := range ctx.packageData.Aliases {
277+
for _, c := range typesCtxs {
278+
if _, ok := ctx.packageData.Imports[t.Name]; ok {
279+
break
280+
}
281+
for _, at := range c.packageData.Aliases {
282+
if at.Name != t.Name {
283+
continue
284+
}
285+
if at.Length != t.Length {
286+
continue
287+
}
288+
if at.Type != t.Type {
289+
continue
290+
}
291+
ctx.packageData.Imports[t.Name] = Import{
292+
Package: c.packageName,
293+
}
294+
}
295+
}
296+
}
297+
for _, t := range ctx.packageData.Enums {
298+
for _, c := range typesCtxs {
299+
if _, ok := ctx.packageData.Imports[t.Name]; ok {
300+
break
301+
}
302+
for _, at := range c.packageData.Enums {
303+
if at.Name != t.Name {
304+
continue
305+
}
306+
if at.Type != t.Type {
307+
continue
308+
}
309+
ctx.packageData.Imports[t.Name] = Import{
310+
Package: c.packageName,
311+
}
312+
}
313+
}
314+
}
315+
for _, t := range ctx.packageData.Unions {
316+
for _, c := range typesCtxs {
317+
if _, ok := ctx.packageData.Imports[t.Name]; ok {
318+
break
319+
}
320+
for _, at := range c.packageData.Unions {
321+
if at.Name != t.Name {
322+
continue
323+
}
324+
ctx.packageData.Imports[t.Name] = Import{
325+
Package: c.packageName,
326+
}
327+
}
328+
}
329+
}
330+
return nil
331+
}
332+
213333
func logf(f string, v ...interface{}) {
214334
if *debugMode {
215335
logrus.Debugf(f, v...)

cmd/binapi-generator/objects.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ type Package struct {
1414
Unions []Union
1515
Messages []Message
1616
RefMap map[string]string
17+
Imports map[string]Import
18+
}
19+
20+
type Import struct {
21+
Package string
1722
}
1823

1924
// Service represents VPP binary API service

cmd/binapi-generator/parse.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,9 @@ const (
7575
// parsePackage parses provided JSON data into objects prepared for code generation
7676
func parsePackage(ctx *context, jsonRoot *jsongo.Node) (*Package, error) {
7777
pkg := Package{
78-
Name: ctx.packageName,
79-
RefMap: make(map[string]string),
78+
Name: ctx.packageName,
79+
RefMap: make(map[string]string),
80+
Imports: map[string]Import{},
8081
}
8182

8283
// parse CRC for API version

0 commit comments

Comments
 (0)