前言
我們先前在建置 Objective-C 開發環境時,曾經用 GCC 或 Clang 直接在命令列編譯 Objective-C 程式。如果在 Mac 上使用 Xcode 等 IDE 寫 Objective-C 程式的話,已經有 IDE 輔助了,不太需要輸入指令。相對來說,在 GNUstep 開發環境上編譯 Objective-C 的指令偏長,不太可能每次都重新輪入。
著眼於此議題,GNUstep 開發團隊以 GNU Make 為基礎寫了 GNUstep Make,這是一個以 Makefile 為基礎的小型框架,用來簡化編譯 Objective-C 程式的任務。
GNUstep Make 是以 GNU Make 為基礎所開發的命令列工具。對於習慣用 IDE 的讀者來說,可能對這類手刻工具不太能適應。基本上 Make 是將編譯流程自動化 (build automation) 的軟體,只是 Make 是單獨存在的工具,不綁定任何 IDE。筆者先前在這裡撰寫一些 GNU Make 相關的文章,有興趣的讀者可自行前往閱讀。
在本文中,我們以一個 Hello World 程式來說明如何使用 GNUstep Make。如果是在 GNU/Linux 等類 Unix 系統下,以系統的終端機環境來操作即可。如果在 Windows 下,則要搭配 MSYS 附帶的終端機環境,像是 GNUstep 安裝程式附的 MSYS 終端機或 MSYS2 的 MinGW (32 bit 或 64 bit) 終端機。
建立專案架構
先建立 hello 目錄,並將工作目錄移到該目錄下:
$ mkdir hello
$ cd hello
我們的專案程式很少,故採扁平式架構,將所有檔案放在專案的根目錄中。假定我們的主程式是 main.m ,我們想建立的程式是 hello (在 Windows 上是 hello.exe)。建立 GNUmakefile 檔案,並寫入以下內容:
# Include the common variables defined by the Makefile Package
include $(GNUSTEP_MAKEFILES)/common.make
# Build a simple Objective-C program
TOOL_NAME = hello
# The Objective-C files to compile
hello_OBJC_FILES = main.m
-include GNUmakefile.preamble
# Include in the rules for making GNUstep command-line programs
include $(GNUSTEP_MAKEFILES)/tool.make
-include GNUmakefile.postamble
這個 GNUmakefile 是整個專案的重點,我們來看一下如何撰寫這個設定檔。
GNUstep Make 入門
GNUstep Make 的工作原理是預寫好一些 Make 設定檔,我們要使用時使用 include
語法引入這些設定檔即可。由於引入的方式有一定的順序,使用起來比較像是框架而非函式庫。
一開始會引入 $(GNUSTEP_MAKEFILES)/common.make ,這是所有 GNUstep 專案都會用到的設定。細心的讀者可能會懷疑變數 GNUSTEP_MAKEFILES 是從那裡來的?這要藉由吃入環境變數來導入這個變數,下文會談及如何操作。
TOOL_NAME 代表這個專案編譯出來的執行檔的名稱,在本例中執行檔的名稱是 hello 。執行檔的名稱會影響到一部分設定檔的程式碼,需注意。
hello_OBJC_FILES 代表這個專案會引入的原始碼檔案,在本例中我們只引入一個檔案 main.m 。要注意這裡的 hello 是按照 TOOL_NAME 的名稱來寫的,筆者無法確認當初為何要這樣設計,不過這是 GNUstep Make 的內隱規則之一。
由於我們的專案類型是命令列工具,故我們引入 $(GNUSTEP_MAKEFILES)/tool.make 。隨著專案類型不同,實際引入的檔案也會相異。
至於 GNUmakefile.preamble 和 GNUmakefile.postamble 則不是必要的,視需求再引入即可。我們在 include
語句前加上負號 -
,代表這個專案沒有引入這兩個選擇性的設定檔。如果要引入,將負號 -
移除即可。
建立 Hello World 程式
接著,以編輯器或 IDE 建立 main.m 檔案:
#import <Foundation/Foundation.h>
#define PRINT(FORMAT, ...) \
fprintf(stderr, "%s\n", \
[[NSString stringWithFormat:FORMAT, ##__VA_ARGS__] UTF8String]);
int main (int argc, const char * argv[])
{
@autoreleasepool {
PRINT(@"Hello World");
}
return 0;
}
我們這個檔案是 Hello World 程式的變體,好處是不會印出時間戳記。
引入 GNUstep Make 設定檔
接著,要讀入 GNUstep Make 中的 GNUstep.sh 命令稿,這個檔案會導入相關的環境變數等狀態。以 Windows 下的 MinGW 64 bit 為例:
$ source /C/GNUstep/GNUstep/System/Library/Makefiles/GNUstep.sh
如果在 GNU/Linux 等類 Unix 系統內,可參考以下指令:
$ . /usr/GNUstep/System/Library/Makefiles/GNUstep.sh
不同系統內的 GNUstep 路徑可能相異,需隨實際情形修改。
如果在 macOS 內,可參考以下指令:
$ . /usr/local/Cellar/gnustep-make/2.7.0/Library/GNUstep/Makefiles/GNUstep.sh
要注意這個指令會隨 gnustep-make 套件的版本號改變,不要死背這個路徑。此外,即使我們用 GNUstep Make 管理專案,在 macOS 內編譯時不是用 GNUstep,而是用 Cocoa,有可能出現程式碼正確但不相容的情形。
如果很常編譯 Objective-C 程式的話,可以自行把這行指令放入個人設定檔,像是 ~/.bash_profile 或 ~/.zshrc 等。
透過吃入這個命令稿,就會引入 GNUstep Make 應有的環璄變數。
編譯 Hello World 專案
最後,輸入 make
指令即可編譯此程式:
$ make
This is gnustep-make 2.6.8. Type 'make print-gnustep-make-help' for help.
Running in gnustep-make version 2 strict mode.
Making all for tool hello...
Compiling file main.m ...
Linking tool hello ...
編譯好的程式會放在 obj 子目錄中:
$ obj/hello
Hello World
編譯其他類型的專案
在本範例中,我們以初學者常見的命令列程式為例來說明如何使用 GNUstep Make 編譯程式。但 GNUstep Make 能夠編譯的專案類型很多,不限於本文所介紹的類型。如果想編譯其他類型的程式,可參考 GNUstep Make 的官方手冊。
結語
說實在的,Makefile 還是比 IDE 內建的專案管理功能來得麻煩一些,為什麼 GNUstep 要這樣設計專案管理工具呢?筆者以為,因為 GNU Make 不會被特定 IDE 綁住,可以在純終端機環境下工作,GNU Make 本身又是類 Unix 系統上常見的專案管理工具,直接使用 Makefile 而不是重新發明新的工具,相對來說是最符合經濟效益的。