Skip to content

Commit 52ea7b9

Browse files
committed
Fix vendor/modules.txt using string matching
1 parent 9b61b6f commit 52ea7b9

1 file changed

Lines changed: 120 additions & 6 deletions

File tree

vendor.go

Lines changed: 120 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"log"
99
"os"
1010
"path/filepath"
11+
"regexp"
1112
"strings"
1213

1314
"golang.org/x/mod/modfile"
@@ -85,24 +86,30 @@ func stubModulesTxt() {
8586
}
8687

8788
modFile := loadModFile(filepath.Join(modRoot, "go.mod"))
88-
8989
vdir := filepath.Join(modRoot, "vendor")
9090

9191
if gv := modFile.Go; gv != nil && semver.Compare("v"+gv.Version, "v1.14") >= 0 {
92-
// If the Go version is at least 1.14, generate a dummy modules.txt using only the information
93-
// in the go.mod file
92+
// Find imports from all Go files in the project
93+
usedPackages := findPackagesInSourceCode(modRoot)
9494

9595
generated := make(map[module.Version]bool)
9696
var buf bytes.Buffer
9797
for _, r := range modFile.Require {
98-
// TODO: support replace lines
9998
generated[r.Mod] = true
10099
line := moduleLine(r.Mod, module.Version{})
101100
buf.WriteString(line)
102-
103101
buf.WriteString("## explicit\n")
104102

105-
buf.WriteString(r.Mod.Path + "\n")
103+
// List submodules that are actually used in the source code
104+
submodulesForModule := findSubmodulesForModule(r.Mod.Path, usedPackages)
105+
if len(submodulesForModule) > 0 {
106+
for _, submod := range submodulesForModule {
107+
buf.WriteString(submod + "\n")
108+
}
109+
} else {
110+
// If no specific submodules are used, just list the module path itself
111+
buf.WriteString(r.Mod.Path + "\n")
112+
}
106113
}
107114

108115
// Record unused and wildcard replacements at the end of the modules.txt file:
@@ -133,3 +140,110 @@ func stubModulesTxt() {
133140
}
134141
}
135142
}
143+
144+
// findPackagesInSourceCode scans all Go files in the directory tree and extracts import paths
145+
func findPackagesInSourceCode(root string) map[string]bool {
146+
packages := make(map[string]bool)
147+
148+
err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
149+
if err != nil {
150+
return err
151+
}
152+
153+
// Skip vendor directory and hidden directories
154+
if info.IsDir() && (info.Name() == "vendor" || strings.HasPrefix(info.Name(), ".")) {
155+
return filepath.SkipDir
156+
}
157+
158+
// Only process Go files
159+
if !info.IsDir() && strings.HasSuffix(path, ".go") {
160+
content, err := os.ReadFile(path)
161+
if err != nil {
162+
return err
163+
}
164+
165+
// Simple parsing to find import statements
166+
// This is a basic implementation - a more robust solution would use go/parser
167+
lines := strings.Split(string(content), "\n")
168+
inImport := false
169+
for _, line := range lines {
170+
line = strings.TrimSpace(line)
171+
172+
if strings.HasPrefix(line, "import (") {
173+
inImport = true
174+
continue
175+
}
176+
177+
if inImport && strings.HasPrefix(line, ")") {
178+
inImport = false
179+
continue
180+
}
181+
182+
if strings.HasPrefix(line, "import ") || inImport {
183+
// Extract package path from import line
184+
pkgPath := extractPackagePath(line)
185+
if pkgPath != "" {
186+
packages[pkgPath] = true
187+
}
188+
}
189+
}
190+
}
191+
return nil
192+
})
193+
194+
if err != nil {
195+
log.Printf("Warning: error walking source directory: %v", err)
196+
}
197+
198+
return packages
199+
}
200+
201+
// extractPackagePath parses an import line to extract the package path
202+
func extractPackagePath(line string) string {
203+
line = strings.TrimSpace(line)
204+
205+
// Skip if not an import line
206+
if !(strings.HasPrefix(line, "import ") || strings.Contains(line, "\"") || strings.Contains(line, "`")) {
207+
return ""
208+
}
209+
210+
// Handle quoted imports
211+
if strings.Contains(line, "\"") {
212+
start := strings.Index(line, "\"")
213+
end := strings.LastIndex(line, "\"")
214+
if start >= 0 && end > start {
215+
return line[start+1 : end]
216+
}
217+
}
218+
219+
// Handle backtick imports
220+
if strings.Contains(line, "`") {
221+
start := strings.Index(line, "`")
222+
end := strings.LastIndex(line, "`")
223+
if start >= 0 && end > start {
224+
return line[start+1 : end]
225+
}
226+
}
227+
228+
return ""
229+
}
230+
231+
// findSubmodulesForModule returns the submodules of a given module that are actually used in the source code
232+
func findSubmodulesForModule(modulePath string, usedPackages map[string]bool) []string {
233+
var submodules []string
234+
235+
for pkg := range usedPackages {
236+
// Check if this package belongs to the module
237+
if strings.HasPrefix(pkg, modulePath) {
238+
// Extract the part after modulePath
239+
suffix := pkg[len(modulePath):]
240+
matched, _ := regexp.MatchString(`^/v[1-9][0-9]*(/|$)`, suffix)
241+
242+
if !matched {
243+
submodules = append(submodules, pkg)
244+
}
245+
}
246+
}
247+
248+
return submodules
249+
}

0 commit comments

Comments
 (0)