Clang 的編譯——面向 Clang Static Analyzer 開發的編譯配置指北

隨着時代的發展,LLVM 項目已經變得越來越大了。代碼越來越多,越來越複雜,於是也變得越來越不好編譯了。從最開始做作業時第一次嘗試編譯 Clang 3.7 開始,到現在已在 LLVM 項目中有過幾個 commit,最直觀的感受就是編譯的時間以及消耗的內存越來越多。以至於在編譯時直接大手一揮地 make all && make check-all 已經成爲了一種奢侈,也就只在實驗室的 256 核 2TB 內存的服務器上纔敢這麼做,而日常開發用的服務器也已經經過過若干次添加內存而達到了 256GB 之巨的容量。於是,便想到要去折騰一下配置和編譯的流程,以便能讓自己將更多的精力投入到開發中,而不是一邊等編譯一邊刷手機。

正巧,最近在 LLVM 的項目週報中翻到了一本新書以及兩篇新文章都有涉及相關內容,於是便去研讀了一下。在借鑑了這些文字中提及的內容之後,找到了一個更適合自己現在開發工作的配置。先放出我 CMake 時的參數配置,後面再細細說。

cmake ../llvm \
    -G Ninja \
    -DLLVM_ENABLE_PROJECTS=clang \
    -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
    -DLLVM_TARGETS_TO_BUILD=X86 \
    -DLLVM_USE_SPLIT_DWARF=ON \
    -DBUILD_SHARED_LIBS=ON \
    -DLLVM_OPTIMIZED_TABLEGEN=ON \
    -DLLVM_ENABLE_Z3_SOLVER=ON \
    -DCLANG_ENABLE_ARCMT=OFF \
    -DCMAKE_C_COMPILER=clang \
    -DCMAKE_C_FLAGS=-fno-limit-debug-info \
    -DCMAKE_CXX_COMPILER=clang++ \
    -DCMAKE_CXX_FLAGS=-fno-limit-debug-info \
    -DLLVM_USE_LINKER=lld

作爲一個 Clang Static Analyzer (CSA) 相關的開發者,日常會使用到的 LLVM 組件也僅有三個:clangclang-check 以及 clang-extdef-mapping。而且,clang-extdef-mapping 更多的是作爲工具使用而非去修改它的代碼。因此,對於提升編譯效率,我首先想到的便是只編譯這三個組件。然而,自己 naive 的想法并沒有得到多大的優化。作爲開發者,編譯操作往往不是針對所有代碼的,往往只會有數個文件需要編譯。此時,鏈接 clang 便成了整個編譯過程中最耗時的步驟。此外,在 GNU Make 在編譯的過程中每次都會觸發 TableGen 的重新生成。而這一部分的耗時相比於每個需要被重新編譯的源代碼來説,也是不可小覷的。因此,本次介紹的編譯配置主要針對這些問題。

首先,在開發過程中所必須的功能參數如下。這些參數是根據我這邊開發的需求來配置的,因此並不一定適用於所有人。這裏也不做過多介紹,只是簡單列舉一下。

    • -DLLVM_ENABLE_PROJECTS=clang:啓用 clang 子項目。這個是編譯 CSA 所必須的。
    • -DCMAKE_EXPORT_COMPILE_COMMANDS=ON:導出 compilation database 來給 clangd 使用。主要用於代碼補全和定義引用查找。
    • -DENABLE_z3_SOLVER=ON:啓用 Z3 求解器。主要用於 CSA 中的路徑約束求解。

剩下的參數,便是在此之上對於編譯效率優化的參數了。介紹的順序是依照我添加到配置中的時間先後來排序的。

-DLLVM_TARGETS_TO_BUILD=X86,僅編譯 X86 後端的部分。LLVM 支持交叉編譯,但這一特性對於 CSA 開發來説并不是必須的。因此,這裏僅僅啓用了 X86 這一種 target 來避免編譯過多不需要的其他代碼。這個 target 會默認同時啓用 32 位和 64 位的後端代碼。這裏需要注意一下,因爲我這邊開發用的機器是 x86_64 架構的,因此是這樣配置的。若開發平臺為其他架構,則需要調整這裏的參數配置。

-DLLVM_USE_SPLIT_DWARF=ON,拆分 debug 信息與可執行程序。這個配置會在編譯時添加 -gsplit-dwarf 參數,將 debug 信息生成到額外的 dwo 文件中。由於鏈接時不再需要將 debug 信息拷貝到生成的二進制程序中,因而可以減小鏈接時的内存開銷並同時減小編譯完成之後的磁盤空間占用。

