美思 [Windows] 程式設計教學:發佈基於 MSYS2 的應用程式

Facebook Twitter LinkedIn LINE Skype EverNote GMail Yahoo Email

前言

當應用程式寫完後,會將該程式移到異地執行,這時候就牽涉到部署程式的議題。部署程式的方式會因平台而異,本文介紹在 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, Michelle Chen. 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 環境。

關於作者

身為資訊領域碩士,美思認為開發應用程式的目的是為社會帶來價值。如果在這個過程中該軟體能成為永續經營的項目,那就是開發者和使用者雙贏的局面。

美思喜歡用開源技術來解決各式各樣的問題,但必要時對專有技術也不排斥。閒暇之餘,美思將所學寫成文章,放在這個網站上和大家分享。