私はメイクファイルが初めてなので、メイクファイルを使用してディレクトリを作成したいと思います。私のプロジェクトディレクトリはこのようなものです
+--Project
+--output
+--source
+Testfile.cpp
+Makefile
すべてのオブジェクトと出力をそれぞれの出力フォルダーに入れたいです。コンパイル後にこのようなフォルダ構造を作成したいと思います。
+--Project
+--output
+--debug (or release)
+--objs
+Testfile.o
+Testfile (my executable file)
+--source
+Testfile.cpp
+Makefile
いくつかのオプションを試しましたが、成功しませんでした。 make fileを使用してディレクトリを作成するのを手伝ってください。あなたの考慮のために、私はMakefileを投稿しています。
#---------------------------------------------------------------------
# Input dirs, names, files
#---------------------------------------------------------------------
OUTPUT_ROOT := output/
TITLE_NAME := TestProj
ifdef DEBUG
TITLE_NAME += _DEBUG
else
ifdef RELEASE
TITLE_NAME += _RELEASE
endif
endif
# Include all the source files here with the directory tree
SOURCES := \
source/TestFile.cpp \
#---------------------------------------------------------------------
# configs
#---------------------------------------------------------------------
ifdef DEBUG
OUT_DIR := $(OUTPUT_ROOT)debug
CC_FLAGS := -c -Wall
else
ifdef RELEASE
OUT_DIR := $(OUTPUT_ROOT)release
CC_FLAGS := -c -Wall
else
$(error no build type defined)
endif
endif
# Put objects in the output directory.
OUT_O_DIR := $(OUT_DIR)/objs
#---------------------------------------------------------------------
# settings
#---------------------------------------------------------------------
OBJS = $(SOURCES:.cpp=.o)
DIRS = $(subst /,/,$(sort $(dir $(OBJS))))
DIR_TARGET = $(OUT_DIR)
OUTPUT_TARGET = $(OUT_DIR)/$(TITLE_NAME)
CC_FLAGS +=
LCF_FLAGS :=
LD_FLAGS :=
#---------------------------------------------------------------------
# executables
#---------------------------------------------------------------------
MD := mkdir
RM := rm
CC := g++
#---------------------------------------------------------------------
# rules
#---------------------------------------------------------------------
.PHONY: all clean title
all: title
clean:
$(RM) -rf $(OUT_DIR)
$(DIR_TARGET):
$(MD) -p $(DIRS)
.cpp.o:
@$(CC) -c $< -o $@
$(OBJS): $(OUT_O_DIR)/%.o: %.cpp
@$(CC) -c $< -o $@
title: $(DIR_TARGET) $(OBJS)
前もって感謝します。私もミスをした場合は私を案内してください。
これはそれを行うでしょう-Unixのような環境を想定しています。
MKDIR_P = mkdir -p
.PHONY: directories
all: directories program
directories: ${OUT_DIR}
${OUT_DIR}:
${MKDIR_P} ${OUT_DIR}
これは、最上位ディレクトリで実行する必要があります-または、$ {OUT_DIR}の定義は、実行場所に関連して正しい必要があります。もちろん、Peter Millerの「 Recursive Make Thoughd Harmful 」の指示に従うと、とにかくトップレベルディレクトリでmakeを実行することになります。
私は現在これ(RMCH)で遊んでいます。テストの場として使用している一連のソフトウェアに少し適応させる必要がありました。このスイートには、15個のディレクトリに分散したソースでビルドされた多数の個別のプログラムがあり、その一部は共有されています。しかし、少し注意して、それを行うことができます。 OTOH、初心者には向かないかもしれません。
コメントに記載されているように、「ディレクトリ」のアクションとして「mkdir」コマンドをリストするのは間違っています。コメントにも記載されているように、「出力/デバッグの方法がわからない」エラーを修正する他の方法があります。 1つは、「ディレクトリ」行の依存関係を削除することです。これは、作成するように要求されたすべてのディレクトリがすでに存在する場合、「mkdir -p」がエラーを生成しないためです。もう1つは、表示されているメカニズムで、存在しない場合にのみディレクトリの作成を試みます。 「修正された」バージョンは昨夜私が念頭に置いていたものですが、両方の手法が機能します(出力/デバッグは存在するがディレクトリではなくファイルである場合は両方とも問題があります)。
私の意見では、技術的または設計的な意味で、ディレクトリをメイクファイルのターゲットと見なすべきではありません。 filesを作成し、ファイルの作成に新しいディレクトリが必要な場合はquietly関連ファイルのルール内にディレクトリを作成します。
通常のファイルまたは「パターン化された」ファイルをターゲットにしている場合は、make
の内部変数$(@D)
を使用します。つまり、「現在のターゲットが存在するディレクトリ」(cmp。with _$@
_はターゲット用)。例えば、
_$(OUT_O_DIR)/%.o: %.cpp
@mkdir -p $(@D)
@$(CC) -c $< -o $@
title: $(OBJS)
_
次に、実質的に同じことを実行します。すべての$(OBJS)
のディレクトリを作成しますが、それほど複雑ではありません。
さまざまなアプリケーションで同じポリシー(ファイルはターゲットであり、ディレクトリは決してない)が使用されます。たとえば、git
リビジョン管理システムはディレクトリを保存しません。
注:これを使用する場合、便利な変数を導入し、make
の展開ルールを利用すると便利な場合があります。
_dir_guard=@mkdir -p $(@D)
$(OUT_O_DIR)/%.o: %.cpp
$(dir_guard)
@$(CC) -c $< -o $@
$(OUT_O_DIR_DEBUG)/%.o: %.cpp
$(dir_guard)
@$(CC) -g -c $< -o $@
title: $(OBJS)
_
または、KISS。
DIRS=build build/bins
...
$(Shell mkdir -p $(DIRS))
これにより、Makefileの解析後にすべてのディレクトリが作成されます。
make
インとオフは、ディレクトリターゲットをファイルターゲットとまったく同じように処理します。したがって、次のようなルールを簡単に記述できます。
outDir/someTarget: Makefile outDir
touch outDir/someTarget
outDir:
mkdir -p outDir
それに関する唯一の問題は、ディレクトリのタイムスタンプが内部のファイルに対して行われる処理に依存することです。上記のルールの場合、これは次の結果につながります。
$ make
mkdir -p outDir
touch outDir/someTarget
$ make
touch outDir/someTarget
$ make
touch outDir/someTarget
$ make
touch outDir/someTarget
これは間違いなくあなたが望むものではありません。ファイルをタッチするたびに、ディレクトリもタッチします。また、ファイルはディレクトリに依存しているため、ファイルは古くなっているように見え、強制的に再構築されます。
ただし、ディレクトリのタイムスタンプを無視するようにmakeに指示するによって、このループを簡単に解除できます。これは、ディレクトリを順序専用の前提条件として宣言することにより実行されます。
# The pipe symbol tells make that the following prerequisites are order-only
# |
# v
outDir/someTarget: Makefile | outDir
touch outDir/someTarget
outDir:
mkdir -p outDir
これにより、以下が正しく生成されます。
$ make
mkdir -p outDir
touch outDir/someTarget
$ make
make: 'outDir/someTarget' is up to date.
TL; DR:
ディレクトリを作成するルールを作成します。
$(OUT_DIR):
mkdir -p $(OUT_DIR)
そして、内部のもののターゲットがディレクトリの順序のみに依存するようにします:
$(OUT_DIR)/someTarget: ... | $(OUT_DIR)
承認されたものを含むすべてのソリューションには、それぞれのコメントに記載されているいくつかの問題があります。 @ jonathan-lefflerによって受け入れられた回答 はすでに非常に優れていますが、前提条件が必ずしも順番に構築される必要はない(たとえば[make -j
]の間に)有効になりません。ただし、directories
前提条件をall
からprogram
に移動するだけで、AFAICTを実行するたびに再構築が発生します。次の解決策にはその問題はなく、AFAICSは意図したとおりに機能します。
MKDIR_P := mkdir -p
OUT_DIR := build
.PHONY: directories all clean
all: $(OUT_DIR)/program
directories: $(OUT_DIR)
$(OUT_DIR):
${MKDIR_P} $(OUT_DIR)
$(OUT_DIR)/program: | directories
touch $(OUT_DIR)/program
clean:
rm -rf $(OUT_DIR)
ビルドするファイルを定義し、ディレクトリを自動的に作成できる、かなり合理的なソリューションを思いつきました。最初に、makefileがビルドされるすべてのファイルのファイル名を保持する変数ALL_TARGET_FILES
を定義します。次に、次のコードを使用します。
define depend_on_dir
$(1): | $(dir $(1))
ifndef $(dir $(1))_DIRECTORY_RULE_IS_DEFINED
$(dir $(1)):
mkdir -p $$@
$(dir $(1))_DIRECTORY_RULE_IS_DEFINED := 1
endif
endef
$(foreach file,$(ALL_TARGET_FILES),$(eval $(call depend_on_dir,$(file))))
仕組みは次のとおりです。関数depend_on_dir
を定義します。この関数は、ファイル名を取得し、ファイルを含むディレクトリにファイルを依存させるルールを生成し、必要に応じてそのディレクトリを作成するルールを定義します。次に、各ファイル名でforeach
からcall
にこの関数を使用し、eval
を使用します。
eval
をサポートするGNU makeのバージョンが必要なことに注意してください。これはバージョン3.81以降です。
OSの独立性は私にとって非常に重要なので、mkdir -p
はオプションではありません。 eval
を使用して、親ディレクトリに前提条件を持つディレクトリターゲットを作成するこの一連の関数を作成しました。これには、make -j 2
は、依存関係が正しく判断されるため、問題なく機能します。
# convenience function for getting parent directory, will eventually return ./
# $(call get_parent_dir,somewhere/on/earth/) -> somewhere/on/
get_parent_dir=$(dir $(patsubst %/,%,$1))
# function to create directory targets.
# All directories have order-only-prerequisites on their parent directories
# https://www.gnu.org/software/make/manual/html_node/Prerequisite-Types.html#Prerequisite-Types
TARGET_DIRS:=
define make_dirs_recursively
TARGET_DIRS+=$1
$1: | $(if $(subst ./,,$(call get_parent_dir,$1)),$(call get_parent_dir,$1))
mkdir $1
endef
# function to recursively get all directories
# $(call get_all_dirs,things/and/places/) -> things/ things/and/ things/and/places/
# $(call get_all_dirs,things/and/places) -> things/ things/and/
get_all_dirs=$(if $(subst ./,,$(dir $1)),$(call get_all_dirs,$(call get_parent_dir,$1)) $1)
# function to turn all targets into directories
# $(call get_all_target_dirs,obj/a.o obj/three/b.o) -> obj/ obj/three/
get_all_target_dirs=$(sort $(foreach target,$1,$(call get_all_dirs,$(dir $(target)))))
# create target dirs
create_dirs=$(foreach dirname,$(call get_all_target_dirs,$1),$(eval $(call make_dirs_recursively,$(dirname))))
TARGETS := w/h/a/t/e/v/e/r/things.dat w/h/a/t/things.dat
all: $(TARGETS)
# this must be placed after your .DEFAULT_GOAL, or you can manually state what it is
# https://www.gnu.org/software/make/manual/html_node/Special-Variables.html
$(call create_dirs,$(TARGETS))
# $(TARGET_DIRS) needs to be an order-only-prerequisite
w/h/a/t/e/v/e/r/things.dat: w/h/a/t/things.dat | $(TARGET_DIRS)
echo whatever happens > $@
w/h/a/t/things.dat: | $(TARGET_DIRS)
echo whatever happens > $@
たとえば、上記を実行すると次のものが作成されます。
$ make
mkdir w/
mkdir w/h/
mkdir w/h/a/
mkdir w/h/a/t/
mkdir w/h/a/t/e/
mkdir w/h/a/t/e/v/
mkdir w/h/a/t/e/v/e/
mkdir w/h/a/t/e/v/e/r/
echo whatever happens > w/h/a/t/things.dat
echo whatever happens > w/h/a/t/e/v/e/r/things.dat
あなたが初心者であることを考えると、私はまだこれをやろうとしないと思います。間違いなく可能ですが、Makefileを不必要に複雑にします。 makeに慣れるまで、単純な方法に固執します。
ただし、ソースディレクトリとは異なるディレクトリにビルドする1つの方法は [〜#〜] vpath [〜#〜] ;です。 パターンルール
https://www.oreilly.com/library/view/managing-projects-with/0596006101/ch12.html を参照してください
REQUIRED_DIRS = ...
_MKDIRS := $(Shell for d in $(REQUIRED_DIRS); \
do \
[[ -d $$d ]] || mkdir -p $$d; \
done)
$(objects) : $(sources)
Ubuntuを使用しているときに、Makefileの先頭にこれを追加する必要もありました。
Shell := /bin/bash # Use bash syntax