Skip to content

Commit aa9e3e1

Browse files
committed
The build_cache.path setting now affect also the sketches cache
1 parent f99f927 commit aa9e3e1

13 files changed

+93
-60
lines changed

commands/service_compile.go

+31-23
Original file line numberDiff line numberDiff line change
@@ -160,9 +160,8 @@ func (s *arduinoCoreServerImpl) Compile(req *rpc.CompileRequest, stream rpc.Ardu
160160
return errors.New(i18n.Tr("Firmware encryption/signing requires all the following properties to be defined: %s", "build.keys.keychain, build.keys.sign_key, build.keys.encrypt_key"))
161161
}
162162

163-
// Generate or retrieve build path
163+
// Retrieve build path from user arguments
164164
var buildPath *paths.Path
165-
isDefaultBuildPath := false
166165
if buildPathArg := req.GetBuildPath(); buildPathArg != "" {
167166
buildPath = paths.New(req.GetBuildPath()).Canonical()
168167
if in, _ := buildPath.IsInsideDir(sk.FullPath); in && buildPath.IsDir() {
@@ -171,34 +170,22 @@ func (s *arduinoCoreServerImpl) Compile(req *rpc.CompileRequest, stream rpc.Ardu
171170
}
172171
}
173172
}
174-
if buildPath == nil {
175-
buildPath = sk.DefaultBuildPath()
176-
isDefaultBuildPath = true
177-
}
178-
if err = buildPath.MkdirAll(); err != nil {
179-
return &cmderrors.PermissionDeniedError{Message: i18n.Tr("Cannot create build directory"), Cause: err}
180-
}
181173

