情人節在銀行過——中銀香港開戶記

本文記錄了從深圳出發過羅湖口岸到粉嶺開立中銀香港賬戶的過程,以期作爲之後希望開立賬戶者的參考。

動機與背景

這隻大貓咪終於博士畢業啦!!!啦啦啦!!!現在是有鈴鐺的大貓咪啦!!!

算是作爲畢業旅行吧,也算作是同其他幾位姐妹一同返鄉,於是借用春節的假期規劃了一個深圳 – 香港 – 曼谷的旅行規劃。多年未去深圳,甚是想念。返鄉曼谷,除了看護那位手術的姐妹外,就是回去出生的地方看看,以及跟其他同期的姐妹約飯了。而專程繞路香港的目的,也就是本文的主題了:去開立個戶頭。當然,也順便購置了新的手機,因爲現在在用的在畢業離開後就要交還了。

路程

出發前一晚詢問了一下本地朋友羅湖口岸的人流情況,規劃了一下行程。通關大致耗時半小時左右,到達港鐵羅湖站的時間是九點四十左右。使用通關閘機過關的速度很快,但好像如果有小朋友則需要走人工通道過關。兩側過關的閘機都是兩道門,外門刷通行證進入,內門按指紋離開,與機場自主通關的閘機相同。而香港側的入境閘機會在按指紋後打印過關小票,取小票後開門。要記得收好這枚小票,因爲開戶時需要使用。

由於港鐵新近啓用了 Visa 刷卡閘機,因此可以直接使用 Visa 信用卡搭乘港鐵列車而毋須辦理八達通 [1]。這一點對於我這樣的排斥掃描 QR Code 而 prefer 刷卡的單次訪客非常友好。從羅湖口岸香港側出來進入港鐵羅湖站的話,能刷 Visa 卡的淺藍色專用閘機在最內側,靠近八達通的開卡窗口。雖說港鐵保證了每個站都至少有兩部這樣的閘機,但對於出入口多的大站則無法保證每個出入口都有安設(例如:九龍塘站的部分出入口就僅有八達通閘機)。刷 Visa 卡的黑色讀卡區和 QR Code 掃描器一般在閘機的直立面,而刷八達通的橙色讀卡區一般在頂面 [1]。此外,也可以使用雲閃付、支付寶等 App 搭乘港鐵。但流程就不如刷 Visa 卡搬絲滑順暢了。與國內的部分支持銀聯卡的地鐵的結算形式不同,港鐵對於 Visa 卡是按日結算的。因此在出入閘機後信用卡上都不會有記錄,而在港鐵的官方查詢平臺 [2],我亦未能查到當日的刷卡記錄(大貓咪攤手)。

在拜讀了衛斯理 [3] 的經驗整理後,我亦選擇了粉嶺作爲開戶的地點。這裏距離羅湖站更近(僅有兩站的距離),便於速戰速決 [3]。此外,朋友也推薦了荃灣、葵芳、粉嶺、上水幾處,並表示不要去油尖旺、荔枝角、元朗等地。在衛斯理的經驗總結文中推薦了順豐和中行相對角的這兩家銀行,位置在和豐街與聯興街的交叉口處 [4]。但由於我本來的目的是開立一個戶頭即可,因此選擇了距離粉嶺站更近的中行粉嶺中心分行 [5],有約步行六分鐘的距離 [6]。

賬戶開立

賬戶開立在進門左側的櫃檯辦理,需要先在正對大門處的取號機取號,而存取款的業務則是在右側的櫃檯處排隊進行。我抵達銀行的時間約十點鐘剛過,左側櫃檯等待的人很少,在我之前只有一位客戶正在辦理業務,因此很快就排到了。來櫃檯前建議提前下載好中銀香港的 App [7][8],以便快速錄入個人資料。但如果無法使用手機或身份證信息識別失敗,亦可由櫃員手工錄入並完成開戶流程。銀行中有 Wifi,也可以在等候的時候再行下載。

爲我辦理業務的是一位年輕的男性櫃員,普通話很流利,服務也很熱情。在說明要開立賬戶的來意之後,櫃員向我索要了相關證件(身份證、港澳通行證、過關小票)以及地址證明。在我準備的地址證明材料中,櫃員採納了戶口簿複印件,即身份證上的住址。除此之外也說信用卡賬單亦可。但租房合同、電話費發票、供電費發票等材料未被採納。因此,準備如果時間充分的話,建議電話聯繫信用卡發卡行補寄紙質信用卡賬單。或者直接使用身份證和戶口簿登記的地址。

