Compare commits

...

14 Commits

Author SHA1 Message Date
Andrew Johnson
dc61957963 Set SNAPSHOT for R3.14.12.5 2015-03-24 09:57:35 -05:00
Janet Anderson
583108e42a Set version number to R3.14.12.5. 2015-03-24 08:50:39 -05:00
Andrew Johnson
9470830091 catools: Fix printing -ve DBF_LONG values on 64-bit 2015-03-23 15:59:11 -05:00
Andrew Johnson
e84e7b930f Release notes for ao-convert changes 2015-03-20 17:05:16 -05:00
Andrew Johnson
f498b36438 Prevent overflows in ao value conversion.
Also makes ROFF fields unsigned, needed for ROFF=0x80000000
2015-03-20 16:34:14 -05:00
Andrew Johnson
d927afe257 Make including cfg/CONFIG* files unconditional 2015-03-19 10:18:23 -05:00
Andrew Johnson
f994944531 libCom/posix: Remove pthread_setcanceltype() 2015-03-18 11:36:06 -05:00
Andrew Johnson
5ffda58351 ca: Fix missing guard argument 2015-03-13 11:08:05 -05:00
Andrew Johnson
f6373093a5 Fix Windows linkage warning in alarmString.h 2015-03-13 10:56:03 -05:00
Andrew Johnson
8215244a28 Fix Windows time discontinuity report 2015-03-13 10:06:23 -05:00
Andrew Johnson
de298da94c Fix Jenkins instability on Windows 2015-03-11 10:53:37 -05:00
Andrew Johnson
753bfcc579 Check sender threads are starting 2015-03-10 18:14:11 -05:00
Andrew Johnson
4568cd3616 Chasing Jenkins instability on Windows 2015-03-10 14:17:28 -05:00
Andrew Johnson
e75ec5f85b Set snapshot to -rc1-DEV 2015-03-09 14:33:17 -05:00
13 changed files with 144 additions and 91 deletions

View File

