我一直在研究Makefile,它将在所有子目录中搜索包含源代码文件的src
目录并编译对象。稍后它会将对象链接到与Makefile相同的目录中的bin
目录中的二进制文件。每个二进制文件都以它链接目标文件的子目录命名。对不起,如果这听起来有些令人困惑......
这是一个显示我的意思的图表:
Makefile
app1-\
src-\
main.c
obj-\
main.o
app2-\
src-\
main.c
obj-\
main.o
bin-\
app1
app2
目前,每当我运行Makefile时,它都会很好地编译目标文件,但是当涉及链接它们时,它会尝试将所有这些文件链接到第一个二进制文件中。
错误:
Generating dependencies for problem2.1/src/2-1.c...
Compiling problem2.1/src/2-1.c...
Generating dependencies for problem2.1/src/2-1.c...
Compiling problem2.1/src/2-1.c...
Linking bin/problem2.1...
./problem2.2/obj/2-2.o: In function `main':
/cygdrive/c/Users/Hans/git/opsys/task_01/problem2.1/src/2-1.c:9: multiple definition of `_main'
./problem2.1/obj/2-1.o:/cygdrive/c/Users/Hans/git/opsys/task_01/problem2.1/src/2-1.c:9: first defined here
collect2: ld returned 1 exit status
Makefile:57: recipe for target `bin/problem2.1' failed
make: *** [bin/problem2.1] Error 1
我认为这里的主要问题是我误解了Makefiles,有什么方法可以做我想做的事情吗?
到目前为止,唯一与此类似的是使用make的递归功能,这是唯一的方法吗?
我的Makefile:
SRCEXT = c
SRCDIR = src
OBJDIR = obj
BINDIR = bin
SUBDIRS := $(shell find . -type d -name '*$(SRCDIR)*' -exec dirname {} \; | uniq)
SRCDIRS := $(shell find $(SUBDIRS) -name '*.$(SRCEXT)' -exec dirname {} \; | uniq)
OBJDIRS := $(subst src,obj,$(SRCDIRS))
SRCS := $(shell find $(SRCDIRS) -name '*.$(SRCEXT)')
OBJREF := $(subst src,obj,$(SRCS))
OBJS := $(patsubst %.$(SRCEXT),%.o,$(OBJREF))
APPS := $(subst ./,,$(SUBDIRS))
DEBUG = -g
CFLAGS = -Wall -pedantic -ansi -c $(DEBUG) $(INCLUDES)
ifeq ($(SRCEXT), cpp)
CC = $(CXX)
else
CFLAGS += -std=gnu99
endif
.PHONY: all clean distclean
all: $(BINDIR)/$(APPS)
$(BINDIR)/$(APPS): buildrepo $(OBJS)
@mkdir -p `dirname $@`
@echo "Linking $@..."
@$(CC) $(OBJS) $(LDFLAGS) -o $@
$(OBJS): $(SRCS)
@echo "Generating dependencies for $<..."
@$(call make-depend,$<,$@,$(subst .o,.d,$@))
@echo "Compiling $<..."
@$(CC) $(CFLAGS) $< -o $@
clean:
$(RM) -r $(OBJDIRS)
distclean: clean
$(RM) -r $(BINDIR)
buildrepo:
@$(call make-repo)
define make-repo
for dir in $(OBJDIRS); \
do \
mkdir -p $$dir; \
done
endef
# usage: $(call make-depend,source-file,object-file,depend-file)
define make-depend
$(CC) -MM -MF $3 -MP -MT $2 $(CFLAGS) $1
endef
答案 0 :(得分:0)
你的makefile有几个问题。
如果您的find
支持,则可以使用-printf '%h '
代替-exec dirname {} \;
这样可以为每个目录保存一个单独的进程(dirname
)。
APPS := $(subst ./,,$(SUBDIRS))
提供app1 app2
,$(BINDIR)/$(APPS)
提供bin/app1 app2
,不 bin/app1 bin/app2
。
您可以使用APPS := $(subst ./,$(BINDIR),$(SUBDIRS))
代替all: $(APPS)
@mkdir -p `dirname $@`
相当于@mkdir -p $(BINDIR)
您可以使用make-depend
创建依赖项,但不要在生成文件中包含生成的依赖项文件。
你必须在makefile中的某个地方include $(wildcard *.d)
或类似的东西。
$(BINDIR)/$(APPS): buildrepo $(OBJS)
表示所有应用程序都依赖于所有对象。
当您最终将应用程序与$(CC) $(OBJS) $(LDFLAGS) -o $@
链接时,它会尝试将所有对象合并到一个应用程序中,从而产生错误消息。
您可以通过在子目录中使用多个makefile并使用
循环它们来解决此问题$(SUBDIRS):
for d in $(SUBDIRS); do $(MAKE) -C $$d; done
另一种解决方案可能是使用make-depend
分别为每个应用程序创建依赖关系和规则。
答案 1 :(得分:0)
这种“构建你能找到的一切”方法有点危险,而且解决方案也很棘手。你可能最好维护一个目标列表,每个目标都有一个源列表。但如果你真的想这样做......
基本问题在于:
$(BINDIR)/$(APPS): buildrepo $(OBJS)
...
@$(CC) $(OBJS) $(LDFLAGS) -o $@
(目标是错误的,但没关系。)
目标可能是bin/app1
,但$(OBJS)
是“./app1/obj/main.o ./app2/obj/main.o”,因此这是链接器尝试链接的内容。 (另外,./app2/obj/main.o
是先决条件。)我们需要的是针对目标定制的对象列表,如下所示:
bin/app1: ./app1/obj/main.o
这个应该很容易(通过从$(OBJS)
筛选“app1”),但不是,因为Make的模式匹配非常差。
如果我们事先知道“./app1”,我们可以构建规则:
$(BINDIR)/$(notdir ./app1): $(subst .c,.o,$(subst /src/,/obj/,$(shell find ./app1/src -name '*.$(SRCEXT)')))
我们可以像以下那样将丑陋与“./app1”区分开来:
template = $(BINDIR)/$(notdir $(1)): $(subst .c,.o,$(subst /src/,/obj/,$(shell find $(1)/src -name '*.$(SRCEXT)')))
$(eval $(call template,./app1))
我们不希望为每个二进制文件执行此操作。但我们可以让 Make 为每个二进制文件执行此操作:
template = $(BINDIR)/$(notdir $(1)): $(subst .c,.o,$(subst /src/,/obj/,$(shell find $(1)/src -name '*.$(SRCEXT)')))
$(foreach dir,$(SUBDIRS),$(eval $(call template,$(dir)))
现在我们所需要的只是规则的主体:
$(BINDIR)/%:
@mkdir -p `dirname $@`
@echo "Linking $@..."
@$(CC) $^ $(LDFLAGS) -o $@
和正确的默认规则:
all: $(addprefix $(BINDIR)/, $(APPS))