cwi-dis / cwipc_unity

Unity package for cwipc pointcloud libraries
0 stars 0 forks source link

Fix Silicon Mac dynamic library loading. #23

Closed jackjansen closed 11 months ago

jackjansen commented 11 months ago

We need to fix things on the M1 Macs, both for development (running from the editor) and for eventual distribution.

Part of the underlying problem is that /opt/homebrew/lib isn't in the dylib search path.

A previous attempt to fix this by putting symlinks into Runtime/Plugins/ unfortunately doesn't work: they break using cwipc_unity on Windows, because the symlinks will point to files that don't exist, and Unity gets very upset about that.

My current thinking is that we try to load the dynamic libraries from two places (with the [DllImport()] attribute:

It remains to be seen whether we can dynamically change the DllImport() parameter, I think I looked at it in the past and it is not possible. Maybe we can do something with virtual const string myDllName if that is allowed, otherwise maybe we can use some template trick. If all else fails we duplicate the code.

Assigning to @troeggla for comments, please assign back.

jackjansen commented 11 months ago

Oh yes, something that I tried in the past (and failed, at that time) was to use a #if directive, this works fine for having code that is only enabled on Mac, or Windows, or in the Editor, or whatever.

But it seems there is no predefined symbol that distinguishes between Intel Mac (or M1 in Rosetta Unity) and native M1 Mac.

jackjansen commented 11 months ago

Various other ideas that might help:

SetDllDirectory() could be used to add /opt/homebrew/lib to the search path. See https://learn.microsoft.com/en-gb/windows/win32/api/winbase/nf-winbase-setdlldirectorya?redirectedfrom=MSDN

It is possible to create your own DLL resolver, but this looks pretty complex: https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.nativelibrary.setdllimportresolver?view=net-7.0

jackjansen commented 11 months ago

Oh yes: if a dynamic library with the name given in the DllImport attribute is already loaded into the process then that instance is used.

So yet another option is to first load the correct dynamic library (using some .net method) and only then accessing the methods.

jackjansen commented 11 months ago

I have a gross hack that I think works.

On Mac we set the dllName to __Internal, which means that the symbols are looked up from the global namespace (i.e. in any module that has already been loaded).

And before we access any symbol from (for example) cwipc_util we semi-manually load the correct dynamic library.

Semi-manually, because Mono doesn't export the dynamic library loaders that .NET has.

So we have two classes, one which uses cwipc_util as the dynamic library name and one which has /opt/homebrew/lib/libcwipc_util.dylib. We try the first one first, and if that fails we try the second.