採納地址證明之後,櫃員詢問了我開立戶頭的目的。我的回答是用於投資,並表示開戶後會直接存入 1000 美金。然後櫃員就開始了戶頭開立的流程,並沒有向我索要相關投資經驗的證明。流程的第一步是使用中銀香港的 App 掃描櫃員提供的 QR Code 並拍攝讀取身份證的相關信息。但由於拍攝了很多次都讀取失敗,因此櫃員決定直接手工爲我錄入個人資料,並讓我將姓名、電話號碼、工作單位、職務等信息寫在一張便條上給他。信息錄入完成之後有一個關於投資經驗的問卷,內容大概是年收入、投資經驗等,櫃員會將顯示器轉到我這邊,由我來選擇他負責點選。完成流程之後會設置一個密碼並核對信息和簽字。最後,賬戶相關的信息會被打印在一張紙上,紙上記錄的賬戶信息可以用於登入中銀香港的 App,也可以直接用於存款,而銀行卡則後續會寄送到被採納了的地址證明中的地址。整個過程耗時大概半小時。

完成上述流程後,我直接去旁邊的現金櫃檯排隊並存入帶過來的美金。由於沒有實體卡,因此不能使用 ATM 進行存取款,只能在櫃檯辦理。現金櫃檯處的人很多而開放的窗口只有一個,因此排隊存款用了很久。存款時需要將錢和剛剛那張記錄賬戶信息的紙遞給櫃員。櫃員會詢問金額,並在完成後返還一張信息單據。然後,存入的錢就可以直接在 App 中外匯部分查詢到了。至此,全部銀行事物處理完畢。

總結與彩蛋

中銀香港粉嶺中心分行開戶需要準備:過關小票、身份證、港澳通行證、戶口簿複印件/房產證複印件/銀行補寄的信用卡賬單、以及中銀香港的 App。銀行卡會後續寄出,開戶後的相關銀行事務可以通過櫃員出具的賬戶信息紙直接進行。

由於今年春節正好是正月初五,作爲開工第一天,剛剛好遇到了銀行的上司來窗口爲櫃員們派發開工利是。然後,也碰到了來存款的客戶爲旁邊窗口的櫃員派發了利是。開工大吉~

檢視 Clang Static Analyzer 的 ExplodedGraph

之前一直好奇爲何 Clang Static Analyzer (CSA) 中 dump 函式都是以 JSON 的形式輸出的,今天纔發現原來是與檢視 ExplodedGraph 相關。

通過 dump.ViewExplodedGraph 這個 checker 可以將當前被分析程式碼的 ExplodedGraph 以 dot 檔案的形式報存下來。但其中的每一個 ExplodedNode 都依然是 JSON 格式的字串。如果需要清晰展現其中的內容,則需要 exploded-graph-rewrite 工具來對這份 dot 檔案進行處理。

該工具位於 LLVM 項目下的 ./clang/utils/analyzer/exploded-graph-rewriter.py 位置。使用該工具可以將剛剛生成的 ExplodedGraph 的 dot 檔案轉換爲對應的 SVG 檔案(其中使用了 GraphViz 負責格式轉換),並同時對每一個 ExplodedNode 中的 JSON 字串進行解析並生成對應的表格化展示,以方便查看。

處理前的 ExplodedNode(JSON 格式):

{ "state_id": 135,
  "program_points": [
    { "kind": "Statement", "stmt_kind": "DeclRefExpr", "stmt_id": 627, "pointer": "0x5556f11554b8", "pretty": "x", "location": { "line": 3, "column": 20, "file": "temp.cpp" }, "stmt_point_kind": "PostLValue", "tag": null, "node_id": 8, "is_sink": 0, "has_report": 0 },
    { "kind": "Statement", "stmt_kind": "DeclRefExpr", "stmt_id": 627, "pointer": "0x5556f11554b8", "pretty": "x", "location": { "line": 3, "column": 20, "file": "temp.cpp" }, "stmt_point_kind": "PostStmt", "tag": null, "node_id": 9, "is_sink": 0, "has_report": 0 }
  ],
  "program_state": {
    "store": { "pointer": "0x5556f1178008", "items": [
      { "cluster": "x", "pointer": "0x5556f1177f70", "items": [
        { "kind": "Direct", "offset": 0, "value": "0 S32b" }
      ]}
    ]},
    "environment": { "pointer": "0x5556f11776d0", "items": [
      { "lctx_id": 1, "location_context": "#0 Call", "calling": "f", "location": null, "items": [
        { "stmt_id": 627, "pretty": "x", "value": "&x" }
      ]}
    ]},
    "constraints": null,
    "equivalence_classes": null,
    "disequality_info": null,
    "dynamic_types": null,
    "dynamic_casts": null,
    "constructing_objects": null,
    "checker_messages": null
  }
}

