diff --git a/.appveyor.yml b/.appveyor.yml index c830ae1a8..a02fe1af7 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -25,14 +25,19 @@ init: # Set clone depth (do not fetch complete history) clone_depth: 5 -# Skipping commits affecting only specific files +# Skip commits affecting only specific files skip_commits: files: - - 'documentation/*' + # Removed - 'documentation/*' + # Unfortunately Appveyor only looks at the HEAD (latest) commit, so if + # the last commit pushed to a PR only updates the Release Notes the whole + # PR build gets skipped. GitHub Actions looks at the complete list + # of files changed since its last build, which is much more useful. - 'startup/*' - '.github/*' - '.tools/*' - '.gitattributes' + - '.lgtm.yml' - '**/*.html' - '**/*.md' @@ -67,6 +72,8 @@ environment: - CMP: vs2010 - CMP: gcc APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 + # TODO: static linking w/ readline isn't working. Bypass auto-detect + COMMANDLINE_LIBRARY: EPICS # Platform: processor architecture platform: diff --git a/.appveyor/epics-base-7.yml b/.appveyor/epics-base-7.yml index 4a01ccde5..1ed3def80 100644 --- a/.appveyor/epics-base-7.yml +++ b/.appveyor/epics-base-7.yml @@ -32,13 +32,18 @@ init: # Set clone depth (do not fetch complete history) clone_depth: 5 -# Skipping commits affecting only specific files +# Skip commits affecting only specific files skip_commits: files: - - 'documentation/*' + # Removed - 'documentation/*' + # Unfortunately Appveyor only looks at the HEAD (latest) commit, so if + # the last commit pushed to a PR only updates the Release Notes the whole + # PR build gets skipped. GitHub Actions looks at the complete list + # of files changed since its last build, which is much more useful. - 'startup/*' - '.github/*' - '.tools/*' + - '.lgtm.yml' - '.gitattributes' - '**/*.html' - '**/*.md' @@ -73,6 +78,9 @@ environment: - CMP: vs2012 - CMP: vs2010 - CMP: gcc + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 + # TODO: static linking w/ readline isn't working. Bypass auto-detect + COMMANDLINE_LIBRARY: EPICS # Platform: processor architecture platform: diff --git a/.ci b/.ci index d675de24e..aea790683 160000 --- a/.ci +++ b/.ci @@ -1 +1 @@ -Subproject commit d675de24e6a2be018f6ff1dc35618c16dd621727 +Subproject commit aea79068391fe407fcb817c036bc54f26342bdaa diff --git a/.github/workflows/ci-scripts-build.yml b/.github/workflows/ci-scripts-build.yml index b9b3a0c12..945fea713 100644 --- a/.github/workflows/ci-scripts-build.yml +++ b/.github/workflows/ci-scripts-build.yml @@ -15,6 +15,7 @@ on: - 'startup/*' - '.appveyor/*' - '.tools/*' + - '.lgtm.yml' - '.gitattributes' - '**/*.html' - '**/*.md' @@ -24,6 +25,7 @@ on: - 'startup/*' - '.appveyor/*' - '.tools/*' + - '.lgtm.yml' - '.gitattributes' - '**/*.html' - '**/*.md' @@ -34,7 +36,7 @@ env: EPICS_TEST_IMPRECISE_TIMING: YES jobs: - build-base: + native: name: ${{ matrix.name }} runs-on: ${{ matrix.os }} # Set environment variables from matrix parameters @@ -45,6 +47,7 @@ jobs: RTEMS: ${{ matrix.rtems }} RTEMS_TARGET: ${{ matrix.rtems_target }} EXTRA: ${{ matrix.extra }} + EXTRA1: ${{ matrix.extra1 }} TEST: ${{ matrix.test }} strategy: fail-fast: false @@ -69,10 +72,17 @@ jobs: extra: "CMD_CXXFLAGS=-std=c++11" name: "Ub-20 gcc-9 C++11, static" - - os: ubuntu-16.04 + - os: ubuntu-20.04 + cmp: gcc + configuration: static + extra: "CMD_CFLAGS=-funsigned-char" + extra1: "CMD_CXXFLAGS=-funsigned-char" + name: "Ub-20 gcc-9 unsigned char" + + - os: ubuntu-20.04 cmp: clang configuration: default - name: "Ub-16 clang-9" + name: "Ub-20 clang-10" - os: ubuntu-20.04 cmp: clang @@ -85,7 +95,6 @@ jobs: configuration: default rtems: "5" rtems_target: RTEMS-pc686-qemu - test: NO name: "Ub-20 gcc-9 + RT-5.1 pc686" - os: ubuntu-20.04 @@ -137,6 +146,7 @@ jobs: rtems: "4.10" name: "Ub-20 gcc-9 + RT-4.10" rtems_target: RTEMS-pc386-qemu + test: NO - os: ubuntu-20.04 cmp: gcc @@ -145,35 +155,6 @@ jobs: name: "Ub-20 gcc-9 + RT-4.9" rtems_target: RTEMS-pc386-qemu - - os: ubuntu-16.04 - cmp: gcc-4.8 - utoolchain: "4.8" - configuration: default - name: "Ub-16 gcc-4.8" - - - os: ubuntu-16.04 - cmp: gcc-4.9 - utoolchain: "4.9" - configuration: default - name: "Ub-16 gcc-4.9" - - - os: ubuntu-20.04 - cmp: gcc-8 - utoolchain: "8" - configuration: default - name: "Ub-20 gcc-8" - - - os: ubuntu-20.04 - cmp: gcc-9 - utoolchain: "9" - configuration: default - name: "Ub-20 gcc-9" - - - os: ubuntu-20.04 - cmp: clang - configuration: default - name: "Ub-20 clang-10" - - os: macos-latest cmp: clang configuration: default @@ -200,7 +181,7 @@ jobs: name: "Win2019 mingw" steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: submodules: true - name: Automatic core dumper analysis @@ -210,14 +191,86 @@ jobs: sudo apt-get update sudo apt-get -y install qemu-system-x86 g++-mingw-w64-x86-64 gdb if: runner.os == 'Linux' - - name: "apt-get install ${{ matrix.cmp }}" + - name: Prepare and compile dependencies + run: python .ci/cue.py prepare + - name: Build main module + run: python .ci/cue.py build + - name: Run main module tests + run: python .ci/cue.py -T 60M test + - name: Upload tapfiles Artifact + if: ${{ always() }} + uses: actions/upload-artifact@v3 + with: + name: tapfiles ${{ matrix.name }} + path: '**/O.*/*.tap' + if-no-files-found: ignore + - name: Collect and show test results + if: ${{ always() }} + run: python .ci/cue.py -T 5M test-results + + docker: + name: ${{ matrix.name }} + runs-on: ubuntu-latest + container: + image: ${{ matrix.image }} + # Set environment variables from matrix parameters + env: + CMP: ${{ matrix.cmp }} + BCFG: ${{ matrix.configuration }} + EXTRA: ${{ matrix.extra }} + TEST: ${{ matrix.test }} + strategy: + fail-fast: false + matrix: + # Job names also name artifacts, character limitations apply + include: + - name: "CentOS-7" + image: centos:7 + cmp: gcc + configuration: default + + - name: "Fedora-33" + image: fedora:33 + cmp: gcc + configuration: default + + - name: "Fedora-latest" + image: fedora:latest + cmp: gcc + configuration: default + + steps: + - name: "Build newer Git" + # actions/checkout@v2 wants git >=2.18 + # centos:7 has 1.8 + if: matrix.image=='centos:7' run: | - sudo apt-get update - sudo apt-get -y install software-properties-common - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test - sudo apt-get update - sudo apt-get -y install g++-${{ matrix.utoolchain }} - if: matrix.utoolchain + yum -y install curl make gcc curl-devel expat-devel gettext-devel openssl-devel zlib-devel perl-ExtUtils-MakeMaker + curl https://mirrors.edge.kernel.org/pub/software/scm/git/git-2.29.0.tar.gz | tar -xz + cd git-* + make -j2 prefix=/usr/local all + make prefix=/usr/local install + cd .. + rm -rf git-* + type -a git + git --version + - name: "Redhat setup" + run: | + dnfyum() { + dnf -y "$@" || yum -y "$@" + return $? + } + dnfyum install python3 gdb make perl gcc-c++ glibc-devel readline-devel ncurses-devel perl-devel perl-Test-Simple + git --version || dnfyum install git + # rather than just bite the bullet and link python3 -> python, + # people would rather just break all existing scripts... + [ -e /usr/bin/python ] || ln -sf python3 /usr/bin/python + python --version + - uses: actions/checkout@v3 + with: + submodules: true + - name: Automatic core dumper analysis + uses: mdavidsaver/ci-core-dumper@master - name: Prepare and compile dependencies run: python .ci/cue.py prepare - name: Build main module @@ -226,7 +279,7 @@ jobs: run: python .ci/cue.py -T 20M test - name: Upload tapfiles Artifact if: ${{ always() }} - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: tapfiles ${{ matrix.name }} path: '**/O.*/*.tap' diff --git a/.lgtm.yml b/.lgtm.yml new file mode 100644 index 000000000..e3027329e --- /dev/null +++ b/.lgtm.yml @@ -0,0 +1,50 @@ +# Configuration for lgtm.com +# + +path_classifiers: + test: + - exclude: / + - test + - "modules/*/test*" + library: + - modules/libcom/src/yacc + - modules/libcom/src/flex + template: + - src/template + - modules/ca/src/template + - modules/database/src/template + +extraction: + cpp: + prepare: + packages: + - "libreadline-dev" + index: + build_command: + - "g++ --version" + - "make --version" + - "perl --version" + - "make -sj2 || echo '*** Build failed, ignored for lgtm ***'" + + python: + index: + include: + - src/tools + + # Interpreted languages to be excluded + javascript: + index: + exclude: + - "*" + + # Compiled languages to be excluded + java: + index: + build_command: "echo No Java code in this project" + csharp: + index: + build_command: "echo No C# code in this project" + go: + index: + build_command: "echo No Go code in this project" + diff --git a/.tools/make-tar.sh b/.tools/make-tar.sh index 3fe1b4a46..928006288 100755 --- a/.tools/make-tar.sh +++ b/.tools/make-tar.sh @@ -140,10 +140,12 @@ sed \ -e '/\/\.ci-local\//d' \ -e '/\/\.tools\//d' \ -e '/\/jenkins\//d' \ - -e '/\/\.git/d' \ + -e '/\/\.github\//d' \ + -e '/\/\.gitmodules$/d' \ -e '/\/\.hgtags$/d' \ -e '/\/\.cproject$/d' \ -e '/\/\.project$/d' \ + -e '/\/\.lgtm\.yml$/d' \ -e '/\/\.travis\.yml$/d' \ -e '/\/\.appveyor\.yml$/d' \ -e '/\/\.readthedocs\.yml$/d' \ diff --git a/configure/CONFIG b/configure/CONFIG index df454b29b..e72537db1 100644 --- a/configure/CONFIG +++ b/configure/CONFIG @@ -34,6 +34,11 @@ ifeq ($(origin EPICS_HOST_ARCH), undefined) EHA := endif +# Make Perl hash iteration reproducible. +# See: https://reproducible-builds.org/docs/stable-outputs/ +# +export PERL_HASH_SEED = 0 + -include $(CONFIG)/RELEASE -include $(CONFIG)/RELEASE.$(EPICS_HOST_ARCH) -include $(CONFIG)/RELEASE.$(EPICS_HOST_ARCH).Common diff --git a/configure/CONFIG.gnuCommon b/configure/CONFIG.gnuCommon index 0a7820a1c..b97b5601e 100644 --- a/configure/CONFIG.gnuCommon +++ b/configure/CONFIG.gnuCommon @@ -51,6 +51,7 @@ OPT_CXXFLAGS_NO = -g CODE_LDFLAGS = $(PROF_CXXFLAGS_$(PROFILE)) $(GPROF_CXXFLAGS_$(GPROF)) CODE_LDFLAGS += $(ASAN_LDFLAGS_$(ENABLE_ASAN)) +OPT_LDFLAGS_NO = -g PIPE_CFLAGS_YES_YES = -pipe PIPE_CFLAGS = $(PIPE_CFLAGS_$(GCC_PIPE)_$(GNU)) diff --git a/configure/CONFIG_BASE b/configure/CONFIG_BASE index 4b15e4cdc..a86076520 100644 --- a/configure/CONFIG_BASE +++ b/configure/CONFIG_BASE @@ -38,10 +38,16 @@ TOOLS = $(abspath $(EPICS_BASE_HOST_BIN)) FIND_TOOL = $(firstword $(wildcard $(TOOLS)/$(1) \ $(TOP)/src/tools/$(1)) $(EPICS_BASE)/src/tools/$(1)) +#--------------------------------------------------------------- +# Find Perl modules for dependencies +FIND_PM = $(wildcard $(EPICS_BASE)/lib/perl/$(1)) + #--------------------------------------------------------------- # EPICS Base build tools and tool flags -PODTOHTML = $(PERL) $(TOOLS)/podToHtml.pl +PODTOHTML_pl = $(TOOLS)/podToHtml.pl +PODTOHTML_dep = $(PODTOHTML_pl) $(call FIND_PM,EPICS/PodHtml.pm) +PODTOHTML = $(PERL) $(PODTOHTML_pl) CONVERTRELEASE = $(PERL) $(call FIND_TOOL,convertRelease.pl) FULLPATHNAME = $(PERL) $(TOOLS)/fullPathName.pl GENVERSIONHEADER = $(PERL) $(TOOLS)/genVersionHeader.pl $(QUIET_FLAG) $(QUESTION_FLAG) diff --git a/configure/CONFIG_BASE_VERSION b/configure/CONFIG_BASE_VERSION index efa0afee7..12120cddb 100644 --- a/configure/CONFIG_BASE_VERSION +++ b/configure/CONFIG_BASE_VERSION @@ -48,15 +48,15 @@ EPICS_VERSION = 7 EPICS_REVISION = 0 # EPICS_MODIFICATION must be a number >=0 and <256 -EPICS_MODIFICATION = 6 +EPICS_MODIFICATION = 7 # EPICS_PATCH_LEVEL must be a number (win32 resource file requirement) # Not included in the official EPICS version number if zero -EPICS_PATCH_LEVEL = 0 +EPICS_PATCH_LEVEL = 1 # Immediately after an official release the EPICS_PATCH_LEVEL is incremented # and the -DEV suffix is added (similar to the Maven -SNAPSHOT versions) -EPICS_DEV_SNAPSHOT= +EPICS_DEV_SNAPSHOT=-DEV # No changes should be needed below here diff --git a/configure/CONFIG_CA_VERSION b/configure/CONFIG_CA_VERSION index ce95a0ad3..1fe4f1c25 100644 --- a/configure/CONFIG_CA_VERSION +++ b/configure/CONFIG_CA_VERSION @@ -2,11 +2,11 @@ EPICS_CA_MAJOR_VERSION = 4 EPICS_CA_MINOR_VERSION = 14 -EPICS_CA_MAINTENANCE_VERSION = 0 +EPICS_CA_MAINTENANCE_VERSION = 3 # Development flag, set to zero for release versions -EPICS_CA_DEVELOPMENT_FLAG = 0 +EPICS_CA_DEVELOPMENT_FLAG = 1 # Immediately after a release the MAINTENANCE_VERSION # will be incremented and the DEVELOPMENT_FLAG set to 1 diff --git a/configure/CONFIG_COMMON b/configure/CONFIG_COMMON index 316f971d7..19b26c24c 100644 --- a/configure/CONFIG_COMMON +++ b/configure/CONFIG_COMMON @@ -56,6 +56,7 @@ GNU_DIR = /usr # Directories INSTALL_LOCATION = $(TOP) +INSTALL_ABSOLUTE = $(abspath $(INSTALL_LOCATION)) INSTALL_LOCATION_LIB = $(INSTALL_LOCATION)/lib INSTALL_LOCATION_BIN = $(INSTALL_LOCATION)/bin @@ -71,23 +72,30 @@ INSTALL_DBD = $(INSTALL_LOCATION)/dbd INSTALL_DB = $(INSTALL_LOCATION)/db INSTALL_CONFIG = $(INSTALL_LOCATION)/configure -FINAL_LOCATION = $(shell $(PERL) $(TOOLS)/fullPathName.pl $(INSTALL_LOCATION)) +#------------------------------------------------------- +# These are default settings that may be overridden later -# Directory for OS independant build created files -COMMON_DIR = ../O.Common +# Eventual install path (to be compiled into binaries) +FINAL_LOCATION = $(INSTALL_ABSOLUTE) -# IOC's absolute path to $(TOP), may be overridden inside the application -IOCS_APPL_TOP = $(shell $(FULLPATHNAME) $(INSTALL_LOCATION)) +# IOC's view of install path +IOCS_APPL_TOP = $(INSTALL_ABSOLUTE) #------------------------------------------------------- -# Silencing the build - suppress messages during 'make -s' +# How to portably check the flags to make +define checkflags + make-$1 = $(findstring $1,$(filter-out --%,$(MAKEFLAGS))) +endef +# This is extensible to most single letter flags: +$(foreach flag,s q, $(eval $(call checkflags,$(flag)))) + +# Silent builds - suppress messages during 'make -s' NOP = : -ECHO = @$(if $(filter -s,$(MFLAGS)),$(NOP),echo) -QUIET_FLAG := $(if $(filter -s,$(MFLAGS)),-q,) +ECHO = @$(if $(make-s),$(NOP),echo) +QUIET_FLAG := $(if $(make-s),-q,) -#------------------------------------------------------- # Convert 'make -q' flag into '-i' for genVersionHeader.pl -QUESTION_FLAG := $(if $(filter -q,$(MFLAGS)),-i,) +QUESTION_FLAG := $(if $(make-q),-i,) #------------------------------------------------------- ifdef T_A @@ -133,6 +141,8 @@ LIB_SUFFIX = SHRLIB_PREFIX = $(LIB_PREFIX) DLLSTUB_PREFIX = $(LIB_PREFIX) DLLSTUB_SUFFIX = $(LIB_SUFFIX) +LOADABLE_SHRLIB_PREFIX = $(SHRLIB_PREFIX) +LOADABLE_SHRLIB_SUFFIX = $(SHRLIB_SUFFIX) BUILDLIB_PREFIX_YES = $(DLLSTUB_PREFIX) BUILDLIB_PREFIX_NO = $(LIB_PREFIX) @@ -153,12 +163,14 @@ CMPLR_SRC_DIRS += . $(foreach dir, .. $(SRC_DIRS), \ ALL_SRC_DIRS = $(CMPLR_SRC_DIRS) $(OS_SRC_DIRS) $(GENERIC_SRC_DIRS) #-------------------------------------------------- +# Directory for OS independant build created files +COMMON_DIR = ../O.Common + # compile line include directories -INSTALL_INCLUDES += \ - -I$(INSTALL_INCLUDE)/compiler/$(CMPLR_CLASS) \ - -I$(INSTALL_INCLUDE)/os/$(OS_CLASS) \ - -I$(INSTALL_INCLUDE) -SRC_INCLUDES = -I$(COMMON_DIR) $(addprefix -I, $(wildcard $(ALL_SRC_DIRS))) +INSTALL_INCLUDE_DIRS = $(INSTALL_INCLUDE)/compiler/$(CMPLR_CLASS) \ + $(INSTALL_INCLUDE)/os/$(OS_CLASS) $(INSTALL_INCLUDE) +INSTALL_INCLUDES += $(addprefix -I, $(INSTALL_INCLUDE_DIRS)) +SRC_INCLUDES = $(addprefix -I, $(COMMON_DIR) $(wildcard $(ALL_SRC_DIRS))) #-------------------------------------------------- # Target filename definitions @@ -172,23 +184,27 @@ TESTSHRLIBNAME = $(TESTSHRLIBNAME_$(SHARED_LIBRARIES)) #-------------------------------------------------- # obj files -TARGET_OBJS = $($*_LDOBJS) $(addsuffix $(OBJ),$(basename $($*_OBJS) $($*_SRCS))) +TARGET_OBJS = $($*_LDOBJS) $(addsuffix $(OBJ), \ + $(basename $($*_OBJS) $($*_SRCS))) -PRODUCT_OBJS = $(addsuffix $(OBJ),$(basename $(SRCS) $(USR_SRCS) $(PROD_SRCS) $(USR_OBJS) $(PROD_OBJS))) +PRODUCT_OBJS = $(addsuffix $(OBJ), \ + $(basename $(SRCS) $(USR_SRCS) $(PROD_SRCS) $(USR_OBJS) $(PROD_OBJS))) PROD_LD_OBJS = $(TARGET_OBJS) $(PRODUCT_OBJS) -LIBRARY_OBJS = $(addsuffix $(OBJ),$(basename $(SRCS) $(USR_SRCS) $(LIB_SRCS) $(LIBSRCS) $(USR_OBJS) $(LIB_OBJS))) +LIBRARY_OBJS = $(addsuffix $(OBJ), \ + $(basename $(SRCS) $(USR_SRCS) $(LIB_SRCS) $(LIBSRCS) $(USR_OBJS) $(LIB_OBJS))) LIBRARY_LD_OBJS = $(TARGET_OBJS) $(LIBRARY_OBJS) #-------------------------------------------------- # Windows resource files -TARGET_RESS = $(if $(RES),$(addsuffix $(RES),$(basename $($*_RCS))),) +TARGET_RESS = $(if $(RES), $(addsuffix $(RES), $(basename $($*_RCS)))) -PROD_RESS = $(if $(RES),$(addsuffix $(RES),$(basename $(RCS) $(PROD_RCS))),) +PROD_RESS = $(if $(RES), $(addsuffix $(RES), $(basename $(RCS) $(PROD_RCS)))) PROD_LD_RESS = $(TARGET_RESS) $(PROD_RESS) -LIBRARY_RESS = $(if $(RES),$(addsuffix $(RES),$(basename $(RCS) $(LIB_RCS) $(LIBRARY_RCS))),) +LIBRARY_RESS = $(if $(RES), $(addsuffix $(RES), \ + $(basename $(RCS) $(LIB_RCS) $(LIBRARY_RCS)))) LIBRARY_LD_RESS = $(TARGET_RESS) $(LIBRARY_RESS) #-------------------------------------------------- @@ -257,6 +273,7 @@ WARN_CXXFLAGS = $(WARN_CXXFLAGS_$($(BUILD_CLASS)_WARN)) OPT_CPPFLAGS = $(OPT_CPPFLAGS_$($(BUILD_CLASS)_OPT)) OPT_CFLAGS = $(OPT_CFLAGS_$($(BUILD_CLASS)_OPT)) OPT_CXXFLAGS = $(OPT_CXXFLAGS_$($(BUILD_CLASS)_OPT)) +OPT_LDFLAGS = $(OPT_LDFLAGS_$($(BUILD_CLASS)_OPT)) # Static build flags STATIC_CFLAGS = $(STATIC_CFLAGS_$(STATIC_BUILD)) @@ -265,19 +282,19 @@ STATIC_LDFLAGS = $(STATIC_LDFLAGS_$(STATIC_BUILD)) STATIC_LDLIBS = $(STATIC_LDLIBS_$(STATIC_BUILD)) #-------------------------------------------------- -# cflags for shared library src files (from SHRLIB_CFLAGS) -LIBRARY_SRCS=$(basename $(foreach lib,$(LIBRARY) $(TESTLIBRARY) $(LOADABLE_LIBRARY),$($(lib)_OBJSNAME) $(LIBRARY_OBJS))) -LIBRARY_SRC_CFLAGS=$($(patsubst $*,SHRLIB,$(findstring $*,$(LIBRARY_SRCS)))_CFLAGS) +# cflags for shared library src files +LIBRARY_SRCS = $(basename $(foreach lib, \ + $(LIBRARY) $(TESTLIBRARY) $(LOADABLE_LIBRARY), \ + $($(lib)_OBJSNAME) $(LIBRARY_OBJS))) +LIBRARY_SRC_CFLAGS = $(if $(findstring $*, $(LIBRARY_SRCS)), $(SHRLIB_CFLAGS)) #-------------------------------------------------- -# prefix, suffix, and ldflags for loadable shared libraries -TARGET_LIB_LDFLAGS=$($(patsubst $*,LOADABLE_,$(findstring $*,$(LOADABLE_LIBRARY)))SHRLIB_LDFLAGS) -LOADABLE_SHRLIB_PREFIX=$(SHRLIB_PREFIX) -LOADABLE_SHRLIB_SUFFIX=$(SHRLIB_SUFFIX) +# ldflags for loadable and shared libraries +TARGET_LIB_LDFLAGS = $(if $(findstring $*, $(LOADABLE_LIBRARY)), \ + $(LOADABLE_SHRLIB_LDFLAGS), $(SHRLIB_LDFLAGS)) #-------------------------------------------------- # Command-line input support default -COMMANDLINE_LIBRARY = EPICS OP_SYS_LDLIBS += $(LDLIBS_$(COMMANDLINE_LIBRARY)) OP_SYS_LDFLAGS += $(LDFLAGS_$(COMMANDLINE_LIBRARY)) RUNTIME_LDFLAGS += $(RUNTIME_LDFLAGS_$(COMMANDLINE_LIBRARY)) @@ -285,31 +302,31 @@ RUNTIME_LDFLAGS += $(RUNTIME_LDFLAGS_$(COMMANDLINE_LIBRARY)) #-------------------------------------------------- # Flags -INCLUDES = -I. $(SRC_INCLUDES) $(INSTALL_INCLUDES) $(RELEASE_INCLUDES)\ - $(TARGET_INCLUDES) $(USR_INCLUDES) $(CMD_INCLUDES) $(OP_SYS_INCLUDES)\ - $($(BUILD_CLASS)_INCLUDES) +INCLUDES = -I. $(SRC_INCLUDES) $(INSTALL_INCLUDES) $(RELEASE_INCLUDES) \ + $(TARGET_INCLUDES) $(USR_INCLUDES) $(CMD_INCLUDES) $(OP_SYS_INCLUDES) \ + $($(BUILD_CLASS)_INCLUDES) -CFLAGS = $($(BUILD_CLASS)_CFLAGS) $(POSIX_CFLAGS) $(OPT_CFLAGS)\ - $(DEBUG_CFLAGS) $(PIPE_CFLAGS) $(WARN_CFLAGS) $(TARGET_CFLAGS)\ - $(USR_CFLAGS) $(CMD_CFLAGS) $(ARCH_DEP_CFLAGS) $(CODE_CFLAGS)\ - $(STATIC_CFLAGS) $(OP_SYS_CFLAGS) $(LIBRARY_SRC_CFLAGS) +CFLAGS = $($(BUILD_CLASS)_CFLAGS) $(POSIX_CFLAGS) $(OPT_CFLAGS) \ + $(DEBUG_CFLAGS) $(PIPE_CFLAGS) $(WARN_CFLAGS) $(TARGET_CFLAGS) \ + $(USR_CFLAGS) $(CMD_CFLAGS) $(ARCH_DEP_CFLAGS) $(CODE_CFLAGS) \ + $(STATIC_CFLAGS) $(OP_SYS_CFLAGS) $(LIBRARY_SRC_CFLAGS) -CXXFLAGS = $($(BUILD_CLASS)_CXXFLAGS) $(POSIX_CXXFLAGS) $(OPT_CXXFLAGS)\ - $(DEBUG_CXXFLAGS) $(PIPE_CFLAGS) $(WARN_CXXFLAGS) $(TARGET_CXXFLAGS)\ - $(USR_CXXFLAGS) $(CMD_CXXFLAGS) $(ARCH_DEP_CXXFLAGS) $(CODE_CXXFLAGS)\ - $(STATIC_CXXFLAGS) $(OP_SYS_CXXFLAGS) $(LIBRARY_SRC_CFLAGS) +CXXFLAGS = $($(BUILD_CLASS)_CXXFLAGS) $(POSIX_CXXFLAGS) $(OPT_CXXFLAGS) \ + $(DEBUG_CXXFLAGS) $(PIPE_CFLAGS) $(WARN_CXXFLAGS) $(TARGET_CXXFLAGS) \ + $(USR_CXXFLAGS) $(CMD_CXXFLAGS) $(ARCH_DEP_CXXFLAGS) $(CODE_CXXFLAGS) \ + $(STATIC_CXXFLAGS) $(OP_SYS_CXXFLAGS) $(LIBRARY_SRC_CFLAGS) -LDFLAGS = $(OPT_LDFLAGS) $(TARGET_LDFLAGS) $(USR_LDFLAGS) $(CMD_LDFLAGS)\ - $(POSIX_LDFLAGS) $(ARCH_DEP_LDFLAGS) $(DEBUG_LDFLAGS) $(OP_SYS_LDFLAGS)\ - $($(BUILD_CLASS)_LDFLAGS) $(RUNTIME_LDFLAGS) $(CODE_LDFLAGS) +LDFLAGS = $(OPT_LDFLAGS) $(TARGET_LDFLAGS) $(USR_LDFLAGS) $(CMD_LDFLAGS) \ + $(POSIX_LDFLAGS) $(ARCH_DEP_LDFLAGS) $(DEBUG_LDFLAGS) $(OP_SYS_LDFLAGS) \ + $($(BUILD_CLASS)_LDFLAGS) $(RUNTIME_LDFLAGS) $(CODE_LDFLAGS) -LDLIBS = $(POSIX_LDLIBS) $(ARCH_DEP_LDLIBS) $(DEBUG_LDLIBS) $(OP_SYS_LDLIBS)\ - $(GNU_LDLIBS_$(GNU)) +LDLIBS = $(POSIX_LDLIBS) $(ARCH_DEP_LDLIBS) $(DEBUG_LDLIBS) $(OP_SYS_LDLIBS) \ + $(GNU_LDLIBS_$(GNU)) -CPPFLAGS = $($(BUILD_CLASS)_CPPFLAGS) $(POSIX_CPPFLAGS) $(OPT_CPPFLAGS)\ - $(DEBUG_CPPFLAGS) $(WARN_CPPFLAGS) $(BASE_CPPFLAGS) $(TARGET_CPPFLAGS)\ - $(USR_CPPFLAGS) $(CMD_CPPFLAGS) $(ARCH_DEP_CPPFLAGS) $(OP_SYS_CPPFLAGS)\ - $(OP_SYS_INCLUDE_CPPFLAGS) $(CODE_CPPFLAGS) $(API_CPPFLAGS) +CPPFLAGS = $($(BUILD_CLASS)_CPPFLAGS) $(POSIX_CPPFLAGS) $(OPT_CPPFLAGS) \ + $(DEBUG_CPPFLAGS) $(WARN_CPPFLAGS) $(BASE_CPPFLAGS) $(TARGET_CPPFLAGS) \ + $(USR_CPPFLAGS) $(CMD_CPPFLAGS) $(ARCH_DEP_CPPFLAGS) $(OP_SYS_CPPFLAGS) \ + $(OP_SYS_INCLUDE_CPPFLAGS) $(CODE_CPPFLAGS) $(API_CPPFLAGS) #-------------------------------------------------- # ar definition default @@ -396,7 +413,7 @@ INSTALL_DOCS = $(DOCS:%= $(INSTALL_DOC)/%) INSTALL_HTMLS = $(HTMLS:%= $(INSTALL_HTML)/$(HTMLS_DIR)/%) INSTALL_TEMPLATE = $(addprefix $(INSTALL_TEMPLATES_SUBDIR)/, \ - $(subst $(CONFIG),top/configure,$(TEMPLATES))) + $(subst $(CONFIG),top/configure,$(TEMPLATES))) INSTALL_CONFIGS = $(CONFIGS:%= $(INSTALL_CONFIG)/%) INSTALL_BIN_INSTALLS = $(addprefix $(INSTALL_BIN)/,$(notdir $(BIN_INSTALLS))) @@ -431,42 +448,41 @@ INSTALL_INC += $(foreach inc, $(INC), \ $(CMPLR_INSTALL_INC) \ $(OS_INSTALL_INC) \ $(GENERIC_INSTALL_INC) \ - $(GENERATED_INSTALL_INC) ) ) -INSTALL_INC += $(addprefix $(INSTALL_INCLUDE)/os/$(OS_CLASS)/, $(INC_$(OS_CLASS)) ) + $(GENERATED_INSTALL_INC))) +INSTALL_INC += $(addprefix $(INSTALL_INCLUDE)/os/$(OS_CLASS)/, $(INC_$(OS_CLASS))) # # Rule 0 # -CMPLR_INSTALL_INC = $(addprefix $(INSTALL_INCLUDE)/compiler/$(CMPLR_CLASS)/, $(INSTALL_INC_jjj) ) -INSTALL_INC_jjj = $(foreach dir, $(CMPLR_SRC_DIRS), $(INSTALL_INC_iii) ) -INSTALL_INC_iii = $(subst $(dir)/, , $(INSTALL_INC_hhh) ) -INSTALL_INC_hhh = $(wildcard $(addsuffix /$(inc), $(dir)) ) +CMPLR_INSTALL_INC = $(addprefix $(INSTALL_INCLUDE)/compiler/$(CMPLR_CLASS)/, \ + $(foreach dir, $(CMPLR_SRC_DIRS), \ + $(subst $(dir)/,, $(wildcard $(addsuffix /$(inc), $(dir)))))) # # Rule 1 # -OS_INSTALL_INC = $(addprefix $(INSTALL_INCLUDE)/os/$(OS_CLASS)/, $(INSTALL_INC_ggg) ) -INSTALL_INC_ggg = $(foreach dir, $(OS_SRC_DIRS), $(INSTALL_INC_fff) ) -INSTALL_INC_fff = $(subst $(dir)/, , $(INSTALL_INC_eee) ) -INSTALL_INC_eee = $(wildcard $(addsuffix /$(inc), $(dir)) ) +OS_INSTALL_INC = $(addprefix $(INSTALL_INCLUDE)/os/$(OS_CLASS)/, \ + $(foreach dir, $(OS_SRC_DIRS), \ + $(subst $(dir)/,, $(wildcard $(addsuffix /$(inc), $(dir)))))) # # Rule 2 # -GENERIC_INSTALL_INC = $(addprefix $(INSTALL_INCLUDE)/, $(INSTALL_INC_ccc) ) -INSTALL_INC_ccc = $(foreach dir, .. $(SRC_DIRS), $(INSTALL_INC_bbb) ) -INSTALL_INC_bbb = $(subst $(dir)/, , $(INSTALL_INC_aaa) ) -INSTALL_INC_aaa = $(wildcard $(addsuffix /$(inc), $(dir)) ) +GENERIC_INSTALL_INC = $(addprefix $(INSTALL_INCLUDE)/, \ + $(foreach dir, .. $(SRC_DIRS), \ + $(subst $(dir)/,, $(wildcard $(addsuffix /$(inc), $(dir)))))) # # Rule 3 # GENERATED_INSTALL_INC = $(INSTALL_INCLUDE)/$(inc) -COMMON_INC += $(filter $(COMMON_DIR)/%, $(foreach file, $(INC), \ - $(firstword $(SOURCE_INC) $(COMMON_DIR)/$(file) ) ) ) -SOURCE_INC = $(wildcard $(file) $(SOURCE_INC_bbb) ) -SOURCE_INC_bbb = $(foreach dir, $(ALL_SRC_DIRS), $(SOURCE_INC_aaa) ) -SOURCE_INC_aaa = $(addsuffix /$(file), $(dir) ) +#--------------------------------------------------------------- +# Files listed in INC that must first be created in O.Common +COMMON_INC += $(filter $(COMMON_DIR)/%, \ + $(foreach file, $(INC), \ + $(firstword $(wildcard $(file) \ + $(foreach dir, $(ALL_SRC_DIRS), \ + $(addsuffix /$(file), $(dir)))) $(COMMON_DIR)/$(file)))) endif diff --git a/configure/CONFIG_DATABASE_MODULE b/configure/CONFIG_DATABASE_MODULE index 3376ff67c..14c219c2c 100644 --- a/configure/CONFIG_DATABASE_MODULE +++ b/configure/CONFIG_DATABASE_MODULE @@ -5,22 +5,28 @@ # in file LICENSE that is included with this distribution. #************************************************************************* -# Set EPICS_DATABASE if necessary -ifndef EPICS_DATABASE - EPICS_DATABASE = $(if $(BUILDING_DATABASE),$(INSTALL_LOCATION),$(EPICS_BASE)) +# Installed perl scripts and dependent modules that have +# a significant effect on the script's output +DBDEXPAND_pl = $(EPICS_BASE_HOST_BIN)/dbdExpand.pl +DBDTORECTYPEH_pl = $(EPICS_BASE_HOST_BIN)/dbdToRecordtypeH.pl +DBDTORECTYPEH_dep = $(DBDTORECTYPEH_pl) $(call FIND_PM,DBD/Rec*.pm) +DBDTOMENUH_pl = $(EPICS_BASE_HOST_BIN)/dbdToMenuH.pl +DBDTOMENUH_dep = $(DBDTOMENUH_pl) $(call FIND_PM,DBD/Menu.pm) +DBDTOHTML_pl = $(EPICS_BASE_HOST_BIN)/dbdToHtml.pl +DBDTOHTML_dep = $(DBDTOHTML_pl) $(call FIND_PM,EPICS/Pod*Html.pm) +REGRECDEVDRV_pl = $(EPICS_BASE_HOST_BIN)/registerRecordDeviceDriver.pl +REGRECDEVDRV_dep = $(REGRECDEVDRV_pl) - # Paths to tools built here - EPICS_DATABASE_HOST_BIN = $(EPICS_DATABASE)/bin/$(EPICS_HOST_ARCH) -endif +# Commands for running scripts in recipes +DBEXPAND = $(PERL) $(DBDEXPAND_pl) +DBTORECORDTYPEH = $(PERL) $(DBDTORECTYPEH_pl) +DBTOMENUH = $(PERL) $(DBDTOMENUH_pl) +DBDTOHTML = $(PERL) $(DBDTOHTML_pl) +REGISTERRECORDDEVICEDRIVER = $(PERL) $(REGRECDEVDRV_pl) -# Set location of locally-built tools -MAKEBPT = $(EPICS_DATABASE_HOST_BIN)/makeBpt$(HOSTEXE) -DBEXPAND = $(PERL) $(EPICS_DATABASE_HOST_BIN)/dbdExpand.pl -DBTORECORDTYPEH = $(PERL) $(EPICS_DATABASE_HOST_BIN)/dbdToRecordtypeH.pl -DBTOMENUH = $(PERL) $(EPICS_DATABASE_HOST_BIN)/dbdToMenuH.pl -DBDTOHTML = $(PERL) $(EPICS_DATABASE_HOST_BIN)/dbdToHtml.pl -REGISTERRECORDDEVICEDRIVER = $(PERL) $(EPICS_DATABASE_HOST_BIN)/registerRecordDeviceDriver.pl -MSI3_15 = $(EPICS_DATABASE_HOST_BIN)/msi$(HOSTEXE) +# Installed binary executables, quoted for running on Windows +MAKEBPT = "$(EPICS_BASE_HOST_BIN)/makeBpt$(HOSTEXE)" +MSI3_15 = "$(EPICS_BASE_HOST_BIN)/msi$(HOSTEXE)" # Libraries needed to link a basic IOC EPICS_BASE_IOC_LIBS = dbRecStd dbCore ca Com diff --git a/configure/CONFIG_DATABASE_VERSION b/configure/CONFIG_DATABASE_VERSION index 348a69b11..8b4002ae6 100644 --- a/configure/CONFIG_DATABASE_VERSION +++ b/configure/CONFIG_DATABASE_VERSION @@ -1,12 +1,12 @@ # Version number for the database APIs and shared library EPICS_DATABASE_MAJOR_VERSION = 3 -EPICS_DATABASE_MINOR_VERSION = 20 -EPICS_DATABASE_MAINTENANCE_VERSION = 0 +EPICS_DATABASE_MINOR_VERSION = 22 +EPICS_DATABASE_MAINTENANCE_VERSION = 1 # Development flag, set to zero for release versions -EPICS_DATABASE_DEVELOPMENT_FLAG = 0 +EPICS_DATABASE_DEVELOPMENT_FLAG = 1 # Immediately after a release the MAINTENANCE_VERSION # will be incremented and the DEVELOPMENT_FLAG set to 1 diff --git a/configure/CONFIG_LIBCOM_MODULE b/configure/CONFIG_LIBCOM_MODULE index 32bb937c2..428d17a8e 100644 --- a/configure/CONFIG_LIBCOM_MODULE +++ b/configure/CONFIG_LIBCOM_MODULE @@ -5,9 +5,10 @@ # in file LICENSE that is included with this distribution. #************************************************************************* -# Set location of locally generated tools -YACC = $(abspath $(EPICS_BASE)/bin/$(EPICS_HOST_ARCH))/antelope$(HOSTEXE) -LEX = $(abspath $(EPICS_BASE)/bin/$(EPICS_HOST_ARCH))/e_flex$(HOSTEXE) \ +# Our locally-built tools +# Windows can need these paths to be quoted +YACC = "$(EPICS_BASE_HOST_BIN)/antelope$(HOSTEXE)" +LEX = "$(EPICS_BASE_HOST_BIN)/e_flex$(HOSTEXE)" \ -S$(EPICS_BASE)/include/flex.skel.static # Default stack size for osiThread diff --git a/configure/CONFIG_LIBCOM_VERSION b/configure/CONFIG_LIBCOM_VERSION index 76a5a5fcf..898df2c67 100644 --- a/configure/CONFIG_LIBCOM_VERSION +++ b/configure/CONFIG_LIBCOM_VERSION @@ -1,12 +1,12 @@ # Version number for the libcom APIs and shared library EPICS_LIBCOM_MAJOR_VERSION = 3 -EPICS_LIBCOM_MINOR_VERSION = 20 -EPICS_LIBCOM_MAINTENANCE_VERSION = 0 +EPICS_LIBCOM_MINOR_VERSION = 22 +EPICS_LIBCOM_MAINTENANCE_VERSION = 1 # Development flag, set to zero for release versions -EPICS_LIBCOM_DEVELOPMENT_FLAG = 0 +EPICS_LIBCOM_DEVELOPMENT_FLAG = 1 # Immediately after a release the MAINTENANCE_VERSION # will be incremented and the DEVELOPMENT_FLAG set to 1 diff --git a/configure/RULES.Db b/configure/RULES.Db index 406c1eb16..f08af423d 100644 --- a/configure/RULES.Db +++ b/configure/RULES.Db @@ -17,7 +17,7 @@ TEMPL_SUFFIX ?= .template # vpath vpath %.pm $(USR_VPATH) $(SRC_DIRS) $(dir $(DBD)) -vpath %.pod $(USR_VPATH) $(SRC_DIRS) $(dir $(DBD)) +vpath %.pod $(USR_VPATH) $(SRC_DIRS) .. $(dir $(DBD)) vpath %.dbd $(USR_VPATH) $(SRC_DIRS) $(dir $(DBD)) vpath %.db $(USR_VPATH) $(SRC_DIRS) $(dir $(DB)) vpath %.vdb $(USR_VPATH) $(SRC_DIRS) $(dir $(DB)) @@ -217,32 +217,32 @@ realclean: clean #--------------------------------------------------------------- # Dependency files -%Record.h$(DEP): $(COMMON_DIR)/%Record.dbd +%Record.h$(DEP): $(COMMON_DIR)/%Record.dbd $(DBDTORECTYPEH_dep) @$(RM) $@ @$(DBTORECORDTYPEH) -D $(DBDFLAGS) -o $(COMMONDEP_TARGET) $< > $@ @echo "$(COMMONDEP_TARGET): ../Makefile" >> $@ -%Record.h$(DEP): %Record.dbd +%Record.h$(DEP): %Record.dbd $(DBDTORECTYPEH_dep) @$(RM) $@ @$(DBTORECORDTYPEH) -D $(DBDFLAGS) -o $(COMMONDEP_TARGET) $< > $@ @echo "$(COMMONDEP_TARGET): ../Makefile" >> $@ -%Record.h$(DEP): ../%Record.dbd +%Record.h$(DEP): ../%Record.dbd $(DBDTORECTYPEH_dep) @$(RM) $@ @$(DBTORECORDTYPEH) -D $(DBDFLAGS) -o $(COMMONDEP_TARGET) $< > $@ @echo "$(COMMONDEP_TARGET): ../Makefile" >> $@ -menu%.h$(DEP): $(COMMON_DIR)/menu%.dbd +menu%.h$(DEP): $(COMMON_DIR)/menu%.dbd $(DBDTOMENUH_dep) @$(RM) $@ @$(DBTOMENUH) -D $(DBDFLAGS) -o $(COMMONDEP_TARGET) $< > $@ @echo "$(COMMONDEP_TARGET): ../Makefile" >> $@ -menu%.h$(DEP): menu%.dbd +menu%.h$(DEP): menu%.dbd $(DBDTOMENUH_dep) @$(RM) $@ @$(DBTOMENUH) -D $(DBDFLAGS) -o $(COMMONDEP_TARGET) $< > $@ @echo "$(COMMONDEP_TARGET): ../Makefile" >> $@ -menu%.h$(DEP): ../menu%.dbd +menu%.h$(DEP): ../menu%.dbd $(DBDTOMENUH_dep) @$(RM) $@ @$(DBTOMENUH) -D $(DBDFLAGS) -o $(COMMONDEP_TARGET) $< > $@ @echo "$(COMMONDEP_TARGET): ../Makefile" >> $@ @@ -261,7 +261,7 @@ menu%.h$(DEP): ../menu%.dbd @$(DBEXPAND) -D $(DBDFLAGS) -o $(COMMONDEP_TARGET) $< > $@ @echo "$(COMMONDEP_TARGET): ../Makefile" >> $@ -%.dbd$(DEP): +%.dbd$(DEP): $($*_DBD) @$(RM) $@ @$(DBEXPAND) -D $(DBDFLAGS) -o $(COMMONDEP_TARGET) $($*_DBD) > $@ @echo "$(COMMONDEP_TARGET): ../Makefile" >> $@ @@ -330,32 +330,32 @@ $(INSTALL_DB)/%$(TEMPL_SUFFIX): ../%$(TEMPL_SUFFIX) #--------------------------------------------------------------- # INC files -$(COMMON_DIR)/%Record.h: $(COMMON_DIR)/%Record.dbd +$(COMMON_DIR)/%Record.h: $(COMMON_DIR)/%Record.dbd $(DBDTORECTYPEH_dep) @$(RM) $(notdir $@) $(DBTORECORDTYPEH) $(DBDFLAGS) -o $(notdir $@) $< @$(MV) $(notdir $@) $@ -$(COMMON_DIR)/%Record.h: %Record.dbd +$(COMMON_DIR)/%Record.h: %Record.dbd $(DBDTORECTYPEH_dep) @$(RM) $(notdir $@) $(DBTORECORDTYPEH) $(DBDFLAGS) -o $(notdir $@) $< @$(MV) $(notdir $@) $@ -$(COMMON_DIR)/%Record.h: ../%Record.dbd +$(COMMON_DIR)/%Record.h: ../%Record.dbd $(DBDTORECTYPEH_dep) @$(RM) $(notdir $@) $(DBTORECORDTYPEH) $(DBDFLAGS) -o $(notdir $@) $< @$(MV) $(notdir $@) $@ -$(COMMON_DIR)/menu%.h: $(COMMON_DIR)/menu%.dbd +$(COMMON_DIR)/menu%.h: $(COMMON_DIR)/menu%.dbd $(DBDTOMENUH_dep) @$(RM) $(notdir $@) $(DBTOMENUH) $(DBDFLAGS) -o $(notdir $@) $< @$(MV) $(notdir $@) $@ -$(COMMON_DIR)/menu%.h: menu%.dbd +$(COMMON_DIR)/menu%.h: menu%.dbd $(DBDTOMENUH_dep) @$(RM) $(notdir $@) $(DBTOMENUH) $(DBDFLAGS) -o $(notdir $@) $< @$(MV) $(notdir $@) $@ -$(COMMON_DIR)/menu%.h: ../menu%.dbd +$(COMMON_DIR)/menu%.h: ../menu%.dbd $(DBDTOMENUH_dep) @$(RM) $(notdir $@) $(DBTOMENUH) $(DBDFLAGS) -o $(notdir $@) $< @$(MV) $(notdir $@) $@ @@ -366,6 +366,7 @@ $(COMMON_DIR)/menu%.h: ../menu%.dbd # DBD files $(COMMON_DIR)/bpt%.dbd: bpt%.data + $(ECHO) "Converting data from $<" @$(RM) $(notdir $@) $(MAKEBPT) $< $(notdir $@) @$(MV) $(notdir $@) $@ @@ -399,7 +400,7 @@ $(COMMON_DBDCATS):$(COMMON_DIR)/%.dbd: $(DBDCAT_COMMAND) @$(MV) $(notdir $@) $@ -$(COMMON_DIR)/%.dbd: +$(COMMON_DIR)/%.dbd: $($*_DBD) $(ECHO) "Creating dbd file $(notdir $@)" @$(RM) $(notdir $@) $(DBEXPAND) $(DBDFLAGS) -o $(notdir $@) $($*_DBD) @@ -429,33 +430,33 @@ $(foreach file, $(DBD_INSTALLS), $(eval $(call DBD_INSTALLS_template, $(file)))) #--------------------------------------------------------------- # HTML files -$(COMMON_DIR)/%.html: %.dbd.pod +$(COMMON_DIR)/%.html: %.dbd.pod $(DBDTOHTML_pl) @$(RM) $(notdir $@) $(DBDTOHTML) $(DBDFLAGS) -o $(notdir $@) $< @$(MV) $(notdir $@) $@ -$(COMMON_DIR)/%.html: %.pod +$(COMMON_DIR)/%.html: %.pod $(PODTOHTML_dep) @$(RM) $(notdir $@) $(PODTOHTML) -s -s -o $(notdir $@) $< @$(MV) $(notdir $@) $@ -$(COMMON_DIR)/%.html: %.pm +$(COMMON_DIR)/%.html: %.pm $(PODTOHTML_dep) @$(RM) $(notdir $@) $(PODTOHTML) -o $(notdir $@) $< @$(MV) $(notdir $@) $@ -$(COMMON_DIR)/%.html: ../%.pm +$(COMMON_DIR)/%.html: ../%.pm $(PODTOHTML_dep) @$(RM) $(notdir $@) $(PODTOHTML) -s -o $(notdir $@) $< @$(MKDIR) $(dir $@) @$(MV) $(notdir $@) $@ -$(COMMON_DIR)/%.html: ../%.pl +$(COMMON_DIR)/%.html: ../%.pl $(PODTOHTML_dep) @$(RM) $(notdir $@) $(PODTOHTML) -s -o $(notdir $@) $< @$(MV) $(notdir $@) $@ -.PRECIOUS: $(COMMON_DIR)/%.html %.html +.PRECIOUS: $(COMMON_DIR)/%.html #--------------------------------------------------------------- # DB files @@ -526,16 +527,19 @@ $(foreach file, $(DB_INSTALLS), $(eval $(call DB_INSTALLS_template, $(file)))) #--------------------------------------------------------------- # register record,device,driver support -%_registerRecordDeviceDriver.cpp: $(COMMON_DIR)/%.dbd +%_registerRecordDeviceDriver.cpp: $(COMMON_DIR)/%.dbd $(REGRECDEVDRV_dep) @$(RM) $@ - $(REGISTERRECORDDEVICEDRIVER) $(REGRDDFLAGS) -o $@ $< $(basename $@) $(IOCS_APPL_TOP) + $(REGISTERRECORDDEVICEDRIVER) $(REGRDDFLAGS) -o $@ \ + $< $(basename $@) $(IOCS_APPL_TOP) -%_registerRecordDeviceDriver.cpp: %.dbd +%_registerRecordDeviceDriver.cpp: %.dbd $(REGRECDEVDRV_dep) @$(RM) $@ - $(REGISTERRECORDDEVICEDRIVER) $(REGRDDFLAGS) -o $@ $< $(basename $@) $(IOCS_APPL_TOP) + $(REGISTERRECORDDEVICEDRIVER) $(REGRDDFLAGS) -o $@ \ + $< $(basename $@) $(IOCS_APPL_TOP) -%_registerRecordDeviceDriver.cpp: ../%.dbd +%_registerRecordDeviceDriver.cpp: ../%.dbd $(REGRECDEVDRV_dep) @$(RM) $@ - $(REGISTERRECORDDEVICEDRIVER) $(REGRDDFLAGS) -o $@ $< $(basename $@) $(IOCS_APPL_TOP) + $(REGISTERRECORDDEVICEDRIVER) $(REGRDDFLAGS) -o $@ \ + $< $(basename $@) $(IOCS_APPL_TOP) .PRECIOUS: %_registerRecordDeviceDriver.cpp diff --git a/configure/RULES_BUILD b/configure/RULES_BUILD index 7a0212e98..e505b01c0 100644 --- a/configure/RULES_BUILD +++ b/configure/RULES_BUILD @@ -224,7 +224,7 @@ endif checkRelease: +$(CONVERTRELEASE) checkRelease warnRelease: - $(CONVERTRELEASE) checkRelease + -$(CONVERTRELEASE) checkRelease noCheckRelease: ifeq ($(EPICS_HOST_ARCH),$(T_A)) $(info Warning: RELEASE file consistency checks have been disabled) @@ -386,23 +386,25 @@ $(MODNAME): %$(MODEXT): %$(EXE) # Automated testing runtests: run-tap-tests -run-tap-tests: $(TESTSCRIPTS.t) +run-tap-tests: | build ifneq ($(TESTSCRIPTS.t),) ifdef RUNTESTS_ENABLED - $(ECHO) "$(PROVE) $^" - @$(PROVE) $^ || $(PROVE_FAILURE) + $(ECHO) "$(PROVE) $(TESTSCRIPTS.t)" + @$(PROVE) $(TESTSCRIPTS.t) || $(PROVE_FAILURE) endif endif tapfiles: $(TAPFILES) junitfiles: $(JUNITFILES) +# prevent deletion of partial output from failing tests +.PRECIOUS: $(TAPFILES) $(JUNITFILES) test-results: tap-results tap-results: $(TAPFILES) ifneq ($(strip $(TAPFILES)),) ifdef RUNTESTS_ENABLED - $(ECHO) "$(PROVE.tap) $^" - @$(PROVE.tap) $^ || $(PROVE_FAILURE) + $(ECHO) "$(PROVE.tap) $(TAPFILES)" + @$(PROVE.tap) $(TAPFILES) || $(PROVE_FAILURE) endif CURRENT_TAPFILES := $(wildcard $(TAPFILES)) @@ -418,7 +420,7 @@ ifneq ($(CURRENT_JUNITFILES),) endif # A .tap file is the output from running the associated test script -$(TAPFILES.t): %.tap: %.t +$(TAPFILES.t): %.tap: %.t | build ifdef RUNTESTS_ENABLED $(ECHO) "$(PERL) $< -tap > $@" @$(PERL) $< -tap > $@ || $(TAPFILE_FAILURE) @@ -574,6 +576,10 @@ $(INSTALL_DOC)/%: ../% $(ECHO) "Installing doc $@" @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(INSTALL_DOC) +$(INSTALL_HTML)/$(HTMLS_DIR)/%: $(COMMON_DIR)/% + $(ECHO) "Installing generated html $@" + @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D) + $(INSTALL_HTML)/$(HTMLS_DIR)/%: % $(ECHO) "Installing html $@" @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D) @@ -582,10 +588,6 @@ $(INSTALL_HTML)/$(HTMLS_DIR)/%: ../% $(ECHO) "Installing html $@" @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D) -$(INSTALL_HTML)/$(HTMLS_DIR)/%: $(COMMON_DIR)/% - $(ECHO) "Installing generated html $@" - @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D) - $(INSTALL_TEMPLATES_SUBDIR)/%: ../% $(ECHO) "Installing $@" @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D) diff --git a/configure/RULES_COMMON b/configure/RULES_COMMON index ff428c342..ddc4d5913 100644 --- a/configure/RULES_COMMON +++ b/configure/RULES_COMMON @@ -27,7 +27,7 @@ $(foreach file,$(MAKEFILE_LIST), \ PRINT_Var = $(@:PRINT.%=%) PRINT.%: - @echo $(PRINT_Var) = '$($(PRINT_Var))' + @+echo $(PRINT_Var) = '$($(PRINT_Var))' .PHONY: PRINT PRINT.% diff --git a/configure/RULES_EXPAND b/configure/RULES_EXPAND index 16c299c6d..b523cb5e1 100644 --- a/configure/RULES_EXPAND +++ b/configure/RULES_EXPAND @@ -47,7 +47,7 @@ vpath %@ $(USR_VPATH) $(ALL_SRC_DIRS) # INC += myVersion.h # Default settings -EXPAND_TOOL ?= $(PERL) $(TOOLS)/expandVars.pl +EXPAND_TOOL ?= $(PERL) $(TOOLS)/expandVars.pl $(QUIET_FLAG) EXPANDARCH = -a $(T_A) EXPANDFLAGS += -t $(INSTALL_LOCATION) @@ -55,22 +55,17 @@ EXPANDFLAGS += $(addprefix -D ,$(EXPAND_VARS) $($@_EXPAND_VARS)) EXPANDFLAGS += $(foreach var, $(EXPAND_ME) $($@_EXPAND_ME), \ -D$(var)="$(strip $($(var)))") -# The names of files to be expanded must end with '@' +# Output files EXPANDED = $(EXPAND:%@=%) -EXPANDED_COM = $(EXPAND_COMMON:%@=%) -EXPANDED_COMMON = $(EXPANDED_COM:%=$(COMMON_DIR)/%) +EXPANDED_COMMON = $(EXPAND_COMMON:%@=$(COMMON_DIR)/%) $(EXPANDED): %: %@ $(ECHO) "Expanding $< to $@" - @$(RM) $@ $(EXPAND_TOOL) $(EXPANDARCH) $(EXPANDFLAGS) $($@_EXPANDFLAGS) $< $@ -$(EXPANDED_COM): %: %@ +$(EXPANDED_COMMON): $(COMMON_DIR)/%: %@ $(ECHO) "Expanding $< to $(COMMON_DIR)/$@" - @$(RM) $@ $(EXPAND_TOOL) $(EXPANDFLAGS) $($@_EXPANDFLAGS) $< $@ -$(EXPANDED_COMMON): $(COMMON_DIR)/%: % - @$(MV) $< $@ clean: expand_clean diff --git a/configure/RULES_MODULES b/configure/RULES_MODULES index a0886bdf3..b8e18aecb 100644 --- a/configure/RULES_MODULES +++ b/configure/RULES_MODULES @@ -22,13 +22,14 @@ # 7. Submodules must have a configure/CONFIG_SITE file that contains # -include $(TOP)/../CONFIG_SITE.local -# Add checked-out submodules to DIRS -LIVE_SUBMODULES = $(subst /Makefile,,$(wildcard $(addsuffix /Makefile, $(SUBMODULES)))) -DIRS += $(LIVE_SUBMODULES) +# Add checked-out submodules to DIRS, unless INSTALL_LOCATION is empty +LIVE_SUBMODULES = $(subst /Makefile,, \ + $(wildcard $(addsuffix /Makefile, $(SUBMODULES)))) +live = $(if $(wildcard $(INSTALL_CONFIG)/RULES_TOP),LIVE,DEAD) +DIRS += $($(live)_SUBMODULES) include $(CONFIG)/RULES_DIRS -INSTALL_LOCATION_ABS := $(abspath $(INSTALL_LOCATION)) RELEASE_LOCAL := RELEASE.$(EPICS_HOST_ARCH).local # Ensure that RELEASE..local exists before doing anything else @@ -38,14 +39,13 @@ all host $(DIRS) $(ARCHS) $(ACTIONS) $(dirActionTargets) $(dirArchTargets) \ # Convenience target RELEASE.host: $(RELEASE_LOCAL) -$(RELEASE_LOCAL): Makefile CONFIG_SITE.local +$(RELEASE_LOCAL): Makefile $(CONFIG)/CONFIG_SITE \ + $(wildcard $(CONFIG)/CONFIG_SITE.local) $(ECHO) Creating $@ with - $(ECHO) " $(PARENT_MODULE) = $(INSTALL_LOCATION_ABS)" - @echo $(PARENT_MODULE) = $(INSTALL_LOCATION_ABS)> $@ -realclean: - $(RM) $(wildcard RELEASE.*.local) + $(ECHO) " $(PARENT_MODULE) = $(INSTALL_ABSOLUTE)" + @echo $(PARENT_MODULE) = $(INSTALL_ABSOLUTE)> $@ -.PHONY: RELEASE.host realclean +.PHONY: RELEASE.host # Testing: Combine test failure logs from the live submodules TESTS_FAILED_LOGS = $(wildcard $(addsuffix /$(TESTS_FAILED_LOG), \ diff --git a/configure/RULES_TOP b/configure/RULES_TOP index 60b52e0de..50079b446 100644 --- a/configure/RULES_TOP +++ b/configure/RULES_TOP @@ -23,35 +23,42 @@ ifndef DISABLE_TOP_RULES # Rules for a regular application top directory # -distclean: realclean cvsclean realuninstall + # When run by 'make distclean' the realuninstall target also + # removes any modules/RELEASE..local files + distclean: realclean cvsclean realuninstall -realuninstall: uninstallDirs + realuninstall: uninstallDirs $(RMDIR) $(INSTALL_LOCATION_BIN) $(INSTALL_LOCATION_LIB) + ifeq (modules,$(filter modules,$(DIRS))) + ifeq (distclean,$(filter distclean,$(MAKECMDGOALS))) + $(RM) $(wildcard modules/RELEASE.*.local) + endif + endif -UNINSTALL_DIRS += $(INSTALL_DB) $(INSTALL_DBD) $(INSTALL_DOC) $(INSTALL_HTML) -UNINSTALL_DIRS += $(INSTALL_INCLUDE) $(INSTALL_TEMPLATES) $(DIRECTORY_TARGETS) -ifneq ($(INSTALL_LOCATION),$(TOP)) - UNINSTALL_DIRS += $(INSTALL_CONFIG) -endif -uninstallDirs: | clean + UNINSTALL_DIRS += $(INSTALL_DB) $(INSTALL_DBD) $(INSTALL_DOC) $(INSTALL_HTML) + UNINSTALL_DIRS += $(INSTALL_INCLUDE) $(INSTALL_TEMPLATES) $(DIRECTORY_TARGETS) + ifneq ($(INSTALL_LOCATION),$(TOP)) + UNINSTALL_DIRS += $(INSTALL_CONFIG) + endif + uninstallDirs: $(RMDIR) $(UNINSTALL_DIRS) # Remove the bin and lib directories if they have no sub-directories # -EMPTY_INSTALL_DIRS = \ - $(if $(wildcard $(INSTALL_LOCATION_BIN)/*),,$(INSTALL_LOCATION_BIN)) \ - $(if $(wildcard $(INSTALL_LOCATION_LIB)/*),,$(INSTALL_LOCATION_LIB)) -uninstall: archuninstall uninstallDirs | clean + EMPTY_INSTALL_DIRS = \ + $(if $(wildcard $(INSTALL_LOCATION_BIN)/*),,$(INSTALL_LOCATION_BIN)) \ + $(if $(wildcard $(INSTALL_LOCATION_LIB)/*),,$(INSTALL_LOCATION_LIB)) + uninstall: archuninstall uninstallDirs $(RMDIR) $(EMPTY_INSTALL_DIRS) -archuninstall: $(addprefix uninstall$(DIVIDER),$(BUILD_ARCHS)) + archuninstall: $(addprefix uninstall$(DIVIDER),$(BUILD_ARCHS)) -uninstall$(DIVIDER)%: | clean + uninstall$(DIVIDER)%: $(RMDIR) $(addsuffix /$(subst uninstall$(DIVIDER),,$@), \ $(INSTALL_LOCATION_BIN) $(INSTALL_LOCATION_LIB)) -# Only run this at the top of the parent -runtests test-results: + # Only run this at the top of the parent + runtests test-results: @$(SHOWTESTFAILURES) else @@ -76,7 +83,6 @@ help: @echo " inc - Installs header, dbd and html files" @echo " build - Builds and installs all targets" @echo " install - Builds and installs all targets" - @echo " buildInstall - Same as install (deprecated)" @echo " clean - Removes the O. dirs created by running make" @echo " In O. dir, clean removes build created files" @echo " realclean - Removes ALL O. dirs" @@ -101,7 +107,8 @@ ifndef DISABLE_TOP_RULES @echo " uninstall$(DIVIDER) - Remove bin & lib directories for only." @echo " uninstall - Remove install directories created by this hostarch." @echo " realuninstall - Removes ALL install dirs" - @echo " distclean - Same as realclean cvsclean realuninstall." + @echo " distclean - Does realclean cvsclean realuninstall and deletes any" + @echo " generated modules/RELEASE..local files" endif @echo " help - Prints this list of valid make targets " @echo "Object targets are supported by the O. level Makefile .e.g" diff --git a/configure/os/CONFIG.Common.RTEMS b/configure/os/CONFIG.Common.RTEMS index b7a42d137..a736addaa 100644 --- a/configure/os/CONFIG.Common.RTEMS +++ b/configure/os/CONFIG.Common.RTEMS @@ -77,7 +77,10 @@ CPPFLAGS += $($(BUILD_CLASS)_CPPFLAGS) $(POSIX_CPPFLAGS) $(OPT_CPPFLAGS)\ $(USR_CPPFLAGS) $(CMD_CPPFLAGS) $(ARCH_DEP_CPPFLAGS) $(OP_SYS_CPPFLAGS)\ $(OP_SYS_INCLUDE_CPPFLAGS) $(CODE_CPPFLAGS) -ECHO = @$(if $(filter -s,$(MFLAGS)),$(NOP),echo) +ECHO = @$(if $(make-s),$(NOP),echo) + +# Originally set in os/CONFIG.UnixCommon.Common +MKDIR = mkdir -p #-------------------------------------------------- # Although RTEMS uses gcc, it wants to use gcc its own way diff --git a/configure/os/CONFIG.Common.RTEMS-mvme2700 b/configure/os/CONFIG.Common.RTEMS-mvme2700 index 0ee8ac862..82715277e 100644 --- a/configure/os/CONFIG.Common.RTEMS-mvme2700 +++ b/configure/os/CONFIG.Common.RTEMS-mvme2700 @@ -11,14 +11,15 @@ MUNCH_SUFFIX = .boot define MUNCH_CMD $(RTEMS_TOOLS)/bin/$(OBJCOPY_FOR_TARGET) -O binary -R .comment -S $< rtems gzip -f9 rtems - $(RTEMS_TOOLS)/bin/$(LD_FOR_TARGET) -o $@ \ + $(RTEMS_TOOLS)/bin/$(LD_FOR_TARGET) -o $@.elf \ $(PROJECT_RELEASE)/lib/bootloader.o \ --just-symbols=$< \ -b binary rtems.gz \ - --no-warn-mismatch \ + --no-warn-mismatch \ -T $(PROJECT_RELEASE)/lib/ppcboot.lds \ -Map $<.map - rm -f rtems.gz + $(RTEMS_TOOLS)/bin/$(OBJCOPY_FOR_TARGET) -O binary $@.elf $@ + rm -f rtems.gz $@.elf endef include $(CONFIG)/os/CONFIG.Common.RTEMS diff --git a/configure/os/CONFIG.Common.UnixCommon b/configure/os/CONFIG.Common.UnixCommon index 1151dce3f..6163b7975 100644 --- a/configure/os/CONFIG.Common.UnixCommon +++ b/configure/os/CONFIG.Common.UnixCommon @@ -56,7 +56,7 @@ SHRLIB_LDLIBS = $(addprefix -l, $($*_LDLIBS) $(LIB_LIBS) $(USR_LIBS)) \ SHRLIB_DEPLIB_DIRS = $(foreach word, \ $(sort $(INSTALL_LIB)/ $(dir $($*_DEPLIBS) $(SHRLIB_DEPLIBS))), \ - $(shell $(FULLPATHNAME) $(word))) + $(abspath $(word))) SHRLIBDIR_LDFLAGS += $(SHRLIB_DEPLIB_DIRS:%=-L%) @@ -85,7 +85,7 @@ PROD_LDLIBS += $($(firstword $(LDLIBS_STATIC_$(STATIC_BUILD)) \ PROD_DEPLIB_DIRS = $(foreach word, \ $(sort $(INSTALL_LIB)/ $(dir $($*_DEPLIBS) $(PROD_DEPLIBS))), \ - $(shell $(FULLPATHNAME) $(word))) + $(abspath $(word))) PRODDIR_LDFLAGS += $(PROD_DEPLIB_DIRS:%=-L%) diff --git a/configure/os/CONFIG.Common.iosCommon b/configure/os/CONFIG.Common.iosCommon index f9e5750e3..5c248f38a 100644 --- a/configure/os/CONFIG.Common.iosCommon +++ b/configure/os/CONFIG.Common.iosCommon @@ -77,7 +77,7 @@ OP_SYS_LDFLAGS += -dynamic -Z -L$(SDK_DIR)/usr/lib -L$(SDK_DIR)/usr/lib/system # Shared libraries SHRLIB_VERSION = $(EPICS_VERSION).$(EPICS_REVISION).$(EPICS_MODIFICATION) SHRLIB_LDFLAGS = -dynamiclib -flat_namespace -undefined suppress \ - -install_name $(shell $(FULLPATHNAME) $(INSTALL_LIB))/$@ \ + -install_name $(abspath $(INSTALL_LIB))/$@ \ -compatibility_version $(EPICS_VERSION).$(EPICS_REVISION) \ -current_version $(SHRLIB_VERSION) SHRLIB_SUFFIX_BASE = .dylib diff --git a/configure/os/CONFIG.Common.vxWorks-e500v2 b/configure/os/CONFIG.Common.vxWorks-e500v2 new file mode 100644 index 000000000..1a7ea2bce --- /dev/null +++ b/configure/os/CONFIG.Common.vxWorks-e500v2 @@ -0,0 +1,21 @@ +# CONFIG.Common.vxWorks-e500v2 +# +# Definitions for vxWorks-e500v2 target archs (MVME2500) +# Sites may override these definitions in CONFIG_SITE.Common.vxWorks-e500v2 +#------------------------------------------------------- + +# Include definitions common to all vxWorks target archs +include $(CONFIG)/os/CONFIG.Common.vxWorksCommon + +# Vx GNU cross compiler suffix +CMPLR_SUFFIX = ppc + +ARCH_CLASS = ppc + +# Architecture specific build flags +ARCH_DEP_CFLAGS += -te500v2 -mhard-float +ARCH_DEP_CPPFLAGS += -DCPU=PPC85XX +ARCH_DEP_CFLAGS += -DCPU_VARIANT=_ppc85XX_e500v2 +ARCH_DEP_CFLAGS += -mlongcall + +GNU_TARGET = powerpc-wrs-vxworks diff --git a/configure/os/CONFIG.Common.win32-x86-mingw b/configure/os/CONFIG.Common.win32-x86-mingw index 7c03781b0..c49a413f5 100644 --- a/configure/os/CONFIG.Common.win32-x86-mingw +++ b/configure/os/CONFIG.Common.win32-x86-mingw @@ -12,7 +12,7 @@ ARCH_CLASS = x86 POSIX = NO # Definitions used when COMMANDLINE_LIBRARY is READLINE -LDLIBS_READLINE = -lreadline -lcurses +LDLIBS_READLINE = -lreadline -ltermcap ARCH_DEP_CFLAGS += -m32 ARCH_DEP_LDFLAGS += -m32 diff --git a/configure/os/CONFIG.darwinCommon.darwinCommon b/configure/os/CONFIG.darwinCommon.darwinCommon index 3ee6fd5f9..c0a69f46c 100644 --- a/configure/os/CONFIG.darwinCommon.darwinCommon +++ b/configure/os/CONFIG.darwinCommon.darwinCommon @@ -66,8 +66,8 @@ GNU = NO # # Darwin shared libraries # -SHRLIB_LDFLAGS = -dynamiclib -flat_namespace -undefined dynamic_lookup \ - -install_name $(shell $(FULLPATHNAME) $(INSTALL_LIB))/$@ \ +SHRLIB_LDFLAGS = -dynamiclib -flat_namespace \ + -install_name $(abspath $(INSTALL_LIB))/$@ \ $(addprefix -compatibility_version , $(SHRLIB_VERSION)) \ $(addprefix -current_version , $(SHRLIB_VERSION)) SHRLIB_SUFFIX_BASE = .dylib diff --git a/configure/os/CONFIG.linux-arm.linux-arm b/configure/os/CONFIG.linux-arm.linux-arm index 2e7687d0a..f96877da4 100644 --- a/configure/os/CONFIG.linux-arm.linux-arm +++ b/configure/os/CONFIG.linux-arm.linux-arm @@ -6,3 +6,8 @@ # Include common gnu compiler definitions include $(CONFIG)/CONFIG.gnuCommon + +STATIC_LDFLAGS_YES= -Wl,-Bstatic +STATIC_LDFLAGS_NO= +STATIC_LDLIBS_YES= -Wl,-Bdynamic +STATIC_LDLIBS_NO= diff --git a/configure/os/CONFIG.linux-x86.win32-x86-mingw b/configure/os/CONFIG.linux-x86.win32-x86-mingw index 655b33f23..35aa20b56 100644 --- a/configure/os/CONFIG.linux-x86.win32-x86-mingw +++ b/configure/os/CONFIG.linux-x86.win32-x86-mingw @@ -21,4 +21,4 @@ LOADABLE_SHRLIB_LDFLAGS = -shared \ GNU_LDLIBS_YES = # Link with system libraries -OP_SYS_LDLIBS = -lpsapi -lws2_32 -ladvapi32 -luser32 -lkernel32 -lwinmm -ldbghelp +OP_SYS_LDLIBS += -lpsapi -lws2_32 -ladvapi32 -luser32 -lkernel32 -lwinmm -ldbghelp diff --git a/configure/os/CONFIG.win32-x86-mingw.win32-x86-mingw b/configure/os/CONFIG.win32-x86-mingw.win32-x86-mingw index 8430011c4..9c4f56a5c 100644 --- a/configure/os/CONFIG.win32-x86-mingw.win32-x86-mingw +++ b/configure/os/CONFIG.win32-x86-mingw.win32-x86-mingw @@ -32,4 +32,4 @@ LOADABLE_SHRLIB_LDFLAGS = -shared \ GNU_LDLIBS_YES = # Link with system libraries -OP_SYS_LDLIBS = -lpsapi -lws2_32 -ladvapi32 -luser32 -lkernel32 -lwinmm -ldbghelp +OP_SYS_LDLIBS += -lpsapi -lws2_32 -ladvapi32 -luser32 -lkernel32 -lwinmm -ldbghelp diff --git a/configure/os/CONFIG_SITE.Common.linux-aarch64 b/configure/os/CONFIG_SITE.Common.linux-aarch64 index 5c34e9e10..7da494439 100644 --- a/configure/os/CONFIG_SITE.Common.linux-aarch64 +++ b/configure/os/CONFIG_SITE.Common.linux-aarch64 @@ -34,5 +34,6 @@ # WARNING: Variables that are set in $(CONFIG)/CONFIG.gnuCommon cannot be # overridden in this file for native builds, e.g. variables such as # OPT_CFLAGS_YES, WARN_CFLAGS, SHRLIB_LDFLAGS -# They must be set in CONFIG_SITE.linux-aarch64.linux-aarch64 instead. +# They must be set in CONFIG_SITE.linux-aarch64.linux-aarch64 or for +# cross-builds in CONFIG_SITE..linux-aarch64 instead. diff --git a/configure/os/CONFIG_SITE.Common.linux-arm b/configure/os/CONFIG_SITE.Common.linux-arm index 90f792cde..3dc372497 100644 --- a/configure/os/CONFIG_SITE.Common.linux-arm +++ b/configure/os/CONFIG_SITE.Common.linux-arm @@ -15,11 +15,6 @@ # to inform the system of the shared library location. -# Use GNU Readline if the header file is installed -COMMANDLINE_LIBRARY = $(strip $(if $(wildcard \ - $(firstword $(READLINE_DIR) $(GNU_DIR))/include/readline/readline.h), \ - READLINE, EPICS)) - # If libreadline needs additional libraries to be linked with it, try # uncommenting each of the lines below in turn, starting with the top # one and working downwards, until the build succeeds. Do a 'make rebuild' diff --git a/configure/os/CONFIG_SITE.Common.linux-microblaze b/configure/os/CONFIG_SITE.Common.linux-microblaze index b66b4cced..760ad6da6 100644 --- a/configure/os/CONFIG_SITE.Common.linux-microblaze +++ b/configure/os/CONFIG_SITE.Common.linux-microblaze @@ -12,10 +12,6 @@ GNU_DIR = /usr/local/vw/microblaze-2.0/microblazeel-unknown-linux-gnu -# Use GNU Readline if the header file is installed -COMMANDLINE_LIBRARY = $(strip $(if $(wildcard \ - $(GNU_DIR)/include/readline/readline.h), READLINE, EPICS)) - # If libreadline needs additional libraries to be linked with it, try # uncommenting each of the lines below in turn, starting with the top # one and working downwards, until the build succeeds. Do a 'make rebuild' diff --git a/configure/os/CONFIG_SITE.Common.linux-x86 b/configure/os/CONFIG_SITE.Common.linux-x86 index ae779c265..c0f50a37d 100644 --- a/configure/os/CONFIG_SITE.Common.linux-x86 +++ b/configure/os/CONFIG_SITE.Common.linux-x86 @@ -15,10 +15,6 @@ # to inform the system of the shared library location. -# Use GNU Readline if the header file is installed -COMMANDLINE_LIBRARY = $(strip $(if $(wildcard \ - $(GNU_DIR)/include/readline/readline.h), READLINE, EPICS)) - # If libreadline needs additional libraries to be linked with it, try # uncommenting each of the lines below in turn, starting with the top # one and working downwards, until the build succeeds. Do a 'make rebuild' diff --git a/configure/os/CONFIG_SITE.Common.linux-x86_64 b/configure/os/CONFIG_SITE.Common.linux-x86_64 index e098b7601..b98385f16 100644 --- a/configure/os/CONFIG_SITE.Common.linux-x86_64 +++ b/configure/os/CONFIG_SITE.Common.linux-x86_64 @@ -15,10 +15,6 @@ # to inform the system of the shared library location. -# Use GNU Readline if the header file is installed -COMMANDLINE_LIBRARY = $(strip $(if $(wildcard \ - $(GNU_DIR)/include/readline/readline.h), READLINE, EPICS)) - # If libreadline needs additional libraries to be linked with it, try # uncommenting each of the lines below in turn, starting with the top # one and working downwards, until the build succeeds. Do a 'make rebuild' diff --git a/configure/os/CONFIG_SITE.Common.linux-xscale_be b/configure/os/CONFIG_SITE.Common.linux-xscale_be index 5fb5a4642..2905b88ba 100644 --- a/configure/os/CONFIG_SITE.Common.linux-xscale_be +++ b/configure/os/CONFIG_SITE.Common.linux-xscale_be @@ -3,11 +3,6 @@ # Site-specific settings for the linux-xscale_be target -# Use GNU Readline if the header file is installed -COMMANDLINE_LIBRARY = $(strip $(if $(wildcard \ - $(firstword $(READLINE_DIR) $(GNU_DIR))/include/readline/readline.h), \ - READLINE, EPICS)) - # If libreadline needs additional libraries to be linked with it, try # uncommenting each of the lines below in turn, starting with the top # one and working downwards, until the build succeeds. Do a 'make rebuild' diff --git a/configure/os/CONFIG_SITE.darwinCommon.darwinCommon b/configure/os/CONFIG_SITE.darwinCommon.darwinCommon index 297fd8e4f..6cc9a9de0 100644 --- a/configure/os/CONFIG_SITE.darwinCommon.darwinCommon +++ b/configure/os/CONFIG_SITE.darwinCommon.darwinCommon @@ -3,21 +3,24 @@ # Site specific definitions for darwin builds #------------------------------------------------------- -# Note the dir/firstword/wildcard functions below are used -# to avoid warnings about missing directories. +# These settings are designed for users of Homebrew. +# Users of other third-party package managers are welcome to +# provide patches appropriate for their manager. +ifneq (,$(wildcard /opt/homebrew)) + # Default location on aarch64 + HOMEBREW_DIR = /opt/homebrew +else ifneq (,$(wildcard /usr/local/Homebrew)) + # Default location on x86_64 + HOMEBREW_DIR = /usr/local +endif -# Mix-and-match of different package systems is probably not advisable, -# but you can try that if you like... - -# Uncomment these definitions when using Homebrew packages: -#OP_SYS_INCLUDES += -I/usr/local/include -#OP_SYS_LDFLAGS += $(addprefix -L,$(dir $(firstword $(wildcard /usr/local/lib/*)))) - -# Uncomment these definitions when using DarwinPorts packages: -#OP_SYS_INCLUDES += -I/opt/local/include -#OP_SYS_LDFLAGS += $(addprefix -L,$(dir $(firstword $(wildcard /opt/local/lib/*)))) - -# Uncomment these definitions when using Fink packages: -#OP_SYS_INCLUDES += -I/sw/include -#OP_SYS_LDFLAGS += $(addprefix -L,$(dir $(firstword $(wildcard /sw/lib/*)))) +# Look for Homebrew's readline +ifneq (,$(wildcard $(HOMEBREW_DIR)/opt/readline)) + READLINE_DIR = $(HOMEBREW_DIR)/opt/readline +endif +# Use GNU readline if it's avaiilable +ifneq (,$(wildcard $(READLINE_DIR)/include/readline/readline.h)) + INCLUDES_READLINE = -I$(READLINE_DIR)/include + LDFLAGS_READLINE = -L$(READLINE_DIR)/lib +endif diff --git a/configure/os/CONFIG_SITE.linux-x86_64.linux-aarch64 b/configure/os/CONFIG_SITE.linux-x86_64.linux-aarch64 index f4748621f..569d1c384 100644 --- a/configure/os/CONFIG_SITE.linux-x86_64.linux-aarch64 +++ b/configure/os/CONFIG_SITE.linux-x86_64.linux-aarch64 @@ -1,6 +1,6 @@ -# CONFIG_SITE.linux-x86.linux-aarch64 +# CONFIG_SITE.linux-x86_64.linux-aarch64 # -# Site specific definitions for linux-x86 host - linux-aarch64 target builds +# Site specific definitions for linux-x86_64 host - linux-aarch64 target builds #------------------------------------------------------- # Set GNU crosscompiler target name diff --git a/configure/toolchain.c b/configure/toolchain.c index a97085ae3..2f76b475b 100644 --- a/configure/toolchain.c +++ b/configure/toolchain.c @@ -35,3 +35,15 @@ OS_API = posix OS_API = score # endif #endif + +#ifdef __has_include +# if defined(__rtems__) && __RTEMS_MAJOR__<5 && __has_include() +COMMANDLINE_LIBRARY ?= LIBTECLA +# elif __has_include() +COMMANDLINE_LIBRARY ?= READLINE +# else +COMMANDLINE_LIBRARY ?= EPICS +# endif +#else +COMMANDLINE_LIBRARY ?= EPICS +#endif diff --git a/documentation/ComponentReference.pod b/documentation/ComponentReference.pod new file mode 100644 index 000000000..95169598b --- /dev/null +++ b/documentation/ComponentReference.pod @@ -0,0 +1,170 @@ +=head1 EPICS Component Reference Manual + +This document provides reference information about the record types, +menus, link types and channel filters included with EPICS Base. + +Many details about the record and menu definitions are derived automatically +from the source code at build time. + + +=head2 Introduction and IOC Concepts + +These links point to an external website where introductory and overview +documentation is now being published. + +=over + +=item * L + +=item * L + +=back + + +=head2 Record Type Definitions + +These sections describe common aspects of the record types: + +=over + +=item * L + +=item * L + +=item * L + +=back + + +These are the record types supplied with EPICS Base: + +=over + +=item * L + +=item * L + +=item * L + +=item * L + +=item * L + +=item * L + +=item * L + +=item * L + +=item * L + +=item * L + +=item * L + +=item * L + +=item * L + +=item * L + +=item * L<64-bit Integer Input Record (int64in)|int64inRecord> + +=item * L<64-bit Integer Output Record (int64out)|int64outRecord> + +=item * L + +=item * L + +=item * L + +=item * L + +=item * L + +=item * L + +=item * L + +=item * L + +=item * L + +=item * L + +=item * L - Release Manager - - Edit and commit changes to the EPICS version number file - configure/CONFIG_BASE_VERSION. - - - - Release Manager - Tag the module in Git, using these tag conventions: -
    -
  • - R7.0.5-pren - — pre-release tag -
  • -
  • - R7.0.5-rcn - — release candidate tag -
  • -
