From 647d62909ff9967d8f90d4c8a6df574148bd1139 Mon Sep 17 00:00:00 2001 From: Luca Bianconi Date: Tue, 14 Feb 2023 14:29:20 +0100 Subject: [PATCH 1/4] feat: expand properties option --- internal/cli/compile/compile.go | 63 ++++++++++++++--- .../compile_3/compile_show_properties_test.go | 69 +++++++++++++++++-- 2 files changed, 120 insertions(+), 12 deletions(-) diff --git a/internal/cli/compile/compile.go b/internal/cli/compile/compile.go index cb3c460a46b..fc5db0defe0 100644 --- a/internal/cli/compile/compile.go +++ b/internal/cli/compile/compile.go @@ -38,15 +38,40 @@ import ( "github.com/arduino/arduino-cli/table" "github.com/arduino/arduino-cli/version" "github.com/arduino/go-paths-helper" + "github.com/arduino/go-properties-orderedmap" "github.com/fatih/color" "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) +type showPropertiesMode int + +const ( + showPropertiesModeDisabled showPropertiesMode = iota + showPropertiesModePattern + showPropertiesModeValue +) + +func getShowPropertiesMode(showProperties string) showPropertiesMode { + if showProperties == "" { + // default with no flag + return showPropertiesModeDisabled + } + val, ok := map[string]showPropertiesMode{ + "disabled": showPropertiesModeDisabled, + "pattern": showPropertiesModePattern, + "value": showPropertiesModeValue, + }[showProperties] + if !ok { + return showPropertiesModePattern + } + return val +} + var ( fqbnArg arguments.Fqbn // Fully Qualified Board Name, e.g.: arduino:avr:uno. profileArg arguments.Profile // Profile to use - showProperties bool // Show all build preferences used instead of compiling. + showProperties string // Show all build preferences used instead of compiling. preprocess bool // Print preprocessed code to stdout. buildCachePath string // Builds of 'core.a' are saved into this path to be cached and reused. buildPath string // Path where to save compiled files. @@ -95,7 +120,13 @@ func NewCommand() *cobra.Command { fqbnArg.AddToCommand(compileCommand) profileArg.AddToCommand(compileCommand) compileCommand.Flags().BoolVar(&dumpProfile, "dump-profile", false, tr("Create and print a profile configuration from the build.")) - compileCommand.Flags().BoolVar(&showProperties, "show-properties", false, tr("Show all build properties used instead of compiling.")) + compileCommand.Flags().StringVar( + &showProperties, + "show-properties", + "disabled", + tr(`Show build properties instead of compiling. With "pattern" properties are returned as they are defined. If "value" the placeholders are replaced with compilation context values.`), + ) + compileCommand.Flags().Lookup("show-properties").NoOptDefVal = "pattern" // default if the flag is present with no value compileCommand.Flags().BoolVar(&preprocess, "preprocess", false, tr("Print preprocessed code to stdout instead of compiling.")) compileCommand.Flags().StringVar(&buildCachePath, "build-cache-path", "", tr("Builds of 'core.a' are saved into this path to be cached and reused.")) compileCommand.Flags().StringVarP(&exportDir, "output-dir", "", "", tr("Save build artifacts in this directory.")) @@ -188,9 +219,10 @@ func runCompileCommand(cmd *cobra.Command, args []string) { overrides = o.Overrides } + showPropertiesM := getShowPropertiesMode(showProperties) var stdOut, stdErr io.Writer var stdIORes func() *feedback.OutputStreamsResult - if showProperties { + if showPropertiesM != showPropertiesModeDisabled { stdOut, stdErr, stdIORes = feedback.NewBufferedStreams() } else { stdOut, stdErr, stdIORes = feedback.OutputStreams() @@ -200,7 +232,7 @@ func runCompileCommand(cmd *cobra.Command, args []string) { Instance: inst, Fqbn: fqbn, SketchPath: sketchPath.String(), - ShowProperties: showProperties, + ShowProperties: showPropertiesM != showPropertiesModeDisabled, Preprocess: preprocess, BuildCachePath: buildCachePath, BuildPath: buildPath, @@ -318,7 +350,7 @@ func runCompileCommand(cmd *cobra.Command, args []string) { BuilderResult: compileRes, ProfileOut: profileOut, Success: compileError == nil, - showOnlyProperties: showProperties, + showPropertiesMode: showPropertiesM, } if compileError != nil { @@ -353,9 +385,24 @@ func runCompileCommand(cmd *cobra.Command, args []string) { } feedback.FatalResult(res, feedback.ErrGeneric) } + if showPropertiesM == showPropertiesModeValue { + expandPropertiesInResult(res) + } feedback.PrintResult(res) } +func expandPropertiesInResult(res *compileResult) { + expanded, err := properties.LoadFromSlice(res.BuilderResult.GetBuildProperties()) + if err != nil { + res.Error = tr(err.Error()) + } + expandedSlice := make([]string, expanded.Size()) + for i, k := range expanded.Keys() { + expandedSlice[i] = strings.Join([]string{k, expanded.ExpandPropsInString(expanded.Get(k))}, "=") + } + res.BuilderResult.BuildProperties = expandedSlice +} + type compileResult struct { CompilerOut string `json:"compiler_out"` CompilerErr string `json:"compiler_err"` @@ -364,7 +411,7 @@ type compileResult struct { ProfileOut string `json:"profile_out,omitempty"` Error string `json:"error,omitempty"` - showOnlyProperties bool + showPropertiesMode showPropertiesMode } func (r *compileResult) Data() interface{} { @@ -372,8 +419,8 @@ func (r *compileResult) Data() interface{} { } func (r *compileResult) String() string { - if r.showOnlyProperties { - return strings.Join(r.BuilderResult.BuildProperties, fmt.Sprintln()) + if r.showPropertiesMode != showPropertiesModeDisabled { + return strings.Join(r.BuilderResult.GetBuildProperties(), fmt.Sprintln()) } titleColor := color.New(color.FgHiGreen) diff --git a/internal/integrationtest/compile_3/compile_show_properties_test.go b/internal/integrationtest/compile_3/compile_show_properties_test.go index 2aff1164439..f3e962b5fef 100644 --- a/internal/integrationtest/compile_3/compile_show_properties_test.go +++ b/internal/integrationtest/compile_3/compile_show_properties_test.go @@ -16,14 +16,19 @@ package compile_test import ( + "encoding/json" "testing" "github.com/arduino/arduino-cli/internal/integrationtest" + "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" "github.com/arduino/go-properties-orderedmap" "github.com/stretchr/testify/require" - "go.bug.st/testifyjson/requirejson" ) +type cliCompileResponse struct { + BuilderResult *commands.CompileResponse `json:"builder_result"` +} + func TestCompileShowProperties(t *testing.T) { env, cli := integrationtest.CreateArduinoCLIWithEnvironment(t) defer env.CleanUp() @@ -36,17 +41,25 @@ func TestCompileShowProperties(t *testing.T) { bareMinimum := cli.CopySketch("bare_minimum") // Test --show-properties output is clean + // properties are not expanded stdout, stderr, err := cli.Run("compile", "--fqbn", "arduino:avr:uno", "-v", "--show-properties", bareMinimum.String()) require.NoError(t, err) - _, err = properties.LoadFromBytes(stdout) + props, err := properties.LoadFromBytes(stdout) require.NoError(t, err, "Output must be a clean property list") require.Empty(t, stderr) + require.True(t, props.ContainsKey("archive_file_path")) + require.Contains(t, props.Get("archive_file_path"), "{build.path}") // Test --show-properties --format JSON output is clean + // properties are not expanded 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) + props, err = properties.LoadFromSlice( + requireCompileResponseJson(t, stdout).BuilderResult.GetBuildProperties()) + require.NoError(t, err) + require.True(t, props.ContainsKey("archive_file_path")) + require.Contains(t, props.Get("archive_file_path"), "{build.path}") // 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()) @@ -58,6 +71,54 @@ func TestCompileShowProperties(t *testing.T) { // 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) + requireCompileResponseJson(t, stdout) + + // Test --show-properties=pattern output is clean + // properties are not expanded + stdout, stderr, err = cli.Run("compile", "--fqbn", "arduino:avr:uno", "-v", "--show-properties=pattern", bareMinimum.String()) + require.NoError(t, err) + props, err = properties.LoadFromBytes(stdout) + require.NoError(t, err, "Output must be a clean property list") + require.Empty(t, stderr) + require.True(t, props.ContainsKey("archive_file_path")) + require.Contains(t, props.Get("archive_file_path"), "{build.path}") + + // Test --show-properties=pattern output is clean + // properties are not expanded + stdout, stderr, err = cli.Run("compile", "--fqbn", "arduino:avr:uno", "-v", "--show-properties=pattern", "--format", "json", bareMinimum.String()) + require.NoError(t, err) + require.Empty(t, stderr) + props, err = properties.LoadFromSlice( + requireCompileResponseJson(t, stdout).BuilderResult.GetBuildProperties()) + require.NoError(t, err) + require.True(t, props.ContainsKey("archive_file_path")) + require.Contains(t, props.Get("archive_file_path"), "{build.path}") + + // Test --show-properties=value output is clean + // properties are expanded + stdout, stderr, err = cli.Run("compile", "--fqbn", "arduino:avr:uno", "-v", "--show-properties=value", bareMinimum.String()) + require.NoError(t, err) + props, err = properties.LoadFromBytes(stdout) + require.NoError(t, err, "Output must be a clean property list") + require.Empty(t, stderr) + require.True(t, props.ContainsKey("archive_file_path")) + require.NotContains(t, props.Get("archive_file_path"), "{build.path}") + + // Test --show-properties=value --format JSON output is clean + // properties are expanded + stdout, stderr, err = cli.Run("compile", "--fqbn", "arduino:avr:uno", "-v", "--show-properties=value", "--format", "json", bareMinimum.String()) + require.NoError(t, err) + require.Empty(t, stderr) + props, err = properties.LoadFromSlice( + requireCompileResponseJson(t, stdout).BuilderResult.GetBuildProperties()) + require.NoError(t, err) + require.True(t, props.ContainsKey("archive_file_path")) + require.NotContains(t, props.Get("archive_file_path"), "{build.path}") +} + +func requireCompileResponseJson(t *testing.T, stdout []byte) *cliCompileResponse { + var compileResponse cliCompileResponse + require.NoError(t, json.Unmarshal(stdout, &compileResponse)) + return &compileResponse } From 4d68aaed95d9cdf55c18493af7ef4cc9773ccf09 Mon Sep 17 00:00:00 2001 From: Luca Bianconi Date: Thu, 16 Feb 2023 20:30:18 +0100 Subject: [PATCH 2/4] refactor: cleanup --- internal/cli/compile/compile.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/internal/cli/compile/compile.go b/internal/cli/compile/compile.go index fc5db0defe0..44ce8511f5f 100644 --- a/internal/cli/compile/compile.go +++ b/internal/cli/compile/compile.go @@ -52,20 +52,16 @@ const ( showPropertiesModeValue ) -func getShowPropertiesMode(showProperties string) showPropertiesMode { - if showProperties == "" { - // default with no flag - return showPropertiesModeDisabled - } +func parseShowPropertiesMode(showProperties string) (showPropertiesMode, error) { val, ok := map[string]showPropertiesMode{ "disabled": showPropertiesModeDisabled, "pattern": showPropertiesModePattern, "value": showPropertiesModeValue, }[showProperties] if !ok { - return showPropertiesModePattern + return showPropertiesModeDisabled, fmt.Errorf(tr("invalid option '%s'.", showProperties)) } - return val + return val, nil } var ( @@ -219,7 +215,11 @@ func runCompileCommand(cmd *cobra.Command, args []string) { overrides = o.Overrides } - showPropertiesM := getShowPropertiesMode(showProperties) + showPropertiesM, err := parseShowPropertiesMode(showProperties) + if err != nil { + feedback.Fatal(tr("Error parsing --show-properties flag: %v", err), feedback.ErrGeneric) + } + var stdOut, stdErr io.Writer var stdIORes func() *feedback.OutputStreamsResult if showPropertiesM != showPropertiesModeDisabled { From 654a5c62439a0895326adff2114adf3c8ffad34e Mon Sep 17 00:00:00 2001 From: Luca Bianconi Date: Fri, 17 Feb 2023 11:40:25 +0100 Subject: [PATCH 3/4] style: renamed options --- internal/cli/compile/compile.go | 16 ++++++++-------- .../compile_3/compile_show_properties_test.go | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/internal/cli/compile/compile.go b/internal/cli/compile/compile.go index 44ce8511f5f..16188196ee0 100644 --- a/internal/cli/compile/compile.go +++ b/internal/cli/compile/compile.go @@ -48,15 +48,15 @@ type showPropertiesMode int const ( showPropertiesModeDisabled showPropertiesMode = iota - showPropertiesModePattern - showPropertiesModeValue + showPropertiesModeUnexpanded + showPropertiesModeExpanded ) func parseShowPropertiesMode(showProperties string) (showPropertiesMode, error) { val, ok := map[string]showPropertiesMode{ - "disabled": showPropertiesModeDisabled, - "pattern": showPropertiesModePattern, - "value": showPropertiesModeValue, + "disabled": showPropertiesModeDisabled, + "unexpanded": showPropertiesModeUnexpanded, + "expanded": showPropertiesModeExpanded, }[showProperties] if !ok { return showPropertiesModeDisabled, fmt.Errorf(tr("invalid option '%s'.", showProperties)) @@ -120,9 +120,9 @@ func NewCommand() *cobra.Command { &showProperties, "show-properties", "disabled", - tr(`Show build properties instead of compiling. With "pattern" properties are returned as they are defined. If "value" the placeholders are replaced with compilation context values.`), + tr(`Show build properties instead of compiling. With "expanded" the placeholders are replaced with compilation context values.`), ) - compileCommand.Flags().Lookup("show-properties").NoOptDefVal = "pattern" // default if the flag is present with no value + compileCommand.Flags().Lookup("show-properties").NoOptDefVal = "unexpanded" // default if the flag is present with no value compileCommand.Flags().BoolVar(&preprocess, "preprocess", false, tr("Print preprocessed code to stdout instead of compiling.")) compileCommand.Flags().StringVar(&buildCachePath, "build-cache-path", "", tr("Builds of 'core.a' are saved into this path to be cached and reused.")) compileCommand.Flags().StringVarP(&exportDir, "output-dir", "", "", tr("Save build artifacts in this directory.")) @@ -385,7 +385,7 @@ func runCompileCommand(cmd *cobra.Command, args []string) { } feedback.FatalResult(res, feedback.ErrGeneric) } - if showPropertiesM == showPropertiesModeValue { + if showPropertiesM == showPropertiesModeExpanded { expandPropertiesInResult(res) } feedback.PrintResult(res) diff --git a/internal/integrationtest/compile_3/compile_show_properties_test.go b/internal/integrationtest/compile_3/compile_show_properties_test.go index f3e962b5fef..780431659d2 100644 --- a/internal/integrationtest/compile_3/compile_show_properties_test.go +++ b/internal/integrationtest/compile_3/compile_show_properties_test.go @@ -74,9 +74,9 @@ func TestCompileShowProperties(t *testing.T) { require.Empty(t, stderr) requireCompileResponseJson(t, stdout) - // Test --show-properties=pattern output is clean + // Test --show-properties=unexpanded output is clean // properties are not expanded - stdout, stderr, err = cli.Run("compile", "--fqbn", "arduino:avr:uno", "-v", "--show-properties=pattern", bareMinimum.String()) + stdout, stderr, err = cli.Run("compile", "--fqbn", "arduino:avr:uno", "-v", "--show-properties=unexpanded", bareMinimum.String()) require.NoError(t, err) props, err = properties.LoadFromBytes(stdout) require.NoError(t, err, "Output must be a clean property list") @@ -84,9 +84,9 @@ func TestCompileShowProperties(t *testing.T) { require.True(t, props.ContainsKey("archive_file_path")) require.Contains(t, props.Get("archive_file_path"), "{build.path}") - // Test --show-properties=pattern output is clean + // Test --show-properties=unexpanded output is clean // properties are not expanded - stdout, stderr, err = cli.Run("compile", "--fqbn", "arduino:avr:uno", "-v", "--show-properties=pattern", "--format", "json", bareMinimum.String()) + stdout, stderr, err = cli.Run("compile", "--fqbn", "arduino:avr:uno", "-v", "--show-properties=unexpanded", "--format", "json", bareMinimum.String()) require.NoError(t, err) require.Empty(t, stderr) props, err = properties.LoadFromSlice( @@ -95,9 +95,9 @@ func TestCompileShowProperties(t *testing.T) { require.True(t, props.ContainsKey("archive_file_path")) require.Contains(t, props.Get("archive_file_path"), "{build.path}") - // Test --show-properties=value output is clean + // Test --show-properties=expanded output is clean // properties are expanded - stdout, stderr, err = cli.Run("compile", "--fqbn", "arduino:avr:uno", "-v", "--show-properties=value", bareMinimum.String()) + stdout, stderr, err = cli.Run("compile", "--fqbn", "arduino:avr:uno", "-v", "--show-properties=expanded", bareMinimum.String()) require.NoError(t, err) props, err = properties.LoadFromBytes(stdout) require.NoError(t, err, "Output must be a clean property list") @@ -105,9 +105,9 @@ func TestCompileShowProperties(t *testing.T) { require.True(t, props.ContainsKey("archive_file_path")) require.NotContains(t, props.Get("archive_file_path"), "{build.path}") - // Test --show-properties=value --format JSON output is clean + // Test --show-properties=expanded --format JSON output is clean // properties are expanded - stdout, stderr, err = cli.Run("compile", "--fqbn", "arduino:avr:uno", "-v", "--show-properties=value", "--format", "json", bareMinimum.String()) + stdout, stderr, err = cli.Run("compile", "--fqbn", "arduino:avr:uno", "-v", "--show-properties=expanded", "--format", "json", bareMinimum.String()) require.NoError(t, err) require.Empty(t, stderr) props, err = properties.LoadFromSlice( From 53a93998f1f50d993ec68a9108a82c166cd47e88 Mon Sep 17 00:00:00 2001 From: Luca Bianconi Date: Fri, 17 Feb 2023 16:48:17 +0100 Subject: [PATCH 4/4] chore: updated usage --- internal/cli/compile/compile.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/cli/compile/compile.go b/internal/cli/compile/compile.go index 16188196ee0..09806965f4d 100644 --- a/internal/cli/compile/compile.go +++ b/internal/cli/compile/compile.go @@ -120,7 +120,7 @@ func NewCommand() *cobra.Command { &showProperties, "show-properties", "disabled", - tr(`Show build properties instead of compiling. With "expanded" the placeholders are replaced with compilation context values.`), + tr(`Show build properties instead of compiling. The properties are returned exactly as they are defined. Use "--show-properties=expanded" to replace placeholders with compilation context values.`),, ) compileCommand.Flags().Lookup("show-properties").NoOptDefVal = "unexpanded" // default if the flag is present with no value compileCommand.Flags().BoolVar(&preprocess, "preprocess", false, tr("Print preprocessed code to stdout instead of compiling."))