包含从另一个 Makefile 生成的依赖文件时的 Makefile 错误

Makefile error when including dependency files generated from another Makefile

我有目录的当前结构:

/build/common 包含从文件夹 common 的编译中获得的对象和依赖文件以及 build/myFolder.

的等效文件

基本上 /myFolder/ 包含一个需要 common 中包含的某些功能的程序。因此,/myFolder/Makefile 也会调用 /common/Makefile 进行编译。

./common/Makefile 如下:

CC = gcc

CURRENT_DIR := $(shell basename $(CURDIR))
SOURCEDIR   := ./
SOURCES     := $(wildcard $(SOURCEDIR)/*.c)
OBJDIR      := ../build/$(CURRENT_DIR)

OBJECTS     := $(patsubst $(SOURCEDIR)/%.c,$(OBJDIR)/%.o, $(SOURCES))
DEPENDS     := $(patsubst $(SOURCEDIR)/%.c,$(OBJDIR)/%.d, $(SOURCES))

# ADD MORE WARNINGS!
WARNING := -Wall -Wextra

all: $(OBJECTS)

clean:
    $(RM) $(OBJECTS) $(DEPENDS) $(EXECUTABLE)

-include $(DEPENDS) 

$(OBJDIR)/%.o: $(SOURCEDIR)/%.c Makefile | $(OBJDIR)
    $(CC) $(WARNING) -MMD -MP -c $< -o $@

$(OBJDIR):
    mkdir -p $(OBJDIR)

./myFolder/Makefile如下:

CC = gcc

INC_PATH       := -I../common/
BUILD_DIR_NAME := build
BUILDDIR       := ../$(BUILD_DIR_NAME)
CURR_DIR_NAME  := $(shell basename $(CURDIR))
SOURCEDIR      := ./
SOURCES        := $(wildcard $(SOURCEDIR)/*.c)
OBJDIR         := $(BUILDDIR)/$(CURR_DIR_NAME)
OBJECTS        := $(patsubst $(SOURCEDIR)/%.c,$(OBJDIR)/%.o, $(SOURCES))
DEPENDS        := $(patsubst $(SOURCEDIR)/%.c,$(OBJDIR)/%.d, $(SOURCES))

COMMONDIR_NAME := common
COMMONDIR      := ../$(COMMONDIR_NAME)
SOURCESCOMMON  := $(wildcard $(COMMONDIR)/*.c)
OBJDIRCOMMON   := $(BUILDDIR)/$(COMMONDIR_NAME)
OBJECTSCOMMON  := $(patsubst $(COMMONDIR)/%.c,$(OBJDIRCOMMON)/%.o, $(SOURCESCOMMON))
DEPENDSCOMMON  := $(patsubst $(COMMONDIR)/%.c,$(OBJDIRCOMMON)/%.d, $(SOURCESCOMMON))

# ADD MORE WARNINGS!
WARNING := -Wall -Wextra

EXECUTABLE := ../out_file

# .PHONY means these rules get executed even if
# files of those names exist.
.PHONY: all clean

# The first rule is the default, ie. "make",
# "make all" and "make parking" mean the same
all: $(EXECUTABLE)

clean:
    $(RM) $(OBJECTS) $(DEPENDS) $(EXECUTABLE)
    +$(MAKE) -C $(COMMONDIR) clean

# Linking the executable from the object files
# $^   # "src.c src.h" (all prerequisites)
$(EXECUTABLE): $(OBJECTS) $(OBJECTSCOMMON)
    $(CC) $(WARNING) $^ -o $@

-include $(DEPENDS) $(DEPENDSCOMMON)

$(OBJDIR):
    mkdir -p $(OBJDIR)

$(OBJDIR)/%.o: $(SOURCEDIR)/%.c Makefile | $(OBJDIR)
    $(CC) $(WARNING) -MMD -MP -c $(INC_PATH) $< -o $@

$(OBJDIRCOMMON):
    mkdir -p $(OBJDIRCOMMON)

$(OBJDIRCOMMON)/%.o: $(COMMONDIR)/%.c $(COMMONDIR)/Makefile| $(OBJDIRCOMMON)
    +$(MAKE) -C $(COMMONDIR)

如果我进入文件夹 myFolder 并第一次使用 make 编译,一切正常。 如果我再试一次,我会收到以下错误:

make: *** No rule to make target `error.c', needed by `../build/common/error.o'.  Stop.

如果我先 make clean 然后 make 就可以了。

引起问题的行如下:

-include $(DEPENDS) $(DEPENDSCOMMON)

特别是 $(DEPENDSCOMMON)。使用 -include $(DEPENDS) 而不是它,它可以完美地工作。

现在,- 告诉 Makefile 如果文件不存在则不要抱怨。如果它们存在,它们将被包含并根据依赖关系正确地重新编译您的源代码。出于这个原因,我希望不会在第一次尝试时编译,但如果它在第一次尝试时编译,也不会产生这样的错误(因为所有文件都存在)。

有人可以告诉我我缺少什么吗?

为了完整起见,这里还有其他文件,它们只是示例:

main.c

#include "../common/error.h"
#include <stdio.h>


int main(int argc, char *argv[])
{
    if (argc<1) err_sys("some error");
    printf("Hello world\n");
    return 0;
}

error.c

#include <stdio.h>

void err_sys(const char *fmt, ...)
{
    printf("some err\n");
}

error.h

#ifndef _ERROR_H_
#define _ERROR_H_
#endif

/*Fatal error related to a system cal1.
* Print a message and terminate*/
void err_sys(const char *fmt,...);

错误与您的 Makefile 结构有关。

主要问题是您在 common/Makefile 中生成依赖项并在 myFolder/Makefile 中使用它。为 $(SOURCEDIR)/%.c 生成依赖项,例如./somefile.c 仅在 common 中有效,在 myFolder

中无效

一个相关的问题是您在 myFolder/Makefile 中复制了 common/Makefile 的许多部分。在这种情况下使用单独的 Makefile 意义不大。

解决此问题的一种方法是从 common 中的文件构建一个库并仅引用 myFolder/Makefile 中的库。这避免了从 common/MakefilemyFolder/Makefile 的重复信息。 我将您的 Makefile 更改为以这种方式工作。 (可能还有改进的余地,我只是想让它发挥作用。)

common/Makefile:

CC = gcc

CURRENT_DIR := $(shell basename $(CURDIR))
SOURCEDIR   := ./
SOURCES     := $(wildcard $(SOURCEDIR)/*.c)
OBJDIR      := ../build/$(CURRENT_DIR)

LIBNAME=libcommon.a
COMMONLIB = $(OBJDIR)/$(LIBNAME)

OBJECTS     := $(patsubst $(SOURCEDIR)/%.c,$(OBJDIR)/%.o, $(SOURCES))
DEPENDS     := $(patsubst $(SOURCEDIR)/%.c,$(OBJDIR)/%.d, $(SOURCES))

.PHONY: all clean

# ADD MORE WARNINGS!
WARNING := -Wall -Wextra

all: $(COMMONLIB)

clean:
        $(RM) $(OBJECTS) $(DEPENDS) $(COMMONLIB)

-include $(DEPENDS)

$(OBJDIR)/%.o: $(SOURCEDIR)/%.c Makefile | $(OBJDIR)
        $(CC) $(WARNING) -MMD -MP -c $< -o $@

$(OBJDIR):
        mkdir -p $(OBJDIR)


$(COMMONLIB): $(OBJECTS)
        $(AR) $(ARFLAGS) $@ $^

myFolder/Makefile:

CC = gcc

INC_PATH       := -I../common/
BUILD_DIR_NAME := build
BUILDDIR       := ../$(BUILD_DIR_NAME)
CURR_DIR_NAME  := $(shell basename $(CURDIR))
SOURCEDIR      := ./
SOURCES        := $(wildcard $(SOURCEDIR)/*.c)
OBJDIR         := $(BUILDDIR)/$(CURR_DIR_NAME)
OBJECTS        := $(patsubst $(SOURCEDIR)/%.c,$(OBJDIR)/%.o, $(SOURCES))
DEPENDS        := $(patsubst $(SOURCEDIR)/%.c,$(OBJDIR)/%.d, $(SOURCES))

COMMONDIR_NAME := common
COMMONDIR      := ../$(COMMONDIR_NAME)

OBJDIRCOMMON   := $(BUILDDIR)/$(COMMONDIR_NAME)

LIBNAME=libcommon.a
COMMONLIB = $(OBJDIRCOMMON)/$(LIBNAME)


.PHONY: buildlib clean all

# ADD MORE WARNINGS!
WARNING := -Wall -Wextra

EXECUTABLE := ../out_file

# .PHONY means these rules get executed even if
# files of those names exist.
.PHONY: all clean

# The first rule is the default, ie. "make",
# "make all" and "make parking" mean the same
all: $(EXECUTABLE)

clean:
    $(RM) $(OBJECTS) $(DEPENDS) $(EXECUTABLE)
    +$(MAKE) -C $(COMMONDIR) clean

# Linking the executable from the object files
# $^   # "src.c src.h" (all prerequisites)
$(EXECUTABLE): $(OBJECTS) $(COMMONLIB)
    $(CC) $(WARNING) $^ -o $@

-include $(DEPENDS)

$(OBJDIR):
    mkdir -p $(OBJDIR)

$(OBJDIR)/%.o: $(SOURCEDIR)/%.c Makefile | $(OBJDIR)
    $(CC) $(WARNING) -MMD -MP -c $(INC_PATH) $< -o $@

$(COMMONLIB): FORCE
    +$(MAKE) -C $(COMMONDIR)

FORCE:

注意:我使用没有规则的目标 FORCE 作为依赖项,而不是将 $(COMMONLIB) 声明为 .PHONY 以强制 运行 递归 make .当我尝试使用 .PHONY 时,可执行文件每次都在不检查库文件的时间戳的情况下重新构建。


另一种选择是丢弃 common 中的 Makefile,并用相应的编译命令替换 myFolder/Makefile 中的递归 make 调用。

$(OBJDIRCOMMON)/%.o: $(COMMONDIR)/%.c Makefile| $(OBJDIRCOMMON)
    $(CC) $(WARNING) -MMD -MP -c $< -o $@

备注

当您使用选项 -I.

将其指定为包含目录时,您不需要在 #include 指令中使用相对路径 ../common

我添加了 .PHONY 来声明您的虚假目标。

对于自动依赖生成和关于 Makefile 的一般建议,我建议阅读 http://make.mad-scientist.net/papers/ 上的论文。

您也可以考虑使用 Makefile 生成器,例如 cmake

如果您从此处复制并粘贴 Makefile 代码,请确保将行开头的空格替换为制表符。