Напишите универсальное правило создания файлов для подкаталогов

#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)