Skip to content

Commit 0116e5d

Browse files
committed
fix: correctly cache shared objects
1 parent 75d5aab commit 0116e5d

File tree

2 files changed

+177
-2
lines changed

2 files changed

+177
-2
lines changed

internal/cache/artifacts.go

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ func RestoreArtifacts(cacheDir, destDir string, outputs []string) error {
6666
// CollectOutputs scans for compiled output files specific to the given source file.
6767
// It checks two locations:
6868
// 1. The source file directory for .ush header files
69-
// 2. The SPlsWork directory for other artifacts
69+
// 2. The SPlsWork directory for source-specific artifacts
7070
//
7171
// Returns paths relative to the source directory (e.g., "example.ush", "SPlsWork/example.dll")
7272
func CollectOutputs(sourceFile string) ([]string, error) {
@@ -118,6 +118,85 @@ func CollectOutputs(sourceFile string) ([]string, error) {
118118
return outputs, nil
119119
}
120120

121+
// CollectSharedFiles scans the SPlsWork directory for shared library files
122+
// that are not specific to any source file (DLLs, config files, etc.)
123+
// Returns paths relative to the source directory (e.g., "SPlsWork/Version.ini")
124+
func CollectSharedFiles(sourceDir string) ([]string, error) {
125+
var sharedFiles []string
126+
127+
splsWorkDir := filepath.Join(sourceDir, "SPlsWork")
128+
129+
entries, err := os.ReadDir(splsWorkDir)
130+
if err != nil {
131+
if os.IsNotExist(err) {
132+
return nil, nil // No SPlsWork directory
133+
}
134+
return nil, fmt.Errorf("failed to read SPlsWork directory: %w", err)
135+
}
136+
137+
for _, entry := range entries {
138+
if entry.IsDir() {
139+
continue
140+
}
141+
142+
name := entry.Name()
143+
144+
// Check if this is a shared file (not matching any source pattern)
145+
// Shared files: *.dll, *.dat, *.xml, *.ini (except source-specific ones)
146+
if isSharedFile(name) {
147+
sharedFiles = append(sharedFiles, filepath.Join("SPlsWork", name))
148+
}
149+
}
150+
151+
return sharedFiles, nil
152+
}
153+
154+
// isSharedFile checks if a file is a shared library/config file
155+
func isSharedFile(filename string) bool {
156+
// Common shared file patterns in SPlsWork
157+
ext := filepath.Ext(filename)
158+
baseName := filename[:len(filename)-len(ext)]
159+
160+
// DLL files that don't match source patterns
161+
if ext == ".dll" {
162+
// Check if it's NOT a source-specific DLL (which would be in format "sourcename.dll")
163+
// Shared DLLs have names like "ManagedUtilities.dll", "SplusLibrary.dll"
164+
// If it contains certain keywords, it's shared
165+
sharedKeywords := []string{"Managed", "Simpl", "Sharp", "Splus", "Smart", "Utilities", "Newtonsoft", "Json"}
166+
for _, keyword := range sharedKeywords {
167+
if containsIgnoreCase(baseName, keyword) {
168+
return true
169+
}
170+
}
171+
}
172+
173+
// Config/data files are always shared
174+
if ext == ".ini" || ext == ".xml" || ext == ".dat" || ext == ".der" {
175+
return true
176+
}
177+
178+
return false
179+
}
180+
181+
// containsIgnoreCase checks if a string contains a substring (case-insensitive)
182+
func containsIgnoreCase(s, substr string) bool {
183+
s = filepath.Base(s) // normalize
184+
return len(s) >= len(substr) &&
185+
(s == substr ||
186+
len(s) > len(substr) &&
187+
(s[:len(substr)] == substr || s[len(s)-len(substr):] == substr ||
188+
findSubstring(s, substr)))
189+
}
190+
191+
func findSubstring(s, substr string) bool {
192+
for i := 0; i <= len(s)-len(substr); i++ {
193+
if s[i:i+len(substr)] == substr {
194+
return true
195+
}
196+
}
197+
return false
198+
}
199+
121200
// isOutputFile checks if a filename belongs to the given source base name
122201
func isOutputFile(filename, baseName string) bool {
123202
fileBase := filename[:len(filename)-len(filepath.Ext(filename))]

internal/cache/cache.go

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,38 @@ func (c *Cache) Store(sourceFile string, cfg *config.Config, success bool) error
167167
}
168168
}
169169

170+
// Cache shared files (only once, if not already cached)
171+
if success {
172+
sourceDir := filepath.Dir(sourceFile)
173+
if err := c.cacheSharedFiles(sourceDir); err != nil {
174+
// Don't fail the whole operation if shared files caching fails
175+
fmt.Fprintf(os.Stderr, "Warning: Failed to cache shared files: %v\n", err)
176+
}
177+
}
178+
179+
return nil
180+
}
181+
182+
// cacheSharedFiles caches shared library files if not already cached
183+
func (c *Cache) cacheSharedFiles(sourceDir string) error {
184+
// Check if shared files are already cached
185+
sharedDir := filepath.Join(c.root, "shared")
186+
if _, err := os.Stat(sharedDir); err == nil {
187+
// Already cached, skip
188+
return nil
189+
}
190+
191+
// Collect shared files
192+
sharedFiles, err := CollectSharedFiles(sourceDir)
193+
if err != nil || len(sharedFiles) == 0 {
194+
return err
195+
}
196+
197+
// Copy shared files to cache
198+
if err := CopyArtifacts(sourceDir, sharedDir, sharedFiles); err != nil {
199+
return fmt.Errorf("failed to copy shared files: %w", err)
200+
}
201+
170202
return nil
171203
}
172204