@@ -14,17 +14,17 @@
#
ifneq ($(wildcard $(TOP)/configure/CONFIG_BASE_VERSION),)
EPICS_BASE = $(INSTALL_LOCATION)
CONFIG = $(TOP)/configure
BASE_TOP=YES
EPICS_BASE = $(INSTALL_LOCATION)
CONFIG = $(TOP)/configure
BASE_TOP=YES
else
CONFIG ?= $(EPICS_BASE)/configure
CONFIG ?= $(EPICS_BASE)/configure
endif
# Provide a default if the user hasn't set EPICS_HOST_ARCH
ifeq ($(origin EPICS_HOST_ARCH), undefined)
# NB: Must use a simply expanded variable here for performance:
EPICS_HOST_ARCH := $(shell $(CONFIG)/../startup/EpicsHostArch.pl)
# NB: We use a simply expanded variable here for performance:
EPICS_HOST_ARCH := $(shell $(CONFIG)/../startup/EpicsHostArch.pl)
endif
#
@@ -34,8 +34,8 @@ endif
-include $(CONFIG)/RELEASE.$(EPICS_HOST_ARCH)
-include $(CONFIG)/RELEASE.$(EPICS_HOST_ARCH).Common
ifdef T_A
-include $(CONFIG)/RELEASE.Common.$(T_A)
-include $(CONFIG)/RELEASE.$(EPICS_HOST_ARCH).$(T_A)
-include $(CONFIG)/RELEASE.Common.$(T_A)
-include $(CONFIG)/RELEASE.$(EPICS_HOST_ARCH).$(T_A)
endif
include $(CONFIG)/CONFIG_COMMON
@@ -60,60 +60,62 @@ include $(CONFIG)/os/CONFIG.$(EPICS_HOST_ARCH).Common
RELEASE_TOPS := $(shell $(CONVERTRELEASE) -T $(TOP) releaseTops)
ifdef T_A
# Cross compile specific definitions
#
ifneq ($(EPICS_HOST_ARCH),$(T_A))
include $(CONFIG)/CONFIG.CrossCommon
endif
# Cross compile specific definitions
#
ifneq ($(EPICS_HOST_ARCH),$(T_A))
include $(CONFIG)/CONFIG.CrossCommon
endif
# Target architecture specific definitions
#
-include $(CONFIG)/os/CONFIG.Common.$(T_A)
# Target architecture specific definitions
#
-include $(CONFIG)/os/CONFIG.Common.$(T_A)
# Host-Target architecture specific definitions
#
-include $(CONFIG)/os/CONFIG.$(EPICS_HOST_ARCH).$(T_A)
# Host-Target architecture specific definitions
#
-include $(CONFIG)/os/CONFIG.$(EPICS_HOST_ARCH).$(T_A)
# RELEASE file specific definitions
#
ifneq ($(CONFIG),$(TOP)/configure)
-include $(CONFIG)/CONFIG_APP_INCLUDE
endif
# RELEASE file specific definitions
#
ifneq ($(CONFIG),$(TOP)/configure)
-include $(CONFIG)/CONFIG_APP_INCLUDE
endif
# Site specific target and host-target definitions
#
-include $(CONFIG)/os/CONFIG_SITE.Common.$(T_A)
-include $(CONFIG)/os/CONFIG_SITE.$(EPICS_HOST_ARCH).$(T_A)
endif # ifdef T_A
# Site specific target and host-target definitions
#
-include $(CONFIG)/os/CONFIG_SITE.Common.$(T_A)
-include $(CONFIG)/os/CONFIG_SITE.$(EPICS_HOST_ARCH).$(T_A)
# Include <top>/cfg/CONFIG* definitions from tops defined in RELEASE* files
#
ifneq ($(CONFIG),$(TOP)/configure)
RELEASE_TOPS_REVERSE := $(shell \
$(PERL) -e '$$,=" ";print reverse @ARGV' $(RELEASE_TOPS))
RELEASE_CFG_CONFIGS = $(foreach top, $(RELEASE_TOPS_REVERSE), \
$(wildcard $($(top))/cfg/CONFIG*))
ifneq ($(RELEASE_CFG_CONFIGS),)
include $(RELEASE_CFG_CONFIGS)
endif
RELEASE_TOPS_REVERSE := $(shell \
$(PERL) -e '$$,=" ";print reverse @ARGV' $(RELEASE_TOPS))
RELEASE_CFG_CONFIGS = $(foreach top, $(RELEASE_TOPS_REVERSE), \
$(wildcard $($(top))/cfg/CONFIG*))
ifneq ($(RELEASE_CFG_CONFIGS),)
include $(RELEASE_CFG_CONFIGS)
endif
endif
# Include $(INSTALL_CFG)/CONFIG* definitions
#
TOP_CFG_CONFIGS = $(wildcard $(INSTALL_CFG)/CONFIG*)
ifneq ($(TOP_CFG_CONFIGS),)
include $(TOP_CFG_CONFIGS)
include $(TOP_CFG_CONFIGS)
endif
endif # ifdef T_A
# User specific definitions
#
-include $(HOME)/configure/CONFIG_USER
-include $(HOME)/configure/CONFIG_USER.$(EPICS_HOST_ARCH)
ifdef T_A
-include $(HOME)/configure/CONFIG_USER.Common.$(T_A)
-include $(HOME)/configure/CONFIG_USER.$(EPICS_HOST_ARCH).$(T_A)
-include $(HOME)/configure/CONFIG_USER.Common.$(T_A)
-include $(HOME)/configure/CONFIG_USER.$(EPICS_HOST_ARCH).$(T_A)
endif

View File

@@ -38,11 +38,11 @@ EPICS_PATCH_LEVEL = 5
#EPICS_DEV_SNAPSHOT=-pre1-DEV
#EPICS_DEV_SNAPSHOT=-pre2
#EPICS_DEV_SNAPSHOT=-pre2-DEV
EPICS_DEV_SNAPSHOT=-rc1
#EPICS_DEV_SNAPSHOT=-rc1
#EPICS_DEV_SNAPSHOT=-rc1-DEV
#EPICS_DEV_SNAPSHOT=-rc2
#EPICS_DEV_SNAPSHOT=-rc2-DEV
#EPICS_DEV_SNAPSHOT=
EPICS_DEV_SNAPSHOT=
# No changes should be needed below here

View File

