Skip to content

Commit b4d5111

Browse files
committed
Add automatic setting of LD_LIBRARY_PATH for Posix
1 parent 0e32807 commit b4d5111

File tree

2 files changed

+91
-5
lines changed

2 files changed

+91
-5
lines changed

changelog/shared-load-path.dd

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
Automatic setting of ``LD_LIBRARY_PATH`` on Posix to executable directory.
2+
3+
When using dub to run a D program via ``run`` or ``test`` operations it will now automatically add for Posix targets except for OSX the ``LD_LIBRARY_PATH`` environment variable to the directory of the executable.
4+
5+
This enables shared library dependencies to work in these operations without setting the RPATH variable.
6+
It is however still your responsibility to set the RPATH variable when handling installation or prior to debugging.
7+
This can be done quite conveniently using the ``patchelf`` program via:
8+
9+
```sh
10+
patchelf --force-rpath --set-rpath ~/projects/myproject ./executable
11+
```
12+
13+
OSX support has been disabled, support for ``DYLD_FALLBACK_LIBRARY_PATH`` has been written, however this will need separate testing as OSX has some weird behavior surrounding these environment variables.
14+
It may not be required as OSX is supposed to look in the current working directory for shared libraries by default, similar to Windows.
15+
16+
As a consequence of this change the environment variables ``LD_LIBRARY_PATH``, ``DYLD_LIBRARY_PATH``, and ``DYLD_FALLBACK_LIBRARY_PATH`` all support appending when used as an environment variable instead of overriding.

source/dub/generators/build.d

+75-5
Original file line numberDiff line numberDiff line change
@@ -676,19 +676,89 @@ class BuildGenerator : ProjectGenerator {
676676
if (!exe_file_path.absolute) exe_file_path = cwd ~ exe_file_path;
677677
runPreRunCommands(m_project.rootPackage, m_project, settings, buildsettings);
678678
logInfo("Running", Color.green, "%s %s", exe_file_path.relativeTo(runcwd), run_args.join(" "));
679+
679680
string[string] env;
680-
foreach (aa; [buildsettings.environments, buildsettings.runEnvironments])
681-
foreach (k, v; aa)
682-
env[k] = v;
681+
682+
{
683+
// When providing an AA to std.process it'll ignore the parent environment if Config.newEnv is set,
684+
// so don't forget to copy over the global environment, otherwise it won't pass it into the child process.
685+
// We'll do this because we want full control over the variables as we're overriding and appending to some.
686+
env = environment.toAA;
687+
688+
// copy environment variables over to run with
689+
foreach (aa; [buildsettings.environments, buildsettings.runEnvironments]) {
690+
foreach (k, v; aa) {
691+
switch(k) {
692+
case "LD_LIBRARY_PATH":
693+
case "DYLD_LIBRARY_PATH":
694+
case "DYLD_FALLBACK_LIBRARY_PATH":
695+
// for these we probably want to go with an append only approach, rather than setting it
696+
697+
if (k in env) {
698+
env[k] = env[k] ~ ":" ~ v;
699+
break;
700+
} else
701+
goto default;
702+
703+
default:
704+
env[k] = v;
705+
break;
706+
}
707+
}
708+
}
709+
710+
// set the executable directory into the library search path if required
711+
string exe_directory = exe_file_path.parentPath.toNativeString;
712+
713+
version(OSX) {
714+
// We should not need to deal with OSX under the condition of when the current working directory
715+
// is the directory of the executable.
716+
// This has similar behavior to Windows.
717+
718+
// However OSX is quite weird, just having a slash in an appropriete environment variable
719+
// DYLD_FALLBACK_LIBRARY_PATH, DYLD_LIBRARY_PATH, or LD_LIBRARY_PATH will lead to different behaviors.
720+
// These behaviors are not always desirable and has lead to Homebrew community ignoring it completely.
721+
722+
// For the time being we won't support setting DYLD_FALLBACK_LIBRARY_PATH although it is implemented.
723+
// Due to being unable to test the scenario when cwd != executable directory.
724+
725+
version(none) {
726+
string previous = env.get("DYLD_FALLBACK_LIBRARY_PATH", null);
727+
728+
if (previous.length > 0) {
729+
env["DYLD_FALLBACK_LIBRARY_PATH"] = exe_directory ~ ":" ~ previous;
730+
} else
731+
env["DYLD_FALLBACK_LIBRARY_PATH"] = exe_directory;
732+
}
733+
} else version(Posix) {
734+
// When on Posix, we also want to ensure LD_LIBRARY_PATH is set to the programs directory
735+
// By setting this environment variable we ensure any shared libraries in the same directory as
736+
// our program will be found and loaded automatically.
737+
// If we don't do this, dub run and dub test may not run due to missing shared libraries being found.
738+
739+
// Alternatively you can use patchelf, set LD_LIBRARY_PATH manually or set RPATH during linking.
740+
// I.e. patchelf --force-rpath --set-rpath ~/projects/ProjectSidero/eventloop/examples/networking ./example_networking
741+
// Or: export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:~/projects/ProjectSidero/eventloop/examples/networking
742+
// For a D compiler this should work: -L-rpath=.
743+
744+
string previous = env.get("LD_LIBRARY_PATH", null);
745+
746+
if (previous.length > 0) {
747+
env["LD_LIBRARY_PATH"] = exe_directory ~ ":" ~ previous;
748+
} else
749+
env["LD_LIBRARY_PATH"] = exe_directory;
750+
}
751+
}
752+
683753
if (settings.runCallback) {
684754
auto res = execute([ exe_file_path.toNativeString() ] ~ run_args,
685-
env, Config.none, size_t.max, runcwd.toNativeString());
755+
env, Config.newEnv, size_t.max, runcwd.toNativeString());
686756
settings.runCallback(res.status, res.output);
687757
settings.targetExitStatus = res.status;
688758
runPostRunCommands(m_project.rootPackage, m_project, settings, buildsettings);
689759
} else {
690760
auto prg_pid = spawnProcess([ exe_file_path.toNativeString() ] ~ run_args,
691-
env, Config.none, runcwd.toNativeString());
761+
env, Config.newEnv, runcwd.toNativeString());
692762
auto result = prg_pid.wait();
693763
settings.targetExitStatus = result;
694764
runPostRunCommands(m_project.rootPackage, m_project, settings, buildsettings);

0 commit comments

Comments
 (0)