Skip to content
Snippets Groups Projects
  • Simon Rose's avatar
    ac5611be
    Small fix for weird buffering issue · ac5611be
    Simon Rose authored
    This seems to be a very specific, limited issue, affecting virtualbox VMs only. There appears to
    be an issue with how append data is buffered and written to disk. You can see this with the
    following commands:
    
    ```console
    $ rm -f out.tmp && seq 1569 >> out.tmp && ls -l out.tmp
    -rw-r--r-- 1 simonrose vagrant 8188 Feb 14 12:12 se.tmp
    $ rm -f out.tmp && seq 1560 >> out.tmp && ls -l out.tmp
    -rw-r--r-- 1 simonrose vagrant 8192 Feb 14 12:12 se.tmp
    $ rm -f out.tmp && seq 9999 >> out.tmp && ls -l out.tmp
    -rw-r--r-- 1 simonrose vagrant 8192 Feb 14 12:12 se.tmp
    ```
    Note that the last two files are the same size, when they should clearly be quite different. If we
    remove the `>>` and replace it with a `>`, then all works correctly:
    ```console
    $ rm -f out.tmp && seq 9999 > out.tmp && ls -l out.tmp
    -rw-r--r-- 1 simonrose vagrant 48888 Feb 14 13:14 out.tmp
    ```
    Note the file size in this case.
    ac5611be
    History
    Small fix for weird buffering issue
    Simon Rose authored
    This seems to be a very specific, limited issue, affecting virtualbox VMs only. There appears to
    be an issue with how append data is buffered and written to disk. You can see this with the
    following commands:
    
    ```console
    $ rm -f out.tmp && seq 1569 >> out.tmp && ls -l out.tmp
    -rw-r--r-- 1 simonrose vagrant 8188 Feb 14 12:12 se.tmp
    $ rm -f out.tmp && seq 1560 >> out.tmp && ls -l out.tmp
    -rw-r--r-- 1 simonrose vagrant 8192 Feb 14 12:12 se.tmp
    $ rm -f out.tmp && seq 9999 >> out.tmp && ls -l out.tmp
    -rw-r--r-- 1 simonrose vagrant 8192 Feb 14 12:12 se.tmp
    ```
    Note that the last two files are the same size, when they should clearly be quite different. If we
    remove the `>>` and replace it with a `>`, then all works correctly:
    ```console
    $ rm -f out.tmp && seq 9999 > out.tmp && ls -l out.tmp
    -rw-r--r-- 1 simonrose vagrant 48888 Feb 14 13:14 out.tmp
    ```
    Note the file size in this case.
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
driver.makefile 30.72 KiB
# driver.makefile
#
# This generic makefile compiles EPICS modules (drivers, records, snl, ...)
# for all installed EPICS versions.
# Read this documentation and the inline comments carefully before
# changing anything in this file.
#
# Usage: Create a Makefile containig the line:
#        include /ioc/tool/driver.makefile
#        Optionally add variable definitions below that line.
#
# This makefile automatically finds the source file (unless overwritten with
# the SOURCES variable in your Makefile) and generates a module consisting
# of a library and .dbd file for each EPICS version and each target architecture.
# Therefore, it calls itself recursively.
#
# - First run: (see comment ## RUN 1)
#   Find out what to build
#   Iterate over all installed EPICS versions
#
# - Second run: (see comment ## RUN 2)
#   Find the sources etc.
#   Include EPICS configuration files for this ${EPICSVERSION}
#   Iterate over all target architectures (${T_A}) defined for this version
#
# - Third run: (see comment ## RUN 3)
#   Check which target architectures to build.
#   Create O.${EPICSVERSION}_${T_A} subdirectories if necessary.
#   Change to O.${EPICSVERSION}_${T_A} subdirectories.
#
# - Fourth run: (see comment ## RUN 4)
#   Compile everything.
#
# Module names are derived from the directory name (unless overwritten
# with the MODULE variable in your Makefile).
# A LIBVERSION number is generated from the latest CVS or GIT tag of the sources.
# If any file is not up-to-date in CVS/GIT, not tagged, or tagged differently from the
# other files, the version is a test version and labelled with the user name.
# The library is installed to ${EPICS_MODULES}/${MODULE}/${LIBVERSION}/lib/${T_A}/.
# A module can be loaded with  require "<module>" [,"<version>"] [,"<variable>=<substitution>, ..."]
#
# User variables (add them to your Makefile, none is required):
# MODULE
#    Name of the built module.
#    If not defined, it is derived from the directory name.
# SOURCES
#    All source files to compile.
#    If not defined, default is all *.c *.cc *.cpp *.st *.stt in
#    the source directory (where you run make).
#    If you define this, you must list ALL sources.
# DBDS
#    All dbd files of the project.
#    If not defined, default is all *.dbd files in the source directory.
# HEADERS
#    Header files to install (e.g. to be included by other drivers)
#    If not defined, all headers are for local use only.
# EXCLUDE_VERSIONS
#    EPICS versions to skip. Usually 3.13 or 3.14
# ARCH_FILTER
#    Sub set of architectures to build for, e.g. %-ppc604

# Get the location of this file.
MAKEHOME:=$(dir $(lastword ${MAKEFILE_LIST}))
# Get the name of the Makefile that included this file.
USERMAKEFILE:=$(lastword $(filter-out $(lastword ${MAKEFILE_LIST}), ${MAKEFILE_LIST}))


##---## In E3, We only use ONE EPICS_BASE in order to COMPILE A MODULE
##---##
##---## In E3,  EPICS_LOCATION is the EPICS BASE  /testing/epics/base-MAJ.MIN.REV[.PATCH]
EPICS_LOCATION =
##---## In E3, we extract BASE_VERSION from EPICS_LOCATION
E3_EPICS_VERSION:=$(patsubst base-%,%,$(notdir $(EPICS_LOCATION)))
E3_SITEMODS_PATH =
BUILD_EPICS_VERSIONS = $(E3_EPICS_VERSION)
##---##

BUILDCLASSES = Linux
EPICS_MODULES =

