/
generate.go
176 lines (142 loc) · 4.16 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
// +build ignore
// Copyright 2016 govend. All rights reserved.
// Use of this source code is governed by an Apache 2.0
// license that can be found in the LICENSE file.
//
// This file generates stdpkgs.go, which contains the standard library packages.
//
// This file has been modified from its original source:
// https://github.com/golang/tools/blob/master/imports/mkindex.go
//
package main
import (
"bytes"
"fmt"
"go/build"
"go/format"
"go/token"
"io/ioutil"
"log"
"os"
"path"
"path/filepath"
)
var (
pkgs = map[string][]stdpkg{}
fset = token.NewFileSet()
)
// stdpkg represents a standard package in "stdpkgs.go".
type stdpkg struct {
path string // full pkg import path, e.g. "net/http"
dir string // absolute file path to pkg directory e.g. "/usr/lib/go/src/fmt"
}
func main() {
// start with the default context
ctx := build.Default
// remove the GOPATH, we only want to search packages in the GOROOT
ctx.GOPATH = ""
// iterate through the list of package source root directories
for _, path := range ctx.SrcDirs() {
// open the file
f, err := os.Open(path)
if err != nil {
log.Print(err)
continue
}
// gather all the child names from the directory in a single slice
children, err := f.Readdir(-1)
f.Close() // close the file
if err != nil {
log.Print(err)
continue
}
// iterate through each child name
for _, child := range children {
if child.IsDir() { // check the child name is a directory
load(path, child.Name()) // load the package path and name.
}
}
}
// write preliminary file data such as comments, package name, structs, etc..
var buf bytes.Buffer
buf.WriteString(`// Copyright 2016 govend. All rights reserved.
// Use of this source code is governed by an Apache 2.0
// license that can be found in the LICENSE file.
//
// this file is auto-generated by generate.go
//
`)
buf.WriteString("package filters\n")
buf.WriteString(`
type stdpkg struct {
path, dir string
}
`)
// write the dynamic list of standard packages
fmt.Fprintf(&buf, "var stdpkgs = %#v\n", pkgs)
// transfer buffer bytes to final source
src := buf.Bytes()
// replace main.pkg type name with pkg
src = bytes.Replace(src, []byte("main.stdpkg"), []byte("stdpkg"), -1)
// replace actual GOROOT with "/go"
src = bytes.Replace(src, []byte(ctx.GOROOT), []byte("/go"), -1)
// add line wrapping and better formatting.
src = bytes.Replace(src, []byte("map[string][]stdpkg{"), []byte("map[string][]stdpkg{\n"), -1)
src = bytes.Replace(src, []byte("[]stdpkg{stdpkg{"), []byte("{\n{"), -1)
src = bytes.Replace(src, []byte(", stdpkg"), []byte(",\nstdpkg"), -1)
src = bytes.Replace(src, []byte("stdpkg{path"), []byte("{path"), -1)
src = bytes.Replace(src, []byte("}}, "), []byte("},\n},\n"), -1)
src = bytes.Replace(src, []byte("true, "), []byte("true,\n"), -1)
src = bytes.Replace(src, []byte("}}}"), []byte("},\n},\n}"), -1)
// format all the source bytes
src, err := format.Source(src)
if err != nil {
log.Fatal(err)
}
// write source bytes to the "stdpkgs.go" file
if err := ioutil.WriteFile("stdpkgs.go", src, 0644); err != nil {
log.Fatal(err)
}
}
// load takes a path root and import path.
func load(root, importpath string) {
// get package name
name := path.Base(importpath)
if name == "testdata" {
return
}
// calculate the package source directory
dir := filepath.Join(root, importpath)
// append the package values to the package map
pkgs[name] = append(pkgs[name], stdpkg{
path: importpath,
dir: dir,
})
// get the package directory
pkgDir, err := os.Open(dir)
if err != nil {
return
}
// gather all the child names from the directory in a single slice
children, err := pkgDir.Readdir(-1)
// close the file and check for errors
pkgDir.Close()
if err != nil {
return
}
// iterate through each child name
for _, child := range children {
name := child.Name()
if name == "" { // check that the childs names not blank
continue
}
// handle special package name cases
if c := name[0]; c == '.' || ('0' <= c && c <= '9') {
continue
}
// check if the child name is a directory
if child.IsDir() {
load(root, filepath.Join(importpath, name)) // load package path and name
}
}
}