Makefile

一、通用示例

CC = gcc
CFLAGS = -lpthread -L ./tlACSClib -lNetDEVSDK
GFLAGS = -g -Wall -O3
TARGET = tlACSC
SRCS = $(wildcard *.c)
OBJS = $(patsubst %c, %o, $(SRCS))

$(TARGET) : $(OBJS)
$(CC) $(GFLAGS) $^ -o $(TARGET) $(CFLAGS)

%.o : %.c
$(CC) $(GFLAGS) -c $< -o $@

.PHONY : clean
clean:
rm -rf $(OBJS) $(TARGET)

二、= 和 :=

      1、“=”

      make会将整个makefile展开后,再决定变量的值。也就是说,变量的值将会是整个makefile中最后被指定的值。看例子:

            x = foo
            y = $(x) bar
            x = xyz

      在上例中,y的值将会是 xyz bar ,而不是 foo bar 。

      2、“:=”

      “:=”表示变量的值决定于它在makefile中的位置,而不是整个makefile展开后的最终值。

            x := foo
            y := $(x) bar
            x := xyz

      在上例中,y的值将会是 foo bar ,而不是 xyz bar 了。

3、

other= 是最基本的赋值
:= 是覆盖之前的值
?= 是如果没有被赋值过就赋予等号后面的值
+= 是添加等号后面的值

4、include、-include、sinclude

include包含其他的Makefile文件或者.mk文件。从而可以使用包含文件中的变量。
-include是指定编译器,即使没有这个文件也照常编译,不报错。编译继续进行。
sinclude和-include的效果一样

5、命令前加@和-

如果make执行的命令前面加了@字符,则不显示命令本身而只显示它的结果; 

通常make执行的命令如果出错(该命令的退出状态非0)就立刻终止,不再执行后续命令,但如果命令前面加了-号,即使这条命令出错,make也会继续执行后续命令

6、多级目录make -C

确实,当一个Makefile包含另一个Makefile时,可以实现递归构建,这在管理多目录项目时非常有用。这种方法允许你将大型项目分解成更小的、更易于管理的部分,每个部分都有自己的Makefile。然后,主Makefile可以调用这些子Makefile来构建整个项目。

递归构建通常通过以下方式实现:

在主Makefile中使用make -C命令:
-C选项告诉make切换到指定的目录,并在那里执行Makefile。这允许你从项目的顶层Makefile中调用位于子目录中的Makefile。例如,如果你的项目结构如下:

project/

├── Makefile # 主Makefile

├── src/

│ └── Makefile # 子Makefile

└── include/

└── …

你可以在顶层Makefile中这样写:

all: 
    make -C src

这将切换到src目录并执行那里的Makefile。

在子Makefile中定义目标:
每个子Makefile应该定义它自己的构建目标,这些目标可以被顶层Makefile调用。这样,你可以在每个子目录中独立地管理构建过程。

传递变量:
有时,你可能需要在子Makefile中使用顶层Makefile中定义的变量。你可以使用make的命令行参数来传递这些变量。例如,在顶层Makefile中:

COMMON_FLAGS = -Wall -g

all:

make -C src COMMON_FLAGS=”$(COMMON_FLAGS)”

然后在子Makefile中,你可以这样使用这些变量:

gcc $(COMMON_FLAGS) -o my_program my_program.c

递归深度:
注意,递归构建可能会增加构建的复杂性,特别是当项目非常大且包含许多子目录时。确保你的Makefile结构清晰,并且每个子Makefile都明确其职责,以避免不必要的复杂性。

使用include指令:
虽然include指令本身不直接支持递归构建,但它可以用于将公共的Makefile片段(如变量定义、规则等)包含到多个Makefile中,从而减少重复代码并提高可维护性。

递归构建是多目录项目管理中的一种强大工具,但也需要谨慎使用,以确保构建过程的清晰性、可维护性和效率。

7、在Makefile的上下文中,由于每行通常都被视为一个独立的命令

在Makefile中使用if语句时,为了可读性和维护性,通常建议将if语句的各个部分(如条件判断、then子句、else子句和fi结束标记)写成多行。

Makefile中的if语句实际上是通过调用shell脚本来实现的,因此它们遵循shell脚本的语法规则。在shell脚本中,if语句可以跨越多行,而Makefile只是提供了一个执行这些脚本的环境。

为了将if语句写成多行,你可以使用反斜杠(\)来指示当前行的继续,或者在每个逻辑行的末尾使用换行符。然而,在Makefile的上下文中,由于每行通常都被视为一个独立的命令,因此你通常需要使用反斜杠来明确指示if语句的跨行继续。

以下是一个Makefile中使用if语句并写成多行的示例:

target:  
    @if [ "$(SOME_VARIABLE)" = "some_value" ]; then \  
        echo "SOME_VARIABLE is set to some_value"; \  
    else \  
        echo "SOME_VARIABLE is not set to some_value"; \  
    fi

在这个例子中,if语句被分成了多行,每行的末尾都使用了反斜杠来指示下一行是当前逻辑行的继续。这样做提高了代码的可读性,使得其他开发者更容易理解你的意图。

因此,尽管技术上可能将if语句的所有部分压缩到同一逻辑行(通过省略反斜杠并使用分号分隔命令),但这样做通常是不推荐的,因为它会牺牲代码的可读性和可维护性。最佳实践是将if语句写成多行,以清晰地表达条件逻辑和相应的操作。