makefile 完美教程


簡介

Makefile 是和 make 命令一起配合使用的,很多大型項目的編譯都是通過 Makefile 來組織的,。
我建立工程的方法有以下三點:
1.makefile:
優點:使用非常廣泛,通用性強,可跨平台。
缺點:語法比較蛋疼。要寫出一個通用,便於管理,且兼容性強的makefile比較困難。

2.cmake:
優點:簡單易用,使用較廣泛,方便管理,可跨平台。
缺點:自動生成的makefile太臃腫。

3.sh腳本:
優點:自由,高度定制。簡單易用,可操作性強。方便維護。(甚至還可以生成makefile)
缺點:sh建的工程太少了(估計沒人這么搞吧)

但我考慮到方便移植和管理其他人的工程還是選擇了第一種,以makefile建立管理工程。(其實我的內心是比較向往第三種sh腳本建工程的)。下面來介紹下makefile的規則以及語法

PS:我本意是想寫短一點的,只是想寫點常用的東西,方便大家查閱。精煉了這么久,可還有這么多內容(15000字)。所以,大家還是耐心的學習吧,想學好linux這是必不可少的一步。


規則

說明

<target1 > <target2>.... : <prerequisites1> <prerequisites2>...
[TAB] <command1>
[TAB] <command2>
...

target可以是一個object file(目標文件),也可以是一個執行文件,還可以是一個標簽(label)。對於標簽這種特性,在后續的“偽目標”章節中會有敘述。

<prerequisites>就是,要生成那個target所需要的文件或是目標。

<command>也就是make需要執行的命令。(任意的shell命令)

這是一個文件的依賴關系,也就是說,<target>這一個或多個的目標文件依賴於<prerequisites>中的文件,其生成規則定義在 <command>中。說白一點就是說,<prerequisites>中如果有一個以上的文件比<target>文件要新的話,<command>所定義的命令就會被執行。這就是makefile的規則。也就是makefile中最核心的內容。

經典示例

原始版本

objects = main.o kbd.o command.o display.o \
		insert.o search.o files.o utils.o

edit : $(objects)
	cc -o edit $(objects)
main.o : main.c defs.h
	cc -c main.c
kbd.o : kbd.c defs.h command.h
	cc -c kbd.c
command.o : command.c defs.h command.h
	cc -c command.c
display.o : display.c defs.h buffer.h
	cc -c display.c
insert.o : insert.c defs.h buffer.h
	cc -c insert.c
search.o : search.c defs.h buffer.h
	cc -c search.c
files.o : files.c defs.h buffer.h command.h
	cc -c files.c
utils.o : utils.c defs.h
	cc -c utils.c
clean :
	rm edit $(objects)

自動推導版本

objects = main.o kbd.o command.o display.o \
		insert.o search.o files.o utils.o
 cc = gcc

edit : $(objects)
	cc -o edit $(objects)

main.o : defs.h
kbd.o : defs.h command.h
command.o : defs.h command.h
display.o : defs.h buffer.h
insert.o : defs.h buffer.h
search.o : defs.h buffer.h
files.o : defs.h buffer.h command.h
utils.o : defs.h

.PHONY : clean
clean :
	rm edit $(objects)

理解

1.內容展開

list_a = 1.a 2.a
list_b = 1.b 2.b 3.b

#list_a中的元素每次單個展開連接list_b中的所有元素
#結果如下
test:list_a
%.a:list_b #展開:
	1.a:1.b 2.b 3.b		#此時&@=1.a $<=1.b 2.b 3.b
	2.a:1.b 2.b 3.b		#此時&@=2.a $<=1.b 2.b 3.b

#內容還可以繼續展開
%.b:%.x #自動推導展開
	1.b:1.x
	2.b:2.x
	3.b:3.x

2.include 相當於將內容復制展開,export可以共享全局變量。

3.用make遍歷子目錄makefile時相當於,用新進程開啟make。因此export不能共享全局變量。

注意

1.注意空格與TAB(有些編輯器會自動將TAB轉換成4個空格,難以發現)

2.注意 = 與 := 的區別
包括export 在內的所有變量都不能共享
如果使用cd &&make進行遍歷。那么最好轉換成絕對路徑

3.如果使用cd &&make進行遍歷。那么最好轉換成絕對路徑


語法

1.符號

1) 命令前綴

