Compare commits
134 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
841ef37820 | ||
|
|
f550f278ad | ||
|
|
1706da98cc | ||
|
|
e8be04bc43 | ||
|
|
dee526f2f6 | ||
|
|
59cffd51ae | ||
|
|
9b5034f307 | ||
|
|
e2881fb37e | ||
|
|
afd9697290 | ||
|
|
9988be0906 | ||
|
|
a6812c5869 | ||
|
|
b89494a840 | ||
|
|
771ad6a442 | ||
|
|
d214d81794 | ||
|
|
c70b1a9a29 | ||
|
|
c9c4eea0f8 | ||
|
|
78ce2dac05 | ||
|
|
b2285bb8aa | ||
|
|
59cb5ba6a0 | ||
|
|
c8e5deca76 | ||
|
|
5b09bb6e71 | ||
|
|
ede52d96b8 | ||
|
|
ceccafe823 | ||
|
|
f955199805 | ||
|
|
2af0c10470 | ||
|
|
89f0f1338a | ||
|
|
6ce8dfec01 | ||
|
|
c1ab30142a | ||
|
|
2db2f1a53f | ||
|
|
b811d3402f | ||
|
|
a64cb4a877 | ||
|
|
e75a72ef76 | ||
|
|
33e099a51b | ||
|
|
345cfcffa8 | ||
|
|
655c5bed92 | ||
|
|
2c99c36ef9 | ||
|
|
71d4d3ecbd | ||
|
|
86138fdd23 | ||
|
|
8c993405b4 | ||
|
|
96259b7bdc | ||
|
|
bf8ff4e5f1 | ||
|
|
25576c316a | ||
|
|
2d3de1916b | ||
|
|
fe34c6f35e | ||
|
|
52cdfe0a44 | ||
|
|
16ddcaeaed | ||
|
|
297206e247 | ||
|
|
3110322f8c | ||
|
|
63bf8a8219 | ||
|
|
ec7193d0be | ||
|
|
17dea964a6 | ||
|
|
12283ce25a | ||
|
|
f486dba478 | ||
|
|
1f95d0db12 | ||
|
|
e02c5c3026 | ||
|
|
8e9d75ad71 | ||
|
|
630663caa8 | ||
|
|
7a84a1abba | ||
|
|
a9dbd4dd39 | ||
|
|
ccf34ae3d0 | ||
|
|
f559c36a6f | ||
|
|
8217fa86fe | ||
|
|
f0e118da91 | ||
|
|
5a55aa17d9 | ||
|
|
f79f2cf8fa | ||
|
|
f0f7a1ef06 | ||
|
|
1412abadf9 | ||
|
|
3a0371894b | ||
|
|
e92a9ae426 | ||
|
|
3a5fb898d6 | ||
|
|
2a14647eec | ||
|
|
597393a8ee | ||
|
|
bad8b25e4e | ||
|
|
7fe8373c32 | ||
|
|
72be690fec | ||
|
|
a5b3157ec1 | ||
|
|
9a062cd6a1 | ||
|
|
592b935146 | ||
|
|
638391249d | ||
|
|
cde682f7ba | ||
|
|
89da4130fc | ||
|
|
1d2637a04e | ||
|
|
a2123db9fb | ||
|
|
7f55bb0386 | ||
|
|
511a541f31 | ||
|
|
394c39da51 | ||
|
|
632d1f45c8 | ||
|
|
0f75e0aa7f | ||
|
|
c93ec231a2 | ||
|
|
d1149a0ba9 | ||
|
|
8c3c5a9731 | ||
|
|
95cb81c286 | ||
|
|
44510f2fb2 | ||
|
|
933733465e | ||
|
|
688f32cff0 | ||
|
|
d3feb1e2f9 | ||
|
|
62c11c22c9 | ||
|
|
736075daf6 | ||
|
|
9ef3b77348 | ||
|
|
d15c8093ec | ||
|
|
0211698b69 | ||
|
|
e860617389 | ||
|
|
e918994704 | ||
|
|
5eb7da4595 | ||
|
|
2206934ae2 | ||
|
|
318fc96912 | ||
|
|
a58cc37a5e | ||
|
|
b5e041b991 | ||
|
|
5e1bad2b34 | ||
|
|
0ae50485cf | ||
|
|
a9606dbf6e | ||
|
|
409ee26fae | ||
|
|
9a4787155c | ||
|
|
84dba0d2b7 | ||
|
|
87f6c3dec9 | ||
|
|
940814becf | ||
|
|
68a1a529b2 | ||
|
|
ce38caf41b | ||
|
|
f03f10e664 | ||
|
|
b4f4fb853d | ||
|
|
b518ebe85b | ||
|
|
e461d782f4 | ||
|
|
ef2a381e92 | ||
|
|
265d4962a4 | ||
|
|
db9267bbd5 | ||
|
|
1cf3fa9ba9 | ||
|
|
3e8b0028dc | ||
|
|
d8f18c27f4 | ||
|
|
801c01b9b6 | ||
|
|
0c1874bbfe | ||
|
|
bc7ee94e2c | ||
|
|
3fb10b6d59 | ||
|
|
a8fdf2efeb | ||
|
|
c9eda3ca48 |
@@ -48,6 +48,7 @@ environment:
|
||||
TOOLCHAIN: 2017
|
||||
- TOOLCHAIN: cygwin
|
||||
- TOOLCHAIN: mingw
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
||||
|
||||
# Platform: architecture
|
||||
platform:
|
||||
@@ -60,6 +61,10 @@ matrix:
|
||||
# VS Express installs don't have the 64 bit compiler
|
||||
- platform: x64
|
||||
TOOLCHAIN: 10.0
|
||||
# Cygwin static-debug has compiler problems
|
||||
- configuration: static-debug
|
||||
TOOLCHAIN: cygwin
|
||||
|
||||
|
||||
#---------------------------------#
|
||||
# building & testing #
|
||||
@@ -81,6 +86,9 @@ test_script:
|
||||
|
||||
notifications:
|
||||
|
||||
- provider: Slack
|
||||
incoming_webhook:
|
||||
secure: RYOm3FIUYeZGjWKaeTVKwq+C3fzK54AKwbmAoECED45mex3lN+8HmrC845a6mg9xPUJ/ND51RopWVaKDD9/UzaM0SO195RQLKqUTIUafiuM=
|
||||
- provider: Email
|
||||
to:
|
||||
- core-talk@aps.anl.gov
|
||||
on_build_success: false
|
||||
|
||||
- provider: GitHubPullRequest
|
||||
|
||||
@@ -42,13 +42,13 @@ if "%TOOLCHAIN%"=="mingw" (
|
||||
set "MAKE=mingw32-make"
|
||||
if "%OS%"=="64BIT" (
|
||||
set "EPICS_HOST_ARCH=windows-x64-mingw"
|
||||
set "INCLUDE=C:\tools\mingw64\include;%INCLUDE%"
|
||||
set "PATH=C:\tools\mingw64\bin;%PATH%"
|
||||
set "INCLUDE=C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\include;%INCLUDE%"
|
||||
set "PATH=C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin;%PATH%"
|
||||
echo [INFO] MinGW Toolchain 64bit
|
||||
) else (
|
||||
set "EPICS_HOST_ARCH=win32-x86-mingw"
|
||||
set "INCLUDE=C:\tools\mingw32\include;%INCLUDE%"
|
||||
set "PATH=C:\tools\mingw32\bin;%PATH%"
|
||||
set "INCLUDE=C:\mingw-w64\i686-6.3.0-posix-dwarf-rt_v5-rev1\mingw32\include;%INCLUDE%"
|
||||
set "PATH=C:\mingw-w64\i686-6.3.0-posix-dwarf-rt_v5-rev1\mingw32\bin;%PATH%"
|
||||
echo [INFO] MinGW Toolchain 32bit
|
||||
)
|
||||
echo [INFO] Compiler Version
|
||||
|
||||
@@ -55,16 +55,9 @@ if "%TOOLCHAIN%"=="mingw" (
|
||||
) || (
|
||||
echo [INFO] EPICS set up for optimized build
|
||||
)
|
||||
if "%OS%"=="64BIT" (
|
||||
echo [INFO] Installing MinGW 64bit
|
||||
cinst mingw || cinst mingw
|
||||
) else (
|
||||
echo [INFO] Installing MinGW 32bit
|
||||
cinst mingw --x86 || cinst mingw --x86
|
||||
)
|
||||
)
|
||||
|
||||
echo [INFO] Installing Make 4.1
|
||||
curl -fsS --retry 3 -o C:\tools\make-4.1.zip https://epics.anl.gov/download/tools/make-4.1-win64.zip
|
||||
echo [INFO] Installing Make 4.2.1 from ANL web site
|
||||
curl -fsS --retry 3 -o C:\tools\make-4.2.1.zip https://epics.anl.gov/download/tools/make-4.2.1-win64.zip
|
||||
cd \tools
|
||||
"C:\Program Files\7-Zip\7z" e make-4.1.zip
|
||||
"C:\Program Files\7-Zip\7z" e make-4.2.1.zip
|
||||
|
||||
@@ -16,27 +16,53 @@ PREFIX="$3"
|
||||
if ! [ "$TOPREV" ]
|
||||
then
|
||||
cat <<EOF >&2
|
||||
usage: $0 [rev] [outfile.tar.gz] [prefix/]
|
||||
usage: $0 <rev> [<outfile> [<prefix>]]
|
||||
|
||||
<rev> may be any git revision spec. (tag, branch, or commit id).
|
||||
|
||||
Output file may be .tar.gz, .tar.bz2, or any extension supported by "tar -a".
|
||||
If output file name is omitted, "base-<rev>.tar.gz" will be used.
|
||||
If <prefix> is omitted, the default prefix is "base-<rev>/".
|
||||
If provided, <outfile> must end with ".tar", ".tar.gz" or ".tar.bz2".
|
||||
If <outfile> is omitted, "base-<rev>.tar.gz" will be used.
|
||||
If provided, <prefix> must end with "/". If <prefix> is omitted,
|
||||
the default is "base-<rev>/".
|
||||
EOF
|
||||
exit 1
|
||||
fi
|
||||
|
||||
[ "$FINALTAR" ] || FINALTAR="base-$TOPREV.tar.gz"
|
||||
[ "$PREFIX" ] || PREFIX="base-$TOPREV/"
|
||||
case "$FINALTAR" in
|
||||
"")
|
||||
TAROPT=-z
|
||||
FINALTAR="base-$TOPREV.tar.gz"
|
||||
;;
|
||||
*.tar)
|
||||
TAROPT=""
|
||||
;;
|
||||
*.tar.gz)
|
||||
TAROPT=-z
|
||||
;;
|
||||
*.tar.bz2)
|
||||
TAROPT=-j
|
||||
;;
|
||||
*)
|
||||
die "outfile must end with '.tar.gz' or '.tar.bz2'"
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$PREFIX" in
|
||||
*/) ;;
|
||||
*) die "Prefix must end with '/'";;
|
||||
"")
|
||||
PREFIX="base-$TOPREV/"
|
||||
;;
|
||||
*/)
|
||||
;;
|
||||
*)
|
||||
die "Prefix must end with '/'"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Check for both <tag> and R<tag>
|
||||
if ! [ `git tag -l $TOPREV` ]
|
||||
if [ "$TOPREV" = "HEAD" ]
|
||||
then
|
||||
true
|
||||
elif ! [ `git tag -l $TOPREV` ]
|
||||
then
|
||||
if [ `git tag -l R$TOPREV` ]
|
||||
then
|
||||
@@ -94,13 +120,11 @@ then
|
||||
fi
|
||||
|
||||
# Use the filtered list to build the final tar
|
||||
# The -a option chooses compression automatically based on output file name.
|
||||
|
||||
tar -C "$TDIR"/tar --files-from="$TDIR"/list.2 -caf "$FINALTAR"
|
||||
tar -c $TAROPT -C "$TDIR"/tar -T "$TDIR"/list.2 -f "$FINALTAR"
|
||||
|
||||
echo "Wrote $FINALTAR"
|
||||
|
||||
tar -taf "$FINALTAR" > "$TDIR"/list.3
|
||||
tar -t $TAROPT -f "$FINALTAR" > "$TDIR"/list.3
|
||||
|
||||
# make sure we haven't picked up anything extra
|
||||
if ! diff -u "$TDIR"/list.2 "$TDIR"/list.3
|
||||
|
||||
@@ -30,10 +30,11 @@ endif # BASE_TOP
|
||||
#---------------------------------------------------------------
|
||||
# Where to find the installed build tools
|
||||
# Windows does not like commands with relative paths starting ../
|
||||
# but the Perl scripts in TOP/src/tools are OK
|
||||
# so TOOLS must be an absolute path, although Perl scripts are OK.
|
||||
# FIND_TOOL is for scripts run before the build reaches src/tools.
|
||||
|
||||
TOOLS = $(abspath $(EPICS_BASE_HOST_BIN))
|
||||
FIND_TOOL = $(firstword $(wildcard $(TOOLS)/$(1) $(TOP)/src/tools/$(1)))
|
||||
FIND_TOOL = $(firstword $(wildcard $(TOOLS)/$(1) $(EPICS_BASE)/src/tools/$(1)))
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# EPICS Base build tools and tool flags
|
||||
@@ -54,3 +55,8 @@ INSTALL_LIBRARY = $(INSTALL)
|
||||
# tools for making header dependencies and variable replacement
|
||||
MKMF = $(PERL) $(TOOLS)/mkmf.pl
|
||||
REPLACEVAR = $(PERL) $(TOOLS)/replaceVAR.pl
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# tools for cleaning out unwanted files
|
||||
CVSCLEAN = $(call FIND_TOOL,cvsclean.pl)
|
||||
DEPCLEAN = $(call FIND_TOOL,depclean.pl)
|
||||
|
||||
@@ -48,13 +48,15 @@ EPICS_VERSION = 7
|
||||
EPICS_REVISION = 0
|
||||
|
||||
# EPICS_MODIFICATION must be a number >=0 and <256
|
||||
EPICS_MODIFICATION = 2
|
||||
EPICS_MODIFICATION = 3
|
||||
|
||||
# EPICS_PATCH_LEVEL must be a number (win32 resource file requirement)
|
||||
# Not included if zero
|
||||
# Not included in the official EPICS version number if zero
|
||||
EPICS_PATCH_LEVEL = 0
|
||||
|
||||
# This will end in -DEV between official releases
|
||||
# Between official releases, the EPICS_PATCH_LEVEL gets incremented
|
||||
# and a -DEV suffix is added (similar to the Maven -SNAPSHOT versions)
|
||||
EPICS_DEV_SNAPSHOT=
|
||||
#EPICS_DEV_SNAPSHOT=-DEV
|
||||
#EPICS_DEV_SNAPSHOT=-pre1
|
||||
#EPICS_DEV_SNAPSHOT=-pre1-DEV
|
||||
@@ -64,7 +66,6 @@ EPICS_PATCH_LEVEL = 0
|
||||
#EPICS_DEV_SNAPSHOT=-rc1-DEV
|
||||
#EPICS_DEV_SNAPSHOT=-rc2
|
||||
#EPICS_DEV_SNAPSHOT=-rc2-DEV
|
||||
EPICS_DEV_SNAPSHOT=
|
||||
|
||||
# No changes should be needed below here
|
||||
|
||||
|
||||
@@ -34,14 +34,9 @@
|
||||
# The future dates below assume the rules don't get changed;
|
||||
# see http://www.timeanddate.com/time/dst/2018.html to check.
|
||||
#
|
||||
# DST for 2018 US: Mar 11 - Nov 04
|
||||
# EU: Mar 25 - Oct 28
|
||||
EPICS_TIMEZONE = CUS::360:031102:110402
|
||||
#EPICS_TIMEZONE = MET::-60:032502:102803
|
||||
#
|
||||
# DST for 2019 US: Mar 10 - Nov 03
|
||||
# EU: Mar 31 - Oct 27
|
||||
#EPICS_TIMEZONE = CUS::360:031002:110302
|
||||
EPICS_TIMEZONE = CUS::360:031002:110302
|
||||
#EPICS_TIMEZONE = MET::-60:033102:102703
|
||||
#
|
||||
# DST for 2020 US: Mar 08 - Nov 01
|
||||
@@ -58,6 +53,11 @@ EPICS_TIMEZONE = CUS::360:031102:110402
|
||||
# EU: Mar 27 - Oct 30
|
||||
#EPICS_TIMEZONE = CUS::360:031302:110602
|
||||
#EPICS_TIMEZONE = MET::-60:032702:103003
|
||||
#
|
||||
# DST for 2023 US: Mar 13 - Nov 06
|
||||
# EU: Mar 27 - Oct 30
|
||||
#EPICS_TIMEZONE = CUS::360:031202:110502
|
||||
#EPICS_TIMEZONE = MET::-60:032602:102903
|
||||
|
||||
# EPICS_TS_NTP_INET
|
||||
# NTP time server ip address for VxWorks and RTEMS.
|
||||
|
||||
@@ -202,9 +202,9 @@ endif
|
||||
|
||||
$(TESTPRODNAME) $(PRODNAME): $(PRODUCT_OBJS) $(PROD_RESS) $(PROD_DEPLIBS)
|
||||
|
||||
$(TESTPRODNAME) $(PRODNAME): %$(EXE):
|
||||
$(TESTPRODNAME) $(PRODNAME): %$(EXE): | $(INSTALL_LIB)
|
||||
@$(RM) $@
|
||||
$(DEBUGCMD) $(LINK.cpp)
|
||||
$(LINK.cpp)
|
||||
$(MT_EXE_COMMAND)
|
||||
|
||||
%_ctdt$(OBJ) : %_ctdt.c
|
||||
@@ -305,6 +305,10 @@ $(LOADABLE_SHRLIBNAME): $(LOADABLE_SHRLIB_PREFIX)%$(LOADABLE_SHRLIB_SUFFIX):
|
||||
$(LINK.shrlib)
|
||||
$(MT_DLL_COMMAND)
|
||||
|
||||
$(LIBNAME) $(SHRLIBNAME) $(LOADABLE_SHRLIBNAME): | $(INSTALL_LIB)
|
||||
$(INSTALL_LIB):
|
||||
@$(MKDIR) $@
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# C++ munching for VxWorks
|
||||
|
||||
@@ -467,9 +471,11 @@ $(INSTALL_TCLLIB)/%: ../%
|
||||
@$(INSTALL) -d -m $(BIN_PERMISSIONS) $< $(INSTALL_TCLLIB)
|
||||
endif
|
||||
|
||||
ifneq ($(TCLINDEX),)
|
||||
$(INSTALL_TCLLIB)/$(TCLINDEX): $(INSTALL_TCLLIBS)
|
||||
$(ECHO) "Updating $@"
|
||||
$(ECHO) eval auto_mkindex $(INSTALL_TCLLIB) "$(TCLLIBNAME)" | tclsh
|
||||
endif
|
||||
|
||||
$(INSTALL_LOADABLE_SHRLIBS): $(INSTALL_SHRLIB)/%: %
|
||||
$(ECHO) "Installing loadable shared library $@"
|
||||
@@ -547,6 +553,6 @@ include $(CONFIG)/RULES_EXPAND
|
||||
include $(CONFIG)/RULES_COMMON
|
||||
|
||||
else
|
||||
$(warning RULES_BUILD included more than once. \
|
||||
Use 'make show-makefiles' to work out why.)
|
||||
$(warning Warning: Base configure/RULES_BUILD file included more than once. \
|
||||
Does configure/RELEASE have multiple pointers to $(EPICS_BASE)?)
|
||||
endif # BASE_RULES_BUILD
|
||||
|
||||
@@ -8,16 +8,18 @@
|
||||
# These rules show the set of Makefiles, config files and
|
||||
# rules files loaded by GNUmake.
|
||||
|
||||
# Protect against filenames containing colons (Windows)
|
||||
SAFE_MAKEFILES = $(subst :,__colon__,$(MAKEFILE_LIST))
|
||||
SHOW_MAKEFILES = $(SAFE_MAKEFILES:%=show-makefile.%)
|
||||
show-makefiles: $(SHOW_MAKEFILES)
|
||||
show-makefiles::
|
||||
@echo
|
||||
@echo Makefiles read:
|
||||
define SHOW_MAKEFILE_template
|
||||
show-makefiles::
|
||||
@echo " $(1)"
|
||||
endef
|
||||
$(foreach file,$(MAKEFILE_LIST), \
|
||||
$(eval $(call SHOW_MAKEFILE_template,$(file))))
|
||||
|
||||
# The sort prevents warnings about duplicate targets:
|
||||
$(sort $(SHOW_MAKEFILES)): show-makefile.%:
|
||||
@echo " $(subst __colon__,:,$(@:show-makefile.%=%))"
|
||||
.PHONY: show-makefiles
|
||||
|
||||
.PHONY: show-makefiles show-makefile.%
|
||||
|
||||
# These rules support printing a Makefile variable values.
|
||||
# Many variables are only set inside an O.<arch> build directory.
|
||||
@@ -30,6 +32,17 @@ PRINT.%:
|
||||
.PHONY: PRINT PRINT.%
|
||||
|
||||
|
||||
# Clean rules for recursively deleting editor backup files
|
||||
# and dependency (.d) files from CWD and below.
|
||||
|
||||
cvsclean:
|
||||
$(PERL) $(CVSCLEAN)
|
||||
depclean:
|
||||
$(PERL) $(DEPCLEAN)
|
||||
|
||||
.PHONY: cvsclean depclean
|
||||
|
||||
|
||||
# User specific rules
|
||||
#
|
||||
-include $(HOME)/configure/RULES_USER
|
||||
|
||||
@@ -86,10 +86,11 @@ $(DIRS) $(dirActionTargets) $(dirArchTargets) $(dirActionArchTargets) :
|
||||
$(ARCHS) $(ACTIONS) $(actionArchTargets) :%: \
|
||||
$(foreach dir, $(DIRS), $(dir)$(DIVIDER)%)
|
||||
|
||||
.PHONY : $(DIRS) all host rebuild
|
||||
.PHONY : $(ARCHS) $(ACTIONS)
|
||||
.PHONY : $(dirActionTargets) $(dirArchTargets)
|
||||
.PHONY : $(dirActionArchTargets)
|
||||
.PHONY : $(actionArchTargets)
|
||||
|
||||
.PHONY: $(DIRS) all host rebuild
|
||||
.PHONY: $(ARCHS) $(ACTIONS)
|
||||
.PHONY: $(dirActionTargets) $(dirArchTargets)
|
||||
.PHONY: $(dirActionArchTargets)
|
||||
.PHONY: $(actionArchTargets)
|
||||
|
||||
include $(CONFIG)/RULES_COMMON
|
||||
|
||||
47
configure/RULES_MODULES
Normal file
47
configure/RULES_MODULES
Normal file
@@ -0,0 +1,47 @@
|
||||
#*************************************************************************
|
||||
# EPICS BASE is distributed subject to a Software License Agreement found
|
||||
# in file LICENSE that is included with this distribution.
|
||||
#*************************************************************************
|
||||
|
||||
# Support modules can use these rules to build submodules too.
|
||||
#
|
||||
# The requirements to do so are:
|
||||
# 1. Create a file CONFIG_SITE.local in the same directory as the
|
||||
# Makefile, which defines these variables (the last one is empty):
|
||||
# PARENT_MODULE - The name submodules call their parent
|
||||
# INSTALL_LOCATION := $($(PARENT_MODULE))
|
||||
# CONFIG_INSTALLS =
|
||||
# 2. The Makefile must set TOP and include $(TOP)/configure/CONFIG and
|
||||
# CONFIG_SITE.local
|
||||
# 3. Submodules are added to the SUBMODULES variable in the Makefile
|
||||
# 4. Dependencies between submodules must be set using
|
||||
# <name>_DEPEND_DIRS = <prerequisites>
|
||||
# 5. The Makefile must end by including $(TOP)/configure/RULES_MODULES
|
||||
# 6. Submodules must have a configure/RELEASE file that contains
|
||||
# -include $(TOP)/../RELEASE.$(EPICS_HOST_ARCH).local
|
||||
# 7. Submodules must have a configure/CONFIG_SITE file that contains
|
||||
# -include $(TOP)/../CONFIG_SITE.local
|
||||
|
||||
# Add checked-out submodules to DIRS
|
||||
DIRS += $(subst /Makefile,,$(wildcard $(addsuffix /Makefile, $(SUBMODULES))))
|
||||
|
||||
include $(CONFIG)/RULES_DIRS
|
||||
|
||||
INSTALL_LOCATION_ABS := $(abspath $(INSTALL_LOCATION))
|
||||
RELEASE_LOCAL := RELEASE.$(EPICS_HOST_ARCH).local
|
||||
|
||||
# Ensure that RELEASE.<host>.local exists before doing anything else
|
||||
all host $(DIRS) $(ARCHS) $(ACTIONS) $(dirActionTargets) $(dirArchTargets) \
|
||||
$(dirActionArchTargets) $(actionArchTargets): | $(RELEASE_LOCAL)
|
||||
|
||||
# Convenience target
|
||||
RELEASE.host: $(RELEASE_LOCAL)
|
||||
|
||||
$(RELEASE_LOCAL): Makefile CONFIG_SITE.local
|
||||
$(ECHO) Creating $@ with
|
||||
$(ECHO) " $(PARENT_MODULE) = $(INSTALL_LOCATION_ABS)"
|
||||
@echo $(PARENT_MODULE) = $(INSTALL_LOCATION_ABS)> $@
|
||||
realclean:
|
||||
$(RM) $(wildcard RELEASE.*.local)
|
||||
|
||||
.PHONY: RELEASE.host realclean
|
||||
@@ -9,24 +9,30 @@
|
||||
|
||||
include $(CONFIG)/RULES_DIRS
|
||||
|
||||
# Disable most top rules when installing into a parent's tree, indicated
|
||||
# by PARENT_MODULE being set in the modules/CONFIG_SITE.local file and
|
||||
# INSTALL_LOCATION pointing to the the same place as in the parent.
|
||||
ifeq ($(origin PARENT_MODULE),file)
|
||||
ifeq ($(INSTALL_LOCATION),$($(PARENT_MODULE)))
|
||||
DISABLE_TOP_RULES=YES
|
||||
endif
|
||||
endif
|
||||
|
||||
ifndef DISABLE_TOP_RULES
|
||||
#
|
||||
# Rules for a regular application top directory
|
||||
#
|
||||
|
||||
distclean: realclean cvsclean realuninstall
|
||||
|
||||
CVSCLEAN = $(call FIND_TOOL,cvsclean.pl)
|
||||
cvsclean:
|
||||
$(PERL) $(CVSCLEAN)
|
||||
|
||||
DEPCLEAN = $(call FIND_TOOL,depclean.pl)
|
||||
depclean:
|
||||
$(PERL) $(DEPCLEAN)
|
||||
|
||||
realuninstall: uninstallDirs
|
||||
$(RMDIR) $(INSTALL_LOCATION_BIN)
|
||||
$(RMDIR) $(INSTALL_LOCATION_LIB)
|
||||
|
||||
UNINSTALL_DIRS += $(INSTALL_DBD) $(INSTALL_INCLUDE) $(INSTALL_DOC) \
|
||||
$(INSTALL_HTML) $(INSTALL_TEMPLATES) $(INSTALL_DB) $(DIRECTORY_TARGETS)
|
||||
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)
|
||||
UNINSTALL_DIRS += $(INSTALL_CONFIG)
|
||||
endif
|
||||
uninstallDirs:
|
||||
$(RMDIR) $(UNINSTALL_DIRS)
|
||||
@@ -40,6 +46,8 @@ uninstall$(DIVIDER)%:
|
||||
$(RMDIR) $(INSTALL_LOCATION_BIN)/$(archPart)
|
||||
$(RMDIR) $(INSTALL_LOCATION_LIB)/$(archPart)
|
||||
|
||||
# Remove the bin and lib directories if they have no sub-directories
|
||||
#
|
||||
cleandirs:
|
||||
@$(NOP)
|
||||
ifeq ($(wildcard $(INSTALL_LOCATION_BIN)/*),)
|
||||
@@ -49,6 +57,16 @@ ifeq ($(wildcard $(INSTALL_LOCATION_LIB)/*),)
|
||||
$(RMDIR) $(INSTALL_LOCATION_LIB)
|
||||
endif
|
||||
|
||||
else
|
||||
#
|
||||
# Using a disabled rule aborts
|
||||
#
|
||||
|
||||
cleandirs distclean uninstall realuninstall archuninstall:
|
||||
$(error Target '$@' not available in a submodule)
|
||||
|
||||
endif # DISABLE_TOP_RULES
|
||||
|
||||
|
||||
help:
|
||||
@echo "Usage: gnumake [options] [target] ..."
|
||||
@@ -64,33 +82,39 @@ help:
|
||||
@echo " Cannot be used within an O.<arch> dir"
|
||||
@echo " rebuild - Same as clean install"
|
||||
@echo " archclean - Removes O.<arch> dirs but not O.Common dir"
|
||||
@echo " runtests - Run self-tests, summarize results"
|
||||
@echo " depclean - Removes .d files from all O.<arch> dirs."
|
||||
@echo " cvsclean - Removes backup files etc. from all dirs below"
|
||||
@echo " runtests - Run self-tests, summarize results immediately"
|
||||
@echo " tapfiles - Run self-tests, save to O.<arch>/*.tap files"
|
||||
@echo " test-results - Summarize all O.<arch>/*.tap files"
|
||||
@echo " clean-tests - Removes all O.<arch>/*.tap files"
|
||||
@echo "\"Partial\" build targets supported by Makefiles:"
|
||||
@echo " host - Builds and installs $(EPICS_HOST_ARCH) only."
|
||||
@echo " inc$(DIVIDER)<arch> - Installs <arch> only header files."
|
||||
@echo " build$(DIVIDER)<arch> - Builds and installs <arch> only."
|
||||
@echo " install$(DIVIDER)<arch> - Builds and installs <arch> only."
|
||||
@echo " clean$(DIVIDER)<arch> - Cleans <arch> binaries in O.<arch> dirs only."
|
||||
@echo " uninstall$(DIVIDER)<arch> - Remove bin & lib directories for <arch> only."
|
||||
@echo "Targets supported by top level Makefile:"
|
||||
ifndef DISABLE_TOP_RULES
|
||||
@echo " archuninstall - Remove bin & lib directories created by this hostarch."
|
||||
@echo " uninstall$(DIVIDER)<arch> - Remove bin & lib directories for <arch> only."
|
||||
@echo " uninstall - Remove install directories created by this hostarch."
|
||||
@echo " realuninstall - Removes ALL install dirs"
|
||||
@echo " distclean - Same as realclean cvsclean realuninstall."
|
||||
@echo " depclean - Removes all .d files from O.<arch> directories."
|
||||
@echo " cvsclean - Removes cvs .#* files in all dirs of directory tree"
|
||||
endif
|
||||
@echo " help - Prints this list of valid make targets "
|
||||
@echo "Indiv. object targets are supported by O.<arch> level Makefile .e.g"
|
||||
@echo "Object targets are supported by the O.<arch> level Makefile .e.g"
|
||||
@echo " xxxRecord.o"
|
||||
|
||||
.PHONY: cleandirs distclean cvsclean depclean
|
||||
.PHONY: cleandirs distclean uninstall help
|
||||
.PHONY: realuninstall archuninstall uninstallDirs
|
||||
.PHONY: uninstall help
|
||||
|
||||
# Include <top>/cfg/TOP_RULES* files from tops defined in RELEASE* files
|
||||
#
|
||||
RELEASE_CFG_TOP_RULES = $(foreach top, $(RELEASE_TOPS), \
|
||||
$(wildcard $($(top))/cfg/TOP_RULES*))
|
||||
ifneq ($(RELEASE_CFG_TOP_RULES),)
|
||||
include $(RELEASE_CFG_TOP_RULES)
|
||||
ifndef DISABLE_TOP_RULES
|
||||
# Include <top>/cfg/TOP_RULES* files from tops defined in RELEASE* files
|
||||
#
|
||||
RELEASE_CFG_TOP_RULES = $(foreach top, $(RELEASE_TOPS), \
|
||||
$(wildcard $($(top))/cfg/TOP_RULES*))
|
||||
ifneq ($(RELEASE_CFG_TOP_RULES),)
|
||||
include $(RELEASE_CFG_TOP_RULES)
|
||||
endif
|
||||
endif
|
||||
|
||||
@@ -65,7 +65,7 @@ GNU = NO
|
||||
#
|
||||
# Darwin shared libraries
|
||||
#
|
||||
SHRLIB_LDFLAGS = -dynamiclib -undefined dynamic_lookup \
|
||||
SHRLIB_LDFLAGS = -dynamiclib -flat_namespace -undefined dynamic_lookup \
|
||||
-install_name $(shell $(FULLPATHNAME) $(INSTALL_LIB))/$@ \
|
||||
$(addprefix -compatibility_version , $(SHRLIB_VERSION)) \
|
||||
$(addprefix -current_version , $(SHRLIB_VERSION))
|
||||
|
||||
@@ -4,17 +4,17 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
|
||||
<title>Known Problems in EPICS 7.0.2</title>
|
||||
<title>Known Problems in EPICS 7.0.3</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1 style="text-align: center">EPICS 7.0.2: Known Problems</h1>
|
||||
<h1 style="text-align: center">EPICS 7.0.3: Known Problems</h1>
|
||||
|
||||
<p>Any patch files linked below should be applied at the root of the
|
||||
base-7.0.2 tree. Download them, then use the GNU Patch program as
|
||||
base-7.0.3 tree. Download them, then use the GNU Patch program as
|
||||
follows:</p>
|
||||
|
||||
<blockquote><pre>% <b>cd <i>/path/to/</i>base-7.0.2</b>
|
||||
<blockquote><pre>% <b>cd <i>/path/to/</i>base-7.0.3</b>
|
||||
% <b>patch -p1 < <i>/path/to/</i>file.patch</b></pre></blockquote>
|
||||
|
||||
<p>The following problems were known by the developers at the time of this
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
Installation Instructions
|
||||
|
||||
EPICS Base Release 7.0.2
|
||||
EPICS Base Release 7.0.3
|
||||
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
Table of Contents
|
||||
|
||||
* What is EPICS base?
|
||||
* What is new in this release?
|
||||
* Copyright
|
||||
* Supported platforms
|
||||
* Supported compilers
|
||||
* Software requirements
|
||||
* Host system storage requirements
|
||||
* Documentation
|
||||
* Directory Structure
|
||||
* Build related components
|
||||
* Building EPICS base (Unix and Win32)
|
||||
* Example application and extension
|
||||
* Multiple host platforms
|
||||
* What is EPICS base?
|
||||
* What is new in this release?
|
||||
* Copyright
|
||||
* Supported platforms
|
||||
* Supported compilers
|
||||
* Software requirements
|
||||
* Host system storage requirements
|
||||
* Documentation
|
||||
* Directory Structure
|
||||
* Build related components
|
||||
* Building EPICS base (Unix and Win32)
|
||||
* Example application and extension
|
||||
* Multiple host platforms
|
||||
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<BODY>
|
||||
<CENTER>
|
||||
<H1>Installation Instructions</H1>
|
||||
<H2>EPICS Base Release 7.0.2</H2><BR>
|
||||
<H2>EPICS Base Release 7.0.3</H2><BR>
|
||||
</CENTER>
|
||||
<HR>
|
||||
<H3> Table of Contents</H3>
|
||||
|
||||
@@ -20,8 +20,7 @@ release.</p>
|
||||
which should also be read to understand what has changed since an earlier
|
||||
release.</p>
|
||||
|
||||
|
||||
<h1 align="center">EPICS Release 7.0.2</h1>
|
||||
<h1 align="center">EPICS Release 7.0.3</h1>
|
||||
|
||||
<!-- Insert new items immediately below this template ...
|
||||
|
||||
@@ -31,6 +30,124 @@ release.</p>
|
||||
|
||||
-->
|
||||
|
||||
<h3>epicsTimeGetCurrent() optimization</h3>
|
||||
|
||||
<p>Add a fast path to epicsTimeGetCurrent() and related calls
|
||||
in the common case where only the default OS current time provider is
|
||||
registered. This path does not take the global mutex guarding
|
||||
the time providers list, potentially reducing lock contention.</p>
|
||||
|
||||
<h3>dbEvent tweak Queue size</h3>
|
||||
|
||||
<p>The size of the queue used by dbEvent to push monitor updates
|
||||
has been slightly increased based on DBR_TIME_DOUBLE to better fill
|
||||
an ethernet frame. This may result in slightly fewer, but larger
|
||||
frames being sent.</p>
|
||||
|
||||
<h3>mbbo/mbbiDirect number of bits as precision</h3>
|
||||
|
||||
<p>Report NOBT as "precision" through the dbAccess API.
|
||||
This is not accessible through CA, but is planned to be used through
|
||||
QSRV.</p>
|
||||
|
||||
<h1 align="center">EPICS Release 7.0.2.2</h1>
|
||||
|
||||
<h3>Build System changes</h3>
|
||||
|
||||
<ul>
|
||||
|
||||
<li>The GNUmake build targets <tt>cvsclean</tt> and <tt>depclean</tt> are now
|
||||
available from any directory; previously they were only available from
|
||||
application top directories.</li>
|
||||
|
||||
<li>The approach that EPICS Base uses for building submodules inside the parent
|
||||
module looks useful for support modules too. The rules for building submodules
|
||||
have been modified and extracted into a new <tt>RULES_MODULES</tt> file, so a
|
||||
support module will be able to use them too without having to copy them into its
|
||||
own <tt>modules/Makefile</tt>. There are some specific requirements that support
|
||||
modules and their submodules must follow, which are described as comments in the
|
||||
new <tt>base/configure/RULES_MODULES</tt> file itself.</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<h3>EPICS_BASE_VERSION Update Policy change</h3>
|
||||
|
||||
<p>In the past, a build of EPICS using sources checked out from the repository
|
||||
branch between official releases would have shown the version number of the
|
||||
previous release, followed by a <q>-DEV</q> suffix, for example
|
||||
<q>7.0.2.1-DEV</q>.</p>
|
||||
|
||||
<p>The policy that controls when the number gets updated has been changed, and
|
||||
now immediately after a release has been tagged the version number will be
|
||||
updated to the next patch release version, plus the <q>-DEV</q> suffix as
|
||||
before. Thus following <q>7.0.2.2</q> the version number will show as
|
||||
<q>7.0.2.3-DEV</q>. This does not require the next official release to be
|
||||
numbered <q>7.0.2.3</q> though, it could become <q>7.0.3</q> or even
|
||||
<q>7.1.0</q> if the changes incorporated into it are more substantial than bug
|
||||
fixes.</p>
|
||||
|
||||
<h3>Drop CLOCK_MONOTONIC_RAW from posix/osdMonotonic.c</h3>
|
||||
|
||||
<p>Turns out this is ~10x slower to query than CLOCK_MONOTONIC.</p>
|
||||
|
||||
|
||||
<h1 align="center">EPICS Release 7.0.2.1</h1>
|
||||
|
||||
<h3>Linking shared libraries on macOS</h3>
|
||||
|
||||
<p>The linker flag <tt>-flat_namespace</tt> has been restored for creating
|
||||
shared libraries, although not for loadable libraries (bundles). This was
|
||||
required for building using the latest versions of Apple XCode.</p>
|
||||
|
||||
<h3>Fix DB_LINK loop breaking</h3>
|
||||
|
||||
<p>A regression was introduced in 7.0.2 which caused record chains with loops to
|
||||
be incorrectly broken. Processing should be skipped when a DB_LINK with Process
|
||||
Passive (PP) closes a loop to a synchronous record.</p>
|
||||
|
||||
<p>Instead in 7.0.2 the targeted record would be processed if processing began
|
||||
with a remote action (or some other caller of <tt>dbPutField()</tt>). This would
|
||||
result in the loop running a second time. The loop would be broken on the second
|
||||
iteration.</p>
|
||||
|
||||
<p><a href="https://bugs.launchpad.net/epics-base/+bug/1809570">See lp:
|
||||
#1809570</a></p>
|
||||
|
||||
<h3>Old dbStaticLib APIs removed</h3>
|
||||
|
||||
<p>Support for some obsolete dbStaticLib Database Configuration Tool (DCT) APIs
|
||||
was removed some time ago, but vestiges of them still remained. The following
|
||||
routines and macros and have now finally been removed:</p>
|
||||
|
||||
<ul>
|
||||
<li>int dbGetFieldType(DBENTRY *pdbentry)</li>
|
||||
<li>int dbGetLinkType(DBENTRY *pdbentry)</li>
|
||||
<li>DCT_STRING</li>
|
||||
<li>DCT_INTEGER</li>
|
||||
<li>DCT_REAL</li>
|
||||
<li>DCT_MENU</li>
|
||||
<li>DCT_MENUFORM</li>
|
||||
<li>DCT_INLINK</li>
|
||||
<li>DCT_OUTLINK</li>
|
||||
<li>DCT_FWDLINK</li>
|
||||
<li>DCT_NOACCESS</li>
|
||||
<li>DCT_LINK_CONSTANT</li>
|
||||
<li>DCT_LINK_FORM</li>
|
||||
<li>DCT_LINK_PV</li>
|
||||
</ul>
|
||||
|
||||
<h3>Fix for <tt>dbhcr</tt> before <tt>iocInit</tt></h3>
|
||||
|
||||
<p>The <tt>dbhcr</tt> command used to work before <tt>iocInit</tt> as well as
|
||||
afterwards. It displays all records that have hardware addresses (VME_IO,
|
||||
CAMAC_IO, GPIB_IO, INST_IO etc.) but stopped working if run before iocInit due
|
||||
to the rewrite of the link address parser code in dbStaticLib. This release
|
||||
fixes that issue, although in some cases the output may be slightly different
|
||||
than it used to be.</p>
|
||||
|
||||
|
||||
<h1 align="center">EPICS Release 7.0.2</h1>
|
||||
|
||||
<h3>Launchpad Bugs</h3>
|
||||
|
||||
<p>The list of tracked bugs fixed in this release can be found on the
|
||||
@@ -920,6 +1037,24 @@ of its CALLBACK objects.</p>
|
||||
|
||||
<!-- Insert inherited items immediately below here ... -->
|
||||
|
||||
<h3>Cleaning up with Multiple CA contexts in a Process</h3>
|
||||
|
||||
<p>Bruno Martins reported a problem with the CA client library at shutdown in a
|
||||
process that uses multiple CA client contexts. The first context that triggers
|
||||
the CA client exit handler prevents any others from being able to clean up
|
||||
because it resets the ID of an internal epicsThreadPrivate variable which is
|
||||
shared by all clients. This action has been removed from the client library,
|
||||
which makes cleanup of clients like this possible.</p>
|
||||
|
||||
<h3>Perl CA bindings fixed for macOS Mojave</h3>
|
||||
|
||||
<p>Apple removed some Perl header files from macOS Mojave that were available
|
||||
in their SDK, requiring a change to the include paths used when compiling the
|
||||
CA bindings. The new version should build on new and older macOS versions, and
|
||||
these changes may also help other targets that have an incomplete installation
|
||||
of Perl (the build will continue after printing a warning that the Perl CA
|
||||
bindings could not be built).</p>
|
||||
|
||||
<h3>Routine <tt>epicsTempName()</tt> removed from libCom</h3>
|
||||
|
||||
<p>This routine was a simple wrapper around the C89 function <tt>tmpnam()</tt>
|
||||
|
||||
@@ -37,6 +37,8 @@ that should be performed when creating production releases of EPICS Base.</p>
|
||||
|
||||
<h3>The Release Process</h3>
|
||||
|
||||
<h4>Full Process</h4>
|
||||
|
||||
<p>The version released on the Feature Freeze date is designated the first
|
||||
pre-release, <tt>-pre1</tt>. The first release candidate <tt>-rc1</tt> is the
|
||||
first version that has undergone testing by the developers and has shown no
|
||||
@@ -49,6 +51,13 @@ release candidate has been available for 2 weeks without any new problems being
|
||||
reported or major changes having to be committed, the final release can be
|
||||
made.</p>
|
||||
|
||||
<h4>Short Process for Patch Releases</h4>
|
||||
|
||||
<p>The Patch Release date and its scope are agreed upon a few weeks ahead of the
|
||||
release. If no blocking issues are raised, the release is made by the Release
|
||||
Manager on or as soon as possible after that date, following the steps below
|
||||
starting at <a href="#ReleaseApproval">Release Approval</a>.</p>
|
||||
|
||||
<h3>Roles</h3>
|
||||
|
||||
<p>The following roles are used below:</p>
|
||||
@@ -64,6 +73,7 @@ made.</p>
|
||||
<dd>Responsible for the EPICS website</dd>
|
||||
</dl>
|
||||
|
||||
<form>
|
||||
<table border="1" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
@@ -116,80 +126,81 @@ made.</p>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>Website Manager</td>
|
||||
<td>Create a release milestone on Launchpad if necessary and set the
|
||||
expected release date. Note that pre-release and release-candidate
|
||||
versions do not appear on Launchpad, only the final release.</td>
|
||||
<td>Create a release milestone on Launchpad. If a target release date is
|
||||
known set "Date Targeted" to the expected release date. Note that
|
||||
pre-release and release-candidate versions should not get Launchpad
|
||||
milestones, only the final release.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th colspan="3">Creating pre-release and release-candidate versions</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td><input type="checkbox"></td>
|
||||
<td>Release Manager</td>
|
||||
<!-- Submodules... -->
|
||||
<td>Edit and commit changes to the EPICS version number file
|
||||
configure/CONFIG_BASE_VERSION.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td><input type="checkbox"></td>
|
||||
<td>Release Manager</td>
|
||||
<td>Tag the module in Git, using these tag conventions:
|
||||
<ul>
|
||||
<li>
|
||||
<tt>R7.0.2-pre<i>n</i></tt>
|
||||
<tt>R7.0.3-pre<i>n</i></tt>
|
||||
— pre-release tag
|
||||
</li>
|
||||
<li>
|
||||
<tt>R7.0.2-rc<i>n</i></tt>
|
||||
<tt>R7.0.3-rc<i>n</i></tt>
|
||||
— release candidate tag
|
||||
</li>
|
||||
</ul>
|
||||
<blockquote><tt>
|
||||
cd base-7.0<br />
|
||||
git tag -m 'ANJ: Tagged for 7.0.2-rc1' R7.0.2-rc1
|
||||
git tag -m 'ANJ: Tagged for 7.0.3-rc1' R7.0.3-rc1
|
||||
</tt></blockquote>
|
||||
Note that submodules must <em>not</em> 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.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td><input type="checkbox"></td>
|
||||
<td>Release Manager</td>
|
||||
<td>Export the tagged version into a tarfile. The <tt>make-tar.sh</tt>
|
||||
script generates a gzipped tarfile directly from the tag, excluding the
|
||||
files and directories that are only used for continuous integration:
|
||||
<blockquote><tt>
|
||||
cd base-7.0<br />
|
||||
./.tools/make-tar.sh R7.0.2-rc1 base-7.0.2-rc1.tar.gz base-7.0.2-rc1/
|
||||
./.tools/make-tar.sh R7.0.3-rc1 base-7.0.3-rc1.tar.gz base-7.0.3-rc1/
|
||||
</tt></blockquote>
|
||||
Create a GPG signature file of the tarfile as follows:
|
||||
<blockquote><tt>
|
||||
gpg --armor --sign --detach-sig base-7.0.2-rc1.tar.gz
|
||||
gpg --armor --sign --detach-sig base-7.0.3-rc1.tar.gz
|
||||
</tt></blockquote>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td><input type="checkbox"></td>
|
||||
<td>Release Manager</td>
|
||||
<td>Test the tarfile by extracting its contents and building it on at
|
||||
least one supported platform.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td><input type="checkbox"></td>
|
||||
<td>Website Manager</td>
|
||||
<td>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.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td><input type="checkbox"></td>
|
||||
<td>Website Manager</td>
|
||||
<td>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.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td><input type="checkbox"></td>
|
||||
<td>Website Manager</td>
|
||||
<td>Create or modify the webpage for the new release with links to the
|
||||
release documents and tar file. Pre-release and release-candidate
|
||||
@@ -249,10 +260,11 @@ made.</p>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th colspan="3">Release Approval</th>
|
||||
<th colspan="3" id="ReleaseApproval">
|
||||
Release Approval</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td><input type="checkbox"></td>
|
||||
<td>Release Manager</td>
|
||||
<td>Obtain a positive <q>Ok to release</q> from all platform developers
|
||||
once a release candidate version has gone for 2 weeks without any major
|
||||
@@ -262,7 +274,7 @@ made.</p>
|
||||
<th colspan="3">Creating the final release version</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td><input type="checkbox"></td>
|
||||
<td>Release Manager</td>
|
||||
<td>
|
||||
<p>For each external submodule, check if the module's release version
|
||||
@@ -274,7 +286,7 @@ made.</p>
|
||||
commit.</p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td><input type="checkbox"></td>
|
||||
<td>Release Manager</td>
|
||||
<td>Edit and commit changes to the EPICS Base version number file and
|
||||
the embedded module version files:
|
||||
@@ -286,33 +298,33 @@ made.</p>
|
||||
</ul></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td><input type="checkbox"></td>
|
||||
<td>Release Manager</td>
|
||||
<td>Tag the epics-base module in Git:
|
||||
<blockquote><tt>
|
||||
cd base-7.0<br />
|
||||
git tag -m 'ANJ: Tagged for 7.0.2' R7.0.2</i>
|
||||
git tag -m 'ANJ: Tagged for 7.0.3' R7.0.3</i>
|
||||
</tt></blockquote>
|
||||
Don't push the new tag to the Launchpad repository yet.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td><input type="checkbox"></td>
|
||||
<td>Release Manager</td>
|
||||
<td>Export the tagged version into a tarfile. The <tt>make-tar.sh</tt>
|
||||
script generates a gzipped tarfile directly from the tag, excluding the
|
||||
files and directories that are only used for continuous integration:
|
||||
<blockquote><tt>
|
||||
cd base-7.0<br />
|
||||
./.tools/make-tar.sh R7.0.2 base-7.0.2.tar.gz base-7.0.2/
|
||||
./.tools/make-tar.sh R7.0.3 base-7.0.3.tar.gz base-7.0.3/
|
||||
</tt></blockquote>
|
||||
Create a GPG signature file of the tarfile as follows:
|
||||
<blockquote><tt>
|
||||
gpg --armor --sign --detach-sig base-7.0.2.tar.gz
|
||||
gpg --armor --sign --detach-sig base-7.0.3.tar.gz
|
||||
</tt></blockquote>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td><input type="checkbox"></td>
|
||||
<td>Release Manager</td>
|
||||
<td>Test the tar file by extracting its contents and building it on at
|
||||
least one supported platform. When this succeeds the new git tag can be
|
||||
@@ -323,63 +335,109 @@ made.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td><input type="checkbox"></td>
|
||||
<td>Release Manager</td>
|
||||
<td>Edit and commit changes to the EPICS Base version number file and
|
||||
the embedded module version files:
|
||||
<ul>
|
||||
<li>configure/CONFIG_BASE_VERSION </li>
|
||||
<li>modules/libcom/configure/CONFIG_LIBCOM_VERSION</li>
|
||||
<li>modules/ca/configure/CONFIG_CA_VERSION</li>
|
||||
<li>modules/database/configure/CONFIG_DATABASE_VERSION</li>
|
||||
</ul>
|
||||
Version numbers should be set to the next expected patch release,
|
||||
with a "-DEV" tag added (where applicable).
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th colspan="3">Publish to epics.anl.gov</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input type="checkbox"></td>
|
||||
<td>Release Manager</td>
|
||||
<td>Copy the tarfile and its signature to the Base download area of the
|
||||
website.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>Release Manager</td>
|
||||
<td>Find all Launchpad bug reports with the status Fix Committed which
|
||||
have been fixed in this release and mark them Fix Released.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th colspan="3">Publish and Announce it</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>Website Manager</td>
|
||||
<td>Upload the tar file and its <tt>.asc</tt> signature file to the
|
||||
Launchpad milestone for this release version.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td><input type="checkbox"></td>
|
||||
<td>Website Manager</td>
|
||||
<td>Update the website subdirectory that holds the release
|
||||
documentation, and copy in the files from the base/documentation
|
||||
directory of the tarfile.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td><input type="checkbox"></td>
|
||||
<td>Website Manager</td>
|
||||
<td>Update the webpage for the new release with links to the release
|
||||
documents and tar file.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td><input type="checkbox"></td>
|
||||
<td>Website Manager</td>
|
||||
<td>Add the new release tar file to the website Base download index
|
||||
page.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td><input type="checkbox"></td>
|
||||
<td>Website Manager</td>
|
||||
<td>Link to the release webpage from other relevent areas of the
|
||||
website - update front page and sidebars.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td><input type="checkbox"></td>
|
||||
<td>Website Manager</td>
|
||||
<td>Add an entry to the website News page, linking to the new version
|
||||
webpage.</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td> </td>
|
||||
<th colspan="3">Publish to epics-controls</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input type="checkbox"></td>
|
||||
<td>Website Manager</td>
|
||||
<td>Upload the tar file and its <tt>.asc</tt> signature file to the
|
||||
epics-controls web-server [ToDo: ssh-key, location]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input type="checkbox"></td>
|
||||
<td>Website Manager</td>
|
||||
<td>Follow instructions on
|
||||
<a href="https://epics-controls.org/resources-and-support/documents/epics-website-documentation/adding-a-page-for-a-new-release/">
|
||||
Add a page for a new release</a> to create a new release webpage (not
|
||||
required for a patch release though, just edit the existing page).</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th colspan="3">Publish to Launchpad</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input type="checkbox"></td>
|
||||
<td>Website Manager</td>
|
||||
<td>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
|
||||
the tar file and its <tt>.asc</tt> signature file to the new Launchpad
|
||||
release page.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input type="checkbox"></td>
|
||||
<td>Release Manager</td>
|
||||
<td>Find all Launchpad bug reports with the status Fix Committed which
|
||||
have been fixed in this release and mark them Fix Released.</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th colspan="3">Make Announcement</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input type="checkbox"></td>
|
||||
<td>Release Manager</td>
|
||||
<td>Announce the release on the tech-talk mailing list.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3,8 +3,11 @@
|
||||
# in file LICENSE that is included with this distribution.
|
||||
#*************************************************************************
|
||||
|
||||
# When building submodules, this should always be true:
|
||||
INSTALL_LOCATION = $(EPICS_BASE)
|
||||
# The name our submodules know us by:
|
||||
PARENT_MODULE = EPICS_BASE
|
||||
|
||||
# Stop submodules from installing their configuration files:
|
||||
# When building submodules, this should always be true:
|
||||
INSTALL_LOCATION := $($(PARENT_MODULE))
|
||||
|
||||
# Stop submodules installing their configure/ files into our area
|
||||
CONFIG_INSTALLS =
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
TOP = ..
|
||||
include $(TOP)/configure/CONFIG
|
||||
include CONFIG_SITE.local
|
||||
|
||||
# Submodules for bundle build
|
||||
SUBMODULES += libcom
|
||||
@@ -39,20 +40,4 @@ example_DEPEND_DIRS = pva2pva pvaClient
|
||||
# Allow sites to add extra submodules
|
||||
-include Makefile.local
|
||||
|
||||
# Add only checked-out submodules to DIRS
|
||||
DIRS += $(subst /Makefile,,$(wildcard $(addsuffix /Makefile, $(SUBMODULES))))
|
||||
|
||||
include $(TOP)/configure/RULES_DIRS
|
||||
|
||||
INSTALL_LOCATION_ABS := $(abspath $(INSTALL_LOCATION))
|
||||
RELEASE_LOCAL := RELEASE.$(EPICS_HOST_ARCH).local
|
||||
|
||||
# Ensure that RELEASE.<host>.local exists before doing anything else
|
||||
all host $(DIRS) $(ARCHS) $(ACTIONS) $(dirActionTargets) $(dirArchTargets) \
|
||||
$(dirActionArchTargets) $(actionArchTargets): | $(RELEASE_LOCAL)
|
||||
|
||||
$(RELEASE_LOCAL):
|
||||
$(ECHO) Creating $@, EPICS_BASE = $(INSTALL_LOCATION_ABS)
|
||||
@echo EPICS_BASE = $(INSTALL_LOCATION_ABS)> $@
|
||||
realclean:
|
||||
$(RM) $(RELEASE_LOCAL)
|
||||
include $(TOP)/configure/RULES_MODULES
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
EPICS_CA_MAJOR_VERSION = 4
|
||||
EPICS_CA_MINOR_VERSION = 13
|
||||
EPICS_CA_MAINTENANCE_VERSION = 3
|
||||
EPICS_CA_MAINTENANCE_VERSION = 4
|
||||
EPICS_CA_DEVELOPMENT_FLAG = 0
|
||||
|
||||
@@ -3143,7 +3143,8 @@ indicating the current state of the channel.</p>
|
||||
<dl>
|
||||
<dt><code>COUNT</code></dt>
|
||||
<dd>The element count to be read from the specified channel. A count of
|
||||
zero means use the current element count from the server.</dd>
|
||||
zero means use the current element count from the server, effectively
|
||||
resulting in a variable size array subscription.</dd>
|
||||
</dl>
|
||||
<dl>
|
||||
<dt><code>CHID</code></dt>
|
||||
|
||||
@@ -45,20 +45,12 @@ static epicsThreadOnceId cacOnce = EPICS_THREAD_ONCE_INIT;
|
||||
|
||||
const unsigned ca_client_context :: flushBlockThreshold = 0x58000;
|
||||
|
||||
extern "C" void cacExitHandler ( void *)
|
||||
{
|
||||
epicsThreadPrivateDelete ( caClientCallbackThreadId );
|
||||
caClientCallbackThreadId = 0;
|
||||
delete ca_client_context::pDefaultServiceInstallMutex;
|
||||
}
|
||||
|
||||
// runs once only for each process
|
||||
extern "C" void cacOnceFunc ( void * )
|
||||
{
|
||||
caClientCallbackThreadId = epicsThreadPrivateCreate ();
|
||||
assert ( caClientCallbackThreadId );
|
||||
ca_client_context::pDefaultServiceInstallMutex = newEpicsMutex;
|
||||
epicsAtExit ( cacExitHandler,0 );
|
||||
}
|
||||
|
||||
extern epicsThreadPrivateId caClientContextId;
|
||||
@@ -67,6 +59,8 @@ cacService * ca_client_context::pDefaultService = 0;
|
||||
epicsMutex * ca_client_context::pDefaultServiceInstallMutex;
|
||||
|
||||
ca_client_context::ca_client_context ( bool enablePreemptiveCallback ) :
|
||||
mutex(__FILE__, __LINE__),
|
||||
cbMutex(__FILE__, __LINE__),
|
||||
createdByThread ( epicsThreadGetIdSelf () ),
|
||||
ca_exception_func ( 0 ), ca_exception_arg ( 0 ),
|
||||
pVPrintfFunc ( errlogVprintf ), fdRegFunc ( 0 ), fdRegArg ( 0 ),
|
||||
|
||||
@@ -289,7 +289,6 @@ private:
|
||||
};
|
||||
|
||||
extern "C" void cacOnceFunc ( void * );
|
||||
extern "C" void cacExitHandler ( void *);
|
||||
|
||||
struct ca_client_context : public cacContextNotify
|
||||
{
|
||||
@@ -429,7 +428,6 @@ private:
|
||||
ca_client_context & operator = ( const ca_client_context & );
|
||||
|
||||
friend void cacOnceFunc ( void * );
|
||||
friend void cacExitHandler ( void *);
|
||||
static cacService * pDefaultService;
|
||||
static epicsMutex * pDefaultServiceInstallMutex;
|
||||
static const unsigned flushBlockThreshold;
|
||||
|
||||
@@ -37,6 +37,7 @@ repeaterSubscribeTimer::repeaterSubscribeTimer (
|
||||
epicsMutex & cbMutexIn, cacContextNotify & ctxNotifyIn ) :
|
||||
timer ( queueIn.createTimer () ), iiu ( iiuIn ),
|
||||
cbMutex ( cbMutexIn ),ctxNotify ( ctxNotifyIn ),
|
||||
stateMutex(__FILE__, __LINE__),
|
||||
attempts ( 0 ), registered ( false ), once ( false )
|
||||
{
|
||||
}
|
||||
|
||||
@@ -8,6 +8,18 @@
|
||||
TOP = ../..
|
||||
include $(TOP)/configure/CONFIG
|
||||
|
||||
ifdef T_A
|
||||
PERL_VERSION = $(shell $(PERL) ../perlConfig.pl version)
|
||||
PERL_ARCHNAME = $(shell $(PERL) ../perlConfig.pl archname)
|
||||
PERL_ARCHPATH := $(PERL_VERSION)/$(PERL_ARCHNAME)
|
||||
|
||||
PERL_ARCHLIB := $(shell $(PERL) ../perlConfig.pl archlib)
|
||||
PERL_h = $(PERL_ARCHLIB)/CORE/perl.h
|
||||
|
||||
EXTUTILS := $(shell $(PERL) ../perlConfig.pl privlib)/ExtUtils
|
||||
PERLBIN := $(shell $(PERL) ../perlConfig.pl bin)
|
||||
XSUBPP := $(firstword $(wildcard $(PERLBIN)/xsubpp $(EXTUTILS)/xsubpp))
|
||||
|
||||
# Special settings for Darwin:
|
||||
ifeq ($(OS_CLASS),Darwin)
|
||||
# Use hdepends command (not GNU compiler flags)
|
||||
@@ -18,22 +30,23 @@ ifeq ($(OS_CLASS),Darwin)
|
||||
# Perl loadable libraries on Darwin have funny names
|
||||
LOADABLE_SHRLIB_PREFIX =
|
||||
LOADABLE_SHRLIB_SUFFIX = .$(shell $(PERL) ../perlConfig.pl dlext)
|
||||
|
||||
ifeq ($(wildcard $(PERL_h)),)
|
||||
# Perl's headers moved in Mojave
|
||||
SDK_PATH := $(shell xcodebuild -version -sdk macosx Path)
|
||||
PERL_ARCHLIB := $(SDK_PATH)/$(PERL_ARCHLIB)
|
||||
endif
|
||||
endif
|
||||
|
||||
ifdef T_A
|
||||
PERL_VERSION = $(shell $(PERL) ../perlConfig.pl version)
|
||||
PERL_ARCHNAME = $(shell $(PERL) ../perlConfig.pl archname)
|
||||
PERL_ARCHPATH := $(PERL_VERSION)/$(PERL_ARCHNAME)
|
||||
|
||||
EXTUTILS := $(shell $(PERL) ../perlConfig.pl privlib)/ExtUtils
|
||||
PERLBIN := $(shell $(PERL) ../perlConfig.pl bin)
|
||||
XSUBPP := $(firstword $(wildcard $(PERLBIN)/xsubpp $(EXTUTILS)/xsubpp))
|
||||
|
||||
ifeq ($(T_A),$(EPICS_HOST_ARCH)) # No cross-builds (wrong Perl!)
|
||||
ifeq ($(strip $(XSUBPP)),)
|
||||
$(warning Perl's xsubpp program was not found.)
|
||||
$(warning The Perl CA module will not be built.)
|
||||
else
|
||||
ifeq ($(T_A),$(EPICS_HOST_ARCH)) # No cross-builds (wrong Perl!)
|
||||
ifeq ($(wildcard $(PERL_h)),)
|
||||
$(warning Perl's C header files were not found.)
|
||||
$(warning The Perl CA module will not be built.)
|
||||
else
|
||||
ifeq ($(findstring $(OS_CLASS),WIN32 cygwin32),) # Doesn't build on WIN32
|
||||
LOADABLE_LIBRARY_HOST = Cap5
|
||||
|
||||
@@ -50,10 +63,11 @@ endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
Cap5_SRCS = Cap5.xs
|
||||
Cap5_LIBS = ca Com
|
||||
Cap5_INCLUDES = -I$(shell $(PERL) ../perlConfig.pl archlib)/CORE
|
||||
Cap5_INCLUDES = -I$(PERL_ARCHLIB)/CORE
|
||||
Cap5_CFLAGS = $(shell $(PERL) ../perlConfig.pl ccflags)
|
||||
|
||||
CLEANS += Cap5.c
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
EPICS_DATABASE_MAJOR_VERSION = 3
|
||||
EPICS_DATABASE_MINOR_VERSION = 17
|
||||
EPICS_DATABASE_MAINTENANCE_VERSION = 2
|
||||
EPICS_DATABASE_MAINTENANCE_VERSION = 4
|
||||
EPICS_DATABASE_DEVELOPMENT_FLAG = 0
|
||||
|
||||
@@ -55,7 +55,7 @@ typedef struct cbQueueSet {
|
||||
epicsRingPointerId queue;
|
||||
int queueOverflow;
|
||||
int queueOverflows;
|
||||
int shutdown;
|
||||
int shutdown; // use atomic
|
||||
int threadsConfigured;
|
||||
int threadsRunning;
|
||||
} cbQueueSet;
|
||||
@@ -71,12 +71,15 @@ epicsExportAddress(int,callbackParallelThreadsDefault);
|
||||
/* Timer for Delayed Requests */
|
||||
static epicsTimerQueueId timerQueue;
|
||||
|
||||
/* Shutdown handling */
|
||||
enum ctl {ctlInit, ctlRun, ctlPause, ctlExit};
|
||||
static volatile enum ctl cbCtl;
|
||||
static epicsEventId startStopEvent;
|
||||
enum cbState_t {
|
||||
cbInit, /* before callbackInit() and after callbackCleanup() */
|
||||
cbRun, /* after callbackInit() and before callbackStop() */
|
||||
cbStop, /* after callbackStop() and before callbackCleanup() */
|
||||
};
|
||||
|
||||
static int callbackIsInit;
|
||||
static int cbState; // holdscbState_t, use atomic ops
|
||||
|
||||
static epicsEventId startStopEvent;
|
||||
|
||||
/* Static data */
|
||||
static char *threadNamePrefix[NUM_CALLBACK_PRIORITIES] = {
|
||||
@@ -96,7 +99,7 @@ static int priorityValue[NUM_CALLBACK_PRIORITIES] = {0, 1, 2};
|
||||
|
||||
int callbackSetQueueSize(int size)
|
||||
{
|
||||
if (callbackIsInit) {
|
||||
if (epicsAtomicGetIntT(&cbState)!=cbInit) {
|
||||
fprintf(stderr, "Callback system already initialized\n");
|
||||
return -1;
|
||||
}
|
||||
@@ -107,7 +110,7 @@ int callbackSetQueueSize(int size)
|
||||
int callbackQueueStatus(const int reset, callbackQueueStats *result)
|
||||
{
|
||||
int ret;
|
||||
if (!callbackIsInit) return -1;
|
||||
if (epicsAtomicGetIntT(&cbState)==cbInit) return -1;
|
||||
if (result) {
|
||||
int prio;
|
||||
result->size = callbackQueueSize;
|
||||
@@ -151,7 +154,7 @@ void callbackQueueShow(const int reset)
|
||||
|
||||
int callbackParallelThreads(int count, const char *prio)
|
||||
{
|
||||
if (callbackIsInit) {
|
||||
if (epicsAtomicGetIntT(&cbState)!=cbInit) {
|
||||
fprintf(stderr, "Callback system already initialized\n");
|
||||
return -1;
|
||||
}
|
||||
@@ -207,7 +210,7 @@ static void callbackTask(void *arg)
|
||||
taskwdInsert(0, NULL, NULL);
|
||||
epicsEventSignal(startStopEvent);
|
||||
|
||||
while(!mySet->shutdown) {
|
||||
while(!epicsAtomicGetIntT(&mySet->shutdown)) {
|
||||
void *ptr;
|
||||
if (epicsRingPointerIsEmpty(mySet->queue))
|
||||
epicsEventMustWait(mySet->semWakeUp);
|
||||
@@ -230,11 +233,10 @@ void callbackStop(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (cbCtl == ctlExit) return;
|
||||
cbCtl = ctlExit;
|
||||
if (epicsAtomicCmpAndSwapIntT(&cbState, cbRun, cbStop)!=cbRun) return;
|
||||
|
||||
for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) {
|
||||
callbackQueue[i].shutdown = 1;
|
||||
epicsAtomicSetIntT(&callbackQueue[i].shutdown, 1);
|
||||
epicsEventSignal(callbackQueue[i].semWakeUp);
|
||||
}
|
||||
|
||||
@@ -252,6 +254,10 @@ void callbackCleanup(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
if(epicsAtomicCmpAndSwapIntT(&cbState, cbStop, cbInit)!=cbStop) {
|
||||
fprintf(stderr, "callbackCleanup() but not stopped\n");
|
||||
}
|
||||
|
||||
for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) {
|
||||
cbQueueSet *mySet = &callbackQueue[i];
|
||||
|
||||
@@ -261,7 +267,6 @@ void callbackCleanup(void)
|
||||
}
|
||||
|
||||
epicsTimerQueueRelease(timerQueue);
|
||||
callbackIsInit = 0;
|
||||
memset(callbackQueue, 0, sizeof(callbackQueue));
|
||||
}
|
||||
|
||||
@@ -271,15 +276,14 @@ void callbackInit(void)
|
||||
int j;
|
||||
char threadName[32];
|
||||
|
||||
if (callbackIsInit) {
|
||||
errlogMessage("Warning: callbackInit called again before callbackCleanup\n");
|
||||
if (epicsAtomicCmpAndSwapIntT(&cbState, cbInit, cbRun)!=cbInit) {
|
||||
fprintf(stderr, "Warning: callbackInit called again before callbackCleanup\n");
|
||||
return;
|
||||
}
|
||||
callbackIsInit = 1;
|
||||
|
||||
if(!startStopEvent)
|
||||
startStopEvent = epicsEventMustCreate(epicsEventEmpty);
|
||||
cbCtl = ctlRun;
|
||||
|
||||
timerQueue = epicsTimerQueueAllocate(0, epicsThreadPriorityScanHigh);
|
||||
|
||||
for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) {
|
||||
|
||||
@@ -601,6 +601,31 @@ all_done:
|
||||
return status;
|
||||
}
|
||||
|
||||
long dbEntryToAddr(const DBENTRY *pdbentry, DBADDR *paddr)
|
||||
{
|
||||
dbFldDes *pflddes = pdbentry->pflddes;
|
||||
short dbfType = pflddes->field_type;
|
||||
|
||||
paddr->precord = pdbentry->precnode->precord;
|
||||
paddr->pfield = pdbentry->pfield;
|
||||
paddr->pfldDes = pflddes;
|
||||
paddr->no_elements = 1;
|
||||
paddr->field_type = dbfType;
|
||||
paddr->field_size = pflddes->size;
|
||||
paddr->special = pflddes->special;
|
||||
paddr->dbr_field_type = mapDBFToDBR[dbfType];
|
||||
|
||||
if (paddr->special == SPC_DBADDR) {
|
||||
const rset *prset = dbGetRset(paddr);
|
||||
|
||||
/* Let record type modify paddr */
|
||||
if (prset && prset->cvt_dbaddr) {
|
||||
return prset->cvt_dbaddr(paddr);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fill out a database structure (*paddr) for
|
||||
* a record given by the name "pname."
|
||||
@@ -611,9 +636,7 @@ all_done:
|
||||
long dbNameToAddr(const char *pname, DBADDR *paddr)
|
||||
{
|
||||
DBENTRY dbEntry;
|
||||
dbFldDes *pflddes;
|
||||
long status = 0;
|
||||
short dbfType;
|
||||
|
||||
if (!pname || !*pname || !pdbbase)
|
||||
return S_db_notFound;
|
||||
@@ -628,46 +651,28 @@ long dbNameToAddr(const char *pname, DBADDR *paddr)
|
||||
status = dbGetAttributePart(&dbEntry, &pname);
|
||||
if (status) goto finish;
|
||||
|
||||
pflddes = dbEntry.pflddes;
|
||||
dbfType = pflddes->field_type;
|
||||
|
||||
paddr->precord = dbEntry.precnode->precord;
|
||||
paddr->pfield = dbEntry.pfield;
|
||||
paddr->pfldDes = pflddes;
|
||||
paddr->no_elements = 1;
|
||||
paddr->field_type = dbfType;
|
||||
paddr->field_size = pflddes->size;
|
||||
paddr->special = pflddes->special;
|
||||
paddr->dbr_field_type = mapDBFToDBR[dbfType];
|
||||
|
||||
if (paddr->special == SPC_DBADDR) {
|
||||
rset *prset = dbGetRset(paddr);
|
||||
|
||||
/* Let record type modify paddr */
|
||||
if (prset && prset->cvt_dbaddr) {
|
||||
status = prset->cvt_dbaddr(paddr);
|
||||
if (status)
|
||||
goto finish;
|
||||
dbfType = paddr->field_type;
|
||||
}
|
||||
}
|
||||
status = dbEntryToAddr(&dbEntry, paddr);
|
||||
if (status) goto finish;
|
||||
|
||||
/* Handle field modifiers */
|
||||
if (*pname++ == '$') {
|
||||
short dbfType = paddr->field_type;
|
||||
|
||||
/* Some field types can be accessed as char arrays */
|
||||
if (dbfType == DBF_STRING) {
|
||||
paddr->no_elements = paddr->field_size;
|
||||
paddr->field_type = DBF_CHAR;
|
||||
paddr->field_size = 1;
|
||||
paddr->dbr_field_type = DBR_CHAR;
|
||||
} else if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) {
|
||||
}
|
||||
else if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) {
|
||||
/* Clients see a char array, but keep original dbfType */
|
||||
paddr->no_elements = PVLINK_STRINGSZ;
|
||||
paddr->field_size = 1;
|
||||
paddr->dbr_field_type = DBR_CHAR;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
status = S_dbLib_fieldNotFound;
|
||||
goto finish;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -679,7 +684,7 @@ finish:
|
||||
void dbInitEntryFromAddr(struct dbAddr *paddr, DBENTRY *pdbentry)
|
||||
{
|
||||
struct dbCommon *prec = paddr->precord;
|
||||
dbCommonPvt *ppvt = CONTAINER(prec, dbCommonPvt, common);
|
||||
dbCommonPvt *ppvt = dbRec2Pvt(prec);
|
||||
|
||||
memset((char *)pdbentry,'\0',sizeof(DBENTRY));
|
||||
|
||||
@@ -693,7 +698,7 @@ void dbInitEntryFromAddr(struct dbAddr *paddr, DBENTRY *pdbentry)
|
||||
|
||||
void dbInitEntryFromRecord(struct dbCommon *prec, DBENTRY *pdbentry)
|
||||
{
|
||||
dbCommonPvt *ppvt = CONTAINER(prec, dbCommonPvt, common);
|
||||
dbCommonPvt *ppvt = dbRec2Pvt(prec);
|
||||
|
||||
memset((char *)pdbentry,'\0',sizeof(DBENTRY));
|
||||
|
||||
|
||||
@@ -204,6 +204,8 @@ struct dbr_alDouble {DBRalDouble};
|
||||
#define S_db_notInit (M_dbAccess|67) /*Not initialized*/
|
||||
#define S_db_bufFull (M_dbAccess|68) /*Buffer full*/
|
||||
|
||||
struct dbEntry;
|
||||
|
||||
epicsShareFunc long dbPutSpecial(struct dbAddr *paddr,int pass);
|
||||
epicsShareFunc rset * dbGetRset(const struct dbAddr *paddr);
|
||||
epicsShareFunc long dbPutAttribute(
|
||||
@@ -213,8 +215,29 @@ epicsShareFunc int dbGetFieldIndex(const struct dbAddr *paddr);
|
||||
epicsShareFunc long dbScanPassive(
|
||||
struct dbCommon *pfrom,struct dbCommon *pto);
|
||||
epicsShareFunc long dbProcess(struct dbCommon *precord);
|
||||
epicsShareFunc long dbNameToAddr(
|
||||
const char *pname,struct dbAddr *);
|
||||
epicsShareFunc 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().
|
||||
*/
|
||||
epicsShareFunc long dbEntryToAddr(const struct dbEntry *pdbentry,
|
||||
struct dbAddr *paddr);
|
||||
|
||||
/** Initialize DBENTRY from a valid dbAddr*
|
||||
* Constant time equivalent of dbInitEntry() then dbFindRecord(),
|
||||
* and finally dbFollowAlias().
|
||||
*/
|
||||
epicsShareFunc void dbInitEntryFromAddr(struct dbAddr *paddr,
|
||||
struct dbEntry *pdbentry);
|
||||
|
||||
/** Initialize DBENTRY from a valid record (dbCommon*)
|
||||
* Constant time equivalent of dbInitEntry() then dbFindRecord(),
|
||||
* and finally dbFollowAlias() when no field is specified.
|
||||
*/
|
||||
epicsShareFunc void dbInitEntryFromRecord(struct dbCommon *prec,
|
||||
struct dbEntry *pdbentry);
|
||||
|
||||
epicsShareFunc devSup* dbDTYPtoDevSup(dbRecordType *prdes, int dtyp);
|
||||
epicsShareFunc devSup* dbDSETtoDevSup(dbRecordType *prdes, struct dset *pdset);
|
||||
epicsShareFunc long dbGetField(
|
||||
|
||||
@@ -473,9 +473,7 @@ dbChannel * dbChannelCreate(const char *name)
|
||||
dbChannel *chan = NULL;
|
||||
char *cname;
|
||||
dbAddr *paddr;
|
||||
dbFldDes *pflddes;
|
||||
long status;
|
||||
short dbfType;
|
||||
|
||||
if (!name || !*name || !pdbbase)
|
||||
return NULL;
|
||||
@@ -498,32 +496,14 @@ dbChannel * dbChannelCreate(const char *name)
|
||||
ellInit(&chan->post_chain);
|
||||
|
||||
paddr = &chan->addr;
|
||||
pflddes = dbEntry.pflddes;
|
||||
dbfType = pflddes->field_type;
|
||||
|
||||
paddr->precord = dbEntry.precnode->precord;
|
||||
paddr->pfield = dbEntry.pfield;
|
||||
paddr->pfldDes = pflddes;
|
||||
paddr->no_elements = 1;
|
||||
paddr->field_type = dbfType;
|
||||
paddr->field_size = pflddes->size;
|
||||
paddr->special = pflddes->special;
|
||||
paddr->dbr_field_type = mapDBFToDBR[dbfType];
|
||||
|
||||
if (paddr->special == SPC_DBADDR) {
|
||||
rset *prset = dbGetRset(paddr);
|
||||
|
||||
/* Let record type modify paddr */
|
||||
if (prset && prset->cvt_dbaddr) {
|
||||
status = prset->cvt_dbaddr(paddr);
|
||||
if (status)
|
||||
goto finish;
|
||||
dbfType = paddr->field_type;
|
||||
}
|
||||
}
|
||||
status = dbEntryToAddr(&dbEntry, paddr);
|
||||
if (status)
|
||||
goto finish;
|
||||
|
||||
/* Handle field modifiers */
|
||||
if (*pname) {
|
||||
short dbfType = paddr->field_type;
|
||||
|
||||
if (*pname == '$') {
|
||||
/* Some field types can be accessed as char arrays */
|
||||
if (dbfType == DBF_STRING) {
|
||||
@@ -531,12 +511,14 @@ dbChannel * dbChannelCreate(const char *name)
|
||||
paddr->field_type = DBF_CHAR;
|
||||
paddr->field_size = 1;
|
||||
paddr->dbr_field_type = DBR_CHAR;
|
||||
} else if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) {
|
||||
}
|
||||
else if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) {
|
||||
/* Clients see a char array, but keep original dbfType */
|
||||
paddr->no_elements = PVLINK_STRINGSZ;
|
||||
paddr->field_size = 1;
|
||||
paddr->dbr_field_type = DBR_CHAR;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
status = S_dbLib_fieldNotFound;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
@@ -1,14 +1,27 @@
|
||||
#ifndef DBCOMMONPVT_H
|
||||
#define DBCOMMONPVT_H
|
||||
|
||||
#include <compilerDependencies.h>
|
||||
#include <dbDefs.h>
|
||||
#include "dbCommon.h"
|
||||
|
||||
struct epicsThreadOSD;
|
||||
|
||||
/** Base internal additional information for every record
|
||||
*/
|
||||
typedef struct dbCommonPvt {
|
||||
struct dbRecordNode *recnode;
|
||||
|
||||
/* Thread which is currently processing this record */
|
||||
struct epicsThreadOSD* procThread;
|
||||
|
||||
struct dbCommon common;
|
||||
} dbCommonPvt;
|
||||
|
||||
static EPICS_ALWAYS_INLINE
|
||||
dbCommonPvt* dbRec2Pvt(struct dbCommon *prec)
|
||||
{
|
||||
return CONTAINER(prec, dbCommonPvt, common);
|
||||
}
|
||||
|
||||
#endif // DBCOMMONPVT_H
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
#include "dbAddr.h"
|
||||
#include "dbBase.h"
|
||||
#include "dbBkpt.h"
|
||||
#include "dbCommon.h"
|
||||
#include "dbCommonPvt.h"
|
||||
#include "dbConvertFast.h"
|
||||
#include "dbConvert.h"
|
||||
#include "db_field_log.h"
|
||||
@@ -386,8 +386,11 @@ static long processTarget(dbCommon *psrc, dbCommon *pdst)
|
||||
{
|
||||
char context[40] = "";
|
||||
int trace = dbAccessDebugPUTF && *dbLockSetAddrTrace(psrc);
|
||||
int claim_src = dbRec2Pvt(psrc)->procThread==NULL;
|
||||
int claim_dst = psrc!=pdst && dbRec2Pvt(pdst)->procThread==NULL;
|
||||
long status;
|
||||
epicsUInt8 pact = psrc->pact;
|
||||
epicsThreadId self = epicsThreadGetIdSelf();
|
||||
|
||||
psrc->pact = TRUE;
|
||||
|
||||
@@ -406,14 +409,11 @@ static long processTarget(dbCommon *psrc, dbCommon *pdst)
|
||||
printf("%s: '%s' -> '%s' with PUTF=%u\n",
|
||||
context, psrc->name, pdst->name, psrc->putf);
|
||||
|
||||
if (pdst->putf)
|
||||
errlogPrintf("Warning: '%s.PUTF' found true with PACT false\n",
|
||||
pdst->name);
|
||||
|
||||
pdst->putf = psrc->putf;
|
||||
}
|
||||
else if (psrc->putf) {
|
||||
/* The dst record is busy (awaiting async reprocessing) and
|
||||
else if (psrc->putf && claim_dst) {
|
||||
/* The dst record is busy (awaiting async reprocessing),
|
||||
* not being processed recursively by us, and
|
||||
* we were originally triggered by a call to dbPutField(),
|
||||
* so we mark the dst record for reprocessing once the async
|
||||
* completion is over.
|
||||
@@ -426,17 +426,43 @@ static long processTarget(dbCommon *psrc, dbCommon *pdst)
|
||||
pdst->rpro = TRUE;
|
||||
}
|
||||
else {
|
||||
/* The dst record is busy, but we weren't triggered by a call
|
||||
* to dbPutField(). Do nothing.
|
||||
/* The dst record is busy, but either is being processed recursively,
|
||||
* or wasn't triggered by a call to dbPutField(). Do nothing.
|
||||
*/
|
||||
if (trace)
|
||||
printf("%s: '%s' -> Active '%s', done\n",
|
||||
context, psrc->name, pdst->name);
|
||||
}
|
||||
|
||||
if(claim_src) {
|
||||
dbRec2Pvt(psrc)->procThread = self;
|
||||
}
|
||||
if(claim_dst) {
|
||||
dbRec2Pvt(pdst)->procThread = self;
|
||||
}
|
||||
|
||||
if(dbRec2Pvt(psrc)->procThread!=self ||
|
||||
dbRec2Pvt(pdst)->procThread!=self) {
|
||||
errlogPrintf("Logic Error: processTarget 1 from %p, %s(%p) -> %s(%p)\n",
|
||||
self, psrc->name, dbRec2Pvt(psrc), pdst->name, dbRec2Pvt(pdst));
|
||||
}
|
||||
|
||||
status = dbProcess(pdst);
|
||||
|
||||
psrc->pact = pact;
|
||||
|
||||
if(dbRec2Pvt(psrc)->procThread!=self ||
|
||||
dbRec2Pvt(pdst)->procThread!=self) {
|
||||
errlogPrintf("Logic Error: processTarget 2 from %p, %s(%p) -> %s(%p)\n",
|
||||
self, psrc->name, dbRec2Pvt(psrc), pdst->name, dbRec2Pvt(pdst));
|
||||
}
|
||||
|
||||
if(claim_src) {
|
||||
dbRec2Pvt(psrc)->procThread = NULL;
|
||||
}
|
||||
if(claim_dst) {
|
||||
dbRec2Pvt(pdst)->procThread = NULL;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -49,7 +49,13 @@
|
||||
#include "link.h"
|
||||
#include "special.h"
|
||||
|
||||
#define EVENTSPERQUE 32
|
||||
/* Queue size based on Ethernet MTU of 1500 bytes.
|
||||
* Assume <=66 bytes of ethernet+IP+TCP overhead
|
||||
* and 40 byte CA messages (DBF_TIME_DOUBLE).
|
||||
*
|
||||
* (1500-66)/40 -> 35
|
||||
*/
|
||||
#define EVENTSPERQUE 36
|
||||
#define EVENTENTRIES 4 /* the number of que entries for each event */
|
||||
#define EVENTQUESIZE (EVENTENTRIES * EVENTSPERQUE)
|
||||
#define EVENTQEMPTY ((struct evSubscrip *)NULL)
|
||||
|
||||
@@ -86,12 +86,6 @@ typedef struct notifyGlobal {
|
||||
|
||||
static notifyGlobal *pnotifyGlobal = 0;
|
||||
|
||||
/*Local routines*/
|
||||
static void notifyInit(processNotify *ppn);
|
||||
static void notifyCleanup(processNotify *ppn);
|
||||
static void restartCheck(processNotifyRecord *ppnr);
|
||||
static void callDone(dbCommon *precord,processNotify *ppn);
|
||||
static void processNotifyCommon(processNotify *ppn,dbCommon *precord);
|
||||
static void notifyCallback(CALLBACK *pcallback);
|
||||
|
||||
#define ellSafeAdd(list,listnode) \
|
||||
@@ -210,7 +204,7 @@ static void callDone(dbCommon *precord, processNotify *ppn)
|
||||
return;
|
||||
}
|
||||
|
||||
static void processNotifyCommon(processNotify *ppn,dbCommon *precord)
|
||||
static void processNotifyCommon(processNotify *ppn, dbCommon *precord, int first)
|
||||
{
|
||||
notifyPvt *pnotifyPvt = (notifyPvt *) ppn->pnotifyPvt;
|
||||
int didPut = 0;
|
||||
@@ -256,6 +250,9 @@ static void processNotifyCommon(processNotify *ppn,dbCommon *precord)
|
||||
doProcess = 1;
|
||||
|
||||
if (doProcess) {
|
||||
if (first) {
|
||||
precord->putf = TRUE;
|
||||
}
|
||||
ppn->wasProcessed = 1;
|
||||
precord->ppn = ppn;
|
||||
ellSafeAdd(&pnotifyPvt->waitList, &precord->ppnr->waitNode);
|
||||
@@ -298,7 +295,7 @@ static void notifyCallback(CALLBACK *pcallback)
|
||||
return;
|
||||
}
|
||||
if(pnotifyPvt->state == notifyRestartCallbackRequested) {
|
||||
processNotifyCommon(ppn, precord);
|
||||
processNotifyCommon(ppn, precord, 0);
|
||||
return;
|
||||
}
|
||||
/* All done. Clean up and call userCallback */
|
||||
@@ -382,7 +379,7 @@ void dbProcessNotify(processNotify *ppn)
|
||||
precord->ppnr->precord = precord;
|
||||
ellInit(&precord->ppnr->restartList);
|
||||
}
|
||||
processNotifyCommon(ppn, precord);
|
||||
processNotifyCommon(ppn, precord, 1);
|
||||
}
|
||||
|
||||
void dbNotifyCancel(processNotify *ppn)
|
||||
@@ -582,7 +579,7 @@ static void tpnThread(void *pvt)
|
||||
processNotify *ppn = (processNotify *) ptpnInfo->ppn;
|
||||
|
||||
dbProcessNotify(ppn);
|
||||
epicsEventWait(ptpnInfo->callbackDone);
|
||||
epicsEventMustWait(ptpnInfo->callbackDone);
|
||||
dbNotifyCancel(ppn);
|
||||
epicsEventDestroy(ptpnInfo->callbackDone);
|
||||
dbChannelDelete(ppn->chan);
|
||||
|
||||
@@ -434,6 +434,10 @@ static void dbMenuHead(char *name)
|
||||
dbMenu *pdbMenu;
|
||||
GPHENTRY *pgphentry;
|
||||
|
||||
if (!*name) {
|
||||
yyerrorAbort("dbMenuHead: Menu name can't be empty");
|
||||
return;
|
||||
}
|
||||
pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->menuList);
|
||||
if(pgphentry) {
|
||||
duplicate = TRUE;
|
||||
@@ -447,6 +451,10 @@ static void dbMenuHead(char *name)
|
||||
|
||||
static void dbMenuChoice(char *name,char *value)
|
||||
{
|
||||
if (!*name) {
|
||||
yyerror("dbMenuChoice: Menu choice name can't be empty");
|
||||
return;
|
||||
}
|
||||
if(duplicate) return;
|
||||
allocTemp(epicsStrDup(name));
|
||||
allocTemp(epicsStrDup(value));
|
||||
@@ -494,6 +502,10 @@ static void dbRecordtypeHead(char *name)
|
||||
dbRecordType *pdbRecordType;
|
||||
GPHENTRY *pgphentry;
|
||||
|
||||
if (!*name) {
|
||||
yyerrorAbort("dbRecordtypeHead: Recordtype name can't be empty");
|
||||
return;
|
||||
}
|
||||
pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->recordTypeList);
|
||||
if(pgphentry) {
|
||||
duplicate = TRUE;
|
||||
@@ -512,6 +524,10 @@ static void dbRecordtypeFieldHead(char *name,char *type)
|
||||
dbFldDes *pdbFldDes;
|
||||
int i;
|
||||
|
||||
if (!*name) {
|
||||
yyerrorAbort("dbRecordtypeFieldHead: Field name can't be empty");
|
||||
return;
|
||||
}
|
||||
if(duplicate) return;
|
||||
pdbFldDes = dbCalloc(1,sizeof(dbFldDes));
|
||||
allocTemp(pdbFldDes);
|
||||
@@ -580,7 +596,7 @@ static void dbRecordtypeFieldItem(char *name,char *value)
|
||||
if(sscanf(value,"%hd",&pdbFldDes->special)==1) {
|
||||
return;
|
||||
}
|
||||
yyerror("Illegal special value.");
|
||||
yyerror("Illegal 'special' value.");
|
||||
return;
|
||||
}
|
||||
if(strcmp(name,"pp")==0) {
|
||||
@@ -589,13 +605,13 @@ static void dbRecordtypeFieldItem(char *name,char *value)
|
||||
} else if((strcmp(value,"NO")==0) || (strcmp(value,"FALSE")==0)) {
|
||||
pdbFldDes->process_passive = FALSE;
|
||||
} else {
|
||||
yyerror("Illegal value. Must be NO or YES");
|
||||
yyerror("Illegal 'pp' value, must be YES/NO/TRUE/FALSE");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(strcmp(name,"interest")==0) {
|
||||
if(sscanf(value,"%hd",&pdbFldDes->interest)!=1)
|
||||
yyerror("Illegal value. Must be integer");
|
||||
yyerror("Illegal 'interest' value, must be integer");
|
||||
return;
|
||||
}
|
||||
if(strcmp(name,"base")==0) {
|
||||
@@ -604,13 +620,13 @@ static void dbRecordtypeFieldItem(char *name,char *value)
|
||||
} else if(strcmp(value,"HEX")==0) {
|
||||
pdbFldDes->base = CT_HEX;
|
||||
} else {
|
||||
yyerror("Illegal value. Must be CT_DECIMAL or CT_HEX");
|
||||
yyerror("Illegal 'base' value, must be DECIMAL/HEX");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(strcmp(name,"size")==0) {
|
||||
if(sscanf(value,"%hd",&pdbFldDes->size)!=1)
|
||||
yyerror("Illegal value. Must be integer");
|
||||
yyerror("Illegal 'size' value, must be integer");
|
||||
return;
|
||||
}
|
||||
if(strcmp(name,"extra")==0) {
|
||||
@@ -802,6 +818,10 @@ static void dbDriver(char *name)
|
||||
drvSup *pdrvSup;
|
||||
GPHENTRY *pgphentry;
|
||||
|
||||
if (!*name) {
|
||||
yyerrorAbort("dbDriver: Driver name can't be empty");
|
||||
return;
|
||||
}
|
||||
pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->drvList);
|
||||
if(pgphentry) {
|
||||
return;
|
||||
@@ -841,6 +861,10 @@ static void dbRegistrar(char *name)
|
||||
dbText *ptext;
|
||||
GPHENTRY *pgphentry;
|
||||
|
||||
if (!*name) {
|
||||
yyerrorAbort("dbRegistrar: Registrar name can't be empty");
|
||||
return;
|
||||
}
|
||||
pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->registrarList);
|
||||
if(pgphentry) {
|
||||
return;
|
||||
@@ -860,6 +884,10 @@ static void dbFunction(char *name)
|
||||
dbText *ptext;
|
||||
GPHENTRY *pgphentry;
|
||||
|
||||
if (!*name) {
|
||||
yyerrorAbort("dbFunction: Function name can't be empty");
|
||||
return;
|
||||
}
|
||||
pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->functionList);
|
||||
if(pgphentry) {
|
||||
return;
|
||||
@@ -879,6 +907,10 @@ static void dbVariable(char *name, char *type)
|
||||
dbVariableDef *pvar;
|
||||
GPHENTRY *pgphentry;
|
||||
|
||||
if (!*name) {
|
||||
yyerrorAbort("dbVariable: Variable name can't be empty");
|
||||
return;
|
||||
}
|
||||
pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->variableList);
|
||||
if(pgphentry) {
|
||||
return;
|
||||
@@ -899,6 +931,10 @@ static void dbBreakHead(char *name)
|
||||
brkTable *pbrkTable;
|
||||
GPHENTRY *pgphentry;
|
||||
|
||||
if (!*name) {
|
||||
yyerrorAbort("dbBreakHead: Breaktable name can't be empty");
|
||||
return;
|
||||
}
|
||||
pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->bptList);
|
||||
if(pgphentry) {
|
||||
duplicate = TRUE;
|
||||
@@ -1001,6 +1037,10 @@ static void dbRecordHead(char *recordType, char *name, int visible)
|
||||
DBENTRY *pdbentry;
|
||||
long status;
|
||||
|
||||
if (!*name) {
|
||||
yyerrorAbort("dbRecordHead: Record name can't be empty");
|
||||
return;
|
||||
}
|
||||
badch = strpbrk(name, " \"'.$");
|
||||
if (badch) {
|
||||
epicsPrintf("Bad character '%c' in record name \"%s\"\n",
|
||||
@@ -1108,6 +1148,10 @@ static void dbRecordInfo(char *name, char *value)
|
||||
tempListNode *ptempListNode;
|
||||
long status;
|
||||
|
||||
if (!*name) {
|
||||
yyerrorAbort("dbRecordInfo: Info item name can't be empty");
|
||||
return;
|
||||
}
|
||||
if (duplicate) return;
|
||||
ptempListNode = (tempListNode *)ellFirst(&tempList);
|
||||
pdbentry = ptempListNode->item;
|
||||
@@ -1132,6 +1176,10 @@ static void dbRecordAlias(char *name)
|
||||
tempListNode *ptempListNode;
|
||||
long status;
|
||||
|
||||
if (!*name) {
|
||||
yyerrorAbort("dbRecordAlias: Alias name can't be empty");
|
||||
return;
|
||||
}
|
||||
if (duplicate) return;
|
||||
ptempListNode = (tempListNode *)ellFirst(&tempList);
|
||||
pdbentry = ptempListNode->item;
|
||||
@@ -1149,6 +1197,10 @@ static void dbAlias(char *name, char *alias)
|
||||
DBENTRY dbEntry;
|
||||
DBENTRY *pdbEntry = &dbEntry;
|
||||
|
||||
if (!*alias) {
|
||||
yyerrorAbort("dbAlias: Alias name can't be empty");
|
||||
return;
|
||||
}
|
||||
dbInitEntry(pdbbase, pdbEntry);
|
||||
if (dbFindRecord(pdbEntry, name)) {
|
||||
epicsPrintf("Alias \"%s\" refers to unknown record \"%s\"\n",
|
||||
|
||||
@@ -79,16 +79,6 @@ epicsShareDef maplinkType pamaplinkType[LINK_NTYPES] = {
|
||||
{"VXI_IO",VXI_IO}
|
||||
};
|
||||
|
||||
static int mapDBFtoDCT[DBF_NOACCESS+1] = {
|
||||
DCT_STRING,
|
||||
DCT_INTEGER,DCT_INTEGER,DCT_INTEGER,DCT_INTEGER,DCT_INTEGER,DCT_INTEGER,
|
||||
DCT_REAL,DCT_REAL,
|
||||
DCT_INTEGER,
|
||||
DCT_MENU,
|
||||
DCT_MENUFORM,
|
||||
DCT_INLINK,DCT_OUTLINK,DCT_FWDLINK,
|
||||
DCT_NOACCESS};
|
||||
|
||||
/*forward references for private routines*/
|
||||
static void dbMsgPrint(DBENTRY *pdbentry, const char *fmt, ...)
|
||||
EPICS_PRINTF_STYLE(2,3);
|
||||
@@ -166,38 +156,6 @@ void dbFreePath(DBBASE *pdbbase)
|
||||
}
|
||||
|
||||
|
||||
static void entryErrMessage(DBENTRY *pdbentry,long status,char *mess)
|
||||
{
|
||||
char message[200];
|
||||
char *pmessage=&message[0];
|
||||
dbRecordNode *precnode = pdbentry->precnode;
|
||||
dbFldDes *pflddes = pdbentry->pflddes;
|
||||
char *pname = NULL;
|
||||
|
||||
*pmessage=0;
|
||||
if(pdbentry->precordType) pname = pdbentry->precordType->name;
|
||||
if(pname) {
|
||||
strcat(pmessage,"RecordType:");
|
||||
strcat(pmessage,pname);
|
||||
}
|
||||
if(precnode){
|
||||
if (dbIsAlias(pdbentry))
|
||||
strcat(pmessage," Record Alias:");
|
||||
else
|
||||
strcat(pmessage," Record:");
|
||||
strcat(pmessage,(char *)precnode->precord);
|
||||
}
|
||||
if(pflddes) {
|
||||
char *pstr=pflddes->name;
|
||||
|
||||
strcat(pmessage," Field:");
|
||||
strcat(pmessage,pstr);
|
||||
}
|
||||
strcat(pmessage,"\n");
|
||||
strcat(pmessage,mess);
|
||||
errMessage(status,pmessage);
|
||||
}
|
||||
|
||||
static void zeroDbentry(DBENTRY *pdbentry)
|
||||
{
|
||||
/*NOTE that pdbbase and message MUST NOT be set to NULL*/
|
||||
@@ -1398,19 +1356,6 @@ long dbNextField(DBENTRY *pdbentry,int dctonly)
|
||||
}
|
||||
}
|
||||
|
||||
int dbGetFieldType(DBENTRY *pdbentry)
|
||||
{
|
||||
dbFldDes *pflddes = pdbentry->pflddes;
|
||||
long status;
|
||||
|
||||
if(!pflddes){
|
||||
status = S_dbLib_flddesNotFound;
|
||||
entryErrMessage(pdbentry,status,"dbGetFieldType");
|
||||
return(status);
|
||||
}
|
||||
return(mapDBFtoDCT[pflddes->field_type]);
|
||||
}
|
||||
|
||||
int dbGetNFields(DBENTRY *pdbentry,int dctonly)
|
||||
{
|
||||
dbRecordType *precordType = pdbentry->precordType;
|
||||
@@ -3216,48 +3161,21 @@ int dbGetNLinks(DBENTRY *pdbentry)
|
||||
return((int)precordType->no_links);
|
||||
}
|
||||
|
||||
long dbGetLinkField(DBENTRY *pdbentry,int index)
|
||||
long dbGetLinkField(DBENTRY *pdbentry, int index)
|
||||
{
|
||||
dbRecordType *precordType = pdbentry->precordType;
|
||||
dbFldDes *pflddes;
|
||||
dbRecordType *precordType = pdbentry->precordType;
|
||||
dbFldDes *pflddes;
|
||||
|
||||
if (!precordType)
|
||||
return S_dbLib_recordTypeNotFound;
|
||||
|
||||
if (index < 0 || index >= precordType->no_links)
|
||||
return S_dbLib_badLink;
|
||||
|
||||
if(!precordType) return(S_dbLib_recordTypeNotFound);
|
||||
if(index<0 || index>=precordType->no_links) return(S_dbLib_badLink);
|
||||
pdbentry->indfield = precordType->link_ind[index];
|
||||
pdbentry->pflddes = pflddes = precordType->papFldDes[pdbentry->indfield];
|
||||
dbGetFieldAddress(pdbentry);
|
||||
return(0);
|
||||
}
|
||||
|
||||
int dbGetLinkType(DBENTRY *pdbentry)
|
||||
{
|
||||
dbFldDes *pflddes;
|
||||
DBLINK *plink;
|
||||
int field_type;
|
||||
|
||||
dbGetFieldAddress(pdbentry);
|
||||
pflddes = pdbentry->pflddes;
|
||||
if(!pflddes) return(-1);
|
||||
plink = (DBLINK *)pdbentry->pfield;
|
||||
if(!plink) return(-1);
|
||||
field_type = pflddes->field_type;
|
||||
switch (field_type) {
|
||||
case DBF_INLINK:
|
||||
case DBF_OUTLINK:
|
||||
case DBF_FWDLINK:
|
||||
switch(plink->type) {
|
||||
case CONSTANT:
|
||||
return(DCT_LINK_CONSTANT);
|
||||
case PV_LINK:
|
||||
case PN_LINK:
|
||||
case DB_LINK:
|
||||
case CA_LINK:
|
||||
return(DCT_LINK_PV);
|
||||
default:
|
||||
return(DCT_LINK_FORM);
|
||||
}
|
||||
}
|
||||
return(-1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dbDumpPath(DBBASE *pdbbase)
|
||||
@@ -3556,68 +3474,110 @@ void dbDumpBreaktable(DBBASE *pdbbase,const char *name)
|
||||
return;
|
||||
}
|
||||
|
||||
static char *bus[VXI_IO+1] = {"","","VME","CAMAC","AB",
|
||||
"GPIB","BITBUS","","","","","","INST","BBGPIB","VXI"};
|
||||
void dbReportDeviceConfig(dbBase *pdbbase,FILE *report)
|
||||
static char *bus[LINK_NTYPES] = {
|
||||
"", /* CONSTANT */
|
||||
NULL, /* PV_LINK */
|
||||
"VME",
|
||||
"CAMAC",
|
||||
"AB",
|
||||
"GPIB",
|
||||
"BITBUS",
|
||||
NULL, /* MACRO_LINK */
|
||||
NULL, /* JSON_LINK */
|
||||
NULL, /* PN_LINK */
|
||||
NULL, /* DB_LINK */
|
||||
NULL, /* CA_LINK */
|
||||
"INST",
|
||||
"BBGPIB",
|
||||
"VXI"
|
||||
};
|
||||
void dbReportDeviceConfig(dbBase *pdbbase, FILE *report)
|
||||
{
|
||||
DBENTRY dbentry;
|
||||
DBENTRY *pdbentry=&dbentry;
|
||||
long status;
|
||||
char linkValue[messagesize];
|
||||
char dtypValue[50];
|
||||
char cvtValue[40];
|
||||
int ilink,nlinks;
|
||||
struct link *plink;
|
||||
int linkType;
|
||||
FILE *stream = (report==0) ? stdout : report;
|
||||
DBENTRY dbentry, *pdbentry = &dbentry;
|
||||
long status;
|
||||
FILE *stream = report ? report : stdout;
|
||||
|
||||
if(!pdbbase) {
|
||||
fprintf(stderr,"pdbbase not specified\n");
|
||||
return;
|
||||
if (!pdbbase) {
|
||||
fprintf(stderr, "dbReportDeviceConfig: pdbbase not specified\n");
|
||||
return;
|
||||
}
|
||||
|
||||
dbInitEntry(pdbbase,pdbentry);
|
||||
status = dbFirstRecordType(pdbentry);
|
||||
while(!status) {
|
||||
status = dbFirstRecord(pdbentry);
|
||||
while(!status) {
|
||||
nlinks = dbGetNLinks(pdbentry);
|
||||
for(ilink=0; ilink<nlinks; ilink++) {
|
||||
status = dbGetLinkField(pdbentry,ilink);
|
||||
if(status || dbGetLinkType(pdbentry)!=DCT_LINK_FORM) continue;
|
||||
plink = pdbentry->pfield;
|
||||
linkType = plink->type;
|
||||
if(bus[linkType][0]==0) continue;
|
||||
strncpy(linkValue, dbGetString(pdbentry), NELEMENTS(linkValue)-1);
|
||||
linkValue[NELEMENTS(linkValue)-1] = '\0';
|
||||
status = dbFindField(pdbentry,"DTYP");
|
||||
if(status) break;
|
||||
strcpy(dtypValue,dbGetString(pdbentry));
|
||||
status = dbFindField(pdbentry,"LINR");
|
||||
if(status) {
|
||||
cvtValue[0] = 0;
|
||||
} else {
|
||||
if(strcmp(dbGetString(pdbentry),"LINEAR")!=0) {
|
||||
cvtValue[0] = 0;
|
||||
} else {
|
||||
strcpy(cvtValue,"cvt(");
|
||||
status = dbFindField(pdbentry,"EGUL");
|
||||
if(!status) strcat(cvtValue,dbGetString(pdbentry));
|
||||
status = dbFindField(pdbentry,"EGUF");
|
||||
if(!status) {
|
||||
strcat(cvtValue,",");
|
||||
strcat(cvtValue,dbGetString(pdbentry));
|
||||
}
|
||||
strcat(cvtValue,")");
|
||||
}
|
||||
}
|
||||
fprintf(stream,"%-8s %-20s %-20s %-20s %-s\n",
|
||||
bus[linkType],linkValue,dtypValue,
|
||||
dbGetRecordName(pdbentry),cvtValue);
|
||||
break;
|
||||
}
|
||||
status = dbNextRecord(pdbentry);
|
||||
}
|
||||
status = dbNextRecordType(pdbentry);
|
||||
while (!status) {
|
||||
const int nlinks = dbGetNLinks(pdbentry);
|
||||
|
||||
status = dbFirstRecord(pdbentry);
|
||||
while (!status) {
|
||||
int ilink;
|
||||
|
||||
for (ilink=0; ilink<nlinks; ilink++) {
|
||||
char linkValue[messagesize];
|
||||
char dtypValue[50];
|
||||
char cvtValue[40];
|
||||
struct link *plink;
|
||||
int linkType;
|
||||
|
||||
status = dbGetLinkField(pdbentry, ilink);
|
||||
if (status)
|
||||
continue;
|
||||
|
||||
plink = pdbentry->pfield;
|
||||
linkType = plink->type;
|
||||
if (plink->text) { /* Not yet parsed */
|
||||
dbLinkInfo linfo;
|
||||
|
||||
if (dbParseLink(plink->text, pdbentry->pflddes->field_type, &linfo))
|
||||
continue;
|
||||
|
||||
linkType = linfo.ltype;
|
||||
if (linkType && bus[linkType])
|
||||
strncpy(linkValue, plink->text, messagesize-1);
|
||||
|
||||
dbFreeLinkInfo(&linfo);
|
||||
}
|
||||
else {
|
||||
strncpy(linkValue, dbGetString(pdbentry), messagesize-1);
|
||||
}
|
||||
|
||||
if (!linkType || !bus[linkType])
|
||||
continue;
|
||||
linkValue[messagesize-1] = '\0';
|
||||
|
||||
status = dbFindField(pdbentry, "DTYP");
|
||||
if (status)
|
||||
break; /* Next record type */
|
||||
|
||||
strcpy(dtypValue, dbGetString(pdbentry));
|
||||
status = dbFindField(pdbentry, "LINR");
|
||||
if (status) {
|
||||
cvtValue[0] = 0;
|
||||
}
|
||||
else {
|
||||
if (strcmp(dbGetString(pdbentry), "LINEAR") != 0) {
|
||||
cvtValue[0] = 0;
|
||||
}
|
||||
else {
|
||||
strcpy(cvtValue,"cvt(");
|
||||
status = dbFindField(pdbentry, "EGUL");
|
||||
if (!status)
|
||||
strcat(cvtValue, dbGetString(pdbentry));
|
||||
status = dbFindField(pdbentry, "EGUF");
|
||||
if (!status) {
|
||||
strcat(cvtValue, ",");
|
||||
strcat(cvtValue, dbGetString(pdbentry));
|
||||
}
|
||||
strcat(cvtValue, ")");
|
||||
}
|
||||
}
|
||||
fprintf(stream,"%-8s %-20s %-20s %-20s %-s\n",
|
||||
bus[linkType], linkValue, dtypValue,
|
||||
dbGetRecordName(pdbentry), cvtValue);
|
||||
break;
|
||||
}
|
||||
status = dbNextRecord(pdbentry);
|
||||
}
|
||||
status = dbNextRecordType(pdbentry);
|
||||
}
|
||||
dbFinishEntry(pdbentry);
|
||||
finishOutstream(stream);
|
||||
|
||||
@@ -28,25 +28,9 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*Field types as seen by static database access clients*/
|
||||
#define DCT_STRING 0
|
||||
#define DCT_INTEGER 1
|
||||
#define DCT_REAL 2
|
||||
#define DCT_MENU 3
|
||||
#define DCT_MENUFORM 4
|
||||
#define DCT_INLINK 5
|
||||
#define DCT_OUTLINK 6
|
||||
#define DCT_FWDLINK 7
|
||||
#define DCT_NOACCESS 8
|
||||
|
||||
/*Link types as seen by static database access clients*/
|
||||
#define DCT_LINK_CONSTANT 0
|
||||
#define DCT_LINK_FORM 1
|
||||
#define DCT_LINK_PV 2
|
||||
|
||||
typedef dbBase DBBASE;
|
||||
|
||||
typedef struct{
|
||||
typedef struct dbEntry {
|
||||
DBBASE *pdbbase;
|
||||
dbRecordType *precordType;
|
||||
dbFldDes *pflddes;
|
||||
@@ -57,9 +41,6 @@ typedef struct{
|
||||
short indfield;
|
||||
} DBENTRY;
|
||||
|
||||
struct dbAddr;
|
||||
struct dbCommon;
|
||||
|
||||
/* Static database access routines*/
|
||||
epicsShareFunc DBBASE * dbAllocBase(void);
|
||||
epicsShareFunc void dbFreeBase(DBBASE *pdbbase);
|
||||
@@ -68,18 +49,6 @@ epicsShareFunc void dbFreeEntry(DBENTRY *pdbentry);
|
||||
epicsShareFunc void dbInitEntry(DBBASE *pdbbase,
|
||||
DBENTRY *pdbentry);
|
||||
|
||||
/** Initialize DBENTRY from a valid dbAddr*.
|
||||
* Constant time equivalent of dbInitEntry() then dbFindRecord(),
|
||||
* and finally dbFollowAlias()
|
||||
*/
|
||||
epicsShareFunc void dbInitEntryFromAddr(struct dbAddr *paddr, DBENTRY *pdbentry);
|
||||
|
||||
/** Initialize DBENTRY from a valid record (dbCommon*).
|
||||
* Constant time equivalent of dbInitEntry() then dbFindRecord(),
|
||||
* and finally dbFollowAlias() when no field is specified.
|
||||
*/
|
||||
epicsShareFunc void dbInitEntryFromRecord(struct dbCommon *prec, DBENTRY *pdbentry);
|
||||
|
||||
epicsShareFunc void dbFinishEntry(DBENTRY *pdbentry);
|
||||
epicsShareFunc DBENTRY * dbCopyEntry(DBENTRY *pdbentry);
|
||||
epicsShareFunc void dbCopyEntryContents(DBENTRY *pfrom,
|
||||
@@ -139,7 +108,6 @@ epicsShareFunc long dbGetAttributePart(DBENTRY *pdbentry,
|
||||
|
||||
epicsShareFunc long dbFirstField(DBENTRY *pdbentry, int dctonly);
|
||||
epicsShareFunc long dbNextField(DBENTRY *pdbentry, int dctonly);
|
||||
epicsShareFunc int dbGetFieldType(DBENTRY *pdbentry);
|
||||
epicsShareFunc int dbGetNFields(DBENTRY *pdbentry, int dctonly);
|
||||
epicsShareFunc char * dbGetFieldName(DBENTRY *pdbentry);
|
||||
epicsShareFunc char * dbGetDefault(DBENTRY *pdbentry);
|
||||
@@ -231,7 +199,6 @@ epicsShareFunc linkSup * dbFindLinkSup(dbBase *pdbbase,
|
||||
|
||||
epicsShareFunc int dbGetNLinks(DBENTRY *pdbentry);
|
||||
epicsShareFunc long dbGetLinkField(DBENTRY *pdbentry, int index);
|
||||
epicsShareFunc int dbGetLinkType(DBENTRY *pdbentry);
|
||||
|
||||
/* Dump routines */
|
||||
epicsShareFunc void dbDumpPath(DBBASE *pdbbase);
|
||||
|
||||
@@ -179,7 +179,7 @@ long dbFreeRecord(DBENTRY *pdbentry)
|
||||
if(!pdbRecordType) return(S_dbLib_recordTypeNotFound);
|
||||
if(!precnode) return(S_dbLib_recNotFound);
|
||||
if(!precnode->precord) return(S_dbLib_recNotFound);
|
||||
free(CONTAINER(precnode->precord, dbCommonPvt, common));
|
||||
free(dbRec2Pvt(precnode->precord));
|
||||
precnode->precord = NULL;
|
||||
return(0);
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ SRC_DIRS += $(IOCDIR)/dbtemplate
|
||||
|
||||
PROD_HOST += msi
|
||||
|
||||
msi_SRCS = msi.c
|
||||
msi_SRCS = msi.cpp
|
||||
msi_LIBS += Com
|
||||
HTMLS += msi.html
|
||||
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
|
||||
/* msi - macro substitutions and include */
|
||||
|
||||
#include <string>
|
||||
#include <list>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
@@ -18,7 +21,6 @@
|
||||
|
||||
#include <dbDefs.h>
|
||||
#include <macLib.h>
|
||||
#include <ellLib.h>
|
||||
#include <errlog.h>
|
||||
#include <epicsString.h>
|
||||
#include <osiFileName.h>
|
||||
@@ -56,32 +58,35 @@ int din = 0;
|
||||
typedef struct inputData inputData;
|
||||
|
||||
static void inputConstruct(inputData **ppvt);
|
||||
static void inputDestruct(inputData *pvt);
|
||||
static void inputAddPath(inputData *pvt, char *pval);
|
||||
static void inputBegin(inputData *pvt, char *fileName);
|
||||
static char *inputNextLine(inputData *pvt);
|
||||
static void inputNewIncludeFile(inputData *pvt, char *name);
|
||||
static void inputErrPrint(inputData *pvt);
|
||||
static void inputDestruct(inputData * const pvt);
|
||||
static void inputAddPath(inputData * const pvt, const char * const pval);
|
||||
static void inputBegin(inputData * const pvt, const char * const fileName);
|
||||
static char *inputNextLine(inputData * const pvt);
|
||||
static void inputNewIncludeFile(inputData * const pvt, const char * const name);
|
||||
static void inputErrPrint(const inputData * const pvt);
|
||||
|
||||
/* Module to read the substitution file */
|
||||
typedef struct subInfo subInfo;
|
||||
|
||||
static void substituteOpen(subInfo **ppvt, char *substitutionName);
|
||||
static void substituteDestruct(subInfo *pvt);
|
||||
static int substituteGetNextSet(subInfo *pvt, char **filename);
|
||||
static int substituteGetGlobalSet(subInfo *pvt);
|
||||
static char *substituteGetReplacements(subInfo *pvt);
|
||||
static char *substituteGetGlobalReplacements(subInfo *pvt);
|
||||
static void substituteOpen(subInfo **ppvt, const std::string& substitutionName);
|
||||
static void substituteDestruct(subInfo * const pvt);
|
||||
static bool substituteGetNextSet(subInfo * const pvt, char **filename);
|
||||
static bool substituteGetGlobalSet(subInfo * const pvt);
|
||||
static const char *substituteGetReplacements(subInfo * const pvt);
|
||||
static const char *substituteGetGlobalReplacements(subInfo * const pvt);
|
||||
|
||||
/* Forward references to local routines */
|
||||
static void usageExit(int status);
|
||||
static void abortExit(int status);
|
||||
static void addMacroReplacements(MAC_HANDLE *macPvt, char *pval);
|
||||
static void makeSubstitutions(inputData *inputPvt, MAC_HANDLE *macPvt, char *templateName);
|
||||
static void usageExit(const int status);
|
||||
static void abortExit(const int status);
|
||||
static void addMacroReplacements(MAC_HANDLE * const macPvt,
|
||||
const char * const pval);
|
||||
static void makeSubstitutions(inputData * const inputPvt,
|
||||
MAC_HANDLE * const macPvt,
|
||||
const char * const templateName);
|
||||
|
||||
/*Global variables */
|
||||
static int opt_V = 0;
|
||||
static int opt_D = 0;
|
||||
static bool opt_D = false;
|
||||
|
||||
static char *outFile = 0;
|
||||
static int numDeps = 0, depHashes[MAX_DEPS];
|
||||
@@ -92,23 +97,21 @@ int main(int argc,char **argv)
|
||||
inputData *inputPvt;
|
||||
MAC_HANDLE *macPvt;
|
||||
char *pval;
|
||||
int narg;
|
||||
char *substitutionName = 0;
|
||||
std::string substitutionName;
|
||||
char *templateName = 0;
|
||||
int i;
|
||||
int localScope = 1;
|
||||
bool localScope = true;
|
||||
|
||||
inputConstruct(&inputPvt);
|
||||
macCreateHandle(&macPvt, 0);
|
||||
while ((argc > 1) && (argv[1][0] == '-')) {
|
||||
narg = (strlen(argv[1]) == 2) ? 2 : 1;
|
||||
int narg = (strlen(argv[1]) == 2) ? 2 : 1;
|
||||
pval = (narg == 1) ? (argv[1] + 2) : argv[2];
|
||||
|
||||
if (strncmp(argv[1], "-I", 2) == 0) {
|
||||
inputAddPath(inputPvt, pval);
|
||||
}
|
||||
else if (strcmp(argv[1], "-D") == 0) {
|
||||
opt_D = 1;
|
||||
opt_D = true;
|
||||
narg = 1; /* no argument for this option */
|
||||
}
|
||||
else if(strncmp(argv[1], "-o", 2) == 0) {
|
||||
@@ -118,14 +121,14 @@ int main(int argc,char **argv)
|
||||
addMacroReplacements(macPvt, pval);
|
||||
}
|
||||
else if(strncmp(argv[1], "-S", 2) == 0) {
|
||||
substitutionName = epicsStrDup(pval);
|
||||
substitutionName = pval;
|
||||
}
|
||||
else if (strcmp(argv[1], "-V") == 0) {
|
||||
opt_V = 1;
|
||||
narg = 1; /* no argument for this option */
|
||||
}
|
||||
else if (strcmp(argv[1], "-g") == 0) {
|
||||
localScope = 0;
|
||||
localScope = false;
|
||||
narg = 1; /* no argument for this option */
|
||||
}
|
||||
else if (strcmp(argv[1], "-h") == 0) {
|
||||
@@ -137,7 +140,7 @@ int main(int argc,char **argv)
|
||||
}
|
||||
|
||||
argc -= narg;
|
||||
for (i = 1; i < argc; i++)
|
||||
for (int i = 1; i < argc; i++)
|
||||
argv[i] = argv[i + narg];
|
||||
}
|
||||
|
||||
@@ -165,24 +168,24 @@ int main(int argc,char **argv)
|
||||
if (argc == 2)
|
||||
templateName = epicsStrDup(argv[1]);
|
||||
|
||||
if (!substitutionName) {
|
||||
if (substitutionName.empty()) {
|
||||
STEP("Single template+substitutions file");
|
||||
makeSubstitutions(inputPvt, macPvt, templateName);
|
||||
}
|
||||
else {
|
||||
subInfo *substitutePvt;
|
||||
char *filename = 0;
|
||||
int isGlobal, isFile;
|
||||
bool isGlobal, isFile;
|
||||
|
||||
STEPS("Substitutions from file", substitutionName);
|
||||
STEPS("Substitutions from file", substitutionName.c_str());
|
||||
substituteOpen(&substitutePvt, substitutionName);
|
||||
do {
|
||||
isGlobal = substituteGetGlobalSet(substitutePvt);
|
||||
if (isGlobal) {
|
||||
STEP("Handling global macros");
|
||||
pval = substituteGetGlobalReplacements(substitutePvt);
|
||||
if (pval)
|
||||
addMacroReplacements(macPvt, pval);
|
||||
const char *macStr = substituteGetGlobalReplacements(substitutePvt);
|
||||
if (macStr)
|
||||
addMacroReplacements(macPvt, macStr);
|
||||
}
|
||||
else if ((isFile = substituteGetNextSet(substitutePvt, &filename))) {
|
||||
if (templateName)
|
||||
@@ -193,11 +196,12 @@ int main(int argc,char **argv)
|
||||
}
|
||||
|
||||
STEPS("Handling template file", filename);
|
||||
while ((pval = substituteGetReplacements(substitutePvt))) {
|
||||
const char *macStr;
|
||||
while ((macStr = substituteGetReplacements(substitutePvt))) {
|
||||
if (localScope)
|
||||
macPushScope(macPvt);
|
||||
|
||||
addMacroReplacements(macPvt, pval);
|
||||
addMacroReplacements(macPvt, macStr);
|
||||
makeSubstitutions(inputPvt, macPvt, filename);
|
||||
|
||||
if (localScope)
|
||||
@@ -207,18 +211,18 @@ int main(int argc,char **argv)
|
||||
} while (isGlobal || isFile);
|
||||
substituteDestruct(substitutePvt);
|
||||
}
|
||||
errlogFlush();
|
||||
macDeleteHandle(macPvt);
|
||||
errlogFlush(); // macLib calls errlogPrintf()
|
||||
inputDestruct(inputPvt);
|
||||
if (opt_D) {
|
||||
printf("\n");
|
||||
}
|
||||
fflush(stdout);
|
||||
free(templateName);
|
||||
free(substitutionName);
|
||||
return opt_V & 2;
|
||||
}
|
||||
|
||||
void usageExit(int status)
|
||||
void usageExit(const int status)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"Usage: msi [options] [template]\n"
|
||||
@@ -236,7 +240,7 @@ void usageExit(int status)
|
||||
exit(status);
|
||||
}
|
||||
|
||||
void abortExit(int status)
|
||||
void abortExit(const int status)
|
||||
{
|
||||
if (outFile) {
|
||||
fclose(stdout);
|
||||
@@ -245,7 +249,8 @@ void abortExit(int status)
|
||||
exit(status);
|
||||
}
|
||||
|
||||
static void addMacroReplacements(MAC_HANDLE *macPvt, char *pval)
|
||||
static void addMacroReplacements(MAC_HANDLE * const macPvt,
|
||||
const char * const pval)
|
||||
{
|
||||
char **pairs;
|
||||
long status;
|
||||
@@ -268,7 +273,9 @@ static void addMacroReplacements(MAC_HANDLE *macPvt, char *pval)
|
||||
typedef enum {cmdInclude,cmdSubstitute} cmdType;
|
||||
static const char *cmdNames[] = {"include","substitute"};
|
||||
|
||||
static void makeSubstitutions(inputData *inputPvt, MAC_HANDLE *macPvt, char *templateName)
|
||||
static void makeSubstitutions(inputData * const inputPvt,
|
||||
MAC_HANDLE * const macPvt,
|
||||
const char * const templateName)
|
||||
{
|
||||
char *input;
|
||||
static char buffer[MAX_BUFFER_SIZE];
|
||||
@@ -292,7 +299,6 @@ static void makeSubstitutions(inputData *inputPvt, MAC_HANDLE *macPvt, char *tem
|
||||
if (command) {
|
||||
char *pstart;
|
||||
char *pend;
|
||||
char *copy;
|
||||
int cmdind=-1;
|
||||
int i;
|
||||
|
||||
@@ -325,16 +331,15 @@ static void makeSubstitutions(inputData *inputPvt, MAC_HANDLE *macPvt, char *tem
|
||||
/*skip quote and any trailing blanks*/
|
||||
while (*++p == ' ') ;
|
||||
if (*p != '\n' && *p != 0) goto endcmd;
|
||||
copy = calloc(pend-pstart + 1, sizeof(char));
|
||||
strncpy(copy, pstart, pend-pstart);
|
||||
std::string copy = std::string(pstart, pend);
|
||||
|
||||
switch(cmdind) {
|
||||
case cmdInclude:
|
||||
inputNewIncludeFile(inputPvt,copy);
|
||||
inputNewIncludeFile(inputPvt, copy.c_str());
|
||||
break;
|
||||
|
||||
case cmdSubstitute:
|
||||
addMacroReplacements(macPvt,copy);
|
||||
addMacroReplacements(macPvt, copy.c_str());
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -342,7 +347,6 @@ static void makeSubstitutions(inputData *inputPvt, MAC_HANDLE *macPvt, char *tem
|
||||
inputErrPrint(inputPvt);
|
||||
abortExit(1);
|
||||
}
|
||||
free(copy);
|
||||
expand = 0;
|
||||
}
|
||||
|
||||
@@ -361,94 +365,72 @@ endcmd:
|
||||
}
|
||||
|
||||
typedef struct inputFile {
|
||||
ELLNODE node;
|
||||
char *filename;
|
||||
std::string filename;
|
||||
FILE *fp;
|
||||
int lineNum;
|
||||
} inputFile;
|
||||
|
||||
typedef struct pathNode {
|
||||
ELLNODE node;
|
||||
char *directory;
|
||||
} pathNode;
|
||||
|
||||
struct inputData {
|
||||
ELLLIST inputFileList;
|
||||
ELLLIST pathList;
|
||||
std::list<inputFile> inputFileList;
|
||||
std::list<std::string> pathList;
|
||||
char inputBuffer[MAX_BUFFER_SIZE];
|
||||
inputData() { memset(inputBuffer, 0, sizeof(inputBuffer) * sizeof(inputBuffer[0])); };
|
||||
};
|
||||
|
||||
static void inputOpenFile(inputData *pinputData, char *filename);
|
||||
static void inputOpenFile(inputData *pinputData, const char * const filename);
|
||||
static void inputCloseFile(inputData *pinputData);
|
||||
static void inputCloseAllFiles(inputData *pinputData);
|
||||
|
||||
static void inputConstruct(inputData **ppvt)
|
||||
{
|
||||
inputData *pinputData;
|
||||
|
||||
pinputData = calloc(1, sizeof(inputData));
|
||||
ellInit(&pinputData->inputFileList);
|
||||
ellInit(&pinputData->pathList);
|
||||
*ppvt = pinputData;
|
||||
*ppvt = new inputData;
|
||||
}
|
||||
|
||||
static void inputDestruct(inputData *pinputData)
|
||||
static void inputDestruct(inputData * const pinputData)
|
||||
{
|
||||
pathNode *ppathNode;
|
||||
|
||||
inputCloseAllFiles(pinputData);
|
||||
while ((ppathNode = (pathNode *) ellFirst(&pinputData->pathList))) {
|
||||
ellDelete(&pinputData->pathList, &ppathNode->node);
|
||||
free(ppathNode->directory);
|
||||
free(ppathNode);
|
||||
}
|
||||
free(pinputData);
|
||||
delete(pinputData);
|
||||
}
|
||||
|
||||
static void inputAddPath(inputData *pinputData, char *path)
|
||||
static void inputAddPath(inputData * const pinputData, const char * const path)
|
||||
{
|
||||
ELLLIST *ppathList = &pinputData->pathList;
|
||||
pathNode *ppathNode;
|
||||
const char *pcolon;
|
||||
const char *pdir;
|
||||
size_t len;
|
||||
int emptyName;
|
||||
const char sep = *OSI_PATH_LIST_SEPARATOR;
|
||||
|
||||
ENTER;
|
||||
pdir = path;
|
||||
/*an empty name at beginning, middle, or end means current directory*/
|
||||
while (pdir && *pdir) {
|
||||
emptyName = ((*pdir == sep) ? 1 : 0);
|
||||
bool emptyName = (*pdir == sep);
|
||||
if (emptyName) ++pdir;
|
||||
|
||||
ppathNode = (pathNode *) calloc(1, sizeof(pathNode));
|
||||
ellAdd(ppathList, &ppathNode->node);
|
||||
|
||||
std::string directory;
|
||||
if (!emptyName) {
|
||||
pcolon = strchr(pdir, sep);
|
||||
len = (pcolon ? (pcolon - pdir) : strlen(pdir));
|
||||
if (len > 0) {
|
||||
ppathNode->directory = (char *) calloc(len + 1, sizeof(char));
|
||||
strncpy(ppathNode->directory, pdir, len);
|
||||
directory = std::string(pdir, len);
|
||||
pdir = pcolon;
|
||||
/*unless at end skip past first colon*/
|
||||
if (pdir && *(pdir + 1) != 0) ++pdir;
|
||||
}
|
||||
else { /*must have been trailing : */
|
||||
emptyName = 1;
|
||||
emptyName = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (emptyName) {
|
||||
ppathNode->directory = (char *) calloc(2, sizeof(char));
|
||||
strcpy(ppathNode->directory, ".");
|
||||
directory = ".";
|
||||
}
|
||||
|
||||
pinputData->pathList.push_back(directory);
|
||||
}
|
||||
EXIT;
|
||||
}
|
||||
|
||||
static void inputBegin(inputData *pinputData, char *fileName)
|
||||
static void inputBegin(inputData * const pinputData, const char * const fileName)
|
||||
{
|
||||
ENTER;
|
||||
inputCloseAllFiles(pinputData);
|
||||
@@ -456,16 +438,16 @@ static void inputBegin(inputData *pinputData, char *fileName)
|
||||
EXIT;
|
||||
}
|
||||
|
||||
static char *inputNextLine(inputData *pinputData)
|
||||
static char *inputNextLine(inputData * const pinputData)
|
||||
{
|
||||
inputFile *pinputFile;
|
||||
char *pline;
|
||||
std::list<inputFile>& inFileList = pinputData->inputFileList;
|
||||
|
||||
ENTER;
|
||||
while ((pinputFile = (inputFile *) ellFirst(&pinputData->inputFileList))) {
|
||||
pline = fgets(pinputData->inputBuffer, MAX_BUFFER_SIZE, pinputFile->fp);
|
||||
while (!inFileList.empty()) {
|
||||
inputFile& inFile = inFileList.front();
|
||||
char *pline = fgets(pinputData->inputBuffer, MAX_BUFFER_SIZE, inFile.fp);
|
||||
if (pline) {
|
||||
++pinputFile->lineNum;
|
||||
++inFile.lineNum;
|
||||
EXITS(pline);
|
||||
return pline;
|
||||
}
|
||||
@@ -475,32 +457,31 @@ static char *inputNextLine(inputData *pinputData)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void inputNewIncludeFile(inputData *pinputData, char *name)
|
||||
static void inputNewIncludeFile(inputData * const pinputData,
|
||||
const char * const name)
|
||||
{
|
||||
ENTER;
|
||||
inputOpenFile(pinputData,name);
|
||||
EXIT;
|
||||
}
|
||||
|
||||
static void inputErrPrint(inputData *pinputData)
|
||||
static void inputErrPrint(const inputData *const pinputData)
|
||||
{
|
||||
inputFile *pinputFile;
|
||||
|
||||
ENTER;
|
||||
fprintf(stderr, "input: '%s' at ", pinputData->inputBuffer);
|
||||
pinputFile = (inputFile *) ellFirst(&pinputData->inputFileList);
|
||||
while (pinputFile) {
|
||||
fprintf(stderr, "line %d of ", pinputFile->lineNum);
|
||||
const std::list<inputFile>& inFileList = pinputData->inputFileList;
|
||||
std::list<inputFile>::const_iterator inFileIt = inFileList.begin();
|
||||
while (inFileIt != inFileList.end()) {
|
||||
fprintf(stderr, "line %d of ", inFileIt->lineNum);
|
||||
|
||||
if (pinputFile->filename) {
|
||||
fprintf(stderr, " file %s\n", pinputFile->filename);
|
||||
if (!inFileIt->filename.empty()) {
|
||||
fprintf(stderr, " file %s\n", inFileIt->filename.c_str());
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "stdin:\n");
|
||||
}
|
||||
|
||||
pinputFile = (inputFile *) ellNext(&pinputFile->node);
|
||||
if (pinputFile) {
|
||||
if (++inFileIt != inFileList.end()) {
|
||||
fprintf(stderr, " included from ");
|
||||
}
|
||||
else {
|
||||
@@ -511,12 +492,11 @@ static void inputErrPrint(inputData *pinputData)
|
||||
EXIT;
|
||||
}
|
||||
|
||||
static void inputOpenFile(inputData *pinputData,char *filename)
|
||||
static void inputOpenFile(inputData *pinputData, const char * const filename)
|
||||
{
|
||||
ELLLIST *ppathList = &pinputData->pathList;
|
||||
pathNode *ppathNode = 0;
|
||||
inputFile *pinputFile;
|
||||
char *fullname = 0;
|
||||
std::list<std::string>& pathList = pinputData->pathList;
|
||||
std::list<std::string>::iterator pathIt = pathList.end();
|
||||
std::string fullname;
|
||||
FILE *fp = 0;
|
||||
|
||||
ENTER;
|
||||
@@ -524,24 +504,19 @@ static void inputOpenFile(inputData *pinputData,char *filename)
|
||||
STEP("Using stdin");
|
||||
fp = stdin;
|
||||
}
|
||||
else if ((ellCount(ppathList) == 0) || strchr(filename, '/')){
|
||||
else if (pathList.empty() || strchr(filename, '/')){
|
||||
STEPS("Opening ", filename);
|
||||
fp = fopen(filename, "r");
|
||||
}
|
||||
else {
|
||||
ppathNode = (pathNode *) ellFirst(ppathList);
|
||||
while (ppathNode) {
|
||||
fullname = calloc(strlen(filename) + strlen(ppathNode->directory) + 2,
|
||||
sizeof(char));
|
||||
strcpy(fullname, ppathNode->directory);
|
||||
strcat(fullname, "/");
|
||||
strcat(fullname, filename);
|
||||
pathIt = pathList.begin();
|
||||
while(pathIt != pathList.end()) {
|
||||
fullname = *pathIt + "/" + filename;
|
||||
STEPS("Trying", filename);
|
||||
fp = fopen(fullname, "r");
|
||||
fp = fopen(fullname.c_str(), "r");
|
||||
if (fp)
|
||||
break;
|
||||
free(fullname);
|
||||
ppathNode = (pathNode *) ellNext(&ppathNode->node);
|
||||
++pathIt;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -552,20 +527,20 @@ static void inputOpenFile(inputData *pinputData,char *filename)
|
||||
}
|
||||
|
||||
STEP("File opened");
|
||||
pinputFile = calloc(1, sizeof(inputFile));
|
||||
inputFile inFile = inputFile();
|
||||
|
||||
if (ppathNode) {
|
||||
pinputFile->filename = fullname;
|
||||
if (pathIt != pathList.end()) {
|
||||
inFile.filename = fullname;
|
||||
}
|
||||
else if (filename) {
|
||||
pinputFile->filename = epicsStrDup(filename);
|
||||
inFile.filename = filename;
|
||||
}
|
||||
else {
|
||||
pinputFile->filename = epicsStrDup("stdin");
|
||||
inFile.filename = "stdin";
|
||||
}
|
||||
|
||||
if (opt_D) {
|
||||
int hash = epicsStrHash(pinputFile->filename, 12345);
|
||||
int hash = epicsStrHash(inFile.filename.c_str(), 12345);
|
||||
int i = 0;
|
||||
int match = 0;
|
||||
|
||||
@@ -578,7 +553,7 @@ static void inputOpenFile(inputData *pinputData,char *filename)
|
||||
if (!match) {
|
||||
const char *wrap = numDeps ? " \\\n" : "";
|
||||
|
||||
printf("%s %s", wrap, pinputFile->filename);
|
||||
printf("%s %s", wrap, inFile.filename.c_str());
|
||||
if (numDeps < MAX_DEPS) {
|
||||
depHashes[numDeps++] = hash;
|
||||
}
|
||||
@@ -589,33 +564,29 @@ static void inputOpenFile(inputData *pinputData,char *filename)
|
||||
}
|
||||
}
|
||||
|
||||
pinputFile->fp = fp;
|
||||
ellInsert(&pinputData->inputFileList, 0, &pinputFile->node);
|
||||
inFile.fp = fp;
|
||||
pinputData->inputFileList.push_front(inFile);
|
||||
EXIT;
|
||||
}
|
||||
|
||||
static void inputCloseFile(inputData *pinputData)
|
||||
{
|
||||
inputFile *pinputFile;
|
||||
|
||||
std::list<inputFile>& inFileList = pinputData->inputFileList;
|
||||
ENTER;
|
||||
pinputFile = (inputFile *) ellFirst(&pinputData->inputFileList);
|
||||
if (pinputFile) {
|
||||
ellDelete(&pinputData->inputFileList, &pinputFile->node);
|
||||
if (fclose(pinputFile->fp))
|
||||
fprintf(stderr, "msi: Can't close input file '%s'\n", pinputFile->filename);
|
||||
free(pinputFile->filename);
|
||||
free(pinputFile);
|
||||
if(!inFileList.empty()) {
|
||||
inputFile& inFile = inFileList.front();
|
||||
if (fclose(inFile.fp))
|
||||
fprintf(stderr, "msi: Can't close input file '%s'\n", inFile.filename.c_str());
|
||||
inFileList.erase(inFileList.begin());
|
||||
}
|
||||
EXIT;
|
||||
}
|
||||
|
||||
static void inputCloseAllFiles(inputData *pinputData)
|
||||
{
|
||||
inputFile *pinputFile;
|
||||
|
||||
ENTER;
|
||||
while ((pinputFile = (inputFile *) ellFirst(&pinputData->inputFileList))) {
|
||||
const std::list<inputFile>& inFileList = pinputData->inputFileList;
|
||||
while(!inFileList.empty()) {
|
||||
inputCloseFile(pinputData);
|
||||
}
|
||||
EXIT;
|
||||
@@ -627,7 +598,7 @@ typedef enum {
|
||||
} tokenType;
|
||||
|
||||
typedef struct subFile {
|
||||
char *substitutionName;
|
||||
std::string substitutionName;
|
||||
FILE *fp;
|
||||
int lineNum;
|
||||
char inputBuffer[MAX_BUFFER_SIZE];
|
||||
@@ -636,25 +607,20 @@ typedef struct subFile {
|
||||
char string[MAX_BUFFER_SIZE];
|
||||
} subFile;
|
||||
|
||||
typedef struct patternNode {
|
||||
ELLNODE node;
|
||||
char *var;
|
||||
} patternNode;
|
||||
|
||||
struct subInfo {
|
||||
subFile *psubFile;
|
||||
int isFile;
|
||||
bool isFile;
|
||||
char *filename;
|
||||
int isPattern;
|
||||
ELLLIST patternList;
|
||||
size_t size;
|
||||
size_t curLength;
|
||||
char *macroReplacements;
|
||||
bool isPattern;
|
||||
std::list<std::string> patternList;
|
||||
std::string macroReplacements;
|
||||
subInfo() : psubFile(NULL), isFile(false), filename(NULL),
|
||||
isPattern(false) {};
|
||||
};
|
||||
|
||||
static char *subGetNextLine(subFile *psubFile);
|
||||
static tokenType subGetNextToken(subFile *psubFile);
|
||||
static void subFileErrPrint(subFile *psubFile,char * message);
|
||||
static void subFileErrPrint(subFile *psubFile, const char * message);
|
||||
static void freeSubFile(subInfo *psubInfo);
|
||||
static void freePattern(subInfo *psubInfo);
|
||||
static void catMacroReplacements(subInfo *psubInfo,const char *value);
|
||||
@@ -668,7 +634,7 @@ void freeSubFile(subInfo *psubInfo)
|
||||
if (fclose(psubFile->fp))
|
||||
fprintf(stderr, "msi: Can't close substitution file\n");
|
||||
}
|
||||
free(psubFile);
|
||||
delete(psubFile);
|
||||
free(psubInfo->filename);
|
||||
psubInfo->psubFile = 0;
|
||||
EXIT;
|
||||
@@ -676,43 +642,36 @@ void freeSubFile(subInfo *psubInfo)
|
||||
|
||||
void freePattern(subInfo *psubInfo)
|
||||
{
|
||||
patternNode *ppatternNode;
|
||||
|
||||
ENTER;
|
||||
while ((ppatternNode = (patternNode *) ellFirst(&psubInfo->patternList))) {
|
||||
ellDelete(&psubInfo->patternList, &ppatternNode->node);
|
||||
free(ppatternNode->var);
|
||||
free(ppatternNode);
|
||||
}
|
||||
psubInfo->isPattern = 0;
|
||||
psubInfo->patternList.clear();
|
||||
psubInfo->isPattern = false;
|
||||
EXIT;
|
||||
}
|
||||
|
||||
static void substituteDestruct(subInfo *psubInfo)
|
||||
static void substituteDestruct(subInfo * const psubInfo)
|
||||
{
|
||||
ENTER;
|
||||
freeSubFile(psubInfo);
|
||||
freePattern(psubInfo);
|
||||
free(psubInfo);
|
||||
delete(psubInfo);
|
||||
EXIT;
|
||||
}
|
||||
|
||||
static void substituteOpen(subInfo **ppvt, char *substitutionName)
|
||||
static void substituteOpen(subInfo **ppvt, const std::string& substitutionName)
|
||||
{
|
||||
subInfo *psubInfo;
|
||||
subFile *psubFile;
|
||||
FILE *fp;
|
||||
|
||||
ENTER;
|
||||
psubInfo = calloc(1, sizeof(subInfo));
|
||||
psubInfo = new subInfo;
|
||||
*ppvt = psubInfo;
|
||||
psubFile = calloc(1, sizeof(subFile));
|
||||
psubFile = new subFile;
|
||||
psubInfo->psubFile = psubFile;
|
||||
ellInit(&psubInfo->patternList);
|
||||
|
||||
fp = fopen(substitutionName, "r");
|
||||
fp = fopen(substitutionName.c_str(), "r");
|
||||
if (!fp) {
|
||||
fprintf(stderr, "msi: Can't open file '%s'\n", substitutionName);
|
||||
fprintf(stderr, "msi: Can't open file '%s'\n", substitutionName.c_str());
|
||||
abortExit(1);
|
||||
}
|
||||
|
||||
@@ -725,7 +684,7 @@ static void substituteOpen(subInfo **ppvt, char *substitutionName)
|
||||
EXIT;
|
||||
}
|
||||
|
||||
static int substituteGetGlobalSet(subInfo *psubInfo)
|
||||
static bool substituteGetGlobalSet(subInfo * const psubInfo)
|
||||
{
|
||||
subFile *psubFile = psubInfo->psubFile;
|
||||
|
||||
@@ -737,17 +696,16 @@ static int substituteGetGlobalSet(subInfo *psubInfo)
|
||||
strcmp(psubFile->string, "global") == 0) {
|
||||
subGetNextToken(psubFile);
|
||||
EXITD(1);
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
EXITD(0);
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
static int substituteGetNextSet(subInfo *psubInfo,char **filename)
|
||||
static bool substituteGetNextSet(subInfo * const psubInfo,char **filename)
|
||||
{
|
||||
subFile *psubFile = psubInfo->psubFile;
|
||||
patternNode *ppatternNode;
|
||||
|
||||
ENTER;
|
||||
*filename = 0;
|
||||
@@ -756,7 +714,7 @@ static int substituteGetNextSet(subInfo *psubInfo,char **filename)
|
||||
|
||||
if (psubFile->token == tokenEOF) {
|
||||
EXITD(0);
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (psubFile->token == tokenString &&
|
||||
@@ -764,7 +722,7 @@ static int substituteGetNextSet(subInfo *psubInfo,char **filename)
|
||||
size_t len;
|
||||
|
||||
STEP("Parsed 'file'");
|
||||
psubInfo->isFile = 1;
|
||||
psubInfo->isFile = true;
|
||||
if (subGetNextToken(psubFile) != tokenString) {
|
||||
subFileErrPrint(psubFile, "Parse error, expecting a filename");
|
||||
abortExit(1);
|
||||
@@ -799,7 +757,7 @@ static int substituteGetNextSet(subInfo *psubInfo,char **filename)
|
||||
|
||||
if (psubFile->token == tokenLBrace) {
|
||||
EXITD(1);
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (psubFile->token == tokenRBrace) {
|
||||
@@ -815,7 +773,7 @@ static int substituteGetNextSet(subInfo *psubInfo,char **filename)
|
||||
|
||||
STEP("Parsed 'pattern'");
|
||||
freePattern(psubInfo);
|
||||
psubInfo->isPattern = 1;
|
||||
psubInfo->isPattern = true;
|
||||
|
||||
while (subGetNextToken(psubFile) == tokenSeparator);
|
||||
|
||||
@@ -825,15 +783,13 @@ static int substituteGetNextSet(subInfo *psubInfo,char **filename)
|
||||
}
|
||||
STEP("Parsed '{'");
|
||||
|
||||
while (1) {
|
||||
while (true) {
|
||||
while (subGetNextToken(psubFile) == tokenSeparator);
|
||||
|
||||
if (psubFile->token != tokenString)
|
||||
break;
|
||||
|
||||
ppatternNode = calloc(1, sizeof(patternNode));
|
||||
ellAdd(&psubInfo->patternList, &ppatternNode->node);
|
||||
ppatternNode->var = epicsStrDup(psubFile->string);
|
||||
psubInfo->patternList.push_back(psubFile->string);
|
||||
}
|
||||
|
||||
if (psubFile->token != tokenRBrace) {
|
||||
@@ -843,23 +799,21 @@ static int substituteGetNextSet(subInfo *psubInfo,char **filename)
|
||||
|
||||
subGetNextToken(psubFile);
|
||||
EXITD(1);
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
static char *substituteGetGlobalReplacements(subInfo *psubInfo)
|
||||
static const char *substituteGetGlobalReplacements(subInfo * const psubInfo)
|
||||
{
|
||||
subFile *psubFile = psubInfo->psubFile;
|
||||
|
||||
ENTER;
|
||||
if (psubInfo->macroReplacements)
|
||||
psubInfo->macroReplacements[0] = 0;
|
||||
psubInfo->curLength = 0;
|
||||
psubInfo->macroReplacements.clear();
|
||||
|
||||
while (psubFile->token == tokenSeparator)
|
||||
subGetNextToken(psubFile);
|
||||
|
||||
if (psubFile->token == tokenRBrace && psubInfo->isFile) {
|
||||
psubInfo->isFile = 0;
|
||||
psubInfo->isFile = false;
|
||||
free(psubInfo->filename);
|
||||
psubInfo->filename = 0;
|
||||
freePattern(psubInfo);
|
||||
@@ -877,12 +831,12 @@ static char *substituteGetGlobalReplacements(subInfo *psubInfo)
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
while (true) {
|
||||
switch(subGetNextToken(psubFile)) {
|
||||
case tokenRBrace:
|
||||
subGetNextToken(psubFile);
|
||||
EXITS(psubInfo->macroReplacements);
|
||||
return psubInfo->macroReplacements;
|
||||
EXITS(psubInfo->macroReplacements.c_str());
|
||||
return psubInfo->macroReplacements.c_str();
|
||||
|
||||
case tokenSeparator:
|
||||
catMacroReplacements(psubInfo, ",");
|
||||
@@ -902,21 +856,18 @@ static char *substituteGetGlobalReplacements(subInfo *psubInfo)
|
||||
}
|
||||
}
|
||||
|
||||
static char *substituteGetReplacements(subInfo *psubInfo)
|
||||
static const char *substituteGetReplacements(subInfo * const psubInfo)
|
||||
{
|
||||
subFile *psubFile = psubInfo->psubFile;
|
||||
patternNode *ppatternNode;
|
||||
|
||||
ENTER;
|
||||
if (psubInfo->macroReplacements)
|
||||
psubInfo->macroReplacements[0] = 0;
|
||||
psubInfo->curLength = 0;
|
||||
psubInfo->macroReplacements.clear();
|
||||
|
||||
while (psubFile->token == tokenSeparator)
|
||||
subGetNextToken(psubFile);
|
||||
|
||||
if (psubFile->token==tokenRBrace && psubInfo->isFile) {
|
||||
psubInfo->isFile = 0;
|
||||
psubInfo->isFile = false;
|
||||
free(psubInfo->filename);
|
||||
psubInfo->filename = 0;
|
||||
freePattern(psubInfo);
|
||||
@@ -936,15 +887,16 @@ static char *substituteGetReplacements(subInfo *psubInfo)
|
||||
}
|
||||
|
||||
if (psubInfo->isPattern) {
|
||||
int gotFirstPattern = 0;
|
||||
bool gotFirstPattern = false;
|
||||
|
||||
while (subGetNextToken(psubFile) == tokenSeparator);
|
||||
ppatternNode = (patternNode *) ellFirst(&psubInfo->patternList);
|
||||
while (1) {
|
||||
std::list<std::string>& patternList = psubInfo->patternList;
|
||||
std::list<std::string>::iterator patternIt = patternList.begin();
|
||||
while (true) {
|
||||
if (psubFile->token == tokenRBrace) {
|
||||
subGetNextToken(psubFile);
|
||||
EXITS(psubInfo->macroReplacements);
|
||||
return psubInfo->macroReplacements;
|
||||
EXITS(psubInfo->macroReplacements.c_str());
|
||||
return psubInfo->macroReplacements.c_str();
|
||||
}
|
||||
|
||||
if (psubFile->token != tokenString) {
|
||||
@@ -954,13 +906,13 @@ static char *substituteGetReplacements(subInfo *psubInfo)
|
||||
|
||||
if (gotFirstPattern)
|
||||
catMacroReplacements(psubInfo, ",");
|
||||
gotFirstPattern = 1;
|
||||
gotFirstPattern = true;
|
||||
|
||||
if (ppatternNode) {
|
||||
catMacroReplacements(psubInfo, ppatternNode->var);
|
||||
if (patternIt != patternList.end()) {
|
||||
catMacroReplacements(psubInfo, patternIt->c_str());
|
||||
catMacroReplacements(psubInfo, "=");
|
||||
catMacroReplacements(psubInfo, psubFile->string);
|
||||
ppatternNode = (patternNode *) ellNext(&ppatternNode->node);
|
||||
++patternIt;
|
||||
}
|
||||
else {
|
||||
subFileErrPrint(psubFile, "Warning, too many values given");
|
||||
@@ -969,12 +921,12 @@ static char *substituteGetReplacements(subInfo *psubInfo)
|
||||
while (subGetNextToken(psubFile) == tokenSeparator);
|
||||
}
|
||||
}
|
||||
else while(1) {
|
||||
else while(true) {
|
||||
switch(subGetNextToken(psubFile)) {
|
||||
case tokenRBrace:
|
||||
subGetNextToken(psubFile);
|
||||
EXITS(psubInfo->macroReplacements);
|
||||
return psubInfo->macroReplacements;
|
||||
EXITS(psubInfo->macroReplacements.c_str());
|
||||
return psubInfo->macroReplacements.c_str();
|
||||
|
||||
case tokenSeparator:
|
||||
catMacroReplacements(psubInfo, ",");
|
||||
@@ -1017,11 +969,12 @@ static char *subGetNextLine(subFile *psubFile)
|
||||
return &psubFile->inputBuffer[0];
|
||||
}
|
||||
|
||||
static void subFileErrPrint(subFile *psubFile,char * message)
|
||||
static void subFileErrPrint(subFile *psubFile, const char * message)
|
||||
{
|
||||
fprintf(stderr, "msi: %s\n",message);
|
||||
fprintf(stderr, " in substitution file '%s' at line %d:\n %s",
|
||||
psubFile->substitutionName, psubFile->lineNum, psubFile->inputBuffer);
|
||||
psubFile->substitutionName.c_str(), psubFile->lineNum,
|
||||
psubFile->inputBuffer);
|
||||
}
|
||||
|
||||
|
||||
@@ -1107,32 +1060,8 @@ done:
|
||||
|
||||
static void catMacroReplacements(subInfo *psubInfo, const char *value)
|
||||
{
|
||||
size_t len = strlen(value);
|
||||
|
||||
ENTER;
|
||||
if (psubInfo->size <= (psubInfo->curLength + len)) {
|
||||
size_t newsize = psubInfo->size + MAX_BUFFER_SIZE;
|
||||
char *newbuf;
|
||||
|
||||
STEP("Enlarging buffer");
|
||||
if (newsize <= psubInfo->curLength + len)
|
||||
newsize = psubInfo->curLength + len + 1;
|
||||
newbuf = calloc(1, newsize);
|
||||
if (!newbuf) {
|
||||
fprintf(stderr, "calloc failed for size %lu\n",
|
||||
(unsigned long) newsize);
|
||||
abortExit(1);
|
||||
}
|
||||
if (psubInfo->macroReplacements) {
|
||||
memcpy(newbuf, psubInfo->macroReplacements, psubInfo->curLength);
|
||||
free(psubInfo->macroReplacements);
|
||||
}
|
||||
psubInfo->size = newsize;
|
||||
psubInfo->macroReplacements = newbuf;
|
||||
}
|
||||
|
||||
STEPS("Appending", value);
|
||||
strcat(psubInfo->macroReplacements, value);
|
||||
psubInfo->curLength += len;
|
||||
psubInfo->macroReplacements += value;
|
||||
EXIT;
|
||||
}
|
||||
@@ -241,7 +241,7 @@ Assuming there is a system state called "blue", that is being controlled by
|
||||
some other facility such as a timing system, updates could be restricted to
|
||||
periods only when "blue" is true by using
|
||||
|
||||
Hal$ camonitor 'test:channel' 'test:channel.{"while":"blue"}'
|
||||
Hal$ camonitor 'test:channel' 'test:channel.{"sync":{"while":"blue"}}'
|
||||
...
|
||||
|
||||
=cut
|
||||
|
||||
@@ -348,9 +348,9 @@ static long get_enum_strs(const DBADDR *paddr,struct dbr_enumStrs *pes)
|
||||
/*SETTING no_str=0 breaks channel access clients*/
|
||||
pes->no_str = 2;
|
||||
memset(pes->strs,'\0',sizeof(pes->strs));
|
||||
strncpy(pes->strs[0],prec->znam,sizeof(prec->znam));
|
||||
strncpy(pes->strs[0],prec->znam,sizeof(pes->strs[0]));
|
||||
if(*prec->znam!=0) pes->no_str=1;
|
||||
strncpy(pes->strs[1],prec->onam,sizeof(prec->onam));
|
||||
strncpy(pes->strs[1],prec->onam,sizeof(pes->strs[1]));
|
||||
if(*prec->onam!=0) pes->no_str=2;
|
||||
return(0);
|
||||
}
|
||||
|
||||
@@ -183,17 +183,16 @@ static long special(DBADDR *paddr, int after)
|
||||
|
||||
#define indexof(field) int64inRecord##field
|
||||
|
||||
static long get_units(DBADDR *paddr,char *units)
|
||||
static long get_units(DBADDR *paddr, char *units)
|
||||
{
|
||||
int64inRecord *prec=(int64inRecord *)paddr->precord;
|
||||
int64inRecord *prec = (int64inRecord *) paddr->precord;
|
||||
|
||||
if(paddr->pfldDes->field_type == DBF_LONG) {
|
||||
strncpy(units,prec->egu,DB_UNITS_SIZE);
|
||||
if (paddr->pfldDes->field_type == DBF_INT64) {
|
||||
strncpy(units, prec->egu, DB_UNITS_SIZE);
|
||||
}
|
||||
return(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd)
|
||||
{
|
||||
int64inRecord *prec=(int64inRecord *)paddr->precord;
|
||||
@@ -255,7 +254,7 @@ static long get_alarm_double(DBADDR *paddr, struct dbr_alDouble *pad)
|
||||
|
||||
static void checkAlarms(int64inRecord *prec, epicsTimeStamp *timeLast)
|
||||
{
|
||||
enum {
|
||||
enum {
|
||||
range_Lolo = 1,
|
||||
range_Low,
|
||||
range_Normal,
|
||||
|
||||
@@ -51,7 +51,7 @@ static long special(DBADDR *, int);
|
||||
#define get_array_info NULL
|
||||
#define put_array_info NULL
|
||||
#define get_units NULL
|
||||
#define get_precision NULL
|
||||
static long get_precision(const DBADDR *, long *);
|
||||
#define get_enum_str NULL
|
||||
#define get_enum_strs NULL
|
||||
#define put_enum_str NULL
|
||||
@@ -204,6 +204,16 @@ static long special(DBADDR *paddr, int after)
|
||||
}
|
||||
}
|
||||
|
||||
static long get_precision(const DBADDR *paddr,long *precision)
|
||||
{
|
||||
mbbiDirectRecord *prec=(mbbiDirectRecord *)paddr->precord;
|
||||
if(dbGetFieldIndex(paddr)==mbbiDirectRecordVAL)
|
||||
*precision = prec->nobt;
|
||||
else
|
||||
recGblGetPrec(paddr,precision);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void monitor(mbbiDirectRecord *prec)
|
||||
{
|
||||
epicsUInt16 events = recGblResetAlarms(prec);
|
||||
|
||||
@@ -51,7 +51,7 @@ static long special(DBADDR *, int);
|
||||
#define get_array_info NULL
|
||||
#define put_array_info NULL
|
||||
#define get_units NULL
|
||||
#define get_precision NULL
|
||||
static long get_precision(const DBADDR *, long *);
|
||||
#define get_enum_str NULL
|
||||
#define get_enum_strs NULL
|
||||
#define put_enum_str NULL
|
||||
@@ -313,6 +313,16 @@ static long special(DBADDR *paddr, int after)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long get_precision(const DBADDR *paddr,long *precision)
|
||||
{
|
||||
mbboDirectRecord *prec=(mbboDirectRecord *)paddr->precord;
|
||||
if(dbGetFieldIndex(paddr)==mbboDirectRecordVAL)
|
||||
*precision = prec->nobt;
|
||||
else
|
||||
recGblGetPrec(paddr,precision);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void monitor(mbboDirectRecord *prec)
|
||||
{
|
||||
epicsUInt16 events = recGblResetAlarms(prec);
|
||||
|
||||
@@ -99,7 +99,7 @@ static void monitor(stateRecord *prec)
|
||||
monitor_mask = recGblResetAlarms(prec);
|
||||
if(strncmp(prec->oval,prec->val,sizeof(prec->val))) {
|
||||
db_post_events(prec,&(prec->val[0]),monitor_mask|DBE_VALUE|DBE_LOG);
|
||||
strncpy(prec->oval,prec->val,sizeof(prec->val));
|
||||
strncpy(prec->oval,prec->val,sizeof(prec->oval));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -121,8 +121,7 @@ static long init_record(struct dbCommon *pcommon, int pass)
|
||||
if (status)
|
||||
return status;
|
||||
}
|
||||
|
||||
strncpy(prec->oval, prec->val, sizeof(prec->val));
|
||||
strncpy(prec->oval, prec->val, sizeof(prec->oval));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -183,7 +182,7 @@ static void monitor(stringinRecord *prec)
|
||||
|
||||
if (strncmp(prec->oval, prec->val, sizeof(prec->val))) {
|
||||
monitor_mask |= DBE_VALUE | DBE_LOG;
|
||||
strncpy(prec->oval, prec->val, sizeof(prec->val));
|
||||
strncpy(prec->oval, prec->val, sizeof(prec->oval));
|
||||
}
|
||||
|
||||
if (prec->mpst == stringinPOST_Always)
|
||||
|
||||
@@ -126,8 +126,7 @@ static long init_record(struct dbCommon *pcommon, int pass)
|
||||
if(status)
|
||||
return status;
|
||||
}
|
||||
|
||||
strncpy(prec->oval, prec->val, sizeof(prec->val));
|
||||
strncpy(prec->oval, prec->val, sizeof(prec->oval));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -215,7 +214,7 @@ static void monitor(stringoutRecord *prec)
|
||||
|
||||
if (strncmp(prec->oval, prec->val, sizeof(prec->val))) {
|
||||
monitor_mask |= DBE_VALUE | DBE_LOG;
|
||||
strncpy(prec->oval, prec->val, sizeof(prec->val));
|
||||
strncpy(prec->oval, prec->val, sizeof(prec->oval));
|
||||
}
|
||||
|
||||
if (prec->mpst == stringoutPOST_Always)
|
||||
|
||||
@@ -212,7 +212,7 @@ sub _getline {
|
||||
|
||||
my $line = readline $self->{stdout};
|
||||
if (defined $line) {
|
||||
chomp $line;
|
||||
$line =~ s/[\r\n]+ $//x; # chomp broken on Windows?
|
||||
printf "#%d >> %s\n", $self->{pid}, $line if $self->{debug};
|
||||
}
|
||||
elsif (eof($self->{stdout})) {
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
use strict;
|
||||
use Test;
|
||||
|
||||
BEGIN {plan tests => 9}
|
||||
BEGIN {plan tests => 12}
|
||||
|
||||
# Check include/substitute command model
|
||||
ok(msi('-I .. ../t1-template.txt'), slurp('../t1-result.txt'));
|
||||
@@ -33,16 +33,9 @@ ok(msi('-S../t6-substitute.txt ../t6-template.txt'), slurp('../t6-result.txt'));
|
||||
|
||||
# Output option -o and verbose option -V
|
||||
my $out = 't7-output.txt';
|
||||
my $count = 5; # Try up to 5 times...
|
||||
my $result;
|
||||
do {
|
||||
unlink $out;
|
||||
msi("-I.. -V -o $out ../t1-template.txt");
|
||||
$result = slurp($out);
|
||||
print "# msi output file empty, retrying\n"
|
||||
if $result eq '';
|
||||
} while ($result eq '') && (--$count > 0);
|
||||
ok($result, slurp('../t7-result.txt'));
|
||||
unlink $out;
|
||||
msi("-I.. -V -o $out ../t1-template.txt");
|
||||
ok(slurp($out), slurp('../t7-result.txt'));
|
||||
|
||||
# Dependency generation, include/substitute model
|
||||
ok(msi('-I.. -D -o t8.txt ../t1-template.txt'), slurp('../t8-result.txt'));
|
||||
@@ -50,6 +43,17 @@ ok(msi('-I.. -D -o t8.txt ../t1-template.txt'), slurp('../t8-result.txt'));
|
||||
# Dependency generation, dbLoadTemplate format
|
||||
ok(msi('-I.. -D -ot9.txt -S ../t2-substitution.txt'), slurp('../t9-result.txt'));
|
||||
|
||||
# Substitution file, variable format, with 0 variable definitions
|
||||
ok(msi('-I. -I.. -S ../t10-substitute.txt'), slurp('../t10-result.txt'));
|
||||
|
||||
# Substitution file, pattern format, with 0 pattern definitions
|
||||
ok(msi('-I. -I.. -S ../t11-substitute.txt'), slurp('../t11-result.txt'));
|
||||
|
||||
# Substitution file, environment variable macros in template filename
|
||||
my %envs = (TEST_NO => 12, PREFIX => 't');
|
||||
@ENV{ keys %envs } = values %envs;
|
||||
ok(msi('-I. -I.. -S ../t12-substitute.txt'), slurp('../t12-result.txt'));
|
||||
delete @ENV{ keys %envs }; # Not really needed
|
||||
|
||||
# Test support routines
|
||||
|
||||
@@ -63,21 +67,8 @@ sub slurp {
|
||||
|
||||
sub msi {
|
||||
my ($args) = @_;
|
||||
my $nul = $^O eq 'MSWin32' ? 'NUL' : '/dev/null';
|
||||
my $msi = '@TOP@/bin/@ARCH@/msi';
|
||||
$msi .= '.exe' if ($^O eq 'MSWin32') || ($^O eq 'cygwin');
|
||||
my $result;
|
||||
if ($args =~ m/-o / && $args !~ m/-D/) {
|
||||
# An empty result is expected
|
||||
$result = `$msi $args`;
|
||||
}
|
||||
else {
|
||||
# Try up to 5 times, sometimes msi fails on Windows
|
||||
my $count = 5;
|
||||
do {
|
||||
$result = `$msi $args`;
|
||||
print "# result of '$msi $args' empty, retrying\n"
|
||||
if $result eq '';
|
||||
} while ($result eq '') && (--$count > 0);
|
||||
}
|
||||
return $result;
|
||||
$msi =~ tr(/)(\\) if $^O eq 'MSWin32';
|
||||
return `$msi $args 2>$nul`;
|
||||
}
|
||||
|
||||
4
modules/database/test/ioc/dbtemplate/t10-result.txt
Normal file
4
modules/database/test/ioc/dbtemplate/t10-result.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
# comment line
|
||||
a=$(a)
|
||||
# comment line
|
||||
a=gbl
|
||||
8
modules/database/test/ioc/dbtemplate/t10-substitute.txt
Normal file
8
modules/database/test/ioc/dbtemplate/t10-substitute.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
file t10-template.txt {
|
||||
{}
|
||||
}
|
||||
|
||||
global { a=gbl }
|
||||
file t10-template.txt {
|
||||
{}
|
||||
}
|
||||
2
modules/database/test/ioc/dbtemplate/t10-template.txt
Normal file
2
modules/database/test/ioc/dbtemplate/t10-template.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
# comment line
|
||||
a=$(a)
|
||||
4
modules/database/test/ioc/dbtemplate/t11-result.txt
Normal file
4
modules/database/test/ioc/dbtemplate/t11-result.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
# comment line
|
||||
a=$(a)
|
||||
# comment line
|
||||
a=gbl
|
||||
10
modules/database/test/ioc/dbtemplate/t11-substitute.txt
Normal file
10
modules/database/test/ioc/dbtemplate/t11-substitute.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
file t11-template.txt {
|
||||
pattern {}
|
||||
{}
|
||||
}
|
||||
|
||||
global { a=gbl }
|
||||
file t11-template.txt {
|
||||
pattern {}
|
||||
{}
|
||||
}
|
||||
2
modules/database/test/ioc/dbtemplate/t11-template.txt
Normal file
2
modules/database/test/ioc/dbtemplate/t11-template.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
# comment line
|
||||
a=$(a)
|
||||
2
modules/database/test/ioc/dbtemplate/t12-result.txt
Normal file
2
modules/database/test/ioc/dbtemplate/t12-result.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
# comment line
|
||||
a=foo
|
||||
3
modules/database/test/ioc/dbtemplate/t12-substitute.txt
Normal file
3
modules/database/test/ioc/dbtemplate/t12-substitute.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
file $(PREFIX)$(TEST_NO)-template.txt {
|
||||
{ a=foo }
|
||||
}
|
||||
2
modules/database/test/ioc/dbtemplate/t12-template.txt
Normal file
2
modules/database/test/ioc/dbtemplate/t12-template.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
# comment line
|
||||
a=$(a)
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include <testMain.h>
|
||||
#include <dbUnitTest.h>
|
||||
#include <errlog.h>
|
||||
#include <dbCommon.h>
|
||||
#include <dbAccess.h>
|
||||
#include <epicsEvent.h>
|
||||
@@ -16,6 +17,7 @@
|
||||
#include <iocsh.h>
|
||||
#include "registryFunction.h"
|
||||
#include <subRecord.h>
|
||||
#include <dbScan.h>
|
||||
|
||||
epicsEventId done;
|
||||
static int waitFor;
|
||||
@@ -28,11 +30,17 @@ long doneSubr(subRecord *prec)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
void dummydone(void *usr, struct dbCommon* prec)
|
||||
{
|
||||
epicsEventMustTrigger(done);
|
||||
}
|
||||
|
||||
void asyncproctest_registerRecordDeviceDriver(struct dbBase *);
|
||||
|
||||
MAIN(asyncproctest)
|
||||
{
|
||||
testPlan(21);
|
||||
testPlan(27);
|
||||
|
||||
done = epicsEventMustCreate(epicsEventEmpty);
|
||||
|
||||
@@ -99,6 +107,40 @@ MAIN(asyncproctest)
|
||||
testdbGetFieldEqual("chain3", DBF_LONG, 7);
|
||||
testdbGetFieldEqual("chain3.A", DBF_LONG, 2);
|
||||
|
||||
testDiag("===== Chain 4 ======");
|
||||
|
||||
{
|
||||
dbCommon *dummy=testdbRecordPtr("chain4_dummy");
|
||||
|
||||
testdbPutFieldOk("chain4_pos.PROC", DBF_LONG, 0);
|
||||
|
||||
/* sync once queue to wait for any queued RPRO */
|
||||
scanOnceCallback(dummy, dummydone, NULL);
|
||||
|
||||
if (epicsEventWaitWithTimeout(done, 10.0) != epicsEventOK)
|
||||
testAbort("Processing timed out");
|
||||
|
||||
testdbGetFieldEqual("chain4_pos", DBF_SHORT, 1);
|
||||
testdbGetFieldEqual("chain4_rel", DBF_SHORT, 1);
|
||||
testdbGetFieldEqual("chain4_lim", DBF_SHORT, 1);
|
||||
}
|
||||
|
||||
testDiag("===== Chain 5 ======");
|
||||
|
||||
{
|
||||
dbCommon *dummy=testdbRecordPtr("chain4_dummy");
|
||||
|
||||
testdbPutFieldOk("chain5_cnt.PROC", DBF_LONG, 0);
|
||||
|
||||
/* sync once queue to wait for any queued RPRO */
|
||||
scanOnceCallback(dummy, dummydone, NULL);
|
||||
|
||||
if (epicsEventWaitWithTimeout(done, 10.0) != epicsEventOK)
|
||||
testAbort("Processing timed out");
|
||||
|
||||
testdbGetFieldEqual("chain5_cnt", DBF_SHORT, 1);
|
||||
}
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
|
||||
@@ -53,3 +53,41 @@ record(sub, "done3") {
|
||||
field(FLNK, "chain3")
|
||||
field(TPRO, "$(TPRO=)")
|
||||
}
|
||||
|
||||
|
||||
# loop breaking regression
|
||||
# should _not_ RPRO
|
||||
record(calcout,"chain4_pos") {
|
||||
field(CALC, "E:=E+1;E")
|
||||
field(OUT,"chain4_rel.A PP")
|
||||
field(TPRO, "$(TPRO=)")
|
||||
}
|
||||
|
||||
record(calc,"chain4_rel") {
|
||||
field(CALC, "E:=E+1;E")
|
||||
field(FLNK,"chain4_lim")
|
||||
field(TPRO, "$(TPRO=)")
|
||||
}
|
||||
|
||||
record(calc,"chain4_lim") {
|
||||
field(CALC, "E:=E+1;E")
|
||||
field(INPA,"chain4_pos PP")
|
||||
field(INPB,"chain4_pos.HIGH PP")
|
||||
field(INPC,"chain4_pos.LOW PP")
|
||||
field(FLNK,"chain4_pos")
|
||||
field(TPRO, "$(TPRO=)")
|
||||
}
|
||||
|
||||
record(bo, "chain4_dummy") {
|
||||
field(TPRO, "$(TPRO=)")
|
||||
}
|
||||
|
||||
# loop breaking regression part 2
|
||||
# selft link should _not_ RPRO
|
||||
record(calcout,"chain5_cnt") {
|
||||
field(CALC, "E:=E+1;E")
|
||||
field(INPA,"chain5_cnt.A PP")
|
||||
field(OUT,"chain5_cnt.A PP")
|
||||
field(FLNK,"chain5_cnt")
|
||||
field(TPRO, "$(TPRO=)")
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ $ENV{HARNESS_ACTIVE} = 1 if scalar @ARGV && shift eq '-tap';
|
||||
$ENV{EPICS_CA_AUTO_ADDR_LIST} = 'NO';
|
||||
$ENV{EPICS_CA_ADDR_LIST} = 'localhost';
|
||||
$ENV{EPICS_CA_SERVER_PORT} = 55064;
|
||||
$ENV{EPICS_CAS_BEACON_PORT} = 55065;
|
||||
$ENV{EPICS_CAS_INTF_ADDR_LIST} = 'localhost';
|
||||
|
||||
$ENV{EPICS_PVA_AUTO_ADDR_LIST} = 'NO';
|
||||
@@ -29,13 +30,32 @@ my $prefix = "test-$$";
|
||||
my $ioc = EPICS::IOC->new();
|
||||
#$ioc->debug(1);
|
||||
|
||||
$SIG{__DIE__} = $SIG{INT} = $SIG{QUIT} = $SIG{ALRM} = sub {
|
||||
$ioc->kill
|
||||
if ref($ioc) eq 'EPICS::IOC' && $ioc->started;
|
||||
$SIG{__DIE__} = $SIG{INT} = $SIG{QUIT} = sub {
|
||||
$ioc->kill;
|
||||
BAIL_OUT('Caught signal');
|
||||
};
|
||||
|
||||
alarm 30;
|
||||
|
||||
# Watchdog utilities
|
||||
|
||||
sub kill_bail {
|
||||
my $doing = shift;
|
||||
return sub {
|
||||
$ioc->kill;
|
||||
BAIL_OUT("Timeout $doing");
|
||||
}
|
||||
}
|
||||
|
||||
sub watchdog (&$$) {
|
||||
my ($do, $timeout, $abort) = @_;
|
||||
$SIG{ALRM} = $abort;
|
||||
alarm $timeout;
|
||||
&$do;
|
||||
alarm 0;
|
||||
}
|
||||
|
||||
|
||||
# Start the IOC
|
||||
|
||||
my $softIoc = "$bin/softIocPVA$exe";
|
||||
$softIoc = "$bin/softIoc$exe"
|
||||
@@ -43,42 +63,78 @@ $softIoc = "$bin/softIoc$exe"
|
||||
BAIL_OUT("Can't find a softIoc executable")
|
||||
unless -x $softIoc;
|
||||
|
||||
$ioc->start($softIoc, '-x', $prefix);
|
||||
$ioc->cmd; # Wait for command prompt
|
||||
watchdog {
|
||||
$ioc->start($softIoc, '-x', $prefix);
|
||||
$ioc->cmd; # Wait for command prompt
|
||||
} 10, kill_bail('starting softIoc');
|
||||
|
||||
|
||||
# Get Base Version number from PV
|
||||
|
||||
my $pv = "$prefix:BaseVersion";
|
||||
|
||||
my @pvs = $ioc->dbl('stringin');
|
||||
grep(m/$pv/, @pvs)
|
||||
or BAIL_OUT('No BaseVersion record found');
|
||||
watchdog {
|
||||
my @pvs = $ioc->dbl('stringin');
|
||||
grep(m/^ $pv $/x, @pvs)
|
||||
or BAIL_OUT('No BaseVersion record found');
|
||||
} 10, kill_bail('running dbl');
|
||||
|
||||
my $version = $ioc->dbgf("$pv");
|
||||
my $version;
|
||||
watchdog {
|
||||
$version = $ioc->dbgf("$pv");
|
||||
} 10, kill_bail('getting BaseVersion');
|
||||
like($version, qr/^ \d+ \. \d+ \. \d+ /x,
|
||||
"Got BaseVersion '$version' from iocsh");
|
||||
|
||||
note("CA server configuration:\n",
|
||||
map(" $_\n", $ioc->cmd('casr', 1)));
|
||||
|
||||
my $caget = "$bin/caget$exe";
|
||||
# Channel Access
|
||||
|
||||
SKIP: {
|
||||
skip "caget not available", 1 unless -x $caget;
|
||||
my $caVersion = `$caget -w5 $pv`;
|
||||
like($caVersion, qr/$pv \s+ \Q$version\E/x,
|
||||
'Got same BaseVersion from caget');
|
||||
my $caget = "$bin/caget$exe";
|
||||
skip "caget not available", 1
|
||||
unless -x $caget;
|
||||
|
||||
# CA Server Diagnostics
|
||||
|
||||
watchdog {
|
||||
note("CA server configuration:\n",
|
||||
map(" $_\n", $ioc->cmd('casr', 1)));
|
||||
} 10, kill_bail('running casr');
|
||||
|
||||
# CA Client test
|
||||
|
||||
watchdog {
|
||||
my $caVersion = `$caget -w5 $pv`;
|
||||
like($caVersion, qr/^ $pv \s+ \Q$version\E $/x,
|
||||
'Got same BaseVersion from caget');
|
||||
} 10, kill_bail('doing caget');
|
||||
}
|
||||
|
||||
my $pvget = "$bin/pvget$exe";
|
||||
|
||||
# PV Access
|
||||
|
||||
SKIP: {
|
||||
my $pvget = "$bin/pvget$exe";
|
||||
skip "softIocPVA not available", 1
|
||||
if $softIoc eq "$bin/softIoc$exe";
|
||||
note("PVA server configuration:\n",
|
||||
map(" $_\n", $ioc->cmd('pvasr')));
|
||||
|
||||
# PVA Server Diagnostics
|
||||
|
||||
watchdog {
|
||||
note("PVA server configuration:\n",
|
||||
map(" $_\n", $ioc->cmd('pvasr')));
|
||||
} 10, kill_bail('running pvasr');
|
||||
|
||||
skip "pvget not available", 1
|
||||
unless -x $pvget;
|
||||
my $pvaVersion = `$pvget -w5 $pv`;
|
||||
like($pvaVersion, qr/$pv \s .* \Q$version\E/x,
|
||||
'Got same BaseVersion from pvget');
|
||||
|
||||
# PVA Client test
|
||||
|
||||
watchdog {
|
||||
my $pvaVersion = `$pvget -w5 $pv`;
|
||||
like($pvaVersion, qr/^ $pv \s .* \Q$version\E \s* $/x,
|
||||
'Got same BaseVersion from pvget');
|
||||
} 10, kill_bail('doing pvget');
|
||||
}
|
||||
|
||||
$ioc->kill;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
EPICS_LIBCOM_MAJOR_VERSION = 3
|
||||
EPICS_LIBCOM_MINOR_VERSION = 17
|
||||
EPICS_LIBCOM_MAINTENANCE_VERSION = 2
|
||||
EPICS_LIBCOM_MAINTENANCE_VERSION = 5
|
||||
EPICS_LIBCOM_DEVELOPMENT_FLAG = 0
|
||||
|
||||
@@ -319,7 +319,7 @@ static const iocshArg epicsMutexShowAllArg1 = { "level",iocshArgInt};
|
||||
static const iocshArg * const epicsMutexShowAllArgs[2] =
|
||||
{&epicsMutexShowAllArg0,&epicsMutexShowAllArg1};
|
||||
static const iocshFuncDef epicsMutexShowAllFuncDef =
|
||||
{"epicsMutexShowAll",1,epicsMutexShowAllArgs};
|
||||
{"epicsMutexShowAll",2,epicsMutexShowAllArgs};
|
||||
static void epicsMutexShowAllCallFunc(const iocshArgBuf *args)
|
||||
{
|
||||
epicsMutexShowAll(args[0].ival,args[1].ival);
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
|
||||
static unsigned short ioc_log_port;
|
||||
static long ioc_log_file_limit;
|
||||
static char ioc_log_file_name[256];
|
||||
static char ioc_log_file_name[512];
|
||||
static char ioc_log_file_command[256];
|
||||
|
||||
|
||||
@@ -866,7 +866,12 @@ static int setupSIGHUP(struct ioc_log_server *pserver)
|
||||
*/
|
||||
static void sighupHandler(int signo)
|
||||
{
|
||||
(void) write(sighupPipe[1], "SIGHUP\n", 7);
|
||||
const char msg[] = "SIGHUP\n";
|
||||
const ssize_t bytesWritten = write(sighupPipe[1], msg, sizeof(msg));
|
||||
if (bytesWritten != sizeof(msg)) {
|
||||
fprintf(stderr, "iocLogServer: failed to write to SIGHUP pipe because "
|
||||
"`%s'\n", strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -884,7 +889,10 @@ static void serviceSighupRequest(void *pParam)
|
||||
/*
|
||||
* Read and discard message from pipe.
|
||||
*/
|
||||
(void) read(sighupPipe[0], buff, sizeof buff);
|
||||
if (read(sighupPipe[0], buff, sizeof buff) <= 0) {
|
||||
fprintf(stderr, "iocLogServer: failed to read from SIGHUP pipe because "
|
||||
"`%s'\n", strerror(errno));
|
||||
};
|
||||
|
||||
/*
|
||||
* Determine new log file name.
|
||||
|
||||
@@ -171,6 +171,7 @@ ipAddrToAsciiEngine & ipAddrToAsciiEngine::allocate ()
|
||||
}
|
||||
|
||||
ipAddrToAsciiGlobal::ipAddrToAsciiGlobal () :
|
||||
mutex(__FILE__, __LINE__),
|
||||
thread ( *this, "ipToAsciiProxy",
|
||||
epicsThreadGetStackSize(epicsThreadStackBig),
|
||||
epicsThreadPriorityLow ),
|
||||
|
||||
@@ -78,6 +78,11 @@ static epicsThreadOnceId onceId = EPICS_THREAD_ONCE_INIT;
|
||||
|
||||
static const char * const tsfmt = "%Y-%m-%d %H:%M:%S.%09f";
|
||||
|
||||
/* defined in osiClockTime.c or osdTime.cpp */
|
||||
int osdTimeGetCurrent ( epicsTimeStamp *pDest );
|
||||
/* cleared if/when gtPvt.timeProviders contains more than the default osdTimeGetCurrent() */
|
||||
static int useOsdGetCurrent = 1;
|
||||
|
||||
/* Implementation */
|
||||
|
||||
static void generalTime_InitOnce(void *dummy)
|
||||
@@ -103,6 +108,9 @@ int generalTimeGetExceptPriority(epicsTimeStamp *pDest, int *pPrio, int ignore)
|
||||
gtProvider *ptp;
|
||||
int status = S_time_noProvider;
|
||||
|
||||
if(useOsdGetCurrent)
|
||||
return osdTimeGetCurrent(pDest);
|
||||
|
||||
generalTime_Init();
|
||||
|
||||
IFDEBUG(2)
|
||||
@@ -148,6 +156,9 @@ int epicsShareAPI epicsTimeGetCurrent(epicsTimeStamp *pDest)
|
||||
int status = S_time_noProvider;
|
||||
epicsTimeStamp ts;
|
||||
|
||||
if(useOsdGetCurrent)
|
||||
return osdTimeGetCurrent(pDest);
|
||||
|
||||
generalTime_Init();
|
||||
|
||||
IFDEBUG(20)
|
||||
@@ -370,6 +381,11 @@ static void insertProvider(gtProvider *ptp, ELLLIST *plist, epicsMutexId lock)
|
||||
ellAdd(plist, &ptp->node);
|
||||
}
|
||||
|
||||
/* Check to see if we have more than just the OS default time source */
|
||||
if(plist==>Pvt.timeProviders && (ellCount(plist)!=1 || ptp->get.Time!=&osdTimeGetCurrent)) {
|
||||
useOsdGetCurrent = 0;
|
||||
}
|
||||
|
||||
epicsMutexUnlock(lock);
|
||||
}
|
||||
|
||||
|
||||
@@ -205,9 +205,6 @@ epicsTime::epicsTime (const epicsTimeStamp &ts)
|
||||
epicsTime::epicsTime () :
|
||||
secPastEpoch(0u), nSec(0u) {}
|
||||
|
||||
epicsTime::epicsTime (const epicsTime &t) :
|
||||
secPastEpoch (t.secPastEpoch), nSec (t.nSec) {}
|
||||
|
||||
epicsTime epicsTime::getCurrent ()
|
||||
{
|
||||
epicsTimeStamp current;
|
||||
|
||||
@@ -82,7 +82,6 @@ public:
|
||||
class formatProblemWithStructTM {};
|
||||
|
||||
epicsTime ();
|
||||
epicsTime ( const epicsTime & t );
|
||||
|
||||
static epicsTime getEvent ( const epicsTimeEvent & );
|
||||
static epicsTime getCurrent ();
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
static clock_serv_t host_clock;
|
||||
|
||||
extern "C" {
|
||||
static int osdTimeGetCurrent (epicsTimeStamp *pDest)
|
||||
int osdTimeGetCurrent (epicsTimeStamp *pDest)
|
||||
{
|
||||
mach_timespec_t mts;
|
||||
struct timespec ts;
|
||||
|
||||
@@ -47,7 +47,8 @@
|
||||
|
||||
extern "C" void setThreadName ( DWORD dwThreadID, LPCSTR szThreadName );
|
||||
|
||||
static int osdTimeGetCurrent ( epicsTimeStamp *pDest );
|
||||
extern "C"
|
||||
int osdTimeGetCurrent ( epicsTimeStamp *pDest );
|
||||
|
||||
// for mingw
|
||||
#if !defined ( MAXLONGLONG )
|
||||
@@ -116,7 +117,7 @@ static int done = timeRegister();
|
||||
//
|
||||
// osdTimeGetCurrent ()
|
||||
//
|
||||
static int osdTimeGetCurrent ( epicsTimeStamp *pDest )
|
||||
int osdTimeGetCurrent ( epicsTimeStamp *pDest )
|
||||
{
|
||||
assert ( pCurrentTime );
|
||||
|
||||
|
||||
@@ -7,12 +7,30 @@
|
||||
* Author: Till Straumann <strauman@slac.stanford.edu>, 2011, 2014
|
||||
*/
|
||||
|
||||
// pull in libc feature test macros
|
||||
#include <stdlib.h>
|
||||
|
||||
// execinfo.h may not be present if uclibc is configured to omit backtrace()
|
||||
#if !defined(__UCLIBC_MAJOR__) || defined(__UCLIBC_HAS_EXECINFO__)
|
||||
# define HAS_EXECINFO 1
|
||||
#else
|
||||
# define HAS_EXECINFO 0
|
||||
#endif
|
||||
|
||||
#if HAS_EXECINFO
|
||||
|
||||
#include <execinfo.h>
|
||||
|
||||
#endif
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "epicsStackTracePvt.h"
|
||||
|
||||
int epicsBackTrace(void **buf, int buf_sz)
|
||||
{
|
||||
#if HAS_EXECINFO
|
||||
return backtrace(buf, buf_sz);
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -18,9 +18,6 @@ void osdMonotonicInit(void)
|
||||
{
|
||||
unsigned i;
|
||||
clockid_t ids[] = {
|
||||
#ifdef CLOCK_MONOTONIC_RAW
|
||||
CLOCK_MONOTONIC_RAW, /* Linux specific */
|
||||
#endif
|
||||
#ifdef CLOCK_HIGHRES
|
||||
CLOCK_HIGHRES, /* solaris specific */
|
||||
#endif
|
||||
|
||||
@@ -153,6 +153,10 @@ epicsMutexLockStatus epicsMutexOsdTryLock(struct epicsMutexOSD * pmutex)
|
||||
|
||||
void epicsMutexOsdShow(struct epicsMutexOSD * pmutex, unsigned int level)
|
||||
{
|
||||
/* GLIBC w/ NTPL is passing the &lock.__data.__lock as the first argument (UADDR)
|
||||
* of the futex() syscall. __lock is at offset 0 of the enclosing structures.
|
||||
*/
|
||||
printf(" pthread_mutex_t* uaddr=%p\n", &pmutex->lock);
|
||||
}
|
||||
|
||||
#else /*defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE)>=500 */
|
||||
|
||||
@@ -567,14 +567,13 @@ static epicsThreadOSD *createImplicit(void)
|
||||
pthreadInfo->osiPriority = 0;
|
||||
|
||||
#if defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && _POSIX_THREAD_PRIORITY_SCHEDULING > 0
|
||||
{
|
||||
struct sched_param param;
|
||||
int policy;
|
||||
if(pthread_getschedparam(tid,&policy,¶m) == 0)
|
||||
pthreadInfo->osiPriority =
|
||||
(param.sched_priority - pcommonAttr->minPriority) * 100.0 /
|
||||
if(pthread_getschedparam(tid,&pthreadInfo->schedPolicy,&pthreadInfo->schedParam) == 0) {
|
||||
if ( pcommonAttr->usePolicy && pthreadInfo->schedPolicy == pcommonAttr->schedPolicy ) {
|
||||
pthreadInfo->osiPriority =
|
||||
(pthreadInfo->schedParam.sched_priority - pcommonAttr->minPriority) * 100.0 /
|
||||
(pcommonAttr->maxPriority - pcommonAttr->minPriority + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* _POSIX_THREAD_PRIORITY_SCHEDULING */
|
||||
|
||||
status = pthread_setspecific(getpthreadInfo,(void *)pthreadInfo);
|
||||
@@ -907,7 +906,7 @@ epicsShareFunc double epicsShareAPI epicsThreadSleepQuantum ()
|
||||
{
|
||||
double hz;
|
||||
hz = sysconf ( _SC_CLK_TCK );
|
||||
if(hz<0)
|
||||
if(hz<=0)
|
||||
return 0.0;
|
||||
return 1.0 / hz;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
LAST_RESORT_PRIORITY, osdTimeGetCurrent)
|
||||
|
||||
extern "C" {
|
||||
static int osdTimeGetCurrent (epicsTimeStamp *pDest)
|
||||
int osdTimeGetCurrent (epicsTimeStamp *pDest)
|
||||
{
|
||||
struct timeval tv;
|
||||
struct timezone tz;
|
||||
|
||||
@@ -65,7 +65,7 @@ typedef int SOCKET;
|
||||
#define socket_ioctl(A,B,C) ioctl(A,B,(int)C)
|
||||
typedef int osiSockIoctl_t;
|
||||
typedef int osiSocklen_t;
|
||||
typedef int osiSockOptMcastLoop_t;
|
||||
typedef char osiSockOptMcastLoop_t;
|
||||
typedef char osiSockOptMcastTTL_t;
|
||||
|
||||
#define FD_IN_FDSET(FD) ((FD)<FD_SETSIZE&&(FD)>=0)
|
||||
|
||||
@@ -40,7 +40,7 @@ static struct {
|
||||
static epicsThreadOnceId onceId = EPICS_THREAD_ONCE_INIT;
|
||||
|
||||
|
||||
#if defined(CLOCK_REALTIME) && !defined(_WIN32)
|
||||
#if defined(CLOCK_REALTIME) && !defined(_WIN32) && !defined(__APPLE__)
|
||||
/* This code is not used on systems without Posix CLOCK_REALTIME,
|
||||
* but the only way to detect that is from the OS headers, so the
|
||||
* Makefile can't exclude compiling this file on those systems.
|
||||
@@ -48,7 +48,7 @@ static epicsThreadOnceId onceId = EPICS_THREAD_ONCE_INIT;
|
||||
|
||||
/* Forward references */
|
||||
|
||||
static int ClockTimeGetCurrent(epicsTimeStamp *pDest);
|
||||
int osdTimeGetCurrent(epicsTimeStamp *pDest);
|
||||
|
||||
#if defined(vxWorks) || defined(__rtems__)
|
||||
static void ClockTimeSync(void *dummy);
|
||||
@@ -89,7 +89,7 @@ static void ClockTime_InitOnce(void *pfirst)
|
||||
|
||||
/* Register as a time provider */
|
||||
generalTimeRegisterCurrentProvider("OS Clock", LAST_RESORT_PRIORITY,
|
||||
ClockTimeGetCurrent);
|
||||
osdTimeGetCurrent);
|
||||
}
|
||||
|
||||
void ClockTime_Init(int synchronize)
|
||||
@@ -125,7 +125,7 @@ void ClockTime_Init(int synchronize)
|
||||
else {
|
||||
/* No synchronization thread */
|
||||
if (firstTime)
|
||||
ClockTimeGetCurrent(&ClockTimePvt.startTime);
|
||||
osdTimeGetCurrent(&ClockTimePvt.startTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -191,7 +191,7 @@ static void ClockTimeSync(void *dummy)
|
||||
|
||||
/* Time Provider Routine */
|
||||
|
||||
static int ClockTimeGetCurrent(epicsTimeStamp *pDest)
|
||||
int osdTimeGetCurrent(epicsTimeStamp *pDest)
|
||||
{
|
||||
struct timespec clockNow;
|
||||
|
||||
|
||||
@@ -147,7 +147,7 @@ private:
|
||||
epicsThread thread;
|
||||
const double sleepQuantum;
|
||||
bool okToShare;
|
||||
bool exitFlag;
|
||||
int exitFlag; // use atomic ops
|
||||
bool terminateFlag;
|
||||
void run ();
|
||||
void reschedule ();
|
||||
|
||||
@@ -25,6 +25,7 @@ const double timerQueue :: exceptMsgMinPeriod = 60.0 * 5.0; // seconds
|
||||
epicsTimerQueue::~epicsTimerQueue () {}
|
||||
|
||||
timerQueue::timerQueue ( epicsTimerQueueNotify & notifyIn ) :
|
||||
mutex(__FILE__, __LINE__),
|
||||
notify ( notifyIn ),
|
||||
pExpireTmr ( 0 ),
|
||||
processThread ( 0 ),
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "epicsAtomic.h"
|
||||
#include "timerPrivate.h"
|
||||
#include "errlog.h"
|
||||
|
||||
@@ -46,7 +47,7 @@ timerQueueActive ::
|
||||
_refMgr ( refMgr ), queue ( *this ), thread ( *this, "timerQueue",
|
||||
epicsThreadGetStackSize ( epicsThreadStackMedium ), priority ),
|
||||
sleepQuantum ( epicsThreadSleepQuantum() ), okToShare ( okToShareIn ),
|
||||
exitFlag ( false ), terminateFlag ( false )
|
||||
exitFlag ( 0 ), terminateFlag ( false )
|
||||
{
|
||||
}
|
||||
|
||||
@@ -59,7 +60,7 @@ timerQueueActive::~timerQueueActive ()
|
||||
{
|
||||
this->terminateFlag = true;
|
||||
this->rescheduleEvent.signal ();
|
||||
while ( ! this->exitFlag ) {
|
||||
while ( ! epics::atomic::get(this->exitFlag) ) {
|
||||
this->exitEvent.wait ( 1.0 );
|
||||
}
|
||||
// in case other threads are waiting here also
|
||||
@@ -87,7 +88,7 @@ void timerQueueActive :: _printLastChanceExceptionMessage (
|
||||
|
||||
void timerQueueActive :: run ()
|
||||
{
|
||||
this->exitFlag = false;
|
||||
epics::atomic::set(this->exitFlag, 0);
|
||||
while ( ! this->terminateFlag ) {
|
||||
try {
|
||||
double delay = this->queue.process ( epicsTime::getCurrent() );
|
||||
@@ -105,7 +106,7 @@ void timerQueueActive :: run ()
|
||||
epicsThreadSleep ( 10.0 );
|
||||
}
|
||||
}
|
||||
this->exitFlag = true;
|
||||
epics::atomic::set(this->exitFlag, 1);
|
||||
this->exitEvent.signal (); // no access to queue after exitEvent signal
|
||||
}
|
||||
|
||||
@@ -143,7 +144,7 @@ void timerQueueActive::show ( unsigned int level ) const
|
||||
printf ( "exit event\n" );
|
||||
this->exitEvent.show ( level - 1u );
|
||||
printf ( "exitFlag = %c, terminateFlag = %c\n",
|
||||
this->exitFlag ? 'T' : 'F',
|
||||
epics::atomic::get(this->exitFlag) ? 'T' : 'F',
|
||||
this->terminateFlag ? 'T' : 'F' );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "timerPrivate.h"
|
||||
|
||||
timerQueueActiveMgr::timerQueueActiveMgr ()
|
||||
:mutex(__FILE__, __LINE__)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -255,8 +255,8 @@ TESTSPEC_RTEMS = libComTestHarness.boot; epicsRunLibComTests
|
||||
|
||||
TESTSCRIPTS_HOST += $(TESTS:%=%.t)
|
||||
ifneq ($(filter $(T_A),$(CROSS_COMPILER_RUNTEST_ARCHS)),)
|
||||
TESTPROD_RTEMS = $(TESTPROD_HOST)
|
||||
TESTSCRIPTS_RTEMS += $(filter-out epicsUnitTestTest.t, $(TESTS:%=%.t))
|
||||
TESTPROD = $(TESTPROD_HOST)
|
||||
TESTSCRIPTS += $(filter-out epicsUnitTestTest.t, $(TESTS:%=%.t))
|
||||
endif
|
||||
|
||||
|
||||
@@ -288,4 +288,15 @@ TESTPROD_HOST += cvtFastPerform
|
||||
cvtFastPerform_SRCS += cvtFastPerform.cpp
|
||||
testHarness_SRCS += cvtFastPerform.cpp
|
||||
|
||||
ifeq ($(OS_CLASS),Linux)
|
||||
ifeq ($(USE_POSIX_THREAD_PRIORITY_SCHEDULING),YES)
|
||||
TESTPROD_HOST += nonEpicsThreadPriorityTest
|
||||
nonEpicsThreadPriorityTest_SRCS += nonEpicsThreadPriorityTest.cpp
|
||||
nonEpicsThreadPriorityTest_SYS_LIBS += $(POSIX_LDLIBS:-l%=%)
|
||||
testHarness_SRCS += nonEpicsThreadPriorityTest.cpp
|
||||
epicsRunLibComTests_CFLAGS += -DHAVE_PTHREAD_PRIORITY_SCHEDULING
|
||||
TESTS += nonEpicsThreadPriorityTest
|
||||
endif
|
||||
endif
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
|
||||
@@ -78,6 +78,7 @@ typedef struct {
|
||||
size_t checkLen;
|
||||
epicsEventId jammer;
|
||||
int jam;
|
||||
epicsEventId done;
|
||||
} clientPvt;
|
||||
|
||||
static void testLogPrefix(void);
|
||||
@@ -102,12 +103,13 @@ static const char prefixexpectedmsg[] = "A message without prefix"
|
||||
static char prefixmsgbuffer[1024];
|
||||
|
||||
|
||||
static
|
||||
void testEqInt_(int lhs, int rhs, const char *LHS, const char *RHS)
|
||||
static void
|
||||
testEqInt_(int line, int lhs, int rhs, const char *LHS, const char *RHS)
|
||||
{
|
||||
testOk(lhs==rhs, "%s (%d) == %s (%d)", LHS, lhs, RHS, rhs);
|
||||
testOk(lhs==rhs, "%d: %s (%d) == %s (%d)", line, LHS, lhs, RHS, rhs);
|
||||
}
|
||||
#define testEqInt(L, R) testEqInt_(L, R, #L, #R);
|
||||
#define testEqInt(L, R) testEqInt_(__LINE__, L, R, #L, #R);
|
||||
|
||||
static
|
||||
void logClient(void* raw, const char* msg)
|
||||
{
|
||||
@@ -161,6 +163,7 @@ void logClient(void* raw, const char* msg)
|
||||
}
|
||||
|
||||
pvt->count++;
|
||||
epicsEventSignal(pvt->done);
|
||||
}
|
||||
|
||||
MAIN(epicsErrlogTest)
|
||||
@@ -169,7 +172,7 @@ MAIN(epicsErrlogTest)
|
||||
char msg[256];
|
||||
clientPvt pvt, pvt2;
|
||||
|
||||
testPlan(32);
|
||||
testPlan(40);
|
||||
|
||||
strcpy(msg, truncmsg);
|
||||
|
||||
@@ -188,7 +191,9 @@ MAIN(epicsErrlogTest)
|
||||
pvt2.jam = 0;
|
||||
|
||||
pvt.jammer = epicsEventMustCreate(epicsEventEmpty);
|
||||
pvt.done = epicsEventMustCreate(epicsEventEmpty);
|
||||
pvt2.jammer = epicsEventMustCreate(epicsEventEmpty);
|
||||
pvt2.done = epicsEventMustCreate(epicsEventEmpty);
|
||||
|
||||
testDiag("Check listener registration");
|
||||
|
||||
@@ -200,6 +205,7 @@ MAIN(epicsErrlogTest)
|
||||
errlogPrintfNoConsole("%s", pvt.expect);
|
||||
errlogFlush();
|
||||
|
||||
epicsEventMustWait(pvt.done);
|
||||
testEqInt(pvt.count, 1);
|
||||
|
||||
errlogAddListener(&logClient, &pvt2);
|
||||
@@ -210,7 +216,10 @@ MAIN(epicsErrlogTest)
|
||||
errlogPrintfNoConsole("%s", pvt.expect);
|
||||
errlogFlush();
|
||||
|
||||
epicsEventMustWait(pvt.done);
|
||||
testEqInt(pvt.count, 2);
|
||||
|
||||
epicsEventMustWait(pvt2.done);
|
||||
testEqInt(pvt2.count, 1);
|
||||
|
||||
/* Removes the first listener */
|
||||
@@ -223,6 +232,10 @@ MAIN(epicsErrlogTest)
|
||||
errlogPrintfNoConsole("%s", pvt2.expect);
|
||||
errlogFlush();
|
||||
|
||||
testOk(epicsEventWaitWithTimeout(pvt.done, 0.5) == epicsEventWaitTimeout,
|
||||
"%d: Listener 1 didn't run", __LINE__);
|
||||
testOk(epicsEventTryWait(pvt2.done) == epicsEventOK,
|
||||
"%d: Listener 2 ran", __LINE__);
|
||||
testEqInt(pvt.count, 2);
|
||||
testEqInt(pvt2.count, 2);
|
||||
|
||||
@@ -234,6 +247,10 @@ MAIN(epicsErrlogTest)
|
||||
errlogPrintfNoConsole("Something different");
|
||||
errlogFlush();
|
||||
|
||||
testOk(epicsEventWaitWithTimeout(pvt.done, 0.5) == epicsEventWaitTimeout,
|
||||
"%d: Listener 1 didn't run", __LINE__);
|
||||
testOk(epicsEventTryWait(pvt2.done) == epicsEventWaitTimeout,
|
||||
"%d: Listener 2 didn't run", __LINE__);
|
||||
testEqInt(pvt.count, 2);
|
||||
testEqInt(pvt2.count, 2);
|
||||
|
||||
@@ -248,6 +265,7 @@ MAIN(epicsErrlogTest)
|
||||
errlogPrintfNoConsole("%s", longmsg);
|
||||
errlogFlush();
|
||||
|
||||
epicsEventMustWait(pvt.done);
|
||||
testEqInt(pvt.count, 3);
|
||||
|
||||
pvt.expect = NULL;
|
||||
@@ -259,13 +277,15 @@ MAIN(epicsErrlogTest)
|
||||
pvt.jam = 1;
|
||||
|
||||
errlogPrintfNoConsole("%s", longmsg);
|
||||
epicsThreadSleep(0.1);
|
||||
|
||||
testOk(epicsEventWaitWithTimeout(pvt.done, 0.5) == epicsEventWaitTimeout,
|
||||
"%d: Listener 1 didn't run", __LINE__);
|
||||
testEqInt(pvt.count, 3);
|
||||
|
||||
epicsEventSignal(pvt.jammer);
|
||||
errlogFlush();
|
||||
|
||||
epicsEventMustWait(pvt.done);
|
||||
testEqInt(pvt.count, 4);
|
||||
|
||||
testDiag("Find buffer capacity (%u theoretical)",LOGBUFSIZE);
|
||||
@@ -303,6 +323,9 @@ MAIN(epicsErrlogTest)
|
||||
errlogFlush();
|
||||
}
|
||||
|
||||
testOk(epicsEventTryWait(pvt.done) == epicsEventOK,
|
||||
"%d: Listener 1 ran", __LINE__);
|
||||
|
||||
testDiag("Checking buffer use after partial flush");
|
||||
|
||||
/* Use the numbers from the largest block size above */
|
||||
@@ -317,16 +340,18 @@ MAIN(epicsErrlogTest)
|
||||
for (i = 0; i < N; i++) {
|
||||
errlogPrintfNoConsole("%s", msg);
|
||||
}
|
||||
epicsThreadSleep(0.1); /* should really be a second Event */
|
||||
|
||||
testOk(epicsEventWaitWithTimeout(pvt.done, 0.5) == epicsEventWaitTimeout,
|
||||
"%d: Listener 1 didn't run", __LINE__);
|
||||
testEqInt(pvt.count, 0);
|
||||
|
||||
/* Extract the first 2 messages, 2*(sizeof(msgNode) + 128) bytes */
|
||||
pvt.jam = -2;
|
||||
epicsEventSignal(pvt.jammer);
|
||||
epicsThreadSleep(0.1);
|
||||
epicsThreadSleep(0.5);
|
||||
|
||||
testDiag("Drained %u messages", pvt.count);
|
||||
epicsEventMustWait(pvt.done);
|
||||
testEqInt(pvt.count, 2);
|
||||
|
||||
/* The buffer has space for 1 more message: sizeof(msgNode) + 256 bytes */
|
||||
@@ -335,12 +360,15 @@ MAIN(epicsErrlogTest)
|
||||
testDiag("Overflow the buffer");
|
||||
errlogPrintfNoConsole("%s", msg);
|
||||
|
||||
testOk(epicsEventWaitWithTimeout(pvt.done, 0.5) == epicsEventWaitTimeout,
|
||||
"%d: Listener 1 didn't run", __LINE__);
|
||||
testEqInt(pvt.count, 2);
|
||||
|
||||
epicsEventSignal(pvt.jammer); /* Empty */
|
||||
errlogFlush();
|
||||
|
||||
testDiag("Logged %u messages", pvt.count);
|
||||
epicsEventMustWait(pvt.done);
|
||||
testEqInt(pvt.count, N+1);
|
||||
|
||||
/* Clean up */
|
||||
|
||||
@@ -381,8 +381,14 @@ MAIN(epicsStdlibTest)
|
||||
|
||||
testOk(epicsParseFloat("1e-40", &f, NULL) == S_stdlib_underflow,
|
||||
"Float '1e-40' => underflow");
|
||||
#ifdef vxWorks
|
||||
testTodoBegin("Not detected on VxWorks");
|
||||
#endif
|
||||
testOk(epicsParseDouble("1e-330", &d, NULL) == S_stdlib_underflow,
|
||||
"Double '1e-330' => underflow");
|
||||
#ifdef vxWorks
|
||||
testTodoEnd();
|
||||
#endif
|
||||
|
||||
testOk(epicsScanFloat("1e30", &f) && fabs(f - 1e30) < 1e24,
|
||||
"Float '1e30'");
|
||||
|
||||
@@ -108,11 +108,11 @@ inline double delayVerify::delay () const
|
||||
|
||||
double delayVerify::checkError () const
|
||||
{
|
||||
const double messageThresh = 2.0; // percent
|
||||
const double messageThresh = 5.0; // percent
|
||||
double actualDelay = this->expireStamp - this->beginStamp;
|
||||
double measuredError = actualDelay - this->expectedDelay;
|
||||
double percentError = 100.0 * fabs ( measuredError ) / this->expectedDelay;
|
||||
if ( ! testOk1 ( percentError < messageThresh ) ) {
|
||||
if ( ! testOk ( percentError < messageThresh, "%f < %f", percentError, messageThresh ) ) {
|
||||
testDiag ( "delay = %f s, error = %f s (%.1f %%)",
|
||||
this->expectedDelay, measuredError, percentError );
|
||||
}
|
||||
|
||||
100
modules/libcom/test/nonEpicsThreadPriorityTest.cpp
Normal file
100
modules/libcom/test/nonEpicsThreadPriorityTest.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
#include <epicsThread.h>
|
||||
#include "epicsUnitTest.h"
|
||||
#include "testMain.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef __rtems__
|
||||
|
||||
/* RTEMS is posix but currently does not use the pthread API */
|
||||
MAIN(nonEpicsThreadPriorityTest)
|
||||
{
|
||||
testPlan(1);
|
||||
testSkip(1, "Platform does not use pthread API");
|
||||
return testDone();
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void *nonEpicsTestFunc(void *arg)
|
||||
{
|
||||
unsigned int pri;
|
||||
// epicsThreadGetIdSelf() creates an EPICS context
|
||||
// verify that the priority computed by epics context
|
||||
// is OK
|
||||
pri = epicsThreadGetPriority( epicsThreadGetIdSelf() );
|
||||
if ( ! testOk( 0 == pri, "'createImplicit' assigned correct priority (%d) to non-EPICS thread", pri) ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (void*)1;
|
||||
}
|
||||
|
||||
|
||||
static void testFunc(void *arg)
|
||||
{
|
||||
epicsEventId ev = (epicsEventId)arg;
|
||||
int policy;
|
||||
struct sched_param param;
|
||||
int status;
|
||||
pthread_t tid;
|
||||
void *rval;
|
||||
pthread_attr_t attr;
|
||||
|
||||
status = pthread_getschedparam(pthread_self(), &policy,¶m);
|
||||
if ( status ) {
|
||||
testSkip(1, "pthread_getschedparam failed");
|
||||
goto done;
|
||||
}
|
||||
|
||||
if ( SCHED_FIFO != policy ) {
|
||||
testSkip(1, "nonEpicsThreadPriorityTest must be executed with privileges to use SCHED_FIFO");
|
||||
goto done;
|
||||
}
|
||||
|
||||
if ( pthread_attr_init( &attr ) ) {
|
||||
testSkip(1, "pthread_attr_init failed");
|
||||
goto done;
|
||||
}
|
||||
if ( pthread_attr_setinheritsched( &attr, PTHREAD_EXPLICIT_SCHED ) ) {
|
||||
testSkip(1, "pthread_attr_setinheritsched failed");
|
||||
goto done;
|
||||
}
|
||||
if ( pthread_attr_setschedpolicy ( &attr, SCHED_OTHER ) ) {
|
||||
testSkip(1, "pthread_attr_setschedpolicy failed");
|
||||
goto done;
|
||||
}
|
||||
param.sched_priority = 0;
|
||||
if ( pthread_attr_setschedparam ( &attr, ¶m ) ) {
|
||||
testSkip(1, "pthread_attr_setschedparam failed");
|
||||
goto done;
|
||||
}
|
||||
|
||||
if ( pthread_create( &tid, &attr, nonEpicsTestFunc, 0 ) ) {
|
||||
testSkip(1, "pthread_create failed");
|
||||
goto done;
|
||||
}
|
||||
|
||||
if ( pthread_join( tid, &rval ) ) {
|
||||
testSkip(1, "pthread_join failed");
|
||||
goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
|
||||
epicsEventSignal( ev );
|
||||
}
|
||||
|
||||
MAIN(nonEpicsThreadPriorityTest)
|
||||
{
|
||||
testPlan(2);
|
||||
epicsEventId testComplete = epicsEventMustCreate(epicsEventEmpty);
|
||||
epicsThreadMustCreate("nonEpicsThreadPriorityTest", epicsThreadPriorityLow,
|
||||
epicsThreadGetStackSize(epicsThreadStackMedium),
|
||||
testFunc, testComplete);
|
||||
epicsEventWaitStatus status = epicsEventWait(testComplete);
|
||||
testOk(status == epicsEventWaitOK,
|
||||
"epicsEventWait returned %d", status);
|
||||
return testDone();
|
||||
}
|
||||
#endif
|
||||
Submodule modules/normativeTypes updated: 41d56fdf89...c6168a7477
Submodule modules/pvAccess updated: 5e79342219...936f5d35d8
Submodule modules/pvData updated: d776f6eaf0...6ceaa6adb0
Submodule modules/pvDatabase updated: 07a951c3a2...70ee857782
Submodule modules/pva2pva updated: a245adb5cf...ce39c93201
Submodule modules/pvaClient updated: b1c101578b...aba40922e6
Reference in New Issue
Block a user