bc28d14ebe
Idea: a release will be a git tag, distributed as an autogenerated archive (eg. by GitLab release feature). This version string must therefore be stored in a revisioned file, so we use `.version` for this purpose. Before tagging a new release this file will have to be updated with the new version number. When building gray the revision (shown in the output headers and usage screen) will be set to either - the latest commit hash when building from a git checkout, - from the .version file when building from release archive. The implementation is based on whether the .git directory is present.
273 lines
6.9 KiB
Makefile
273 lines
6.9 KiB
Makefile
# 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 = $(shell find '$(SRCDIR)' -name '*.f90')
|
|
|
|
# All Fortran object
|
|
OBJECTS = $(addprefix $(OBJDIR)/,$(notdir $(SOURCES:f90=o)))
|
|
|
|
# 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) $(GRAY)-convert
|
|
LIBRARIES = $(LIBGRAY)
|
|
MANPAGES = $(addprefix $(SHAREDIR)/,gray.1 gray-convert.1 gray.ini.5 \
|
|
profiles.txt.5 beamdata.txt.5 magneticdata.txt.5)
|
|
|
|
##
|
|
## Version information
|
|
##
|
|
|
|
ifneq ($(wildcard .git/*),)
|
|
# We are building from a git checkout
|
|
|
|
# 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")
|
|
|
|
REVISION ?= $(GIT_REV)$(GIT_DIRTY)
|
|
else
|
|
# We are building from a release archive, use the version
|
|
REVISION ?= v$(file < .version)
|
|
endif
|
|
|
|
# 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
|
|
##
|
|
|
|
LD = $(FC)
|
|
FFLAGS += -I$(INCDIR) -fpic
|
|
LDFLAGS += -L$(LIBDIR)
|
|
CPPFLAGS += -DREVISION=\"$(REVISION)\" -DPREFIX=\"$(PREFIX)\"
|
|
|
|
# Compiler-specific flags
|
|
ifdef GNU
|
|
FFLAGS += -J$(OBJDIR) -frecursive
|
|
FFLAGS += -fmax-errors=1 -ffree-line-length-none
|
|
CPPFLAGS += -DGNU
|
|
endif
|
|
ifdef INTEL
|
|
FFLAGS += -module $(OBJDIR) -assume recursion -heap-arrays
|
|
FFLAGS += -diag-error-limit=1
|
|
CPPFLAGS += -DINTEL
|
|
endif
|
|
|
|
ifdef PARALLEL
|
|
FFLAGS += -ftree-parallelize-loops=$(PARALLEL) -fopt-info-loop
|
|
LDFLAGS += -fopenmp
|
|
endif
|
|
|
|
# Sync the output from jobs running in parallel
|
|
MAKEFLAGS += --output-sync
|
|
|
|
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
|
|
all: $(BINARIES) $(LIBRARIES)
|
|
|
|
# Remove all generated files
|
|
.PHONY: clean
|
|
clean:
|
|
rm -r $(BUILDDIR)
|
|
|
|
# Run all tests
|
|
.PHONY: check
|
|
check: $(shell python -Bm tests --list-cases)
|
|
|
|
# Run a test case
|
|
tests.%: $(GRAY)
|
|
python -Bm tests '$@' --binary '$(GRAY)' --buffer
|
|
|
|
# Install libraries, binaries and documentation
|
|
.PHONY: install-bin
|
|
install-bin: $(BINARIES) $(LIBRARIES)
|
|
mkdir -p $(PREFIX)/{bin,lib}
|
|
install -m555 -t $(PREFIX)/bin $(BINDIR)/*
|
|
install -m555 -t $(PREFIX)/lib $(LIBDIR)/*
|
|
|
|
.PHONY: install-doc
|
|
install-doc: $(SHAREDIR)/doc $(MANPAGES)
|
|
mkdir -p $(PREFIX)/share/{doc/res,man/man{1,5}}
|
|
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
|
|
|
|
.PHONY: install
|
|
install: install-bin install-doc
|
|
|
|
# dependencies
|
|
$(OBJDIR)/%.o: $(OBJDIR)/%.d
|
|
|
|
# GRAY binary
|
|
$(GRAY): $(OBJDIR)/main.o $(LIBGRAY) | $(BINDIR)
|
|
$(LD) $(LDFLAGS) -o '$@' $< -lgray
|
|
|
|
# gray-convert binary
|
|
$(GRAY)-convert: $(OBJDIR)/main_convert.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))
|
|
|
|
# Don't update archives in parallel, it's unsupported
|
|
.NOTPARALLEL: $(LIBDIR)/libgray.a
|
|
|
|
# GRAY static library when compiling from JETTO
|
|
-include ../include.mk
|
|
$(JLIBDIR)/libgray.a: $(LIBDIR)/libgray.a
|
|
mkdir -p $(LIBDIR)
|
|
install -m755 '$<' '$@'
|
|
|
|
# All documentation
|
|
.PHONY: doc
|
|
docs: $(SHAREDIR)/doc $(MANPAGES)
|
|
|
|
.PHONY: $(SHAREDIR)/doc
|
|
$(SHAREDIR)/doc: | $(SHAREDIR)
|
|
+make -C doc
|
|
|
|
# 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)/gray-convert.1: $(GRAY)-convert | $(SHAREDIR)
|
|
help2man '$<' -N -n 'configuration format converter' > '$@'
|
|
|
|
$(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
|
|
|
|
# Search for source code in vendor/ subdirectory
|
|
vpath %.f90 $(SRCDIR):$(SRCDIR)/vendor
|
|
|
|
# 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: %.f90 | $(OBJDIR)
|
|
@printf '$(@:d=o): $< \\\n' > '$@'
|
|
@grep -vE '^[[:blank:]]*use.*(intrinsic|iso_|ifport)' '$<' | \
|
|
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: %.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 '$@'
|