diff --git a/commands/compile/compile.go b/commands/compile/compile.go index c91f1017614..2a881f23829 100644 --- a/commands/compile/compile.go +++ b/commands/compile/compile.go @@ -19,6 +19,7 @@ import ( "context" "fmt" "io" + "sort" "strings" "github.com/arduino/arduino-cli/arduino" @@ -185,17 +186,32 @@ func Compile(ctx context.Context, req *rpc.CompileRequest, outStream, errStream } }() - // if --preprocess or --show-properties were passed, we can stop here + defer func() { + buildProperties := builderCtx.BuildProperties + if buildProperties == nil { + return + } + keys := buildProperties.Keys() + sort.Strings(keys) + for _, key := range keys { + r.BuildProperties = append(r.BuildProperties, key+"="+buildProperties.Get(key)) + } + }() + if req.GetShowProperties() { - compileErr := builder.RunParseHardwareAndDumpBuildProperties(builderCtx) + // Just get build properties and exit + compileErr := builder.RunParseHardware(builderCtx) if compileErr != nil { - compileErr = &arduino.CompileFailedError{Message: err.Error()} + compileErr = &arduino.CompileFailedError{Message: compileErr.Error()} } return r, compileErr - } else if req.GetPreprocess() { + } + + if req.GetPreprocess() { + // Just output preprocessed source code and exit compileErr := builder.RunPreprocess(builderCtx) if compileErr != nil { - compileErr = &arduino.CompileFailedError{Message: err.Error()} + compileErr = &arduino.CompileFailedError{Message: compileErr.Error()} } return r, compileErr } diff --git a/internal/cli/compile/compile.go b/internal/cli/compile/compile.go index c790d493222..cb3c460a46b 100644 --- a/internal/cli/compile/compile.go +++ b/internal/cli/compile/compile.go @@ -20,6 +20,7 @@ import ( "encoding/json" "errors" "fmt" + "io" "os" "strings" @@ -187,6 +188,14 @@ func runCompileCommand(cmd *cobra.Command, args []string) { overrides = o.Overrides } + var stdOut, stdErr io.Writer + var stdIORes func() *feedback.OutputStreamsResult + if showProperties { + stdOut, stdErr, stdIORes = feedback.NewBufferedStreams() + } else { + stdOut, stdErr, stdIORes = feedback.OutputStreams() + } + compileRequest := &rpc.CompileRequest{ Instance: inst, Fqbn: fqbn, @@ -212,9 +221,7 @@ func runCompileCommand(cmd *cobra.Command, args []string) { EncryptKey: encryptKey, SkipLibrariesDiscovery: skipLibrariesDiscovery, } - stdOut, stdErr, stdIORes := feedback.OutputStreams() compileRes, compileError := compile.Compile(context.Background(), compileRequest, stdOut, stdErr, nil) - if compileError == nil && uploadAfterCompile { userFieldRes, err := upload.SupportedUserFields(context.Background(), &rpc.SupportedUserFieldsRequest{ Instance: inst, @@ -306,11 +313,12 @@ func runCompileCommand(cmd *cobra.Command, args []string) { stdIO := stdIORes() res := &compileResult{ - CompilerOut: stdIO.Stdout, - CompilerErr: stdIO.Stderr, - BuilderResult: compileRes, - ProfileOut: profileOut, - Success: compileError == nil, + CompilerOut: stdIO.Stdout, + CompilerErr: stdIO.Stderr, + BuilderResult: compileRes, + ProfileOut: profileOut, + Success: compileError == nil, + showOnlyProperties: showProperties, } if compileError != nil { @@ -355,6 +363,8 @@ type compileResult struct { Success bool `json:"success"` ProfileOut string `json:"profile_out,omitempty"` Error string `json:"error,omitempty"` + + showOnlyProperties bool } func (r *compileResult) Data() interface{} { @@ -362,6 +372,10 @@ func (r *compileResult) Data() interface{} { } func (r *compileResult) String() string { + if r.showOnlyProperties { + return strings.Join(r.BuilderResult.BuildProperties, fmt.Sprintln()) + } + titleColor := color.New(color.FgHiGreen) nameColor := color.New(color.FgHiYellow) pathColor := color.New(color.FgHiBlack) diff --git a/internal/cli/feedback/stdio.go b/internal/cli/feedback/stdio.go index 787435462e1..87f37f0c227 100644 --- a/internal/cli/feedback/stdio.go +++ b/internal/cli/feedback/stdio.go @@ -16,6 +16,7 @@ package feedback import ( + "bytes" "errors" "io" ) @@ -56,6 +57,24 @@ func OutputStreams() (io.Writer, io.Writer, func() *OutputStreamsResult) { return feedbackOut, feedbackErr, getOutputStreamResult } +// NewBufferedStreams returns a pair of io.Writer to buffer the command output. +// The returned writers will accumulate the output until the command +// execution is completed. The io.Writes will not affect other feedback streams. +// +// This function returns also a callback that must be called when the +// command execution is completed, it will return an *OutputStreamsResult +// object that can be used as a Result or to retrieve the accumulated output +// to embed it in another object. +func NewBufferedStreams() (io.Writer, io.Writer, func() *OutputStreamsResult) { + out, err := &bytes.Buffer{}, &bytes.Buffer{} + return out, err, func() *OutputStreamsResult { + return &OutputStreamsResult{ + Stdout: out.String(), + Stderr: err.String(), + } + } +} + func getOutputStreamResult() *OutputStreamsResult { return &OutputStreamsResult{ Stdout: bufferOut.String(), diff --git a/internal/integrationtest/compile_3/compile_show_properties_test.go b/internal/integrationtest/compile_3/compile_show_properties_test.go new file mode 100644 index 00000000000..2aff1164439 --- /dev/null +++ b/internal/integrationtest/compile_3/compile_show_properties_test.go @@ -0,0 +1,63 @@ +// This file is part of arduino-cli. +// +// Copyright 2022 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 compile_test + +import ( + "testing" + + "github.com/arduino/arduino-cli/internal/integrationtest" + "github.com/arduino/go-properties-orderedmap" + "github.com/stretchr/testify/require" + "go.bug.st/testifyjson/requirejson" +) + +func TestCompileShowProperties(t *testing.T) { + env, cli := integrationtest.CreateArduinoCLIWithEnvironment(t) + defer env.CleanUp() + + _, _, err := cli.Run("core", "update-index") + require.NoError(t, err) + _, _, err = cli.Run("core", "install", "arduino:avr") + require.NoError(t, err) + + bareMinimum := cli.CopySketch("bare_minimum") + + // Test --show-properties output is clean + stdout, stderr, err := cli.Run("compile", "--fqbn", "arduino:avr:uno", "-v", "--show-properties", bareMinimum.String()) + require.NoError(t, err) + _, err = properties.LoadFromBytes(stdout) + require.NoError(t, err, "Output must be a clean property list") + require.Empty(t, stderr) + + // Test --show-properties --format JSON output is clean + stdout, stderr, err = cli.Run("compile", "--fqbn", "arduino:avr:uno", "-v", "--show-properties", "--format", "json", bareMinimum.String()) + require.NoError(t, err) + requirejson.Parse(t, stdout, "Output must be a valid JSON") + require.Empty(t, stderr) + + // Test --show-properties output is clean, with a wrong FQBN + stdout, stderr, err = cli.Run("compile", "--fqbn", "arduino:avr:unoa", "-v", "--show-properties", bareMinimum.String()) + require.Error(t, err) + _, err = properties.LoadFromBytes(stdout) + require.NoError(t, err, "Output must be a clean property list") + require.NotEmpty(t, stderr) + + // Test --show-properties --format JSON output is clean, with a wrong FQBN + stdout, stderr, err = cli.Run("compile", "--fqbn", "arduino:avr:unoa", "-v", "--show-properties", "--format", "json", bareMinimum.String()) + require.Error(t, err) + requirejson.Parse(t, stdout, "Output must be a valid JSON") + require.Empty(t, stderr) +} diff --git a/legacy/builder/builder.go b/legacy/builder/builder.go index 7bf8b81926c..00af87a6931 100644 --- a/legacy/builder/builder.go +++ b/legacy/builder/builder.go @@ -162,22 +162,6 @@ func (s *Preprocess) Run(ctx *types.Context) error { return nil } -type ParseHardwareAndDumpBuildProperties struct{} - -func (s *ParseHardwareAndDumpBuildProperties) Run(ctx *types.Context) error { - if ctx.BuildPath == nil { - ctx.BuildPath = sketch.GenBuildPath(ctx.SketchLocation) - } - - commands := []types.Command{ - &ContainerSetupHardwareToolsLibsSketchAndProps{}, - - &DumpBuildProperties{}, - } - - return runCommands(ctx, commands) -} - func runCommands(ctx *types.Context, commands []types.Command) error { ctx.Progress.AddSubSteps(len(commands)) defer ctx.Progress.RemoveSubSteps() @@ -203,9 +187,14 @@ func RunBuilder(ctx *types.Context) error { return command.Run(ctx) } -func RunParseHardwareAndDumpBuildProperties(ctx *types.Context) error { - command := ParseHardwareAndDumpBuildProperties{} - return command.Run(ctx) +func RunParseHardware(ctx *types.Context) error { + if ctx.BuildPath == nil { + ctx.BuildPath = sketch.GenBuildPath(ctx.SketchLocation) + } + commands := []types.Command{ + &ContainerSetupHardwareToolsLibsSketchAndProps{}, + } + return runCommands(ctx, commands) } func RunPreprocess(ctx *types.Context) error { diff --git a/legacy/builder/dump_build_properties.go b/legacy/builder/dump_build_properties.go deleted file mode 100644 index 998430e65d3..00000000000 --- a/legacy/builder/dump_build_properties.go +++ /dev/null @@ -1,38 +0,0 @@ -// This file is part of arduino-cli. -// -// Copyright 2020 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 builder - -import ( - "fmt" - "sort" - - "github.com/arduino/arduino-cli/legacy/builder/types" -) - -type DumpBuildProperties struct{} - -func (s *DumpBuildProperties) Run(ctx *types.Context) error { - buildProperties := ctx.BuildProperties - - keys := buildProperties.Keys() - sort.Strings(keys) - - for _, key := range keys { - fmt.Println(key + "=" + buildProperties.Get(key)) - } - - return nil -} diff --git a/rpc/cc/arduino/cli/commands/v1/compile.pb.go b/rpc/cc/arduino/cli/commands/v1/compile.pb.go index 7d562a0e2e2..83455c64ecb 100644 --- a/rpc/cc/arduino/cli/commands/v1/compile.pb.go +++ b/rpc/cc/arduino/cli/commands/v1/compile.pb.go @@ -49,7 +49,7 @@ type CompileRequest struct { Fqbn string `protobuf:"bytes,2,opt,name=fqbn,proto3" json:"fqbn,omitempty"` // The path where the sketch is stored. SketchPath string `protobuf:"bytes,3,opt,name=sketch_path,json=sketchPath,proto3" json:"sketch_path,omitempty"` - // Show all build preferences used instead of compiling. + // Just get the build properties and do not run the full compile. ShowProperties bool `protobuf:"varint,4,opt,name=show_properties,json=showProperties,proto3" json:"show_properties,omitempty"` // Print preprocessed code to stdout instead of compiling. Preprocess bool `protobuf:"varint,5,opt,name=preprocess,proto3" json:"preprocess,omitempty"` @@ -340,6 +340,8 @@ type CompileResponse struct { BuildPlatform *InstalledPlatformReference `protobuf:"bytes,7,opt,name=build_platform,json=buildPlatform,proto3" json:"build_platform,omitempty"` // Completions reports of the compilation process (stream) Progress *TaskProgress `protobuf:"bytes,8,opt,name=progress,proto3" json:"progress,omitempty"` + // Build properties used for compiling + BuildProperties []string `protobuf:"bytes,9,rep,name=build_properties,json=buildProperties,proto3" json:"build_properties,omitempty"` } func (x *CompileResponse) Reset() { @@ -430,6 +432,13 @@ func (x *CompileResponse) GetProgress() *TaskProgress { return nil } +func (x *CompileResponse) GetBuildProperties() []string { + if x != nil { + return x.BuildProperties + } + return nil +} + type ExecutableSectionSize struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -574,7 +583,7 @@ var file_cc_arduino_cli_commands_v1_compile_proto_rawDesc = []byte{ 0x72, 0x69, 0x64, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xab, 0x04, 0x0a, 0x0f, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xd6, 0x04, 0x0a, 0x0f, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x6f, 0x75, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x72, 0x72, 0x5f, @@ -609,18 +618,20 @@ var file_cc_arduino_cli_commands_v1_compile_proto_rawDesc = []byte{ 0x28, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x67, 0x72, - 0x65, 0x73, 0x73, 0x22, 0x5a, 0x0a, 0x15, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x61, 0x62, 0x6c, - 0x65, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x12, 0x0a, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, - 0x73, 0x69, 0x7a, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x69, 0x7a, 0x65, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x6d, 0x61, 0x78, 0x53, 0x69, 0x7a, 0x65, 0x42, - 0x48, 0x5a, 0x46, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x72, - 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2f, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2d, 0x63, 0x6c, - 0x69, 0x2f, 0x72, 0x70, 0x63, 0x2f, 0x63, 0x63, 0x2f, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, - 0x2f, 0x63, 0x6c, 0x69, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2f, 0x76, 0x31, - 0x3b, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, + 0x65, 0x73, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x70, 0x72, 0x6f, + 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x62, + 0x75, 0x69, 0x6c, 0x64, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x22, 0x5a, + 0x0a, 0x15, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x65, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x73, + 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, + 0x19, 0x0a, 0x08, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x07, 0x6d, 0x61, 0x78, 0x53, 0x69, 0x7a, 0x65, 0x42, 0x48, 0x5a, 0x46, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, + 0x2f, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2d, 0x63, 0x6c, 0x69, 0x2f, 0x72, 0x70, 0x63, + 0x2f, 0x63, 0x63, 0x2f, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2f, 0x63, 0x6c, 0x69, 0x2f, + 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2f, 0x76, 0x31, 0x3b, 0x63, 0x6f, 0x6d, 0x6d, + 0x61, 0x6e, 0x64, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/rpc/cc/arduino/cli/commands/v1/compile.proto b/rpc/cc/arduino/cli/commands/v1/compile.proto index 1870fd10a71..5a695a45cac 100644 --- a/rpc/cc/arduino/cli/commands/v1/compile.proto +++ b/rpc/cc/arduino/cli/commands/v1/compile.proto @@ -32,7 +32,7 @@ message CompileRequest { string fqbn = 2; // The path where the sketch is stored. string sketch_path = 3; - // Show all build preferences used instead of compiling. + // Just get the build properties and do not run the full compile. bool show_properties = 4; // Print preprocessed code to stdout instead of compiling. bool preprocess = 5; @@ -112,6 +112,8 @@ message CompileResponse { InstalledPlatformReference build_platform = 7; // Completions reports of the compilation process (stream) TaskProgress progress = 8; + // Build properties used for compiling + repeated string build_properties = 9; } message ExecutableSectionSize {