如何查看嵌套 Go 依赖项的完整依赖关系树

问题描述 投票:0回答:6

我正在尝试调试 CI 中的以下构建错误,其中“A 依赖于 B,而 B 无法构建,因为它依赖于 C。”我正在构建不直接依赖于 kafkaAvailMonitor.go 的数据服务,这使得此错误难以追踪。换句话说:

数据(我正在构建的)取决于(?),这取决于 kafkaAvailMonitor.go

对于开发人员来说,修复问题似乎微不足道,他们只是“去获取任何东西”,但我不能在发布过程中这样做 - 我必须找到添加依赖项的人并要求他们修复它。

我知道有一些工具可以可视化依赖关系树和其他更复杂的构建系统,但这似乎是一个非常基本的问题:有什么方法可以查看完整的依赖关系树以了解导致构建问题的原因?

go build -a -v

../../../msgq/kafkaAvailMonitor.go:8:2: cannot find package 
  "github.com/Shopify/sarama/tz/breaker" in any of:
  /usr/lib/go-1.6/src/github.com/Shopify/sarama/tz/breaker (from $GOROOT)
  /home/jenkins/go/src/github.com/Shopify/sarama/tz/breaker (from $GOPATH)
  /home/jenkins/vendor-library/src/github.com/Shopify/sarama/tz/breaker
  /home/jenkins/go/src/github.com/Shopify/sarama/tz/breaker
  /home/jenkins/vendor-library/src/github.com/Shopify/sarama/tz/breaker
go build dependency-management
6个回答
50
投票

使用模块时,您也许可以从

go mod graph
获得您需要的东西。

用法:go mod graph

Graph 打印模块需求图(应用了替换)
以文本形式。输出中的每一行都有两个以空格分隔的字段:一个模块
及其要求之一。每个模块都被标识为以下形式的字符串
路径@version,但主模块除外,主模块没有@version后缀。

即,对于原始问题,运行

go mod graph | grep github.com/Shopify/sarama
,然后仔细查看左侧的每个条目。


24
投票

如果以下不是堆栈跟踪,那是什么?

这是 Go 正在寻找丢失的包的路径列表。

我不知道谁在导入 kafkaAvailMonitor.go

它不是“导入的”,只是您的源代码的一部分并进行编译。
但它无法编译,因为它需要

github.com/Shopify/sarama/tz/breaker
,而它不在
GOROOT
GOPATH
中。

不过,请检查您的直接包裹上会返回什么

go list
,看看是否提到了kafkaAvailMonitor

go list

 可以显示您的包直接依赖的包,或其完整的传递依赖项集。

