Skip to content

Added debug check command to check if a combination of board/programmer supports debugging. #2443

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Merged
merged 8 commits into from
Dec 1, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions commands/daemon/debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,9 @@ func (s *ArduinoCoreServerImpl) GetDebugConfig(ctx context.Context, req *rpc.Get
res, err := cmd.GetDebugConfig(ctx, req)
return res, convertErrorToRPCStatus(err)
}

// IsDebugSupported checks if debugging is supported for a given configuration
func (s *ArduinoCoreServerImpl) IsDebugSupported(ctx context.Context, req *rpc.IsDebugSupportedRequest) (*rpc.IsDebugSupportedResponse, error) {
res, err := cmd.IsDebugSupported(ctx, req)
return res, convertErrorToRPCStatus(err)
}
2 changes: 1 addition & 1 deletion commands/debug/debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ func Debug(ctx context.Context, req *rpc.GetDebugConfigRequest, inStream io.Read

// getCommandLine compose a debug command represented by a core recipe
func getCommandLine(req *rpc.GetDebugConfigRequest, pme *packagemanager.Explorer) ([]string, error) {
debugInfo, err := getDebugProperties(req, pme)
debugInfo, err := getDebugProperties(req, pme, false)
if err != nil {
return nil, err
}
Expand Down
80 changes: 62 additions & 18 deletions commands/debug/debug_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package debug
import (
"context"
"encoding/json"
"errors"
"slices"
"strconv"
"strings"
Expand All @@ -41,25 +42,66 @@ func GetDebugConfig(ctx context.Context, req *rpc.GetDebugConfigRequest) (*rpc.G
return nil, &arduino.InvalidInstanceError{}
}
defer release()
return getDebugProperties(req, pme)
return getDebugProperties(req, pme, false)
}

func getDebugProperties(req *rpc.GetDebugConfigRequest, pme *packagemanager.Explorer) (*rpc.GetDebugConfigResponse, error) {
// TODO: make a generic function to extract sketch from request
// and remove duplication in commands/compile.go
if req.GetSketchPath() == "" {
return nil, &arduino.MissingSketchPathError{}
// IsDebugSupported checks if the given board/programmer configuration supports debugging.
func IsDebugSupported(ctx context.Context, req *rpc.IsDebugSupportedRequest) (*rpc.IsDebugSupportedResponse, error) {
pme, release := instances.GetPackageManagerExplorer(req.GetInstance())
if pme == nil {
return nil, &arduino.InvalidInstanceError{}
}
defer release()
_, err := getDebugProperties(&rpc.GetDebugConfigRequest{
Instance: req.GetInstance(),
Fqbn: req.GetFqbn(),
SketchPath: "",
Port: req.GetPort(),
Interpreter: req.GetInterpreter(),
ImportDir: "",
Programmer: req.GetProgrammer(),
}, pme, true)
var x *arduino.FailedDebugError
if errors.As(err, &x) {
return &rpc.IsDebugSupportedResponse{DebuggingSupported: false}, nil
}
sketchPath := paths.New(req.GetSketchPath())
sk, err := sketch.New(sketchPath)
if err != nil {
return nil, &arduino.CantOpenSketchError{Cause: err}
return nil, err
}
return &rpc.IsDebugSupportedResponse{DebuggingSupported: true}, nil
}

func getDebugProperties(req *rpc.GetDebugConfigRequest, pme *packagemanager.Explorer, skipSketchChecks bool) (*rpc.GetDebugConfigResponse, error) {
var (
sketchName string
sketchDefaultFQBN string
sketchDefaultBuildPath *paths.Path
)
if !skipSketchChecks {
// TODO: make a generic function to extract sketch from request
// and remove duplication in commands/compile.go
if req.GetSketchPath() == "" {
return nil, &arduino.MissingSketchPathError{}
}
sketchPath := paths.New(req.GetSketchPath())
sk, err := sketch.New(sketchPath)
if err != nil {
return nil, &arduino.CantOpenSketchError{Cause: err}
}
sketchName = sk.Name
sketchDefaultFQBN = sk.GetDefaultFQBN()
sketchDefaultBuildPath = sk.DefaultBuildPath()
} else {
// Use placeholder sketch data
sketchName = "Sketch"
sketchDefaultFQBN = ""
sketchDefaultBuildPath = paths.New("SketchBuildPath")
}

// XXX Remove this code duplication!!
fqbnIn := req.GetFqbn()
if fqbnIn == "" && sk != nil {
fqbnIn = sk.GetDefaultFQBN()
if fqbnIn == "" {
fqbnIn = sketchDefaultFQBN
}
if fqbnIn == "" {
return nil, &arduino.MissingFQBNError{}
Expand Down Expand Up @@ -109,16 +151,18 @@ func getDebugProperties(req *rpc.GetDebugConfigRequest, pme *packagemanager.Expl
if importDir := req.GetImportDir(); importDir != "" {
importPath = paths.New(importDir)
} else {
importPath = sk.DefaultBuildPath()
importPath = sketchDefaultBuildPath
}
if !importPath.Exist() {
return nil, &arduino.NotFoundError{Message: tr("Compiled sketch not found in %s", importPath)}
}
if !importPath.IsDir() {
return nil, &arduino.NotFoundError{Message: tr("Expected compiled sketch in directory %s, but is a file instead", importPath)}
if !skipSketchChecks {
if !importPath.Exist() {
return nil, &arduino.NotFoundError{Message: tr("Compiled sketch not found in %s", importPath)}
}
if !importPath.IsDir() {
return nil, &arduino.NotFoundError{Message: tr("Expected compiled sketch in directory %s, but is a file instead", importPath)}
}
}
toolProperties.SetPath("build.path", importPath)
toolProperties.Set("build.project_name", sk.Name+".ino")
toolProperties.Set("build.project_name", sketchName+".ino")

// Set debug port property
port := req.GetPort()
Expand Down
29 changes: 17 additions & 12 deletions internal/cli/debug/debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,27 +36,31 @@ import (
"github.com/spf13/cobra"
)

var (
fqbnArg arguments.Fqbn
portArgs arguments.Port
interpreter string
importDir string
printInfo bool
programmer arguments.Programmer
tr = i18n.Tr
)
var tr = i18n.Tr

// NewCommand created a new `upload` command
func NewCommand() *cobra.Command {
var (
fqbnArg arguments.Fqbn
portArgs arguments.Port
interpreter string
importDir string
printInfo bool
programmer arguments.Programmer
)

debugCommand := &cobra.Command{
Use: "debug",
Short: tr("Debug Arduino sketches."),
Long: tr("Debug Arduino sketches. (this command opens an interactive gdb session)"),
Example: " " + os.Args[0] + " debug -b arduino:samd:mkr1000 -P atmel_ice /home/user/Arduino/MySketch",
Args: cobra.MaximumNArgs(1),
Run: runDebugCommand,
Run: func(cmd *cobra.Command, args []string) {
runDebugCommand(args, &portArgs, &fqbnArg, interpreter, importDir, &programmer, printInfo)
},
}

debugCommand.AddCommand(newDebugCheckCommand())
fqbnArg.AddToCommand(debugCommand)
portArgs.AddToCommand(debugCommand)
programmer.AddToCommand(debugCommand)
Expand All @@ -67,7 +71,8 @@ func NewCommand() *cobra.Command {
return debugCommand
}

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

Expand All @@ -81,7 +86,7 @@ func runDebugCommand(command *cobra.Command, args []string) {
if err != nil {
feedback.FatalError(err, feedback.ErrGeneric)
}
fqbn, port := arguments.CalculateFQBNAndPort(&portArgs, &fqbnArg, instance, sk.GetDefaultFqbn(), sk.GetDefaultPort(), sk.GetDefaultProtocol())
fqbn, port := arguments.CalculateFQBNAndPort(portArgs, fqbnArg, instance, sk.GetDefaultFqbn(), sk.GetDefaultPort(), sk.GetDefaultProtocol())
debugConfigRequested := &rpc.GetDebugConfigRequest{
Instance: instance,
Fqbn: fqbn,
Expand Down
89 changes: 89 additions & 0 deletions internal/cli/debug/debug_check.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// This file is part of arduino-cli.
//
// Copyright 2023 ARDUINO SA (http://www.arduino.cc/)
//
// This software is released under the GNU General Public License version 3,
// which covers the main part of arduino-cli.
// The terms of this license can be found at:
// https://www.gnu.org/licenses/gpl-3.0.en.html
//
// You can be released from the requirements of the above licenses by purchasing
// a commercial license. Buying such a license is mandatory if you want to
// modify or otherwise use the software for commercial activities involving the
// Arduino software without disclosing the source code of your own applications.
// To purchase a commercial license, send an email to license@arduino.cc.

package debug

import (
"context"
"os"

"github.com/arduino/arduino-cli/commands/debug"
"github.com/arduino/arduino-cli/internal/cli/arguments"
"github.com/arduino/arduino-cli/internal/cli/feedback"
"github.com/arduino/arduino-cli/internal/cli/feedback/result"
"github.com/arduino/arduino-cli/internal/cli/instance"
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)

func newDebugCheckCommand() *cobra.Command {
var (
fqbnArg arguments.Fqbn
portArgs arguments.Port
interpreter string
programmer arguments.Programmer
)
debugCheckCommand := &cobra.Command{
Use: "check",
Short: tr("Check if the given board/programmer combination supports debugging."),
Example: " " + os.Args[0] + " debug check -b arduino:samd:mkr1000 -P atmel_ice",
Run: func(cmd *cobra.Command, args []string) {
runDebugCheckCommand(&portArgs, &fqbnArg, interpreter, &programmer)
},
}
fqbnArg.AddToCommand(debugCheckCommand)
portArgs.AddToCommand(debugCheckCommand)
programmer.AddToCommand(debugCheckCommand)
debugCheckCommand.Flags().StringVar(&interpreter, "interpreter", "console", tr("Debug interpreter e.g.: %s", "console, mi, mi1, mi2, mi3"))
return debugCheckCommand
}

func runDebugCheckCommand(portArgs *arguments.Port, fqbnArg *arguments.Fqbn, interpreter string, programmerArg *arguments.Programmer) {
instance := instance.CreateAndInit()
logrus.Info("Executing `arduino-cli debug`")

port, err := portArgs.GetPort(instance, "", "")
if err != nil {
feedback.FatalError(err, feedback.ErrBadArgument)
}
fqbn := fqbnArg.String()
resp, err := debug.IsDebugSupported(context.Background(), &rpc.IsDebugSupportedRequest{
Instance: instance,
Fqbn: fqbn,
Port: port,
Interpreter: interpreter,
Programmer: programmerArg.String(instance, fqbn),
})
if err != nil {
feedback.FatalError(err, feedback.ErrGeneric)
}
feedback.PrintResult(&debugCheckResult{result.NewIsDebugSupportedResponse(resp)})
}

type debugCheckResult struct {
Result *result.IsDebugSupportedResponse
}

func (d *debugCheckResult) Data() interface{} {
return d.Result
}

func (d *debugCheckResult) String() string {
if d.Result.DebuggingSupported {
return tr("The given board/programmer configuration supports debugging.")
}
return tr("The given board/programmer configuration does NOT support debugging.")
}
10 changes: 10 additions & 0 deletions internal/cli/feedback/result/rpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -1057,3 +1057,13 @@ func NewCompileDiagnosticNote(cdn *rpc.CompileDiagnosticNote) *CompileDiagnostic
Column: cdn.GetColumn(),
}
}

type IsDebugSupportedResponse struct {
DebuggingSupported bool `json:"debugging_supported"`
}

func NewIsDebugSupportedResponse(resp *rpc.IsDebugSupportedResponse) *IsDebugSupportedResponse {
return &IsDebugSupportedResponse{
DebuggingSupported: resp.GetDebuggingSupported(),
}
}
4 changes: 4 additions & 0 deletions internal/cli/feedback/result/rpc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,10 @@ func TestAllFieldAreMapped(t *testing.T) {
compileDiagnosticNoteRpc := &rpc.CompileDiagnosticNote{}
compileDiagnosticNoteResult := result.NewCompileDiagnosticNote(compileDiagnosticNoteRpc)
mustContainsAllPropertyOfRpcStruct(t, compileDiagnosticNoteRpc, compileDiagnosticNoteResult)

isDebugSupportedResponseRpc := &rpc.IsDebugSupportedResponse{}
isDebugSupportedResponseResult := result.NewIsDebugSupportedResponse(isDebugSupportedResponseRpc)
mustContainsAllPropertyOfRpcStruct(t, isDebugSupportedResponseRpc, isDebugSupportedResponseResult)
}

func TestEnumsMapsEveryRpcCounterpart(t *testing.T) {
Expand Down
24 changes: 24 additions & 0 deletions internal/integrationtest/debug/debug_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,16 @@ func TestDebug(t *testing.T) {
require.NoError(t, err)

// Install cores
_, _, err = cli.Run("core", "install", "arduino:avr")
require.NoError(t, err)
_, _, err = cli.Run("core", "install", "arduino:samd")
require.NoError(t, err)

integrationtest.CLISubtests{
{"Start", testDebuggerStarts},
{"WithPdeSketchStarts", testDebuggerWithPdeSketchStarts},
{"DebugInformation", testAllDebugInformation},
{"DebugCheck", testDebugCheck},
}.Run(t, env, cli)
}

Expand Down Expand Up @@ -331,3 +334,24 @@ func testAllDebugInformation(t *testing.T, env *integrationtest.Environment, cli
}
}
}

func testDebugCheck(t *testing.T, env *integrationtest.Environment, cli *integrationtest.ArduinoCLI) {
_, _, err := cli.Run("debug", "check", "-b", "arduino:samd:mkr1000")
require.Error(t, err)

out, _, err := cli.Run("debug", "check", "-b", "arduino:samd:mkr1000", "-P", "atmel_ice")
require.NoError(t, err)
require.Contains(t, string(out), "The given board/programmer configuration supports debugging.")

out, _, err = cli.Run("debug", "check", "-b", "arduino:samd:mkr1000", "-P", "atmel_ice", "--format", "json")
require.NoError(t, err)
requirejson.Query(t, out, `.debugging_supported`, `true`)

out, _, err = cli.Run("debug", "check", "-b", "arduino:avr:uno", "-P", "atmel_ice")
require.NoError(t, err)
require.Contains(t, string(out), "The given board/programmer configuration does NOT support debugging.")

out, _, err = cli.Run("debug", "check", "-b", "arduino:avr:uno", "-P", "atmel_ice", "--format", "json")
require.NoError(t, err)
requirejson.Query(t, out, `.debugging_supported`, `false`)
}
Loading