Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 5d44cb3

Browse files
author
Luca Bianconi
committedJan 17, 2023
feat: purge cache after expiration time
1 parent 271d241 commit 5d44cb3

File tree

17 files changed

+289
-35
lines changed

17 files changed

+289
-35
lines changed
 

‎.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,6 @@ venv
2828
/docsgen/arduino-cli.exe
2929
/docs/rpc/*.md
3030
/docs/commands/*.md
31+
32+
# Delve debugger binary file
33+
__debug_bin

‎arduino/sketch/sketch.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,5 +302,10 @@ func GenBuildPath(sketchPath *paths.Path) *paths.Path {
302302
}
303303
md5SumBytes := md5.Sum([]byte(path))
304304
md5Sum := strings.ToUpper(hex.EncodeToString(md5SumBytes[:]))
305-
return paths.TempDir().Join("arduino", "sketch-"+md5Sum)
305+
306+
return getSketchesCacheDir().Join(md5Sum)
307+
}
308+
309+
func getSketchesCacheDir() *paths.Path {
310+
return paths.TempDir().Join("arduino", "sketches").Canonical()
306311
}

‎arduino/sketch/sketch_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -286,10 +286,10 @@ func TestNewSketchFolderSymlink(t *testing.T) {
286286
}
287287

288288
func TestGenBuildPath(t *testing.T) {
289-
want := paths.TempDir().Join("arduino", "sketch-ACBD18DB4CC2F85CEDEF654FCCC4A4D8")
289+
want := paths.TempDir().Join("arduino", "sketches", "ACBD18DB4CC2F85CEDEF654FCCC4A4D8")
290290
assert.True(t, GenBuildPath(paths.New("foo")).EquivalentTo(want))
291291

292-
want = paths.TempDir().Join("arduino", "sketch-D41D8CD98F00B204E9800998ECF8427E")
292+
want = paths.TempDir().Join("arduino", "sketches", "D41D8CD98F00B204E9800998ECF8427E")
293293
assert.True(t, GenBuildPath(nil).EquivalentTo(want))
294294
}
295295

‎buildcache/build_cache.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// This file is part of arduino-cli.
2+
//
3+
// Copyright 2020 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 license@arduino.cc.
15+
16+
package buildcache
17+
18+
import (
19+
"time"
20+
21+
"github.com/arduino/go-paths-helper"
22+
"github.com/pkg/errors"
23+
"github.com/sirupsen/logrus"
24+
)
25+
26+
const lastUsedFileName = ".last-used"
27+
28+
// GetOrCreate retrieves or creates the cache directory at the given path
29+
// If the cache already exists the lifetime of the cache is extended.
30+
func GetOrCreate(dir *paths.Path) error {
31+
if !dir.Exist() {
32+
if err := dir.MkdirAll(); err != nil {
33+
return err
34+
}
35+
}
36+
37+
return dir.Join(lastUsedFileName).WriteFile([]byte{})
38+
}
39+
40+
// Purge removes all cache directories within baseDir that have expired
41+
// To know how long ago a directory has been last used
42+
// it checks into the .last-used file.
43+
func Purge(baseDir *paths.Path, ttl time.Duration) {
44+
files, err := baseDir.ReadDir()
45+
if err != nil {
46+
return
47+
}
48+
for _, file := range files {
49+
if file.IsDir() {
50+
removeIfExpired(file, ttl)
51+
}
52+
}
53+
}
54+
55+
func removeIfExpired(dir *paths.Path, ttl time.Duration) {
56+
fileInfo, err := dir.Join().Stat()
57+
if err != nil {
58+
return
59+
}
60+
lifeExpectancy := ttl - time.Since(fileInfo.ModTime())
61+
if lifeExpectancy > 0 {
62+
return
63+
}
64+
logrus.Tracef(`Purging cache directory "%s". Expired by %s`, dir, lifeExpectancy.Abs())
65+
err = dir.RemoveAll()
66+
if err != nil {
67+
logrus.Tracef(`Error while pruning cache directory "%s": %s`, dir, errors.WithStack(err))
68+
}
69+
}

‎buildcache/build_cache_test.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// This file is part of arduino-cli.
2+
//
3+
// Copyright 2020 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 license@arduino.cc.
15+
16+
package buildcache
17+
18+
import (
19+
"testing"
20+
"time"
21+
22+
"github.com/arduino/go-paths-helper"
23+
"github.com/stretchr/testify/require"
24+
)
25+
26+
func Test_UpdateLastUsedFileNotExisting(t *testing.T) {
27+
testBuildDir := paths.New(t.TempDir(), "sketches", "xxx")
28+
require.NoError(t, testBuildDir.MkdirAll())
29+
timeBeforeUpdating := time.Unix(0, 0)
30+
requireCorrectUpdate(t, testBuildDir, timeBeforeUpdating)
31+
}
32+
33+
func Test_UpdateLastUsedFileExisting(t *testing.T) {
34+
testBuildDir := paths.New(t.TempDir(), "sketches", "xxx")
35+
require.NoError(t, testBuildDir.MkdirAll())
36+
37+
// create the file
38+
preExistingFile := testBuildDir.Join(lastUsedFileName)
39+
require.NoError(t, preExistingFile.WriteFile([]byte{}))
40+
timeBeforeUpdating := time.Now().Add(-time.Second)
41+
preExistingFile.Chtimes(time.Now(), timeBeforeUpdating)
42+
requireCorrectUpdate(t, testBuildDir, timeBeforeUpdating)
43+
}
44+
45+
func requireCorrectUpdate(t *testing.T, dir *paths.Path, prevModTime time.Time) {
46+
require.NoError(t, GetOrCreate(dir))
47+
expectedFile := dir.Join(lastUsedFileName)
48+
fileInfo, err := expectedFile.Stat()
49+
require.Nil(t, err)
50+
require.Greater(t, fileInfo.ModTime(), prevModTime)
51+
}
52+
53+
func TestPurge(t *testing.T) {
54+
ttl := time.Minute
55+
56+
dirToPurge := paths.New(t.TempDir(), "root")
57+
58+
lastUsedTimesByDirPath := map[*paths.Path]time.Time{
59+
(dirToPurge.Join("old")): time.Now().Add(-ttl - time.Hour),
60+
(dirToPurge.Join("fresh")): time.Now().Add(-ttl + time.Minute),
61+
}
62+
63+
// create the metadata files
64+
for dirPath, lastUsedTime := range lastUsedTimesByDirPath {
65+
require.NoError(t, dirPath.MkdirAll())
66+
infoFilePath := dirPath.Join(lastUsedFileName).Canonical()
67+
require.NoError(t, infoFilePath.WriteFile([]byte{}))
68+
// make sure access time does not matter
69+
accesstime := time.Now()
70+
require.NoError(t, infoFilePath.Chtimes(accesstime, lastUsedTime))
71+
}
72+
73+
Purge(dirToPurge, ttl)
74+
75+
files, err := dirToPurge.Join("fresh").Stat()
76+
require.Nil(t, err)
77+
require.True(t, files.IsDir())
78+
require.True(t, dirToPurge.Exist())
79+
}

‎commands/compile/compile.go

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,11 @@ import (
2727
"github.com/arduino/arduino-cli/arduino/cores"
2828
"github.com/arduino/arduino-cli/arduino/cores/packagemanager"
2929
"github.com/arduino/arduino-cli/arduino/sketch"
30+
"github.com/arduino/arduino-cli/buildcache"
3031
"github.com/arduino/arduino-cli/commands"
3132
"github.com/arduino/arduino-cli/configuration"
3233
"github.com/arduino/arduino-cli/i18n"
34+
"github.com/arduino/arduino-cli/inventory"
3335
"github.com/arduino/arduino-cli/legacy/builder"
3436
"github.com/arduino/arduino-cli/legacy/builder/types"
3537
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
@@ -135,6 +137,11 @@ func Compile(ctx context.Context, req *rpc.CompileRequest, outStream, errStream
135137
if err = builderCtx.BuildPath.MkdirAll(); err != nil {
136138
return nil, &arduino.PermissionDeniedError{Message: tr("Cannot create build directory"), Cause: err}
137139
}
140+
141+
buildcache.GetOrCreate(builderCtx.BuildPath)
142+
// cache is purged after compilation to not remove entries that might be required
143+
defer maybePurgeBuildCache()
144+
138145
builderCtx.CompilationDatabase = bldr.NewCompilationDatabase(
139146
builderCtx.BuildPath.Join("compile_commands.json"),
140147
)
@@ -143,8 +150,7 @@ func Compile(ctx context.Context, req *rpc.CompileRequest, outStream, errStream
143150

144151
// Optimize for debug
145152
builderCtx.OptimizeForDebug = req.GetOptimizeForDebug()
146-
147-
builderCtx.CoreBuildCachePath = paths.TempDir().Join("arduino", "core-cache")
153+
builderCtx.CoreBuildCachePath = paths.TempDir().Join("arduino", "cores")
148154

149155
builderCtx.Jobs = int(req.GetJobs())
150156

@@ -284,3 +290,24 @@ func Compile(ctx context.Context, req *rpc.CompileRequest, outStream, errStream
284290

285291
return r, nil
286292
}
293+
294+
// maybePurgeBuildCache runs the build files cache purge if the policy conditions are met.
295+
func maybePurgeBuildCache() {
296+
297+
compilationsBeforePurge := configuration.Settings.GetUint("build_cache.compilations_before_purge")
298+
// 0 means never purge
299+
if compilationsBeforePurge == 0 {
300+
return
301+
}
302+
compilationSinceLastPurge := inventory.Store.GetUint("build_cache.compilation_count_since_last_purge")
303+
compilationSinceLastPurge++
304+
inventory.Store.Set("build_cache.compilation_count_since_last_purge", compilationSinceLastPurge)
305+
defer inventory.WriteStore()
306+
if compilationsBeforePurge == 0 || compilationSinceLastPurge < compilationsBeforePurge {
307+
return
308+
}
309+
inventory.Store.Set("build_cache.compilation_count_since_last_purge", 0)
310+
cacheTTL := configuration.Settings.GetDuration("build_cache.ttl").Abs()
311+
buildcache.Purge(paths.TempDir().Join("arduino", "cores"), cacheTTL)
312+
buildcache.Purge(paths.TempDir().Join("arduino", "sketches"), cacheTTL)
313+
}

‎configuration/defaults.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package configuration
1818
import (
1919
"path/filepath"
2020
"strings"
21+
"time"
2122

2223
"github.com/spf13/viper"
2324
)
@@ -41,6 +42,8 @@ func SetDefaults(settings *viper.Viper) {
4142

4243
// Sketch compilation
4344
settings.SetDefault("sketch.always_export_binaries", false)
45+
settings.SetDefault("build_cache.ttl", time.Hour*24*30)
46+
settings.SetDefault("build_cache.compilations_before_purge", 10)
4447

4548
// daemon settings
4649
settings.SetDefault("daemon.port", "50051")

‎docs/configuration.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@
3333
to the sketch folder. This is the equivalent of using the [`--export-binaries`][arduino-cli compile options] flag.
3434
- `updater` - configuration options related to Arduino CLI updates
3535
- `enable_notification` - set to `false` to disable notifications of new Arduino CLI releases, defaults to `true`
36+
- `build_cache` configuration options related to the compilation cache
37+
- `compilations_before_purge` - interval, in number of compilations, at which the cache is purged, defaults to `10`.
38+
When `0` the cache is never purged.
39+
- `ttl` - cache expiration time of build folders. If the cache is hit by a compilation the corresponding build files
40+
lifetime is renewed. The value format must be a valid input for
41+
[time.ParseDuration()](https://pkg.go.dev/time#ParseDuration), defaults to `720h` (30 days).
3642

3743
## Configuration methods
3844

‎internal/integrationtest/arduino-cli.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ func NewArduinoCliWithinEnvironment(env *Environment, config *ArduinoCLIConfig)
112112
"ARDUINO_DATA_DIR": cli.dataDir.String(),
113113
"ARDUINO_DOWNLOADS_DIR": cli.stagingDir.String(),
114114
"ARDUINO_SKETCHBOOK_DIR": cli.sketchbookDir.String(),
115+
"ARDUINO_BUILD_CACHE_COMPILATIONS_BEFORE_PURGE": "0",
115116
}
116117
env.RegisterCleanUpCallback(cli.CleanUp)
117118
return cli

‎internal/integrationtest/compile_1/compile_test.go

Lines changed: 72 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@ import (
2020
"encoding/hex"
2121
"encoding/json"
2222
"os"
23+
"sort"
2324
"strings"
2425
"testing"
26+
"time"
2527

2628
"github.com/arduino/arduino-cli/internal/integrationtest"
2729
"github.com/arduino/go-paths-helper"
@@ -47,6 +49,7 @@ func TestCompile(t *testing.T) {
4749
{"WithoutFqbn", compileWithoutFqbn},
4850
{"ErrorMessage", compileErrorMessage},
4951
{"WithSimpleSketch", compileWithSimpleSketch},
52+
{"WithCachePurgeNeeded", compileWithCachePurgeNeeded},
5053
{"OutputFlagDefaultPath", compileOutputFlagDefaultPath},
5154
{"WithSketchWithSymlinkSelfloop", compileWithSketchWithSymlinkSelfloop},
5255
{"BlacklistedSketchname", compileBlacklistedSketchname},
@@ -112,6 +115,35 @@ func compileErrorMessage(t *testing.T, env *integrationtest.Environment, cli *in
112115
}
113116

114117
func compileWithSimpleSketch(t *testing.T, env *integrationtest.Environment, cli *integrationtest.ArduinoCLI) {
118+
compileWithSimpleSketchCustomEnv(t, env, cli, cli.GetDefaultEnv())
119+
}
120+
121+
func compileWithCachePurgeNeeded(t *testing.T, env *integrationtest.Environment, cli *integrationtest.ArduinoCLI) {
122+
// create directories that must be purged
123+
baseDir := paths.TempDir().Join("arduino", "sketches")
124+
125+
// purge case: last used file too old
126+
oldDir1 := baseDir.Join("test_old_sketch_1")
127+
require.NoError(t, oldDir1.MkdirAll())
128+
require.NoError(t, oldDir1.Join(".last-used").WriteFile([]byte{}))
129+
require.NoError(t, oldDir1.Join(".last-used").Chtimes(time.Now(), time.Unix(0, 0)))
130+
// no purge case: last used file not existing
131+
missingFileDir := baseDir.Join("test_sketch_2")
132+
require.NoError(t, missingFileDir.MkdirAll())
133+
134+
defer oldDir1.RemoveAll()
135+
defer missingFileDir.RemoveAll()
136+
137+
customEnv := cli.GetDefaultEnv()
138+
customEnv["ARDUINO_BUILD_CACHE_COMPILATIONS_BEFORE_PURGE"] = "1"
139+
compileWithSimpleSketchCustomEnv(t, env, cli, customEnv)
140+
141+
// check that purge has been run
142+
require.NoFileExists(t, oldDir1.String())
143+
require.DirExists(t, missingFileDir.String())
144+
}
145+
146+
func compileWithSimpleSketchCustomEnv(t *testing.T, env *integrationtest.Environment, cli *integrationtest.ArduinoCLI, customEnv map[string]string) {
115147
sketchName := "CompileIntegrationTest"
116148
sketchPath := cli.SketchbookDir().Join(sketchName)
117149
defer sketchPath.RemoveAll()
@@ -127,7 +159,7 @@ func compileWithSimpleSketch(t *testing.T, env *integrationtest.Environment, cli
127159
require.NoError(t, err)
128160

129161
// Build sketch for arduino:avr:uno with json output
130-
stdout, _, err = cli.Run("compile", "-b", fqbn, sketchPath.String(), "--format", "json")
162+
stdout, _, err = cli.RunWithCustomEnv(customEnv, "compile", "-b", fqbn, sketchPath.String(), "--format", "json")
131163
require.NoError(t, err)
132164
// check is a valid json and contains requested data
133165
var compileOutput map[string]interface{}
@@ -140,7 +172,7 @@ func compileWithSimpleSketch(t *testing.T, env *integrationtest.Environment, cli
140172
md5 := md5.Sum(([]byte(sketchPath.String())))
141173
sketchPathMd5 := strings.ToUpper(hex.EncodeToString(md5[:]))
142174
require.NotEmpty(t, sketchPathMd5)
143-
buildDir := paths.TempDir().Join("arduino", "sketch-"+sketchPathMd5)
175+
buildDir := paths.TempDir().Join("arduino", "sketches", sketchPathMd5)
144176
require.FileExists(t, buildDir.Join(sketchName+".ino.eep").String())
145177
require.FileExists(t, buildDir.Join(sketchName+".ino.elf").String())
146178
require.FileExists(t, buildDir.Join(sketchName+".ino.hex").String())
@@ -374,7 +406,7 @@ func compileWithOutputDirFlag(t *testing.T, env *integrationtest.Environment, cl
374406
md5 := md5.Sum(([]byte(sketchPath.String())))
375407
sketchPathMd5 := strings.ToUpper(hex.EncodeToString(md5[:]))
376408
require.NotEmpty(t, sketchPathMd5)
377-
buildDir := paths.TempDir().Join("arduino", "sketch-"+sketchPathMd5)
409+
buildDir := paths.TempDir().Join("arduino", "sketches", sketchPathMd5)
378410
require.FileExists(t, buildDir.Join(sketchName+".ino.eep").String())
379411
require.FileExists(t, buildDir.Join(sketchName+".ino.elf").String())
380412
require.FileExists(t, buildDir.Join(sketchName+".ino.hex").String())
@@ -441,7 +473,7 @@ func compileWithCustomBuildPath(t *testing.T, env *integrationtest.Environment,
441473
md5 := md5.Sum(([]byte(sketchPath.String())))
442474
sketchPathMd5 := strings.ToUpper(hex.EncodeToString(md5[:]))
443475
require.NotEmpty(t, sketchPathMd5)
444-
buildDir := paths.TempDir().Join("arduino", "sketch-"+sketchPathMd5)
476+
buildDir := paths.TempDir().Join("arduino", "sketches", sketchPathMd5)
445477
require.NoFileExists(t, buildDir.Join(sketchName+".ino.eep").String())
446478
require.NoFileExists(t, buildDir.Join(sketchName+".ino.elf").String())
447479
require.NoFileExists(t, buildDir.Join(sketchName+".ino.hex").String())
@@ -975,7 +1007,7 @@ func compileWithInvalidBuildOptionJson(t *testing.T, env *integrationtest.Enviro
9751007
md5 := md5.Sum(([]byte(sketchPath.String())))
9761008
sketchPathMd5 := strings.ToUpper(hex.EncodeToString(md5[:]))
9771009
require.NotEmpty(t, sketchPathMd5)
978-
buildDir := paths.TempDir().Join("arduino", "sketch-"+sketchPathMd5)
1010+
buildDir := paths.TempDir().Join("arduino", "sketches", sketchPathMd5)
9791011

9801012
_, _, err = cli.Run("compile", "-b", fqbn, sketchPath.String(), "--verbose")
9811013
require.NoError(t, err)
@@ -1008,18 +1040,41 @@ func compileWithRelativeBuildPath(t *testing.T, env *integrationtest.Environment
10081040
absoluteBuildPath := cli.SketchbookDir().Join("build_path")
10091041
builtFiles, err := absoluteBuildPath.ReadDir()
10101042
require.NoError(t, err)
1011-
require.Contains(t, builtFiles[8].String(), sketchName+".ino.eep")
1012-
require.Contains(t, builtFiles[9].String(), sketchName+".ino.elf")
1013-
require.Contains(t, builtFiles[10].String(), sketchName+".ino.hex")
1014-
require.Contains(t, builtFiles[11].String(), sketchName+".ino.with_bootloader.bin")
1015-
require.Contains(t, builtFiles[12].String(), sketchName+".ino.with_bootloader.hex")
1016-
require.Contains(t, builtFiles[0].String(), "build.options.json")
1017-
require.Contains(t, builtFiles[1].String(), "compile_commands.json")
1018-
require.Contains(t, builtFiles[2].String(), "core")
1019-
require.Contains(t, builtFiles[3].String(), "includes.cache")
1020-
require.Contains(t, builtFiles[4].String(), "libraries")
1021-
require.Contains(t, builtFiles[6].String(), "preproc")
1022-
require.Contains(t, builtFiles[7].String(), "sketch")
1043+
1044+
expectedFiles := []string{
1045+
sketchName + ".ino.eep",
1046+
sketchName + ".ino.elf",
1047+
sketchName + ".ino.hex",
1048+
sketchName + ".ino.with_bootloader.bin",
1049+
sketchName + ".ino.with_bootloader.hex",
1050+
"build.options.json",
1051+
"compile_commands.json",
1052+
"core",
1053+
"includes.cache",
1054+
"libraries",
1055+
"preproc",
1056+
"sketch",
1057+
}
1058+
1059+
foundFiles := []string{}
1060+
for _, builtFile := range builtFiles {
1061+
if sliceIncludes(expectedFiles, builtFile.Base()) {
1062+
foundFiles = append(foundFiles, builtFile.Base())
1063+
}
1064+
}
1065+
sort.Strings(expectedFiles)
1066+
sort.Strings(foundFiles)
1067+
require.Equal(t, expectedFiles, foundFiles)
1068+
}
1069+
1070+
// TODO: remove this when a generic library is introduced
1071+
func sliceIncludes[T comparable](slice []T, target T) bool {
1072+
for _, e := range slice {
1073+
if e == target {
1074+
return true
1075+
}
1076+
}
1077+
return false
10231078
}
10241079

10251080
func compileWithFakeSecureBootCore(t *testing.T, env *integrationtest.Environment, cli *integrationtest.ArduinoCLI) {

‎internal/integrationtest/compile_2/compile_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ func recompileWithDifferentLibrary(t *testing.T, env *integrationtest.Environmen
145145
md5 := md5.Sum(([]byte(sketchPath.String())))
146146
sketchPathMd5 := strings.ToUpper(hex.EncodeToString(md5[:]))
147147
require.NotEmpty(t, sketchPathMd5)
148-
buildDir := paths.TempDir().Join("arduino", "sketch-"+sketchPathMd5)
148+
buildDir := paths.TempDir().Join("arduino", "sketches", sketchPathMd5)
149149

150150
// Compile sketch using library not managed by CLI
151151
stdout, _, err := cli.Run("compile", "-b", fqbn, "--library", manuallyInstalledLibPath.String(), sketchPath.String(), "-v")

‎internal/integrationtest/core/core_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ func TestCoreInstallEsp32(t *testing.T) {
253253
md5 := md5.Sum(([]byte(sketchPath.String())))
254254
sketchPathMd5 := strings.ToUpper(hex.EncodeToString(md5[:]))
255255
require.NotEmpty(t, sketchPathMd5)
256-
buildDir := paths.TempDir().Join("arduino", "sketch-"+sketchPathMd5)
256+
buildDir := paths.TempDir().Join("arduino", "sketches", sketchPathMd5)
257257
require.FileExists(t, buildDir.Join(sketchName+".ino.partitions.bin").String())
258258
}
259259

‎internal/integrationtest/upload_mock/upload_mock_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -697,7 +697,7 @@ func TestUploadSketch(t *testing.T) {
697697
func generateBuildDir(sketchPath *paths.Path, t *testing.T) *paths.Path {
698698
md5 := md5.Sum(([]byte(sketchPath.String())))
699699
sketchPathMd5 := strings.ToUpper(hex.EncodeToString(md5[:]))
700-
buildDir := paths.TempDir().Join("arduino", "sketch-"+sketchPathMd5)
700+
buildDir := paths.TempDir().Join("arduino", "sketches", sketchPathMd5)
701701
require.NoError(t, buildDir.MkdirAll())
702702
require.NoError(t, buildDir.ToAbs())
703703
return buildDir

‎legacy/builder/add_additional_entries_to_context.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ func (*AddAdditionalEntriesToContext) Run(ctx *types.Context) error {
3535
if err != nil {
3636
return errors.WithStack(err)
3737
}
38-
librariesBuildPath, err := buildPath.Join("libraries").Abs()
38+
librariesBuildPath, err := buildPath.Join(constants.FOLDER_LIBRARIES).Abs()
3939
if err != nil {
4040
return errors.WithStack(err)
4141
}

‎legacy/builder/constants/constants.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ const FOLDER_CORE = "core"
4747
const FOLDER_PREPROC = "preproc"
4848
const FOLDER_SKETCH = "sketch"
4949
const FOLDER_TOOLS = "tools"
50+
const FOLDER_LIBRARIES = "libraries"
5051
const LIBRARY_ALL_ARCHS = "*"
5152
const LIBRARY_EMAIL = "email"
5253
const LIBRARY_FOLDER_ARCH = "arch"

‎legacy/builder/phases/core_builder.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"os"
2020
"strings"
2121

22+
"github.com/arduino/arduino-cli/buildcache"
2223
"github.com/arduino/arduino-cli/i18n"
2324
"github.com/arduino/arduino-cli/legacy/builder/builder_utils"
2425
"github.com/arduino/arduino-cli/legacy/builder/constants"
@@ -92,9 +93,10 @@ func compileCore(ctx *types.Context, buildPath *paths.Path, buildCachePath *path
9293

9394
var targetArchivedCore *paths.Path
9495
if buildCachePath != nil {
95-
archivedCoreName := GetCachedCoreArchiveFileName(buildProperties.Get(constants.BUILD_PROPERTIES_FQBN),
96+
archivedCoreName := GetCachedCoreArchiveDirName(buildProperties.Get(constants.BUILD_PROPERTIES_FQBN),
9697
buildProperties.Get("compiler.optimization_flags"), realCoreFolder)
97-
targetArchivedCore = buildCachePath.Join(archivedCoreName)
98+
buildcache.GetOrCreate(buildCachePath.Join(archivedCoreName))
99+
targetArchivedCore = buildCachePath.Join(archivedCoreName, "core.a")
98100
canUseArchivedCore := !ctx.OnlyUpdateCompilationDatabase &&
99101
!ctx.Clean &&
100102
!builder_utils.CoreOrReferencedCoreHasChanged(realCoreFolder, targetCoreFolder, targetArchivedCore)
@@ -137,19 +139,19 @@ func compileCore(ctx *types.Context, buildPath *paths.Path, buildCachePath *path
137139
return archiveFile, variantObjectFiles, nil
138140
}
139141

140-
// GetCachedCoreArchiveFileName returns the filename to be used to store
142+
// GetCachedCoreArchiveDirName returns the directory name to be used to store
141143
// the global cached core.a.
142-
func GetCachedCoreArchiveFileName(fqbn string, optimizationFlags string, coreFolder *paths.Path) string {
144+
func GetCachedCoreArchiveDirName(fqbn string, optimizationFlags string, coreFolder *paths.Path) string {
143145
fqbnToUnderscore := strings.Replace(fqbn, ":", "_", -1)
144146
fqbnToUnderscore = strings.Replace(fqbnToUnderscore, "=", "_", -1)
145147
if absCoreFolder, err := coreFolder.Abs(); err == nil {
146148
coreFolder = absCoreFolder
147149
} // silently continue if absolute path can't be detected
148150
hash := utils.MD5Sum([]byte(coreFolder.String() + optimizationFlags))
149-
realName := "core_" + fqbnToUnderscore + "_" + hash + ".a"
151+
realName := fqbnToUnderscore + "_" + hash
150152
if len(realName) > 100 {
151-
// avoid really long names, simply hash the final part
152-
realName = "core_" + utils.MD5Sum([]byte(fqbnToUnderscore+"_"+hash)) + ".a"
153+
// avoid really long names, simply hash the name
154+
realName = utils.MD5Sum([]byte(fqbnToUnderscore + "_" + hash))
153155
}
154156
return realName
155157
}

‎legacy/builder/test/builder_test.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -380,10 +380,13 @@ func TestBuilderCacheCoreAFile(t *testing.T) {
380380

381381
// Pick timestamp of cached core
382382
coreFolder := paths.New("downloaded_hardware", "arduino", "avr")
383-
coreFileName := phases.GetCachedCoreArchiveFileName(ctx.FQBN.String(), ctx.OptimizationFlags, coreFolder)
384-
cachedCoreFile := ctx.CoreBuildCachePath.Join(coreFileName)
383+
coreFileName := phases.GetCachedCoreArchiveDirName(ctx.FQBN.String(), ctx.OptimizationFlags, coreFolder)
384+
cachedCoreFile := ctx.CoreBuildCachePath.Join(coreFileName, "core.a")
385385
coreStatBefore, err := cachedCoreFile.Stat()
386386
require.NoError(t, err)
387+
lastUsedFile := ctx.CoreBuildCachePath.Join(coreFileName, ".last-used")
388+
_, err = lastUsedFile.Stat()
389+
require.NoError(t, err)
387390

388391
// Run build again, to verify that the builder skips rebuilding core.a
389392
err = bldr.Run(ctx)

0 commit comments

Comments
 (0)
Please sign in to comment.