Skip to content

Commit ebc28e1

Browse files
authored
Compile extract all artifacts in "sketch/build" folder (#687)
* Deprecated exportFile/importFile in favor of exportDir/importDir * Updated compile/upload cli commands * Compile now saves artifacts in 'sketch/build/<FQBN>/...' folder * Upload now uses export folder * Test fix: use --output-dir option instead of deprecated --output * Text fix: no more need to check if "extension won't be added if already present" * Added Debug.ImportDir and deprecated Debug.ImportFile * Upload now uses export folder * Fixed GetCommandLine test * Fixed test_core_install_esp32
1 parent c387167 commit ebc28e1

File tree

20 files changed

+345
-237
lines changed

20 files changed

+345
-237
lines changed

Diff for: cli/compile/compile.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ var (
4646
uploadAfterCompile bool // Upload the binary after the compilation.
4747
port string // Upload port, e.g.: COM10 or /dev/ttyACM0.
4848
verify bool // Upload, verify uploaded binary after the upload.
49-
exportFile string // The compiled binary is written to this file
49+
exportDir string // The compiled binary is written to this file
5050
dryRun bool // Use this flag to now write the output file
5151
libraries []string // List of custom libraries paths separated by commas. Or can be used multiple times for multiple libraries paths.
5252
optimizeForDebug bool // Optimize compile output for debug, not for release
@@ -67,7 +67,7 @@ func NewCommand() *cobra.Command {
6767
command.Flags().BoolVar(&showProperties, "show-properties", false, "Show all build properties used instead of compiling.")
6868
command.Flags().BoolVar(&preprocess, "preprocess", false, "Print preprocessed code to stdout instead of compiling.")
6969
command.Flags().StringVar(&buildCachePath, "build-cache-path", "", "Builds of 'core.a' are saved into this path to be cached and reused.")
70-
command.Flags().StringVarP(&exportFile, "output", "o", "", "Filename of the compile output.")
70+
command.Flags().StringVarP(&exportDir, "output-dir", "", "", "Save build artifacts in this directory.")
7171
command.Flags().BoolVarP(&dryRun, "dry-run", "n", false, "Perform the build but do not copy the compile output file.")
7272
command.Flags().StringVar(&buildPath, "build-path", "",
7373
"Path where to save compiled files. If omitted, a directory will be created in the default temporary path of your OS.")
@@ -115,7 +115,7 @@ func run(cmd *cobra.Command, args []string) {
115115
Verbose: verbose,
116116
Quiet: quiet,
117117
VidPid: vidPid,
118-
ExportFile: exportFile,
118+
ExportDir: exportDir,
119119
DryRun: dryRun,
120120
Libraries: libraries,
121121
OptimizeForDebug: optimizeForDebug,
@@ -134,7 +134,7 @@ func run(cmd *cobra.Command, args []string) {
134134
Port: port,
135135
Verbose: verbose,
136136
Verify: verify,
137-
ImportFile: exportFile,
137+
ImportDir: exportDir,
138138
}, os.Stdout, os.Stderr)
139139

140140
if err != nil {

Diff for: cli/debug/debug.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ var (
3737
verbose bool
3838
verify bool
3939
interpreter string
40-
importFile string
40+
importDir string
4141
)
4242

4343
// NewCommand created a new `upload` command
@@ -54,7 +54,7 @@ func NewCommand() *cobra.Command {
5454
debugCommand.Flags().StringVarP(&fqbn, "fqbn", "b", "", "Fully Qualified Board Name, e.g.: arduino:avr:uno")
5555
debugCommand.Flags().StringVarP(&port, "port", "p", "", "Debug port, e.g.: COM10 or /dev/ttyACM0")
5656
debugCommand.Flags().StringVar(&interpreter, "interpreter", "console", "Debug interpreter e.g.: console, mi, mi1, mi2, mi3")
57-
debugCommand.Flags().StringVarP(&importFile, "input", "i", "", "Input file to be uploaded for debug.")
57+
debugCommand.Flags().StringVarP(&importDir, "input-dir", "", "", "Direcory containing binaries for debug.")
5858

5959
return debugCommand
6060
}
@@ -82,7 +82,7 @@ func run(command *cobra.Command, args []string) {
8282
SketchPath: sketchPath.String(),
8383
Port: port,
8484
Interpreter: interpreter,
85-
ImportFile: importFile,
85+
ImportDir: importDir,
8686
}, os.Stdin, os.Stdout, ctrlc); err != nil {
8787
feedback.Errorf("Error during Debug: %v", err)
8888
os.Exit(errorcodes.ErrGeneric)

Diff for: cli/upload/upload.go

+7-7
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,11 @@ import (
3030
)
3131

3232
var (
33-
fqbn string
34-
port string
35-
verbose bool
36-
verify bool
37-
importFile string
33+
fqbn string
34+
port string
35+
verbose bool
36+
verify bool
37+
importDir string
3838
)
3939

4040
// NewCommand created a new `upload` command
@@ -50,7 +50,7 @@ func NewCommand() *cobra.Command {
5050

5151
uploadCommand.Flags().StringVarP(&fqbn, "fqbn", "b", "", "Fully Qualified Board Name, e.g.: arduino:avr:uno")
5252
uploadCommand.Flags().StringVarP(&port, "port", "p", "", "Upload port, e.g.: COM10 or /dev/ttyACM0")
53-
uploadCommand.Flags().StringVarP(&importFile, "input", "i", "", "Input file to be uploaded.")
53+
uploadCommand.Flags().StringVarP(&importDir, "input-dir", "", "", "Direcory containing binaries to upload.")
5454
uploadCommand.Flags().BoolVarP(&verify, "verify", "t", false, "Verify uploaded binary after the upload.")
5555
uploadCommand.Flags().BoolVarP(&verbose, "verbose", "v", false, "Optional, turns on verbose mode.")
5656

@@ -77,7 +77,7 @@ func run(command *cobra.Command, args []string) {
7777
Port: port,
7878
Verbose: verbose,
7979
Verify: verify,
80-
ImportFile: importFile,
80+
ImportDir: importDir,
8181
}, os.Stdout, os.Stderr); err != nil {
8282
feedback.Errorf("Error during Upload: %v", err)
8383
os.Exit(errorcodes.ErrGeneric)

Diff for: commands/compile/compile.go

+33-44
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ package compile
1717

1818
import (
1919
"context"
20-
"errors"
2120
"fmt"
2221
"io"
2322
"path/filepath"
@@ -37,6 +36,7 @@ import (
3736
"github.com/arduino/arduino-cli/telemetry"
3837
paths "github.com/arduino/go-paths-helper"
3938
properties "github.com/arduino/go-properties-orderedmap"
39+
"github.com/pkg/errors"
4040
"github.com/segmentio/stats/v4"
4141
"github.com/sirupsen/logrus"
4242
"github.com/spf13/viper"
@@ -55,11 +55,16 @@ func Compile(ctx context.Context, req *rpc.CompileReq, outStream, errStream io.W
5555
"verbose": strconv.FormatBool(req.Verbose),
5656
"quiet": strconv.FormatBool(req.Quiet),
5757
"vidPid": req.VidPid,
58-
"exportFile": telemetry.Sanitize(req.ExportFile),
58+
"exportFile": telemetry.Sanitize(req.ExportFile), // deprecated
59+
"exportDir": telemetry.Sanitize(req.GetExportDir()),
5960
"jobs": strconv.FormatInt(int64(req.Jobs), 10),
6061
"libraries": strings.Join(req.Libraries, ","),
6162
}
6263

64+
if req.GetExportFile() != "" {
65+
outStream.Write([]byte(fmt.Sprintln("Compile.ExportFile has been deprecated. The ExportFile parameter will be ignored, use ExportDir instead.")))
66+
}
67+
6368
// Use defer func() to evaluate tags map when function returns
6469
// and set success flag inspecting the error named return parameter
6570
defer func() {
@@ -197,54 +202,38 @@ func Compile(ctx context.Context, req *rpc.CompileReq, outStream, errStream io.W
197202
}
198203

199204
if !req.GetDryRun() {
200-
// FIXME: Make a function to obtain these info...
201-
outputPath := paths.New(
202-
builderCtx.BuildProperties.ExpandPropsInString("{build.path}/{recipe.output.tmp_file}")) // "/build/path/sketch.ino.bin"
203-
ext := outputPath.Ext() // ".hex" | ".bin"
204-
base := outputPath.Base() // "sketch.ino.hex"
205-
base = base[:len(base)-len(ext)] // "sketch.ino"
206-
207-
// FIXME: Make a function to produce a better name...
208-
// Make the filename without the FQBN configs part
209-
fqbnSuffix := strings.Replace(fqbn.StringWithoutConfig(), ":", ".", -1)
210-
211205
var exportPath *paths.Path
212-
var exportFile string
213-
if req.GetExportFile() == "" {
214-
exportPath = sketch.FullPath
215-
exportFile = sketch.Name + "." + fqbnSuffix // "sketch.arduino.avr.uno"
206+
if exportDir := req.GetExportDir(); exportDir != "" {
207+
exportPath = paths.New(exportDir)
216208
} else {
217-
exportPath = paths.New(req.GetExportFile()).Parent()
218-
exportFile = paths.New(req.GetExportFile()).Base()
219-
if strings.HasSuffix(exportFile, ext) {
220-
exportFile = exportFile[:len(exportFile)-len(ext)]
221-
}
209+
exportPath = sketch.FullPath
210+
// Add FQBN (without configs part) to export path
211+
fqbnSuffix := strings.Replace(fqbn.StringWithoutConfig(), ":", ".", -1)
212+
exportPath = exportPath.Join("build").Join(fqbnSuffix)
213+
}
214+
logrus.WithField("path", exportPath).Trace("Saving sketch to export path.")
215+
if err := exportPath.MkdirAll(); err != nil {
216+
return nil, errors.Wrap(err, "creating output dir")
222217
}
223218

224-
// Copy "sketch.ino.*.hex" / "sketch.ino.*.bin" artifacts to sketch directory
225-
srcDir, err := outputPath.Parent().ReadDir() // read "/build/path/*"
226-
if err != nil {
227-
return nil, fmt.Errorf("reading build directory: %s", err)
219+
// Copy all "sketch.ino.*" artifacts to the export directory
220+
baseName, ok := builderCtx.BuildProperties.GetOk("build.project_name") // == "sketch.ino"
221+
if !ok {
222+
return nil, errors.New("missing 'build.project_name' build property")
228223
}
229-
srcDir.FilterPrefix(base + ".")
230-
srcDir.FilterSuffix(ext)
231-
for _, srcOutput := range srcDir {
232-
srcFilename := srcOutput.Base() // "sketch.ino.*.bin"
233-
srcFilename = srcFilename[len(base):] // ".*.bin"
234-
dstOutput := exportPath.Join(exportFile + srcFilename)
235-
logrus.WithField("from", srcOutput).WithField("to", dstOutput).Debug("copying sketch build output")
236-
if err = srcOutput.CopyTo(dstOutput); err != nil {
237-
return nil, fmt.Errorf("copying output file: %s", err)
238-
}
224+
buildFiles, err := builderCtx.BuildPath.ReadDir()
225+
if err != nil {
226+
return nil, errors.Errorf("reading build directory: %s", err)
239227
}
240-
241-
// Copy .elf file to sketch directory
242-
srcElf := outputPath.Parent().Join(base + ".elf")
243-
if srcElf.Exist() {
244-
dstElf := exportPath.Join(exportFile + ".elf")
245-
logrus.WithField("from", srcElf).WithField("to", dstElf).Debug("copying sketch build output")
246-
if err = srcElf.CopyTo(dstElf); err != nil {
247-
return nil, fmt.Errorf("copying elf file: %s", err)
228+
buildFiles.FilterPrefix(baseName)
229+
for _, buildFile := range buildFiles {
230+
exportedFile := exportPath.Join(buildFile.Base())
231+
logrus.
232+
WithField("src", buildFile).
233+
WithField("dest", exportedFile).
234+
Trace("Copying artifact.")
235+
if err = buildFile.CopyTo(exportedFile); err != nil {
236+
return nil, errors.Wrapf(err, "copying output file %s", buildFile)
248237
}
249238
}
250239
}

Diff for: commands/debug/debug.go

+16-28
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,10 @@ func Debug(ctx context.Context, req *dbg.DebugConfigReq, inStream io.Reader, out
115115

116116
// getCommandLine compose a debug command represented by a core recipe
117117
func getCommandLine(req *dbg.DebugConfigReq, pm *packagemanager.PackageManager) ([]string, error) {
118+
if req.GetImportFile() != "" {
119+
return nil, errors.New("the ImportFile parameter has been deprecated, use ImportDir instead")
120+
}
121+
118122
// TODO: make a generic function to extract sketch from request
119123
// and remove duplication in commands/compile.go
120124
if req.GetSketchPath() == "" {
@@ -185,40 +189,24 @@ func getCommandLine(req *dbg.DebugConfigReq, pm *packagemanager.PackageManager)
185189
}
186190
}
187191

188-
// Set path to compiled binary
189-
// Make the filename without the FQBN configs part
190-
fqbn.Configs = properties.NewMap()
191-
fqbnSuffix := strings.Replace(fqbn.String(), ":", ".", -1)
192-
193192
var importPath *paths.Path
194-
var importFile string
195-
if req.GetImportFile() == "" {
196-
importPath = sketch.FullPath
197-
importFile = sketch.Name + "." + fqbnSuffix
193+
if importDir := req.GetImportDir(); importDir != "" {
194+
importPath = paths.New(importDir)
198195
} else {
199-
importPath = paths.New(req.GetImportFile()).Parent()
200-
importFile = paths.New(req.GetImportFile()).Base()
196+
// TODO: Create a function to obtain importPath from sketch
197+
importPath = sketch.FullPath
198+
// Add FQBN (without configs part) to export path
199+
fqbnSuffix := strings.Replace(fqbn.StringWithoutConfig(), ":", ".", -1)
200+
importPath = importPath.Join("build").Join(fqbnSuffix)
201201
}
202-
203-
outputTmpFile, ok := toolProperties.GetOk("recipe.output.tmp_file")
204-
outputTmpFile = toolProperties.ExpandPropsInString(outputTmpFile)
205-
if !ok {
206-
return nil, fmt.Errorf("property 'recipe.output.tmp_file' not defined")
202+
if !importPath.Exist() {
203+
return nil, fmt.Errorf("compiled sketch not found in %s", importPath)
207204
}
208-
ext := filepath.Ext(outputTmpFile)
209-
if strings.HasSuffix(importFile, ext) {
210-
importFile = importFile[:len(importFile)-len(ext)]
205+
if !importPath.IsDir() {
206+
return nil, fmt.Errorf("expected compiled sketch in directory %s, but is a file instead", importPath)
211207
}
212-
213208
toolProperties.SetPath("build.path", importPath)
214-
toolProperties.Set("build.project_name", importFile)
215-
uploadFile := importPath.Join(importFile + ext)
216-
if _, err := uploadFile.Stat(); err != nil {
217-
if os.IsNotExist(err) {
218-
return nil, fmt.Errorf("compiled sketch %s not found", uploadFile.String())
219-
}
220-
return nil, errors.Wrap(err, "cannot open sketch")
221-
}
209+
toolProperties.Set("build.project_name", sketch.Name+".ino")
222210

223211
// Set debug port property
224212
port := req.GetPort()

Diff for: commands/debug/debug_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ func TestGetCommandLine(t *testing.T) {
5656
fmt.Sprintf(" %s/arduino-test/tools/openocd/0.10.0-arduino7/bin/openocd%s", dataDir, toolExtension) +
5757
fmt.Sprintf(" -s \"%s/arduino-test/tools/openocd/0.10.0-arduino7/share/openocd/scripts/\"", dataDir) +
5858
fmt.Sprintf(" --file \"%s/arduino-test/samd/variants/arduino_zero/openocd_scripts/arduino_zero.cfg\"", customHardware) +
59-
fmt.Sprintf(" -c \"gdb_port pipe\" -c \"telnet_port 0\" -c init -c halt %s/hello.arduino-test.samd.arduino_zero_edbg.elf", sketchPath)
59+
fmt.Sprintf(" -c \"gdb_port pipe\" -c \"telnet_port 0\" -c init -c halt %s/build/arduino-test.samd.arduino_zero_edbg/hello.ino.elf", sketchPath)
6060

6161
command, err := getCommandLine(req, pm)
6262
assert.Nil(t, err)
@@ -77,7 +77,7 @@ func TestGetCommandLine(t *testing.T) {
7777
fmt.Sprintf(" %s/arduino-test/tools/openocd/0.10.0-arduino7/bin/openocd%s", dataDir, toolExtension) +
7878
fmt.Sprintf(" -s \"%s/arduino-test/tools/openocd/0.10.0-arduino7/share/openocd/scripts/\"", dataDir) +
7979
fmt.Sprintf(" --file \"%s/arduino-test/samd/variants/mkr1000/openocd_scripts/arduino_zero.cfg\"", customHardware) +
80-
fmt.Sprintf(" -c \"gdb_port pipe\" -c \"telnet_port 0\" -c init -c halt %s/hello.arduino-test.samd.mkr1000.elf", sketchPath)
80+
fmt.Sprintf(" -c \"gdb_port pipe\" -c \"telnet_port 0\" -c init -c halt %s/build/arduino-test.samd.mkr1000/hello.ino.elf", sketchPath)
8181

8282
command2, err := getCommandLine(req2, pm)
8383
assert.Nil(t, err)

Diff for: commands/upload/upload.go

+12-29
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@ import (
2020
"fmt"
2121
"io"
2222
"net/url"
23-
"os"
24-
"path/filepath"
2523
"strings"
2624
"time"
2725

@@ -147,40 +145,25 @@ func Upload(ctx context.Context, req *rpc.UploadReq, outStream io.Writer, errStr
147145
uploadProperties.Set("upload.verify", uploadProperties.Get("upload.params.noverify"))
148146
}
149147

150-
// Set path to compiled binary
151-
// Make the filename without the FQBN configs part
152-
fqbn.Configs = properties.NewMap()
153-
fqbnSuffix := strings.Replace(fqbn.String(), ":", ".", -1)
154-
155148
var importPath *paths.Path
156-
var importFile string
157-
if req.GetImportFile() == "" {
158-
importPath = sketch.FullPath
159-
importFile = sketch.Name + "." + fqbnSuffix
149+
if importDir := req.GetImportDir(); importDir != "" {
150+
importPath = paths.New(importDir)
160151
} else {
161-
importPath = paths.New(req.GetImportFile()).Parent()
162-
importFile = paths.New(req.GetImportFile()).Base()
152+
// TODO: Create a function to obtain importPath from sketch
153+
importPath = sketch.FullPath
154+
// Add FQBN (without configs part) to export path
155+
fqbnSuffix := strings.Replace(fqbn.StringWithoutConfig(), ":", ".", -1)
156+
importPath = importPath.Join("build").Join(fqbnSuffix)
163157
}
164158

165-
outputTmpFile, ok := uploadProperties.GetOk("recipe.output.tmp_file")
166-
outputTmpFile = uploadProperties.ExpandPropsInString(outputTmpFile)
167-
if !ok {
168-
return nil, fmt.Errorf("property 'recipe.output.tmp_file' not defined")
159+
if !importPath.Exist() {
160+
return nil, fmt.Errorf("compiled sketch not found in %s", importPath)
169161
}
170-
ext := filepath.Ext(outputTmpFile)
171-
if strings.HasSuffix(importFile, ext) {
172-
importFile = importFile[:len(importFile)-len(ext)]
162+
if !importPath.IsDir() {
163+
return nil, fmt.Errorf("expected compiled sketch in directory %s, but is a file instead", importPath)
173164
}
174-
175165
uploadProperties.SetPath("build.path", importPath)
176-
uploadProperties.Set("build.project_name", importFile)
177-
uploadFile := importPath.Join(importFile + ext)
178-
if _, err := uploadFile.Stat(); err != nil {
179-
if os.IsNotExist(err) {
180-
return nil, fmt.Errorf("compiled sketch %s not found", uploadFile.String())
181-
}
182-
return nil, fmt.Errorf("cannot open sketch: %s", err)
183-
}
166+
uploadProperties.Set("build.project_name", sketch.Name+".ino")
184167

185168
// Perform reset via 1200bps touch if requested
186169
if uploadProperties.GetBoolean("upload.use_1200bps_touch") {

0 commit comments

Comments
 (0)