Merge remote-tracking branch 'lp-make/rpath-origin' into 7.0

* lp-make/rpath-origin:
  makeRPath allow multiple root directories
  redo softIoc to be more c++y
  rpath $ORIGIN doc
  older binutils compat
  travis-ci test rpath $ORIGIN
  epicsGetExecName WIN32, Darwin, solaris, freebsd
  epicsGetExecDir() paths relative to executable
  LINKER_USE_RPATH=ORIGIN

# Conflicts:
#	configure/os/CONFIG.Common.linuxCommon
This commit is contained in:
Michael Davidsaver
2019-10-06 20:05:47 -07:00
21 changed files with 673 additions and 194 deletions

View File

@ -15,7 +15,7 @@ addons:
script:
- .ci/travis-build.sh
env:
- CMPLR=gcc
- CMPLR=gcc EXTRA=LINKER_USE_RPATH=ORIGIN
- CMPLR=clang
- CMPLR=gcc STATIC=YES
- CMPLR=clang STATIC=YES

View File

@ -45,6 +45,8 @@ FULLPATHNAME = $(PERL) $(TOOLS)/fullPathName.pl
TAPTOJUNIT = $(PERL) $(TOOLS)/tap-to-junit-xml.pl
GENVERSIONHEADER = $(PERL) $(TOOLS)/genVersionHeader.pl $(QUIET_FLAG) $(QUESTION_FLAG)
MAKERPATH = $(PYTHON) $(TOOLS)/makeRPath.py
#---------------------------------------------------------------
# tools for installing libraries and products
INSTALL = $(PERL) $(TOOLS)/installEpics.pl $(QUIET_FLAG)

View File

@ -38,6 +38,8 @@ BUILD_ARCHS = $(EPICS_HOST_ARCH) $(CROSS1) $(CROSS2)
# otherwise override this in os/CONFIG_SITE.<host_arch>.Common
PERL = perl -CSD
PYTHON = python
#-------------------------------------------------------
# Check configure/RELEASE file for consistency
CHECK_RELEASE_YES = checkRelease

View File

@ -169,10 +169,18 @@ EPICS_SITE_VERSION =
GCC_PIPE = NO
# Set RPATH when linking executables and libraries.
# Must be either YES or NO. If you set this to NO you must also provide a
# Must be either YES, NO, or ORIGIN. If you set this to NO you must also provide a
# way for Base executables to find their shared libraries when they are
# run at build-time, e.g. set the LD_LIBRARY_PATH environment variable.
# ORIGIN is a feature of the ELF executable format used by Linux, freebsd, and solaris.
LINKER_USE_RPATH = YES
# Only used when LINKER_USE_RPATH=ORIGIN
# The build time root(s) of the relocatable tree (separate multiple w/ ':').
# Linking to libraries under any root directory will be relative.
# Linking to libraries outside of this root will be absolute.
# All root directories are considered to be the same.
LINKER_ORIGIN_ROOT = $(INSTALL_LOCATION)
# Overrides for the settings above may appear in a CONFIG_SITE.local file
-include $(CONFIG)/CONFIG_SITE.local

View File

@ -196,6 +196,13 @@ ifeq ($(EPICS_HOST_ARCH),$(T_A))
$(info Warning: RELEASE file consistency checks have been disabled)
endif
# $(FINAL_DIR) signals eventual install locations to makeRPath script
$(TESTPRODNAME): FINAL_DIR=.
$(PRODNAME): FINAL_DIR=$(INSTALL_BIN)
$(TESTSHRLIBNAME): FINAL_DIR=.
$(SHRLIBNAME): FINAL_DIR=$(INSTALL_SHRLIB)
$(LOADABLE_SHRLIBNAME): FINAL_DIR=$(INSTALL_SHRLIB)
#---------------------------------------------------------------
# The order of the following rules is
# VERY IMPORTANT !!!!

View File

