From 2d7539cd79c4e67be73a5c7b5f00929e37bcb063 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 20 Jun 2022 15:46:19 +0200 Subject: [PATCH 1/3] Language server now skips libraries detection while editing A full build is made only at language server startup. Other full builds must be performed by the IDE and build_path should be passed to the LS with a custom command. --- ls/builder.go | 27 +++++++++++++++++---------- ls/ls.go | 27 ++++++++++++++++++++++++++- ls/lsp_server_ide.go | 16 ++++++++++++++++ 3 files changed, 59 insertions(+), 11 deletions(-) diff --git a/ls/builder.go b/ls/builder.go index 66d5233..db59c54 100644 --- a/ls/builder.go +++ b/ls/builder.go @@ -92,7 +92,7 @@ func (r *SketchRebuilder) rebuilderLoop() { r.cancel = cancel r.mutex.Unlock() - if err := r.doRebuild(ctx, logger); err != nil { + if err := r.doRebuild(ctx, false, logger); err != nil { logger.Logf("Error: %s", err) } @@ -104,10 +104,16 @@ func (r *SketchRebuilder) rebuilderLoop() { } } -func (r *SketchRebuilder) doRebuild(ctx context.Context, logger jsonrpc.FunctionLogger) error { +func (r *SketchRebuilder) doRebuild(ctx context.Context, fullRebuild bool, logger jsonrpc.FunctionLogger) error { ls := r.ls + var buildPath *paths.Path + if fullRebuild { + buildPath = ls.fullBuildPath + } else { + buildPath = ls.buildPath + } - if success, err := ls.generateBuildEnvironment(ctx, logger); err != nil { + if success, err := ls.generateBuildEnvironment(ctx, fullRebuild, buildPath, logger); err != nil { return err } else if !success { return fmt.Errorf("build failed") @@ -123,9 +129,7 @@ func (r *SketchRebuilder) doRebuild(ctx context.Context, logger jsonrpc.Function default: } - if err := ls.buildPath.Join("compile_commands.json").CopyTo(ls.compileCommandsDir.Join("compile_commands.json")); err != nil { - logger.Logf("ERROR: updating compile_commands: %s", err) - } + ls.CopyBuildResults(logger, buildPath, fullRebuild) if cppContent, err := ls.buildSketchCpp.ReadFile(); err == nil { oldVesrion := ls.sketchMapper.CppText.Version @@ -166,11 +170,10 @@ func (r *SketchRebuilder) doRebuild(ctx context.Context, logger jsonrpc.Function return nil } -func (ls *INOLanguageServer) generateBuildEnvironment(ctx context.Context, logger jsonrpc.FunctionLogger) (bool, error) { +func (ls *INOLanguageServer) generateBuildEnvironment(ctx context.Context, fullBuild bool, buildPath *paths.Path, logger jsonrpc.FunctionLogger) (bool, error) { // Extract all build information from language server status ls.readLock(logger, false) sketchRoot := ls.sketchRoot - buildPath := ls.buildPath config := ls.config type overridesFile struct { Overrides map[string]string `json:"overrides"` @@ -204,6 +207,7 @@ func (ls *INOLanguageServer) generateBuildEnvironment(ctx context.Context, logge BuildPath: buildPath.String(), CreateCompilationDatabaseOnly: true, Verbose: true, + SkipLibrariesDiscovery: !fullBuild, } compileReqJson, _ := json.MarshalIndent(compileReq, "", " ") logger.Logf("Running build with: %s", string(compileReqJson)) @@ -264,9 +268,12 @@ func (ls *INOLanguageServer) generateBuildEnvironment(ctx context.Context, logge "--source-override", overridesJSON.String(), "--build-path", buildPath.String(), "--format", "json", - //"--clean", - sketchRoot.String(), } + if !fullBuild { + args = append(args, "--skip-libraries-discovery") + } + args = append(args, sketchRoot.String()) + cmd, err := executils.NewProcess(nil, args...) if err != nil { return false, errors.Errorf("running %s: %s", strings.Join(args, " "), err) diff --git a/ls/ls.go b/ls/ls.go index 3776d9d..f92ad72 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -40,6 +40,7 @@ type INOLanguageServer struct { buildPath *paths.Path buildSketchRoot *paths.Path buildSketchCpp *paths.Path + fullBuildPath *paths.Path sketchRoot *paths.Path sketchName string sketchMapper *sourcemapper.SketchMapper @@ -136,9 +137,16 @@ func NewINOLanguageServer(stdin io.Reader, stdout io.Writer, config *Config) *IN ls.buildSketchRoot = ls.buildPath.Join("sketch") } + if tmp, err := paths.MkTempDir("", "arduino-language-server"); err != nil { + log.Fatalf("Could not create temp folder: %s", err) + } else { + ls.fullBuildPath = tmp.Canonical() + } + logger.Logf("Initial board configuration: %s", ls.config.Fqbn) logger.Logf("Language server build path: %s", ls.buildPath) logger.Logf("Language server build sketch root: %s", ls.buildSketchRoot) + logger.Logf("Language server FULL build path: %s", ls.fullBuildPath) logger.Logf("Language server compile-commands: %s", ls.compileCommandsDir.Join("compile_commands.json")) ls.IDE = NewIDELSPServer(logger, stdin, stdout, ls) @@ -166,7 +174,7 @@ func (ls *INOLanguageServer) InitializeReqFromIDE(ctx context.Context, logger js ls.sketchName = ls.sketchRoot.Base() ls.buildSketchCpp = ls.buildSketchRoot.Join(ls.sketchName + ".ino.cpp") - if success, err := ls.generateBuildEnvironment(context.Background(), logger); err != nil { + if success, err := ls.generateBuildEnvironment(context.Background(), true, ls.buildPath, logger); err != nil { logger.Logf("error starting clang: %s", err) return } else if !success { @@ -1172,6 +1180,23 @@ func (ls *INOLanguageServer) TextDocumentDidCloseNotifFromIDE(logger jsonrpc.Fun } } +func (ls *INOLanguageServer) FullBuildCompletedFromIDE(logger jsonrpc.FunctionLogger, params *FullBuildResult) { + ls.writeLock(logger, true) + defer ls.writeUnlock(logger) + ls.CopyBuildResults(logger, params.BuildPath, true) +} + +func (ls *INOLanguageServer) CopyBuildResults(logger jsonrpc.FunctionLogger, buildPath *paths.Path, fullRebuild bool) { + if err := buildPath.Join("compile_commands.json").CopyTo(ls.compileCommandsDir.Join("compile_commands.json")); err != nil { + logger.Logf("ERROR: updating compile_commands: %s", err) + } + if fullRebuild { + if err := buildPath.Join("libraries.cache").CopyTo(ls.compileCommandsDir.Join("libraries.cache")); err != nil { + logger.Logf("ERROR: updating libraires.cache: %s", err) + } + } +} + func (ls *INOLanguageServer) PublishDiagnosticsNotifFromClangd(logger jsonrpc.FunctionLogger, clangParams *lsp.PublishDiagnosticsParams) { ls.readLock(logger, false) defer ls.readUnlock(logger) diff --git a/ls/lsp_server_ide.go b/ls/lsp_server_ide.go index 2f295d5..4520d6e 100644 --- a/ls/lsp_server_ide.go +++ b/ls/lsp_server_ide.go @@ -4,6 +4,7 @@ import ( "context" "io" + "github.com/arduino/go-paths-helper" "github.com/fatih/color" "go.bug.st/json" "go.bug.st/lsp" @@ -20,6 +21,7 @@ func NewIDELSPServer(logger jsonrpc.FunctionLogger, in io.Reader, out io.Writer, ls: ls, } server.conn = lsp.NewServer(in, out, server) + server.conn.RegisterCustomNotification("arduino/buildCompleted", server.ArduinoBuildCompleted) server.conn.SetLogger(&LSPLogger{ IncomingPrefix: "IDE --> LS", OutgoingPrefix: "IDE <-- LS", @@ -267,3 +269,17 @@ func (server *IDELSPServer) TextDocumentDidSave(logger jsonrpc.FunctionLogger, p func (server *IDELSPServer) TextDocumentDidClose(logger jsonrpc.FunctionLogger, params *lsp.DidCloseTextDocumentParams) { server.ls.TextDocumentDidCloseNotifFromIDE(logger, params) } + +// FullBuildResult is a custom notification from the Arduino IDE, sent +type FullBuildResult struct { + BuildPath *paths.Path `json:"build_path"` +} + +func (server *IDELSPServer) ArduinoBuildCompleted(logger jsonrpc.FunctionLogger, raw json.RawMessage) { + var params FullBuildResult + if err := json.Unmarshal(raw, ¶ms); err != nil { + logger.Logf("ERROR decoding FullBuildResult: %s", err) + } else { + server.ls.FullBuildCompletedFromIDE(logger, ¶ms) + } +} From fb9ce10359c1563eeb17b964dd07767e08d4cf9a Mon Sep 17 00:00:00 2001 From: Akos Kitta Date: Fri, 24 Jun 2022 11:13:22 +0200 Subject: [PATCH 2/3] init Signed-off-by: Akos Kitta --- ls/builder.go | 50 +++++++++++++++++++++++++------------------- ls/ls.go | 36 ++++++++++++++++--------------- ls/lsp_server_ide.go | 11 +++++----- 3 files changed, 53 insertions(+), 44 deletions(-) diff --git a/ls/builder.go b/ls/builder.go index db59c54..e56e1c7 100644 --- a/ls/builder.go +++ b/ls/builder.go @@ -25,18 +25,24 @@ import ( "google.golang.org/grpc" ) +type rebuildSketchParams struct { + trigger chan chan<- bool + fullRebuild bool +} + type SketchRebuilder struct { - ls *INOLanguageServer - trigger chan chan<- bool - cancel func() - mutex sync.Mutex + ls *INOLanguageServer + params *rebuildSketchParams + cancel func() + mutex sync.Mutex } func NewSketchBuilder(ls *INOLanguageServer) *SketchRebuilder { + p := rebuildSketchParams{trigger: make(chan chan<- bool, 1), fullRebuild: false} res := &SketchRebuilder{ - trigger: make(chan chan<- bool, 1), - cancel: func() {}, - ls: ls, + params: &p, + cancel: func() {}, + ls: ls, } go func() { defer streams.CatchAndLogPanic() @@ -45,25 +51,26 @@ func NewSketchBuilder(ls *INOLanguageServer) *SketchRebuilder { return res } -func (ls *INOLanguageServer) triggerRebuildAndWait(logger jsonrpc.FunctionLogger) { +func (ls *INOLanguageServer) triggerRebuildAndWait(logger jsonrpc.FunctionLogger, fullRebuild bool) { completed := make(chan bool) - ls.sketchRebuilder.TriggerRebuild(completed) + ls.sketchRebuilder.TriggerRebuild(completed, fullRebuild) ls.writeUnlock(logger) <-completed ls.writeLock(logger, true) } -func (ls *INOLanguageServer) triggerRebuild() { - ls.sketchRebuilder.TriggerRebuild(nil) +func (ls *INOLanguageServer) triggerRebuild(fullRebuild bool) { + ls.sketchRebuilder.TriggerRebuild(nil, fullRebuild) } -func (r *SketchRebuilder) TriggerRebuild(completed chan<- bool) { +func (r *SketchRebuilder) TriggerRebuild(completed chan<- bool, fullRebuild bool) { r.mutex.Lock() defer r.mutex.Unlock() r.cancel() // Stop possibly already running builds + r.params.fullRebuild = fullRebuild select { - case r.trigger <- completed: + case r.params.trigger <- completed: default: } } @@ -71,12 +78,12 @@ func (r *SketchRebuilder) TriggerRebuild(completed chan<- bool) { func (r *SketchRebuilder) rebuilderLoop() { logger := NewLSPFunctionLogger(color.HiMagentaString, "SKETCH REBUILD: ") for { - completed := <-r.trigger + completed := <-r.params.trigger for { // Concede a 200ms delay to accumulate bursts of changes select { - case <-r.trigger: + case <-r.params.trigger: continue case <-time.After(time.Second): } @@ -88,11 +95,12 @@ func (r *SketchRebuilder) rebuilderLoop() { ctx, cancel := context.WithCancel(context.Background()) r.mutex.Lock() - logger.Logf("Sketch rebuild started") + fullRebuild := r.params.fullRebuild + logger.Logf("Sketch rebuild started. Full-rebuild: %v", fullRebuild) r.cancel = cancel r.mutex.Unlock() - if err := r.doRebuild(ctx, false, logger); err != nil { + if err := r.doRebuild(ctx, fullRebuild, logger); err != nil { logger.Logf("Error: %s", err) } @@ -132,9 +140,9 @@ func (r *SketchRebuilder) doRebuild(ctx context.Context, fullRebuild bool, logge ls.CopyBuildResults(logger, buildPath, fullRebuild) if cppContent, err := ls.buildSketchCpp.ReadFile(); err == nil { - oldVesrion := ls.sketchMapper.CppText.Version + oldVersion := ls.sketchMapper.CppText.Version ls.sketchMapper = sourcemapper.CreateInoMapper(cppContent) - ls.sketchMapper.CppText.Version = oldVesrion + 1 + ls.sketchMapper.CppText.Version = oldVersion + 1 ls.sketchMapper.DebugLogAll() } else { return errors.WithMessage(err, "reading generated cpp file from sketch") @@ -147,7 +155,7 @@ func (r *SketchRebuilder) doRebuild(ctx context.Context, fullRebuild bool, logge TextDocument: lsp.TextDocumentIdentifier{URI: cppURI}, } if err := ls.Clangd.conn.TextDocumentDidSave(didSaveParams); err != nil { - logger.Logf("error reinitilizing clangd:", err) + logger.Logf("error reinitializing clangd:", err) return err } @@ -163,7 +171,7 @@ func (r *SketchRebuilder) doRebuild(ctx context.Context, fullRebuild bool, logge }, } if err := ls.Clangd.conn.TextDocumentDidChange(didChangeParams); err != nil { - logger.Logf("error reinitilizing clangd:", err) + logger.Logf("error reinitializing clangd:", err) return err } diff --git a/ls/ls.go b/ls/ls.go index f92ad72..64c4271 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -217,10 +217,10 @@ func (ls *INOLanguageServer) InitializeReqFromIDE(ctx context.Context, logger js clangInitializeParams.RootPath = ls.buildSketchRoot.String() clangInitializeParams.RootURI = lsp.NewDocumentURIFromPath(ls.buildSketchRoot) if clangInitializeResult, clangErr, err := ls.Clangd.conn.Initialize(ctx, &clangInitializeParams); err != nil { - logger.Logf("error initilizing clangd: %v", err) + logger.Logf("error initializing clangd: %v", err) return } else if clangErr != nil { - logger.Logf("error initilizing clangd: %v", clangErr.AsError()) + logger.Logf("error initializing clangd: %v", clangErr.AsError()) return } else { logger.Logf("clangd successfully started: %s", string(lsp.EncodeMessage(clangInitializeResult))) @@ -355,7 +355,7 @@ func (ls *INOLanguageServer) InitializeReqFromIDE(ctx context.Context, logger js // TokenModifiers: []string{}, // }, // Range: false, - // Full: &lsp.SemantiTokenFullOptions{ + // Full: &lsp.SemanticTokenFullOptions{ // Delta: true, // }, // }, @@ -445,7 +445,7 @@ func (ls *INOLanguageServer) TextDocumentCompletionReqFromIDE(ctx context.Contex if clangItem.Command != nil { c := ls.clang2IdeCommand(logger, *clangItem.Command) if c == nil { - continue // Skit item with unsupported command convertion + continue // Skit item with unsupported command conversion } ideCommand = c } @@ -603,7 +603,7 @@ func (ls *INOLanguageServer) TextDocumentDefinitionReqFromIDE(ctx context.Contex func (ls *INOLanguageServer) TextDocumentTypeDefinitionReqFromIDE(ctx context.Context, logger jsonrpc.FunctionLogger, ideParams *lsp.TypeDefinitionParams) ([]lsp.Location, []lsp.LocationLink, *jsonrpc.ResponseError) { // XXX: This capability is not advertised in the initialization message (clangd - // does not advetise it either, so maybe we should just not implement it) + // does not advertise it either, so maybe we should just not implement it) ls.readLock(logger, true) defer ls.readUnlock(logger) @@ -982,9 +982,10 @@ func (ls *INOLanguageServer) TextDocumentDidOpenNotifFromIDE(logger jsonrpc.Func return } + // TODO: trigger `fullRebuild` if mail sketch file? if ls.ideURIIsPartOfTheSketch(ideTextDocItem.URI) { if !clangURI.AsPath().Exist() { - ls.triggerRebuildAndWait(logger) + ls.triggerRebuildAndWait(logger, true) } } @@ -1034,7 +1035,7 @@ func (ls *INOLanguageServer) TextDocumentDidChangeNotifFromIDE(logger jsonrpc.Fu ls.writeLock(logger, true) defer ls.writeUnlock(logger) - ls.triggerRebuild() + ls.triggerRebuild(false) logger.Logf("didChange(%s)", ideParams.TextDocument) for _, change := range ideParams.ContentChanges { @@ -1134,14 +1135,14 @@ func (ls *INOLanguageServer) TextDocumentDidSaveNotifFromIDE(logger jsonrpc.Func // so we will not forward notification on saves in the sketch folder. logger.Logf("notification is not forwarded to clang") - ls.triggerRebuild() + ls.triggerRebuild(false) } func (ls *INOLanguageServer) TextDocumentDidCloseNotifFromIDE(logger jsonrpc.FunctionLogger, ideParams *lsp.DidCloseTextDocumentParams) { ls.writeLock(logger, true) defer ls.writeUnlock(logger) - ls.triggerRebuild() + ls.triggerRebuild(false) inoIdentifier := ideParams.TextDocument if _, exist := ls.trackedIdeDocs[inoIdentifier.URI.AsPath().String()]; exist { @@ -1180,10 +1181,10 @@ func (ls *INOLanguageServer) TextDocumentDidCloseNotifFromIDE(logger jsonrpc.Fun } } -func (ls *INOLanguageServer) FullBuildCompletedFromIDE(logger jsonrpc.FunctionLogger, params *FullBuildResult) { +func (ls *INOLanguageServer) FullBuildCompletedFromIDE(logger jsonrpc.FunctionLogger, params *DidCompleteBuildParams) { ls.writeLock(logger, true) defer ls.writeUnlock(logger) - ls.CopyBuildResults(logger, params.BuildPath, true) + ls.CopyBuildResults(logger, params.BuildOutputUri.AsPath(), true) } func (ls *INOLanguageServer) CopyBuildResults(logger jsonrpc.FunctionLogger, buildPath *paths.Path, fullRebuild bool) { @@ -1192,8 +1193,9 @@ func (ls *INOLanguageServer) CopyBuildResults(logger jsonrpc.FunctionLogger, bui } if fullRebuild { if err := buildPath.Join("libraries.cache").CopyTo(ls.compileCommandsDir.Join("libraries.cache")); err != nil { - logger.Logf("ERROR: updating libraires.cache: %s", err) + logger.Logf("ERROR: updating libraries.cache: %s", err) } + ls.triggerRebuildAndWait(logger, false) } } @@ -1221,7 +1223,7 @@ func (ls *INOLanguageServer) PublishDiagnosticsNotifFromClangd(logger jsonrpc.Fu ls.ideInoDocsWithDiagnostics[ideInoURI] = true } - // .. and cleanup all previouse diagnostics that are no longer valid... + // .. and cleanup all previous diagnostics that are no longer valid... for ideInoURI := range ls.ideInoDocsWithDiagnostics { if _, ok := allIdeParams[ideInoURI]; ok { continue @@ -1243,7 +1245,7 @@ func (ls *INOLanguageServer) PublishDiagnosticsNotifFromClangd(logger jsonrpc.Fu _ = json.Unmarshal(ideDiag.Code, &code) switch code { case "": - // Filter unkown non-string codes + // Filter unknown non-string codes case "drv_unknown_argument_with_suggestion": // Skip errors like: "Unknown argument '-mlongcalls'; did you mean '-mlong-calls'?" case "drv_unknown_argument": @@ -1321,7 +1323,7 @@ func (ls *INOLanguageServer) ideURIIsPartOfTheSketch(ideURI lsp.DocumentURI) boo func (ls *INOLanguageServer) ProgressNotifFromClangd(logger jsonrpc.FunctionLogger, progress *lsp.ProgressParams) { var token string if err := json.Unmarshal(progress.Token, &token); err != nil { - logger.Logf("error decoding progess token: %s", err) + logger.Logf("error decoding progress token: %s", err) return } switch value := progress.TryToDecodeWellKnownValues().(type) { @@ -1495,7 +1497,7 @@ func (ls *INOLanguageServer) clang2IdeCommand(logger jsonrpc.FunctionLogger, cla converted, err := json.Marshal(v) if err != nil { - panic("Internal Error: json conversion of codeAcion command arguments") + panic("Internal Error: json conversion of codeAction command arguments") } ideCommand.Arguments[i] = converted } @@ -1521,7 +1523,7 @@ func (ls *INOLanguageServer) cpp2inoWorkspaceEdit(logger jsonrpc.FunctionLogger, continue } - // ...otherwise convert edits to the sketch.ino.cpp into multilpe .ino edits + // ...otherwise convert edits to the sketch.ino.cpp into multiple .ino edits for _, edit := range edits { inoURI, inoRange, inPreprocessed, err := ls.clang2IdeRangeAndDocumentURI(logger, editURI, edit.Range) if err != nil { diff --git a/ls/lsp_server_ide.go b/ls/lsp_server_ide.go index 4520d6e..aeaa496 100644 --- a/ls/lsp_server_ide.go +++ b/ls/lsp_server_ide.go @@ -4,7 +4,6 @@ import ( "context" "io" - "github.com/arduino/go-paths-helper" "github.com/fatih/color" "go.bug.st/json" "go.bug.st/lsp" @@ -21,7 +20,7 @@ func NewIDELSPServer(logger jsonrpc.FunctionLogger, in io.Reader, out io.Writer, ls: ls, } server.conn = lsp.NewServer(in, out, server) - server.conn.RegisterCustomNotification("arduino/buildCompleted", server.ArduinoBuildCompleted) + server.conn.RegisterCustomNotification("ino/didCompleteBuild", server.ArduinoBuildCompleted) server.conn.SetLogger(&LSPLogger{ IncomingPrefix: "IDE --> LS", OutgoingPrefix: "IDE <-- LS", @@ -270,13 +269,13 @@ func (server *IDELSPServer) TextDocumentDidClose(logger jsonrpc.FunctionLogger, server.ls.TextDocumentDidCloseNotifFromIDE(logger, params) } -// FullBuildResult is a custom notification from the Arduino IDE, sent -type FullBuildResult struct { - BuildPath *paths.Path `json:"build_path"` +// DidCompleteBuildParams is a custom notification from the Arduino IDE, sent +type DidCompleteBuildParams struct { + BuildOutputUri *lsp.DocumentURI `json:"buildOutputUri"` } func (server *IDELSPServer) ArduinoBuildCompleted(logger jsonrpc.FunctionLogger, raw json.RawMessage) { - var params FullBuildResult + var params DidCompleteBuildParams if err := json.Unmarshal(raw, ¶ms); err != nil { logger.Logf("ERROR decoding FullBuildResult: %s", err) } else { From 1b1c4e2508a026cf097495719944fc83030405be Mon Sep 17 00:00:00 2001 From: Akos Kitta Date: Fri, 24 Jun 2022 14:02:14 +0200 Subject: [PATCH 3/3] log cache update. Signed-off-by: Akos Kitta --- ls/ls.go | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/ls/ls.go b/ls/ls.go index 64c4271..c9f9ba5 100644 --- a/ls/ls.go +++ b/ls/ls.go @@ -1188,14 +1188,21 @@ func (ls *INOLanguageServer) FullBuildCompletedFromIDE(logger jsonrpc.FunctionLo } func (ls *INOLanguageServer) CopyBuildResults(logger jsonrpc.FunctionLogger, buildPath *paths.Path, fullRebuild bool) { - if err := buildPath.Join("compile_commands.json").CopyTo(ls.compileCommandsDir.Join("compile_commands.json")); err != nil { + fromCommands := buildPath.Join("compile_commands.json") + toCommands := ls.compileCommandsDir.Join("compile_commands.json") + if err := fromCommands.CopyTo(toCommands); err != nil { logger.Logf("ERROR: updating compile_commands: %s", err) + } else { + logger.Logf("Updated 'compile_commands'. Copied: %v to %v", fromCommands, toCommands) } if fullRebuild { - if err := buildPath.Join("libraries.cache").CopyTo(ls.compileCommandsDir.Join("libraries.cache")); err != nil { + fromCache := buildPath.Join("libraries.cache") + toCache := ls.compileCommandsDir.Join("libraries.cache") + if err := fromCache.CopyTo(toCache); err != nil { logger.Logf("ERROR: updating libraries.cache: %s", err) + } else { + logger.Logf("Updated 'libraries.cache'. Copied: %v to %v", fromCache, toCache) } - ls.triggerRebuildAndWait(logger, false) } }