Building programs from separate makefiles

I have several makefiles to build various programs in a software suite (currently 4 programs). I want to create a main Makefile so that I can build everything I need.

Unsure on the way I should proceed, for example using

include fdtc.mk

or calling

$(MAKE) -f ./mk/Makefile nfdtc 

Here is a listing of one makefile, the others are similar

#####
#
# Makefile for fdtc application
#
#####

#--------------------------------------------------------------------------------------

# Source code paths

OPSYS = $(shell uname -s )
TARGET = nraypk

FDTC_INCDIR = ../include/fdtc

FDTC_PRGDIR = ../src/app
FDTC_LIBDIR = ../src/flib/fdtc
PLOTX_LIBDIR = ../src/flib/plotx11

OBJDIR = ./obj
BINDIR = ./bin

APP = $(BINDIR)/$(TARGET)

FDTC_APPDIR = ../src/app
FDTC_LIBDIR = ../src/flib/fdtc
PLOT_LIBDIR = ../src/flib/pltlib

OBJDIR = ./obj
BINDIR = ./bin

#--------------------------------------------------------------------------------------

# Set Fortran compiler
FCOMP = gfortran

# Set Fortran compiler options
FCOMP_OPT = -O -Wall -fbacktrace -fno-align-commons -I$(FDTC_INCDIR)

# Set Fortran compiler options
FLINK_OPT = -O -Wall -fbacktrace -fno-align-commons -I$(FDTC_INCDIR)

#--------------------------------------------------------------------------------------

# Source files

FDTC_APPSRC = $(FDTC_APPDIR)/fdtc.f

FDTC_LIBSRC = $(FDTC_LIBDIR)/model.f \
    $(FDTC_LIBDIR)/time.f \
    $(FDTC_LIBDIR)/findiff.f \
    $(FDTC_LIBDIR)/findiff2d.f \
    $(FDTC_LIBDIR)/stencils.f \
    $(FDTC_LIBDIR)/stencils2d.f \
    $(FDTC_LIBDIR)/misc.f \
    $(FDTC_LIBDIR)/plt.f \
    $(FDTC_LIBDIR)/blkdat.f

PLOTX_SRC = $(PLOTX_LIBDIR)/nopltlib.f

FDTC_LIBINC = $(FDTC_INCDIR)/fd.par \
    $(FDTC_INCDIR)/fd.com

#--------------------------------------------------------------------------------------
# Object files

FDTC_PRGOBJ = $(subst .f,.o,$(subst $(RAYPK_PRGDIR),$(OBJDIR),$(RAYPK_PRGSRC)))
FDTC_LIBOBJ = $(subst .f,.o,$(subst $(RAYPK_LIBDIR),$(OBJDIR),$(RAYPK_LIBSRC)))
PLOTX_LIBOBJ = $(subst .f,.o,$(subst $(PLOTX_LIBDIR),$(OBJDIR),$(PLOTX_LIBSRC)))

FDTC_APPOBJ = $(OBJDIR)/fdtc.o

FDTC_LIBOBJ = $(OBJDIR)/model.o \
    $(OBJDIR)/time.o \
    $(OBJDIR)/findiff.o  \
    $(OBJDIR)/findiff2d.o \
    $(OBJDIR)/stencils.o \
    $(OBJDIR)/stencils2d.o \
    $(OBJDIR)/misc.o \
    $(OBJDIR)/plt.o \
    $(OBJDIR)/blkdat.o

PLOTX_LIBOBJ = $(OBJDIR)/nopltlib.o

FDTC_ALLOBJ = $(FDTC_APPOBJ) $(FDTC_LIBOBJ) $(PLOTX_LIBOBJ)

# Name of FDTC application
FDTC_APPNAME = $(BINDIR)/nfdtc

#--------------------------------------------------------------------------------------

default: help

help:
    @echo " "
    @echo "Operating System Detected: $(OPSYS) "
    @echo " "
    @echo "USAGE: "
    @echo " "
    @echo "  make -f nraypk.mk           To get this listing"
    @echo "  make -f nraypk.mk help      To get this listing"
    @echo "  make -f nraypk.mk list      List the details of building nraypk"
    @echo "  make -f nraypk.mk nraypk    Builds the application nraypk in BINDIR"
    @echo "  make -f nraypk.mk clean     Remove *.o and executable files"