@ -25,11 +25,13 @@ STATIC_LDLIBS_YES= -Wl,-Bdynamic
# Set runtime path for shared libraries if LINKER_USE_RPATH=YES
SHRLIBDIR_RPATH_LDFLAGS_YES = $(SHRLIB_DEPLIB_DIRS:%=-Wl,-rpath,%)
SHRLIBDIR_RPATH_LDFLAGS_ORIGIN = $(shell $(MAKERPATH) -O '\$$ORIGIN' -F $(FINAL_DIR) -R $(LINKER_ORIGIN_ROOT) $(SHRLIB_DEPLIB_DIRS))
SHRLIBDIR_LDFLAGS += \
$(SHRLIBDIR_RPATH_LDFLAGS_$(LINKER_USE_RPATH))
# Set runtime path for products if LINKER_USE_RPATH=YES
PRODDIR_RPATH_LDFLAGS_YES = $(PROD_DEPLIB_DIRS:%=-Wl,-rpath,%)
PRODDIR_RPATH_LDFLAGS_ORIGIN = $(shell $(MAKERPATH) -O '\$$ORIGIN' -F $(FINAL_DIR) -R $(LINKER_ORIGIN_ROOT) $(PROD_DEPLIB_DIRS))
PRODDIR_LDFLAGS += \
$(PRODDIR_RPATH_LDFLAGS_$(LINKER_USE_RPATH))

View File