[不用前綴 ]輸出執行的命令以及命令執行的結果, 出錯的話停止執行
[前綴 @]只輸出命令執行的結果, 出錯的話停止執行
[前綴 -]命令執行有錯的話, 忽略錯誤, 繼續執行

2) 通配符

*表示任意一個或多個字符
? 表示任意一個字符
[...] [abcd] 表示a,b,c,d中任意一個字符, [^abcd]表示除a,b,c,d以外的字符, [0-9]表示 0~9中任意一個數字

3) 自動變量

在下文的 自動變量

4) 賦值

= :=

相同點:都是給內容賦值;
區別:與Verilog的=和<=類似。
其中 = 和 := 的區別在於 :

= 只能使用前面定義好的變量,
= 可以使用后面定義的變量

賦值符號 [=]

= 最后再賦值

# Makefile內容
OBJS2 = $(OBJS1) programC.o
OBJS1 = programA.o programB.o

all:
    @echo $(OBJS2)

# bash中執行 make, 可以看出雖然 OBJS1 是在 OBJS2 之后定義的, 但在 OBJS2中可以提前使用
$ make
programA.o programB.o programC.o

賦值符號 [:=]

:= 立即賦值

# Makefile內容
OBJS2 := $(OBJS1) programC.o
OBJS1 := programA.o programB.o

all:
    @echo $(OBJS2)

# bash中執行 make, 可以看出 OBJS2 中的 $(OBJS1) 為空
$ make
programC.o

賦值符號 [+=]

+= 變量追加值

# Makefile內容
SRCS := programA.c programB.c programC.c
SRCS += programD.c

all:
    @echo "SRCS: " $(SRCS)

# bash中運行make
$ make
SRCS: programA.c programB.c programC.c programD.c

5) 偽目標

.PHONY

.PHONY : clean
clean :
	-rm edit $(objects)

2. 隱含規則

1) 自動推倒命名:

編譯C時,*.o 的目標會自動推導為 *.c

2) 隱含變量

[RM] rm -f
[AR] ar
[CC] cc
[CXX] g++
[ARFLAGS] AR命令的參數
[CFLAGS] 語言編譯器的參數
[CXXFLAGS] C++語言編譯器的參數

3) 自動變量

[$@] 目標集合
[$%] 當目標是函數庫文件時, 表示其中的目標文件名
[$<] 第一個依賴目標. 如果依賴目標是多個, 逐個表示依賴目標
[$?] 比目標新的依賴目標的集合
[$^] 所有依賴目標的集合, 會去除重復的依賴目標
[$+] 所有依賴目標的集合, 不會去除重復的依賴目標
[$*] 這個是GNU make特有的, 其它的make不一定支持

1.o: 1.c
	@echo $@ : $< 
	
# bash中執行 make:
1.o : 1.c

3.定義

不限於makefile還有部分shell指令

1) 查看依賴關系

gcc -MM

$ gcc -MM kvm_main.c 
kvm_main.o: kvm_main.c iodev.h coalesced_mmio.hasync_pf.h  
 #這句就可以加到 Makefile 中作為編譯 kvm_main.o 的依賴關系

2) 定義命令包

define <command-name>
command
...
endef

# Makefile 內容
define run-hello-makefile
@echo -n "Hello"
@echo " Makefile!"
@echo "這里可以執行多條 Shell 命令!"
endef

all:
    $(run-hello-makefile)


# bash 中運行make
$ make
Hello Makefile!
這里可以執行多條 Shell 命令!

3) 條件判斷

# Makefile 內容
all:
ifeq ("aa", "bb")
    @echo "equal"
else
    @echo "not equal"
endif

# bash 中執行 make
$ make
not equal

4) 自定義函數

$(call <expression>,<parm1>,<parm2>,<parm3>...)

# Makefile 內容
hello = "hello " $(1) " world"

all:
    @echo $(call hello,"我是參數1")

# bash 中執行 make
$ make
hello 我是參數1 world

5) 執行shell指令