@@ -176,8 +208,72 @@ func (c *Cache) Restore(entry *Entry, destDir string) error {
176208
return fmt.Errorf("cannot restore failed build or build with no outputs")
177209
}
178210

211+
// Restore source-specific artifacts
179212
artifactDir := c.artifactDir(entry.Hash)
180-
return RestoreArtifacts(artifactDir, destDir, entry.Outputs)
213+
if err := RestoreArtifacts(artifactDir, destDir, entry.Outputs); err != nil {
214+
return err
215+
}
216+
217+
// Restore shared files if needed (if SPlsWork exists but shared files are missing)
218+
if err := c.restoreSharedFiles(destDir); err != nil {
219+
// Don't fail if shared files restoration fails - they might already exist
220+
// or will be recreated on next full compile
221+
fmt.Fprintf(os.Stderr, "Warning: Failed to restore shared files: %v\n", err)
222+
}
223+
224+
return nil
225+
}
226+
227+
// restoreSharedFiles restores shared library files if they're missing
228+
func (c *Cache) restoreSharedFiles(destDir string) error {
229+
sharedDir := filepath.Join(c.root, "shared")
230+
231+
// Check if we have cached shared files
232+
if _, err := os.Stat(sharedDir); os.IsNotExist(err) {
233+
return nil // No shared files cached, skip
234+
}
235+
236+
// Check if shared files already exist in destination
237+
splsWorkDir := filepath.Join(destDir, "SPlsWork")
238+
if needsSharedFiles, err := checkSharedFilesExist(splsWorkDir); err != nil || !needsSharedFiles {
239+
return err // Either error or files already exist
240+
}
241+
242+
// Collect what shared files we have cached
243+
entries, err := os.ReadDir(filepath.Join(sharedDir, "SPlsWork"))
244+
if err != nil {
245+
return err
246+
}
247+
248+
var sharedFiles []string
249+
for _, entry := range entries {
250+
if !entry.IsDir() {
251+
sharedFiles = append(sharedFiles, filepath.Join("SPlsWork", entry.Name()))
252+
}
253+
}
254+
255+
// Restore shared files
256+
return RestoreArtifacts(sharedDir, destDir, sharedFiles)
257+
}
258+
259+
// checkSharedFilesExist checks if shared files are missing from SPlsWork
260+
// Returns true if shared files need to be restored
261+
func checkSharedFilesExist(splsWorkDir string) (bool, error) {
262+
// If SPlsWork doesn't exist, we definitely need shared files
263+
if _, err := os.Stat(splsWorkDir); os.IsNotExist(err) {
264+
return true, nil
265+
}
266+
267+
// Check for presence of at least one common shared file
268+
commonSharedFiles := []string{"Version.ini", "ManagedUtilities.dll", "SplusLibrary.dll"}
269+
for _, file := range commonSharedFiles {
270+
if _, err := os.Stat(filepath.Join(splsWorkDir, file)); err == nil {
271+
return false, nil // At least one shared file exists, assume others are there
272+
}
273+
}
274+
275+
// No shared files found, need to restore them
276+
return true, nil
181277
}
182278

183279
// Clear removes all cache entries and artifacts

0 commit comments

Comments
 (0)