文章标题 原创 翻译 转载 文章内容 我们知道通过```go mod graph```命令可以输出当前工程的模块依赖图,但是这个输出是纯文本的并且只是简单的一对一的关系。 很难看出所有模块之间的依赖关系,如果能用图的形式来显示就清晰多了。 [Graphviz](https://www.ibm.com/developerworks/cn/aix/library/au-aix-graphviz/index.html)工具可以画出我们需要的图表。 # 实现步骤 * 使用go mod graph命令得到各模块之间的依赖关系 * 将上述文本转换成dot文件格式(画图需要用到这个文件) * 使用```dot example1.dot –Tsvg –o example1.svg```命令生成图表 当然Graphviz非常强大这里只使用了很小的一点功能。 # 演示 如下文本是开发本网站的模块依赖,将其粘贴到[ningto.com/graph](https://ningto.com/graph)提交到后台就会返回生成的图表链接, 如:![https://ningto.com/upload/graph/c25e61f17241380e226493670092182c.svg](https://ningto.com/upload/graph/c25e61f17241380e226493670092182c.svg) ```cassandraql ningtogo github.com/PuerkitoBio/goquery@v1.5.0 ningtogo github.com/gin-contrib/sessions@v0.0.0-20190512062852-3cb4c4f2d615 ningtogo github.com/gin-gonic/gin@v1.4.0 ningtogo github.com/globalsign/mgo@v0.0.0-20181015135952-eeefdecb41b8 ningtogo github.com/kardianos/osext@v0.0.0-20190222173326-2bc1f35cddc0 ningtogo github.com/kr/pretty@v0.1.0 ningtogo github.com/microcosm-cc/bluemonday@v1.0.2 ningtogo github.com/mojocn/base64Captcha@v0.0.0-20190716153509-e5e80f1b3816 ningtogo github.com/robfig/cron@v1.2.0 ningtogo github.com/sergi/go-diff@v1.0.0 ningtogo github.com/sevlyar/go-daemon@v0.1.5 ningtogo github.com/shurcooL/github_flavored_markdown@v0.0.0-20181002035957-2122de532470 ningtogo github.com/shurcooL/go@v0.0.0-20190704215121-7189cc372560 ningtogo github.com/shurcooL/go-goon@v0.0.0-20170922171312-37c2f522c041 ningtogo github.com/shurcooL/highlight_diff@v0.0.0-20181222201841-111da2e7d480 ningtogo github.com/shurcooL/highlight_go@v0.0.0-20181215221002-9d8641ddf2e1 ningtogo github.com/shurcooL/octicon@v0.0.0-20181222203144-9ff1a4cf27f4 ningtogo github.com/shurcooL/sanitized_anchor_name@v1.0.0 ningtogo github.com/sourcegraph/annotate@v0.0.0-20160123013949-f4cad6c6324d ningtogo github.com/sourcegraph/syntaxhighlight@v0.0.0-20170531221838-bd320f5d308e ningtogo github.com/streadway/amqp@v0.0.0-20190827072141-edfb9018d271 github.com/microcosm-cc/bluemonday@v1.0.2 golang.org/x/net@v0.0.0-20181220203305-927f97764cc3 github.com/kr/pretty@v0.1.0 github.com/kr/text@v0.1.0 github.com/shurcooL/github_flavored_markdown@v0.0.0-20181002035957-2122de532470 github.com/russross/blackfriday@v1.5.2 github.com/gin-gonic/gin@v1.4.0 github.com/gin-contrib/sse@v0.0.0-20190301062529-5545eab6dad3 github.com/gin-gonic/gin@v1.4.0 github.com/golang/protobuf@v1.3.1 github.com/gin-gonic/gin@v1.4.0 github.com/json-iterator/go@v1.1.6 github.com/gin-gonic/gin@v1.4.0 github.com/mattn/go-isatty@v0.0.7 github.com/gin-gonic/gin@v1.4.0 github.com/modern-go/concurrent@v0.0.0-20180306012644-bacd9c7ef1dd github.com/gin-gonic/gin@v1.4.0 github.com/modern-go/reflect2@v1.0.1 github.com/gin-gonic/gin@v1.4.0 github.com/stretchr/testify@v1.3.0 github.com/gin-gonic/gin@v1.4.0 github.com/ugorji/go@v1.1.4 github.com/gin-gonic/gin@v1.4.0 golang.org/x/net@v0.0.0-20190503192946-f4e77d36d62c github.com/gin-gonic/gin@v1.4.0 gopkg.in/go-playground/assert.v1@v1.2.1 github.com/gin-gonic/gin@v1.4.0 gopkg.in/go-playground/validator.v8@v8.18.2 github.com/gin-gonic/gin@v1.4.0 gopkg.in/yaml.v2@v2.2.2 golang.org/x/net@v0.0.0-20190503192946-f4e77d36d62c golang.org/x/crypto@v0.0.0-20190308221718-c2843e01d9a2 golang.org/x/net@v0.0.0-20190503192946-f4e77d36d62c golang.org/x/text@v0.3.0 golang.org/x/crypto@v0.0.0-20190308221718-c2843e01d9a2 golang.org/x/sys@v0.0.0-20190215142949-d0b11bdaac8a github.com/mattn/go-isatty@v0.0.7 golang.org/x/sys@v0.0.0-20190222072716-a9d3bda3a223 github.com/mojocn/base64Captcha@v0.0.0-20190716153509-e5e80f1b3816 github.com/golang/freetype@v0.0.0-20170609003504-e2365dfdc4a0 github.com/mojocn/base64Captcha@v0.0.0-20190716153509-e5e80f1b3816 golang.org/x/image@v0.0.0-20190501045829-6d32002ffd75 github.com/gin-contrib/sessions@v0.0.0-20190512062852-3cb4c4f2d615 github.com/boj/redistore@v0.0.0-20180917114910-cd5dcc76aeff github.com/gin-contrib/sessions@v0.0.0-20190512062852-3cb4c4f2d615 github.com/bradfitz/gomemcache@v0.0.0-20190329173943-551aad21a668 github.com/gin-contrib/sessions@v0.0.0-20190512062852-3cb4c4f2d615 github.com/bradleypeabody/gorilla-sessions-memcache@v0.0.0-20181103040241- 659414f458e1 github.com/gin-contrib/sessions@v0.0.0-20190512062852-3cb4c4f2d615 github.com/gin-gonic/gin@v1.4.0 github.com/gin-contrib/sessions@v0.0.0-20190512062852-3cb4c4f2d615 github.com/globalsign/mgo@v0.0.0-20181015135952-eeefdecb41b8 github.com/gin-contrib/sessions@v0.0.0-20190512062852-3cb4c4f2d615 github.com/gomodule/redigo@v2.0.0+incompatible github.com/gin-contrib/sessions@v0.0.0-20190512062852-3cb4c4f2d615 github.com/gorilla/context@v1.1.1 github.com/gin-contrib/sessions@v0.0.0-20190512062852-3cb4c4f2d615 github.com/gorilla/sessions@v1.1.3 github.com/gin-contrib/sessions@v0.0.0-20190512062852-3cb4c4f2d615 github.com/kidstuff/mongostore@v0.0.0-20181113001930-e650cd85ee4b github.com/gin-contrib/sessions@v0.0.0-20190512062852-3cb4c4f2d615 github.com/memcachier/mc@v2.0.1+incompatible github.com/gin-contrib/sessions@v0.0.0-20190512062852-3cb4c4f2d615 github.com/quasoft/memstore@v0.0.0-20180925164028-84a050167438 github.com/PuerkitoBio/goquery@v1.5.0 github.com/andybalholm/cascadia@v1.0.0 github.com/PuerkitoBio/goquery@v1.5.0 golang.org/x/net@v0.0.0-20181114220301-adae6a3d119a gopkg.in/yaml.v2@v2.2.2 gopkg.in/check.v1@v0.0.0-20161208181325-20d25e280405 golang.org/x/image@v0.0.0-20190501045829-6d32002ffd75 golang.org/x/text@v0.3.0 github.com/gorilla/sessions@v1.1.3 github.com/gorilla/context@v1.1.1 github.com/gorilla/sessions@v1.1.3 github.com/gorilla/securecookie@v1.1.1 github.com/boj/redistore@v0.0.0-20180917114910-cd5dcc76aeff github.com/gomodule/redigo@v2.0.0+incompatible github.com/boj/redistore@v0.0.0-20180917114910-cd5dcc76aeff github.com/gorilla/securecookie@v1.1.1 github.com/boj/redistore@v0.0.0-20180917114910-cd5dcc76aeff github.com/gorilla/sessions@v1.1.1 github.com/gorilla/sessions@v1.1.1 github.com/gorilla/context@v1.1.1 github.com/gorilla/sessions@v1.1.1 github.com/gorilla/securecookie@v1.1.1 github.com/andybalholm/cascadia@v1.0.0 golang.org/x/net@v0.0.0-20180218175443-cbe0f9307d01 github.com/kr/text@v0.1.0 github.com/kr/pty@v1.1.1 github.com/stretchr/testify@v1.3.0 github.com/davecgh/go-spew@v1.1.0 github.com/stretchr/testify@v1.3.0 github.com/pmezard/go-difflib@v1.0.0 github.com/stretchr/testify@v1.3.0 github.com/stretchr/objx@v0.1.0 ``` # 实现源码 ``` package model import ( "bytes" "crypto/md5" "errors" "fmt" "html/template" "io/ioutil" "ningtogo/configs" "ningtogo/tools/util" "os" "os/exec" "path" "strings" ) type ModGraph struct { Horizontal bool RemoveVersion bool Maps map[string]int Deps map[int][]int Content string Format string } const GraphHost = "https://ningto.com/upload/graph/" var GraphDir = path.Join(configs.UploadDir(), "graph") func init() { os.MkdirAll(GraphDir, os.ModePerm) } func NewModGraph(content string, format string) *ModGraph { m := new(ModGraph) m.Horizontal = true m.Maps = make(map[string]int) m.Deps = make(map[int][]int) m.Content = strings.ReplaceAll(content, "\r\n", "\n") if format != "svg" && format != "png" { m.Format = "svg" } else { m.Format = format } return m } func (m *ModGraph) FindLocal() (string, error) { name := m.unique() + m.Format f := path.Join(GraphDir, name) if util.PathExist(f) { return GraphHost + name, nil } return "", errors.New("local not found") } func (m *ModGraph) Parse() error { rowList := strings.Split(m.Content, "\n") index := 0 for _, row := range rowList { colList := strings.Split(row, " ") if len(colList) < 2 { continue } a := colList[0] b := colList[1] if m.RemoveVersion { f1 := strings.LastIndex(a, "@") f2 := strings.LastIndex(b, "@") if f1 > 0 { a = a[:f1] } if f2 > 0 { b = b[:f2] } } if len(a) == 0 || len(b) == 0 { continue } i, _ := m.Maps[a] j, _ := m.Maps[b] if i == 0 { index += 1 i = index m.Maps[a] = i } if j == 0 { index += 1 j = index m.Maps[b] = j } if _, ok := m.Deps[i]; !ok { m.Deps[i] = []int{} } m.Deps[i] = append(m.Deps[i], j) } return nil } func (m *ModGraph) Render() (string, error) { digraph := `digraph { {{ if .Horizontal }}rankdir=LR;{{ end }} node [shape=box]; {{range $key, $val := .Maps -}} {{$val}} [label="{{$key}}"]; {{end -}} {{range $key, $valList := .Deps}} {{- range $valList -}} {{$key}} -> {{.}}; {{end -}} {{end -}} }` digraph = strings.ReplaceAll(digraph, "\r", "") t := template.Must(template.New("digraph").Parse(digraph)) var buf bytes.Buffer err := t.Execute(&buf, *m) if err != nil { return "", err } inputPath := path.Join(GraphDir, m.unique()+".dot") err = ioutil.WriteFile(inputPath, buf.Bytes(), os.ModePerm) if err != nil { return "", err } outputName := m.unique() + "." + m.Format outputPath := path.Join(GraphDir, outputName) _, err = exec.Command("dot", inputPath, "-T"+m.Format, "-o", outputPath).Output() if err != nil { return "", err } return GraphHost + outputName, nil } func (m *ModGraph) unique() string { return fmt.Sprintf("%x", md5.Sum([]byte(fmt.Sprintf("%v", *m)))) } ``` 文章类别 Python Mobile Android Java Shell Life Database Bug Windows IOS Tools Boost Node.js Mac Product Tips C/C++ Golang Javascript React Qt MQ MongoDB Design Web Linux LLM ChatGPT RAG AI 提交