前言
當應用程式寫完後,會將該程式移到異地執行,這時候就牽涉到部署程式的議題。部署程式的方式會因平台而異,本文介紹在 Windows 上部署自 MSYS2 環境編譯出來的執行檔的方式。
只要能夠在 MSYS2 環境編譯的應用程式,應該都可以用本文所介紹的方式來部署程式,像是 C、C++、Objective-C、Vala 等皆可。但 Perl、Python、Ruby 等直譯語言的部署方式不同於編譯語言,不在本文討論的範圍內。
Windows 執行檔查找相依函式庫的方式
如果執行檔採用靜態編譯,就可以直接部署。但執行檔採用動態編譯,則要一併附帶動態函式庫。根據微軟的官方文件可知,執行檔查找動態函式庫的順序會受到 HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\SafeDllSearchMode
機碼的設置而異。
若 SafeDllSearchMode
機碼開啟時,執行檔查找動態函式庫的順序如下:
- 執行檔所在的路徑
- 系統路徑
- 16 位元系統路徑
- Windows 所在路徑
- 工作目錄
- PATH 所在路徑
反之,若 SafeDllSearchMode
機碼關閉時,執行檔以下列順序查找動態函式庫:
- 執行檔所在的路徑
- 工作目錄
- 系統路徑
- 16 位元系統路徑
- Windows 所在路徑
- PATH 所在路徑
總和來說,最簡單的方式是將執行檔和動態函式庫放在同一個資料夾。不論 SafeDllSearchMode
是否有開啟,都是優先查找函式庫的位置。
用 ldd
查詢執行檔或動態函式庫的相依性
MSYS2 的 ldd(1)
非常好用,可以用來查找執行檔所相依的動態函式庫。甚至非 MSYS2 專案所提供的編譯器所編譯的執行檔也可以使用。像筆者就拿過 Free Pascal 和 Golang 所編譯出來的執行檔來測試,ldd
皆可正確地顯示出其相依性。
但 ldd
只會顯示出相依的動態函式庫,程式設計者仍需要手動拷貝這些函式庫到執行檔所在的位置。為了減輕這些機械性的勞動,筆者寫了個 shell 命令稿。詳見下一節的說明。
自動拷貝動態函式庫的 Shell 命令稿
承上,以下是筆者所寫的 shell 命令稿:
#!/bin/sh
# deploy2win - Deploy a MSYS2 executable to native Windows environment
#
# Copyright (c) 2020, OpenTechTutorials. Licensed under MIT.
target="$1"
# Check whether `$target` is an executable.
if ! [ -x "$target" ];
then
echo "Not a valid executable: ${target}" >&2
exit 1
fi
# Get the destination directory.
dest="$(dirname $(realpath $target))"
# Iterate over the required DLLs, excluding those in system directories.
for dll in `ldd $target | grep -i -v WINDOWS | cut -d ' ' -f 3`;
do
src="$(dirname $(realpath $dll))"
# If the `$src` of the `$dll` is not the same as `$dest`,
# copy the `$dll` to `$dest`.
if [ "$src" != "$dest" ];
then
cp "$dll" "$dest" || (
echo "Fail to copy $dll to $dest" >&2
)
fi
done
這個命令稿一開始會以 -x
檢查輸入的參數是否為執行檔路徑。確認輸入的路徑正確後,才會進行下一步動作。
該命令稿根據執行檔所在的位置取得其所在的目錄 dest
。待會會用到這個位置。
關鍵的指令在 ldd $target | grep -i -v WINDOWS | cut -d ' ' -f 3
,該行指令結合了 ldd(1)
、grep(1)
、cut(1)
三個指令,再用管線 (pipe) 將三者串接起來。這個指令會先用 ldd
查找執行檔的相依函式庫,再用 grep
去除 Windows 內建函式庫,最後用 cut
過濾出該函式庫所在的路徑。
由於函式庫往往有多個,這裡用 for
迴圈進行多次迭代。在每次迭代中,會檢查函式庫所在的位置 src
和執行檔所在位置 dest
是否相異,若相異,則拷貝該函式庫。由於拷貝是有可能失敗的動作,要用防衛性程式設計的方式,在拷貝失敗時吐出錯誤訊息。
該命令稿的使用方式如下:
$ deploy2win path/to/executable
建議先將執行檔移到獨立的目錄下,因為有時候函式庫會有多個。
用乾淨的 Windows 環境測試發佈的執行檔
使用上述 shell 命令稿不保證能夠順利部署應用程式。最好還是找一個乾淨的 Windows 環境進行必要的測試。
如果讀者不想花錢購買 Windows 的授權,可以到這裡取得合法的 Windows 映像檔。這本來是用來測試 Internet Explorer 和 Edge Legacy 的映像檔,但也剛好是乾淨的 Windows 環境。