package gocompiler import ( "os" "path" "path/filepath" "strings" "golang.org/x/mod/modfile" ) // dotSlash is ./ var dotSlash = string([]rune{'.', os.PathSeparator}) // RelocateGoModFile transforms all references to a new path. // // expects the modfile to have been parsed with an absolute path. func RelocateGoModFile( modf *modfile.File, nextModPath string, ) error { // Identify the original module path for relative replacements. prevGoModPath := modf.Syntax.Name // has no effect if the path is already absolute. modPathAbs, err := filepath.Abs(path.Dir(prevGoModPath)) if err != nil { return err } // Resolve the destination directory for the relocated module. nextGoModDir := path.Dir(nextModPath) codegenModDir, err := filepath.Abs(nextGoModDir) if err != nil { return err } // Collect relative replacements using the destination module directory. var adjOps [](func() error) for _, srcReplace := range modf.Replace { newPath := srcReplace.New.Path // if the path replacement has a ./ or ../ prefix, update the relative path if strings.HasPrefix(newPath, "./") || strings.HasPrefix(newPath, "../") { // join the absolute path to the module with the relative replacement // prevNewPathAbs is the absolute path to the replacement module. prevNewPathAbs := filepath.Join(modPathAbs, newPath) // newPathRelative is the relative path from the new module to the replacement. newPathRelative, err := filepath.Rel(codegenModDir, prevNewPathAbs) if err != nil { return err } // ensure starts with ./ newPathRelative = ensureStartsWithDotSlash(newPathRelative) // add a new replacement to override the old oldSrcReplacePath := srcReplace.Old.Path oldSrcReplaceVersion := srcReplace.Old.Version oldSrcReplaceNewVersion := srcReplace.New.Version adjOps = append(adjOps, func() error { return modf.AddReplace( oldSrcReplacePath, oldSrcReplaceVersion, newPathRelative, oldSrcReplaceNewVersion, ) }) } } // Apply the replacement paths after inspecting the original module. for _, op := range adjOps { if err := op(); err != nil { return err } } // Remove obsolete module syntax after replacing the paths. modf.Cleanup() return nil } // ensureStartsWithDotSlash returns the string with a ./ or ../ prefix func ensureStartsWithDotSlash(p string) string { // ensure starts with ./ or ../ - check simply for '.' if !strings.HasPrefix(p, dotSlash[:1]) { p = dotSlash + p } return p }