0

src私は、すべてのサブディレクトリでソースコードファイルを含むディレクトリを検索し、オブジェクトをコンパイルするMakefileに取り組んでいます。後で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 $@、すべてのオブジェクトを1つのアプリケーションに結合しようとするため、エラーメッセージが表示されます。

サブディレクトリでいくつかの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 に答える