包含从另一个 Makefile 生成的依赖文件时的 Makefile 错误
Makefile error when including dependency files generated from another Makefile
我有目录的当前结构:
- ./我的文件夹/
- 生成文件
- main.c
- ./常见/
- 生成文件
- error.c
- error.h
- ./build/
- /普通/
- /我的文件夹/
/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/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)
声明为 .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 代码,请确保将行开头的空格替换为制表符。
我有目录的当前结构:
- ./我的文件夹/
- 生成文件
- main.c
- ./常见/
- 生成文件
- error.c
- error.h
- ./build/
- /普通/
- /我的文件夹/
/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/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)
声明为 .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 代码,请确保将行开头的空格替换为制表符。