8000 First experiments with RPC · arduino/arduino-builder@6366c3f · GitHub
[go: up one dir, main page]

Skip to content

Commit 6366c3f

Browse files
committed
First experiments with RPC
1 parent 29bab0d commit 6366c3f

File tree

5 files changed

+243
-3
lines changed

5 files changed

+243
-3
lines changed

src/arduino.cc/arduino-builder/main.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,13 @@ import (
4141
"strings"
4242
"syscall"
4343

44+
"runtime/pprof"
45+
"runtime/trace"
46+
4447
"arduino.cc/builder"
4548
"arduino.cc/builder/gohasissues"
4649
"arduino.cc/builder/i18n"
50+
"arduino.cc/builder/jsonrpc"
4751
"arduino.cc/builder/types"
4852
"arduino.cc/builder/utils"
4953
"arduino.cc/properties"
@@ -80,8 +84,10 @@ const FLAG_LOGGER_HUMAN = "human"
8084
const FLAG_LOGGER_HUMANTAGS = "humantags"
8185
const FLAG_LOGGER_MACHINE = "machine"
8286
const FLAG_VERSION = "version"
87+
const FLAG_DAEMON = "daemon"
8388
const FLAG_VID_PID = "vid-pid"
8489
const FLAG_JOBS = "jobs"
90+
const FLAG_TRACE = "trace"
8591

8692
type foldersFlag []string
8793

@@ -139,8 +145,10 @@ var debugLevelFlag *int
139145
var warningsLevelFlag *string
140146
var loggerFlag *string
141147
var versionFlag *bool
148+
var daemonFlag *bool
142149
var vidPidFlag *string
143150
var jobsFlag *int
151+
var traceFlag *bool
144152

145153
func init() {
146154
compileFlag = flag.Bool(FLAG_ACTION_COMPILE, false, "compiles the given sketch")
@@ -164,13 +172,39 @@ func init() {
164172
warningsLevelFlag = flag.String(FLAG_WARNINGS, "", "Sets warnings level. Available values are '"+FLAG_WARNINGS_NONE+"', '"+FLAG_WARNINGS_DEFAULT+"', '"+FLAG_WARNINGS_MORE+"' and '"+FLAG_WARNINGS_ALL+"'")
165173
loggerFlag = flag.String(FLAG_LOGGER, FLAG_LOGGER_HUMAN, "Sets type of logger. Available values are '"+FLAG_LOGGER_HUMAN+"', '"+FLAG_LOGGER_HUMANTAGS+"', '"+FLAG_LOGGER_MACHINE+"'")
166174
versionFlag = flag.Bool(FLAG_VERSION, false, "prints version and exits")
175+
daemonFlag = flag.Bool(FLAG_DAEMON, false, "daemonizes and serves its functions via rpc")
167176
vidPidFlag = flag.String(FLAG_VID_PID, "", "specify to use vid/pid specific build properties, as defined in boards.txt")
168177
jobsFlag = flag.Int(FLAG_JOBS, 0, "specify how many concurrent gcc processes should run at the same time. Defaults to the number of available cores on the running machine")
178+
traceFlag = flag.Bool(FLAG_TRACE, false, "traces the whole process lifecycle")
169179
}
170180

171181
func main() {
182+
172183
flag.Parse()
173184

185+
if *traceFlag {
186+
f, err := os.Create("trace.out")
187+
if err != nil {
188+
panic(err)
189+
}
190+
defer f.Close()
191+
192+
f2, err := os.Create("profile.out")
193+
if err != nil {
194+
panic(err)
195+
}
196+
defer f2.Close()
197+
198+
pprof.StartCPUProfile(f2)
199+
defer pprof.StopCPUProfile()
200+
201+
err = trace.Start(f)
202+
if err != nil {
203+
panic(err)
204+
}
205+
defer trace.Stop()
206+
}
207+
174208
if *versionFlag {
175209
fmt.Println("Arduino Builder " + VERSION)
176210
fmt.Println("Copyright (C) 2015 Arduino LLC and contributors")
@@ -188,6 +222,15 @@ func main() {
188222

189223
ctx := &types.Context{}
190224

225+
if *daemonFlag {
226+
var loggerBuffer []string
227+
logger := i18n.AccumulatorLogger{}
228+
logger.Buffer = &loggerBuffer
229+
//logger := i18n.HumanLogger{}
230+
ctx.SetLogger(logger)
231+
jsonrpc.RegisterAndServeJsonRPC(ctx)
232+
}
233+
191234
if *buildOptionsFileFlag != "" {
192235
buildOptions := make(properties.Map)
193236
if _, err := os.Stat(*buildOptionsFileFlag); err == nil {

F438 src/arduino.cc/builder/i18n/i18n.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ type Logger interface {
4949
UnformattedWrite(w io.Writer, data []byte)
5050
Println(level string, format string, a ...interface{})
5151
Name() string
52+
Flush() string
5253
}
5354

5455
type NoopLogger struct{}
@@ -61,10 +62,44 @@ func (s NoopLogger) UnformattedWrite(w io.Writer, data []byte) {}
6162

6263
func (s NoopLogger) Println(level string, format string, a ...interface{}) {}
6364

65+
func (s NoopLogger) Flush() string {
66+
return ""
67+
}
68+
6469
func (s NoopLogger) Name() string {
6570
return "noop"
6671
}
6772

73+
type AccumulatorLogger struct {
74+
Buffer *[]string
75+
}
76+
77+
func (s AccumulatorLogger) Fprintln(w io.Writer, level string, format string, a ...interface{}) {
78+
*s.Buffer = append(*s.Buffer, Format(format, a...))
79+
}
80+
81+
func (s AccumulatorLogger) UnformattedFprintln(w io.Writer, str string) {
82+
*s.Buffer = append(*s.Buffer, str)
83+
}
84+
85+
func (s AccumulatorLogger) UnformattedWrite(w io.Writer, data []byte) {
86+
*s.Buffer = append(*s.Buffer, string(data))
87+
}
88+
89+
func (s AccumulatorLogger) Println(level string, format string, a ...interface{}) {
90+
s.Fprintln(nil, level, format, a...)
91+
}
92+
93+
func (s AccumulatorLogger) Flush() string {
94+
str := strings.Join(*s.Buffer, "\n")
95+
*s.Buffer = (*s.Buffer)[0:0]
96+
return str
97+
}
98+
99+
func (s AccumulatorLogger) Name() string {
100+
return "accumulator"
101+
}
102+
68103
type HumanTagsLogger struct{}
69104

70105
func (s HumanTagsLogger) Fprintln(w io.Writer, level string, format string, a ...interface{}) {
@@ -84,6 +119,10 @@ func (s HumanTagsLogger) UnformattedWrite(w io.Writer, data []byte) {
84119
write(w, data)
85120
}
86121

122+
func (s HumanTagsLogger) Flush() string {
123+
return ""
124+
}
125+
87126
func (s HumanTagsLogger) Name() string {
88127
return "humantags"
89128
}
@@ -106,6 +145,10 @@ func (s HumanLogger) UnformattedWrite(w io.Writer, data []byte) {
106145
write(w, data)
107146
}
108147

148+
func (s HumanLogger) Flush() string {
149+
return ""
150+
}
151+
109152
func (s HumanLogger) Name() string {
110153
return "human"
111154
}
@@ -124,6 +167,10 @@ func (s MachineLogger) UnformattedFprintln(w io.Writer, str string) {
124167
fprintln(w, str)
125168
}
126169

170+
func (s MachineLogger) Flush() string {
171+
return ""
172+
}
173+
127174
func (s MachineLogger) Name() string {
128175
return "machine"
129176
}

src/arduino.cc/builder/jsonrpc/rpc.go

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
package jsonrpc
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"log"
7+
"net/http"
8+
"strings"
9+
10+
"arduino.cc/builder"
11+
"arduino.cc/builder/types"
12+
"github.com/fsnotify/fsnotify"
13+
"github.com/osamingo/jsonrpc"
14+
"golang.org/x/net/context"
15+
)
16+
17+
type (
18+
BuildHandler struct {
19+
watcher *fsnotify.Watcher
20+
ctx *types.Context
21+
}
22+
BuildParams struct {
23+
HardwareFolders string
24+
ToolsFolders string
25+
BuiltInLibrariesFolders string
26+
OtherLibrariesFolders string
27+
SketchLocation string
28+
FQBN string
29+
ArduinoAPIVersion string
30+
CustomBuildProperties string
31+
Verbose bool
32+
BuildCachePath string
33+
BuildPath string
34+
WarningsLevel string
35+
}
36+
BuildResult struct {
37+
Message string `json:"message"`
38+
Error error
39+
}
40+
)
41+
42+
type (
43+
WatchHandler struct {
44+
watcher *fsnotify.Watcher
45+
}
46+
WatchParams struct {
47+
Path string `json:"path"`
48+
}
49+
WatchResult struct {
50+
Message string `json:"message"`
51+
}
52+
)
53+
54+
func (h *BuildHandler) ServeJSONRPC(c context.Context, params *json.RawMessage) (interface{}, *jsonrpc.Error) {
55+
56+
var p BuildParams
57+
if err := jsonrpc.Unmarshal(params, &p); err != nil {
58+
fmt.Println(err)
59+
return nil, err
60+
}
61+
62+
h.ctx.HardwareFolders = strings.Split(p.HardwareFolders, ",")
63+
h.ctx.ToolsFolders = strings.Split(p.ToolsFolders, ",")
64+
h.ctx.BuiltInLibrariesFolders = strings.Split(p.BuiltInLibrariesFolders, ",")
65+
h.ctx.OtherLibrariesFolders = strings.Split(p.OtherLibrariesFolders, ",")
66+
h.ctx.SketchLocation = p.SketchLocation
67+
h.ctx.CustomBuildProperties = strings.Split(p.CustomBuildProperties, ",")
68+
h.ctx.ArduinoAPIVersion = p.ArduinoAPIVersion
69+
h.ctx.FQBN = p.FQBN
70+
h.ctx.Verbose = p.Verbose
71+
h.ctx.BuildCachePath = p.BuildCachePath
72+
h.ctx.BuildPath = p.BuildPath
73+
h.ctx.WarningsLevel = p.WarningsLevel
74+
75+
err := builder.RunBuilder(h.ctx)
76+
if err != nil {
77+
return BuildResult{
78+
Message: h.ctx.GetLogger().Flush(),
79+
Error: err,
80+
}, nil
81+
}
82+
83+
return BuildResult{
84+
Message: h.ctx.GetLogger().Flush(),
85+
}, nil
86+
}
87+
88+
func (h *WatchHandler) ServeJSONRPC(c context.Context, params *json.RawMessage) (interface{}, *jsonrpc.Error) {
89+
90+
var p WatchParams
91+
if err := jsonrpc.Unmarshal(params, &p); err != nil {
92+
return nil, err
93+
}
94+
95+
err := h.watcher.Add(p.Path)
96+
if err != nil {
97+
return nil, jsonrpc.ErrInvalidParams()
98+
}
99+
return BuildResult{
100+
Message: "OK " + p.Path,
101+
}, nil
102+
}
103+
104+
func startWatching(ctx *types.Context) *fsnotify.Watcher {
105+
watcher, err := fsnotify.NewWatcher()
106+
if err != nil {
107+
log.Fatal(err)
108+
}
109+
110+
go func() {
111+
for {
112+
select {
113+
case event := <-watcher.Events:
114+
ctx.CanUseCachedTools = false
115+
log.Println("event:", event)
116+
case err := <-watcher.Errors:
117+
log.Println("error:", err)
118+
}
119+
}
120+
}()
121+
return watcher
122+
}
123+
124+
func RegisterAndServeJsonRPC(ctx *types.Context) {
125+
126+
watcher := startWatching(ctx)
127+
128+
jsonrpc.RegisterMethod("Main.Build", &BuildHandler{watcher, ctx}, BuildParams{}, BuildResult{})
129+
jsonrpc.RegisterMethod("Main.AddWatchPath", &WatchHandler{watcher}, WatchParams{}, WatchResult{})
130+
131+
http.HandleFunc("/jrpc", func(w http.ResponseWriter, r *http.Request) {
132+
jsonrpc.HandlerFunc(r.Context(), w, r)
133+
})
134+
http.HandleFunc("/jrpc/debug", jsonrpc.DebugHandlerFunc)
135+
if err := http.ListenAndServe(":8888", nil); err != nil {
136+
log.Fatalln(err)
137+
}
138+
}

src/arduino.cc/builder/tools_loader.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,16 @@
3030
package builder
3131

3232
import (
33+
"fmt"
34+
"os"
35+
"path/filepath"
36+
"strings"
37+
3338
"arduino.cc/builder/constants"
3439
"arduino.cc/builder/gohasissues"
3540
"arduino.cc/builder/i18n"
3641
"arduino.cc/builder/types"
3742
"arduino.cc/builder/utils"
38-
"os"
39-
"path/filepath"
40-
"strings"
4143
)
4244

4345
type ToolsLoader struct{}
@@ -47,6 +49,11 @@ func (s *ToolsLoader) Run(ctx *types.Context) error {
4749

4850
tools := []*types.Tool{}
4951

52+
if ctx.CanUseCachedTools {
53+
fmt.Println("no fs modification, can use cached ctx")
54+
return nil
55+
}
56+
5057
for _, folder := range folders {
5158
builtinToolsVersionsFile, err := findBuiltinToolsVersionsFile(folder)
5259
if err != nil {
@@ -82,6 +89,8 @@ func (s *ToolsLoader) Run(ctx *types.Context) error {
8289
}
8390
}
8491

92+
ctx.CanUseCachedTools = true
93+
8594
ctx.Tools = tools
8695

8796
return nil

src/arduino.cc/builder/types/context.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,9 @@ type Context struct {
9090

9191
// ReadFileAndStoreInContext command
9292
FileToRead string
93+
94+
// Reuse old tools since the backing storage didn't change
95+
CanUseCachedTools bool
9396
}
9497

9598
func (ctx *Context) ExtractBuildOptions() properties.Map {

0 commit comments

Comments
 (0)
0