C++之Makefile语法与写法

始、Makefile的规则

教学博客 系统性的博客2

关键:规则的嵌套——会先完成规则的先验条件

   变量使用时都用${ }包围起来,才能取到变量的实际值,否则取到的就是一个值为变量名的东西了。变量能赋值的,要不然怎么叫变量呢

  这里的变量在实际执行时,会被换成它们的实际值,变量名起简化和占位符的作用。

 

一、变量与函数

1. 函数notdir,wildcard和patsubst

博客

这三个都是函数名,函数格式为:return = $(func arg1,arg2,..),即 $ 和 () 的结合。

例:将当前文件夹中所有以 .c为后缀的文件名替换为以 .o为后缀,并返回一个以空格为间隔的列表

SRC = $(patsubst %.c,%.o,${./})

 2. 逻辑判断  ——参考博客

  ifeq (arg1, arg2):当arg1 == arg2 时,为true。

# 1. 连续的结构,只需要一个endif
ifeq (${mode}, TE)
    EXECUTABLE := te
else ifeq (${mode}, TR)
    EXECUTABLE := tr 
endif

# 2. 非连续的两个结构,每个结构都需要一个endif
ifeq (${mode}, TE)
    EXECUTABLE := te
endif
ifeq (${mode}, TR)
    EXECUTABLE := tr
endif

 

  可以逻辑判断实现条件编译,如下,不同mode值将执行不同的规则。

ifeq (${mode}, train}
.PHONY : target ${TRAIN}
target : ${TRAIN}
else ifeq (${mode}, test}
.PHONY : target ${TEST}
target : ${TEST}
endif

 

 

二、Makefile中的自动化变量$@, $^, $< , $?, $%, $+, $* 等的含义  ——参考博客

$@   表示目标文件
$^   表示所有的依赖文件
$+   这个变量很像“$^”,也是所有依赖目标的集合。只是它不去除重复的依赖目标
$<   表示第一个依赖文件
$?   表示比目标还要新的依赖文件列表 
$%   仅当目标是函数库文件时,表示规则中的目标成员名。例如,如果一个目标是“foo.a(bar.o)”,那么,“$%”就是“bar.o”,“$@”就是“foo.a”。如果目标不是函数库文件(Unix下是[.a],Windows下是[.lib]),那么,其值为空

$*   这个变量表示目标模式中“%”及其之前的部分。如果目标是“dir/a.foo.b”,并且目标的模式是“a.%.b”,那么,“$*”的值就是“dir/a.foo”。这个变量对于构造有关联的文件名是比较有较。
    如果目标中没有模式的定义,那么“$*”也就不能被推导出,但是,如果目标文件的后缀是make所识别的,那么“$*”就是除了后缀的那一部分。例如:如果目标是“foo.c”,因为“.c”是make所能识别的后缀名,所以,“$*”的值就是“foo”。
    这个特性是GNU make的,很有可能不兼容于其它版本的make,所以,你应该尽量避免使用“$*”,除非是在隐含规则或是静态模式中。如果目标中的后缀是make所不能识别的,那么“$*”就是空值。

 

三、规则  ——参考 博客

1. makefile中的规则的基本格式如下:

targets:dependeds
    commands

targets:要生成的文件(多个文件则以空格分开),或者是一个动作,可用称为伪目标;若target为空(未赋值的变量),则该规则无效

dependeds:执行此规则所必须的依赖,可以是其它target,形成递归嵌套;所有依赖都满足后才会执行command生成target;

command:规则所执行的命令,以tab键开始;规则后的所有连续的 以tab键开始的行 都会被视作command。tab、多个空格 二者是不等效的

eg:

...

main.o:main.cpp
    gcc -o main.o ...

test:test.o
  ...

.PHONY:clean
clean:
  rm -rf ...

  

  一次执行makefile,有一个最终目标,这个最终目标可能会带动其它依赖目标的生成。

2. 规则的执行:

1)直接执行 make 时,先查找makefile中的第一个规则,以这个规则的目标(若有多个目标,则以第一个)为最终目标,完成这个规则的执行;  ==》如果要执行其它规则,需要指定目标,如 make test、 make clean

2)依赖项dependeds将从左往右检查是否存在,如果需要执行其它嵌套的规则,会先执行,若不存在且无生成规则,则停止并放弃检查后面的依赖项。

3)除了第一个规则或者指定的规则,.PHONY指定的目标也会在执行时生成。

  

3. 伪目标

  上面的clean是伪目标,所以也不会真正生成clean文件。由于它没有依赖项,make无法判断伪目标的依赖关系,所以要执行相关命令需要显式地指定目标,如:make clean,这种规则每次指定时都一定会执行。

  .PHONY可以将目标显示地声明为伪目标。即使真的有一个文件和伪目标同名,.PHONY指定的伪目标的规则在执行时也不会生成文件。

  .PHONY也可以用于指定一个伪目标,该伪目标依赖于多个真实目标,如下: 

.PHONY:all  # .PHONY后面跟着all,all没有具体的生成命令,但是all依赖于aa和bb,故而aa、bb会先生成
all:aa bb
aa: xxx
  ...

   伪目标每次要生成时都会生成,无论是否有更改;其它规则如果是未更改则不会重新生成。

* 遇到一个问题,以上面的.PHONY:all为例,会出现  make: Nothing to be donefor'all'. 

解决:改为以下写法即可达到每次make都重新执行规定的目标

.PHONY:all aa bb
all:aa bb

.PHONY不影响第一规则为默认规则,若.PHONY所在规则不是第一规则,其指定的规则用make是不会默认执行的。

  下面写法里,执行 make,则aa,bb都会生成,.PHONY行 和 伪目标all行可以调换顺序,等效。

.PHONY:all aa bb
all:aa bb
aa: xxx
  ...

  下面的例子中,执行 make,只有aa会生成。

aa: xxx
  ...
.PHONY:all aa bb
all:aa bb

 

四、参数传递

一个例子 :这个用 -D 传递进去是一个宏,这个宏也可以赋值 更多

-DDEBUG   #相当于在代码里面定义了 #define DEBUG 1
-DDEBUG=2  #相当于在代码里面定义了 #define DEBUG 2。

另一个例子

Makefile文件:
 
ARCH := $(shell arch)
 
ifeq ($(ARCH), "x86_64")
SETTING := 1
else
SETTING := 0
endif
 
CXX := g++ -std=c++11
CFLAGS := -Wall -g -O2 -D${SETTING}
ALL = main
OBJECT =  main.o
LIBS =
 
%.o:%.cpp
    $(CXX) -c $^ -o $@  $(LIBS) $(CFLAGS)
 
$(ALL):$(OBJECT)
    $(CXX) -o $@ $^ $(LIBS) $(CFLAGS)
 
.PHONY:clean
clean:
    rm -rf $(ALL) $(OBJECT)
// main.cpp
#include <iostream> using namespace std; int main(){ cout << "Hello World!" << endl; #ifdef SETTING cout<<"setting = 1"<<endl; #else cout<<"setting = 0"<<endl; #endif return 0; }

 

也可以用传统的传参方法,传给main函数的argv。

 

posted @ 2022-04-23 22:24  谷小雨  阅读(940)  评论(0编辑  收藏  举报