-
Notifications
You must be signed in to change notification settings - Fork 152
/
generate.go
135 lines (122 loc) · 3.31 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
package cmd
import (
"os"
"path"
"path/filepath"
"strings"
"github.com/dave/jennifer/jen"
"github.com/spf13/cobra"
)
// generateCmd represents the generate command
var generateCmd = &cobra.Command{
Use: "generate",
Short: "Generate Go source from Flux source",
Long: `This utility generates a Go source file that imports the Flux packages.
All Flux packages need to also be a Go package.
A placeholder file will be added when needed.`,
RunE: generate,
}
var (
pkgName,
rootDir,
importFile string
)
func init() {
rootCmd.AddCommand(generateCmd)
generateCmd.Flags().StringVar(&pkgName, "go-pkg", "", "The fully qualified Go package name of the root package.")
generateCmd.Flags().StringVar(&rootDir, "root-dir", ".", "The root level directory for all packages.")
generateCmd.Flags().StringVar(&importFile, "import-file", "packages.go", "Location relative to root-dir to place a file to import all generated packages.")
}
const placeholder = "placeholder.go"
func generate(cmd *cobra.Command, args []string) error {
var goPackages []string
err := walkDirs(rootDir, func(dir string) error {
hasGo, hasFlux, err := dirProps(dir)
if err != nil {
return nil
}
goPath := path.Join(pkgName, dir)
if hasFlux && !isInternal(dir) {
// We need a package import
goPackages = append(goPackages, goPath)
if !hasGo {
// We need a placeholder file to ensure this is a Go package.
if err := savePlaceholder(filepath.Join(dir, placeholder), goPath); err != nil {
return err
}
} else {
// We no longer need the placeholder file
os.Remove(filepath.Join(dir, placeholder))
}
}
return nil
})
if err != nil {
return err
}
// Write the import file
f := jen.NewFile(path.Base(pkgName))
f.HeaderComment(`// DO NOT EDIT: This file is autogenerated via the builtin command.
//
// The imports in this file ensures that all the init functions runs and registers
// the builtins for the flux runtime
`)
f.Anon(goPackages...)
return f.Save(filepath.Join(rootDir, importFile))
}
func walkDirs(path string, f func(dir string) error) error {
files, err := os.ReadDir(path)
if err != nil {
return err
}
if err := f(path); err != nil {
return err
}
for _, file := range files {
if file.IsDir() {
if err := walkDirs(filepath.Join(path, file.Name()), f); err != nil {
return err
}
}
}
return nil
}
func dirProps(dir string) (hasGo, hasFlux bool, err error) {
files, err := os.ReadDir(dir)
if err != nil {
return
}
for _, f := range files {
if filepath.Ext(f.Name()) == ".go" &&
!strings.HasSuffix(path.Base(f.Name()), "_test.go") &&
// Do not count the placeholder file as Go because
// otherwise we will delete it.
f.Name() != placeholder {
hasGo = true
}
if filepath.Ext(f.Name()) == ".flux" {
hasFlux = true
}
}
return
}
func isInternal(p string) bool {
parts := strings.Split(p, "/")
// The toplevel `internal` package is allowed
// so start after that path element.
for _, part := range parts[1:] {
if part == "internal" {
return true
}
}
return false
}
func savePlaceholder(fpath, goPkgPath string) error {
// Write the import file
f := jen.NewFile(path.Base(goPkgPath))
f.HeaderComment(`// DO NOT EDIT: This file is autogenerated via the builtin command.
//
// This file ensures that this directory is a Go package
`)
return f.Save(fpath)
}