Skip to content

Compile extract all artifacts #687

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
May 15, 2020
8 changes: 4 additions & 4 deletions cli/compile/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ var (
uploadAfterCompile bool // Upload the binary after the compilation.
port string // Upload port, e.g.: COM10 or /dev/ttyACM0.
verify bool // Upload, verify uploaded binary after the upload.
exportFile string // The compiled binary is written to this file
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
Expand All @@ -67,7 +67,7 @@ func NewCommand() *cobra.Command {
command.Flags().BoolVar(&showProperties, "show-properties", false, "Show all build properties used instead of compiling.")
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(&exportFile, "output", "o", "", "Filename of the compile output.")
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.")
Expand Down Expand Up @@ -115,7 +115,7 @@ func run(cmd *cobra.Command, args []string) {
Verbose: verbose,
Quiet: quiet,
VidPid: vidPid,
ExportFile: exportFile,
ExportDir: exportDir,
DryRun: dryRun,
Libraries: libraries,
OptimizeForDebug: optimizeForDebug,
Expand All @@ -134,7 +134,7 @@ func run(cmd *cobra.Command, args []string) {
Port: port,
Verbose: verbose,
Verify: verify,
ImportFile: exportFile,
ImportDir: exportDir,
}, os.Stdout, os.Stderr)

if err != nil {
Expand Down
6 changes: 3 additions & 3 deletions cli/debug/debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ var (
verbose bool
verify bool
interpreter string
importFile string
importDir string
)

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

return debugCommand
}
Expand Down Expand Up @@ -82,7 +82,7 @@ func run(command *cobra.Command, args []string) {
SketchPath: sketchPath.String(),
Port: port,
Interpreter: interpreter,
ImportFile: importFile,
ImportDir: importDir,
}, os.Stdin, os.Stdout, ctrlc); err != nil {
feedback.Errorf("Error during Debug: %v", err)
os.Exit(errorcodes.ErrGeneric)
Expand Down
14 changes: 7 additions & 7 deletions cli/upload/upload.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ import (
)

var (
fqbn string
port string
verbose bool
verify bool
importFile string
fqbn string
port string
verbose bool
verify bool
importDir string
)

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

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

Expand All @@ -77,7 +77,7 @@ func run(command *cobra.Command, args []string) {
Port: port,
Verbose: verbose,
Verify: verify,
ImportFile: importFile,
ImportDir: importDir,
}, os.Stdout, os.Stderr); err != nil {
feedback.Errorf("Error during Upload: %v", err)
os.Exit(errorcodes.ErrGeneric)
Expand Down
77 changes: 33 additions & 44 deletions commands/compile/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ package compile

import (
"context"
"errors"
"fmt"
"io"
"path/filepath"
Expand All @@ -37,6 +36,7 @@ import (
"github.com/arduino/arduino-cli/telemetry"
paths "github.com/arduino/go-paths-helper"
properties "github.com/arduino/go-properties-orderedmap"
"github.com/pkg/errors"
"github.com/segmentio/stats/v4"
"github.com/sirupsen/logrus"
"github.com/spf13/viper"
Expand All @@ -55,11 +55,16 @@ 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),
"exportFile": telemetry.Sanitize(req.ExportFile), // deprecated
"exportDir": telemetry.Sanitize(req.GetExportDir()),
"jobs": strconv.FormatInt(int64(req.Jobs), 10),
"libraries": strings.Join(req.Libraries, ","),
}

if req.GetExportFile() != "" {
outStream.Write([]byte(fmt.Sprintln("Compile.ExportFile has been deprecated. The ExportFile parameter will be ignored, use ExportDir instead.")))
}

// Use defer func() to evaluate tags map when function returns
// and set success flag inspecting the error named return parameter
defer func() {
Expand Down Expand Up @@ -197,54 +202,38 @@ func Compile(ctx context.Context, req *rpc.CompileReq, outStream, errStream io.W
}

if !req.GetDryRun() {
// FIXME: Make a function to obtain these info...
outputPath := paths.New(
builderCtx.BuildProperties.ExpandPropsInString("{build.path}/{recipe.output.tmp_file}")) // "/build/path/sketch.ino.bin"
ext := outputPath.Ext() // ".hex" | ".bin"
base := outputPath.Base() // "sketch.ino.hex"
base = base[:len(base)-len(ext)] // "sketch.ino"

// FIXME: Make a function to produce a better name...
// Make the filename without the FQBN configs part
fqbnSuffix := strings.Replace(fqbn.StringWithoutConfig(), ":", ".", -1)

var exportPath *paths.Path
var exportFile string
if req.GetExportFile() == "" {
exportPath = sketch.FullPath
exportFile = sketch.Name + "." + fqbnSuffix // "sketch.arduino.avr.uno"
if exportDir := req.GetExportDir(); exportDir != "" {
exportPath = paths.New(exportDir)
} else {
exportPath = paths.New(req.GetExportFile()).Parent()
exportFile = paths.New(req.GetExportFile()).Base()
if strings.HasSuffix(exportFile, ext) {
exportFile = exportFile[:len(exportFile)-len(ext)]
}
exportPath = sketch.FullPath
// Add FQBN (without configs part) to export path
fqbnSuffix := strings.Replace(fqbn.StringWithoutConfig(), ":", ".", -1)
exportPath = exportPath.Join("build").Join(fqbnSuffix)
}
logrus.WithField("path", exportPath).Trace("Saving sketch to export path.")
if err := exportPath.MkdirAll(); err != nil {
return nil, errors.Wrap(err, "creating output dir")
}

// Copy "sketch.ino.*.hex" / "sketch.ino.*.bin" artifacts to sketch directory
srcDir, err := outputPath.Parent().ReadDir() // read "/build/path/*"
if err != nil {
return nil, fmt.Errorf("reading build directory: %s", err)
// Copy all "sketch.ino.*" artifacts to the export directory
baseName, ok := builderCtx.BuildProperties.GetOk("build.project_name") // == "sketch.ino"
if !ok {
return nil, errors.New("missing 'build.project_name' build property")
}
srcDir.FilterPrefix(base + ".")
srcDir.FilterSuffix(ext)
for _, srcOutput := range srcDir {
srcFilename := srcOutput.Base() // "sketch.ino.*.bin"
srcFilename = srcFilename[len(base):] // ".*.bin"
dstOutput := exportPath.Join(exportFile + srcFilename)
logrus.WithField("from", srcOutput).WithField("to", dstOutput).Debug("copying sketch build output")
if err = srcOutput.CopyTo(dstOutput); err != nil {
return nil, fmt.Errorf("copying output file: %s", err)
}
buildFiles, err := builderCtx.BuildPath.ReadDir()
if err != nil {
return nil, errors.Errorf("reading build directory: %s", err)
}

// Copy .elf file to sketch directory
srcElf := outputPath.Parent().Join(base + ".elf")
if srcElf.Exist() {
dstElf := exportPath.Join(exportFile + ".elf")
logrus.WithField("from", srcElf).WithField("to", dstElf).Debug("copying sketch build output")
if err = srcElf.CopyTo(dstElf); err != nil {
return nil, fmt.Errorf("copying elf file: %s", err)
buildFiles.FilterPrefix(baseName)
for _, buildFile := range buildFiles {
exportedFile := exportPath.Join(buildFile.Base())
logrus.
WithField("src", buildFile).
WithField("dest", exportedFile).
Trace("Copying artifact.")
if err = buildFile.CopyTo(exportedFile); err != nil {
return nil, errors.Wrapf(err, "copying output file %s", buildFile)
}
}
}
Expand Down
44 changes: 16 additions & 28 deletions commands/debug/debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,10 @@ func Debug(ctx context.Context, req *dbg.DebugConfigReq, inStream io.Reader, out

// getCommandLine compose a debug command represented by a core recipe
func getCommandLine(req *dbg.DebugConfigReq, pm *packagemanager.PackageManager) ([]string, error) {
if req.GetImportFile() != "" {
return nil, errors.New("the ImportFile parameter has been deprecated, use ImportDir instead")
}

// TODO: make a generic function to extract sketch from request
// and remove duplication in commands/compile.go
if req.GetSketchPath() == "" {
Expand Down Expand Up @@ -185,40 +189,24 @@ func getCommandLine(req *dbg.DebugConfigReq, pm *packagemanager.PackageManager)
}
}

// Set path to compiled binary
// Make the filename without the FQBN configs part
fqbn.Configs = properties.NewMap()
fqbnSuffix := strings.Replace(fqbn.String(), ":", ".", -1)

var importPath *paths.Path
var importFile string
if req.GetImportFile() == "" {
importPath = sketch.FullPath
importFile = sketch.Name + "." + fqbnSuffix
if importDir := req.GetImportDir(); importDir != "" {
importPath = paths.New(importDir)
} else {
importPath = paths.New(req.GetImportFile()).Parent()
importFile = paths.New(req.GetImportFile()).Base()
// TODO: Create a function to obtain importPath from sketch
importPath = sketch.FullPath
// Add FQBN (without configs part) to export path
fqbnSuffix := strings.Replace(fqbn.StringWithoutConfig(), ":", ".", -1)
importPath = importPath.Join("build").Join(fqbnSuffix)
}

outputTmpFile, ok := toolProperties.GetOk("recipe.output.tmp_file")
outputTmpFile = toolProperties.ExpandPropsInString(outputTmpFile)
if !ok {
return nil, fmt.Errorf("property 'recipe.output.tmp_file' not defined")
if !importPath.Exist() {
return nil, fmt.Errorf("compiled sketch not found in %s", importPath)
}
ext := filepath.Ext(outputTmpFile)
if strings.HasSuffix(importFile, ext) {
importFile = importFile[:len(importFile)-len(ext)]
if !importPath.IsDir() {
return nil, fmt.Errorf("expected compiled sketch in directory %s, but is a file instead", importPath)
}

toolProperties.SetPath("build.path", importPath)
toolProperties.Set("build.project_name", importFile)
uploadFile := importPath.Join(importFile + ext)
if _, err := uploadFile.Stat(); err != nil {
if os.IsNotExist(err) {
return nil, fmt.Errorf("compiled sketch %s not found", uploadFile.String())
}
return nil, errors.Wrap(err, "cannot open sketch")
}
toolProperties.Set("build.project_name", sketch.Name+".ino")

// Set debug port property
port := req.GetPort()
Expand Down
4 changes: 2 additions & 2 deletions commands/debug/debug_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func TestGetCommandLine(t *testing.T) {
fmt.Sprintf(" %s/arduino-test/tools/openocd/0.10.0-arduino7/bin/openocd%s", dataDir, toolExtension) +
fmt.Sprintf(" -s \"%s/arduino-test/tools/openocd/0.10.0-arduino7/share/openocd/scripts/\"", dataDir) +
fmt.Sprintf(" --file \"%s/arduino-test/samd/variants/arduino_zero/openocd_scripts/arduino_zero.cfg\"", customHardware) +
fmt.Sprintf(" -c \"gdb_port pipe\" -c \"telnet_port 0\" -c init -c halt %s/hello.arduino-test.samd.arduino_zero_edbg.elf", sketchPath)
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)

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

command2, err := getCommandLine(req2, pm)
assert.Nil(t, err)
Expand Down
41 changes: 12 additions & 29 deletions commands/upload/upload.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ import (
"fmt"
"io"
"net/url"
"os"
"path/filepath"
"strings"
"time"

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

// Set path to compiled binary
// Make the filename without the FQBN configs part
fqbn.Configs = properties.NewMap()
fqbnSuffix := strings.Replace(fqbn.String(), ":", ".", -1)

var importPath *paths.Path
var importFile string
if req.GetImportFile() == "" {
importPath = sketch.FullPath
importFile = sketch.Name + "." + fqbnSuffix
if importDir := req.GetImportDir(); importDir != "" {
importPath = paths.New(importDir)
} else {
importPath = paths.New(req.GetImportFile()).Parent()
importFile = paths.New(req.GetImportFile()).Base()
// TODO: Create a function to obtain importPath from sketch
importPath = sketch.FullPath
// Add FQBN (without configs part) to export path
fqbnSuffix := strings.Replace(fqbn.StringWithoutConfig(), ":", ".", -1)
importPath = importPath.Join("build").Join(fqbnSuffix)
}

outputTmpFile, ok := uploadProperties.GetOk("recipe.output.tmp_file")
outputTmpFile = uploadProperties.ExpandPropsInString(outputTmpFile)
if !ok {
return nil, fmt.Errorf("property 'recipe.output.tmp_file' not defined")
if !importPath.Exist() {
return nil, fmt.Errorf("compiled sketch not found in %s", importPath)
}
ext := filepath.Ext(outputTmpFile)
if strings.HasSuffix(importFile, ext) {
importFile = importFile[:len(importFile)-len(ext)]
if !importPath.IsDir() {
return nil, fmt.Errorf("expected compiled sketch in directory %s, but is a file instead", importPath)
}

uploadProperties.SetPath("build.path", importPath)
uploadProperties.Set("build.project_name", importFile)
uploadFile := importPath.Join(importFile + ext)
if _, err := uploadFile.Stat(); err != nil {
if os.IsNotExist(err) {
return nil, fmt.Errorf("compiled sketch %s not found", uploadFile.String())
}
return nil, fmt.Errorf("cannot open sketch: %s", err)
}
uploadProperties.Set("build.project_name", sketch.Name+".ino")

// Perform reset via 1200bps touch if requested
if uploadProperties.GetBoolean("upload.use_1200bps_touch") {
Expand Down
Loading