简介

在 Makefile 中,有一些内置的变量可以用于表示常见的目录路径。以下是一些常用的 Makefile 内置变量相关的目录:

  1. $(CURDIR): 当前工作目录的绝对路径。
  2. $(SRCDIR): 源代码目录的路径。
  3. $(INCDIR): 头文件目录的路径。
  4. $(BINDIR): 可执行文件目录的路径。
  5. $(LIBDIR): 库文件目录的路径。

这些内置变量可以用于指定源代码、头文件、可执行文件和库文件的目录路径。你可以根据需要在 Makefile 中使用这些变量,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 当前工作目录
CURRENT_DIR := $(CURDIR)

# 源代码目录
SRC_DIR := $(CURRENT_DIR)/src

# 头文件目录
INC_DIR := $(CURRENT_DIR)/include

# 可执行文件目录
BIN_DIR := $(CURRENT_DIR)/bin

# 库文件目录
LIB_DIR := $(CURRENT_DIR)/lib

# 编译选项
CXXFLAGS := -std=c++11 -Wall -I$(INC_DIR)

# 目标文件和可执行文件名
TARGET := $(BIN_DIR)/myapp
OBJS := $(SRC_DIR)/main.o $(SRC_DIR)/foo.o $(SRC_DIR)/bar.o

# 默认目标
all: $(TARGET)

# 生成可执行文件
$(TARGET): $(OBJS)
	$(CXX) $(CXXFLAGS) -o $@ $(OBJS)

# 编译每个源文件
$(SRC_DIR)/%.o: $(SRC_DIR)/%.cpp
	$(CXX) $(CXXFLAGS) -c $< -o $@

# 清理生成的文件
clean:
	rm -f $(OBJS) $(TARGET)

在上述示例中,我们使用了 $(CURDIR) 变量获取当前工作目录的路径,并根据它构建了其他目录的路径。然后,我们使用这些路径变量来设置源代码、头文件、可执行文件和库文件的路径。

示例

编写 Makefile 是一种常用的方式来管理和构建 C++ 项目。下面是一个简单的示例 Makefile,展示了如何编写一个基本的 C++ 项目的 Makefile。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 编译器设置
CXX = g++
CXXFLAGS = -std=c++11 -Wall

# 目标文件和可执行文件名
TARGET = myapp
OBJS = main.o foo.o bar.o

# 默认目标
all: $(TARGET)

# 生成可执行文件
$(TARGET): $(OBJS)
	$(CXX) $(CXXFLAGS) -o $@ $(OBJS)

# 编译每个源文件
%.o: %.cpp
	$(CXX) $(CXXFLAGS) -c $< -o $@

# 清理生成的文件
clean:
	rm -f $(OBJS) $(TARGET)

在上述示例中,我们定义了以下内容:

  • CXXCXXFLAGS 变量用于设置编译器和编译选项。
  • TARGET 变量用于指定最终生成的可执行文件名。
  • OBJS 变量用于指定目标文件(即编译后的中间文件)。
  • all 是默认目标,用于构建整个项目。它依赖于 $(TARGET)
  • $(TARGET) 目标用于生成最终的可执行文件。它依赖于 $(OBJS)
  • %.o 是一个模式规则,用于编译每个源文件。它依赖于对应的 .cpp 文件,并使用 $<$@ 分别表示依赖和目标文件。
  • clean 目标用于清理生成的目标文件和可执行文件。

你可以根据实际项目的需要进行修改和扩展。例如,你可以添加更多的源文件、头文件目录、链接库等。

要使用上述 Makefile,只需在项目根目录下创建一个名为 Makefile(注意大小写),并将上述内容粘贴到文件中。然后,在项目根目录下执行 make 命令即可构建项目。执行 make clean 可以清理生成的文件。

请注意,Makefile 的编写可能会因具体的项目需求和构建环境而有所差异。上述示例仅为一个简单的起点,你可以根据自己的需要进行进一步的定制和优化。同时,还可以考虑使用更高级的构建工具和自动化构建系统(如 CMake、Autotools 等)来管理更复杂的项目。

书写规则:

  • 依赖关系:生成目标的方法

规则语法:

1
2
3
4
5
6
  targets:prerequisites
      command
  targets:文件名,以空格分开,可以使用通配符
  command:命令行,如果不与prerequisites在一行,必须在下一行以Tab键开头,如果在一行,用分号做分割
  prerequisites:目标所依赖的文件
  反斜杠(\):换行符

