Solution Guide for Migrating UNIX Build Environments

第 4 章 - 遷移建置系統

本页内容

簡介與目標
開發解決方案元件
建置或遷移系統測試
整合解決方案

簡介與目標

在遷移專案中,「開發」階段是指小組建置解決方案元件 (程式碼、基礎結構以及文件) 的時間。一般而言,這項工作包括修改現有的程式碼,讓它可以在新環境中運作。撰寫新的程式碼時,通常會有某些方面的原始元件保留不變,例如公開的 API,或是特定的元件行為。在這方面的現有程式碼修改和新程式碼開發,都會視為是遷移活動。因此,本指南將 MSF「開發」階段視為是「遷移」階段。

雖然開發工作是這個階段和本指南的重點,但是所有的小組角色都要主動參與交付成果的建置和測試。例如,「使用者經驗角色」會依需要準備教育訓練的資料。部分的開發工作可能會繼續進入「穩定」階段,以便回應測試。

這個階段是以「範圍完成階段性目標」做為正式的結束。在這個主要的階段性目標,小組會獲得贊助者及關鍵參與者的正式核准,而所有的解決方案要素以及解決方案功能都是根據「規劃」階段期間所同意的功能規格來建置及完成的。

下面的清單列出了這個階段所進行的主要工作:

  • 開發解決方案元件。

  • 建置或遷移要在「穩定」階段期間使用的系統測試。

  • 在一系列的每日建置中以累加方式建置解決方案。

  • 測試解決方案 (執行程式碼元件、資料庫、安全性測試、程式碼檢閱以及驗證系統測試)。

開發解決方案元件

對於建置系統遷移而言,「開發解決方案元件」意指確定並移植構成建置系統的個別解決方案元件。建置流程通常會有許多不同的元件,包括在建置流程期間編譯及執行的 makefile、如 sedawk 等應用程式、shell 指令碼以及個別原始程式檔。您必須確定能夠維護重要的功能 — 也就是說,擁有模組、指令碼和工具來執行建置系統必須執行的工作。在某些情況下,您會開發能在 Windows 環境中執行特定功能的新檔案或指令碼。當系統穩定後,再讓所有的元件共同運作得更加理想,以完成目標。

在實際執行 UNIX 建置流程遷移時,您必須處理從擁有一組特定工具的環境移到另一個擁有相同工具、但不同實作方式的環境時,所引起的技術和相容性的問題。以下各節說明了從 UNIX (特別是 Solaris) 遷移到適用於 Windows 的三種不同 UNIX 環境產品的其中一種時,可能會發生的許多問題。

開發解決方案元件

Windows 和 UNIX 檔案系統除了路徑分隔符號的方向之外,還有許多不同之處。它們各自有不允許的字元集,而檔案和路徑名稱的最大限制也不同。Windows 允許額外裝置的規格 (如 A:、C: 等等),而 UNIX 則將裝置的位置隱藏在單一根目錄檔案樹狀結構中。

與 FAT 檔案系統相比,Windows 上的 NTFS 檔案系統與 UNIX 在語意和行為方面大都類似,因此遷移中需要針對檔案系統所做的變更會減至最少。NTFS 檔案系統支援許多的 UNIX 功能,例如區分大小寫的檔案和目錄名稱、目錄周遊權限、使用者擁有者和群組擁有者之間劃分的檔案擁有權、檔案和目錄的三種存取時間、硬式連結 (hard link) 以及檔案存取權限的機制。

區分大小寫

在 UNIX 上,檔案系統會區分大小寫。在 Windows 上,檔案系統可以保留大小寫區別或區分大小寫。保留大小寫區別的檔案系統,會允許將檔案命名為 Makefile 或 makefile,保留名稱的大小寫,但是檔案系統不允許兩者同時存在:它不會允許兩個檔案具有相同的名稱,只有大小寫不同。這種行為會視檔案系統和用於存取檔案系統的子系統而定。利用依存於 Interix 子系統的公用程式,FAT32 和 SMB 網路裝載的檔案系統是保留大小寫區別的;本機 NTFS 檔案系統則是區分大小寫的。

如果您是使用 Win32 子系統,則所有的檔案系統都是保留大小寫區別的。在尋找檔案時,它們不會使用大小寫做為條件。換言之,Makefile 或 makefile 其中之一可以存在於相同的目錄中,但無法兩種同時並存,而且可以使用含有任何大小寫字母的名稱來存取其中一個檔案,例如 MAKEFILE、Makefile 或 makefile。

如果您決定要使用 Win32 UNIX 工具組,例如 MKS Toolkit,或是如果希望建置流程可以在網路裝載的檔案系統上運作,您的遷移流程會有一部分是確保相同目錄中的所有檔案不論大小寫,都具有不同的名稱,例如 Makefile 和 makefile。

路徑名稱語法

Windows 上的檔案名稱語法會依您所選擇的 UNIX 環境而有所不同。

在 Interix UNIX 環境中,只支援 UNIX 檔案名稱語法。Interix 環境提供了含有單一根目錄的檔案系統檢視,其中根目錄便是安裝 Interix 的目錄。透過磁碟機代號存取不同的磁碟,可以透過特殊目錄 /dev/fs (例如 /dev/fs/C/dir/file) 來進行。網路共用也可以透過特殊目錄 /net (例如 /net/server/share/dir/file) 來使用。

Cygwin 也使用自己的裝載點和裝載表的實作方式,提供單一根目錄檔案系統的假象。MKS 環境不支援單一根目錄檔案系統的概念,意指在遷移至 MKS 環境時,UNIX 絕對檔案名稱可能會是個問題。要避免這個問題的其中一種方法,就是建立已知 UNIX 目錄名稱的 symlink,然後讓它們指向 MKS 安裝中的實際位置。例如,可以建立 /bin 的 symlink 如下:

ln –s $ROOTDIR/mksnt  /bin

Cygwin 和 MKS 環境都支援 Windows 檔案名稱語法,包括磁碟機代號 (c:/dir/file) 和 UNC 路徑名稱慣例 (//server/share/dir/file)。這些環境也支援使用正斜線 (/) 和反斜線 (\) 做為分隔符號。

保留的檔案名稱

Win32 子系統中會保留特定的檔案名稱 (因此,保留適用於 MKS 和 Cygwin 環境)。下列保留的裝置名稱不得用來做為檔案名稱: CON、PRN、AUX、CLOCK$、NUL、COM1、COM2、COM3、COM4、COM5、COM6、COM7、COM8、COM9、LPT1、LPT2、LPT3、LPT4、LPT5、LPT6、LPT7、LPT8 和 LPT9。您必須避免使用這些名稱做為檔案名稱尾碼或檔案名稱本身,因此必須避免使用如 aux.c、file.aux 或 NUL.txt 等名稱。

Interix 子系統沒有這種保留或限制。如果您決定要使用以 Interix 工具為基礎的建置環境,並利用這些保留的檔案名稱,請記住這些名稱將無法從 Win32 環境或 Win32 公用程式來存取。

檔案名稱字元集限制

在 Windows 上對於檔案和路徑名稱所允許的字元集的限制,比在 UNIX 上更為嚴格。表 4.1 中提供了不得在 NTFS 檔案系統上的 Windows 檔案和路徑名稱中使用的字元。

表 4.1 Windows 檔案和路徑名稱的限制字元

字元

名稱

^A .. ^_

ASCII 控制字元 (編碼值 1 到 31)

>

大於

<

小於

*

星號

:

冒號

"

雙引號

?

問號

\

反斜線

|

直線

/

斜線

Interix 子系統為這些限制提供了一些替代字元,表 4.2 中列出了 Interix 允許的其他字元。

表 4.2 Interix 檔案和路徑名稱中允許的其他字元

字元

名稱

^A .. ^_

ASCII 控制字元 (編碼值 1 到 31)

*

星號

:

冒號

?

問號

|

直線

由於 NTFS 檔案系統是使用 UNICODE 編碼來儲存檔案名稱,因此 Interix 可以偵測到這些特殊字元,並對應至特別保留的 UNICODE 編碼。只有 Interix 環境支援這些對應。Cygwin 和 MKS 環境 (以 Win32 子系統為基礎的公用程式) 無法顯示這些字元。

冒號字元

Windows 檔案系統不允許檔案名稱中有冒號字元,除非是要參照代表裝載的檔案系統之磁碟機代號,例如 c:/tmp。 有兩個與冒號字元有關的議題:

  • 當原始的 UNIX 建置流程中所使用的檔案名稱含有內嵌冒號字元時,例如「:rofix」。

  • 當使用含有冒號字元的有效 Windows 檔案名稱 (例如 c:/tmp) 做為 makefile 中的目標時。

當使用 Interix 環境時,第一個議題不會是問題,因為 Interix 子系統的檔案系統語意和 UNIX 語意幾乎是完全一樣的:冒號字元可以用於 Interix 為主的 make 公用程式 (/bin/make、gmake)。Interix 子系統會自動對應冒號字元和 Windows 檔案系統接受的特殊字元。第二個議題並不適用,因為 Interix 無法辨識 Windows 路徑名稱 (例如 c:/tmp)。

當使用 Cygwin 環境時,第一個議題會是問題,因為 Windows 環境不允許檔案名稱中有冒號字元。第二個議題不是問題;目標可以是含有磁碟機代號規範的絕對路徑名稱。

當使用 MKS Toolkit 環境時,第一個議題會是問題,因為 Windows 不允許檔案名稱中有冒號字元。第二個議題也會是問題,由於 MKS make 剖析尋找目標的 makefile 的方式所致。如果目標檔案名稱含有冒號字元,冒號字元便會被視為是目標和先決條件之間的分隔符號。若要解決這個問題,您必須將目標名稱加上引號。例如:

"c:/tmp/a.exe" :  "c:/tmp/main.obj"

句號字元

Win32 子系統無法處理以句號 (.) 結尾的檔案。使用 MKS Toolkit 或 Cygwin 環境時,必須重新命名這些類型的檔案。如果您使用的是 Interix 環境,則不必進行變更。

Symlink

Windows 檔案系統支援符號連結的方式和 UNIX 系統定義 symlink 的方式不同。然而,Windows 提供了「連接點」。連接點只能在本機 NTFS 檔案系統上使用,而且只能和目錄、而非檔案一起使用。您可以使用連接點來接合目標資料夾和另一個 NTFS 資料夾,或在 NTFS 連接點上裝載磁碟區。程式不會意識到連接點的存在。

MKS Toolkit ln 命令是使用 NTFS 連接點來執行符號連結。指定 -s 選項只能和目錄一起使用。如果這個選項和檔案一起使用,將會顯示錯誤。

Interix 子系統已執行自己的特殊機制,以便在任何類型的檔案系統 (NTFS 或 FAT、本機或網路) 上提供 UNIX symlink。symlink 檔案是特別的資料檔案,只有在 Interix 環境中才能識別為 symlink。Win32 應用程式無法識別它們。

Cygwin ln 命令也支援建立 symlink 的 -s 選項。它是使用 Window 的「捷徑」機制,其中原始程式檔是具有 .lnk 尾碼的特殊資料檔案。只有可識別這個特殊 .lnk 尾碼的 Win32 應用程式 (包括如檔案總管等應用程式) 才會將這些檔案視為符號連結。這些符號連結可以在任何類型的檔案系統上建立。

硬式連結

硬式連結只是目錄項目的另一種說法,是名稱與檔案產生關聯的方式。NTFS 檔案系統支援相同檔案有多個目錄項目 (或多個名稱)。用於建立這些連結的 UNIX 公用程式稱為 ln,三種 UNIX 環境可攜性產品都支援這項功能。

權限和安全性

Windows 和 UNIX 上的權限和安全性模型是不同的。Windows 和 UNIX 作業系統會針對使用者識別和資源存取控制使用本質不同的機制。在 UNIX 中,有非常簡單的使用者和群組 ID,是使用 32 位元數值來代表的;Windows 則使用使用者和群組安全性識別碼 (SID),通常是相當大的可變長度數值 - 字串格式中的一般 SID 可能會是「S-1-5-21-1431262831-1455604309-1834353910-1000」。在檔案保護方面,UNIX 會使用 9 個檔案權限位元,擁有者、群組和其他權限各 3 個位元;而 Windows 則是使用「判別存取控制清單」(Discretionary Access Control list,DACL),支援 14 種以上的不同存取權,每一種都可以指派給個別使用者或群組。

Windows 上的各個 UNIX 環境都會對應 Windows 上不同典型的 UNIX。MKS 公用程式提供了最簡化的支援,幾乎沒有精確的使用者和群組 ID 的支援,也幾乎沒有檔案權限和 DACL 之間的對應。Interix 環境提供了最確實且精準的對應,本機和通用網域帳戶都有專用的 ID,UNIX 檔案權限位元和 DACL 之間也有精確的轉譯機制。Cygwin 環境執行的對應和 Interix 的方法類似,不過有一些 Cygwin 沒有處理的功能。

在建置流程中,應該要幾乎沒有安全性的問題才行。chmod(1) 或 install(1) 公用程式可能會發生一些問題;您可以檢查看看是否符合流程的安全性需要。例如,如果應用程式需要在特殊的安全性內容中執行 (例如 setuid 應用程式),並且是使用特殊權限位元來安裝,您就需要決定 Windows 上是否仍然需要這種類型的安裝。另一個例子是,當建置流程本身自動建立必須執行的 shell 指令碼時。若要確保指令碼是可執行的,建置流程可以叫用 chmod(1) 公用程式。在這種情況中,簡單的

chmod +x scriptfile

可能就必須要存在於指令碼或 makefile 中。所有 Windows 上的 UNIX 環境均支援這個命令。

install 公用程式是建置系統中常用的公用程式。它用於將檔案或執行檔移動或複製到常用的安裝位置,並設定檔案的權限以及擁有者和群組識別 (使用 install 時,安全性和權限問題都很常見)。所有 Windows 上的 UNIX 環境均支援這個命令。它是 Interix 和 Cygwin 的標準散發的一部分。MKS 版本位於散發程式媒體的範例/目錄中。

遠端檔案系統

標準的 UNIX 網路檔案系統是「網路檔案系統」(NFS)。標準的 Windows 網路檔案系統是 Common Internet File System (CIFS),它是以「伺服器訊息區」(SMB) 為基礎。

有 Windows NFS 用戶端和 UNIX CIFS 伺服器可以提供網路檔案系統交互操作性。即使您設計了您的系統,讓原始程式檔可以停留在 UNIX 檔案系統中,而且建置可以在 Windows 系統上執行,您還是需要確定由 NFS 用戶端或 CIFS 轉譯的檔案名稱是否與 Windows 相容。 NFS 和 CIFS 軟體會對不合法的名稱執行名稱轉換。在 Windows 電腦上使用 NFS 用戶端,或是在 UNIX 電腦上使用如 Samba 等 CIFS 伺服器,意指不需要移動程式碼本身;但是使用 NFS 用戶端或 Samba 不會減少遷移中任何其他的義務。例如,區分大小寫的遠端檔案系統可能是很重要的需求,特別是如果您在相同目錄中使用名為 makefilemakefile 的檔案時。

另一個要考慮的問題是當需要使用遠端檔案系統,讓系統時間彼此保持同步時。由於 make 公用程式對於檔案上的時間戳記十分敏感,因此永遠保持時間的同步化是非常重要的。否則,make 公用程式可能無法決定正確的先決條件和依存性。幸好,從 Windows 2000 開始的所有 Windows 系統中均預設為具有時間服務。不屬於網域的系統會自動與「網域控制站」同步化。其他系統 (如 UNIX 系統和非網域 Windows 系統) 則必須要正確地設定時間伺服器。

遷移 makefile

make 公用程式可以位於所有的 UNIX 平台上。有時候各平台上會有數種不同版本的 make。例如,Solaris 有三種: /usr/ccs/bin/make/usr/xpg4/bin/make/usr/lib/svr4.make。後者是散發給擁有從 SystemVr4 系統移植過來的 makefile 的使用者。前面兩項是以 Sun Microsystems 的舊有實作方式為基礎,並支援 Solaris 的許多特定功能。第二種是提供給試著根據 XPG 和 POSIX 規格來建立可攜式 make 環境的使用者。

在大部分的 UNIX 平台上,make 的 GNU 實作方式 — 稱為 gmake — 亦可供使用。這個版本的 make 的特色包括:

  • 符合 POSIX 標準規格。

  • 與其他版本的 make 相容,例如 BSD 4.3 和 System V 版本。

  • 許多自己獨有的功能。

如果您的建置流程已經使用 gmake,或許您應該繼續在 Windows 上使用它。這樣會比較方便,因為 gmake 在三種 UNIX 環境中都可以使用。下列各節討論了 make 中的許多功能,在 UNIX 的舊有實作方式和 Windows 上的版本之間有所不同。這將會幫助您確定在建置環境的遷移中,潛在的問題範圍。

make 啟動

當您執行 make 時,通常會從讀取含有所有預設規則以及預先定義的巨集定義的檔案開始。這有時會稱為啟動或預設 makefile。若要遷移您的 makefile,可能會需要在 Windows 上的啟動 makefile 中增加特殊規則。

對於 MKS make,這個啟動檔案的預設位置是 $ROOTDIR/etc/startup.mk。MAKESTARTUP 環境變數可以覆寫這個檔案。例如,當您安裝 MKS Toolkit for Developers 或 MKS Toolkit for Enterprise Developers 時,這個環境變數會設定為 $ROOTDIR/etc/nutc.mk。

對於 Interix make,預設位置是 /usr/share/mk/sys.mk。Interix make 也會使用 MAKESTARTUP 環境變數為使用者提供覆寫預設檔案的方法。

在 Solaris 系統上,make 永遠會讀取它找到的第一個檔案,先搜尋 ./make.rules,然後再搜尋位於 /usr/share/lib/make/make.rules 的預設規則檔案。

對於 gmake,預設規則是內建在程式中的。它們並不像其他版本的 make 一樣,可以用在不同檔案中讀取的方式來加入。

包含其他 makefile

在 Solaris make 中,include 指示詞可以用於指定另一個檔案,該檔案的處理方式應該像在 makefile 中的此行加入其內容一樣。include 一字必須做為一行的最前面七個字母,後面接著空格或定位字元。

MKS make、Interix makegmake 也支援這種語法。

多行註解

以往註解都是以數字符號 (#) 開頭,直到找到非逸出新行字元為止。例如:

# this is a multi-line comment \
   line 2 of the comment \
   line 3 ends with a non-escaped newline and ends the comment

這項定義也為 POSIX 和 UNIX 標準所運用。Solaris make、MKS makegmake 都支援這種語法。然而,Interix make 則否。撰寫多行註解的替代方法是撰寫多個單行註解,每行開頭都使用 # 字元。

巨集

巨集是簡單的變數,而巨集名稱會獲派一個字串值 — 由一串字元所構成的值。巨集可以出現在 makefile 中的任何地方。巨集定義 (或指派) 的語法為:

VAR = 字元字串

巨集值可以延伸到多行上,方法是在行尾加上反斜線 (逸出新行)。習慣上 (但並非必要) 會使用空格開始下一行,因為空格比較容易閱讀。

巨集通常會使用語法 $(macroname) 來叫用。單一字元的巨集名稱在參照時可以不使用括號:$x。當參照巨集時,它們的值可以擴充。這種擴充要視巨集在 makefile 中的位置而定。

make 的不同實作方式之間的基本巨集使用是一致的。然而,每種實作方式可能會支援不同的功能,以下各節說明了其中數種屬性。

特殊巨集名稱

make 有特別解譯的巨集名稱。不同版本的 make 有自己的特殊巨集,不過下面兩種是通用的:

  • SHELL

    SHELL 巨集是不會從環境繼承其值的特殊巨集。這種巨集只能在 makefile 中設定。這種巨集的值會指定執行配方命令時要使用的程式。它通常是設定為完整路徑名稱 (例如 /bin/sh)。如果您在 UNIX makefile 中使用這項功能,必須確定這個完整路徑名稱同樣存在於 Windows 上的 UNIX 環境中;否則就必須要變更這個值或移除這個巨集。

    對於 MKS make,/bin/sh 的值可以使用,不過只有在您於系統上建立 /bin/sh 路徑名稱時才能使用。您可以建立這個目錄,然後從 $ROOTDIR/mksnt 複製所有需要的公用程式 (如 sh),或者如果這是本機 NTFS 檔案系統,您可以建立 $ROOTDIR/mksnt 的 symlink,例如:

    ln –s $ROOTDIR/mksnt /bin

    請注意,您必須在叫用 make 的每個檔案系統上建立這個路徑名稱,因為根目錄的位置 (/) 與您目前的工作目錄的檔案系統有關。

    另一項替代解決方案是將 SHELL 巨集的值變更為 $MKSBIN/sh.exe 或 $ROOTDIR/mksnt/sh.exe。

  • MAKEFLAGS

    MAKEFLAGS 含有 make 命令旗標的清單,包括巨集指派。它們在 make 中的用法和在命令列上一樣,會在 make 的任何遞迴叫用中傳遞。MAKEFLAGS 中有幾種命令旗標是無效的,因為它們在這裡沒有意義;如需特定資訊,請參閱 make(1) 參考手冊頁。

    這個特殊巨集是由 make 建立的,並含有 make 命令所使用的旗標和巨集定義。如果已設定 MAKEFLAGS 環境變數,make 便會像命令列選項一樣來解譯值。除了命令列中所指定的選項之外,還會解譯這些值。當 make 啟動時,它會建立 MAKEFLAGS 巨集,其中含有命令列中所指定的以及來自 MAKEFLAGS 環境變數的所有選項和巨集定義。這個 MAKEFLAGS 巨集值一定會匯出到環境中,因此任何的巢狀 make 命令都會繼承叫用上層 make 的選項和巨集定義。

    在使用 MAKEFLAGS 之前,UNIX 舊版的 make 是使用名為 MFLAGS 的變數,只維護選項引數而非巨集定義。許多目前版本的 make 仍然只為了要能夠與舊版相容而繼續支援 MFLAGS。

    請注意,儲存在 MAKEFLAGS 中的值的格式在不同版本的 make 之間是不一致的。有些版本的 make 會將所有的選項集中在一起,不論有沒有前置連字號字元 (例如,-ek 或 ek);而有些則使用前置連字號字元並保持分開的形式 (例如,-e –k)。也請同時注意,MKS make 不會在 MAKEFLAGS 中儲存命令列上所定義的巨集定義。它只儲存選項。如果您對 make 執行巢狀 (或遞迴) 呼叫,那麼對於 MKS 環境,您就必須修改 makefile,以便明確包含需要往下傳遞到下一層 make 的任何巨集定義。一般而言,MAKEFLAGS 會自動為您處理這種情況。

巨集指派

為巨集指派值的方式通常是使用表單

VAR = value

其中 value 的值是在第一次「使用」巨集時所評估及擴充的,而不是在指派值時。

巨集指派的另一個變化是語法:

VAR := value

大部分版本的 make 都支援這個指派運算子,但是它在 Solaris make 上的意義和在其他版本上不同。在大部分版本的 make 中,這個運算子會在定義而非使用時,造成對值進行掃描和擴充。在 Solaris 上,這種語法會解譯為條件式巨集指派。在本章的<條件式巨集定義>一節中會說明這種類型的指派。

巨集指派中的命令取代

make 的部分實作方式已經引入專門的指派類型。例如,Solaris 上的 make 支援命令取代巨集指派。也就是說,當為巨集指派值時,它會執行 shell 命令列,並為變數指派結果。其語法為

VAR:sh = shell_cmd_line

Interix make 支援這種語法,但是 MKS makegmake 則否。如果您選擇使用 MKS make,可能就可以在叫用 make 之前,擷取 shell_cmd_line 的結果,以便取代這個指派,並傳遞命令列巨集定義中的值,例如:

make  VAR="$(shell_cmd_line)"  <other operands  . . .>

如果您是使用 gmake,則可以使用 gmake 中的特殊 shell 功能。在這種情況下,您可以在 makefile 中將巨集指派重新撰寫為:

VAR := $(shell shell_cmd_line)

巨集參照中的命令取代

當參照巨集時,其值會取代巨集。下例說明了如何使用 shell 命令的值來取代巨集。當參照巨集 CMD 時,便會執行 shell 命令 cat object_file_list ,而且會指派結果給 CMD 的值。

CMD = cat object_file_list
program : ${CMD:sh}
cc –o  $@  $?

此處如果檔案 object_file_list 含有 hello.o goodbye.o,則目標程式會視這些物件檔案名稱而定,並且會使用這些相同的物件檔案來建置。

Solaris make 和 Interix make 都支援這項功能。gmake 公用程式和 MKS make 公用程式則否。

巨集修飾詞

巨集通常會在參照時擴充。當巨集擴充時,可以使用不同的方法來修改擴充中的值。這種修飾詞語法在不同版本的 make 之間會有所不同,而且是潛在的遷移問題。

巨集修飾詞的語法為:

VAR:modifier[:modifier …]

有兩種受到廣泛支援的修飾詞格式。 第一種是:

old_suffix=new_suffix

所有版本的 make 都支援這種修飾詞語法。這是尾碼取代修飾詞,含有尾碼 old_suffix 的每個字的 old_suffix 都會以

new_suffix 來取代。例如:

OBJS=${SRCS:.c=.o}

會將變數 SRCS 中的 C 原始程式檔名稱的清單轉換為物件檔案名稱的清單,然後指派給變數 OBJS。

第二種修飾詞格式是:

prefix%suffix = str1%str2

這是模式比對取代修飾詞。等號 (=) 運算子左邊的百分比 (%) 與任何的字元字串均相符。因此,如果一個字同時包含首碼和尾碼,% 就代表這兩者之間所有的字元。這個字串將會被取代變成右邊的所有 %。例如:

FULLNAME=/usr/local/bin/perl
SUBDIRS=${FULLNAME:/usr%/perl=%}

會將 SUBDIRS 的值設定為 local/bin。

右邊可以出現任何數目的 % 字元。

條件式巨集定義

只有在 make 處理來自特定清單的目標時,條件式巨集定義才會指派值給變數。只有在 Solaris 版本的 make 中,才會有這種條件式定義,它的格式為:

target-list := VAR = value

這項定義會在 make 處理名為 target-list 的目標以及任何的依存性時,將值指派給指示的變數 VAR。只有在處理這些目標及其依存性時,巨集定義才會生效。

搜尋路徑

許多大型建置系統都將原始程式檔放在一或多個不同的目錄中。make 公用程式提供了一種機制,可以搜尋一或多個目錄,以尋找目標、依存性或 .include 檔案。大部分的實作方式都支援 VPATH 巨集。這是一個特別的巨集,其值會指定 make 應搜尋的目錄的清單。這份目錄清單的格式和 PATH 環境變數相同 - 每個目錄都是以冒號 (:) 字元隔開。

唯一已知不支援這個巨集的實作方式是 MKS make。它所使用的語法稍微有些不同。它是使用稱為 .SOURCE 的特殊目標來代替巨集。與 .SOURCE 相關聯的依存性清單就是 make 應該要搜尋的目錄清單。

如果您正從 UNIX 移植到 MKS Toolkit,而且是使用 VPATH 巨集,就必須要將 makefile 改為使用 .SOURCE。例如,將 makefile 從:

VPATH = src:headers:../othersrc

改為:

.SOURCE: src headers ../othersrc

預設規則和預設巨集值

make 啟動時,它所執行的首要工作之一就是設定預設規則 (例如尾碼規則) 的清單,並初始化一組預設的巨集名稱。

對於 Solaris、MKS 和 Interix,這些定義是位在它們的預設「規則」或「啟動」檔案中 (請參閱本章稍早的<make 啟動>一節)。對於 gmake,這些定義會被硬式編碼至二進位碼檔案中。

許多傳統的巨集和尾碼規則和 make 大部分的實作方式是一樣的。如 CFLAGS、LDFLAGS、CC 和 LD 等名稱都很常用。Solaris makegmake 已定義更多的巨集和規則。如 COMPILE.c 和 LINK.c 等名稱可以在 Solaris 和 gmake 中使用,但無法在 Interix 或 MKS 中使用。如果您是從 Solaris 遷移,則應該檢查以確定所依據的所有預先定義之巨集,都存在於所選擇的目標實作方式中。如果沒有,就必須在自己的環境中增加它們。

檔案名稱尾碼

在 UNIX 上,檔案名稱格式並沒有特別的意義。任何特定的格式都只是慣例,這些慣例通常並不是強制性的,同時 UNIX 應用程式或 UNIX 系統本身也沒有給予特別的意義。例如,物件檔案的副檔名通常都是 .o。但是副檔名也可以簡單地使用 .obj 或 .object。檔案名稱語法和 C 編譯器或連結器無關,它只是檢查檔案是否包含有效的物件檔案格式。

在 Windows 上,檔案名稱尾碼具有特殊的意義。許多 Windows 應用程式都會讓特殊行為和有特定尾碼的檔案產生關聯。由於這是您的應用程式或建構流程遷移的一部分,因此您可能會發現必須建立有 Windows 特定尾碼的檔案名稱,以便讓其他的 Windows 應用程式可以辨識出它們。這可能會影響到您的 makefile 或設定指令碼。

例如,只有在檔案的副檔名為 .exe 時,MKS Toolkit 中的 shell 才會執行二進位執行檔。 否則便會試著將它解譯為 shell 指令碼,因而造成錯誤。在建置流程中,這通常不是問題,因為可執行檔通常是由 ccgcc 命令建立的,而這些編譯器會自動建立尾碼為 .exe 的可執行檔。例如,在 MKS Toolkit 和 Cygwin 中,命令 cc –o hello hello.c (若為 Cygwin 則以 gcc 取代 cc) 會建立名為 hello.exe 的檔案。即使您可能已經明確地要求沒有尾碼的執行檔,還是會得到一個有正確尾碼的檔案。

較為普遍的問題是在 shell 指令碼中,或在明確參照可執行檔名稱的 makefile 中。所參照的檔案名稱或許會因為新的 .exe 尾碼之故,而與所產生的執行檔名稱不符。在 makefile 中,影響最大的就是目標名稱。例如,規則

hello:
cc –o hello hello.c

會造成建立檔案 hello.exe;但是 hello.exe 不是目標名稱,hello 才是。由於目標檔案 hello 從未建立,因此 make 一定會執行這個配方。

在某些情況中 (例如 Cygwin),gmake 公用程式已經過修改,因此會自動處理這種特殊情況,而且 hello 和 hello.exe 名稱之間不會發生不符的情況。

若是使用 Interix,檔案尾碼會有不同的問題。wcc 編譯器指令碼不會建立副檔名為 .exe 的執行檔。這樣很好,因為您所有的建置和建構工具都會繼續正常運作,因為所有與檔案名稱語法有關的假設和依存性都會保持相同。但是這可能表示最後的 Windows 應用程式二進位碼檔案可能沒有使用正確的 Windows 檔案名稱格式,讓它可以由其他的 Windows 應用程式 (例如檔案總管) 來執行。在這種情況下,您可以在 makefile 中新增命令來重新命名檔案,或是建立建置後指令碼來重新命名最終二進位碼檔案,使用符合您需要的檔案名稱格式。

隱含規則

隱含規則是 make 運作方法的基礎核心,它們會根據檔案名稱中的一般模式,比對特定檔案名稱,以建立目標與先決條件的關聯。有兩種常用的隱含規則類型:尾碼規則和模式比對規則。make 的所有實作方式均支援尾碼規則,而這些規則是以比對副檔名的模式為基礎。

有些實作方式會支援較有彈性的「模式比對」規則,允許針對任何部分的檔案名稱進行模式比對,而不是只有尾碼。有時候,這種類型的推斷規則稱為中繼規則 (meta-rule)。

尾碼規則

每一種 make 實作方式都有一組隱含尾碼規則。這些規則定義了如何建立不同類型的檔案,或許還有如何從不同類型進行轉換。這些類型的檔案是以它們的檔案名稱尾碼來識別的。make 公用程式是少數以檔案名稱尾碼為基礎的 UNIX 公用程式之一。

尾碼規則機制是以副檔名為基礎,副檔名是 UNIX 社群中的標準,並用於在原始程式碼的開發和編譯流程中,界定不同類型的檔案。

所有預設尾碼規則的定義通常都是在 make 啟動設定檔中。您需要檢查以確定您在 Windows 上所使用的 make 程式有正確的隱含規則,可以建立沒有尾碼的檔案。例如,下列的尾碼規則

.c:
$(CC) –o $@ $(CFLAGS) $(LDFLAGS) $<

可以使用下行來建立 hello 目標檔案:

hello:  hello.c

然而,MKS make 公用程式遺失了這項隱含規則。如果沒有這項規則,MKS make 便不知道如何從 hello.c 建置名為 hello 的檔案。要在 makefile 中增加這項規則很容易,但是這樣做可能無法解決所有的問題。使用 MKS Toolkit 時,仍然會有 cc 建立名為 hello.exe (不是 hello) 的檔案,而 make 無法辨識這兩個檔案是相等的問題。因此每次您執行 make 時,永遠都找不到目標檔案名稱 hello,而一定會執行隱含規則。

為了協助處理 UNIX 和 Windows 之間不同的尾碼,MKS make 啟動檔案會定義一些簡單的巨集,例如 $E、$O、$S 和 $A,然後分別指派給傳統的 Windows 尾碼:.exe、.obj、.s 和 .lib:

E = .exe
S = .s
O = .obj
A = .lib

如果您計劃在 Windows 和 UNIX 之間共用 makefile,可以使用這些巨集取代 makefile 中的這些尾碼,然後使用適合 UNIX 的值在 UNIX make 啟動檔案中有條件地定義這些巨集。例如:

E =
S = .s
O = .o
A = .a

這項技巧不限於 MKS make 環境;它可以用於任何的 make 環境中。

模式比對規則

模式比對規則可以用於根據檔案名稱首碼、尾碼或兩者兼具,來指定目標和依存性之間的關係。這種規則類型的範例為:

% : RCS/%,v
     co –l $<

Solaris make 在許多年前便已經引用了模式比對規則。從那時開始,MKS 和 GNU 就將此類規則加入了它們的 make 版本中。Interix 版本的 make 不支援這項機制。

程式庫和保存檔支援

有些版本的 UNIX make (例如 Solaris 和 System V 的實作方式) 支援用於偵測儲存在保存檔中檔案依存性的語法。保存檔是由 ar(1) 公用程式建立的,含有稱為成員的檔案集合。保存檔最常用於儲存供編譯期間使用的物件檔案。

在舊版的 make 所支援的語法中,可以指定保存檔的成員做為目標或先決條件。保存檔的成員會使用

archive(member [member ...]) 的語法來指定,例如 example.a(member1.o member2.o)。

例如,假設您的 makefile 含有

example.a : example.a(member1.o member2.o)

然後您輸入:

make example.a

make 將會建立檔案 member1.o 和 member2.o (使用隱含規則,如 .c.o,並假定原始程式檔可供使用),接著建立以這兩個 .o 檔案填入的保存檔 example.a。

所有版本的 gmake 和 Interix 版本的 make 都會提供這種語法的支援。MKS make 不支援這種語法。

請注意,在使用 Interix make 時,隱含的 .o.a 和 .c.a 規則 (可以在檔案 /usr/share/mk/sys.mk 中找到) 會有問題。在解析保存檔依存性關係時,便會使用這些隱含規則。

這些規則通常看起來如下:

.o.a:
$(AR) $(ARFLAGS) $@ $*.o
rm -f $*.o
.c.a:
$(CC) -c $(CFLAGS) $<
$(AR) $(ARFLAGS) $@ $*.o
rm -f $*.o

但是如果它們變成看起來如下,可以修正:

.o.a:
$(AR) $(ARFLAGS) ${.ARCHIVE} $*.o
rm -f $*.o
.c.a:
$(CC) -c $(CFLAGS) $<
$(AR) $(ARFLAGS) ${.ARCHIVE} $*.o
rm -f $*.o

修訂控制 (RCS、SCCS)

版本控制和組態管理通常是分開處理的,而且與建置管理流程無關。撰寫分開的工具,要比為 make 撰寫版本控制隱含規則並讓它正常運作簡單多了。允許 make 自動更新原始程式檔會有一些基本的問題,尤其是在多人使用相同的檔案時。讓程式設計人員負責在建置之前取得正確的檔案版本,會比較安全。

舊版的 UNIX make 有「原始程式碼控制系統」(SCCS) 的特殊內建支援,這是一種在 System V 上開發、非常舊的修訂控制系統。即使現在仍然使用 SCCS,它很快就會被更先進的修訂版本和組態管理工具取代。

SCCS 的支援必須以不同的方法從其他規則內建到 make,因為 SCCS 是以首碼、而非尾碼來管理檔案的。例如,當名為 hello.c 的 C 原始程式檔置於 SCCS 控制下時,會建立首碼為 s. 的檔案,例如 s.hello.c。gmake 中會為 SCCS 提供部分的內建支援,但在 Interix 和 MKS 版本的 make 中則否。

從您一直在使用 SCCS 的 UNIX 系統遷移至 Windows 會引起一些問題,因為 SCCS 工具不是由 MKS Toolkit、Interix 或 Cygwin 產品提供的。雖然 SCCS 並不特別普遍,但是您的 UNIX 系統中可能會有。如果 SCCS 是一項需求,便會有一些潛在的解決方案。有數種替代的 SCCS 實作方式具有免費的原始程式碼,您必須自行取得該程式碼,並移植到 Windows。

另一種常用的修訂控制系統稱為 RCS。這種系統是使用檔案的尾碼而非首碼 (同 SCCS)。如此可讓撰寫 make 的隱含規則更加簡單。RCS 可以用來做為所有 UNIX 環境產品的一部分: MKS、Interix 和 Cygwin。

另外還有其他的原始程式碼控制工具,例如 Perforce 和 ClearCase。UNIX 系統上提供的預設 make 規則不適用於這些系統。如果您已經具有與這些系統有關的規則,應該就可以在任何 UNIX 環境中將它們遷移到 Windows。

先決條件清單中的動態巨集

一般來說,動態巨集無法用於先決條件清單中;它們只能用於配方。有一種情況可以在先決條件清單中使用動態巨集 $@。在本例中,必須在巨集中再加上一個貨幣符號 ($),例如 $$@。這個例子向您示範如何參照先決條件清單中的目前目標名稱:

file1 file2 file3 : $$@.c

相等於

file1 : file1.c
file2 : file2.c
file2 : file3.c

Solaris、Interix 和 MKS make 中都支援這項功能,但在 gmake 中則否。

特別目標

所有版本的 make 都支援由 make 特別處理的一組特殊功能目標。這些目標中有些是已知的,而且在所有實作方式上的行為都相同,例如目標 .DEFAULT、.IGNORE、.PRECIOUS 和 .SUFFIXES。

但是各種實作方式也支援該實作方式專用的特別目標。例如,Solaris 版本的 make 定義了數項專用的特別目標,例如 .INIT、.KEEP_STATE、.KEEP_STATE_FILE 和 .MAKE_VERSION。 其他版本的 make 有自己的清單,例如 gmake 中的 DELETE_ON_ERROR 和 .PHONY 或是 BSD 版本的 make 中的 .OPTIONAL、.BEGIN 和 .END。

表 4.3 顯示了部分非傳統的特別目標。

表 4.3 非傳統的特別目標

Solaris

Interix

Gmake

MKS

.INIT

.BEGIN

.DONE

.END

(使用 VPATH 巨集)

.PATH

(使用 VPATH 巨集)

.SOURCE

.KEEP_STATE

.KEEP_STATE_FILE

.SCCS_GET

轉譯編譯器選項

cc 命令 (或 gcc) 是程式設計人員與編譯器的介面。它可以是編譯器程式本身,或是編譯器的包裝函式或前端。各種編譯器都接受不同的命令列選項。若要轉換選項,您需要知道現有選項的意義,以及所選環境中的相等選項是什麼 (若有相等選項)。UNIX cc 編譯器支援許多選項。這些選項中有許多都可以在許多 UNIX 平台上共用。不過各種平台也有自己的一組特殊選項。表 4.5 說明了一些在 UNIX 上叫用 C 編譯器時較常用的選項,以及 Windows 上各種 C 編譯器公用程式所使用的最相近的相等選項 (如果有的話)。

如果您的建置流程需要不支援的編譯器選項,就必須找出解決方法。您可以直接編輯 MKS cc 和 Interix wcc 公用程式,因為它們是為 Microsoft C 編譯器提供前端的指令碼。請查看 Microsoft C 編譯器選項,決定是否有選項符合您的需要,然後在 ccwcc 指令碼中增加指示,提供這項功能。

各種編譯器的位置如下:

表 4.4 編譯器位置

編譯器

位置

MKS Toolkit for Developers

$ROOTDIR/etc/compiler.ccg

MKS Toolkit for Enterprise Developers

$ROOTDIR/etc/nutccg/cc.ccg

SFU Interix

wcc (來自 http://www.interopsystems.com)

Cygwin 工具

/bin/gcc

兩種 MKS Toolkit 編譯器指令碼之間的差異,在於 etc/nutccg/cc.ccg 含有 MKS NuTcracker UNIX 可攜性 API 的支援。etc/compiler.ccg 指令碼是用於建置只使用 Windows 所提供的程式庫和功能的應用程式。

表 4.5 顯示不同編譯器所提供的許多常用 C 編譯器選項。

表 4.5 常用的編譯器選項

說明

Solaris

gcc

Interix wcc

MKS cc

編譯但不連結

-c

-c

-c

-c

不在前置處理期間移除註解

-C

-C

-C

-C

使用定義 variable 來定義巨集

-D variable

-D variable

-D variable

-D variable

僅作前置處理

-E

-E

-E

-E

指定 include 目錄 incl_dir (大寫的 I)

-I incl_dir

-I incl_dir

-I incl_dir

-I incl_dir

指定程式庫 lib (小寫的 L)

-l lib

-l lib

-l lib

-l lib

在 lib_dirs 中搜尋程式庫

-L ib_dirs

-L ib_dirs

-L ib_dirs

-L ib_dirs

最佳化

-O

-O

-O

-O

命名結果檔案 outfile

-o outfile

-o outfile

-o outfile

-o outfile

僅作前置處理並將輸出儲存到副檔名為 .i 的檔案

-P

<na>

-P

-P

建立等量執行檔

-s

-s

-s

-s

編譯為組合語言程式碼

-S

-S

-S

-S

傳遞 arg 至連結器或編譯器或至元件 c

-W c,arg

-W c,arg

-W arg
<傳遞至編譯器>

-Y arg
<傳遞至連結器>

-W/ arg

在完全符合的 ANSI 模式中工作

-Xc

-ansi

-Xc

-Xc

在 ANSI 加上擴充模組 (預設模式) 中工作

-Xa

<na>

-Xa

-Xa

在 ANSI 和 K&R 模式中工作

-Xs

<na>

<na>

-Xs

K&R 模式

-Xt

<na>

<na>

-Xt

對於 MKS cc 而言,-l 選項有一個相當大的問題:標準的 -ll-ly 版本有一個錯誤,因為選項會指示編譯器要包括來自 lexyacc 支援程式庫的函式。在 MKS cc 命令中使用這些選項時,會出現錯誤:

Warning: Could not locate -ll; assuming "l.lib"
LINK : fatal error LNK1181: cannot open input file l.lib'

問題可能是因為 lexyacc 的程式庫安裝時使用了錯誤的名稱,用的是 lex.lib 和 yacc.lib (在 $ROOTDIR/lib 中) 而非 l.lib 和 y.lib。若要修正這個問題,請將 lex.lib 複製到 l.lib,而 yacc.lib 複製到 y.lib。

連結器

連結器公用程式是用來將物件檔案合併在一起,以建立執行檔或共用程式庫。它會解析物件檔案中的符號名稱和記憶體參照,組成完整的執行檔。在 UNIX 上,這個公用程式通常稱為 ld,而在 Windows 上則稱為 link.exe

一般來說,您會使用編譯器來建立執行檔或共用程式庫。編譯器會以適當的方式自動執行連結器。編譯器通常會支援命令列選項,您可以在其中指定連結器特定的選項,當叫用連結器時便會通過這些選項。 然而,有時您會想要直接叫用連結器。如果您想要直接叫用,就必須決定在 UNIX 上這樣做的原因,不論在建置 Windows 時是否仍然必要;如果必要,則需判定是否需要修改連結器命令列選項。就和編譯器一樣,不同平台上的連結器會支援不同的命令列選項。

表 4.6 常用的連結器選項

說明

Solaris

GNU ld

Windows link.exe

MKS ld

將最初的進入點設定為 entrypoint

-e entrypoint

-entry: entrypoint

-e entrypoint

-e entrypoint

在程式庫搜尋目錄中新增 directory

-L directory

-L directory

<na>

-L directory

在連結中包含程式庫 x

-l x

-l x

<必須指定 libx.lib 做為運算元>

-l x
[先搜尋 libx.a 然後搜尋 x.lib]

在 mapfile 中建立位址對應

-M mapfile

-M<輸出移至 stdout>

-map: mapfile

-W/map: mapfile

命名結果檔案 outfile

-o outfile

-o outfile

-o outfile

-o outfile

移除偵錯符號

-s

-s

-debug:none

-s

動態與靜態程式庫

當建置執行檔程式時,您通常會連結部分程式庫與物件檔案。程式庫有兩種:靜態與動態。靜態程式庫是用於建置獨立作業、與其他程式庫沒有依存性的執行檔。所有的可執行程式碼都包含在一個檔案中。有了動態連結,執行檔便會依存在開始執行時所連接的共用程式庫。這些共用程式庫可以由許多不同的可執行程式來使用。如此一來,執行檔便不會像靜態版本那麼大。

UNIX 上的共用程式庫的一般副檔名為 .so,Windows 上則為 .dll;雖然版本處理和識別屬性有所不同,但是這些差異可能會隱藏在建置系統中。

遷移 Shell 指令碼

如果您的建置流程是使用 shell 指令碼,就必須檢查有沒有可能會在 Windows 上執行時遇到的潛在問題。幸好,幾乎所有的 shell 指令碼都是針對 Bourne shell 或 POSIX.2 shell 所撰寫的,例如 Korn shell 或 bash。這表示指令碼本身很少會造成遷移的問題。大部分的問題都是起因自作業系統的差異,例如檔案命名語法以及由其他公用程式所支援的命令列選項。

其中一個較常見的問題是使用 UNIX 絕對路徑名稱,例如 /bin/cp 或 /tmp。其中有些 (如 /tmp) 是由 UNIX 環境自動處理的,有些則否。MKS 環境是唯一不支援單一根目錄系統的;在其中,根目錄 (/) 永遠是參照到檔案系統中的固定目錄。MKS 環境提供多個根目錄,每個裝載的檔案系統都有一個目錄 (也就是磁碟機代號裝載),因為這就是 Windows 運作的方式。避免變更指令碼中這些路徑名稱的一個方法,就是建立目錄符號連結,讓常用目錄指向它們在 $ROOTDIR 中的對應部分,和 /bin 與 /usr/bin 指向 $ROOTDIR/mksnt 的方式一樣。

遷移其他命令

在建置流程中會叫用許多 UNIX 命令,不論是在 makefile 的目標配方中或是在建置週期期間叫用的 shell 指令碼中。遷移流程的其中一部分,就是要確保這些命令能夠在 Windows 平台上繼續運作。以下各小節針對建置流程中一些較常用的公用程式,提供各種已知問題的相關資訊。

lex

lex 公用程式是建立 C 程式的工具;C 程式可以執行字元輸入的語彙處理或剖析。它主要是用來做為 yacc 公用程式的介面。lex 公用程式讀取具有對應動作的一組規則,然後產生可以用來執行這些規則的 C 程式。

lex 有數種常用的實作方式:來自 GNU 的 flex、MKS lex 和原始的 AT&T lex。許多商用 UNIX 實作方式都是以 AT&T 版本為基礎。Interix 實作方式是以 GNU flex 為基礎。這些大部分都是彼此相容的,讓移植含有 lex 規則的檔案變得非常容易。可能只需要少許的變更。

有關 GNU flex、AT&T lex、MKS lex 和 POSIX lex 規格之間差異的討論,您可以參閱下列的資源:

http://www.gnu.org/software/flex/manual/html_mono/flex.html

http://www.mkssoftware.com/docs/wp/wp_lyuse.asp

yacc

yacc 公用程式是剖析器產生器。它會建立 C 程式,該程式可以用來根據指定的一組規則和規格 (稱為文法) 來剖析輸入。使用者會提供對應到這些規則的一組動作。當剖析器偵測到相符的記錄時,便會叫用這些動作。如此,yacc 公用程式便可以用來開發各種類型的語言剖析器,從用於簡單的桌上型計算機到複雜的程式設計語言均可。yacc 公用程式通常是和 lex 公用程式一起使用。

yacc 公用程式有數種常用的實作方式:以原始的 AT&T 程式碼為基礎的版本、柏克萊大學的版本 (公用網域 BSD 版本) 以及 GNU 版本 (也稱為 Bison)。Interix 版本是以 BSD 實作方式為基礎,而 MKS 版本是以來自原始的 AT&T 程式碼的 SCO 程式碼為基礎。

然而,這些版本的實作方式都是以原始的 AT&T UNIX 實作方式的行為做為基礎。因此,在各版本之間遷移您的 yacc 規格檔案通常是相當瑣碎的工作。最常見的問題似乎都是內部限制方面的問題,例如緩衝區大小和記憶體限制。在大部分的情況中,GNU 版本 (Bison) 似乎是功能最多的,也比較少出現這類的問題。

awk

awk 公用程式是一種字串操作和報告產生語言。它是用於根據模式比對來套用複雜的文字轉換。

awk 的版本在各平台上都是一致的。雖然早期的 UNIX 系統在「舊的」awk 和「新的」awk (nawk) 之間有區別,但是現代的 UNIX 系統只使用新的 awk。大幅擴充 awk 的唯一實作方式就是 Cygwin 中的 GNU awk,或 gawk。然而,gawk 可供 Windows 使用,因此如果您在 UNIX 上是使用 gawk,在 Windows 上便應該使用 gawk

偶爾會發生的一個相容性問題,就是 for 陳述式中陣列上反覆運算的順序。反覆運算的順序在實作方式之間可以有所不同,因為程式在陣列間周遊的順序並沒有保證。在 awk 中,所有的陣列都是有關聯的,因此並沒有依索引排列的隱含順序;for 陳述式可以依任何順序執行元素。

cp

cp 公用程式是 UNIX 上標準的檔案複製工具。有兩個選項:-r-R,它們會執行遞迴複製 (包括目錄和特殊檔案)。這些選項對於 UNIX 特殊檔案的處理方式或許有所不同;-R 的行為是由 POSIX.2 指定的,而 -r 的行為則否。請檢查您的 shell 指令碼和 makefile 中 cp -r 的實例,並決定應如何處理特殊檔案。

chmod

chmod 公用程式會變更檔案上的 UNIX 權限位元。UNIX 權限是以三組位元為基礎。Windows 權限是以存取控制清單為基礎,這是一種較有彈性、但較複雜的機制。如需有關權限的詳細討論,請參閱《UNIX 應用程式遷移指南》。

由於 Windows 具有頗為複雜的存取控制機制,因此 Windows 上大部分的 UNIX 環境都不會精確執行 UNIX 和 Windows 機制之間的對應。只有 Interix 會精確執行。除非您使用的是 Interix,否則在使用任何處理權限位元的 UNIX 命令時都必須小心,因為您可能無法獲得和預期完全相同的結果。

diff

diff 公用程式會比較兩個文字檔案,並提供其間的差異清單。這些差異通常會顯示在表單中,該表單可以放在指令碼內,以便將一個文字檔案轉換成另一個文字檔案。

有時建置工具或開發人員本身會使用 diff 公用程式來判定特定的檔案是否完全相同。大部分的 diff 實作方式都可以成功地比較文字和二進位檔案。然而,MKS Toolkit 版本則無法使用 diff 來比較二進位檔案;這個 diff 公用程式會對每個二進位檔案都產生錯誤訊息。若要避開這個問題,請在您知道要比較二進位檔案時,使用 cmp 公用程式。

ln

ln 公用程式會建立新的檔案名稱,並連結到現有的檔案。它是用來代替複製檔案的。有兩種連結:硬式連結和 symlink。這些系統中所有的 ln 公用程式在使用 Windows NTFS 檔案系統時,都可以建立硬式連結。

有了 Interix,ln 公用程式便可以建立檔案和目錄的 symlink;然而,這些 symlink 只有在 Interix 環境中才能被辨識。這些 symlink 檔案在透過 Windows 環境存取時,會顯示為系統資料檔案。

MKS ln 公用程式可以使用 Windows 連接點,來建立目錄的 symlink。MKS ln 公用程式不支援檔案的 symlink 的建立。

Cygwin ln 公用程式會建立 Windows 捷徑或 .lnk 檔案,以支援 -s 選項建立 symlink。這並不是真正的符號連結;而是只能和可辨識這項功能的 Windows 公用程式 (例如檔案總管) 一起使用的 Windows shell 功能。對於許多其他的 Windows 公用程式 (和 Interix 公用程式),符號連結會顯示為含有二進位資料的一般檔案。

sed

sed 命令是用於變更文字資料流的資料流處理編輯器。它常用於以某種方式轉換輸出,以產生新的命令檔或 makefile。

sed 有兩個常見的遷移問題。第一個問題是,舊版的 sed 常常只以基本規則運算式套件的子集為主,而且所執行的子集在各系統之間可能會有所不同。MKS、Interix 和 Solaris sed 的版本都宣告具有 POSIX.2 相容性,因此其規則運算式應該是可移植的。Cygwin 版本的 sed 應該要與 POSIX.2 相容,但是在 sed(1) 參考手冊頁中說明了已知的不相容情形。如果您不是使用與 POSIX.2 相容的版本的 sed,便需要檢查所使用的規則運算式,並決定需要做的變更 (如果有的話)。規則運算式字元句號 (.) 和星號 (*) 永遠受到支援。括弧運算式中的中繼字元解譯 (例如 [A-z*]),在非 POSIX 實作方式之間會有所不同。

第二個問題是輸出不相容。sed 命令有自動列印功能,會自動撰寫相符的行。還有一個命令 (p) 會列印作業的結果。停用自動列印或使用 p 命令時,有些實作方式會輸出該行兩次;有些則只列印一行。這兩種在 POSIX 之下都是有效的,因此都不是錯誤。如果您的 sed 指令碼是使用其中一種列印行為,可能就會無法移植。請重寫為使用 -n 選項,然後明確列印出您所要的結果。

建置或遷移系統測試

在這個階段,您會需要建置系統測試。它們會使用您稍早建立的範例 makefile 來執行。

您或許已經擁有用於檢查 UNIX 建置系統的測試;如果有,應該將它們遷移至 Windows,以構成測試系統的基準。然而,由於建置系統測試相當稀少,您可能須要自行建立這些測試。您可以由此處實作測試規格。

請從決定要使用何種 UNIX 環境的驗證測試開始。以它們做為基準,然後從此處開始開發測試。當建置系統進入「穩定」階段時,就會定期執行這些測試。

整合解決方案

務必要記住,在這個階段,您正在建置將會安裝在一般使用者電腦上的環境,會需要考量一些重要的因素,例如環境變數。「使用者經驗角色」必須要能掌握這些因素,以便為一般使用者記錄建置系統。

有一些元件無法單獨測試:提供一段不會實際搬移檔案的 cp 命令沒有什麼意義;取代 cp 命令的工作,比放入 stub 檔進行測試的工作要花更多的力氣。shell 指令碼之類的項目,會自動包含並測試一些其他的元件。然而,最後您必須要整合所有的部分。

由於建置系統的性質之故,開發人員或許會一次在所有不同的元件上工作,循環進行它們:首先,是對一組 makefile 進行變更,改正檔案名稱和路徑名稱,同時改為正確的編譯器選項;然後迅速執行 make 命令,確保它能夠正常運作;接下來針對配方行做一些修正;然後在組建其中一個目標時會呼叫的 shell 指令碼上作業;最後再回到 makefile。

測試解決方案

最初的元件測試會在開發系統上完成。它們會移至範例站台,然後再測試一次。將建置元件移至測試和範例站台,是讓建置系統運作的一個重要步驟。就像偶爾必須去除所有的中繼檔案、單獨從來源組建應用程式一樣,您必須清除中繼檔案的範例站台,重新安裝建置系統的目前版本,然後再試一次。

在這個階段結束時,您的建置遷移小組應該要具有:

  • 可執行的建置系統,雖然可能還有一些問題。

  • 說明安裝和使用建置系統的初步文件,包括檔案名稱對應以及 UNIX 可攜性環境的使用。

將會用來評定系統行為效能的一組系統測試。測試計畫中會就衡量準則作出定義,視您組織的特定需求而定。