MODULE_LOCATION =${EPICS_MODULES}/$(or ${PRJ},$(error PRJ not defined))/$(or ${LIBVERSION},$(error LIBVERSION not defined))


DOCUEXT = txt html htm doc pdf ps tex dvi gif jpg png
DOCUEXT += TXT HTML HTM DOC PDF PS TEX DVI GIF JPG PNG
DOCUEXT += template db dbt subs subst substitutions script

# Override config here:
-include ${MAKEHOME}/config

# Use fancy glob to find latest versions.
SHELL = /bin/bash -O extglob

# Some shell commands:
RMDIR = rm -rf
LN = ln -s
EXISTS = test -e
NM = nm
RM = rm -f
CP = cp
MKDIR = mkdir -p -m 775

# This is to allow for build numbers in recognized versions. First regex is for grep, second for sed.
VERSIONREGEX = [0-9]+\.[0-9]+\.[0-9]+(\+[0-9]+)?

# Some generated file names:
VERSIONFILE = ${PRJ}_version_${LIBVERSION}.c
REGISTRYFILE = ${PRJ}_registerRecordDeviceDriver.cpp
SUBFUNCFILE = ${PRJ}_subRecordFunctions.dbd
DEPFILE = ${PRJ}.dep
METAFILE = ${PRJ}_meta.yaml

# Clear potential environment variables.
TEMPLATES=
SOURCES=
DBDS=
DBD_INSTALLS=
HEADERS=
BASH_ENV=
ENV=

# Default target is "build" for all versions.
# Don't install anything (different from default EPICS make rules).
default: build

prebuild:

IGNOREFILES = .gitignore
%: ${IGNOREFILES}
${IGNOREFILES}:
	@echo -e "O.*\n.gitignore" > $@

# Function that removes duplicates without re-ordering (unlike sort):
define uniq
  $(eval seen :=) \
  $(foreach _,$1,$(if $(filter $_,${seen}),,$(eval seen += $_))) \
  ${seen}
endef

# Function that fetches the correct build number from the shared filesystem.
# Usage:
#
#   $(call FETCH_BUILD_NUMBER,$(E3_SITEMODS_PATH),module)
#
define FETCH_BUILD_NUMBER
$(shell $(MAKEHOME)/build_number.sh $(1) $(2) $($(2)_VERSION))
endef

ifndef EPICSVERSION
## RUN 1
# In source directory

$(foreach v,$(sort $(basename ${BUILD_EPICS_VERSIONS})),$(eval EPICS_VERSIONS_$v=$(filter $v.%,${BUILD_EPICS_VERSIONS})))


# Default module name is name of current directory.
# But in case of "src" or "snl", use parent directory instead.
# Avoid using environment variables for MODULE or PROJECT
MODULE=
PROJECT=
PRJDIR:=$(subst -,_,$(subst .,_,$(notdir $(patsubst %Lib,%,$(patsubst %/snl,%,$(patsubst %/src,%,${PWD}))))))
PRJ = $(strip $(or ${MODULE},${PROJECT},${PRJDIR}))
export PRJ

OS_CLASS_LIST = $(BUILDCLASSES)
export OS_CLASS_LIST

export ARCH_FILTER
export EXCLUDE_ARCHS
export MAKE_FIRST

# Since we force modules to be in lowercase, we need to use the correct variables here
# e.g. MCoreUtils_E3_GIT_URL vs mcoreutils_E3_GIT_URL
${PRJ}_E3_GIT_URL := $(${PROJECT}_E3_GIT_URL)
export ${PRJ}_E3_GIT_URL
${PRJ}_E3_GIT_DESC := $(${PROJECT}_E3_GIT_DESC)
export ${PRJ}_E3_GIT_DESC
${PRJ}_E3_GIT_STATUS := $(${PROJECT}_E3_GIT_STATUS)
export ${PRJ}_E3_GIT_STATUS

export SUBS
export TMPS

clean::
	$(RMDIR) O.*

uninstall:
	$(RMDIR) ${MODULE_LOCATION}

help:
	@echo "usage:"
	@for target in '' build '<EPICS version>' \
	install 'install.<EPICS version>' \
	uninstall 'uninstall.<EPICS version>' \
        installui uninstallui \
	clean help version; \
	do echo "  make $$target"; \
	done
	@echo "Makefile variables:(defaults) [comment]"
	@echo "  MODULE           (${PRJ}) [from current directory name]"
	@echo "  PROJECT          [older name for MODULE]"
	@echo "  SOURCES          (*.c *.cc *.cpp *.st *.stt *.gt)"
	@echo "  DBDS             (*.dbd)"
	@echo "  HEADERS          () [only those to install]"
	@echo "  TEMPLATES        (*.template *.db *.subs) [db files]"
	@echo "  SCRIPTS          (*.cmd) [startup and other scripts]"
	@echo "  BINS             () [programs to install]"
	@echo "  QT               (qt/*) [QT user interfaces to install]"
	@echo "  EXCLUDE_VERSIONS () [versions not to build, e.g. 3.14]"
	@echo "  EXCLUDE_ARCHS    () [target architectures not to build]"
	@echo "  ARCH_FILTER      () [target architectures to build, e.g. SL6%]"
	@echo "  BUILDCLASSES     (Linux)"
	@echo "  <module>_VERSION () [build against specific version of other module]"

debug::
	@echo "===================== Pass 1 ====================="
	@echo "BUILD_EPICS_VERSIONS = ${BUILD_EPICS_VERSIONS}"
	@echo "BUILDCLASSES = ${BUILDCLASSES}"
	@echo "LIBVERSION = ${LIBVERSION}"
	@echo "VERSIONCHECKFILES = ${VERSIONCHECKFILES}"
	@echo "ARCH_FILTER = ${ARCH_FILTER}"
	@echo "PRJ = ${PRJ}"

# Loop over all EPICS versions for second run.
MAKEVERSION = ${MAKE} -f ${USERMAKEFILE} LIBVERSION=${LIBVERSION}

build install debug db_internal:: ${IGNOREFILES}
	@+for VERSION in ${BUILD_EPICS_VERSIONS}; do ${MAKEVERSION} EPICSVERSION=$$VERSION $@; done