@@ -13,6 +13,14 @@
<!-- Insert new items immediately below here ... -->
<h3>aoRecord raw conversion overflows</h3>
<p>The ao record type now checks converted raw values and limits them to the
32-bit integer range before writing them to the RVAL field. Previously value
overflows relied on Undefined Behaviour which could give different results on
different platforms. The ROFF fields of the ao and ai record types are now
DBF_ULONG to allow an ROFF setting of 0x80000000 to work properly.</p>
<h3>Changes to &lt;top&gt;/cfg/* files</h3>
<p>The order in which cfg/CONFIG* and cfg/RULES* files are included from support

View File

@@ -591,7 +591,7 @@ void ca_client_context :: whenThereIsAnExceptionDestroySyncGroupIO (
}
else {
// dont reverse the lock hierarchy
epicsGuardRelease < epicsMutex > guardRelease ();
epicsGuardRelease < epicsMutex > guardRelease ( guard );
{
//
// we will definately stall out here if all of the

View File

@@ -87,7 +87,8 @@ static void sprint_long (char *ret, dbr_long_t val, IntFormatT outType)
"0x%lX" /* hex */
};
sprintf(ret, fmt[outType], val);
/* Formats have long modifier, pass value as a long */
sprintf(ret, fmt[outType], (long) val);
}
}

View File