list:
    @echo ""
    @echo "OPSYS:  $(OPSYS)"
    @echo "APPL:   $(TARGET)"
    @echo "FCOMP:  $(FORTRAN_COMPILER)"
    @echo "CCOMP:  $(C_COMPILER)"
    @echo "CURDIR: $(CURDIR)"
    @echo ""
    @echo "---------------"
    @echo ""
    @echo "DIRECTORIES (relative to CURDIR)"
    @echo ""
    @echo "  OBJDIR = $(OBJDIR)"
    @echo "  BINDIR = $(BINDIR)"
    @echo ""
    @echo "---------------"
    @echo ""
    @echo "SOURCE FILES (relative to CURDIR)"
    @echo ""
    @echo "  RAYPK_PRGSRC = $(RAYPK_PRGSRC)"
    @echo ""
    @echo "  RAYPK_LIBSRC = $(LIBSRC_MSG1)"
    @$(foreach s, $(LIBSRC_MSG2), echo "                 $(s)";)
    @echo ""
    @echo "  PLOTX_LIBSRC = $(PLOTX_LIBSRC)"
    @echo ""
    @echo "---------------"
    @echo ""
    @echo "OBJECT FILES (relative to CURDIR)"
    @echo ""
    @echo "  RAYPK_PRGOBJ = $(RAYPK_PRGOBJ)"
    @echo ""
    @echo "  RAYPK_LIBOBJ = $(LIBOBJ_MSG1)"
    @$(foreach s, $(LIBOBJ_MSG2), echo "                 $(s)";)
    @echo ""
    @echo "  PLOTX_LIBOBJ = $(PLOTX_LIBOBJ)"

#--------------------------------------------------------------------------------------

# Generate nfdtc application
nfdtc : $(FDTC_ALLOBJ) $(FDTC_LIBINC)
    $(FCOMP) $(FLINK_OPT) $(FDTC_ALLOBJ) -o $(FDTC_APPNAME)

# Compile main program
$(FDTC_APPOBJ) : $(FDTC_APPSRC) $(FDTC_LIBINC)
    $(FCOMP) $(FCOMP_OPT) -c $(FDTC_APPSRC)
    mv *.o $(OBJDIR)

# Compile FD Library
$(FDTC_LIBOBJ) : $(FDTC_LIBSRC) $(FDTC_LIBINC)
    $(FCOMP) $(FCOMP_OPT) -c $(FDTC_LIBSRC)
    mv *.o $(OBJDIR)

# Compile PLOT Library, with plotting mode disabled
$(FDTC_NOPLOT_LIBOBJ) : $(NOPLOT_SRC) $(FDTC_LIBINC)
    $(FCOMP) $(FCOMP_OPT) -c $(NOPLOT_SRC)
    mv *.o $(OBJDIR)

#--------------------------------------------------------------------------------------

