Unverified Commit a1e69dc9 authored by Luca Bianconi's avatar Luca Bianconi Committed by GitHub

feat: option to expand properties with flag --show-properties (#2074)

parent 425aaf05
...@@ -38,15 +38,36 @@ import ( ...@@ -38,15 +38,36 @@ import (
"github.com/arduino/arduino-cli/table" "github.com/arduino/arduino-cli/table"
"github.com/arduino/arduino-cli/version" "github.com/arduino/arduino-cli/version"
"github.com/arduino/go-paths-helper" "github.com/arduino/go-paths-helper"
"github.com/arduino/go-properties-orderedmap"
"github.com/fatih/color" "github.com/fatih/color"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
type showPropertiesMode int
const (
showPropertiesModeDisabled showPropertiesMode = iota
showPropertiesModeUnexpanded
showPropertiesModeExpanded
)
func parseShowPropertiesMode(showProperties string) (showPropertiesMode, error) {
val, ok := map[string]showPropertiesMode{
"disabled": showPropertiesModeDisabled,
"unexpanded": showPropertiesModeUnexpanded,
"expanded": showPropertiesModeExpanded,
}[showProperties]
if !ok {
return showPropertiesModeDisabled, fmt.Errorf(tr("invalid option '%s'.", showProperties))
}
return val, nil
}
var ( var (
fqbnArg arguments.Fqbn // Fully Qualified Board Name, e.g.: arduino:avr:uno. fqbnArg arguments.Fqbn // Fully Qualified Board Name, e.g.: arduino:avr:uno.
profileArg arguments.Profile // Profile to use 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. preprocess bool // Print preprocessed code to stdout.
buildCachePath string // Builds of 'core.a' are saved into this path to be cached and reused. buildCachePath string // Builds of 'core.a' are saved into this path to be cached and reused.
buildPath string // Path where to save compiled files. buildPath string // Path where to save compiled files.
...@@ -95,7 +116,13 @@ func NewCommand() *cobra.Command { ...@@ -95,7 +116,13 @@ func NewCommand() *cobra.Command {
fqbnArg.AddToCommand(compileCommand) fqbnArg.AddToCommand(compileCommand)
profileArg.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(&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. 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.")) 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().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.")) compileCommand.Flags().StringVarP(&exportDir, "output-dir", "", "", tr("Save build artifacts in this directory."))
...@@ -188,9 +215,14 @@ func runCompileCommand(cmd *cobra.Command, args []string) { ...@@ -188,9 +215,14 @@ func runCompileCommand(cmd *cobra.Command, args []string) {
overrides = o.Overrides overrides = o.Overrides
} }
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 stdOut, stdErr io.Writer
var stdIORes func() *feedback.OutputStreamsResult var stdIORes func() *feedback.OutputStreamsResult
if showProperties { if showPropertiesM != showPropertiesModeDisabled {
stdOut, stdErr, stdIORes = feedback.NewBufferedStreams() stdOut, stdErr, stdIORes = feedback.NewBufferedStreams()
} else { } else {
stdOut, stdErr, stdIORes = feedback.OutputStreams() stdOut, stdErr, stdIORes = feedback.OutputStreams()
...@@ -200,7 +232,7 @@ func runCompileCommand(cmd *cobra.Command, args []string) { ...@@ -200,7 +232,7 @@ func runCompileCommand(cmd *cobra.Command, args []string) {
Instance: inst, Instance: inst,
Fqbn: fqbn, Fqbn: fqbn,
SketchPath: sketchPath.String(), SketchPath: sketchPath.String(),
ShowProperties: showProperties, ShowProperties: showPropertiesM != showPropertiesModeDisabled,
Preprocess: preprocess, Preprocess: preprocess,
BuildCachePath: buildCachePath, BuildCachePath: buildCachePath,
BuildPath: buildPath, BuildPath: buildPath,
...@@ -318,7 +350,7 @@ func runCompileCommand(cmd *cobra.Command, args []string) { ...@@ -318,7 +350,7 @@ func runCompileCommand(cmd *cobra.Command, args []string) {
BuilderResult: compileRes, BuilderResult: compileRes,
ProfileOut: profileOut, ProfileOut: profileOut,
Success: compileError == nil, Success: compileError == nil,
showOnlyProperties: showProperties, showPropertiesMode: showPropertiesM,
} }
if compileError != nil { if compileError != nil {
...@@ -353,9 +385,24 @@ func runCompileCommand(cmd *cobra.Command, args []string) { ...@@ -353,9 +385,24 @@ func runCompileCommand(cmd *cobra.Command, args []string) {
} }
feedback.FatalResult(res, feedback.ErrGeneric) feedback.FatalResult(res, feedback.ErrGeneric)
} }
if showPropertiesM == showPropertiesModeExpanded {
expandPropertiesInResult(res)
}
feedback.PrintResult(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 { type compileResult struct {
CompilerOut string `json:"compiler_out"` CompilerOut string `json:"compiler_out"`
CompilerErr string `json:"compiler_err"` CompilerErr string `json:"compiler_err"`
...@@ -364,7 +411,7 @@ type compileResult struct { ...@@ -364,7 +411,7 @@ type compileResult struct {
ProfileOut string `json:"profile_out,omitempty"` ProfileOut string `json:"profile_out,omitempty"`
Error string `json:"error,omitempty"` Error string `json:"error,omitempty"`
showOnlyProperties bool showPropertiesMode showPropertiesMode
} }
func (r *compileResult) Data() interface{} { func (r *compileResult) Data() interface{} {
...@@ -372,8 +419,8 @@ func (r *compileResult) Data() interface{} { ...@@ -372,8 +419,8 @@ func (r *compileResult) Data() interface{} {
} }
func (r *compileResult) String() string { func (r *compileResult) String() string {
if r.showOnlyProperties { if r.showPropertiesMode != showPropertiesModeDisabled {
return strings.Join(r.BuilderResult.BuildProperties, fmt.Sprintln()) return strings.Join(r.BuilderResult.GetBuildProperties(), fmt.Sprintln())
} }
titleColor := color.New(color.FgHiGreen) titleColor := color.New(color.FgHiGreen)
......
...@@ -16,14 +16,19 @@ ...@@ -16,14 +16,19 @@
package compile_test package compile_test
import ( import (
"encoding/json"
"testing" "testing"
"github.com/arduino/arduino-cli/internal/integrationtest" "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/arduino/go-properties-orderedmap"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.bug.st/testifyjson/requirejson"
) )
type cliCompileResponse struct {
BuilderResult *commands.CompileResponse `json:"builder_result"`
}
func TestCompileShowProperties(t *testing.T) { func TestCompileShowProperties(t *testing.T) {
env, cli := integrationtest.CreateArduinoCLIWithEnvironment(t) env, cli := integrationtest.CreateArduinoCLIWithEnvironment(t)
defer env.CleanUp() defer env.CleanUp()
...@@ -36,17 +41,25 @@ func TestCompileShowProperties(t *testing.T) { ...@@ -36,17 +41,25 @@ func TestCompileShowProperties(t *testing.T) {
bareMinimum := cli.CopySketch("bare_minimum") bareMinimum := cli.CopySketch("bare_minimum")
// Test --show-properties output is clean // 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()) stdout, stderr, err := cli.Run("compile", "--fqbn", "arduino:avr:uno", "-v", "--show-properties", bareMinimum.String())
require.NoError(t, err) 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.NoError(t, err, "Output must be a clean property list")
require.Empty(t, stderr) 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 // 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()) stdout, stderr, err = cli.Run("compile", "--fqbn", "arduino:avr:uno", "-v", "--show-properties", "--format", "json", bareMinimum.String())
require.NoError(t, err) require.NoError(t, err)
requirejson.Parse(t, stdout, "Output must be a valid JSON")
require.Empty(t, stderr) 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 // 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()) stdout, stderr, err = cli.Run("compile", "--fqbn", "arduino:avr:unoa", "-v", "--show-properties", bareMinimum.String())
...@@ -58,6 +71,54 @@ func TestCompileShowProperties(t *testing.T) { ...@@ -58,6 +71,54 @@ func TestCompileShowProperties(t *testing.T) {
// Test --show-properties --format JSON output is clean, with a wrong FQBN // 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()) stdout, stderr, err = cli.Run("compile", "--fqbn", "arduino:avr:unoa", "-v", "--show-properties", "--format", "json", bareMinimum.String())
require.Error(t, err) require.Error(t, err)
requirejson.Parse(t, stdout, "Output must be a valid JSON")
require.Empty(t, stderr) require.Empty(t, stderr)
requireCompileResponseJson(t, stdout)
// Test --show-properties=unexpanded output is clean
// properties are not expanded
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")
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=unexpanded output is clean
// properties are not expanded
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(
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=expanded output is clean
// properties are expanded
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")
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=expanded --format JSON output is clean
// properties are expanded
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(
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
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment