這篇文章主要介紹“怎么實(shí)現(xiàn)Go Module依賴關(guān)系的可視化”,在日常操作中,相信很多人在怎么實(shí)現(xiàn)Go Module依賴關(guān)系的可視化問題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”怎么實(shí)現(xiàn)Go Module依賴關(guān)系的可視化”的疑惑有所幫助!接下來,請(qǐng)跟著小編一起來學(xué)習(xí)吧!
創(chuàng)新互聯(lián)堅(jiān)持“要么做到,要么別承諾”的工作理念,服務(wù)領(lǐng)域包括:成都網(wǎng)站設(shè)計(jì)、成都網(wǎng)站建設(shè)、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣等服務(wù),滿足客戶于互聯(lián)網(wǎng)時(shí)代的納雍網(wǎng)站設(shè)計(jì)、移動(dòng)媒體設(shè)計(jì)的需求,幫助企業(yè)找到有效的互聯(lián)網(wǎng)解決方案。努力成為您成熟可靠的網(wǎng)絡(luò)建設(shè)合作伙伴!
最近,我開發(fā)了一個(gè)非常簡(jiǎn)單的小工具,總的代碼量 200 行不到。今天,簡(jiǎn)單介紹下它。這是個(gè)什么工具呢?它是一個(gè)用于可視化展示 Go Module 依賴關(guān)系的工具。
為什么會(huì)想到開發(fā)這個(gè)工具?主要有兩點(diǎn)原因:
一是最近經(jīng)??吹酱蠹以谏鐓^(qū)討論 Go Module。于是,我也花了一些時(shí)間研究了下。期間,遇到了一個(gè)需求,如何清晰地識(shí)別模塊中依賴項(xiàng)之間的關(guān)系。一番了解后,發(fā)現(xiàn)了 go mod graph
。
效果如下:
$ go mod graph github.com/poloxue/testmod golang.org/x/text@v0.3.2 github.com/poloxue/testmod rsc.io/quote/v3@v3.1.0 github.com/poloxue/testmod rsc.io/sampler@v1.3.1 golang.org/x/text@v0.3.2 golang.org/x/tools@v0.0.0-20180917221912-90fa682c2a6e rsc.io/quote/v3@v3.1.0 rsc.io/sampler@v1.3.0 rsc.io/sampler@v1.3.1 golang.org/x/text@v0.0.0-20170915032832-14c0d48ead0c rsc.io/sampler@v1.3.0 golang.org/x/text@v0.0.0-20170915032832-14c0d48ead0c
每一行的格式是 模塊 依賴模塊
,基本能滿足要求,但總覺得還是不那么直觀。
二是我之前手里有一個(gè)項(xiàng)目,包管理一直用的是 dep。于是,我也了解了下它,把官方文檔仔細(xì)讀了一遍。其中的某個(gè)章節(jié)介紹了依賴項(xiàng)可視化展示的方法。
文檔中給出的包關(guān)系圖:
<center> <img src="https://blogimg.poloxue.com/0014-go-mod-graph-visible-02.png"> </center>
看到這張圖的時(shí)候,眼睛瞬間就亮了,圖形化就是優(yōu)秀,不同依賴之間的關(guān)系一目了然。這不就是我想要的效果嗎?666,點(diǎn)個(gè)贊。
但 ...,隨之而來的問題是,go mod 沒這個(gè)能力啊。怎么辦?
先看看是不是已經(jīng)有人做了這件事了。網(wǎng)上搜了下,沒找到。那是不是能自己實(shí)現(xiàn)?應(yīng)該可以借鑒下 dep 的思路吧?
如下是 dep 依賴實(shí)現(xiàn)可視化的方式:
# linux $ sudo apt-get install graphviz $ dep status -dot | dot -T png | display # macOS $ brew install graphviz $ dep status -dot | dot -T png | open -f -a /Applications/Preview.app # Windows > choco install graphviz.portable > dep status -dot | dot -T png -o status.png; start status.png
這里展示了三大系統(tǒng)下的使用方式,它們都安裝了一個(gè)軟件包,graphviz。從名字上看,這應(yīng)該是一個(gè)用來實(shí)現(xiàn)可視化的軟件,即用來畫圖的。事實(shí)也是這樣,可以看看它的官網(wǎng)。
再看下它的使用,發(fā)現(xiàn)都是通過管道命令組合的方式,而且前面的部分基本相同,都是 dep status -dot | dot -T png
。后面的部分在不同的系統(tǒng)就不同了,Linux 是 display
,MacOS 是 open -f -a /Applications/Preview.app
,Window 是 start status.png
。
稍微分析下就會(huì)明白,前面是生成圖片,后面是顯示圖片。因?yàn)椴煌到y(tǒng)的圖片展示命令不同,所以后面的部分也就不同了。
現(xiàn)在關(guān)心的重點(diǎn)在前面,即 dep status -dot | dot -T png
干了啥,它究竟是如何實(shí)現(xiàn)繪圖的?大致猜測(cè),dot -T png 是由 dep status -dot 提供的數(shù)據(jù)生成圖片。繼續(xù)看下 dep status -dot
的執(zhí)行效果吧。
$ dep status -dot digraph { node [shape=box]; 2609291568 [label="github.com/poloxue/hellodep"]; 953278068 [label="rsc.io/quote\nv3.1.0"]; 3852693168 [label="rsc.io/sampler\nv1.0.0"]; 2609291568 -> 953278068; 953278068 -> 3852693168; }
咋一看,輸出的是一段看起來不知道是啥的代碼,這應(yīng)該是 graphviz 用于繪制圖表的語言。那是不是還有學(xué)習(xí)下?當(dāng)然不用啊,這里用的很簡(jiǎn)單,直接套用就行了。
試著分析一下吧,前面兩行可以不用關(guān)心,這應(yīng)該是 graphviz 特定的寫法,表示要畫的是什么圖。我們主要關(guān)心如何將數(shù)據(jù)以正確形式提供出來。
2609291568 [label="github.com/poloxue/hellodep"]; 953278068 [label="rsc.io/quote\nv3.1.0"]; 3852693168 [label="rsc.io/sampler\nv1.0.0"]; 2609291568 -> 953278068; 953278068 -> 3852693168;
一看就知道,這里有兩種結(jié)構(gòu),分別是為依賴項(xiàng)關(guān)聯(lián) ID ,和通過 ID 和 ->
表示依賴間的關(guān)系。
按上面的猜想,我們可以試著畫出一個(gè)簡(jiǎn)單的圖, 用于表示 a 模塊依賴 b 模塊。執(zhí)行命令如下,將繪圖代碼通過 each
管道的方式發(fā)送給 dot
命令。
$ echo 'digraph { node [shape=box]; 1 [label="a"]; 2 [label="b"]; 1 -> 2; }' | dot -T png | open -f -a /Applications/Preview.app
效果如下:
<center> <img src="https://blogimg.poloxue.com/0014-go-mod-graph-visible-03.png"/> </center>
繪制一個(gè)依賴關(guān)系圖竟然這么簡(jiǎn)單。
看到這里,是不是發(fā)現(xiàn)問題已經(jīng)變得非常簡(jiǎn)單了。我們只要將 go mod graph
的輸出轉(zhuǎn)化為類似的結(jié)構(gòu)就能實(shí)現(xiàn)可視化了。
接下來,開發(fā)這個(gè)小程序吧,我將這個(gè)小程序命名為 modv
,即 module visible 的意思。項(xiàng)目源碼位于 poloxue/modv。
先要檢查數(shù)據(jù)輸入管道是否正常。
我們的目標(biāo)是使用類似 dep
中作圖的方式,go mod graph
通過管道將數(shù)據(jù)傳遞給 modv
。因此,要先檢查 os.Stdin
,即檢查標(biāo)準(zhǔn)輸入狀態(tài)是否正常, 以及是否是管道傳輸。
下面是 main 函數(shù)的代碼,位于 main.go 中。
func main() { info, err := os.Stdin.Stat() if err != nil { fmt.Println("os.Stdin.Stat:", err) PrintUsage() os.Exit(1) } // 是否是管道傳輸 if info.Mode()&os.ModeNamedPipe == 0 { fmt.Println("command err: command is intended to work with pipes.") PrintUsage() os.Exit(1) }
一旦確認(rèn)輸入設(shè)備一切正常,我們就可以進(jìn)入到數(shù)據(jù)讀取、解析與渲染的流程了。
mg := NewModuleGraph(os.Stdin) mg.Parse() mg.Render(os.Stdout) }
接下來,開始具體看看如何實(shí)現(xiàn)數(shù)據(jù)的處理流程。
先定義一個(gè)結(jié)構(gòu)體,并大致定義整個(gè)流程。
type ModGraph struct { Reader io.Reader // 讀取數(shù)據(jù)流 } func NewModGraph(r io.Reader) *ModGraph { return &ModGraph{Reader: r} } // 執(zhí)行數(shù)據(jù)的處理轉(zhuǎn)化 func (m *ModGraph) Parse() error {} // 結(jié)果渲染與輸出 func (m *ModGraph) Render(w io.Writer) error {}
再看下 go mod graph
的輸出吧,如下:
github.com/poloxue/testmod golang.org/x/text@v0.3.2 github.com/poloxue/testmod rsc.io/quote/v3@v3.1.0 ...
每一行的結(jié)構(gòu)是 模塊 依賴項(xiàng)
。現(xiàn)在的目標(biāo)是要它解析成下面這樣的結(jié)構(gòu):
digraph { node [shape=box]; 1 github.com/poloxue/testmod; 2 golang.org/x/text@v0.3.2; 3 rsc.io/quote/v3@v3.1.0; 1 -> 2; 1 -> 3; }
前面說過,這里包含了兩種不同的結(jié)構(gòu),分別是模塊與 ID 關(guān)聯(lián)關(guān)系,以及模塊 ID 表示模塊間的依賴關(guān)聯(lián)。為 ModGraph
結(jié)構(gòu)體增加兩個(gè)成員表示它們。
type ModGraph struct { r io.Reader // 數(shù)據(jù)流讀取實(shí)例,這里即 os.Stdin // 每一項(xiàng)名稱與 ID 的映射 Mods map[string]int // ID 和依賴 ID 關(guān)系映射,一個(gè) ID 可能依賴多個(gè)項(xiàng) Dependencies map[int][]int }
要注意的是,增加了兩個(gè) map 成員后,記住要在 NewModGraph
中初始化下它們。
如何進(jìn)行解析?
介紹到這里,目標(biāo)已經(jīng)很明白了。就是要將輸入數(shù)據(jù)解析到 Mods
和 Dependencies
兩個(gè)成員中,實(shí)現(xiàn)代碼都在 Parse
方法中。
為了方便進(jìn)行數(shù)據(jù)讀取,首先,我們利用 bufio
基于 reader
創(chuàng)建一個(gè)新的 bufReader
,
func (m *ModGraph) Parse() error { bufReader := bufio.NewReader(m.Reader) ...
為便于按行解析數(shù)據(jù),我們通過 bufReader 的 ReadBytes()
方法循環(huán)一行一行地讀取 os.Stdin 中的數(shù)據(jù)。然后,對(duì)每一行數(shù)據(jù)按空格切分,獲取到依賴關(guān)系的兩項(xiàng)。代碼如下:
for { relationBytes, err := bufReader.ReadBytes('\n') if err != nil { if err == io.EOF { return nil } return err } relation := bytes.Split(relationBytes, []byte(" ")) // module and dependency mod, depMod := strings.TrimSpace(string(relation[0])), strings.TrimSpace(string(relation[1])) ... }
接下來,就是將解析出來的依賴關(guān)系組織到 Mods
和 Dependencies
兩個(gè)成員中。模塊 ID 是生成規(guī)則采用的是最簡(jiǎn)單的實(shí)現(xiàn)方式,從 1 自增。實(shí)現(xiàn)代碼如下:
modId, ok := m.Mods[mod] if !ok { modId = serialID m.Mods[mod] = modId serialID += 1 } depModId, ok := m.Mods[depMod] if !ok { depModId = serialID m.Mods[depMod] = depModId serialID += 1 } if _, ok := m.Dependencies[modId]; ok { m.Dependencies[modId] = append(m.Dependencies[modId], depModId) } else { m.Dependencies[modId] = []int{depModId} }
解析的工作到這里就結(jié)束了。
這個(gè)小工具還剩下最后一步工作要做,即將解析出來的數(shù)據(jù)渲染出來,以滿足 graphviz
工具的作圖要求。實(shí)現(xiàn)代碼是 Render
部分:
首先,定義一個(gè)模板,以生成滿足要求的輸出格式。
var graphTemplate = `digraph { node [shape=box]; {{ range $mod, $modId := .mods -}} {{ $modId }} [label="{{ $mod }}"]; {{ end -}} {{- range $modId, $depModIds := .dependencies -}} {{- range $_, $depModId := $depModIds -}} {{ $modId }} -> {{ $depModId }}; {{ end -}} {{- end -}} } `
這一塊沒啥好介紹的,主要是要熟悉 Go 中的 text/template
模板的語法規(guī)范。為了展示友好,這里通過 -
實(shí)現(xiàn)換行的去除,整體而言不影響閱讀。
接下來,看 Render
方法的實(shí)現(xiàn),把前面解析出來的 Mods
和 Dependencies
放入模板進(jìn)行渲染。
func (m *ModuleGraph) Render(w io.Writer) error { templ, err := template.New("graph").Parse(graphTemplate) if err != nil { return fmt.Errorf("templ.Parse: %v", err) } if err := templ.Execute(w, map[string]interface{}{ "mods": m.Mods, "dependencies": m.Dependencies, }); err != nil { return fmt.Errorf("templ.Execute: %v", err) } return nil }
現(xiàn)在,全部工作都完成了。最后,將這個(gè)流程整合到 main 函數(shù)。接下來就是使用了。
開始體驗(yàn)下吧。補(bǔ)充一句,這個(gè)工具,我現(xiàn)在只測(cè)試了 Mac 下的使用,如有問題,歡迎提出來。
首先,要先安裝一下 graphviz
,安裝的方式在本文開頭已經(jīng)介紹了,選擇你的系統(tǒng)安裝方式。
接著是安裝 modv
,命令如下:
$ go get github.com/poloxue/modv
安裝完成!簡(jiǎn)單測(cè)試下它的使用。
以 MacOS 為例。先下載測(cè)試庫,github.com/poloxue/testmod。 進(jìn)入 testmod 目錄執(zhí)行命令:
$ go mod graph | modv | dot -T png | open -f -a /Applications/Preview.app
如果執(zhí)行成功,將看到如下的效果:
完美地展示了各個(gè)模塊之間的依賴關(guān)系。
本文是篇實(shí)踐性的文章,從一個(gè)簡(jiǎn)單想法到成功呈現(xiàn)出一個(gè)可以使用的工具。雖然,開發(fā)起來并不難,從開發(fā)到完成,僅僅花了一兩個(gè)小時(shí)。但我的感覺,這確實(shí)是個(gè)有實(shí)際價(jià)值的工具。
還有一些想法沒有實(shí)現(xiàn)和驗(yàn)證,比如一旦項(xiàng)目較大,是否可以方便的展示某個(gè)指定節(jié)點(diǎn)的依賴樹,而非整個(gè)項(xiàng)目。還有,在其他項(xiàng)目向 Go Module 遷移的時(shí)候,這個(gè)小工具是否能產(chǎn)生一些價(jià)值。
到此,關(guān)于“怎么實(shí)現(xiàn)Go Module依賴關(guān)系的可視化”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)砀鄬?shí)用的文章!
分享文章:怎么實(shí)現(xiàn)GoModule依賴關(guān)系的可視化
當(dāng)前URL:http://chinadenli.net/article4/iegpie.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站制作、面包屑導(dǎo)航、網(wǎng)站內(nèi)鏈、Google、定制網(wǎng)站、靜態(tài)網(wǎng)站
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)