Permalink
Show file tree
Hide file tree
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
17 changed files
with
333 additions
and
33 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -28,3 +28,6 @@ venv | ||
| /docsgen/arduino-cli.exe | ||
| /docs/rpc/*.md | ||
| /docs/commands/*.md | ||
|
|
||
| # Delve debugger binary file | ||
| __debug_bin | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,113 @@ | ||
| // 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 [email protected]. | ||
|
|
||
| package buildcache | ||
|
|
||
| import ( | ||
| "time" | ||
|
|
||
| "github.com/arduino/go-paths-helper" | ||
| "github.com/pkg/errors" | ||
| "github.com/sirupsen/logrus" | ||
| ) | ||
|
|
||
| const ( | ||
| createDirErrCode = 1 | ||
| fileWriteErrCode = 2 | ||
| ) | ||
|
|
||
| type cacheError struct { | ||
| Code int | ||
| wrappedErr error | ||
| } | ||
|
|
||
| func (e cacheError) Error() string { | ||
| return e.wrappedErr.Error() | ||
| } | ||
|
|
||
| func (e cacheError) Unwrap() error { | ||
| return e.wrappedErr | ||
| } | ||
|
|
||
| func (e cacheError) Is(target error) bool { | ||
| te, ok := target.(cacheError) | ||
| return ok && te.Code == e.Code | ||
| } | ||
|
|
||
| var ( | ||
| // CreateDirErr error occurred when creating the cache directory | ||
| CreateDirErr = cacheError{Code: createDirErrCode} | ||
| // FileWriteErr error occurred when writing the placeholder file | ||
| FileWriteErr = cacheError{Code: fileWriteErrCode} | ||
| ) | ||
|
|
||
| const lastUsedFileName = ".last-used" | ||
|
|
||
| // BuildCache represents a cache of built files (sketches and cores), it's designed | ||
| // to work on directories. Given a directory as "base" it handles direct subdirectories as | ||
| // keys | ||
| type BuildCache struct { | ||
| baseDir *paths.Path | ||
| } | ||
|
|
||
| // GetOrCreate retrieves or creates the cache directory at the given path | ||
| // If the cache already exists the lifetime of the cache is extended. | ||
| func (bc *BuildCache) GetOrCreate(key string) (*paths.Path, error) { | ||
| keyDir := bc.baseDir.Join(key) | ||
| if err := keyDir.MkdirAll(); err != nil { | ||
| return nil, cacheError{createDirErrCode, err} | ||
| } | ||
|
|
||
| if err := keyDir.Join(lastUsedFileName).WriteFile([]byte{}); err != nil { | ||
| return nil, cacheError{fileWriteErrCode, err} | ||
| } | ||
| return keyDir, nil | ||
| } | ||
|
|
||
| // 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 (bc *BuildCache) Purge(ttl time.Duration) { | ||
| files, err := bc.baseDir.ReadDir() | ||
| if err != nil { | ||
| return | ||
| } | ||
| for _, file := range files { | ||
| if file.IsDir() { | ||
| removeIfExpired(file, ttl) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // New instantiates a build cache | ||
| func New(baseDir *paths.Path) *BuildCache { | ||
| return &BuildCache{baseDir} | ||
| } | ||
|
|
||
| func removeIfExpired(dir *paths.Path, ttl time.Duration) { | ||
| fileInfo, err := dir.Join(lastUsedFileName).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)) | ||
| } | ||
| } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| // 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 [email protected]. | ||
|
|
||
| package buildcache | ||
|
|
||
| import ( | ||
| "testing" | ||
| "time" | ||
|
|
||
| "github.com/arduino/go-paths-helper" | ||
| "github.com/stretchr/testify/require" | ||
| ) | ||
|
|
||
| func Test_UpdateLastUsedFileNotExisting(t *testing.T) { | ||
| testBuildDir := paths.New(t.TempDir(), "sketches", "xxx") | ||
| require.NoError(t, testBuildDir.MkdirAll()) | ||
| timeBeforeUpdating := time.Unix(0, 0) | ||
| requireCorrectUpdate(t, testBuildDir, timeBeforeUpdating) | ||
| } | ||
|
|
||
| func Test_UpdateLastUsedFileExisting(t *testing.T) { | ||
| testBuildDir := paths.New(t.TempDir(), "sketches", "xxx") | ||
| require.NoError(t, testBuildDir.MkdirAll()) | ||
|
|
||
| // create the file | ||
| preExistingFile := testBuildDir.Join(lastUsedFileName) | ||
| require.NoError(t, preExistingFile.WriteFile([]byte{})) | ||
| timeBeforeUpdating := time.Now().Add(-time.Second) | ||
| preExistingFile.Chtimes(time.Now(), timeBeforeUpdating) | ||
| requireCorrectUpdate(t, testBuildDir, timeBeforeUpdating) | ||
| } | ||
|
|
||
| func requireCorrectUpdate(t *testing.T, dir *paths.Path, prevModTime time.Time) { | ||
| _, err := New(dir.Parent()).GetOrCreate(dir.Base()) | ||
| require.NoError(t, err) | ||
| expectedFile := dir.Join(lastUsedFileName) | ||
| fileInfo, err := expectedFile.Stat() | ||
| require.Nil(t, err) | ||
| require.Greater(t, fileInfo.ModTime(), prevModTime) | ||
| } | ||
|
|
||
| func TestPurge(t *testing.T) { | ||
| ttl := time.Minute | ||
|
|
||
| dirToPurge := paths.New(t.TempDir(), "root") | ||
|
|
||
| lastUsedTimesByDirPath := map[*paths.Path]time.Time{ | ||
| (dirToPurge.Join("old")): time.Now().Add(-ttl - time.Hour), | ||
| (dirToPurge.Join("fresh")): time.Now().Add(-ttl + time.Minute), | ||
| } | ||
|
|
||
| // create the metadata files | ||
| for dirPath, lastUsedTime := range lastUsedTimesByDirPath { | ||
| require.NoError(t, dirPath.MkdirAll()) | ||
| infoFilePath := dirPath.Join(lastUsedFileName).Canonical() | ||
| require.NoError(t, infoFilePath.WriteFile([]byte{})) | ||
| // make sure access time does not matter | ||
| accesstime := time.Now() | ||
| require.NoError(t, infoFilePath.Chtimes(accesstime, lastUsedTime)) | ||
| } | ||
|
|
||
| New(dirToPurge).Purge(ttl) | ||
|
|
||
| require.False(t, dirToPurge.Join("old").Exist()) | ||
| require.True(t, dirToPurge.Join("fresh").Exist()) | ||
| } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.