-
- cd base-7.0
- git tag -m 'ANJ: Tagged for 7.0.5-rc1' R7.0.5-rc1 -
- Note that submodules must not be tagged with the version used - for the top-level, they each have their own separate version numbers - that are only tagged at the final release. - - - - Release Manager - Export the tagged version into a tarfile. The make-tar.sh - script generates a gzipped tarfile directly from the tag, excluding the - files and directories that are only used for continuous integration: -
- cd base-7.0
- ./.tools/make-tar.sh R7.0.5-rc1 base-7.0.5-rc1.tar.gz base-7.0.5-rc1/ -
- Create a GPG signature file of the tarfile as follows: -
- gpg --armor --sign --detach-sig base-7.0.5-rc1.tar.gz -
- - - - - Release Manager - Test the tarfile by extracting its contents and building it on at - least one supported platform. - - - - Website Manager - Copy the tarfile and its signature to the Base download area of the - website and add the new files to the website Base download index - page. - - - - Website Manager - Create or update a website subdirectory to hold the release - documentation, and copy in selected files from the base/documentation - and base/html directories of the tarfile. - - - - Website Manager - Create or modify the webpage for the new release with links to the - release documents and tar file. Pre-release and release-candidate - versions should use the page and URL for the final release version - number. + known set "Date Targeted" to the expected release date. Testing @@ -250,11 +155,8 @@ starting at Release Approval.

