聊聊程式碼倉庫視覺化:gource 篇

soulteary發表於2022-05-26

本篇文章將聊聊如何高效的將程式碼倉庫中的提交記錄和目錄結構,快速轉變為“酷炫的視訊”。分享如何使用 Docker 在不同 CPU 架構的裝置上執行 gource,以及如何基於最新的 M1 Pro 晶片的裝置,讓製作視覺化視訊的效率成倍提升。

寫在前面

前一陣為了慶祝社群專案 Milvus 在 GitHub 獲得了一萬顆星星,我製作了一個視訊,用來動態的展示在過去的時間裡,這個專案的具體提交狀況和專案組織架構變化狀況。

使用 Gource 對程式碼倉庫進行視覺化

最近有同事聊開源專案的“維護血淚史”時,又提到了這件事。勾起了我當時製作視訊的痛苦回憶:當時的視訊製作方案是使用 docker 執行 gource。在針對諸如 Milvus 倉庫這種提交量比較大的倉庫時(1.4萬提交),想要生成視覺化視訊,使用我手頭的 i9 處理器的裝置,至少需要跑個把小時。而當我將相同的操作換到 M1 裝置(M1 Pro)執行後,或許是因為 docker 中的應用並未針對 ARM 晶片做優化、又或許是 docker 中的程式版本不夠新,相同的工作量,甚至需要跑半天才能搞定!
不論如何,這個結果未免太不科學了。

上萬次提交的開源專案背後的程式碼變動

且不說 M1 的執行結果“出乎意料”,但就是個把小時的視訊生成時間,也讓我感覺挺不舒服的。作為一個追求效率的老程式設計師,我花了一些時間,終於摸索出了這個問題的“正確答案”:如果使用針對 M1 晶片而編譯的程式,整個視訊的生成時間可以縮短到半個小時左右,相比較之前提升效果頗為明顯。

在展開聊聊我是如何做的之前,我想先介紹一下 gource 這款開源軟體。

關於 Gource

2009 年,來自紐西蘭的工程師 Andrew Caudwell,希望能夠將各種程式碼版本管理軟體的資訊視覺化,於是他使用 C++ 編寫了 Gource 這個程式。2011 年,專案從 Google Code 遷移至 GitHub 後,專案開啟了年更模式。

比較幸運的是,截止本文成文寫出的時候,軟體已經發布了今年的兩個重要更新:包含視網膜螢幕的支援,以及針對字型縮放功能進行了大量修正,並將軟體使用的正則庫升級為了 PCRE2,程式版本更新到了 0.53 。

因為專案在 GitHub 釋出頁面中只提供了 Windows 版本的程式,所以如果我們想獲取 Linux / macOS 的新版本程式,就只能自己進行編譯啦。(Ubuntu APT 倉庫中的版本還停留在 2019 年釋出的 0.51)

接下來,我們先來聊聊如何進行編譯,如果你希望使用 Docker 或 x86 裝置,可以閱讀本文後面的章節。

在 M1 裝置上進行 Gource 的編譯

為了能夠在 macOS 上完成新版本的程式編譯,我們需要先完成 gource 的依賴安裝:

brew install pkg-config freetype2 pcre2 glow sdl2 sdl2_image boost glm autoconf

如果你沒有完成上述依賴的安裝,那麼在執行 ./configure 的時候,一定會遇到諸如下面的問題:

