github.com/please-build/go-rules/tools/please_go@v0.0.0-20240319165128-ea27d6f5caba/generate/gomoddeps/gomoddeps.go (about) 1 // Package gomoddeps parses dependencies and replacements from the host and module go.mod files. 2 package gomoddeps 3 4 import ( 5 "errors" 6 "fmt" 7 "io/fs" 8 "os" 9 10 "golang.org/x/mod/modfile" 11 ) 12 13 // GetCombinedDepsAndReplacements returns dependencies and replacements after inspecting both 14 // the host and the module go.mod files. 15 // Module's replacement are only returned if there is no host go.mod file. 16 func GetCombinedDepsAndReplacements(hostGoModPath, moduleGoModPath string) ([]string, map[string]string, error) { 17 var err error 18 19 hostDeps := []string{} 20 hostReplacements := map[string]string{} 21 if hostGoModPath != "" { 22 hostDeps, hostReplacements, err = getDepsAndReplacements(hostGoModPath, false) 23 if err != nil { 24 return nil, nil, fmt.Errorf("failed to read host repo go.mod %q: %w", hostGoModPath, err) 25 } 26 } 27 28 var moduleDeps []string 29 var moduleReplacements = map[string]string{} 30 useLaxParsingForModule := true 31 if hostGoModPath == "" { 32 // If we're only considering the module then we want to extract the replacement's as well (lax mode 33 // doesn't parse them). 34 useLaxParsingForModule = false 35 } 36 moduleDeps, moduleReplacements, err = getDepsAndReplacements(moduleGoModPath, useLaxParsingForModule) 37 if err != nil { 38 if errors.Is(err, fs.ErrNotExist) { 39 return hostDeps, hostReplacements, nil 40 } 41 return nil, nil, fmt.Errorf("failed to read module go.mod %q: %w", moduleGoModPath, err) 42 } 43 44 var replacements map[string]string 45 if hostGoModPath == "" { 46 replacements = moduleReplacements 47 } else { 48 replacements = hostReplacements 49 } 50 51 return append(hostDeps, moduleDeps...), replacements, nil 52 } 53 54 // getDepsAndReplacements parses the go.mod file and returns all the dependencies 55 // and replacement directives from it. 56 func getDepsAndReplacements(goModPath string, useLaxParsing bool) ([]string, map[string]string, error) { 57 data, err := os.ReadFile(goModPath) 58 if err != nil { 59 return nil, nil, err 60 } 61 62 var modFile *modfile.File 63 if useLaxParsing { 64 modFile, err = modfile.ParseLax(goModPath, data, nil) 65 } else { 66 modFile, err = modfile.Parse(goModPath, data, nil) 67 } 68 if err != nil { 69 return nil, nil, err 70 } 71 72 moduleDeps := make([]string, 0, len(modFile.Require)) 73 // TODO we could probably validate these are known modules 74 for _, req := range modFile.Require { 75 moduleDeps = append(moduleDeps, req.Mod.Path) 76 } 77 78 replacements := make(map[string]string, len(modFile.Replace)) 79 for _, replace := range modFile.Replace { 80 replacements[replace.Old.Path] = replace.New.Path 81 } 82 83 return moduleDeps, replacements, nil 84 }