0

我一直在研究一个 Makefile,它将在所有子目录中搜索src包含源代码文件的目录并编译对象。稍后它会将对象链接到bin与 Makefile 位于同一目录中的目录中的二进制文件中。每个二进制文件都将以其链接目标文件的子目录命名。对不起,如果这听起来有点混乱......

这是一个图表来说明我的意思:

 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
4

2 回答 2

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),或类似的东西,在你的 makefile 的某个地方。

  • $(BINDIR)/$(APPS): buildrepo $(OBJS)意味着,所有应用程序都依赖于所有对象。

    当您最终将应用程序与 链接时$(CC) $(OBJS) $(LDFLAGS) -o $@,它会尝试将所有对象组合到一个应用程序中,这会导致您的错误消息。

您可以通过在子目录中使用多个 makefile 并使用循环遍历它们来解决此问题

$(SUBDIRS):
    for d in $(SUBDIRS); do $(MAKE) -C $$d; done

另一种解决方案是make-depend分别为每个应用程序创建依赖项和规则。

于 2013-01-17T16:24:11.477 回答
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

应该很容易(通过从 中筛选“app1” $(OBJS)),但事实并非如此,因为 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))
于 2013-01-17T17:00:27.427 回答