發佈基於 MSYS2 的應用程式

    前言

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

    【分享本文】
    Facebook Twitter LinkedIn LINE Skype EverNote GMail Yahoo Yahoo
    【追蹤本站】
    Facebook Facebook Twitter