-
Notifications
You must be signed in to change notification settings - Fork 24
/
package.go
170 lines (147 loc) · 4.33 KB
/
package.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
package main
import (
"errors"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"text/template"
"golang.org/x/tools/go/packages"
)
// A Package holds the typedef, function, and enum definitions for a Go package.
type Package struct {
Name string
API string
Version Version
Profile string
TmplDir string
Typedefs []*Typedef
Enums map[string]*Enum
Functions map[string]*PackageFunction
}
// A PackageFunction is a package-specific Function wrapper.
type PackageFunction struct {
Function
Required bool
Doc string
}
// Comment returns the comment explaining the function.
func (f *PackageFunction) Comment() string {
var lines []string
if f.Doc != "" {
lines = append(lines, "// "+f.Doc)
}
// Adds explanations about C types that are unsafe.Pointer in Go world.
// See also https://github.com/go-gl/gl/issues/113.
for _, p := range f.Function.Parameters {
if t := p.Type; t.GoType() == "unsafe.Pointer" && t.Name != "void" && t.Name != "GLvoid" {
lines = append(lines, fmt.Sprintf("// Parameter %s has type %s.", p.Name, t.GoCType()))
}
}
if r := f.Function.Return; r.GoType() == "unsafe.Pointer" && r.Name != "void" && r.Name != "GLvoid" {
lines = append(lines, fmt.Sprintf("// Return value has type %s.", r.GoCType()))
}
return strings.Join(lines, "\n")
}
// UniqueName returns a globally unique Go-compatible name for this package.
func (pkg *Package) UniqueName() string {
version := strings.Replace(pkg.Version.String(), ".", "", -1)
return fmt.Sprintf("%s%s%s", pkg.API, pkg.Profile, version)
}
// GeneratePackage writes a Go package to specified directory.
func (pkg *Package) GeneratePackage(dir string) error {
if err := os.MkdirAll(dir, 0755); err != nil {
return err
}
if err := pkg.generateFile("package", dir); err != nil {
return err
}
if err := pkg.generateFile("conversions", dir); err != nil {
return err
}
if err := pkg.generateFile("procaddr", dir); err != nil {
return err
}
if pkg.HasDebugCallbackFeature() {
if err := pkg.generateFile("debug", dir); err != nil {
return err
}
}
// gofmt the generated .go files.
if err := exec.Command("gofmt", "-w", dir).Run(); err != nil {
return fmt.Errorf("gofmt error: %v", err)
}
return nil
}
func (pkg *Package) generateFile(file, dir string) error {
out, err := os.Create(filepath.Join(dir, file+".go"))
if err != nil {
return err
}
defer out.Close()
fns := template.FuncMap{
"replace": strings.Replace,
"toUpper": strings.ToUpper,
}
tmpl := template.Must(template.New(file + ".tmpl").Funcs(fns).ParseFiles(filepath.Join(pkg.TmplDir, file+".tmpl")))
return tmpl.Execute(NewBlankLineStrippingWriter(out), pkg)
}
// HasDebugCallbackFeature returns whether this package exposes the ability to
// set a debug callback. Used to determine whether to include the necessary
// GL-specific callback code.
func (pkg *Package) HasDebugCallbackFeature() bool {
for _, fn := range pkg.Functions {
for _, param := range fn.Parameters {
if param.Type.IsDebugProc() {
return true
}
}
}
return false
}
// HasRequiredFunctions returns true if at least one function in this package
// is required.
func (pkg *Package) HasRequiredFunctions() bool {
for _, fn := range pkg.Functions {
if fn.Required {
return true
}
}
return false
}
// Filter removes any enums, or functions found in this package that are not
// listed in the given lookup maps. If either of the maps has a length of zero,
// filtering does not occur for that type (e.g. all functions are left intact).
func (pkg *Package) Filter(enums, functions map[string]bool) {
if len(enums) > 0 {
// Remove any enum not listed in the enums lookup map.
for name := range pkg.Enums {
_, valid := enums[name]
if !valid {
delete(pkg.Enums, name)
}
}
}
if len(functions) > 0 {
// Remove any function not listed in the functions lookup map.
for name := range pkg.Functions {
_, valid := functions[name]
if !valid {
delete(pkg.Functions, name)
}
}
}
}
// importPathToDir resolves the absolute path from importPath.
// There needs to be a valid Go package inside that import path.
func importPathToDir(importPath string) (string, error) {
pkgs, err := packages.Load(nil, importPath)
if err != nil {
return "", err
}
if len(pkgs[0].GoFiles) == 0 {
return "", errors.New("no Go file available")
}
return filepath.Dir(pkgs[0].GoFiles[0]), nil
}