diff --git a/cli/compile/compile.go b/cli/compile/compile.go index 5e85e4638c1..e9922978f0a 100644 --- a/cli/compile/compile.go +++ b/cli/compile/compile.go @@ -47,11 +47,11 @@ var ( port string // Upload port, e.g.: COM10 or /dev/ttyACM0. verify bool // Upload, verify uploaded binary after the upload. exportDir string // The compiled binary is written to this file - dryRun bool // Use this flag to now write the output file libraries []string // List of custom libraries paths separated by commas. Or can be used multiple times for multiple libraries paths. optimizeForDebug bool // Optimize compile output for debug, not for release programmer string // Use the specified programmer to upload clean bool // Cleanup the build folder and do not use any cached build + exportBinaries bool // Copies compiled binaries to sketch folder when true ) // NewCommand created a new `compile` command @@ -70,7 +70,6 @@ func NewCommand() *cobra.Command { command.Flags().BoolVar(&preprocess, "preprocess", false, "Print preprocessed code to stdout instead of compiling.") command.Flags().StringVar(&buildCachePath, "build-cache-path", "", "Builds of 'core.a' are saved into this path to be cached and reused.") command.Flags().StringVarP(&exportDir, "output-dir", "", "", "Save build artifacts in this directory.") - command.Flags().BoolVarP(&dryRun, "dry-run", "n", false, "Perform the build but do not copy the compile output file.") command.Flags().StringVar(&buildPath, "build-path", "", "Path where to save compiled files. If omitted, a directory will be created in the default temporary path of your OS.") command.Flags().StringSliceVar(&buildProperties, "build-properties", []string{}, @@ -88,6 +87,9 @@ func NewCommand() *cobra.Command { command.Flags().BoolVar(&optimizeForDebug, "optimize-for-debug", false, "Optional, optimize compile output for debugging, rather than for release.") command.Flags().StringVarP(&programmer, "programmer", "P", "", "Optional, use the specified programmer to upload.") command.Flags().BoolVar(&clean, "clean", false, "Optional, cleanup the build folder and do not use any cached build.") + command.Flags().BoolVarP(&exportBinaries, "export-binaries", "e", false, "If set built binaries will be exported to the sketch folder.") + + configuration.Settings.BindPFlag("sketch.always_export_binaries", command.Flags().Lookup("export-binaries")) return command } @@ -120,10 +122,10 @@ func run(cmd *cobra.Command, args []string) { Quiet: quiet, VidPid: vidPid, ExportDir: exportDir, - DryRun: dryRun, Libraries: libraries, OptimizeForDebug: optimizeForDebug, Clean: clean, + ExportBinaries: exportBinaries, }, os.Stdout, os.Stderr, configuration.Settings.GetString("logging.level") == "debug") if err != nil { @@ -139,7 +141,7 @@ func run(cmd *cobra.Command, args []string) { Port: port, Verbose: verbose, Verify: verify, - ImportDir: exportDir, + ImportDir: buildPath, Programmer: programmer, }, os.Stdout, os.Stderr) diff --git a/commands/compile/compile.go b/commands/compile/compile.go index efc164d97c6..11b2f8148e2 100644 --- a/commands/compile/compile.go +++ b/commands/compile/compile.go @@ -24,6 +24,7 @@ import ( "strconv" "strings" + bldr "github.com/arduino/arduino-cli/arduino/builder" "github.com/arduino/arduino-cli/arduino/cores" "github.com/arduino/arduino-cli/arduino/cores/packagemanager" "github.com/arduino/arduino-cli/arduino/sketches" @@ -54,15 +55,11 @@ func Compile(ctx context.Context, req *rpc.CompileReq, outStream, errStream io.W "verbose": strconv.FormatBool(req.Verbose), "quiet": strconv.FormatBool(req.Quiet), "vidPid": req.VidPid, - "exportFile": telemetry.Sanitize(req.ExportFile), // deprecated "exportDir": telemetry.Sanitize(req.GetExportDir()), "jobs": strconv.FormatInt(int64(req.Jobs), 10), "libraries": strings.Join(req.Libraries, ","), "clean": strconv.FormatBool(req.GetClean()), - } - - if req.GetExportFile() != "" { - outStream.Write([]byte(fmt.Sprintln("Compile.ExportFile has been deprecated. The ExportFile parameter will be ignored, use ExportDir instead."))) + "exportBinaries": strconv.FormatBool(req.GetExportBinaries()), } // Use defer func() to evaluate tags map when function returns @@ -127,12 +124,14 @@ func Compile(ctx context.Context, req *rpc.CompileReq, outStream, errStream io.W builderCtx.OtherLibrariesDirs = paths.NewPathList(req.GetLibraries()...) builderCtx.OtherLibrariesDirs.Add(configuration.LibrariesDir(configuration.Settings)) - if req.GetBuildPath() != "" { + if req.GetBuildPath() == "" { + builderCtx.BuildPath = bldr.GenBuildPath(sketch.FullPath) + } else { builderCtx.BuildPath = paths.New(req.GetBuildPath()) - err = builderCtx.BuildPath.MkdirAll() - if err != nil { - return nil, fmt.Errorf("cannot create build directory: %s", err) - } + } + + if err = builderCtx.BuildPath.MkdirAll(); err != nil { + return nil, fmt.Errorf("cannot create build directory: %s", err) } builderCtx.Verbose = req.GetVerbose() @@ -202,7 +201,8 @@ func Compile(ctx context.Context, req *rpc.CompileReq, outStream, errStream io.W return nil, err } - if !req.GetDryRun() { + // If the export directory is set we assume you want to export the binaries + if req.GetExportBinaries() || req.GetExportDir() != "" { var exportPath *paths.Path if exportDir := req.GetExportDir(); exportDir != "" { exportPath = paths.New(exportDir) diff --git a/commands/upload/upload.go b/commands/upload/upload.go index 858549e42ee..3e67b9d88b1 100644 --- a/commands/upload/upload.go +++ b/commands/upload/upload.go @@ -23,6 +23,7 @@ import ( "path/filepath" "strings" + bldr "github.com/arduino/arduino-cli/arduino/builder" "github.com/arduino/arduino-cli/arduino/cores" "github.com/arduino/arduino-cli/arduino/cores/packagemanager" "github.com/arduino/arduino-cli/arduino/serialutils" @@ -425,16 +426,9 @@ func determineBuildPathAndSketchName(importFile, importDir string, sketch *sketc return nil, "", fmt.Errorf("no sketch or build directory/file specified") } - // Case 4: only sketch specified. In this case we use the default sketch build path + // Case 4: only sketch specified. In this case we use the generated build path // and the given sketch name. - - // TODO: Create a function to obtain importPath from sketch - // Add FQBN (without configs part) to export path - if fqbn == nil { - return nil, "", fmt.Errorf("missing FQBN") - } - fqbnSuffix := strings.Replace(fqbn.StringWithoutConfig(), ":", ".", -1) - return sketch.FullPath.Join("build").Join(fqbnSuffix), sketch.Name + ".ino", nil + return bldr.GenBuildPath(sketch.FullPath), sketch.Name + ".ino", nil } func detectSketchNameFromBuildPath(buildPath *paths.Path) (string, error) { diff --git a/commands/upload/upload_test.go b/commands/upload/upload_test.go index ab991921077..df3658d81e6 100644 --- a/commands/upload/upload_test.go +++ b/commands/upload/upload_test.go @@ -21,6 +21,7 @@ import ( "strings" "testing" + "github.com/arduino/arduino-cli/arduino/builder" "github.com/arduino/arduino-cli/arduino/cores" "github.com/arduino/arduino-cli/arduino/cores/packagemanager" "github.com/arduino/arduino-cli/arduino/sketches" @@ -76,15 +77,14 @@ func TestDetermineBuildPathAndSketchName(t *testing.T) { {"", "testdata/build_path_2", nil, nil, "testdata/build_path_2", "Blink.ino"}, // 03: error: used both importPath and importFile {"testdata/build_path_2/Blink.ino.hex", "testdata/build_path_2", nil, nil, "", ""}, - // 04: error: only sketch without FQBN - {"", "", blonk, nil, "", ""}, + // 04: only sketch without FQBN + {"", "", blonk, nil, builder.GenBuildPath(blonk.FullPath).String(), "Blonk.ino"}, // 05: use importFile to detect build.path and project_name, sketch is ignored. {"testdata/build_path_2/Blink.ino.hex", "", blonk, nil, "testdata/build_path_2", "Blink.ino"}, // 06: use importPath as build.path and Blink as project name, ignore the sketch Blonk {"", "testdata/build_path_2", blonk, nil, "testdata/build_path_2", "Blink.ino"}, // 07: error: used both importPath and importFile {"testdata/build_path_2/Blink.ino.hex", "testdata/build_path_2", blonk, nil, "", ""}, - // 08: error: no data passed in {"", "", nil, fqbn, "", ""}, // 09: use importFile to detect build.path and project_name, fqbn ignored @@ -94,14 +94,13 @@ func TestDetermineBuildPathAndSketchName(t *testing.T) { // 11: error: used both importPath and importFile {"testdata/build_path_2/Blink.ino.hex", "testdata/build_path_2", nil, fqbn, "", ""}, // 12: use sketch to determine project name and sketch+fqbn to determine build path - {"", "", blonk, fqbn, "testdata/Blonk/build/arduino.samd.mkr1000", "Blonk.ino"}, + {"", "", blonk, fqbn, builder.GenBuildPath(blonk.FullPath).String(), "Blonk.ino"}, // 13: use importFile to detect build.path and project_name, sketch+fqbn is ignored. {"testdata/build_path_2/Blink.ino.hex", "", blonk, fqbn, "testdata/build_path_2", "Blink.ino"}, // 14: use importPath as build.path and Blink as project name, ignore the sketch Blonk, ignore fqbn {"", "testdata/build_path_2", blonk, fqbn, "testdata/build_path_2", "Blink.ino"}, // 15: error: used both importPath and importFile {"testdata/build_path_2/Blink.ino.hex", "testdata/build_path_2", blonk, fqbn, "", ""}, - // 16: importPath containing multiple firmwares, but one has the same name as the containing folder {"", "testdata/firmware", nil, fqbn, "testdata/firmware", "firmware.ino"}, // 17: importFile among multiple firmwares diff --git a/configuration/defaults.go b/configuration/defaults.go index 61e495e884e..68bc171fdb2 100644 --- a/configuration/defaults.go +++ b/configuration/defaults.go @@ -36,6 +36,9 @@ func SetDefaults(settings *viper.Viper) { settings.SetDefault("directories.Downloads", filepath.Join(getDefaultArduinoDataDir(), "staging")) settings.SetDefault("directories.User", getDefaultUserDir()) + // Sketch compilation + settings.SetDefault("sketch.always_export_binaries", false) + // daemon settings settings.SetDefault("daemon.port", "50051") @@ -52,4 +55,5 @@ func SetDefaults(settings *viper.Viper) { settings.BindEnv("directories.User", "ARDUINO_SKETCHBOOK_DIR") settings.BindEnv("directories.Downloads", "ARDUINO_DOWNLOADS_DIR") settings.BindEnv("directories.Data", "ARDUINO_DATA_DIR") + settings.BindEnv("sketch.always_export_binaries", "ARDUINO_SKETCH_ALWAYS_EXPORT_BINARIES") } diff --git a/legacy/builder/builder.go b/legacy/builder/builder.go index e9aafb0b73c..e84c2222f82 100644 --- a/legacy/builder/builder.go +++ b/legacy/builder/builder.go @@ -42,10 +42,6 @@ const DEFAULT_SOFTWARE = "ARDUINO" type Builder struct{} func (s *Builder) Run(ctx *types.Context) error { - if ctx.BuildPath == nil { - ctx.BuildPath = bldr.GenBuildPath(ctx.SketchLocation) - } - if err := bldr.EnsureBuildPathExists(ctx.BuildPath.String()); err != nil { return err } diff --git a/rpc/commands/compile.pb.go b/rpc/commands/compile.pb.go index 0edd056793a..98907ff28cb 100644 --- a/rpc/commands/compile.pb.go +++ b/rpc/commands/compile.pb.go @@ -45,26 +45,24 @@ type CompileReq struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Instance *Instance `protobuf:"bytes,1,opt,name=instance,proto3" json:"instance,omitempty"` // Arduino Core Service instance from the `Init` response. - Fqbn string `protobuf:"bytes,2,opt,name=fqbn,proto3" json:"fqbn,omitempty"` // Fully Qualified Board Name, e.g.: `arduino:avr:uno`. If this field is not defined, the FQBN of the board attached to the sketch via the `BoardAttach` method is used. - SketchPath string `protobuf:"bytes,3,opt,name=sketchPath,proto3" json:"sketchPath,omitempty"` // The path where the sketch is stored. - ShowProperties bool `protobuf:"varint,4,opt,name=showProperties,proto3" json:"showProperties,omitempty"` // Show all build preferences used instead of compiling. - Preprocess bool `protobuf:"varint,5,opt,name=preprocess,proto3" json:"preprocess,omitempty"` // Print preprocessed code to stdout instead of compiling. - BuildCachePath string `protobuf:"bytes,6,opt,name=buildCachePath,proto3" json:"buildCachePath,omitempty"` // Builds of 'core.a' are saved into this path to be cached and reused. - BuildPath string `protobuf:"bytes,7,opt,name=buildPath,proto3" json:"buildPath,omitempty"` // Path to use to store the files used for the compilation. If omitted, a directory will be created in the operating system's default temporary path. - BuildProperties []string `protobuf:"bytes,8,rep,name=buildProperties,proto3" json:"buildProperties,omitempty"` // List of custom build properties separated by commas. - Warnings string `protobuf:"bytes,9,opt,name=warnings,proto3" json:"warnings,omitempty"` // Used to tell gcc which warning level to use. The level names are: "none", "default", "more" and "all". - Verbose bool `protobuf:"varint,10,opt,name=verbose,proto3" json:"verbose,omitempty"` // Turns on verbose mode. - Quiet bool `protobuf:"varint,11,opt,name=quiet,proto3" json:"quiet,omitempty"` // Suppresses almost every output. - VidPid string `protobuf:"bytes,12,opt,name=vidPid,proto3" json:"vidPid,omitempty"` // VID/PID specific build properties. - // Deprecated: Do not use. - ExportFile string `protobuf:"bytes,13,opt,name=exportFile,proto3" json:"exportFile,omitempty"` // DEPRECATED: use exportDir instead - Jobs int32 `protobuf:"varint,14,opt,name=jobs,proto3" json:"jobs,omitempty"` // The max number of concurrent compiler instances to run (as `make -jx`). If jobs is set to 0, it will use the number of available CPUs as the maximum. - Libraries []string `protobuf:"bytes,15,rep,name=libraries,proto3" json:"libraries,omitempty"` // List of custom libraries paths separated by commas. - OptimizeForDebug bool `protobuf:"varint,16,opt,name=optimizeForDebug,proto3" json:"optimizeForDebug,omitempty"` // Optimize compile output for debug, not for release. - DryRun bool `protobuf:"varint,17,opt,name=dryRun,proto3" json:"dryRun,omitempty"` // When set to `true` the compiled binary will not be copied to the export directory. - ExportDir string `protobuf:"bytes,18,opt,name=export_dir,json=exportDir,proto3" json:"export_dir,omitempty"` // Optional: save the build artifacts in this directory, the directory must exist. - Clean bool `protobuf:"varint,19,opt,name=clean,proto3" json:"clean,omitempty"` // Optional: cleanup the build folder and do not use any previously cached build + Instance *Instance `protobuf:"bytes,1,opt,name=instance,proto3" json:"instance,omitempty"` // Arduino Core Service instance from the `Init` response. + Fqbn string `protobuf:"bytes,2,opt,name=fqbn,proto3" json:"fqbn,omitempty"` // Fully Qualified Board Name, e.g.: `arduino:avr:uno`. If this field is not defined, the FQBN of the board attached to the sketch via the `BoardAttach` method is used. + SketchPath string `protobuf:"bytes,3,opt,name=sketchPath,proto3" json:"sketchPath,omitempty"` // The path where the sketch is stored. + ShowProperties bool `protobuf:"varint,4,opt,name=showProperties,proto3" json:"showProperties,omitempty"` // Show all build preferences used instead of compiling. + Preprocess bool `protobuf:"varint,5,opt,name=preprocess,proto3" json:"preprocess,omitempty"` // Print preprocessed code to stdout instead of compiling. + BuildCachePath string `protobuf:"bytes,6,opt,name=buildCachePath,proto3" json:"buildCachePath,omitempty"` // Builds of 'core.a' are saved into this path to be cached and reused. + BuildPath string `protobuf:"bytes,7,opt,name=buildPath,proto3" json:"buildPath,omitempty"` // Path to use to store the files used for the compilation. If omitted, a directory will be created in the operating system's default temporary path. + BuildProperties []string `protobuf:"bytes,8,rep,name=buildProperties,proto3" json:"buildProperties,omitempty"` // List of custom build properties separated by commas. + Warnings string `protobuf:"bytes,9,opt,name=warnings,proto3" json:"warnings,omitempty"` // Used to tell gcc which warning level to use. The level names are: "none", "default", "more" and "all". + Verbose bool `protobuf:"varint,10,opt,name=verbose,proto3" json:"verbose,omitempty"` // Turns on verbose mode. + Quiet bool `protobuf:"varint,11,opt,name=quiet,proto3" json:"quiet,omitempty"` // Suppresses almost every output. + VidPid string `protobuf:"bytes,12,opt,name=vidPid,proto3" json:"vidPid,omitempty"` // VID/PID specific build properties. + Jobs int32 `protobuf:"varint,14,opt,name=jobs,proto3" json:"jobs,omitempty"` // The max number of concurrent compiler instances to run (as `make -jx`). If jobs is set to 0, it will use the number of available CPUs as the maximum. + Libraries []string `protobuf:"bytes,15,rep,name=libraries,proto3" json:"libraries,omitempty"` // List of custom libraries paths separated by commas. + OptimizeForDebug bool `protobuf:"varint,16,opt,name=optimizeForDebug,proto3" json:"optimizeForDebug,omitempty"` // Optimize compile output for debug, not for release. + ExportDir string `protobuf:"bytes,18,opt,name=export_dir,json=exportDir,proto3" json:"export_dir,omitempty"` // Optional: save the build artifacts in this directory, the directory must exist. + Clean bool `protobuf:"varint,19,opt,name=clean,proto3" json:"clean,omitempty"` // Optional: cleanup the build folder and do not use any previously cached build + ExportBinaries bool `protobuf:"varint,20,opt,name=export_binaries,json=exportBinaries,proto3" json:"export_binaries,omitempty"` // When set to `true` the compiled binary will be copied to the export directory. } func (x *CompileReq) Reset() { @@ -183,14 +181,6 @@ func (x *CompileReq) GetVidPid() string { return "" } -// Deprecated: Do not use. -func (x *CompileReq) GetExportFile() string { - if x != nil { - return x.ExportFile - } - return "" -} - func (x *CompileReq) GetJobs() int32 { if x != nil { return x.Jobs @@ -212,13 +202,6 @@ func (x *CompileReq) GetOptimizeForDebug() bool { return false } -func (x *CompileReq) GetDryRun() bool { - if x != nil { - return x.DryRun - } - return false -} - func (x *CompileReq) GetExportDir() string { if x != nil { return x.ExportDir @@ -233,6 +216,13 @@ func (x *CompileReq) GetClean() bool { return false } +func (x *CompileReq) GetExportBinaries() bool { + if x != nil { + return x.ExportBinaries + } + return false +} + type CompileResp struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -295,7 +285,7 @@ var file_commands_compile_proto_rawDesc = []byte{ 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x17, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x1a, 0x15, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, - 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xea, 0x04, 0x0a, 0x0a, 0x43, 0x6f, 0x6d, + 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xd7, 0x04, 0x0a, 0x0a, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x12, 0x3d, 0x0a, 0x08, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, @@ -321,28 +311,27 @@ var file_commands_compile_proto_rawDesc = []byte{ 0x07, 0x76, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x71, 0x75, 0x69, 0x65, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x71, 0x75, 0x69, 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x69, 0x64, 0x50, 0x69, 0x64, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, - 0x76, 0x69, 0x64, 0x50, 0x69, 0x64, 0x12, 0x22, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, - 0x46, 0x69, 0x6c, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0a, - 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6a, 0x6f, - 0x62, 0x73, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x6a, 0x6f, 0x62, 0x73, 0x12, 0x1c, - 0x0a, 0x09, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, 0x18, 0x0f, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x09, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, 0x12, 0x2a, 0x0a, 0x10, - 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x46, 0x6f, 0x72, 0x44, 0x65, 0x62, 0x75, 0x67, - 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, - 0x46, 0x6f, 0x72, 0x44, 0x65, 0x62, 0x75, 0x67, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x72, 0x79, 0x52, - 0x75, 0x6e, 0x18, 0x11, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x64, 0x72, 0x79, 0x52, 0x75, 0x6e, - 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x64, 0x69, 0x72, 0x18, 0x12, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x44, 0x69, 0x72, 0x12, - 0x14, 0x0a, 0x05, 0x63, 0x6c, 0x65, 0x61, 0x6e, 0x18, 0x13, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, - 0x63, 0x6c, 0x65, 0x61, 0x6e, 0x22, 0x4b, 0x0a, 0x0b, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, - 0x52, 0x65, 0x73, 0x70, 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, 0x73, 0x74, 0x72, 0x65, 0x61, - 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x65, 0x72, 0x72, 0x53, 0x74, 0x72, 0x65, - 0x61, 0x6d, 0x42, 0x2d, 0x5a, 0x2b, 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, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, - 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x76, 0x69, 0x64, 0x50, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6a, 0x6f, 0x62, 0x73, 0x18, 0x0e, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x6a, 0x6f, 0x62, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x6c, 0x69, + 0x62, 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, 0x18, 0x0f, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x6c, + 0x69, 0x62, 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, 0x12, 0x2a, 0x0a, 0x10, 0x6f, 0x70, 0x74, 0x69, + 0x6d, 0x69, 0x7a, 0x65, 0x46, 0x6f, 0x72, 0x44, 0x65, 0x62, 0x75, 0x67, 0x18, 0x10, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x10, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x46, 0x6f, 0x72, 0x44, + 0x65, 0x62, 0x75, 0x67, 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x64, + 0x69, 0x72, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, + 0x44, 0x69, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6c, 0x65, 0x61, 0x6e, 0x18, 0x13, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x05, 0x63, 0x6c, 0x65, 0x61, 0x6e, 0x12, 0x27, 0x0a, 0x0f, 0x65, 0x78, 0x70, + 0x6f, 0x72, 0x74, 0x5f, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x69, 0x65, 0x73, 0x18, 0x14, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0e, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x42, 0x69, 0x6e, 0x61, 0x72, 0x69, + 0x65, 0x73, 0x22, 0x4b, 0x0a, 0x0b, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, + 0x70, 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, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x65, 0x72, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x42, + 0x2d, 0x5a, 0x2b, 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, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/rpc/commands/compile.proto b/rpc/commands/compile.proto index 6b547fd448a..fcba5a0fb41 100644 --- a/rpc/commands/compile.proto +++ b/rpc/commands/compile.proto @@ -34,13 +34,12 @@ message CompileReq { bool verbose = 10; // Turns on verbose mode. bool quiet = 11; // Suppresses almost every output. string vidPid = 12; // VID/PID specific build properties. - string exportFile = 13 [deprecated = true]; // DEPRECATED: use exportDir instead int32 jobs = 14; // The max number of concurrent compiler instances to run (as `make -jx`). If jobs is set to 0, it will use the number of available CPUs as the maximum. repeated string libraries = 15; // List of custom libraries paths separated by commas. bool optimizeForDebug = 16; // Optimize compile output for debug, not for release. - bool dryRun = 17; // When set to `true` the compiled binary will not be copied to the export directory. string export_dir = 18; // Optional: save the build artifacts in this directory, the directory must exist. bool clean = 19; // Optional: cleanup the build folder and do not use any previously cached build + bool export_binaries = 20; // When set to `true` the compiled binary will be copied to the export directory. } message CompileResp { diff --git a/test/test_compile.py b/test/test_compile.py index 8b1b9c9f78e..6e83c74b2ef 100644 --- a/test/test_compile.py +++ b/test/test_compile.py @@ -14,10 +14,13 @@ # a commercial license, send an email to license@arduino.cc. import os import platform +import tempfile +import hashlib +from pathlib import Path import pytest -from .common import running_on_ci, parse_json_traces +from .common import running_on_ci def test_compile_without_fqbn(run_command): @@ -40,39 +43,34 @@ def test_compile_with_simple_sketch(run_command, data_dir, working_dir): run_command("core install arduino:avr") sketch_name = "CompileIntegrationTest" - sketch_path = os.path.join(data_dir, sketch_name) + sketch_path = Path(data_dir, sketch_name) fqbn = "arduino:avr:uno" # Create a test sketch - result = run_command("sketch new {}".format(sketch_path)) + result = run_command(f"sketch new {sketch_path}") assert result.ok - assert "Sketch created in: {}".format(sketch_path) in result.stdout + assert f"Sketch created in: {sketch_path}" in result.stdout # Build sketch for arduino:avr:uno - log_file_name = "compile.log" - log_file_path = os.path.join(data_dir, log_file_name) - result = run_command( - "compile -b {fqbn} {sketch_path} --log-format json --log-file {log_file} --log-level trace".format( - fqbn=fqbn, sketch_path=sketch_path, log_file=log_file_path - ) - ) + result = run_command(f"compile -b {fqbn} {sketch_path}") assert result.ok - # let's test from the logs if the hex file produced by successful compile is moved to our sketch folder - log_json = open(log_file_path, "r") - traces = parse_json_traces(log_json.readlines()) - assert f"Compile {sketch_path} for {fqbn} started" in traces - assert f"Compile {sketch_name} for {fqbn} successful" in traces + # Verifies expected binaries have been built + sketch_path_md5 = hashlib.md5(bytes(sketch_path)).hexdigest().upper() + build_dir = Path(tempfile.gettempdir(), f"arduino-sketch-{sketch_path_md5}") + assert (build_dir / f"{sketch_name}.ino.eep").exists() + assert (build_dir / f"{sketch_name}.ino.elf").exists() + assert (build_dir / f"{sketch_name}.ino.hex").exists() + assert (build_dir / f"{sketch_name}.ino.with_bootloader.bin").exists() + assert (build_dir / f"{sketch_name}.ino.with_bootloader.hex").exists() - # Test the --output-dir flag with absolute path - target = os.path.join(data_dir, "test_dir") - result = run_command( - "compile -b {fqbn} {sketch_path} --output-dir {target}".format( - fqbn=fqbn, sketch_path=sketch_path, target=target - ) - ) - assert result.ok - assert os.path.exists(target) and os.path.isdir(target) + # Verifies binaries are not exported by default to Sketch folder + sketch_build_dir = Path(sketch_path, "build", fqbn.replace(":", ".")) + assert not (sketch_build_dir / f"{sketch_name}.ino.eep").exists() + assert not (sketch_build_dir / f"{sketch_name}.ino.elf").exists() + assert not (sketch_build_dir / f"{sketch_name}.ino.hex").exists() + assert not (sketch_build_dir / f"{sketch_name}.ino.with_bootloader.bin").exists() + assert not (sketch_build_dir / f"{sketch_name}.ino.with_bootloader.hex").exists() @pytest.mark.skipif( @@ -233,3 +231,112 @@ def test_compile_without_precompiled_libraries(run_command, data_dir): "compile -b arduino:mbed:nano33ble {}/libraries/BSEC_Software_Library/examples/basic/".format(data_dir) ) assert result.ok + + +def test_compile_with_output_dir_flag(run_command, data_dir): + # Init the environment explicitly + run_command("core update-index") + + # Download latest AVR + run_command("core install arduino:avr") + + sketch_name = "CompileWithOutputDir" + sketch_path = Path(data_dir, sketch_name) + fqbn = "arduino:avr:uno" + + # Create a test sketch + result = run_command(f"sketch new {sketch_path}") + assert result.ok + assert f"Sketch created in: {sketch_path}" in result.stdout + + # Test the --output-dir flag with absolute path + output_dir = Path(data_dir, "test_dir", "output_dir") + result = run_command(f"compile -b {fqbn} {sketch_path} --output-dir {output_dir}") + assert result.ok + + # Verifies expected binaries have been built + sketch_path_md5 = hashlib.md5(bytes(sketch_path)).hexdigest().upper() + build_dir = Path(tempfile.gettempdir(), f"arduino-sketch-{sketch_path_md5}") + assert (build_dir / f"{sketch_name}.ino.eep").exists() + assert (build_dir / f"{sketch_name}.ino.elf").exists() + assert (build_dir / f"{sketch_name}.ino.hex").exists() + assert (build_dir / f"{sketch_name}.ino.with_bootloader.bin").exists() + assert (build_dir / f"{sketch_name}.ino.with_bootloader.hex").exists() + + # Verifies binaries are exported when --output-dir flag is specified + assert output_dir.exists() + assert output_dir.is_dir() + assert (output_dir / f"{sketch_name}.ino.eep").exists() + assert (output_dir / f"{sketch_name}.ino.elf").exists() + assert (output_dir / f"{sketch_name}.ino.hex").exists() + assert (output_dir / f"{sketch_name}.ino.with_bootloader.bin").exists() + assert (output_dir / f"{sketch_name}.ino.with_bootloader.hex").exists() + + +def test_compile_with_export_binaries_flag(run_command, data_dir): + # Init the environment explicitly + run_command("core update-index") + + # Download latest AVR + run_command("core install arduino:avr") + + sketch_name = "CompileWithExportBinariesFlag" + sketch_path = Path(data_dir, sketch_name) + fqbn = "arduino:avr:uno" + + # Create a test sketch + assert run_command("sketch new {}".format(sketch_path)) + + # Test the --output-dir flag with absolute path + result = run_command(f"compile -b {fqbn} {sketch_path} --export-binaries") + assert result.ok + assert Path(sketch_path, "build").exists() + assert Path(sketch_path, "build").is_dir() + + # Verifies binaries are exported when --export-binaries flag is set + assert (sketch_path / "build" / fqbn.replace(":", ".") / f"{sketch_name}.ino.eep").exists() + assert (sketch_path / "build" / fqbn.replace(":", ".") / f"{sketch_name}.ino.elf").exists() + assert (sketch_path / "build" / fqbn.replace(":", ".") / f"{sketch_name}.ino.hex").exists() + assert (sketch_path / "build" / fqbn.replace(":", ".") / f"{sketch_name}.ino.with_bootloader.bin").exists() + assert (sketch_path / "build" / fqbn.replace(":", ".") / f"{sketch_name}.ino.with_bootloader.hex").exists() + + +def test_compile_with_custom_build_path(run_command, data_dir): + # Init the environment explicitly + run_command("core update-index") + + # Download latest AVR + run_command("core install arduino:avr") + + sketch_name = "CompileWithBuildPath" + sketch_path = Path(data_dir, sketch_name) + fqbn = "arduino:avr:uno" + + # Create a test sketch + result = run_command(f"sketch new {sketch_path}") + assert result.ok + assert f"Sketch created in: {sketch_path}" in result.stdout + + # Test the --build-path flag with absolute path + build_path = Path(data_dir, "test_dir", "build_dir") + result = run_command(f"compile -b {fqbn} {sketch_path} --build-path {build_path}") + print(result.stderr) + assert result.ok + + # Verifies expected binaries have been built to build_path + assert build_path.exists() + assert build_path.is_dir() + assert (build_path / f"{sketch_name}.ino.eep").exists() + assert (build_path / f"{sketch_name}.ino.elf").exists() + assert (build_path / f"{sketch_name}.ino.hex").exists() + assert (build_path / f"{sketch_name}.ino.with_bootloader.bin").exists() + assert (build_path / f"{sketch_name}.ino.with_bootloader.hex").exists() + + # Verifies there are no binaries in temp directory + sketch_path_md5 = hashlib.md5(bytes(sketch_path)).hexdigest().upper() + build_dir = Path(tempfile.gettempdir(), f"arduino-sketch-{sketch_path_md5}") + assert not (build_dir / f"{sketch_name}.ino.eep").exists() + assert not (build_dir / f"{sketch_name}.ino.elf").exists() + assert not (build_dir / f"{sketch_name}.ino.hex").exists() + assert not (build_dir / f"{sketch_name}.ino.with_bootloader.bin").exists() + assert not (build_dir / f"{sketch_name}.ino.with_bootloader.hex").exists() diff --git a/test/test_core.py b/test/test_core.py index 51d3bab80bf..dcea5f552c3 100644 --- a/test/test_core.py +++ b/test/test_core.py @@ -16,6 +16,8 @@ import platform import pytest import simplejson as json +import tempfile +import hashlib from pathlib import Path @@ -160,17 +162,18 @@ def test_core_install_without_updateindex(run_command): def test_core_install_esp32(run_command, data_dir): # update index url = "https://dl.espressif.com/dl/package_esp32_index.json" - assert run_command("core update-index --additional-urls={}".format(url)) + assert run_command(f"core update-index --additional-urls={url}") # install 3rd-party core - assert run_command("core install esp32:esp32@1.0.4 --additional-urls={}".format(url)) + assert run_command(f"core install esp32:esp32@1.0.4 --additional-urls={url}") # create a sketch and compile to double check the core was successfully installed - sketch_path = os.path.join(data_dir, "test_core_install_esp32") - assert run_command("sketch new {}".format(sketch_path)) - assert run_command("compile -b esp32:esp32:esp32 {}".format(sketch_path)) + sketch_name = "test_core_install_esp32" + sketch_path = os.path.join(data_dir, sketch_name) + assert run_command(f"sketch new {sketch_path}") + assert run_command(f"compile -b esp32:esp32:esp32 {sketch_path}") # prevent regressions for https://github.com/arduino/arduino-cli/issues/163 - assert os.path.exists( - os.path.join(sketch_path, "build/esp32.esp32.esp32/test_core_install_esp32.ino.partitions.bin",) - ) + sketch_path_md5 = hashlib.md5(sketch_path.encode()).hexdigest().upper() + build_dir = Path(tempfile.gettempdir(), f"arduino-sketch-{sketch_path_md5}") + assert (build_dir / f"{sketch_name}.ino.partitions.bin").exists() def test_core_download(run_command, downloads_dir): diff --git a/test/test_upload.py b/test/test_upload.py index 32cf98071ab..60d0149dde4 100644 --- a/test/test_upload.py +++ b/test/test_upload.py @@ -13,7 +13,7 @@ # software without disclosing the source code of your own applications. To purchase # a commercial license, send an email to license@arduino.cc. import os -import time +from pathlib import Path import pytest @@ -28,36 +28,75 @@ def test_upload(run_command, data_dir, detected_boards): run_command("core update-index") for board in detected_boards: - # Download core + # Download platform run_command(f"core install {board.core}") # Create a sketch - sketch_name = "foo" - sketch_path = os.path.join(data_dir, sketch_name) + sketch_name = f"TestUploadSketch{board.id}" + sketch_path = Path(data_dir, sketch_name) fqbn = board.fqbn address = board.address assert run_command(f"sketch new {sketch_path}") # Build sketch assert run_command(f"compile -b {fqbn} {sketch_path}") + + # Verifies binaries are not exported + assert not (sketch_path / "build").exists() + # Upload without port must fail - result = run_command(f"upload -b {fqbn} {sketch_path}") - assert result.failed + assert not run_command(f"upload -b {fqbn} {sketch_path}") + # Upload - res = run_command(f"upload -b {fqbn} -p {address} {sketch_path}") - print(res.stderr) - assert res - - # multiple uploads requires some pauses - time.sleep(2) - # Upload using --input-dir reusing standard sketch "build" folder artifacts - fqbn_path = fqbn.replace(":", ".") - assert run_command(f"upload -b {fqbn} -p {address} --input-dir {sketch_path}/build/{fqbn_path} {sketch_path}") - - # multiple uploads requires some pauses - time.sleep(2) - # Upload using --input-file reusing standard sketch "build" folder artifacts - assert run_command( - f"upload -b {fqbn} -p {address} --input-file {sketch_path}/build/{fqbn_path}/{sketch_name}.ino.bin" - ) + assert run_command(f"upload -b {fqbn} -p {address} {sketch_path}") + + +def test_upload_with_input_dir_flag(run_command, data_dir, detected_boards): + # Init the environment explicitly + run_command("core update-index") + + for board in detected_boards: + # Download board platform + run_command(f"core install {board.core}") + + # Create sketch + sketch_name = f"TestUploadInputDirSketch{board.id}" + sketch_path = Path(data_dir, sketch_name) + fqbn = board.fqbn + address = board.address + assert run_command(f"sketch new {sketch_path}") + + # Build sketch and export binaries to custom directory + output_dir = Path(data_dir, "test_dir", sketch_name, "build") + assert run_command(f"compile -b {fqbn} {sketch_path} --output-dir {output_dir}") + + # Upload with --input-dir flag + assert run_command(f"upload -b {fqbn} -p {address} --input-dir {output_dir} {sketch_path}") + + +def test_upload_with_input_file_flag(run_command, data_dir, detected_boards): + # Init the environment explicitly + run_command("core update-index") + + for board in detected_boards: + # Download board platform + run_command(f"core install {board.core}") + + # Create sketch + sketch_name = f"TestUploadInputFileSketch{board.id}" + sketch_path = Path(data_dir, sketch_name) + fqbn = board.fqbn + address = board.address + assert run_command(f"sketch new {sketch_path}") + + # Build sketch and export binaries to custom directory + output_dir = Path(data_dir, "test_dir", sketch_name, "build") + assert run_command(f"compile -b {fqbn} {sketch_path} --output-dir {output_dir}") + + # We don't need a specific file when using the --input-file flag to upload since + # it's just used to calculate the directory, so it's enough to get a random file + # that's inside that directory + input_file = next(output_dir.glob(f"{sketch_name}.ino.*")) + # Upload using --input-file + assert run_command(f"upload -b {fqbn} -p {address} --input-file {input_file}") def test_upload_after_attach(run_command, data_dir, detected_boards): @@ -117,3 +156,42 @@ def run_test(s): run_test(sketch_path) run_test(sketch_main_file) + + +def test_compile_and_upload_combo_with_custom_build_path(run_command, data_dir, detected_boards, wait_for_board): + # Init the environment explicitly + run_command("core update-index") + + # Install required core(s) + run_command("core install arduino:avr@1.8.3") + run_command("core install arduino:samd@1.8.6") + + sketch_name = "CompileAndUploadCustomBuildPathIntegrationTest" + sketch_path = Path(data_dir, sketch_name) + assert run_command(f"sketch new {sketch_path}") + + for board in detected_boards: + fqbn_normalized = board.fqbn.replace(":", "-") + log_file_name = f"{fqbn_normalized}-compile.log" + log_file = Path(data_dir, log_file_name) + command_log_flags = f"--log-format json --log-file {log_file} --log-level trace" + + wait_for_board() + + build_path = Path(data_dir, "test_dir", fqbn_normalized, "build_dir") + result = run_command( + f"compile -b {board.fqbn} " + + f"--upload -p {board.address} " + + f"--build-path {build_path} " + + f"{sketch_path} {command_log_flags}" + ) + print(result.stderr) + assert result.ok + + # check from the logs if the bin file were uploaded on the current board + log_json = open(log_file, "r") + traces = parse_json_traces(log_json.readlines()) + assert f"Compile {sketch_path} for {board.fqbn} started" in traces + assert f"Compile {sketch_name} for {board.fqbn} successful" in traces + assert f"Upload {sketch_path} on {board.fqbn} started" in traces + assert "Upload successful" in traces