-DLLVM_OPTIMIZED_TABLEGEN=ON,對 llvm-tblgenclang-tblgen 程序使用編譯優化。由於 TableGen 後端并不是開發 CSA 時所需要 debug 的部分,通過這個參數可以在 build 目錄下的 NATIVE 目錄中配置一個 release 編譯的環境,並在此環境中編譯這兩個程序。這樣,可以更迅速地完成 TableGen 的部分。但缺點是會在編譯過程中再一次出現 CMake 配置過程,并且編譯這兩個程序的進度條是另算的,因此不建議有强迫症的同學使用。

-DCLANG_ENABLE_ARCMT=OFF,禁用 ARCMT。這一部分主要與 Objective-C 相關,且 CSA 中并不涉及相關内容,因此將其禁用。這部分我的瞭解並不多,但禁用之後沒有發現過多問題,因而這個選項就保留了下來。

-G Ninja,默認使用 Ninja 替代 GNU Make 來控制構建過程。Ninja 可以預先規劃所有的構建指令,並僅執行編譯過程中需要被執行的步驟,從而可以避免在使用 GNU Make 時遇到的即使該步驟無事可做卻亦會被檢視一遍而造成效率低下的問題。這一點的優化主要體現在增量編譯的過程中。當修改了一個源代碼文件之後,僅有相關的編譯和鏈接任務會被 Ninja 激活並重新被執行。但若是使用 GNU Make 的話,則依舊會重新遍歷所有子文件夾中的全部任務,也因此造成了效率的低下。這裏還要說一下這兩個工具最大的不同:控制臺輸出的不同。Ninja 的編譯進度為任務個數,新的一條 log 會覆蓋掉之前一條,不會造成刷屏。而 GNU Make 的進度為百分比,新的一條 log 會換行輸出,因此一次增量編譯時可能會無法找到被執行了的編譯指令,需要向上滾屏纔能找到。

-DLLVM_USE_LINKER=lld,使用 lld 或其他非 BFD 鏈接器來鏈接二進制程序。這裏所説的 BFD 鏈接器我并沒有具體去查它的概念,因此也無法做過多原理方面的解釋。但這一條是若干 blog 以及書上都予以推薦的,而個人測試效果確實也可以實現加速,因此就採用了這一配置。書上提到的鏈接器包括 GNU 的 Gold 以及 LLVM 的 LLD,個人使用體驗感覺 LLD 會更快一些,因此也就採用了這個配置。

-DCMAKE_C_COMPILER=clang-DCMAKE_CXX_COMPILER=clang++,使用 Clang 來替代 GCC 編譯代碼。LLVM 的 buildbot 是使用 CCCXX 兩個環境變量來設置編譯器的,但我這裏給出的配置是使用 CMake 的編譯器配置變量來做這件事。個人認爲,採用這種方式為 CMake 設置編譯器導致出問題的概率會更低。此外,在使用 Clang 編譯代碼時,會導致 libstdc++ 的 pretty print 信息無法被 GDB 和 LLDB 正確加載,從而導致無法打印 STL 容器的問題。添加 -fno-limit-debug-info 參數可以有效解決這一問題。若還不能解決,則可以使用 STL 的 Debug Mode 來實現打印 STL 容器的功能(編譯時定義 _GLIBCXX_DEBUG 宏即可啓用),但這也會導致一定程度上的效率低下的問題。若介意相關問題,則可以依舊採用 GCC 來編譯代碼。

最後,-DBUILD_SHARED_LIBS=ON,將每個組件的鏈接庫生成為動態鏈接庫。這個參數也是我猶豫再三之後最後啓用的一個選項。請注意這個參數與 a 參數的不同:我這裏啓用的這個參數是每一個組件生成一個獨立的動態鏈接庫,而 s 選項則會為 LLVM 和 Clang 各生成一個動態鏈接庫(共計兩個文件)。啓用這個選項會帶來一利一弊。這個選項的優勢在於可以讓 clang 以及 clang-check 這些程序在連接時不用像靜態鏈接那樣寫入大量的磁盤文件,從而使得編譯過程變得輕鬆迅速。但劣勢在於 debug 時,每次重新運行程序都會需要大量的時間來加載各個動態鏈接庫以及其中的 debug 信息。因此,更適宜採用日志 debug 的方法,以及選擇性地在 debug 時手動加載所需的動態鏈接庫的 debug 信息。後者需要對於每一個函數所在的庫有比較深刻的瞭解,同時在啓動 gdb 時也需要一些代碼來手動處理相關事項。關於這一點,我會在後續的其他文章中詳細介紹,更新之後我會附在本文的最後。