checking for FT2... configure: error: in `/Users/soulteary/lab/gource-0.53':
configure: error: The pkg-config script could not be found or is too old.  Make sure it
is in your PATH or set the PKG_CONFIG environment variable to the full
path to pkg-config.

...

No package 'libpcre2-8' found

...

當我們安裝完畢依賴之後,還需要配置一下編譯引數,讓程式在編譯的時候能夠找到我們剛剛安裝的依賴。不然,就會出現類似下面的錯誤:

checking for boostlib >= 1.46 (104600)... configure: We could not detect the boost libraries (version 1.46 or higher). If you have a staged boost library (still not installed) please specify $BOOST_ROOT in your environment and do not give a PATH to --with-boost option.  If you are sure you have boost installed, then check your version number looking in <boost/version.hpp>. See http://randspringer.de/boost for more documentation.
configure: error: Boost Filesystem >= 1.46 is required. Please see INSTALL

...

configure: error: GLM headers are required. Please see INSTALL

...

對於 boost 框架,我們可以通過簡單使用 --with-boost 引數來指定依賴的目錄,而對於 glm(OpenGL Mathematics),因為它是一個僅包含標頭檔案的數學庫,所以我們必須使用 CPPFLAGS 等引數,將路徑傳遞給 configure

但是我們要如何在 macOS 中獲得由 brew 安裝的 glm 或 boost 路徑呢?這裡可以將下面兩種方法進行組合使用。

第一種查詢路徑的方法是使用 brew list 命令,獲取我們安裝的某個軟體的詳細目錄列表,在輸出日誌中尋找或嘗試出正確的目錄。以 boost 舉例,當我們執行完畢 brew list boost 之後,能夠看到類似下面的輸出結果:

/opt/homebrew/Cellar/boost/1.78.0_1/include/boost/ (15026 files)
/opt/homebrew/Cellar/boost/1.78.0_1/lib/libboost_atomic-mt.dylib
/opt/homebrew/Cellar/boost/1.78.0_1/lib/libboost_chrono-mt.dylib
...

其中 /opt/homebrew/Cellar/boost/1.78.0_1/ 就是 boost 的根目錄,將這個路徑拼合為 --with-boost=/opt/homebrew/Cellar/boost/1.78.0_1/ 引數,然後就能夠在編譯中使用 boost 啦。

第二種路徑的查詢方法,是使用 pkg-config 工具,輸出 C++ 專案編譯可以使用的具體目錄引數。特別適合 glm 這類專案。我們通過為 pkg-config 新增引數,可以得到命令 pkg-config glm --libs --cflags,當命令執行完畢,就能夠得到編譯時可以直接使用的目錄地址了:

-I/opt/homebrew/Cellar/glm/0.9.9.8/include

將上面的引數進行整合,不難得到在 M1 裝置上的進行編譯配置的完整命令:

./configure --with-boost=/opt/homebrew/Cellar/boost/1.78.0_1/ CPPFLAGS="-I/opt/homebrew/Cellar/glm/0.9.9.8/include"

當命令執行完畢,不出意外,我們將看到類似下面的日誌輸出:

checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for a thread-safe mkdir -p... build-aux/install-sh -c -d
checking for gawk... no
checking for mawk... no
checking for nawk... no
checking for awk... awk
checking whether make sets $(MAKE)... yes
checking whether make supports nested variables... yes
checking build system type... arm-apple-darwin21.4.0
checking host system type... arm-apple-darwin21.4.0
checking for g++... g++
checking whether the C++ compiler works... yes
checking for C++ compiler default output file name... a.out
checking for suffix of executables... 
checking whether we are cross compiling... no
checking for suffix of object files... o
checking whether we are using the GNU C++ compiler... yes
checking whether g++ accepts -g... yes
checking for style of include used by make... GNU
checking dependency style of g++... gcc3
checking for timegm... yes
checking for unsetenv... yes
checking how to run the C++ preprocessor... g++ -E
checking for X... disabled
checking for a sed that does not truncate output... /usr/bin/sed
checking for gcc... gcc
checking whether we are using the GNU C compiler... yes
checking whether gcc accepts -g... yes
checking for gcc option to accept ISO C89... none needed
checking whether gcc understands -c and -o together... yes
checking dependency style of gcc... gcc3
checking for the pthreads library -lpthreads... no
checking whether pthreads work without any flags... yes
checking for joinable pthread attribute... PTHREAD_CREATE_JOINABLE
checking if more special flags are required for pthreads... -D_THREAD_SAFE
checking how to run the C preprocessor... gcc -E
checking for grep that handles long lines and -e... /usr/bin/grep
checking for egrep... /usr/bin/grep -E
checking for ANSI C header files... yes
checking for sys/types.h... yes
checking for sys/stat.h... yes
checking for stdlib.h... yes
checking for string.h... yes
checking for memory.h... yes
checking for strings.h... yes
checking for inttypes.h... yes
checking for stdint.h... yes
checking for unistd.h... yes
checking whether we are using the Microsoft C compiler... no
checking windows.h usability... no
checking windows.h presence... no
checking for windows.h... no
checking for GL/gl.h... no
checking for OpenGL/gl.h... yes
checking for OpenGL library... -framework OpenGL
checking for GL/glu.h... no
checking for OpenGL/glu.h... yes
checking for OpenGL Utility library... yes
checking for varargs GLU tesselator callback function type... no
checking for pkg-config... /opt/homebrew/bin/pkg-config
checking pkg-config is at least version 0.9.0... yes
checking for FT2... yes
checking for PCRE2... yes
checking for GLEW... yes
checking for SDL2... yes
checking for PNG... yes
checking for IMG_LoadPNG_RW... yes
checking for IMG_LoadJPG_RW... yes
checking for boostlib >= 1.46 (104600) includes in "/opt/homebrew/Cellar/boost/1.78.0_1//include"... yes
checking for boostlib >= 1.46 (104600) lib path in "/opt/homebrew/Cellar/boost/1.78.0_1//lib/arm-darwin21.4.0"... no
checking for boostlib >= 1.46 (104600) lib path in "/opt/homebrew/Cellar/boost/1.78.0_1//lib"... yes
checking for boostlib >= 1.46 (104600)... yes
checking whether the Boost::System library is available... yes
checking for exit in -lboost_system... yes
checking whether the Boost::Filesystem library is available... yes
checking for exit in -lboost_filesystem... yes
checking glm/glm.hpp usability... yes
checking glm/glm.hpp presence... yes
checking for glm/glm.hpp... yes
checking that generated files are newer than configure... done
configure: creating ./config.status
config.status: creating Makefile
config.status: executing depfiles commands

接下來,執行 make ,系統就會開始對 gource 進行編譯,在編譯完成之後,接著執行 sudo make install,gource 的編譯安全就結束啦。

在 M1 裝置上使用 Gource 進行程式碼倉庫視覺化

在使用 gource 製作視訊前,我們需要評估專案所需的硬碟空間,生成視訊的尺寸和倉庫的提交量(commits)、總的檔案目錄數量、專案維護時間長,都有很大關係,這裡以前文中提到的 Milvus 倉庫為例。

這個倉庫從 2019 年開始維護,截止當前有 1.4 萬次提交,如果想我們生成 1280x720 尺寸的視訊內容,假設將專案每天的提交資料展示的時間設定為 1 秒,過程中將輸出 370 多 GB 的 臨時檔案(PPM 截圖檔案),所以在開始進行倉庫視覺化之前,請確認你的硬碟留有足夠的空間

下載要進行視覺化的程式碼倉庫

視覺化的第一步,是將我們要視覺化的倉庫下載到本地,比如:

git clone https://github.com/milvus-io/milvus.git

使用 Gource 進行視覺化渲染

接下來,我們需要使用 gource 指定我們未來希望得到的視訊的最大解析度,以及一些關鍵細節:

  • 我們希望這個視訊中,每一天的展示時間為多久(本例為1秒)
  • 我們希望這個視訊中,視訊的最大幀率是多少(本例為30幀)
  • 我們希望輸出的檔名和剛剛使用 git clone 下載好的倉庫的目錄名是什麼
gource --viewport 1280x720 \
    --high-dpi \
    --seconds-per-day 1 \
    --output-ppm-stream milvus.ppm \
    --output-framerate 30 \
    milvus

執行上面的命令,程式將會開啟一個預覽介面,開始將倉庫的每一次提交記錄和當時的目錄結構進行視覺化繪製。

使用 Gource 進行逐幀繪製

經過相對漫長的等待之後(19分鐘左右),當命令執行完畢,我們就得到了包含所有程式碼倉庫提交資訊、目錄變化資訊的臨時檔案:milvus.ppm

使用 ffmpeg 生成最終的視訊檔案

我們在上一步得到的檔案,足足有 370 GB 之大。為了得到一個方便後續剪輯或在各種網路平臺上傳播的檔案,我們還需要使用 ffmpeg 對其進行格式轉換。

如果你沒有安裝過 ffmpeg,可以考慮使用下面的命令完成安裝(本文使用版本為 5.0.1)。

brew install ffmpeg

想要生成一個相容性比較好的 H264 格式的 milvus.mp4 檔案,可以使用下面的命令:

ffmpeg -y -r 30 -f image2pipe -loglevel info -vcodec ppm -i ./milvus.ppm -vcodec libx264 -preset medium -pix_fmt yuv420p -crf 1 -threads 0 -bf 0 ./milvus.mp4

在 M1 裝置上火力全開的 ffmpeg

耐心等待命令執行完畢(14分鐘左右),我們就能夠得到包含酷炫結果的視訊檔案啦。相比較上一步驟中的 370GB 臨時檔案,視訊檔案顯得相對小巧,只需要 12GB 左右的空間。

使用 Docker 進行程式碼倉庫的視覺化

如果你不追求更高的轉換效率,可以接受“離線任務”的執行方式,可以考慮使用開源專案 sandrokeil/docker-files/ 中的 gource 映象。

使用方法非常簡單,只需要一條命令:

docker run --rm -it -v `pwd`/repo:/repos -v `pwd`/results:/results -v `pwd`/avatars:/avatars -v `pwd`/mp3s:/mp3s sandrokeil/gource:latest

在上面的命令中,我們需要做一些簡單的準備:

  • 把我們的程式碼倉庫放置於當前目錄下的 repo 目錄中。
  • 將我們計劃進行替代的使用者頭像放在 avatars 目錄中。
  • 如果你希望程式生成視訊的過程中,順帶完成背景音的配樂,可以將 mp3 檔案放在 mp3s 目錄中。

當命令執行完畢之後,我們就能夠在本地的 results 目錄中找到我們的視覺化視訊檔案了。

其他

除了忠實還原倉庫中的每一次提交之外,Gource 還支援根據引數篩選時間啟止、篩選生成指定使用者的貢獻記錄、甚至搭配 shell 可以篩選生成指定目錄的變化記錄。

所以,當我們想進行某個大版本回顧,或者慶祝某位開源社群的使用者成為專案 maintainer 時,把這些“混雜著程式碼的時間碎片”通過視訊進行還原呈現,或許是一個不錯的主意。

最後

希望這篇內容能夠幫到有相同需求的你。

下一篇相同主題的內容,我將分享 gource 之外的專案視覺化方案,一個相對輕量的方案。

--EOF


本文使用「署名 4.0 國際 (CC BY 4.0)」許可協議,歡迎轉載、或重新修改使用,但需要註明來源。 署名 4.0 國際 (CC BY 4.0)

本文作者: 蘇洋

建立時間: 2022年05月10日
統計字數: 8873字
閱讀時間: 18分鐘閱讀
本文連結: https://soulteary.com/2022/05...

相關文章