% go list -f '{{ .Imports }}' github.com/davecheney/profile [io/ioutil log os os/signal path/filepath runtime runtime/pprof] % go list -f '{{ .Deps }}' github.com/davecheney/profile [bufio bytes errors fmt io io/ioutil log math os os/signal path/filepath reflect run

然后您可以编写 go list 脚本来列出

所有依赖项。 例如,请参阅
这个 bash 脚本,作者:Noel Cower (nilium

)

#!/usr/bin/env bash # Usage: lsdep [PACKAGE...] # # Example (list github.com/foo/bar and package dir deps [the . argument]) # $ lsdep github.com/foo/bar . # # By default, this will list dependencies (imports), test imports, and test # dependencies (imports made by test imports). You can recurse further by # setting TESTIMPORTS to an integer greater than one, or to skip test # dependencies, set TESTIMPORTS to 0 or a negative integer. : "${TESTIMPORTS:=1}" lsdep_impl__ () { local txtestimps='{{range $v := .TestImports}}{{print . "\n"}}{{end}}' local txdeps='{{range $v := .Deps}}{{print . "\n"}}{{end}}' { go list -f "${txtestimps}${txdeps}" "$@" if [[ -n "${TESTIMPORTS}" ]] && [[ "${TESTIMPORTS:-1}" -gt 0 ]] then go list -f "${txtestimps}" "$@" | sort | uniq | comm -23 - <(go list std | sort) | TESTIMPORTS=$((TESTIMPORTS - 1)) xargs bash -c 'lsdep_impl__ "$@"' "$0" fi } | sort | uniq | comm -23 - <(go list std | sort) } export -f lsdep_impl__ lsdep_impl__ "$@"
    

10
投票
我只想在这里提一下,

go mod why

也可以提供帮助。无论如何,你无法获取并显示整棵树。但是您可以追溯到子依赖项的一个分支,直到其父根。

示例:

$ go mod why github.com/childdep # github.com/childdep github.com/arepo.git/service github.com/arepo.git/service.test github.com/anotherrepo.git/mocks github.com/childdep
这意味着,您终于在“anotherrepo.git/mocks”中导入了“childdep”。


8
投票
可以尝试这个

https://github.com/vc60er/deptree

redis git:(master) go mod graph | deptree -d 3 package: github.com/go-redis/redis/v9 dependence tree: ┌── github.com/cespare/xxhash/[email protected] ├── github.com/dgryski/[email protected] ├── github.com/fsnotify/[email protected] │ └── golang.org/x/[email protected] ├── github.com/nxadm/[email protected] │ ├── github.com/fsnotify/[email protected] │ │ └── golang.org/x/[email protected] │ └── gopkg.in/[email protected] ├── github.com/onsi/[email protected] │ ├── github.com/go-task/[email protected] │ │ ├── github.com/davecgh/[email protected] │ │ └── github.com/stretchr/[email protected] │ │ └── ...
    

2
投票
上面的答案仍然没有向我显示依赖关系树,因此我花时间编写了一个 Python 脚本来完成我需要的操作 - 希望这对其他人有帮助。

上述解决方案(其他建议如 go list)的问题是它只告诉我顶层。他们不会“遍历树”。这是我得到的输出 - 它没有比 go build 给我的更多帮助。

.../npd/auth/ .../mon/mlog .../auth/service
这就是我想要得到的 - 我知道 auth 被破坏(顶部)并且断路器在 go build 中被破坏(底部),但我不知道中间是什么 - 我下面的脚本给了我这个输出。

.../npd/auth/ .../npd/auth/service .../npd/auth/resource .../npd/auth/storage .../npd/middleware .../npd/metrics/persist .../npd/kafka .../vendor-library/src/github.com/Shopify/sarama .../vendor-library/src/github.com/Shopify/sarama/vz/breaker
我的Python脚本:

import subprocess import os folder_locations=['.../go/src','.../vendor-library/src'] def getImports(_cwd): #When the commands were combined they overflowed the bugger and I couldn't find a workaround cmd1 = ["go", "list", "-f", " {{.ImportPath}}","./..."] cmd2 = ["go", "list", "-f", " {{.Imports}}","./..."] process = subprocess.Popen(' '.join(cmd1), cwd=_cwd,shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) out1, err = process.communicate() process = subprocess.Popen(' '.join(cmd2), cwd=_cwd,shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) out2, err = process.communicate() out2clean=str(out2).replace("b'",'').replace('[','').replace(']','').replace("'",'') return str(out1).split('\\n'),out2clean.split('\\n') def getFullPath(rel_path): for i in folder_locations: if os.path.exists(i+'/'+rel_path): return i+'/'+rel_path return None def getNextImports(start,depth): depth=depth+1 indent = '\t'*(depth+1) for i,val in enumerate(start.keys()): if depth==1: print (val) out1,out2=getImports(val) noDeps=True for j in out2[i].split(' '): noDeps=False _cwd2=getFullPath(j) new_tree = {_cwd2:[]} not_exists = (not _cwd2 in alltmp) if not_exists: print(indent+_cwd2) start[val].append(new_tree) getNextImports(new_tree,depth) alltmp.append(_cwd2) if noDeps: print(indent+'No deps') _cwd = '/Users/.../npd/auth' alltmp=[] start_root={_cwd:[]} getNextImports(start_root,0)
    

0
投票

go mod graph

https://github.com/vc60er/deptree
 只能处理 
go mod,但不能处理包

我发现工具

深度可以处理包依赖树

并且它支持 -explain 显示

包导入链,就像 go mod Why 一样。

https://github.com/KyleBanks/深度

© www.soinside.com 2019 - 2024. All rights reserved.