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 }