Skip to content

Commit a291698

Browse files
committed
dap: support stopOnEntry to configure behavior when starting the debugger
This will configure the default behavior when beginning to evaluate a build target. When `stopOnEntry` is used, it will default to `stepNext`. Otherwise, `stepContinue` will be used. Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
1 parent 0c74726 commit a291698

File tree

7 files changed

+76
-67
lines changed

7 files changed

+76
-67
lines changed

commands/dap.go

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55

66
"github.com/docker/buildx/dap"
7+
"github.com/docker/buildx/dap/common"
78
"github.com/docker/buildx/util/cobrautil"
89
"github.com/docker/buildx/util/ioset"
910
"github.com/docker/buildx/util/progress"
@@ -20,31 +21,18 @@ func dapCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
2021
}
2122
cobrautil.MarkCommandExperimental(cmd)
2223

23-
flags := cmd.Flags()
24-
flags.StringVar(&options.OnFlag, "on", "error", "When to pause the adapter ([always, error])")
25-
26-
cobrautil.MarkFlagsExperimental(flags, "on")
27-
2824
dapBuildCmd := buildCmd(dockerCli, rootOpts, &options)
2925
dapBuildCmd.Args = cobra.RangeArgs(0, 1)
3026
cmd.AddCommand(dapBuildCmd)
3127
return cmd
3228
}
3329

34-
type dapOptions struct {
35-
// OnFlag is a flag to configure the timing of launching the debugger.
36-
OnFlag string
37-
}
30+
type dapOptions struct{}
3831

3932
func (d *dapOptions) New(in ioset.In) (debuggerInstance, error) {
40-
invokeConfig, err := parseInvokeConfig("", d.OnFlag)
41-
if err != nil {
42-
return nil, err
43-
}
44-
4533
conn := dap.NewConn(in.Stdin, in.Stdout)
4634
return &adapterProtocolDebugger{
47-
Adapter: dap.New[LaunchConfig](invokeConfig),
35+
Adapter: dap.New[LaunchConfig](),
4836
conn: conn,
4937
}, nil
5038
}
@@ -53,6 +41,7 @@ type LaunchConfig struct {
5341
Dockerfile string `json:"dockerfile,omitempty"`
5442
ContextPath string `json:"contextPath,omitempty"`
5543
Target string `json:"target,omitempty"`
44+
common.Config
5645
}
5746

