func moveOutput(target *core.BuildTarget, tmpOutput, realOutput string, filegroup bool) (bool, error) { // hash the file newHash, err := pathHash(tmpOutput, false) if err != nil { return true, err } realOutputExists := core.PathExists(realOutput) // If this is a filegroup we hardlink the outputs over and so the two files may actually be // the same file. If so don't do anything else and especially don't delete & recreate the // file because other things might be using it already (because more than one filegroup can // own the same file). if filegroup && realOutputExists && core.IsSameFile(tmpOutput, realOutput) { movePathHash(tmpOutput, realOutput, filegroup) // make sure this is updated regardless return false, nil } if realOutputExists { if oldHash, err := pathHash(realOutput, false); err != nil { return true, err } else if bytes.Equal(oldHash, newHash) { // We already have the same file in the current location. Don't bother moving it. log.Debug("Checking %s vs. %s, hashes match", tmpOutput, realOutput) return false, nil } if err := os.RemoveAll(realOutput); err != nil { return true, err } } movePathHash(tmpOutput, realOutput, filegroup) // Check if we need a directory for this output. dir := path.Dir(realOutput) if !core.PathExists(dir) { if err := os.MkdirAll(dir, core.DirPermissions); err != nil { return true, err } } // If the output file is in plz-out/tmp we can just move it to save time, otherwise we need // to copy so we don't move files from other directories. if strings.HasPrefix(tmpOutput, target.TmpDir()) { if err := os.Rename(tmpOutput, realOutput); err != nil { return true, err } } else { if err := core.RecursiveCopyFile(tmpOutput, realOutput, target.OutMode(), filegroup, false); err != nil { if filegroup && os.IsExist(err) && core.IsSameFile(tmpOutput, realOutput) { // It's possible for two filegroups to race building simultaneously. In that // case one will fail with an ErrExist, which is OK as far as we're concerned // here as long as the file we tried to write really is the same as the input. return true, nil } return true, err } } if target.IsBinary { if err := os.Chmod(realOutput, target.OutMode()); err != nil { return true, err } } return true, nil }
func (cache *dirCache) RetrieveExtra(target *core.BuildTarget, key []byte, out string) bool { outDir := path.Join(core.RepoRoot, target.OutDir()) cacheDir := cache.getPath(target, key) cachedOut := path.Join(cacheDir, out) realOut := path.Join(outDir, out) if !core.PathExists(cachedOut) { log.Debug("%s: %s doesn't exist in dir cache", target.Label, cachedOut) return false } log.Debug("Retrieving %s: %s from dir cache...", target.Label, cachedOut) if dir := path.Dir(realOut); dir != "." { if err := os.MkdirAll(dir, core.DirPermissions); err != nil { log.Warning("Failed to create output directory %s: %s", dir, err) return false } } // It seems to be quite important that we unlink the existing file first to avoid ETXTBSY errors // in cases where we're running an existing binary (as Please does during bootstrap, for example). if err := os.RemoveAll(realOut); err != nil { log.Warning("Failed to unlink existing output %s: %s", realOut, err) return false } // Recursively hardlink files back out of the cache if err := core.RecursiveCopyFile(cachedOut, realOut, fileMode(target), true, true); err != nil { log.Warning("Failed to move cached file to output: %s -> %s: %s", cachedOut, realOut, err) return false } log.Debug("Retrieved %s: %s from dir cache", target.Label, cachedOut) return true }
func (cache *dirCache) storeFile(target *core.BuildTarget, out, cacheDir string) { log.Debug("Storing %s: %s in dir cache...", target.Label, out) if dir := path.Dir(out); dir != "." { if err := os.MkdirAll(path.Join(cacheDir, dir), core.DirPermissions); err != nil { log.Warning("Failed to create cache directory %s: %s", path.Join(cacheDir, dir), err) return } } outFile := path.Join(core.RepoRoot, target.OutDir(), out) cachedFile := path.Join(cacheDir, out) // Remove anything existing if err := os.RemoveAll(cachedFile); err != nil { log.Warning("Failed to remove existing cached file %s: %s", cachedFile, err) } else if err := os.MkdirAll(cacheDir, core.DirPermissions); err != nil { log.Warning("Failed to create cache directory %s: %s", cacheDir, err) return } else if err := core.RecursiveCopyFile(outFile, cachedFile, fileMode(target), true, true); err != nil { // Cannot hardlink files into the cache, must copy them for reals. log.Warning("Failed to store cache file %s: %s", cachedFile, err) } }