美思 [Objective-C] 程式設計教學:在 Windows 以 GNUstep 建立 Objective-C 開發環境

Facebook Twitter LinkedIn LINE Skype EverNote GMail Yahoo Email

前言

由於 Windows 不是 Objective-C 的官方開發環境,無法使用 Cocoa。在 Windows 上,只能使用 MinGW (GCC) 搭配 GNUstep 或 ObjFW 做為 Objective-C 的物件庫。

一般來說,會優先使用 GNUstep,因為該物件庫在 API 上相容於 Cocoa,學習曲線會比較平滑。而且 GNUstep 可和 Cocoa 交互參考,可讀的線上資料會比較多。本文會介紹如何建置 GNUstep 開發環境。

使用 GNUstep 官方安裝程式

比較簡單的方式是到 GNUstep 的官網下載給 Windows 用的安裝程式。諘依序安裝以下三個開發工具:

  • GNUstep MSYS System (MinGW + MSYS 環境)
  • GNUstep Core (函式庫)
  • GNUstep Devel (標頭檔和開發工具)

這時候會得到 MinGW (GCC) 加上 GNUstep 物件庫。但 GNUstep 官方的 Windows 安裝程式的版本滯後,會得到陳舊的開發環境。如果願意花點時間,建議自行從 Msys2 開發環境編譯 GNUstep。本文的後半部會說明自行編譯 GNUstep 的流程。

支援 Objective-C 的編輯器

對於 Objective-C 來說,Windows 反而是小眾環境,所以沒什麼大型 IDE 可用。以下是支援 Objective-C 的編輯器:

前兩者是免費的編輯器,對 Objective-C 有基本的支援。後者則是商業軟體,可免費試用 30 天,有興趣的讀者可自行選購。

自行編譯 GNUstep 原始碼

在 Unix 上編譯軟體算是家常便飯,但 Windows 使用者反而較少自行編譯軟體,所以筆者在這裡說明編譯 GNUstep 的流程。

前置作業

請讀者先安裝 MSYS2。若對 MSYS2 不熟悉,可參考這篇文章

本小節的操作會在 Msys 終端機。請將工作目錄切換到家目錄:

$ cd

先安裝基本的工具:

$ pacman -S wget git

拷貝相關工具命令稿:

$ git clone https://github.com/gnustep/tools-scripts.git

這個專案有幾個安裝過程會用到的命令稿,就不需要逐一打指令。

下載以下四個開發工具的穩定版本壓縮檔:

$ wget --no-check-certificate -c https://github.com/gnustep/tools-make/releases/download/make-2_9_1/gnustep-make-2.9.1.tar.gz
$ wget --no-check-certificate -c https://github.com/gnustep/libs-base/releases/download/base-1_29_0/gnustep-base-1.29.0.tar.gz
$ wget --no-check-certificate -c https://github.com/gnustep/libs-gui/releases/download/gui-0_30_0/gnustep-gui-0.30.0.tar.gz
$ wget --no-check-certificate -c https://github.com/gnustep/libs-back/releases/download/back-0_30_0/gnustep-back-0.30.0.tar.gz

雖然也可以直接拷貝 GNUstep 的 master 分支的原始碼來用。但該分支的原始碼比較不穩定,不建議使用。如果對 GNUstep 很有愛,想要幫 GNUstep 測試和除錯,可以自行玩玩看。

這裡的軟體版本號是實際到各個專案去查閱而得的,每隔一陣子就會變動。請不要死背這個版本號。

雖然筆者先前發給 GNUstep 官方團隊的 PR 被拒絕,但 GNUstep 過一陣子後的確修好了 GNUstep 在 MSYS2 上的臭蟲。由此可知,即使 PR 被拒絕,仍然可以對自由軟體間接做出貢獻。

然後將各個專案重新整理名稱,這是為了和 build-mingw64_nt (編譯用命令稿) 內的目錄名稱一致:

$ tar xf gnustep-make-2.9.1.tar.gz
$ mv gnustep-make-2.9.1 tools-make

$ tar xf gnustep-base-1.29.0.tar.gz
$ mv gnustep-base-1.29.0 libs-base

$ tar xf gnustep-gui-0.30.0.tar.gz
$ mv gnustep-gui-0.30.0 libs-gui

$ tar xf gnustep-back-0.30.0.tar.gz
$ mv gnustep-back-0.30.0 libs-back

安裝 GNUstep 相依性

切換到 Msys 終端機,執行以下命令稿:

$ ./tools-scripts/install-dependencies-mingw64_nt

安裝套件的過程中可能需要手動確認幾次,請自行完成。

編譯 GNUstep

切換到 MinGW64 終端機,執行以下命令稿:

