diff --git a/.travis.yml b/.travis.yml index d6130c753..e7b1ed57a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -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 diff --git a/configure/CONFIG_BASE b/configure/CONFIG_BASE index 963b83a32..0b857c22f 100644 --- a/configure/CONFIG_BASE +++ b/configure/CONFIG_BASE @@ -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) diff --git a/configure/CONFIG_COMMON b/configure/CONFIG_COMMON index d3a66968f..6f9a0733b 100644 --- a/configure/CONFIG_COMMON +++ b/configure/CONFIG_COMMON @@ -38,6 +38,8 @@ BUILD_ARCHS = $(EPICS_HOST_ARCH) $(CROSS1) $(CROSS2) # otherwise override this in os/CONFIG_SITE..Common PERL = perl -CSD +PYTHON = python + #------------------------------------------------------- # Check configure/RELEASE file for consistency CHECK_RELEASE_YES = checkRelease diff --git a/configure/CONFIG_SITE b/configure/CONFIG_SITE index b657f5b65..c46703f84 100644 --- a/configure/CONFIG_SITE +++ b/configure/CONFIG_SITE @@ -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 diff --git a/configure/RULES_BUILD b/configure/RULES_BUILD index 4f71838fa..84902c39e 100644 --- a/configure/RULES_BUILD +++ b/configure/RULES_BUILD @@ -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 !!!! diff --git a/configure/os/CONFIG.Common.linuxCommon b/configure/os/CONFIG.Common.linuxCommon index 5405be723..24b1a0c33 100644 --- a/configure/os/CONFIG.Common.linuxCommon +++ b/configure/os/CONFIG.Common.linuxCommon @@ -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)) diff --git a/modules/database/src/std/softIoc/softMain.cpp b/modules/database/src/std/softIoc/softMain.cpp index 8400a6554..bc945c80e 100644 --- a/modules/database/src/std/softIoc/softMain.cpp +++ b/modules/database/src/std/softIoc/softMain.cpp @@ -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 -#include -#include -#include -#include +#include +#include +#include +#include +#include #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: "< 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 Access Security configuration file. Macro substitution is\n" + " performed.\n" + "\n" + " -m =,... Set/replace macro definitions used by subsequent -d and\n" + " -a.\n" + "\n" + " -d Load records from file (dbLoadRecords). Macro substitution is\n" + " performed.\n" + "\n" + " -x Load softIocExit.db. Provides a record \":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); - 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: -"<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; } diff --git a/modules/libcom/src/misc/unixFileName.h b/modules/libcom/src/misc/unixFileName.h index 36e818c8f..9d7af252c 100644 --- a/modules/libcom/src/misc/unixFileName.h +++ b/modules/libcom/src/misc/unixFileName.h @@ -14,7 +14,29 @@ #ifndef unixFileNameH #define unixFileNameH +#include + +#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 */ diff --git a/modules/libcom/src/osi/Makefile b/modules/libcom/src/osi/Makefile index ecbf4c23b..0352e9ffe 100644 --- a/modules/libcom/src/osi/Makefile +++ b/modules/libcom/src/osi/Makefile @@ -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 diff --git a/modules/libcom/src/osi/os/Darwin/osdgetexec.c b/modules/libcom/src/osi/os/Darwin/osdgetexec.c new file mode 100644 index 000000000..4e4961c59 --- /dev/null +++ b/modules/libcom/src/osi/os/Darwin/osdgetexec.c @@ -0,0 +1,50 @@ + +#include +#include + +#include + +#define epicsExportSharedSymbols +#include + +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; +} diff --git a/modules/libcom/src/osi/os/Linux/osdgetexec.c b/modules/libcom/src/osi/os/Linux/osdgetexec.c new file mode 100644 index 000000000..cba5d78a8 --- /dev/null +++ b/modules/libcom/src/osi/os/Linux/osdgetexec.c @@ -0,0 +1,54 @@ + +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include + +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; +} diff --git a/modules/libcom/src/osi/os/WIN32/osdgetexec.c b/modules/libcom/src/osi/os/WIN32/osdgetexec.c new file mode 100644 index 000000000..a46ce50cd --- /dev/null +++ b/modules/libcom/src/osi/os/WIN32/osdgetexec.c @@ -0,0 +1,52 @@ + +#include +#include +#include + +#define epicsExportSharedSymbols +#include + +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; +} diff --git a/modules/libcom/src/osi/os/WIN32/osiFileName.h b/modules/libcom/src/osi/os/WIN32/osiFileName.h index 6ff0308b2..ced745f71 100644 --- a/modules/libcom/src/osi/os/WIN32/osiFileName.h +++ b/modules/libcom/src/osi/os/WIN32/osiFileName.h @@ -15,7 +15,29 @@ #ifndef osiFileNameH #define osiFileNameH +#include + +#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 */ diff --git a/modules/libcom/src/osi/os/cygwin32/osiFileName.h b/modules/libcom/src/osi/os/cygwin32/osiFileName.h index 6d7fd6eb9..1e7799098 100644 --- a/modules/libcom/src/osi/os/cygwin32/osiFileName.h +++ b/modules/libcom/src/osi/os/cygwin32/osiFileName.h @@ -14,7 +14,29 @@ #ifndef osiFileNameH #define osiFileNameH +#include + +#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 */ diff --git a/modules/libcom/src/osi/os/default/osdgetexec.c b/modules/libcom/src/osi/os/default/osdgetexec.c new file mode 100644 index 000000000..0bec9ead9 --- /dev/null +++ b/modules/libcom/src/osi/os/default/osdgetexec.c @@ -0,0 +1,14 @@ +#include + +#define epicsExportSharedSymbols +#include + +char *epicsGetExecName(void) +{ + return NULL; +} + +char *epicsGetExecDir(void) +{ + return NULL; +} diff --git a/modules/libcom/src/osi/os/freebsd/osdgetexec.c b/modules/libcom/src/osi/os/freebsd/osdgetexec.c new file mode 100644 index 000000000..39c0a163d --- /dev/null +++ b/modules/libcom/src/osi/os/freebsd/osdgetexec.c @@ -0,0 +1,68 @@ + +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include + +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; +} diff --git a/modules/libcom/src/osi/os/solaris/osdgetexec.c b/modules/libcom/src/osi/os/solaris/osdgetexec.c new file mode 100644 index 000000000..ff9739ca9 --- /dev/null +++ b/modules/libcom/src/osi/os/solaris/osdgetexec.c @@ -0,0 +1,30 @@ + +#include +#include + +#define epicsExportSharedSymbols +#include + +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; +} diff --git a/modules/libcom/test/Makefile b/modules/libcom/test/Makefile index e90c6bc90..7fde7313e 100755 --- a/modules/libcom/test/Makefile +++ b/modules/libcom/test/Makefile @@ -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 diff --git a/modules/libcom/test/testexecname.c b/modules/libcom/test/testexecname.c new file mode 100644 index 000000000..87be847a0 --- /dev/null +++ b/modules/libcom/test/testexecname.c @@ -0,0 +1,24 @@ + +#include + +#include +#include + +#include + +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(); +} diff --git a/src/tools/Makefile b/src/tools/Makefile index 0df179781..0d0c011f5 100644 --- a/src/tools/Makefile +++ b/src/tools/Makefile @@ -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 diff --git a/src/tools/makeRPath.py b/src/tools/makeRPath.py new file mode 100644 index 000000000..000b8b450 --- /dev/null +++ b/src/tools/makeRPath.py @@ -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))