-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathprecompile.go
132 lines (115 loc) · 3.72 KB
/
precompile.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
// Program precompile updates pre-built standard library packages for the
// playground.
//
// This script performs the following sequence of steps:
//
// - Enumerate all standard packages that should be available in the playground.
// - Precompile them, including transitive dependencies.
// - Delete all old precompiled archive.
// - Write all new precompiled archive in their place.
//
// This will use the same GopherJS version as specified in the playground gm.mod
// to ensure consistency. The script uses GopherJS compiler API directly, so
// it doesn't require the GopherJS tool to be installed.
package main
import (
"flag"
"fmt"
gobuild "go/build"
"os"
"path/filepath"
"strings"
"github.com/gopherjs/gopherjs/build"
"github.com/gopherjs/gopherjs/compiler"
log "github.com/sirupsen/logrus"
)
type logLevelFlag struct{ log.Level }
func (l *logLevelFlag) Set(raw string) error { return l.UnmarshalText([]byte(raw)) }
var (
logLevel logLevelFlag = logLevelFlag{Level: log.ErrorLevel}
)
func init() {
flag.Var(&logLevel, "log_level", "Default logging level.")
}
func run() error {
s, err := build.NewSession(&build.Options{
Verbose: true,
Minify: true,
NoCache: true,
})
if err != nil {
return fmt.Errorf("failed to create a build session: %w", err)
}
packages, err := s.XContext().Match([]string{"std"})
if err != nil {
return fmt.Errorf("failed to enumerate standard library packages")
}
packages = importable(packages)
packages = append(packages, "github.com/gopherjs/gopherjs/js", "github.com/gopherjs/gopherjs/nosync")
for _, pkg := range packages {
_, err := s.BuildImportPath(pkg)
if err != nil {
return fmt.Errorf("failed to precompile package %q: %w", pkg, err)
}
}
target, err := targetDir(s)
if err := os.RemoveAll(target); err != nil {
return fmt.Errorf("failed to clean out old precompiled archives: %w", err)
}
for _, archive := range s.UpToDateArchives {
if err := writeArchive(target, archive); err != nil {
return fmt.Errorf("failed to write package %q archive: %w", archive.ImportPath, err)
}
}
return nil
}
func writeArchive(target string, archive *compiler.Archive) error {
path := filepath.Join(target, filepath.FromSlash(archive.ImportPath)+".a.js")
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
return fmt.Errorf("failed to create precompiled package directory %q: %w", filepath.Dir(path), err)
}
f, err := os.Create(path)
if err != nil {
return fmt.Errorf("failed to create precompiled archive %q: %w", path, err)
}
defer f.Close()
return compiler.WriteArchive(archive, f)
}
// targetDir returns path to the directory where precompiled packages must be
// stored.
func targetDir(s *build.Session) (string, error) {
pkg, err := s.XContext().Import("github.com/gopherjs/gopherjs.github.io/playground", "", gobuild.FindOnly)
if err != nil {
return "", fmt.Errorf("failed to find playground package directory: %w", err)
}
target := filepath.Join(pkg.Dir, "pkg")
if _, err := os.Stat(target); os.IsNotExist(err) {
return "", fmt.Errorf("target directory %q not found", target)
}
return target, nil
}
// importable excludes packages that are incompatible with GopherJS or can't be
// directly imported by the user code. The remaining packages will be used as a
// starting points for precompilation.
func importable(all []string) []string {
result := []string{}
for _, pkg := range all {
switch {
case strings.HasPrefix(pkg, "vendor/"),
strings.Contains(pkg, "internal"),
strings.Contains(pkg, "pprof"),
strings.Contains(pkg, "plugin"):
continue
default:
result = append(result, pkg)
}
}
return result
}
func main() {
flag.Parse()
log.SetLevel(logLevel.Level)
if err := run(); err != nil {
log.Fatalf("Precompilation failed: %v", err)
}
}