Merge ~epics-core/epics-base/+git/make:rpath-origin into ~epics-core/epics-base/+git/epics-base:7.0
- Git
- lp:~epics-core/epics-base/+git/make
- rpath-origin
- Merge into 7.0
Status: | Merged |
---|---|
Approved by: | Andrew Johnson |
Approved revision: | 784d619bdee4653f8412f664b7338fe42f67e047 |
Merged at revision: | 02bec52f0a966381a43149e685afb28b68fb2939 |
Proposed branch: | ~epics-core/epics-base/+git/make:rpath-origin |
Merge into: | ~epics-core/epics-base/+git/epics-base:7.0 |
Diff against target: |
1096 lines (+673/-194) 21 files modified
.travis.yml (+1/-1) configure/CONFIG_BASE (+2/-0) configure/CONFIG_COMMON (+2/-0) configure/CONFIG_SITE (+9/-1) configure/RULES_BUILD (+7/-0) configure/os/CONFIG.Common.linuxCommon (+2/-0) modules/database/src/std/softIoc/softMain.cpp (+204/-192) modules/libcom/src/misc/unixFileName.h (+22/-0) modules/libcom/src/osi/Makefile (+1/-0) modules/libcom/src/osi/os/Darwin/osdgetexec.c (+50/-0) modules/libcom/src/osi/os/Linux/osdgetexec.c (+54/-0) modules/libcom/src/osi/os/WIN32/osdgetexec.c (+52/-0) modules/libcom/src/osi/os/WIN32/osiFileName.h (+22/-0) modules/libcom/src/osi/os/cygwin32/osiFileName.h (+22/-0) modules/libcom/src/osi/os/default/osdgetexec.c (+14/-0) modules/libcom/src/osi/os/freebsd/osdgetexec.c (+68/-0) modules/libcom/src/osi/os/solaris/osdgetexec.c (+30/-0) modules/libcom/test/Makefile (+5/-0) modules/libcom/test/testexecname.c (+24/-0) src/tools/Makefile (+2/-0) src/tools/makeRPath.py (+80/-0) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Andrew Johnson | Approve | ||
Martin Konrad (community) | c++ part only | Approve | |
mdavidsaver | Approve | ||
Review via email: mp+359132@code.launchpad.net |
Commit message
Description of the change
Change to automate the creation of relative associations between shared libraries and executables (via. -rpath $ORIGIN) on Linux and other ELF targets. Also add epicsGetExecDir() to fetch the directory containing the executable which launched the running process.
These two combine to allow a complete built tree to be moved (relocated), or accessed from a different absolute path (eg. different NFS mount point).
eg.
cat <<EOF > configure/
LINKER_
EOF
make INSTALL_
mv usr1 usr2
./usr2/
This also works for downstream modules, even in cases like P4P where the install path is customized.
When working with a complex install tree, the new $(LINKER_
mdavidsaver (mdavidsaver) wrote : | # |
mdavidsaver (mdavidsaver) wrote : | # |
In the course of testing this I've come across two gcc/binutils bugs related to $ORIGIN. Both have been fixed, but will remain an issue for older toolchains.
Prior to binutils 2.28 -rpath $ORIGIN entries weren't correctly used to locate library dependencies. That is, when 'ld' needs to find a library on which an explicitly specified library depends.
Practically speaking this is a problem with the 'calc' module which has optional dependencies on sscan and seq. Downstream modules like streamdevice link against 'calc' without mentioning 'sscan' and 'pv'. This is apparently even stricter than is necessary for single pass static linking.
A partial workaround without side effects is to also include an absolute '-rpath-link' for every relative '-rpath'.
https:/
I've also seen that even older gcc (circa RHEL5) seem to expand '$' variables internally before passing to 'ld'. So preventing expansion with single quotes like -Wl,-rpath,
I haven't able to find a ticket on this one.
mdavidsaver (mdavidsaver) wrote : | # |
I've been testing this idea for the past ~3 months in the context of a mini distribution 'https:/
Andrew Johnson (anj) wrote : | # |
Core group: See Notes below. ANJ to test on Solaris, and rewrite makeRPath into Perl.
mdavidsaver (mdavidsaver) wrote : | # |
Updated. Now possible to give multiple values for LINKER_ORIGIN_ROOT. This allows for more complicated build environments where some libraries are in their final locations while some are in a staging area.
Andrew Johnson (anj) wrote : | # |
Still reviewing. You completely rewrote softMain.cpp since I last looked at this, and I do intend to convert makeRPath into Perl.
Andrew Johnson (anj) wrote : | # |
Videocon 9/17: Merge is acceptable in principle after a review from RL.
Martin Konrad (info-martin-konrad) wrote : | # |
I actually found time to review the C++ part. Looks straight forward to me. Thumbs up for printing the usage information that was hidden in a comment before.
Andrew Johnson (anj) wrote : | # |
Group 10/4: No issues.
Preview Diff
1 | diff --git a/.travis.yml b/.travis.yml |
2 | index d6130c7..e7b1ed5 100644 |
3 | --- a/.travis.yml |
4 | +++ b/.travis.yml |
5 | @@ -15,7 +15,7 @@ addons: |
6 | script: |
7 | - .ci/travis-build.sh |
8 | env: |
9 | - - CMPLR=gcc |
10 | + - CMPLR=gcc EXTRA=LINKER_USE_RPATH=ORIGIN |
11 | - CMPLR=clang |
12 | - CMPLR=gcc STATIC=YES |
13 | - CMPLR=clang STATIC=YES |
14 | diff --git a/configure/CONFIG_BASE b/configure/CONFIG_BASE |
15 | index 963b83a..0b857c2 100644 |
16 | --- a/configure/CONFIG_BASE |
17 | +++ b/configure/CONFIG_BASE |
18 | @@ -45,6 +45,8 @@ FULLPATHNAME = $(PERL) $(TOOLS)/fullPathName.pl |
19 | TAPTOJUNIT = $(PERL) $(TOOLS)/tap-to-junit-xml.pl |
20 | GENVERSIONHEADER = $(PERL) $(TOOLS)/genVersionHeader.pl $(QUIET_FLAG) $(QUESTION_FLAG) |
21 | |
22 | +MAKERPATH = $(PYTHON) $(TOOLS)/makeRPath.py |
23 | + |
24 | #--------------------------------------------------------------- |
25 | # tools for installing libraries and products |
26 | INSTALL = $(PERL) $(TOOLS)/installEpics.pl $(QUIET_FLAG) |
27 | diff --git a/configure/CONFIG_COMMON b/configure/CONFIG_COMMON |
28 | index d3a6696..6f9a073 100644 |
29 | --- a/configure/CONFIG_COMMON |
30 | +++ b/configure/CONFIG_COMMON |
31 | @@ -38,6 +38,8 @@ BUILD_ARCHS = $(EPICS_HOST_ARCH) $(CROSS1) $(CROSS2) |
32 | # otherwise override this in os/CONFIG_SITE.<host_arch>.Common |
33 | PERL = perl -CSD |
34 | |
35 | +PYTHON = python |
36 | + |
37 | #------------------------------------------------------- |
38 | # Check configure/RELEASE file for consistency |
39 | CHECK_RELEASE_YES = checkRelease |
40 | diff --git a/configure/CONFIG_SITE b/configure/CONFIG_SITE |
41 | index b657f5b..c46703f 100644 |
42 | --- a/configure/CONFIG_SITE |
43 | +++ b/configure/CONFIG_SITE |
44 | @@ -169,10 +169,18 @@ EPICS_SITE_VERSION = |
45 | GCC_PIPE = NO |
46 | |
47 | # Set RPATH when linking executables and libraries. |
48 | -# Must be either YES or NO. If you set this to NO you must also provide a |
49 | +# Must be either YES, NO, or ORIGIN. If you set this to NO you must also provide a |
50 | # way for Base executables to find their shared libraries when they are |
51 | # run at build-time, e.g. set the LD_LIBRARY_PATH environment variable. |
52 | +# ORIGIN is a feature of the ELF executable format used by Linux, freebsd, and solaris. |
53 | LINKER_USE_RPATH = YES |
54 | |
55 | +# Only used when LINKER_USE_RPATH=ORIGIN |
56 | +# The build time root(s) of the relocatable tree (separate multiple w/ ':'). |
57 | +# Linking to libraries under any root directory will be relative. |
58 | +# Linking to libraries outside of this root will be absolute. |
59 | +# All root directories are considered to be the same. |
60 | +LINKER_ORIGIN_ROOT = $(INSTALL_LOCATION) |
61 | + |
62 | # Overrides for the settings above may appear in a CONFIG_SITE.local file |
63 | -include $(CONFIG)/CONFIG_SITE.local |
64 | diff --git a/configure/RULES_BUILD b/configure/RULES_BUILD |
65 | index 2a78a96..3306fb3 100644 |
66 | --- a/configure/RULES_BUILD |
67 | +++ b/configure/RULES_BUILD |
68 | @@ -196,6 +196,13 @@ ifeq ($(EPICS_HOST_ARCH),$(T_A)) |
69 | $(info Warning: RELEASE file consistency checks have been disabled) |
70 | endif |
71 | |
72 | +# $(FINAL_DIR) signals eventual install locations to makeRPath script |
73 | +$(TESTPRODNAME): FINAL_DIR=. |
74 | +$(PRODNAME): FINAL_DIR=$(INSTALL_BIN) |
75 | +$(TESTSHRLIBNAME): FINAL_DIR=. |
76 | +$(SHRLIBNAME): FINAL_DIR=$(INSTALL_SHRLIB) |
77 | +$(LOADABLE_SHRLIBNAME): FINAL_DIR=$(INSTALL_SHRLIB) |
78 | + |
79 | #--------------------------------------------------------------- |
80 | # The order of the following rules is |
81 | # VERY IMPORTANT !!!! |
82 | diff --git a/configure/os/CONFIG.Common.linuxCommon b/configure/os/CONFIG.Common.linuxCommon |
83 | index 965de09..e8e9ab3 100644 |
84 | --- a/configure/os/CONFIG.Common.linuxCommon |
85 | +++ b/configure/os/CONFIG.Common.linuxCommon |
86 | @@ -25,11 +25,13 @@ STATIC_LDLIBS_YES= -Wl,-Bdynamic |
87 | |
88 | # Set runtime path for shared libraries if USE_RPATH=YES and STATIC_BUILD=NO |
89 | SHRLIBDIR_RPATH_LDFLAGS_YES_NO = $(SHRLIB_DEPLIB_DIRS:%=-Wl,-rpath,%) |
90 | +SHRLIBDIR_RPATH_LDFLAGS_ORIGIN_NO = $(shell $(MAKERPATH) -O '\$$ORIGIN' -F $(FINAL_DIR) -R $(LINKER_ORIGIN_ROOT) $(SHRLIB_DEPLIB_DIRS)) |
91 | SHRLIBDIR_LDFLAGS += \ |
92 | $(SHRLIBDIR_RPATH_LDFLAGS_$(LINKER_USE_RPATH)_$(STATIC_BUILD)) |
93 | |
94 | # Set runtime path for products if USE_RPATH=YES and STATIC_BUILD=NO |
95 | PRODDIR_RPATH_LDFLAGS_YES_NO = $(PROD_DEPLIB_DIRS:%=-Wl,-rpath,%) |
96 | +PRODDIR_RPATH_LDFLAGS_ORIGIN_NO = $(shell $(MAKERPATH) -O '\$$ORIGIN' -F $(FINAL_DIR) -R $(LINKER_ORIGIN_ROOT) $(PROD_DEPLIB_DIRS)) |
97 | PRODDIR_LDFLAGS += \ |
98 | $(PRODDIR_RPATH_LDFLAGS_$(LINKER_USE_RPATH)_$(STATIC_BUILD)) |
99 | |
100 | diff --git a/modules/database/src/std/softIoc/softMain.cpp b/modules/database/src/std/softIoc/softMain.cpp |
101 | index 8400a65..bc945c8 100644 |
102 | --- a/modules/database/src/std/softIoc/softMain.cpp |
103 | +++ b/modules/database/src/std/softIoc/softMain.cpp |
104 | @@ -9,225 +9,237 @@ |
105 | |
106 | /* Author: Andrew Johnson Date: 2003-04-08 */ |
107 | |
108 | -/* Usage: |
109 | - * softIoc [-D softIoc.dbd] [-h] [-S] [-s] [-a ascf] |
110 | - * [-m macro=value,macro2=value2] [-d file.db] |
111 | - * [-x prefix] [st.cmd] |
112 | - * |
113 | - * If used the -D option must come first, and specify the |
114 | - * path to the softIoc.dbd file. The compile-time install |
115 | - * location is saved in the binary as a default. |
116 | - * |
117 | - * Usage information will be printed if -h is given, then |
118 | - * the program will exit normally. |
119 | - * |
120 | - * The -S option prevents an interactive shell being started |
121 | - * after all arguments have been processed. |
122 | - * |
123 | - * Previous versions accepted a -s option to cause a shell |
124 | - * to be started; this option is still accepted but ignored |
125 | - * since a command shell is now started by default. |
126 | - * |
127 | - * Access Security can be enabled with the -a option giving |
128 | - * the name of the configuration file; if any macros were |
129 | - * set with -m before the -a option was given, they will be |
130 | - * used as access security substitution macros. |
131 | - * |
132 | - * Any number of -m and -d arguments can be interspersed; |
133 | - * the macros are applied to the following .db files. Each |
134 | - * later -m option causes earlier macros to be discarded. |
135 | - * |
136 | - * The -x option loads the softIocExit.db with the macro |
137 | - * IOC set to the string provided. This database contains |
138 | - * a subroutine record named $(IOC):exit which has its field |
139 | - * SNAM set to "exit". When this record is processed, the |
140 | - * subroutine that runs will call epicsExit() with the value |
141 | - * of the field A determining whether the exit status is |
142 | - * EXIT_SUCCESS if (A == 0.0) or EXIT_FAILURE (A != 0.0). |
143 | - * |
144 | - * A st.cmd file is optional. If any databases were loaded |
145 | - * the st.cmd file will be run *after* iocInit. To perform |
146 | - * iocsh commands before iocInit, all database loading must |
147 | - * be performed by the script itself, or by the user from |
148 | - * the interactive IOC shell. |
149 | - */ |
150 | - |
151 | -#include <stddef.h> |
152 | -#include <stdlib.h> |
153 | -#include <stddef.h> |
154 | -#include <string.h> |
155 | -#include <stdio.h> |
156 | +#include <iostream> |
157 | +#include <string> |
158 | +#include <list> |
159 | +#include <stdexcept> |
160 | |
161 | +#include <epicsGetopt.h> |
162 | #include "registryFunction.h" |
163 | #include "epicsThread.h" |
164 | #include "epicsExit.h" |
165 | #include "epicsStdio.h" |
166 | +#include "epicsString.h" |
167 | #include "dbStaticLib.h" |
168 | #include "subRecord.h" |
169 | #include "dbAccess.h" |
170 | #include "asDbLib.h" |
171 | #include "iocInit.h" |
172 | #include "iocsh.h" |
173 | +#include "osiFileName.h" |
174 | #include "epicsInstallDir.h" |
175 | |
176 | extern "C" int softIoc_registerRecordDeviceDriver(struct dbBase *pdbbase); |
177 | |
178 | -#define DBD_FILE EPICS_BASE "/dbd/softIoc.dbd" |
179 | -#define EXIT_FILE EPICS_BASE "/db/softIocExit.db" |
180 | +#ifndef EPICS_BASE |
181 | +// so IDEs knows EPICS_BASE is a string constant |
182 | +# define EPICS_BASE "/" |
183 | +# error -DEPICS_BASE required |
184 | +#endif |
185 | |
186 | -const char *arg0; |
187 | -const char *base_dbd = DBD_FILE; |
188 | -const char *exit_db = EXIT_FILE; |
189 | +#define DBD_BASE "dbd/softIoc.dbd" |
190 | +#define EXIT_BASE "db/softIocExit.db" |
191 | +#define DBD_FILE_REL "../../" DBD_BASE |
192 | +#define EXIT_FILE_REL "../../" EXIT_BASE |
193 | +#define DBD_FILE EPICS_BASE "/" DBD_BASE |
194 | +#define EXIT_FILE EPICS_BASE "/" EXIT_BASE |
195 | |
196 | +namespace { |
197 | |
198 | static void exitSubroutine(subRecord *precord) { |
199 | epicsExitLater((precord->a == 0.0) ? EXIT_SUCCESS : EXIT_FAILURE); |
200 | } |
201 | |
202 | -static void usage(int status) { |
203 | - printf("Usage: %s [-D softIoc.dbd] [-h] [-S] [-a ascf]\n", arg0); |
204 | - puts("\t[-m macro=value,macro2=value2] [-d file.db]"); |
205 | - puts("\t[-x prefix] [st.cmd]"); |
206 | - puts("Compiled-in path to softIoc.dbd is:"); |
207 | - printf("\t%s\n", base_dbd); |
208 | - epicsExit(status); |
209 | +void usage(const char *arg0, const std::string& base_dbd) { |
210 | + std::cout<<"Usage: "<<arg0<< |
211 | + " [-D softIoc.dbd] [-h] [-S] [-s] [-a ascf]\n" |
212 | + "[-m macro=value,macro2=value2] [-d file.db]\n" |
213 | + "[-x prefix] [st.cmd]\n" |
214 | + "\n" |
215 | + " -D <dbd> If used, must come first. Specify the path to the softIoc.dbdfile." |
216 | + " The compile-time install location is saved in the binary as a default.\n" |
217 | + "\n" |
218 | + " -h Print this mesage and exit.\n" |
219 | + "\n" |
220 | + " -S Prevents an interactive shell being started.\n" |
221 | + "\n" |
222 | + " -s Previously caused a shell to be started. Now accepted and ignored.\n" |
223 | + "\n" |
224 | + " -a <acf> Access Security configuration file. Macro substitution is\n" |
225 | + " performed.\n" |
226 | + "\n" |
227 | + " -m <MAC>=<value>,... Set/replace macro definitions used by subsequent -d and\n" |
228 | + " -a.\n" |
229 | + "\n" |
230 | + " -d <db> Load records from file (dbLoadRecords). Macro substitution is\n" |
231 | + " performed.\n" |
232 | + "\n" |
233 | + " -x <prefix> Load softIocExit.db. Provides a record \"<prefix>:exit\".\n" |
234 | + " Put 0 to exit with success, or non-zero to exit with an error.\n" |
235 | + "\n" |
236 | + "Any number of -m and -d arguments can be interspersed; the macros are applied\n" |
237 | + "to the following .db files. Each later -m option causes earlier macros to be\n" |
238 | + "discarded.\n" |
239 | + "\n" |
240 | + "A st.cmd file is optional. If any databases were loaded the st.cmd file will\n" |
241 | + "be run *after* iocInit. To perform iocsh commands before iocInit, all database\n" |
242 | + "loading must be performed by the script itself, or by the user from the\n" |
243 | + "interactive IOC shell.\n" |
244 | + "\n" |
245 | + "Compiled-in path to softIoc.dbd is:\n" |
246 | + "\t"<<base_dbd.c_str()<<"\n"; |
247 | } |
248 | |
249 | - |
250 | -int main(int argc, char *argv[]) |
251 | +void errIf(int ret, const std::string& msg) |
252 | { |
253 | - char *dbd_file = const_cast<char*>(base_dbd); |
254 | - char *macros = NULL; |
255 | - char xmacro[PVNAME_STRINGSZ + 4]; |
256 | - int startIocsh = 1; /* default = start shell */ |
257 | - int loadedDb = 0; |
258 | - |
259 | - arg0 = strrchr(*argv, '/'); |
260 | - if (!arg0) { |
261 | - arg0 = *argv; |
262 | - } else { |
263 | - ++arg0; /* skip the '/' */ |
264 | - } |
265 | - |
266 | - --argc, ++argv; |
267 | - |
268 | - /* Do this here in case the dbd file not available */ |
269 | - if (argc>0 && **argv=='-' && (*argv)[1]=='h') { |
270 | - usage(EXIT_SUCCESS); |
271 | - } |
272 | - |
273 | - if (argc>1 && **argv=='-' && (*argv)[1]=='D') { |
274 | - dbd_file = *++argv; |
275 | - argc -= 2; |
276 | - ++argv; |
277 | - } |
278 | - |
279 | - if (dbLoadDatabase(dbd_file, NULL, NULL)) { |
280 | - epicsExit(EXIT_FAILURE); |
281 | - } |
282 | - |
283 | + if(ret) |
284 | + throw std::runtime_error(msg); |
285 | +} |
286 | + |
287 | +void lazy_dbd(const std::string& dbd_file) { |
288 | + static bool loaded; |
289 | + if(loaded) return; |
290 | + loaded = true; |
291 | + |
292 | + errIf(dbLoadDatabase(dbd_file.c_str(), NULL, NULL), |
293 | + std::string("Failed to load DBD file: ")+dbd_file); |
294 | + std::cout<<"dbLoadDatabase(\""<<dbd_file<<"\")\n"; |
295 | + |
296 | softIoc_registerRecordDeviceDriver(pdbbase); |
297 | + std::cout<<"softIoc_registerRecordDeviceDriver(pdbbase)\n"; |
298 | registryFunctionAdd("exit", (REGISTRYFUNCTION) exitSubroutine); |
299 | +} |
300 | |
301 | - while (argc>1 && **argv == '-') { |
302 | - switch ((*argv)[1]) { |
303 | - case 'a': |
304 | - if (macros) asSetSubstitutions(macros); |
305 | - asSetFilename(*++argv); |
306 | - --argc; |
307 | - break; |
308 | - |
309 | - case 'd': |
310 | - if (dbLoadRecords(*++argv, macros)) { |
311 | - epicsExit(EXIT_FAILURE); |
312 | - } |
313 | - loadedDb = 1; |
314 | - --argc; |
315 | - break; |
316 | - |
317 | - case 'h': |
318 | - usage(EXIT_SUCCESS); |
319 | - |
320 | - case 'm': |
321 | - macros = *++argv; |
322 | - --argc; |
323 | - break; |
324 | - |
325 | - case 'S': |
326 | - startIocsh = 0; |
327 | - break; |
328 | - |
329 | - case 's': |
330 | - break; |
331 | - |
332 | - case 'x': |
333 | - epicsSnprintf(xmacro, sizeof xmacro, "IOC=%s", *++argv); |
334 | - if (dbLoadRecords(exit_db, xmacro)) { |
335 | - epicsExit(EXIT_FAILURE); |
336 | - } |
337 | - loadedDb = 1; |
338 | - --argc; |
339 | - break; |
340 | - |
341 | - default: |
342 | - printf("%s: option '%s' not recognized\n", arg0, *argv); |
343 | - usage(EXIT_FAILURE); |
344 | - } |
345 | - --argc; |
346 | - ++argv; |
347 | - } |
348 | - |
349 | - if (argc>0 && **argv=='-') { |
350 | - switch((*argv)[1]) { |
351 | - case 'a': |
352 | - case 'd': |
353 | - case 'm': |
354 | - case 'x': |
355 | - printf("%s: missing argument to option '%s'\n", arg0, *argv); |
356 | - usage(EXIT_FAILURE); |
357 | - |
358 | - case 'h': |
359 | - usage(EXIT_SUCCESS); |
360 | - |
361 | - case 'S': |
362 | - startIocsh = 0; |
363 | - break; |
364 | - |
365 | - case 's': |
366 | - break; |
367 | - |
368 | - default: |
369 | - printf("%s: option '%s' not recognized\n", arg0, *argv); |
370 | - usage(EXIT_FAILURE); |
371 | - } |
372 | - --argc; |
373 | - ++argv; |
374 | - } |
375 | - |
376 | - if (loadedDb) { |
377 | - iocInit(); |
378 | - epicsThreadSleep(0.2); |
379 | - } |
380 | - |
381 | - /* run user's startup script */ |
382 | - if (argc>0) { |
383 | - if (iocsh(*argv)) epicsExit(EXIT_FAILURE); |
384 | - epicsThreadSleep(0.2); |
385 | - loadedDb = 1; /* Give it the benefit of the doubt... */ |
386 | - } |
387 | - |
388 | - /* start an interactive shell if it was requested */ |
389 | - if (startIocsh) { |
390 | - iocsh(NULL); |
391 | - } else { |
392 | - if (loadedDb) { |
393 | - epicsThreadExitMain(); |
394 | - } else { |
395 | - printf("%s: Nothing to do!\n", arg0); |
396 | - usage(EXIT_FAILURE); |
397 | - } |
398 | +} // namespace |
399 | + |
400 | +int main(int argc, char *argv[]) |
401 | +{ |
402 | + try { |
403 | + std::string dbd_file(DBD_FILE), |
404 | + exit_file(EXIT_FILE), |
405 | + macros, // scratch space for macros (may be given more than once) |
406 | + xmacro; |
407 | + bool interactive = true; |
408 | + bool loadedDb = false; |
409 | + |
410 | + // attempt to compute relative paths |
411 | + { |
412 | + std::string prefix; |
413 | + char *cprefix = epicsGetExecDir(); |
414 | + if(cprefix) { |
415 | + try { |
416 | + prefix = cprefix; |
417 | + free(cprefix); |
418 | + } catch(...) { |
419 | + free(cprefix); |
420 | + throw; |
421 | + } |
422 | + } |
423 | + |
424 | + dbd_file = prefix + DBD_FILE_REL; |
425 | + exit_file = prefix + EXIT_FILE_REL; |
426 | + } |
427 | + |
428 | + int opt; |
429 | + |
430 | + while ((opt = getopt(argc, argv, "ha:d:m:Ssx:")) != -1) { |
431 | + switch (opt) { |
432 | + case 'h': /* Print usage */ |
433 | + usage(argv[0], dbd_file); |
434 | + epicsExit(0); |
435 | + return 0; |
436 | + default: |
437 | + usage(argv[0], dbd_file); |
438 | + std::cerr<<"Unknown argument: -"<<char(opt)<<"\n"; |
439 | + epicsExit(2); |
440 | + return 2; |
441 | + case 'a': |
442 | + lazy_dbd(dbd_file); |
443 | + if (!macros.empty()) { |
444 | + if(asSetSubstitutions(macros.c_str())) |
445 | + throw std::bad_alloc(); |
446 | + std::cout<<"asSetSubstitutions(\""<<macros<<"\")\n"; |
447 | + } |
448 | + if(asSetFilename(optarg)) |
449 | + throw std::bad_alloc(); |
450 | + std::cout<<"asSetFilename(\""<<optarg<<"\")\n"; |
451 | + break; |
452 | + case 'd': |
453 | + lazy_dbd(dbd_file); |
454 | + errIf(dbLoadRecords(optarg, macros.c_str()), |
455 | + std::string("Failed to load: ")+optarg); |
456 | + std::cout<<"dbLoadRecords(\""<<optarg<<"\""; |
457 | + if(!macros.empty()) |
458 | + std::cout<<", \""<<macros<<"\""; |
459 | + std::cout<<")\n"; |
460 | + loadedDb = true; |
461 | + break; |
462 | + case 'm': |
463 | + macros = optarg; |
464 | + break; |
465 | + case 'S': |
466 | + interactive = false; |
467 | + break; |
468 | + case 's': |
469 | + break; // historical |
470 | + case 'x': |
471 | + lazy_dbd(dbd_file); |
472 | + xmacro = "IOC="; |
473 | + xmacro += optarg; |
474 | + errIf(dbLoadRecords(exit_file.c_str(), xmacro.c_str()), |
475 | + std::string("Failed to load: ")+exit_file); |
476 | + loadedDb = true; |
477 | + break; |
478 | + } |
479 | + } |
480 | + |
481 | + lazy_dbd(dbd_file); |
482 | + |
483 | + if(optind<argc) { |
484 | + // run script |
485 | + // ignore any extra positional args (historical) |
486 | + |
487 | + std::cout<<"# Begin "<<argv[optind]<<"\n"; |
488 | + errIf(iocsh(argv[optind]), |
489 | + std::string("Error in ")+argv[optind]); |
490 | + std::cout<<"# End "<<argv[optind]<<"\n"; |
491 | + |
492 | + epicsThreadSleep(0.2); |
493 | + loadedDb = true; /* Give it the benefit of the doubt... */ |
494 | + } |
495 | + |
496 | + if (loadedDb) { |
497 | + std::cout<<"iocInit()\n"; |
498 | + iocInit(); |
499 | + epicsThreadSleep(0.2); |
500 | + } |
501 | + |
502 | + if(interactive) { |
503 | + std::cout.flush(); |
504 | + std::cerr.flush(); |
505 | + if(iocsh(NULL)) { |
506 | + epicsExit(1); |
507 | + return 1; |
508 | + } |
509 | + |
510 | + } else { |
511 | + if (loadedDb) { |
512 | + epicsThreadExitMain(); |
513 | + |
514 | + } else { |
515 | + usage(argv[0], dbd_file); |
516 | + std::cerr<<"Nothing to do!\n"; |
517 | + epicsExit(1); |
518 | + return 1; |
519 | + } |
520 | + } |
521 | + |
522 | + epicsExit(0); |
523 | + return 0; |
524 | + |
525 | + }catch(std::exception& e){ |
526 | + std::cerr<<"Error: "<<e.what()<<"\n"; |
527 | + epicsExit(2); |
528 | + return 2; |
529 | } |
530 | - epicsExit(EXIT_SUCCESS); |
531 | - /*Note that the following statement will never be executed*/ |
532 | - return 0; |
533 | } |
534 | diff --git a/modules/libcom/src/misc/unixFileName.h b/modules/libcom/src/misc/unixFileName.h |
535 | index 36e818c..9d7af25 100644 |
536 | --- a/modules/libcom/src/misc/unixFileName.h |
537 | +++ b/modules/libcom/src/misc/unixFileName.h |
538 | @@ -14,7 +14,29 @@ |
539 | #ifndef unixFileNameH |
540 | #define unixFileNameH |
541 | |
542 | +#include <shareLib.h> |
543 | + |
544 | +#ifdef __cplusplus |
545 | +extern "C" { |
546 | +#endif |
547 | + |
548 | #define OSI_PATH_LIST_SEPARATOR ":" |
549 | #define OSI_PATH_SEPARATOR "/" |
550 | |
551 | +/** Return the absolute path of the current executable. |
552 | + @returns NULL or the path. Caller must free() |
553 | + */ |
554 | +epicsShareFunc |
555 | +char *epicsGetExecName(void); |
556 | + |
557 | +/** Return the absolute path of the directory containing the current executable. |
558 | + @returns NULL or the path. Caller must free() |
559 | + */ |
560 | +epicsShareFunc |
561 | +char *epicsGetExecDir(void); |
562 | + |
563 | +#ifdef __cplusplus |
564 | +} |
565 | +#endif |
566 | + |
567 | #endif /* unixFileNameH */ |
568 | diff --git a/modules/libcom/src/osi/Makefile b/modules/libcom/src/osi/Makefile |
569 | index ecbf4c2..0352e9f 100644 |
570 | --- a/modules/libcom/src/osi/Makefile |
571 | +++ b/modules/libcom/src/osi/Makefile |
572 | @@ -123,6 +123,7 @@ Com_SRCS += osdMonotonic.c |
573 | Com_SRCS += osdProcess.c |
574 | Com_SRCS += osdNetIntf.c |
575 | Com_SRCS += osdMessageQueue.c |
576 | +Com_SRCS += osdgetexec.c |
577 | |
578 | Com_SRCS += devLibVME.c |
579 | Com_SRCS += devLibVMEOSD.c |
580 | diff --git a/modules/libcom/src/osi/os/Darwin/osdgetexec.c b/modules/libcom/src/osi/os/Darwin/osdgetexec.c |
581 | new file mode 100644 |
582 | index 0000000..4e4961c |
583 | --- /dev/null |
584 | +++ b/modules/libcom/src/osi/os/Darwin/osdgetexec.c |
585 | @@ -0,0 +1,50 @@ |
586 | + |
587 | +#include <string.h> |
588 | +#include <stdlib.h> |
589 | + |
590 | +#include <mach-o/dyld.h> |
591 | + |
592 | +#define epicsExportSharedSymbols |
593 | +#include <osiFileName.h> |
594 | + |
595 | +char *epicsGetExecName(void) |
596 | +{ |
597 | + uint32_t max = 64u; |
598 | + char *ret = NULL; |
599 | + |
600 | + while(1) { |
601 | + char *temp = realloc(ret, max); |
602 | + if(!temp) { |
603 | + /* we treat alloc failure as terminal */ |
604 | + free(ret); |
605 | + ret = NULL; |
606 | + break; |
607 | + } |
608 | + ret = temp; |
609 | + |
610 | + /* cf. "man 3 dyld" */ |
611 | + if(_NSGetExecutablePath(ret, &max)==0) { |
612 | + /* max left unchanged */ |
613 | + ret[max-1] = '\0'; |
614 | + break; |
615 | + } |
616 | + /* max has been updated with required size */ |
617 | + } |
618 | + |
619 | + /* TODO: _NSGetExecutablePath() doesn't follow symlinks */ |
620 | + |
621 | + return ret; |
622 | +} |
623 | + |
624 | +char *epicsGetExecDir(void) |
625 | +{ |
626 | + char *ret = epicsGetExecName(); |
627 | + if(ret) { |
628 | + char *sep = strrchr(ret, '/'); |
629 | + if(sep) { |
630 | + /* nil the charactor after the / */ |
631 | + sep[1] = '\0'; |
632 | + } |
633 | + } |
634 | + return ret; |
635 | +} |
636 | diff --git a/modules/libcom/src/osi/os/Linux/osdgetexec.c b/modules/libcom/src/osi/os/Linux/osdgetexec.c |
637 | new file mode 100644 |
638 | index 0000000..cba5d78 |
639 | --- /dev/null |
640 | +++ b/modules/libcom/src/osi/os/Linux/osdgetexec.c |
641 | @@ -0,0 +1,54 @@ |
642 | + |
643 | +#include <string.h> |
644 | +#include <stdlib.h> |
645 | +#include <unistd.h> |
646 | +#include <limits.h> |
647 | + |
648 | +#define epicsExportSharedSymbols |
649 | +#include <osiFileName.h> |
650 | + |
651 | +char *epicsGetExecName(void) |
652 | +{ |
653 | + size_t max = PATH_MAX; |
654 | + char *ret = NULL; |
655 | + ssize_t n; |
656 | + |
657 | + while(1) { |
658 | + char *temp = realloc(ret, max); |
659 | + if(!temp) { |
660 | + /* we treat alloc failure as terminal */ |
661 | + free(ret); |
662 | + ret = NULL; |
663 | + break; |
664 | + } |
665 | + ret = temp; |
666 | + |
667 | + n = readlink("/proc/self/exe", ret, max); |
668 | + if(n == -1) { |
669 | + free(ret); |
670 | + ret = NULL; |
671 | + break; |
672 | + } else if(n < max) { |
673 | + /* readlink() never adds a nil */ |
674 | + ret[n] = '\0'; |
675 | + break; |
676 | + } |
677 | + |
678 | + max += 64; |
679 | + } |
680 | + |
681 | + return ret; |
682 | +} |
683 | + |
684 | +char *epicsGetExecDir(void) |
685 | +{ |
686 | + char *ret = epicsGetExecName(); |
687 | + if(ret) { |
688 | + char *sep = strrchr(ret, '/'); |
689 | + if(sep) { |
690 | + /* nil the charactor after the / */ |
691 | + sep[1] = '\0'; |
692 | + } |
693 | + } |
694 | + return ret; |
695 | +} |
696 | diff --git a/modules/libcom/src/osi/os/WIN32/osdgetexec.c b/modules/libcom/src/osi/os/WIN32/osdgetexec.c |
697 | new file mode 100644 |
698 | index 0000000..a46ce50 |
699 | --- /dev/null |
700 | +++ b/modules/libcom/src/osi/os/WIN32/osdgetexec.c |
701 | @@ -0,0 +1,52 @@ |
702 | + |
703 | +#include <string.h> |
704 | +#include <stdlib.h> |
705 | +#include <windows.h> |
706 | + |
707 | +#define epicsExportSharedSymbols |
708 | +#include <osiFileName.h> |
709 | + |
710 | +char *epicsGetExecName(void) |
711 | +{ |
712 | + size_t max = 128; |
713 | + char *ret = NULL; |
714 | + DWORD n; |
715 | + |
716 | + while(1) { |
717 | + char *temp = realloc(ret, max); |
718 | + if(!temp) { |
719 | + /* we treat alloc failure as terminal */ |
720 | + free(ret); |
721 | + ret = NULL; |
722 | + break; |
723 | + } |
724 | + ret = temp; |
725 | + |
726 | + n = GetModuleFileName(NULL, ret, max); |
727 | + if(n == 0) { |
728 | + free(ret); |
729 | + ret = NULL; |
730 | + break; |
731 | + } else if(n < max) { |
732 | + ret[n] = '\0'; |
733 | + break; |
734 | + } |
735 | + |
736 | + max += 64; |
737 | + } |
738 | + |
739 | + return ret; |
740 | +} |
741 | + |
742 | +char *epicsGetExecDir(void) |
743 | +{ |
744 | + char *ret = epicsGetExecName(); |
745 | + if(ret) { |
746 | + char *sep = strrchr(ret, '\\'); |
747 | + if(sep) { |
748 | + /* nil the charactor after the / */ |
749 | + sep[1] = '\0'; |
750 | + } |
751 | + } |
752 | + return ret; |
753 | +} |
754 | diff --git a/modules/libcom/src/osi/os/WIN32/osiFileName.h b/modules/libcom/src/osi/os/WIN32/osiFileName.h |
755 | index 6ff0308..ced745f 100644 |
756 | --- a/modules/libcom/src/osi/os/WIN32/osiFileName.h |
757 | +++ b/modules/libcom/src/osi/os/WIN32/osiFileName.h |
758 | @@ -15,7 +15,29 @@ |
759 | #ifndef osiFileNameH |
760 | #define osiFileNameH |
761 | |
762 | +#include <shareLib.h> |
763 | + |
764 | +#ifdef __cplusplus |
765 | +extern "C" { |
766 | +#endif |
767 | + |
768 | #define OSI_PATH_LIST_SEPARATOR ";" |
769 | #define OSI_PATH_SEPARATOR "\\" |
770 | |
771 | +/** Return the absolute path of the current executable. |
772 | + @returns NULL or the path. Caller must free() |
773 | + */ |
774 | +epicsShareFunc |
775 | +char *epicsGetExecName(void); |
776 | + |
777 | +/** Return the absolute path of the directory containing the current executable. |
778 | + @returns NULL or the path. Caller must free() |
779 | + */ |
780 | +epicsShareFunc |
781 | +char *epicsGetExecDir(void); |
782 | + |
783 | +#ifdef __cplusplus |
784 | +} |
785 | +#endif |
786 | + |
787 | #endif /* osiFileNameH */ |
788 | diff --git a/modules/libcom/src/osi/os/cygwin32/osiFileName.h b/modules/libcom/src/osi/os/cygwin32/osiFileName.h |
789 | index 6d7fd6e..1e77990 100644 |
790 | --- a/modules/libcom/src/osi/os/cygwin32/osiFileName.h |
791 | +++ b/modules/libcom/src/osi/os/cygwin32/osiFileName.h |
792 | @@ -14,7 +14,29 @@ |
793 | #ifndef osiFileNameH |
794 | #define osiFileNameH |
795 | |
796 | +#include <shareLib.h> |
797 | + |
798 | +#ifdef __cplusplus |
799 | +extern "C" { |
800 | +#endif |
801 | + |
802 | #define OSI_PATH_LIST_SEPARATOR ";" |
803 | #define OSI_PATH_SEPARATOR "\\" |
804 | |
805 | +/** Return the absolute path of the current executable. |
806 | + @returns NULL or the path. Caller must free() |
807 | + */ |
808 | +epicsShareFunc |
809 | +char *epicsGetExecName(void); |
810 | + |
811 | +/** Return the absolute path of the directory containing the current executable. |
812 | + @returns NULL or the path. Caller must free() |
813 | + */ |
814 | +epicsShareFunc |
815 | +char *epicsGetExecDir(void); |
816 | + |
817 | +#ifdef __cplusplus |
818 | +} |
819 | +#endif |
820 | + |
821 | #endif /* osiFileNameH */ |
822 | diff --git a/modules/libcom/src/osi/os/default/osdgetexec.c b/modules/libcom/src/osi/os/default/osdgetexec.c |
823 | new file mode 100644 |
824 | index 0000000..0bec9ea |
825 | --- /dev/null |
826 | +++ b/modules/libcom/src/osi/os/default/osdgetexec.c |
827 | @@ -0,0 +1,14 @@ |
828 | +#include <stdlib.h> |
829 | + |
830 | +#define epicsExportSharedSymbols |
831 | +#include <osiFileName.h> |
832 | + |
833 | +char *epicsGetExecName(void) |
834 | +{ |
835 | + return NULL; |
836 | +} |
837 | + |
838 | +char *epicsGetExecDir(void) |
839 | +{ |
840 | + return NULL; |
841 | +} |
842 | diff --git a/modules/libcom/src/osi/os/freebsd/osdgetexec.c b/modules/libcom/src/osi/os/freebsd/osdgetexec.c |
843 | new file mode 100644 |
844 | index 0000000..39c0a16 |
845 | --- /dev/null |
846 | +++ b/modules/libcom/src/osi/os/freebsd/osdgetexec.c |
847 | @@ -0,0 +1,68 @@ |
848 | + |
849 | +#include <string.h> |
850 | +#include <stdlib.h> |
851 | +#include <unistd.h> |
852 | +#include <limits.h> |
853 | + |
854 | +#define epicsExportSharedSymbols |
855 | +#include <osiFileName.h> |
856 | + |
857 | +char *epicsGetExecName(void) |
858 | +{ |
859 | + size_t max = PATH_MAX; |
860 | + char *ret = NULL; |
861 | + ssize_t n; |
862 | + |
863 | + while(1) { |
864 | + char *temp = realloc(ret, max); |
865 | + if(!temp) { |
866 | + /* we treat alloc failure as terminal */ |
867 | + free(ret); |
868 | + ret = NULL; |
869 | + break; |
870 | + } |
871 | + ret = temp; |
872 | + |
873 | + n = readlink("/proc/curproc/file", ret, max); |
874 | + if(n == -1) { |
875 | + free(ret); |
876 | + ret = NULL; |
877 | + break; |
878 | + } else if(n < max) { |
879 | + /* readlink() never adds a nil */ |
880 | + ret[n] = '\0'; |
881 | + break; |
882 | + } |
883 | + |
884 | + max += 64; |
885 | + } |
886 | + |
887 | + if(!ret) { |
888 | + int mib[4]; |
889 | + mib[0] = CTL_KERN; |
890 | + mib[1] = KERN_PROC; |
891 | + mib[2] = KERN_PROC_PATHNAME; |
892 | + mib[3] = -1; |
893 | + |
894 | + ret = malloc(max); |
895 | + if(ret) { |
896 | + sysctl(mib, 4, ret, &cb, NULL, 0); |
897 | + /* TODO: error check */ |
898 | + } |
899 | + } |
900 | + |
901 | + return ret; |
902 | +} |
903 | + |
904 | +char *epicsGetExecDir(void) |
905 | +{ |
906 | + char *ret = epicsGetExecName(); |
907 | + if(ret) { |
908 | + char *sep = strrchr(ret, '/'); |
909 | + if(sep) { |
910 | + /* nil the charactor after the / */ |
911 | + sep[1] = '\0'; |
912 | + } |
913 | + } |
914 | + return ret; |
915 | +} |
916 | diff --git a/modules/libcom/src/osi/os/solaris/osdgetexec.c b/modules/libcom/src/osi/os/solaris/osdgetexec.c |
917 | new file mode 100644 |
918 | index 0000000..ff9739c |
919 | --- /dev/null |
920 | +++ b/modules/libcom/src/osi/os/solaris/osdgetexec.c |
921 | @@ -0,0 +1,30 @@ |
922 | + |
923 | +#include <string.h> |
924 | +#include <stdlib.h> |
925 | + |
926 | +#define epicsExportSharedSymbols |
927 | +#include <osiFileName.h> |
928 | + |
929 | +char *epicsGetExecName(void) |
930 | +{ |
931 | + const char *raw = getexecname(); |
932 | + char *ret = NULL; |
933 | + /* manpage says getexecname() might return a relative path. we treat this as an error */ |
934 | + if(raw[0]=='/') { |
935 | + ret = strdup(raw); |
936 | + } |
937 | + return ret; |
938 | +} |
939 | + |
940 | +char *epicsGetExecDir(void) |
941 | +{ |
942 | + char *ret = epicsGetExecName(); |
943 | + if(ret) { |
944 | + char *sep = strrchr(ret, '/'); |
945 | + if(sep) { |
946 | + /* nil the charactor after the / */ |
947 | + sep[1] = '\0'; |
948 | + } |
949 | + } |
950 | + return ret; |
951 | +} |
952 | diff --git a/modules/libcom/test/Makefile b/modules/libcom/test/Makefile |
953 | index f2e495a..4a5e457 100755 |
954 | --- a/modules/libcom/test/Makefile |
955 | +++ b/modules/libcom/test/Makefile |
956 | @@ -232,6 +232,11 @@ osiSockTest_SRCS += osiSockTest.c |
957 | testHarness_SRCS += osiSockTest.c |
958 | TESTS += osiSockTest |
959 | |
960 | +TESTPROD_HOST += testexecname |
961 | +testexecname_SRCS += testexecname.c |
962 | +# no point in including in testHarness. Not implemented for RTEMS/vxWorks. |
963 | +TESTS += testexecname |
964 | + |
965 | ifeq ($(BUILD_CLASS),HOST) |
966 | ifneq ($(OS_CLASS),WIN32) |
967 | # This test can only be run on a build host, and is broken on Windows |
968 | diff --git a/modules/libcom/test/testexecname.c b/modules/libcom/test/testexecname.c |
969 | new file mode 100644 |
970 | index 0000000..87be847 |
971 | --- /dev/null |
972 | +++ b/modules/libcom/test/testexecname.c |
973 | @@ -0,0 +1,24 @@ |
974 | + |
975 | +#include <string.h> |
976 | + |
977 | +#include <epicsUnitTest.h> |
978 | +#include <testMain.h> |
979 | + |
980 | +#include <osiFileName.h> |
981 | + |
982 | +MAIN(testexecname) |
983 | +{ |
984 | + testPlan(1); |
985 | + |
986 | + { |
987 | + char *buf = epicsGetExecName(); |
988 | + if(!buf) { |
989 | + testSkip(1, "epicsGetExecName() not available for this target"); |
990 | + } else { |
991 | + char *loc = strstr(buf, "testexecname"); |
992 | + testOk(!!loc, "Find \"testexecname\" in \"%s\"", buf); |
993 | + } |
994 | + } |
995 | + |
996 | + return testDone(); |
997 | +} |
998 | diff --git a/src/tools/Makefile b/src/tools/Makefile |
999 | index 0df1797..0d0c011 100644 |
1000 | --- a/src/tools/Makefile |
1001 | +++ b/src/tools/Makefile |
1002 | @@ -39,6 +39,8 @@ PERL_SCRIPTS += tap-to-junit-xml.pl |
1003 | PERL_SCRIPTS += useManifestTool.pl |
1004 | PERL_SCRIPTS += genVersionHeader.pl |
1005 | |
1006 | +PERL_SCRIPTS += makeRPath.py |
1007 | + |
1008 | HTMLS = style.css |
1009 | HTMLS += EPICS/Getopts.html |
1010 | HTMLS += EPICS/Path.html |
1011 | diff --git a/src/tools/makeRPath.py b/src/tools/makeRPath.py |
1012 | new file mode 100644 |
1013 | index 0000000..000b8b4 |
1014 | --- /dev/null |
1015 | +++ b/src/tools/makeRPath.py |
1016 | @@ -0,0 +1,80 @@ |
1017 | +#!/usr/bin/env python |
1018 | + |
1019 | +from __future__ import print_function |
1020 | + |
1021 | +import sys |
1022 | +import os |
1023 | +from collections import OrderedDict # used as OrderedSet |
1024 | + |
1025 | +from argparse import ArgumentParser |
1026 | + |
1027 | +if os.environ.get('EPICS_DEBUG_RPATH','')=='YES': |
1028 | + sys.stderr.write('%s'%sys.argv) |
1029 | + |
1030 | +P = ArgumentParser(description='''Compute and output -rpath entries for each of the given paths. |
1031 | + Paths under --root will be computed as relative to --final .''', |
1032 | +epilog=''' |
1033 | +eg. A library to be placed in /build/lib and linked against libraries in |
1034 | +'/build/lib', '/build/module/lib', and '/other/lib' would pass: |
1035 | + |
1036 | + "makeRPath.py -F /build/lib -R /build /build/lib /build/module/lib /other/lib" |
1037 | +which prints "-Wl,-rpath,$ORIGIN/. -Wl,-rpath,$ORIGIN/../module/lib -Wl,-rpath,/other/lib" |
1038 | +''') |
1039 | +P.add_argument('-F','--final',default=os.getcwd(), help='Final install location for ELF file') |
1040 | +P.add_argument('-R','--root',default='', help='Root(s) of relocatable tree. Separate with :') |
1041 | +P.add_argument('-O', '--origin', default='$ORIGIN') |
1042 | +P.add_argument('path', nargs='*') |
1043 | +args = P.parse_args() |
1044 | + |
1045 | +# eg. |
1046 | +# target to be installed as: /build/bin/blah |
1047 | +# |
1048 | +# post-install will copy as: /install/bin/blah |
1049 | +# |
1050 | +# Need to link against: |
1051 | +# /install/lib/libA.so |
1052 | +# /build/lib/libB.so |
1053 | +# /other/lib/libC.so |
1054 | +# |
1055 | +# Want final result to be: |
1056 | +# -rpath $ORIGIN/../lib -rpath /other/lib \ |
1057 | +# -rpath-link /build/lib -rpath-link /install/lib |
1058 | + |
1059 | +fdir = os.path.abspath(args.final) |
1060 | +roots = [os.path.abspath(root) for root in args.root.split(':') if len(root)] |
1061 | + |
1062 | +# find the root which contains the final location |
1063 | +froot = None |
1064 | +for root in roots: |
1065 | + frel = os.path.relpath(fdir, root) |
1066 | + if not frel.startswith('..'): |
1067 | + # final dir is under this root |
1068 | + froot = root |
1069 | + break |
1070 | + |
1071 | +if froot is None: |
1072 | + sys.stderr.write("makeRPath: Final location %s\nNot under any of: %s\n"%(fdir, roots)) |
1073 | + sys.exit(1) |
1074 | + |
1075 | +output = OrderedDict() |
1076 | +for path in args.path: |
1077 | + path = os.path.abspath(path) |
1078 | + |
1079 | + for root in roots: |
1080 | + rrel = os.path.relpath(path, root) |
1081 | + if not rrel.startswith('..'): |
1082 | + # path is under this root |
1083 | + |
1084 | + # some older binutils don't seem to handle $ORIGIN correctly |
1085 | + # when locating dependencies of libraries. So also provide |
1086 | + # the absolute path for internal use by 'ld' only. |
1087 | + output['-Wl,-rpath-link,'+path] = True |
1088 | + |
1089 | + # frel is final location relative to enclosing root |
1090 | + # rrel is target location relative to enclosing root |
1091 | + path = os.path.relpath(rrel, frel) |
1092 | + break |
1093 | + |
1094 | + output['-Wl,-rpath,'+os.path.join(args.origin, path)] = True |
1095 | + |
1096 | +print(' '.join(output)) |
This change adds a small python script. I could not find a GNU Make equivalent for os.path.relpath(). So this MR will likely have to wait until someone translates this into perl.
The epicsGetExecDir() implementations for solaris and freebsd have not been tested. As both of these targets use ELF, $ORIGIN should work there. However, I have no plans to tests on either of these targets and will remove these implementations if they are a barrier to acceptance.
Also, to be clear. This is not a change to the default behavior. Unless specifically required with LINKER_ USE_RPATH= ORIGIN linking continues to be done with absolute rpath.