@@ -43,7 +43,7 @@ typedef enum {
#define INVALID_ALARM epicsSevInvalid
#define lastEpicsAlarmSev epicsSevInvalid
epicsShareExtern const char *epicsAlarmSeverityStrings [ALARM_NSEV];
extern const char *epicsAlarmSeverityStrings [ALARM_NSEV];
/* ALARM STATUS - must match menuAlarmStat.dbd */
@@ -98,7 +98,7 @@ typedef enum {
#define WRITE_ACCESS_ALARM epicsAlarmWriteAccess
#define lastEpicsAlarmCond epicsAlarmWriteAccess
epicsShareExtern const char *epicsAlarmConditionStrings [ALARM_NSTATUS];
extern const char *epicsAlarmConditionStrings [ALARM_NSTATUS];
#ifdef __cplusplus
}

View File

@@ -366,7 +366,7 @@ void currentTime::getCurrentTime ( epicsTimeStamp & dest )
LONGLONG epicsTimeCurrent = this->epicsTimeLast + offset;
if ( this->epicsTimeLast > epicsTimeCurrent ) {
double diff = static_cast < double >
( this->epicsTimeLast - epicsTimeCurrent );
( this->epicsTimeLast - epicsTimeCurrent ) / EPICS_TIME_TICKS_PER_SEC;
errlogPrintf (
"currentTime::getCurrentTime(): %f sec "
"time discontinuity detected\n",

View File

@@ -368,20 +368,17 @@ static void once(void)
checkStatusOnce(status,"atexit");
epicsThreadOnceCalled = 1;
}
static void * start_routine(void *arg)
{
epicsThreadOSD *pthreadInfo = (epicsThreadOSD *)arg;
int status;
int oldtype;
sigset_t blockAllSig;
sigfillset(&blockAllSig);
pthread_sigmask(SIG_SETMASK,&blockAllSig,NULL);
status = pthread_setspecific(getpthreadInfo,arg);
checkStatusQuit(status,"pthread_setspecific","start_routine");
status = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,&oldtype);
checkStatusQuit(status,"pthread_setcanceltype","start_routine");
status = mutexLock(&listLock);
checkStatusQuit(status,"pthread_mutex_lock","start_routine");
ellAdd(&pthreadList,&pthreadInfo->node);

View File

@@ -25,8 +25,10 @@
#include "testMain.h"
static const char *msg1 = "1234567890This is a very long message.";
static volatile int testExit = 0;
static volatile int sendExit = 0;
static volatile int recvExit = 0;
static epicsEventId finished;
static unsigned int mediumStack;
/*
* In Numerical Recipes in C: The Art of Scientific Computing (William H.
@@ -77,6 +79,7 @@ extern "C" void
receiver(void *arg)
{
epicsMessageQueue *q = (epicsMessageQueue *)arg;
const char *myName = epicsThreadGetNameSelf();
char cbuf[80];
int expectmsg[4];
int len;
@@ -85,10 +88,10 @@ receiver(void *arg)
for (sender = 1 ; sender <= 4 ; sender++)
expectmsg[sender-1] = 1;
while (!testExit) {
while (!recvExit) {
cbuf[0] = '\0';
len = q->receive(cbuf, sizeof cbuf, 2.0);
if (len < 0 && !testExit) {
if (len < 0 && !recvExit) {
testDiag("receiver() received unexpected timeout");
++errors;
}
@@ -96,7 +99,8 @@ receiver(void *arg)
sender >= 1 && sender <= 4) {
if (expectmsg[sender-1] != msgNum) {
++errors;
testDiag("%s received %d '%.*s' -- expected %d", epicsThreadGetNameSelf(), len, len, cbuf, expectmsg[sender-1]);
testDiag("%s received %d '%.*s' -- expected %d",
myName, len, len, cbuf, expectmsg[sender-1]);
}
expectmsg[sender-1] = msgNum + 1;
epicsThreadSleep(0.001 * (randBelow(20)));
@@ -104,10 +108,11 @@ receiver(void *arg)
}
for (sender = 1 ; sender <= 4 ; sender++) {
if (expectmsg[sender-1] > 1)
testDiag("Sender %d -- %d messages", sender, expectmsg[sender-1]-1);
testDiag("Received %d messages from Sender %d",
expectmsg[sender-1]-1, sender);
}
testOk1(errors == 0);
testDiag("Receiver finished");
testDiag("%s exiting", myName);
epicsEventSignal(finished);
}
@@ -119,17 +124,18 @@ sender(void *arg)
int len;
int i = 0;
while (!testExit) {
while (!sendExit) {
len = sprintf(cbuf, "%s -- %d.", epicsThreadGetNameSelf(), ++i);
while (q->trySend((void *)cbuf, len) < 0)
epicsThreadSleep(0.005 * (randBelow(5)));
epicsThreadSleep(0.005 * (randBelow(20)));
}
testDiag("%s exiting, sent %d messages", epicsThreadGetNameSelf(), i-1);
testDiag("%s exiting, sent %d messages", epicsThreadGetNameSelf(), i);
}
extern "C" void messageQueueTest(void *parm)
{
epicsThreadId myThreadId = epicsThreadGetIdSelf();
unsigned int i;
char cbuf[80];
int len;
@@ -153,20 +159,23 @@ extern "C" void messageQueueTest(void *parm)
len = q1->receive(cbuf, sizeof cbuf);
testOk1(q1->pending() == 3);
if (!testOk1((len == want) && (strncmp(msg1, cbuf, len) == 0)))
testDiag("wanted:%d '%.*s' got:%d '%.*s'", want, want, msg1, len, len, cbuf);
testDiag("wanted:%d '%.*s' got:%d '%.*s'",
want, want, msg1, len, len, cbuf);
want++;
len = q1->receive(cbuf, sizeof cbuf);
testOk1(q1->pending() == 2);
if (!testOk1((len == want) && (strncmp(msg1, cbuf, len) == 0)))
testDiag("wanted:%d '%.*s' got:%d '%.*s'", want, want, msg1, len, len, cbuf);
testDiag("wanted:%d '%.*s' got:%d '%.*s'",
want, want, msg1, len, len, cbuf);
q1->trySend((void *)msg1, i++);
want++;
len = q1->receive(cbuf, sizeof cbuf);
testOk1(q1->pending() == 2);
if (!testOk1((len == want) && (strncmp(msg1, cbuf, len) == 0)))
testDiag("wanted:%d '%.*s' got:%d '%.*s'", want, want, msg1, len, len, cbuf);
testDiag("wanted:%d '%.*s' got:%d '%.*s'",
want, want, msg1, len, len, cbuf);
q1->trySend((void *)msg1, i++);
testOk1(q1->pending() == 3);
@@ -176,7 +185,8 @@ extern "C" void messageQueueTest(void *parm)
testOk(q1->pending() == i, "q1->pending() == %d", i);
want++;
if (!testOk1((len == want) & (strncmp(msg1, cbuf, len) == 0)))
testDiag("wanted:%d '%.*s' got:%d '%.*s'", want, want, msg1, len, len, cbuf);
testDiag("wanted:%d '%.*s' got:%d '%.*s'",
want, want, msg1, len, len, cbuf);
}
testOk1(q1->pending() == 0);
@@ -194,20 +204,23 @@ extern "C" void messageQueueTest(void *parm)
len = q1->receive(cbuf, sizeof cbuf);
testOk1(q1->pending() == 3);
if (!testOk1((len == want) && (strncmp(msg1, cbuf, len) == 0)))
testDiag("wanted:%d '%.*s' got:%d '%.*s'", want, want, msg1, len, len, cbuf);
testDiag("wanted:%d '%.*s' got:%d '%.*s'",
want, want, msg1, len, len, cbuf);
want++;
len = q1->receive(cbuf, sizeof cbuf);
testOk1(q1->pending() == 2);
if (!testOk1((len == want) && (strncmp(msg1, cbuf, len) == 0)))
testDiag("wanted:%d '%.*s' got:%d '%.*s'", want, want, msg1, len, len, cbuf);
testDiag("wanted:%d '%.*s' got:%d '%.*s'",
want, want, msg1, len, len, cbuf);
q1->send((void *)msg1, i++, 1.0);
want++;
len = q1->receive(cbuf, sizeof cbuf);
testOk1(q1->pending() == 2);
if (!testOk1((len == want) && (strncmp(msg1, cbuf, len) == 0)))
testDiag("wanted:%d '%.*s' got:%d '%.*s'", want, want, msg1, len, len, cbuf);
testDiag("wanted:%d '%.*s' got:%d '%.*s'",
want, want, msg1, len, len, cbuf);
q1->send((void *)msg1, i++, 1.0);
testOk1(q1->pending() == 3);
@@ -217,7 +230,8 @@ extern "C" void messageQueueTest(void *parm)
testOk(q1->pending() == i, "q1->pending() == %d", i);
want++;
if (!testOk1((len == want) && (strncmp(msg1, cbuf, len) == 0)))
testDiag("wanted:%d '%.*s' got:%d '%.*s'", want, want, msg1, len, len, cbuf);
testDiag("wanted:%d '%.*s' got:%d '%.*s'",
want, want, msg1, len, len, cbuf);
}
testOk1(q1->pending() == 0);
@@ -233,7 +247,8 @@ extern "C" void messageQueueTest(void *parm)
testOk1(q1->pending() == 0);
testDiag("Single receiver with invalid size, single sender tests:");
epicsThreadCreate("Bad Receiver", epicsThreadPriorityMedium, epicsThreadGetStackSize(epicsThreadStackMedium), badReceiver, q1);
epicsThreadCreate("Bad Receiver", epicsThreadPriorityMedium,
mediumStack, badReceiver, q1);
epicsThreadSleep(1.0);
testOk(q1->send((void *)msg1, 10) == 0, "Send with waiting receiver");
epicsThreadSleep(2.0);
@@ -241,8 +256,9 @@ extern "C" void messageQueueTest(void *parm)
epicsThreadSleep(2.0);
testDiag("Single receiver, single sender tests:");
epicsThreadSetPriority(epicsThreadGetIdSelf(), epicsThreadPriorityHigh);
epicsThreadCreate("Receiver one", epicsThreadPriorityMedium, epicsThreadGetStackSize(epicsThreadStackMedium), receiver, q1);
epicsThreadSetPriority(myThreadId, epicsThreadPriorityHigh);
epicsThreadCreate("Receiver one", epicsThreadPriorityMedium,
mediumStack, receiver, q1);
for (pass = 1 ; pass <= 3 ; pass++) {
for (i = 0 ; i < 10 ; i++) {
if (q1->trySend((void *)msg1, i) < 0)
@@ -254,7 +270,7 @@ extern "C" void messageQueueTest(void *parm)
case 1:
if (i<6)
testDiag(" priority-based scheduler, sent %d messages", i);
epicsThreadSetPriority(epicsThreadGetIdSelf(), epicsThreadPriorityLow);
epicsThreadSetPriority(myThreadId, epicsThreadPriorityLow);
break;
case 2:
if (i<10)
@@ -274,27 +290,42 @@ extern "C" void messageQueueTest(void *parm)
*/
testDiag("Single receiver, multiple sender tests:");
testDiag("This test lasts 60 seconds...");
epicsThreadCreate("Sender 1", epicsThreadPriorityLow, epicsThreadGetStackSize(epicsThreadStackMedium), sender, q1);
epicsThreadCreate("Sender 2", epicsThreadPriorityMedium, epicsThreadGetStackSize(epicsThreadStackMedium), sender, q1);
epicsThreadCreate("Sender 3", epicsThreadPriorityHigh, epicsThreadGetStackSize(epicsThreadStackMedium), sender, q1);
epicsThreadCreate("Sender 4", epicsThreadPriorityHigh, epicsThreadGetStackSize(epicsThreadStackMedium), sender, q1);
testOk(!!epicsThreadCreate("Sender 1", epicsThreadPriorityLow,
mediumStack, sender, q1),
"Created Sender 1");
testOk(!!epicsThreadCreate("Sender 2", epicsThreadPriorityMedium,
mediumStack, sender, q1),
"Created Sender 2");
testOk(!!epicsThreadCreate("Sender 3", epicsThreadPriorityHigh,
mediumStack, sender, q1),
"Created Sender 3");
testOk(!!epicsThreadCreate("Sender 4", epicsThreadPriorityHigh,
mediumStack, sender, q1),
"Created Sender 4");
epicsThreadSleep(60.0);
for (i = 0; i < 10; i++) {
testDiag("... %2d", 10 - i);
epicsThreadSleep(6.0);
}
testExit = 1;
sendExit = 1;
epicsThreadSleep(1.0);
recvExit = 1;
testDiag("Scheduler exiting");
}
MAIN(epicsMessageQueueTest)
{
testPlan(58);
testPlan(62);
finished = epicsEventMustCreate(epicsEventEmpty);
mediumStack = epicsThreadGetStackSize(epicsThreadStackMedium);
epicsThreadCreate("messageQueueTest", epicsThreadPriorityMedium,
epicsThreadGetStackSize(epicsThreadStackMedium),
messageQueueTest, NULL);
mediumStack, messageQueueTest, NULL);
epicsEventWait(finished);
epicsEventMustWait(finished);
testDiag("Main thread signalled");
epicsThreadSleep(1.0);
return testDone();

View File

@@ -176,8 +176,8 @@ recordtype(ai) {
pp(TRUE)
interest(2)
}
field(ROFF,DBF_LONG) {
prompt("Raw Offset, obsolete")
field(ROFF,DBF_ULONG) {
prompt("Raw Offset")
pp(TRUE)
interest(2)
}

View File

@@ -469,10 +469,20 @@ static void convert(aoRecord *prec, double value)
}
value -= prec->aoff;
if (prec->aslo != 0) value /= prec->aslo;
if (value >= 0.0)
prec->rval = (epicsInt32)(value + 0.5) - prec->roff;
else
prec->rval = (epicsInt32)(value - 0.5) - prec->roff;
/* Apply raw offset and limits, round to 32-bit integer */
value -= prec->roff;
if (value >= 0.0) {
if (value >= (0x7fffffff - 0.5))
prec->rval = 0x7fffffff;
else
prec->rval = (epicsInt32)(value + 0.5);
} else {
if (value > (0.5 - 0x80000000))
prec->rval = (epicsInt32)(value - 0.5);
else
prec->rval = 0x80000000;
}
}