$(shell <shell command>) 與 `<shell command>`
作用:執行一個shell命令, 並將shell命令的結果作為返回.
注意:` 是反引號(英文狀態下,鍵盤的ESC下面那個鍵)

4.函數

不限於makefile還有部分shell指令

1) 傳參(同一個進程)

export

export EX_VAR = value
export EX_VAR := value
export EX_VAR += value
#= := += 與上面的描述基本相同

注意:是同一個進程下的make才有用。當多級遍歷make時是無法全局的。

2) 字符串處理

(1) 字符串替換函數

$( subst<source>,<new>,<text>)

功能: 把字符串<text> 中的<source> 替換為<new>
返回: 替換過的字符串

# Makefile 內容
all:
    @echo $(subst t,e,maktfilt)  <-- 將t替換為e

# bash 中執行 make
$ make
makefile

(2) 字符串中每個元素替換函數

$( patsubst <source>,<new>,<text>)

功能: 把字符串 <text> 中的的每個元素 <source> 替換為<new>
返回: 替換過的字符串

# Makefile 內容
all:
    @echo $(patsubst %.c,%.o,programA.c programB.c)

# bash 中執行 make
$ make
programA.o programB.o

(3) 去空格函數

$(strip <text>)

功能: 去掉 <string> 字符串中開頭和結尾的空字符
返回: 被去掉空格的字符串值

# Makefile 內容
VAL := " aa bb cc "

all:
    @echo "去除空格前: " $(VAL)
    @echo "去除空格后: " $(strip $(VAL))

# bash 中執行 make
$ make
去除空格前: aa bb cc 
去除空格后: aa bb cc

(4) 判斷字符串內是否存在特定字符串

$(findstring<text>,<elem>)

功能: 在字符串 <text> 中查找 <elem> 字符串是否存在
返回: 如果找到, 返回 <elem> 字符串, 否則返回空字符串

# Makefile 內容
VAL := " aa bb cc "

all:
    @echo $(findstring aa,$(VAL))
    @echo $(findstring ab,$(VAL))

# bash 中執行 make
$ make
aa

3) 文件元素相關

(1) 取文件函數

保留符合條件的元素

$(filter <elem1 elem2...>,<text>)

# Makefile 內容
all:
    @echo $(filter %.o %.a,program.c program.o program.a)
	
# bash 中執行 make
$ make
program.o program.a
program.c
去掉符合條件的元素

$(filter-out <elem1 elem2...>,<text>)

# Makefile 內容
all:
    @echo $(filter-out %.o %.a,program.c program.o program.a)

# bash 中執行 make
$ make
program.c
獲取該目錄下所有文件

獲取該目錄下所有.x文件
$(wildcard *.x)

# Makefile 內容
all:
    @echo $(wildcard *.c)

# bash 中執行 make
$ make
a.x b.x c.x

(2) 路徑函數

去掉路徑

$(notdir <elem elem...>)

功能: 去掉序列的路徑
返回: 文件名序列 <elem> 中的非目錄部分

# Makefile 內容
all:
    @echo $(notdir /home/a.c ./bb.c ../c.c d.c)

# bash 中執行 make
$ make
a.c bb.c c.c d.c
取路徑

$(dir <elem1 elem2...>)

功能: 從文件名序列 <elem> 中取出目錄部分
返回: 文件名序列 <elem> 中的目錄部分

# Makefile 內容
all:
    @echo $(dir /home/a.c ./bb.c ../c.c d.c)


# bash 中執行 make
$ make
/home/ ./ ../ ./

(3) 取前后綴

取后綴函數

$(suffix <elem1 elem2...>)

功能: 從文件名序列 <elem> 中取出各個文件名的后綴
返回: 文件名序列 <elem> 中各個文件名的后綴, 沒有后綴則返回空字符串

# Makefile 內容
all:
    @echo $(suffix /home/a.c ./b.o ../c.a d)

# bash 中執行 make
$ make
.c .o .a
取前綴函數

$(basename <elem1 elem2...>)

功能: 從文件名序列 <elem> 中取出各個文件名的前綴
返回: 文件名序列 <elem> 中各個文件名的前綴, 沒有前綴則返回空字符串

# Makefile 內容
all:
    @echo $(basename /home/a.c ./b.o ../c.a /home/.d .e)


# bash 中執行 make
$ make
/home/a ./b ../c /home/

(3) 增添前后綴

增添后綴函數

$(addsuffix <suffix>,<elem1 elem2...>)

功能: 把后綴 <suffix> 加到 <elem> 中的每個單詞后面
返回: 加過后綴的文件名序列

# Makefile 內容
all:
    @echo $(addsuffix .c,/home/a b ./c.o ../d.c)


# bash 中執行 make
$ make
/home/a.c b.c ./c.o.c ../d.c.c
增添前綴函數

$(addprefix <prefix>, <elem1 elem2...>)

功能: 把前綴 <prefix> 加到 <elem> 中的每個單詞前面
返回: 加過前綴的文件名序列

# Makefile 內容
all:
    @echo $(addprefix test_,/home/a.c b.c ./d.c)

# bash 中執行 make
$ make
test_/home/a.c test_b.c test_./d.c

我寫的管理大型項目的makefile

以下內容為基礎版本。可以混編C和C++。
但沒有加入平台兼容性驗證等功能。
在ubuntu16.04下測試通過。

tree
.
── app
   ── app1
      ── main.c
      └── makefile
   └── makefile
── make_conf.mk
── makefile
── make_fun.mk
── module
   ── makefile
   └── test
       ── drive
          ── cpp_test.cpp
          ── cpp_test.h
          ── test.c
          └── test.h
       ── hal
          ── test_hal.c
          └── test_hal.h
       └── makefile
└── output
    ── bin
       ── app1.bin
       └── app2.bin
    ── lib
       └── libobj.a
    └── obj
        ── app
           ── app1
              └── main.o
           └── app2
               └── main.o
        └── module
            └── test
                ── drive
                   ── cpp_test.o
                   └── test.o
                └── hal
                    └── test_hal.o

17 directories, 22 files

頂層 makefile

# -------------------------------------------
# FileName :makefile
# Author :wind 
# Date :2018-1-16
# Description 
#
# Top makefile.
#
# ------------------------------------------- 

# 設置當前路徑
DIR_ROOT:=.

# 設置遞歸子目錄
DIR_LIST_SUB :=module app


include $(DIR_ROOT)/make_conf.mk
all:make_root
clean:make_clean
include $(DIR_ROOT)/make_fun.mk

中間遍歷層 makefile

# -------------------------------------------
# FileName :xx/makefile
# Author :wind 
# Date :2018-1-16
# Description 
#
# Level1 makefile.
#
# ------------------------------------------- 

# 設置當前路徑
DIR_ROOT:=..

# 添加遞歸子目錄
DIR_LIST_SUB :=\
app1\
app2\


# 添加源文件
FILE_LIST_C +=\


FILE_LIST_CPP +=\


FILE_LIST_S +=\



# 添加頭文件路徑
DIR_LIST_INCLUDE+=\




include $(DIR_ROOT)/make_conf.mk 
all:make_show make_subdir
clean:make_clean
include $(DIR_ROOT)/make_fun.mk

app的makefile

# -------------------------------------------
# FileName :xx/xx/makefile
# Author :wind 
# Date :2018-1-16
# Description 
#
# Level2 makefile.
#
# ------------------------------------------- 

# 設置當前路徑
DIR_ROOT:=../..

# 添加遞歸子目錄
DIR_LIST_SUB :=\

# 添加源文件
FILE_LIST_C +=\
main.c\


FILE_LIST_CPP +=\


FILE_LIST_S +=\



# 添加頭文件路徑
DIR_LIST_INCLUDE+=\
$(DIR_ROOT)/module/test/hal\
$(DIR_ROOT)/module/test/drive\



include $(DIR_ROOT)/make_conf.mk 
all:make_show make_app
clean:make_clean
include $(DIR_ROOT)/make_fun.mk

drive的makefile

# -------------------------------------------
# FileName :xx/xx/makefile
# Author :wind 
# Date :2018-1-16
# Description 
#
# Level2 makefile.
#
# ------------------------------------------- 

# 設置當前路徑
DIR_ROOT:=../..

# 添加遞歸子目錄
DIR_LIST_SUB :=\

# 添加源文件
FILE_LIST_C +=\
drive/test.c\
hal/test_hal.c\

FILE_LIST_CPP +=\
drive/cpp_test.cpp\

FILE_LIST_S +=\


# 添加頭文件路徑
DIR_LIST_INCLUDE+=\
hal\
drive\



include $(DIR_ROOT)/make_conf.mk 
all:make_show make_lib_a
clean:make_clean
include $(DIR_ROOT)/make_fun.mk

配置文件 make_conf.mk

# -------------------------------------------
# FileName :make_set.inc
# Author :wind 
# Date :2018-1-16
# Description 
#
# 工程相關設置。
#
# -------------------------------------------

# 設置常用指令
# -------------------------------------------
RM = rm -f
MV = mv -f
MKDIR = mkdir -p
RMDIR = rm -rf   


# 顏色輸出
# -------------------------------------------
ECHO_END:=\033[0m" ECHO_GREEN:=echo "\033[32m
ECHO_RED:=echo "\033[31m ECHO_YELLOW:=echo "\033[33m
ECHO_BLUE:=echo "\033[34m ECHO_GREEN_YELLOW:=echo "\033[42;30m

# 編譯缺省設置
# -------------------------------------------

# 默認編譯
CXX:=g++

#編譯選項
COMPILE_C ?= $(CXX)
COMPILE_CXX ?= $(CXX)
COMPILE_ASM ?= $(CXX)
COMPILE_AR ?= ar

# 設置優化等級
OPT ?=0

# 設置警告開關
COMPILE_WARN ?= -Wall  

# 設置靜態編譯
COMPILE_STATIC ?= -s

# 在環境基礎下添加設置
CFLAGS+= $(DIR_LIST_INCLUDE_I) $(COMPILE_WARN) -O$(OPT) $(COMPILE_STATIC)
CXXFLAGS+= $(DIR_LIST_INCLUDE_I) $(COMPILE_WARN) -O$(OPT) $(COMPILE_STATIC)
ASFLAGS+= -Wa,-adhlns=$(<:.S=.lst),-gstabs $(DIR_LIST_INCLUDE_I)

# 編譯設置匯總 
COMPILE_CFLAGS = $(CFLAGS)
COMPILE_CXXFLAGS = $(CXXFLAGS)
COMPILE_ASFLAGS = $(ASFLAGS)

#鏈接選項
LDFLAGS+= -lstdc++#編譯C++
LDFLAGS+= -lpthread#使用了線程
LDFLAGS+= -fPIC#編譯為位置獨立的代碼
LDFLAGS+= -ldl#引用動態庫
LDFLAGS+= $(DIR_LIST_INCLUDE_I)

#引用其他靜態庫
FILE_LIST_LIB_A+=\

功能文件 make_fun.mk

# -------------------------------------------
# FileName :make_fun.mk
# Author :wind 
# Date :2018-1-16
# Description 
#
# 實際編譯過程。
#s
# ------------------------------------------- 

# 路徑關系
# -------------------------------------------
DIR_ROOT_REAL=$(realpath $(DIR_ROOT))
NAME_MODULE := $(notdir $(CURDIR))#所在的文件夾名稱
DIR_OUTPUT:=$(DIR_ROOT)/output
DIR_BIN:=$(DIR_OUTPUT)/bin
DIR_OBJ:=$(DIR_OUTPUT)/obj
DIR_LIB:=$(DIR_OUTPUT)/lib


# 路徑處理成可用參數
# -------------------------------------------
DIR_LIST_INCLUDE_I:=$(addprefix -I,$(DIR_LIST_INCLUDE))#添加編譯選項-I
DIR_LIST_SUB:=$(addprefix $(CURDIR)/,$(DIR_LIST_SUB))#轉換成絕對路徑
DIR_CURDIR:=$(subst $(DIR_ROOT_REAL),,$(CURDIR))#相對路徑
DIR_OBJ_OUT:=$(DIR_OBJ)$(DIR_CURDIR)


#文件處理
FILE_LIST_OBJ:=$(FILE_LIST_C:%.c=%.o)
FILE_LIST_OBJ+=$(FILE_LIST_CPP:%.cpp=%.o)
FILE_LIST_OBJ+=$(FILE_LIST_S:%.s=%.o)

FILE_LIST_OBJ:=$(addprefix $(DIR_OBJ_OUT)/,$(FILE_LIST_OBJ))#轉換成帶絕對路徑的中間文件
FILE_LIB_A:=$(DIR_LIB)/libobj.a
FILE_LIST_LIB_APP:=$(DIR_BIN)/$(NAME_MODULE).bin

# 函數庫
# ------------------------------------------- 
make_root:make_start make_subdir make_end

# 工程開始
make_start:
	@$(ECHO_BLUE)\t-----------------------------$(ECHO_END)
	@$(ECHO_BLUE)\t-\t [編譯開始]  \t    -$(ECHO_END)
	@$(ECHO_BLUE)\t-----------------------------$(ECHO_END)
	@$(ECHO_BLUE)[COMPILE_C]$(COMPILE_C) 	 	[COMPILE_CFLAGS]$(COMPILE_CFLAGS)$(ECHO_END)
	@$(ECHO_BLUE)[COMPILE_CXX]$(COMPILE_CXX) 	[COMPILE_CXXFLAGS]$(COMPILE_CXXFLAGS)$(ECHO_END)
	@$(ECHO_BLUE)[COMPILE_ASM]$(COMPILE_ASM) 	[COMPILE_ASFLAGS]$(COMPILE_ASFLAGS)$(ECHO_END)
	@$(ECHO_BLUE)[COMPILE_AR]$(COMPILE_AR)   	[LDFLAGS]$(LDFLAGS)$(ECHO_END)
	@$(ECHO_BLUE)[OPT]$(OPT)  $(ECHO_END)
	@$(ECHO_BLUE)[COMPILE_STATIC]$(COMPILE_STATIC)  $(ECHO_END)
	@$(ECHO_BLUE)[FILE_LIST_LIB_A]$(FILE_LIST_LIB_A)  $(ECHO_END)


# 工程完成
make_end: 
	@$(ECHO_BLUE)[編譯完成]$(ECHO_END)         

# 遞歸子目錄
make_subdir:
	@for list in $(DIR_LIST_SUB);\
	do\
		cd $$list && make all|| exit 1;\
	done

# 生成可執行文件
make_app:make_lib_a make_bin

# 生成靜態鏈接庫
make_lib_a:make_obj
	@$(MKDIR) `dirname $(FILE_LIB_A)`
	@$(ECHO_YELLOW)[$(COMPILE_AR)]-rcs $(FILE_LIB_A) $(FILE_LIST_OBJ)$(ECHO_END)
	$(COMPILE_AR) -rcs $(FILE_LIB_A) $(FILE_LIST_OBJ)

# 鏈接
make_bin:$(FILE_LIST_LIB_APP)

# 編譯
make_obj:$(FILE_LIST_OBJ)

# 清除
make_clean:
	$(RMDIR) $(DIR_OUTPUT)
	
# 顯示參數,方便調試
make_show:
	@$(ECHO_GREEN_YELLOW)[DIR_CURDIR] $(DIR_CURDIR) $(ECHO_END)
	@$(ECHO_GREEN)[FILE_LIST_C] $(FILE_LIST_C) $(ECHO_END)
	@$(ECHO_GREEN)[DIR_LIST_INCLUDE] $(subst $(DIR_ROOT)/,[DIR_ROOT]/,$(DIR_LIST_INCLUDE)) $(ECHO_END)
	
	
# 文件操作過程
# ------------------------------------------- 

# 編譯過程成中間文件
$(DIR_OBJ_OUT)/%.o: %.c
	@$(MKDIR) `dirname $@`
	@$(ECHO_YELLOW)[$(COMPILE_C)]$< -o $@ $(ECHO_END)
	$(COMPILE_C) -c $(COMPILE_CFLAGS) $< -o $@

$(DIR_OBJ_OUT)/%.o: %.cpp
	@$(MKDIR) `dirname $@`
	@$(ECHO_YELLOW)[$(COMPILE_CXX)]$< -o $@ $(ECHO_END)
	$(COMPILE_CXX) -c $(COMPILE_CXXFLAGS) $< -o $@

$(DIR_OBJ_OUT)/%.o: %.s
	@$(MKDIR) `dirname $@`
	@$(ECHO_YELLOW)[$(COMPILE_C)]$< -o $@ $(ECHO_END)
	$(COMPILE_ASM) -c $(COMPILE_ASFLAGS) $< -o $@

 
# 鏈接成二進制文件
$(FILE_LIST_LIB_APP): $(FILE_LIB_A)
	@$(MKDIR) `dirname $@`
	@$(ECHO_YELLOW)[$(COMPILE_C)]$< -o $@ $(ECHO_END)
	$(COMPILE_C) $(LDFLAGS) -L$(DIR_LIB) -lobj $< $(FILE_LIST_LIB_A) -o $@



內容推薦

跟我一起寫Makefile
Makefile 使用總結
項目實用makefile
Makefile之大型工程項目子目錄Makefile的一種通用寫法

引用本地址

https://www.cnblogs.com/wittxie/p/9836097.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM