diff --git a/commands/compile/compile.go b/commands/compile/compile.go index ba904fddad0..92858dd68d6 100644 --- a/commands/compile/compile.go +++ b/commands/compile/compile.go @@ -81,7 +81,11 @@ func Compile(ctx context.Context, req *rpc.CompileRequest, outStream, errStream fqbnIn := req.GetFqbn() if fqbnIn == "" && sk != nil { - fqbnIn = sk.GetDefaultFQBN() + if pme.GetProfile() != nil { + fqbnIn = pme.GetProfile().FQBN + } else { + fqbnIn = sk.GetDefaultFQBN() + } } if fqbnIn == "" { return nil, &arduino.MissingFQBNError{} diff --git a/commands/upload/upload.go b/commands/upload/upload.go index 5fe21089848..0bf0ae86ec0 100644 --- a/commands/upload/upload.go +++ b/commands/upload/upload.go @@ -143,12 +143,17 @@ func Upload(ctx context.Context, req *rpc.UploadRequest, outStream io.Writer, er } defer pmeRelease() + fqbn := req.GetFqbn() + if fqbn == "" && pme.GetProfile() != nil { + fqbn = pme.GetProfile().FQBN + } + updatedPort, err := runProgramAction( pme, sk, req.GetImportFile(), req.GetImportDir(), - req.GetFqbn(), + fqbn, req.GetPort(), req.GetProgrammer(), req.GetVerbose(), diff --git a/docs/sketch-project-file.md b/docs/sketch-project-file.md index 01a5607c8db..640ede7505e 100644 --- a/docs/sketch-project-file.md +++ b/docs/sketch-project-file.md @@ -95,6 +95,8 @@ profiles: - ArduinoIoTCloud (1.0.2) - Arduino_ConnectionHandler (0.6.4) - TinyDHT sensor library (1.1.0) + +default_profile: nanorp ``` ### Building a sketch @@ -116,6 +118,16 @@ not be used in any way. In other words, the build is isolated from the system an specified in the profile: this will ensure that the build is portable and reproducible independently from the platforms and libraries installed in the system. +### Using a default profile + +If a `default_profile` is specified in the `sketch.yaml` then the “classic” compile command: + +``` +arduino-cli compile [sketch] +``` + +will, instead, trigger a profile-based build using the default profile indicated in the `sketch.yaml`. + ## Default flags for Arduino CLI usage The sketch project file may be used to set the default value for some command line flags of the Arduino CLI, in @@ -124,6 +136,7 @@ particular: - The `default_fqbn` key sets the default value for the `--fqbn` flag - The `default_port` key sets the default value for the `--port` flag - The `default_protocol` key sets the default value for the `--protocol` flag +- The `default_profile` key sets the default value for the `--profile` flag For example: @@ -131,8 +144,9 @@ For example: default_fqbn: arduino:avr:uno default_port: /dev/ttyACM0 default_protocol: serial +default_profile: myprofile ``` -With this configuration set, it is not necessary to specify the `--fqbn`, `--port`, or `--protocol` flags to the -[`arduino-cli compile`](commands/arduino-cli_compile.md) or [`arduino-cli upload`](commands/arduino-cli_upload.md) +With this configuration set, it is not necessary to specify the `--fqbn`, `--port`, `--protocol` or `--profile` flags to +the [`arduino-cli compile`](commands/arduino-cli_compile.md) or [`arduino-cli upload`](commands/arduino-cli_upload.md) commands when compiling or uploading the sketch. diff --git a/internal/cli/arguments/fqbn.go b/internal/cli/arguments/fqbn.go index b48ae8baf46..84c805c0088 100644 --- a/internal/cli/arguments/fqbn.go +++ b/internal/cli/arguments/fqbn.go @@ -89,5 +89,5 @@ func CalculateFQBNAndPort(portArgs *Port, fqbnArg *Fqbn, instance *rpc.Instance, if err != nil { feedback.Fatal(tr("Error getting port metadata: %v", err), feedback.ErrGeneric) } - return fqbn, port.ToRPC() + return fqbn, port } diff --git a/internal/cli/arguments/port.go b/internal/cli/arguments/port.go index d8b0617cb58..08192049c2c 100644 --- a/internal/cli/arguments/port.go +++ b/internal/cli/arguments/port.go @@ -20,7 +20,6 @@ import ( "time" "github.com/arduino/arduino-cli/arduino" - "github.com/arduino/arduino-cli/arduino/discovery" "github.com/arduino/arduino-cli/commands" "github.com/arduino/arduino-cli/commands/board" "github.com/arduino/arduino-cli/internal/cli/feedback" @@ -70,8 +69,7 @@ func (p *Port) GetPortAddressAndProtocol(instance *rpc.Instance, defaultAddress, // GetPort returns the Port obtained by parsing command line arguments. // The extra metadata for the ports is obtained using the pluggable discoveries. -func (p *Port) GetPort(instance *rpc.Instance, defaultAddress, defaultProtocol string) (*discovery.Port, error) { - // TODO: REMOVE discovery from here (use board.List instead) +func (p *Port) GetPort(instance *rpc.Instance, defaultAddress, defaultProtocol string) (*rpc.Port, error) { address := p.address protocol := p.protocol @@ -84,7 +82,7 @@ func (p *Port) GetPort(instance *rpc.Instance, defaultAddress, defaultProtocol s // the attached board without specifying explictly a port. // Tools that work this way must be specified using the property // "BOARD_ID.upload.tool.default" in the platform's boards.txt. - return &discovery.Port{ + return &rpc.Port{ Protocol: "default", }, nil } @@ -113,13 +111,13 @@ func (p *Port) GetPort(instance *rpc.Instance, defaultAddress, defaultProtocol s } port := portEvent.Port if (protocol == "" || protocol == port.Protocol) && address == port.Address { - return port, nil + return port.ToRPC(), nil } case <-deadline: // No matching port found if protocol == "" { - return &discovery.Port{ + return &rpc.Port{ Address: address, Protocol: "serial", }, nil diff --git a/internal/cli/burnbootloader/burnbootloader.go b/internal/cli/burnbootloader/burnbootloader.go index 45f0374bd27..23cd9a5006e 100644 --- a/internal/cli/burnbootloader/burnbootloader.go +++ b/internal/cli/burnbootloader/burnbootloader.go @@ -76,7 +76,7 @@ func runBootloaderCommand(command *cobra.Command, args []string) { if _, err := upload.BurnBootloader(context.Background(), &rpc.BurnBootloaderRequest{ Instance: instance, Fqbn: fqbn.String(), - Port: discoveryPort.ToRPC(), + Port: discoveryPort, Verbose: verbose, Verify: verify, Programmer: programmer.String(), diff --git a/internal/cli/compile/compile.go b/internal/cli/compile/compile.go index baa1dff5f89..dfd5d4c70ba 100644 --- a/internal/cli/compile/compile.go +++ b/internal/cli/compile/compile.go @@ -159,15 +159,25 @@ func runCompileCommand(cmd *cobra.Command, args []string) { } sketchPath := arguments.InitSketchPath(path) - inst, profile := instance.CreateAndInitWithProfile(profileArg.Get(), sketchPath) - if fqbnArg.String() == "" { - fqbnArg.Set(profile.GetFqbn()) - } sk, err := sketch.LoadSketch(context.Background(), &rpc.LoadSketchRequest{SketchPath: sketchPath.String()}) if err != nil { feedback.FatalError(err, feedback.ErrGeneric) } + + var inst *rpc.Instance + var profile *rpc.Profile + + if profileArg.Get() == "" { + inst, profile = instance.CreateAndInitWithProfile(sk.GetDefaultProfile().GetName(), sketchPath) + } else { + inst, profile = instance.CreateAndInitWithProfile(profileArg.Get(), sketchPath) + } + + if fqbnArg.String() == "" { + fqbnArg.Set(profile.GetFqbn()) + } + fqbn, port := arguments.CalculateFQBNAndPort(&portArgs, &fqbnArg, inst, sk.GetDefaultFqbn(), sk.GetDefaultPort(), sk.GetDefaultProtocol()) if keysKeychain != "" || signKey != "" || encryptKey != "" { diff --git a/internal/cli/upload/upload.go b/internal/cli/upload/upload.go index 1fa91086ac0..3c33f2675b0 100644 --- a/internal/cli/upload/upload.go +++ b/internal/cli/upload/upload.go @@ -24,8 +24,8 @@ import ( "github.com/arduino/arduino-cli/arduino" "github.com/arduino/arduino-cli/arduino/cores/packagemanager" - "github.com/arduino/arduino-cli/arduino/sketch" "github.com/arduino/arduino-cli/commands" + sk "github.com/arduino/arduino-cli/commands/sketch" "github.com/arduino/arduino-cli/commands/upload" "github.com/arduino/arduino-cli/i18n" "github.com/arduino/arduino-cli/internal/cli/arguments" @@ -90,22 +90,31 @@ func runUploadCommand(command *cobra.Command, args []string) { arguments.WarnDeprecatedFiles(sketchPath) } - sk, err := sketch.New(sketchPath) + sketch, err := sk.LoadSketch(context.Background(), &rpc.LoadSketchRequest{SketchPath: sketchPath.String()}) if err != nil && importDir == "" && importFile == "" { feedback.Fatal(tr("Error during Upload: %v", err), feedback.ErrGeneric) } - instance, profile := instance.CreateAndInitWithProfile(profileArg.Get(), sketchPath) + var inst *rpc.Instance + var profile *rpc.Profile + + if profileArg.Get() == "" { + inst, profile = instance.CreateAndInitWithProfile(sketch.GetDefaultProfile().GetName(), sketchPath) + } else { + inst, profile = instance.CreateAndInitWithProfile(profileArg.Get(), sketchPath) + } + if fqbnArg.String() == "" { fqbnArg.Set(profile.GetFqbn()) } - defaultFQBN := sk.GetDefaultFQBN() - defaultAddress, defaultProtocol := sk.GetDefaultPortAddressAndProtocol() - fqbn, port := arguments.CalculateFQBNAndPort(&portArgs, &fqbnArg, instance, defaultFQBN, defaultAddress, defaultProtocol) + defaultFQBN := sketch.GetDefaultFqbn() + defaultAddress := sketch.GetDefaultPort() + defaultProtocol := sketch.GetDefaultProtocol() + fqbn, port := arguments.CalculateFQBNAndPort(&portArgs, &fqbnArg, inst, defaultFQBN, defaultAddress, defaultProtocol) userFieldRes, err := upload.SupportedUserFields(context.Background(), &rpc.SupportedUserFieldsRequest{ - Instance: instance, + Instance: inst, Fqbn: fqbn, Protocol: port.Protocol, }) @@ -122,7 +131,7 @@ func runUploadCommand(command *cobra.Command, args []string) { } // FIXME: Here we must not access package manager... - pme, release := commands.GetPackageManagerExplorer(&rpc.UploadRequest{Instance: instance}) + pme, release := commands.GetPackageManagerExplorer(&rpc.UploadRequest{Instance: inst}) platform := pme.FindPlatform(&packagemanager.PlatformReference{ Package: split[0], PlatformArchitecture: split[1], @@ -156,7 +165,7 @@ func runUploadCommand(command *cobra.Command, args []string) { stdOut, stdErr, stdIOResult := feedback.OutputStreams() req := &rpc.UploadRequest{ - Instance: instance, + Instance: inst, Fqbn: fqbn, SketchPath: path, Port: port, diff --git a/internal/integrationtest/profiles/profiles_test.go b/internal/integrationtest/profiles/profiles_test.go index 9cd1ffcb556..4915bed5cf7 100644 --- a/internal/integrationtest/profiles/profiles_test.go +++ b/internal/integrationtest/profiles/profiles_test.go @@ -20,6 +20,7 @@ import ( "github.com/arduino/arduino-cli/internal/integrationtest" "github.com/stretchr/testify/require" + "go.bug.st/testifyjson/requirejson" ) func TestCompileWithProfiles(t *testing.T) { @@ -69,3 +70,83 @@ func TestBuilderDidNotCatchLibsFromUnusedPlatforms(t *testing.T) { require.NotContains(t, string(stdout), "samd") require.NotContains(t, string(stderr), "samd") } + +func TestCompileWithDefaultProfile(t *testing.T) { + env, cli := integrationtest.CreateArduinoCLIWithEnvironment(t) + defer env.CleanUp() + + // Init the environment explicitly + _, _, err := cli.Run("core", "update-index") + require.NoError(t, err) + + // Installa core/libs globally + _, _, err = cli.Run("core", "install", "arduino:avr@1.8.3") + require.NoError(t, err) + + // copy sketch_with_profile into the working directory + sketchWithoutDefProfilePath := cli.CopySketch("sketch_without_default_profile") + sketchWithDefProfilePath := cli.CopySketch("sketch_with_default_profile") + + { + // no default profile -> error missing FQBN + _, _, err := cli.Run("compile", sketchWithoutDefProfilePath.String(), "--format", "json") + require.Error(t, err) + } + { + // specified fbqn -> compile with specified FQBN and use global installation + stdout, _, err := cli.Run("compile", "-b", "arduino:avr:nano", sketchWithoutDefProfilePath.String(), "--format", "json") + require.NoError(t, err) + jsonOut := requirejson.Parse(t, stdout) + jsonOut.Query(".builder_result.build_platform").MustContain(`{"id":"arduino:avr", "version":"1.8.3"}`) + jsonOut.Query(".builder_result.build_properties").MustContain(`[ "build.fqbn=arduino:avr:nano" ]`) + } + { + // specified profile -> use the specified profile + stdout, _, err := cli.Run("compile", "--profile", "avr1", sketchWithoutDefProfilePath.String(), "--format", "json") + require.NoError(t, err) + jsonOut := requirejson.Parse(t, stdout) + jsonOut.Query(".builder_result.build_platform").MustContain(`{"id":"arduino:avr", "version":"1.8.4"}`) + jsonOut.Query(".builder_result.build_properties").MustContain(`[ "build.fqbn=arduino:avr:uno" ]`) + } + { + // specified profile and fqbn -> use the specified profile and override fqbn + stdout, _, err := cli.Run("compile", "--profile", "avr1", "-b", "arduino:avr:nano", sketchWithoutDefProfilePath.String(), "--format", "json") + require.NoError(t, err) + jsonOut := requirejson.Parse(t, stdout) + jsonOut.Query(".builder_result.build_platform").MustContain(`{"id":"arduino:avr", "version":"1.8.4"}`) + jsonOut.Query(".builder_result.build_properties").MustContain(`[ "build.fqbn=arduino:avr:nano" ]`) + } + + { + // default profile -> use default profile + stdout, _, err := cli.Run("compile", sketchWithDefProfilePath.String(), "--format", "json") + require.NoError(t, err) + jsonOut := requirejson.Parse(t, stdout) + jsonOut.Query(".builder_result.build_platform").MustContain(`{"id":"arduino:avr", "version":"1.8.5"}`) + jsonOut.Query(".builder_result.build_properties").MustContain(`[ "build.fqbn=arduino:avr:leonardo" ]`) + } + { + // default profile, specified fbqn -> use default profile, override fqbn + stdout, _, err := cli.Run("compile", "-b", "arduino:avr:nano", sketchWithDefProfilePath.String(), "--format", "json") + require.NoError(t, err) + jsonOut := requirejson.Parse(t, stdout) + jsonOut.Query(".builder_result.build_platform").MustContain(`{"id":"arduino:avr", "version":"1.8.5"}`) + jsonOut.Query(".builder_result.build_properties").MustContain(`[ "build.fqbn=arduino:avr:nano" ]`) + } + { + // default profile, specified different profile -> use the specified profile + stdout, _, err := cli.Run("compile", "--profile", "avr1", sketchWithDefProfilePath.String(), "--format", "json") + require.NoError(t, err) + jsonOut := requirejson.Parse(t, stdout) + jsonOut.Query(".builder_result.build_platform").MustContain(`{"id":"arduino:avr", "version":"1.8.4"}`) + jsonOut.Query(".builder_result.build_properties").MustContain(`[ "build.fqbn=arduino:avr:uno" ]`) + } + { + // default profile, specified different profile and fqbn -> use the specified profile and override fqbn + stdout, _, err := cli.Run("compile", "--profile", "avr1", "-b", "arduino:avr:nano", sketchWithDefProfilePath.String(), "--format", "json") + require.NoError(t, err) + jsonOut := requirejson.Parse(t, stdout) + jsonOut.Query(".builder_result.build_platform").MustContain(`{"id":"arduino:avr", "version":"1.8.4"}`) + jsonOut.Query(".builder_result.build_properties").MustContain(`[ "build.fqbn=arduino:avr:nano" ]`) + } +} diff --git a/internal/integrationtest/testdata/sketch_with_default_profile/sketch.yaml b/internal/integrationtest/testdata/sketch_with_default_profile/sketch.yaml new file mode 100644 index 00000000000..95d13d5754e --- /dev/null +++ b/internal/integrationtest/testdata/sketch_with_default_profile/sketch.yaml @@ -0,0 +1,12 @@ +profiles: + avr1: + fqbn: arduino:avr:uno + platforms: + - platform: arduino:avr (1.8.4) + + avr2: + fqbn: arduino:avr:leonardo + platforms: + - platform: arduino:avr (1.8.5) + +default_profile: avr2 diff --git a/internal/integrationtest/testdata/sketch_with_default_profile/sketch_with_default_profile.ino b/internal/integrationtest/testdata/sketch_with_default_profile/sketch_with_default_profile.ino new file mode 100644 index 00000000000..660bdbccfdb --- /dev/null +++ b/internal/integrationtest/testdata/sketch_with_default_profile/sketch_with_default_profile.ino @@ -0,0 +1,2 @@ +void setup() {} +void loop() {} diff --git a/internal/integrationtest/testdata/sketch_without_default_profile/sketch.yaml b/internal/integrationtest/testdata/sketch_without_default_profile/sketch.yaml new file mode 100644 index 00000000000..1344a8fa507 --- /dev/null +++ b/internal/integrationtest/testdata/sketch_without_default_profile/sketch.yaml @@ -0,0 +1,10 @@ +profiles: + avr1: + fqbn: arduino:avr:uno + platforms: + - platform: arduino:avr (1.8.4) + + avr2: + fqbn: arduino:avr:leonardo + platforms: + - platform: arduino:avr (1.8.5) diff --git a/internal/integrationtest/testdata/sketch_without_default_profile/sketch_without_default_profile.ino b/internal/integrationtest/testdata/sketch_without_default_profile/sketch_without_default_profile.ino new file mode 100644 index 00000000000..660bdbccfdb --- /dev/null +++ b/internal/integrationtest/testdata/sketch_without_default_profile/sketch_without_default_profile.ino @@ -0,0 +1,2 @@ +void setup() {} +void loop() {}