# This Makefile requires GNU Make specifically, not just any POSIX
# make - it relies on several GNU-only extensions throughout ($(shell
# ...) below, "+=" appending to MAKEFLAGS, "ifeq"/"else ifeq"
# conditionals, and "%:%.f90"-style pattern rules), none of which a
# BSD/POSIX make understands. In practice this is not an extra
# install step on any of bifpy's three target platforms: it is
# Linux's own default `make`, it is what Xcode's Command Line Tools
# put at /usr/bin/make on macOS (an old GNU Make 3.81, not BSD make),
# and it is what a MinGW/MSYS2 install provides on Windows alongside
# gfortran itself - see README.md's own "Building" section.
#
# Disables GNU Make's built-in implicit rules - in particular its
# legacy Modula-2 rule "%.o : %.mod", which otherwise collides with
# Fortran's own unrelated use of the .mod extension for module
# interface files: after `make clean` (which removes .o files but
# deliberately leaves .mod files alone - see clean/distclean below),
# a stale src/foo.mod with no matching src/foo.o made Make pick that
# builtin rule over this file's own explicit .f90.o rule, failing with
# "m2c: No such file or directory" (there is no Modula-2 compiler here
# - "m2c" is just that rule's hardcoded compiler name). Safe to
# disable entirely: every rule actually used to build this project
# (.f90.o, %:%.f90, %:%.f, depends/%.o) is already defined explicitly
# below, not inherited from Make's built-in database.
MAKEFLAGS += -r

# The shared library's own file extension is platform-specific -
# gfortran's -shared flag builds a real ELF .so on Linux, a Mach-O
# .dylib on macOS, and (via MinGW-w64 gfortran, the toolchain
# implied by requiring gfortran+make on Windows at all, since MSVC
# cannot compile Fortran) a PE .dll on Windows - ctypes.CDLL loads
# each of these correctly, but the file must exist under the name its
# own platform expects. UNAME_S is left blank rather than probed with
# $(shell uname -s) when uname itself is unavailable (a plain Windows
# cmd.exe/PowerShell shell, as opposed to a MinGW/MSYS2 one, which
# does provide uname) - SOEXT then falls back to .dll, since gfortran
# on Windows only ever comes from a MinGW-family toolchain anyway.
UNAME_S := $(shell uname -s 2>/dev/null)
ifeq ($(UNAME_S),Darwin)
  SOEXT := dylib
else ifeq ($(UNAME_S),Linux)
  SOEXT := so
else
  SOEXT := dll
endif

F77    := gfortran
F90    := gfortran
LIBAUT := bifpy
LIBDIR := lib
MKADIR := mkdir -p
MODULS := auto_constants nompi f2003 mesh support io ae interfaces \
          toolboxae equilibrium solvebv bvp toolboxbv homcont maps \
          floquet periodic optimization parabolic timeint user_c \
          utility
MODOBJ := $(patsubst %, src/%.o, $(MODULS))
MODDIR := src
# capi/*.f90 (bifpy's own code, not part of MODULS) ends up in AUTOBJ
# below, which the shared/static targets build after MODOBJ - so by
# the time it's compiled, the vendored modules it USEs already have
# their .mod interface files available.
LIBSRC := $(wildcard src/*.f90) $(wildcard capi/*.f90)
AUTOBJ := $(filter-out $(MODOBJ),$(patsubst %.f90,%.o, $(LIBSRC)))
DEPDIR := depends
DEPSRC := $(wildcard $(DEPDIR)/*.f)
DEPOBJ := $(patsubst %.f,%.o, $(DEPSRC))
INCDIR := include

# Auto-07p's own original command-line front end (PROGRAM AUTO,
# vendored at native/main.f90 - see vendor-manifest.txt's own comment
# on why it lives outside src/), needed by bifpy.auto's transpile=True
# native-execution fast path. Deliberately NOT part of LIBSRC/AUTOBJ
# above: it must never end up inside libbifpy.so/.a (it is itself a
# PROGRAM, and every native run needs its own copy linked directly into
# a fresh executable, exactly how real Auto-07p's own demo build always
# links main.o - never through a shared/static library). NATIVEOBJ is
# built by the same generic .f90.o rule below; the explicit prerequisite
# rule further down (native/main.o: $(MODOBJ)) makes sure the module
# interface files it USEs (AUTO_CONSTANTS, IO, SUPPORT, ...) already
# exist under $(MODDIR) before it is compiled.
NATIVEOBJ := native/main.o

.SUFFIXES: .o .f90
.PHONY: $(LIBDIR) update native

all: shared

.f90.o: 
	$(F90) -c -I $(INCDIR) -fPIC -J $(MODDIR) -o $@ $<

%:%.f90
	$(F90) -o $@ $< -I $(INCDIR) -L $(LIBDIR) -l $(LIBAUT) -J $(MODDIR)

%:%.f
	$(F77) -o $@ $< -I $(INCDIR) -L $(LIBDIR) -l $(LIBAUT) -J $(MODDIR)

# This was previously written as "depends/%.f.o:", a pattern that can
# only ever match a literal target ending in ".f.o" (e.g.
# depends/blas.f.o) - never depends/blas.o, the target actually
# needed below. That meant this rule was already dead: what really
# built depends/*.o was GNU Make's built-in "%.o : %.f" rule, silently
# relied on until MAKEFLAGS += -r above disabled it. Fixed to be a
# real pattern rule, %.o depending on %.f, matching the working
# .f90.o rule above.
depends/%.o: depends/%.f
	$(F77) -c -o $@ $<

static: $(MODOBJ) $(AUTOBJ) $(DEPOBJ) $(LIBDIR)
	ar cr $(LIBDIR)/lib$(LIBAUT).a $(AUTOBJ) $(MODOBJ) $(DEPOBJ)

shared: $(MODOBJ) $(AUTOBJ) $(DEPOBJ) $(LIBDIR)
	$(F90) -shared -o $(LIBDIR)/lib$(LIBAUT).$(SOEXT) $(AUTOBJ) $(MODOBJ) $(DEPOBJ)

# Everything bifpy.auto's transpile=True fast path needs at runtime:
# lib/libbifpy.a (static, so a generated native executable is
# self-contained rather than needing libbifpy.so on its own load path)
# plus native/main.o, ready to be linked directly against a freshly
# generated equation file's own object - see NATIVEOBJ's own comment
# above for why main.o is kept separate from both library targets.
native/main.o: $(MODOBJ)

native: static $(NATIVEOBJ)

$(LIBDIR):
	$(MKADIR) $(LIBDIR)

clean:
	$(RM) $(AUTOBJ) $(MODOBJ) $(DEPOBJ) $(NATIVEOBJ)

distclean: clean
	$(RM) $(MODDIR)/*.mod
	$(RM) -r $(LIBDIR)

update:
	./update
