// key 表示一组缓存,pathList 即这组缓存相关的文件 // 比较容易用错的情况: // key := 1 // pathList := []string{"/a.txt","/b.txt"} // a.txt 或者 b.txt 中任意有文件发生变化,这个 key 对应的缓存都会被更新 // 如果要对单个文件进行缓存控制,那么应该一个文件一个 key func MustMd5FileChangeCache(key string, pathList []string, f func()) { // 此处需要考虑, // 用户新添加了一个文件 // 用户删除了一个文件 // 用户编辑了一个文件 // 用户在目录里面添加了一个文件 // 用在在目录里面删除了一个文件 //读取文件修改时间缓存信息 toChange := false cacheInfo := map[string]string{} cacheFilePath := getFileChangeCachePath(key) err := kmgGob.ReadFile(cacheFilePath, &cacheInfo) if err != nil { //忽略缓存读取的任何错误 cacheInfo = map[string]string{} } hasReadFileMap := map[string]bool{} for _, path := range pathList { statList, err := kmgFile.GetAllFileAndDirectoryStat(path) if err != nil { if os.IsNotExist(err) { toChange = true //fmt.Printf("[MustFileChangeCache] path:[%s] not exist\n", path) break } panic(err) } for _, stat := range statList { if stat.Fi.IsDir() { continue } hasReadFileMap[stat.FullPath] = true if stat.Fi.Mode()&os.ModeSymlink == os.ModeSymlink { if "symlink_"+kmgFile.MustReadSymbolLink(stat.FullPath) != cacheInfo[stat.FullPath] { toChange = true break } continue } if kmgCrypto.MustMd5File(stat.FullPath) != cacheInfo[stat.FullPath] { toChange = true //fmt.Printf("[MustMd5FileChangeCache] path:[%s] mod md5 not match save[%s] file[%s]\n", stat.FullPath, // cacheMd5, kmgCrypto.MustMd5File(stat.FullPath)) break } } if toChange { break } } //删除一个已经存储在缓存列表里面的文件,是一个修改. for fullPath := range cacheInfo { if !hasReadFileMap[fullPath] { toChange = true break } } if !toChange { return } f() cacheInfo = map[string]string{} for _, path := range pathList { statList, err := kmgFile.GetAllFileAndDirectoryStat(path) if err != nil { panic(err) } for _, stat := range statList { if stat.Fi.IsDir() { continue } if stat.Fi.Mode()&os.ModeSymlink == os.ModeSymlink { linkToPath := kmgFile.MustReadSymbolLink(stat.FullPath) cacheInfo[stat.FullPath] = "symlink_" + linkToPath continue } cacheInfo[stat.FullPath] = kmgCrypto.MustMd5File(stat.FullPath) } } kmgFile.MustMkdirForFile(cacheFilePath) kmgGob.MustWriteFile(cacheFilePath, cacheInfo) //保存文件缓存信息 return }
func goRunInstall(goPath string, pathOrPkg string) { //只能更新本GOPATH里面的pkg,不能更多多个GOPATH里面其他GOPATH的pkg缓存. // go install 已知bug1 删除某个package里面的部分文件,然后由于引用到了旧的实现的代码,不会报错.删除pkg解决问题. // go install 已知bug2 如果一个package先是main,然后build了一个东西,然后又改成了非main,再gorun会使用旧的缓存/bin/里面的缓存. // TODO 已知bug3 当同一个项目的多个编译目标使用了同一个pkg,然后这个pkg变化了,缓存会出现A/B 问题,导致缓存完全无用. ctx := &goRunInstallContext{ platform: kmgPlatform.GetCompiledPlatform().String(), targetPkgMapCache: map[string]bool{}, pkgMapCache: map[string]*gorunCachePkgInfo{}, } ctx.targetCachePath = filepath.Join(goPath, "tmp", "gorun", kmgCrypto.Md5HexFromString("target_"+pathOrPkg+"_"+ctx.platform)) ctx.pkgCachePath = filepath.Join(goPath, "tmp", "gorun", "allPkgCache") ok := ctx.goRunInstallIsValidAndInvalidCache(goPath, pathOrPkg) if ok { //fmt.Println("use cache") return } //fmt.Println("not use cache") runCmdSliceWithGoPath(goPath, []string{"go", "install", pathOrPkg}) // 填充缓存 platform := ctx.platform ctx.targetPkgMapCache = map[string]bool{} outputJson := kmgCmd.CmdSlice([]string{"go", "list", "-json", pathOrPkg}). MustSetEnv("GOPATH", goPath).MustCombinedOutput() listObj := &struct { Deps []string Name string }{} kmgJson.MustUnmarshal(outputJson, &listObj) if listObj.Name != "main" { fmt.Printf("run non main package %s\n", pathOrPkg) return } listObj.Deps = append(listObj.Deps, pathOrPkg) for _, pkgName := range listObj.Deps { srcpkgPath := filepath.Join(goPath, "src", pkgName) fileList, err := kmgFile.ReadDirFileOneLevel(srcpkgPath) if err != nil { if !os.IsNotExist(err) { panic(err) } // 没有找到pkg,可能是这个pkg在GOROOT出现过,此处暂时不管. continue } ctx.targetPkgMapCache[pkgName] = true pkgInfo := &gorunCachePkgInfo{ GoFileMap: map[string]uint64{}, } for _, file := range fileList { ext := filepath.Ext(file) if ext == ".go" { pkgInfo.GoFileMap[file] = mustCheckSumFile(filepath.Join(srcpkgPath, file)) } } pkgInfo.IsMain = pkgName == pathOrPkg pkgInfo.Name = pkgName pkgBinPath := pkgInfo.getPkgBinPath(goPath, platform) pkgInfo.PkgMd5 = mustCheckSumFile(pkgBinPath) ctx.pkgMapCache[pkgName] = pkgInfo } kmgGob.MustWriteFile(ctx.targetCachePath, ctx.targetPkgMapCache) kmgGob.MustWriteFile(ctx.pkgCachePath, ctx.pkgMapCache) }
// 这个函数bug太多,以废弃,请使用 MustMd5FileChangeCache // 根据文件变化,对f这个请求进行缓存 // key表示这件事情的缓存key // pathList表示需要监控的目录 // 文件列表里面如果有文件不存在,会运行代码 // 已知bug,小于1秒的修改不能被检测到. // @deprecated func MustFileChangeCache(key string, pathList []string, f func()) { //读取文件修改时间缓存信息 toChange := false cacheInfo := map[string]time.Time{} cacheFilePath := getFileChangeCachePath(key) err := kmgGob.ReadFile(cacheFilePath, &cacheInfo) if err != nil { cacheInfo = map[string]time.Time{} } for _, path := range pathList { statList, err := kmgFile.GetAllFileAndDirectoryStat(path) if err != nil { if os.IsNotExist(err) { toChange = true //fmt.Printf("[MustFileChangeCache] path:[%s] not exist\n", path) break } panic(err) } for _, stat := range statList { if stat.Fi.IsDir() { continue } cacheTime := cacheInfo[stat.FullPath] if cacheTime.IsZero() { toChange = true //fmt.Printf("[MustFileChangeCache] path:[%s] no save mod time\n", stat.FullPath) break } if stat.Fi.ModTime() != cacheTime { toChange = true //fmt.Printf("[MustFileChangeCache] path:[%s] mod time not match save[%s] file[%s]\n", stat.FullPath, // cacheTime, stat.Fi.ModTime()) break } } if toChange { break } } if !toChange { return } f() cacheInfo = map[string]time.Time{} for _, path := range pathList { statList, err := kmgFile.GetAllFileAndDirectoryStat(path) if err != nil { panic(err) } for _, stat := range statList { if stat.Fi.IsDir() { continue } cacheInfo[stat.FullPath] = stat.Fi.ModTime() } } kmgFile.MustMkdirForFile(cacheFilePath) kmgGob.MustWriteFile(cacheFilePath, cacheInfo) //保存文件缓存信息 return }