define VERSIONRULES
$(1): ${IGNOREFILES}
	@+for VERSION in $${EPICS_VERSIONS_$(1)}; do $${MAKEVERSION} EPICSVERSION=$$$$VERSION build; done

%.$(1): ${IGNOREFILES}
	@+for VERSION in $${EPICS_VERSIONS_$(1)}; do $${MAKEVERSION} EPICSVERSION=$$$$VERSION $${@:%.$(1)=%}; done
endef
$(foreach v,$(sort $(basename ${INSTALLED_EPICS_VERSIONS})),$(eval $(call VERSIONRULES,$v)))

# Handle cases where user requests one specific version:
# make <action>.<version> instead of make <action> or
# make <version> instead of make
# EPICS version must be installed but need not be in EPICS_VERSIONS
${INSTALLED_EPICS_VERSIONS}: ${IGNOREFILES}
	+${MAKEVERSION} EPICSVERSION=$@ build

${INSTALLED_EPICS_VERSIONS:%=build.%}: ${IGNOREFILES}
	+${MAKEVERSION} EPICSVERSION=${@:build.%=%} build

${INSTALLED_EPICS_VERSIONS:%=install.%}: ${IGNOREFILES}
	+${MAKEVERSION} EPICSVERSION=${@:install.%=%} install

${INSTALLED_EPICS_VERSIONS:%=debug.%}:
	+${MAKEVERSION} EPICSVERSION=${@:debug.%=%} debug

# Install user interfaces to global location.
# Keep a list of installed files in a hidden file for uninstall.
define INSTALL_UI_RULE
INSTALL_$(1)=$(2)
$(1)_FILES=$$(wildcard $$(or $${$(1)},$(3)))
installui: install$(1)
install$(1): uninstall$(1)
	@$$(if $${$(1)_FILES},echo "Installing $(1) user interfaces";$$(MKDIR) $${INSTALL_$(1)})
	@$$(if $${$(1)_FILES},$(CP) -v -t $${INSTALL_$(1)} $${$(1)_FILES:%='%'})
	@$$(if $${$(1)_FILES},echo "$$(patsubst %,'%',$$(notdir $${$(1)_FILES}))" > $${INSTALL_$(1)}/.$${PRJ}-$$(LIBVERSION)-$(1).txt)

uninstallui: uninstall$(1)
uninstall$(1):
	@echo "Removing old $(1) user interfaces"
	@$$(RM) -v $$(addprefix $${INSTALL_$(1)}/,$$(sort $$(patsubst %,'%',$$(notdir $${$(1)_FILES})) $$(shell cat $${INSTALL_$(1)}/.$${PRJ}-*.txt 2>/dev/null)) .$${PRJ}-*-$(1).txt)
endef

