Skip to content

Commit ce6bb98

Browse files
committed
Added debug check command to check if a combination of board/programmer supports debugging. (arduino#2443)
* Moved rcp message to proper position * Added gRPC command to check for debugger support * Made debug flags var non-global * Implementation of 'debug check' command * Implementation of cli command 'debug check' * added integration test * Renamed field for clarity * Added minimum debug_fqbn computation in 'debug check' command
1 parent 4d20a25 commit ce6bb98

File tree

13 files changed

+844
-304
lines changed

13 files changed

+844
-304
lines changed

arduino/cores/fqbn.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,16 @@ type FQBN struct {
3030
Configs *properties.Map
3131
}
3232

33+
// MustParseFQBN extract an FQBN object from the input string
34+
// or panics if the input is not a valid FQBN.
35+
func MustParseFQBN(fqbnIn string) *FQBN {
36+
res, err := ParseFQBN(fqbnIn)
37+
if err != nil {
38+
panic(err)
39+
}
40+
return res
41+
}
42+
3343
// ParseFQBN extract an FQBN object from the input string
3444
func ParseFQBN(fqbnIn string) (*FQBN, error) {
3545
// Split fqbn

commands/daemon/debug.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,9 @@ func (s *ArduinoCoreServerImpl) GetDebugConfig(ctx context.Context, req *rpc.Get
6666
res, err := cmd.GetDebugConfig(ctx, req)
6767
return res, convertErrorToRPCStatus(err)
6868
}
69+
70+
// IsDebugSupported checks if debugging is supported for a given configuration
71+
func (s *ArduinoCoreServerImpl) IsDebugSupported(ctx context.Context, req *rpc.IsDebugSupportedRequest) (*rpc.IsDebugSupportedResponse, error) {
72+
res, err := cmd.IsDebugSupported(ctx, req)
73+
return res, convertErrorToRPCStatus(err)
74+
}

commands/debug/debug.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ func Debug(ctx context.Context, req *rpc.GetDebugConfigRequest, inStream io.Read
118118

119119
// getCommandLine compose a debug command represented by a core recipe
120120
func getCommandLine(req *rpc.GetDebugConfigRequest, pme *packagemanager.Explorer) ([]string, error) {
121-
debugInfo, err := getDebugProperties(req, pme)
121+
debugInfo, err := getDebugProperties(req, pme, false)
122122
if err != nil {
123123
return nil, err
124124
}

commands/debug/debug_info.go

Lines changed: 80 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ package debug
1818
import (
1919
"context"
2020
"encoding/json"
21+
"errors"
22+
"reflect"
2123
"slices"
2224
"strconv"
2325
"strings"
@@ -41,25 +43,83 @@ func GetDebugConfig(ctx context.Context, req *rpc.GetDebugConfigRequest) (*rpc.G
4143
return nil, &arduino.InvalidInstanceError{}
4244
}
4345
defer release()
44-
return getDebugProperties(req, pme)
46+
return getDebugProperties(req, pme, false)
4547
}
4648

47-
func getDebugProperties(req *rpc.GetDebugConfigRequest, pme *packagemanager.Explorer) (*rpc.GetDebugConfigResponse, error) {
48-
// TODO: make a generic function to extract sketch from request
49-
// and remove duplication in commands/compile.go
50-
if req.GetSketchPath() == "" {
51-
return nil, &arduino.MissingSketchPathError{}
49+
// IsDebugSupported checks if the given board/programmer configuration supports debugging.
50+
func IsDebugSupported(ctx context.Context, req *rpc.IsDebugSupportedRequest) (*rpc.IsDebugSupportedResponse, error) {
51+
pme, release := instances.GetPackageManagerExplorer(req.GetInstance())
52+
if pme == nil {
53+
return nil, &arduino.InvalidInstanceError{}
54+
}
55+
defer release()
56+
configRequest := &rpc.GetDebugConfigRequest{
57+
Instance: req.GetInstance(),
58+
Fqbn: req.GetFqbn(),
59+
SketchPath: "",
60+
Port: req.GetPort(),
61+
Interpreter: req.GetInterpreter(),
62+
ImportDir: "",
63+
Programmer: req.GetProgrammer(),
64+
}
65+
expectedOutput, err := getDebugProperties(configRequest, pme, true)
66+
var x *arduino.FailedDebugError
67+
if errors.As(err, &x) {
68+
return &rpc.IsDebugSupportedResponse{DebuggingSupported: false}, nil
5269
}
53-
sketchPath := paths.New(req.GetSketchPath())
54-
sk, err := sketch.New(sketchPath)
5570
if err != nil {
56-
return nil, &arduino.CantOpenSketchError{Cause: err}
71+
return nil, err
72+
}
73+
74+
// Compute the minimum FQBN required to get the same debug configuration.
75+
// (i.e. the FQBN cleaned up of the options that do not affect the debugger configuration)
76+
minimumFQBN := cores.MustParseFQBN(req.GetFqbn())
77+
for _, config := range minimumFQBN.Configs.Keys() {
78+
checkFQBN := minimumFQBN.Clone()
79+
checkFQBN.Configs.Remove(config)
80+
configRequest.Fqbn = checkFQBN.String()
81+
checkOutput, err := getDebugProperties(configRequest, pme, true)
82+
if err == nil && reflect.DeepEqual(expectedOutput, checkOutput) {
83+
minimumFQBN.Configs.Remove(config)
84+
}
85+
}
86+
return &rpc.IsDebugSupportedResponse{
87+
DebuggingSupported: true,
88+
DebugFqbn: minimumFQBN.String(),
89+
}, nil
90+
}
91+
92+
func getDebugProperties(req *rpc.GetDebugConfigRequest, pme *packagemanager.Explorer, skipSketchChecks bool) (*rpc.GetDebugConfigResponse, error) {
93+
var (
94+
sketchName string
95+
sketchDefaultFQBN string
96+
sketchDefaultBuildPath *paths.Path
97+
)
98+
if !skipSketchChecks {
99+
// TODO: make a generic function to extract sketch from request
100+
// and remove duplication in commands/compile.go
101+
if req.GetSketchPath() == "" {
102+
return nil, &arduino.MissingSketchPathError{}
103+
}
104+
sketchPath := paths.New(req.GetSketchPath())
105+
sk, err := sketch.New(sketchPath)
106+
if err != nil {
107+
return nil, &arduino.CantOpenSketchError{Cause: err}
108+
}
109+
sketchName = sk.Name
110+
sketchDefaultFQBN = sk.GetDefaultFQBN()
111+
sketchDefaultBuildPath = sk.DefaultBuildPath()
112+
} else {
113+
// Use placeholder sketch data
114+
sketchName = "Sketch"
115+
sketchDefaultFQBN = ""
116+
sketchDefaultBuildPath = paths.New("SketchBuildPath")
57117
}
58118

59119
// XXX Remove this code duplication!!
60120
fqbnIn := req.GetFqbn()
61-
if fqbnIn == "" && sk != nil {
62-
fqbnIn = sk.GetDefaultFQBN()
121+
if fqbnIn == "" {
122+
fqbnIn = sketchDefaultFQBN
63123
}
64124
if fqbnIn == "" {
65125
return nil, &arduino.MissingFQBNError{}
@@ -124,16 +184,18 @@ func getDebugProperties(req *rpc.GetDebugConfigRequest, pme *packagemanager.Expl
124184
if importDir := req.GetImportDir(); importDir != "" {
125185
importPath = paths.New(importDir)
126186
} else {
127-
importPath = sk.DefaultBuildPath()
128-
}
129-
if !importPath.Exist() {
130-
return nil, &arduino.NotFoundError{Message: tr("Compiled sketch not found in %s", importPath)}
187+
importPath = sketchDefaultBuildPath
131188
}
132-
if !importPath.IsDir() {
133-
return nil, &arduino.NotFoundError{Message: tr("Expected compiled sketch in directory %s, but is a file instead", importPath)}
189+
if !skipSketchChecks {
190+
if !importPath.Exist() {
191+
return nil, &arduino.NotFoundError{Message: tr("Compiled sketch not found in %s", importPath)}
192+
}
193+
if !importPath.IsDir() {
194+
return nil, &arduino.NotFoundError{Message: tr("Expected compiled sketch in directory %s, but is a file instead", importPath)}
195+
}
134196
}
135197
toolProperties.SetPath("build.path", importPath)
136-
toolProperties.Set("build.project_name", sk.Name+".ino")
198+
toolProperties.Set("build.project_name", sketchName+".ino")
137199

138200
// Set debug port property
139201
port := req.GetPort()

internal/cli/debug/debug.go

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,27 +36,31 @@ import (
3636
"github.com/spf13/cobra"
3737
)
3838

39-
var (
40-
fqbnArg arguments.Fqbn
41-
portArgs arguments.Port
42-
interpreter string
43-
importDir string
44-
printInfo bool
45-
programmer arguments.Programmer
46-
tr = i18n.Tr
47-
)
39+
var tr = i18n.Tr
4840

4941
// NewCommand created a new `upload` command
5042
func NewCommand() *cobra.Command {
43+
var (
44+
fqbnArg arguments.Fqbn
45+
portArgs arguments.Port
46+
interpreter string
47+
importDir string
48+
printInfo bool
49+
programmer arguments.Programmer
50+
)
51+
5152
debugCommand := &cobra.Command{
5253
Use: "debug",
5354
Short: tr("Debug Arduino sketches."),
5455
Long: tr("Debug Arduino sketches. (this command opens an interactive gdb session)"),
5556
Example: " " + os.Args[0] + " debug -b arduino:samd:mkr1000 -P atmel_ice /home/user/Arduino/MySketch",
5657
Args: cobra.MaximumNArgs(1),
57-
Run: runDebugCommand,
58+
Run: func(cmd *cobra.Command, args []string) {
59+
runDebugCommand(args, &portArgs, &fqbnArg, interpreter, importDir, &programmer, printInfo)
60+
},
5861
}
5962

63+
debugCommand.AddCommand(newDebugCheckCommand())
6064
fqbnArg.AddToCommand(debugCommand)
6165
portArgs.AddToCommand(debugCommand)
6266
programmer.AddToCommand(debugCommand)
@@ -67,7 +71,8 @@ func NewCommand() *cobra.Command {
6771
return debugCommand
6872
}
6973

70-
func runDebugCommand(command *cobra.Command, args []string) {
74+
func runDebugCommand(args []string, portArgs *arguments.Port, fqbnArg *arguments.Fqbn,
75+
interpreter string, importDir string, programmer *arguments.Programmer, printInfo bool) {
7176
instance := instance.CreateAndInit()
7277
logrus.Info("Executing `arduino-cli debug`")
7378

@@ -81,7 +86,7 @@ func runDebugCommand(command *cobra.Command, args []string) {
8186
if err != nil {
8287
feedback.FatalError(err, feedback.ErrGeneric)
8388
}
84-
fqbn, port := arguments.CalculateFQBNAndPort(&portArgs, &fqbnArg, instance, sk.GetDefaultFqbn(), sk.GetDefaultPort(), sk.GetDefaultProtocol())
89+
fqbn, port := arguments.CalculateFQBNAndPort(portArgs, fqbnArg, instance, sk.GetDefaultFqbn(), sk.GetDefaultPort(), sk.GetDefaultProtocol())
8590
debugConfigRequested := &rpc.GetDebugConfigRequest{
8691
Instance: instance,
8792
Fqbn: fqbn,

internal/cli/debug/debug_check.go

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
// This file is part of arduino-cli.
2+
//
3+
// Copyright 2023 ARDUINO SA (http://www.arduino.cc/)
4+
//
5+
// This software is released under the GNU General Public License version 3,
6+
// which covers the main part of arduino-cli.
7+
// The terms of this license can be found at:
8+
// https://www.gnu.org/licenses/gpl-3.0.en.html
9+
//
10+
// You can be released from the requirements of the above licenses by purchasing
11+
// a commercial license. Buying such a license is mandatory if you want to
12+
// modify or otherwise use the software for commercial activities involving the
13+
// Arduino software without disclosing the source code of your own applications.
14+
// To purchase a commercial license, send an email to license@arduino.cc.
15+
16+
package debug
17+
18+
import (
19+
"context"
20+
"os"
21+
22+
"github.com/arduino/arduino-cli/commands/debug"
23+
"github.com/arduino/arduino-cli/internal/cli/arguments"
24+
"github.com/arduino/arduino-cli/internal/cli/feedback"
25+
"github.com/arduino/arduino-cli/internal/cli/instance"
26+
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
27+
"github.com/sirupsen/logrus"
28+
"github.com/spf13/cobra"
29+
)
30+
31+
func newDebugCheckCommand() *cobra.Command {
32+
var (
33+
fqbnArg arguments.Fqbn
34+
portArgs arguments.Port
35+
interpreter string
36+
programmer arguments.Programmer
37+
)
38+
debugCheckCommand := &cobra.Command{
39+
Use: "check",
40+
Short: tr("Check if the given board/programmer combination supports debugging."),
41+
Example: " " + os.Args[0] + " debug check -b arduino:samd:mkr1000 -P atmel_ice",
42+
Run: func(cmd *cobra.Command, args []string) {
43+
runDebugCheckCommand(&portArgs, &fqbnArg, interpreter, &programmer)
44+
},
45+
}
46+
fqbnArg.AddToCommand(debugCheckCommand)
47+
portArgs.AddToCommand(debugCheckCommand)
48+
programmer.AddToCommand(debugCheckCommand)
49+
debugCheckCommand.Flags().StringVar(&interpreter, "interpreter", "console", tr("Debug interpreter e.g.: %s", "console, mi, mi1, mi2, mi3"))
50+
return debugCheckCommand
51+
}
52+
53+
func runDebugCheckCommand(portArgs *arguments.Port, fqbnArg *arguments.Fqbn, interpreter string, programmerArg *arguments.Programmer) {
54+
instance := instance.CreateAndInit()
55+
logrus.Info("Executing `arduino-cli debug`")
56+
57+
port, err := portArgs.GetPort(instance, "", "")
58+
if err != nil {
59+
feedback.FatalError(err, feedback.ErrBadArgument)
60+
}
61+
fqbn := fqbnArg.String()
62+
resp, err := debug.IsDebugSupported(context.Background(), &rpc.IsDebugSupportedRequest{
63+
Instance: instance,
64+
Fqbn: fqbn,
65+
Port: port,
66+
Interpreter: interpreter,
67+
Programmer: programmerArg.String(instance, fqbn),
68+
})
69+
if err != nil {
70+
feedback.FatalError(err, feedback.ErrGeneric)
71+
}
72+
feedback.PrintResult(&debugCheckResult{&isDebugSupportedResponse{
73+
DebuggingSupported: resp.GetDebuggingSupported(),
74+
DebugFQBN: resp.GetDebugFqbn(),
75+
}})
76+
}
77+
78+
type isDebugSupportedResponse struct {
79+
DebuggingSupported bool `json:"debugging_supported"`
80+
DebugFQBN string `json:"debug_fqbn,omitempty"`
81+
}
82+
83+
type debugCheckResult struct {
84+
Result *isDebugSupportedResponse
85+
}
86+
87+
func (d *debugCheckResult) Data() interface{} {
88+
return d.Result
89+
}
90+
91+
func (d *debugCheckResult) String() string {
92+
if d.Result.DebuggingSupported {
93+
return tr("The given board/programmer configuration supports debugging.")
94+
}
95+
return tr("The given board/programmer configuration does NOT support debugging.")
96+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy