digitalmars.D.learn - How to compile Phobos with other D code to create a shared library?
- data pulverizer (26/26) May 31 2021 Hi,
- kinke (3/5) May 31 2021 The problem is almost certainly `-betterC`, which disables
- data pulverizer (22/27) May 31 2021 Thanks - I should have know that one! I've removed the flag but
- Steven Schveighoffer (6/36) May 31 2021 ticksPerSecond is initialized in the runtime just before static
- data pulverizer (4/8) May 31 2021 Nope, I guess I'm supposed to be? if so where do I place the
- Steven Schveighoffer (8/22) May 31 2021 You need to call it wherever you think it might not have been called yet...
- data pulverizer (24/31) May 31 2021 Many thanks!
- Adam D. Ruppe (17/28) May 31 2021 Passing one of those to a C function is iffy anyway because the C
- Steven Schveighoffer (5/44) Jun 01 2021 You should use whatever Julia is expecting for memory management. I
- data pulverizer (9/9) Jun 01 2021 Doing `Runtime.initialize` is working with Julia but not yet R,
- Steven Schveighoffer (10/21) Jun 02 2021 What's happening is that the dynamic linker is trying to resolve that
- data pulverizer (47/56) Jun 02 2021 I ran `ldd`, it looks as if all the symbols are resolved
- bachmeier (12/21) Jun 02 2021 Are you aware of my embedr project, which handles all that for
- data pulverizer (11/22) Jun 04 2021 Thanks. Looks like I have some more reading to do. I did know
- bachmeier (18/21) Jun 04 2021 It requires an R package if you want to call D functions from R.
- Alain De Vos (1/1) Jun 04 2021 Dub is probably not much of a help :)
- bachmeier (5/6) Jun 04 2021 That's right. I typically don't use Dub when I'm calling D
- data pulverizer (23/40) Jun 05 2021 I've just read the documentation and had a go, **compiling with
Hi, I am trying to compile D code to a shared library to be called by another language (in this case Julia). I can get a basic call to work but I can't call functions that use Phobos (I have no idea how to compile against it). I have found some documentation [here](https://github.com/mesonbuild/meson/issues/6786) and [here](https://dlang.org/articles/dll-linux.html) that lists compilation against specific flags so for instance I have tried: ``` ldc2 jbasic.d -O3 -link-defaultlib-shared --betterC --boundscheck=off -nogc -shared -of=jbasic.so ``` But in Julia (and the equivalent thing happens in R), when I attempt to load the function I get an error: ``` ERROR: could not load library "./jbasic.so" ./jbasic.so: undefined symbol: _D3std6random__T21MersenneTwisterEngineTmVmi64Vmi312Vmi156Vmi31VmN5403634167711393303Vmi29Vmi6148914691236517205Vmi17Vmi8202884508482404352Vmi37VmN2270628950310912Vmi43Vmi6364136223846793005ZQGt6__initZ ``` I tried the equivalent with `dmd`: ``` dmd jbasic.d -O -defaultlib=libphobos2.so -betterC -fPIC -boundscheck=off -shared -of=jbasic.so ``` with the same result. Thank you.
May 31 2021
On Monday, 31 May 2021 at 19:21:52 UTC, data pulverizer wrote:ldc2 jbasic.d -O3 -link-defaultlib-shared --betterC --boundscheck=off -nogc -shared -of=jbasic.soThe problem is almost certainly `-betterC`, which disables linking against Phobos and druntime.
May 31 2021
On Monday, 31 May 2021 at 20:32:11 UTC, kinke wrote:On Monday, 31 May 2021 at 19:21:52 UTC, data pulverizer wrote:Thanks - I should have know that one! I've removed the flag but now I'm getting the following error when I try to call the function: ``` Aborting from src/core/time.d(2131) MonoTimeImpl!(ClockType.normal) failed to get the frequency of the system's monotonic clock. signal (6): Aborted in expression starting at REPL[2]:1 gsignal at x86_64-linux-gnu/libc.so.6 (unknown line) abort at x86_64-linux-gnu/libc.so.6 (unknown line) _D4core8internal5abortQgFNbNiNfMAyaMQemZv at /lib64/libphobos2.so.0.90 (unknown line) _D4core4time__T12MonoTimeImplVEQBdQBb9ClockTypei0ZQBj8currTimeFNbNdNiNeZSQC QCr__TQCpVQCei0ZQCz at dmd/current/lib64/libphobos2.so.0.90 (unknown line) _D3std6random13bootstrapSeedFNbNiZm at dmd/current/lib64/libphobos2.so.0.90 (unknown line) _D3std6random__T17unpredictableSeedTmZQwFNbNdNiNeZm at dmd/current/lib64/libphobos2.so.0.90 (unknown line) _D3std6random17unpredictableSeedFNbNdNiNeZk at dmd/current/lib64/libphobos2.so.0.90 (unknown line) ```ldc2 jbasic.d -O3 -link-defaultlib-shared --betterC --boundscheck=off -nogc -shared -of=jbasic.soThe problem is almost certainly `-betterC`, which disables linking against Phobos and druntime.
May 31 2021
On 5/31/21 4:41 PM, data pulverizer wrote:On Monday, 31 May 2021 at 20:32:11 UTC, kinke wrote:ticksPerSecond is initialized in the runtime just before static constructors are run. See https://github.com/dlang/druntime/blob/2d8b28da39e8bc3bc3172c69bb96c35d77f40d2a/src/rt/dmain2.d#L130 Are you calling Runtime.initialize()? -SteveOn Monday, 31 May 2021 at 19:21:52 UTC, data pulverizer wrote:Thanks - I should have know that one! I've removed the flag but now I'm getting the following error when I try to call the function: ``` Aborting from src/core/time.d(2131) MonoTimeImpl!(ClockType.normal) failed to get the frequency of the system's monotonic clock. signal (6): Aborted in expression starting at REPL[2]:1 gsignal at x86_64-linux-gnu/libc.so.6 (unknown line) abort at x86_64-linux-gnu/libc.so.6 (unknown line) _D4core8internal5abortQgFNbNiNfMAyaMQemZv at /lib64/libphobos2.so.0.90 (unknown line) _D4core4time__T12MonoTimeImplVEQBdQBb9ClockTypei0ZQBj8currTimeFNbNdNiNeZSQC QCr__TQCpVQCei0ZQCz at dmd/current/lib64/libphobos2.so.0.90 (unknown line) _D3std6random13bootstrapSeedFNbNiZm at dmd/current/lib64/libphobos2.so.0.90 (unknown line) _D3std6random__T17unpredictableSeedTmZQwFNbNdNiNeZm at dmd/current/lib64/libphobos2.so.0.90 (unknown line) _D3std6random17unpredictableSeedFNbNdNiNeZk at dmd/current/lib64/libphobos2.so.0.90 (unknown line) ```ldc2 jbasic.d -O3 -link-defaultlib-shared --betterC --boundscheck=off -nogc -shared -of=jbasic.soThe problem is almost certainly `-betterC`, which disables linking against Phobos and druntime.
May 31 2021
On Monday, 31 May 2021 at 21:01:19 UTC, Steven Schveighoffer wrote:ticksPerSecond is initialized in the runtime just before static constructors are run. See https://github.com/dlang/druntime/blob/2d8b28da39e8bc3bc3172c69bb96c35d77f40d2a/src/rt/dmain2.d#L130 Are you calling Runtime.initialize()?Nope, I guess I'm supposed to be? if so where do I place the call(s) for initialize() and terminate()?
May 31 2021
On 5/31/21 5:20 PM, data pulverizer wrote:On Monday, 31 May 2021 at 21:01:19 UTC, Steven Schveighoffer wrote:You need to call it wherever you think it might not have been called yet. It's reentrant, so if you call it more than once, it will only initialize once, and count how many times you have to call `Runtime.terminate`. Best to use a `scope(exit)` to call `Runtime.terminate` if you are calling it periodically. -SteveticksPerSecond is initialized in the runtime just before static constructors are run. See https://github.com/dlang/druntime/blob/2d8b28da39e8bc3bc3172c69bb96c35d77f40d2a/ rc/rt/dmain2.d#L130 Are you calling Runtime.initialize()?Nope, I guess I'm supposed to be? if so where do I place the call(s) for initialize() and terminate()?
May 31 2021
On Monday, 31 May 2021 at 21:26:15 UTC, Steven Schveighoffer wrote:You need to call it wherever you think it might not have been called yet. It's reentrant, so if you call it more than once, it will only initialize once, and count how many times you have to call `Runtime.terminate`. Best to use a `scope(exit)` to call `Runtime.terminate` if you are calling it periodically.Many thanks! Something interesting is using arrays. I can see that if you instantiate an array within the D function using `new`, for instance ``` auto result = new double[n]; ``` you can't return that array to Julia (or whatever) if your function does ``` scope(exit) Runtime.terminate(); ``` and it segfaults. But while you can do: ``` auto result = cast(double*)malloc(double.sizeof * n); ``` you're left with the issue of not being able to free the memory since you've terminated the runtime. So I guess you have to appropriately pair up `initialize` and `terminate` as appropriate to kind of *startup* and and *shutdown* the connection with druntime with your allocations if they stay within D?
May 31 2021
On Monday, 31 May 2021 at 21:46:09 UTC, data pulverizer wrote:Something interesting is using arrays. I can see that if you instantiate an array within the D function using `new`, for instancePassing one of those to a C function is iffy anyway because the C function can hide it from the garbage collector. What happens here is Runtime.terminate does a final GC sweep to gracefully clean up as much as it can before it close... and it thinks that array is unused, so it frees it. GC.addRoot(array) can tell it not to free normally... but i'm not sure if runtime.terminate cares about that since terminate makes it think the whole thing is going down anyway.auto result = cast(double*)malloc(double.sizeof * n); ``` you're left with the issue of not being able to free the memory since you've terminated the runtime.You can free still, malloc and free are not controlled by the D runtime. This is sometimes a better idea anyway when passing it to C function. I don't know how Julia does it though.So I guess you have to appropriately pair up `initialize` and `terminate` as appropriate to kind of *startup* and and *shutdown* the connection with druntime with your allocations if they stay within D?But yeah the best way to do it is to initialize once on library load then terminate once when the whole thing is finished. Typically C plugin systems have some hook for this you can use. Like some onload function they define you can use or something.
May 31 2021
On 5/31/21 5:46 PM, data pulverizer wrote:On Monday, 31 May 2021 at 21:26:15 UTC, Steven Schveighoffer wrote:You should use whatever Julia is expecting for memory management. I don't know what that is, but typically you want to use your host language's memory management system to create blocks instead of the D GC. -SteveYou need to call it wherever you think it might not have been called yet. It's reentrant, so if you call it more than once, it will only initialize once, and count how many times you have to call `Runtime.terminate`. Best to use a `scope(exit)` to call `Runtime.terminate` if you are calling it periodically.Many thanks! Something interesting is using arrays. I can see that if you instantiate an array within the D function using `new`, for instance ``` auto result = new double[n]; ``` you can't return that array to Julia (or whatever) if your function does ``` scope(exit) Runtime.terminate(); ``` and it segfaults. But while you can do: ``` auto result = cast(double*)malloc(double.sizeof * n); ``` you're left with the issue of not being able to free the memory since you've terminated the runtime. So I guess you have to appropriately pair up `initialize` and `terminate` as appropriate to kind of *startup* and and *shutdown* the connection with druntime with your allocations if they stay within D?
Jun 01 2021
Doing `Runtime.initialize` is working with Julia but not yet R, I'm getting a clock/GLIBC error ``` Error in dyn.load("rbasic.so") : unable to load shared object 'code/rbasic.so': lib/x86_64-linux-gnu/librt.so.1: undefined symbol: __clock_nanosleep, version GLIBC_PRIVATE ``` do I need to import anything else?
Jun 01 2021
On 6/1/21 2:18 PM, data pulverizer wrote:Doing `Runtime.initialize` is working with Julia but not yet R, I'm getting a clock/GLIBC error ``` Error in dyn.load("rbasic.so") : unable to load shared object 'code/rbasic.so': lib/x86_64-linux-gnu/librt.so.1: undefined symbol: __clock_nanosleep, version GLIBC_PRIVATE ``` do I need to import anything else?What's happening is that the dynamic linker is trying to resolve that symbol, but cannot find it in the given library. Try `ldd code/rbasic.so` and see if it tells you the things it is looking for. Many times, you will see "Not found" or something and you know what library to install/look for. Otherwise, it might be that librt.so.1 seemed to have __clock_nanosleep when it was built, but the librt.so.1 it is loading during runtime does not have it. Are you building on one machine and then running on another? -Steve
Jun 02 2021
On Wednesday, 2 June 2021 at 17:49:49 UTC, Steven Schveighoffer wrote:What's happening is that the dynamic linker is trying to resolve that symbol, but cannot find it in the given library. Try `ldd code/rbasic.so` and see if it tells you the things it is looking for. Many times, you will see "Not found" or something and you know what library to install/look for.I ran `ldd`, it looks as if all the symbols are resolved ``` $ ldd rbasic.so linux-vdso.so.1 (0x00007ffe7af56000) libR.so => /lib/libR.so (0x00007f269adb8000) libdruntime-ldc-shared.so.94 => /snap/ldc2/170/bin/../lib/libdruntime-ldc-shared.so.94 (0x00007f269ac0c000) libblas.so.3 => /lib/x86_64-linux-gnu/libblas.so.3 (0x00007f269920d000) libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f26990be000) libreadline.so.8 => /lib/x86_64-linux-gnu/libreadline.so.8 (0x00007f269906e000) libpcre2-8.so.0 => /lib/x86_64-linux-gnu/libpcre2-8.so.0 (0x00007f2698fdc000) liblzma.so.5 => /lib/x86_64-linux-gnu/liblzma.so.5 (0x00007f2698fb3000) libbz2.so.1.0 => /lib/x86_64-linux-gnu/libbz2.so.1.0 (0x00007f2698fa0000) libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007f2698f84000) libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f2698f7e000) libicuuc.so.66 => /lib/x86_64-linux-gnu/libicuuc.so.66 (0x00007f2698d98000) libicui18n.so.66 => /lib/x86_64-linux-gnu/libicui18n.so.66 (0x00007f2698a97000) libgomp.so.1 => /lib/x86_64-linux-gnu/libgomp.so.1 (0x00007f2698a55000) libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f2698a32000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f2698840000) /lib64/ld-linux-x86-64.so.2 (0x00007f269b29e000) librt.so.1 => /snap/core/current/lib/x86_64-linux-gnu/librt.so.1 (0x00007f2698638000) libgcc_s.so.1 => /snap/core/current/lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f2698420000) libtinfo.so.6 => /lib/x86_64-linux-gnu/libtinfo.so.6 (0x00007f26983f0000) libicudata.so.66 => /lib/x86_64-linux-gnu/libicudata.so.66 (0x00007f269692f000) libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f269674e000) ```Otherwise, it might be that librt.so.1 seemed to have __clock_nanosleep when it was built, but the librt.so.1 it is loading during runtime does not have it. Are you building on one machine and then running on another?I am building and running on the same machine.
Jun 02 2021
On Tuesday, 1 June 2021 at 18:18:35 UTC, data pulverizer wrote:Doing `Runtime.initialize` is working with Julia but not yet R, I'm getting a clock/GLIBC error ``` Error in dyn.load("rbasic.so") : unable to load shared object 'code/rbasic.so': lib/x86_64-linux-gnu/librt.so.1: undefined symbol: __clock_nanosleep, version GLIBC_PRIVATE ``` do I need to import anything else?Are you aware of my embedr project, which handles all that for you, and does a lot of other stuff? https://embedr.netlify.app If you want to do it yourself, you can see the boilerplate you need to add to call a shared library from R here: https://bitbucket.org/bachmeil/embedr/src/bebf67e5b30cd4163214c7f44f9907e7d7490dc0/R/compile.R#lines-99 If you want to read the R manual, the relevant section is here: https://cran.r-project.org/doc/manuals/r-release/R-exts.html#dyn_002eload-and-dyn_002eunload If you don't do that, it's unlikely to work unless you get lucky. If you want to load foo.so, you need a corresponding R_init_foo function where you call Runtime.initialize.
Jun 02 2021
On Wednesday, 2 June 2021 at 23:23:58 UTC, bachmeier wrote:Are you aware of my embedr project, which handles all that for you, and does a lot of other stuff? https://embedr.netlify.app If you want to do it yourself, you can see the boilerplate you need to add to call a shared library from R here: https://bitbucket.org/bachmeil/embedr/src/bebf67e5b30cd4163214c7f44f9907e7d7490dc0/R/compile.R#lines-99 If you want to read the R manual, the relevant section is here: https://cran.r-project.org/doc/manuals/r-release/R-exts.html#dyn_002eload-and-dyn_002eunload If you don't do that, it's unlikely to work unless you get lucky. If you want to load foo.so, you need a corresponding R_init_foo function where you call Runtime.initialize.Thanks. Looks like I have some more reading to do. I did know about embedr, but I saw it had dependencies and I wanted a standalone and fully transparent D solution. I'm already calling R routines from D using standalone Rmath and being able to call D from R and D have independent full access to R's internals would be awesome. But your package certainly provides very useful information. The reference to R's dyn.load internals looks useful and relevant and I'll be trying those once I get the time. Probably during the weekend.
Jun 04 2021
On Friday, 4 June 2021 at 07:26:53 UTC, data pulverizer wrote:Thanks. Looks like I have some more reading to do. I did know about embedr, but I saw it had dependencies and I wanted a standalone and fully transparent D solution.It requires an R package if you want to call D functions from R. You need to link to R itself if you want to do something like pass a vector from R to D and then access that data from D. Since R is aware of the location of all the underlying libraries, it's easiest to let it generate the dub.sdl for you. I guess you're using the older .C interface [as done here](http://users.stat.umn.edu/~geyer/rc/). Good enough if passing double* and int* pointers to D functions will suffice. Only thing you need to do to initialize the D runtime is add this to your D code ``` struct DllInfo; extern(C) void R_init_libfoo(DllInfo * info) { Runtime.initialize(); } ``` where foo is the name of the library you're loading (foo.so).
Jun 04 2021
On Friday, 4 June 2021 at 15:33:32 UTC, Alain De Vos wrote:Dub is probably not much of a help :)That's right. I typically don't use Dub when I'm calling D functions from R. It's the only way you can use a Dub package like Mir, though, so that's why you might want it to generate a dub.sdl for you.
Jun 04 2021
On Friday, 4 June 2021 at 15:19:17 UTC, bachmeier wrote:It requires an R package if you want to call D functions from R. You need to link to R itself if you want to do something like pass a vector from R to D and then access that data from D. Since R is aware of the location of all the underlying libraries, it's easiest to let it generate the dub.sdl for you. I guess you're using the older .C interface [as done here](http://users.stat.umn.edu/~geyer/rc/). Good enough if passing double* and int* pointers to D functions will suffice. Only thing you need to do to initialize the D runtime is add this to your D code ``` struct DllInfo; extern(C) void R_init_libfoo(DllInfo * info) { Runtime.initialize(); } ``` where foo is the name of the library you're loading (foo.so).I've just read the documentation and had a go, **compiling with GDC worked for both `.C` and `.Call`**. I tried with all three compilers DMD, LDC, and GDC, and looked at the outputs with the nm tool. I don't think this matters much but DMD and LDC are more likely to list functions (DMD) or symbols in referenced modules (LDC) as weak symbols. I think the more likely the reason GDC worked is because it is a GNU compiler this has full GLIBC compatibility with reference to the `librt.so.1` `GLIBC_PRIVATE` error. The R ext documentation you kindly referenced says: *"By default, R uses the operating-system-specific dynamic loader to lookup the symbol in all loaded DLLs and the R executable or libraries it is linked to."* and that: *"Registering routines has two main advantages: it provides a faster way to find the address of the entry point via tables stored in the DLL at compilation time, and it provides a run-time check that the entry point is called with the right number of arguments and, optionally, the right argument types."* So registering symbols is not the issue. The error was specific to GLIBC which is probably why the GNU compiler worked. Thank you.
Jun 05 2021