diff --git a/configure/CONFIG_BASE b/configure/CONFIG_BASE index 8934a8e5d..8db74bac7 100644 --- a/configure/CONFIG_BASE +++ b/configure/CONFIG_BASE @@ -68,11 +68,11 @@ DBTOMENUH = $(PERL) $(TOOLS)/dbdToMenuH.pl REGISTERRECORDDEVICEDRIVER = $(PERL) $(TOOLS)/registerRecordDeviceDriver.pl CONVERTRELEASE = $(PERL) $(call FIND_TOOL,convertRelease.pl) FULLPATHNAME = $(PERL) $(TOOLS)/fullPathName.pl +GENVERSIONHEADER = $(PERL) $(TOOLS)/genVersionHeader.pl $(QUIET_FLAG) #------------------------------------------------------- # tools for installing libraries and products -INSTALL_QUIETLY := $(if $(findstring s,$(MAKEFLAGS)),-q,) -INSTALL = $(PERL) $(TOOLS)/installEpics.pl $(INSTALL_QUIETLY) +INSTALL = $(PERL) $(TOOLS)/installEpics.pl $(QUIET_FLAG) INSTALL_PRODUCT = $(INSTALL) INSTALL_LIBRARY = $(INSTALL) diff --git a/configure/CONFIG_COMMON b/configure/CONFIG_COMMON index fcdadb7bf..e74f05e20 100644 --- a/configure/CONFIG_COMMON +++ b/configure/CONFIG_COMMON @@ -85,6 +85,7 @@ IOCS_APPL_TOP = $(shell $(FULLPATHNAME) $(INSTALL_LOCATION)) # Make echo output - suppress echoing if make's '-s' flag is set NOP = : ECHO = @$(if $(findstring s,$(patsubst T_A=%,,$(MAKEFLAGS))),$(NOP),echo) +QUIET_FLAG := $(if $(findstring s,$(MAKEFLAGS)),-q,) #------------------------------------------------------- ifdef T_A @@ -334,6 +335,14 @@ COMPILE.cpp = $(CCC) $(CPPFLAGS) $(CXXFLAGS) $(INCLUDES) # C preprocessor command PREPROCESS.cpp = $(CPP) $(CPPFLAGS) $(INCLUDES) $< > $@ +#-------------------------------------------------- +# genVersion header defaults + +# C macro name +GENVERSIONMACRO = VCSVERSION +# C macro default value (empty to use date+time) +GENVERSIONDEFAULT = + #-------------------------------------------------- # Header dependency file generation diff --git a/configure/RULES_BUILD b/configure/RULES_BUILD index a0f9537fe..7bc4c77d9 100644 --- a/configure/RULES_BUILD +++ b/configure/RULES_BUILD @@ -346,6 +346,14 @@ tapfiles: $(TESTSCRIPTS) $(TAPFILES) @$(RM) $@ $(PERL) $(TOOLS)/makeTestfile.pl $@ $< +#--------------------------------------------------------------- +# Generate header with version number from VCS + +ifneq ($(GENVERSION),) +$(COMMON_DIR)/$(GENVERSION): FORCE + $(GENVERSIONHEADER) -t $(TOP) -N $(GENVERSIONMACRO) -V "$(GENVERSIONDEFAULT)" $@ +endif + #--------------------------------------------------------------- # Install rules for BIN_INSTALLS and LIB_INSTALLS @@ -479,7 +487,7 @@ $(INSTALL_TEMPLATES_SUBDIR)/%: % .PRECIOUS: $(COMMON_INC) .PHONY: all host inc build install clean rebuild buildInstall build_clean -.PHONY: runtests tapfiles checkRelease warnRelease noCheckRelease +.PHONY: runtests tapfiles checkRelease warnRelease noCheckRelease FORCE endif # BASE_RULES_BUILD # EOF RULES_BUILD diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html index d90a5847a..43838dcf6 100644 --- a/documentation/RELEASE_NOTES.html +++ b/documentation/RELEASE_NOTES.html @@ -20,6 +20,16 @@ --> +

Generate Version Header

+ +

A Perl script and Makefile rules have been added to allow modules to generate +a C header file with a macro defined with an automatically updated identifier. +This is a VCS revision ID (Darcs, Git, Mercurial Subversion and Bazaar are all +supported) or the date/time of the build if no VCS system is in use.

+ +

The makeBaseApp example template has been updated with a new device support +which makes this identifier visible via a lsi (long string input) record.

+

epicsTime API return status