處理後的同一個 ExplodedNode:

ExplodedNode after being handled

Commit Phabricator Revision to GitHub

Summarized workflow from LLVM documentation. Just keep this as a checklist for future commits. In this passage, I will use D102669 as an example to show the entire commit procedure. Since the commit is done on the patch branch. Let’s start by checking out the branch.

1. Check out the patch branch.

> git checkout D102669

2. Update commit message. Go to the Phabricator page of D102669. Copy the revision title as the header of the commit message. Then, on the option-menu to the right, select Edit Revision. Copy the revision summary as the body of the commit message. Remember to add a blank line after the header. Finally, add the revision link as the last line.

Go to the Phabricator page of D102669. On the option menu to the right, select Edit Revision.

3. Rebase the patch onto the latest commit on GitHub and then rerun the test cases locally.

> git pull --rebase origin main
> ninja check-clang-analysis

4. Re-check the list of commits to be pushed.

> git log origin/main...HEAD

5. Push the commit.

git push origin HEAD:main

As a first-time committer, the push operation was rejected twice before it was finally committed. First, GitHub requires the git push operation cannot be authenticated via username and password. Therefore, I replaced the remote link with the SSH link to solve the problem.

Username for 'https://github.com': Snape3058 
Password for 'https://[email protected]': 
remote: Support for password authentication was removed on August 13, 2021. Please use a personal access token instead. 
remote: Please see https://github.blog/2020-12-15-token-authentication-requirements-for-git-operations/ for more information. 
fatal: Authentication failed for 'https://github.com/llvm/llvm-project.git/'

Second, when you set your Emails private, GitHub will reject the pushing requirement if the operation will leak your Email address. Therefore, you need to uncheck the option to allow this operation.

remote: Resolving deltas: 100% (21/21), completed with 21 local objects.                                                                                                                                             
remote: error: GH007: Your push would publish a private email address.                                                                                                                                               
remote: You can make your email public or disable this protection by visiting:                                                                                                                                       
remote: http://github.com/settings/emails                                                                                                                                                                            
To github.com:llvm/llvm-project.git                                                                                                                                                                                  
 ! [remote rejected]           HEAD -> main (push declined due to email privacy restrictions)                                                                                                                        
error: failed to push some refs to '[email protected]:llvm/llvm-project.git'

That’s all for my first commit to GitHub for the LLVM project. Hope it would be helpful to my future commits and other contributors.

References:

1. https://llvm.org/docs/DeveloperPolicy.html#commit-messages
2. https://www.llvm.org/docs/Phabricator.html#committing-a-change
3. https://www.llvm.org/docs/GettingStarted.html#commit-from-git

鯨魚背上的大貓咪——在 Linux 電腦上通過 Docker 來運行 Mac OS X

鯨魚,即是 Docker。而 Mac OS X,因其之前使用大型貓咪來命名,故而稱其爲大貓咪。在 Linux 電腦上通過 Docker 來運行 Mac OS X,自然也就是鯨魚背上的大貓咪了。——題記

在處理 D102669 時,遇到了一個僅在 Mac 上纔會觸發的 bug。而手上除了豹豹有一臺公司的 MBP 外,整個家族也就再無觸手可即的 Mac 了。雖說是公司的電腦,但畢竟是豹豹的,所以也就不好意思拿來編譯和 debug。再加上一直以來在 OS X 上都沒有搞掂 debug 相關的事情,於是便萌生了自己來弄一個 Mac 電腦的想法。

作爲一個窮鬼書生,買是買不起的,畢竟家父不是國王。於是,便思考起左道旁門來。比如:進到一家蘋果店裏,把展出的 Mac 電腦連到實驗室的伺服器上開一個反向代理,然後白嫖人家的展示機。不過最後還是鑑於良心上的譴責以及確實登錄會存在問題而放棄了這個方案。(好孩子不可以學我這樣做哦)

