Создание каталогов с помощью 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)


спасибо заранее. Пожалуйста, руководство мне, если я сделал какие-либо ошибки.

800   8  

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

make in и 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

    Ничего не найдено.