github.com/aykevl/tinygo@v0.5.0/buildcache.go (about) 1 package main 2 3 import ( 4 "io" 5 "os" 6 "path/filepath" 7 "time" 8 ) 9 10 // Get the cache directory, usually ~/.cache/tinygo 11 func cacheDir() string { 12 home := getHomeDir() 13 dir := filepath.Join(home, ".cache", "tinygo") 14 return dir 15 } 16 17 // Return the newest timestamp of all the file paths passed in. Used to check 18 // for stale caches. 19 func cacheTimestamp(paths []string) (time.Time, error) { 20 var timestamp time.Time 21 for _, path := range paths { 22 st, err := os.Stat(path) 23 if err != nil { 24 return time.Time{}, err 25 } 26 if timestamp.IsZero() { 27 timestamp = st.ModTime() 28 } else if timestamp.Before(st.ModTime()) { 29 timestamp = st.ModTime() 30 } 31 } 32 return timestamp, nil 33 } 34 35 // Try to load a given file from the cache. Return "", nil if no cached file can 36 // be found (or the file is stale), return the absolute path if there is a cache 37 // and return an error on I/O errors. 38 // 39 // TODO: the configKey is currently ignored. It is supposed to be used as extra 40 // data for the cache key, like the compiler version and arguments. 41 func cacheLoad(name, configKey string, sourceFiles []string) (string, error) { 42 dir := cacheDir() 43 cachepath := filepath.Join(dir, name) 44 cacheStat, err := os.Stat(cachepath) 45 if os.IsNotExist(err) { 46 return "", nil // does not exist 47 } else if err != nil { 48 return "", err // cannot stat cache file 49 } 50 51 sourceTimestamp, err := cacheTimestamp(sourceFiles) 52 if err != nil { 53 return "", err // cannot stat source files 54 } 55 56 if cacheStat.ModTime().After(sourceTimestamp) { 57 return cachepath, nil 58 } else { 59 os.Remove(cachepath) 60 // stale cache 61 return "", nil 62 } 63 } 64 65 // Store the file located at tmppath in the cache with the given name. The 66 // tmppath may or may not be gone afterwards. 67 // 68 // Note: the configKey is ignored, see cacheLoad. 69 func cacheStore(tmppath, name, configKey string, sourceFiles []string) (string, error) { 70 // get the last modified time 71 if len(sourceFiles) == 0 { 72 panic("cache: no source files") 73 } 74 75 // TODO: check the config key 76 77 dir := cacheDir() 78 err := os.MkdirAll(dir, 0777) 79 if err != nil { 80 return "", err 81 } 82 cachepath := filepath.Join(dir, name) 83 err = moveFile(tmppath, cachepath) 84 if err != nil { 85 return "", err 86 } 87 return cachepath, nil 88 } 89 90 // moveFile renames the file from src to dst. If renaming doesn't work (for 91 // example, the rename crosses a filesystem boundary), the file is copied and 92 // the old file is removed. 93 func moveFile(src, dst string) error { 94 err := os.Rename(src, dst) 95 if err == nil { 96 // Success! 97 return nil 98 } 99 // Failed to move, probably a different filesystem. 100 // Do a copy + remove. 101 inf, err := os.Open(src) 102 if err != nil { 103 return err 104 } 105 defer inf.Close() 106 outpath := dst + ".tmp" 107 outf, err := os.Create(outpath) 108 if err != nil { 109 return err 110 } 111 112 _, err = io.Copy(outf, inf) 113 if err != nil { 114 os.Remove(outpath) 115 return err 116 } 117 118 err = os.Rename(dst+".tmp", dst) 119 if err != nil { 120 return err 121 } 122 123 return outf.Close() 124 }