5847
type adapterProtocolDebugger struct {

dap/adapter.go

Lines changed: 41 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,20 @@ import (
1111
"sync/atomic"
1212

1313
"github.com/docker/buildx/build"
14+
"github.com/docker/buildx/dap/common"
1415
"github.com/google/go-dap"
1516
gateway "github.com/moby/buildkit/frontend/gateway/client"
1617
"github.com/pkg/errors"
1718
"golang.org/x/sync/errgroup"
1819
)
1920

20-
type Adapter[T any] struct {
21+
type Adapter[C LaunchConfig] struct {
2122
srv *Server
2223
eg *errgroup.Group
23-
cfg build.InvokeConfig
24+
cfg common.Config
2425

2526
initialized chan struct{}
26-
started chan launchResponse[T]
27+
started chan launchResponse[C]
2728
configuration chan struct{}
2829

2930
evaluateReqCh chan *evaluateRequest
@@ -36,24 +37,21 @@ type Adapter[T any] struct {
3637
idPool *idPool
3738
}
3839

39-
func New[T any](cfg *build.InvokeConfig) *Adapter[T] {
40-
d := &Adapter[T]{
40+
func New[C LaunchConfig]() *Adapter[C] {
41+
d := &Adapter[C]{
4142
initialized: make(chan struct{}),
42-
started: make(chan launchResponse[T], 1),
43+
started: make(chan launchResponse[C], 1),
4344
configuration: make(chan struct{}),
4445
evaluateReqCh: make(chan *evaluateRequest),
4546
threads: make(map[int]*thread),
4647
nextThreadID: 1,
4748
idPool: new(idPool),
4849
}
49-
if cfg != nil {
50-
d.cfg = *cfg
51-
}
5250
d.srv = NewServer(d.dapHandler())
5351
return d
5452
}
5553

56-
func (d *Adapter[T]) Start(ctx context.Context, conn Conn) (T, error) {
54+
func (d *Adapter[C]) Start(ctx context.Context, conn Conn) (C, error) {
5755
d.eg, _ = errgroup.WithContext(ctx)
5856
d.eg.Go(func() error {
5957
return d.srv.Serve(ctx, conn)
@@ -65,10 +63,11 @@ func (d *Adapter[T]) Start(ctx context.Context, conn Conn) (T, error) {
6563
if !ok {
6664
resp.Error = context.Canceled
6765
}
66+
d.cfg = resp.Config.GetConfig()
6867
return resp.Config, resp.Error
6968
}
7069

71-
func (d *Adapter[T]) Stop() error {
70+
func (d *Adapter[C]) Stop() error {
7271
if d.eg == nil {
7372
return nil
7473
}
@@ -96,44 +95,44 @@ func (d *Adapter[T]) Stop() error {
9695
return err
9796
}
9897

99-
func (d *Adapter[T]) Initialize(c Context, req *dap.InitializeRequest, resp *dap.InitializeResponse) error {
98+
func (d *Adapter[C]) Initialize(c Context, req *dap.InitializeRequest, resp *dap.InitializeResponse) error {
10099
close(d.initialized)
101100

102101
// Set capabilities.
103102
resp.Body.SupportsConfigurationDoneRequest = true
104103
return nil
105104
}
106105

107-
type launchResponse[T any] struct {
108-
Config T
106+
type launchResponse[C any] struct {
107+
Config C
109108
Error error
110109
}
111110

112-
func (d *Adapter[T]) Launch(c Context, req *dap.LaunchRequest, resp *dap.LaunchResponse) error {
111+
func (d *Adapter[C]) Launch(c Context, req *dap.LaunchRequest, resp *dap.LaunchResponse) error {
113112
defer close(d.started)
114113

115-
var cfg T
114+
var cfg C
116115
if err := json.Unmarshal(req.Arguments, &cfg); err != nil {
117-
d.started <- launchResponse[T]{Error: err}
116+
d.started <- launchResponse[C]{Error: err}
118117
return err
119118
}
120119

121120
d.start(c)
122121

123-
d.started <- launchResponse[T]{Config: cfg}
122+
d.started <- launchResponse[C]{Config: cfg}
124123
return nil
125124
}
126125

127-
func (d *Adapter[T]) Disconnect(c Context, req *dap.DisconnectRequest, resp *dap.DisconnectResponse) error {
126+
func (d *Adapter[C]) Disconnect(c Context, req *dap.DisconnectRequest, resp *dap.DisconnectResponse) error {
128127
close(d.evaluateReqCh)
129128
return nil
130129
}
131130

132-
func (d *Adapter[T]) start(c Context) {
131+
func (d *Adapter[C]) start(c Context) {
133132
c.Go(d.launch)
134133
}
135134

136-
func (d *Adapter[T]) Continue(c Context, req *dap.ContinueRequest, resp *dap.ContinueResponse) error {
135+
func (d *Adapter[C]) Continue(c Context, req *dap.ContinueRequest, resp *dap.ContinueResponse) error {
137136
d.threadsMu.RLock()
138137
t := d.threads[req.Arguments.ThreadId]
139138
d.threadsMu.RUnlock()
@@ -142,7 +141,7 @@ func (d *Adapter[T]) Continue(c Context, req *dap.ContinueRequest, resp *dap.Con
142141
return nil
143142
}
144143

145-
func (d *Adapter[T]) Next(c Context, req *dap.NextRequest, resp *dap.NextResponse) error {
144+
func (d *Adapter[C]) Next(c Context, req *dap.NextRequest, resp *dap.NextResponse) error {
146145
d.threadsMu.RLock()
147146
t := d.threads[req.Arguments.ThreadId]
148147
d.threadsMu.RUnlock()
@@ -151,7 +150,7 @@ func (d *Adapter[T]) Next(c Context, req *dap.NextRequest, resp *dap.NextRespons
151150
return nil
152151
}
153152

154-
func (d *Adapter[T]) SetBreakpoints(c Context, req *dap.SetBreakpointsRequest, resp *dap.SetBreakpointsResponse) error {
153+
func (d *Adapter[C]) SetBreakpoints(c Context, req *dap.SetBreakpointsRequest, resp *dap.SetBreakpointsResponse) error {
155154
// TODO: implement breakpoints
156155
for range req.Arguments.Breakpoints {
157156
// Fail to create all breakpoints that were requested.
@@ -163,13 +162,13 @@ func (d *Adapter[T]) SetBreakpoints(c Context, req *dap.SetBreakpointsRequest, r
163162
return nil
164163
}
165164

166-
func (d *Adapter[T]) ConfigurationDone(c Context, req *dap.ConfigurationDoneRequest, resp *dap.ConfigurationDoneResponse) error {
165+
func (d *Adapter[C]) ConfigurationDone(c Context, req *dap.ConfigurationDoneRequest, resp *dap.ConfigurationDoneResponse) error {
167166
d.configuration <- struct{}{}
168167
close(d.configuration)
169168
return nil
170169
}
171170

172-
func (d *Adapter[T]) launch(c Context) {
171+
func (d *Adapter[C]) launch(c Context) {
173172
// Send initialized event.
174173
c.C() <- &dap.InitializedEvent{
175174
Event: dap.Event{
@@ -198,7 +197,7 @@ func (d *Adapter[T]) launch(c Context) {
198197
started := c.Go(func(c Context) {
199198
defer d.deleteThread(c, t)
200199
defer close(req.errCh)
201-
req.errCh <- t.Evaluate(c, req.c, req.ref, req.meta, req.inputs)
200+
req.errCh <- t.Evaluate(c, req.c, req.ref, req.meta, req.inputs, d.cfg)
202201
})
203202

204203
if !started {
@@ -209,7 +208,7 @@ func (d *Adapter[T]) launch(c Context) {
209208
}
210209
}
211210

212-
func (d *Adapter[T]) newThread(ctx Context, name string) (t *thread) {
211+
func (d *Adapter[C]) newThread(ctx Context, name string) (t *thread) {
213212
d.threadsMu.Lock()
214213
id := d.nextThreadID
215214
t = &thread{
@@ -232,14 +231,14 @@ func (d *Adapter[T]) newThread(ctx Context, name string) (t *thread) {
232231
return t
233232
}
234233

235-
func (d *Adapter[T]) getThread(id int) (t *thread) {
234+
func (d *Adapter[C]) getThread(id int) (t *thread) {
236235
d.threadsMu.Lock()
237236
t = d.threads[id]
238237
d.threadsMu.Unlock()
239238
return t
240239
}
241240

242-
func (d *Adapter[T]) deleteThread(ctx Context, t *thread) {
241+
func (d *Adapter[C]) deleteThread(ctx Context, t *thread) {
243242
d.threadsMu.Lock()
244243
delete(d.threads, t.id)
245244
d.threadsMu.Unlock()
@@ -262,7 +261,7 @@ type evaluateRequest struct {
262261
errCh chan<- error
263262
}
264263

265-
func (d *Adapter[T]) EvaluateResult(ctx context.Context, name string, c gateway.Client, res *gateway.Result, inputs build.Inputs) error {
264+
func (d *Adapter[C]) EvaluateResult(ctx context.Context, name string, c gateway.Client, res *gateway.Result, inputs build.Inputs) error {
266265
eg, _ := errgroup.WithContext(ctx)
267266
if res.Ref != nil {
268267
eg.Go(func() error {
@@ -279,7 +278,7 @@ func (d *Adapter[T]) EvaluateResult(ctx context.Context, name string, c gateway.
279278
return eg.Wait()
280279
}
281280

282-
func (d *Adapter[T]) evaluateRef(ctx context.Context, name string, c gateway.Client, ref gateway.Reference, meta map[string][]byte, inputs build.Inputs) error {
281+
func (d *Adapter[C]) evaluateRef(ctx context.Context, name string, c gateway.Client, ref gateway.Reference, meta map[string][]byte, inputs build.Inputs) error {
283282
errCh := make(chan error, 1)
284283

285284
// Send a solve request to the launch routine
@@ -307,7 +306,7 @@ func (d *Adapter[T]) evaluateRef(ctx context.Context, name string, c gateway.Cli
307306
}
308307
}
309308

310-
func (d *Adapter[T]) Threads(c Context, req *dap.ThreadsRequest, resp *dap.ThreadsResponse) error {
309+
func (d *Adapter[C]) Threads(c Context, req *dap.ThreadsRequest, resp *dap.ThreadsResponse) error {
311310
d.threadsMu.RLock()
312311
defer d.threadsMu.RUnlock()
313312

@@ -321,7 +320,7 @@ func (d *Adapter[T]) Threads(c Context, req *dap.ThreadsRequest, resp *dap.Threa
321320
return nil
322321
}
323322

324-
func (d *Adapter[T]) StackTrace(c Context, req *dap.StackTraceRequest, resp *dap.StackTraceResponse) error {
323+
func (d *Adapter[C]) StackTrace(c Context, req *dap.StackTraceRequest, resp *dap.StackTraceResponse) error {
325324
t := d.getThread(req.Arguments.ThreadId)
326325
if t == nil {
327326
return errors.Errorf("no such thread: %d", req.Arguments.ThreadId)
@@ -331,7 +330,7 @@ func (d *Adapter[T]) StackTrace(c Context, req *dap.StackTraceRequest, resp *dap
331330
return nil
332331
}
333332

334-
func (d *Adapter[T]) Source(c Context, req *dap.SourceRequest, resp *dap.SourceResponse) error {
333+
func (d *Adapter[C]) Source(c Context, req *dap.SourceRequest, resp *dap.SourceResponse) error {
335334
fname := req.Arguments.Source.Path
336335

337336
dt, ok := d.sourceMap.Get(fname)
@@ -343,7 +342,7 @@ func (d *Adapter[T]) Source(c Context, req *dap.SourceRequest, resp *dap.SourceR
343342
return nil
344343
}
345344

346-
func (d *Adapter[T]) evaluate(ctx context.Context, name string, c gateway.Client, res *gateway.Result, opt build.Options) error {
345+
func (d *Adapter[C]) evaluate(ctx context.Context, name string, c gateway.Client, res *gateway.Result, opt build.Options) error {
347346
errCh := make(chan error, 1)
348347

349348
started := d.srv.Go(func(ctx Context) {
@@ -362,13 +361,13 @@ func (d *Adapter[T]) evaluate(ctx context.Context, name string, c gateway.Client
362361
}
363362
}
364363

365-
func (d *Adapter[T]) Handler() build.Handler {
364+
func (d *Adapter[C]) Handler() build.Handler {
366365
return build.Handler{
367366
Evaluate: d.evaluate,
368367
}
369368
}
370369

371-
func (d *Adapter[T]) dapHandler() Handler {
370+
func (d *Adapter[C]) dapHandler() Handler {
372371
return Handler{
373372
Initialize: d.Initialize,
374373
Launch: d.Launch,
@@ -383,15 +382,15 @@ func (d *Adapter[T]) dapHandler() Handler {
383382
}
384383
}
385384

386-
func (d *Adapter[T]) Out() io.Writer {
387-
return &adapterWriter[T]{d}
385+
func (d *Adapter[C]) Out() io.Writer {
386+
return &adapterWriter[C]{d}
388387
}
389388

390-
type adapterWriter[T any] struct {
391-
*Adapter[T]
389+
type adapterWriter[C LaunchConfig] struct {
390+
*Adapter[C]
392391
}
393392

394-
func (d *adapterWriter[T]) Write(p []byte) (n int, err error) {
393+
func (d *adapterWriter[C]) Write(p []byte) (n int, err error) {
395394
started := d.srv.Go(func(c Context) {
396395
<-d.initialized
397396

dap/adapter_test.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,14 @@ import (
77
"testing"
88
"time"
99

10+
"github.com/docker/buildx/dap/common"
1011
"github.com/google/go-dap"
1112
"github.com/stretchr/testify/assert"
1213
"golang.org/x/sync/errgroup"
1314
)
1415

1516
func TestLaunch(t *testing.T) {
16-
adapter, conn, client := NewTestAdapter[any](t)
17+
adapter, conn, client := NewTestAdapter[common.Config](t)
1718

1819
ctx, cancel := context.WithTimeoutCause(context.Background(), 10*time.Second, context.DeadlineExceeded)
1920
defer cancel()
@@ -68,7 +69,7 @@ func TestLaunch(t *testing.T) {
6869
eg.Wait()
6970
}
7071

71-
func NewTestAdapter[T any](t *testing.T) (*Adapter[T], Conn, *Client) {
72+
func NewTestAdapter[C LaunchConfig](t *testing.T) (*Adapter[C], Conn, *Client) {
7273
t.Helper()
7374

7475
rd1, wr1 := io.Pipe()
@@ -84,7 +85,7 @@ func NewTestAdapter[T any](t *testing.T) (*Adapter[T], Conn, *Client) {
8485
clientConn.Close()
8586
})
8687

87-
adapter := New[T](nil)
88+
adapter := New[C]()
8889
t.Cleanup(func() { adapter.Stop() })
8990

9091
client := NewClient(clientConn)

dap/common/config.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package common
2+
3+
type Config struct {
4+
StopOnEntry bool `json:"stopOnEntry,omitempty"`
5+
}
6+
7+
func (c Config) GetConfig() Config {
8+
return c
9+
}

dap/config.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package dap
2+
3+
import "github.com/docker/buildx/dap/common"
4+
5+
type LaunchConfig interface {
6+
GetConfig() common.Config
7+
}

0 commit comments

Comments
 (0)