diff --git a/App/tools/driver.makefile b/App/tools/driver.makefile index 4c834e3..1c68daf 100644 --- a/App/tools/driver.makefile +++ b/App/tools/driver.makefile @@ -1,8 +1,8 @@ # driver.makefile # -# $Header: /cvs/G/DRV/misc/App/tools/driver.makefile,v 1.101 2015/06/22 07:42:01 zimoch Exp $ +# $Header: /cvs/G/DRV/misc/App/tools/driver.makefile,v 1.100 2014/12/10 14:15:29 zimoch Exp $ # -# This generic makefile compiles EPICS code (drivers, records, snl, ...) +# This generic makefile compiles EPICS modules (drivers, records, snl, ...) # for all installed EPICS versions in parallel. # Read this documentation and the inline comments carefully before # changing anything in this file. @@ -24,27 +24,22 @@ # Find the sources etc. # Include EPICS configuration files for this ${EPICSVERSION} # Iterate over all target architectures (${T_A}) defined for this version -# Create O.${EPICSVERSION}_${T_A} subdirectories if necessary. # # - Third run: (see comment ## RUN 3) -# Compile (or install, uninstall, etc) everything +# 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. # # Library names are derived from the directory name (unless overwritten # with the PROJECT variable in your Makefile). -# A version number is appended to the name which is derived from -# the latest CVS tag on the files in the source directory. -# If any file is not up-to-date in CVS, not tagged, or tagged differently from the +# 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 ${INSTALL_ROOT}/R${EPICSVERSION}/${T_A}. -# Symbolic links are set to the latest version, the highest minor release -# if each major release, and the highest patch level of each minor release. -# Test versions are never linked, however. -# The library can be loaded with require "" [,""] -# -# This makefile can also be used to build local libraries for IOC projects -# instead of drivers. Here however, no versioning is done (i.e. no -# version string is appended) and the libraries cannot be installed with -# this makefile. Use slsinstall to install the IOC project instead. +# The library is installed to ${EPICS_MODULES}/${PROJECT}/${LIBVERSION}/lib/${T_A}. +# The library can be loaded with require "" [,""] [,""] # # User variables (add them to your Makefile, none is required): # PROJECT @@ -74,8 +69,8 @@ USERMAKEFILE:=$(lastword $(filter-out $(lastword ${MAKEFILE_LIST}), ${MAKEFILE_L # Some configuration DEFAULT_EPICS_VERSIONS = 3.13.9 3.13.10 3.14.8 3.14.12 BUILDCLASSES = vxWorks -INSTBASE ?= /work -INSTALL_ROOT=${INSTBASE}/iocBoot +EPICS_MODULES ?= /ioc/modules +MODULE_LOCATION = ${EPICS_MODULES}/$(or ${PRJ},$(error PRJ not defined))/$(or ${LIBVERSION},$(error LIBVERSION not defined)) EPICS_LOCATION = /usr/local/epics SNCSEQ=${EPICS_BASE}/../seq @@ -83,73 +78,48 @@ 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 -LOCALPROJECTS = /X/ /A/ /F/ /P/ /S/ /Z/ - #override config here -include ${MAKEHOME}/config -# Are we in an IOC project directory? -# YES: -# - Don't use versions. -# - Install to IOC directory with slsinstall. -# - Use for local code (snl, subroutine records, etc.) -# - Autodetected: SLS beamline and machine, PROSCAN, FEL -# NO: -# - Get version number from CVS tag. -# - Install to driver pool with make install. -# - Use for drivers and other modules of global interest. -# - This is the default. -# User can overwrite USE_LIBVERSION in the Makefile. - -# Where are we in CVS (or in PWD if no CVS is around)? -THISDIR := $(if $(wildcard CVS/Repository),/$(shell cat CVS/Repository),${PWD}) -# Do not use versions on known local projects (IOC projects) -USE_LIBVERSION := $(if $(strip $(foreach d,${LOCALPROJECTS},$(findstring $d,${THISDIR}))),NO,YES) - -# Some shortcuts -MAKEVERSION = ${MAKE} -f ${USERMAKEFILE} LIBVERSION=${LIBVERSION} - # Some shell commands LN = ln -s EXISTS = test -e NM = nm RMDIR = rm -rf RM = rm -f +CP = cp # some generated file names -VERSIONFILE = ${PRJ}_Version${LIBVERSION}.c +VERSIONFILE = ${PRJ}_version_${LIBVERSION}.c REGISTRYFILE = ${PRJ}_registerRecordDeviceDriver.cpp EXPORTFILE = ${PRJ}_exportAddress.c SUBFUNCFILE = ${PRJ}_subRecordFunctions.dbd +DEPFILE = ${PRJ}.dep # Default target is "build" for all versions. # Don't install anything (different from default EPICS make rules) default: build IGNOREFILES = .cvsignore .gitignore +%: ${IGNOREFILES} ${IGNOREFILES}: @echo -e "O.*\n.cvsignore\n.gitignore" > $@ ifndef EPICSVERSION ## RUN 1 -# in source directory, first run +# in source directory # Find out which EPICS versions to build INSTALLED_EPICS_VERSIONS := $(patsubst ${EPICS_LOCATION}/base-%,%,$(wildcard ${EPICS_LOCATION}/base-*[0-9])) EPICS_VERSIONS = $(filter-out ${EXCLUDE_VERSIONS:=%},${DEFAULT_EPICS_VERSIONS}) MISSING_EPICS_VERSIONS = $(filter-out ${BUILD_EPICS_VERSIONS},${EPICS_VERSIONS}) BUILD_EPICS_VERSIONS = $(filter ${INSTALLED_EPICS_VERSIONS},${EPICS_VERSIONS}) -EPICS_VERSIONS_3.13 = $(filter 3.13.%,${BUILD_EPICS_VERSIONS}) -EPICS_VERSIONS_3.14 = $(filter 3.14.%,${BUILD_EPICS_VERSIONS}) -EPICS_VERSIONS_3.15 = $(filter 3.15.%,${BUILD_EPICS_VERSIONS}) +$(foreach v,$(sort $(basename ${BUILD_EPICS_VERSIONS})),$(eval EPICS_VERSIONS_$v=$(filter $v.%,${BUILD_EPICS_VERSIONS}))) #check only what is needed to build the lib? But what is that? VERSIONCHECKFILES = ${SOURCES} ${DBDS} $(foreach v,3.13 3.14 3.15, ${SOURCES_$v} ${DBDS_$v}) VERSIONCHECKCMD = ${MAKEHOME}/getVersion.tcl ${VERSIONDEBUGFLAG} ${VERSIONCHECKFILES} -LIBVERSION_YES = $(or $(filter-out test,$(shell ${VERSIONCHECKCMD} 2>/dev/null)),${USER},test) -LIBVERSION_Yes = $(LIBVERSION_YES) -LIBVERSION_yes = $(LIBVERSION_YES) -LIBVERSION = ${LIBVERSION_${USE_LIBVERSION}} +LIBVERSION = $(or $(filter-out test,$(shell ${VERSIONCHECKCMD} 2>/dev/null)),${USER},test) VERSIONDEBUGFLAG = $(if ${VERSIONDEBUG}, -d) # Default project name is name of current directory. @@ -164,20 +134,32 @@ export OS_CLASS_LIST export ARCH_FILTER export EXCLUDE_ARCHS -clean:: - $(RMDIR) O.* -# find . -name "*~" -exec $(RM) {} \; +# shell commands +RMDIR = rm -rf +LN = ln -s +EXISTS = test -e +NM = nm +RM = rm -f +MKDIR = mkdir -p -m 775 -clean.3.%:: - $(RMDIR) O.${@:clean.%=%}* +clean: + $(RMDIR) O.* + +clean.%: + $(RMDIR) $(wildcard O.*${@:clean.%=%}*) + +uninstall: + $(RMDIR) ${MODULE_LOCATION} + +uninstall.%: + $(RMDIR) $(wildcard ${MODULE_LOCATION}/R*${@:uninstall.%=%}*) help: @echo "usage:" @for target in '' build '' \ install 'install.' \ uninstall 'uninstall.' \ - install-headers 'install-headers.' \ - install-doc install-templates clean help version; \ + clean help version; \ do echo " make $$target"; \ done @echo "Makefile variables: (defaults)" @@ -202,32 +184,30 @@ debug:: @echo "MISSING_EPICS_VERSIONS = ${MISSING_EPICS_VERSIONS}" @echo "EPICS_VERSIONS_3.13 = ${EPICS_VERSIONS_3.13}" @echo "EPICS_VERSIONS_3.14 = ${EPICS_VERSIONS_3.14}" + @echo "EPICS_VERSIONS_3.15 = ${EPICS_VERSIONS_3.15}" @echo "BUILDCLASSES = ${BUILDCLASSES}" - @echo "USE_LIBVERSION = ${USE_LIBVERSION}" @echo "LIBVERSION = ${LIBVERSION}" @echo "VERSIONCHECKFILES = ${VERSIONCHECKFILES}" @echo "ARCH_FILTER = ${ARCH_FILTER}" # Loop over all EPICS versions for second run. -build install uninstall install-headers install-doc install-templates debug:: ${IGNOREFILES} - for VERSION in ${BUILD_EPICS_VERSIONS}; do \ - ${MAKEVERSION} EPICSVERSION=$$VERSION $@ || exit; done +MAKEVERSION = ${MAKE} -f ${USERMAKEFILE} LIBVERSION=${LIBVERSION} -# Handle cases where user requests 3.13 or 3.14 +build install debug:: ${IGNOREFILES} + for VERSION in ${BUILD_EPICS_VERSIONS}; do ${MAKEVERSION} EPICSVERSION=$$VERSION $@; done + +# Handle cases where user requests a group of EPICS versions # make .3.13 or make .3.14 instead of make or # make 3.13 or make 3.14 instead of make define VERSIONRULES -$(1): - for VERSION in $${EPICS_VERSIONS_$(1)}; do \ - $${MAKEVERSION} EPICSVERSION=$$$$VERSION build || exit; done +$(1): ${IGNOREFILES} + for VERSION in $${EPICS_VERSIONS_$(1)}; do $${MAKEVERSION} EPICSVERSION=$$$$VERSION build; done -%.$(1): - for VERSION in $${EPICS_VERSIONS_$(1)}; do \ - $${MAKEVERSION} EPICSVERSION=$$$$VERSION $${@:%.$(1)=%} || exit; done +%.$(1): ${IGNOREFILES} + for VERSION in $${EPICS_VERSIONS_$(1)}; do $${MAKEVERSION} EPICSVERSION=$$$$VERSION $${@:%.$(1)=%}; done endef - -$(foreach v,3.13 3.14 3.15,$(eval $(call VERSIONRULES,$v))) +$(foreach v,$(sort $(basename ${INSTALLED_EPICS_VERSIONS})),$(eval $(call VERSIONRULES,$v))) # Handle cases where user requests one specific version # make . instead of make or @@ -242,12 +222,6 @@ ${INSTALLED_EPICS_VERSIONS:%=build.%}: ${INSTALLED_EPICS_VERSIONS:%=install.%}: ${MAKEVERSION} EPICSVERSION=${@:install.%=%} install -${INSTALLED_EPICS_VERSIONS:%=uninstall.%}: - ${MAKEVERSION} EPICSVERSION=${@:uninstall.%=%} uninstall - -${INSTALLED_EPICS_VERSIONS:%=install-headers.%}: - ${MAKEVERSION} EPICSVERSION=${@:install-headers.%=%} install-headers - ${INSTALLED_EPICS_VERSIONS:%=debug.%}: ${MAKEVERSION} EPICSVERSION=${@:debug.%=%} debug @@ -292,17 +266,20 @@ ${EPICS_BASE}/config/CONFIG: @echo "ERROR: EPICS release ${EPICSVERSION} not installed on this host." -include ${EPICS_BASE}/config/CONFIG +#relax 3.13 cross compilers (default is STRICT) +CMPLR=STD +GCC_STD = $(GCC) +CXXCMPLR=ANSI +G++_ANSI = $(G++) -ansi OBJ=.o export BUILD_TYPE=Vx endif # 3.13 -INSTALL_LOCATION = ${INSTALL_ROOT}/R${EPICSVERSION} - ifndef T_A -### RUN 2 +## RUN 2 # target achitecture not yet defined # but EPICSVERSION is already known -# still in source directory, second run +# still in source directory # Look for sources etc. # Export everything for third run @@ -344,12 +321,17 @@ TEMPLS += ${TEMPLATES_${EPICS_BASETYPE}} TEMPLS += ${TEMPLATES_${EPICSVERSION}} export TEMPLS +SCR = $(or ${SCRIPTS},$(wildcard *.cmd)) +export SCR + +export CFG + DOCUDIR = . #DOCU = $(foreach DIR,${DOCUDIR},$(wildcard ${DIR}/*README*) $(foreach EXT,${DOCUEXT}, $(wildcard ${DIR}/*.${EXT}))) export DOCU # Loop over all target architectures for third run -# Go to O.${T_A} subdirectory because Rules.Vx only work there: +# Go to O.${T_A} subdirectory because RULES.Vx only work there: ifeq (${EPICS_BASETYPE},3.14) CROSS_COMPILER_TARGET_ARCHS += ${EPICS_HOST_ARCH} @@ -373,23 +355,18 @@ DBDFILES_vxWorks += ${DBDS_${EPICS_BASETYPE}_vxWorks} DBDFILES_vxWorks += ${DBDS_vxWorks_${EPICS_BASETYPE}} export DBDFILES_vxWorks -# Do not install without version -ifndef LIBVERSION -install uninstall install-headers:: - @echo "ERROR: Can't $@ without LIBVERSION defined" - @exit 1 -endif # !LIBVERSION - -install build install-headers debug:: +install build debug:: @echo "MAKING EPICS VERSION R${EPICSVERSION}" +uninstall:: + $(RMDIR) ${INSTALL_REV} + debug:: @echo "EPICS_BASE = ${EPICS_BASE}" @echo "EPICSVERSION = ${EPICSVERSION}" @echo "EPICS_BASETYPE = ${EPICS_BASETYPE}" @echo "CROSS_COMPILER_TARGET_ARCHS = ${CROSS_COMPILER_TARGET_ARCHS}" @echo "EXCLUDE_ARCHS = ${EXCLUDE_ARCHS}" - @echo "INSTALL_LOCATION = ${INSTALL_LOCATION}" @echo "LIBVERSION = ${LIBVERSION}" @echo "RELEASE_TOPS = ${RELEASE_TOPS}" @@ -402,9 +379,6 @@ ifeq (${EPICS_BASETYPE},3.14) BUILDDIRS += O.${EPICSVERSION}_Common endif -${BUILDDIRS}: - mkdir $@ - define MAKELINKDIRS LINKDIRS+=O.${EPICSVERSION}_$1 O.${EPICSVERSION}_$1: @@ -413,73 +387,87 @@ endef $(foreach a,${CROSS_COMPILER_TARGET_ARCHS},$(foreach l,$(LINK_$a),$(eval $(call MAKELINKDIRS,$l,$a)))) -install build install-headers debug:: ${BUILDDIRS} ${LINKDIRS} +install:: + @test ! -d ${MODULE_LOCATION}/R${EPICSVERSION} || \ + (echo -e "Error: ${MODULE_LOCATION}/R${EPICSVERSION} already exists.If you really want to overwrite then uninstall first."; false) + +install build:: # Delete old build if INSTBASE has changed. @for ARCH in ${CROSS_COMPILER_TARGET_ARCHS}; do \ - echo ${INSTBASE} | cmp -s O.${EPICSVERSION}_$$ARCH/INSTBASE - || $(RM) O.${EPICSVERSION}_$$ARCH/*; \ - ${MAKE} -C O.${EPICSVERSION}_$$ARCH -f ../${USERMAKEFILE} T_A=$$ARCH $@; \ + echo $(realpath ${EPICS_MODULES}) | cmp -s O.${EPICSVERSION}_$$ARCH/INSTBASE - || $(RMDIR) O.${EPICSVERSION}_$$ARCH; \ done -# No need to create O.${T_A} subdirectory here: -uninstall install-doc install-templates:: - @echo "MAKING EPICS VERSION R${EPICSVERSION}" - for ARCH in ${CROSS_COMPILER_TARGET_ARCHS}; do \ - ${MAKEVERSION} T_A=$$ARCH $@; done +install build debug:: + @for ARCH in ${CROSS_COMPILER_TARGET_ARCHS}; do \ + umask 002; ${MAKE} -f ${USERMAKEFILE} T_A=$$ARCH $@; \ + done else # T_A + +ifeq ($(filter O.%,$(notdir ${CURDIR})),) ## RUN 3 # target architecture defined -# third run, in O.* directory for build, install, install-headers -# still in source directory for uninstall, install-doc, install-templates +# still in source directory, third run ifeq ($(filter ${OS_CLASS},${OS_CLASS_LIST}),) -install%: build -install: build -build%: build -build: - @echo Skipping ${T_A} because $(if ${OS_CLASS},${OS_CLASS} is not in BUILDCLASSES = ${BUILDCLASSES},it is not available for R$(EPICSVERSION).) +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 ($(wildcard $(firstword ${CC})),) -install%: build -install: build -build%: build -build: +install% build%: build +install build: @echo Warning: Skipping ${T_A} because cross compiler $(firstword ${CC}) is not installed. %: @true else +O.%: + $(MKDIR) $@ + +install build debug: O.${EPICSVERSION}_Common O.${EPICSVERSION}_${T_A} + @${MAKE} -C O.${EPICSVERSION}_${T_A} -f ../${USERMAKEFILE} $@ + +endif + +else # in O.* +## RUN 4 +# in O.* directory CFLAGS += ${EXTRA_CFLAGS} -LIBVERSIONSTR = $(if ${LIBVERSION},-${LIBVERSION}) TESTVERSION := $(shell echo "${LIBVERSION}" | grep -v -E "^[0-9]+\.[0-9]+\.[0-9]+\$$") -PROJECTDBD=${if $(strip ${DBDFILES}),${PRJ}${LIBVERSIONSTR}.dbd} -DEPFILE = ${PRJ}${LIBVERSIONSTR}.dep - -INSTALL_BIN = ${INSTALL_LOCATION}/${T_A} -INSTALL_DOC = $(dir ${INSTALL_LOCATION})driverdoc -INSTALL_TEMPL = $(dir ${INSTALL_LOCATION})templates -INSTALL_LIBRARY = $(addprefix ${INSTALL_BIN}/,${PROJECTLIB}) -INSTALL_PROJECTDBD = $(addprefix ${INSTALL_DBD}/,${PROJECTDBD}) -INSTALL_HDRS = $(patsubst %.h,${INSTALL_INCLUDE}/%${LIBVERSIONSTR}.h, $(notdir ${HDRS})) -INSTALL_DOCUS = $(addprefix ${INSTALL_DOC}/${PRJ}/,$(notdir ${DOCU})) -INSTALL_TEMPLATES = $(addprefix ${INSTALL_TEMPL}/,$(subst .,${LIBVERSIONSTR}.,$(notdir ${TEMPLS}))) -INSTALL_DEP = ${INSTALL_BIN}/${DEPFILE} - -INSTALLDIRS = ${INSTALL_LOCATION} ${INSTALL_INCLUDE} ${INSTALL_BIN} -INSTALLDIRS += ${INSTALL_DBD} ${INSTALL_DOC} ${INSTALL_DOC}/${PRJ} -INSTALLDIRS += ${INSTALL_TEMPL} +PROJECTDBD=${if $(strip ${DBDFILES}),./${PRJ}.dbd} COMMON_DIR_3.14 = ../O.${EPICSVERSION}_Common COMMON_DIR_3.13 = . COMMON_DIR = ${COMMON_DIR_${EPICS_BASETYPE}} -debug:: +#remove include directory for this module from search path +#3.13 and 3.14 use different variables +INSTALL_INCLUDES = +EPICS_INCLUDES = + +# Add include directory of foreign modules to include file search path +# Default is to use latest version of any module +# The user can overwrite by defining _VERSION= +# For each foreign module look for include/os/$(OS_CLASS)/ and include/ for the EPICS base version in use +# The user can overwrite (or add) by defining _INC= (not recommended!) +# Only really existing directories are added to the search path +define ADD_FOREIGN_INCLUDES +# If you find out why make fails without this line please tell me. +$(1)_VERSION=$(2) +$(1)_INC=include/os/$(OS_CLASS) include +USR_INCLUDES += $$(addprefix -I,$$(realpath $$(addprefix ${EPICS_MODULES}/$(1)/$$($(1)_VERSION)/R${EPICSVERSION}/,$$($(1)_INC)))) +endef +# The tricky part is to sort versions numerically. Make can't but ls -v can. Only accept numerical versions. +$(eval $(foreach m,$(filter-out %/$(PRJ),$(wildcard ${EPICS_MODULES}/*)),$(call ADD_FOREIGN_INCLUDES,$(notdir $m),$(lastword $(shell ls -v $m|grep -E "[0-9]+\.[0-9]+\.[0-9]+"))))) + +debug: @echo "BUILDCLASSES = ${BUILDCLASSES}" @echo "OS_CLASS = ${OS_CLASS}" @echo "T_A = ${T_A}" @@ -500,6 +488,9 @@ debug:: @echo "DBDFILES = ${DBDFILES}" @echo "LIBVERSION = ${LIBVERSION}" @echo "TESTVERSION = ${TESTVERSION}" + @echo "MODULE_LOCATION = ${MODULE_LOCATION}" + + ifeq (${EPICS_BASETYPE},3.13) INSTALLRULE=install:: @@ -511,79 +502,38 @@ BUILDRULE=build: BASERULES=${EPICS_BASE}/configure/RULES endif # 3.14 -$(INSTALLRULE) build ${INSTALLDIRS} ${INSTALL_HDRS} ${INSTALL_TEMPLATES} +INSTALL_REV = ${MODULE_LOCATION}/R${EPICSVERSION} +INSTALL_BIN = ${INSTALL_REV}/bin/$(T_A) +INSTALL_LIB = ${INSTALL_REV}/lib/$(T_A) +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} -install-headers:: ${INSTALL_LOCATION} ${INSTALL_INCLUDE} -install-headers:: ${INSTALL_HDRS} -install-templates:: ${INSTALL_TEMPLATES} -install-doc:: ${INSTALL_LOCATION} ${INSTALL_DOC} ${INSTALL_DOC}/${PRJ} ${INSTALL_DOCUS} +#INSTALL_DOCUS = $(addprefix ${INSTALL_DOC}/${PRJ}/,$(notdir ${DOCU})) -#link only non-test versions -SETLINKS=$(if ${TESTVERSION},@\#,${MAKEHOME}setLinks.tcl) - -${INSTALLDIRS}: - mkdir -m 775 $@ - -${INSTALL_DOC}/${PRJ}/%: % - @echo "Installing documentation $@" - $(RM) $@ - cp $^ $@ - chmod 444 $@ - -${INSTALL_TEMPL}/%${LIBVERSIONSTR}.template: %.template - @echo "Installing template file $@" - $(RM) $@ - echo "#${PRJ}Lib ${LIBVERSION}" > $@ - cat $^ >> $@ - chmod 444 $@ - $(SETLINKS) ${INSTALL_TEMPL} .template $(basename $(notdir $^)) - -${INSTALL_TEMPL}/%${LIBVERSIONSTR}.db: %.db - @echo "Installing template file $@" - $(RM) $@ - echo "#${PRJ}Lib ${LIBVERSION}" > $@ - cat $^ >> $@ - chmod 444 $@ - $(SETLINKS) ${INSTALL_TEMPL} .db $(basename $(notdir $^)) - -ifeq ($(filter O.%,$(notdir ${CURDIR})),) -# still in source directory, third run -# EPICSVERSION and T_A defined - -RMFILES += ${INSTALL_BIN}/${PRJ}Lib${LIBVERSIONSTR} -RMFILES += ${INSTALL_BIN}/${PRJ}Lib${LIBVERSIONSTR}.munch -RMFILES += ${INSTALL_BIN}/lib${PRJ}${LIBVERSIONSTR}.so -RMFILES += ${INSTALL_DEP} -RMFILES += ${INSTALL_PROJECTDBD} -RMFILES += ${INSTALL_HDRS} -RMFILES += ${INSTALL_TEMPLATES} - -uninstall:: - @for i in ${RMFILES}; \ - do ${EXISTS} $$i && echo "Uninstalling $$i" && $(RM) $$i; \ - done; true - $(SETLINKS) ${INSTALL_BIN} "" ${PRJ}Lib - $(SETLINKS) ${INSTALL_BIN} .munch ${PRJ}Lib - $(SETLINKS) ${INSTALL_BIN} .so lib${PRJ} - $(SETLINKS) ${INSTALL_BIN} .dep ${PRJ} - $(SETLINKS) ${INSTALL_DBD} .dbd $(notdir ${INSTALL_PROJECTDBD:%${LIBVERSIONSTR}.dbd=%}) - $(SETLINKS) ${INSTALL_INCLUDE} .h $(notdir ${HDRS:%.h=%}) - $(SETLINKS) ${INSTALL_TEMPL} .template $(notdir ${TEMPLS:%.template=%}) - $(SETLINKS) ${INSTALL_TEMPL} .db $(notdir ${TEMPLS:%.db=%}) - -${INSTALL_INCLUDE}/%${LIBVERSIONSTR}.h: %.h - @echo "Installing header file $@" - $(RM) $@ - echo "#define __${PRJ}Lib__ ${MAJOR}.${MINOR}" > $@ - cat $^ >> $@ - chmod 444 $@ - $(SETLINKS) ${INSTALL_INCLUDE} .h $(basename $(notdir $^)) - -vpath %.db $(sort $(dir ${TEMPLS})) -vpath %.template $(sort $(dir ${TEMPLS})) -vpath % $(sort $(dir ${DOCU})) - -else # in O.* directory, third run +#${INSTALL_DOC}/${PRJ}/%: % +# @echo "Installing documentation $@" +# $(RM) $@ +# cp $^ $@ +# chmod 444 $@ +# +#${INSTALL_TEMPL}/%.template: %.template +# @echo "Installing template file $@" +# $(RM) $@ +# echo "#${PRJ}Lib ${LIBVERSION}" > $@ +# cat $^ >> $@ +# chmod 444 $@ +# $(SETLINKS) ${INSTALL_TEMPL} .template $(basename $(notdir $^)) +# +#${INSTALL_TEMPL}/%.db: %.db +# @echo "Installing template file $@" +# $(RM) $@ +# $(CP) $^ >> $@ +# chmod 444 $@ +# $(SETLINKS) ${INSTALL_TEMPL} .db $(basename $(notdir $^)) # add sources for specific epics types (3.13 or 3.14) or architectures ARCH_PARTS = ${T_A} $(subst -, ,${T_A}) ${OS_CLASS} @@ -596,43 +546,44 @@ DBDFILES += $(foreach PART, ${ARCH_PARTS}, ${DBDFILES_${EPICS_BASETYPE}_${PART}} ifeq (${EPICS_BASETYPE},3.13) # only 3.13 from here -PROJECTLIB = $(if ${LIBOBJS},${PRJ}Lib${LIBVERSIONSTR},) # Convert sources to object code, skip .a and .o here LIBOBJS += $(patsubst %,%.o,$(notdir $(basename $(filter-out %.o %.a,${SRCS})))) # add all .a and .o with absolute path LIBOBJS += $(filter /%.o /%.a,${SRCS}) # add all .a and .o with relative path, but prefix with ../ LIBOBJS += $(patsubst %,../%,$(filter-out /%,$(filter %.o %.a,${SRCS}))) -LIBOBJS += ${LIBRARIES:%=${INSTALL_BIN}/%Lib} -LIBNAME = ${PROJECTLIB} +LIBOBJS += ${LIBRARIES:%=${INSTALL_LIB}/%Lib} +LIBNAME = $(if ${LIBOBJS},${PRJ}Lib,) # must be the un-munched name +PROJECTLIB = ${LIBNAME:%=%.munch} +PROD = ${PROJECTLIB} #add munched library for C++ code (does not work for Tornado 1) -ifneq ($(filter %.cc %.cpp %.C,${SRCS}),) -ifeq ($(filter T1-%,${T_A}),) -PROD = ${PROJECTLIB}.munch -endif # T1- T_A -endif # .cc or .cpp found +#ifneq ($(filter %.cc %.cpp %.C,${SRCS}),) +#ifeq ($(filter T1-%,${T_A}),) +#PROD = ${PROJECTLIB}.munch +#endif # T1- T_A +#endif # .cc or .cpp found else # only 3.14 from here ifeq (${OS_CLASS},vxWorks) # only install the munched lib INSTALL_PROD= -PROJECTLIB = $(if ${LIBOBJS},${PRJ}Lib${LIBVERSIONSTR}.munch,) +PROJECTLIB = $(if ${LIBOBJS},${PRJ}Lib.munch,) else -PROJECTLIB = $(if ${LIBOBJS},${LIB_PREFIX}${PRJ}${LIBVERSIONSTR}${SHRLIB_SUFFIX},) +PROJECTLIB = $(if ${LIBOBJS},${LIB_PREFIX}${PRJ}${SHRLIB_SUFFIX},) endif # vxWorks PROD_vxWorks=${PROJECTLIB} LIBOBJS += $(addsuffix $(OBJ),$(notdir $(basename $(filter-out %.o %.a,$(sort ${SRCS}))))) -LIBOBJS += ${LIBRARIES:%=${INSTALL_BIN}/%Lib} +LIBOBJS += ${LIBRARIES:%=${INSTALL_LIB}/%Lib} LIBS = -L ${EPICS_BASE_LIB} ${BASELIBS:%=-l%} LINK.cpp += ${LIBS} PRODUCT_OBJS = ${LIBOBJS} # Linux -LOADABLE_LIBRARY=$(if ${LIBOBJS},${PRJ}${LIBVERSIONSTR},) +LOADABLE_LIBRARY=$(if ${LIBOBJS},${PRJ},) LIBRARY_OBJS = ${LIBOBJS} # Handle registry stuff automagically if we have a dbd file. @@ -643,12 +594,9 @@ endif # both, 3.13 and 3.14 from here # If we build a library and use versions, provide a version variable. ifdef PROJECTLIB -ifdef LIBVERSION LIBOBJS += $(addsuffix $(OBJ),$(basename ${VERSIONFILE})) -endif # LIBVERSION endif # PROJECTLIB -ifdef LIBVERSION ifndef TESTVERSION # Provide a global symbol for every version with the same # major and equal or smaller minor version number. @@ -659,32 +607,15 @@ MAJOR_MINOR_PATCH=$(subst ., ,${LIBVERSION}) MAJOR=$(word 1,${MAJOR_MINOR_PATCH}) MINOR=$(word 2,${MAJOR_MINOR_PATCH}) PATCH=$(word 3,${MAJOR_MINOR_PATCH}) -ALLMINORS := $(shell for ((i=0;i<=${MINOR};i++));do echo $$i;done) -PREREQUISITES = $(shell ${MAKEHOME}/getPrerequisites.tcl ${INSTALL_INCLUDE} | grep -vw ${PRJ}) -ifeq (${OS_CLASS}, vxWorks) -PROVIDES = ${ALLMINORS:%=--defsym __${PRJ}Lib_${MAJOR}.%=0} -endif # vxWorks -ifeq (${OS_CLASS}, Linux) -PROVIDES = ${ALLMINORS:%=-Wl,--defsym,${PRJ}Lib_${MAJOR}.%=0} -endif # Linux endif # TESTVERSION -endif # LIBVERSION defined - -LDFLAGS += ${PROVIDES} ${USR_LDFLAGS_${T_A}} # Create and include dependency files CPPFLAGS += -MD # 3.14.12 already defines -MDD here (what we don't want): HDEPENDSCFLAGS = +HDEPENDS_CMD = -include *.d -# Setup searchpaths from all used files -vpath % .. -vpath % $(sort $(dir ${SRCS:%=../%})) -vpath %.h $(addprefix ../,$(sort $(dir $(filter-out /%,${HDRS})))) $(dir $(filter /%,${HDRS})) -vpath %.template $(sort $(dir ${TEMPLS:%=../%})) -vpath %.db $(sort $(dir ${TEMPLS:%=../%})) -vpath %.dbd $(sort $(dir ${DBDFILES:%=../%})) #VPATH += $(sort $(dir ${DOCU:%=../%})) DBDDIRS = $(sort $(dir ${DBDFILES:%=../%})) @@ -724,89 +655,85 @@ SNC_CFLAGS=-I ${SNCSEQ}/include endif # 3.14 -${BUILDRULE} PROJECTINFOS $(addprefix ${COMMON_DIR}/,$(addsuffix Record.h,${RECORDS})) ${PROJECTDBD} ${DEPFILE} - -PROJECTINFOS: - @echo ${PRJ} > PROJECTNAME - @echo ${INSTBASE} > INSTBASE - @echo ${PROJECTLIB} ${PROJECTDBD} ${DEPFILE} > PRODUCTS - -# Build one 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 for a loadable module. -${PROJECTDBD}: ${DBDFILES} - @echo "Expanding $@" - ${MAKEHOME}/expandDBD.tcl ${EXPANDARG} ${DBDEXPANDPATH} $^ > $@ - -# Install everything and set up symbolic links -ifeq (${EPICS_BASETYPE},3.14) -${INSTALL_BIN}/${PROJECTLIB}: ${PROJECTLIB} - @echo "Installing library $@" - $(RM) $@ - cp $^ $@ - chmod 444 $@ -ifeq (${OS_CLASS},vxWorks) - $(SETLINKS) ${INSTALL_BIN} .munch ${PRJ}Lib -else - $(SETLINKS) ${INSTALL_BIN} ${SHRLIB_SUFFIX} ${LIB_PREFIX}${PRJ} -endif -else -${INSTALL_BIN}/${PROJECTLIB}.munch: ${PROJECTLIB}.munch - @echo "Installing munched library $@" - $(RM) $@ - cp $^ $@ - chmod 444 $@ - $(SETLINKS) ${INSTALL_BIN} .munch ${PRJ}Lib - -${INSTALL_BIN}/${PROJECTLIB}: ${PROJECTLIB} - @echo "Installing library $@" - $(RM) $@ - cp $^ $@ - chmod 444 $@ - $(SETLINKS) ${INSTALL_BIN} "" ${PRJ}Lib -endif - -${INSTALL_BIN}/${DEPFILE}: ${DEPFILE} - @echo "Installing dependency file $@" - $(RM) $@ - cp $^ $@ - chmod 444 $@ - $(SETLINKS) ${INSTALL_BIN} .dep ${PRJ} - -${INSTALL_DBD}/%.dbd: %.dbd - @echo "Installing dbd file $@" - $(RM) $@ - cp $^ $@ - chmod 444 $@ - $(SETLINKS) ${INSTALL_DBD} .dbd ${PRJ} - -# Add a #define so that users of the header know the version. -${INSTALL_INCLUDE}/%${LIBVERSIONSTR}.h: %.h - @echo "Installing header file $@" - $(RM) $@ - echo "#define __${PRJ}Lib__ ${MAJOR}.${MINOR}" > $@ - cat $^ >> $@ - chmod 444 $@ - $(SETLINKS) ${INSTALL_INCLUDE} .h $(basename $(notdir $^)) - +${BUILDRULE} PROJECTINFOS +${BUILDRULE} ${PROJECTDBD} +${BUILDRULE} $(addprefix ${COMMON_DIR}/,$(addsuffix Record.h,${RECORDS})) +${BUILDRULE} ${DEPFILE} # Include default EPICS Makefiles (version dependent) # avoid library installation when doing 'make build' INSTALL_LOADABLE_SHRLIBS= +# avoid installing .munch to bin +INSTALL_MUNCHS= include ${BASERULES} #Fix release rules RELEASE_DBDFLAGS = -I ${EPICS_BASE}/dbd RELEASE_INCLUDES = -I${EPICS_BASE}/include +#for 3.15: RELEASE_INCLUDES += -I${EPICS_BASE}/include/compiler/${CMPLR_CLASS} RELEASE_INCLUDES += -I${EPICS_BASE}/include/os/${OS_CLASS} +#for 3.13: +EPICS_INCLUDES += -I$(EPICS_BASE_INCLUDE) -I$(EPICS_BASE_INCLUDE)/os/$(OS_CLASS) -#relax 3.13 cross compilers (default is STRICT) -CMPLR=ANSI -CXXCMPLR=ANSI -G++_ANSI = $(G++) -ansi +# Setup searchpaths from all used files +#vpath % .. +# find all sources whatever suffix +$(foreach filetype,SRCS TEMPLS SCR,$(foreach ext,$(sort $(suffix ${${filetype}})),$(eval vpath %${ext} $(sort $(dir $(filter %${ext},${${filetype}:%=../%})))))) +# find dbd files but remove ../ to avoid circular dependency if source dbd has the same name as the project dbd +vpath %.dbd $(filter-out ../,$(sort $(dir ${DBDFILES:%=../%}))) +# find header files to install +vpath %.h $(addprefix ../,$(sort $(dir ${HDRS} ${SRCS}))) +#vpath %.h $(addprefix ../,$(sort $(dir $(filter-out /%,${HDRS})))) $(dir $(filter /%,${HDRS})) # why headers starting with / ?? + + +PRODUCTS = ${PROJECTLIB} ${PROJECTDBD} ${DEPFILE} +PROJECTINFOS: + @echo ${PRJ} > PROJECTNAME + @echo $(realpath ${EPICS_MODULES}) > INSTBASE + @echo ${PRODUCTS} > PRODUCTS + @echo ${LIBVERSION} > LIBVERSION + +# Build one 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 for a loadable module. +${PROJECTDBD}: ${DBDFILES:%=../%} + @echo "Expanding $@" + ${MAKEHOME}/expandDBD.tcl ${EXPANDARG} ${DBDEXPANDPATH} $^ > $@ + +# Install everything +INSTALL_LIBS = ${PROJECTLIB:%=${INSTALL_LIB}/%} +INSTALL_DEPS = ${DEPFILE:%=${INSTALL_LIB}/%} +INSTALL_DBDS = ${PROJECTDBD:%=${INSTALL_DBD}/%} +INSTALL_HDRS = $(addprefix ${INSTALL_INCLUDE}/,$(notdir ${HDRS})) +INSTALL_DBS = $(addprefix ${INSTALL_DB}/,$(notdir ${TEMPLS})) +INSTALL_SCRS = $(SCR:%=$(INSTALL_SCR)/%) +INSTALL_CFGS = $(CFG:%=$(INSTALL_CFG)/%) + +INSTALLS += ${INSTALL_SCRS} ${INSTALL_HDRS} ${INSTALL_DBDS} ${INSTALL_DBS} ${INSTALL_LIBS} ${INSTALL_DEPS} ${INSTALL_CFGS} + +${INSTALLRULE} ${INSTALLS} + +${INSTALL_DBDS}: $(notdir ${INSTALL_DBDS}) + @echo "Installing module dbd file $@" + $(INSTALL) -d -m444 $< $(@D) + +${INSTALL_LIBS}: $(notdir ${INSTALL_LIBS}) + @echo "Installing module library $@" + $(INSTALL) -d -m555 $< $(@D) + +${INSTALL_DEPS}: $(notdir ${INSTALL_DEPS}) + @echo "Installing module dependency file $@" + $(INSTALL) -d -m444 $< $(@D) + +${INSTALL_DBS}: $(notdir ${INSTALL_DBS}) + @echo "Installing module template file $@" + $(INSTALL) -d -m444 $< $(@D) + +${INSTALL_SCR}: $(notdir ${SCR}) + @echo "Installing script $@" + $(INSTALL) -d -m444 $< $(@D) -${INSTALLRULE} ${INSTALL_DOCUS} ${INSTALL_PROJECTDBD} ${INSTALL_LIBRARY} ${INSTALL_DEP} # Create SNL code from st/stt file # (RULES.Vx only allows ../%.st, 3.14 has no .st rules at all) @@ -855,7 +782,7 @@ MUNCH=tclsh $(VX_DIR)/host/src/hutils/munch.tcl %.munch: CMPLR=TRAD %.munch: % @echo Munching $< - @ $(RM) ctct.o ctdt.c + $(RM) ctct.o ctdt.c $(NM) $< | $(MUNCH) > ctdt.c $(COMPILE.c) ctdt.c $(LINK.c) $@ $< ctdt.o @@ -927,12 +854,11 @@ ${DEPFILE}: ${LIBOBJS} @echo "Collecting dependencies" $(RM) $@ @echo "# Generated file. Do not edit." > $@ - ${MAKEHOME}/getPrerequisites.tcl -dep ${INSTALL_INCLUDE} | grep -vw ${PRJ} >> $@; true + cat *.d | sed 's/ /\n/g' | sed -n 's%$(EPICS_MODULES)/*\([^/]*\)/\([^/]*\)/.*%\1 \2+%p'|sort -u >> $@ $(BUILDRULE) $(RM) MakefileInclude endif # in O.* directory endif # T_A defined -endif # OS_CLASS in BUILDCLASSES endif # EPICSVERSION defined diff --git a/App/tools/getPrerequisites.tcl b/App/tools/getPrerequisites.tcl deleted file mode 100755 index 96675ef..0000000 --- a/App/tools/getPrerequisites.tcl +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/tclsh - -if {[lindex $argv 0] == "-dep"} { - set depstyle 1 - set argv [lrange $argv 1 end] -} else { - set depstyle 0 -} -set installdir [lindex $argv 0] -set prerequisites {} -foreach filename [glob -nocomplain *.d] { - set file [open $filename] - set contents [read $file] - close $file - foreach word $contents { - set header [string trim $word] - if [string match $installdir/* $header] { - set file [open $header] - while {[regexp {^#define __(.*)Lib__ ([0-9]+\.[0-9]+)$} \ - [gets $file] match lib version]} { - if $depstyle { - lappend prerequisites "$lib $version" - } else { - lappend prerequisites ${lib}Lib_$version - } - } - close $file - } - } -} -set prerequisites [lsort -unique $prerequisites] -puts [join $prerequisites "\n"] - -# $Header: /cvs/G/DRV/misc/App/tools/getPrerequisites.tcl,v 1.2 2010/08/03 08:42:40 zimoch Exp $ diff --git a/App/tools/setLinks.tcl b/App/tools/setLinks.tcl deleted file mode 100755 index a127fc2..0000000 --- a/App/tools/setLinks.tcl +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/tclsh - -package require Tclx - -set dir [lindex $argv 0] -set ext [lindex $argv 1] -set names [lrange $argv 2 end] -if {![file isdirectory $dir]} exit - -if {[lindex [file split $dir] 1] == "prod"} { - proc mklink {target link} { - global dir - puts "Linking $link -> $target" - puts "repository -H pc770 slink $target $dir/$link" - } - proc rmlink {link} { - } -} else { - proc mklink {target link} { - puts "Linking $link -> $target" - link -sym $target $link - } - proc rmlink {link} { - puts "removing link $link" - file delete $link - } -} - -cd $dir -foreach name $names { - set links [glob -nocomplain -type l $name-*$ext] - lappend links $name$ext - foreach file $links { - rmlink $file - } - set files [glob -nocomplain -types f $name-*$ext] - set files [lsort -decreasing -dictionary $files] - set oldmajor "" - set oldminor "" - set first 1 - foreach file $files { - if {[regexp {(.*)-([0-9]+)\.([0-9]+)\.([0-9]+)(.*)} $file \ - match head major minor patch tail] && \ - $head == $name && $tail == $ext} { - if {$first} { - mklink $file $name$ext - set first 0 - } - if {$major != $oldmajor} { - mklink $file $name-$major$ext - set oldmajor $major - set oldminor "" - } - if {$minor != $oldminor} { - mklink $file $name-$major.$minor$ext - set oldminor $minor - } - } - } -} - -# $Header: /cvs/G/DRV/misc/App/tools/setLinks.tcl,v 1.3 2012/09/27 08:30:42 zimoch Exp $ diff --git a/iocsh b/iocsh index e31ab43..709f301 100755 --- a/iocsh +++ b/iocsh @@ -166,7 +166,7 @@ fi # setup search path for require ODIR=O.${BASE}_$EPICS_HOST_ARCH -EPICS_DRIVER_PATH=.:bin:snl:../snl:$ODIR:src/$ODIR:snl/$ODIR:../snl/$ODIR:${EPICS_DRIVER_PATH#:} +EPICS_DRIVER_PATH=.:bin:snl:../snl:$ODIR:src/$ODIR:snl/$ODIR:../snl/$ODIR:$EPICS_DRIVER_PATH #Special PSI: find installation base for libs from working directory D=$(rp $PWD) @@ -181,7 +181,13 @@ if [ -z "$INSTBASE" ] then INSTBASE=/work fi -EPICS_DRIVER_PATH=${EPICS_DRIVER_PATH%:}:$INSTBASE/iocBoot/R$BASE/$EPICS_HOST_ARCH + +if [ -n "$EPICS_MODULES" ] +then + EPICS_DRIVER_PATH+=:$EPICS_MODULES +fi + +EPICS_DRIVER_PATH+=:$INSTBASE/iocBoot/R$BASE/$EPICS_HOST_ARCH # convert for win32-x86 arch if [ ${EPICS_HOST_ARCH#win32-} != $EPICS_HOST_ARCH ] @@ -330,8 +336,33 @@ then [ -z "$REQUIRE" ] && REQUIRE=misc LIBPREFIX=lib LIBPOSTFIX=.so - [ -z "$REQUIRE_LIB" ] && REQUIRE_LIB=$INSTBASE/iocBoot/R$BASE/$EPICS_HOST_ARCH/${LIBPREFIX}${REQUIRE}${LIBPOSTFIX} - [ -z "$REQUIRE_DBD" ] && REQUIRE_DBD=$INSTBASE/iocBoot/R$BASE/dbd/${REQUIRE}.dbd + + if [ "$EPICS_MODULES" ] + then + if [ -z "$REQUIRE_LIB" ] + then + if [ "$REQUIRE_VERSION" ] + then + REQUIRE_LIB=$EPICS_MODULES/$REQUIRE/$REQUIRE_VERSION/R$BASE/lib/$EPICS_HOST_ARCH/$LIBPREFIX$REQUIRE$LIBPOSTFIX + else + REQUIRE_LIB=$(ls -rvd $EPICS_MODULES/$REQUIRE/*.*.*|head -n1)/R$BASE/lib/$EPICS_HOST_ARCH/$LIBPREFIX$REQUIRE$LIBPOSTFIX + fi + fi + REQUIRE_DBD=$(dirname $(dirname $(dirname $REQUIRE_LIB)))/dbd/$REQUIRE.dbd + else + if [ -z "$REQUIRE_LIB" ] + then + if [ "$REQUIRE_VERSION" ] + then + REQUIRE_LIB=$INSTBASE/iocBoot/R$BASE/$EPICS_HOST_ARCH/$LIBPREFIX$REQUIRE-$REQUIRE_VERSION$LIBPOSTFIX + else + REQUIRE_LIB=$INSTBASE/iocBoot/R$BASE/$EPICS_HOST_ARCH/$LIBPREFIX$REQUIRE$LIBPOSTFIX + fi + fi + f=${REQUIRE_LIB##*$LIBPREFIX} + f=${f%$LIBPOSTFIX} + REQUIRE_DBD=$(dirname $(dirname $REQUIRE_LIB))/dbd/$f.dbd + fi if [ ! -f $REQUIRE_LIB ] then echo "Library $REQUIRE_LIB not found." >&2 diff --git a/require.c b/require.c index 01851a8..4d05da1 100644 --- a/require.c +++ b/require.c @@ -7,26 +7,39 @@ * * DISCLAIMER: Use at your own risc and so on. No warranty, no refund. */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + #include #include #include #include #include #include +#include +#include +#include #include #ifdef BASE_VERSION #define EPICS_3_13 #define epicsStdoutPrintf printf -int dbLoadDatabase(char *filename, char *path, char *substitutions); +extern int dbLoadDatabase(const char *filename, const char *path, const char *substitutions); +extern int dbLoadTemplate(const char *filename); +int dbLoadRecords(const char *filename, const char *substitutions) +{ + /* This implementation respects EPICS_DB_INCLUDE_PATH */ + return dbLoadDatabase(filename, NULL, substitutions); +} extern volatile int interruptAccept; -#else -#define EPICS_3_14 +#else /* 3.14+ */ #include #include epicsShareFunc int epicsShareAPI iocshCmd (const char *cmd); #include #include +#include #include #endif @@ -52,25 +65,32 @@ static int firstTime = 1; #include #include #include - #include + #include + #include + #include "strdup.h" + #include "asprintf.h" #define HMODULE MODULE_ID #undef INFIX #define INFIX "Lib" #define EXT ".munch" - #define getAddress(module,name) __extension__ \ - ({SYM_TYPE t; char* a=NULL; symFindByName(sysSymTbl, (name), &a, &t); a;}) + #define getAddress(module, name) __extension__ \ + ({SYM_TYPE t; char* a = NULL; symFindByName(sysSymTbl, (name), &a, &t); a;}) - #define snprintf(s, n, f, args...) sprintf(s, f, ## args) + /* vxWorks has no snprintf() */ + #define snprintf(s, maxchars, f, args...) __extension__ \ + ({int printed=sprintf(s, f, ## args); assert(printed < maxchars); printed;}) + extern char** ppGlobalEnviron; + extern int execute(); + #elif defined (__unix) - #define __USE_GNU #include #define HMODULE void * - #define getAddress(module,name) (dlsym(module, name)) + #define getAddress(module, name) dlsym(module, name) #ifdef CYGWIN32 @@ -83,6 +103,7 @@ static int firstTime = 1; #define EXT ".so" #endif + extern char** environ; #elif defined (_WIN32) @@ -93,18 +114,20 @@ static int firstTime = 1; #define PATHSEP ";" #define EXT ".dll" - #define getAddress(module,name) (GetProcAddress(module, name)) + #define getAddress(module, name) GetProcAddress(module, name) #else #warning unknwn OS - #define getAddress(module,name) NULL + #define getAddress(module, name) NULL #endif -#define toStr2(x) x -#define toStr(x) toStr2(#x) -const char epicsRelease[] = toStr(EPICS_RELEASE); -const char targetArch[] = toStr(T_A); +#define LIBDIR "lib" DIRSEP +#define TEMPLATEDIR "db" + +const char epicsRelease[] = EPICS_RELEASE; +const char targetArch[] = T_A; +const char osClass[] = OS_CLASS; /* loadlib (library) Find a loadable library by name and load it. @@ -114,20 +137,20 @@ static HMODULE loadlib(const char* libname) { HMODULE libhandle = NULL; - if (!libname) + if (libname == NULL) { fprintf (stderr, "missing library name\n"); return NULL; } #if defined (__unix) - if (!(libhandle = dlopen(libname, RTLD_NOW|RTLD_GLOBAL))) + if ((libhandle = dlopen(libname, RTLD_NOW|RTLD_GLOBAL)) == NULL) { fprintf (stderr, "Loading %s library failed: %s\n", libname, dlerror()); } #elif defined (_WIN32) - if (!(libhandle = LoadLibrary(libname))) + if ((libhandle = LoadLibrary(libname)) == NULL) { LPVOID lpMsgBuf; @@ -174,89 +197,117 @@ static HMODULE loadlib(const char* libname) typedef struct moduleitem { struct moduleitem* next; - char name[100]; - char version[20]; + char content[0]; } moduleitem; moduleitem* loadedModules = NULL; -static void registerModule(const char* module, const char* version) +static void registerModule(const char* module, const char* version, const char* location) { - moduleitem* m = (moduleitem*) calloc(sizeof (moduleitem),1); - if (!m) + moduleitem* m; + size_t lm = strlen(module) + 1; + size_t lv = (version ? strlen(version) : 0) + 1; + size_t ll = (location ? strlen(location) : 0) + 1; + + if (requireDebug) + printf("require: registerModule(%s,%s,%s)\n", module, version, location); + m = (moduleitem*) malloc(sizeof(moduleitem) + lm + lv + ll); + if (m == NULL) { - printf ("require: out of memory\n"); - } - else - { - strncpy (m->name, module, sizeof(m->name)); - strncpy (m->version, version, sizeof(m->version)); - m->next = loadedModules; - loadedModules = m; + fprintf(stderr, "require: out of memory\n"); + return; } + strcpy (m->content, module); + strcpy (m->content+lm, version ? version : ""); + strcpy (m->content+lm+lv, location ? location : ""); + m->next = loadedModules; + loadedModules = m; } #if defined (vxWorks) -BOOL findLibRelease ( +static BOOL findLibRelease ( char *name, /* symbol name */ int val, /* value of symbol */ SYM_TYPE type, /* symbol type */ int arg, /* user-supplied arg */ UINT16 group /* group number */ ) { - char libname [20]; - int e; + /* find symbols with a name like "_LibRelease" */ + char* module; + size_t lm; + if (name[0] != '_') return TRUE; - e = strlen(name) - 10; - if (e <= 0 || e > 20) return TRUE; - if (strcmp(name+e, "LibRelease") != 0) return TRUE; - strncpy(libname, name+1, e-1); - libname[e-1]=0; - if (!getLibVersion(libname)) + lm = strlen(name); + if (lm <= 10) /* strlen("LibRelease") */ return TRUE; + lm -= 10; + if (strcmp(name+lm, "LibRelease") != 0) return TRUE; + module = strdup(name+1); + module[lm-1]=0; + if (getLibVersion(module) == NULL) { - registerModule(libname, (char*)val); + registerModule(module, (char*)val, NULL); } + free(module); return TRUE; } static void registerExternalModules() { + /* iterate over all symbols */ symEach(sysSymTbl, (FUNCPTR)findLibRelease, 0); } #elif defined (__linux) +/* This is the Linux link.h, not the EPICS link.h ! */ #include -int findLibRelease ( +static int findLibRelease ( struct dl_phdr_info *info, /* shared library info */ size_t size, /* size of info structure */ void *data /* user-supplied arg */ ) { void *handle; - char symname [80]; - const char* p; - char* q; + char* name; + char* location = NULL; + char* p; char* version; + char* symname; - if (!info->dlpi_name || !info->dlpi_name[0]) return 0; - p = strrchr(info->dlpi_name, '/'); - if (p) p+=4; else p=info->dlpi_name + 3; + /* find a symbol with a name like "_LibRelease" + where is from the library name "/lib.so" */ + if (info->dlpi_name == NULL || info->dlpi_name[0] == 0) return 0; /* no library name */ + name = malloc(strlen(info->dlpi_name)+11); /* get a modifiable copy + space for "LibRelease" */ + if (name == NULL) + { + perror("require"); + return 0; + } + strcpy(name, info->dlpi_name); + handle = dlopen(info->dlpi_name, RTLD_LAZY); /* re-open already loaded library */ + p = strrchr(name, '/'); /* find file name part in "/lib.so" */ + if (p) {location = name; *++p=0;} /* terminate "/" */ + else p=name; + symname = p+2; /* replace "lib" with "_" */ symname[0] = '_'; - for (q=symname+1; *p && *p != '.' && *p != '-' && q < symname+11; p++, q++) *q=*p; - strcpy(q, "LibRelease"); - handle = dlopen(info->dlpi_name, RTLD_NOW|RTLD_GLOBAL); - version = dlsym(handle, symname); - dlclose(handle); - *q = 0; + p = strchr(symname, '.'); /* replace ".so" with "LibRelease" */ + if (p == NULL) p = symname + strlen(symname); + strcpy(p, "LibRelease"); + version = dlsym(handle, symname); /* find symbol "_LibRelease" */ if (version) { - registerModule(symname+1, version); + *p=0; symname++; /* build module name "" */ + if ((p = strstr(name, DIRSEP LIBDIR)) != NULL) + p[1]=0; /* cut "" before libdir */ + registerModule(symname, version, location); } + dlclose(handle); + free(name); return 0; } static void registerExternalModules() { + /* iterate over all loaded libraries */ dl_iterate_phdr(findLibRelease, NULL); } @@ -264,7 +315,7 @@ static void registerExternalModules() static void registerExternalModules() { - ; + fprintf (stderr, "How to find symbols on Windows?\n"); } @@ -282,19 +333,36 @@ const char* getLibVersion(const char* libname) for (m = loadedModules; m; m=m->next) { - if (strncmp(m->name, libname, sizeof(m->name)) == 0) + if (strcmp(m->content, libname) == 0) { - return m->version; + return m->content+strlen(m->content)+1; } } return NULL; } -int libversionShow(const char* pattern) +const char* getLibLocation(const char* libname) { moduleitem* m; + char *v; + + for (m = loadedModules; m; m=m->next) + { + if (strcmp(m->content, libname) == 0) + { + v = m->content+strlen(m->content)+1; + return v+strlen(v)+1; + } + } + return NULL; +} + +int libversionShow(const char* pattern, int showLocation) +{ + moduleitem* m; + int lm, lv; - if (firstTime) + if (firstTime) /* can only happen on vxWorks */ { firstTime=0; registerExternalModules(); @@ -302,8 +370,13 @@ int libversionShow(const char* pattern) for (m = loadedModules; m; m=m->next) { - if (pattern && !strstr(m->name, pattern)) return 0; - epicsStdoutPrintf("%15s %s\n", m->name, m->version); + if (pattern && *pattern && !strstr(m->content, pattern)) continue; + lm = strlen(m->content)+1; + lv = strlen(m->content+lm)+1; + epicsStdoutPrintf("%20s %-20s %s\n", + m->content, + m->content+lm, + showLocation ? m->content+lm+lv : ""); } return 0; } @@ -311,7 +384,6 @@ int libversionShow(const char* pattern) #define MISMATCH -1 #define EXACT 0 #define MATCH 1 -#define COMPATIBLE 2 #define TESTVERS 3 static int compareVersions(const char* request, const char* found) @@ -319,7 +391,14 @@ static int compareVersions(const char* request, const char* found) int found_major, found_minor=0, found_patch=0, found_parts; int req_major, req_minor, req_patch, req_parts; - if (!request || !*request) return MATCH; /* No particular version request. */ + found_parts = sscanf(found, "%d.%d.%d", &found_major, &found_minor, &found_patch); + + if (request == NULL || request[0] == 0) /* No particular version request. match anything */ + { + if (found_parts == 0) return TESTVERS; /* Test version found */ + return MATCH; + } + if (strcmp(found, request) == 0) return EXACT; /* Exact match. */ /* Numerical version compare. Format is major.minor.patch @@ -328,15 +407,43 @@ static int compareVersions(const char* request, const char* found) */ req_parts = sscanf(request, "%d.%d.%d", &req_major, &req_minor, &req_patch); if (req_parts == 0) return MISMATCH; /* Test version request not found */ - found_parts = sscanf(found, "%d.%d.%d", &found_major, &found_minor, &found_patch); if (found_parts == 0) return TESTVERS; /* Test version found */ if (found_major != req_major) return MISMATCH; /* major mismatch */ if (req_parts == 1) return MATCH; /* only major requested matches */ if (found_minor < req_minor) return MISMATCH; /* minor too small */ - if (found_minor > req_minor) return COMPATIBLE; /* minor larger than required */ + if (found_minor > req_minor) /* minor larger than required */ + { + if(request[strlen(request)-1] == '+') + return MATCH; + else + return MISMATCH; + } if (req_parts == 2) return MATCH; /* major and minor requested matches */ if (found_patch < req_patch) return MISMATCH; /* patch level too small */ - return COMPATIBLE; /* patch level higher or equal but "+" requested */ + return MATCH; /* patch level higher or equal but "+" requested */ +} + +static int putenvprintf(const char* format, ...) __attribute__((format(printf,1,2))); +static int putenvprintf(const char* format, ...) +{ + va_list ap; + char *var; + + va_start(ap, format); + if (vasprintf(&var, format, ap) < 0) + { + perror("require putenv"); + return errno; + } + va_end(ap); + + if (requireDebug) + printf("require: putenv(\"%s\")\n", var); + putenv(var); +#ifdef vxWorks + free(var); +#endif + return 0; } /* require (module) @@ -351,55 +458,272 @@ it calls epicsExit to abort the application. */ /* wrapper to abort statup script */ -static int require_priv(const char* module, const char* ver, const char* args); +static int require_priv(const char* module, const char* version, const char* args); -int require(const char* module, const char* ver, const char* args) +int require(const char* module, const char* version, const char* args) { - if (firstTime) + int status; + + if (firstTime) /* can only happen on vxWorks */ { firstTime=0; registerExternalModules(); } - if (!module) + if (getenv("EPICS_DB_INCLUDE_PATH") == NULL) + putenv("EPICS_DB_INCLUDE_PATH=."); + + if (getenv("T_A") == NULL) + putenvprintf("T_A=%s", targetArch); + + if (getenv("EPICS_RELEASE") == NULL) + putenvprintf("EPICS_RELEASE=%s", epicsRelease); + + if (module == NULL) { printf("Usage: require \"\" [, \"\"] [, \"\"]\n"); - printf("Loads " PREFIX "" INFIX "[-]" EXT " and dbd/[-].dbd\n"); -#ifdef EPICS_3_14 + printf("Loads " PREFIX "" INFIX EXT " and .dbd\n"); +#ifndef EPICS_3_13 printf("And calls _registerRecordDeviceDriver\n"); #endif - printf("If available, runs startup script snippet or loads substitution file with args\n"); + printf("If available, runs startup script snippet or loads substitution file or templates with args\n"); return -1; } - if (require_priv(module, ver, args) != 0 && !interruptAccept) + /* either order for version and args, either may be empty or NULL */ + if (version && strchr(version, '=')) { - /* require failed in startup script before iocInit */ - fprintf(stderr, "Aborting startup script\n"); -#ifdef vxWorks - shellScriptAbort(); -#else - epicsExit(1); -#endif - return -1; + const char *v = version; + version = args; + args = v; + if (requireDebug) + printf("require: swap version and args\n"); } + + status = require_priv(module, version, args); + if (status == 0) return 0; + if (status != -1) perror("require"); + if (interruptAccept) return status; + + /* require failed in startup script before iocInit */ + fprintf(stderr, "Aborting startup script\n"); + #ifdef vxWorks + shellScriptAbort(); + #else + epicsExit(1); + #endif + return status; +} + +static off_t fileSize(const char* filename) +{ + struct stat filestat; + if (stat( +#ifdef vxWorks + (char*) /* vxWorks has buggy stat prototype */ +#endif + filename, &filestat) != 0) return -1; + return filestat.st_size; +} +#define fileExists(filename) (fileSize(filename)>=0) + +static int handleDependencies(const char* module, char* depfilename) +{ + FILE* depfile; + char buffer[40]; + char *end; /* end of string */ + char *rmodule; /* required module */ + char *rversion; /* required version */ + + /* parse dependency file if exists */ + if (!fileExists(depfilename)) + { + if (requireDebug) + printf("require: no dependency file %s\n", depfilename); + return 0; + } + if (requireDebug) + printf("require: parsing dependency file %s\n", depfilename); + depfile = fopen(depfilename, "r"); + while (fgets(buffer, sizeof(buffer)-1, depfile)) + { + rmodule = buffer; + /* ignore leading spaces */ + while (isspace((unsigned char)*rmodule)) rmodule++; + /* ignore empty lines and comment lines */ + if (*rmodule == 0 || *rmodule == '#') continue; + /* rmodule at start of module name */ + rversion = rmodule; + /* find end of module name */ + while (*rversion && !isspace((unsigned char)*rversion)) rversion++; + /* terminate module name */ + *rversion++ = 0; + /* ignore spaces */ + while (isspace((unsigned char)*rversion)) rversion++; + /* rversion at start of version */ + end = rversion; + /* find end of version */ + while (*end && !isspace((unsigned char)*end)) end++; + /* add + if not yet there */ + if (*(end-1) != '+') *end++ = '+'; + /* terminate version */ + *end = 0; + printf("Module %s depends on %s %s\n", module, rmodule, rversion); + if (require(rmodule, rversion, NULL) != 0) + { + fclose(depfile); + return -1; + } + } + fclose(depfile); return 0; } -static int checkLoadedVersion(const char* module, const char* version) +int runScript(const char* filename, const char* args) { - const char* loaded; + MAC_HANDLE *mac = NULL; + FILE* file = NULL; + char* line_raw = NULL; + char* line_exp = NULL; + long line_raw_size = 100; + long line_exp_size = line_raw_size; + char** pairs; + int status = 0; - if (requireDebug) - printf("require: checking module %s version %s\n", - module, vers); + pairs = (char*[]){ "", "environ", NULL, NULL }; + + if ((file = fopen(filename, "r")) == NULL) { perror(filename); return errno; } + if (macCreateHandle(&mac, pairs) != 0) goto error; + macSuppressWarning(mac, 1); + #ifdef EPICS_3_13 + /* Have no environment macro substitution, thus load envionment explicitly */ + /* Actually environmant macro substitution was introduced in 3.14.3 */ + for (pairs = environ; *pairs; pairs++) + { + char* var, *eq; + if (requireDebug) + printf("runScript: environ %s\n", *pairs); - found = getLibVersion(module); - if (loaded) + /* take a copy to replace '=' with 0 */ + if ((var = strdup(*pairs)) == NULL) goto error; + eq = strchr(var, '='); + if (eq) + { + *eq = 0; + macPutValue(mac, var, eq+1); + } + free(var); + } + #endif + + if (args) { if (requireDebug) - printf("require: %s version %s already loaded\n", - module, found); + printf("runScript: macParseDefns \"%s\"\n", args); + macParseDefns(mac, (char*)args, &pairs); + macInstallMacros(mac, pairs); + free(pairs); + } + + /* execute line by line after expanding macros with arguments or environment */ + if ((line_raw = malloc(line_raw_size)) == NULL) goto error; + if ((line_exp = malloc(line_exp_size)) == NULL) goto error; + while (fgets(line_raw, line_raw_size, file)) + { + const unsigned char* p; + long len; + + /* check if we have a line longer than the buffer size */ + while (line_raw[(len = strlen(line_raw))-1] != '\n' && !feof(file)) + { + if (requireDebug) + printf("runScript partial line: \"%s\"\n", line_raw); + if ((line_raw = realloc(line_raw, line_raw_size *= 2)) == NULL) goto error; + if (fgets(line_raw + len, line_raw_size - len, file) == NULL) break; + } + if (requireDebug) + printf("runScript raw line: %s", line_raw); + /* expand and check the buffer size (different epics versions write different may number of bytes)*/ + while ((len = labs(macExpandString(mac, line_raw, line_exp, line_exp_size-1))) >= line_exp_size-2) + { + if (requireDebug) + printf("runScript: grow expand buffer: len=%ld size=%ld\n", len, line_exp_size); + free(line_exp); + if ((line_exp = malloc(line_exp_size *= 2)) == NULL) goto error; + } + printf("%s", line_exp); + p=(unsigned char*)line_exp; + while (isspace(*p)) p++; + if (*p == 0 || *p == '#') continue; +#ifdef vxWorks + status = execute(line_exp); +#else + status = iocshCmd(line_exp); +#endif + if (status != 0) break; + } + goto end; +error: + if (errno) + { + status = errno; + perror("runScript"); + } +end: + free(line_raw); + free(line_exp); + if (mac) macDeleteHandle(mac); + if (file) fclose(file); + return status; +} + +static int require_priv(const char* module, const char* version, const char* args) +{ + int status; + char* versionstr; + const char* loaded = NULL; + const char* found = NULL; + HMODULE libhandle; + int ifexists = 0; + const char* driverpath; + const char* dirname; + const char *end; + + DIR* dir; + struct dirent* dirent; + int releasediroffs; + int libdiroffs; + int extoffs; + char* founddir = NULL; + char* symbolname; + char filename[NAME_MAX]; + + if (requireDebug) + printf("require: module=\"%s\" version=\"%s\" args=\"%s\"\n", module, version, args); + +#define TRY_FILE(offs, args...) \ + (snprintf(filename + offs, sizeof(filename) - offs, args) && \ + (fileExists(filename) || (requireDebug && !printf("require: no %s\n", filename)))) + +#define TRY_NONEMPTY_FILE(offs, args...) \ + (snprintf(filename + offs, sizeof(filename) - offs, args) && \ + (fileSize(filename)>0 || (requireDebug && !printf("require: no %s\n", filename)))) + + driverpath = getenv("EPICS_DRIVER_PATH"); + if (driverpath == NULL) driverpath = "."; + if (requireDebug) + printf("require: searchpath=%s\n", driverpath); + + if (version && strcmp(version,"ifexists") == 0) + { + ifexists = 1; + version = NULL; + } + + /* check already loaded verion */ + loaded = getLibVersion(module); + if (loaded) + { /* Library already loaded. Check Version. */ switch (compareVersions(version, loaded)) { @@ -409,399 +733,416 @@ static int checkLoadedVersion(const char* module, const char* version) module, version, loaded); return -1; case TESTVERS: - printf("Warning: %s test version %s already loaded where %s was requested\n", - module, loaded, version); - break; - default: /* EXACT or MATCH or COMPATIBLE */ - printf ("%s %s already loaded\n", module, loaded); + if (version) + { + printf("Warning: Module %s version %s already loaded where %s was requested\n", + module, loaded, version); + } + default: /* EXACT or MATCH */ + printf ("Module %s version %s already loaded\n", module, loaded); } - /* Already loaded version is ok */ - return 0; - } -} - -static int require_priv(const char* module, const char* vers, const char* args) -{ - char version[20]; - const char* found; - struct stat filestat; - HMODULE libhandle; - char* p; - char *end; /* end of string */ - const char sep[1] = PATHSEP; - char* driverpath; - - memset(version, 0, sizeof(version)); - if (vers) strncpy(version, vers, sizeof(version)); - - if (checkLoadedVersion(module, version) == 0) return 0; - - - driverpath = getenv("EPICS_DRIVER_PATH"); - if (!driverpath) driverpath = "."; - - if (requireDebug) - printf("require: searchpath=%s\n", driverpath); - - /* NEW */ - - /* Search for module in driverpath */ - for (p = driverpath; p != NULL; p = end) - { - char libdir[256]; - char fulllibdir[256]; - char foundlibdir[256]; - DIR* dir; - struct dirent* dirent; - - end = strchr(p, sep[0]); - snprintf(libdir, sizeof(libdir), "%.*s" DIRSEP "%s", - (int)(end?(end++-p):sizeof(libdir)), p, module); - - /* Ignore empty driverpath elements */ - if (libdir[0] == 0) continue; - - /* Does the module directory exist? */ - dir = opendir(libdir); + dirname = getLibLocation(module); + if (dirname[0] == 0) return 0; if (requireDebug) - printf("require: directory candidate %s %sfound\n", libdir, dir?"":"not "); - if (!dir) continue; - - /* Found module directory in driverpath. Now look for versions. */ - while ((dirent = readdir(dir)) != NULL) - { - #ifdef _DIRENT_HAVE_D_TYPE - if (dirent->d_type != DT_DIR && dirent->d_type != DT_UNKNOWN) continue; /* not a directories */ - #endif - if (dirent->d_name[0] == '.') continue; /* ignore hidden directories */ - - /* Look for highest matching version. */ - if (requireDebug) - printf("require: checking %s against %s\n", - dirent->d_name, version); - switch (compareVersions(version, dirent->d_name)) - { - int i; - case MISMATCH: - if (requireDebug) - printf("require: %s %s does not match %s\n", - module, dirent->d_name, version); - continue; - case MATCH: - snprintf(foundlibdir, sizeof(foundlibdir), "%s" DIRSEP "%n%s", - libdir, &i, dirent->d_name); - found = foundlibdir + i; - if (requireDebug) - printf("require: %s %s matches %s exactly\n", - module, found, version); - /* We are done. */ - end = NULL; - break; - case COMPATIBLE: /* Potential version found. */ - if (requireDebug) - printf("require: %s %s may match %s\n", - module, dirent->d_name, version); - - /* Check if it has our EPICS version and architecture. */ - snprintf(fulllibdir, sizeof(fulllibdir), - "%s" DIRSEP "%s" DIRSEP "%s" DIRSEP "lib" DIRSEP "%s" DIRSEP, - libdir, dirent->d_name, epicsRelease, targetArch); - if (stat(fulllibdir, &filestat) == 0) - { - if (requireDebug) - printf("require: %s %s has no support for %s %s\n", - module, dirent->d_name, epicsRelease, targetArch); - continue; - } - - /* Is it higher than the one we found before? */ - if (compareVersions(found, dirent->d_name) == 1) - { - if (requireDebug) - printf("require: %s %s looks promising\n", - module, dirent->d_name); - snprintf(foundlibdir, sizeof(foundlibdir), "%s" DIRSEP "%n%s", - libdir, &i, dirent->d_name); - found = foundlibdir + i; - } - /* Keep trying */; - continue; - } - break; - } - closedir(dir); - if (!found && requireDebug) - printf("require: No matching version in %s\n", libdir); + printf("require: library found in %s\n", dirname); + snprintf(filename, sizeof(filename), "%s%n", dirname, &releasediroffs); } - if (found) + else { if (requireDebug) - printf("require: found module in %s\n", foundlibdir); + printf("require: no %s version loaded yet\n", module); - /* check dependencies */ - - /* load library */ - - /* load dbd file */ - - /* call register function */ - - /* load startup script */ - - /* load substitution file */ - - - return -1; - } - char libname[256]; - char dbdname[256]; - char depname[256]; - char fulllibname[256]; - char fulldbdname[256]; - char fulldepname[256]; - char symbolname[256]; - - if (requireDebug) - printf("require: trying the old way\n"); - /* OLD */ - /* user may give a minimal version (e.g. "1.2.4+") load highest matching version (here "1.2") and check later + (old style only) */ - if (isdigit((unsigned char)version[0]) && version[strlen(version)-1] == '+') + if (version) { - char* p = strrchr(version, '.'); - if (!p) p = version; - *p = 0; - } - - /* make filenames with or without version string */ - - if (version[0]) - { - sprintf(libname, PREFIX "%s" INFIX "-%s" EXT, module, version); - sprintf(depname, "%s-%s.dep", module, version); + if (asprintf(&versionstr, "-%s", version) < 0) return errno; + if (isdigit((unsigned char)version[0]) && version[strlen(version)-1] == '+') + { + char* p = strrchr(versionstr, '.'); + if (p == NULL) p = versionstr; + *p = 0; + } } else - { - sprintf(libname, PREFIX "%s" INFIX EXT, module); - sprintf(depname, "%s.dep", module); - } + versionstr = calloc(2,1); if (requireDebug) - { - printf("require: libname is %s\n", libname); - printf("require: depname is %s\n", depname); - } + printf("require: versionstr = \"%s\"\n", versionstr); - /* search for library in driverpath */ - for (p = driverpath; p != NULL; p = end) - { - end = strchr(p, sep[0]); - if (end) + /* versionstr == "-" or "" */ + + /* Search for module in driverpath */ + for (dirname = driverpath; dirname != NULL; dirname = end) + { + /* get one directory from driverpath */ + int dirlen; + int modulediroffs; + + end = strchr(dirname, PATHSEP[0]); + if (end && end[1] == DIRSEP[0] && end[2] == DIRSEP[0]) /* "http://..." and friends */ + end = strchr(end+2, PATHSEP[0]); + if (end) dirlen = end++ - dirname; + else dirlen = strlen(dirname); + if (dirlen == 0) continue; /* ignore empty driverpath elements */ + + if (requireDebug) + printf("require: trying %.*s\n", dirlen, dirname); + + snprintf(filename, sizeof(filename), "%.*s" DIRSEP "%s" DIRSEP "%n", + dirlen, dirname, module, &modulediroffs); + dirlen++; + /* filename = "/[dirlen]/[modulediroffs]" */ + + /* Does the module directory exist? */ + dir = opendir(filename); + if (dir) { - sprintf (libdir, "%.*s", (int)(end-p), p); - end++; + if (requireDebug) + printf("require: found directory %s\n", filename); + + /* Now look for versions. */ + while ((dirent = readdir(dir)) != NULL) + { + #ifdef _DIRENT_HAVE_D_TYPE + if (dirent->d_type != DT_DIR && dirent->d_type != DT_UNKNOWN) continue; /* not a directory */ + #endif + if (dirent->d_name[0] == '.') continue; /* ignore hidden directories */ + + /* Look for highest matching version. */ + if (requireDebug) + printf("require: checking version %s against required %s\n", + dirent->d_name, version); + + switch ((status = compareVersions(version, dirent->d_name))) + { + case EXACT: /* exact match found */ + { + if (requireDebug) + printf("require: %s %s matches %s exactly\n", + module, dirent->d_name, version); + /* We are done. */ + end = NULL; + break; + } + case MATCH: /* all given numbers match. */ + { + if (requireDebug) + printf("require: %s %s may match %s\n", + module, dirent->d_name, version); + + /* Check if it has our EPICS version and architecture. */ + /* Even if it has no library, at least it has a dep file in the lib dir */ + + /* filename = "/[dirlen]/[modulediroffs]" */ + if (!TRY_FILE(modulediroffs, "%s" DIRSEP "R%s" DIRSEP LIBDIR "%s" DIRSEP, + dirent->d_name, epicsRelease, targetArch)) + /* filename = "/[dirlen]/[modulediroffs]/R/lib//" */ + { + if (requireDebug) + printf("require: %s %s has no support for %s %s\n", + module, dirent->d_name, epicsRelease, targetArch); + continue; + } + + /* Is it higher than the one we found before? */ + if (found && requireDebug) + printf("require: %s %s support for %s %s found, compare against previously found %s\n", + module, dirent->d_name, epicsRelease, targetArch, found); + if (!found || compareVersions(found, dirent->d_name) == MATCH) + { + if (requireDebug) + printf("require: %s %s looks promising\n", module, dirent->d_name); + break; + } + continue; + } + default: + { + if (requireDebug) + printf("require: %s %s does not match %s\n", + module, dirent->d_name, version); + continue; + } + } + /* we have found something (EXACT or MATCH) */ + free(founddir); + /* filename = "/[dirlen]/[modulediroffs]..." */ + if (asprintf(&founddir, "%.*s%s", modulediroffs, filename, dirent->d_name) < 0) return errno; + /* founddir = "/[dirlen]/[modulediroffs]" */ + found = founddir + modulediroffs; /* version part in the path */ + } + closedir(dir); } else { - sprintf (libdir, "%s", p); - } - /* ignore empty driverpath elements */ - if (libdir[0] == 0) continue; + /* filename = "/[dirlen]/" */ + if (requireDebug) + printf("require: no %s\n", filename); - sprintf (fulllibname, "%s" DIRSEP "%s", libdir, libname); - sprintf (fulldepname, "%s" DIRSEP "%s", libdir, depname); - if (requireDebug) - printf("require: looking for %s\n", fulllibname); - if (stat(fulllibname, &filestat) == 0) break; -#ifdef vxWorks - /* now without the .munch */ - fulllibname[strlen(fulllibname)-6] = 0; - if (requireDebug) - printf("require: looking for %s\n", fulllibname); - if (stat(fulllibname, &filestat) == 0) break; -#endif - /* allow dependency without library for aliasing */ - if (requireDebug) - printf("require: looking for %s\n", fulldepname); - if (stat(fulldepname, &filestat) == 0) break; - } - if (!p) - { - fprintf(stderr, "Library %s not found in EPICS_DRIVER_PATH=%s\n", - libname, driverpath); - return -1; - } - if (requireDebug) - printf("require: found in %s\n", p); - - /* parse dependency file if exists */ - if (stat(fulldepname, &filestat) == 0) - { - FILE* depfile; - char buffer[40]; - char *rmodule; /* required module */ - char *rversion; /* required version */ - - if (requireDebug) - printf("require: parsing dependency file %s\n", fulldepname); - depfile = fopen(fulldepname, "r"); - while (fgets(buffer, sizeof(buffer)-1, depfile)) - { - rmodule = buffer; - /* ignore leading spaces */ - while (isspace((int)*rmodule)) rmodule++; - /* ignore empty lines and comment lines */ - if (*rmodule == 0 || *rmodule == '#') continue; - /* rmodule at start of module name */ - rversion = rmodule; - /* find end of module name */ - while (*rversion && !isspace((int)*rversion)) rversion++; - /* terminate module name */ - *rversion++ = 0; - /* ignore spaces */ - while (isspace((int)*rversion)) rversion++; - /* rversion at start of version */ - end = rversion; - /* find end of version */ - while (*end && !isspace((int)*end)) end++; - /* append + to version to allow newer compaible versions */ - *end++ = '+'; - /* terminate version */ - *end = 0; - printf("%s depends on %s %s\n", module, rmodule, rversion); - if (require(rmodule, rversion) != 0) + /* try local/old style module only if no new style candidate has been found */ + if (!found) { - fclose(depfile); - return -1; + /* look for dep file */ + releasediroffs = libdiroffs = dirlen; + if (TRY_FILE(dirlen, "%s%s.dep", module, versionstr)) + /* filename = "/[dirlen][releasediroffs][libdiroffs](-)?.dep" */ + if (fileExists(filename)) + { + if (requireDebug) + printf("require: found old style %s\n", filename); + printf ("Module %s %s found in %.*s\n", module, + versionstr+1, dirlen, filename); + goto checkdep; + } + + /* look for library file */ + if (TRY_FILE(dirlen, PREFIX "%s" INFIX "%s%n" EXT, module, versionstr, &extoffs) + /* filename = "/[dirlen][releasediroffs][libdiroffs]PREFIXINFIX(-)?[extoffs]EXT" */ + #ifdef vxWorks + || (filename[dirlen + extoffs] = 0 && /* try without extension */ + (fileExists(filename) || (requireDebug && !printf("require: no %s\n", filename)))) + #endif + ) + { + if (requireDebug) + printf("require: found old style %s\n", filename); + printf ("Module %s s%s found in %.*s\n", module, + versionstr+1, dirlen, filename); + free(versionstr); + goto loadlib; + } } } - fclose(depfile); + /* filename = "/[dirlen]..." */ + if (!found && requireDebug) + printf("require: no matching version in %.*s\n", dirlen, filename); } - - if (stat(fulllibname, &filestat) != 0) - { - /* no library, dep file was an alias */ - if (requireDebug) - printf("require: no library to load\n"); - return 0; - } - - /* load library */ - if (requireDebug) - printf("require: loading library %s\n", fulllibname); - if (!(libhandle = loadlib(fulllibname))) - { - if (requireDebug) - printf("require: loading failed\n"); - return -1; - } - - /* now check if we got what we wanted (with original version number) */ - sprintf (symbolname, "_%sLibRelease", module); - loaded = getAddress(libhandle, symbolname); + free(versionstr); + versionstr = NULL; - if (loaded) + if (!found) { - printf("Loading %s (version %s)\n", fulllibname, loaded); - /* make sure we get the dbd that matches the library version */ - sprintf(dbdname, "%s-%s.dbd", module, loaded); + fprintf(stderr, "Module %s not found\n", module); + return ifexists ? 0 : -1; + } + + /* founddir = "/[dirlen]/" */ + printf ("Module %s version %s found in %s" DIRSEP "\n", module, found, founddir); + + snprintf(filename, sizeof(filename), + "%s" DIRSEP "R%s" DIRSEP "%n" LIBDIR "%s" DIRSEP "%n%s.dep", + founddir, epicsRelease, &releasediroffs, targetArch, &libdiroffs, module); + /* filename = "/[dirlen]//R/[releasediroffs]/lib//[libdiroffs]/module.dep" */ + + /* check dependencies */ + if (requireDebug) + printf("require: looking for dependency file %s\n", filename); + +checkdep: + /* filename = "/[dirlen]//R/[releasediroffs]/lib//[libdiroffs]/module.dep" */ + /* or (old) "/[dirlen]][releasediroffs][libdiroffs](-)?.dep" */ + if (handleDependencies(module, filename) == -1) return -1; + + /* load library */ + snprintf(filename + libdiroffs, sizeof(filename) - libdiroffs, + PREFIX "%s" INFIX "%s%n" EXT, module, versionstr ? versionstr : "", &extoffs); + /* filename = "/[dirlen]//R/[releasediroffs]/lib//[libdiroffs]/PREFIXINFIX[extoffs]EXT" */ + /* or (old) "/[dirlen][releasediroffs][libdiroffs]PREFIXINFIX(-)?[extoffs]EXT" */ + + free(versionstr); + + if (requireDebug) + printf("require: looking for library %s\n", filename); + #ifdef vxWorks + if (!fileExists(filename)) + { + /* try without extension */ + filename[libdiroffs + extoffs] = 0; + if (requireDebug) + printf("require: looking for library %s\n", filename); + } + #endif + if (!fileExists(filename)) + { + printf("Module %s has no library\n", module); } else { - printf("Loading %s (no version)\n", fulllibname); - sprintf(dbdname, "%s.dbd", module); - loaded = ""; - } - - if (compareVersions(vers, loaded) == -1) - { - fprintf(stderr, "Requested %s version %s not available, found only %s.\n", - module, vers, loaded); - return -1; - } - - if (requireDebug) - { - printf("require: dbdname is %s\n", dbdname); - } - /* look for dbd in . ./dbd ../dbd ../../dbd (relative to lib dir) */ - p = PATHSEP DIRSEP "dbd" - PATHSEP DIRSEP ".." DIRSEP "dbd" - PATHSEP DIRSEP ".." DIRSEP ".." DIRSEP "dbd"; - while (p) - { - end = strchr(p, sep[0]); - if (end) +loadlib: + /* filename = "/[dirlen]//R/[releasediroffs]/lib//[libdiroffs]/PREFIXINFIX[extoffs]EXT" */ + /* or (old) "/[dirlen][releasediroffs][libdiroffs]PREFIXINFIX(-)?[extoffs]EXT" */ + printf("Loading library %s\n", filename); + if ((libhandle = loadlib(filename)) == NULL) { - sprintf(fulldbdname, "%s%.*s" DIRSEP "%s", - libdir, (int)(end-p), p, dbdname); - end++; + return -1; + } + + /* now check what version we really got (with compiled-in version number) */ + if (asprintf (&symbolname, "_%sLibRelease", module) < 0) + return errno; + found = getAddress(libhandle, symbolname); + free(symbolname); + printf("Loaded %s version %s\n", module, found); + + /* check what we got */ + if (requireDebug) + printf("require: compare requested version %s with found version %s\n", version, found); + if (compareVersions(version, found) == MISMATCH) + { + fprintf(stderr, "Requested %s version %s not available, found only %s.\n", + module, version, found); + return -1; + } + + /* load dbd file */ + if (TRY_NONEMPTY_FILE(releasediroffs, "dbd" DIRSEP "%s.dbd", module) || + TRY_NONEMPTY_FILE(releasediroffs, "%s.dbd", module)) + { + printf("Loading dbd file %s\n", filename); + if (dbLoadDatabase(filename, NULL, NULL) != 0) + { + fprintf (stderr, "Error loading %s\n", filename); + return -1; + } + + #ifndef EPICS_3_13 + /* when dbd is loaded call register function */ + if (asprintf(&symbolname, "%s_registerRecordDeviceDriver", module) < 0) + return errno; + printf ("Calling function %s\n", symbolname); + #ifdef vxWorks + { + FUNCPTR f = (FUNCPTR) getAddress(NULL, symbolname); + if (f) + f(pdbbase); + else + fprintf (stderr, "require: can't find %s function\n", symbolname); + } + #else /* !vxWorks */ + iocshCmd(symbolname); + #endif /* !vxWorks */ + free(symbolname); + #endif /* !EPICS_3_13 */ } else { - sprintf(fulldbdname, "%s%s" DIRSEP "%s", - libdir, p, dbdname); + /* no dbd file, but that might be OK */ + printf("%s has no dbd file\n", module); } - if (requireDebug) - printf("require: Looking for %s\n", fulldbdname); - if (stat(fulldbdname, &filestat) == 0) break; - p=end; } - - /* if dbd file exists and is not empty load it */ - if (p && filestat.st_size > 0) + /* register module with path */ + filename[releasediroffs] = 0; + registerModule(module, found, filename); + } + + status = 0; + + /* set up environment */ + /* Why putenv()? + vxWorks has no setenv() + Epics 3.13 has no epicsEnvSet() + Do not free the memory given to putenv (except for vxWorks)! + */ + + putenvprintf("MODULE=%s", module); /* "///R/" */ + putenvprintf("%s_DIR=%s", module, filename); + + if (requireDebug) + printf("require: looking for template directory\n"); + /* filename = "/[dirlen]//R/[releasediroffs]..." */ + if (TRY_FILE(releasediroffs, TEMPLATEDIR)) + { + char* old_path; + char* p; + char* q; + + if (requireDebug) + printf("require: found template directory %s\n", filename); + + /* set up db search path environment variables + _TEMPLATES template path of + TEMPLATES template path of the current module (overwritten) + EPICS_DB_INCLUDE_PATH template path of all loaded modules (appended) + */ + + putenvprintf("%s_TEMPLATES=%s", module, filename); + putenvprintf("TEMPLATES=%s", filename); + old_path = getenv("EPICS_DB_INCLUDE_PATH"); + if (loaded) /* Library was already loaded thus filename is already in path. Remove it. */ { - printf("Loading %s\n", fulldbdname); - if (dbLoadDatabase(fulldbdname, NULL, NULL) != 0) + p = strstr(old_path, filename); + if (p) { - fprintf (stderr, "require: can't load %s\n", fulldbdname); - return -1; + /* remove leading or trailing ':' */ + q = p + releasediroffs + sizeof(TEMPLATEDIR)-1; + if (*q == ':') q++; else if (p != old_path && *(p-1) == ':') p--; + strcpy(p, q); } - - /* when dbd is loaded call register function for 3.14 */ -#ifdef EPICS_3_14 - sprintf (symbolname, "%s_registerRecordDeviceDriver", module); - printf ("Calling %s function\n", symbolname); -#ifdef vxWorks - { - FUNCPTR f = (FUNCPTR) getAddress(NULL, symbolname); - if (f) - f(pdbbase); - else - fprintf (stderr, "require: can't find %s function\n", symbolname); - } -#else - iocshCmd(symbolname); -#endif -#endif } + putenvprintf("EPICS_DB_INCLUDE_PATH=%s:%s", filename, old_path); + } + else + { + putenvprintf("TEMPLATES=."); + } + +#define SETUP_PATH(NAME, args...) \ + if (TRY_FILE(releasediroffs, args)) \ + { \ + putenvprintf("%s_" #NAME "=%s", module, filename); \ + putenvprintf(#NAME "=%s", filename); \ + }\ + else \ + { \ + putenvprintf(#NAME "=."); \ + } + + if (loaded && args == NULL) return 0; /* no need to execute startup script twice if not with new arguments */ + + /* load startup script */ + if (requireDebug) + printf("require: looking for startup script\n"); + /* filename = "/[dirlen]//R/[releasediroffs]db" */ + if (TRY_FILE(releasediroffs, "%s.cmd", targetArch) || + TRY_FILE(releasediroffs, "%s.cmd", osClass) || + TRY_FILE(releasediroffs, "startup.cmd") || + TRY_FILE(releasediroffs, ".." DIRSEP "%s.cmd", targetArch) || + TRY_FILE(releasediroffs, ".." DIRSEP "%s.cmd", osClass) || + TRY_FILE(releasediroffs, ".." DIRSEP "startup.cmd") + ) + { + if (args) + printf("Executing script %s with \"%s\"\n", filename, args); else - { - /* no dbd file, but that might be OK */ - printf("no dbd file %s\n", dbdname); - } - - registerModule(module, loaded); - return 0; + printf("Executing script %s\n", filename); + if (runScript(filename, args) != 0) + fprintf (stderr, "Error executing %s\n", filename); + else + printf("Done with script %s\n", filename); + } + return status; } -#ifdef EPICS_3_14 +#ifndef EPICS_3_13 static const iocshArg requireArg0 = { "module", iocshArgString }; -static const iocshArg requireArg1 = { "version", iocshArgString }; -static const iocshArg * const requireArgs[2] = { &requireArg0, &requireArg1 }; -static const iocshFuncDef requireDef = { "require", 2, requireArgs }; +static const iocshArg requireArg1 = { "[version]", iocshArgString }; +static const iocshArg requireArg2 = { "[substitutions]", iocshArgString }; +static const iocshArg * const requireArgs[3] = { &requireArg0, &requireArg1, &requireArg2 }; +static const iocshFuncDef requireDef = { "require", 3, requireArgs }; static void requireFunc (const iocshArgBuf *args) { - require(args[0].sval, args[1].sval); + require(args[0].sval, args[1].sval, args[2].sval); } static const iocshArg libversionShowArg0 = { "pattern", iocshArgString }; -static const iocshArg * const libversionArgs[1] = { &libversionShowArg0 }; -static const iocshFuncDef libversionShowDef = { "libversionShow", 1, libversionArgs }; +static const iocshArg libversionShowArg1 = { "showLocation", iocshArgInt }; +static const iocshArg * const libversionArgs[2] = { &libversionShowArg0, &libversionShowArg1 }; +static const iocshFuncDef libversionShowDef = { "libversionShow", 2, libversionArgs }; static void libversionShowFunc (const iocshArgBuf *args) { - libversionShow(args[0].sval); + libversionShow(args[0].sval, args[1].ival); } static const iocshArg ldArg0 = { "library", iocshArgString }; @@ -812,6 +1153,15 @@ static void ldFunc (const iocshArgBuf *args) loadlib(args[0].sval); } +static const iocshArg runScriptArg0 = { "filename", iocshArgString }; +static const iocshArg runScriptArg1 = { "substitutions", iocshArgString }; +static const iocshArg * const runScriptArgs[2] = { &runScriptArg0, &runScriptArg1 }; +static const iocshFuncDef runScriptDef = { "runScript", 2, runScriptArgs }; +static void runScriptFunc (const iocshArgBuf *args) +{ + runScript(args[0].sval, args[1].sval); +} + static void requireRegister(void) { if (firstTime) { @@ -819,6 +1169,7 @@ static void requireRegister(void) iocshRegister (&ldDef, ldFunc); iocshRegister (&libversionShowDef, libversionShowFunc); iocshRegister (&requireDef, requireFunc); + iocshRegister (&runScriptDef, runScriptFunc); registerExternalModules(); } } @@ -826,3 +1177,4 @@ static void requireRegister(void) epicsExportRegistrar(requireRegister); epicsExportAddress(int, requireDebug); #endif + diff --git a/require.h b/require.h index cf98895..10ad018 100644 --- a/require.h +++ b/require.h @@ -1,8 +1,16 @@ #ifndef require_h #define require_h -int require(const char* libname, const char* version); +#ifdef __cplusplus +extern "C" { +#endif + +int require(const char* libname, const char* version, const char* args); const char* getLibVersion(const char* libname); -int libversionShow(const char* pattern); +int libversionShow(const char* pattern, int showLocation); + +#ifdef __cplusplus +} +#endif #endif