Skip to content

Commit 0fd7e1e

Browse files
authored
Merge pull request #29 from github/owen-mc/subpackes-in-modules-txt
Fix writing `vendor/modules.txt` to deal with subpackages
2 parents 1b65378 + 1e8a138 commit 0fd7e1e

File tree

4 files changed

+89
-17
lines changed

4 files changed

+89
-17
lines changed

depstubber.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"flag"
77
"fmt"
88
"io"
9-
"io/ioutil"
109
"log"
1110
"os"
1211
"path/filepath"
@@ -153,7 +152,7 @@ func createStubs(packageName string, typeNames []string, funcAndVarNames []strin
153152
g.srcFunctions = strings.Join(funcAndVarNames, ",")
154153

155154
if *copyrightFile != "" {
156-
header, err := ioutil.ReadFile(*copyrightFile)
155+
header, err := os.ReadFile(*copyrightFile)
157156
if err != nil {
158157
log.Fatalf("Failed reading copyright file: %v", err)
159158
}

reflect.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import (
88
"flag"
99
"fmt"
1010
"go/build"
11-
"io/ioutil"
1211
"log"
1312
"os"
1413
"os/exec"
@@ -44,7 +43,7 @@ func writeProgram(importPath string, types []string, values []string) ([]byte, e
4443

4544
// run the given program and parse the output as a model.Package.
4645
func run(program string) (*model.PackedPkg, error) {
47-
f, err := ioutil.TempFile("", "")
46+
f, err := os.CreateTemp("", "")
4847
if err != nil {
4948
return nil, err
5049
}
@@ -85,7 +84,7 @@ func run(program string) (*model.PackedPkg, error) {
8584
// parses the output as a model.Package.
8685
func runInDir(program []byte, dir string) (*model.PackedPkg, error) {
8786
// We use TempDir instead of TempFile so we can control the filename.
88-
tmpDir, err := ioutil.TempDir(dir, "depstubber_reflect_")
87+
tmpDir, err := os.MkdirTemp(dir, "depstubber_reflect_")
8988
if err != nil {
9089
return nil, err
9190
}
@@ -101,7 +100,7 @@ func runInDir(program []byte, dir string) (*model.PackedPkg, error) {
101100
progBinary += ".exe"
102101
}
103102

104-
if err := ioutil.WriteFile(filepath.Join(tmpDir, progSource), program, 0600); err != nil {
103+
if err := os.WriteFile(filepath.Join(tmpDir, progSource), program, 0600); err != nil {
105104
return nil, err
106105
}
107106

util.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"errors"
55
"fmt"
66
"io"
7-
"io/ioutil"
87
"log"
98
"os"
109
"runtime/debug"
@@ -23,7 +22,7 @@ func removeDot(s string) string {
2322

2423
// packageNameOfDir get package import path via dir
2524
func packageNameOfDir(srcDir string) (string, error) {
26-
files, err := ioutil.ReadDir(srcDir)
25+
files, err := os.ReadDir(srcDir)
2726
if err != nil {
2827
log.Fatal(err)
2928
}

vendor.go

Lines changed: 84 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@ package main
44

55
import (
66
"bytes"
7-
"io/ioutil"
7+
"go/parser"
8+
"go/token"
89
"log"
910
"os"
1011
"path/filepath"
12+
"regexp"
13+
"sort"
1114
"strings"
1215

1316
"golang.org/x/mod/modfile"
@@ -38,7 +41,7 @@ func findModuleRoot(dir string) (root string) {
3841
}
3942

4043
func loadModFile(filename string) *modfile.File {
41-
data, err := ioutil.ReadFile(filename)
44+
data, err := os.ReadFile(filename)
4245
if err != nil {
4346
panic(err)
4447
}
@@ -85,24 +88,30 @@ func stubModulesTxt() {
8588
}
8689

8790
modFile := loadModFile(filepath.Join(modRoot, "go.mod"))
88-
8991
vdir := filepath.Join(modRoot, "vendor")
9092

9193
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
94+
// Find imports from all Go files in the project
95+
usedPackages := findPackagesInSourceCode(modRoot)
9496

9597
generated := make(map[module.Version]bool)
9698
var buf bytes.Buffer
9799
for _, r := range modFile.Require {
98-
// TODO: support replace lines
99100
generated[r.Mod] = true
100101
line := moduleLine(r.Mod, module.Version{})
101102
buf.WriteString(line)
102-
103103
buf.WriteString("## explicit\n")
104104

105-
buf.WriteString(r.Mod.Path + "\n")
105+
// List package paths that are used in the source code
106+
packagesForModule := findPackagesForModule(r.Mod.Path, usedPackages)
107+
if len(packagesForModule) > 0 {
108+
for _, pkg := range packagesForModule {
109+
buf.WriteString(pkg + "\n")
110+
}
111+
} else {
112+
// If we can't find any packages then just list the module path itself
113+
buf.WriteString(r.Mod.Path + "\n")
114+
}
106115
}
107116

108117
// Record unused and wildcard replacements at the end of the modules.txt file:
@@ -128,8 +137,74 @@ func stubModulesTxt() {
128137
log.Fatalf("go mod vendor: %v", err)
129138
}
130139

131-
if err := ioutil.WriteFile(filepath.Join(vdir, "modules.txt"), buf.Bytes(), 0666); err != nil {
140+
if err := os.WriteFile(filepath.Join(vdir, "modules.txt"), buf.Bytes(), 0666); err != nil {
132141
log.Fatalf("go mod vendor: %v", err)
133142
}
134143
}
135144
}
145+
146+
// findPackagesInSourceCode scans all Go files in the directory tree and extracts import paths
147+
func findPackagesInSourceCode(root string) map[string]bool {
148+
packages := make(map[string]bool)
149+
150+
err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
151+
if err != nil {
152+
return err
153+
}
154+
155+
// Skip vendor directory and hidden directories
156+
if info.IsDir() && (info.Name() == "vendor" || strings.HasPrefix(info.Name(), ".")) {
157+
return filepath.SkipDir
158+
}
159+
160+
// Only process Go files
161+
if !info.IsDir() && strings.HasSuffix(path, ".go") {
162+
fset := token.NewFileSet()
163+
file, err := parser.ParseFile(fset, path, nil, parser.ImportsOnly)
164+
if err != nil {
165+
return err
166+
}
167+
168+
// Extract import paths from the AST
169+
for _, imp := range file.Imports {
170+
pkgPath := strings.Trim(imp.Path.Value, "\"")
171+
packages[pkgPath] = true
172+
}
173+
}
174+
return nil
175+
})
176+
177+
if err != nil {
178+
log.Printf("Warning: error walking source directory: %v", err)
179+
}
180+
181+
return packages
182+
}
183+
184+
// Compile the regular expression once
185+
var majorVersionSuffixRegex = regexp.MustCompile(`^/v[1-9][0-9]*(/|$)`)
186+
187+
// findPackagesForModule returns the submodules of a given module that are actually used in the source code
188+
func findPackagesForModule(modulePath string, usedPackages map[string]bool) []string {
189+
var packages []string
190+
191+
for pkg := range usedPackages {
192+
// Check if this package belongs to the module
193+
if strings.HasPrefix(pkg, modulePath) {
194+
// Extract the part after modulePath
195+
suffix := pkg[len(modulePath):]
196+
197+
// If `suffix` begins with a major version suffix then we do not have the right module
198+
// path. For example, if the module path is `example.com/mymodule` and the package path
199+
// is `example.com/mymodule/v2/submodule` then we should not consider it a match - it
200+
// is really a match for the module `example.com/mymodule/v2`.
201+
if !majorVersionSuffixRegex.MatchString(suffix) {
202+
packages = append(packages, pkg)
203+
}
204+
}
205+
}
206+
207+
// Sort packages for consistent output
208+
sort.Strings(packages)
209+
return packages
210+
}

0 commit comments

Comments
 (0)