Skip to content

Commit dba1f01

Browse files
committed
Implemented a first draft of library caching
1 parent 7226450 commit dba1f01

File tree

3 files changed

+89
-1
lines changed

3 files changed

+89
-1
lines changed

commands/service_compile.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ func (s *arduinoCoreServerImpl) Compile(req *rpc.CompileRequest, stream rpc.Ardu
248248
boardBuildProperties,
249249
buildPath,
250250
req.GetOptimizeForDebug(),
251+
buildCachePath.Join("libraries"),
251252
coreBuildCachePath,
252253
extraCoreBuildCachePaths,
253254
int(req.GetJobs()),

internal/arduino/builder/builder.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@ type Builder struct {
6161
// Custom build properties defined by user (line by line as "key=value" pairs)
6262
customBuildProperties []string
6363

64-
// core related
64+
// cache related
65+
librariesBuildCachePath *paths.Path
6566
coreBuildCachePath *paths.Path
6667
extraCoreBuildCachePaths paths.PathList
6768

@@ -121,6 +122,7 @@ func NewBuilder(
121122
boardBuildProperties *properties.Map,
122123
buildPath *paths.Path,
123124
optimizeForDebug bool,
125+
librariesBuildCachePath *paths.Path,
124126
coreBuildCachePath *paths.Path,
125127
extraCoreBuildCachePaths paths.PathList,
126128
jobs int,
@@ -212,6 +214,7 @@ func NewBuilder(
212214
librariesBuildPath: librariesBuildPath,
213215
jobs: jobs,
214216
customBuildProperties: customBuildPropertiesArgs,
217+
librariesBuildCachePath: librariesBuildCachePath,
215218
coreBuildCachePath: coreBuildCachePath,
216219
extraCoreBuildCachePaths: extraCoreBuildCachePaths,
217220
logger: logger,

internal/arduino/builder/libraries.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,19 @@
1616
package builder
1717

1818
import (
19+
"crypto/md5"
20+
"encoding/hex"
21+
"errors"
22+
"os"
1923
"slices"
2024
"strings"
2125
"time"
2226

2327
f "github.com/arduino/arduino-cli/internal/algorithms"
2428
"github.com/arduino/arduino-cli/internal/arduino/builder/cpp"
29+
"github.com/arduino/arduino-cli/internal/arduino/builder/internal/utils"
2530
"github.com/arduino/arduino-cli/internal/arduino/libraries"
31+
"github.com/arduino/arduino-cli/internal/buildcache"
2632
"github.com/arduino/arduino-cli/internal/i18n"
2733
"github.com/arduino/go-paths-helper"
2834
"github.com/arduino/go-properties-orderedmap"
@@ -128,11 +134,73 @@ func (b *Builder) compileLibraries(libraries libraries.List, includes []string)
128134
return objectFiles, nil
129135
}
130136

137+
// getCachedLibraryArchiveDirName returns the directory name to be used to store
138+
// the global cached lib.a.
139+
func getCachedLibraryArchiveDirName(fqbn string, optimizationFlags string, libFolder *paths.Path) string {
140+
fqbnToUnderscore := strings.ReplaceAll(fqbn, ":", "_")
141+
fqbnToUnderscore = strings.ReplaceAll(fqbnToUnderscore, "=", "_")
142+
if absCoreFolder, err := libFolder.Abs(); err == nil {
143+
libFolder = absCoreFolder
144+
} // silently continue if absolute path can't be detected
145+
146+
md5Sum := func(data []byte) string {
147+
md5sumBytes := md5.Sum(data)
148+
return hex.EncodeToString(md5sumBytes[:])
149+
}
150+
hash := md5Sum([]byte(libFolder.String() + optimizationFlags))
151+
realName := libFolder.Base() + "_" + fqbnToUnderscore + "_" + hash
152+
if len(realName) > 100 {
153+
// avoid really long names, simply hash the name again
154+
realName = md5Sum([]byte(realName))
155+
}
156+
return realName
157+
}
158+
131159
func (b *Builder) compileLibrary(library *libraries.Library, includes []string) (paths.PathList, error) {
132160
if b.logger.Verbose() {
133161
b.logger.Info(i18n.Tr(`Compiling library "%[1]s"`, library.Name))
134162
}
135163

164+
var targetArchivedLibrary *paths.Path
165+
if b.librariesBuildCachePath != nil {
166+
archivedLibName := getCachedLibraryArchiveDirName(
167+
b.buildProperties.Get("build.fqbn"),
168+
b.buildProperties.Get("compiler.optimization_flags"),
169+
library.InstallDir,
170+
)
171+
172+
canUseArchivedLib := func(archivedLib *paths.Path) bool {
173+
if b.onlyUpdateCompilationDatabase || b.clean {
174+
return false
175+
}
176+
if isOlder, err := utils.DirContentIsOlderThan(library.InstallDir, archivedLib); err != nil || !isOlder {
177+
// Recreate the archive if ANY of the library files has changed
178+
return false
179+
}
180+
return true
181+
}
182+
183+
targetArchivedLibrary = b.librariesBuildCachePath.Join(archivedLibName, "lib.a")
184+
if canUseArchivedLib(targetArchivedLibrary) {
185+
// Extend the build cache expiration time
186+
if _, err := buildcache.New(b.librariesBuildCachePath).GetOrCreate(archivedLibName); errors.Is(err, buildcache.CreateDirErr) {
187+
return nil, errors.New(i18n.Tr("creating libraries cache folder: %s", err))
188+
}
189+
// use archived core
190+
if b.logger.Verbose() {
191+
b.logger.Info(i18n.Tr("Using precompiled library: %[1]s", targetArchivedLibrary))
192+
}
193+
res := paths.NewPathList()
194+
res.Add(targetArchivedLibrary)
195+
return res, nil
196+
}
197+
198+
// Create the build cache folder for the core
199+
if _, err := buildcache.New(b.librariesBuildCachePath).GetOrCreate(archivedLibName); errors.Is(err, buildcache.CreateDirErr) {
200+
return nil, errors.New(i18n.Tr("creating libraries cache folder: %s", err))
201+
}
202+
}
203+
136204
libraryBuildPath := b.librariesBuildPath.Join(library.DirName)
137205
if err := libraryBuildPath.MkdirAll(); err != nil {
138206
return nil, err
@@ -246,6 +314,22 @@ func (b *Builder) compileLibrary(library *libraries.Library, includes []string)
246314
return nil, err
247315
}
248316
archiveFiles.Add(archiveFile)
317+
318+
// archive lib.a
319+
if targetArchivedLibrary != nil && !b.onlyUpdateCompilationDatabase {
320+
err := archiveFile.CopyTo(targetArchivedLibrary)
321+
if b.logger.Verbose() {
322+
if err == nil {
323+
b.logger.Info(i18n.Tr("Archiving built lib (caching) in: %[1]s", targetArchivedLibrary))
324+
} else if os.IsNotExist(err) {
325+
b.logger.Info(i18n.Tr("Unable to cache built lib, please tell %[1]s maintainers to follow %[2]s",
326+
b.actualPlatform,
327+
"https://arduino.github.io/arduino-cli/latest/platform-specification/#recipes-to-build-the-corea-archive-file"))
328+
} else {
329+
b.logger.Info(i18n.Tr("Error archiving built lib (caching) in %[1]s: %[2]s", targetArchivedLibrary, err))
330+
}
331+
}
332+
}
249333
}
250334

251335
return archiveFiles, nil

0 commit comments

Comments
 (0)