Release Manager Check that documentation has been updated: @@ -266,9 +168,7 @@ starting at Release Approval.

Release Manager - Obtain a positive Ok to release from all platform developers - once a release candidate version has gone for 2 weeks without any major - new issues being reported. + Obtain a positive Ok to release from developers. Creating the final release version @@ -277,9 +177,12 @@ starting at Release Approval.

Release Manager -

For each external submodule in turn (assuming it has not been tagged - yet):

+

For each external submodule in turn (assuming it has not been + tagged yet):

    +
  1. git grep UNRELEASED and insert the module version to any + doxygen annotations that have a @since UNRELEASED comment. + Commit (don't push yet).
  2. Check that the module's Release Notes have been updated to cover all changes; add items as necessary, and set the module version number and release date if appropriate. Convert to HTML and view in @@ -288,7 +191,7 @@ starting at Release Approval.

    cd base-7.0/modules/<module>/documentation
    pandoc -f gfm -t html -o RELEASE_NOTES.html RELEASE_NOTES.md - Commit changes (don't push yet).
  3. + Commit changes (don't push).
  4. Edit the module's release version file configure/CONFIG_module_VERSION and its top-level @@ -298,7 +201,7 @@ starting at Release Approval.

  5. Tag the module:
    - git tag -m 'ANJ: Tag for EPICS 7.0.5' <module-version> + git tag -m 'ANJ: Tag for EPICS 7.0.7' <module-version>
  6. @@ -326,15 +229,24 @@ starting at Release Approval.

-

Commit all the submodule updates to the 7.0 branch.

+

After all submodules complete commit the submodule updates + which were added for each submodule in step 4 above to the 7.0 branch + (don't push). After committing, make sure that the output from + git submodule status --cached only shows the appropriate + version tags in the right-most parenthesized column with no + -n-gxxxxxxx suffix.

Release Manager - Edit the main EPICS Base version file and the built-in module version - files: + +

git grep UNRELEASED and insert the release version to any + doxygen annotations that have a @since UNRELEASED comment. + Commit (don't push).

+

Edit the main EPICS Base version file and the built-in module version + files:

  • configure/CONFIG_BASE_VERSION
  • configure/CONFIG_LIBCOM_VERSION
  • @@ -346,6 +258,9 @@ starting at Release Approval.

    PATCH_LEVEL value should have been incremented after the previous release tag was applied. Set all DEVELOPMENT_FLAG values to 0 and EPICS_DEV_SNAPSHOT to the empty string.

    +

    Edit the headings in the Release Notes to show the appropriate + version number and remove the warning about this being an unreleased + version of EPICS.

    Commit these changes (don't push).

    @@ -355,9 +270,9 @@ starting at Release Approval.

    Tag the epics-base module in Git:
    cd base-7.0
    - git tag -m 'ANJ: Tagged for release' R7.0.5 + git tag -m 'ANJ: Tagged for release' R7.0.7
    -

    Don't push these commits or the new tag to the Launchpad repository +

    Don't push anything to the Launchpad repository yet.

    @@ -376,6 +291,9 @@ starting at Release Approval.

    release by incrementing the MAINTENANCE_VERSION or PATCH_LEVEL value in each file. Set all DEVELOPMENT_FLAG values to 1 and EPICS_DEV_SNAPSHOT to "-DEV".

    +

    Set up the headings in the Release Notes for the next release + version number and restore the warning about this being an unreleased + version of EPICS.

    Commit these changes (don't push).

    @@ -387,12 +305,12 @@ starting at Release Approval.

    files and directories that are only used for continuous integration:
    cd base-7.0
    - ./.tools/make-tar.sh R7.0.5 ../base-7.0.5.tar.gz base-7.0.5/ + ./.tools/make-tar.sh R7.0.7 ../base-7.0.7.tar.gz base-7.0.7/
    Create a GPG signature file of the tarfile as follows:
    cd ..
    - gpg --armor --sign --detach-sig base-7.0.5.tar.gz + gpg --armor --sign --detach-sig base-7.0.7.tar.gz
    @@ -412,38 +330,38 @@ starting at Release Approval.

    - Release Manager + Website Editor Copy the tarfile and its signature to the Base download area of the website. - Website Manager + Website Editor Update the website subdirectory that holds the release documentation, and copy in the files from the base/documentation directory of the tarfile. - Website Manager + Website Editor Update the webpage for the new release with links to the release documents and tar file. - Website Manager + Website Editor Add the new release tar file to the website Base download index page. - Website Manager + Website Editor Link to the release webpage from other relevent areas of the website - update front page and sidebars. - Website Manager + Website Editor Add an entry to the website News page, linking to the new version webpage. @@ -453,17 +371,17 @@ starting at Release Approval.

    - Website Manager + Website Editor Upload the tar file and its .asc signature file to the epics-controls web-server.
    - scp base-7.0.5.tar.gz base-7.0.5.tar.gz.asc epics-controls:download/base
    + scp base-7.0.7.tar.gz base-7.0.7.tar.gz.asc epics-controls:download/base
    - Website Manager + Website Editor Follow instructions on Add a page for a new release to create a new release webpage (not @@ -478,7 +396,7 @@ starting at Release Approval.

    - Website Manager + Release Manager Go to the Launchpad milestone for this release. Click the Create release button and add the release date. Put a URL for the release page in the Release notes box, and click the Create release button. Upload diff --git a/documentation/mainpage.dox b/documentation/mainpage.dox index 472f1d999..ed3d688ef 100644 --- a/documentation/mainpage.dox +++ b/documentation/mainpage.dox @@ -5,11 +5,11 @@ Documentation index @li @ref releasenotes @li @ref install -@li @ref recordrefmanual +@li EPICS Component Reference Manual +@li Field Modifiers and Channel Filters +@li Extensible IOC Database Links @li Channel Access Reference Manual -@li Server Side Filters Reference @li msi: Macro Substitution and Include Tool -@li JSON Link Types @li Perl 5 Interface to Channel Access */ diff --git a/modules/CONFIG_SITE.local b/modules/CONFIG_SITE.local index db97a5b78..f0becc2bb 100644 --- a/modules/CONFIG_SITE.local +++ b/modules/CONFIG_SITE.local @@ -2,6 +2,16 @@ # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* +# +# modules/CONFIG_SITE.local +# +# Despite the .local in its name, this file *is* included in EPICS Base +# sources and should *not* be modified by sites. This trick is necessary +# to allow external submodules to be placed inside the modules directory +# without having to modify them, as long as their configure/CONFIG_SITE +# file includes the standard line +# -include $(TOP)/../CONFIG_SITE.local +# that causes this file to be read in during submodule builds. # The name our submodules know us by: PARENT_MODULE = EPICS_BASE diff --git a/modules/ca/src/client/access.cpp b/modules/ca/src/client/access.cpp index 9c94d61c5..58cc4bca5 100644 --- a/modules/ca/src/client/access.cpp +++ b/modules/ca/src/client/access.cpp @@ -88,7 +88,7 @@ const char * ca_message_text [] "Bad event subscription (monitor) identifier", "Remote channel has new network address", "New or resumed network connection", -"Specified task isnt a member of a CA context", +"Specified task isn't a member of a CA context", "Attempt to use defunct CA feature failed", "The supplied string is empty", @@ -324,7 +324,7 @@ int epicsStdCall ca_create_channel ( *chanptr = pChanNotify; pChanNotify->initiateConnect ( guard ); // no need to worry about a connect preempting here because - // the connect sequence will not start untill initiateConnect() + // the connect sequence will not start until initiateConnect() // is called } catch ( cacChannel::badString & ) { @@ -384,9 +384,9 @@ int epicsStdCall ca_clear_channel ( chid pChan ) // we will definately stall out here if all of the // following are true // - // o user creates non-preemtive mode client library context + // o user creates non-preemptive mode client library context // o user doesnt periodically call a ca function - // o user calls this function from an auxiillary thread + // o user calls this function from an auxiliary thread // CallbackGuard cbGuard ( cac.cbMutex ); epicsGuard < epicsMutex > guard ( cac.mutex ); @@ -720,7 +720,7 @@ int epicsStdCall ca_context_status ( ca_client_context * pcac, unsigned level ) /* * ca_current_context () * - * used when an auxillary thread needs to join a CA client context started + * used when an auxiliary thread needs to join a CA client context started * by another thread */ // extern "C" @@ -740,7 +740,7 @@ struct ca_client_context * epicsStdCall ca_current_context () /* * ca_attach_context () * - * used when an auxillary thread needs to join a CA client context started + * used when an auxiliary thread needs to join a CA client context started * by another thread */ // extern "C" diff --git a/modules/ca/src/client/acctst.c b/modules/ca/src/client/acctst.c index 83d848f12..34298680c 100644 --- a/modules/ca/src/client/acctst.c +++ b/modules/ca/src/client/acctst.c @@ -494,7 +494,7 @@ void verifyConnectionHandlerConnect ( appChan *pChans, unsigned chanCount, /* * verifyBlockingConnect () * - * 1) verify that we dont print a disconnect message when + * 1) verify that we don't print a disconnect message when * we delete the last channel * * 2) verify that we delete the connection to the IOC @@ -645,7 +645,7 @@ void verifyBlockingConnect ( appChan *pChans, unsigned chanCount, status = ca_pend_io ( 1e-16 ); if ( status == ECA_TIMEOUT ) { /* - * we end up here if the channel isnt on the same host + * we end up here if the channel isn't on the same host */ epicsThreadSleep ( 0.1 ); ca_poll (); @@ -1112,7 +1112,7 @@ void verifyHighThroughputRead ( chid chan, unsigned interestLevel ) unsigned i; /* - * verify we dont jam up on many uninterrupted + * verify we don't jam up on many uninterrupted * solicitations */ if ( ca_read_access (chan) ) { @@ -1152,7 +1152,7 @@ void verifyHighThroughputWrite ( chid chan, unsigned interestLevel ) } /* - * verify we dont jam up on many uninterrupted + * verify we don't jam up on many uninterrupted * get callback requests */ void verifyHighThroughputReadCallback ( chid chan, unsigned interestLevel ) @@ -1181,7 +1181,7 @@ void verifyHighThroughputReadCallback ( chid chan, unsigned interestLevel ) } /* - * verify we dont jam up on many uninterrupted + * verify we don't jam up on many uninterrupted * put callback request */ void verifyHighThroughputWriteCallback ( chid chan, unsigned interestLevel ) @@ -1239,7 +1239,7 @@ void verifyBadString ( chid chan, unsigned interestLevel ) verify ( status == ECA_NORMAL ); if ( strcmp ( stimStr, respStr ) ) { printf ( - "Test fails if stim \"%s\" isnt roughly equiv to resp \"%s\"\n", + "Test fails if stim \"%s\" isn't roughly equiv to resp \"%s\"\n", stimStr, respStr); } showProgressEnd ( interestLevel ); @@ -1395,7 +1395,7 @@ static void multiSubscrDestroyNoLateCallbackThread ( void * pParm ) /* * raise the priority of the current thread hoping to improve our - * likelyhood of detecting a bug + * likelihood of detecting a bug */ priorityOfTestThread = epicsThreadGetPrioritySelf (); epicsThreadSetPriority ( epicsThreadGetIdSelf(), epicsThreadPriorityHigh ); @@ -1445,7 +1445,7 @@ static void multiSubscrDestroyNoLateCallbackThread ( void * pParm ) } /* - * verify that, in a preemtive callback mode client, a subscription callback never + * verify that, in a preemptive callback mode client, a subscription callback never * comes after the subscription is destroyed */ static void multiSubscrDestroyNoLateCallbackTest ( const char *pName, unsigned interestLevel ) @@ -1563,7 +1563,7 @@ void multiSubscriptionDeleteTest ( chid chan, unsigned interestLevel ) /* * singleSubscriptionDeleteTest * - * verify that we dont fail when we repeatedly create + * verify that we don't fail when we repeatedly create * and delete only one subscription with a high level of * traffic on it */ @@ -1617,7 +1617,7 @@ void singleSubscriptionDeleteTest ( chid chan, unsigned interestLevel ) /* * channelClearWithEventTrafficTest * - * verify that we can delete a channel that has subcriptions + * verify that we can delete a channel that has subscriptions * attached with heavy update traffic */ void channelClearWithEventTrafficTest ( const char *pName, unsigned interestLevel ) @@ -2481,7 +2481,7 @@ void monitorUpdateTest ( chid chan, unsigned interestLevel ) showProgress ( interestLevel ); /* - * attempt to uncover problems where the last event isnt sent + * attempt to uncover problems where the last event isn't sent * and hopefully get into a flow control situation */ prevPassCount = 0u; @@ -2522,7 +2522,7 @@ void monitorUpdateTest ( chid chan, unsigned interestLevel ) ca_poll (); /* emulate typical GUI */ for ( j = 0; j < NELEMENTS ( test ); j++ ) { /* - * we shouldnt see old monitors because + * we shouldn't see old monitors because * we resubscribed */ verify ( test[j].count <= i + 2 ); @@ -3016,7 +3016,7 @@ void testMultithreadSubscr ( void * pParm ) } /* - * test installation of subscriptions similar to usage paterns + * test installation of subscriptions similar to usage patterns * employed by modern versions of the sequencer */ void verifyMultithreadSubscr ( const char * pName, unsigned interestLevel ) @@ -3111,7 +3111,7 @@ void fdManagerVerify ( const char * pName, unsigned interestLevel ) status = ca_flush_io (); verify ( status == ECA_NORMAL ); - /* look for infinite loop in fd manager schedualing */ + /* look for infinite loop in fd manager scheduling */ epicsTimeGetCurrent ( & begin ); eventCount = 0u; while ( 1 ) { @@ -3175,7 +3175,7 @@ void verifyConnectWithDisconnectedChannels ( * we should be able to connect to a valid * channel within a reasonable delay even * though there is one permanently - * diasconnected channel + * disconnected channel */ status = ca_pend_io ( timeoutToPendIO ); verify ( status == ECA_NORMAL ); @@ -3491,7 +3491,7 @@ int acctst ( const char * pName, unsigned interestLevel, unsigned channelCount, /* * CA pend event delay accuracy test - * (CA asssumes that search requests can be sent + * (CA assumes that search requests can be sent * at least every 25 mS on all supported os) */ printf ( "\n" ); diff --git a/modules/ca/src/client/acctstRegister.cpp b/modules/ca/src/client/acctstRegister.cpp index c619f97bb..8f69d25bc 100644 --- a/modules/ca/src/client/acctstRegister.cpp +++ b/modules/ca/src/client/acctstRegister.cpp @@ -32,7 +32,13 @@ static const iocshArg *acctstArgs[] = &acctstArg3, &acctstArg4 }; -static const iocshFuncDef acctstFuncDef = {"acctst", 5, acctstArgs}; +static const iocshFuncDef acctstFuncDef = { + "acctst", + 5, + acctstArgs, + "Execute a Channel Access regression test.\n\n" + "For more information, see the 'acctst' documentation in the Channel Access reference.\n", +}; /* Wrapper called by iocsh, selects the argument types that print needs */ diff --git a/modules/ca/src/client/bhe.cpp b/modules/ca/src/client/bhe.cpp index d7fb77785..3808f061c 100644 --- a/modules/ca/src/client/bhe.cpp +++ b/modules/ca/src/client/bhe.cpp @@ -130,8 +130,8 @@ bool bhe::updatePeriod ( guard.assertIdenticalMutex ( this->mutex ); // - // this block is enetered if the beacon was created as a side effect of - // creating a connection and so we dont yet know the first beacon time + // this block is entered if the beacon was created as a side effect of + // creating a connection and so we don't yet know the first beacon time // and sequence number // if ( this->timeStamp == epicsTime () ) { @@ -154,7 +154,7 @@ bool bhe::updatePeriod ( return false; } - // 1) detect beacon duplications due to redundant routes + // 1) detect beacon duplication due to redundant routes // 2) detect lost beacons due to input queue overrun or damage if ( CA_V410 ( protocolRevision ) ) { unsigned beaconSeqAdvance; @@ -175,7 +175,7 @@ bool bhe::updatePeriod ( // throw out sequence numbers that jump forward by only a few numbers // (this situation is probably caused by a duplicate route - // or a beacon due to input queue overun) + // or a beacon due to input queue overrun) if ( beaconSeqAdvance > 1 && beaconSeqAdvance < 4 ) { logBeaconDiscard ( beaconSeqAdvance, currentTime ); return false; @@ -244,8 +244,8 @@ bool bhe::updatePeriod ( /* * Is this an IOC seen because of an IOC reboot * (beacon come at a higher rate just after the - * IOC reboots). Lower tolarance here because we - * dont have to worry about lost beacons. + * IOC reboots). Lower tolerance here because we + * don't have to worry about lost beacons. * * It may be possible to get false triggers here * if the client is busy, but this does not cause diff --git a/modules/ca/src/client/caProto.h b/modules/ca/src/client/caProto.h index c6e211c11..57b280177 100644 --- a/modules/ca/src/client/caProto.h +++ b/modules/ca/src/client/caProto.h @@ -58,8 +58,8 @@ #define CA_REPEATER_PORT (CA_PORT_BASE+CA_MAJOR_PROTOCOL_REVISION*2u+1u) /* - * 1500 (max of ethernet and 802.{2,3} MTU) - 20(IP) - 8(UDP) - * (the MTU of Ethernet is currently independent of its speed varient) + * 1500 (max of Ethernet and 802.{2,3} MTU) - 20(IP) - 8(UDP) + * (the MTU of Ethernet is currently independent of its speed variant) */ #define ETHERNET_MAX_UDP ( 1500u - 20u - 8u ) #define MAX_UDP_RECV ( 0xffff + 16u ) /* allow large frames to be received in the future */ @@ -117,7 +117,7 @@ typedef ca_uint32_t caResId; /* * for use with search and not_found (if search fails and - * its not a broadcast tell the client to look elesewhere) + * its not a broadcast tell the client to look elsewhere) */ #define DOREPLY 10u #define DONTREPLY 5u @@ -176,7 +176,7 @@ typedef struct ca_hdr { struct mon_info { ca_float32_t m_lval; /* low delta */ ca_float32_t m_hval; /* high delta */ - ca_float32_t m_toval; /* period btween samples */ + ca_float32_t m_toval; /* period between samples */ ca_uint16_t m_mask; /* event select mask */ ca_uint16_t m_pad; /* extend to 32 bits */ }; diff --git a/modules/ca/src/client/ca_client_context.cpp b/modules/ca/src/client/ca_client_context.cpp index 1c2f985b5..1e830fd08 100644 --- a/modules/ca/src/client/ca_client_context.cpp +++ b/modules/ca/src/client/ca_client_context.cpp @@ -224,7 +224,7 @@ void ca_client_context::changeExceptionEvent ( epicsGuard < epicsMutex > guard ( this->mutex ); this->ca_exception_func = pfunc; this->ca_exception_arg = arg; -// should block here until releated callback in progress completes +// should block here until related callback in progress completes } void ca_client_context::replaceErrLogHandler ( @@ -237,7 +237,7 @@ void ca_client_context::replaceErrLogHandler ( else { this->pVPrintfFunc = epicsVprintf; } -// should block here until releated callback in progress completes +// should block here until related callback in progress completes } void ca_client_context::registerForFileDescriptorCallBack ( @@ -252,7 +252,7 @@ void ca_client_context::registerForFileDescriptorCallBack ( // w/o having sent the wakeup message this->_sendWakeupMsg (); } -// should block here until releated callback in progress completes +// should block here until related callback in progress completes } int ca_client_context :: printFormated ( @@ -392,9 +392,19 @@ void ca_client_context :: vSignal ( } epicsTime current = epicsTime::getCurrent (); - char date[64]; - current.strftime ( date, sizeof ( date ), "%a %b %d %Y %H:%M:%S.%f"); - this->printFormated ( " Current Time: %s\n", date ); + try { + char date[64]; + current.strftime ( date, sizeof ( date ), "%a %b %d %Y %H:%M:%S.%f"); + this->printFormated ( " Current Time: %s\n", date ); + } + catch ( std::exception & except ) { + errlogPrintf ( + "CA client library thread \"%s\" caught C++ exception \"%s\"\n", + epicsThreadGetNameSelf (), except.what () ); + epicsTimeStamp now = current; + this->printFormated ( " Current Time: %u.%u\n", + now.secPastEpoch, now.nsec ); + } /* * Terminate execution if unsuccessful @@ -768,9 +778,9 @@ LIBCA_API int epicsStdCall ca_clear_subscription ( evid pMon ) // we will definately stall out here if all of the // following are true // - // o user creates non-preemtive mode client library context + // o user creates non-preemptive mode client library context // o user doesnt periodically call a ca function - // o user calls this function from an auxiillary thread + // o user calls this function from an auxiliary thread // CallbackGuard cbGuard ( cac.cbMutex ); epicsGuard < epicsMutex > guard ( cac.mutex ); diff --git a/modules/ca/src/client/cac.cpp b/modules/ca/src/client/cac.cpp index b1abfc42a..f8d1d0800 100644 --- a/modules/ca/src/client/cac.cpp +++ b/modules/ca/src/client/cac.cpp @@ -285,7 +285,7 @@ cac::~cac () // this blocks until the UDP thread exits so that // it will not sneak in any new clients // - // lock intentionally not held here so that we dont deadlock + // lock intentionally not held here so that we don't deadlock // waiting for the UDP thread to exit while it is waiting to // get the lock. { @@ -312,7 +312,7 @@ cac::~cac () // // wait for all tcp threads to exit // - // this will block for oustanding sends to go out so dont + // this will block for outstanding sends to go out so don't // hold a lock while waiting // { @@ -411,7 +411,7 @@ void cac::show ( ::printf ( "Channel Access Client Context at %p for user %s\n", static_cast ( this ), this->pUserName ); - // this also supresses the "defined, but not used" + // this also suppresses the "defined, but not used" // warning message ::printf ( "\trevision \"%s\"\n", pVersionCAC ); diff --git a/modules/ca/src/client/cadef.h b/modules/ca/src/client/cadef.h index f64e3899c..12861c384 100644 --- a/modules/ca/src/client/cadef.h +++ b/modules/ca/src/client/cadef.h @@ -129,7 +129,7 @@ typedef unsigned CA_SYNC_GID; #define CA_OP_CONN_UP 6 #define CA_OP_CONN_DOWN 7 -/* depricated */ +/* deprecated */ #define CA_OP_SEARCH 2 /* @@ -464,7 +464,7 @@ LIBCA_API int epicsStdCall ca_array_get_callback /* Specify a function to be executed whenever significant changes */ /* occur to a channel. */ /* NOTES: */ -/* 1) Evid may be omited by passing a NULL pointer */ +/* 1) Evid may be omitted by passing a NULL pointer */ /* */ /* 2) An array count of zero specifies the native db count */ /* */ @@ -559,19 +559,19 @@ LIBCA_API chid epicsStdCall ca_evid_to_chid ( evid id ); /* * ca_pend_event() * - * timeOut R wait for this delay in seconds + * timeout R wait for this delay in seconds */ -LIBCA_API int epicsStdCall ca_pend_event (ca_real timeOut); +LIBCA_API int epicsStdCall ca_pend_event (ca_real timeout); #define ca_poll() ca_pend_event(1e-12) /* * ca_pend_io() * - * timeOut R wait for this delay in seconds but return early + * timeout R wait for this delay in seconds but return early * if all get requests (or search requests with null * connection handler pointer have completed) */ -LIBCA_API int epicsStdCall ca_pend_io (ca_real timeOut); +LIBCA_API int epicsStdCall ca_pend_io (ca_real timeout); /* calls ca_pend_io() if early is true otherwise ca_pend_event() is called */ LIBCA_API int epicsStdCall ca_pend (ca_real timeout, int early); @@ -837,7 +837,7 @@ LIBCA_API double epicsStdCall ca_beacon_period (chid chan); LIBCA_API double epicsStdCall ca_receive_watchdog_delay (chid chan); /* - * used when an auxillary thread needs to join a CA client context started + * used when an auxiliary thread needs to join a CA client context started * by another thread */ LIBCA_API struct ca_client_context * epicsStdCall ca_current_context (); diff --git a/modules/ca/src/client/casw.cpp b/modules/ca/src/client/casw.cpp index 54a14036c..c9ff465f7 100644 --- a/modules/ca/src/client/casw.cpp +++ b/modules/ca/src/client/casw.cpp @@ -188,7 +188,7 @@ int main ( int argc, char ** argv ) epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); epicsSocketDestroy ( sock ); - errlogPrintf ("casw: error from recv was = \"%s\"\n", + errlogPrintf ("casw: " ERL_ERROR " from recv was = \"%s\"\n", sockErrBuf ); return -1; } @@ -224,7 +224,7 @@ int main ( int argc, char ** argv ) * always set this field to INADDR_ANY * * clients always assume that if this - * field is set to something that isnt INADDR_ANY + * field is set to something that isn't INADDR_ANY * then it is the overriding IP address of the server. */ ina.sin_family = AF_INET; @@ -235,7 +235,7 @@ int main ( int argc, char ** argv ) } else { /* - * old servers dont supply this and the + * old servers don't supply this and the * default port must be assumed */ ina.sin_port = htons ( serverPort ); diff --git a/modules/ca/src/client/comBuf.cpp b/modules/ca/src/client/comBuf.cpp index 8f92170dd..5c9083ed7 100644 --- a/modules/ca/src/client/comBuf.cpp +++ b/modules/ca/src/client/comBuf.cpp @@ -45,7 +45,7 @@ bool comBuf::flushToWire ( wireSendAdapter & wire, const epicsTime & currentTime return true; } -// throwing the exception from a function that isnt inline +// throwing the exception from a function that isn't inline // shrinks the GNU compiled object code void comBuf::throwInsufficentBytesException () { diff --git a/modules/ca/src/client/comBuf.h b/modules/ca/src/client/comBuf.h index 6a4899414..3d5f58c45 100644 --- a/modules/ca/src/client/comBuf.h +++ b/modules/ca/src/client/comBuf.h @@ -88,6 +88,7 @@ public: bool push ( const T & value ); template < class T > unsigned push ( const T * pValue, unsigned nElem ); + unsigned push ( const char * pValue, unsigned nElem ); unsigned push ( const epicsInt8 * pValue, unsigned nElem ); unsigned push ( const epicsUInt8 * pValue, unsigned nElem ); unsigned push ( const epicsOldString * pValue, unsigned nElem ); @@ -208,6 +209,11 @@ inline unsigned comBuf :: push ( const epicsUInt8 *pValue, unsigned nElem ) return copyInBytes ( pValue, nElem ); } +inline unsigned comBuf :: push ( const char *pValue, unsigned nElem ) +{ + return copyInBytes ( pValue, nElem ); +} + inline unsigned comBuf :: push ( const epicsOldString * pValue, unsigned nElem ) { unsigned index = this->nextWriteIndex; diff --git a/modules/ca/src/client/comQueRecv.cpp b/modules/ca/src/client/comQueRecv.cpp index 026a84ced..d7d07361b 100644 --- a/modules/ca/src/client/comQueRecv.cpp +++ b/modules/ca/src/client/comQueRecv.cpp @@ -46,7 +46,7 @@ void comQueRecv::clear () this->nBytesPending = 0u; } -unsigned comQueRecv::copyOutBytes ( epicsInt8 *pBuf, unsigned nBytes ) +unsigned comQueRecv::copyOutBytes ( char *pBuf, unsigned nBytes ) { unsigned totalBytes = 0u; do { @@ -196,7 +196,7 @@ epicsUInt16 comQueRecv::popUInt16 () if ( ! pComBuf ) { comBuf::throwInsufficentBytesException (); } - // try first for all in one buffer efficent version + // try first for all in one buffer efficient version epicsUInt16 tmp = 0; comBuf::popStatus status = pComBuf->pop ( tmp ); if ( status.success ) { @@ -215,7 +215,7 @@ epicsUInt32 comQueRecv::popUInt32 () if ( ! pComBuf ) { comBuf::throwInsufficentBytesException (); } - // try first for all in one buffer efficent version + // try first for all in one buffer efficient version epicsUInt32 tmp = 0; comBuf::popStatus status = pComBuf->pop ( tmp ); if ( status.success ) { @@ -230,7 +230,7 @@ epicsUInt32 comQueRecv::popUInt32 () bool comQueRecv::popOldMsgHeader ( caHdrLargeArray & msg ) { - // try first for all in one buffer efficent version + // try first for all in one buffer efficient version comBuf * pComBuf = this->bufs.first (); if ( ! pComBuf ) { return false; diff --git a/modules/ca/src/client/comQueRecv.h b/modules/ca/src/client/comQueRecv.h index ad8bead88..01c961e51 100644 --- a/modules/ca/src/client/comQueRecv.h +++ b/modules/ca/src/client/comQueRecv.h @@ -33,7 +33,7 @@ public: comQueRecv ( comBufMemoryManager & ); ~comQueRecv (); unsigned occupiedBytes () const; - unsigned copyOutBytes ( epicsInt8 *pBuf, unsigned nBytes ); + unsigned copyOutBytes ( char *pBuf, unsigned nBytes ); unsigned removeBytes ( unsigned nBytes ); void pushLastComBufReceived ( comBuf & ); void clear (); diff --git a/modules/ca/src/client/comQueSend.cpp b/modules/ca/src/client/comQueSend.cpp index e782a7017..c0c535f42 100644 --- a/modules/ca/src/client/comQueSend.cpp +++ b/modules/ca/src/client/comQueSend.cpp @@ -27,17 +27,17 @@ // 1) Allow sufficent headroom so that users will be able to perform // a reasonable amount of IO within CA callbacks without experiencing // a push/pull deadlock. If a potential push/pull deadlock situation -// occurs then detect and avoid it and provide diagnotic to the user +// occurs then detect and avoid it and provide diagnostic to the user // via special status. -// 2) Return status to the user when there is insufficent memory to +// 2) Return status to the user when there is insufficient memory to // queue a complete message. // 3) return status to the user when a message cant be flushed because // a connection dropped. -// 4) Do not allocate too much memory in exception situatons (such as +// 4) Do not allocate too much memory in exception situations (such as // after a circuit disconnect). // 5) Avoid allocating more memory than is absolutely necessary to meet // the above requirements. -// 6) Message fragments must never be sent to the IOC when there isnt +// 6) Message fragments must never be sent to the IOC when there isn't // enough memory to queue part of a message (we also must not force // a disconnect because the client is starved for memory). // 7) avoid the need to check status for each byte pushed into the @@ -45,7 +45,7 @@ // // Implementation: // 1) When queuing a complete message, first test to see if a flush is -// required. If it is a receive thread scheduals the flush with the +// required. If it is a receive thread schedules the flush with the // send thread, and otherwise directly execute the system call. The // send thread must run at a higher priority than the receive thread // if we are to minimize memory consumption. @@ -58,9 +58,9 @@ // a) A user is queuing more requests that demand a response from a // callback than are removed by the response that initiated the // callback, and this situation persists for many callbacks until -// all buffering in the system is exausted. +// all buffering in the system is exhausted. // b) A user is queuing many requests that demand a response from one -// callback until all buffering in the system is exausted. +// callback until all buffering in the system is exhausted. // c) Some combination of both (a) nad (b). // // diff --git a/modules/ca/src/client/convert.cpp b/modules/ca/src/client/convert.cpp index e043ee2fe..361b60222 100644 --- a/modules/ca/src/client/convert.cpp +++ b/modules/ca/src/client/convert.cpp @@ -35,7 +35,7 @@ #include "caerr.h" /* - * NOOP if this isnt required + * NOOP if this isn't required */ #ifdef EPICS_CONVERSION_REQUIRED @@ -326,7 +326,7 @@ arrayElementCount num /* number of values */ ** int encode; boolean, if true vax to ieee ** else ieee to vax ** -** converts fields ofstruct in HOST format to ieee format +** converts fields of struct in HOST format to ieee format ** or ** converts fields of struct in NET format to fields with HOST ** format @@ -1022,7 +1022,7 @@ arrayElementCount num /* number of values */ ** int encode; boolean, if true vax to ieee ** else ieee to vax ** -** converts fields ofstruct in HOST format to ieee format +** converts fields of struct in HOST format to ieee format ** or ** converts fields of struct in NET format to fields with HOST ** format @@ -1056,7 +1056,7 @@ arrayElementCount num /* number of values */ /**************************************************************************** ** cvrt_sts_long(s,d) ** -** converts fields ofstruct in HOST format to ieee format +** converts fields of struct in HOST format to ieee format ** or ** converts fields of struct in NET format to fields with HOST ** format @@ -1118,7 +1118,7 @@ arrayElementCount num /* number of values */ /**************************************************************************** ** cvrt_time_short(s,d) ** -** converts fields ofstruct in HOST format to ieee format +** converts fields of struct in HOST format to ieee format ** or ** converts fields of struct in NET format to fields with HOST ** format @@ -1239,7 +1239,7 @@ arrayElementCount num /* number of values */ /**************************************************************************** ** cvrt_sts_char(s,d) ** -** converts fields ofstruct in HOST format to ieee format +** converts fields of struct in HOST format to ieee format ** or ** converts fields of struct in NET format to fields with HOST ** format @@ -1274,7 +1274,7 @@ arrayElementCount num /* number of values */ /**************************************************************************** ** cvrt_time_long(s,d) ** -** converts fields ofstruct in HOST format to ieee format +** converts fields of struct in HOST format to ieee format ** or ** converts fields of struct in NET format to fields with HOST ** format @@ -1325,7 +1325,7 @@ arrayElementCount num /* number of values */ for(i=0; i guard ( this->mutex ); // a few legacy clients have a direct pointer to this buffer so we - // set the entrire string to nill terminators before we start copying + // set the entire string to nill terminators before we start copying // in the name (this reduces the chance that another thread will see // garbage characters). size_t newNameLen = strlen ( pHostNameIn ); diff --git a/modules/ca/src/client/iocinf.cpp b/modules/ca/src/client/iocinf.cpp index 7a0853f00..c760a0d34 100644 --- a/modules/ca/src/client/iocinf.cpp +++ b/modules/ca/src/client/iocinf.cpp @@ -79,7 +79,7 @@ extern "C" int epicsStdCall addAddrToChannelAccessAddressList const char *pStr; const char *pToken; struct sockaddr_in addr; - char buf[32u]; /* large enough to hold an IP address */ + char buf[256u]; /* large enough to hold an IP address or hostname */ int status, ret = -1; pStr = envGetConfigParamPtr (pEnv); @@ -189,7 +189,7 @@ extern "C" void epicsStdCall configureChannelAccessAddressList int yes; /* - * dont load the list twice + * don't load the list twice */ assert ( ellCount (pList) == 0 ); diff --git a/modules/ca/src/client/iocinf.h b/modules/ca/src/client/iocinf.h index ae775241d..0dadf44b0 100644 --- a/modules/ca/src/client/iocinf.h +++ b/modules/ca/src/client/iocinf.h @@ -56,7 +56,7 @@ static const double CA_CONN_VERIFY_PERIOD = 30.0; /* (sec) how often to request * monitor flow control * * turning this down effects maximum throughput - * because we dont get an optimal number of bytes + * because we don't get an optimal number of bytes * per network frame */ static const unsigned contiguousMsgCountWhichTriggersFlowControl = 10u; diff --git a/modules/ca/src/client/msgForMultiplyDefinedPV.cpp b/modules/ca/src/client/msgForMultiplyDefinedPV.cpp index f65b392ef..63c00d108 100644 --- a/modules/ca/src/client/msgForMultiplyDefinedPV.cpp +++ b/modules/ca/src/client/msgForMultiplyDefinedPV.cpp @@ -57,7 +57,7 @@ void msgForMultiplyDefinedPV::transactionComplete ( const char * pHostNameRej ) // calls into cac for the notification // the msg object (= this) is being deleted as part of the notification this->cb.pvMultiplyDefinedNotify ( *this, this->channel, this->acc, pHostNameRej ); - // !! dont touch 'this' pointer after this point because object has been deleted !! + // !! don't touch 'this' pointer after this point because object has been deleted !! } void * msgForMultiplyDefinedPV::operator new ( size_t size, diff --git a/modules/ca/src/client/nciu.cpp b/modules/ca/src/client/nciu.cpp index 61ccebafc..a862cb228 100644 --- a/modules/ca/src/client/nciu.cpp +++ b/modules/ca/src/client/nciu.cpp @@ -147,14 +147,14 @@ void nciu::connect ( unsigned nativeType, guard, this->accessRightState ); } - // channel uninstal routine grabs the callback lock so + // channel uninstall routine grabs the callback lock so // a channel will not be deleted while a call back is // in progress // // the callback lock is also taken when a channel // disconnects to prevent a race condition with the // code below - ie we hold the callback lock here - // so a chanel cant be destroyed out from under us. + // so a channel cant be destroyed out from under us. this->notify().connectNotify ( guard ); } diff --git a/modules/ca/src/client/netIO.h b/modules/ca/src/client/netIO.h index 2560663ba..e5b5814bc 100644 --- a/modules/ca/src/client/netIO.h +++ b/modules/ca/src/client/netIO.h @@ -32,7 +32,7 @@ // destructor is virtual (therefore it is protected). // I assume that SUNPRO will fix this in future versions. // With other compilers we get warnings (and -// potential problems) if we dont make the baseNMIU +// potential problems) if we don't make the baseNMIU // destructor virtual. #if defined ( __SUNPRO_CC ) && ( __SUNPRO_CC <= 0x540 ) # define NETIO_VIRTUAL_DESTRUCTOR diff --git a/modules/ca/src/client/oldAccess.h b/modules/ca/src/client/oldAccess.h index cace26ec1..2c39d2768 100644 --- a/modules/ca/src/client/oldAccess.h +++ b/modules/ca/src/client/oldAccess.h @@ -591,16 +591,16 @@ void ca_client_context :: whenThereIsAnExceptionDestroySyncGroupIO ( io.destroy ( *this->pCallbackGuard.get(), guard ); } else { - // dont reverse the lock hierarchy + // don't reverse the lock hierarchy epicsGuardRelease < epicsMutex > guardRelease ( guard ); { // // we will definately stall out here if all of the // following are true // - // o user creates non-preemtive mode client library context + // o user creates non-preemptive mode client library context // o user doesnt periodically call a ca function - // o user calls this function from an auxiillary thread + // o user calls this function from an auxiliary thread // CallbackGuard cbGuard ( this->cbMutex ); epicsGuard < epicsMutex > guard ( this->mutex ); diff --git a/modules/ca/src/client/oldChannelNotify.cpp b/modules/ca/src/client/oldChannelNotify.cpp index 0716a430b..71688c94c 100644 --- a/modules/ca/src/client/oldChannelNotify.cpp +++ b/modules/ca/src/client/oldChannelNotify.cpp @@ -571,7 +571,7 @@ int epicsStdCall ca_create_subscription ( oldSubscription ( guard, *pChan, pChan->io, tmpType, count, mask, pCallBack, pCallBackArg, monixptr ); - // dont touch object created after above new because + // don't touch object created after above new because // the first callback might have canceled, and therefore // destroyed, it return ECA_NORMAL; diff --git a/modules/ca/src/client/oldSubscription.cpp b/modules/ca/src/client/oldSubscription.cpp index b1290b2b8..72a51d9ad 100644 --- a/modules/ca/src/client/oldSubscription.cpp +++ b/modules/ca/src/client/oldSubscription.cpp @@ -41,7 +41,7 @@ oldSubscription::oldSubscription ( *pEventId = this; } io.subscribe ( guard, type, nElem, mask, *this, &this->id ); - // Dont touch this pointer after this point because the + // Don't touch this pointer after this point because the // 1st update callback might cancel the subscription and // thereby destroy this object. } diff --git a/modules/ca/src/client/repeater.cpp b/modules/ca/src/client/repeater.cpp index e597d66f6..e198a25d4 100644 --- a/modules/ca/src/client/repeater.cpp +++ b/modules/ca/src/client/repeater.cpp @@ -79,7 +79,7 @@ /* * these can be external since there is only one instance - * per machine so we dont care about reentrancy + * per machine so we don't care about reentrancy */ static tsDLList < repeaterClient > client_list; @@ -335,7 +335,7 @@ static void fanOut ( const osiSockAddr & from, const void * pMsg, while ( ( pclient = client_list.get () ) ) { theClients.add ( *pclient ); - /* Dont reflect back to sender */ + /* Don't reflect back to sender */ if ( pclient->identicalAddress ( from ) ) { continue; } @@ -392,7 +392,7 @@ static void register_new_client ( osiSockAddr & from, * repeater would not always allow the loopback address * as a local client address so current clients alternate * between the address of the first non-loopback interface - * found and the loopback addresss when subscribing with + * found and the loopback address when subscribing with * the CA repeater until all CA repeaters have been updated * to current code. */ @@ -452,7 +452,7 @@ static void register_new_client ( osiSockAddr & from, } /* - * send a noop message to all other clients so that we dont + * send a noop message to all other clients so that we don't * accumulate sockets when there are no beacons */ caHdr noop; diff --git a/modules/ca/src/client/searchTimer.cpp b/modules/ca/src/client/searchTimer.cpp index 5a4fa446a..60b153639 100644 --- a/modules/ca/src/client/searchTimer.cpp +++ b/modules/ca/src/client/searchTimer.cpp @@ -279,8 +279,8 @@ epicsTimerNotify::expireStatus searchTimer::expire ( if ( this->searchAttempts ) { char buf[64]; currentTime.strftime ( buf, sizeof(buf), "%M:%S.%09f"); - debugPrintf ( ("sent %u delay sec=%f Rts=%s\n", - nFrameSent, this->period(), buf ) ); + debugPrintf ( ("sent %u delay Rts=%s\n", + nFrameSent, buf ) ); } # endif @@ -317,7 +317,7 @@ void searchTimer :: show ( unsigned level ) const // // Reset the delay to the next search request if we get -// at least one response. However, dont reset this delay if we +// at least one response. However, don't reset this delay if we // get a delayed response to an old search request. // void searchTimer::uninstallChanDueToSuccessfulSearchResponse ( diff --git a/modules/ca/src/client/searchTimer.h b/modules/ca/src/client/searchTimer.h index 74c8bde6e..ca5d2c55d 100644 --- a/modules/ca/src/client/searchTimer.h +++ b/modules/ca/src/client/searchTimer.h @@ -82,8 +82,8 @@ private: double framesPerTry; /* # of UDP frames per search try */ double framesPerTryCongestThresh; /* one half N tries w congest */ unsigned retry; - unsigned searchAttempts; /* num search tries after last timer experation */ - unsigned searchResponses; /* num search resp after last timer experation */ + unsigned searchAttempts; /* num search tries after last timer expiration */ + unsigned searchResponses; /* num search resp after last timer expiration */ const unsigned index; ca_uint32_t dgSeqNoAtTimerExpireBegin; ca_uint32_t dgSeqNoAtTimerExpireEnd; diff --git a/modules/ca/src/client/syncgrp.cpp b/modules/ca/src/client/syncgrp.cpp index bee6ea8dc..4f79348fd 100644 --- a/modules/ca/src/client/syncgrp.cpp +++ b/modules/ca/src/client/syncgrp.cpp @@ -82,9 +82,9 @@ extern "C" int epicsStdCall ca_sg_delete ( const CA_SYNC_GID gid ) // we will definately stall out here if all of the // following are true // - // o user creates non-preemtive mode client library context + // o user creates non-preemptive mode client library context // o user doesnt periodically call a ca function - // o user calls this function from an auxiillary thread + // o user calls this function from an auxiliary thread // CallbackGuard cbGuard ( pcac->cbMutex ); epicsGuard < epicsMutex > guard ( pcac->mutex ); @@ -106,9 +106,9 @@ void sync_group_reset ( ca_client_context & client, CASG & sg ) // we will definately stall out here if all of the // following are true // - // o user creates non-preemtive mode client library context + // o user creates non-preemptive mode client library context // o user doesnt periodically call a ca function - // o user calls this function from an auxiillary thread + // o user calls this function from an auxiliary thread // CallbackGuard cbGuard ( client.cbMutex ); epicsGuard < epicsMutex > guard ( client.mutex ); @@ -219,9 +219,9 @@ extern "C" int epicsStdCall ca_sg_test ( const CA_SYNC_GID gid ) // we will definately stall out here if all of the // following are true // - // o user creates non-preemtive mode client library context + // o user creates non-preemptive mode client library context // o user doesnt periodically call a ca function - // o user calls this function from an auxiillary thread + // o user calls this function from an auxiliary thread // CallbackGuard cbGuard ( pcac->cbMutex ); epicsGuard < epicsMutex > guard ( pcac->mutex ); diff --git a/modules/ca/src/client/tcpRecvWatchdog.cpp b/modules/ca/src/client/tcpRecvWatchdog.cpp index 9e0bf0188..367e09237 100644 --- a/modules/ca/src/client/tcpRecvWatchdog.cpp +++ b/modules/ca/src/client/tcpRecvWatchdog.cpp @@ -176,7 +176,7 @@ void tcpRecvWatchdog::sendBacklogProgressNotify ( { guard.assertIdenticalMutex ( this->mutex ); - // We dont set "beaconAnomaly" to be false here because, after we see a + // We don't set "beaconAnomaly" to be false here because, after we see a // beacon anomaly (which could be transiently detecting a reboot) we will // not trust the beacon as an indicator of a healthy server until we // receive at least one message from the server. diff --git a/modules/ca/src/client/tcpiiu.cpp b/modules/ca/src/client/tcpiiu.cpp index e601c8d24..30f6fad17 100644 --- a/modules/ca/src/client/tcpiiu.cpp +++ b/modules/ca/src/client/tcpiiu.cpp @@ -81,7 +81,7 @@ void tcpSendThread::run () while ( true ) { - // dont wait if there is still labor to be done below + // don't wait if there is still labor to be done below if ( ! laborPending ) { epicsGuardRelease < epicsMutex > unguard ( guard ); this->iiu.sendThreadFlushEvent.wait (); @@ -124,7 +124,7 @@ void tcpSendThread::run () } else { // This wakes up the resp thread so that it can call - // the connect callback. This isnt maximally efficent + // the connect callback. This isn't maximally efficient // but it has the excellent side effect of not requiring // that the UDP thread take the callback lock. There are // almost no V42 servers left at this point. @@ -178,7 +178,7 @@ void tcpSendThread::run () char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); - errlogPrintf ("CAC TCP clean socket shutdown error was %s\n", + errlogPrintf ("CAC TCP clean socket shutdown " ERL_ERROR " was %s\n", sockErrBuf ); } } @@ -194,7 +194,7 @@ void tcpSendThread::run () char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); - errlogPrintf ("CAC TCP clean socket shutdown error was %s\n", + errlogPrintf ("CAC TCP clean socket shutdown " ERL_ERROR " was %s\n", sockErrBuf ); } } @@ -204,7 +204,7 @@ void tcpSendThread::run () while ( ! this->iiu.recvThread.exitWait ( 30.0 ) ) { // it is possible to get stuck here if the user calls - // ca_context_destroy() when a circuit isnt known to + // ca_context_destroy() when a circuit isn't known to // be unresponsive, but is. That situation is probably // rare, and the IP kernel might have a timeout for // such situations, nevertheless we will attempt to deal @@ -283,7 +283,7 @@ unsigned tcpiiu::sendBytes ( const void *pBuf, char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); - errlogPrintf ( "CAC: unexpected TCP send error: %s\n", + errlogPrintf ( "CAC: unexpected TCP send " ERL_ERROR ": %s\n", sockErrBuf ); } @@ -322,7 +322,7 @@ void tcpiiu::recvBytes ( return; } - // if the circuit was locally aborted then supress + // if the circuit was locally aborted then suppress // warning messages about bad file descriptor etc if ( this->state != iiucs_connected && this->state != iiucs_clean_shutdown ) { @@ -358,9 +358,9 @@ void tcpiiu::recvBytes ( epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); - // the replacable printf handler isnt called here - // because it reqires a callback lock which probably - // isnt appropriate here + // the replaceable printf handler isn't called here + // because it requires a callback lock which probably + // isn't appropriate here char name[64]; this->hostNameCacheInstance.getName ( name, sizeof ( name ) ); @@ -535,11 +535,11 @@ void tcpRecvThread::run () } // - // we dont feel comfortable calling this with a lock applied + // we don't feel comfortable calling this with a lock applied // (it might block for longer than we like) // - // we would prefer to improve efficency by trying, first, a - // recv with the new MSG_DONTWAIT flag set, but there isnt + // we would prefer to improve efficiency by trying, first, a + // recv with the new MSG_DONTWAIT flag set, but there isn't // universal support // bool bytesArePending = this->iiu.bytesArePendingInOS (); @@ -957,7 +957,7 @@ void tcpiiu::initiateAbortShutdown ( char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); - errlogPrintf ( "CAC TCP socket linger set error was %s\n", + errlogPrintf ( "CAC TCP socket linger set " ERL_ERROR " was %s\n", sockErrBuf ); } this->discardingPendingData = true; @@ -988,7 +988,7 @@ void tcpiiu::initiateAbortShutdown ( char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); - errlogPrintf ("CAC TCP socket shutdown error was %s\n", + errlogPrintf ("CAC TCP socket shutdown " ERL_ERROR " was %s\n", sockErrBuf ); } } @@ -1002,7 +1002,7 @@ void tcpiiu::initiateAbortShutdown ( }; // - // wake up the send thread if it isnt blocking in send() + // wake up the send thread if it isn't blocking in send() // this->sendThreadFlushEvent.signal (); this->flushBlockEvent.signal (); @@ -1058,7 +1058,7 @@ void tcpiiu::show ( unsigned level ) const this->_receiveThreadIsBusy ); } if ( level > 2u ) { - ::printf ( "\tvirtual circuit socket identifier %d\n", this->sock ); + ::printf ( "\tvirtual circuit socket identifier %d\n", (int)this->sock ); ::printf ( "\tsend thread flush signal:\n" ); this->sendThreadFlushEvent.show ( level-2u ); ::printf ( "\tsend thread:\n" ); @@ -1580,7 +1580,7 @@ void tcpiiu::subscriptionRequest ( maxBytes = MAX_TCP; } unsigned dataType = subscr.getType ( guard ); - // data type bounds checked when sunscription created + // data type bounds checked when subscription created arrayElementCount maxElem = ( maxBytes - dbr_size[dataType] ) / dbr_value_size[dataType]; if ( nElem > maxElem ) { throw cacChannel::msgBodyCacheTooSmall (); @@ -1633,7 +1633,7 @@ void tcpiiu::subscriptionUpdateRequest ( throw cacChannel::msgBodyCacheTooSmall (); } comQueSendMsgMinder minder ( this->sendQue, guard ); - // nElem boounds checked above + // nElem bounds checked above this->sendQue.insertRequestHeader ( CA_PROTO_READ_NOTIFY, 0u, static_cast < ca_uint16_t > ( dataType ), @@ -1712,7 +1712,7 @@ void tcpiiu :: flush ( epicsGuard < epicsMutex > & guard ) this->flushRequest ( guard ); // the process thread is not permitted to flush as this // can result in a push / pull deadlock on the TCP pipe. - // Instead, the process thread scheduals the flush with the + // Instead, the process thread schedules the flush with the // send thread which runs at a higher priority than the // receive thread. The same applies to the UDP thread for // locking hierarchy reasons. @@ -1817,7 +1817,7 @@ void tcpiiu::disconnectAllChannels ( } while ( nciu * pChan = this->createRespPend.get () ) { - // we dont yet know the server's id so we cant + // we don't yet know the server's id so we cant // send a channel delete request and will instead // trust that the server can do the proper cleanup // when the circuit disconnects @@ -1848,7 +1848,7 @@ void tcpiiu::disconnectAllChannels ( while ( nciu * pChan = this->unrespCircuit.get () ) { // if we know that the circuit is unresponsive - // then we dont send a channel delete request and + // then we don't send a channel delete request and // will instead trust that the server can do the // proper cleanup when the circuit disconnects pChan->disconnectAllIO ( cbGuard, guard ); @@ -1883,7 +1883,7 @@ void tcpiiu::unlinkAllChannels ( while ( nciu * pChan = this->createRespPend.get () ) { pChan->channelNode::listMember = channelNode::cs_none; - // we dont yet know the server's id so we cant + // we don't yet know the server's id so we cant // send a channel delete request and will instead // trust that the server can do the proper cleanup // when the circuit disconnects @@ -1921,7 +1921,7 @@ void tcpiiu::unlinkAllChannels ( channelNode::cs_none; pChan->disconnectAllIO ( cbGuard, guard ); // if we know that the circuit is unresponsive - // then we dont send a channel delete request and + // then we don't send a channel delete request and // will instead trust that the server can do the // proper cleanup when the circuit disconnects pChan->serviceShutdownNotify ( cbGuard, guard ); @@ -1951,7 +1951,7 @@ void tcpiiu::installChannel ( this->channelCountTot++; chan.channelNode::listMember = channelNode::cs_createReqPend; chan.searchReplySetUp ( *this, sidIn, typeIn, countIn, guard ); - // The tcp send thread runs at apriority below the udp thread + // The tcp send thread runs at a priority below the udp thread // so that this will not send small packets this->sendThreadFlushEvent.signal (); } @@ -2060,7 +2060,7 @@ bool tcpiiu::bytesArePendingInOS () const } return false; #else - osiSockIoctl_t bytesPending = 0; /* shut up purifys yapping */ + osiSockIoctl_t bytesPending = 0; /* shut up Purify's yapping */ int status = socket_ioctl ( this->sock, FIONREAD, & bytesPending ); if ( status >= 0 ) { diff --git a/modules/ca/src/client/test/ca_test.c b/modules/ca/src/client/test/ca_test.c index 0d1ec2f57..adae20b84 100644 --- a/modules/ca/src/client/test/ca_test.c +++ b/modules/ca/src/client/test/ca_test.c @@ -279,7 +279,7 @@ skip_rest: /* * wait for the operation to complete - * (outstabnding decrements to zero) + * (outstanding decrements to zero) */ while(ntries){ ca_pend_event(1.0); diff --git a/modules/ca/src/client/udpiiu.cpp b/modules/ca/src/client/udpiiu.cpp index 723117a71..c6ffb6def 100644 --- a/modules/ca/src/client/udpiiu.cpp +++ b/modules/ca/src/client/udpiiu.cpp @@ -202,7 +202,7 @@ udpiiu::udpiiu ( char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); - errlogPrintf("CAC: failed to set mcast ttl %d\n", ttl); + errlogPrintf("CAC: failed to set mcast ttl %d\n", (int)ttl); } } #endif @@ -266,7 +266,7 @@ udpiiu::udpiiu ( epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); epicsSocketDestroy ( this->sock ); - errlogPrintf ( "CAC: getsockname () error was \"%s\"\n", sockErrBuf ); + errlogPrintf ( "CAC: getsockname () " ERL_ERROR " was \"%s\"\n", sockErrBuf ); throwWithLocation ( noSocket () ); } if ( tmpAddr.sa.sa_family != AF_INET) { @@ -428,7 +428,7 @@ void udpRecvThread::run () char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); - errlogPrintf ( "CAC: UDP recv error was \"%s\"\n", + errlogPrintf ( "CAC: UDP recv " ERL_ERROR " was \"%s\"\n", sockErrBuf ); } } @@ -536,9 +536,9 @@ void epicsStdCall caRepeaterRegistrationMessage ( if ( status < 0 ) { int errnoCpy = SOCKERRNO; /* - * Different OS return different codes when the repeater isnt running. - * Its ok to supress these messages because I print another warning message - * if we time out registerring with the repeater. + * Different OS return different codes when the repeater isn't running. + * Its ok to suppress these messages because I print another warning message + * if we time out registering with the repeater. * * Linux returns SOCK_ECONNREFUSED * Windows 2000 returns SOCK_ECONNRESET @@ -673,7 +673,7 @@ bool udpiiu :: searchRespAction ( const epicsTime & currentTime ) { /* - * we dont currently know what to do with channel's + * we don't currently know what to do with channel's * found to be at non-IP type addresses */ if ( addr.sa.sa_family != AF_INET ) { @@ -762,7 +762,7 @@ bool udpiiu::beaconAction ( * always set this field to INADDR_ANY * * clients always assume that if this - * field is set to something that isnt INADDR_ANY + * field is set to something that isn't INADDR_ANY * then it is the overriding IP address of the server. */ ina.sin_family = AF_INET; @@ -772,7 +772,7 @@ bool udpiiu::beaconAction ( } else { /* - * old servers dont supply this and the + * old servers don't supply this and the * default port must be assumed */ ina.sin_port = htons ( this->serverPort ); @@ -874,7 +874,7 @@ void udpiiu::postMsg ( size = pCurMsg->m_postsize + sizeof ( *pCurMsg ); /* - * dont allow msg body extending beyond frame boundary + * don't allow msg body extending beyond frame boundary */ if ( size > blockSize ) { char buf[64]; @@ -1044,7 +1044,7 @@ void udpiiu :: SearchRespCallback :: notify ( const osiSockAddr & addr, const epicsTime & currentTime ) { /* - * we dont currently know what to do with channel's + * we don't currently know what to do with channel's * found to be at non-IP type addresses */ if ( addr.sa.sa_family != AF_INET ) { @@ -1119,7 +1119,7 @@ bool udpiiu :: datagramFlush ( { guard.assertIdenticalMutex ( cacMutex ); - // dont send the version header by itself + // don't send the version header by itself if ( this->nBytesInXmitBuf <= sizeof ( caHdr ) ) { return false; } diff --git a/modules/ca/src/perl/Cap5.xs b/modules/ca/src/perl/Cap5.xs index 6d06a15f1..5da894808 100644 --- a/modules/ca/src/perl/Cap5.xs +++ b/modules/ca/src/perl/Cap5.xs @@ -12,6 +12,13 @@ * here and just generates unnecessary compiler warnings. */ #define REENTRINC +/* Clang-12 and later generates many warnings about compound token */ +#ifdef __has_warning +# if __has_warning("-Wcompound-token-split-by-macro") +# pragma clang diagnostic ignored "-Wcompound-token-split-by-macro" +# endif +#endif + #include "EXTERN.h" #include "perl.h" #include "XSUB.h" @@ -201,6 +208,8 @@ SV * newSVdbr(struct event_handler_args *peha) { if (is_primitive) { if (value_type == DBR_CHAR) { /* Long string => Perl scalar */ + if (peha->count == 0) + return newSVpvn(peha->dbr, 0); ((char *)peha->dbr) [peha->count - 1] = 0; return newSVpv(peha->dbr, 0); } @@ -271,8 +280,12 @@ SV * newSVdbr(struct event_handler_args *peha) { char *str = dbr_value_ptr(peha->dbr, peha->type); /* Long string => Perl scalar */ - str[peha->count - 1] = 0; - val = newSVpv(str, 0); + if (peha->count == 0) + val = newSVpvn(str, 0); + else { + str[peha->count - 1] = 0; + val = newSVpv(str, 0); + } } else if (peha->count == 1) { /* Single value => Perl scalar */ val = newSVdbf(value_type, diff --git a/modules/ca/src/perl/capr.pl b/modules/ca/src/perl/capr.pl index ba331cf8e..fdb85dc7f 100644 --- a/modules/ca/src/perl/capr.pl +++ b/modules/ca/src/perl/capr.pl @@ -1,6 +1,6 @@ #!/usr/bin/env perl #************************************************************************* -# Copyright (c) 2005 UChicago Argonne LLC, as Operator of Argonne +# Copyright (c) 2022 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # SPDX-License-Identifier: EPICS # EPICS BASE is distributed subject to a Software License Agreement found @@ -14,26 +14,24 @@ # ####################################################################### +use 5.10.1; use strict; -use FindBin qw($Bin); -use lib ("$Bin/../../lib/perl"); +use FindBin qw($RealBin); +use lib ("$RealBin/../../lib/perl"); use Getopt::Std; -use EPICS::Path; +use Cwd 'abs_path'; use CA; ######### Globals ########## -our ($opt_h, $opt_f, $opt_r); -our $opt_d = $ENV{EPICS_CAPR_DBD_FILE} || "$Bin/../../dbd/softIoc.dbd"; -our $opt_w = 1; +our ($opt_h, $opt_f, $opt_l, $opt_r, $opt_D); +our $opt_d = $ENV{EPICS_CAPR_DBD_FILE} // abs_path("$RealBin/../../dbd/softIoc.dbd"); +our $opt_w = 2; +our $opt_n = 10; my %record = (); # Empty hash to put dbd data in -my $iIdx = 0; # Array indexes for interest, data type and base -my $tIdx = 1; -my $bIdx = 2; -my %device = (); # Empty hash to record which rec types have device support # EPICS field types my %fieldType = ( @@ -58,13 +56,14 @@ my %fieldType = ( ); # globals for sub caget +my %connected; +my $unconnected_count; my %callback_data; -my %timed_out; my $callback_incomplete; ######### Main program ############ -HELP_MESSAGE() unless getopts('hd:f:rw:'); +HELP_MESSAGE() unless getopts('hDd:f:l:n:rw:'); HELP_MESSAGE() if $opt_h; die "File $opt_d not found. (\"capr.pl -h\" gives help)\n" @@ -76,7 +75,7 @@ print "Using $opt_d\n\n"; # Print a list of record types if ($opt_r) { print ("Record types found:\n"); - printList(0); + printList($opt_l); exit; } @@ -100,7 +99,7 @@ if (@ARGV) { } else { # Drop any ".FIELD" part s/\. \w+ $//x; - printRecord($_, 0); + printRecord($_, $opt_l); } } @@ -130,9 +129,11 @@ sub parseDbd { my $thisRecord; my $thisField; my $thisType; + my $thisSize = 0; my $field = {}; my $interest = 0; my $thisBase = 'DECIMAL'; + my $special = ''; while (@dbd) { $_ = shift @dbd; @@ -149,35 +150,47 @@ sub parseDbd { $thisField = $1; $thisType = $2; $isAfield = 1; + $thisSize = 1024 if $thisType =~ m/^ DBF_(IN|OUT|FWD)LINK $/x; } elsif ( m/interest \s* \( \s* (\w+) \s* \)/x ) { die "File format error at line $i of file\n $opt_d\n" unless $level == 2 && $isAfield; $interest = $1; } + elsif ( m/size \s* \( \s* ([0-9]+) \s* \)/x ) { + die "File format error at line $i of file\n $opt_d\n" + unless $level == 2 && $isAfield; + $thisSize = $1; + } + elsif ( m/special \s* \( \s* (\w+) \s* \)/x ) { + die "File format error at line $i of file\n $opt_d\n" + unless $level == 2 && $isAfield; + $special = $1; + } elsif ( m/base \s* \( \s* (\w+) \s* \)/x ) { die "File format error at line $i of file\n $opt_d\n" unless $level == 2 && $isAfield; $thisBase = $1; } - elsif ( m/device \s* \( (\w+) \s* ,/x ) { - die "File format error at line $i of file\n $opt_d\n" - unless $level == 0; - $device{$1}++; - } if ( m/\{/ ) { $level++; } if ( m/\}/ ) { if ($level == 2 && $isAfield) { - my $params = []; - $params->[$iIdx] = $interest; - $params->[$tIdx] = $thisType; - $params->[$bIdx] = $thisBase; + my $params = {}; + $params->{interest} = $interest; + $params->{dbfType} = $thisType; + $params->{base} = $thisBase; + $params->{special} = $special; + $params->{size} = $thisSize; $field->{$thisField} = $params; + + # Reset default values $isAfield = 0; - $interest = 0; # reset default - $thisBase = 'DECIMAL'; # reset default + $interest = 0; + $thisBase = 'DECIMAL'; + $special = ''; + $thisSize = 0; } elsif ($level == 1 && $isArecord) { $isArecord = 0; @@ -207,21 +220,23 @@ sub getRecType { # Given the record type and field, returns the interest level, data type # and number base for the field -# Usage: ($dataType, $interest, $base) = getFieldParams($recType, $field); +# Usage: ($dataType, $interest, $base, $special, $size) = getFieldParams($recType, $field); sub getFieldParams { my ($recType, $field) = @_; my $params = $record{$recType}{$field} or die "Can't find params for $recType.$field"; - exists($fieldType{$params->[$tIdx]}) || + exists($fieldType{$params->{dbfType}}) || die "Field data type $field for $recType not found in dbd file --"; - exists($params->[$iIdx]) || + exists($params->{interest}) || die "Interest level for $field in $recType not found in dbd file --"; - my $fType = $fieldType{$params->[$tIdx]}; - my $fInterest = $params->[$iIdx]; - my $fBase = $params->[$bIdx]; - return ($fType, $fInterest, $fBase); + my $fType = $fieldType{$params->{dbfType}}; + my $fInterest = $params->{interest}; + my $fBase = $params->{base}; + my $fSpecial = $params->{special}; + my $fSize = $params->{size}; + return ($fType, $fInterest, $fBase, $fSpecial, $fSize); } # Prints field name and data for given field. Formats output so @@ -233,27 +248,37 @@ sub printField { my $screenWidth = 80; my ($outStr, $wide); - my $field = "$fieldName:"; - if ( $dataType eq 'DBF_STRING' ) { - $outStr = sprintf('%-5s %s', $field, $fieldData); - } elsif ( $base eq 'HEX' ) { - my $val = ( $dataType eq 'DBF_CHAR' ) ? ord($fieldData) : $fieldData; - $outStr = sprintf('%-5s 0x%x', $field, $val); - } elsif ( $dataType eq 'DBF_DOUBLE' || $dataType eq 'DBF_FLOAT' ) { - $outStr = sprintf('%-5s %.8f', $field, $fieldData); - } elsif ( $dataType eq 'DBF_CHAR' ) { - $outStr = sprintf('%-5s %d', $field, ord($fieldData)); - } else { - # DBF_INT64, DBF_LONG, DBF_SHORT, - # DBF_UINT64, DBF_ULONG, DBF_USHORT, DBF_UCHAR, - $outStr = sprintf('%-5s %d', $field, $fieldData); + if (ref $fieldData eq 'ARRAY') { + my $elems = scalar @{$fieldData}; + my $field = "$fieldName\[$elems\]:"; + my $count = $elems > $opt_n ? $opt_n : $elems; + my @show = @{$fieldData}[0 .. $count - 1]; + $outStr = sprintf('%-5s %s', $field, join(', ', @show)); + $outStr .= ", ..." if $elems > $count; + } + else { + my $field = "$fieldName:"; + if ( $dataType eq 'DBF_STRING' ) { + $outStr = sprintf('%-5s %s', $field, $fieldData); + } elsif ( $base eq 'HEX' ) { + my $val = ( $dataType eq 'DBF_CHAR' ) ? ord($fieldData) : $fieldData; + $outStr = sprintf('%-5s 0x%x', $field, $val); + } elsif ( $dataType eq 'DBF_DOUBLE' || $dataType eq 'DBF_FLOAT' ) { + $outStr = sprintf('%-5s %.8f', $field, $fieldData); + } elsif ( $dataType eq 'DBF_UCHAR' ) { + $outStr = sprintf('%-5s %d', $field, ord($fieldData)); + } else { + # DBF_INT64, DBF_LONG, DBF_SHORT, + # DBF_UINT64, DBF_ULONG, DBF_USHORT, DBF_UCHAR, + $outStr = sprintf('%-5s %d', $field, $fieldData); + } } my $len = length($outStr); - if ($len <= 20) { $wide = 20; } - elsif ( $len <= 40 ) { $wide = 40; } - elsif ( $len <= 60 ) { $wide = 60; } + if ($len < 20) { $wide = 20; } + elsif ( $len < 40 ) { $wide = 40; } + elsif ( $len < 60 ) { $wide = 60; } else { $wide = 80;} my $pad = $wide - $len; @@ -269,49 +294,64 @@ sub printField { return $col; } -# Query for a list of fields simultaneously. -# The results are filled in the the %callback_data global hash -# and the result of the operation is the number of read pvs +# Query the native values of a list of PVs simultaneously. +# The data is returned in the the %callback_data global hash. +# The return value is the number of read pvs # # NOTE: Not re-entrant because results are written to global hash # %callback_data # # Usage: $fields_read = caget( @pvlist ) sub caget { - my @chans = map { CA->new($_); } @_; - #clear any previous results; + %connected = (); + $unconnected_count = scalar @_; %callback_data = (); - %timed_out = (); + $callback_incomplete = 0; - eval { CA->pend_io($opt_w); }; - if ($@) { - if ($@ =~ m/^ECA_TIMEOUT/) { - my $name = $chans[0]->name; - my $err = (@chans > 1) ? 'some fields' : "'$name'"; - print "Channel connect timed out: $err not found.\n"; - foreach my $chan (@chans) { - $timed_out{$chan->name} = !$chan->is_connected; - } - @chans = grep { $_->is_connected } @chans; - } else { - die $@; - } - } + my @chans = map { + print " Creating channel for $_\n" if $opt_D; + CA->new($_, \&canew_callback); + } @_; + my $channel_count = scalar @chans; + return 0 unless $channel_count gt 0; - map { - $_->get_callback(\&caget_callback, $_->field_type); - } @chans; + print " $channel_count channels created.\n" if $opt_D; - my $fields_read = $callback_incomplete = @chans; - CA->pend_event(0.1) - while $callback_incomplete; - return $fields_read; + my $elapsed = 0; + do { + print " Waiting for $unconnected_count channels to connect\n" + if $unconnected_count && $opt_D; + print " Waiting for data from $callback_incomplete channels\n" + if $callback_incomplete && $opt_D; + CA->pend_event(0.1); + $elapsed += 0.1; + } until (($elapsed > $opt_w) or + (scalar %connected && $callback_incomplete == 0)); + my $data_count = scalar keys %callback_data; + printf " Got data from %d of %d channels\n", $data_count, $channel_count + if $opt_D; + return $data_count; +} + +sub canew_callback { + my ($chan, $up) = @_; + return unless $up; + $connected{$chan->name} = $chan; + $unconnected_count--; + my $ftype = $chan->field_type; + my $count = $chan->element_count; + $ftype = 'DBR_LONG' if $ftype eq 'DBR_CHAR' && $count == 1; + print " Getting ${\$chan->name} as $ftype\n" if $opt_D; + # We have to fetch all elements so we can show how many there are + $chan->get_callback(\&caget_callback, $ftype); + $callback_incomplete++; } sub caget_callback { my ($chan, $status, $data) = @_; die $status if $status; + print " Got ${\$chan->name} = '$data'\n" if $opt_D; $callback_data{$chan->name} = $data; $callback_incomplete--; } @@ -333,69 +373,83 @@ sub printRecord { my @ftypes = (); #types, from parser my @bases = (); #bases, from parser foreach my $field (sort keys %{$record{$recType}}) { - # Skip DTYP field if this rec type doesn't have device support defined - next if $field eq 'DTYP' && !exists($device{$recType}); - - my ($fType, $fInterest, $base) = getFieldParams($recType, $field); - # FIXME: Support waveform.VAL fields etc. - unless( $fType eq 'DBF_NOACCESS' ) { - if ($interest >= $fInterest ) { - my $fToGet = "$name.$field"; - push @fields_pr, $field; - push @readlist, $fToGet; - push @ftypes, $fType; - push @bases, $base; - } + my ($fType, $fInterest, $base, $special, $size) = + getFieldParams($recType, $field); + next if $fInterest > $interest; + my $fToGet = "$name.$field"; + if ($fType eq 'DBF_NOACCESS') { + next unless $special eq 'SPC_DBADDR'; + $fType = 'DBF_STRING'; } + elsif ($fType eq 'DBF_STRING' && $size >= 40) { + $fToGet .= '$'; + } + push @fields_pr, $field; + push @readlist, $fToGet; + push @ftypes, $fType; + push @bases, $base; } my $fields_read = caget( @readlist ); + my @missing; + # print while iterating over lists gathered my $col = 0; for (my $i=0; $i < scalar @readlist; $i++) { my $field = $fields_pr[$i]; my $fToGet = $readlist[$i]; my ($fType, $data, $base); - next if $timed_out{$fToGet}; + push @missing, $field unless exists $callback_data{$fToGet}; + next unless exists $callback_data{$fToGet}; $fType = $ftypes[$i]; $base = $bases[$i]; $data = $callback_data{$fToGet}; $col = printField($field, $data, $fType, $base, $col); } print("\n"); # Final newline + + printf "\nUnreadable fields: %s\n", join(', ', @missing) if @missing && $opt_D; } # Prints list of record types found in dbd file. If level > 0 -# then the fields of that record type, their interest levels and types are -# also printed. -# Diagnostic routine, usage: void printList(level); +# then uses printRecordList to display all fields in each record type. sub printList { my $level = shift; foreach my $rkey (sort keys(%record)) { - print(" $rkey\n"); if ($level > 0) { - foreach my $fkey (keys %{$record{$rkey}}) { - print("\tField $fkey - interest $record{$rkey}{$fkey}[$iIdx] "); - print("- type $record{$rkey}{$fkey}[$tIdx] "); - print("- base $record{$rkey}{$fkey}[$bIdx]\n"); - } + printRecordList($rkey); + } + else { + print(" $rkey\n"); } } } -# Prints list of fields with interest levels for given record type -# Diagnostic routine, usage: void printRecordList("recordType"); +# Prints list of fields and metadata for given record type sub printRecordList { my $type = shift; if (exists($record{$type}) ) { print("Record type - $type\n"); foreach my $fkey (sort keys %{$record{$type}}) { - printf('%-8s', $fkey); - printf(" interest = $record{$type}{$fkey}[$iIdx]"); - printf(" type = %-12s ",$record{$type}{$fkey}[$tIdx]); - print (" base = $record{$type}{$fkey}[$bIdx]\n"); + my $param = $record{$type}{$fkey}; + my $dbfType = $param->{dbfType}; + printf " %-8s", $fkey; + printf " interest = %d", $param->{interest}; + printf " type = %-12s", $dbfType; + if ($dbfType eq 'DBF_STRING') { + printf " size = %s\n", $param->{size}; + } + elsif ($dbfType =~ m/DBF_U?(CHAR|SHORT|INT|LONG|INT64)/) { + printf " base = %s\n", $param->{base}; + } + elsif ($dbfType eq 'DBF_NOACCESS') { + printf " special = %s\n", $param->{special}; + } + else { + print "\n"; + } } } else { @@ -411,16 +465,20 @@ sub HELP_MESSAGE { " capr.pl [options] []\n", "\n", " -h Print this help message.\n", + " -D Print debug messages.\n", "Channel Access options:\n", " -w : Wait time, specifies CA timeout, default is $opt_w second\n", "Database Definitions:\n", " -d : The file containing record type definitions.\n", " This can be set using the EPICS_CAPR_DBD_FILE environment variable.\n", - " Default: ", AbsPath($opt_d), "\n", + " Default: ", abs_path($opt_d), "\n", "Output Options:\n", " -r Lists all record types in the selected dbd file.\n", - " -f : Lists all fields with their interest level, data type\n", - " and number base for the given record_type.\n", + " -f : Lists all fields with interest level, data type\n", + " and other information for the given record_type.\n", + " -l : interest level\n", + " -n : Maximum number of array elements to display\n", + " Default: $opt_n\n", "\n", "Base version: ", CA->version, "\n"; exit 1; diff --git a/modules/ca/src/tools/caget.c b/modules/ca/src/tools/caget.c index 0aa655ee5..e60f61922 100644 --- a/modules/ca/src/tools/caget.c +++ b/modules/ca/src/tools/caget.c @@ -256,7 +256,7 @@ static int caget (pv *pvs, int nPvs, RequestT request, OutputT format, for (n = 0; n < nPvs; n++) { switch (format) { - case plain: /* Emulate old caget behaviour */ + case plain: /* Emulate old caget behavior */ if (pvs[n].nElems <= 1 && fieldSeparator == ' ') printf("%-30s", pvs[n].name); else printf("%s", pvs[n].name); printf("%c", fieldSeparator); diff --git a/modules/ca/src/tools/caput.c b/modules/ca/src/tools/caput.c index 62def624e..05247643a 100644 --- a/modules/ca/src/tools/caput.c +++ b/modules/ca/src/tools/caput.c @@ -192,7 +192,7 @@ int caget (pv *pvs, int nPvs, OutputT format, for (n = 0; n < nPvs; n++) { switch (format) { - case plain: /* Emulate old caput behaviour */ + case plain: /* Emulate old caput behavior */ if (pvs[n].reqElems <= 1 && fieldSeparator == ' ') printf("%-30s", pvs[n].name); else printf("%s", pvs[n].name); printf("%c", fieldSeparator); diff --git a/modules/database/src/ioc/as/asCa.c b/modules/database/src/ioc/as/asCa.c index d2e9f7591..a250a0f7a 100644 --- a/modules/database/src/ioc/as/asCa.c +++ b/modules/database/src/ioc/as/asCa.c @@ -241,7 +241,7 @@ void asCaStart(void) epicsThreadGetStackSize(epicsThreadStackBig), (EPICSTHREADFUNC)asCaTask,0); if(threadid==0) { - errMessage(0,"asCaStart: taskSpawn Failure\n"); + errMessage(0,"asCaStart: taskSpawn Failure"); } } epicsMutexMustLock(asCaTaskLock); diff --git a/modules/database/src/ioc/as/asIocRegister.c b/modules/database/src/ioc/as/asIocRegister.c index f4426786a..f97917d8e 100644 --- a/modules/database/src/ioc/as/asIocRegister.c +++ b/modules/database/src/ioc/as/asIocRegister.c @@ -16,7 +16,7 @@ #include "asIocRegister.h" /* asSetFilename */ -static const iocshArg asSetFilenameArg0 = { "ascf",iocshArgString}; +static const iocshArg asSetFilenameArg0 = { "ascf",iocshArgStringPath}; static const iocshArg * const asSetFilenameArgs[] = {&asSetFilenameArg0}; static const iocshFuncDef asSetFilenameFuncDef = {"asSetFilename",1,asSetFilenameArgs, @@ -97,7 +97,7 @@ static void aspmemCallFunc(const iocshArgBuf *args) } /* astac */ -static const iocshArg astacArg0 = { "recordname",iocshArgString}; +static const iocshArg astacArg0 = { "recordname",iocshArgStringRecord}; static const iocshArg astacArg1 = { "user",iocshArgString}; static const iocshArg astacArg2 = { "host",iocshArgString}; static const iocshArg * const astacArgs[] = {&astacArg0,&astacArg1,&astacArg2}; diff --git a/modules/database/src/ioc/bpt/RULES b/modules/database/src/ioc/bpt/RULES index cf6232bc6..7239efd02 100644 --- a/modules/database/src/ioc/bpt/RULES +++ b/modules/database/src/ioc/bpt/RULES @@ -11,5 +11,6 @@ # This is a Makefile fragment, see src/ioc/Makefile. -$(patsubst %,$(COMMON_DIR)/%,$(BPT_DBD)) : \ - $(COMMON_DIR)/bpt%.dbd : $(MAKEBPT) +$(addprefix $(COMMON_DIR)/,$(BPT_DBD)) : $(COMMON_DIR)/bpt%.dbd : \ + $(EPICS_BASE_HOST_BIN)/makeBpt$(HOSTEXE) +# Don't try to use $(MAKEBPT) above diff --git a/modules/database/src/ioc/db/RULES b/modules/database/src/ioc/db/RULES index c7dbfe8ef..85fa44a64 100644 --- a/modules/database/src/ioc/db/RULES +++ b/modules/database/src/ioc/db/RULES @@ -13,13 +13,15 @@ THESE_RULES := $(IOCDIR)/db/RULES -dbCommon.h$(DEP): $(COMMON_DIR)/dbCommonRecord.dbd $(THESE_RULES) +dbCommon.h$(DEP): $(COMMON_DIR)/dbCommonRecord.dbd $(THESE_RULES) \ + $(DBDTORECTYPEH_dep) @$(RM) $@ @$(DBTORECORDTYPEH) -D -I ../db -I $(COMMON_DIR) -o $(COMMONDEP_TARGET) $< > $@ $(COMMON_DIR)/dbCommonRecord.html: ../db/dbCommon.dbd.pod -$(COMMON_DIR)/dbCommon.h: $(COMMON_DIR)/dbCommonRecord.dbd $(THESE_RULES) +$(COMMON_DIR)/dbCommon.h: $(COMMON_DIR)/dbCommonRecord.dbd $(THESE_RULES) \ + $(DBDTORECTYPEH_dep) @$(RM) $(notdir $@) $(DBTORECORDTYPEH) -I ../db -I $(COMMON_DIR) -o $(notdir $@) $< @$(MV) $(notdir $@) $@ diff --git a/modules/database/src/ioc/db/callback.c b/modules/database/src/ioc/db/callback.c index 4a26e27ac..d58b8fc6f 100644 --- a/modules/database/src/ioc/db/callback.c +++ b/modules/database/src/ioc/db/callback.c @@ -85,7 +85,7 @@ static epicsEventId startStopEvent; static char *threadNamePrefix[NUM_CALLBACK_PRIORITIES] = { "cbLow", "cbMedium", "cbHigh" }; -#define FULL_MSG(name) "callbackRequest: " name " ring buffer full\n" +#define FULL_MSG(name) "callbackRequest: " ERL_ERROR " " name " ring buffer full\n" static char *fullMessage[NUM_CALLBACK_PRIORITIES] = { FULL_MSG("cbLow"), FULL_MSG("cbMedium"), FULL_MSG("cbHigh") }; @@ -326,17 +326,17 @@ int callbackRequest(epicsCallback *pcallback) cbQueueSet *mySet; if (!pcallback) { - epicsInterruptContextMessage("callbackRequest: pcallback was NULL\n"); + epicsInterruptContextMessage("callbackRequest: " ERL_ERROR " pcallback was NULL\n"); return S_db_notInit; } priority = pcallback->priority; if (priority < 0 || priority >= NUM_CALLBACK_PRIORITIES) { - epicsInterruptContextMessage("callbackRequest: Bad priority\n"); + epicsInterruptContextMessage("callbackRequest: " ERL_ERROR " Bad priority\n"); return S_db_badChoice; } mySet = &callbackQueue[priority]; if (!mySet->queue) { - epicsInterruptContextMessage("callbackRequest: Callbacks not initialized\n"); + epicsInterruptContextMessage("callbackRequest: " ERL_ERROR " Callbacks not initialized\n"); return S_db_notInit; } if (mySet->queueOverflow) return S_db_bufFull; diff --git a/modules/database/src/ioc/db/dbAccess.c b/modules/database/src/ioc/db/dbAccess.c index 8aac52f00..8fcdab8a7 100644 --- a/modules/database/src/ioc/db/dbAccess.c +++ b/modules/database/src/ioc/db/dbAccess.c @@ -529,7 +529,7 @@ long dbProcess(dbCommon *precord) } } - /* If already active dont process */ + /* If already active don't process */ if (precord->pact) { unsigned short monitor_mask; @@ -931,7 +931,7 @@ long dbGet(DBADDR *paddr, short dbrType, no_elements = capacity = pfl->no_elements; } - /* Update field info from record (if neccessary); + /* Update field info from record (if necessary); * may modify paddr->pfield. */ if (!dbfl_has_copy(pfl) && @@ -1104,9 +1104,9 @@ static long dbPutFieldLink(DBADDR *paddr, if (link_info.ltype == PV_LINK && (link_info.modifiers & (pvlOptCA | pvlOptCP | pvlOptCPP)) == 0) { chan = dbChannelCreate(link_info.target); - if (chan && dbChannelOpen(chan) != 0) { - errlogPrintf("ERROR: dbPutFieldLink %s.%s=%s: dbChannelOpen() failed\n", - precord->name, pfldDes->name, link_info.target); + if (chan && (status = dbChannelOpen(chan)) != 0) { + errlogPrintf(ERL_ERROR ": dbPutFieldLink %s.%s=%s: dbChannelOpen() failed w/ 0x%lx\n", + precord->name, pfldDes->name, link_info.target, status); goto cleanup; } } @@ -1325,7 +1325,6 @@ long dbPut(DBADDR *paddr, short dbrType, void *pfieldsave = paddr->pfield; rset *prset = dbGetRset(paddr); long status = 0; - long offset; dbFldDes *pfldDes; int isValueField; @@ -1349,20 +1348,25 @@ long dbPut(DBADDR *paddr, short dbrType, if (status) return status; } - if (paddr->pfldDes->special == SPC_DBADDR && - prset && prset->get_array_info) { - long dummy; + if (nRequest>1 || paddr->pfldDes->special == SPC_DBADDR) { + long offset = 0; + if (paddr->pfldDes->special == SPC_DBADDR && + prset && prset->get_array_info) { + long dummy; - status = prset->get_array_info(paddr, &dummy, &offset); - /* paddr->pfield may be modified */ - if (status) goto done; + status = prset->get_array_info(paddr, &dummy, &offset); + /* paddr->pfield may be modified */ + if (status) goto done; + } if (no_elements < nRequest) nRequest = no_elements; status = dbPutConvertRoutine[dbrType][field_type](paddr, pbuffer, nRequest, no_elements, offset); /* update array info */ - if (!status && prset->put_array_info) + if (!status && paddr->pfldDes->special == SPC_DBADDR && + prset && prset->put_array_info) { status = prset->put_array_info(paddr, nRequest); + } } else { if (nRequest < 1) { recGblSetSevr(precord, LINK_ALARM, INVALID_ALARM); diff --git a/modules/database/src/ioc/db/dbAccessDefs.h b/modules/database/src/ioc/db/dbAccessDefs.h index 771084170..b26a1a56e 100644 --- a/modules/database/src/ioc/db/dbAccessDefs.h +++ b/modules/database/src/ioc/db/dbAccessDefs.h @@ -73,7 +73,7 @@ DBCORE_API extern int dbAccessDebugPUTF; * options has a bit set for each option that was accepted * number_elements is actual number of elements obtained * - * The individual items can be refered to by the expressions:: + * The individual items can be referred to by the expressions:: * * buffer.status * buffer.severity @@ -220,6 +220,8 @@ DBCORE_API long dbNameToAddr(const char *pname, struct dbAddr *paddr); /** Initialize DBADDR from a dbEntry * Also handles SPC_DBADDR processing. This is really an internal * routine for use by dbNameToAddr() and dbChannelCreate(). + * + * \since 7.0.2.1 */ DBCORE_API long dbEntryToAddr(const struct dbEntry *pdbentry, struct dbAddr *paddr); @@ -227,6 +229,8 @@ DBCORE_API long dbEntryToAddr(const struct dbEntry *pdbentry, /** Initialize DBENTRY from a valid dbAddr* * Constant time equivalent of dbInitEntry() then dbFindRecord(), * and finally dbFollowAlias(). + * + * \since 3.16.1 */ DBCORE_API void dbInitEntryFromAddr(struct dbAddr *paddr, struct dbEntry *pdbentry); @@ -234,6 +238,8 @@ DBCORE_API void dbInitEntryFromAddr(struct dbAddr *paddr, /** Initialize DBENTRY from a valid record (dbCommon*) * Constant time equivalent of dbInitEntry() then dbFindRecord(), * and finally dbFollowAlias() when no field is specified. + * + * \since 3.16.1 */ DBCORE_API void dbInitEntryFromRecord(struct dbCommon *prec, struct dbEntry *pdbentry); diff --git a/modules/database/src/ioc/db/dbBkpt.c b/modules/database/src/ioc/db/dbBkpt.c index a32ebab4e..de6fa3f9b 100644 --- a/modules/database/src/ioc/db/dbBkpt.c +++ b/modules/database/src/ioc/db/dbBkpt.c @@ -97,7 +97,7 @@ static long FIND_CONT_NODE( * processing in that lockset to this task. The separate task is * used so that locksets that do not have breakpoints are isolated * from locksets that do. This allows the processing of other - * locksets to continue uninterupted, even if they exist on the same + * locksets to continue uninterrupted, even if they exist on the same * scan list as a lockset containing a breakpoint. * * An entrypoint is the first record that gets processed in a lockset. @@ -250,7 +250,7 @@ static long FIND_CONT_NODE( } /* - * Initialise the breakpoint stack + * Initialize the breakpoint stack */ void dbBkptInit(void) { diff --git a/modules/database/src/ioc/db/dbCa.c b/modules/database/src/ioc/db/dbCa.c index 70f939897..57cca404f 100644 --- a/modules/database/src/ioc/db/dbCa.c +++ b/modules/database/src/ioc/db/dbCa.c @@ -196,6 +196,73 @@ static void caLinkDec(caLink *pca) if (callback) callback(userPvt); } +struct waitPvt { + caLink *pca; + epicsEventId evt; +}; +enum testEvent { + testEventConnect, + testEventCount, +}; + +static +void testdbCaWaitForEventCB(void *raw) +{ + struct waitPvt *pvt = raw; + + epicsMutexMustLock(pvt->pca->lock); + epicsEventMustTrigger(pvt->evt); + epicsMutexUnlock(pvt->pca->lock); +} + +static +void testdbCaWaitForEvent(DBLINK *plink, unsigned long cnt, enum testEvent event) +{ + caLink *pca; + epicsEventId evt = epicsEventMustCreate(epicsEventEmpty); + + dbScanLock(plink->precord); + + assert(plink->type==CA_LINK); + pca = (caLink *)plink->value.pv_link.pvt; + + epicsMutexMustLock(pca->lock); + assert(!pca->monitor && !pca->connect && !pca->userPvt); + + while(!pca->isConnected || (event==testEventCount && pca->nUpdate < cnt)) { + struct waitPvt pvt = {pca, evt}; + pca->connect = &testdbCaWaitForEventCB; + pca->monitor = &testdbCaWaitForEventCB; + pca->userPvt = &pvt; + + epicsMutexUnlock(pca->lock); + dbScanUnlock(plink->precord); + + epicsEventMustWait(evt); + + dbScanLock(plink->precord); + epicsMutexMustLock(pca->lock); + + pca->connect = NULL; + pca->monitor = NULL; + pca->userPvt = NULL; + } + + epicsEventDestroy(evt); + epicsMutexUnlock(pca->lock); + dbScanUnlock(plink->precord); +} + +void testdbCaWaitForConnect(DBLINK *plink) +{ + testdbCaWaitForEvent(plink, 0, testEventConnect); +} + +void testdbCaWaitForUpdateCount(DBLINK *plink, unsigned long cnt) +{ + testdbCaWaitForEvent(plink, cnt, testEventCount); +} + /* Block until worker thread has processed all previously queued actions. * Does not prevent additional actions from being queued. */ @@ -232,22 +299,6 @@ void dbCaSync(void) epicsEventDestroy(wake); } -DBCORE_API unsigned long dbCaGetUpdateCount(struct link *plink) -{ - caLink *pca = (caLink *)plink->value.pv_link.pvt; - unsigned long ret; - - if (!pca) return (unsigned long)-1; - - epicsMutexMustLock(pca->lock); - - ret = pca->nUpdate; - - epicsMutexUnlock(pca->lock); - - return ret; -} - void dbCaCallbackProcess(void *userPvt) { struct link *plink = (struct link *)userPvt; @@ -785,6 +836,8 @@ static void connectionCallback(struct connection_handler_args arg) caLink *pca; short link_action = 0; struct link *plink; + dbCaCallback connect = 0; + void *userPvt = 0; pca = ca_puser(arg.chid); assert(pca); @@ -851,11 +904,16 @@ static void connectionCallback(struct connection_handler_args arg) } pca->gotAttributes = 0; if (pca->dbrType != DBR_STRING) { + /* will run connect() callback later */ link_action |= CA_GET_ATTRIBUTES; + } else { + connect = pca->connect; + userPvt = pca->userPvt; } done: if (link_action) addAction(pca, link_action); epicsMutexUnlock(pca->lock); + if (connect) connect(userPvt); } static void eventCallback(struct event_handler_args arg) @@ -881,10 +939,10 @@ static void eventCallback(struct event_handler_args arg) if (precord) { if (arg.status != ECA_NORDACCESS && arg.status != ECA_GETFAIL) - errlogPrintf("dbCa: eventCallback record %s error %s\n", + errlogPrintf("dbCa: eventCallback record %s " ERL_ERROR " %s\n", precord->name, ca_message(arg.status)); } else { - errlogPrintf("dbCa: eventCallback error %s\n", + errlogPrintf("dbCa: eventCallback " ERL_ERROR " %s\n", ca_message(arg.status)); } goto done; @@ -1029,10 +1087,10 @@ static void getAttribEventCallback(struct event_handler_args arg) if (arg.status != ECA_NORMAL) { dbCommon *precord = plink->precord; if (precord) { - errlogPrintf("dbCa: getAttribEventCallback record %s error %s\n", + errlogPrintf("dbCa: getAttribEventCallback record %s " ERL_ERROR " %s\n", precord->name, ca_message(arg.status)); } else { - errlogPrintf("dbCa: getAttribEventCallback error %s\n", + errlogPrintf("dbCa: getAttribEventCallback " ERL_ERROR " %s\n", ca_message(arg.status)); } epicsMutexUnlock(pca->lock); @@ -1058,6 +1116,7 @@ static void getAttribEventCallback(struct event_handler_args arg) static void dbCaTask(void *arg) { + epicsEventId requestSync = NULL; taskwdInsert(0, NULL, NULL); SEVCHK(ca_context_create(ca_enable_preemptive_callback), "dbCaTask calling ca_context_create"); @@ -1078,13 +1137,20 @@ static void dbCaTask(void *arg) epicsMutexMustLock(workListLock); if (!(pca = (caLink *)ellGet(&workList))){ /* Take off list head */ + if(requestSync) { + /* dbCaSync() requires workListLock to be held here */ + epicsEventMustTrigger(requestSync); + requestSync = NULL; + } epicsMutexUnlock(workListLock); if (dbCaCtl == ctlExit) goto shutdown; break; /* workList is empty */ } link_action = pca->link_action; - if (link_action&CA_SYNC) - epicsEventMustTrigger((epicsEventId)pca->userPvt); /* dbCaSync() requires workListLock to be held here */ + if (link_action&CA_SYNC) { + assert(!requestSync); + requestSync = pca->userPvt; + } pca->link_action = 0; if (link_action & CA_CLEAR_CHANNEL) --removesOutstanding; epicsMutexUnlock(workListLock); /* Give back immediately */ diff --git a/modules/database/src/ioc/db/dbCa.h b/modules/database/src/ioc/db/dbCa.h index 42ce1b338..7b6f9c090 100644 --- a/modules/database/src/ioc/db/dbCa.h +++ b/modules/database/src/ioc/db/dbCa.h @@ -48,8 +48,12 @@ DBCORE_API long dbCaPutLink(struct link *plink,short dbrType, extern struct ca_client_context * dbCaClientContext; #ifdef EPICS_DBCA_PRIVATE_API +/* Wait CA link work queue to become empty. eg. after from dbPut() to OUT */ DBCORE_API void dbCaSync(void); -DBCORE_API unsigned long dbCaGetUpdateCount(struct link *plink); +/* Wait for the data update counter to reach the specified value. */ +DBCORE_API void testdbCaWaitForUpdateCount(DBLINK *plink, unsigned long cnt); +/* Wait for CA link to become connected */ +DBCORE_API void testdbCaWaitForConnect(DBLINK *plink); #endif /* These macros are for backwards compatibility */ diff --git a/modules/database/src/ioc/db/dbChannel.h b/modules/database/src/ioc/db/dbChannel.h index ec86e9e28..23ffd7d09 100644 --- a/modules/database/src/ioc/db/dbChannel.h +++ b/modules/database/src/ioc/db/dbChannel.h @@ -9,14 +9,22 @@ * in file LICENSE that is included with this distribution. \*************************************************************************/ -/* - * Author: Andrew Johnson - * Ralph Lange - */ - #ifndef INC_dbChannel_H #define INC_dbChannel_H +/** \file dbChannel.h + * + * \author Andrew Johnson (ANL) + * \author Ralph Lange (BESSY) + * + * \brief The dbChannel API gives access to record fields. + * + * The dbChannel API is used internally by the IOC and by link types, device + * support and IOC servers (RSRV and QSRV) to access record fields, either + * directly or through one or more server-side filters as specified in the + * channel name used when creating the channel. + */ + #include "dbDefs.h" #include "dbAddr.h" #include "ellLib.h" @@ -30,200 +38,531 @@ extern "C" { #endif -/* +/** * event subscription */ typedef struct evSubscrip { - ELLNODE node; - struct dbChannel *chan; - EVENTFUNC *user_sub; - void *user_arg; - struct event_que *ev_que; - db_field_log **pLastLog; - unsigned long npend; /* n times this event is on the queue */ - unsigned long nreplace; /* n times replacing event on the queue */ - unsigned char select; - char useValque; - char callBackInProgress; - char enabled; + ELLNODE node; + struct dbChannel * chan; + EVENTFUNC * user_sub; + void * user_arg; + struct event_que * ev_que; + db_field_log ** pLastLog; + unsigned long npend; /**< n times this event is on the queue */ + unsigned long nreplace; /**< n times replacing event on the queue */ + unsigned char select; + char useValque; + char callBackInProgress; + char enabled; } evSubscrip; typedef struct chFilter chFilter; -/* A dbChannel points to a record field, and can have multiple filters */ +/** \brief A Database Channel object + * + * A dbChannel is created from a user-supplied channel name, and holds + * pointers to the record & field and information about any filters that + * were specified with it. The dbChannel macros defined in this header + * file should always be used to read data from a dbChannel object, the + * internal implementation may change without notice. + */ typedef struct dbChannel { - const char *name; - dbAddr addr; /* address structure for record/field */ - long final_no_elements; /* final number of elements (arrays) */ - short final_field_size; /* final size of element */ - short final_type; /* final type of database field */ - ELLLIST filters; /* list of filters as created from JSON */ - ELLLIST pre_chain; /* list of filters to be called pre-event-queue */ - ELLLIST post_chain; /* list of filters to be called post-event-queue */ + const char *name; /**< Channel name */ + dbAddr addr; /**< Pointers to record & field */ + long final_no_elements; /**< Final number of array elements */ + short final_field_size; /**< Final size of each element */ + short final_type; /**< Final type of database field */ + ELLLIST filters; /**< Filters used by dbChannel */ + ELLLIST pre_chain; /**< Filters on pre-event-queue chain */ + ELLLIST post_chain; /**< Filters on post-event-queue chain */ } dbChannel; -/* Prototype for the channel event function that is called in filter stacks +/** \brief Event filter function type * - * When invoked the scan lock for the record associated with 'chan' _may_ be locked. - * Unless dbfl_has_copy(pLog), it must call dbScanLock before accessing the data, - * as this indicates the data is still owned by the record. + * Prototype for channel event filter functions. * - * This function has ownership of the field log pLog, if it wishes to discard - * this update it should free the field log with db_delete_field_log() and - * then return NULL. + * When these functions are called the scan lock for the record associated + * with \p chan _may_ already be locked, but they must use dbfl_has_copy() + * to determine whether the data in \p pLog belongs to the record. If that + * returns 0 the function must call dbScanLock() before accessing the data. + * + * A filter function owns the field log \p pLog when called. To discard an + * update it should free the field log using db_delete_field_log() and + * return NULL. */ typedef db_field_log* (chPostEventFunc)(void *pvt, dbChannel *chan, db_field_log *pLog); -/* Return values from chFilterIf->parse_* routines: */ +/** \brief Result returned by chFilterIf parse routines. + * + * The parsing functions from a chFilterIf must return either \p parse_stop + * (in event of an error) or \p parse_continue. + */ typedef enum { parse_stop, parse_continue } parse_result; -/* These routines must be implemented by each filter plug-in */ +/** \brief Channel Filter Interface + * + * Routines to be implemented by each Channel Filter. + */ typedef struct chFilterIf { - /* cleanup pointer passed to dbRegisterFilter(). - * Called during DB shutdown + /** \brief Release private filter data. + * + * Called during database shutdown to release resources allocated by + * the filter. + * \param puser The user-pointer passed into dbRegisterFilter(). */ void (* priv_free)(void *puser); - /* Parsing event handlers: */ - parse_result (* parse_start)(chFilter *filter); - /* If parse_start() returns parse_continue for a filter, one of + + /** \name Parsing event handlers + * + * A filter that doesn't accept a particular JSON value type may use a + * \p NULL pointer to the parsing handler for that value type, which is + * equivalent to a routine that always returns \p parse_stop. + */ + + /** \brief Create new filter instance. + * + * Called when a new filter instance is requested. Filter may allocate + * resources for this instance and store in \p filter->puser. + * If parse_start() returns \p parse_continue for a filter, one of * parse_abort() or parse_end() will later be called for that same * filter. + * \param filter Pointer to instance data. + * \returns \p parse_stop on error, or \p parse_continue + */ + parse_result (* parse_start)(chFilter *filter); + + /** \brief Parsing of filter instance is being cancelled. + * + * This function should release any memory allocated for the given + * \p filter instance; no further parsing handlers will be called for it. + * \param filter Pointer to instance data. */ void (* parse_abort)(chFilter *filter); - /* If parse_abort() is called it should release any memory allocated - * for this filter; no further parse_...() calls will be made; + + /** \brief Parsing of filter instance has completed successfully. + * + * The parser has reached the end of this instance and no further parsing + * handlers will be called for it. The filter must check the instance + * data and indicate whether it was complete or not. + * \param filter Pointer to instance data. + * \returns \p parse_stop on error, or \p parse_continue */ parse_result (* parse_end)(chFilter *filter); - /* If parse_end() returns parse_stop it should have released any - * memory allocated for this filter; no further parse_...() calls will - * be made in this case. + + /** \brief Parser saw \p null value. + * + * Optional. + * Null values are rarely accepted by channel filters. + * \param filter Pointer to instance data. + * \returns \p parse_stop on error, or \p parse_continue */ - parse_result (* parse_null)(chFilter *filter); - parse_result (* parse_boolean)(chFilter *filter, int boolVal); - parse_result (* parse_integer)(chFilter *filter, long integerVal); - parse_result (* parse_double)(chFilter *filter, double doubleVal); - parse_result (* parse_string)(chFilter *filter, const char *stringVal, - size_t stringLen); /* NB: stringVal is not zero-terminated: */ + /** \brief Parser saw boolean value. + * + * Optional. + * \param filter Pointer to instance data. + * \param boolVal true/false Value. + * \returns \p parse_stop on error, or \p parse_continue + */ + parse_result (* parse_boolean)(chFilter *filter, int boolVal); + + /** \brief Parser saw integer value. + * + * Optional. + * \param filter Pointer to instance data. + * \param integerVal Value. + * \returns \p parse_stop on error, or \p parse_continue + */ + parse_result (* parse_integer)(chFilter *filter, long integerVal); + + /** \brief Parser saw double value. + * + * Optional. + * \param filter Pointer to instance data. + * \param doubleVal Value. + * \returns \p parse_stop on error, or \p parse_continue + */ + parse_result (* parse_double)(chFilter *filter, double doubleVal); + + /** \brief Parser saw string value. + * + * Optional. + * \param filter Pointer to instance data. + * \param stringVal Value, not zero-terminated. + * \param stringLen Number of chars in \p stringVal. + * \returns \p parse_stop on error, or \p parse_continue + */ + parse_result (* parse_string)(chFilter *filter, const char *stringVal, + size_t stringLen); + + /** \brief Parser saw start of a JSON map value. + * + * Optional. + * Inside a JSON map all data consists of key/value pairs. + * \param filter Pointer to instance data. + * \returns \p parse_stop on error, or \p parse_continue + */ parse_result (* parse_start_map)(chFilter *filter); + + /** \brief Parser saw a JSON map key. + * + * Optional. + * \param filter Pointer to instance data. + * \param key Value not zero-terminated. + * \param stringLen Number of chars in \p key + * \returns \p parse_stop on error, or \p parse_continue + */ parse_result (* parse_map_key)(chFilter *filter, const char *key, - size_t stringLen); /* NB: key is not zero-terminated: */ + size_t stringLen); + + /** \brief Parser saw end of a JSON map value. + * + * Optional. + * \param filter Pointer to instance data. + * \returns \p parse_stop on error, or \p parse_continue + */ parse_result (* parse_end_map)(chFilter *filter); + /** \brief Parser saw start of a JSON array value. + * + * Optional. + * Data inside a JSON array doesn't have to be all of the same type. + * \param filter Pointer to instance data. + * \returns \p parse_stop on error, or \p parse_continue + */ parse_result (* parse_start_array)(chFilter *filter); + + /** \brief Parser saw end of a JSON array value. + * + * Optional. + * \param filter Pointer to instance data. + * \returns \p parse_stop on error, or \p parse_continue + */ parse_result (* parse_end_array)(chFilter *filter); - /* Channel operations: */ + /** \name Channel operations */ + + /** \brief Open filter on channel. + * + * Optional, initialize instance. + * \param filter Pointer to instance data. + * \returns 0, or an error status value. + */ long (* channel_open)(chFilter *filter); - void (* channel_register_pre) (chFilter *filter, chPostEventFunc **cb_out, void **arg_out, db_field_log *probe); - void (* channel_register_post)(chFilter *filter, chPostEventFunc **cb_out, void **arg_out, db_field_log *probe); - void (* channel_report)(chFilter *filter, int level, const unsigned short indent); + + /** \brief Get pre-chain filter function. + * + * Optional. + * Returns pre-chain filter function and context. + * \param[in] filter Pointer to instance data. + * \param[out] cb_out Write filter function pointer here. + * \param[out] arg_out Write private data pointer here. + * \param[in,out] probe db_field_log with metadata for adjusting. + */ + void (* channel_register_pre) (chFilter *filter, + chPostEventFunc **cb_out, void **arg_out, db_field_log *probe); + + /** \brief Get post-chain filter function. + * + * Optional, return post-chain filter function and context. + * \param[in] filter Pointer to instance data. + * \param[out] cb_out Write filter function pointer here. + * \param[out] arg_out Write private data pointer here. + * \param[in,out] probe db_field_log with metadata for adjusting. + */ + void (* channel_register_post)(chFilter *filter, + chPostEventFunc **cb_out, void **arg_out, db_field_log *probe); + + /** \brief Print information about filter to stdout. + * + * Optional. + * \param filter Pointer to instance data. + * \param level Higher levels may provide more detail. + * \param indent Indent all lines by this many spaces. + */ + void (* channel_report)(chFilter *filter, + int level, const unsigned short indent); + + /** \brief Close filter. + * + * Optional, releases resources allocated for this instance. + * \param filter Pointer to instance data. + */ void (* channel_close)(chFilter *filter); } chFilterIf; -/* A chFilterPlugin holds data for a filter plugin */ +/** \brief Filter plugin data + * + * A chFilterPlugin object holds data about a filter plugin. + */ typedef struct chFilterPlugin { - ELLNODE node; - const char *name; - const chFilterIf *fif; - void *puser; + ELLNODE node; /**< \brief List node (dbBase->filterList) */ + const char *name; /**< \brief Filter name */ + const chFilterIf *fif; /**< \brief Filter interface routines */ + void *puser; /**< \brief For use by the plugin */ } chFilterPlugin; -/* A chFilter holds data for a single filter instance */ +/** \brief Filter instance data + * + * A chFilter holds data about a single filter instance. + */ struct chFilter { - ELLNODE list_node; - ELLNODE pre_node; - ELLNODE post_node; - dbChannel *chan; - const chFilterPlugin *plug; - chPostEventFunc *pre_func; - void *pre_arg; - chPostEventFunc *post_func; - void *post_arg; - void *puser; + ELLNODE list_node; /**< \brief List node (dbChannel->filters) */ + ELLNODE pre_node; /**< \brief List node (dbChannel->pre_chain) */ + ELLNODE post_node; /**< \brief List node (dbChannel->post_chain) */ + dbChannel *chan; /**< \brief The dbChannel we belong to */ + const chFilterPlugin *plug; /**< \brief The plugin that created us */ + chPostEventFunc *pre_func; /**< \brief pre-chain filter function */ + void *pre_arg; /**< \brief pre-chain context pointer */ + chPostEventFunc *post_func; /**< \brief post-chain filter function */ + void *post_arg; /**< \brief post-chain context pointer */ + void *puser; /**< \brief For use by the plugin */ }; struct dbCommon; struct dbFldDes; -DBCORE_API void dbChannelInit (void); +/** \brief Initialize the dbChannel subsystem. */ +DBCORE_API void dbChannelInit(void); + +/** \brief Cleanup the dbChannel subsystem. */ DBCORE_API void dbChannelExit(void); + +/** \brief Test the given PV name for existance. + * + * This routine looks up the given record and field name, but does not check + * whether any field modifiers given after the field name are correct. + * This is sufficient for the correct server to quickly direct searches to the + * IOC that owns that PV name. Field modifiers will be checked when + * dbChannelCreate() is later called with the same name. + * \param name Channel name. + * \returns 0, or an error status value. + */ DBCORE_API long dbChannelTest(const char *name); + +/** \brief Create a dbChannel object for the given PV name. + * + * \param name Channel name. + * \return Pointer to dbChannel object, or NULL if invalid. + */ DBCORE_API dbChannel * dbChannelCreate(const char *name); + +/** \brief Open a dbChannel for doing I/O. + * + * \param chan Pointer to the dbChannel object. + * \returns 0, or an error status value. + */ DBCORE_API long dbChannelOpen(dbChannel *chan); -/*Following is also defined in db_convert.h*/ +/** \brief Request (DBR) type conversion array. + * + * This converter array is declared in db_convert.h but redeclared + * here as it is needed by the dbChannel...CAType macros defined here. + */ DBCORE_API extern unsigned short dbDBRnewToDBRold[]; -/* In the following macros pChan is dbChannel* */ +/** \name dbChannel Inspection Macros */ -/* evaluates to const char* */ +/** \brief Name that defined the channel. + * + * \param pChan Pointer to the dbChannel object. + * \returns const char* + */ #define dbChannelName(pChan) ((pChan)->name) -/* evaluates to struct dbCommon* */ +/** \brief Record the channel connects to. + * + * \param pChan Pointer to the dbChannel object. + * \returns struct dbCommon* + */ #define dbChannelRecord(pChan) ((pChan)->addr.precord) -/* evaluates to struct dbFldDes* */ +/** \brief Field descriptor for the field pointed to. + * + * \param pChan Pointer to the dbChannel object. + * \returns struct dbFldDes* + */ #define dbChannelFldDes(pChan) ((pChan)->addr.pfldDes) -/* evaluates to long */ +/** \brief Number of array elements in the field. + * + * \param pChan Pointer to the dbChannel object. + * \returns long + */ #define dbChannelElements(pChan) ((pChan)->addr.no_elements) -/* evaluates to short */ +/** \brief Data type (DBF type) of the field. + * + * \param pChan Pointer to the dbChannel object. + * \returns short + */ #define dbChannelFieldType(pChan) ((pChan)->addr.field_type) -/* evaluates to short */ +/** \brief Request type (DBR type) of the field. + * + * \param pChan Pointer to the dbChannel object. + * \returns short + */ #define dbChannelExportType(pChan) ((pChan)->addr.dbr_field_type) -/* evaluates to short */ +/** \brief CA data type of the field. + * + * \param pChan Pointer to the dbChannel object. + * \returns short + */ #define dbChannelExportCAType(pChan) (dbDBRnewToDBRold[dbChannelExportType(pChan)]) -/* evaluates to short */ +/** \brief Field (element if array) size in bytes. + * + * \param pChan Pointer to the dbChannel object. + * \returns short + */ #define dbChannelFieldSize(pChan) ((pChan)->addr.field_size) -/* evaluates to long */ +/** \brief Array length after filtering. + * + * \param pChan Pointer to the dbChannel object. + * \returns long + */ #define dbChannelFinalElements(pChan) ((pChan)->final_no_elements) -/* evaluates to short */ +/** \brief Data type after filtering. + * + * \param pChan Pointer to the dbChannel object. + * \returns short + */ #define dbChannelFinalFieldType(pChan) ((pChan)->final_type) -/* evaluates to short */ +/** \brief Channel CA data type after filtering. + * + * \param pChan Pointer to the dbChannel object. + * \returns short + */ #define dbChannelFinalCAType(pChan) (dbDBRnewToDBRold[(pChan)->final_type]) -/* evaluates to short */ +/** \brief Field/element size after filtering, in bytes. + * + * \param pChan Pointer to the dbChannel object. + * \returns short */ #define dbChannelFinalFieldSize(pChan) ((pChan)->final_field_size) -/* evaluates to short */ +/** \brief Field special attribute. + * + * \param pChan Pointer to the dbChannel object. + * \returns short + */ #define dbChannelSpecial(pChan) ((pChan)->addr.special) -/* Channel filters do not get to interpose here since there are many +/** \brief Pointer to the record field. + * + * Channel filters do not get to interpose here since there are many * places where the field pointer is compared with the address of a * specific record field, so they can't modify the pointer value. + * \param pChan Pointer to the dbChannel object. + * \returns void * */ -/* evaluates to void* */ #define dbChannelField(pChan) ((pChan)->addr.pfield) +/** \name dbChannel Operation Functions */ + +/** \brief dbGet() through a dbChannel. + * + * Calls dbGet() for the field that \p chan refers to. + * Only call this routine if the record is already locked. + * \param[in] chan Pointer to the dbChannel object. + * \param[in] type Request type from dbFldTypes.h. + * \param[out] pbuffer Pointer to data buffer. + * \param[in,out] options Request options from dbAccessDefs.h. + * \param[in,out] nRequest Pointer to the element count. + * \param[in] pfl Pointer to a db_field_log or NULL. + * \returns 0, or an error status value. + */ DBCORE_API long dbChannelGet(dbChannel *chan, short type, void *pbuffer, long *options, long *nRequest, void *pfl); + +/** \brief dbGetField() through a dbChannel. + * + * Get values from a PV through a channel. + * This routine locks the record, calls + * dbChannelGet(), then unlocks the record again. + * \param[in] chan Pointer to the dbChannel object. + * \param[in] type Request type from dbFldTypes.h. + * \param[out] pbuffer Pointer to data buffer. + * \param[in,out] options Request options from dbAccessDefs.h. + * \param[in,out] nRequest Pointer to the element count. + * \param[in] pfl Pointer to a db_field_log or NULL. + * \returns 0, or an error status value. + */ DBCORE_API long dbChannelGetField(dbChannel *chan, short type, void *pbuffer, long *options, long *nRequest, void *pfl); + +/** \brief dbPut() through a dbChannel. + * + * Put values to a PV through a channel. Only call this routine if the + * record is already locked. + * Calls dbPut() for the field that \p chan refers to. + * \param chan[in] Pointer to the dbChannel object. + * \param type[in] Request type from dbFldTypes.h. + * \param pbuffer[in] Pointer to data buffer. + * \param nRequest[in] Number of elements in pbuffer. + * \returns 0, or an error status value. + */ DBCORE_API long dbChannelPut(dbChannel *chan, short type, const void *pbuffer, long nRequest); + +/** \brief dbPutField() through a dbChannel. + * + * Put values to a PV through a channel. + * This routine calls dbPutField() for the field that \p chan refers to. + * \param chan[in] Pointer to the dbChannel object. + * \param type[in] Request type from dbFldTypes.h. + * \param pbuffer[in] Pointer to data buffer. + * \param nRequest[in] Number of elements in pbuffer. + * \returns 0, or an error status value. + */ DBCORE_API long dbChannelPutField(dbChannel *chan, short type, const void *pbuffer, long nRequest); + +/** \brief Print report on a channel. + * + * Print information about the channel to stdout. + * \param chan Pointer to the dbChannel object. + * \param level Higher levels may provide more detail. + * \param indent Indent all lines by this many spaces. + */ DBCORE_API void dbChannelShow(dbChannel *chan, int level, const unsigned short indent); + +/** \brief Print report on a channel's filters. + * + * Print information about the channel's filters to stdout. + * \param chan Pointer to the dbChannel object. + * \param level Higher levels may provide more detail. + * \param indent Indent all lines by this many spaces. + */ DBCORE_API void dbChannelFilterShow(dbChannel *chan, int level, const unsigned short indent); + +/** \brief Delete a channel. + * + * Releases resources owned by this channel and its filters. + * \param chan Pointer to the dbChannel object. + */ DBCORE_API void dbChannelDelete(dbChannel *chan); -DBCORE_API void dbRegisterFilter(const char *key, const chFilterIf *fif, void *puser); -DBCORE_API db_field_log* dbChannelRunPreChain(dbChannel *chan, db_field_log *pLogIn); -DBCORE_API db_field_log* dbChannelRunPostChain(dbChannel *chan, db_field_log *pLogIn); + +/** \name Other routines */ + +DBCORE_API void dbRegisterFilter(const char *key, + const chFilterIf *fif, void *puser); +DBCORE_API db_field_log* dbChannelRunPreChain(dbChannel *chan, + db_field_log *pLogIn); +DBCORE_API db_field_log* dbChannelRunPostChain(dbChannel *chan, + db_field_log *pLogIn); DBCORE_API const chFilterPlugin * dbFindFilter(const char *key, size_t len); DBCORE_API void dbChannelGetArrayInfo(dbChannel *chan, void **pfield, long *no_elements, long *offset); diff --git a/modules/database/src/ioc/db/dbCommon.dbd.pod b/modules/database/src/ioc/db/dbCommon.dbd.pod index 5ad4627c5..c8ae7e6ba 100644 --- a/modules/database/src/ioc/db/dbCommon.dbd.pod +++ b/modules/database/src/ioc/db/dbCommon.dbd.pod @@ -77,12 +77,12 @@ A set of periodic scan intervals =back Additional periodic scan rates may be defined for individual IOCs by making a -local copy of menuScan.dbd and adding more choices as required. Scan rates -should normally be defined in order, with the fastest rates appearing first. -Scan periods may now be specified in seconds, minutes, hours or Hertz/Hz, and -plural time units will also be accepted (seconds are used if no unit is -mentioned in the choice string). For example the rates given below are all -valid: +local copy of menuScan.dbd and adding more choices as required. Periodic scan +rates should normally be defined in order following the other scan types, with +the longest periods appearing first. Scan periods can be specified with a unit +string of C/C, C/C, C/C or +C/C. Seconds are used if no unit is included in the choice string. +For example these rates are all valid: 1 hour 0.5 hours @@ -97,7 +97,7 @@ initialization (before the normal scan tasks are started). The B field orders the records within a specific SCAN group. This is not meaningful for passive records. All records of a specified phase are processed -before those with higher phase number. Whenever possible it is better to use +before those with higher phase number. It is generally better practice to use linked passive records to enforce the order of processing rather than a phase number. @@ -109,23 +109,23 @@ The call to post_event is: post_event(short event_number). The B field specifies the scheduling priority for processing records with SCAN=C and asynchronous record completion tasks. -The B field specifies a "disable value". Record processing is -immediately terminated if the value of this field is equal to the value of the -DISA field, i.e. the record is disabled. Note that field values of a record -can be changed by database put or Channel Access, even if a record is +The B field specifies a "disable value". Record processing cannot +begin when the value of this field is equal to the value of the DISA +field, meaning the record is disabled. Note that field values of a record +can be changed by database or Channel Access puts, even if the record is disabled. -The B field contains the value that is compared with DISV to determine -if the record is disabled. The value of the DISA field is obtained via SDIS if -SDIS is a database or channel access link. If SDIS is not a database or -channel access link, then DISA can be set via dbPutField or dbPutLink. - -If the B field of a record is written to, the record is processed. +The B field contains the value that is compared with DISV to determine if +the record is disabled. A value is obtained for the DISA field from the B +link field before the IOC tries to process the record. If SDIS is not set, DISA +may be set by some other method to enable and disable the record. The B field defines the record's "disable severity". If this field is not NO_ALARM and the record is disabled, the record will be put into alarm with this severity and a status of DISABLE_ALARM. +If the B field of a record is written to, the record is processed. + The B field contains the lock set to which this record belongs. All records linked in any way via input, output, or forward database links belong to the same lock set. Lock sets are determined at IOC initialization time, and @@ -135,15 +135,18 @@ The B field counts the number of times dbProcess finds the record active during successive scans, i.e. PACT is TRUE. If dbProcess finds the record active MAX_LOCK times (currently set to 10) it raises a SCAN_ALARM. -The B field is TRUE while the record is being processed. For +The B field is TRUE while the record is active (being processed). For asynchronous records PACT can be TRUE from the time record processing is started until the asynchronous completion occurs. As long as PACT is TRUE, dbProcess will not call the record processing routine. See Application Developers Guide for details on usage of PACT. -The B field is a database link to another record (the "target" record). -Processing a record with a specified FLNK field will force processing of the -target record, provided the target record's SCAN field is set to C. +The B field is a link pointing to another record (the "target" record). +Processing a record with the FLNK field set will trigger processing of the +target record towards the end of processing the first record (but before PACT is +cleared), provided the target record's SCAN field is set to C. If the +FLNK field is a Channel Access link it must point to the PROC field of the +target record. The B field is for internal use by the scanning system. @@ -227,6 +230,8 @@ The B field is for internal use by the scanning system. } field(DISP,DBF_UCHAR) { prompt("Disable putField") + promptgroup("10 - Common") + interest(1) } field(PROC,DBF_UCHAR) { prompt("Force Processing") @@ -236,35 +241,46 @@ The B field is for internal use by the scanning system. =head3 Alarm Fields -These fields indicate the status and severity of alarms, or else determine the +Alarm fields indicate the status and severity of record alarms, or determine how and when alarms are triggered. Of course, many records have alarm-related -fields not common to all records. These fields are listed and explained in the +fields not common to all records. Those fields are listed and explained in the appropriate section on each record. The B field contains the current alarm status. The B field contains the current alarm severity. -These two fields are seen outside database access. The B and B -fields are used by the database access, record support, and device support -routines to set new alarm status and severity values. Whenever any software -component discovers an alarm condition, it uses the following macro function: -recGblSetSevr(precord,new_status,new_severity) This ensures that the current -alarm severity is set equal to the highest outstanding alarm. The file alarm.h -defines all allowed alarm status and severity values. +The B string field may contain more detailed information about the alarm. + +The STAT, SEVR and AMSG fields hold alarm information as seen outside of the +database. The B, B and B fields are used during record +processing by the database access, record support, and device support routines +to set new alarm status and severity values and message text. Whenever any +software component discovers an alarm condition, it calls one of these routines +to register the alarm: + + recGblSetSevr(precord, new_status, new_severity); + recGblSetSevrMsg(precord, new_status, new_severity, "Message", ...); + +These check the current alarm severity and update the NSTA, NSEV and NAMSG +fields if appropriate so they always relate to the highest severity alarm seen +so far during record processing. The file alarm.h defines the allowed alarm +status and severity values. Towards the end of record processing these fields +are copied into the STAT, SEVR and AMSG fields and alarm monitors triggered. The B field contains the highest unacknowledged alarm severity. -The B field specifies if it is necessary to acknowledge transient +The B field specifies whether it is necessary to acknowledge transient alarms. -The B indicates if the record's value is BnBeBined. Typically -this is caused by a failure in device support, the fact that the record has -never been processed, or that the VAL field currently contains a NaN (not a -number). UDF is initialized to TRUE at IOC initialization. Record and device -support routines which write to the VAL field are responsible for setting UDF. +The B indicates if the record's value is BnBeBined. Typically this +is caused by a failure in device support, the fact that the record has never +been processed, or that the VAL field currently contains a NaN (not a number) or +Inf (Infinite) value. UDF defaults to TRUE but can be set in a database file. +Record and device support routines which write to the VAL field are generally +responsible for setting and clearing UDF. -=fields STAT, SEVR, NSTA, NSEV, ACKS, ACKT, UDF +=fields STAT, SEVR, AMSG, NSTA, NSEV, NAMSG, ACKS, ACKT, UDF =cut @@ -422,9 +438,11 @@ The B field is is for private use of the device support modules. =head3 Debugging Fields -The B field is used for trace processing. If this field is non-zero a -message is printed whenever this record is processed, and when any other -record in the same lock-set is processed by a database link from this record. +The B field can be used to trace record processing. When this field is +non-zero and the record is processed, a trace message will be be printed for +this record and any other record in the same lock-set that is triggered by a +database link from this record. The trace message includes the name of the +thread doing the processing, and the name of the record being processed. The B field indicates if there is a breakpoint set at this record. This supports setting a debug breakpoint in the record processing. STEP through @@ -435,32 +453,27 @@ database processing can be supported using this. =head3 Miscellaneous Fields -The B field contains a character string value defining the access -security group for this record. If left empty, the record is placed in group -DEFAULT. +The B string field sets the name of the access security group used for this +record. If left empty, the record is placed in group C. -The B field is a field for private use of the access security system. +The B field is private for use by the access security system. -The B field controls dbPutFields to this record which are normally -issued by channel access. If the field is set to TRUE all dbPutFields -directed to this record are ignored except to the field DISP itself. +The B field can be set to a non-zero value to reject puts from outside of +the IOC (i.e. via Channel Access or PV Access) to any field of the record other +than to the DISP field itself. Field changes and record processing can still be +instigated from inside the IOC using DB links and the IOC scan mechanisms. -The B field specifies the device type for the record. Each record type -has its own set of device support routines which are specified in -devSup.ASCII. If a record type does not have any associated device support, -DTYP and DSET are meaningless. +The B field specifies the device type for the record. Most record types +have their own set of device types which are specified in the IOC's database +definition file. If a record type does not call any device support routines, +the DTYP and DSET fields are not used. -The B field contains the monitor lock. The lock used by the monitor -routines when the monitor list is being used. The list is locked whenever -monitors are being scheduled, invoked, or when monitors are being added to or -removed from the list. This field is accessed only by the dbEvent routines. +The B field contains a mutex which is locked by the monitor routines in +dbEvent.c whenever the monitor list for this record is accessed. -The B field is the head of the list of monitors connected to this +The B field holds a linked list of client monitors connected to this record. Each record support module is responsible for triggering monitors for -any fields that change as a result of record processing. Monitors are present -if mlis count is greater than zero. The call to trigger monitors is: -db_post_event(precord,&data,mask), where "mask" is some combination of -DBE_ALARM, DBE_VALUE, and DBE_LOG. +any fields that change as a result of record processing. The B field contains the address of a putNotify callback. @@ -474,23 +487,44 @@ The B field contains the address of dbRecordType The B field specifies a reprocessing of the record when current processing completes. -The B