/
generate.go
181 lines (166 loc) · 6.13 KB
/
generate.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package swift implements Swift code generation from compiled VDL packages.
package swift
import (
"bytes"
"log"
"path"
"strings"
"v.io/v23/vdl"
"v.io/x/ref/lib/vdl/build"
"v.io/x/ref/lib/vdl/compile"
"v.io/x/ref/lib/vdl/vdlutil"
)
const (
// The data passed into every template must include a FileDoc field, which
// contains the comment for each generated file; e.g. the boilerplate copyright
// header.
header = `{{.FileDoc}}
// This file was auto-generated by the vanadium vdl tool.
`
// fileTmpl is the template to produce a Swift generated file
fileTmpl = header + `// Source: {{ .Source }}
{{ range $import := .ImportedModules }}
import {{ $import }}
{{ end }}
{{ range $tdef := .Tdefs }}
{{ $tdef }}
{{ end }}`
)
// pkgPathXlator is the function used to translate a VDL package path
// into a Swift package path. If nil, no translation takes place.
var pkgPathXlator func(vdlPath string) (swiftPath string)
// SetPkgPathXlator sets the function used to translate a VDL package
// path into a Swift package path.
func SetPkgPathXlator(xlator func(vdlPath string) string) {
pkgPathXlator = xlator
}
// swiftGenPkgPath returns the Swift package path given the VDL package path.
func swiftGenPkgPath(vdlPkgPath string) string {
if pkgPathXlator == nil {
return vdlPkgPath
}
return pkgPathXlator(vdlPkgPath)
}
// SwiftFileInfo stores the name and contents of the generated Swift file.
//
//nolint:revive // API change required.
type SwiftFileInfo struct {
Data []byte
Dir string
Name string
Module string
}
type swiftContext struct {
env *compile.Env
pkg *compile.Package
// genPathToDir allows us to lookup the filesystem dir for any pkg.GenPath
genPathToDir map[string]string
// srcDirs is the combination of VDLROOT & VDLPATHs to provide a set of
// root source directories. These directories provide a stop condition when
// traversing a package's path's parents. In particular, we do this when
// looking for a vdl.config to that specifies a SwiftModule.
srcDirs map[string]bool
// Cache
memoizedTypeNames map[*compile.TypeDef]string
}
// Generate generates Swift files for all VDL files in the provided package,
// returning the list of generated Swift files as a slice. We generate Swift
// files to match the original VDL file layout, with the exception that
// constants are smushed into a <PackageName>.swift that consolidates the
// errors, package documentation (if any), and constants for a given package
// into a struct. This package struct provides a similar context to what
// would be provided by an import in VDL or Java.
// TODO(azinman): Run Swift formatters on the generated files.
func Generate(pkg *compile.Package, env *compile.Env, genPathToDir map[string]string) (ret []SwiftFileInfo) {
srcDirs := map[string]bool{}
for _, dir := range build.SrcDirs(env.Errors) {
srcDirs[dir] = true
}
ctx := &swiftContext{env, pkg, genPathToDir, srcDirs, map[*compile.TypeDef]string{}}
validateSwiftConfig(ctx)
// One file for pkg documentation (if any), metadata, errors and constants.
if pkgData := genSwiftPackageFile(pkg, ctx); pkgData != "" {
ret = append(ret, SwiftFileInfo{
Data: []byte(pkgData),
Name: ctx.swiftPackageName(pkg) + "Package.swift",
Module: ctx.swiftModule(pkg),
})
}
for _, file := range pkg.Files {
if len(file.TypeDefs) == 0 {
// Nothing to generate
continue
}
// Separate file for all typedefs.
tdefs := []string{}
for _, tdef := range file.TypeDefs {
switch tdef.Type.Kind() {
case vdl.Enum:
tdefs = append(tdefs, genSwiftEnumTdef(tdef, ctx))
case vdl.Union:
tdefs = append(tdefs, genSwiftUnionTdef(tdef, ctx))
case vdl.Struct:
tdefs = append(tdefs, genSwiftStructTdef(tdef, ctx))
default:
tdefs = append(tdefs, genSwiftPrimitiveTdef(tdef, ctx))
}
}
// TODO(zinman): loop through file.Interfaces for RPC generation
source := path.Join(ctx.genPathToDir[pkg.GenPath], file.BaseName)
vdlRoot, err := ctx.vdlRootForFilePath(source)
if err != nil {
log.Fatalf("vdl: couldn't find vdl root for path %v: %v", source, err)
}
source = strings.TrimPrefix(source, vdlRoot+"/")
data := struct {
FileDoc string
ImportedModules []string
Tdefs []string
PackagePath string
Source string
}{
FileDoc: pkg.FileDoc,
ImportedModules: ctx.importedModules(file.TypeDefs),
Tdefs: tdefs,
PackagePath: swiftGenPkgPath(pkg.GenPath),
Source: source,
}
var buf bytes.Buffer
err = parseTmpl("swift file", fileTmpl).Execute(&buf, data)
if err != nil {
log.Fatalf("vdl: couldn't execute union template: %v", err)
}
baseName := strings.Replace(file.BaseName, path.Ext(file.BaseName), "", 1)
ret = append(ret, SwiftFileInfo{
Data: buf.Bytes(),
Name: ctx.swiftPackageName(pkg) + vdlutil.FirstRuneToUpper(baseName) + ".swift",
Module: ctx.swiftModule(pkg),
})
}
return
}
// The native types feature is hard to use correctly. E.g. the wire type
// must be statically registered in Swift vdl package in order for the
// wire<->native conversion to work, which is hard to ensure.
//
// Restrict the feature to these whitelisted VDL packages for now.
var nativeTypePackageWhitelist = map[string]bool{
"time": true,
}
func validateSwiftConfig(ctx *swiftContext) {
vdlconfig := path.Join(ctx.pkg.GenPath, ctx.pkg.ConfigName)
// Validate native type configuration. Since native types are hard to use, we
// restrict them to a built-in whitelist of packages for now.
if len(ctx.pkg.Config.Swift.WireToNativeTypes) > 0 && !nativeTypePackageWhitelist[ctx.pkg.Path] {
ctx.env.Errors.Errorf("%s: Swift.WireToNativeTypes is restricted to whitelisted VDL packages", vdlconfig)
}
// Look for a Swift module by walking up... if none found then panic
if ctx.swiftModule(ctx.pkg) == "" {
log.Fatalf("Could not find a swift module for package %v/%v; make sure to have a file named swiftmodule "+
"defined at the root that is a text file that ONLY contains the name of your Swift module.",
ctx.pkg.Path, ctx.pkg.Name)
}
}