# Load the configure script variables -include configure.mk ## ## Directories ## # Structure: # gray - top level # ├── src - source code # ├── doc - documentation # ├── tests - integration tests # └── build - build artifacts # ├── bin - binaries # ├── lib - libraries # ├── obj - objects # └── inc - files to #include SRCDIR = src BUILDDIR = build SHAREDIR = build/share BINDIR = build/bin LIBDIR = build/lib OBJDIR = build/obj INCDIR = build/inc PREFIX ?= /usr # Directories that need to be created DIRS = $(BINDIR) $(OBJDIR) $(INCDIR) $(LIBDIR) $(SHAREDIR) ## ## Files ## # All Fortran source files SOURCES = $(wildcard $(SRCDIR)/*.f90) # All Fortran object OBJECTS = $(patsubst $(SRCDIR)/%.f90,$(OBJDIR)/%.o,$(SOURCES)) # Generated makefiles describing object dependencies DEPS = $(OBJECTS:o=d) # Fortran modules to be bundled into libraries MODULES = $(filter-out $(OBJDIR)/main%,$(OBJECTS)) # Build outputs GRAY = $(BINDIR)/gray LIBGRAY = $(LIBDIR)/libgray.so BINARIES = $(GRAY) LIBRARIES = $(LIBGRAY) MANPAGES = $(addprefix $(SHAREDIR)/,gray.1 gray.ini.5 profiles.txt.5 beamdata.txt.5 magneticdata.txt.5) ## ## Git information (used in the version string) ## # Short hash of the latest commit GIT_REV ?= $(shell git rev-parse --short HEAD) # Whether the worktree and the latest commit differs GIT_DIRTY ?= $(shell test -n "$$(git status --porcelain)" && echo "-dirty") # Source date ifndef SOURCE_DATE_EPOCH # Use current date SOURCE_DATE_EPOCH=$(shell awk 'BEGIN {print srand(srand())}') endif DATE=$(shell LC_TIME=C date -d @$(SOURCE_DATE_EPOCH) '+%B %Y') ## ## Fortran compiler and flags ## # Note: can't use ?= for FC because GNU Make defaults to f77 FC = gfortran LD = gfortran FFLAGS += -J$(OBJDIR) -I$(INCDIR) -ffree-line-length-none -fPIC -frecursive LDFLAGS += -L$(LIBDIR) CPPFLAGS += -DREVISION=\"$(GIT_REV)$(GIT_DIRTY)\" -DPREFIX=\"$(PREFIX)\" ifndef DETERMINISTIC ifdef AR_DEFAULT_DETERMINISTIC # Write timestamps to allow partial updates ARFLAGS = crU endif endif # Static build options ifdef STATIC LIBGRAY := $(LIBGRAY:so=a) LIBRARIES := $(LIBRARIES:so=a) ifndef SEMISTATIC LDFLAGS += -static endif else LDFLAGS += -Wl,-rpath '$$ORIGIN/../lib/' endif # Debug build options ifdef DEBUG FFLAGS += -ggdb -O0 -Wall -Wunused-parameter # Tricks to detect uninitialised memory FFLAGS += -finit-integer=7777777 -finit-real=snan -ffpe-trap=invalid else # Optimization level 3 FFLAGS += -O3 endif ## ## Targets ## .PHONY: all clean check install docs # Don't update archives in parallel, it's unsupported .NOTPARALLEL: $(LIBDIR)/libgray.a all: $(BINARIES) $(LIBRARIES) # Remove all generated files clean: rm -r $(BUILDDIR) # Run tests check: $(GRAY) python -Bm tests --binary $^ # Install libraries, binaries and documentation install: $(BINARIES) $(LIBRARIES) $(SHAREDIR)/doc $(MANPAGES) mkdir -p $(PREFIX)/{bin,lib,share/{doc/res,man/man{1,5}}} install -m555 -t $(PREFIX)/bin $(BINDIR)/* install -m555 -t $(PREFIX)/lib $(LIBDIR)/* install -m644 -t $(PREFIX)/share/doc $(SHAREDIR)/doc/manual.* install -m644 -t $(PREFIX)/share/doc/res $(SHAREDIR)/doc/res/* install -m644 -t $(PREFIX)/share/man/man1 $(SHAREDIR)/*.1 install -m644 -t $(PREFIX)/share/man/man5 $(SHAREDIR)/*.5 # dependencies $(OBJDIR)/%.o: $(OBJDIR)/%.d # GRAY binary $(GRAY): $(OBJDIR)/main.o $(LIBGRAY) | $(BINDIR) $(LD) $(LDFLAGS) -o '$@' $< -lgray # GRAY shared library $(LIBDIR)/libgray.so: $(MODULES) | $(LIBDIR) $(LD) -shared $(LDFLAGS) -o '$@' $^ # GRAY static library $(LIBDIR)/libgray.a($(MODULES)): | $(LIBDIR) $(LIBDIR)/libgray.a: $(LIBDIR)/libgray.a($(MODULES)) # GRAY static library when compiling from JETTO -include ../include.mk $(JLIBDIR)/libgray.a: $(LIBDIR)/libgray.a mkdir -p $(LIBDIR) install -m755 '$<' '$@' # All documentation docs: $(SHAREDIR)/doc $(MANPAGES) $(SHAREDIR)/doc: | $(SHAREDIR) +make -C doc cp -r doc/build/* $(SHAREDIR) # Generated man pages $(SHAREDIR)/gray.1: $(GRAY) doc/man/gray.1 | $(SHAREDIR) help2man '$<' -i doc/man/gray.1 -N -n 'beam-tracing code for EC waves' > '$@' $(SHAREDIR)/%: doc/man/%.md $(GRAY) | $(SHAREDIR) pandoc -s '$<' -t man \ -V date='$(DATE)' \ -V footer='$(shell $(GRAY) --version | sed -n 1p)' \ -V author='$(shell $(GRAY) --version | sed -n "3 s/.$$//p")' \ -o '$@' # Visualise the dependency graph # Note: requires makefile2graph and graphviz graph.svg: Makefile make -Bdn \ | make2graph -b \ | awk -F'( -> )| ;|[[]' ' \ (/label/ && !/(\.o|bin\/gray)/) {bad[$$1]=1; next} \ /->/ {if (bad[$$1] || bad[$$2]) next} \ {print $$0}' \ | sed 's/label/fontsize=30,label/g; s/\.o//' \ | dot -Tsvg -o '$@' ## ## Generic rules ## # Rebuild everything if the makefile changed .EXTRA_PREREQS += Makefile # Automatic generation of the Fortran prerequisites # Note 1: this is needed because gfortran -M flag doesn't properly work; # Note 2: this assumes matching module/file names and no circular # dependencies (ie. the dependency graph is a DAG); $(OBJDIR)/%.d: $(SRCDIR)/%.f90 | $(OBJDIR) @printf '$(@:d=o): $< \\\n' > '$@' @grep -vE '^[[:blank:]]*use.*(intrinsic|iso_)' '$<' | \ sed -nE 's@^[[:blank:]]*use[[:blank:]]+'\ '([^,[:blank:]&]+).*@$(OBJDIR)/\1.o \\@p' | \ sort -u >> '$@' @sed -nE 's@^#include "(.+)"@$(INCDIR)/\1 \\@p' '$<' >> '$@' # Load the generated rules -include $(DEPS) # Compile a generic Fortran source $(OBJDIR)/%.o: $(SRCDIR)/%.f90 | $(OBJDIR) $(INCDIR) $(FC) -cpp $(CPPFLAGS) $(FFLAGS) -c '$<' -o '$@' # Generate Fortran code from awk script $(INCDIR)/%.inc: $(SRCDIR)/%.awk | $(INCDIR) awk -f '$<' > '$@' # Create directories $(DIRS): mkdir -p '$@'