删除文章

确定要删除这篇文章吗?

取消
确定

go mod graph 图表

     阅读(145)  2019-11-14 11:43:11

我们知道通过go mod graph命令可以输出当前工程的模块依赖图,但是这个输出是纯文本的并且只是简单的一对一的关系。 很难看出所有模块之间的依赖关系,如果能用图的形式来显示就清晰多了。

Graphviz工具可以画出我们需要的图表。

实现步骤

  • 使用go mod graph命令得到各模块之间的依赖关系
  • 将上述文本转换成dot文件格式(画图需要用到这个文件)
  • 使用dot example1.dot –Tsvg –o example1.svg命令生成图表

当然Graphviz非常强大这里只使用了很小的一点功能。

演示

如下文本是开发本网站的模块依赖,将其粘贴到ningto.com/graph提交到后台就会返回生成的图表链接, 如:

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))))
}

文章评论

Keep it simple,stupid
文章数
306
总访问量
608519
今日访问
61
最近评论

liangzi : 不错 谢谢分享
tujiaw : registerThreadInactive:如果当前没有激活的线程,就去激活线程,让等待的线程去执行任务。
hgzzx : 佩服佩服。 请教:registerThreadInactive的作用是什么?
xuehaoyun : 很不错,来围观一下
tujiaw : 抱歉csdn code服务关闭了,这个代码我也找不到了
于淞 : 你好,这个文章的源码能分享一下吗,songsong9181@163.com,谢谢了 上面的写错了
于淞 : 你好,这个文章的源码能分享一下吗,838106303@163.com,谢谢了 上面的链接不能用了
tujiaw : 多谢多谢
essaypinglun college-paper.org : 很好的博客,赞赞
Folly : 这个实现有点奇怪,Qt为什么没有统一的比对方法。
回到顶部