From 77222ecd5209b957d25952518afd340a93d6670b Mon Sep 17 00:00:00 2001
From: Martino Facchin <m.facchin@arduino.cc>
Date: Mon, 4 Dec 2023 12:46:34 +0100
Subject: [PATCH] Fix caching for libraries when installation folder differents
 from Name (#2446)

* Fix caching for libraries when installation folder differents from Name

* Prepare infra to add integration test

* Added integration test

---------

Co-authored-by: Cristian Maglie <c.maglie@arduino.cc>
---
 arduino/builder/libraries.go                  |  2 +-
 .../integrationtest/compile_4/compile_test.go | 98 +++++++++++--------
 .../SketchUsingRobotIRRemote.ino              |  7 ++
 3 files changed, 67 insertions(+), 40 deletions(-)
 create mode 100644 internal/integrationtest/compile_4/testdata/SketchUsingRobotIRRemote/SketchUsingRobotIRRemote.ino

diff --git a/arduino/builder/libraries.go b/arduino/builder/libraries.go
index bbe4f514529..88780ee3505 100644
--- a/arduino/builder/libraries.go
+++ b/arduino/builder/libraries.go
@@ -245,7 +245,7 @@ func (b *Builder) removeUnusedCompiledLibraries(importedLibraries libraries.List
 	toLibraryNames := func(libraries []*libraries.Library) []string {
 		libraryNames := []string{}
 		for _, library := range libraries {
-			libraryNames = append(libraryNames, library.Name)
+			libraryNames = append(libraryNames, library.DirName)
 		}
 		return libraryNames
 	}
diff --git a/internal/integrationtest/compile_4/compile_test.go b/internal/integrationtest/compile_4/compile_test.go
index a61d68563a5..42bd97fd1a9 100644
--- a/internal/integrationtest/compile_4/compile_test.go
+++ b/internal/integrationtest/compile_4/compile_test.go
@@ -20,6 +20,7 @@ import (
 	"cmp"
 	"encoding/json"
 	"os/exec"
+	"regexp"
 	"slices"
 	"strings"
 	"testing"
@@ -904,56 +905,75 @@ func comparePreprocessGoldenFile(t *testing.T, sketchDir *paths.Path, preprocess
 	require.Equal(t, buf.String(), strings.ReplaceAll(preprocessedSketch, "\r\n", "\n"))
 }
 
-func TestCoreCaching(t *testing.T) {
+func TestBuildCaching(t *testing.T) {
 	env, cli := integrationtest.CreateArduinoCLIWithEnvironment(t)
 	defer env.CleanUp()
 
-	sketchPath, err := paths.New("..", "testdata", "bare_minimum").Abs()
-	require.NoError(t, err)
-
 	// Install Arduino AVR Boards
-	_, _, err = cli.Run("core", "install", "arduino:avr@1.8.6")
+	_, _, err := cli.Run("core", "install", "arduino:avr@1.8.6")
 	require.NoError(t, err)
 
-	// Create temporary cache dir
-	buildCachePath, err := paths.MkTempDir("", "test_build_cache")
-	require.NoError(t, err)
-	defer buildCachePath.RemoveAll()
+	t.Run("CoreCaching", func(t *testing.T) {
+		sketchPath, err := paths.New("..", "testdata", "bare_minimum").Abs()
+		require.NoError(t, err)
 
-	// Build first time
-	_, _, err = cli.Run("compile", "-b", "arduino:avr:uno", "--build-cache-path", buildCachePath.String(), sketchPath.String())
-	require.NoError(t, err)
+		// Create temporary cache dir
+		buildCachePath, err := paths.MkTempDir("", "test_build_cache")
+		require.NoError(t, err)
+		defer buildCachePath.RemoveAll()
 
-	// Find cached core and save timestamp
-	pathList, err := buildCachePath.ReadDirRecursiveFiltered(nil, paths.FilterPrefixes("core.a"))
-	require.NoError(t, err)
-	require.Len(t, pathList, 1)
-	cachedCoreFile := pathList[0]
-	lastUsedPath := cachedCoreFile.Parent().Join(".last-used")
-	require.True(t, lastUsedPath.Exist())
-	coreStatBefore, err := cachedCoreFile.Stat()
-	require.NoError(t, err)
+		// Build first time
+		_, _, err = cli.Run("compile", "-b", "arduino:avr:uno", "--build-cache-path", buildCachePath.String(), sketchPath.String())
+		require.NoError(t, err)
 
-	// Run build again and check timestamp is unchanged
-	_, _, err = cli.Run("compile", "-b", "arduino:avr:uno", "--build-cache-path", buildCachePath.String(), sketchPath.String())
-	require.NoError(t, err)
-	coreStatAfterRebuild, err := cachedCoreFile.Stat()
-	require.NoError(t, err)
-	require.Equal(t, coreStatBefore.ModTime(), coreStatAfterRebuild.ModTime())
+		// Find cached core and save timestamp
+		pathList, err := buildCachePath.ReadDirRecursiveFiltered(nil, paths.FilterPrefixes("core.a"))
+		require.NoError(t, err)
+		require.Len(t, pathList, 1)
+		cachedCoreFile := pathList[0]
+		lastUsedPath := cachedCoreFile.Parent().Join(".last-used")
+		require.True(t, lastUsedPath.Exist())
+		coreStatBefore, err := cachedCoreFile.Stat()
+		require.NoError(t, err)
 
-	// Touch a file of the core and check if the builder invalidate the cache
-	time.Sleep(time.Second)
-	now := time.Now().Local()
-	coreFolder := cli.DataDir().Join("packages", "arduino", "hardware", "avr", "1.8.6")
-	err = coreFolder.Join("cores", "arduino", "Arduino.h").Chtimes(now, now)
-	require.NoError(t, err)
+		// Run build again and check timestamp is unchanged
+		_, _, err = cli.Run("compile", "-b", "arduino:avr:uno", "--build-cache-path", buildCachePath.String(), sketchPath.String())
+		require.NoError(t, err)
+		coreStatAfterRebuild, err := cachedCoreFile.Stat()
+		require.NoError(t, err)
+		require.Equal(t, coreStatBefore.ModTime(), coreStatAfterRebuild.ModTime())
 
-	// Run build again, to verify that the builder rebuilds core.a
-	_, _, err = cli.Run("compile", "-b", "arduino:avr:uno", "--build-cache-path", buildCachePath.String(), sketchPath.String())
-	require.NoError(t, err)
-	coreStatAfterTouch, err := cachedCoreFile.Stat()
-	require.NoError(t, err)
-	require.NotEqual(t, coreStatBefore.ModTime(), coreStatAfterTouch.ModTime())
+		// Touch a file of the core and check if the builder invalidate the cache
+		time.Sleep(time.Second)
+		now := time.Now().Local()
+		coreFolder := cli.DataDir().Join("packages", "arduino", "hardware", "avr", "1.8.6")
+		err = coreFolder.Join("cores", "arduino", "Arduino.h").Chtimes(now, now)
+		require.NoError(t, err)
+
+		// Run build again, to verify that the builder rebuilds core.a
+		_, _, err = cli.Run("compile", "-b", "arduino:avr:uno", "--build-cache-path", buildCachePath.String(), sketchPath.String())
+		require.NoError(t, err)
+		coreStatAfterTouch, err := cachedCoreFile.Stat()
+		require.NoError(t, err)
+		require.NotEqual(t, coreStatBefore.ModTime(), coreStatAfterTouch.ModTime())
+	})
+
+	t.Run("LibraryCacheWithDifferentDirname", func(t *testing.T) {
+		_, _, err = cli.Run("lib", "install", "Robot IR Remote")
+		require.NoError(t, err)
+
+		// Run first compile
+		sketchPath, err := paths.New("testdata", "SketchUsingRobotIRRemote").Abs()
+		require.NoError(t, err)
+		_, _, err = cli.Run("compile", "-b", "arduino:avr:robotControl", "-v", sketchPath.String())
+		require.NoError(t, err)
+
+		// Run second compile and check that previous build is re-used
+		out, _, err := cli.Run("compile", "-b", "arduino:avr:robotControl", "-v", sketchPath.String())
+		require.NoError(t, err)
+		check := regexp.MustCompile(`(?m)^Using previously compiled file:.*IRremoteTools\.cpp\.o$`)
+		require.True(t, check.Match(out))
+	})
 }
 
 func TestMergeSketchWithBootloader(t *testing.T) {
diff --git a/internal/integrationtest/compile_4/testdata/SketchUsingRobotIRRemote/SketchUsingRobotIRRemote.ino b/internal/integrationtest/compile_4/testdata/SketchUsingRobotIRRemote/SketchUsingRobotIRRemote.ino
new file mode 100644
index 00000000000..4eedf4e2675
--- /dev/null
+++ b/internal/integrationtest/compile_4/testdata/SketchUsingRobotIRRemote/SketchUsingRobotIRRemote.ino
@@ -0,0 +1,7 @@
+#include <RobotIRremote.h>
+
+void setup() {
+}
+
+void loop() {
+}