forked from gnolang/gno
-
Notifications
You must be signed in to change notification settings - Fork 0
/
util.go
236 lines (205 loc) · 5.76 KB
/
util.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
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
package main
import (
"fmt"
"go/ast"
"io"
"io/fs"
"log"
"os"
"os/exec"
"path/filepath"
"strings"
"time"
gno "github.com/gnolang/gno/pkgs/gnolang"
)
func isGnoFile(f fs.DirEntry) bool {
name := f.Name()
return !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".gno") && !f.IsDir()
}
func isFileExist(path string) bool {
_, err := os.Stat(path)
return err == nil
}
func gnoFilesFromArgs(args []string) ([]string, error) {
paths := []string{}
for _, arg := range args {
info, err := os.Stat(arg)
if err != nil {
return nil, fmt.Errorf("invalid file or package path: %w", err)
}
if !info.IsDir() {
curpath := arg
paths = append(paths, curpath)
} else {
err = filepath.WalkDir(arg, func(curpath string, f fs.DirEntry, err error) error {
if err != nil {
return fmt.Errorf("%s: walk dir: %w", arg, err)
}
if !isGnoFile(f) {
return nil // skip
}
paths = append(paths, curpath)
return nil
})
if err != nil {
return nil, err
}
}
}
return paths, nil
}
func gnoPackagesFromArgs(args []string) ([]string, error) {
paths := []string{}
for _, arg := range args {
info, err := os.Stat(arg)
if err != nil {
return nil, fmt.Errorf("invalid file or package path: %w", err)
}
if !info.IsDir() {
paths = append(paths, arg)
} else {
// if the passed arg is a dir, then we'll recursively walk the dir
// and look for directories containing at least one .gno file.
visited := map[string]bool{} // used to run the builder only once per folder.
err = filepath.WalkDir(arg, func(curpath string, f fs.DirEntry, err error) error {
if err != nil {
return fmt.Errorf("%s: walk dir: %w", arg, err)
}
if f.IsDir() {
return nil // skip
}
if !isGnoFile(f) {
return nil // skip
}
parentDir := filepath.Dir(curpath)
if _, found := visited[parentDir]; found {
return nil
}
visited[parentDir] = true
// cannot use path.Join or filepath.Join, because we need
// to ensure that ./ is the prefix to pass to go build.
pkg := "./" + parentDir
paths = append(paths, pkg)
return nil
})
if err != nil {
return nil, err
}
}
}
return paths, nil
}
func fmtDuration(d time.Duration) string {
return fmt.Sprintf("%.2fs", d.Seconds())
}
func guessRootDir() string {
cmd := exec.Command("go", "list", "-m", "-mod=mod", "-f", "{{.Dir}}", "github.com/gnolang/gno")
out, err := cmd.CombinedOutput()
if err != nil {
log.Fatal("can't guess --root-dir, please fill it manually.")
}
rootDir := strings.TrimSpace(string(out))
return rootDir
}
// makeTestGoMod creates the temporary go.mod for test
func makeTestGoMod(path string, packageName string, goversion string) error {
content := fmt.Sprintf("module %s\n\ngo %s\n", packageName, goversion)
return os.WriteFile(path, []byte(content), 0o644)
}
// getPathsFromImportSpec derive and returns ImportPaths
// without ImportPrefix from *ast.ImportSpec
func getPathsFromImportSpec(importSpec []*ast.ImportSpec) (importPaths []importPath) {
for _, i := range importSpec {
path := i.Path.Value[1 : len(i.Path.Value)-1] // trim leading and trailing `"`
if strings.HasPrefix(path, gno.ImportPrefix) {
res := strings.TrimPrefix(path, gno.ImportPrefix)
importPaths = append(importPaths, importPath("."+res))
}
}
return
}
// ResolvePath joins the output dir with relative pkg path
// e.g
// Output Dir: Temp/gno-precompile
// Pkg Path: ../example/gno.land/p/pkg
// Returns -> Temp/gno-precompile/example/gno.land/p/pkg
func ResolvePath(output string, path importPath) (string, error) {
absOutput, err := filepath.Abs(output)
if err != nil {
return "", err
}
absPkgPath, err := filepath.Abs(string(path))
if err != nil {
return "", err
}
pkgPath := strings.TrimPrefix(absPkgPath, guessRootDir())
return filepath.Join(absOutput, pkgPath), nil
}
// WriteDirFile write file to the path and also create
// directory if needed. with:
// Dir perm -> 0755; File perm -> 0o644
func WriteDirFile(pathWithName string, data []byte) error {
path := filepath.Dir(pathWithName)
// Create Dir if not exists
if _, err := os.Stat(path); os.IsNotExist(err) {
os.MkdirAll(path, 0o755)
}
return os.WriteFile(pathWithName, data, 0o644)
}
// copyDir copies the dir from src to dst, the paths have to be
// absolute to ensure consistent behavior.
func copyDir(src, dst string) error {
if !filepath.IsAbs(src) || !filepath.IsAbs(dst) {
return fmt.Errorf("src or dst path not abosulte, src: %s dst: %s", src, dst)
}
entries, err := os.ReadDir(src)
if err != nil {
return fmt.Errorf("cannot read dir: %s", src)
}
if err := os.MkdirAll(dst, 0o755); err != nil {
return fmt.Errorf("failed to create directory: '%s', error: '%w'", dst, err)
}
for _, entry := range entries {
srcPath := filepath.Join(src, entry.Name())
dstPath := filepath.Join(dst, entry.Name())
if entry.Type().IsDir() {
copyDir(srcPath, dstPath)
} else if entry.Type().IsRegular() {
copyFile(srcPath, dstPath)
}
}
return nil
}
// copyFile copies the file from src to dst, the paths have
// to be absolute to ensure consistent behavior.
func copyFile(src, dst string) error {
if !filepath.IsAbs(src) || !filepath.IsAbs(dst) {
return fmt.Errorf("src or dst path not abosulte, src: %s dst: %s", src, dst)
}
// verify if it's regular flile
srcStat, err := os.Stat(src)
if err != nil {
return fmt.Errorf("cannot copy file: %w", err)
}
if !srcStat.Mode().IsRegular() {
return fmt.Errorf("%s not a regular file", src)
}
// create dst file
dstFile, err := os.Create(dst)
if err != nil {
return err
}
defer dstFile.Close()
// open src file
srcFile, err := os.Open(src)
if err != nil {
return err
}
defer srcFile.Close()
// copy srcFile -> dstFile
_, err = io.Copy(dstFile, srcFile)
if err != nil {
return err
}
return nil
}