# You can add more UI rules following this pattern:
#$(eval $(call INSTALL_UI_RULE,VARIABLE,installdir,sourcedefaultlocation))
$(eval $(call INSTALL_UI_RULE,QT,${CONFIGBASE}/qt,qt/*))

else # EPICSVERSION
# EPICSVERSION defined
# Second or third run (see T_A branch below)

EPICS_BASE=${EPICS_LOCATION}

CONFIG=${EPICS_BASE}/configure

# There is no 64 bit support before 3.14.12
ifneq ($(filter %_64,$(EPICS_HOST_ARCH)),)
ifeq ($(wildcard $(EPICS_BASE)/lib/$(EPICS_HOST_ARCH)),)
EPICS_HOST_ARCH:=$(patsubst %_64,%,$(EPICS_HOST_ARCH))
USR_CFLAGS_$(EPICS_HOST_ARCH) += -m32
USR_CXXFLAGS_$(EPICS_HOST_ARCH) += -m32
USR_LDFLAGS_$(EPICS_HOST_ARCH) += -m32
endif
endif


${CONFIG}/CONFIG:
	@echo "ERROR: EPICS release ${EPICSVERSION} not installed on this host."

# Some TOP and EPICS_BASE tweeking necessary to work around release check in 3.14.10+.
EB:=${EPICS_BASE}
TOP:=${EPICS_BASE}
-include ${CONFIG}/CONFIG
BASE_CPPFLAGS=
EPICS_BASE:=${EB}
COMMON_DIR = O.${EPICSVERSION}_Common
ifndef LEGACY_RSET
USR_CPPFLAGS+=-DUSE_TYPED_RSET
endif
SHRLIB_VERSION=
# do not link *everything* with readline (and curses)
COMMANDLINE_LIBRARY =
# Relax (3.13) cross compilers (default is STRICT) to allow sloppier syntax.
CMPLR=STD
GCC_STD = $(GCC)
CXXCMPLR=ANSI
G++_ANSI = $(G++) -ansi
OBJ=.o

O.%:
	+$(MKDIR) $@

ifndef T_A
## RUN 2
# Target achitecture not yet defined
# but EPICSVERSION is already known.
# Still in source directory.

# Look for sources etc.
# Select target architectures to build.
# Export everything for third run:

AUTOSRCS := $(filter-out ~%,$(wildcard *.c *.cc *.cpp *.st *.stt *.gt))
SRCS = $(if ${SOURCES},$(filter-out -none-,${SOURCES}),${AUTOSRCS})
#SRCS += ${SOURCES_${EPICSVERSION}}
export SRCS

DBD_SRCS = $(if ${DBDS},$(filter-out -none-,${DBDS}),$(wildcard menu*.dbd *Record.dbd) $(strip $(filter-out %Include.dbd dbCommon.dbd %Record.dbd,$(wildcard *.dbd)) ${BPTS}))
DBD_SRCS += ${DBDS_${EPICSVERSION}}
export DBD_SRCS

#record dbd files given in DBDS
RECORDS1 = $(patsubst %Record.dbd, %, $(filter-out dev%, $(filter %Record.dbd, $(notdir ${DBD_SRCS}))))
#record dbd files included by files given in DBDS
RECORDS2 = $(filter-out dev%, $(shell ${MAKEHOME}/expandDBD.tcl -r $(addprefix -I, $(sort $(dir ${DBD_SRCS}))) $(realpath ${DBDS})))
RECORDS = $(sort ${RECORDS1} ${RECORDS2})
export RECORDS

MENUS = $(patsubst %.dbd,%.h,$(wildcard menu*.dbd))
export MENUS

BPTS = $(patsubst %.data,%.dbd,$(wildcard bpt*.data))
export BPTS

DBDINSTALLS = $(DBD_INSTALLS)
DBDINSTALLS += $(MENUS)
DBDINSTALLS += $(BPTS)
export DBDINSTALLS

HDRS = ${HEADERS} $(addprefix ${COMMON_DIR}/,$(addsuffix Record.h,${RECORDS}))
HDRS += ${HEADERS_${EPICSVERSION}}
export HDRS

HDR_SUBDIRS = $(KEEP_HEADER_SUBDIRS)
export HDR_SUBDIRS

TEMPLS = $(if ${TEMPLATES},$(filter-out -none-,${TEMPLATES}),$(wildcard *.template *.db *.subs))
TEMPLS += ${TEMPLATES_${EPICSVERSION}}
TEMPLS += $(wildcard $(COMMON_DIR)/*.db)
export TEMPLS

SCR = $(if ${SCRIPTS},$(filter-out -none-,${SCRIPTS}),$(wildcard *.cmd *.iocsh))
SCR += ${SCRIPTS_${EPICSVERSION}}
export SCR

# Filter architectures to build using EXCLUDE_ARCHS and ARCH_FILTER.
CROSS_COMPILER_TARGET_ARCHS := ${EPICS_HOST_ARCH} ${CROSS_COMPILER_TARGET_ARCHS}
CROSS_COMPILER_TARGET_ARCHS := $(filter-out $(addprefix %,${EXCLUDE_ARCHS}),$(filter-out $(addsuffix %,${EXCLUDE_ARCHS}),$(if ${ARCH_FILTER},$(filter ${ARCH_FILTER},${CROSS_COMPILER_TARGET_ARCHS}),${CROSS_COMPILER_TARGET_ARCHS})))

SRCS_Linux = ${SOURCES_Linux}
export SRCS_Linux

# Perform default database expansion of .substitions/.templates into $(COMMON_DIR)
db_internal: $(COMMON_DIR)

define SUBS_EXPAND
vpath $(notdir $2) $(dir $2)
db_internal: $(COMMON_DIR)/$(notdir $(basename $2).db)

$(COMMON_DIR)/$(notdir $(basename $2).db): $(notdir $2)
	@printf "Inflating database ... %44s >>> %40s \n" "$$^" "$$@"
	$(QUIET)$(MSI) -D $$(USR_DBFLAGS) -o $(COMMON_DIR)/$$(notdir $$(basename $2).db) $1 $$^ > $(COMMON_DIR)/$$(notdir $$(basename $2).db).d
	$(QUIET)$(MSI)    $$(USR_DBFLAGS) -o $(COMMON_DIR)/$$(notdir $$(basename $2).db) $1 $$^
endef

$(foreach file,$(SUBS),$(eval $(call SUBS_EXPAND,-S,$(file))))
$(foreach file,$(TMPS),$(eval $(call SUBS_EXPAND,,$(file))))


install build debug:: $(MAKE_FIRST)
	@echo "MAKING EPICS VERSION ${EPICSVERSION}"

uninstall::
	$(RMDIR) ${INSTALL_REV}

debug::
	@echo "===================== Pass 2: EPICSVERSION = $(EPICSVERSION) ====================="
	@echo "EPICS_BASE = ${EPICS_BASE}"
	@echo "CROSS_COMPILER_TARGET_ARCHS = ${CROSS_COMPILER_TARGET_ARCHS}"
	@echo "EXCLUDE_ARCHS = ${EXCLUDE_ARCHS}"
	@echo "LIBVERSION = ${LIBVERSION}"

# Loop over all architectures.
install build debug:: $(COMMON_DIR)
	@+failed_builds=0; \
	for ARCH in ${CROSS_COMPILER_TARGET_ARCHS}; do \
	    umask 002; echo MAKING ARCH $$ARCH; ${MAKE} -f ${USERMAKEFILE} T_A=$$ARCH $@ || ((failed_builds++)); \
	done; \
	((failed_builds == 0))


else # T_A

ifeq ($(filter O.%,$(notdir ${CURDIR})),)
## RUN 3
# Target architecture defined.
# Still in source directory, third run.
debug::
	@echo "===================== Pass 3: T_A = $(T_A) ====================="
	@echo "BINS = $(BINS)"
	@echo "REQ = $(REQ)"
	@echo "VLIBS = $(VLIBS)"

ifeq ($(filter ${OS_CLASS},${OS_CLASS_LIST}),)

install% build%: build
install build:
	@echo Skipping ${T_A} because $(if ${OS_CLASS},OS_CLASS=\"${OS_CLASS}\" is not in BUILDCLASSES=\"${BUILDCLASSES}\",it is not available for R$(EPICSVERSION).)
%:
	@true

else ifeq ($(shell which $(firstword ${CC})),)

install% build%: build
install build:
	@echo Warning: Skipping ${T_A} because cross compiler $(firstword ${CC}) is not installed.
%:
	@true

else

ifeq ($(shell echo "${LIBVERSION}" | grep -v -E "^$(VERSIONREGEX)\$$"),)
install:: build
	@test ! -d ${MODULE_LOCATION}/lib/${T_A} || \
        (echo -e "Error: ${MODULE_LOCATION}/lib/${T_A} already exists.\nNote: If you really want to overwrite then uninstall first."; false)
else
install:: build
	@test ! -d ${MODULE_LOCATION}/lib/${T_A} || \
        (echo -e "Warning: Re-installing ${MODULE_LOCATION}/lib/${T_A}"; \
        $(RMDIR) ${MODULE_LOCATION}/lib/${T_A})
endif

install build debug:: O.${EPICSVERSION}_${T_A}
	@${MAKE} -C O.${EPICSVERSION}_${T_A} -f ../${USERMAKEFILE} $@

endif

# Add sources for specific epics types or architectures.
ARCH_PARTS = ${T_A} $(subst -, ,${T_A}) ${OS_CLASS}
VAR_EXTENSIONS = ${EPICSVERSION} ${ARCH_PARTS} ${ARCH_PARTS:%=${EPICSVERSION}_%}
export VAR_EXTENSIONS

REQ = ${REQUIRED} $(foreach x, ${VAR_EXTENSIONS}, ${REQUIRED_$x})
export REQ

SRCS += $(foreach x, ${VAR_EXTENSIONS}, ${SOURCES_$x})
USR_LIBOBJS += ${LIBOBJS} $(foreach x,${VAR_EXTENSIONS},${LIBOBJS_$x})
export USR_LIBOBJS

BINS += $(foreach x, ${VAR_EXTENSIONS}, ${BINS_$x})
export BINS

VLIBS = $(VENDOR_LIBS) $(foreach x,$(VAR_EXTENSIONS),$(VENDOR_LIBS_$x))
export VLIBS

export CFG

# These variables are written into a .yaml file in the installed module directory to keep track of
# metadata for which module was compiled.

${PRJ}_GIT_DESC := $(shell git describe --tags 2> /dev/null || git rev-parse HEAD 2> /dev/null)
export ${PRJ}_GIT_DESC
# The formatting here is just to make sure this is properly parseable .yaml data
${PRJ}_GIT_STATUS := [ $(shell git status --porcelain 2> /dev/null | grep -v "\.Makefile" | sed 's/^/\\\"/' | sed 's/$$/\\\", /')]
export ${PRJ}_GIT_STATUS

else # in O.*
## RUN 4
# In O.* directory.

# Add macros like USR_CFLAGS_Linux.
EXTENDED_VARS=INCLUDES CFLAGS CXXFLAGS CPPFLAGS CODE_CXXFLAGS LDFLAGS
$(foreach v,${EXTENDED_VARS},$(foreach x,${VAR_EXTENSIONS},$(eval $v+=$${$v_$x}) $(eval USR_$v+=$${USR_$v_$x})))
CFLAGS += ${EXTRA_CFLAGS}

COMMON_DIR = ../O.${EPICSVERSION}_Common

# Remove include directory for this module from search path.
INSTALL_INCLUDES =

# Add include directory of foreign modules to include file search path.
#
# The default behaviour is to start with <module>_VERSION and to select the highest
# available build number, unless a build no. is specified. This is determined with the
# shell script build_number.sh included with require.

define ADD_INCLUDES_TEMPLATE
INSTALL_INCLUDES += $$(patsubst %,-I${2}/$(1)/%/include,$$(call FETCH_BUILD_NUMBER,$(2),$(1)))
endef
$(foreach m,$(filter-out $(PRJ),$(notdir $(wildcard ${E3_SITEMODS_PATH}/*))),$(eval $(call ADD_INCLUDES_TEMPLATE,$m,$(E3_SITEMODS_PATH))))
$(foreach m,$(filter-out $(PRJ),$(notdir $(wildcard ${EPICS_MODULES}/*)))   ,$(eval $(call ADD_INCLUDES_TEMPLATE,$m,$(EPICS_MODULES))))

BASERULES=${EPICS_BASE}/configure/RULES

INSTALL_REV     = ${MODULE_LOCATION}
INSTALL_BIN     = ${INSTALL_REV}/bin/$(T_A)
INSTALL_LIB     = ${INSTALL_REV}/lib/$(T_A)
INSTALL_VLIB   = ${INSTALL_REV}/lib/$(T_A)/vendor
INSTALL_INCLUDE = ${INSTALL_REV}/include
INSTALL_DBD     = ${INSTALL_REV}/dbd
INSTALL_DB      = ${INSTALL_REV}/db
INSTALL_CFG     = ${INSTALL_REV}/cfg
INSTALL_DOC     = ${MODULE_LOCATION}/doc
INSTALL_SCR     = ${INSTALL_REV}

LIBRARY_OBJS = $(strip ${LIBOBJS} $(foreach l,${USR_LIBOBJS},$(addprefix ../,$(filter-out /%,$l))$(filter /%,$l)))

MODULELIB = $(if ${LIBRARY_OBJS},${LIB_PREFIX}${PRJ}${SHRLIB_SUFFIX},)

LIBOBJS += $(addsuffix $(OBJ),$(notdir $(basename $(filter-out %.$(OBJ) %$(LIB_SUFFIX),$(sort ${SRCS})))))
LIBOBJS += $(filter /%.$(OBJ) /%$(LIB_SUFFIX),${SRCS})
LIBOBJS += ${LIBRARIES:%=${INSTALL_LIB}/%Lib}
LIBS = -L ${EPICS_BASE_LIB} ${BASELIBS:%=-l%}
LINK.cpp += ${LIBS}
PRODUCT_OBJS = ${LIBRARY_OBJS}

# Linux
LOADABLE_LIBRARY=$(if ${LIBRARY_OBJS},${PRJ},)

# Handle registry stuff automagically if we have a dbd file.
# See ${REGISTRYFILE} rule below.
LIBOBJS += $(if $(MODULEDBD), $(addsuffix $(OBJ),$(basename ${REGISTRYFILE})))


# For backward compatibility:
# Provide a global symbol for every version with the same
# major and equal or smaller minor version number.
# Other code using this will look for one of those symbols.
# Add an undefined symbol for the version of every used driver.
# This is done with the #define in the used headers (see below).
MAJOR_MINOR_PATCH=$(subst ., ,${LIBVERSION})
MAJOR=$(word 1,${MAJOR_MINOR_PATCH})
MINOR=$(word 2,${MAJOR_MINOR_PATCH})
PATCH=$(word 3,${MAJOR_MINOR_PATCH})
ifneq (${MINOR},)
ALLMINORS := $(shell for ((i=0;i<=${MINOR};i++));do echo $$i;done)
ifeq (${OS_CLASS}, Linux)
PROVIDES = ${ALLMINORS:%=-Wl,--defsym,${PRJ}Lib_${MAJOR}.%=0}
endif # Linux
endif # MINOR
LDFLAGS += ${PROVIDES} ${USR_LDFLAGS_${T_A}}

# Create and include dependency files.
HDEPENDS =
HDEPENDS_METHOD = COMP
HDEPENDS_COMPFLAGS = -c
MKMF = DO_NOT_USE_MKMF
CPPFLAGS += -MD
-include *.d

# Need to find source dbd files relative to one dir up but generated dbd files in this dir.
DBDFILES += ${DBD_SRCS:%=../%}
DBD_PATH = $(sort $(dir ${DBDFILES}))

DBDEXPANDPATH = $(addprefix -I , ${DBD_PATH} ${EPICS_BASE}/dbd)
USR_DBDFLAGS += $(DBDEXPANDPATH)

# Search all directories where sources or headers come from, plus existing os dependend subdirectories.
SRC_INCLUDES = $(addprefix -I, $(wildcard $(foreach d,$(call uniq, $(filter-out /%,$(dir ${SRCS:%=../%} ${HDRS:%=../%}))), $d $(addprefix $d/, os/${OS_CLASS} $(POSIX_$(POSIX)) os/default))))

# Look for includes from standard locations relative to vendor libraries
USR_INCLUDES += $(addprefix -I,$(wildcard $(patsubst %/lib/$(T_A)/,../%/include,$(call uniq,$(dir $(VLIBS))) $(patsubst %/lib/,../%/include,$(call uniq,$(dir $(VLIBS)))))))

# Create dbd file for snl code.
DBDFILES += $(patsubst %.st,%_snl.dbd,$(notdir $(filter %.st,${SRCS})))
DBDFILES += $(patsubst %.stt,%_snl.dbd,$(notdir $(filter %.stt,${SRCS})))

# Create dbd file for GPIB code.
DBDFILES += $(patsubst %.gt,%.dbd,$(notdir $(filter %.gt,${SRCS})))

# snc location
SNCALL=$(shell ls  -dv $(E3_SITEMODS_PATH)/sequencer/$(call FETCH_BUILD_NUMBER,$(E3_SITEMODS_PATH),sequencer)/bin/$(EPICS_HOST_ARCH) 2> /dev/null)
SNC=$(lastword $(SNCALL))/snc

ifneq (,$(strip $(VLIBS)))
USR_LDFLAGS_$(T_A) += $(foreach l,$(VLIBS),-L../$(dir $(l)))
USR_LDFLAGS_$(T_A) += -Wl,-rpath,"\$$ORIGIN/vendor"
endif

ifneq ($(strip ${DBDFILES}),)
MODULEDBD=${PRJ}.dbd
endif

# If we build a library, provide a version variable.
ifneq ($(MODULELIB),)
LIBOBJS += $(addsuffix $(OBJ),$(basename ${VERSIONFILE}))
endif # MODULELIB

debug::
	@echo "===================== Pass 4: Build directory ====================="
	@echo "BUILDCLASSES = ${BUILDCLASSES}"
	@echo "OS_CLASS = ${OS_CLASS}"
	@echo "MODULEDBD = ${MODULEDBD}"
	@echo "RECORDS = ${RECORDS}"
	@echo "MENUS = ${MENUS}"
	@echo "BPTS = ${BPTS}"
	@echo "DBDINSTALLS = ${DBDINSTALLS}"
	@echo "HDRS = ${HDRS}"
	@echo "SOURCES = ${SOURCES}"
	@echo "SOURCES_${OS_CLASS} = ${SOURCES_${OS_CLASS}}"
	@echo "SRCS = ${SRCS}"
	@echo "REQ = ${REQ}"
	@echo "LIBOBJS = ${LIBOBJS}"
	@echo "DBDS = ${DBDS}"
	@echo "DBDS_${OS_CLASS} = ${DBDS_${OS_CLASS}}"
	@echo "DBD_SRCS = ${DBD_SRCS}"
	@echo "DBDFILES = ${DBDFILES}"
	@echo "TEMPLS = ${TEMPLS}"
	@echo "LIBVERSION = ${LIBVERSION}"
	@echo "MODULE_LOCATION = ${MODULE_LOCATION}"

build: MODULEINFOS
build: ${MODULEDBD}
build: $(addprefix ${COMMON_DIR}/,$(addsuffix Record.h,${RECORDS}))
build: ${DEPFILE}

# Include default EPICS Makefiles (version dependent).
# Avoid library installation when doing 'make build'.
INSTALL_LOADABLE_SHRLIBS=

# We ony want to include ${BASERULES} from EPICS base if we are /not/ in debug
# mode. Including this causes all of the source files to be compiled!
ifeq (,$(findstring debug,${MAKECMDGOALS}))
include ${BASERULES}
endif

# Fix incompatible release rules.
RELEASE_DBDFLAGS = -I ${EPICS_BASE}/dbd
RELEASE_INCLUDES = -I${EPICS_BASE}/include
# For EPICS 3.15+:
RELEASE_INCLUDES += -I${EPICS_BASE}/include/compiler/${CMPLR_CLASS}
RELEASE_INCLUDES += -I${EPICS_BASE}/include/os/${OS_CLASS}

# Find all sources and set vpath accordingly.
$(foreach file, ${SRCS} ${TEMPLS} ${DBDINSTALLS} ${SCR}, $(eval vpath $(notdir ${file}) ../$(dir ${file})))
$(foreach file,${VLIBS},$(eval vpath $(notdir ${file}) ../$(dir ${file})))

# Do not treat %.dbd the same way because it creates a circular dependency
# if a source dbd has the same name as the project dbd. Have to clear %.dbd and not use ../ path.
# But the %Record.h and menu%.h rules need to find their dbd files (example: asyn).
vpath %.dbd
vpath %Record.dbd ${DBD_PATH}
vpath menu%.dbd ${DBD_PATH}

# Find header files to install.
vpath %.h $(addprefix ../,$(sort $(dir $(filter-out /%,${HDRS}) ${SRCS}))) $(sort $(dir $(filter /%,${HDRS})))
vpath %.hpp $(addprefix ../,$(sort $(dir $(filter-out /%,${HDRS}) ${SRCS}))) $(sort $(dir $(filter /%,${HDRS})))
vpath %.hh $(addprefix ../,$(sort $(dir $(filter-out /%,${HDRS}) ${SRCS}))) $(sort $(dir $(filter /%,${HDRS})))
vpath %.hxx $(addprefix ../,$(sort $(dir $(filter-out /%,${HDRS}) ${SRCS}))) $(sort $(dir $(filter /%,${HDRS})))


PRODUCTS = ${MODULELIB} ${MODULEDBD} ${DEPFILE} ${METAFILE}
MODULEINFOS:
	@echo ${PRJ} > MODULENAME
	@echo ${PRODUCTS} > PRODUCTS
	@echo ${LIBVERSION} > LIBVERSION

# Build one module dbd file by expanding all source dbd files.
# We can't use dbExpand (from the default EPICS make rules)
# because it has too strict checks to be used for a loadable module.
${MODULEDBD}: ${DBDFILES}
	@echo "Expanding $@"
	${MAKEHOME}expandDBD.tcl -$(basename ${EPICSVERSION}) ${DBDEXPANDPATH} $^ > $@

# Install everything.
INSTALL_LIBS = ${MODULELIB:%=${INSTALL_LIB}/%}
INSTALL_VLIBS = $(addprefix ${INSTALL_VLIB}/,$(notdir ${VLIBS}))
INSTALL_DEPS = ${DEPFILE:%=${INSTALL_LIB}/%}
INSTALL_META = ${METAFILE:%=${INSTALL_REV}/%}
INSTALL_DBDS = ${MODULEDBD:%=${INSTALL_DBD}/%}
INSTALL_DBDS += $(addprefix $(INSTALL_DBD)/,$(notdir ${DBDINSTALLS}))
ifneq ($(strip $(HDR_SUBDIRS)),)
  INSTALL_HDRS = $(addprefix ${INSTALL_INCLUDE}/,$(notdir $(filter-out $(addsuffix /%,$(HDR_SUBDIRS)),${HDRS})))
else
  INSTALL_HDRS = $(addprefix ${INSTALL_INCLUDE}/,$(notdir ${HDRS}))
endif
INSTALL_DBS  = $(addprefix ${INSTALL_DB}/,$(notdir ${TEMPLS}))
INSTALL_SCRS = $(addprefix ${INSTALL_SCR}/,$(notdir ${SCR}))
INSTALL_BINS = $(addprefix ${INSTALL_BIN}/,$(notdir ${BINS}))
INSTALL_CFGS = $(CFG:%=${INSTALL_CFG}/%)

debug::
	@echo "INSTALL_LIB = $(INSTALL_LIB)"
	@echo "INSTALL_LIBS = $(INSTALL_LIBS)"
	@echo "INSTALL_VLIBS = $(INSTALL_VLIBS)"
	@echo "INSTALL_DEPS = $(INSTALL_DEPS)"
	@echo "INSTALL_META = $(INSTALL_META)"
	@echo "INSTALL_DBD = $(INSTALL_DBD)"
	@echo "INSTALL_DBDS = $(INSTALL_DBDS)"
	@echo "INSTALL_INCLUDE = $(INSTALL_INCLUDE)"
	@echo "INSTALL_HDRS = $(INSTALL_HDRS)"
	@echo "INSTALL_DB = $(INSTALL_DB)"
	@echo "INSTALL_DBS = $(INSTALL_DBS)"
	@echo "INSTALL_SCR = $(INSTALL_SCR)"
	@echo "INSTALL_SCRS = $(INSTALL_SCRS)"
	@echo "INSTALL_CFG = $(INSTALL_CFG)"
	@echo "INSTALL_CFGS = $(INSTALL_CFGS)"
	@echo "INSTALL_BIN = $(INSTALL_BIN)"
	@echo "INSTALL_BINS = $(INSTALL_BINS)"
	@echo "HDR_SUBDIRS = $(HDR_SUBDIRS)"

define install_subdirs
$1_HDRS = $$(filter $1/%,$$(HDRS))
INSTALL_HDRS += $$(addprefix $$(INSTALL_INCLUDE)/,$$($1_HDRS:$1/%=%))
vpath %.h ../$1
vpath %.hpp ../$1
vpath %.hh ../$1
vpath %.hxx ../$1
debug::
	@echo "$1_HDRS = $$($1_HDRS)"
endef
$(foreach d,$(HDR_SUBDIRS),$(eval $(call install_subdirs,$d)))

INSTALLS += ${INSTALL_CFGS} ${INSTALL_SCRS} ${INSTALL_HDRS} ${INSTALL_DBDS} ${INSTALL_DBS} ${INSTALL_LIBS} ${INSTALL_VLIBS} ${INSTALL_BINS} ${INSTALL_DEPS} ${INSTALL_META}

install: ${INSTALLS}

${INSTALL_DBDS}: $(notdir ${INSTALL_DBDS})
	@echo "Installing module dbd file(s) $^ to $(@D)"
	$(INSTALL) -d -m$(INSTALL_PERMISSIONS) $^ $(@D)

${INSTALL_LIBS}: $(notdir ${INSTALL_LIBS})
	@echo "Installing module library $@"
	$(INSTALL) -d -m$(SHRLIB_PERMISSIONS) $< $(@D)

${INSTALL_VLIBS}: $(notdir ${INSTALL_VLIBS})
	@echo "Installing vendor library $^ to $(@D)"
	$(INSTALL) -d -m$(SHRLIB_PERMISSIONS) $^ $(@D)

${INSTALL_DEPS}: $(notdir ${INSTALL_DEPS})
	@echo "Installing module dependency file $@"
	$(INSTALL) -d -m$(INSTALL_PERMISSIONS) $< $(@D)

${INSTALL_META}: $(notdir ${INSTALL_META})
	@echo "Installing metadata file $@"
	$(INSTALL) -d -m$(INSTALL_PERMISSIONS) $< $(@D)

${INSTALL_DBS}: $(notdir ${INSTALL_DBS})
	@echo "Installing module template files $^ to $(@D)"
	$(INSTALL) -d -m$(INSTALL_PERMISSIONS) $^ $(@D)

${INSTALL_SCRS}: $(notdir ${SCR})
	@echo "Installing scripts $^ to $(@D)"
	$(INSTALL) -d -m$(BIN_PERMISSIONS) $^ $(@D)

${INSTALL_CFGS}: ${CFGS}
	@echo "Installing configuration files $^ to $(@D)"
	$(INSTALL) -d -m$(INSTALL_PERMISSIONS) $^ $(@D)

${INSTALL_BINS}: $(addprefix ../,$(filter-out /%,${BINS})) $(filter /%,${BINS})
	@echo "Installing binaries $^ to $(@D)"
	$(INSTALL) -d -m$(BIN_PERMISSIONS) $^ $(@D)

# Create SNL code from st/stt file.
# Important to have %.o: %.st and %.o: %.stt rule before %.o: %.c rule!
# Preprocess in any case because docu and implemented EPICS rules mismatch here.

CPPSNCFLAGS1  = $(filter -D%, ${OP_SYS_CFLAGS})
CPPSNCFLAGS1 += $(filter-out ${OP_SYS_INCLUDE_CPPFLAGS} ,${CPPFLAGS}) ${CPPSNCFLAGS}
CPPSNCFLAGS1 += -I $(dir $(SNC))../../include
SNCFLAGS += -r


# 1) ESS uses 7.0.3.1 as the minimal EPICS BASE, so we don't need to check 3.13,
# 2) We also need -c option in $(COMPILE.c) in order to compile generated source file properly
# 3) SNC (2.1.21) should use -o, because without them, snc returns $(*F).i.c instead of $(*F).c
#    With the EPICS standard building rule, -o and mv are used.
#
# Tuesday, November 28 15:59:37 CET 2017, Jeong Han Lee


%$(OBJ) %_snl.dbd: %.st
	@echo ""
	@echo ">> SNC building process .... "
	@echo ">> SNC                  : $(SNC)"
	@echo ">> SNC_VERSION          : $(sequencer_VERSION)"
	@echo ">> Preprocessing $(<F)"
	$(RM) $(*F).i
	$(CPP) ${CPPSNCFLAGS1} $< > $(*F).i
	@echo ">> Converting $(*F).i to $(*F).c"
	$(RM) $@
	@echo ">> SNC is defined as $(SNC)"
	$(SNC) $(TARGET_SNCFLAGS) $(SNCFLAGS) $(*F).i -o $(*F).c.tmp
	@mv $(*F).c.tmp $(*F).c
	@echo ">> Compiling $(*F).c"
	$(RM) $@
	$(COMPILE.c) -c ${SNC_CFLAGS} $(*F).c
	@echo ">> Building $(*F)_snl.dbd"
	awk -F [\(\)]  '/epicsExportRegistrar/ { print "registrar (" $$2 ")"}' $(*F).c > $(*F)_snl.dbd

%$(OBJ) %_snl.dbd: %.stt
	@echo ""
	@echo ">> SNC building process .... "
	@echo ">> SNC                  : $(SNC)"
	@echo ">> SNC_VERSION          : $(sequencer_VERSION)"
	@echo ">> Preprocessing $(<F)"
	$(RM) $(*F).i
	$(CPP) ${CPPSNCFLAGS1} $< > $(*F).i
	@echo ">> Converting $(*F).i to $(*F).c"
	$(RM) $@
	@echo ">> SNC is defined as $(SNC)"
	$(SNC) $(TARGET_SNCFLAGS) $(SNCFLAGS) $(*F).i -o $(*F).c.tmp
	@mv $(*F).c.tmp $(*F).c
	@echo ">> Compiling $(*F).c"
	$(RM) $@
	$(COMPILE.c) -c ${SNC_CFLAGS} $(*F).c
	@echo "Building $(*F)_snl.dbd"
	awk -F [\(\)]  '/epicsExportRegistrar/ { print "registrar(" $$2 ")"}' $(*F).c > $(*F)_snl.dbd


# Create GPIB code from *.gt file.
%.c %.dbd %.list: %.gt
	@echo "Converting $*.gt"
	${LN} $< $(*F).gt
	gdc $(*F).gt

${VERSIONFILE}:
	echo "char _${PRJ}LibRelease[] = \"${LIBVERSION}\";" >> $@

# Create file to fill registry from dbd file.
${REGISTRYFILE}: ${MODULEDBD}
	$(PERL) $(EPICS_BASE_HOST_BIN)/registerRecordDeviceDriver.pl $< $(basename $@) | grep -v 'iocshRegisterCommon();' > $@

${METAFILE}:
	@echo "wrapper_url: '$(${PRJ}_E3_GIT_URL)'" > $@
	@echo "wrapper_git_desc: '$(${PRJ}_E3_GIT_DESC)'" >> $@
	@echo "wrapper_diffs: $(${PRJ}_E3_GIT_STATUS)" >> $@
	@echo "module_git_desc: '$(${PRJ}_GIT_DESC)'" >> $@
	@echo "module_diffs: $(${PRJ}_GIT_STATUS)" >> $@

define DEP_PARSER
s%$(E3_SITEMODS_PATH)/*\([^/]*\)/\([^/]*\)/.*%\1 \2%p; \
s%$(EPICS_MODULES)/*\([^/]*\)/\([^/]*\)/.*%\1 \2%p
endef

# Create dependency file for recursive requires.
.PHONY: ${DEPFILE}
${DEPFILE}: ${LIBOBJS} $(USERMAKEFILE)
	@echo "Collecting dependencies"
	$(RM) $@.tmp
	@echo "# Generated file. Do not edit." > $@
# Check dependencies on other module headers.
	cat *.d 2>/dev/null | sed 's/ /\n/g' | grep -v '$(EPICS_BASE)/include' | sed -n '$(DEP_PARSER)' > $@.tmp
# Manully added dependencies: ${REQ}
	@$(foreach m,${REQ},echo "$m $(or $(and $(or $(wildcard $(EPICS_MODULES)/$m/$($m_VERSION)),$(wildcard $(E3_SITEMODS_PATH)/$m/$($m_VERSION))),$($m_VERSION)),$(error REQUIRED module '$m' version '$($m_VERSION)' does not exist))" >> $@.tmp;)
	cat $@.tmp | sort -u >> $@

endif # In O.* directory
endif # T_A defined
endif # EPICSVERSION defined