diff --git a/buildcache/build_cache.go b/buildcache/build_cache.go index f9119ce96c8..64bb57c924a 100644 --- a/buildcache/build_cache.go +++ b/buildcache/build_cache.go @@ -19,20 +19,51 @@ import ( "time" "github.com/arduino/go-paths-helper" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" ) -// GetOrCreate retrieves or creates the cache directory for the given key inside basePath. +const lastUsedFileName = ".last-used" + +// GetOrCreate retrieves or creates the cache directory at the given path // If the cache already exists the lifetime of the cache is extended. -func GetOrCreate(dir *paths.Path, key string) error { - unusedTTL := time.Hour - _, err := newDirectoryCache(dir, unusedTTL). - GetOrCreate(key, time.Now()) - return err +func GetOrCreate(dir *paths.Path) error { + if !dir.Exist() { + if err := dir.MkdirAll(); err != nil { + return err + } + } + + return dir.Join(lastUsedFileName).WriteFile([]byte{}) } // Purge removes all cache directories within baseDir that have expired // To know how long ago a directory has been last used // it checks into the .last-used file. func Purge(baseDir *paths.Path, ttl time.Duration) { - newDirectoryCache(baseDir, ttl).Purge() + files, err := baseDir.ReadDir() + if err != nil { + return + } + for _, file := range files { + if file.IsDir() { + removeIfExpired(file, ttl) + } + } +} + +func removeIfExpired(dir *paths.Path, ttl time.Duration) { + fileInfo, err := dir.Join().Stat() + if err != nil { + return + } + lifeExpectancy := ttl - time.Since(fileInfo.ModTime()) + if lifeExpectancy > 0 { + return + } + logrus.Tracef(`Purging cache directory "%s". Expired by %s`, dir, lifeExpectancy.Abs()) + err = dir.RemoveAll() + if err != nil { + logrus.Tracef(`Error while pruning cache directory "%s": %s`, dir, errors.WithStack(err)) + } } diff --git a/buildcache/build_cache_test.go b/buildcache/build_cache_test.go index f57fcdc421e..5007be66a57 100644 --- a/buildcache/build_cache_test.go +++ b/buildcache/build_cache_test.go @@ -43,7 +43,7 @@ func Test_UpdateLastUsedFileExisting(t *testing.T) { } func requireCorrectUpdate(t *testing.T, dir *paths.Path, prevModTime time.Time) { - require.NoError(t, GetOrCreate(dir.Parent(), dir.Base())) + require.NoError(t, GetOrCreate(dir)) expectedFile := dir.Join(lastUsedFileName) fileInfo, err := expectedFile.Stat() require.Nil(t, err) diff --git a/buildcache/directory_cache.go b/buildcache/directory_cache.go deleted file mode 100644 index c9762a0070f..00000000000 --- a/buildcache/directory_cache.go +++ /dev/null @@ -1,100 +0,0 @@ -// This file is part of arduino-cli. -// -// Copyright 2020 ARDUINO SA (http://www.arduino.cc/) -// -// This software is released under the GNU General Public License version 3, -// which covers the main part of arduino-cli. -// The terms of this license can be found at: -// https://www.gnu.org/licenses/gpl-3.0.en.html -// -// You can be released from the requirements of the above licenses by purchasing -// a commercial license. Buying such a license is mandatory if you want to -// modify or otherwise use the software for commercial activities involving the -// Arduino software without disclosing the source code of your own applications. -// To purchase a commercial license, send an email to license@arduino.cc. - -package buildcache - -import ( - "time" - - "github.com/arduino/go-paths-helper" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" -) - -const ( - lastUsedFileName = ".last-used" -) - -type directoryCache struct { - baseDir *paths.Path - ttl time.Duration -} - -func (dc *directoryCache) isExpired(key string) time.Duration { - modTime, _ := dc.modTime(key) - return dc.ttl - time.Since(modTime) -} - -func (dc *directoryCache) modTime(key string) (time.Time, error) { - fileInfo, err := dc.baseDir.Join(key, lastUsedFileName).Stat() - if err != nil { - // folders with a missing last used file are not purged - return time.Now().Add(time.Minute), err - } - - return fileInfo.ModTime(), nil -} - -func (dc *directoryCache) GetOrCreate(key string, value time.Time) (time.Time, error) { - existing := true - modTime, err := dc.modTime(key) - if err != nil { - existing = false - } - - subDir := dc.baseDir.Join(key) - err = subDir.MkdirAll() - if err != nil { - return modTime, err - } - err = subDir.Join(lastUsedFileName).WriteFile([]byte{}) - if err != nil || existing { - return modTime, err - } - return time.Now(), nil -} - -func (dc *directoryCache) Purge() error { - files, err := dc.baseDir.ReadDir() - if err != nil { - return err - } - for _, file := range files { - if file.IsDir() { - dc.removeIfExpired(file) - } - } - return nil -} - -func (dc *directoryCache) removeIfExpired(dir *paths.Path) { - lifeExpectancy := dc.isExpired(dir.Base()) - if lifeExpectancy > 0 { - return - } - logrus.Tracef(`Purging cache directory "%s". Expired by %s`, dir, lifeExpectancy) - err := dir.RemoveAll() - - if err != nil { - logrus.Tracef(`Error while pruning cache directory "%s": %s`, dir, errors.WithStack(err)) - } -} - -func newDirectoryCache(baseDir *paths.Path, ttl time.Duration) *directoryCache { - return &directoryCache{ - baseDir: baseDir, - ttl: ttl, - } -} diff --git a/commands/compile/compile.go b/commands/compile/compile.go index 91975d5f101..aeea6ec61bc 100644 --- a/commands/compile/compile.go +++ b/commands/compile/compile.go @@ -138,7 +138,7 @@ func Compile(ctx context.Context, req *rpc.CompileRequest, outStream, errStream return nil, &arduino.PermissionDeniedError{Message: tr("Cannot create build directory"), Cause: err} } - buildcache.GetOrCreate(builderCtx.BuildPath.Parent(), builderCtx.BuildPath.Base()) + buildcache.GetOrCreate(builderCtx.BuildPath) // cache is purged after compilation to not remove entries that might be required defer maybePurgeBuildCache() diff --git a/legacy/builder/phases/core_builder.go b/legacy/builder/phases/core_builder.go index 1ee53f2383a..ecd2a327861 100644 --- a/legacy/builder/phases/core_builder.go +++ b/legacy/builder/phases/core_builder.go @@ -95,7 +95,7 @@ func compileCore(ctx *types.Context, buildPath *paths.Path, buildCachePath *path if buildCachePath != nil { archivedCoreName := GetCachedCoreArchiveDirName(buildProperties.Get(constants.BUILD_PROPERTIES_FQBN), buildProperties.Get("compiler.optimization_flags"), realCoreFolder) - buildcache.GetOrCreate(buildCachePath, archivedCoreName) + buildcache.GetOrCreate(buildCachePath.Join(archivedCoreName)) targetArchivedCore = buildCachePath.Join(archivedCoreName, "core.a") canUseArchivedCore := !ctx.OnlyUpdateCompilationDatabase && !ctx.Clean &&