顯示具有 Makefile 標籤的文章。 顯示所有文章
顯示具有 Makefile 標籤的文章。 顯示所有文章

2014年8月11日 星期一

顯示make編譯信息

現在在編code的時候,因為在確認一些細節,想了解實際的的編
但是,編譯的過程中,並沒有把make的實際細節顯示出來。

我知道可以透過@與-s或--silent把完整的訊息隱藏起來,
但我的Makefile並沒有這一些東西啊,為什麼還是會隱藏起來呢?

原來是在build kernel module的時候,會使用的linux kernel的makefile,而kernel的makefile有預設隱藏這一些訊息。

有兩個方式
1. 則要到kernel的makefile把這一些部分註解掉。
要俢改的檔案位置是在/usr/src/linux-2.6.38.8/Makefile
...
ifeq ($(KBUILD_VERBOSE),1)
# quiet =
# Q =
else
# quiet=quiet_
# Q = @
endif

# If the user is running make -s (silent mode), suppress echoing of
# commands

ifneq ($(findstring s,$(MAKEFLAGS)),)
# quiet=silent_
endif

#export quiet Q KBUILD_VERBOSE
...

如此一來,重新編譯的時候,就可以顯示完整的編譯過程了。

2. 使用make V=1就可以顯示完整的訊息了, 這個方式就超級方便的了。

參考資料:
顯示make編譯信息
Linux內核make命令選項

2011年9月18日 星期日

在Makefile裡面的command line設定變數值

在Makefile裡面的格式如下所示

target : prerequisites
command


請參考nmake 簡單教學

一般來說,要設定變數,只能在Makefile的最上面的部分
在上面那一個文章的Makefile的範例裡面,就是在everything之上設定變數

但是,我現在想做的是,依不同的target設定不同的數值~也就是想把設定數值的部分擺在command裡面。但是,在nmake的環境下,我在設定完後,直接印出來,似乎沒有達到效果。

target : prerequisites
SET TEST_VALUE=YES
ECHO $(TEST_VALUE)


最後,在多方嘗試之後,發現是可以達到我的需求的,但是,有以下幾點限制
1. 在目前的Makefile的command line設定的變數,必需觸發的下一個Makefile內才能使用
2. 變數必需不能有小寫

直接看例子吧~

Hello.c
#include <stdio.h>

int
main (
)
{
printf ("Good Morning!\n");
return 0;
}


例子一:
First.makefile
all:
[tab] SET TEST_VALUE=YES
[tab] nmake /f Second.makefile all

Second.makefile
# Program, flags, etc.
ASM = cl
OBJ = Hello.obj
TARGET = Hello.exe

all: clean everything
!IFDEF TEST_VALUE
[tab]ECHO $(TEST_VALUE)
!ENDIF

everything: $(TARGET)

clean:
[tab]- del $(OBJ) $(TARGET)

.c.obj:
[tab]$(ASM) $< /Fo$@

$(TARGET): $(OBJ)
[tab]$(ASM) $(OBJ) /Fe$@


執行指令
%> nmake /f First.makefile

如此一來,就可以在command line設定變數了,並且可以使用了。

但是,這樣的程式碼實在很難看~
那就用遞迴的方式,自己的Makefile呼叫自己的Makefile
Makefile
# Program, flags, etc.
ASM = cl
OBJ = Hello.obj
TARGET = Hello.exe

!IFDEF TEST_VALUE
all: clean everything
[tab]ECHO $(TEST_VALUE)
!ELSE
all:
[tab]SET TEST_VALUE=YES
[tab]$(MAKE) $@
!ENDIF

everything: $(TARGET)

clean:
[tab]- del $(OBJ) $(TARGET)

.c.obj:
[tab]$(ASM) $< /Fo$@

$(TARGET): $(OBJ)
[tab]$(ASM) $(OBJ) /Fe$@


執行指令
%> nmake

這一個Makefile會被執行兩次,在第一次的時候,TEST_VALUE變數並沒有被定義,
而第二次的時候,TEST_VALUE有被定義,如此一來,只需要一個Makefile就可以了

[2011.11.02 補充]
在make裡面,可以在target裡另外指定變數的值
foo = abc

all: foo = xyz
all:
[tab]echo $(foo)

這個時候的foo的值為xyz
以下的語法也提供相同的功能
all: override foo = xyz
all: export foo =xyz

但是,這個方法,似乎只能在linux下,正統的make下使用~
在nmake並不支援這個語法~

參考資料:
Makefile 語法簡介

2011年1月30日 星期日

DJGPP - HelloWorld

聽說另一個open source的編譯器也可以編寫純DOS的應用程式。

但是,我試到現在,在純DOS下都會出現
Load error: no DPMI - Get csdpmi*b.zip
的錯誤訊息

其實大部分的安裝都很簡單,另外開這一篇是因為Makefile的部分寫法,也就是內隱規則有一點不一樣,這裡特別寫出來。
到目前我用過linux下的make、wmake與DJGPP的make的內隱規則都不一樣~真是麻煩~

Makefile

# Program, flags, ect.

ASM = gcc
OBJ = hello.obj
TARGET = hello.exe

.PHONY: everything all clean

everything: $(TARGET)

all: clean everything

clean:
[tab]if exist *.obj del *.obj
[tab]if exist $(TARGET) del $(TARGET)

$(OBJ): %.obj: %.c
[tab]gcc -c $< -o $@

$(TARGET): $(OBJ)
[tab]gcc $(OBJ) -o $@


參考資料:
DJGPP

2011年1月28日 星期五

Watcom c - HelloWorld

Watcom c是一個Open Source,它可以編譯許多平台的可執行檔~

而我需要用到是因為,我要編譯在純dos下的執行檔,原本我是打算用bat就可以完成我要做的事情,但是,dos底下的bat指令非常的不完全,很基本上動作都做不到,所以只要尋找有什麼東西可以開發純dos下的應用程式。

找到Watcom c是一個不錯的開發套件,他可以開發for許多平台的應用程式,如DOS, DOS/4GW, Windows 3.x, Windows NT, OS/2, Novell NLM
而另一個DJGPP好像也可以build dos下的應用程式

第一步下載Watcom
下載點
安裝很簡單,直接安裝就可以了。

第二步
用命令提示字元到C:\WATCOM目錄
hello.c

#include <stdio.h>

void
main (
  )
{
  printf ("Hello World!!\n");
}


第三步:編譯
% wcl /c hello.c /fo=hello.obj
% wcl /l=dos hello.obj /fe=hello.exe

/l後面接的是要編譯出哪一個平台的應用程式

為了以後修改方便,利用watcom內建的wmake來執行編譯的程序
Makefile
# Program, flags, ect.