因此,看起來比較切實可行的方案就只剩下了黑蘋果和虛擬機。在一次偶然的搜索時,發現了這樣一個項目:Docker-OSX,於是瞬間豁然開朗。這個項目把 QEMU KVM 運行 OS X 虛擬機所必須的文件都封裝到一個 Docker 鏡像中,從而完美地解決了自己配置 OS X 虛擬機搞不掂的問題。使用起來也相當方便且順暢,只需要一條命令把這個 Docker 鏡像運行起來,你便擁有了一個可用的 OS X 環境。

別家的使用教程已經有很多了,這裏就不再贅述,我只給出一些個人的使用經驗。官方教程影片在這裏(https://youtu.be/wLezYl77Ll8),有興趣可自行學習。我這邊使用的是默認版本的鏡像,也就是 Mac OS 10.15 Catalina。啓動之後,默認的鏡像是裸機環境,需要自行安裝系統。安裝大約耗時半小時,安裝完成之後就可以直接使用了。由於沒有 GPU 加速,因此 UI 會比較卡頓。但打開 SSH 登錄之後,直接使用控制檯時並不存在卡頓的問題。因此如果需求是使用 GUI 應用較多的話,還是需要考慮一下配置 GPU 加速的。由於我這邊是跑編譯和 debug,用的工具也是控制檯上運行的 Vim 和 GDB/LLDB,而 GUI 的卡頓對於我的影響也就微乎其微了。

由於編譯需要消耗比較多的記憶體以及 CPU 資源,於是便轉移到了實驗室的伺服器上來運行。這裏便遇到了第一個需要解決的問題:遠端的 QEMU 界面如何在本地顯示。解決的方法就是 X forwarding。首先在 SSH 連接實驗室的伺服器時需要開啓 X forwarding,即 ssh 命令的 -X-Y 參數。同時爲了不卡頓,還需要開啓流量壓縮,即 -C 參數。爲了鑑權方便,我這裏選擇了 -Y 參數。如果是使用其他 SSH 客戶端的話,需要查看一下自己的客戶端如何啓用這兩個選項。

local$ ssh -YC user@hostname

然而,這只是第一步。這只能讓伺服器上運行的 GUI 程式顯示在本地的熒屏上。而對於一個 Docker container 來講,其運行的環境可以認爲完全是另外一臺電腦,因此還需要讓 Docker container 中的 GUI 程式可以利用窩們轉發的 X 數據。需要解決的有兩點:連接和鑑權。這裏採用了直接使用 host 網路來啓動的方式來解決連接問題,即 --net=host。此時,Docker container 中的 DISPLAY 變量不需要額外改動,直接使用外邊的值即可。此外,X server 鑑權所依賴的 $HOME/.Xauthority,也需要給 1000 用戶開放讀取權限並放到 Docker container 中(如果你本人的賬戶 ID 不是 1000 的話)。

remote$ chmod 666 ~/.Xauthority

記憶體和 CPU 用量的設置是通過環境變量來進行的:CPU 是 SMP 以及 CORES,記憶體是 RAM(單位:GiB)。默認參數是 4 核 4 GiB 記憶體。此外,如果想要持久化硬碟中的數據,可以將 Docker image 中的 /home/arch/OSX-KVM/mac_hdd_ng.img 文件拷貝出來,然後在啓動 Docker 的時候放進去即可。

remote$ docker run -it --rm \
          -v /path/to/disk:/tmp/disk \
          --entrypoint=/bin/bash \
          sickcodes/docker-osx:latest
container$ cp /home/arch/OSX-KVM/mac_hdd_ng.img /tmp/disk

啓動 QEMU 時,會將虛擬機 SSH 的 22 端口轉發到 INTERNAL_SSH_PORT(默認爲 10022),以及 VNC 的 5900 端口轉發到 SCREEN_SHARE_PORT(默認爲 5900)。由於前面使用了 host 網路,因此如果這兩個默認佔用的端口如果已被使用,則需要通過這兩個環境變量來重新定義。

remote$ docker run --rm -it \
          --net=host -e DISPLAY=$DISPLAY \
          -v $HOME/.Xauthority:/home/arch/.Xauthority \
          --device /dev/kvm \
          -v /path/to/disk/mac_hdd_ng.img:/home/arch/OSX-KVM/mac_hdd_ng.img \
          -e INTERNAL_SSH_PORT=40022 \
          -e SCREEN_SHARE_PORT=45900 \
          -e SMP=32 -e CORES=32 -e RAM=64 \
          sickcodes/docker-osx:latest

這個時候,如果沒有遇到什麼奇怪的問題的話,應該就可以看到控制檯上刷出一些 QEMU 的啓動 log 然後彈出虛擬機的熒屏窗口了。

至此,你便擁有了一隻鯨魚背上的大貓咪。後面的工作就交給 Homebrew 吧。

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

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

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

cmake ../llvm \
    -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 \
    -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 \
    -DLLVM_ENABLE_BINDINGS=OFF \
    -DCLANG_ENABLE_ARCMT=OFF \ 
    -G Ninja

作爲一個 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 中并不涉及相關内容,因此將其禁用。這部分我的瞭解並不多,但禁用之後沒有發現過多問題,因而這個選項就保留了下來。

-DLLVM_ENABLE_BINDINGS=OFF,禁用 OCaml 和 Go 的 binding 編譯。由於我們本身的目的是做 CSA 開發,並不需要對其他語言的接口提供支持(貌似 CSA 也沒有相關支持?),因此這裏關閉了相關編譯。(211231 更新內容)

-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. Building LLVM with CMake (https://llvm.org/docs/CMake.html)
    2. LLVM Techniques, Tips, and Best Practices Clang and Middle-End Libraries(https://www.oreilly.com/library/view/llvm-techniques-tips/9781838824952/
    3. Speedbuilding LLVM/Clang in 5 minutes(https://www.cambus.net/speedbuilding-llvm-clang-in-5-minutes/
    4. 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 來調試就成了一個很重要的選擇。因此需要好好權衡,該調試的時候還是最好去調試,要不然反而會白白誤了時間。


更新:211231

貌似 GDB 近來對於加載動態鏈接庫時啓用了多線程優化,加載速度大幅提升,因而還是建議在開發過程中啓用 BUILD_SHARED_LIBS 參數。

另,上面這些 CMake 參數已經解釋得比較詳細了,相關參數也可以用於 release 編譯,調整 debug 相關配置之後的用於 release 編譯的 CMake 配置如下。

cmake ../llvm \
    -DLLVM_ENABLE_PROJECTS=clang \
    -DLLVM_TARGETS_TO_BUILD=X86 \
    -DLLVM_ENABLE_Z3_SOLVER=ON \
    -DCMAKE_BUILD_TYPE=Release \
    -DCMAKE_C_COMPILER=clang \
    -DCMAKE_CXX_COMPILER=clang++ \
    -DLLVM_USE_LINKER=lld \
    -DLLVM_ENABLE_BINDINGS=OFF \
    -DCLANG_ENABLE_ARCMT=OFF \
    -DCMAKE_INSTALL_PREFIX=/path/to/llvm/install \
    -G Ninja

相較於 debug 編譯的版本,主要去掉了部分用於控制 debug 信息和方便調試相關的參數,同時啓用了編譯優化。這裏的配置還是面向開發調試以及最終的 release 的。因此,這裏沒有給出 LLVM_BUILD_LLVM_DYLIB 參數的相關配置。如有需要也可以自行開啓。鑑於與上方已介紹過的內容相似,這裏就不再囉嗦了。

本次更新添加了一個新的默認參數 -DLLVM_ENABLE_BINDINGS=OFF,相關介紹詳見上文。


更新:231221

最新的發現是 Ubuntu 默認編譯的 GDB 貌似沒有啓用多線程 shared object 加載,而我能用到是因爲自己編譯的緣故。這裏共享一下 GDB 的配置參數:

./configure \
  --prefix=/path/to/gdb/installation/root \
  --enable-build-with-cxx \
  --with-system-readline \
  --enable-64-bit-bfd \
  --with-system-zlib \
  --with-lzma \
  --enable-vtable-verify \
  --enable-tui \
  --with-path \
  --disable-unit-tests \
  --enable-source-highlight

暫時沒時間去 digging in 到底是哪個編譯配置項導致的多線程加載,日後研究後再修改這裏。

 

从 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 以及主題神馬的了,打算明天或者以後再弄吧。。。