Simple use of makefile

目录

一、Makefile示例及基本使用

1.1 使用依赖规则

1.2 使用伪目标

1.3 搜索源文件

二、使用变量

2.1 赋值操作符

2.1.1 使用”=”操作符

2.1.2 使用”:=” 操作符

2.1.3 使用”?=” 操作符

2.1.4 追加变量的值

2.1.5 自动化变量

三、使用函数

3.1  wildcard函数

3.2 patsubst函数

四、使用命令

4.1 命令出错

五、使用条件判断

六、makefile生成多个可执行程序

七、其它   


一、Makefile示例及基本使用

例1

Makefile内容

# Makefile for myself
# Created, 2017.04.14
##############################################################################

CC                  = g++

SRC_DIR  = ../src
OBJ_DIR  = ../obj
INC_DIR  = ../include


LDFLAGS  = -luuid
CFLAGS   = -I$(INC_DIR)


OBJS     := $(patsubst $(SRC_DIR)/%.cpp, $(OBJ_DIR)/%.o, $(wildcard $(SRC_DIR)/*.cpp))


EXEC        := result
TARGET      := ../bin/$(EXEC)


vpath %.cpp $(SRC_DIR)


$(TARGET) : $(OBJS)
    $(CC) -o $(TARGET) $(OBJS) $(LDFLAGS)



$(OBJ_DIR)/%.o : %.cpp

    @echo [`date "+%Y-%m-%d %H:%M:%S"`] Compile $< ......

    $(CC) $(CFLAGS) -c $< -o $@




.PHONY : clean

clean :

    rm -rf $(TARGET) $(OBJS)

 

目录结构:    

[songbw@hadoop read_configuration_file_general_makefile]$ tree
.
|-- Makefile_original_edition
|-- bin
|-- cfg
|   `-- SeperateFile.cfg
|-- include
|   |-- ReadConfig.h
|   `-- SeperateFile.h
|-- obj
|-- src
|   |-- ReadConfig.cpp
|   |-- SeperateFile.cpp
|   |-- tags
|   `-- testSeperateFileClass.cpp
`-- unix
    |-- Makefile
    |-- Makefile.20200405
    |-- Makefile.bak.bak
    `-- Makefile.bak.impurity

6 directories, 12 files
[songbw@hadoop read_configuration_file_general_makefile]$

整体所在的目录是  /home/songbw/total_Task/read_configuration_file_general_makefile  (公司开发机)

后来copy了一份到我自己的mac,/Users/songbw/OwnProject/read_configuration_file_general_makefile/unix ,只不过mac上缺少 库 /lib/libuuid.so.1.2 ,所以暂时编译不过去

        上面的Makefile_original_edition,是根据/home/songbw/cti/recordback/recordTar_pingAn_showXian/fps/unix/Makefile 搂出来的,我认为关于makefile最基础的东西  

简单解释:  
Makefile 是根据依赖规则执行的,而且好像默认情况下,只会执行第一条依赖规则,
然后根据这条依赖规则去找相应的依赖,除非有all的依赖规则,会执行all 的依赖规则  
如上例中,第一条依赖规则是  $(TARGET) : $(OBJS)  
其中 TARGET 是 result 也就是生成的可执行程序 
OBJS 是 .o 文件,
那么为了生成 result,它要去找 .o 文件,
.o文件没有,但由第二条依赖规则  
$(OBJ_DIR)/%.o : %.cpp  知道 .o 文件依赖于 .cpp文件  
而依赖规则下面的是,如何生成对应的目标的命令   
因为 cpp 文件是存在的,所以就根据此命令生成了 .o 文件  
.o 文件存在了,就根据第一条目标规则下面的命令,生成可执行程序 result   
简单来讲,Makefile 就是这么一个过程

 

  • 上例中 Makefile 的展开

例2

vpath  %.cpp ../src
result : ReadConfig.o SeperateFile.o testSeperateFileClass.o
    g++   -o ../bin/result ../obj/ReadConfig.o ../obj/SeperateFile.o ../obj/testSeperateFileClass.o -luuid

ReadConfig.o : ReadConfig.cpp
    g++ -c   ../src/ReadConfig.cpp  -I../include  -o ../obj/ReadConfig.o

SeperateFile.o : SeperateFile.cpp 
    g++ -c  ../src/SeperateFile.cpp  -I ../include -o  ../obj/SeperateFile.o

testSeperateFileClass.o : testSeperateFileClass.cpp 
    g++ -c  ../src/testSeperateFileClass.cpp -I ../include  -o ../obj/testSeperateFileClass.o 

clean:
    rm  ../bin/result ../obj/ReadConfig.o ../obj/SeperateFile.o ../obj/testSeperateFileClass.o

.IGNORE : clean

 

1.1 使用依赖规则

Command表示命令,如果其不与目标文件和目标文件所依赖的文件在同一行,则必须以Tab键开头,如果和文件依赖规则在一行,那么可以用分号隔开。

 

        对于依赖规则的检查,makefile会优先检查目标文件依赖的文件是否存在,若目标文件依赖的文件是cpp文件,则会去磁盘查找文件是否存在,它是依赖于vpath的,如果是.o文件,它是由.cpp文件生成的,所以理论上它应该是另一个依赖规则的目标文件,那么.o文件就必须和另一个依赖规则的目标文件保持一致,如之前在makefile中尝试过的例子, 

#../bin/result : ../obj/ReadConfig.o ../obj/SeperateFile.o ../obj/testSeperateFileClass.o

的依赖的文件是

../obj/ReadConfig.o ../obj/SeperateFile.o ../obj/testSeperateFileClass.o

而你下面的依赖规则

SeperateFile.o : SeperateFile.cpp ../include/SeperateFile.h

的目标文件确是 SeperateFile.o,如果将目标文件改成 ../obj/SeperateFile.o

就没毛病,也即将依赖规则改为

../obj/SeperateFile.o : SeperateFile.cpp ../include/SeperateFile.h

 

注意:

        虽然 cpp 文件的查找是依赖于vpath的,但是之前用makefile的时候发现,它也会去当前目录下查找 cpp 文件,因为有一次,备份一个cpp文件到makefile的同一级目录,然后去改 src下面的cpp文件,无论如何加日志,可执行程序就是打不出来日志,后来把makefile同一级的那个 cpp 文件删掉就好了

 

1.2 使用伪目标

           在上面的例子中多次提到一个称为clean的目标,这是一个伪目标

clean :

rm -rf $(TARGET) $(OBJS)

.PHONY : clean

注意:伪目标的依赖文件也为伪目标的情况没有看,因为觉得暂时用不到,用到了再说吧

 

1.3 搜索源文件

使用vpath关键字,那么 vpath 关键字的作用和实践层面的意义在哪里呢?

        使Makefile可以通过 vpath 关键字,找到依赖规则中,依赖列表中的文件,举两个小例子,一个是

vpath  %.cpp ../src
ReadConfig.o : ReadConfig.cpp

显然依赖规则中,依赖列表中的文件 ReadConfig.cpp ,makefile是找不到的,因为它是在上一层的src目录下,如果不使用语句 vpath  %.cpp ../src 标识,Makefile是无法执行的,因为它不知道 ReadConfig.cpp 在哪里,第二个例子是使用自动化变量时,如下

vpath  %.cpp ../src

%.o : %.cpp
    g++ -c $< -I../include -o $@

假如 ../src 目录下面,有文件 ReadConfig.cpp,那么针对文件 ReadConfig.cpp,依赖规则 %.o : %.cpp 会被解析成 

ReadConfig.o : ../src/ReadConfig.cpp ,而不是解析成 ReadConfig.o : ReadConfig.cpp ,显然,如果解析成 ReadConfig.o : ReadConfig.cpp 的话,g++ 命令根本无法执行,因为$<会替换成依赖规则中的第一个文件,也即 ReadConfig.cpp,而 ReadConfig.cpp是找不到的,只有在 ../src中才能找到,好,下面开始 vpath 之旅:

vpath关键字非常灵活,它可以指定不同的文件在不同的搜索目录中,其使用方法如下:

vpath  %.cpp ../src

%.cpp表示所有以.cpp结尾的文件,它是要搜索的文件的模式,../src 指定了搜索%.cpp所指定的目录。

后两个关于清除搜索目录的,不是特别理解,先搁置吧

注意:

  1. vpath 只是针对依赖规则中的cpp文件、c文件生效和.h文件
  2. vpath 并不针对命令生效
  3. <<Linux c 程序设计王者归来>>中,有种方式是设置vpath变量,如 vapth = src:../include ,

    但不知道为什么,在我的Makefile中并不生效,先搁置

 

针对例2中下面一段代码

ReadConfig.o : ../src/ReadConfig.cpp

g++ -c   ../src/ReadConfig.cpp  -I../include  -o ../obj/ReadConfig.o

testSeperateFileClass.o : testSeperateFileClass.cpp 

g++ -c  ../src/testSeperateFileClass.cpp -I ../include  -o ../obj/testSeperateFileClass.o

 

解释:

目标依赖文件 ../src/ReadConfig.cpp 显然是可以找到的,因为直接写的是全路径,但是目标依赖文件 testSeperateFileClass.cpp 是如何找到的呢?

        显然在../unix目录下找不到文件 testSeperateFileClass.cpp 因为前面设置了vpath关键字

vpath  %.cpp ../src

所以Makefile在../src文件中就找到了 testSeperateFileClass.cpp 。

目标依赖文件 ReadConfig.o : ../src/ReadConfig.cpp 的命令写成

g++ -c   ReadConfig.cpp  -I../include  -o ../obj/ReadConfig.o 能否执行呢?

答案是否定的,对于命令,Makefile会原样的方式执行,不要以为它会将文件 ReadConfig.cpp 自动变为 ../src/ ReadConfig.cpp 那是不可能的。

 

二、使用变量

       例1中大量使用变量,这样增强了makefile文件的灵活性,可以轻易将代码移植到一个新的平台,此Makefile中的 LDFLAGS  = -luuid ,若起到其它的平台或许需要链接其它的库,这时候只需要改动变量 LDFLAGS ,添加其它的库就行了,makefile的大部分东西都不需要变化。

 

2.1 赋值操作符

2.1.1 使用”=”操作符

2.1.2 使用”:=” 操作符

2.1.3 使用”?=” 操作符

2.1.4 追加变量的值

 

解释:

可以结合例1,好好体会下 =、:=、?=、+= 的用法

 

2.1.5 自动化变量

        makefile中允许使用自动化变量,这些变量的名称是由make工具已经定义好的。Makefile中有7个自动化变量,但我只列出3个经常会用到的,如下所示:

 

解释:

这些自动化变量都是针对依赖规则的,它会针对每一条依赖规则展开,去用自动化变量,如下例子

vpath  %.cpp ../src

$(OBJ_DIR)/%.o : %.cpp

    @echo [`date "+%Y-%m-%d %H:%M:%S"`] Compile $< ......

    $(CC) $(CFLAGS) -c $< -o $@

针对

$(OBJ_DIR)/%.o : %.cpp

会展开成如下3条

../obj/ReadConfig.o : ../src/ReadConfig.cpp
../obj/SeperateFile.o : ../src/SeperateFile.cpp
../obj/testSeperateFileClass.o : ../src/testSeperateFileClass.cpp

拿其中一条

../obj/ReadConfig.o : ../src/ReadConfig.cpp

来说,要执行的编译语句是

g++ -I../include -c ../src/ReadConfig.cpp -o ../obj/ReadConfig.o

那么 ../obj/ReadConfig.o 刚好是依赖规则中的目标文件集,就可以用 $@ 代替啊,而 ../src/ReadConfig.cpp 刚好是依赖列表中的第一个依赖的名字,就可以用$<代替。

 

三、使用函数

3.1  wildcard函数

例3

Makefile如下

# Makefile for myself

# Created, 2017.04.14

##############################################################################

CC                  = g++

SRC_DIR  = ../src

OBJ_DIR  = ../obj

INC_DIR  = ../include

SRC_CON  := $(wildcard $(SRC_DIR)/*.cpp)

all :

echo $(SRC_CON)

.PHONY: all

执行结果:

解析:

       wildcard把 指定目录 ../src/下的所有后缀是cpp的文件全部展开。-s 是让echo命令生效。

3.2 patsubst函数

Makefile如下:

# Makefile for myself

# Created, 2017.04.14

##############################################################################

CC                  = g++

SRC_DIR  = ../src

OBJ_DIR  = ../obj

INC_DIR  = ../include

OBJS     := $(patsubst $(SRC_DIR)/%.cpp, $(OBJ_DIR)/%.o, $(wildcard $(SRC_DIR)/*.cpp))

all :

echo $(OBJS)

.PHONY: all

执行结果:

解析:

模式字符串替换函数 patsubst

由函数wildcard 的功能知,$(wildcard $(SRC_DIR)/*.cpp 的结果是:

../src/ReadConfig.cpp ../src/SeperateFile.cpp ../src/testSeperateFileClass.cpp

而函数patsubst 的功能就是将../src/%.cpp 替换成../obj/%.o

 

四、使用命令

注意

        如果make工具执行时,make的参数是-n或者--just-print,则只会显示命令,而不会执行命令,我们可以用这种方式调试makefile文件,可以打印出命令在make过程中执行的顺序。make的参数是-s或者--silent,则禁止所有命令的显示,不论该命令前是否有@符。

 

4.1 命令出错

Makefile内容如下:

# Makefile for myself

# Created, 2017.04.14

##############################################################################

clean :

    rm  ../bin/result ../obj/ReadConfig.o ../obj/SeperateFile.o ../obj/testSeperateFileClass.o 

.PHONY: clean

 

执行结果:

 

解释:

       出错了,makefile就会停止执行

 

更改Makefile中的内容如下:

# Makefile for myself

# Created, 2017.04.14

##############################################################################

clean :

    -rm  ../bin/result ../obj/ReadConfig.o ../obj/SeperateFile.o ../obj/testSeperateFileClass.o 

.PHONY: clean

 

执行结果:

解释:

       错误已经忽略,这时候Makefile是可以继续往下运行的

 

五、使用条件判断

简单小例子    

COMPUTER_TYPE= $(shell uname -m)
ifeq ($(COMPUTER_TYPE), aarch64)
        a = deps_aarch64
else
        a = deps
endif

all:
        echo $(a)
.PHONY: all

执行结果:   

[songbw@host-192-168-0-147 Makefile_test]$ uname -m

aarch64

[songbw@host-192-168-0-147 Makefile_test]$ make -s all

deps_aarch64

[songbw@host-192-168-0-147 Makefile_test]$

分析:  

代码

COMPUTER_TYPE= $(shell uname -m)   

是将命令  uname -m 的结果送给变量 COMPUTER_TYPE, 所以变量 COMPUTER_TYPE 的值是 aarch64, 代码  

ifeq ($(COMPUTER_TYPE), aarch64)

所用是判断 变量 COMPUTER_TYPE 的值是否是 aarch64, 和编程语言中的 if else 类似,执行结果简单看下,就明白了   

引申,if 的多层次判断

TARGET_ARCH = xxx

ifeq ($(TARGET_ARCH), arm)
    LOCAL_SRC_FILES := a
else ifeq ($(TARGET_ARCH), x86)
    LOCAL_SRC_FILES := b
else ifeq ($(TARGET_ARCH), mips)
    LOCAL_SRC_FILES := c
else
    LOCAL_SRC_FILES := d
endif

all:
        echo $(LOCAL_SRC_FILES)
.PHONY: all

执行结果:  

[songbw@host-192-168-0-147 Makefile_test]$ make -s all

d

[songbw@host-192-168-0-147 Makefile_test]$

说明:  

Makefile 中是只测试了最后一个 else,但其它的是都测试过的,没问题  

 

六、makefile生成多个可执行程序

如下例子

# Makefile for myself
# Created, 2017.04.14
##############################################################################
CC                  = g++
PUBLIC_HOME         = /home/songbw/cti/public

#------------------------------------------------------------------------------
# Public library path are setted here except oracle
#------------------------------------------------------------------------------
LOG4CPPZ_HOME = $(PUBLIC_HOME)/log4cppz
ICE_HOME        = $(PUBLIC_HOME)/ice



SRC_DIR = ../src
SRC_COMMON_DIR  = ../src/common
SRC_CLIENT_DIR  = ../src/client
SRC_SERVER_DIR  = ../src/server

OBJ_COMMON_DIR  = ../obj/common
OBJ_CLIENT_DIR  = ../obj/client
OBJ_SERVER_DIR  = ../obj/server

INC_DIR  = ../include

LDFLAGS  = -L$(LOG4CPPZ_HOME)/lib -L$(ICE_HOME)/lib
LDFLAGS  += -luuid -llog4cppz -lIceUtil -lIce
CFLAGS   = -I$(INC_DIR) -I$(LOG4CPPZ_HOME)/include -I$(ICE_HOME)/include

OBJS_COMMON     := $(patsubst $(SRC_COMMON_DIR)/%.cpp, $(OBJ_COMMON_DIR)/%.o, $(wildcard $(SRC_COMMON_DIR)/*.cpp))
OBJS_CLIENT     := $(patsubst $(SRC_CLIENT_DIR)/%.cpp, $(OBJ_CLIENT_DIR)/%.o, $(wildcard $(SRC_CLIENT_DIR)/*.cpp))
OBJS_SERVER     := $(patsubst $(SRC_SERVER_DIR)/%.cpp, $(OBJ_SERVER_DIR)/%.o, $(wildcard $(SRC_SERVER_DIR)/*.cpp))

EXEC_CLIENT        := QQClient
TARGET_CLIENT      := ../bin/$(EXEC_CLIENT)

EXEC_SERVER        := QQServer
TARGET_SERVER      := ../bin/$(EXEC_SERVER)

vpath %.cpp $(SRC_COMMON_DIR)
vpath %.cpp $(SRC_CLIENT_DIR)
vpath %.cpp $(SRC_SERVER_DIR)

all:$(TARGET_CLIENT) $(TARGET_SERVER)

$(TARGET_CLIENT) : $(OBJS_COMMON) $(OBJS_CLIENT)
	$(CC) -o $(TARGET_CLIENT) $(OBJS_COMMON) $(OBJS_CLIENT) $(LDFLAGS)

$(TARGET_SERVER) : $(OBJS_COMMON) $(OBJS_SERVER)
	$(CC) -o $(TARGET_SERVER) $(OBJS_COMMON) $(OBJS_SERVER) $(LDFLAGS)

$(OBJ_COMMON_DIR)/%.o : %.cpp
	@echo [`date "+%Y-%m-%d %H:%M:%S"`] Compile $< ......
	$(CC) $(CFLAGS) -c $< -o $@

$(OBJ_CLIENT_DIR)/%.o : %.cpp
	@echo [`date "+%Y-%m-%d %H:%M:%S"`] Compile $< ......
	$(CC) $(CFLAGS) -c $< -o $@

$(OBJ_SERVER_DIR)/%.o : %.cpp
	@echo [`date "+%Y-%m-%d %H:%M:%S"`] Compile $< ......
	$(CC) $(CFLAGS) -c $< -o $@

.PHONY : clean
clean :
	rm -rf $(TARGET_CLIENT) $(TARGET_SERVER) $(OBJS_COMMON) $(OBJS_CLIENT) $(OBJS_SERVER)

 

        就是多了一行 all:$(TARGET_CLIENT) $(TARGET_SERVER),否则,只会生成可执行程序QQClient,不信自己可以测试一下,但是如果加上这一行,只需要执行make命令,不需要make all ,就可以把可执行程序QQClient 和 QQServer 都生成,所以我感觉,执行make命令时,会自动找到 Makefile 中的第一条依赖规则,将这条规则执行完后,就等于make命令执行完了,它不会将所有的依赖规则都执行一遍,存放目录:

/home/songbw/myQQ/unix

演示如下:

七、其它   

make JS_DIST=/full/path/to/directory/containing/nspr JS_THREADSAFE=1 -f Makefile.ref

make -f 指定文件进行 make ,不一定是 Makefile 了,比如上面指定的文件是   Makefile.ref   

JS_THREADSAFE=1 是文件 Makefile.ref 中有变量 JS_THREADSAFE,make时把它的值设置成1  

JS_DIST=/full/path/to/directory/containing/nspr 同理  

make -c

make -c 是进入到某个目录下进行 make   

 

make 安装啊,以 cmake 为例   

>./configure --prefix=/opt/cmake

make  

make install  

然后所有的东西的就都在 /opt/cmake 下面了啊 

 

 

 

参考文献:  

https://blog.csdn.net/liwugang43210/article/details/47840429

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值