ASM = wcl
TARGET = hello.exe
OBJ = hello.obj

everything: $(TARGET)

clean: .symbolic
[tab]@if exist $(TARGET) del $(TARGET)
[tab]@if exist $(OBJ) del $(OBJ)

$(TARGET): $(OBJ)
[tab]$(ASM) -bcl=dos $< -fe=$@

.c.obj:
[tab]$(ASM) -c $*.c
#hello.obj: hello.c
#[tab]$(ASM) -c $< -fo=$@


在輸入以下的指令之後,就會出現hello.exe執行檔~
% wmake

參考PGUIDE.pdf,FTools.pdf檔案~

參考資料:
Open Watcom
WATCOMC教學
[C] 使用 fread 讀取檔案的觀念 - 小心使用 End-of-file (EOF)
watcom 编译器怎么使用呀?

2010年2月2日 星期二

nmake 簡單教學

在Linux下使用的是make,而在Windows下使用的就是nmake啦!

在語法上,有一些不同,這裡留下一些記錄!

有很多語法跟在Linux下的make是相同的,可以參考Makefile - 簡單教學

!IFDEF
了解後面的變數是否有被定義,若有被定義的話,就執行後面要執行的指令!

!IFNDEF
了解後面的變數(可以是全域變數)是否有被定義,若有沒有被定義的話,就執行後面要執行的指令!

!IF
在後面的判斷式是否成立

!ENDIF
做為!IFDEF的結尾

!ELSE
與!IF, !ENDIF一起使用的語法

!ELSEIF
是跟!IF與!ELSEIF與!ENDIF一起使用的,與!ELSE不同的是,後面可以接判斷式

!ERROR
顯示出後面的錯誤訊息,並且停止nmake的執行

!MESSAGE
顯示出後面的訊息,但與!ERROR的差別在於,並不會停止nmake的執行!

!INCLUDE
把後面接的檔案的內容貼到目前的位置上

!ELSEIFDEF
!ELSE IFDEF 的同義資料表。

!ELSEIFNDEF
!ELSE IFNDEF 的同義資料表。

!UNDEF macroname
取消定義 macroname。

而要讀取出變數的內容的話,則必需使用$()把它包起來!

下面就一個編譯.cpp的例子
food.h、food.cpp、main.cpp請參考這裡
另外,要注意的一點是,這裡的[tab]指的是,一定要用tab鍵產生出來的,在linux的make,指令前一定要用[tab]鍵,但是,在nmake似乎沒有這一個限制,但是,基於習慣與相容性,我還是會用[tab]鍵。
mymake
# Program, flags, etc.
CC      = cl
CCFLAGS = /c
OBJ     = main.obj food.obj
TARGET  = main.exe

everything: $(TARGET)

clean:
[tab]del $(OBJ) $(TARGET)

all: clean everything

food.obj: food.cpp
[tab]$(CC) $(CCFLAGS) food.cpp /Fo$@

main.obj: main.cpp
[tab]$(CC)  $(CCFLAGS) main.cpp /Fo$@

main.exe: $(OBJ)
[tab]$(CC) $(OBJ) /Fe$@


而,可以透過隱含規則再簡化Makefile的內容,請參考Makefile - 簡單教學
# Program, flags, etc.
CC      = cl
CCFLAGS = /c
OBJ     = main.obj food.obj
TARGET  = main.exe
FLAGS   =

!IF "$(NAME)" == "koba"
!MESSAGE  You want to try Koba phase
FLAGS = /DKOBA
!ELSEIF "$(NAME)" == "caspar"
!MESSAGE  You want to try caspar phase
FLAGS = /DCASPAR
!ELSE
!MESSAGE  You want to try other phase
!ENDIF

everything: $(TARGET)

clean:
[tab]del $(OBJ) $(TARGET)

all: clean everything

.cpp.obj:
[tab]$(CC) $(CCFLAGS) $< /Fo$@ $(FLAGS)

$(TARGET): $(OBJ)
[tab]$(CC) $(OBJ) /Fe$@


這裡要注意的是,在Windows下的nmake與Linux下的make的語法確實有所不同!例如在隱含規則就有非常大的不同。可以參考Makefile - 簡單教學
像是$<(只能用在隱含規則中),而在make就沒有這一個限制!

可以執行以下的命令,看出輸出結果有所不同!
下面三個nmake指印選一個執行
$ nmake /f mymake all name=caspar
$ nmake /f mymake all name=koba
$ nmake /f mymake all

最後再執行編譯出來的結果

$ main.exe

[2011.09.18 補充]
!IF DEFINED(DATE) && DEFINED(TIME)
確認DATE與TIME變數同時有被定義

!IF EXIST(File.txt)
確認File.txt存在時,才做以以下被包住的程式碼

[2011.12.07 補充]
參考簡單的 nmake 使用的 Makefile 範例得知,nmake有一個替換功能
巨集替換

[2012.01.03 補充]
在nmake的指令中,如果要定義巨集有包含空格,請以引號括住這些定義。

這裡要解釋一下,

target : prerequisites
command