$ ./tools-scripts/build-mingw64_nt

這時候會從頭到尾編譯整個 GNUstep。如果順利的話,就可以得到 GNUstep,會安裝在 MinGW64 環境下的 /usr/GNUstep 中。由於編譯時間比較久,可以喝杯咖啡休息一下。

但是,不一定每次編譯都會成功。目前該命令稿沒有檢查錯誤的機制,所以要自行檢查錯誤訊息。可以用 tee(1) 把錯誤訊息存下來再慢慢看。

(選擇性) 錯誤排除

編譯 GNUstep 時,最困難的就是錯誤排除。隨著 MSYS2 環境,每次編譯時有可能有不同的錯誤出現,要有自行勤找資料的精神來除錯。

例如,最新版本的 libxml2 造成 libs-base 的編譯錯誤:

GSXML.m: In function 'getEntityDefault':
GSXML.m:2674:22: error: 'xmlEntity' {aka 'struct _xmlEntity'} has no member named 'checked'
 2674 |               if (ret->checked == 0)
      |                      ^~
GSXML.m:2676:22: error: 'xmlEntity' {aka 'struct _xmlEntity'} has no member named 'checked'
 2676 |                   ret->checked = 1;
      |                      ^~
GSXML.m: In function 'hasInternalSubsetFunction':
GSXML.m:2968:7: warning: '__htmlDefaultSAXHandler' is deprecated [-Wdeprecated-declarations]
 2968 |       has = TREEFUN(hasInternalSubset, (ctx));
      |       ^~~
In file included from C:/tools/msys64/mingw64/include/libxml2/libxml/threads.h:35,
                 from C:/tools/msys64/mingw64/include/libxml2/libxml/xmlmemory.h:222,
                 from C:/tools/msys64/mingw64/include/libxml2/libxml/tree.h:1310,
                 from GSXML.m:82:

解法方式是用 wget(1) 下載舊版的 libxml2:

$ wget -c https://repo.msys2.org/mingw/mingw64/mingw-w64-x86_64-libxml2-2.10.4-1-any.pkg.tar.zst

將 libxml2 手動降級:

$ pacman -U mingw-w64-x86_64-libxml2-2.10.4-1-any.pkg.tar.zst

GNUstep 也是要依賴底層的 C 函式庫才能運作的,錯誤往往來自於兩者的程式碼不匹配。實務上不可能改 GNUstep 的程式碼。把相關的 C 函式庫降級倒是可以嘗試的作法。

讀者不一定會碰到相同的錯誤。還是得培養自己找尋資料、解決問題的能力。

自動載入 GNUstep 開發環境

在 MinGW 終端機下執行以下命令稿:

$ ./tools-scripts/setup-mingw64_nt

以後就可以用 GNUstep Make 編譯 Objective-C 程式。我們會在後文介紹 GNUstep Make 的使用方式。

編譯第一個 Objective-C 程式

用編輯器建立空白的 hello.m 文字檔案,加入以下內容:

#import <Foundation/Foundation.h>

#define PRINT(FORMAT, ...) \
    fprintf(stdout, "%s\n", \
        [[NSString stringWithFormat:FORMAT, ##__VA_ARGS__] UTF8String]);

int main(void)
{
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
    if (!pool)
        return 1;

    PRINT(@"Hello World");

    [pool release];

    return 0;
}

我們的目的是測試開發環境,不說明程式碼。

編譯並執行此範例程式:

$ gcc -o hello hello.m -lobjc -lgnustep-base -I /usr/GNUstep/System/Library/Headers -L /usr/GNUstep/System/Library/Libraries -fconstant-string-class=NSConstantString
$ ./hello.exe
Hello World

GNUstep 視為獨立的系統,不位於 GCC 的標準位置上,所以要加許多額外的參數。日後用 GNUstep Make 就可以改善這個情形。

無法順利執行 GNUstep 執行檔

請將 /usr/GNUstep/System/Tools 加到 PATH 變數中。若讀者的系統路徑和此路徑相異,請自行更改。

在 Windows 原生環境執行 GNUstep 程式

若要在 Windows 原生環境下執行編譯好的 GNUstep 程式,要附帶額外的動態函式庫 (DLL 檔)。以筆者的系統為例,可到 C:\msys64\usr\GNUstep\System\Tools 底下找所需的 DLL 檔。除此之外,在 MSYS2 的執行檔路徑 C:\msys64\mingw64\bin 等處也可能會有所需的 DLL。

因為 GNUstep 相依蠻多函式庫的,要手動逐一拷貝有點費工。筆者寫了個 shell 命令稿 deploy2win,可以簡化拷貝 DLL 的動作。有需要的讀者可以參考一下。

關於作者

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

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