Skip to content

Commit cba02e8

Browse files
authored
Improved compile speed by running multi-threaded library discovery. (#2625)
* Simplified error reporting in library detection There is no need to duplicate the preprocessResult/Err variables. This also simplifies naming making it more straighforward. * Remove useless targetFilePath variable * Slight improvement of removeBuildFromSketchFiles * Rename variables for clarity * Removed hardcoded build.warn_data_percentage in build.options file Also fixed the "low memory" warning printer. * Renamed variables for clarity * Renamed variables for clarity * Pre-compute sourceFile fields, and save the in the includes.cache * Added ObjFileIsUpToDate method to sourceFile * Implemented parallel task runner * Simplify use of properties.SplitQuotedString The new release of the library allow ignoring the returned error. arduino/go-properties-orderedmap#42 * Use runner.Task in GCC preprocessor It slightly simplifies code, but also provide the basis for the next commits. * Parallelize library discovery phase in compile * The number of jobs in library detection now follows --jobs flag * Reordered properties construction for clarity * Reordered compileFileWithRecipe for clarity * Added integration test * fix: libraries are recompiled if the list of include paths changes
1 parent 699ddc0 commit cba02e8

File tree

25 files changed

+665
-290
lines changed

25 files changed

+665
-290
lines changed

commands/service_compile.go

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ func (s *arduinoCoreServerImpl) Compile(req *rpc.CompileRequest, stream rpc.Ardu
169169
if buildPathArg := req.GetBuildPath(); buildPathArg != "" {
170170
buildPath = paths.New(req.GetBuildPath()).Canonical()
171171
if in, _ := buildPath.IsInsideDir(sk.FullPath); in && buildPath.IsDir() {
172-
if sk.AdditionalFiles, err = removeBuildFromSketchFiles(sk.AdditionalFiles, buildPath); err != nil {
172+
if sk.AdditionalFiles, err = removeBuildPathFromSketchFiles(sk.AdditionalFiles, buildPath); err != nil {
173173
return err
174174
}
175175
}
@@ -220,10 +220,6 @@ func (s *arduinoCoreServerImpl) Compile(req *rpc.CompileRequest, stream rpc.Ardu
220220
return err
221221
}
222222

223-
actualPlatform := buildPlatform
224-
otherLibrariesDirs := paths.NewPathList(req.GetLibraries()...)
225-
otherLibrariesDirs.Add(s.settings.LibrariesDir())
226-
227223
var libsManager *librariesmanager.LibrariesManager
228224
if profile != nil {
229225
libsManager = lm
@@ -253,6 +249,11 @@ func (s *arduinoCoreServerImpl) Compile(req *rpc.CompileRequest, stream rpc.Ardu
253249
if req.GetVerbose() {
254250
verbosity = logger.VerbosityVerbose
255251
}
252+
253+
librariesDirs := paths.NewPathList(req.GetLibraries()...) // Array of collection of libraries directories
254+
librariesDirs.Add(s.settings.LibrariesDir())
255+
libraryDirs := paths.NewPathList(req.GetLibrary()...) // Array of single-library directories
256+
256257
sketchBuilder, err := builder.NewBuilder(
257258
ctx,
258259
sk,
@@ -264,16 +265,16 @@ func (s *arduinoCoreServerImpl) Compile(req *rpc.CompileRequest, stream rpc.Ardu
264265
int(req.GetJobs()),
265266
req.GetBuildProperties(),
266267
s.settings.HardwareDirectories(),
267-
otherLibrariesDirs,
268+
librariesDirs,
268269
s.settings.IDEBuiltinLibrariesDir(),
269270
fqbn,
270271
req.GetClean(),
271272
req.GetSourceOverride(),
272273
req.GetCreateCompilationDatabaseOnly(),
273-
targetPlatform, actualPlatform,
274+
targetPlatform, buildPlatform,
274275
req.GetSkipLibrariesDiscovery(),
275276
libsManager,
276-
paths.NewPathList(req.GetLibrary()...),
277+
libraryDirs,
277278
outStream, errStream, verbosity, req.GetWarnings(),
278279
progressCB,
279280
pme.GetEnvVarsForSpawnedProcess(),
@@ -462,15 +463,15 @@ func maybePurgeBuildCache(compilationsBeforePurge uint, cacheTTL time.Duration)
462463
buildcache.New(paths.TempDir().Join("arduino", "sketches")).Purge(cacheTTL)
463464
}
464465

465-
// removeBuildFromSketchFiles removes the files contained in the build directory from
466+
// removeBuildPathFromSketchFiles removes the files contained in the build directory from
466467
// the list of the sketch files
467-
func removeBuildFromSketchFiles(files paths.PathList, build *paths.Path) (paths.PathList, error) {
468+
func removeBuildPathFromSketchFiles(files paths.PathList, build *paths.Path) (paths.PathList, error) {
468469
var res paths.PathList
469470
ignored := false
470471
for _, file := range files {
471472
if isInside, _ := file.IsInsideDir(build); !isInside {
472-
res = append(res, file)
473-
} else if !ignored {
473+
res.Add(file)
474+
} else {
474475
ignored = true
475476
}
476477
}

commands/service_monitor.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ import (
2727
"github.com/arduino/arduino-cli/internal/arduino/cores"
2828
"github.com/arduino/arduino-cli/internal/arduino/cores/packagemanager"
2929
pluggableMonitor "github.com/arduino/arduino-cli/internal/arduino/monitor"
30-
"github.com/arduino/arduino-cli/internal/i18n"
3130
"github.com/arduino/arduino-cli/pkg/fqbn"
3231
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
3332
"github.com/arduino/go-properties-orderedmap"
@@ -265,10 +264,7 @@ func findMonitorAndSettingsForProtocolAndBoard(pme *packagemanager.Explorer, pro
265264
} else if recipe, ok := boardPlatform.MonitorsDevRecipes[protocol]; ok {
266265
// If we have a recipe we must resolve it
267266
cmdLine := boardProperties.ExpandPropsInString(recipe)
268-
cmdArgs, err := properties.SplitQuotedString(cmdLine, `"'`, false)
269-
if err != nil {
270-
return nil, nil, &cmderrors.InvalidArgumentError{Message: i18n.Tr("Invalid recipe in platform.txt"), Cause: err}
271-
}
267+
cmdArgs, _ := properties.SplitQuotedString(cmdLine, `"'`, false)
272268
id := fmt.Sprintf("%s-%s", boardPlatform, protocol)
273269
return pluggableMonitor.New(id, cmdArgs...), boardSettings, nil
274270
}

commands/service_upload.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -720,10 +720,7 @@ func runTool(ctx context.Context, recipeID string, props *properties.Map, outStr
720720
return errors.New(i18n.Tr("no upload port provided"))
721721
}
722722
cmdLine := props.ExpandPropsInString(recipe)
723-
cmdArgs, err := properties.SplitQuotedString(cmdLine, `"'`, false)
724-
if err != nil {
725-
return errors.New(i18n.Tr("invalid recipe '%[1]s': %[2]s", recipe, err))
726-
}
723+
cmdArgs, _ := properties.SplitQuotedString(cmdLine, `"'`, false)
727724

728725
// Run Tool
729726
logrus.WithField("phase", "upload").Tracef("Executing upload tool: %s", cmdLine)

internal/arduino/builder/builder.go

Lines changed: 41 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,6 @@ type Builder struct {
5959
// Parallel processes
6060
jobs int
6161

62-
// Custom build properties defined by user (line by line as "key=value" pairs)
63-
customBuildProperties []string
64-
6562
// core related
6663
coreBuildCachePath *paths.Path
6764
extraCoreBuildCachePaths paths.PathList
@@ -89,7 +86,7 @@ type Builder struct {
8986
lineOffset int
9087

9188
targetPlatform *cores.PlatformRelease
92-
actualPlatform *cores.PlatformRelease
89+
buildPlatform *cores.PlatformRelease
9390

9491
buildArtifacts *buildArtifacts
9592

@@ -125,19 +122,20 @@ func NewBuilder(
125122
coreBuildCachePath *paths.Path,
126123
extraCoreBuildCachePaths paths.PathList,
127124
jobs int,
128-
requestBuildProperties []string,
129-
hardwareDirs, otherLibrariesDirs paths.PathList,
125+
customBuildProperties []string,
126+
hardwareDirs paths.PathList,
127+
librariesDirs paths.PathList,
130128
builtInLibrariesDirs *paths.Path,
131129
fqbn *fqbn.FQBN,
132130
clean bool,
133131
sourceOverrides map[string]string,
134132
onlyUpdateCompilationDatabase bool,
135-
targetPlatform, actualPlatform *cores.PlatformRelease,
133+
targetPlatform, buildPlatform *cores.PlatformRelease,
136134
useCachedLibrariesResolution bool,
137135
librariesManager *librariesmanager.LibrariesManager,
138-
libraryDirs paths.PathList,
136+
customLibraryDirs paths.PathList,
139137
stdout, stderr io.Writer, verbosity logger.Verbosity, warningsLevel string,
140-
progresCB rpc.TaskProgressCB,
138+
progressCB rpc.TaskProgressCB,
141139
toolEnv []string,
142140
) (*Builder, error) {
143141
buildProperties := properties.NewMap()
@@ -146,14 +144,12 @@ func NewBuilder(
146144
}
147145
if sk != nil {
148146
buildProperties.SetPath("sketch_path", sk.FullPath)
147+
buildProperties.Set("build.project_name", sk.MainFile.Base())
148+
buildProperties.SetPath("build.source.path", sk.FullPath)
149149
}
150150
if buildPath != nil {
151151
buildProperties.SetPath("build.path", buildPath)
152152
}
153-
if sk != nil {
154-
buildProperties.Set("build.project_name", sk.MainFile.Base())
155-
buildProperties.SetPath("build.source.path", sk.FullPath)
156-
}
157153
if optimizeForDebug {
158154
if debugFlags, ok := buildProperties.GetOk("compiler.optimization_flags.debug"); ok {
159155
buildProperties.Set("compiler.optimization_flags", debugFlags)
@@ -165,12 +161,11 @@ func NewBuilder(
165161
}
166162

167163
// Add user provided custom build properties
168-
customBuildProperties, err := properties.LoadFromSlice(requestBuildProperties)
169-
if err != nil {
164+
if p, err := properties.LoadFromSlice(customBuildProperties); err == nil {
165+
buildProperties.Merge(p)
166+
} else {
170167
return nil, fmt.Errorf("invalid build properties: %w", err)
171168
}
172-
buildProperties.Merge(customBuildProperties)
173-
customBuildPropertiesArgs := append(requestBuildProperties, "build.warn_data_percentage=75")
174169

175170
sketchBuildPath, err := buildPath.Join("sketch").Abs()
176171
if err != nil {
@@ -190,16 +185,20 @@ func NewBuilder(
190185
}
191186

192187
log := logger.New(stdout, stderr, verbosity, warningsLevel)
193-
libsManager, libsResolver, verboseOut, err := detector.LibrariesLoader(
194-
useCachedLibrariesResolution, librariesManager,
195-
builtInLibrariesDirs, libraryDirs, otherLibrariesDirs,
196-
actualPlatform, targetPlatform,
188+
libsManager, libsResolver, libsLoadingWarnings, err := detector.LibrariesLoader(
189+
useCachedLibrariesResolution,
190+
librariesManager,
191+
builtInLibrariesDirs,
192+
customLibraryDirs,
193+
librariesDirs,
194+
buildPlatform,
195+
targetPlatform,
197196
)
198197
if err != nil {
199198
return nil, err
200199
}
201200
if log.VerbosityLevel() == logger.VerbosityVerbose {
202-
log.Warn(string(verboseOut))
201+
log.Warn(string(libsLoadingWarnings))
203202
}
204203

205204
diagnosticStore := diagnostics.NewStore()
@@ -212,25 +211,26 @@ func NewBuilder(
212211
coreBuildPath: coreBuildPath,
213212
librariesBuildPath: librariesBuildPath,
214213
jobs: jobs,
215-
customBuildProperties: customBuildPropertiesArgs,
216214
coreBuildCachePath: coreBuildCachePath,
217215
extraCoreBuildCachePaths: extraCoreBuildCachePaths,
218216
logger: log,
219217
clean: clean,
220218
sourceOverrides: sourceOverrides,
221219
onlyUpdateCompilationDatabase: onlyUpdateCompilationDatabase,
222220
compilationDatabase: compilation.NewDatabase(buildPath.Join("compile_commands.json")),
223-
Progress: progress.New(progresCB),
221+
Progress: progress.New(progressCB),
224222
executableSectionsSize: []ExecutableSectionSize{},
225223
buildArtifacts: &buildArtifacts{},
226224
targetPlatform: targetPlatform,
227-
actualPlatform: actualPlatform,
225+
buildPlatform: buildPlatform,
228226
toolEnv: toolEnv,
229227
buildOptions: newBuildOptions(
230-
hardwareDirs, otherLibrariesDirs,
231-
builtInLibrariesDirs, buildPath,
228+
hardwareDirs,
229+
librariesDirs,
230+
builtInLibrariesDirs,
231+
buildPath,
232232
sk,
233-
customBuildPropertiesArgs,
233+
customBuildProperties,
234234
fqbn,
235235
clean,
236236
buildProperties.Get("compiler.optimization_flags"),
@@ -322,10 +322,19 @@ func (b *Builder) preprocess() error {
322322
b.librariesBuildPath,
323323
b.buildProperties,
324324
b.targetPlatform.Platform.Architecture,
325+
b.jobs,
325326
)
326327
if err != nil {
327328
return err
328329
}
330+
if b.libsDetector.IncludeFoldersChanged() && b.librariesBuildPath.Exist() {
331+
if b.logger.VerbosityLevel() == logger.VerbosityVerbose {
332+
b.logger.Info(i18n.Tr("The list of included libraries has been changed... rebuilding all libraries."))
333+
}
334+
if err := b.librariesBuildPath.RemoveAll(); err != nil {
335+
return err
336+
}
337+
}
329338
b.Progress.CompleteStep()
330339

331340
b.warnAboutArchIncompatibleLibraries(b.libsDetector.ImportedLibraries())
@@ -492,29 +501,26 @@ func (b *Builder) prepareCommandForRecipe(buildProperties *properties.Map, recip
492501
commandLine = properties.DeleteUnexpandedPropsFromString(commandLine)
493502
}
494503

495-
parts, err := properties.SplitQuotedString(commandLine, `"'`, false)
496-
if err != nil {
497-
return nil, err
498-
}
504+
args, _ := properties.SplitQuotedString(commandLine, `"'`, false)
499505

500506
// if the overall commandline is too long for the platform
501507
// try reducing the length by making the filenames relative
502508
// and changing working directory to build.path
503509
var relativePath string
504510
if len(commandLine) > 30000 {
505511
relativePath = buildProperties.Get("build.path")
506-
for i, arg := range parts {
512+
for i, arg := range args {
507513
if _, err := os.Stat(arg); os.IsNotExist(err) {
508514
continue
509515
}
510516
rel, err := filepath.Rel(relativePath, arg)
511517
if err == nil && !strings.Contains(rel, "..") && len(rel) < len(arg) {
512-
parts[i] = rel
518+
args[i] = rel
513519
}
514520
}
515521
}
516522

517-
command, err := paths.NewProcess(b.toolEnv, parts...)
523+
command, err := paths.NewProcess(b.toolEnv, args...)
518524
if err != nil {
519525
return nil, err
520526
}

0 commit comments

Comments
 (0)