1. $@表示target的第一個名字,也就是代表目前的標的(target)
2. $**目前目標的所有相依性(一般linux下是用$^來表示
3. $?同一個規則的所有先決條件名,但是只有原始程式碼改過的比obj檔新才會符合,也就是比target還新的先決條件檔案。
4. $*同一個規則的第一個先決條件,但是不包含副檔名
5. $<具有比目前目標較晚之時間戳記的相依性檔案。只有在介面規則的命令中才有效。


參考資料:
nmake的用法
筆記:學習vc之cl, link, rc常用參數
命令行編譯工具NMAKE
對於.NET中的NMake和Makefile的理解
NMAKE Reference
[C++] nmake 最簡單範例
Nmake Tool
簡單的 nmake 使用的 Makefile 範例

2009年5月1日 星期五

qmake教學

在寫Unix程式時,需要透過Makefile使得在改寫程式碼時,要重新編譯會比較容易。
但是,好的Makefile確是非常難寫。
是的,Qt提供了qmake可以產生Makefile,而基本上,我們只要學會如何寫*.pro檔就可以很容易產生Makefile啦~

一個項目文件是用來告訴qmake關於為這個應用程序建立makefile所需要的細節。例如,一個*.cpp和*.h的列表、任何應用程序特定配置、例如一個必需要連接的函式庫、或者一個額外的包含路徑,都應該放到項目文件中。

「#」註釋

你可以為項目文件添加註釋。註釋由「#」符號開始,一直到這一行的結束。

「TEMPLATE」模板

模板變數告訴qmake為這個應用程序生成哪種makefile。下面是可供使用的選擇:
app - 建立一個應用程序的makefile。這是預設值,所以如果TEMPLATE沒有被指定,這個將被使用。
lib - 建立一個函式庫的makefile。
vcapp - 建立一個應用程序的Visual Studio項目文件。
vclib - 建立一個函式庫的Visual Studio項目文件。
subdirs - 這是一個特殊的模板,它可以建立一個能夠進入特定目錄並且為一個項目文件生成makefile並且為它調用make的makefile。

「app」模板

「app」模板告訴qmake為建立一個應用程序生成一個makefile。當使用這個模板時,下面這些qmake系統變量是被承認的。你應該在你的.pro文件中使用它們來為你的應用程序指定特定信息。

HEADERS - 應用程序中的所有*.h的列表。
SOURCES - 應用程序中的所有*.cpp的列表。
FORMS - 應用程序中的所有.ui文件(由Qt設計器生成)的列表。
LEXSOURCES - 應用程序中的所有lex源文件的列表。
YACCSOURCES - 應用程序中的所有yacc源文件的列表。
TARGET - 可執行應用程序的名稱。默認值為項目文件的名稱。(如果需要擴展名,會被自動加上。)
DESTDIR - 放置可執行程序目標的目錄。
DEFINES - 應用程序所需的額外的預處理程序定義的列表。(就好像gcc中的 -D)
INCLUDEPATH - 應用程序所需的額外的包含路徑的列表。
DEPENDPATH - 應用程序所依賴的搜索路徑。
VPATH - 尋找補充文件的搜索路徑。
DEF_FILE - 只有Windows需要:應用程序所要連接的.def文件。
RC_FILE - 只有Windows需要:應用程序的資源文件。
RES_FILE - 只有Windows需要:應用程序所要連接的資源文件。

你只需要使用那些你已經有值的系統變量,例如,如果你不需要任何額外的INCLUDEPATH,那麼你就不需要指定它,qmake會為所需的提供默認值。例如,一個實例項目文件也許就像這樣:

TEMPLATE = app
DESTDIR = c:\helloapp
HEADERS += hello.h
SOURCES += hello.cpp
SOURCES += main.cpp
DEFINES += QT_DLL
CONFIG += qt warn_on release

如果條目是單值的,比如template或者目的目錄,我們是用「=」,但如果是多值條目,我們使用「+=」來為這個類型添加現有的條目。使用「=」會用新值替換原有的值,例如,如果我們寫了DEFINES=QT_DLL,其它所有的定義都將被刪除。

「lib」模板

「lib」模板告訴qmake為建立一個庫而生成makefile。當使用這個模板時,除了「app」模板中提到系統變量,還有一個VERSION是被支持的。你需要在為庫指定特定信息的.pro文件中使用它們。
VERSION - 目標庫的版本號,比如,2.3.1。

「subdirs」模板

「subdirs」模板告訴qmake生成一個makefile,它可以進入到特定子目錄並為這個目錄中的項目文件生成makefile並且為它調用make。

在這個模板中只有一個系統變量SUBDIRS可以被識別。這個變量中包含了所要處理的含有項目文件的子目錄的列表。這個項目文件的名稱是和子目錄同名的,這樣qmake就可以發現它。例如,如果子目裡是「myapp」,那麼在這個目錄中的項目文件應該被叫做myapp.pro。

CONFIG變量

配置變量指定了編譯器所要使用的選項和所需要被連接的函式庫。配置變量中可以添加任何東西,但只有下面這些選項可以被qmake識別。

下面這些選項控制著使用哪些編譯器標誌:
release - 應用程序將以release模式連編。如果「debug」被指定,它將被忽略。
debug - 應用程序將以debug模式連編。
warn_on - 編譯器會輸出儘可能多的警告信息。如果「warn_off」被指定,它將被忽略。
warn_off - 編譯器會輸出儘可能少的警告信息。

下面這些選項定義了所要連編的庫/應用程序的類型:
qt - 應用程序是一個Qt應用程序,並且Qt庫將會被連接。
thread - 應用程序是一個多線程應用程序。
x11 - 應用程序是一個X11應用程序或庫。
windows - 只用於「app」模板:應用程序是一個Windows下的窗口應用程序。
console - 只用於「app」模板:應用程序是一個Windows下的控制台應用程序。
dll - 只用於「lib」模板:庫是一個共享庫(dll)。
staticlib - 只用於「lib」模板:庫是一個靜態庫。
plugin - 只用於「lib」模板:庫是一個插件,這將會使dll選項生效。

例如,如果你的應用程序使用Qt庫,並且你想把它連編為一個可調試的多線程的應用程序,你的項目文件應該會有下面這行:

CONFIG += qt thread debug

注意,你必須使用「+=」,不要使用「=」,否則qmake就不能正確使用連編Qt的設置了,比如沒法獲得所編譯的Qt庫的類型了。

如果要使用Qt的網路以及多線程函式庫,可以指定
QT += network

在非 Qt 程序中使用 qmake
事實上,qmake非常好用,我們甚至想在非Qt程序中使用它。這也很容易。只要加入
CONFIG -= qt
那麼qmake就不會添加任何和Qt函式庫相關的header file以及lib之類的進來了。

參考資料:
qmake概念
qmake Command Reference

2009年2月11日 星期三

CMake - 指令說明

指令:
PROJECT(projectname [CXX] [C] [Java])
設定這一個project的名稱,並且設定此project使用的語言
例:
此project名稱為HELLO
PROJECT (HELLO)

SET(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])
設定變數VAR的內容值
例:
設定${SRC_LIST}變數內容為main.c t1.c t2.c
SET(SRC_LIST main.c t1.c t2.c)

MESSAGE([SEND_ERROR | STATUS | FATAL_ERROR] "message to display"
...)
這個指令用於向終端輸出用戶定義的訊息,包含三種類型:
SEND_ERROR:產生錯誤,生成過程被跳過
SATUS:輸出等級為1的訊息
FATAL_ERROR:立即終止所有cmake過程

例:輸出PROJECT_SOURCE_DIR
MESSAGE(STATUS "This is SOURCE dir "${PROJECT_SOURCE_DIR})

ADD_EXECUTABLE(exename [WIN32] [MACOSX_BUNDLE] [EXCLUDE_FROM_ALL] source1 source2 ... sourceN)
exename是指要產生出來的可執行檔,而[WIN32] [MACOSX_BUNDLE]分別在windows與mac才需要設定的,這裡不用理它。[EXCLUDE_FROM_ALL]是先把此目錄編譯排除在外,例如"examples資料夾",而source則是編譯出執行檔exename所需要的原始碼)
ADD_EXECUTABLE(hello main.c func.c)
或
ADD_EXECUTABLE(hello ${SRC_LIST})

ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
這一個指令可以加入目前工程的子目錄,並且可以指定object與target存放的位置,而[EXCLUDE_FROM_ALL]是把此目錄在編譯過程排除在外。例如工程的examples目錄。可能是project完成後,再進入examples目錄單獨編譯
把src目錄加入工程,並且指定編譯輸出路徑為bin
ADD_SUBDIRECTORY(src bin)

