diff --git a/commands/compile/compile.go b/commands/compile/compile.go index 31e0a647b01..22a70de0c52 100644 --- a/commands/compile/compile.go +++ b/commands/compile/compile.go @@ -73,11 +73,14 @@ func Compile(ctx context.Context, req *rpc.CompileRequest, outStream, errStream return nil, &arduino.MissingSketchPathError{} } sketchPath := paths.New(req.GetSketchPath()) - sk, err := sketch.New(sketchPath) - if err != nil { - return nil, &arduino.CantOpenSketchError{Cause: err} + builderCtx := &types.Context{} + builderCtx.PackageManager = pme + if pme.GetProfile() != nil { + builderCtx.LibrariesManager = lm } + sk, newSketchErr := sketch.New(sketchPath) + fqbnIn := req.GetFqbn() if fqbnIn == "" && sk != nil { fqbnIn = sk.GetDefaultFQBN() @@ -111,13 +114,13 @@ func Compile(ctx context.Context, req *rpc.CompileRequest, outStream, errStream securityKeysOverride = append(securityKeysOverride, "build.keys.keychain="+req.KeysKeychain, "build.keys.sign_key="+req.GetSignKey(), "build.keys.encrypt_key="+req.EncryptKey) } - builderCtx := &types.Context{} - builderCtx.PackageManager = pme - if pme.GetProfile() != nil { - builderCtx.LibrariesManager = lm - } builderCtx.UseCachedLibrariesResolution = req.GetSkipLibrariesDiscovery() builderCtx.FQBN = fqbn + defer func() { + appendBuildProperties(r, builderCtx) + }() + r = &rpc.CompileResponse{} + builderCtx.Sketch = sk builderCtx.ProgressCB = progressCB @@ -129,30 +132,11 @@ func Compile(ctx context.Context, req *rpc.CompileRequest, outStream, errStream builderCtx.OtherLibrariesDirs = paths.NewPathList(req.GetLibraries()...) builderCtx.OtherLibrariesDirs.Add(configuration.LibrariesDir(configuration.Settings)) builderCtx.LibraryDirs = paths.NewPathList(req.Library...) - if req.GetBuildPath() == "" { - builderCtx.BuildPath = sk.DefaultBuildPath() - } else { - builderCtx.BuildPath = paths.New(req.GetBuildPath()).Canonical() - if in, err := builderCtx.BuildPath.IsInsideDir(sk.FullPath); err != nil { - return nil, &arduino.NotFoundError{Message: tr("Cannot find build path"), Cause: err} - } else if in && builderCtx.BuildPath.IsDir() { - if sk.AdditionalFiles, err = removeBuildFromSketchFiles(sk.AdditionalFiles, builderCtx.BuildPath); err != nil { - return nil, err - } - } - } - if err = builderCtx.BuildPath.MkdirAll(); err != nil { - return nil, &arduino.PermissionDeniedError{Message: tr("Cannot create build directory"), Cause: err} + err = prepareBuildPath(sk, req.GetBuildPath(), builderCtx) + if err != nil { + return r, err } - buildcache.New(builderCtx.BuildPath.Parent()).GetOrCreate(builderCtx.BuildPath.Base()) - // cache is purged after compilation to not remove entries that might be required - defer maybePurgeBuildCache() - - builderCtx.CompilationDatabase = bldr.NewCompilationDatabase( - builderCtx.BuildPath.Join("compile_commands.json"), - ) - builderCtx.Verbose = req.GetVerbose() // Optimize for debug @@ -187,7 +171,6 @@ func Compile(ctx context.Context, req *rpc.CompileRequest, outStream, errStream builderCtx.OnlyUpdateCompilationDatabase = req.GetCreateCompilationDatabaseOnly() builderCtx.SourceOverride = req.GetSourceOverride() - r = &rpc.CompileResponse{} defer func() { if p := builderCtx.BuildPath; p != nil { r.BuildPath = p.String() @@ -200,18 +183,6 @@ func Compile(ctx context.Context, req *rpc.CompileRequest, outStream, errStream } }() - defer func() { - buildProperties := builderCtx.BuildProperties - if buildProperties == nil { - return - } - keys := buildProperties.Keys() - sort.Strings(keys) - for _, key := range keys { - r.BuildProperties = append(r.BuildProperties, key+"="+buildProperties.Get(key)) - } - }() - if req.GetShowProperties() { // Just get build properties and exit compileErr := builder.RunParseHardware(builderCtx) @@ -221,6 +192,15 @@ func Compile(ctx context.Context, req *rpc.CompileRequest, outStream, errStream return r, compileErr } + if newSketchErr != nil { + // newSketchErr causes to exit only here since the request could have + // been to only show properties until now + return r, &arduino.CantOpenSketchError{Cause: err} + } + + // cache is purged after compilation to not remove entries that might be required + defer maybePurgeBuildCache() + if req.GetPreprocess() { // Just output preprocessed source code and exit compileErr := builder.RunPreprocess(builderCtx) @@ -231,16 +211,7 @@ func Compile(ctx context.Context, req *rpc.CompileRequest, outStream, errStream } defer func() { - importedLibs := []*rpc.Library{} - for _, lib := range builderCtx.ImportedLibraries { - rpcLib, err := lib.ToRPCLibrary() - if err != nil { - msg := tr("Error getting information for library %s", lib.Name) + ": " + err.Error() + "\n" - errStream.Write([]byte(msg)) - } - importedLibs = append(importedLibs, rpcLib) - } - r.UsedLibraries = importedLibs + appendUserLibraries(r, builderCtx, errStream) }() // if it's a regular build, go on... @@ -309,6 +280,61 @@ func Compile(ctx context.Context, req *rpc.CompileRequest, outStream, errStream return r, nil } +func prepareBuildPath(sk *sketch.Sketch, requestedBuildPath string, builderCtx *types.Context) error { + if sk == nil { + return nil + } + + if requestedBuildPath == "" { + builderCtx.BuildPath = sk.DefaultBuildPath() + } else { + builderCtx.BuildPath = paths.New(requestedBuildPath).Canonical() + if in, err := builderCtx.BuildPath.IsInsideDir(sk.FullPath); err != nil { + return &arduino.NotFoundError{Message: tr("Cannot find build path"), Cause: err} + } else if in && builderCtx.BuildPath.IsDir() { + if sk.AdditionalFiles, err = removeBuildFromSketchFiles(sk.AdditionalFiles, builderCtx.BuildPath); err != nil { + return err + } + } + } + + if err := builderCtx.BuildPath.MkdirAll(); err != nil { + return &arduino.PermissionDeniedError{Message: tr("Cannot create build directory"), Cause: err} + } + + builderCtx.CompilationDatabase = bldr.NewCompilationDatabase( + builderCtx.BuildPath.Join("compile_commands.json"), + ) + buildcache.New(builderCtx.BuildPath.Parent()).GetOrCreate(builderCtx.BuildPath.Base()) + return nil +} + +func appendUserLibraries(r *rpc.CompileResponse, builderCtx *types.Context, errStream io.Writer) { + importedLibs := []*rpc.Library{} + for _, lib := range builderCtx.ImportedLibraries { + rpcLib, err := lib.ToRPCLibrary() + if err != nil { + msg := tr("Error getting information for library %s", lib.Name) + ": " + err.Error() + "\n" + errStream.Write([]byte(msg)) + } + importedLibs = append(importedLibs, rpcLib) + } + r.UsedLibraries = importedLibs +} + +func appendBuildProperties(r *rpc.CompileResponse, builderCtx *types.Context) bool { + buildProperties := builderCtx.BuildProperties + if buildProperties == nil { + return true + } + keys := buildProperties.Keys() + sort.Strings(keys) + for _, key := range keys { + r.BuildProperties = append(r.BuildProperties, key+"="+buildProperties.Get(key)) + } + return false +} + // maybePurgeBuildCache runs the build files cache purge if the policy conditions are met. func maybePurgeBuildCache() { diff --git a/internal/cli/arguments/sketch.go b/internal/cli/arguments/sketch.go index 974426044c6..ad983610f0c 100644 --- a/internal/cli/arguments/sketch.go +++ b/internal/cli/arguments/sketch.go @@ -43,7 +43,13 @@ func InitSketchPath(path string) (sketchPath *paths.Path) { } // NewSketch is a helper function useful to create a sketch instance -func NewSketch(sketchPath *paths.Path) *sketch.Sketch { +func NewSketch(sketchPath *paths.Path) (*sketch.Sketch, error) { + return sketch.New(sketchPath) +} + +// MustNewSketch is a helper function useful to create a sketch instance, exits if the +// initialization fails +func MustNewSketch(sketchPath *paths.Path) *sketch.Sketch { sketch, err := sketch.New(sketchPath) if err != nil { feedback.Fatal(tr("Error opening sketch: %v", err), feedback.ErrGeneric) diff --git a/internal/cli/board/attach.go b/internal/cli/board/attach.go index d701ad15e8a..12b4d4b5db5 100644 --- a/internal/cli/board/attach.go +++ b/internal/cli/board/attach.go @@ -50,7 +50,7 @@ func initAttachCommand() *cobra.Command { func runAttachCommand(path string, port *arguments.Port, fqbn string) { sketchPath := arguments.InitSketchPath(path) - sk := arguments.NewSketch(sketchPath) + sk := arguments.MustNewSketch(sketchPath) var currentPort *boardAttachPortResult if currentAddress, currentProtocol := sk.GetDefaultPortAddressAndProtocol(); currentAddress != "" { diff --git a/internal/cli/compile/compile.go b/internal/cli/compile/compile.go index 318fc021dc3..551baa31fad 100644 --- a/internal/cli/compile/compile.go +++ b/internal/cli/compile/compile.go @@ -180,6 +180,10 @@ func runCompileCommand(cmd *cobra.Command, args []string) { feedback.Fatal(tr("You cannot use the %s flag while compiling with a profile.", "--library"), feedback.ErrBadArgument) } } + showPropertiesM, err := parseShowPropertiesMode(showProperties) + if err != nil { + feedback.Fatal(tr("Error parsing --show-properties flag: %v", err), feedback.ErrGeneric) + } path := "" if len(args) > 0 { @@ -187,7 +191,18 @@ func runCompileCommand(cmd *cobra.Command, args []string) { } sketchPath := arguments.InitSketchPath(path) - sk := arguments.NewSketch(sketchPath) + sk, err := arguments.NewSketch(sketchPath) + + if err != nil { + showPropertiesWithEmptySketchPath := path == "" && showPropertiesM != showPropertiesModeDisabled + if showPropertiesWithEmptySketchPath { + // only properties were requested and no sketch path was provided + // an empty sketch is used to collect properties without sketch related values + sk = nil + } else { + feedback.Fatal(tr("Error opening sketch: %v", err), feedback.ErrGeneric) + } + } inst, profile := instance.CreateAndInitWithProfile(profileArg.Get(), sketchPath) if fqbnArg.String() == "" { @@ -215,11 +230,6 @@ func runCompileCommand(cmd *cobra.Command, args []string) { 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 stdIORes func() *feedback.OutputStreamsResult if showPropertiesM != showPropertiesModeDisabled { diff --git a/internal/cli/debug/debug.go b/internal/cli/debug/debug.go index 3078fd8d1c8..bd8c146e5c1 100644 --- a/internal/cli/debug/debug.go +++ b/internal/cli/debug/debug.go @@ -75,7 +75,7 @@ func runDebugCommand(command *cobra.Command, args []string) { } sketchPath := arguments.InitSketchPath(path) - sk := arguments.NewSketch(sketchPath) + sk := arguments.MustNewSketch(sketchPath) fqbn, port := arguments.CalculateFQBNAndPort(&portArgs, &fqbnArg, instance, sk) debugConfigRequested := &dbg.DebugConfigRequest{ Instance: instance, diff --git a/legacy/builder/fail_if_buildpath_equals_sketchpath.go b/legacy/builder/fail_if_buildpath_equals_sketchpath.go index 2ec5c0da662..41f1f42c213 100644 --- a/legacy/builder/fail_if_buildpath_equals_sketchpath.go +++ b/legacy/builder/fail_if_buildpath_equals_sketchpath.go @@ -23,6 +23,9 @@ import ( type FailIfBuildPathEqualsSketchPath struct{} func (s *FailIfBuildPathEqualsSketchPath) Run(ctx *types.Context) error { + if ctx.BuildPath == nil || ctx.Sketch == nil { + return nil + } buildPath := ctx.BuildPath.Canonical() sketchPath := ctx.Sketch.FullPath.Canonical() if buildPath.EqualsTo(sketchPath) { diff --git a/legacy/builder/setup_build_properties.go b/legacy/builder/setup_build_properties.go index 5b191dd8dff..a660ed212fe 100644 --- a/legacy/builder/setup_build_properties.go +++ b/legacy/builder/setup_build_properties.go @@ -45,7 +45,9 @@ func (s *SetupBuildProperties) Run(ctx *types.Context) error { } ctx.OptimizationFlags = buildProperties.Get("compiler.optimization_flags") - buildProperties.SetPath("build.source.path", ctx.Sketch.FullPath) + if ctx.Sketch != nil { + buildProperties.SetPath("build.source.path", ctx.Sketch.FullPath) + } keychainProp := buildProperties.ContainsKey("build.keys.keychain") signProp := buildProperties.ContainsKey("build.keys.sign_key")