實驗我就不放了,其他人的 blog 中已經有很多了。我這裏推薦的這份配置也只是其他人的推薦的總和之中進行的篩選。還是那句話,it works for me。最近大家好像都很熱衷於編譯 LLVM 的競速比賽,有條件的同學也可以自己試試。

這些就是我在編譯 Clang 時所採用的全部配置項,其主要目的就是提高編譯速度。之所以會這麽着急,主要的原因是日常會擔心自己畢業不能。於是也就只能節約時間來做更多的事情。組裏的工具要維護、自己的研究也還要做、LLVM 的 revision 還要繼續提……我也日常在思考,爲什麽別人讀書都這麽輕鬆,而自己從小一直都是媽咪同事的孩子之中讀書最累的那一個。大概,是因爲我是所有人之中最笨的那一個吧……

以上


參考文獻:

    1. LLVM Techniques, Tips, and Best Practices Clang and Middle-End Libraries(https://www.oreilly.com/library/view/llvm-techniques-tips/9781838824952/
    2. Speedbuilding LLVM/Clang in 5 minutes(https://www.cambus.net/speedbuilding-llvm-clang-in-5-minutes/
    3. Speedbuilding LLVM/Clang in 2 minutes on ARM(https://www.cambus.net/speedbuilding-llvm-clang-in-2-minutes-on-arm/

更新:210620

最近發現選項 BUILD_SHARED_LIBS 能帶來的最大收益是鏈接十分迅速。尤其是在 commit 了之後,需要重新鏈接所有庫的版本信息時。幾十個鏈接操作可以在幾秒鐘之内完成,就很贊。但是需要注意的是,由於鏈接迅速,但是 debug 時加載緩慢,因此 debug 操作到底是打印 log 來一點點看還是用 GDB 來調試就成了一個很重要的選擇。因此需要好好權衡,該調試的時候還是最好去調試,要不然反而會白白誤了時間。

从 GitHub 下载一个 Repo 中部分文件内的代码

當需要下載 GitHub 上某一個 Repo 中某個子文件夾中的內容時,可以借用 GitHub 提供的 SVN 支持來實現這一功能。

由於一些大工程中某些組件完全可以獨立使用(比如:LLVM-Lit),這種情況下如果採用完整 clone 整個 repo 然後取出其中所需的部分然後刪掉剩下的部分的方式來處理的話,未免顯得會有些有病。「如果能只下載所需要的這部分就好了!」

比如,需要從 LLVM 的 repo 中下載 LLVM-Lit 的代碼(位於:https://github.com/llvm/llvm-project/tree/main/llvm/utils/lit),則可以執行如下操作。

svn export https://github.com/llvm/llvm-project/trunk/llvm/utils/lit

此時,即可將該 repo 最新 commit 中 llvm/utils/lit 文件夾下的全部內容都獲取到當前目錄下的 lit 文件夾中了。需要注意的是這個 svn 的鏈接與在 GitHub 上瀏覽時瀏覽器中的鏈接並不是相同的。

引用:

  1. https://coderwall.com/p/o2fasg/how-to-download-a-project-subdirectory-from-github
  2. https://docs.github.com/en/github/importing-your-projects-to-github/support-for-subversion-clients

從小包包裏掏出一個魔改過的 OpenRA

最近被自己一隻關在了家裏,每天至多能出門四次。除了每天出門吃飯飯以外,甚至見不到一個人、一隻貓。每天的生活也就只剩下了吃飯飯和上班。而碰巧貓貓正好把家裏的電腦給叼跑了,於是百無聊賴的窩就考慮弄個遊戲來玩玩。然而作爲一個玩遊戲的品味更像是油膩的中年大叔的貓貓,一般經常會玩的遊戲也就是曾經火熱一時的那些 RTS 遊戲了。由於家裏只有一臺辦公用的筆記本,所以並不是很想在上面安裝遊戲,也就更不用說 wine 了。思來想去,能有的選項也就只剩下 OpenRA 這個項目了。

OpenRA 項目脫胎自 Westwood 公司的經典 RTS 遊戲 Red Alert,自 EA 將 Red Alert 開源之後便有了這樣一個全平臺的開源項目。再加上由社區接管之後,大家給這個經典遊戲添加了很多更「現代」的操作方式,以及對於更現代化的設備有了更好的支持,因此相較於最原始的 Red Alert 來說,可玩性和兼容性都有了很大的提升。遊戲通過 .Net 框架實現了跨平臺,因此不光支持 Windows 平臺,Mac OS 和 Linux 也都有良好的支持,甚至 OpenRA 都進入了某些激進的發行版的官方倉庫。

Arch Linux 的 Community 倉庫中的 OpenRA 包
Arch Linux 的 Community 倉庫中的 OpenRA 包

「就決定是你啦!」然而可惜的是 Fedora 的倉庫裏面並木有這個遊戲(「人家才不是地溝油!嗚咕!」),而且窩也木有找到別人編譯好的 RPM 包。於是窩只好跑去了 OpenRA 的官網,所幸的是在那裏窩找到了官方打好的 AppImage 鏡像。這對於窩來說真的是非常舒適的,畢竟如果是 RPM 包的話,窩還要裝很多依賴給它,而 AppImage 則不用。即用即下載,就只有單獨一個文件,而不需要了的時候也不需要卸載,直接刪掉這個文件就好了。

鏡像的文件也並不大,只有不到 19 MB,裏面已經包含了所有運行這個程序所需的環境依賴。然而真正讓遊戲能夠跑起來所需要的遊戲資源(音樂、關卡、地圖等)卻有 100 多 MB,好在是初次打開引擎之後就可以自動下載。但提供下載的只是運行引擎所必須的基礎依賴,如果需要其他資源(過場動畫、額外的音樂包等)則需要自己從原版遊戲光盤上提取。

OpenRA 引擎自帶的資源管理器
OpenRA 引擎自帶的資源管理器 (這裏的截圖是已經安裝了所有必須資源之後的狀態)

下載完必須的資源之後重啓引擎,便可以正常啓動遊戲了。遊戲的主菜單更像是照抄了 C&C Generals 系列的主菜單模式:背景上是一張自動執行的動畫地圖,提供了遊戲整體效果的動態演示,然後 logo、選單、新聞等放置於地圖場景之上。(其實窩對於這種放在背景上自動執行的這種動畫地圖還是很感興趣的,最主要的一點就是這種地圖必須要設計的平衡度非常好,要不然一方將另一方擊敗的話動畫就演不下去了)

OpenRA 主菜單
OpenRA 主菜單

進入遊戲之後,遊戲的 UI 佈局和快捷鍵的設置則是基本照抄 Red Alert 3 的了。雷達、建造選項、金錢、電力等信息放置於右上角,超級武器等支援能力的使用選項則放置於左上角。建造選項爲每行三個共四行,對應各項的快捷鍵分別爲 F1F12 鍵。建築、防禦、士兵、載具、飛行、水上的選單分別對應 E ~ I 鍵,而變賣、電力控制、修理則分別對應於 ZXC 三個鍵。幾乎與 Red Alert 3 的完全相同。而建造選單方面不僅相對於原版 Red Alert 加入了單位生產的排程,甚至針對於建築物的建造也可以排程。相信這一點凡是玩過原作的玩家都會覺得是一個非常好的文明。

兩種陣營各自的建造選單
兩種陣營各自的建造選單

到這裏爲止,目前還沒有任何關於標題裏面所提到的「魔改」的內容,下面的內容纔是魔改一詞所涉及的重點。

由於小的時候常常玩的是 Red Alert 2,再加上窩本身玩 RTS 類遊戲玩得是對於全局的掌控感,以及收集了大量資源之後的滿足感,實際上並不喜歡真刀真槍地打來打去,因此每次玩的時候基本上都只是開一個簡單的 bot 然後慢慢玩到老。因此,儘管 OpenRA 對於遊戲體驗有很大程度的改進,玩過幾盤遊戲之後便也厭了。於是,窩又開始打起了 2 代的主意。

非常巧合的一點是窩在查的時候並沒有刪掉 openra 這一關鍵字,於是便發現了一個令我喜出望外的 OpenRA 的 mod:OpenRA-RA2(項目 repo)。在翻看了 YouTube 上的一些演示視頻之後,於是決定自己來上手試試看。然而不巧的是,廢了很大的力氣窩也並木有找到現成的 prebuild 軟件包(這次更衰)。沒辦法,要想玩的話也就只能自己來 build 這個項目了。試着把 repo 上的代碼拉下來,然後照着 Wiki 上的步驟 make 了一下,果然炸了。。。QAQ

等一下,自己 build 這個項目?憑藉着前 Arch Linux 用戶的本能,窩便又隨手去翻了一下 AUR,沒想到居然真的找到了:openra-ra2-git(AUR 信息頁)。既然有了 AUR,那接下來的工作便簡單了。在 chroot 環境下的 Arch Linux 子系統中通過 yaourt 便可以 build 出一個這個項目的軟件包。然後把軟件包裏的內容安裝到外面的系統上便可以正常運行了。

使用 yaourt 來 build 這個包的這一步進行得非常順利,果然還是別人已經寫好了的自動化腳本用起來更舒服。同時 openra-ra2-git 這個 AUR 包裏不僅提供了 build 的腳本,也額外提供了一些方便於在系統中使用的一些額外的配置文件和圖標等,因此 AUR 上的包往往會讓人用起來非常舒適。

然而 build 好了這個包之後,如何把它安裝到外面的系統裏,同時又讓它能正常運行,這便是在得到了這個包之後的一個新問題了。畢竟 openra-ra2-git 的信息頁上明確寫着要運行這個東西有多達 12 項依賴。幸虧 Arch Linux 打的包一般 granularity 都很大,要是像 Ubuntu 那樣非常細地拆分包的話,真是不知道要再裝多少個依賴包了呢。在對這個問題思考了好久之後,窩突然想到一點:AppImage 包含了運行這個程序所需要的全部必須的依賴,而同時它又可以 extract 到一個文件夾中然後再運行的。而 extract 之後得到的是一個完整的目錄樹,可以往其中添加其他的東西並通過這個環境來運行加進去的東西。

誒?窩不是正好有 OpenRA 的 AppImage 嗎?於是,把 OpenRA 的 AppImage 拆包,然後把 openra-ra2-git 這個軟件包中的內容與拆包之後的目錄樹合併在一起,然後改了一下 AppImage 的 AppRun 腳本,將啓動 OpenRA 的命令換成了啓動 OpenRA-RA2 的。果然引擎正常地跑起來了。

OpenRA-RA2 也需要原版 Red Alert 2 中的資源文件
OpenRA-RA2 也需要原版 Red Alert 2 中的資源文件

然後便是 OpenRA-RA2 的 Wiki 上所提到的運行時所必須的 Red Alert 2 的若干 *.mix 文件了。而窩碰巧有遊戲的原版安裝光盤 ISO 鏡像,於是 mount 了這個鏡像,然後根據 Wiki 上的指引把所需的文件放到了指定的資源文件夾中。啓動引擎,發現還是沒有找到資源文件。複查了幾遍之後發現沒有問題,那唯一的一種可能就是官方 Wiki 上給的資源目錄有問題。於是窩在那個 repo 中搜了一下那個目錄,發現有一份配置文件中有提到這個目錄,然而是一個相對於 OpenRA 引擎資源目錄的一個相對目錄。憑藉直覺窩覺得應該是找到問題了,因爲窩拿到的那個 AppImage 中的引擎所指定的資源目錄和 OpenRA-RA2 的 repo 中假設的資源目錄並不一致(窩這邊的話,OpenRA 的資源目錄是在 ~/.config/openra/Content 目錄,而不是 ~/.openra/Content 目錄)。於是把那些資源文件移到了正確的位置之後,遊戲被正常加載了。

進入遊戲主菜單之後,主菜單的佈局還是與 OpenRA 的相同(畢竟是同一個引擎),而不同的是用作背景的動畫地圖變成了一張空白地圖,甚至連邊框都沒有。這不禁讓人很沮喪。但萬幸的是,自帶的地圖編輯器可以修改這張地圖,所以如果有人有閒心的話是可以做一個動畫地圖來替換這張白板的。

OpenRA RA2 的主菜單
OpenRA RA2 的主菜單

進入遊戲之後,相對於 OpenRA 風格就變了很多。UI 的樣式更接近原版(至少作者把原作的素材都用上了),而佈局和快捷鍵還是 OpenRA 的樣式,並沒有遵循原作對於快捷鍵的設置。但在玩過幾盤 OpenRA 熟悉了鍵位的設置之後,建造、放置,一切又都是那種熟悉的感覺……

OpenRA-RA2 中兩種陣營各自的建造選單
OpenRA-RA2 中兩種陣營各自的建造選單

總體來講,這個 mod 目前的完成度並不低,但也並沒有完全做完全部的功能。單從我隨便試玩的兩盤來講,就發現和很多並沒有完成的功能點。比如:Lightning Storm 並沒有支持,建完 Weather Control Device 之後就沒有然後了;Mirage Tank 並不會變成樹,Chrono Legionnaire 只會在地上噠噠噠跑,等等。遊戲中的一些屬性參數的實現也有些許微妙的不一致,玩起來既有那種熟悉的感覺,又總會覺得哪裏怪怪的,更像是一個新遊戲。而遊戲的手感相較於原版的 Red Alert 2 來講更加的棉軟,像是往棉花包裏面打拳那樣的感覺。希望作者能繼續做下去,完成這個項目,甚至發展成一個類似於 OpenRA 這樣的完整改進版本。就目前來看,403 個 star 、91 個 fork 的關注度雖然並不是很高,但作者幾乎每天都有 commit 這件事還是能讓人有所期盼,布吉島後續作者會不會咕~

猫猫曾说:“Gmail 是最好的邮件客户端”

猫猫曾说:“Gmail 是最好的邮件客户端”。这里讲的 Gmail 指的并不完全是 Gmail 这个电子邮件系统,确切地说是其网页版前端,也就是我们日常工作中使用的最多的一个站点:gmail.com。

正在启动的 Gmail 网页客户端
正在启动的 Gmail 网页客户端

如果你不是像我一样将一个 Gmail 的页面 pin 在浏览器上,并且保持浏览器长期不关的话,这个加载动画将是你在使用 Gmail 的网页客户端的时候经常会看到的。而加载完成之后,打开的便是上面讲到过的这个最好的邮件客户端了。

之所以称其为邮件客户端,不单单是因其可以查看所有你名下的 Gmail 帐号的邮件,而是 Gmail 也可以通过 POP、IMAP 以及 SMTP 等协议来控制其他的邮箱,就像一个本地的邮件客户端那样(例如:Outlook、thunderbird 等)。通过将全部的邮箱聚合在一起,可以将各个被独立割裂的邮箱统一在一起使用。而所有邮件又都放于 Gmail 的服务器上,不会出现像本地邮件客户端那样在更换电脑时需要提前备份邮件数据的情况。而这一切的轻松自如,是从配置 Gmail 的邮件代收代发开始的。

首先进入“设置” -> “帐号和导入” 选项卡(由于日常使用的中文系统比较乱,我的 Google 帐号中配置的文字是简体字,其他语言请自己找找看),里面中间部分会有两项设置,其中一项是配置代发邮箱而另一项则是配置代收邮箱。

“帐号和导入” 选项卡
“帐号和导入” 选项卡

区别于本地邮件客户端,这里收取的邮箱和发送的邮箱是分别独立配置的。也就是说,Gmail 并不是按照邮箱帐号来对邮件进行托管的,而是将所有被托管帐号的邮件都混在一起。这样的设计使得用户不再需要注意邮件是被发送到哪个邮箱的,特别适合于像我这样同时拥有多个面对同一群人的邮箱账户的情况。

配置好代收代发邮箱之后,代收邮箱中的邮件会被统一收到这个 Gmail 邮箱中进行处理,而这个 Gmail 邮箱又同时可以通过配置的若干个发信帐号来发邮件。这样就避免了同时打开多个邮箱的网页客户端,甚至是与同一个人的对话由于收信邮箱的不同而出现割裂的窘境。

其实,好用的网页客户端并不只 Gmail 一家。之前用的 T 实验室的邮箱就基本满足了我对于邮件客户端的需求(有浏览器通知新邮件,界面交互比较符合人类逻辑等)。然而 T 实验室所提供的邮件客户端需要每 24 小时就重新登录一次,而且当登录超时之后便不会再有新邮件提醒了。而我同时所拥有的 S 实验室的邮箱的表现则更差,不仅没有浏览器通知,甚至还会重复的登出账户,这简直是反人类。同时,最近老板添了一个新的毛病——同一封邮件会同时发给我 T 实验室和 S 实验室的两个邮箱帐号,于是就会出现有时同一件事由于这两封邮件而被做了两次的情况。

由于我日常办公使用的是 Gsuit 来处理文档,因此 Google 帐号又是我最常用的邮箱。同时我的 Gmail 是用来接收飞机酒店等通知邮件的重要邮箱,虽然本不想将 Gmail 邮箱用于工作,但不得已也接收了很多与工作相关的邮件。既然这样,便随之萌生了一个想法——干脆使用 Gmail 来管理所有邮件吧。

然而这一工作的开始并不顺利。一来是三个邮箱里已经有了很多历史邮件,而这些邮件又有很多查询的必要。二来是 Gmail 一直一来的问题——会把某些类型的邮件附件文档标记为有害(例如:Office 文档等),从而不将其拉取到 Gmail 中,只会留下一条通知告知说 “喂~你的邮件有毒,我就不吃啦,你自己看着办吧。”,从而弄得我真的很崩溃。但即使是这样,最终我还是没能抵抗得过在一个浏览器中同时开三个邮箱标签的崩溃感,于是我在终于得闲之后大刀阔斧的折腾了一番。

因可疑而被退回服务器的邮件的通知
因可疑而被退回服务器的邮件的通知

在经历了将近三天的漫长折腾之后,三个邮箱终于被窝搞到了一起。终于可以愉快的使用工作邮箱了。同时由于所有邮件都被 Gmail 代管了,因此在移动设备端也仅需要下载一个 Gmail 的 App 即可实现所有邮件的查收和回复。相比于之前需要在 Gmail App 中配置另两个邮箱的帐号信息,两个邮箱的邮件也被割裂到两个不同的帐号中,现在无论从配置和使用上来讲都有了很大程度的愉悦度和方便性上的提升。

代发邮件的配置很简单,只需要配置好 SMTP 服务器的相关信息即可。关于服务器的信息可以查询邮箱提供者所给出的配置教程。需要的信息包括:服务器域名、端口,用户名和密码以及链接的加密方式。一般情况下 Gmail 可以自动探测出相关配置,如需修改请按照配置教程中给定的内容填写。

填入 SMTP 服务器信息
填入 SMTP 服务器信息

而代收配置的方式分为三种。第一种是 Gmailify,即对于部分 Gmail 所支持的邮箱提供商(如:Yahoo、AOL、Outlook、Hotmail),可以通过 Gmail 来直接使用所关联的邮箱。但请注意,Gmailify 仅支持一个邮箱账户。而对于拥有多个邮箱的用户,或者使用了 Gmail 不支持的邮箱的情况,则需要采用后两种方式:POP3 协议收取或邮箱转发推送。对于第一种方式,这里不做过多描述,详细信息请参阅 Gmail 的文档「为其他电子邮件帐号获取 Gmail 功能」。

使用 POP3 协议来收取邮件的方式,即是使 Gmail 工作于邮件客户端的形式。在这种方式下,Gmail 的服务器会定期去配置的服务器中直接收取邮件并呈现于 Gmail 的收件箱中。这种方式的配置类似于配置代发邮箱,在 Gmail 的配置中填入邮箱提供者给出的 POP 协议的配置信息,待 Gmail 查验通过后即可正常使用。

填入 POP 服务器信息
填入 POP 服务器信息

而使用邮箱中的邮件自动转发功能来实现收取的方式,是使 Gmail 工作于普通邮箱的形式。在这种方式下,邮件是通过邮箱提供者服务器自动推送到 Gmail 的收件箱中并呈现的。这种方式的配置需要在源邮箱中进行,一般情况下邮箱都会提供自动转发的功能,使用该功能将邮件转发至自己的 Gmail 邮箱即可。

于是就产生了一个问题:这后两种方式到底那个更好呢?

猫猫说她更倾向于使用 POP 协议的方式来收取邮件。而我的话则是两种都采用了。T 实验室邮箱采用的是 POP 协议收取,而 S 实验室邮箱则采用的是邮箱自动转发来信的方式。主要的原因是 Gmail 对于 S 邮箱的查收非常不及时,而 T 邮箱则可以及时查收。然而并不知道这个查收延时是怎么设定的,也许是依照收件频率吧,反正不能自行配置。为了能够更快的收取 S 邮箱的邮件,于是决定将其配置为自动推送的方式。同时,也是为了兼容上文中提到的老板的新毛病。对于发到 S 邮箱的邮件,如果同时被发到了 T 邮箱,则不进行转发。(真是机智如我)

所以,对于这个问题,我的答案是:如果需要更及时的邮件查收的话,请使用邮件自动转发的方式进行推送。否则的话,可能 POP 协议收取的方式会更好些,毕竟转发是一次新的发送,依然有可能存在未能被 Gmail 成功收到的情况。

至于使用的效果如何,个人觉得 Gmail 的网页端确实很好用。支持图片的粘贴插入,邮件的自动归类,同时查找功能也更加强大。因此,如果后续没有其他使用问题产生的话,打算暂时先这么用下去了。

以上

后记:好像拉取的方式总会出现莫名的延迟,于是最后又改成全部采用原始邮箱自动转发推送的方式了。。。QAQ

從小包包裏掏出一個 WordPress

開站第一篇文章,總結一下如何使用 docker 來構建一個 WordPress 站點,以及期間遇到的問題。本站以個人筆記以及雜談的性質來提供,不求其他人也能看得懂,主要用於備忘。發表的文章所採用的語言包括所有窩會的語言,所以也沒辦法求其他人能一定看得懂。

首先回歸正題。

貓貓之前一直都說 docker 用起來如何方便,然而經過之前一次嘗試構建 mastodon 的慘痛經歷之後,一直都沒能有心情來嘗試搞事情。然而由於最近在趕完若干篇稿子之後,終於難得有幾日的閒暇,以及某些迫不得已的剛需,於是就又打算搞起來惹~。。。

之所以第一發選擇 WordPress,一來是因爲一直想有一個長期的備忘錄性質的筆記本,以及可以讓自己隨意畫畫寫寫的板子;而二來也是因爲據說這個東西搞起來比較簡單。而選擇使用 docker 來作爲搭建的基礎,則完全是因爲貓貓的安利了。當然,方便簡單也是主要被考慮的原因之一。畢竟,作爲一個寫稿子的,技術和能力都比不上經驗豐富的貓貓,也就只有量力而爲了。

起初找到的一個教程是如何使用 docker 起一個數據庫實例和一個 WordPress 實例,然後讓這兩個實例配合在一起使用。然而看過之後就覺得,手動管理兩個實例貌似操作起來過於繁瑣了。於是就想到了之前貓貓在教窩配置 mastodon 的時候提到過的 docker-compose 了。

於是,窩就噠噠噠跑去看 docker-compose 的教程了。由於有了一點點之前的理解,在簡單過了一遍前面的說明之後,就開始自己動手寫了起來:

version: "3"

services:
    db:
        restart: always
        image: mariadb:latest
        environment:
            MYSQL_ROOT_PASSWORD: xxxxxx
            MYSQL_DATABASE: wordpress
        volumes:
            - /root/workspace/wordpress/mysql/:/var/lib/mysql

    wordpress:
        depends_on:
            - db
        restart: always
        image: wordpress:latest
        environment:
            WORDPRESS_DB_HOST: db:3306
            WORDPRESS_DB_USER: root
            WORDPRESS_DB_PASSWORD: xxxxxx
        volumes:
            - /root/workspace/wordpress/html/:/var/www/html
        ports:
            - "80:80"

volumes:
    mysql:
    html:

最終,成品的配置文件就是醬紫惹。其實在寫的過程中又翻到了 docker-compose 教程裏面給的那個 WordPress 的配置文件。本來想用的來着,然而卻怎樣也跑不起來,總在說 MySQL 恢復數據庫失敗。於是,一氣之下就把數據庫換成了上面提到過的那份分開手動控制的那份教程裏面使用的 MariaDB。最後終於是跑起來了,docker-compose 不再報錯了。

於是,興沖沖的跑去用瀏覽器看了一下,然後發現數據庫連接出問題了 T_T。查了下 log 發現是數據庫拒絕連接了。雖然並不清楚是因爲神馬造成的拒絕連接,但是憑着多年以來的直覺,將數據庫用戶換成了 root,於是大功告成。

然而,又有了新的狀況。在配置文件裏面 volume 項中指定的文件夾裏面並沒有文件。問了貓貓,說讓窩看一下“資料庫起來沒有”,顯然這並不是窩應該會的操作。無奈之下,在貓貓的提醒下更新了一下 volume 中的路徑爲絕對路徑,然後又重啓了一下 docker 服務以及這兩個容器。接着收到了一條警告說之前指定的 volume 的值因爲使用了舊的容器所以無效。於是果斷的刪掉了之前的容器,然後又重新開了這兩個容器。終於,volume 文件夾內也有文件了。超讚!

這樣,應該也就差不多了吧,,,大概。。。剩下的也就是折騰 HTTPS 以及主題神馬的了,打算明天或者以後再弄吧。。。