9

GNU Make を使用した C++ の小さなプロジェクトがあります。次のソース ファイルを有効にしたいと考えています。

src/
  a.cpp
  b/
    b.cpp
  c/
    c.cpp

次の出力構造に変換します (この時点では重複については気にしません)。

build/
  a.o
  b.o
  c.o

残念ながら、.o と .d を各 .cpp のすぐ隣に置きます。

OBJS            :=      $(foreach file,$(SRCS),$(file).o)
DEPS            :=      $(patsubst %.o,%.d,$(OBJS))
sinclude $(DEPS)

$(OBJS) : %.o : %.cpp
        @echo Compiling $<
        $(CC) $(CC_FLAGS) $(INCS) -MMD -o $@ $<  

私は $(notdir ...) 関数を認識していますが、この時点でそれを使用してオブジェクトをフィルタリングしようとする試みは失敗に終わりました。誰でもこれに光を当てることができますか?それはかなり合理的なことのように思えます。

4

5 に答える 5

9

これを行うには、少なくとも 2 つの方法があります。最初に (そして私がお勧めするのは)、ビルド ディレクトリをターゲット名に追加できることです (パターン ルールを使用している場合でも)。例えば:

$(OBJS) : build/%.o : %.cpp

次に、VPATH 変数を使用して、make に別のディレクトリで前提条件を検索するように指示できます。これはおそらく、より一般的に (過剰に) 使用されるアプローチです。これには少なくとも 1 つの重大な欠点があります。それは、それを使用した後で「重複」の問題に遭遇した場合、問題を解決する方法がないということです。前者のアプローチでは、常にソース ディレクトリ構造をビルド ディレクトリの下にミラーリングして、重複の衝突を回避できます。

編集:私の以前の回答は詳細が少し不足していたので、それを拡張して、これが実際に宣伝どおりに機能することを示します. 上記の最初の手法を使用して問題を解決する Makefile の完全な動作例を次に示します。これを Makefile に貼り付けて make を実行するだけで、残りの作業が行われ、これが実際に機能することが示されます。

編集:回答テキストでタブ文字を許可するようにSOを取得する方法がわかりません(タブ文字をスペースに置き換えました)。この例をコピーして貼り付けた後、コマンド スクリプトの先頭のスペースをタブに変換する必要があります。

BUILD_DIR := build

SRCS := \
    a.c \
    b.c \
    c.c \
    a/a.c \
    b/b.c \
    c/c.c

OBJS := ${SRCS:%.c=${BUILD_DIR}/%.o}

foo: ${OBJS}
    @echo Linking $@ using $?
    @touch $@

${BUILD_DIR}/%.o: %.c
    @mkdir -p $(dir $@)
    @echo Compiling $< ...
    @touch $@

${SRCS}:
    @echo Creating $@
    @mkdir -p $(dir $@)
    @touch $@

.PHONY: clean
clean:
    rm -f foo
    rm -f ${OBJS}

特に、重複した名前 (ac と a/ac、bc と b/bc など) を持つソース ファイルがあり、これは問題を引き起こさないことに注意してください。また、VPATH は使用されないことに注意してください。これは、固有の制限があるため、使用を避けることをお勧めします。

于 2009-09-02T00:51:21.947 に答える
2
vpath %.cpp src src/b src/c

次に、ディレクトリ名なしでソース ファイルを参照します。Make はvpathを検索します。

于 2009-09-02T00:47:33.160 に答える
1

これは Win32 mingw32-make 用です。それは動作します。最も重要な部分は

-mkdir $(patsubst %/,%,$(dir $@))

win32用。win32 の場合、末尾の / を削除する必要があります。

GENERATED_DIRS = a b

# Dependency generator function
mkdir_deps =$(foreach dir,$(GENERATED_DIRS),$(dir)/.mkdir.done)

# Target rule to create the generated dependency.
# And create the .mkdir.done file for stop continuous recreate the dir
%/.mkdir.done: # target rule
    -mkdir $(patsubst %/,%,$(dir $@))
    echo $(patsubst %/,%,$(dir $@)) >$@

all: $(mkdir_deps)
    @echo Begin

clean:
    @echo Cleaning
于 2012-06-12T12:49:17.547 に答える
1

最初に要求されたフラットなディレクトリ構造を作成します。

vpathソースファイルを追跡するために使用しますが、すべての予約はSRCリストから自動的に行われます。

SRC = a/a.c a/aa.c b/b.c
TARGET = done

FILES = $(notdir $(SRC) )
#make list of source paths, sort also removes duplicates
PATHS = $(sort $(dir $(SRC) ) )

BUILD_DIR = build
OBJ = $(addprefix $(BUILD_DIR)/, $(FILES:.c=.o))
DEP = $(OBJ:.o=.d)

# default target before includes
all: $(TARGET)

include $(DEP)

vpath %.c $(PATHS)

# create dummy dependency files to bootstrap the process
%.d:
    echo a=1 >$@

$(BUILD_DIR)/%.o:%.c
    echo $@: $< > $(patsubst %.o,%.d,$@)
    echo $< >$@

$(TARGET): $(OBJ)
    echo $^ > $@

.PHONY: clean
clean:
    del $(BUILD_DIR)/*.o
    del $(BUILD_DIR)/*.d

醜いもので申し訳ありませんが、echoテストするための勝利ボックスしかありませんでした。

于 2009-11-26T09:14:44.463 に答える
0

このアプローチは特に好きではないかもしれませんが、

AUTOMAKE_OPTIONS =

Makefile.am でうまく機能します。すべてを手書きで書く必要はないと言っているだけです。

于 2011-09-23T11:29:55.433 に答える