我具有目录的当前结构:
[/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
中无效
一个相关的问题是,您从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代码,请确保使用制表符替换行开头的空格。