INSTALL(TARGETS targets...
[[ARCHIVE|LIBRARY|RUNTIME] [DESTINATION <dir>] [PERMISSIONS permissions...]
[CONFIGURATIONS
[Debug|Release|...]]
[COMPONENT <component>]
[OPTIONAL]
] [...])
INSTALL指令用於定義安裝規則,安裝的內容可以包括執行檔、shared library、static library、文件、目錄、腳本
可執行檔myrun安裝到${CMAKE_INSTALL_PREFIX}/bin目錄
shared library:libmylib安裝到${CMAKE_INSTALL_PREFIX}/lib目錄
static library:libmystaticlib安裝到${CMAKE_INSTALL_PREFIX}/libstatic目錄

INSTALL(TARGETS myrun mylib mystaticlib
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION libstatic
)


普通文件的安裝:
INSTALL(FILES files... DESTINATION <dir>
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[RENAME <name>] [OPTIONAL])
如果若不設定權限PERMISSIONS,安裝後的權限為644權限。

非目標的可執行檔(例如腳本之類的)
INSTALL(PROGRAMS files... DESTINATION <dir>
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[RENAME <name>] [OPTIONAL])

跟上面的FILES指令使用方法一樣,唯一的不同是安裝後權限為755

目錄的安裝:
INSTALL(DIRECTORY dirs... DESTINATION <dir>
[FILE_PERMISSIONS permissions...]
[DIRECTORY_PERMISSIONS permissions...]
[USE_SOURCE_PERMISSIONS]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[[PATTERN <pattern> | REGEX <regex>]
[EXCLUDE] [PERMISSIONS permissions...]] [...])

DIRECTORY後面連接的是所在Source目錄的相對路徑,但務必注意:
abc和abc/有很大的區别。
如果目錄名不以/结尾,那麼這個目錄将被安裝為目標路徑下的abc,如果目錄名以/结尾,
代表将這個目錄中的内容安装到目標路徑,但不包括這個目錄本身。
PATTERN用於使用正規表示式進行過濾,PERMISSIONS用於指定PATTERN過濾後的文件

