Skip to content

Commit aa0b4a8

Browse files
author
Lixiaoming
committed
fix(goctl): resolve pb imports across sibling modules
1 parent 5b74b9a commit aa0b4a8

2 files changed

Lines changed: 144 additions & 16 deletions

File tree

tools/goctl/rpc/generator/mkdir.go

Lines changed: 101 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package generator
22

33
import (
4+
"bufio"
5+
"os"
6+
"path"
47
"path/filepath"
58
"strings"
69

@@ -126,48 +129,47 @@ func mkdir(ctx *ctx.ProjectContext, proto parser.Proto, conf *conf.Config, c *ZR
126129
}
127130
inner[etc] = Dir{
128131
Filename: etcDir,
129-
Package: filepath.ToSlash(filepath.Join(ctx.Path, strings.TrimPrefix(etcDir, ctx.Dir))),
132+
Package: resolveDirPackage(ctx, etcDir, ""),
130133
Base: filepath.Base(etcDir),
131134
GetChildPackage: func(childPath string) (string, error) {
132135
return getChildPackage(etcDir, childPath)
133136
},
134137
}
135138
inner[internal] = Dir{
136139
Filename: internalDir,
137-
Package: filepath.ToSlash(filepath.Join(ctx.Path,
138-
strings.TrimPrefix(internalDir, ctx.Dir))),
139-
Base: filepath.Base(internalDir),
140+
Package: resolveDirPackage(ctx, internalDir, ""),
141+
Base: filepath.Base(internalDir),
140142
GetChildPackage: func(childPath string) (string, error) {
141143
return getChildPackage(internalDir, childPath)
142144
},
143145
}
144146
inner[config] = Dir{
145147
Filename: configDir,
146-
Package: filepath.ToSlash(filepath.Join(ctx.Path, strings.TrimPrefix(configDir, ctx.Dir))),
148+
Package: resolveDirPackage(ctx, configDir, ""),
147149
Base: filepath.Base(configDir),
148150
GetChildPackage: func(childPath string) (string, error) {
149151
return getChildPackage(configDir, childPath)
150152
},
151153
}
152154
inner[logic] = Dir{
153155
Filename: logicDir,
154-
Package: filepath.ToSlash(filepath.Join(ctx.Path, strings.TrimPrefix(logicDir, ctx.Dir))),
156+
Package: resolveDirPackage(ctx, logicDir, ""),
155157
Base: filepath.Base(logicDir),
156158
GetChildPackage: func(childPath string) (string, error) {
157159
return getChildPackage(logicDir, childPath)
158160
},
159161
}
160162
inner[server] = Dir{
161163
Filename: serverDir,
162-
Package: filepath.ToSlash(filepath.Join(ctx.Path, strings.TrimPrefix(serverDir, ctx.Dir))),
164+
Package: resolveDirPackage(ctx, serverDir, ""),
163165
Base: filepath.Base(serverDir),
164166
GetChildPackage: func(childPath string) (string, error) {
165167
return getChildPackage(serverDir, childPath)
166168
},
167169
}
168170
inner[svc] = Dir{
169171
Filename: svcDir,
170-
Package: filepath.ToSlash(filepath.Join(ctx.Path, strings.TrimPrefix(svcDir, ctx.Dir))),
172+
Package: resolveDirPackage(ctx, svcDir, ""),
171173
Base: filepath.Base(svcDir),
172174
GetChildPackage: func(childPath string) (string, error) {
173175
return getChildPackage(svcDir, childPath)
@@ -176,7 +178,7 @@ func mkdir(ctx *ctx.ProjectContext, proto parser.Proto, conf *conf.Config, c *ZR
176178

177179
inner[pb] = Dir{
178180
Filename: pbDir,
179-
Package: filepath.ToSlash(filepath.Join(ctx.Path, strings.TrimPrefix(pbDir, ctx.Dir))),
181+
Package: resolveDirPackage(ctx, pbDir, proto.GoPackage),
180182
Base: filepath.Base(pbDir),
181183
GetChildPackage: func(childPath string) (string, error) {
182184
return getChildPackage(pbDir, childPath)
@@ -185,9 +187,8 @@ func mkdir(ctx *ctx.ProjectContext, proto parser.Proto, conf *conf.Config, c *ZR
185187

186188
inner[protoGo] = Dir{
187189
Filename: protoGoDir,
188-
Package: filepath.ToSlash(filepath.Join(ctx.Path,
189-
strings.TrimPrefix(protoGoDir, ctx.Dir))),
190-
Base: filepath.Base(protoGoDir),
190+
Package: resolveDirPackage(ctx, protoGoDir, proto.GoPackage),
191+
Base: filepath.Base(protoGoDir),
191192
GetChildPackage: func(childPath string) (string, error) {
192193
return getChildPackage(protoGoDir, childPath)
193194
},
@@ -212,16 +213,100 @@ func mkdir(ctx *ctx.ProjectContext, proto parser.Proto, conf *conf.Config, c *ZR
212213
func (d *defaultDirContext) SetPbDir(pbDir, grpcDir string) {
213214
d.inner[pb] = Dir{
214215
Filename: pbDir,
215-
Package: filepath.ToSlash(filepath.Join(d.ctx.Path, strings.TrimPrefix(pbDir, d.ctx.Dir))),
216+
Package: resolveDirPackage(d.ctx, pbDir, ""),
216217
Base: filepath.Base(pbDir),
217218
}
218219

219220
d.inner[protoGo] = Dir{
220221
Filename: grpcDir,
221-
Package: filepath.ToSlash(filepath.Join(d.ctx.Path,
222-
strings.TrimPrefix(grpcDir, d.ctx.Dir))),
223-
Base: filepath.Base(grpcDir),
222+
Package: resolveDirPackage(d.ctx, grpcDir, ""),
223+
Base: filepath.Base(grpcDir),
224+
}
225+
}
226+
227+
func resolveDirPackage(project *ctx.ProjectContext, dir, fallback string) string {
228+
dir = filepath.Clean(dir)
229+
projectDir := filepath.Clean(project.Dir)
230+
231+
if rel, ok := relativeImportPath(projectDir, dir); ok {
232+
return joinImportPath(project.Path, rel)
233+
}
234+
235+
if modulePath, moduleDir, ok := resolveModuleForDir(dir); ok {
236+
if rel, ok := relativeImportPath(moduleDir, dir); ok {
237+
return joinImportPath(modulePath, rel)
238+
}
239+
}
240+
241+
if isImportPath(fallback) {
242+
return filepath.ToSlash(fallback)
243+
}
244+
245+
return joinImportPath(project.Path, strings.TrimPrefix(dir, projectDir))
246+
}
247+
248+
func relativeImportPath(baseDir, targetDir string) (string, bool) {
249+
rel, err := filepath.Rel(baseDir, targetDir)
250+
if err != nil {
251+
return "", false
252+
}
253+
if rel == "." {
254+
return "", true
255+
}
256+
if rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator)) || filepath.IsAbs(rel) {
257+
return "", false
258+
}
259+
260+
return filepath.ToSlash(rel), true
261+
}
262+
263+
func joinImportPath(basePath, rel string) string {
264+
if rel == "" || rel == "." {
265+
return filepath.ToSlash(basePath)
224266
}
267+
268+
return path.Join(filepath.ToSlash(basePath), filepath.ToSlash(rel))
269+
}
270+
271+
func isImportPath(value string) bool {
272+
return value != "" && !strings.HasPrefix(value, ".") && !filepath.IsAbs(value)
273+
}
274+
275+
func resolveModuleForDir(dir string) (modulePath, moduleDir string, ok bool) {
276+
current := filepath.Clean(dir)
277+
for {
278+
goMod := filepath.Join(current, "go.mod")
279+
if info, err := os.Stat(goMod); err == nil && !info.IsDir() {
280+
modulePath, err := readModulePath(goMod)
281+
if err == nil && modulePath != "" {
282+
return modulePath, current, true
283+
}
284+
}
285+
286+
parent := filepath.Dir(current)
287+
if parent == current {
288+
return "", "", false
289+
}
290+
current = parent
291+
}
292+
}
293+
294+
func readModulePath(goMod string) (string, error) {
295+
file, err := os.Open(goMod)
296+
if err != nil {
297+
return "", err
298+
}
299+
defer file.Close()
300+
301+
scanner := bufio.NewScanner(file)
302+
for scanner.Scan() {
303+
line := strings.TrimSpace(scanner.Text())
304+
if strings.HasPrefix(line, "module ") {
305+
return strings.TrimSpace(strings.TrimPrefix(line, "module ")), nil
306+
}
307+
}
308+
309+
return "", scanner.Err()
225310
}
226311

227312
func (d *defaultDirContext) GetCall() Dir {

tools/goctl/rpc/generator/mkdir_test.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
package generator
22

33
import (
4+
"os"
5+
"path/filepath"
46
"testing"
57

68
"github.com/emicklei/proto"
79
"github.com/stretchr/testify/assert"
810
"github.com/zeromicro/go-zero/tools/goctl/rpc/parser"
11+
ctxpkg "github.com/zeromicro/go-zero/tools/goctl/util/ctx"
912
)
1013

1114
func TestServiceNameDetermination(t *testing.T) {
@@ -145,3 +148,43 @@ func TestServiceNameFallbackWithEmptyPackage(t *testing.T) {
145148
serviceName := determineServiceName(p, ctx)
146149
assert.Equal(t, "myservice", serviceName)
147150
}
151+
152+
func TestResolveDirPackageWithinCurrentModule(t *testing.T) {
153+
tmp := t.TempDir()
154+
mainDir := filepath.Join(tmp, "sales-admin")
155+
156+
err := os.MkdirAll(filepath.Join(mainDir, "internal", "logic"), 0o755)
157+
assert.NoError(t, err)
158+
err = os.WriteFile(filepath.Join(mainDir, "go.mod"), []byte("module sales-admin\n\ngo 1.25.5\n"), 0o644)
159+
assert.NoError(t, err)
160+
161+
project := &ctxpkg.ProjectContext{
162+
Path: "sales-admin",
163+
Dir: mainDir,
164+
}
165+
166+
got := resolveDirPackage(project, filepath.Join(mainDir, "internal", "logic"), "")
167+
assert.Equal(t, "sales-admin/internal/logic", got)
168+
}
169+
170+
func TestResolveDirPackageForSiblingModule(t *testing.T) {
171+
tmp := t.TempDir()
172+
mainDir := filepath.Join(tmp, "sales-admin")
173+
pbDir := filepath.Join(tmp, "sales-center", "pb", "admin")
174+
175+
err := os.MkdirAll(filepath.Join(mainDir, "internal", "logic"), 0o755)
176+
assert.NoError(t, err)
177+
err = os.MkdirAll(pbDir, 0o755)
178+
assert.NoError(t, err)
179+
err = os.WriteFile(filepath.Join(mainDir, "go.mod"), []byte("module sales-admin\n\ngo 1.25.5\n"), 0o644)
180+
assert.NoError(t, err)
181+
err = os.WriteFile(filepath.Join(tmp, "sales-center", "go.mod"), []byte("module sales-center\n\ngo 1.25.5\n"), 0o644)
182+
assert.NoError(t, err)
183+
project := &ctxpkg.ProjectContext{
184+
Path: "sales-admin",
185+
Dir: mainDir,
186+
}
187+
188+
got := resolveDirPackage(project, pbDir, "")
189+
assert.Equal(t, "sales-center/pb/admin", got)
190+
}

0 commit comments

Comments
 (0)