#makefile #fortran
Вопрос:
У меня есть проект с деревом каталогов, которое выглядит следующим образом:
.
├── modules
│ ├── mod1
│ │ └── mod1.f90
│ ├── mod2
│ │ └── mod2.f90
│ └── mod.f90
└── src
└── main.f90
├── bin
└── Makefile
main.f90
использует все модули, найденные в modules
и в «подмодулях», которые являются mod1/
и mod2/
.
Простой файл Makefile, который я могу написать для компиляции проекта, выглядит примерно так:
F90 = gfortran
FLAGS = -g -I$(BINDIR)
MODFLAGS = -J$(BINDIR)
BINDIR = bin
SRCDIR = src
MODDIR = modules
SMODDIR = $(dir $(wildcard $(MODDIR)/*/.))
MODFILES = $(wildcard $(MODDIR)/*f90)
MODOBJ = $(join $(addsuffix $(MODFROMBIN)/, $(dir $(MODFILES))),
$(notdir $(MODFILES:.f90=.o)))
SMODFILES = $(foreach smoddir, $(SMODDIR), $(wildcard $(smoddir)*f90))
SMODOBJ = $(join $(addsuffix $(SMODFROMBIN)/, $(dir $(SMODFILES))),
$(notdir $(SMODFILES:.f90=.o)))
SRCFILES = $(wildcard $(SRCDIR)/*f90)
SRCOBJ = $(join $(addsuffix $(SRCFROMBIN)/, $(dir $(SRCFILES))),
$(notdir $(SRCFILES:.f90=.o)))
ALLOBJ = $(MODOBJ) $(SMODOBJ) $(SRCOBJ)
BINOBJ = $(addprefix $(BINDIR)/, $(sort $(notdir $(ALLOBJ))))
all: main
$(MODDIR)/../bin/%.o: $(MODDIR)/%.f90
$(F90) $(FLAGS) -c $^ -o $@ $(MODFLAGS)
modules/mod1/../../bin/%.o: modules/mod1/%.f90
$(F90) $(FLAGS) -c $^ -o $@ $(MODFLAGS)
modules/mod2/../../bin/%.o: modules/mod2/%.f90
$(F90) $(FLAGS) -c $^ -o $@ $(MODFLAGS)
$(SRCDIR)/../bin/%.o: $(SRCDIR)/%.f90
$(F90) $(FLAGS) -c $^ -o $@
main: $(ALLOBJ)
$(F90) $(FLAGS) -o ./bin/main $(BINOBJ)
clean:
@rm bin/*.o bin/*.mod
Но теперь я хочу написать общее правило, чтобы иметь возможность компилировать все модули, расположенные в каталогах, которые находятся внутри modules
каталога (код, над которым я работаю, содержит более двух подмодулей, и я не хочу писать столько правил, сколько у меня подкаталогов).
Моей первой попыткой было написать что-то вроде этого:
$(SMODDIR)/../../%.o: $(SMODDIR)/%.f90
$(F90) $(FLAGS) -c $^ -o $@ $(MODFLAGS)
но это не удается; из того, что я понимаю, он поместит все пути к подкаталогам, и я фактически получу правило, которое будет выглядеть следующим образом:
modules/mod1/../../bin/ modules/mod2/../../bin/%.o: modules/mod1/ modules/mod2/%.f90
это действительно выглядит забавно.
Как показано в исходном файле Makefile, его можно получить с помощью
SMODDIR = $(dir $(wildcard $(MODDIR)/*/.))
SMODFILES = $(foreach smoddir, $(SMODDIR), $(wildcard $(smoddir)*f90))
поэтому я думаю, что можно использовать что-то подобное, чтобы иметь более общие правила. Однако я не мог понять, как использовать такой синтаксис для написания правила, которое имеет смысл.
Любая помощь будет признательна!
Комментарии:
1. Можно было бы написать правила для поддержки такого типа компоновки, но сначала возникает очевидный вопрос: почему вы хотите разбросать исходные файлы по всей вашей файловой системе, а не собирать их все вместе
src
?2. Я сделал это слишком быстро, чтобы иметь MWE, извините. Однако я могу вставить
modules
src
, и мой вопрос все еще остается в силе (на самом деле это макет исходного кода).3. Конечно, все равно было бы намного проще иметь
src/main.f90
,src/mod.f90
,src/mod1.f90
иsrc/mod2.f90
? Я не говорю, что вы не можете/не должны поступать по-другому, но часто легче сформулировать ответ, когда мы знаем, почему существует более сложная ситуация.4. На самом деле реальный случай был бы больше похож
src/main.f90
на,src/modules/mod.f90
,src/modules/mod1/mod1.f90
. Причина разброса файлов по подкаталогам заключается в том, чтобы собрать связную группу исходных файлов. В реальном коде это, например, соответствовало бы различным задействованным физическим процессам и так далее.5. Проблема не в том, что у вас есть исходные файлы в разных каталогах. Это прекрасно. Проблема в том, что вы хотите поместить все свои объектные файлы в ОДИН каталог. Если бы ваши объектные файлы имели ту же структуру каталогов, что и исходные файлы , за исключением другого корневого
bin
каталога, то вы могли бы сделать это тривиально с помощью одного правила. Проблема, с которой вы столкнулись, заключается в том, что ваш исходный файл и ваш объектный файл не могут быть связаны друг с другом одним шаблоном, если у них разные структуры каталогов.
Ответ №1:
У вас есть два варианта, чтобы избежать написания большого количества правил.
Во-первых, вы можете использовать VPATH и поместить в него все свои исходные каталоги, что-то вроде этого:
VPATH := $(MODDIR) $(SMODDIR) $(SRCDIR) $(BINDIR)/%.o: %.f90 $(F90) $(FLAGS) -c
lt; -o $@ $(MODFLAGS)
Или вы можете поместить все свои объектные файлы в эквивалентные подкаталоги $(BINDIR), что-то вроде этого:MODOBJ = $(MODFILES:%.f90=$(BINDIR)/%.o) SMODOBJ = $(SMODFILES:%.f90=$(BINDIR)/%.o) SRCOBJ = $(SRCFILES:%.f90=$(BINDIR)/%.o) $(BINDIR)/%.o: %.f90 @mkdir -p $(@D) $(F90) $(FLAGS) -c
lt; -o $@ $(MODFLAGS)