参数

  • make -C dir, --directory=dir : Change to directory dir before reading the makefiles or doing anything else.

变量:

  • 变量在声明时,需要给予初值,而在使用时,需要给变量名前加上$符号,并且用小括号括起来
  • makefile中变量分为环境变量,内置变量和自动变量
  • 环境变量
    • DESKTOP_SESSION : ubuntu
    • XDG_SESSION_TYPE: x11
    • XAUTHORITY : /run/user/1000/gdm/Xauthority
    • _ : /usr/bin/make
  • 自动变量
    • $@ : 表示规则的目标文件名
    • $< : 规则的第一个依赖文件名。如果是一个目标文件使用隐含规则来重建,则代表由隐含规则加入的第一个依赖文件
    • $^ : 规则的所有依赖文件列表,使用空格分隔。如果目标是静态库文件,它所代表的只能是所有库成员(.0文件)名。一个文件可重复的出现在目标的依赖中,变量$^只记录它的一次引用情况。就是说变量$^会去掉重复的依赖文件。
    • $+ : 类似于$^,但是它保留了依赖文件中重复出现的文件。主要用在程序链接时库的交叉引用场合
  • 内置变量
    • $(CURDIR) : 显示当前路径
    • $(SHELL) : /bin/sh
    • $(CC) : cc , C语言编译器的名称
    • $(CPP) : $(CC) -E , C语言预处理器的名称
    • $(CXX) : g++ , C++语言的编译器的名称
    • $(RM) : rm -f , 删除文件程序的名称
    • $(CFLAGS) : C语言编译器的编译选项,无默认值
    • $(CXXFLAGS): C++语言编译器的编译选项,无默认值

运算符

  • ?=
    • 条件变量分配运算符,它仅在尚未定义的变量时具有效果
  • ` += `
    1
    2
    3
    4
    5
    6
    
      variable = main.o merge.o
      variable += delete.o
      # ALL:$(variable)
      all:
      	@echo ${variable} 
      # 输出: main.o merge.o delete.o
    

函数

  • $(abspath names...) : 获取文件的绝对地址

  • ifeq (condition1, condition2) ... endif
    • 如果condition1 等于 conditon2 , 则执行下面的操作,否则不执行
  • ${addprefix "前缀", filenames...}
    • 为文件加前缀
      1
      2
      3
      4
      5
      6
      
      variable = main.o merge.o
      variable += delete.o
      var = ${addprefix -x/, ${variable}}
      all:
      	@echo ${var} 
      # 输出: -x/main.o -x/merge.o -x/delete.o
      
  • ${wildcard /home/user/workspace/shell/*.sh}
    • 获取指定路径下的指定文件
      1
      2
      3
      4
      5
      
        var = ${wildcard ./*.txt}
        all:
        	@echo ${var} 
        # 输出: ./foo.txt ./bar.txt
      
      
  • ${patsubst pattern, replacement, text}
    • 根据相应规则替换文件名
      1
      2
      3
      4
      5
      6
      
      var = ${wildcard ./*.txt}
      pat = ${patsubst %.txt, %.md, ${var}}
      all:
      	@echo ${var} 
      	@echo ${pat}
      # 输出: ./foo.txt ./bar.txt   \  ./foo.md ./bar.md
      

技巧

Makefile中获取当前执行的Makefile所在路径

  • Linux下我们可以用pwd命令来获取当前所执行命令的目录,在Makefile中对应可用PWD := $(shell pwd)来获取。

  • 但是如果子Makefile 文件是从别处执行的(通过make -f .../Makefile 执行),那么$(shell pwd)得到的目录即为执行make -f命令的当前目录
  • 在这种情况下
    • 根据gnu make定义,gnu make 会自动将所有读取的makefile路径都会加入到MAKEFILE_LIST变量中,而且是按照读取的先后顺序添加
    • 所以可以通过$(abspath $(lastword $(MAKEFILE_LIST)))返回当前正在被执行的Makefile的绝对路径,然后通过patsubst去掉文件名得到绝对目录
  • 代码:
    1
    2
    3
    4
    5
    6
    
      makefile_path:=$(abspath $(lastword $(MAKEFILE_LIST)))
      cur_makefile_path:=$(patsubst %/makefile, %/, ${shell ls $(makefile_path)})
    
      all:
      	@echo ${makefile_path}
      	@echo ${cur_makefile_path}