174+
// If no build path has been set by the user:
175+
// - set up the build cache directory
176+
// - set the sketch build path inside the build cache directory.
182177
var coreBuildCachePath *paths.Path
183178
var extraCoreBuildCachePaths paths.PathList
184-
if isDefaultBuildPath {
185-
buildcache.New(buildPath.Parent()).GetOrCreate(buildPath.Base())
186-
// cache is purged after compilation to not remove entries that might be required
187-
188-
defer maybePurgeBuildCache(
189-
s.settings.GetCompilationsBeforeBuildCachePurge(),
190-
s.settings.GetBuildCacheTTL().Abs())
191-
179+
if buildPath == nil {
192180
var buildCachePath *paths.Path
193-
if req.GetBuildCachePath() != "" {
194-
p, err := paths.New(req.GetBuildCachePath()).Abs()
195-
if err != nil {
196-
return &cmderrors.PermissionDeniedError{Message: i18n.Tr("Cannot create build cache directory"), Cause: err}
197-
}
198-
buildCachePath = p
181+
if p := req.GetBuildCachePath(); p != "" {
182+
buildCachePath = paths.New(p)
199183
} else {
200184
buildCachePath = s.settings.GetBuildCachePath()
201185
}
186+
if err := buildCachePath.ToAbs(); err != nil {
187+
return &cmderrors.PermissionDeniedError{Message: i18n.Tr("Cannot create build cache directory"), Cause: err}
188+
}
202189
if err := buildCachePath.MkdirAll(); err != nil {
203190
return &cmderrors.PermissionDeniedError{Message: i18n.Tr("Cannot create build cache directory"), Cause: err}
204191
}
@@ -212,6 +199,17 @@ func (s *arduinoCoreServerImpl) Compile(req *rpc.CompileRequest, stream rpc.Ardu
212199
for i, p := range extraCoreBuildCachePaths {
213200
extraCoreBuildCachePaths[i] = p.Join("cores")
214201
}
202+
203+
buildPath = s.getDefaultSketchBuildPath(sk, buildCachePath)
204+
buildcache.New(buildPath.Parent()).GetOrCreate(buildPath.Base())
205+
206+
// cache is purged after compilation to not remove entries that might be required
207+
defer maybePurgeBuildCache(
208+
s.settings.GetCompilationsBeforeBuildCachePurge(),
209+
s.settings.GetBuildCacheTTL().Abs())
210+
}
211+
if err = buildPath.MkdirAll(); err != nil {
212+
return &cmderrors.PermissionDeniedError{Message: i18n.Tr("Cannot create build directory"), Cause: err}
215213
}
216214

217215
if _, err := pme.FindToolsRequiredForBuild(targetPlatform, buildPlatform); err != nil {
@@ -420,6 +418,16 @@ func (s *arduinoCoreServerImpl) Compile(req *rpc.CompileRequest, stream rpc.Ardu
420418
return nil
421419
}
422420

421+
// getDefaultSketchBuildPath generates the default build directory for a given sketch.
422+
// The sketch build path is inside the build cache path and is unique for each sketch.
423+
// If overriddenBuildCachePath is nil the build cache path is taken from the settings.
424+
func (s *arduinoCoreServerImpl) getDefaultSketchBuildPath(sk *sketch.Sketch, overriddenBuildCachePath *paths.Path) *paths.Path {
425+
if overriddenBuildCachePath == nil {
426+
overriddenBuildCachePath = s.settings.GetBuildCachePath()
427+
}
428+
return overriddenBuildCachePath.Join("sketches", sk.Hash())
429+
}
430+
423431
// maybePurgeBuildCache runs the build files cache purge if the policy conditions are met.
424432
func maybePurgeBuildCache(compilationsBeforePurge uint, cacheTTL time.Duration) {
425433
// 0 means never purge

commands/service_compile_test.go

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// This file is part of arduino-cli.
2+
//
3+
// Copyright 2024 ARDUINO SA (http://www.arduino.cc/)
4+
//
5+
// This software is released under the GNU General Public License version 3,
6+
// which covers the main part of arduino-cli.
7+
// The terms of this license can be found at:
8+
// https://www.gnu.org/licenses/gpl-3.0.en.html
9+
//
10+
// You can be released from the requirements of the above licenses by purchasing
11+
// a commercial license. Buying such a license is mandatory if you want to
12+
// modify or otherwise use the software for commercial activities involving the
13+
// Arduino software without disclosing the source code of your own applications.
14+
// To purchase a commercial license, send an email to [email protected].
15+
16+
package commands
17+
18+
import (
19+
"testing"
20+
21+
"github.com/arduino/arduino-cli/internal/arduino/sketch"
22+
paths "github.com/arduino/go-paths-helper"
23+
"github.com/stretchr/testify/assert"
24+
)
25+
26+
func TestGenBuildPath(t *testing.T) {
27+
want := paths.TempDir().Join("arduino", "sketches", "ACBD18DB4CC2F85CEDEF654FCCC4A4D8")
28+
srv := NewArduinoCoreServer().(*arduinoCoreServerImpl)
29+
act := srv.getDefaultSketchBuildPath(&sketch.Sketch{FullPath: paths.New("foo")}, nil)
30+
assert.True(t, act.EquivalentTo(want))
31+
assert.Equal(t, "ACBD18DB4CC2F85CEDEF654FCCC4A4D8", (&sketch.Sketch{FullPath: paths.New("foo")}).Hash())
32+
}

commands/service_debug.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ func (s *arduinoCoreServerImpl) Debug(stream rpc.ArduinoCoreService_DebugServer)
214214
defer release()
215215

216216
// Exec debugger
217-
commandLine, err := getCommandLine(debugConfReq, pme)
217+
commandLine, err := s.getDebugCommandLine(debugConfReq, pme)
218218
if err != nil {
219219
return err
220220
}
@@ -254,9 +254,9 @@ func (s *arduinoCoreServerImpl) Debug(stream rpc.ArduinoCoreService_DebugServer)
254254
return sendResult(&rpc.DebugResponse_Result{})
255255
}
256256

257-
// getCommandLine compose a debug command represented by a core recipe
258-
func getCommandLine(req *rpc.GetDebugConfigRequest, pme *packagemanager.Explorer) ([]string, error) {
259-
debugInfo, err := getDebugProperties(req, pme, false)
257+
// getDebugCommandLine compose a debug command represented by a core recipe
258+
func (s *arduinoCoreServerImpl) getDebugCommandLine(req *rpc.GetDebugConfigRequest, pme *packagemanager.Explorer) ([]string, error) {
259+
debugInfo, err := s.getDebugProperties(req, pme, false)
260260
if err != nil {
261261
return nil, err
262262
}

commands/service_debug_config.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ func (s *arduinoCoreServerImpl) GetDebugConfig(ctx context.Context, req *rpc.Get
4444
return nil, err
4545
}
4646
defer release()
47-
return getDebugProperties(req, pme, false)
47+
return s.getDebugProperties(req, pme, false)
4848
}
4949

5050
// IsDebugSupported checks if the given board/programmer configuration supports debugging.
@@ -63,7 +63,7 @@ func (s *arduinoCoreServerImpl) IsDebugSupported(ctx context.Context, req *rpc.I
6363
ImportDir: "",
6464
Programmer: req.GetProgrammer(),
6565
}
66-
expectedOutput, err := getDebugProperties(configRequest, pme, true)
66+
expectedOutput, err := s.getDebugProperties(configRequest, pme, true)
6767
var x *cmderrors.FailedDebugError
6868
if errors.As(err, &x) {
6969
return &rpc.IsDebugSupportedResponse{DebuggingSupported: false}, nil
@@ -79,7 +79,7 @@ func (s *arduinoCoreServerImpl) IsDebugSupported(ctx context.Context, req *rpc.I
7979
checkFQBN := minimumFQBN.Clone()
8080
checkFQBN.Configs.Remove(config)
8181
configRequest.Fqbn = checkFQBN.String()
82-
checkOutput, err := getDebugProperties(configRequest, pme, true)
82+
checkOutput, err := s.getDebugProperties(configRequest, pme, true)
8383
if err == nil && reflect.DeepEqual(expectedOutput, checkOutput) {
8484
minimumFQBN.Configs.Remove(config)
8585
}
@@ -90,7 +90,7 @@ func (s *arduinoCoreServerImpl) IsDebugSupported(ctx context.Context, req *rpc.I
9090
}, nil
9191
}
9292

93-
func getDebugProperties(req *rpc.GetDebugConfigRequest, pme *packagemanager.Explorer, skipSketchChecks bool) (*rpc.GetDebugConfigResponse, error) {
93+
func (s *arduinoCoreServerImpl) getDebugProperties(req *rpc.GetDebugConfigRequest, pme *packagemanager.Explorer, skipSketchChecks bool) (*rpc.GetDebugConfigResponse, error) {
9494
var (
9595
sketchName string
9696
sketchDefaultFQBN string
@@ -109,7 +109,7 @@ func getDebugProperties(req *rpc.GetDebugConfigRequest, pme *packagemanager.Expl
109109
}
110110
sketchName = sk.Name
111111
sketchDefaultFQBN = sk.GetDefaultFQBN()
112-
sketchDefaultBuildPath = sk.DefaultBuildPath()
112+
sketchDefaultBuildPath = s.getDefaultSketchBuildPath(sk, nil)
113113
} else {
114114
// Use placeholder sketch data
115115
sketchName = "Sketch"

commands/service_debug_test.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -66,15 +66,16 @@ func TestGetCommandLine(t *testing.T) {
6666
pme, release := pm.NewExplorer()
6767
defer release()
6868

69+
srv := NewArduinoCoreServer().(*arduinoCoreServerImpl)
6970
{
7071
// Check programmer not found
7172
req.Programmer = "not-existent"
72-
_, err := getCommandLine(req, pme)
73+
_, err := srv.getDebugCommandLine(req, pme)
7374
require.Error(t, err)
7475
}
7576

7677
req.Programmer = "edbg"
77-
command, err := getCommandLine(req, pme)
78+
command, err := srv.getDebugCommandLine(req, pme)
7879
require.Nil(t, err)
7980
commandToTest := strings.Join(command, " ")
8081
require.Equal(t, filepath.FromSlash(goldCommand), filepath.FromSlash(commandToTest))
@@ -97,7 +98,7 @@ func TestGetCommandLine(t *testing.T) {
9798
fmt.Sprintf(" --file \"%s/arduino-test/samd/variants/mkr1000/openocd_scripts/arduino_zero.cfg\"", customHardware) +
9899
fmt.Sprintf(" -c \"gdb_port pipe\" -c \"telnet_port 0\" %s/build/arduino-test.samd.mkr1000/hello.ino.elf", sketchPath)
99100

100-
command2, err := getCommandLine(req2, pme)
101+
command2, err := srv.getDebugCommandLine(req2, pme)
101102
assert.Nil(t, err)
102103
commandToTest2 := strings.Join(command2, " ")
103104
assert.Equal(t, filepath.FromSlash(goldCommand2), filepath.FromSlash(commandToTest2))

commands/service_upload.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ func (s *arduinoCoreServerImpl) Upload(req *rpc.UploadRequest, stream rpc.Arduin
189189
})
190190
})
191191
defer errStream.Close()
192-
updatedPort, err := runProgramAction(
192+
updatedPort, err := s.runProgramAction(
193193
stream.Context(),
194194
pme,
195195
sk,
@@ -259,7 +259,7 @@ func (s *arduinoCoreServerImpl) UploadUsingProgrammer(req *rpc.UploadUsingProgra
259259
}, streamAdapter)
260260
}
261261

262-
func runProgramAction(ctx context.Context, pme *packagemanager.Explorer,
262+
func (s *arduinoCoreServerImpl) runProgramAction(ctx context.Context, pme *packagemanager.Explorer,
263263
sk *sketch.Sketch,
264264
importFile, importDir, fqbnIn string, userPort *rpc.Port,
265265
programmerID string,
@@ -432,7 +432,7 @@ func runProgramAction(ctx context.Context, pme *packagemanager.Explorer,
432432
}
433433

434434
if !burnBootloader {
435-
importPath, sketchName, err := determineBuildPathAndSketchName(importFile, importDir, sk)
435+
importPath, sketchName, err := s.determineBuildPathAndSketchName(importFile, importDir, sk)
436436
if err != nil {
437437
return nil, &cmderrors.NotFoundError{Message: i18n.Tr("Error finding build artifacts"), Cause: err}
438438
}
@@ -735,7 +735,7 @@ func runTool(recipeID string, props *properties.Map, outStream, errStream io.Wri
735735
return nil
736736
}
737737

738-
func determineBuildPathAndSketchName(importFile, importDir string, sk *sketch.Sketch) (*paths.Path, string, error) {
738+
func (s *arduinoCoreServerImpl) determineBuildPathAndSketchName(importFile, importDir string, sk *sketch.Sketch) (*paths.Path, string, error) {
739739
// In general, compiling a sketch will produce a set of files that are
740740
// named as the sketch but have different extensions, for example Sketch.ino
741741
// may produce: Sketch.ino.bin; Sketch.ino.hex; Sketch.ino.zip; etc...
@@ -788,7 +788,7 @@ func determineBuildPathAndSketchName(importFile, importDir string, sk *sketch.Sk
788788

789789
// Case 4: only sketch specified. In this case we use the generated build path
790790
// and the given sketch name.
791-
return sk.DefaultBuildPath(), sk.Name + sk.MainFile.Ext(), nil
791+
return s.getDefaultSketchBuildPath(sk, nil), sk.Name + sk.MainFile.Ext(), nil
792792
}
793793

794794
func detectSketchNameFromBuildPath(buildPath *paths.Path) (string, error) {

commands/service_upload_burnbootloader.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ func (s *arduinoCoreServerImpl) BurnBootloader(req *rpc.BurnBootloaderRequest, s
7272
}
7373
defer release()
7474

75-
if _, err := runProgramAction(
75+
if _, err := s.runProgramAction(
7676
stream.Context(),
7777
pme,
7878
nil, // sketch

commands/service_upload_test.go

+7-4
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ func TestDetermineBuildPathAndSketchName(t *testing.T) {
7171
fqbn, err := cores.ParseFQBN("arduino:samd:mkr1000")
7272
require.NoError(t, err)
7373

74+
srv := NewArduinoCoreServer().(*arduinoCoreServerImpl)
75+
7476
tests := []test{
7577
// 00: error: no data passed in
7678
{"", "", nil, nil, "<nil>", ""},
@@ -81,7 +83,7 @@ func TestDetermineBuildPathAndSketchName(t *testing.T) {
8183
// 03: error: used both importPath and importFile
8284
{"testdata/upload/build_path_2/Blink.ino.hex", "testdata/upload/build_path_2", nil, nil, "<nil>", ""},
8385
// 04: only sketch without FQBN
84-
{"", "", blonk, nil, blonk.DefaultBuildPath().String(), "Blonk.ino"},
86+
{"", "", blonk, nil, srv.getDefaultSketchBuildPath(blonk, nil).String(), "Blonk.ino"},
8587
// 05: use importFile to detect build.path and project_name, sketch is ignored.
8688
{"testdata/upload/build_path_2/Blink.ino.hex", "", blonk, nil, "testdata/upload/build_path_2", "Blink.ino"},
8789
// 06: use importPath as build.path and Blink as project name, ignore the sketch Blonk
@@ -97,7 +99,7 @@ func TestDetermineBuildPathAndSketchName(t *testing.T) {
9799
// 11: error: used both importPath and importFile
98100
{"testdata/upload/build_path_2/Blink.ino.hex", "testdata/upload/build_path_2", nil, fqbn, "<nil>", ""},
99101
// 12: use sketch to determine project name and sketch+fqbn to determine build path
100-
{"", "", blonk, fqbn, blonk.DefaultBuildPath().String(), "Blonk.ino"},
102+
{"", "", blonk, fqbn, srv.getDefaultSketchBuildPath(blonk, nil).String(), "Blonk.ino"},
101103
// 13: use importFile to detect build.path and project_name, sketch+fqbn is ignored.
102104
{"testdata/upload/build_path_2/Blink.ino.hex", "", blonk, fqbn, "testdata/upload/build_path_2", "Blink.ino"},
103105
// 14: use importPath as build.path and Blink as project name, ignore the sketch Blonk, ignore fqbn
@@ -111,7 +113,7 @@ func TestDetermineBuildPathAndSketchName(t *testing.T) {
111113
}
112114
for i, test := range tests {
113115
t.Run(fmt.Sprintf("SubTest%02d", i), func(t *testing.T) {
114-
buildPath, sketchName, err := determineBuildPathAndSketchName(test.importFile, test.importDir, test.sketch)
116+
buildPath, sketchName, err := srv.determineBuildPathAndSketchName(test.importFile, test.importDir, test.sketch)
115117
if test.resBuildPath == "<nil>" {
116118
require.Error(t, err)
117119
require.Nil(t, buildPath)
@@ -183,10 +185,11 @@ func TestUploadPropertiesComposition(t *testing.T) {
183185
pme, release := pm.NewExplorer()
184186
defer release()
185187

188+
srv := NewArduinoCoreServer().(*arduinoCoreServerImpl)
186189
testRunner := func(t *testing.T, test test, verboseVerify bool) {
187190
outStream := &bytes.Buffer{}
188191
errStream := &bytes.Buffer{}
189-
_, err := runProgramAction(
192+
_, err := srv.runProgramAction(
190193
context.Background(),
191194
pme,
192195
nil, // sketch

internal/arduino/sketch/sketch.go

-6
Original file line numberDiff line numberDiff line change
@@ -279,12 +279,6 @@ func (e *InvalidSketchFolderNameError) Error() string {
279279
return i18n.Tr("no valid sketch found in %[1]s: missing %[2]s", e.SketchFolder, e.SketchFile)
280280
}
281281

282-
// DefaultBuildPath generates the default build directory for a given sketch.
283-
// The build path is in a temporary directory and is unique for each sketch.
284-
func (s *Sketch) DefaultBuildPath() *paths.Path {
285-
return paths.TempDir().Join("arduino", "sketches", s.Hash())
286-
}
287-
288282
// Hash generate a unique hash for the given sketch.
289283
func (s *Sketch) Hash() string {
290284
path := s.FullPath.String()

internal/arduino/sketch/sketch_test.go

-6
Original file line numberDiff line numberDiff line change
@@ -285,12 +285,6 @@ func TestNewSketchFolderSymlink(t *testing.T) {
285285
require.True(t, sketch.RootFolderFiles.ContainsEquivalentTo(sketchPathSymlink.Join("s_file.S")))
286286
}
287287

288-
func TestGenBuildPath(t *testing.T) {
289-
want := paths.TempDir().Join("arduino", "sketches", "ACBD18DB4CC2F85CEDEF654FCCC4A4D8")
290-
assert.True(t, (&Sketch{FullPath: paths.New("foo")}).DefaultBuildPath().EquivalentTo(want))
291-
assert.Equal(t, "ACBD18DB4CC2F85CEDEF654FCCC4A4D8", (&Sketch{FullPath: paths.New("foo")}).Hash())
292-
}
293-
294288
func TestNewSketchWithSymlink(t *testing.T) {
295289
sketchPath, _ := paths.New("testdata", "SketchWithSymlink").Abs()
296290
mainFilePath := sketchPath.Join("SketchWithSymlink.ino")

internal/cli/compile/compile.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ func NewCommand(srv rpc.ArduinoCoreServiceServer, settings *rpc.Configuration) *
9595
compileCommand.Flags().BoolVar(&dumpProfile, "dump-profile", false, i18n.Tr("Create and print a profile configuration from the build."))
9696
showPropertiesArg.AddToCommand(compileCommand)
9797
compileCommand.Flags().BoolVar(&preprocess, "preprocess", false, i18n.Tr("Print preprocessed code to stdout instead of compiling."))
98-
compileCommand.Flags().StringVar(&buildCachePath, "build-cache-path", "", i18n.Tr("Builds of 'core.a' are saved into this path to be cached and reused."))
98+
compileCommand.Flags().StringVar(&buildCachePath, "build-cache-path", "", i18n.Tr("Builds of cores and sketches are saved into this path to be cached and reused."))
9999
compileCommand.Flags().StringVar(&exportDir, "output-dir", "", i18n.Tr("Save build artifacts in this directory."))
100100
compileCommand.Flags().StringVar(&buildPath, "build-path", "",
101101
i18n.Tr("Path where to save compiled files. If omitted, a directory will be created in the default temporary path of your OS."))

rpc/cc/arduino/cli/commands/v1/compile.pb.go

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rpc/cc/arduino/cli/commands/v1/compile.proto

+2-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ message CompileRequest {
3636
bool show_properties = 4;
3737
// Print preprocessed code to stdout instead of compiling.
3838
bool preprocess = 5;
39-
// Builds of 'core.a' are saved into this path to be cached and reused.
39+
// Builds of core and sketches are saved into this path to be cached and
40+
// reused.
4041
string build_cache_path = 6;
4142
// Path to use to store the files used for the compilation. If omitted,
4243
// a directory will be created in the operating system's default temporary

0 commit comments

Comments
 (0)