Создание каталогов с помощью make file
Я очень новичок в makefiles, и я хочу создавать каталоги с помощью makefile. Мой каталог проекта выглядит так
+--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)
спасибо заранее. Пожалуйста, руководство мне, если я сделал какие-либо ошибки.
8 ответов:
Это сделало бы это - предполагая Unix-подобную среду.
MKDIR_P = mkdir -p .PHONY: directories all: directories program directories: ${OUT_DIR} ${OUT_DIR}: ${MKDIR_P} ${OUT_DIR}Это должно быть выполнено в каталоге верхнего уровня-или определение ${OUT_DIR} должно быть правильным относительно того, где оно выполняется. Конечно, если следовать указам Питера Миллера"Рекурсивный Сделать Считается Вредным" бумага, тогда вы все равно будете запускать make в каталоге верхнего уровня.
Я играю с этим (RMCH) в данный момент. Для этого нужно было немного адаптация к набору программного обеспечения, которое я использую в качестве испытательного полигона. В наборе есть дюжина отдельных программ, построенных с исходным кодом, распространенным по 15 каталогам, некоторые из которых являются общими. Но с небольшой осторожностью это можно сделать. ОТО, это может быть не подходит для новичка.
Как отмечалось в комментариях, перечисление команды "mkdir" в качестве действия для "каталогов" неверно. Как также отмечалось в комментариях, есть и другие способы исправить ошибку " не знаю, как сделать вывод / отладку этот результат. Один из них-удалить зависимость от строки "каталоги". Это работает, потому что "mkdir-p" не создает ошибок, если все каталоги, которые он просит создать, уже существуют. Другой-это показанный механизм, который будет пытаться создать каталог только в том случае, если он не существует. Версия "с поправками" - это то, что я имел в виду вчера вечером, но оба метода работают (и у обоих есть проблемы, если вывод/отладка существует, но это файл, а не каталог).
на мой взгляд, каталоги не должны считаться целями вашего makefile, ни в техническом, ни в дизайнерском смысле. Вы должны создать файлы и если для создания файла требуется новый каталог, то спокойно создайте каталог в правиле для соответствующего файла.
если вы нацелены на обычный или" шаблонный " файл, просто используйте
make'внутренней переменной s$(@D), что означает "каталог, в котором находится текущая цель" (cmp. с$@для цели). Например,$(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)
или, поцелуй.
DIRS=build build/bins ... $(shell mkdir -p $(DIRS))это создаст все каталоги после того, как файл Makefile будет проанализирован.
все решения, включая принятые, имеют некоторые проблемы, как указано в их соответствующих комментариях. Элемент принятый ответ @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)
Я только что придумал довольно разумное решение, которое позволяет вам определять файлы для сборки и автоматически создавать каталоги. Во-первых, определите переменную
ALL_TARGET_FILESкоторый содержит имя файла каждого файла, который ваш makefile будет построен. Затем используйте следующий код: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результат.обратите внимание, что вам понадобится версия GNU make, которая поддерживает
eval, который я думаю, версии 3.81 и выше.
независимость ОС имеет решающее значение для меня, так что
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 %/,%,)) # 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+= : | $(if $(subst ./,,$(call get_parent_dir,)),$(call get_parent_dir,)) mkdir 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 )),$(call get_all_dirs,$(call get_parent_dir,)) ) # 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,,$(call get_all_dirs,$(dir $(target))))) # create target dirs create_dirs=$(foreach dirname,$(call get_all_target_dirs,),$(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
makein и off сам по себе обрабатывает цели каталога так же, как и цели файлов. Таким образом, легко написать такие правила: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 игнорировать метку времени каталога. Это делается путем объявления каталога как prerequsite только для заказа:
# 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)
учитывая, что вы новичок, я бы сказал, Не пытайтесь сделать это еще. это определенно возможно, но без необходимости усложнит ваш Makefile. придерживайтесь простых способов, пока вы не будете более комфортно с make.
тем не менее, один из способов построить в каталоге, отличном от исходного каталога является VPATH; Я предпочитаю правила шаблон
Comments