1

I have the following Makefile sample:

BUILD = build
PATH_POT3 = src/pot/pot3/
MPIFC = mpiifort
FFLAGS = -O3

MOL_LIST = $(shell find $(PATH_POT3) -mindepth 1 -maxdepth 1 -type d)

$(foreach MOL,$(MOL_LIST), \
  $(eval LIST_MOL := $(shell find $(MOL) -name "*.f90")) \
  $(info Fortran files: $(LIST_MOL)) \
  $(foreach SRC,$(LIST_MOL), \
    $(eval OBJ := $(BUILD)/$(notdir $(SRC:.f90=.o))) \
    $(OBJ): $(SRC) ; \
    $(info Compiling: $(SRC)) \
    $(info Output: $(OBJ)) \
    $(MPIFC) $(FFLAGS) -c $(SRC) -o $(OBJ) \
  ) \
)

all: $(foreach MOL,$(MOL_LIST),\
    $(foreach SRC,$(shell find $(MOL) -name "*.f90"),\
        $(BUILD)/$(notdir $(SRC:.f90=.o))\
    )\
)

Here I would like to compile the (recursive) content of each PATH_POT3 subdirectory in a sequential way (one subdirectory after another). Unfortunately, this construction does not provide the desired result as it compiles only the first object file in the list, all the other object files are skipped. The Makefile prints out the following information for my test case:

Fortran files: src/pot/pot3/ar3/pot_ar3.f90 src/pot/pot3/ar3/ar43/pot_ar43.f90
Compiling: mpiifort -O3 -c src/pot/pot3/ar3/pot_ar3.f90 -o build/pot_ar3.o
Output: build/pot_ar3.o
Compiling: mpiifort -O3 -c src/pot/pot3/ar3/ar43/pot_ar43.f90 -o build/pot_ar43.o
Output: build/pot_ar43.o
Fortran files: src/pot/pot3/ar33/pot_ar33.f90 src/pot/pot3/ar33/pot_ar333.f90 src/pot/pot3/ar33/ar53/pot_ar53.f90
Compiling: mpiifort -O3 -c src/pot/pot3/ar33/pot_ar33.f90 -o build/pot_ar33.o
Output: build/pot_ar33.o
Compiling: mpiifort -O3 -c src/pot/pot3/ar33/pot_ar333.f90 -o build/pot_ar333.o
Output: build/pot_ar333.o
Compiling: mpiifort -O3 -c src/pot/pot3/ar33/ar53/pot_ar53.f90 -o build/pot_ar53.o
Output: build/pot_ar53.o
make: 'build/pot_ar3.o' is up to date.

Everything seems to be set correctly, but the compilation is executed only for build/pot_ar3.o.

How should I change this file to obtain the required behavior?

1 Answer 1

2

The issue is with how you're trying to create rule definitions inside the foreach loop. Your info statements show everything is being detected correctly, but only the first object file is being compiled. This happens because Make doesn't process dynamically defined rules within loops the way you're expecting.

Here's a working solution:

BUILD = build
PATH_POT3 = src/pot/pot3/
MPIFC = mpiifort
FFLAGS = -O3

SRCS := $(shell find $(PATH_POT3) -name "*.f90")
OBJS := $(patsubst %.f90,$(BUILD)/%.o,$(notdir $(SRCS)))

all: $(OBJS)

$(BUILD):
    mkdir -p $(BUILD)

define make-depend
$(BUILD)/$(notdir $(basename $(1))).o: $(1) | $(BUILD)
    @echo "Compiling $$< -> $$@"
    $(MPIFC) $(FFLAGS) -c $$< -o $$@
endef

$(foreach src,$(SRCS),$(eval $(call make-depend,$(src))))

.PHONY: all clean

clean:
    rm -rf $(BUILD)

The key here is using eval to actually create proper Make rules from the make-depend function. Your original approach was printing the commands during the parsing phase but wasn't correctly setting up the actual rules.

This works by finding all source files up front and creating a proper rule for each file using eval with the make-depend function. The | symbol ensures the build directory exists before compilation starts.

Hope this will work for you.

** Edit **

This is what we've discussed in chat:

BUILD = build
PATH_POT3 = src/pot/pot3
MPIFC = mpiifort
FFLAGS = -O3
LDFLAGS =

MOL_LIST = $(shell find $(PATH_POT3) -mindepth 1 -maxdepth 1 -type d)

SRCS := $(shell find $(PATH_POT3) -name "*.f90")
OBJS := $(patsubst %.f90,$(BUILD)/%.o,$(notdir $(SRCS)))

all: $(OBJS) $(patsubst %,$(BUILD)/%.exe,$(notdir $(MOL_LIST)))

$(BUILD):
   mkdir -p $(BUILD)

define make-depend
$(BUILD)/$(notdir $(basename $(1))).o: $(1) | $(BUILD)
   @echo "Compiling $$< -> $$@"
   $(MPIFC) $(FFLAGS) -c $$< -o $$@
endef

$(foreach src,$(SRCS),$(eval $(call make-depend,$(src))))

define make-executable
MOL_SRCS_$(1) := $$(shell find $(1) -name "*.f90")
MOL_OBJS_$(1) := $$(patsubst %.f90,$(BUILD)/%.o,$$(notdir $$(MOL_SRCS_$(1))))

$(BUILD)/$$(notdir $(1)).exe: $$(MOL_OBJS_$(1)) | $(BUILD)
   @echo "Linking $$@ from $$^"
   $(MPIFC) $(LDFLAGS) $$^ -o $$@
endef

$(foreach mol,$(MOL_LIST),$(eval $(call make-executable,$(mol))))

.PHONY: all clean

clean:
   rm -rf $(BUILD)
Sign up to request clarification or add additional context in comments.

2 Comments

Thank you very much for posting this answer! After compiling the source files, I need to create a separate executable file for each $(MOL) entry of $(MOL_LIST), based on the object files compiled for the *.f90 entries rooted in $(MOL). For this purpose, I think I need the list of the associated object files, $(OBJ_MOL). Could you please help me how to manage this?
For that you need to create separate executables for each $(MOL) directory based on their compiled object files. That's a good extension to the Makefile. Maybe we can discuss that in this chat room

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.