將icons目錄安裝到 /share/myproj,将scripts/中的内容安装到
/share/myproj
不包含目錄名為CVS的目录,對於scripts/*文件指定權限為 OWNER_EXECUTE
OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ.
INSTALL(DIRECTORY icons scripts/ DESTINATION share/myproj
PATTERN "CVS" EXCLUDE
PATTERN "scripts/*"
PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ
GROUP_EXECUTE GROUP_READ)


$ cmake -DCMAKE_INSTALL_PREFIX=/usr ..
則會把安裝目錄設定在/usr下,但是,若沒有設定的話預設是/usr/local的目錄下

ADD_LIBRARY(libname [SHARED|STATIC|MODULE]
[EXCLUDE_FROM_ALL]
source1 source2 ... sourceN)

SHARED,動態函式庫
STATIC,靜態函式庫
MODULE,在使用dyld的系统有效,如果不支持dyld,则被當作SHARED對待。
EXCLUDE_FROM_ALL參數的意思是這個函式庫不會被默認建構,除非有其他的组件依賴或是手工建構。



變數:
PROJECT_BINARY_DIR
表示執行cmake的路徑
PROJECT_SOURCE_DIR
表示source的路徑,也就是主要的CMakeList.txt的位置
EXECUTABLE_OUTPUT_PATH
指定最終可執行檔存放的位置
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
LIBRARY_OUTPUT_PATH
設定最終產生的shared library存放的位置
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)

Makefile產生後,make所使用的參數:
DCMAKE_INSTALL_PREFIX
設定執行檔安裝的路徑
cmake -DCMAKE_INSTALL_PREFIX=/usr .

2009年2月10日 星期二

自動產生Makefile - CMake

在尋找autotools的文章當中,發現,有一個新的東西CMake,聽說比automake好用。
那就研究一下吧~

實驗一:單一程式檔的 Hello World:
先來個第一步吧~hello world!!
建立CMake/hello/
下面有兩個檔案:CMakeLists.txt與hello.c

ren@notebook:~/Desktop$ tree CMake
CMake
|-- hello
| |-- CMakeLists.txt
| `-- hello.c

hello.c
#include <stdio.h>
int main(){
printf("Hello World!\n");
return 0;
}

CMakeLists.txt
# Project name
PROJECT (HELLO)
# set the variable HELLO_SRCS to be "hello.c"
SET (HELLO_SRCS hello.c)
# helloDemo executable built from hello.c
ADD_EXECUTABLE (hello ${HELLO_SRCS})

$ cmake .
$ make VERBOSE=1


實驗二:把 Hello World 變成函式庫
實驗三:試試使用外部函式庫 libpng 的情況
實驗四:換試試寫個簡單的 Qt4 程式
請參考這裡

小技巧:
哇!在產生Makefile的過程中,會產生一堆檔案,因為要透過svn上資料到server的話,沒有把這一些資料刪除,會很占空間的,這個時候out-source building就派上用場啦~
在剛剛的hello資料夾下產生一個build/的資料夾
$ mkdir build
$ cd build
$ cmake ..
$ make

這個時候產生的檔案都會在build/的資料夾下喔~要刪掉直接刪它就好啦~

用CMake編出來的Makefile在編譯時,不會出現詳細的編譯過程,原來加上VERBOSE=1就可以啦
$ make VERBOSE=1

CMakeLists.txt的內容說明
# Project name
PROJECT(HELLO)
# Source code subdirectories
SUBDIRS(src test)
# Create a library from the hello.cxx file
ADD_LIBRARY(Hello hello.c)
# Location of library include files
INCLUDE_DIRECTORIES(${HELLO_SOURCE_DIR}/src)
# Library location for the linker
LINK_DIRECTORIES(${HELLO_BINARY_DIR}/src)
# helloDemo executable built from demo.c
ADD_EXECUTABLE(helloDemo demo.c)
# Link the executable to the Hello library.
TARGET_LINK_LIBRARIES(helloDemo Hello)


參考資料:
把玩 CMake 的第一步
CMake
貓也會的 CMake
CMake實踐
Jserv「貓也會的 CMake」簡報投影片
在 linux 下使用 CMake 構建應用程序

2009年2月7日 星期六

【轉貼】自動產生Makefile - autoconf & automake

在 Unix 上寫程式的人大概都碰過 Makefile,尤其是用 C 來開發程式的人。用 make來開發和編譯程式的確很方便,可是要寫出一個 Makefile就不簡單了。偏偏介紹 Makefile 的文件不多,GNU Make 那份印出來要幾百頁的文件,光看完 Overview 就快陣亡了,難怪許多人聞 Unix 色變。

本文將介紹如何利用 GNU Autoconf 及 Automake 這兩套軟體來協助我們『自動』產生 Makefile 檔,並且讓開發出來的軟體可以像 Apache, MySQL 和常見的 GNU 軟體一樣,只要會 ``./configure'', ``make'', ``make install'' 就可以把程式安裝到系統中。如果您有心開發 Open Source 的軟體,或只是想在 Unix 系統下寫寫程式。希望這份介紹文件能幫助您輕鬆地進入 Unix Programming 的殿堂。

1. 簡介

Makefile 基本上就是『目標』(target), 『關連』(dependencies) 和『動作』三者所組成的一連串規則。而 make 就會根據 Makefile 的規則來決定如何編譯 (compile) 和連結 (link) 程式。實際上,make 可做的不只是編譯和連結程式,例如 FreeBSD 的 port collection 中, Makefile 還可以做到自動下載原始程式套件,解壓縮 (extract) ,修補 (patch),設定,然後編譯,安裝至系統中。

Makefile 基本構造雖然簡單,但是妥善運用這些規則就也可以變出許多不同的花招。卻也因此,許多剛開始學習寫 Makefile 時會感到沒有規範可循,每個人寫出來的 Makefile 長得都不太一樣,不知道從何下手,而且常常會受限於自己的開發環境,只要環境變數不同或路

徑改一下,可能Makefile 就得跟著修改。雖然有 GNU Makefile Conventions (GNU Makefile 慣例) 訂出一些使用 GNU 程式設計時撰寫 Makefile 的一些標準和規範,但是內容很長而且很複雜, 並且經常做些調整,為了減輕程式設計師維護 Makefile 的負擔,因此有了Automake。

程式設計師只需寫一些預先定義好的巨集 (macro),交給 Automake 處理後會產生一個可供Autoconf 使用的 Makefile.in 檔。再配合利用Autoconf 產生的自動設定檔configure即可產生一份符合 GNU Makefile慣例的 Makeifle 了。

2. 上路之前

在開始試著用 Automake 之前,請先確認你的系統已經安裝以下的軟體:

1. GNU Automake

2. GNU Autoconf

3. GNU m4

4. perl

5. GNU Libtool (如果你需要產生 shared library)

我會建議你最好也使用 GNU C/C++ 編譯器 、GNU Make 以及其它 GNU 的工具程式來做為開發的環境,這些工具都是屬於 Open Source Software不僅免費而且功能強大。如果你是使用Red Hat Linux 可以找到所有上述軟體的 rpm 檔,FreeBSD 也有現成的 package 可以直接安裝,或著你也可以自行下載這些軟體的原始檔回來 DIY。以下的範例是在 Red Hat Linux 5.2 + CLE2 的環境下所完成的。

3. 一個簡單的例子



Automake 所產生的 Makefile 除了可以做到程式的編譯和連結,也已經把如何產生程式文件(如 manual page, info 檔及 dvi 檔) 的動作,還有把原始程式包裝起來以供散 的動作都考慮進去了,所以原始程式所存放的目錄架構最好符合 GNU 的標準慣例,接下來我拿hello.c 來做為例子。

在工作目錄下建立一個新的子目錄 ``devel'',再在 devel 下建立一個``hello'' 的子目錄,這個目錄將作為我們存放 hello 這個程式及其相關檔案的地方:

% mkdir devel

% cd devel

% mkdir hello

% cd hello


用編輯器寫個 hello.c 檔,

#include <stdio.h>
int main(int argc, char** argv)
{
printf("Hello, GNU!");
return 0;
}

接下來就要用 Autoconf 及 Automake 來幫我們產生 Makefile 檔了,

1. 用 autoscan 產生一個 configure.in 的雛型,執行 autoscan 後會產生一個configure.scan 的檔案,我們可以用它做為configure.in檔的藍本。

% autoscan

% ls

configure.scan hello.c


2. 編輯 configure.scan 檔,如下所示,並且把它的檔名改成configure.in

dnl Process this file with autoconf to produce a con figure script.

AC_INIT(hello.c)

AM_INIT_AUTOMAKE(hello, 1.0)

dnl Checks for programs.

AC_PROG_CC

dnl Checks for libraries.

dnl Checks for header files.

dnl Checks for typedefs, structures, and compiler ch aracteristics.

dnl Checks for library functions.

AC_OUTPUT(Makefile)


3. 執行 aclocal 和 autoconf ,分別會產生 aclocal.m4 及 configure 兩個檔案

% aclocal

% autoconf

% ls

aclocal.m4 configure configure.in hello.c



4. 編輯 Makefile.am 檔,內容如下

AUTOMAKE_OPTIONS= foreign

bin_PROGRAMS= hello

hello_SOURCES= hello.c


5. 執行 automake --add-missing ,Automake 會根據 Makefile.am 檔產生一些檔案,包含最重要的 Makefile.in

% automake --add-missing

automake: configure.in: installing `./install-sh'

automake: configure.in: installing `./mkinstalldirs'

automake: configure.in: installing `./missing'


6. 最後執行 ./configure ,

% ./configure

creating cache ./config.cache

checking for a BSD compatible install... /usr/bin/in stall -c

checking whether build environment is sane... yes

checking whether make sets ${MAKE}... yes

checking for working aclocal... found

checking for working autoconf... found

checking for working automake... found

checking for working autoheader... found

checking for working makeinfo... found

checking for gcc... gcc

checking whether the C compiler (gcc ) works... yes

checking whether the C compiler (gcc ) is a cross-co mpiler... no

checking whether we are using GNU C... yes

checking whether gcc accepts -g... yes

updating cache ./config.cache

creating ./config.status

creating Makefile



現在你的目錄下已經產生了一個 Makefile 檔,下個 ``make'' 指令就可以開始編譯 hello.c 成執行檔,執行 ./hello 和 GNU 打聲招呼吧!

% make

gcc -DPACKAGE="hello" -DVERSION="1.0" -I. -I. -g -O2 -c he llo.c

gcc -g -O2 -o hello hello.o

% ./hello

Hello! GNU!



你還可以試試 ``make clean'',''make install'',''make dist'' 看看會有什麼結果。你也可以把產生出來的 Makefile 秀給你的老闆,讓他從此對你刮目相看 :-)

4. 一探究竟



上述產生 Makefile 的過程和以往自行編寫的方式非常不一樣,捨棄傳統自行定義 make 的規則,使用 Automake 只需用到一些已經定義好的巨集即可。我們把巨集及目標 (target)寫在 Makefile.am 檔內,Automake讀入 Makefile.am 檔後會把這一串已經定義好的巨集展

開並且產生對應的Makefile.in 檔, 然後再由 configure 這個 shell script 根據Makefile.in 產生適合的 Makefile。

在此範例中可藉由 Autoconf 及 Automake 工具所產生的檔案有 configure.scan、aclocal.m4、configure、Makefile.in,需要我們加入設定者為 configure.in 及 Makefile.am。

4.1 編輯 configure.in 檔

Autoconf 是用來產生 'configure' 檔的工具。'configure' 是一個shell script,它可以自動設定原始程式以符合各種不同平台上 Unix 系統的特性,並且根據系統三數及環境產生合適的 Makefile 檔或是C 的標頭檔 (header file),讓原始程式可以很方便地在這些不同的平台上被編譯出來。Autoconf 會讀取 configure.in 檔然後產生 'configure' 這個shell script。

configure.in 檔的內容是一連串 GNU m4 的巨集,這些巨集經過autoconf 處理後會變成檢查系統特徵的 shell script。configure.in 內巨集的順序並沒有特別的規定,但是每一個 configure.in 檔必須在所有巨集前加入 AC_INIT 巨集,然後在所有巨集的最後面加上 AC_OUTPUT 巨集。我們可先用 autoscan 掃瞄原始檔以產生一個 configure.scan 檔,再對 configure.scan 做些修改成 configure.in 檔。在範例中所用到的巨集如下:

dnl

這個巨集後面的字不會被處理,可視為註解。

AC_INIT(FILE)

這個巨集用來檢查原始碼所在的路徑,autoscan 會自動產生,我們不必修改它。

AM_INIT_AUTOMAKE(PACKAGE,VERSION)

這是使用 Automake 所必備的巨集,PACKAGE 是我們所要產生軟體套件的名稱,VERSION 是版本編號。

AC_PROG_CC

檢查系統可用的 C 編譯器,如果原始程式是用 C 寫的就需要這個巨集。

AC_OUTPUT(FILE)

設定 configure 所要產生的檔案,如果是 Makefile 的話,configure 便會把它檢查出來的結果帶入 Makefile.in 檔然後產生合適的 Makefile。

實際上,我們使用 Automake 時,還須要一些其它的巨集,這些額外的巨集我們用 aclocal來幫我們產生。執行 aclocal 會產生 aclocal.m4檔,如果沒有特別的用途,我們可以不必修改它,用 aclocal 所產生的巨集會告訴 Automake 怎麼做。

有了 configure.in 及 aclocal.m4 兩個檔案後,便可以執行 autoconf來產生 configure檔了。

4.2 編輯 Makefile.am 檔



接下來我們要編輯 Makefile.am 檔,Automake 會根據 configure.in 中的巨集把Makefile.am 轉成 Makefile.in 檔。Makefile.am 檔定義我們所要產的目標:

AUTOMAKE_OPTIONS

設定 automake 的選項。Automake 主要是幫助開發 GNU 軟體的人員維護軟體套件,所以在執行 automake 時,會檢查目錄下是否存在標準 GNU 軟體套件中應具備的文件檔案,例如 'NEWS'、'AUTHOR'、'ChangeLog' 等文件檔。設成 foreign 時,automake 會改用一般軟體套件的標準來檢查。

bin_PROGRAMS

定義我們所要產生的執行檔檔名。如果要產生多個執行檔,每個檔名用空白字元隔開。

hello_SOURCES

定義 'hello' 這個執行檔所需要的原始檔。如果 'hello' 這個程式是由多個原始檔所產生,必須把它所用到的原始檔都列出來,以空白字元隔開。假設 'hello' 這個程式需要 'hello.c'、'main.c'、

'hello.h'

三個檔案的話,則定義

hello_SOURCES= hello.c main.c hello.h

如果我們定義多個執行檔,則對每個執行檔都要定義相對的filename_SOURCES。

編輯好 Makefile.am 檔,就可以用 automake --add-missing 來產生Makefile.in。加上 --add-missing 選項是告訴 automake 順便幫我們加入包裝一個軟體套件所必備的檔案。Automake 產生出來的 Makefile.in檔是完全符合 GNU Makefile 的慣例,我們只要執行 configure 這個shell script 便可以產生合適的 Makefile 檔了。

4.3 使用 Makefile

利用 configure 所產生的 Makefile 檔有幾個預設的目標可供使用,我們只拿其中幾個簡述如下:

make all

產生我們設定的目標,即此範例中的執行檔。只打 make 也可以,此時會開始編譯原始碼,然後連結,並且產生執行檔。

make clean

清除之前所編譯的執行檔及目的檔 (object file, *.o)。

make distclean

除了清除執行檔和目的檔外,也把 configure 所產生的 Makefile也清除掉。

make install

將程式安裝至系統中。如果原始碼編譯無誤,且執行結果正確,便可以把程式安裝至系統預設的執行檔存放路徑。如果我們用bin_PROGRAMS 巨集的話,程式會被安裝至 /usr/local/bin 這個目錄。

make dist

將程式和相關的檔案包裝成一個壓縮檔以供散播 (distribution) 。執行完在目錄下會產生一個以 PACKAGE-VERSION.tar.gz 為名稱的檔案。PACKAGE 和 VERSION 這兩個變數是根據 configure.in 檔中AM_INIT_AUTOMAKE(PACKAGE, VERSION) 的定義。在此範例中會產生

'hello-1.0.tar.gz' 的檔案。

make distcheck

和 make dist 類似,但是加入檢查包裝後的壓縮檔是否正常。這個目標除了把程式和相關檔案包裝成 tar.gz 檔外,還會自動把這個壓

縮檔解開,執行 configure,並且進行 make all 的動作,確認編譯無誤後,會顯示這個 tar.gz 檔已經準備好可供散播了。這個檢查非常有用,檢查過關的套件,基本上可以給任何一個具備 GNU 發展境的人去重新編譯。就 hello-1.tar.gz 這個範例而言,除了在 Red

Hat Linux 上,在 FreeBSD 2.2.x 版也可以正確地重新編譯。

要注意的是,利用 Autoconf 及 Automake 所產生出來的軟體套件是可以在沒有安裝 Autoconf 及 Automake 的環境上使用的,因為 configure 是一個 shell script,它己被設計可以在一般 Unix 的 sh 這個 shell 下執行。但是如果要修改 configure.in 及 Makefile.a

m 檔再產生新的configure 及 Makefile.in 檔時就一定要有 Autoconf 及 Automake 了。

5. 相關訊息



Autoconf 和 Automake 功能十分強大,你可以從它們所附的 info 檔找到詳細的用法。你也可以從許多現存的 GNU 軟體或 Open Source 軟體中找到相關的 configure.in 或 Makefile.am 檔,它們是學習 Autoconf 及Automake 更多技巧的最佳範例。

這篇簡介只用到了 Autoconf 及 Automake 的皮毛罷了,如果你有心加入Open Source 軟體開發的行列,希望這篇文件能幫助你對產生 Makefile有個簡單的依據。其它有關開發 GNU程式或 C 程式設計及 Makefile 的詳細運用及技巧,我建議你從 GNU Coding Standards3(GNU 編碼標準規定) 讀起,裡面包含了 GNU Makefile 慣例,還有發展 GNU 軟體套件的標準程序和慣例。這些 GNU 軟體的線上說明文件可以在http://www.gnu.org/ 這個網站上找到。

6. 結語



經由 Autoconf 及 Automake 的輔助,產生一個 Makefile 似乎不再像以前那麼困難了,而使用 Autoconf 也使得我們在不同平台上或各家 Unix之間散播及編譯程式變得簡單,這對於在 Unix 系統上開發程式的人員來說減輕了許多負擔。妥善運用這些 GNU 的工具軟體,可

以幫助我們更容易去發展程式,而且更容易維護原始程式碼。

一九九八年是 Open Source 運動風起雲湧的一年,許多 Open Source 的軟體普遍受到網路上大眾的歡迎和使用。感謝所有為 Open Source 奉獻的人們,也希望藉由本文能吸引更多的人加入『自由』、『開放』的 OpenSource 行列。

About this document ...

輕輕鬆鬆產生 Makefile1

This document was generated using the LaTeX2HTML translator Version 98.2 beta6 (August 14th, 1998) Copyright (C) 1993, 1994, 1995, 1996, Nikos Drakos, ComputerBased Learning Unit, University of Leeds.Copyright (C) 1997, 1998, Ross Moore, Mathematics Department,Macquarie University, Sydney.

The command line arguments were:

latex2html -split 0 -show_section_numbers automake.tex

The translation was initiated by on 1999-02-11

Footnotes

... itle1

本文件使用 ChiLaTeX 製作。

... CLE2

CLE (Chinese Linux Extension,Linux 中文延伸套件),

http://cle.linux.org.tw/

... Standards3

GNU Coding Standards, Richard Stallman.

藍森林 http://www.lslnet.com 2001年3月22日 08:44

作 者: 許明彥



參考資料:
【轉貼】makefile
CVS, Automake與Autoconf簡介
Linux系統下Autoconf/AutoMake學習筆記
利用 Autotools 來建立 Makefile 檔案
CVS和automake
automake
Using Automake and Autoconf

2008年8月19日 星期二

Makefile - 簡單教學

在寫大量的程式時,通常要編譯、組譯~
可能要打一堆指令,這一個時候就出現了Makefile的東西了~

先寫一個簡單的東西吧,以下是一個簡單的Makefile的內容
food.h、food.cpp、main.cpp請參考這裡
Makefile

# Program, flags, etc.
ASM = g++
ASMFLAGS =
OBJ=food.o main.o
TARGET=main

.PHONY: everything clean all

everything: $(TARGET)

clean:
  -rm -fv $(OBJ) $(TARGET)

all: clean everything

food.o: food.cpp food.h
  $(ASM) -c $< -o $@

main.o: main.cpp
  $(ASM) -c $< -o $@

main: $(OBJ)
  @$(ASM) $(OBJ) -o $@


這裡要解釋一下,


target : prerequisites
  command

0. 想要得到target,需要執行命令command
1. target依賴於prerequisites,當prerequisites中至少有一個檔案比target檔新時,command才會被執行,而且command前面的空格是用Tab鍵產生的
2. 以字元#開頭的行是注釋。
3. =是用來定義變數,這裡ASM與ASMFLAGS就是兩個變數
4. .PHONY應該指的是make後面可以接的參數指令,那就表示它後面的名字並不是檔案,而僅僅是一種行為的標號,例如make clean、make everything、make all
5. $@表示target的第一個名字,也就是代表目前的標的(target)
6. $<表示prepequisites的第一個名字
7. $?同一個規則的所有先決條件名,但是只有原始程式碼改過的比obj檔新才會符合,也就是比target還新的先決條件檔案。
8. $*同一個規則的第一個先決條件,但是不包含副檔名
9. $^所有先決條件,但是有的make像solaris make, M$ nmake可能不認得這個自動變數。(在nmake是用$**表示)
10. 使用變數用$(VAR)或者${VAR}都可以。
11. 如果要用$,請多加一個$變成$,在Shell Command會用到Shell 變數此時就要加$。
12. 在要執行的指令前面加@,則不會顯示編譯的過程喔~像上面的例子的最後一行,就不會出現編譯的過程哩
13. 若直接輸入make,這時make程式會從第一個名字所代表的動作執行。在本例中,第一個標號是everything,所以make和make everything是一樣的。
14. 在要執行的指令前面加-,表示即使該行指令出錯,也不會中斷Makefile的執行。

當我們要產生執行檔時或是有修改檔案後,要重新編譯,我們就打以下指令

[root@host ~]# make


當我們要刪除執行檔和物件檔時,我們就打以下指令

[root@host ~]# make clean


你看,是不是就很節省時間啊~
而且,現在有很多程式都可以幫我們產生makefile,像是QT就是啦,不過,我們至少要知道它們是怎麼運作的,這樣之後要改也比較方便啊~

[2009.02.28 補充]
若只在指令列上打
$ make
則會尋找Makefile的內容來執行。
若有多個makefile的檔案,則可以使用-f的參數來指定是要使用哪一個makefile
$ make -f makefile
若目錄中有多個資料夾,而每一個資料夾均有一個makefile,則也可以使用make -f的方式由上一層的makefile來控制下面的所有的makefile
下面有一個例子:
情況是makefile.server負責產生server的執行檔
而makefile.client負責產生client的執行檔
我們們直接透過Makefile來完成所有的動作
Makefile
.PHONY: everything all clean clean_target

everything:
make -f makefile.server
make -f makefile.client

all: clean_target everything

clean:
make -f makefile.server clean
make -f makefile.client clean

clean_target:
make -f makefile.server clean_target
make -f makefile.client clean_target


[2009.04.01 補充]
可以使用include的指令喔~
自己的內隱規則
因為可能有的時候你希望做些dependency檢查,或者加上一些gcc 用的旗標,不是很單純的編譯而已你可以給make自訂的內隱規則
樣式規則(pattern rule)
你可以用pattern rule來做一些自定的內隱規則。像這樣
  %.o : %.c prog.h
$(CC) $(CFLAGS) $(DEBUG_FLAG) -c -o $@ $<

%表示所有相對於後面先決條件的檔名的意思,他不是*,因為他 有一對一的相對應關係,foo.o 就要找foo.c,foo1.o就要找foo1.c, 所以他不是*。所以上面的意思是所有碰到要.o的target時,去找相 對應的.c檔,並根據先決條件prog.h做檢查,如果找不到prog.h就不 做下去了。%不只可以表現主檔名,其實可以表現任一個相對應的字串, 所以叫pattern,你可以用s.%.c,不只用&.c,其中對應到%的子字串叫stem

[2009.04.02 補充]
-i   忽略指令執行返回的出錯資訊。
-s   沉默模式,在執行之前不輸出相應的指令行資訊。
-r   禁止使用build-in規則。
-n   非執行模式,輸出所有執行指令,但並不執行。
-t   更新目標文件。
-q   make操作將根據目標文件是否已經更新返回"0"或非"0"的狀態資訊。
-p   輸出所有巨集定義和目標文件描述。
-d   Debug模式,輸出有關文件和檢測時間的詳細資料。

Linux下make標誌位的常用選項與Unix系統中稍有不同,下面我們只列出了不同部分:
-C dir   在讀取 makefile 之前改變到指定的目錄dir。
-I dir   當包含其他 makefile文件時,利用該選項指定搜尋目錄。
-M dir    改變目錄後,再回到原本的目錄 
-h   help文擋,顯示所有的make選項。
-w   在處理 makefile 之前和之後,都顯示工作目錄。

遞迴make
在Makefile裡面叫make來用,請用$(MAKE),例如我們常在source code的 最上層打個make, 它會自動跑到底下所有的目錄去,裡面都有個Makefile,然後每個都做make。 例如
all:
cd user;$(MAKE) $@
cd kernel;$(MAKE) $@


[2009.04.03 補充]
進入每一個子目錄執行Makefile
Makefile例:

SUBDIR = B C
.PHONY: alldir $(SUBDIR)

alldir: $(SUBDIR)

$(SUBDIR):
$(MAKE) -C $@
B: C


這個 Makefile 會依照 SUBDIR 變數裡所包含的資料夾名稱, 一一進去執行make, 需要注意的是第 9 行的敘述, 這是一個處理相依性的例子, "B: C"的意思是, 編譯 B 之前, 要先編譯 C.

[2009.04.06 補充]
以下這一個例子是說明如何在Makefile中使用shell script到達到進入每一個子目標執行其目錄的Makefile,要注意的一點是若要使用到shell的$必需使用$$來表達
# 列出所有的子目錄
SUBDIRS = lib client/version01 server/01_iterative_server server/02_concurrent_server

# 列出所有的make後面可接的指令
INSTR   = everything all clean distclean

.PHONY: $(INSTR)

# 透過shell的指令到每一個子目錄尋找Makefile並執行其makefile
$(INSTR): $(SUBDIRS)
for i in $(SUBDIRS); do (cd $$i && make $@);done


[2009.04.12 補充]
指派巨集的優先順序(Priority of Macro Assignments)

如果以不同方式定義的巨集互相有衝突時(也就是不同的來源定義了相同的巨集名稱),make會採用哪個定義呢? 比如說,如果在描述檔裡頭就定義了DIR,其內容為/usr/proj,但是在目前的環境變數中,也有一個叫做DIR的環境變數,其內容為/usr/proj/lib,那麼make會採用哪一個定義呢? 下面就是優先權的順序,從優先權最大到優先權最小:

1.在使用make指令時,所定義的巨集,但是是位於make指令之後,相較於第2項,雖然同樣都是在命令列上,但是因為順序上的差異引發了微小的不同.
2.在描述檔裡頭的巨集定義.
3.目前的環境變數. 在Korn shell和Bourne shell裡頭,如果把巨集定義在make指令之前的話,此巨集相當於環境變數.
4.make內部的(default,內定的)已有的定義.

說明define-endef指令
在Makefile中若有一些指令複合指令常常一起出現,那麼我們就可以透過define-endef指令它們合併成一個指令,例:
define run-yacc
echo "Hello World"
mv $< $@
endef

foo.c:foo.y
$(run-yacc)

如此一來,當我要執行run-yacc時,他就會轉換成執行
echo "Hello World"
與
mv $< $@ 這兩個指令 控制語法
除了ifeq, ifneq還有ifdef, ifndef, endif, else, define-endef, include,很像c裡面的前置處理
例子:
ifeq ($(CC),gcc)
libs = "good"
else
libs = "not good"
endif

ifneq ($(CC),gcc)
lib = "bad"
else
lib = "not bad"
endif

default:
echo $(libs)
echo $(lib)
echo $(CC)

[2015.03.18 補充]
今天追了Makefile的code,
記錄一下兩個小技巧

因為makefile在編譯的過程中,
會有很多指令,
就算全部讓他顯示出來,
仍會出現太多資訊的情況,能不能像windows一樣,有pause的指令呢?
事情是是有的。
read -p "Press [Enter] key to start backup..."

另外一個是一個很神奇的東西,
就是原本指令只可以執行在command這一個區塊裡面,
然而,我今天看到一個非正規的方式,我覺得是非常不好的,
但是,還是記錄下來。
CP_UICONFIG := $(shell cp $(ROOTFS_PATH)/glue/mk.uicompo $(shell pwd))
這一道指令看似是在做變數給值的動作,實際上是在做cp的動作,
重點是,這一道指令可以不用放在command的區域,
可以放在Makefile一開始設定變數的區域~
害我今天一直找不到檔案在哪裡被更動的@@

另外同時記錄Makefile的四種等號
=
直接assign變數

:=
Makefile會先把整個Makefile展開後,再決定Makefile的變數值。
例如:
x = foo
y = $(x) bar
x = xyz

最後結果y變數會是xyz bar

若要以當時的變數值來設定的話,就要用:=來處理
x := foo
y := $(x) bar
x := xyz

此例最後y變數為foo bar

?=
當變數沒有被定義時,則使用右邊的數值,
若有被定義時,則使用原本的數值

參考資料:
跟我一起寫Makefile
參考資料:文魁資訊-自己動手寫作業系統

[2008.09.19 補充]
Makefile撰寫
Makefile 語法簡介
make 命令和 makefile
Makefile學習教程: 跟我一起寫 Makefile
如何寫一個簡單的 Makefile [1][2]
Tutorial - Makefile
Make - a tutorial
Linux/Unix環境下的make和makefile詳解
GNU make中文手册