The epicsTime routines that used to return epicsTimeERROR now return a specific diff --git a/src/template/base/Makefile b/src/template/base/Makefile index 865d95c4a..bfc6de244 100644 --- a/src/template/base/Makefile +++ b/src/template/base/Makefile @@ -28,9 +28,12 @@ TEMPLATES += top/exampleApp/Makefile TEMPLATES += top/exampleApp/Db/Makefile TEMPLATES += top/exampleApp/Db/dbExample1.db TEMPLATES += top/exampleApp/Db/dbExample2.db +TEMPLATES += top/exampleApp/Db/_APPNAME_Version.db TEMPLATES += top/exampleApp/Db/dbSubExample.db TEMPLATES += top/exampleApp/Db/user.substitutions TEMPLATES += top/exampleApp/src/Makefile +TEMPLATES += top/exampleApp/src/dev_APPNAME_Version.c +TEMPLATES += top/exampleApp/src/dev_APPNAME_Version.dbd TEMPLATES += top/exampleApp/src/xxxRecord.dbd TEMPLATES += top/exampleApp/src/xxxRecord.c TEMPLATES += top/exampleApp/src/devXxxSoft.c diff --git a/src/template/base/top/exampleApp/Db/Makefile b/src/template/base/top/exampleApp/Db/Makefile index 667845ecc..679bba60c 100644 --- a/src/template/base/top/exampleApp/Db/Makefile +++ b/src/template/base/top/exampleApp/Db/Makefile @@ -6,6 +6,7 @@ include $(TOP)/configure/CONFIG # Install databases, templates & substitutions like this DB += dbExample1.db DB += dbExample2.db +DB += _APPNAME_Version.db DB += dbSubExample.db DB += user.substitutions diff --git a/src/template/base/top/exampleApp/Db/_APPNAME_Version.db b/src/template/base/top/exampleApp/Db/_APPNAME_Version.db new file mode 100644 index 000000000..1b03f08ba --- /dev/null +++ b/src/template/base/top/exampleApp/Db/_APPNAME_Version.db @@ -0,0 +1,6 @@ +record(lsi, "$(user):_APPNAME_:version") { + field(DTYP, "_APPNAME_ version") + field(DESC, "Version string") + field(SIZV, "$(SIZV=200)") + field(PINI, "YES") +} diff --git a/src/template/base/top/exampleApp/src/Makefile b/src/template/base/top/exampleApp/src/Makefile index f3b262c77..024726e24 100644 --- a/src/template/base/top/exampleApp/src/Makefile +++ b/src/template/base/top/exampleApp/src/Makefile @@ -13,19 +13,26 @@ DBD += xxxSupport.dbd # Build an IOC support library LIBRARY_IOC += _APPNAME_Support -# Compile and add the code to the support library +# Compile and add code to the support library _APPNAME_Support_SRCS += xxxRecord.c _APPNAME_Support_SRCS += devXxxSoft.c # Link locally-provided code into the support library, -# rather than directly into the IOC application. -# This is required for Windows DLL builds. +# rather than directly into the IOC application, that +# causes problems on Windows DLL builds _APPNAME_Support_SRCS += dbSubExample.c +_APPNAME_Support_SRCS += dev_APPNAME_Version.c _APPNAME_Support_SRCS += _APPNAME_Hello.c _APPNAME_Support_SRCS += initTrace.c _APPNAME_Support_LIBS += $(EPICS_BASE_IOC_LIBS) +# Auto-generate a header file containing a version string. +# Version comes from the VCS if available, else date+time. +GENVERSION = _APPNAME_Version.h +# Macro name +GENVERSIONMACRO = _APPNAME_VERSION + # Build the IOC application PROD_IOC = _APPNAME_ @@ -36,6 +43,7 @@ DBD += _APPNAME_.dbd _APPNAME__DBD += base.dbd _APPNAME__DBD += xxxSupport.dbd _APPNAME__DBD += dbSubExample.dbd +_APPNAME__DBD += dev_APPNAME_Version.dbd _APPNAME__DBD += _APPNAME_Hello.dbd _APPNAME__DBD += initTrace.dbd @@ -77,3 +85,5 @@ include $(TOP)/configure/RULES #---------------------------------------- # ADD EXTRA GNUMAKE RULES BELOW HERE +# Explicit dependency needed for generated header file +dev_APPNAME_Version$(DEP): $(COMMON_DIR)/$(GENVERSION) diff --git a/src/template/base/top/exampleApp/src/dev_APPNAME_Version.c b/src/template/base/top/exampleApp/src/dev_APPNAME_Version.c new file mode 100644 index 000000000..4f2c28f67 --- /dev/null +++ b/src/template/base/top/exampleApp/src/dev_APPNAME_Version.c @@ -0,0 +1,38 @@ +/* dev_APPNAME_Version.c */ +/* Example device support for the lsi (long string input) record + * providing the module version string as the value + */ + +#include +#include +#include + +#include "devSup.h" +#include "lsiRecord.h" + +#include "_APPNAME_Version.h" + +/* must be last include */ +#include "epicsExport.h" + +const char const version[] = _APPNAME_VERSION; + +static long read_string(lsiRecord *prec) +{ + size_t N = sizeof version; + char *buf = prec->val; + + if (N > prec->sizv) + N = prec->sizv; + prec->len = N; + + memcpy(buf, version, N); + buf[N - 1] = '\0'; + + return 0; +} + +static lsidset dev_CSAFEAPPNAME_Version = { + 5, NULL, NULL, NULL, NULL, read_string +}; +epicsExportAddress(dset,dev_CSAFEAPPNAME_Version); diff --git a/src/template/base/top/exampleApp/src/dev_APPNAME_Version.dbd b/src/template/base/top/exampleApp/src/dev_APPNAME_Version.dbd new file mode 100644 index 000000000..67295f3f0 --- /dev/null +++ b/src/template/base/top/exampleApp/src/dev_APPNAME_Version.dbd @@ -0,0 +1 @@ +device(lsi,INST_IO,dev_CSAFEAPPNAME_Version,"_APPNAME_ version") diff --git a/src/template/base/top/exampleBoot/ioc/st.cmd@Common b/src/template/base/top/exampleBoot/ioc/st.cmd@Common index 76360dd08..a2d018e3a 100644 --- a/src/template/base/top/exampleBoot/ioc/st.cmd@Common +++ b/src/template/base/top/exampleBoot/ioc/st.cmd@Common @@ -13,6 +13,7 @@ _CSAFEAPPNAME__registerRecordDeviceDriver pdbbase ## Load record instances dbLoadTemplate "db/user.substitutions" +dbLoadRecords "db/_APPNAME_Version.db", "user=_USER_" dbLoadRecords "db/dbSubExample.db", "user=_USER_" ## Set this to see messages from mySub diff --git a/src/template/base/top/exampleBoot/ioc/st.cmd@RTEMS b/src/template/base/top/exampleBoot/ioc/st.cmd@RTEMS index 4addc5c29..cc96f84ab 100644 --- a/src/template/base/top/exampleBoot/ioc/st.cmd@RTEMS +++ b/src/template/base/top/exampleBoot/ioc/st.cmd@RTEMS @@ -11,6 +11,7 @@ _CSAFEAPPNAME__registerRecordDeviceDriver(pdbbase) ## Load record instances dbLoadTemplate("db/user.substitutions") +dbLoadRecords("db/_APPNAME_Version.db", "user=_USER_") dbLoadRecords("db/dbSubExample.db", "user=_USER_") ## Set this to see messages from mySub diff --git a/src/template/base/top/exampleBoot/ioc/st.cmd@vxWorks b/src/template/base/top/exampleBoot/ioc/st.cmd@vxWorks index 27ad0572b..44a9afc67 100644 --- a/src/template/base/top/exampleBoot/ioc/st.cmd@vxWorks +++ b/src/template/base/top/exampleBoot/ioc/st.cmd@vxWorks @@ -20,6 +20,7 @@ _CSAFEAPPNAME__registerRecordDeviceDriver pdbbase ## Load record instances dbLoadTemplate "db/user.substitutions" +dbLoadRecords "db/_APPNAME_Version.db", "user=_USER_" dbLoadRecords "db/dbSubExample.db", "user=_USER_" ## Set this to see messages from mySub diff --git a/src/tools/Makefile b/src/tools/Makefile index 580e32cb6..6b70b01cf 100644 --- a/src/tools/Makefile +++ b/src/tools/Makefile @@ -46,6 +46,7 @@ PERL_SCRIPTS += mkmf.pl PERL_SCRIPTS += munch.pl PERL_SCRIPTS += replaceVAR.pl PERL_SCRIPTS += useManifestTool.pl +PERL_SCRIPTS += genVersionHeader.pl PERL_SCRIPTS += dbdToMenuH.pl PERL_SCRIPTS += dbdToRecordtypeH.pl diff --git a/src/tools/genVersionHeader.pl b/src/tools/genVersionHeader.pl new file mode 100644 index 000000000..5851ea831 --- /dev/null +++ b/src/tools/genVersionHeader.pl @@ -0,0 +1,171 @@ +#!/usr/bin/env perl +#************************************************************************* +# Copyright (c) 2014 Brookhaven National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* +# +# Generate a C header file which +# defines a macro with a string +# describing the VCS revision +# + +use FindBin qw($Bin); +use lib "$Bin/../../lib/perl"; + +use EPICS::Getopts; +use POSIX qw(strftime); + +use strict; + +# RFC 8601 date+time w/ zone (eg "2014-08-29T09:42:47-0700") +my $tfmt = '%Y-%m-%dT%H:%M:%S'; +$tfmt .= '%z' unless $^O eq 'MSWin32'; # %z returns zone name on Windows +my $now = strftime($tfmt, localtime); + +our ($opt_h, $opt_v, $opt_q); +our $opt_t = '.'; +our $opt_N = 'VCSVERSION'; +our $opt_V = $now; + +my $vcs; + +getopts('hvqt:N:V:') && @ARGV == 1 + or HELP_MESSAGE(); + +my ($outfile) = @ARGV; + +if (!$vcs && -d "$opt_t/_darcs") { # Darcs + print "== Found /_darcs directory\n" if $opt_v; + # v1-4-dirty + # is tag 'v1' plus 4 patches + # with uncommited modifications + my $result = `cd "$opt_t" && echo "\$(darcs show tags | head -1)-\$((\$(darcs changes --count --from-tag .)-1))"`; + chomp $result; + print "== darcs show tags, changes:\n$result\n==\n" if $opt_v; + if (!$? && $result ne '') { + $opt_V = $result; + $vcs = 'Darcs'; + # see if working copy has modifications, additions, removals, or missing files + my $hasmod = `darcs whatsnew --repodir="$opt_t" -l`; + $opt_V .= '-dirty' unless $?; + } +} +if (!$vcs && -d "$opt_t/.hg") { # Mercurial + print "== Found /.hg directory\n" if $opt_v; + # v1-4-abcdef-dirty + # is 4 commits after tag 'v1' with short hash abcdef + # with uncommited modifications + my $result = `hg tip --template '{latesttag}-{latesttagdistance}-{node|short}'`; + print "== hg tip:\n$result\n==\n" if $opt_v; + if (!$? && $result ne '') { + $opt_V = $result; + $vcs = 'Mercurial'; + # see if working copy has modifications, additions, removals, or missing files + my $hasmod = `hg status -m -a -r -d`; + chomp $hasmod; + $opt_V .= '-dirty' if $hasmod ne ''; + } +} +if (!$vcs && -d "$opt_t/.git") { # Git + print "== Found /.git directory\n" if $opt_v; + # v1-4-abcdef-dirty + # is 4 commits after tag 'v1' with short hash abcdef + # with uncommited modifications + my $result = `git describe --always --tags --dirty --abbrev=20`; + chomp $result; + print "== git describe:\n$result\n==\n" if $opt_v; + if (!$? && $result ne '') { + $opt_V = $result; + $vcs = 'Git'; + } +} +if (!$vcs && -d "$opt_t/.svn") { # Subversion + print "== Found /.svn directory\n" if $opt_v; + # 12345-dirty + my $result = `cd "$opt_t" && svn info --non-interactive`; + chomp $result; + print "== svn info:\n$result\n==\n" if $opt_v; + if (!$? && $result =~ /^Revision:\s*(\d+)/m) { + $opt_V = $1; + $vcs = 'Subversion'; + # see if working copy has modifications, additions, removals, or missing files + my $hasmod = `cd "$opt_t" && svn status --non-interactive`; + chomp $hasmod; + $opt_V .= '-dirty' if $hasmod ne ''; + } +} +if (!$vcs && -d "$opt_t/.bzr") { # Bazaar + print "== Found /.bzr directory\n" if $opt_v; + # 12444-anj@aps.anl.gov-20131003210403-icfd8mc37g8vctpf-dirty + my $result = `bzr version-info -q --custom --template="{revno}-{revision_id}-{clean}"`; + print "== bzr version-info:\n$result\n==\n" if $opt_v; + if (!$? && $result ne '') { + $result =~ s/-([01])$/$1 ? '' : '-dirty'/e; + $opt_V = $result; + $vcs = 'Bazaar'; + } +} +if (!$vcs) { + print "== No VCS directories\n" if $opt_v; + if ($opt_V eq '') { + $vcs = 'build date/time'; + $opt_V = $now; + } + else { + $vcs = 'Makefile'; + } +} + +my $output = << "__END"; +/* Generated file, do not edit! */ + +/* Version determined from $vcs */ + +#ifndef $opt_N + #define $opt_N \"$opt_V\" +#endif +__END + +print "== Want:\n$output==\n" if $opt_v; + +my $DST; +if (open($DST, '+<', $outfile)) { + my $actual = join('', <$DST>); + print "== Current:\n$actual==\n" if $opt_v; + + if ($actual eq $output) { + print "Keeping VCS header $outfile\n $opt_N = \"$opt_V\"\n" + unless $opt_q; + exit 0; + } + print "Updating VCS header $outfile\n $opt_N = \"$opt_V\"\n" + unless $opt_q; +} else { + print "Creating VCS header $outfile\n $opt_N = \"$opt_V\"\n" + unless $opt_q; + open($DST, '>', $outfile) + or die "Can't create $outfile: $!\n"; +} + +seek $DST, 0, 0; +truncate $DST, 0; +print $DST $output; +close $DST; + +sub HELP_MESSAGE { + print STDERR <