[Objective-C] 程式設計教學:在 Windows 平台上以 GNUstep 建立開發 Objective-C 程式的開發環境

【分享本文】
Facebook Twitter LinkedIn LINE Skype EverNote GMail Yahoo Email

    前言

    蘋果公司 (Apple Inc.) 沒有將 Cocoa 框架放到其他平台上,日後應該也不會放。若想要在 Windows 上練習 Objective-C,可以用 GNUstep,這是一個 Cocoa 的自由軟體再製版本。GNUstep 有提供 Windows 版本的安裝檔 (installer),但也可以自行從原始碼來編譯;我們兩種方法都會介紹,並且比較其差異。

    一般的方法,使用現有的 GNUstep 安裝程式

    GNUstep 己經預先包好給 Windows 用的安裝檔,下載後即可安裝使用。到 GNUStep for Windows 網站,依序下載並安裝以下套件:

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

    其他套件是選擇性的,可裝可不裝。

    預設情形下,這些套件會安裝到 C:\GNUstep 目錄中。如果終端機無法偵測到 GNUstep 內附的 GCC,可以將 C:\GNUstep\bin 加入 PATH 環境變數中;但實際編譯 Objective-C 程式時,會用到 GNUstep 安裝程式附的 MSYS 環境,我們將於後文說明。

    選擇適用於 GNUstep 的編輯器 (editor) 或整合式開發環境 (IDE)

    由於 Objective-C 的主流平台是 Mac,在 Windows 並沒有什麼大型 IDE 可用。AtomVSCode 對 Objective-C 有語法高亮 (syntax highlighting) 等基本支援。EditRocket 是一個商業編輯器,可試用 30 天後再買,有興趣的讀者可自行試用。

    撰寫 GNUstep 版本的 Hello World 程式

    Hello World 程式是用來確認開發環境可正常運作,一開始不用急著了解程式的語義。本節建立一個 Objective-C 版本的 Hello World 程式。

    用編輯器或 IDE 建立新的檔案 *hello.m*,加入以下內容:

    #import <Foundation/Foundation.h>
    
    int main (int argc, const char * argv[])
    {
        @autoreleasepool {
            NSLog(@"Hello World");
        }
    
        return 0;
    }
    

    現階段 Hello World 程式的用途是確認開發環境可正常運作,暫時不用糾結在語法細節上,我們會於後文介紹其語法。

    撰寫 hello.m 後,使用以下指令來編譯及執行程式:

    C:\path\to\file> gcc -o hello hello.m -IC:\GNUstep\GNUstep\System\Library\Headers -LC:\GNUstep\GNUstep\System\Library\Libraries -lobjc -lgnustep-base -fconstant-string-class=NSConstantString
    C:\path\to\file> .\hello.exe
    2019-03-11 13:37:08.184 Main[10:10] Hello World
    

    由於 GNUstep 不是位於 MinGW 的標準路徑上,所以我們要以 -I 指定頭文字檔 (header) 的位置,以及 -L 指定二進位函式庫的位置。

    讀者一定會覺得這個指令很長,每次都要逐字打相當繁瑣。GNUstep 提供 GNUstep Make,這是一套以 Makefile 為基礎的專案設定框架,用來簡化編譯 Objective-C 程式的過程,我們將於後文介紹這個軟體。

    另外,由於 NSLog 是用於紀錄檔 (log) 的函式,故會跑時間戳記 (time stamp) 出來,和傳統上 C 語言的 printf 函式略有不同。

    替代的方法,自行從原始碼編譯及安裝 GNUstep

    GNUstep 內附的 MinGW + MSYS 開發環境比較舊,不若新版的 MSYS2 來得好用;比起原本的 MinGW,MSYS2 引入源自於 Arch Linux 的 pacman 套件管理程式以及許多自由軟體在 Windows 上的移植品,讓 C (或 C++) 的開發更加方便。我們可以利用 MSYS2 內附的 GCC 來編譯 GNUstep,藉此建立 GNUstep 開發環境。

    Windows 使用者多習慣使用預編好的安裝程式來安裝軟體,甚少自行編譯軟體;但對類 Unix 系統使用者來說,編譯軟體是常做的動作,而且類 Unix 系統對這方面支援比較好一些。筆者使用的主機規格是 Intel i7-8550U 筆電版 + 8GB 記憶體,運行 Windows 10 家用版;在這樣的環境下,完整編譯一次約半小時至一小時左右。編譯中大型軟體通常無法一步到位,還是要有一些試誤精神,筆者將安裝過程紀錄下來,希望可以減少讀者試誤的時間。

    MSYS2 有三個終端機環境,先開啟 MSYS 環境。建立一個目錄 (directory),因為我們會抓數個軟體原始碼。參考以下指令:

    $ mkdir gnustep
    $ cd gnustep
    

    接下來的操作,都會在此工作目錄下。

    用 Git 下載以下專案:

    $ git clone https://github.com/gnustep/tools-make
    $ git clone https://github.com/cwchentw/libs-base.git
    $ git clone https://github.com/gnustep/libs-gui
    $ git clone https://github.com/gnustep/libs-back
    $ git clone https://github.com/cwchentw/tools-scripts.git
    

    在這五個專案中,前四個專案是 GNUstep 實際的函式庫和工具軟體,而第五個專案則是一些跑編譯及安裝 GNUstep 的命令稿。眼尖的讀者會發現其中幾個專案的來源不是 GNUstep 上游,而是筆者 fork 出來的版本,因為這些 fork 出來的專案是筆者實際修改及安裝的版本,目前還沒有被 GNUstep 開發團隊接受。

    tools-scripts 中有針對 32 位元和 64 位元的 MSYS2 所用的安裝相依套件腳本,讀者視自己需求擇一使用即可。這裡以 64 位元的腳本為例:

    $ ./tools-scripts/install-dependencies-msys2-64bit
    

    這個 (修改過的) 腳本會自動更新 pacman 套件資料庫並安裝相依的套件,這個步驟應該不會太久。

    在實際編譯 GNUstep 前,要先 hack 一下 MSYS2 的系統頭文字檔,這是筆者試誤出來的筆記。

    C:\msys64\mingw64\x86_64-w64-mingw32\include\winnetwk.h 的第 124 行第 80 個字元似乎有 bug,應將 BOOL 改為 WINBOOL

    C:\msys64\mingw64\x86_64-w64-mingw32\include\shlwapi.h 在使用 Objective-C 時,無法順利編譯。暫時的 workaround 是在該頭文件的前面加入以下內容:

    #ifndef _INC_SHLWAPI
    #define _INC_SHLWAPI
    
    #include <_mingw_unicode.h>
    #include <winapifamily.h>
    
    /* Some kludge for Obj-C.
       For Obj-C the 'interface' is a keyword, but interface is used
       in midl-code too.  To resolve this conflict for at least the
       main windows API header, we define it here temporary.  */
    #ifdef __OBJC__
    #pragma push_macro("interface")
    #undef interface
    #define interface struct
    #endif
    

    為了怕汙染整個命名空間,要在尾端加入以下內容:

    /* Restore old value of interface for Obj-C.  See above.  */
    #ifdef __OBJC__
    #pragma pop_macro("interface")
    #endif
    
    #endif
    

    修改完這兩處後,應該可以順利編譯 GNUstep 了 (希望如此)。

    關掉原本的 MSYS 終端機,視自己的需求重開一個 MinGW 32 bit 或 MinGW 64 bit 終端機,以本文來說,就是 MinGW 64 bit 終端機。在這兩個終端機中編譯的程式是 Windows 系統原生程式,而且這兩個終端機也可以各自和 Windows 系統目錄互通,其工作原理是在這個環境中編譯好的程式丟到 Windows 目錄下使用。

    開好 MinGW 64 終端機後,重新回到 gnustep 目錄的根目錄,輸入以下指令:

    $ ./tools-scripts/windows-build
    

    如果順利的話,這個指令會依序將 GNUstep 的四個子專案編譯並安裝到 C:\GNUstep 目錄上。我們先前有提過,在筆者的筆電上,整個編譯過程約半小時至一小時,所以可以先去做其他的事。編譯 GNUstep 時把主機上除了 MSYS2 以外的應用程式都關掉的話,編譯速度會快得多,因為有足夠的記憶體,不會動到硬碟快取。

    如果發生錯誤的話,就不要一直重試了;錯誤原因沒有排除的話,仍然會發生錯誤,不會自動修好。這時候建議將 tools-scripts/windows-build 內的指令逐一貼到終端機上,慢慢看是那個環節發生了問題。如果錯誤成因在 GNUstep 子專案本身的話,可以將該專案 fork 一份,進行修改後發出 PR (pull request),若被 GNUstep 開發團隊接受的話,代表讀者也替 GNUstep 專案盡了一些心力。

    最後說明一下,由於 MSYS2 設計成使用動態連結函式庫 (DLL),筆者目前沒有找到可以透過 MSYS2 編譯靜態執行檔的方法。透過 MSYS2 編譯出來的 GNUstep 所編譯的 Objective-C 程式同樣也是透過動態連結來連結 GNUstep 函式庫。這時候可以到 C:\GNUstep\GNUstep\System\Tools 找所需的 DLL;將 DLL 和執行檔放在同一個目錄內即可。

    結語

    在本文中,我們為讀者介紹了兩種在 Windows 平台上建立 GNUstep 開發環境的方式。使用 GNUstep 安裝程式很簡單,但其內附的 MinGW 和 MSYS 環境相對舊;自行從原始碼編譯和安裝 GNUstep 就可以搭配 (較新的) MSYS2 開發環境,但是編譯出來的執行檔使用動態連結。讀者應該權衡兩種方式的利弊得失,選擇適合自己的方式。

    在試用 Windows 平台的 GNUstep 一段時間後,會發現即使正確地編譯和安裝 GNUstep 開發環境,有時候仍然會碰到一些小問題;有些問題可以自行排除,有些問題到最後仍然無法解決。有時候把同樣的程式碼放到 GNU/Linux 平台的 GNUstep 環境去跑,發現程式就可以正常執行了。或許對自由軟體基金會來說,Windows 支援不是該專案首要的目標。如果讀者時常碰到一些非程式碼本身的問題,或許可以轉用 Paiza 雲端開發平台或其他以 GNU/Linux 為基礎的開發平台,以減少不必要的問題。

    【分享本文】
    Facebook Twitter LinkedIn LINE Skype EverNote GMail Yahoo Email
    【追蹤新文章】
    Facebook Twitter Plurk
    標籤: OBJECTIVE-C