clean:
    rm -f -I $(OBJDIR)/*.o $(BINDIR)/xzslice

allclean:
    rm -f -I $(OBJDIR)/*.o $(BINDIR)/*

#######################################################################################
# END

One of the simpler ways to do this is recursive make:

outputfolder/outputfile:outputfolder/Makefile
        make -C outputfolder

which is equivalent to cd outputfolder ; make

This lets you call a makefile in another folder without having to incorporate it into your own lock, stock, and barrel.

make supports recursion directly, and will tell you how deep into recursion it is.

A problem I have is how to pass the option from the main makefile to the subdirectory makefile. I do not like using recursive make build systems (example a make in every directory). However, since I have put all the make files in one place together with a main Makefile, I do not see a problem.

I agree in principle. When writing new makefiles, one should write it as one system so it can run efficiently.

When you have to use someone else's makefiles however, you have very little control.

If you're not rewriting them, cramming them all together in one folder is hardly better than keeping them separate. When they were in folders, they were at least organized. Now it's one awful pile.

What option are you trying to pass, to what? If you're not calling make again, there's not a lot to pass options to.

What I did was to have only one makefile for each application and store them in a single folder. Then the main Makefile will execute the appropriate Makefile

I think I have a solution. Let me try it and will report back.

---------- Post updated at 11:32 AM ---------- Previous update was at 11:26 AM ----------

I am rewriting them.

I cannot quite understand how to use the above code

My directory structure is as below. The user uses Makefile. The makefile calls
the makefile for the specific program.

.
 Makefile
 mk
     fdtc.mk
     raypk.mk
     zslice.mk

Here is the code for Makefile

default: help

help : help-nfdtc help-nraypk help-xraypk help-xzslice 

help-nfdtc : 
    $(MAKE) -f fdtc.mk help

help-nraypk : 
    $(MAKE) -f raypk.mk help

help-xraypk : 
    $(MAKE) -f raypk.mk help

help-xzslice : 
    $(MAKE) -f zslice.mk help

list : list-nfdtc list-nraypk list-xraypk list-xzslice

list-nfdtc :
    $(MAKE) -f fdtc.mk list
    
list-nraypk :
    $(MAKE) -f raypk.mk list

list-xraypk :
    $(MAKE) -f raypk.mk list
    
list-xzslice :
    $(MAKE) -f zslice.mk list
    
#---------------------------------------------------------------------------------------------------

nfdtc :
    $(MAKE) -f fdtc.mk nfdtc

nraypk :
    $(MAKE) -f raypk.mk nraypk

xraypk :
    $(MAKE) -f raypk.mk xraypk

xzslice :
    $(MAKE) -f zslice.mk xzslice

#---------------------------------------------------------------------------------------------------

clean : clean-fdtc clean-raypk clean-zslice

clean-fdtc :
    make -f fdtc.mk clean

clean-raypk :
    make -f raypk.mk clean

clean-zslice :
    make -f zslice.mk clean

I have no idea why you have done what you have, now. Why bother rewriting it if you're going to do exactly what you said you were trying to avoid? You've got exactly what you had before, except instead of nicely organized into folders, it's one enormous mess.

You might as well have just let them live in their own folders and used their old makefiles. Then you could do this:

all:projectfolder1/projectoutputfile projectfolder2/projectoutputfile ...

projectfolder1/projectoutputfile:projectfolder1/Makefile
        make -C projectfolder1

projectfolder2/projectoutputfile:projectfolder2/Makefile
        make -C projectfolder2

Then you could do 'make all' and it would run make in each of the folders.

There was a makefile in every subdirectory which together build each program. Now I have just a single makefile for every program, and a main Makefile.

Yes. So you turned a build system with one makefile, per program..

Into a build system with one makefile, per program...

But jumbled them all into one folder instead of leaving them where they belong.

How is this better exactly?

You can use makefiles to call makefiles in other folders with make -C like I've demonstrated.

I'm getting you, so I should put them in the directory where the main of each program resides. Is this what your are saying? Can do that. The original was using several libraries to build each program, with each library having its own makefile

What are you trying to do with your new makefiles that the old ones couldn't do? If all you want is a central point of control, make -C can do that.

That's what I need central point of control.

Imagine a tree of files like this:

   .
   |-Makefile
   |-folder1
   |---Makefile
   |---out1
   |-folder2
   |---Makefile
   |---out2
   |-folder3
   |---Makefile
   |---out3
   |-folder4
   |---Makefile
   |---out4

The individual makefiles look like this:

out2:
        echo out2 > out2

And the makefile in the root folder looks like this:

OBJS=folder1/out1 folder2/out2 folder3/out3 folder4/out4

finalresult:$(OBJS)
        cat $(OBJS) > finalresult

folder1/out1:
        make -C folder1

folder2/out2:
        make -C folder2

folder3/out3:
        make -C folder3

folder4/out4:
        make -C folder4

clean:
        rm -f $(OBJS) finalresult

So when you run make, the root makefile runs make inside each of the sub-folders, and uses the files they make to assemble the final result.

Does the following rule create a problem? I have a target nraypk
that calls another makefile raypk.mk to build nraypk. Does the
fact that they have the same name (nraypk) cause a problem?

nraypk :
    $(MAKE) -C ./mk -f raypk.mk nraypk

I think that should work, but the way to be sure is to try...

It works.