【问题标题】:Makefile error when including dependency files generated from another Makefile包含从另一个 Makefile 生成的依赖文件时的 Makefile 错误
【发布时间】:2020-07-28 19:08:51
【问题描述】:

我有当前的目录结构:

  • ./myFolder/
    • 生成文件
    • main.c
  • ./common/
    • 生成文件
    • error.c
    • error.h
  • ./build/
    • /常见/
    • /myFolder/

/build/common 包含通过编译文件夹commonbuild/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 在文件不存在时不要抱怨。如果它们存在,它们将被包含并根据依赖关系正确重新编译您的源代码。出于这个原因,我不希望在第一次尝试时编译,但如果它在第一次尝试时编译,也不会产生这样的错误(因为所有文件都存在)。

谁能告诉我我错过了什么?

为了完整起见,这里只是示例的其他文件:

ma​​in.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,...);

【问题讨论】:

    标签: c makefile dependencies include


    【解决方案1】:

    错误与您的 Makefile 结构有关。

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

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

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

    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/ 的论文。

    您也可以考虑使用像 cmake 这样的 Makefile 生成器。

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

    【讨论】:

    • 谢谢,但首先包含$(DEPENDSCOMMON) 是否正确?我会把它扔掉,但是common/Makefile 开始它的生命是为了避免重复。考虑我有另一个文件夹 myFolder2 和另一个 Makefilemain.c 这是一个与前者互补的程序。通过从myFolder/MakefilemyFolder2/Makefile 调用common/Makefile,如果我必须更改common 编译中的某些内容,我可以通过在一个地方应用更改来做到这一点,删除common/Makefile 意味着同时更改@ 987654362@ 和 myFolder2/Makefile.
    • @FrancescoBoi 我忘了删除一些部分。我修改了 Makefile 来解决这个问题(希望如此)。
    • 等等,为什么在$(OBJDIRCOMMON)/%.o: $(COMMONDIR)/%.c $(COMMONDIR)/Makefile| $(OBJDIRCOMMON) $(CC) $(WARNING) -MMD -MP -c $&lt; -o $@ 的答案的第二部分(丢弃 Makefile 的部分)中仍然存在 common/Makefile 如果它应该被丢弃?跨度>
    • @FrancescoBoi 复制粘贴错误,应该是Makefile
    • @FrancescoBoi 我再次修复了FORCE.PHONY 的解释中的错误
    猜你喜欢
    • 1970-01-01
    • 2016-10-13
    • 1970-01-01
    • 1970-01-01
    • 2023-04-11
    • 2017-10-03
    • 1970-01-01
    • 1970-01-01
    • 2016-10-07
    相关资源
    最近更新 更多