@ -9,225 +9,237 @@
/* Author: Andrew Johnson Date: 2003-04-08 */
/* Usage:
* softIoc [-D softIoc.dbd] [-h] [-S] [-s] [-a ascf]
* [-m macro=value,macro2=value2] [-d file.db]
* [-x prefix] [st.cmd]
*
* If used the -D option must come first, and specify the
* path to the softIoc.dbd file. The compile-time install
* location is saved in the binary as a default.
*
* Usage information will be printed if -h is given, then
* the program will exit normally.
*
* The -S option prevents an interactive shell being started
* after all arguments have been processed.
*
* Previous versions accepted a -s option to cause a shell
* to be started; this option is still accepted but ignored
* since a command shell is now started by default.
*
* Access Security can be enabled with the -a option giving
* the name of the configuration file; if any macros were
* set with -m before the -a option was given, they will be
* used as access security substitution macros.
*
* Any number of -m and -d arguments can be interspersed;
* the macros are applied to the following .db files. Each
* later -m option causes earlier macros to be discarded.
*
* The -x option loads the softIocExit.db with the macro
* IOC set to the string provided. This database contains
* a subroutine record named $(IOC):exit which has its field
* SNAM set to "exit". When this record is processed, the
* subroutine that runs will call epicsExit() with the value
* of the field A determining whether the exit status is
* EXIT_SUCCESS if (A == 0.0) or EXIT_FAILURE (A != 0.0).
*
* A st.cmd file is optional. If any databases were loaded
* the st.cmd file will be run *after* iocInit. To perform
* iocsh commands before iocInit, all database loading must
* be performed by the script itself, or by the user from
* the interactive IOC shell.
*/
#include <stddef.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#include <iostream>
#include <string>
#include <list>
#include <stdexcept>
#include <epicsGetopt.h>
#include "registryFunction.h"
#include "epicsThread.h"
#include "epicsExit.h"
#include "epicsStdio.h"
#include "epicsString.h"
#include "dbStaticLib.h"
#include "subRecord.h"
#include "dbAccess.h"
#include "asDbLib.h"
#include "iocInit.h"
#include "iocsh.h"
#include "osiFileName.h"
#include "epicsInstallDir.h"
extern "C" int softIoc_registerRecordDeviceDriver(struct dbBase *pdbbase);
#define DBD_FILE EPICS_BASE "/dbd/softIoc.dbd"
#define EXIT_FILE EPICS_BASE "/db/softIocExit.db"
#ifndef EPICS_BASE
// so IDEs knows EPICS_BASE is a string constant
# define EPICS_BASE "/"
# error -DEPICS_BASE required
#endif
const char *arg0;
const char *base_dbd = DBD_FILE;
const char *exit_db = EXIT_FILE;
#define DBD_BASE "dbd/softIoc.dbd"
#define EXIT_BASE "db/softIocExit.db"
#define DBD_FILE_REL "../../" DBD_BASE
#define EXIT_FILE_REL "../../" EXIT_BASE
#define DBD_FILE EPICS_BASE "/" DBD_BASE
#define EXIT_FILE EPICS_BASE "/" EXIT_BASE
namespace {
static void exitSubroutine(subRecord *precord) {
epicsExitLater((precord->a == 0.0) ? EXIT_SUCCESS : EXIT_FAILURE);
}
static void usage(int status) {
printf("Usage: %s [-D softIoc.dbd] [-h] [-S] [-a ascf]\n", arg0);
puts("\t[-m macro=value,macro2=value2] [-d file.db]");
puts("\t[-x prefix] [st.cmd]");
puts("Compiled-in path to softIoc.dbd is:");
printf("\t%s\n", base_dbd);
epicsExit(status);
void usage(const char *arg0, const std::string& base_dbd) {
std::cout<<"Usage: "<<arg0<<
" [-D softIoc.dbd] [-h] [-S] [-s] [-a ascf]\n"
"[-m macro=value,macro2=value2] [-d file.db]\n"
"[-x prefix] [st.cmd]\n"
"\n"
" -D <dbd> If used, must come first. Specify the path to the softIoc.dbdfile."
" The compile-time install location is saved in the binary as a default.\n"
"\n"
" -h Print this mesage and exit.\n"
"\n"
" -S Prevents an interactive shell being started.\n"
"\n"
" -s Previously caused a shell to be started. Now accepted and ignored.\n"
"\n"
" -a <acf> Access Security configuration file. Macro substitution is\n"
" performed.\n"
"\n"
" -m <MAC>=<value>,... Set/replace macro definitions used by subsequent -d and\n"
" -a.\n"
"\n"
" -d <db> Load records from file (dbLoadRecords). Macro substitution is\n"
" performed.\n"
"\n"
" -x <prefix> Load softIocExit.db. Provides a record \"<prefix>:exit\".\n"
" Put 0 to exit with success, or non-zero to exit with an error.\n"
"\n"
"Any number of -m and -d arguments can be interspersed; the macros are applied\n"
"to the following .db files. Each later -m option causes earlier macros to be\n"
"discarded.\n"
"\n"
"A st.cmd file is optional. If any databases were loaded the st.cmd file will\n"
"be run *after* iocInit. To perform iocsh commands before iocInit, all database\n"
"loading must be performed by the script itself, or by the user from the\n"
"interactive IOC shell.\n"
"\n"
"Compiled-in path to softIoc.dbd is:\n"
"\t"<<base_dbd.c_str()<<"\n";
}
void errIf(int ret, const std::string& msg)
{
if(ret)
throw std::runtime_error(msg);
}
void lazy_dbd(const std::string& dbd_file) {
static bool loaded;
if(loaded) return;
loaded = true;
errIf(dbLoadDatabase(dbd_file.c_str(), NULL, NULL),
std::string("Failed to load DBD file: ")+dbd_file);
std::cout<<"dbLoadDatabase(\""<<dbd_file<<"\")\n";
softIoc_registerRecordDeviceDriver(pdbbase);
std::cout<<"softIoc_registerRecordDeviceDriver(pdbbase)\n";
registryFunctionAdd("exit", (REGISTRYFUNCTION) exitSubroutine);
}
} // namespace
int main(int argc, char *argv[])
{
char *dbd_file = const_cast<char*>(base_dbd);
char *macros = NULL;
char xmacro[PVNAME_STRINGSZ + 4];
int startIocsh = 1; /* default = start shell */
int loadedDb = 0;
arg0 = strrchr(*argv, '/');
if (!arg0) {
arg0 = *argv;
} else {
++arg0; /* skip the '/' */
}
--argc, ++argv;
/* Do this here in case the dbd file not available */
if (argc>0 && **argv=='-' && (*argv)[1]=='h') {
usage(EXIT_SUCCESS);
}
if (argc>1 && **argv=='-' && (*argv)[1]=='D') {
dbd_file = *++argv;
argc -= 2;
++argv;
}
if (dbLoadDatabase(dbd_file, NULL, NULL)) {
epicsExit(EXIT_FAILURE);
}
softIoc_registerRecordDeviceDriver(pdbbase);
registryFunctionAdd("exit", (REGISTRYFUNCTION) exitSubroutine);
try {
std::string dbd_file(DBD_FILE),
exit_file(EXIT_FILE),
macros, // scratch space for macros (may be given more than once)
xmacro;
bool interactive = true;
bool loadedDb = false;
while (argc>1 && **argv == '-') {
switch ((*argv)[1]) {
case 'a':
if (macros) asSetSubstitutions(macros);
asSetFilename(*++argv);
--argc;
break;
case 'd':
if (dbLoadRecords(*++argv, macros)) {
epicsExit(EXIT_FAILURE);
}
loadedDb = 1;
--argc;
break;
case 'h':
usage(EXIT_SUCCESS);
case 'm':
macros = *++argv;
--argc;
break;
case 'S':
startIocsh = 0;
break;
case 's':
break;
case 'x':
epicsSnprintf(xmacro, sizeof xmacro, "IOC=%s", *++argv);
if (dbLoadRecords(exit_db, xmacro)) {
epicsExit(EXIT_FAILURE);
}
loadedDb = 1;
--argc;
break;
default:
printf("%s: option '%s' not recognized\n", arg0, *argv);
usage(EXIT_FAILURE);
}
--argc;
++argv;
// attempt to compute relative paths
{
std::string prefix;
char *cprefix = epicsGetExecDir();
if(cprefix) {
try {
prefix = cprefix;
free(cprefix);
} catch(...) {
free(cprefix);
throw;
}
}
dbd_file = prefix + DBD_FILE_REL;
exit_file = prefix + EXIT_FILE_REL;
}
int opt;
while ((opt = getopt(argc, argv, "ha:d:m:Ssx:")) != -1) {
switch (opt) {
case 'h': /* Print usage */
usage(argv[0], dbd_file);
epicsExit(0);
return 0;
default:
usage(argv[0], dbd_file);
std::cerr<<"Unknown argument: -"<<char(opt)<<"\n";
epicsExit(2);
return 2;
case 'a':
lazy_dbd(dbd_file);
if (!macros.empty()) {
if(asSetSubstitutions(macros.c_str()))
throw std::bad_alloc();
std::cout<<"asSetSubstitutions(\""<<macros<<"\")\n";
}
if(asSetFilename(optarg))
throw std::bad_alloc();
std::cout<<"asSetFilename(\""<<optarg<<"\")\n";
break;
case 'd':
lazy_dbd(dbd_file);
errIf(dbLoadRecords(optarg, macros.c_str()),
std::string("Failed to load: ")+optarg);
std::cout<<"dbLoadRecords(\""<<optarg<<"\"";
if(!macros.empty())
std::cout<<", \""<<macros<<"\"";
std::cout<<")\n";
loadedDb = true;
break;
case 'm':
macros = optarg;
break;
case 'S':
interactive = false;
break;
case 's':
break; // historical
case 'x':
lazy_dbd(dbd_file);
xmacro = "IOC=";
xmacro += optarg;
errIf(dbLoadRecords(exit_file.c_str(), xmacro.c_str()),
std::string("Failed to load: ")+exit_file);
loadedDb = true;
break;
}
}
lazy_dbd(dbd_file);
if(optind<argc) {
// run script
// ignore any extra positional args (historical)
std::cout<<"# Begin "<<argv[optind]<<"\n";
errIf(iocsh(argv[optind]),
std::string("Error in ")+argv[optind]);
std::cout<<"# End "<<argv[optind]<<"\n";
epicsThreadSleep(0.2);
loadedDb = true; /* Give it the benefit of the doubt... */
}
if (loadedDb) {
std::cout<<"iocInit()\n";
iocInit();
epicsThreadSleep(0.2);
}
if(interactive) {
std::cout.flush();
std::cerr.flush();
if(iocsh(NULL)) {
epicsExit(1);
return 1;
}
} else {
if (loadedDb) {
epicsThreadExitMain();
} else {
usage(argv[0], dbd_file);
std::cerr<<"Nothing to do!\n";
epicsExit(1);
return 1;
}
}
epicsExit(0);
return 0;
}catch(std::exception& e){
std::cerr<<"Error: "<<e.what()<<"\n";
epicsExit(2);
return 2;
}
if (argc>0 && **argv=='-') {
switch((*argv)[1]) {
case 'a':
case 'd':
case 'm':
case 'x':
printf("%s: missing argument to option '%s'\n", arg0, *argv);
usage(EXIT_FAILURE);
case 'h':
usage(EXIT_SUCCESS);
case 'S':
startIocsh = 0;
break;
case 's':
break;
default:
printf("%s: option '%s' not recognized\n", arg0, *argv);
usage(EXIT_FAILURE);
}
--argc;
++argv;
}
if (loadedDb) {
iocInit();
epicsThreadSleep(0.2);
}
/* run user's startup script */
if (argc>0) {
if (iocsh(*argv)) epicsExit(EXIT_FAILURE);
epicsThreadSleep(0.2);
loadedDb = 1; /* Give it the benefit of the doubt... */
}
/* start an interactive shell if it was requested */
if (startIocsh) {
iocsh(NULL);
} else {
if (loadedDb) {
epicsThreadExitMain();
} else {
printf("%s: Nothing to do!\n", arg0);
usage(EXIT_FAILURE);
}
}
epicsExit(EXIT_SUCCESS);
/*Note that the following statement will never be executed*/
return 0;
}

View File

@ -14,7 +14,29 @@
#ifndef unixFileNameH
#define unixFileNameH
#include <shareLib.h>
#ifdef __cplusplus
extern "C" {
#endif
#define OSI_PATH_LIST_SEPARATOR ":"
#define OSI_PATH_SEPARATOR "/"
/** Return the absolute path of the current executable.
@returns NULL or the path. Caller must free()
*/
epicsShareFunc
char *epicsGetExecName(void);
/** Return the absolute path of the directory containing the current executable.
@returns NULL or the path. Caller must free()
*/
epicsShareFunc
char *epicsGetExecDir(void);
#ifdef __cplusplus
}
#endif
#endif /* unixFileNameH */

View File

@ -123,6 +123,7 @@ Com_SRCS += osdMonotonic.c
Com_SRCS += osdProcess.c
Com_SRCS += osdNetIntf.c
Com_SRCS += osdMessageQueue.c
Com_SRCS += osdgetexec.c
Com_SRCS += devLibVME.c
Com_SRCS += devLibVMEOSD.c

View File

@ -0,0 +1,50 @@
#include <string.h>
#include <stdlib.h>
#include <mach-o/dyld.h>
#define epicsExportSharedSymbols
#include <osiFileName.h>
char *epicsGetExecName(void)
{
uint32_t max = 64u;
char *ret = NULL;
while(1) {
char *temp = realloc(ret, max);
if(!temp) {
/* we treat alloc failure as terminal */
free(ret);
ret = NULL;
break;
}
ret = temp;
/* cf. "man 3 dyld" */
if(_NSGetExecutablePath(ret, &max)==0) {
/* max left unchanged */
ret[max-1] = '\0';
break;
}
/* max has been updated with required size */
}
/* TODO: _NSGetExecutablePath() doesn't follow symlinks */
return ret;
}
char *epicsGetExecDir(void)
{
char *ret = epicsGetExecName();
if(ret) {
char *sep = strrchr(ret, '/');
if(sep) {
/* nil the charactor after the / */
sep[1] = '\0';
}
}
return ret;
}

View File

@ -0,0 +1,54 @@
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#define epicsExportSharedSymbols
#include <osiFileName.h>
char *epicsGetExecName(void)
{
size_t max = PATH_MAX;
char *ret = NULL;
ssize_t n;
while(1) {
char *temp = realloc(ret, max);
if(!temp) {
/* we treat alloc failure as terminal */
free(ret);
ret = NULL;
break;
}
ret = temp;
n = readlink("/proc/self/exe", ret, max);
if(n == -1) {
free(ret);
ret = NULL;
break;
} else if(n < max) {
/* readlink() never adds a nil */
ret[n] = '\0';
break;
}
max += 64;
}
return ret;
}
char *epicsGetExecDir(void)
{
char *ret = epicsGetExecName();
if(ret) {
char *sep = strrchr(ret, '/');
if(sep) {
/* nil the charactor after the / */
sep[1] = '\0';
}
}
return ret;
}

View File

@ -0,0 +1,52 @@
#include <string.h>
#include <stdlib.h>
#include <windows.h>
#define epicsExportSharedSymbols
#include <osiFileName.h>
char *epicsGetExecName(void)
{
size_t max = 128;
char *ret = NULL;
DWORD n;
while(1) {
char *temp = realloc(ret, max);
if(!temp) {
/* we treat alloc failure as terminal */
free(ret);
ret = NULL;
break;
}
ret = temp;
n = GetModuleFileName(NULL, ret, max);
if(n == 0) {
free(ret);
ret = NULL;
break;
} else if(n < max) {
ret[n] = '\0';
break;
}
max += 64;
}
return ret;
}
char *epicsGetExecDir(void)
{
char *ret = epicsGetExecName();
if(ret) {
char *sep = strrchr(ret, '\\');
if(sep) {
/* nil the charactor after the / */
sep[1] = '\0';
}
}
return ret;
}

View File

@ -15,7 +15,29 @@
#ifndef osiFileNameH
#define osiFileNameH
#include <shareLib.h>
#ifdef __cplusplus
extern "C" {
#endif
#define OSI_PATH_LIST_SEPARATOR ";"
#define OSI_PATH_SEPARATOR "\\"
/** Return the absolute path of the current executable.
@returns NULL or the path. Caller must free()
*/
epicsShareFunc
char *epicsGetExecName(void);
/** Return the absolute path of the directory containing the current executable.
@returns NULL or the path. Caller must free()
*/
epicsShareFunc
char *epicsGetExecDir(void);
#ifdef __cplusplus
}
#endif
#endif /* osiFileNameH */

View File

@ -14,7 +14,29 @@
#ifndef osiFileNameH
#define osiFileNameH
#include <shareLib.h>
#ifdef __cplusplus
extern "C" {
#endif
#define OSI_PATH_LIST_SEPARATOR ";"
#define OSI_PATH_SEPARATOR "\\"
/** Return the absolute path of the current executable.
@returns NULL or the path. Caller must free()
*/
epicsShareFunc
char *epicsGetExecName(void);
/** Return the absolute path of the directory containing the current executable.
@returns NULL or the path. Caller must free()
*/
epicsShareFunc
char *epicsGetExecDir(void);
#ifdef __cplusplus
}
#endif
#endif /* osiFileNameH */

View File

@ -0,0 +1,14 @@
#include <stdlib.h>
#define epicsExportSharedSymbols
#include <osiFileName.h>
char *epicsGetExecName(void)
{
return NULL;
}
char *epicsGetExecDir(void)
{
return NULL;
}

View File

@ -0,0 +1,68 @@
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#define epicsExportSharedSymbols
#include <osiFileName.h>
char *epicsGetExecName(void)
{
size_t max = PATH_MAX;
char *ret = NULL;
ssize_t n;
while(1) {
char *temp = realloc(ret, max);
if(!temp) {
/* we treat alloc failure as terminal */
free(ret);
ret = NULL;
break;
}
ret = temp;
n = readlink("/proc/curproc/file", ret, max);
if(n == -1) {
free(ret);
ret = NULL;
break;
} else if(n < max) {
/* readlink() never adds a nil */
ret[n] = '\0';
break;
}
max += 64;
}
if(!ret) {
int mib[4];
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PATHNAME;
mib[3] = -1;
ret = malloc(max);
if(ret) {
sysctl(mib, 4, ret, &cb, NULL, 0);
/* TODO: error check */
}
}
return ret;
}
char *epicsGetExecDir(void)
{
char *ret = epicsGetExecName();
if(ret) {
char *sep = strrchr(ret, '/');
if(sep) {
/* nil the charactor after the / */
sep[1] = '\0';
}
}
return ret;
}

View File

@ -0,0 +1,30 @@
#include <string.h>
#include <stdlib.h>
#define epicsExportSharedSymbols
#include <osiFileName.h>
char *epicsGetExecName(void)
{
const char *raw = getexecname();
char *ret = NULL;
/* manpage says getexecname() might return a relative path. we treat this as an error */
if(raw[0]=='/') {
ret = strdup(raw);
}
return ret;
}
char *epicsGetExecDir(void)
{
char *ret = epicsGetExecName();
if(ret) {
char *sep = strrchr(ret, '/');
if(sep) {
/* nil the charactor after the / */
sep[1] = '\0';
}
}
return ret;
}

View File

@ -232,6 +232,11 @@ osiSockTest_SRCS += osiSockTest.c
testHarness_SRCS += osiSockTest.c
TESTS += osiSockTest
TESTPROD_HOST += testexecname
testexecname_SRCS += testexecname.c
# no point in including in testHarness. Not implemented for RTEMS/vxWorks.
TESTS += testexecname
ifeq ($(BUILD_CLASS),HOST)
ifneq ($(OS_CLASS),WIN32)
# This test can only be run on a build host, and is broken on Windows

View File

@ -0,0 +1,24 @@
#include <string.h>
#include <epicsUnitTest.h>
#include <testMain.h>
#include <osiFileName.h>
MAIN(testexecname)
{
testPlan(1);
{
char *buf = epicsGetExecName();
if(!buf) {
testSkip(1, "epicsGetExecName() not available for this target");
} else {
char *loc = strstr(buf, "testexecname");
testOk(!!loc, "Find \"testexecname\" in \"%s\"", buf);
}
}
return testDone();
}

View File

@ -39,6 +39,8 @@ PERL_SCRIPTS += tap-to-junit-xml.pl
PERL_SCRIPTS += useManifestTool.pl
PERL_SCRIPTS += genVersionHeader.pl
PERL_SCRIPTS += makeRPath.py
HTMLS = style.css
HTMLS += EPICS/Getopts.html
HTMLS += EPICS/Path.html

80
src/tools/makeRPath.py Normal file
View File

@ -0,0 +1,80 @@
#!/usr/bin/env python
from __future__ import print_function
import sys
import os
from collections import OrderedDict # used as OrderedSet
from argparse import ArgumentParser
if os.environ.get('EPICS_DEBUG_RPATH','')=='YES':
sys.stderr.write('%s'%sys.argv)
P = ArgumentParser(description='''Compute and output -rpath entries for each of the given paths.
Paths under --root will be computed as relative to --final .''',
epilog='''
eg. A library to be placed in /build/lib and linked against libraries in
'/build/lib', '/build/module/lib', and '/other/lib' would pass:
"makeRPath.py -F /build/lib -R /build /build/lib /build/module/lib /other/lib"
which prints "-Wl,-rpath,$ORIGIN/. -Wl,-rpath,$ORIGIN/../module/lib -Wl,-rpath,/other/lib"
''')
P.add_argument('-F','--final',default=os.getcwd(), help='Final install location for ELF file')
P.add_argument('-R','--root',default='', help='Root(s) of relocatable tree. Separate with :')
P.add_argument('-O', '--origin', default='$ORIGIN')
P.add_argument('path', nargs='*')
args = P.parse_args()
# eg.
# target to be installed as: /build/bin/blah
#
# post-install will copy as: /install/bin/blah
#
# Need to link against:
# /install/lib/libA.so
# /build/lib/libB.so
# /other/lib/libC.so
#
# Want final result to be:
# -rpath $ORIGIN/../lib -rpath /other/lib \
# -rpath-link /build/lib -rpath-link /install/lib
fdir = os.path.abspath(args.final)
roots = [os.path.abspath(root) for root in args.root.split(':') if len(root)]
# find the root which contains the final location
froot = None
for root in roots:
frel = os.path.relpath(fdir, root)
if not frel.startswith('..'):
# final dir is under this root
froot = root
break
if froot is None:
sys.stderr.write("makeRPath: Final location %s\nNot under any of: %s\n"%(fdir, roots))
sys.exit(1)
output = OrderedDict()
for path in args.path:
path = os.path.abspath(path)
for root in roots:
rrel = os.path.relpath(path, root)
if not rrel.startswith('..'):
# path is under this root
# some older binutils don't seem to handle $ORIGIN correctly
# when locating dependencies of libraries. So also provide
# the absolute path for internal use by 'ld' only.
output['-Wl,-rpath-link,'+path] = True
# frel is final location relative to enclosing root
# rrel is target location relative to enclosing root
path = os.path.relpath(rrel, frel)
break
output['-Wl,-rpath,'+os.path.join(args.origin, path)] = True
print(' '.join(output))