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

问题描述 投票:0回答:1

我具有目录的当前结构:

  • ./ myFolder /
    • Makefile
    • main.c
  • ./ common /
    • Makefile
    • error.c
    • error.h
  • ./ build /
    • / common /
    • / myFolder /

[/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,...);
c makefile dependencies include
1个回答
1
投票

该错误与您的Makefile结构有关。

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

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

解决此问题的一种方法是从common中的文件构建一个库,并仅引用myFolder/Makefile中的库。这避免了将信息从common/Makefile复制到myFolder/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)强制运行递归make,而是仅在库文件发生更改时才重新构建可执行文件。


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

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

Notes

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

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

有关自动生成依赖项和有关Makefile的一般建议,我建议阅读http://make.mad-scientist.net/papers/处的文章。

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

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

© www.soinside.com 2019 - 2024. All rights reserved.