View File

@@ -82,8 +82,8 @@ recordtype(ao) {
interest(1)
size(16)
}
field(ROFF,DBF_LONG) {
prompt("Raw Offset, obsolete")
field(ROFF,DBF_ULONG) {
prompt("Raw Offset")
pp(TRUE)
interest(2)
}

View File

@@ -10,7 +10,7 @@
# The makeTestfile.pl script generates a file $target.t which is needed
# because some versions of the Perl test harness can only run test scripts
# that are actually written in Perl. The script we generate execs the
# that are actually written in Perl. The script we generate runs the
# real test program which must be in the same directory as the .t file.
# If the script is given an argument -tap it sets HARNESS_ACTIVE in the
# environment to make the epicsUnitTest code generate strict TAP output.
@@ -23,12 +23,16 @@ use strict;
my ($target, $exe) = @ARGV;
# Use system on Windows, exec doesn't work the same there and
# GNUmake thinks the test has finished as soon as Perl exits.
my $exec = $^O eq 'MSWin32' ? "system('./$exe') == 0" : "exec './$exe'";
open(my $OUT, '>', $target) or die "Can't create $target: $!\n";
print $OUT <<EOF;
#!/usr/bin/perl
\$ENV{HARNESS_ACTIVE} = 1 if scalar \@ARGV && shift eq '-tap';
exec './$exe' or die 'exec failed';
$exec or die "Can't run $exe: \$!\\n";
EOF
close $OUT or die "Can't close $target: $!\n";