dlemstra / Magick.NET

The .NET library for ImageMagick
Apache License 2.0
3.48k stars 415 forks source link

Azure function System.DllNotFoundException on linux #1567

Open PetterKnudsen98 opened 9 months ago

PetterKnudsen98 commented 9 months ago

Magick.NET version

Magick.NET-Q16-x64

Environment (Operating system, version and so on)

Linux - Ubuntu 22.04.4

Description

Running .net 6 on an azure function v4 I get the following error message

System.DllNotFoundException: Unable to load shared library 'Magick.Native-Q16-x64.dll' or one of its dependencies. In order to help diagnose loading problems, consider setting the LD_DEBUG environment variable: libMagick.Native-Q16-x64.dll: cannot open shared object file: No such file or directory at ImageMagick.Environment.NativeMethods.X64.Environment_Initialize() at ImageMagick.Environment.NativeEnvironment.Initialize() in /_/src/Magick.NET/Native/Helpers/Environment.cs:line 65 at ImageMagick.Environment.Initialize() in /_/src/Magick.NET/Helpers/Environment.cs:line 21 at ImageMagick.MagickSettings.NativeMagickSettings..cctor() in /_/src/Magick.NET/Native/Settings/MagickSettings.cs:line 315

when executing the following code: using var image = new MagickImage(imageStream);

My .csproj looks like this:


 <PropertyGroup>
        <TargetFramework>net6.0</TargetFramework>
        <AzureFunctionsVersion>v4</AzureFunctionsVersion>
        <MagickCopyNativeLinux>true</MagickCopyNativeLinux>
    </PropertyGroup>

    <ItemGroup>
        ...
        <PackageReference Include="Magick.NET-Q16-x64" Version="13.6.0" />
        ...
    </ItemGroup>

I have seen other suggest that <MagickCopyNativeLinux>true</MagickCopyNativeLinux> fixes the issue, but it doesnt seem to work for me. Is this an azure function issue, or am I just doing something wrong?

It works fine on windows, and I'm not very experience with linux.

Steps to Reproduce

  1. Run on a linux machine, only tested with ubuntu and in azure (it doesnt specify linux version)
  2. Run azure function
  3. Execute code that uses the library
dlemstra commented 9 months ago

How did you test it on Linux? Do you see the Magick.Native-Q16-x64.dll file in your bin directory?

The MagickCopyNativeLinux option does not work for net6.0 because it should be necessary there.

PetterKnudsen98 commented 9 months ago

Its not in the bin directory IIRC. Tried to use SkiaSharp as well, same issue there. Tested with an empty project, worked fine. As soon as it was an azure function it failed. To resolve it with SkiaSharp we added

<PropertyGroup>    
  <RuntimeIdentifier>linux-x64</RuntimeIdentifier>
</PropertyGroup>

<Target Name="CopySkiaLib" AfterTargets="Build">
  <ItemGroup>
    <SkiaSharpLibraryFiles Include="$(TargetDir)/libSkiaSharp.so" />
  </ItemGroup>
  <Copy SourceFiles="@(SkiaSharpLibraryFiles)" DestinationFolder="$(TargetDir)/bin" />
</Target>

I have been meaning to check if a similar workaround will work with magick, but have been a bit busy.

dlemstra commented 9 months ago

I wonder if I could make this more convenient for you by also adding a .targets file for other the netstandard21 build.

PetterKnudsen98 commented 9 months ago

Ok, so I got around to test it.

The following worked for me locally, I have not tried it in azure, but I'm assuming it works since it did for SkiaSharp, so it should for this as well. What I did was add <RuntimeIdentifier>linux-x64</RuntimeIdentifier> as well as the target "CopyMagick".

This should copy the .so file into the bin folder so that the code finds it. RuntimeIdentifier can be changed to <RuntimeIdentifiers>linux-x64;win-x64 (etc) </RuntimeIdentifiers> if multiple runtimes are used, E.G windows in local dev but linux in cloud.

    <PropertyGroup>
        <TargetFramework>net6.0</TargetFramework>
        <AzureFunctionsVersion>v4</AzureFunctionsVersion>
        <_FunctionsSkipCleanOutput>true</_FunctionsSkipCleanOutput>
        <RuntimeIdentifier>linux-x64</RuntimeIdentifier> \
    </PropertyGroup>
    <Target Name="CopyMagick" AfterTargets="Build">
        <ItemGroup>
            <MagickLibraryFiles Include="$(TargetDir)/Magick.Native-Q16-x64.dll.so" />
        </ItemGroup>
        <Copy SourceFiles="@(MagickLibraryFiles)" DestinationFolder="$(TargetDir)/bin" />
    </Target>
dlemstra commented 8 months ago

I just created an function app to test if I can reproduce this. And I am unable to reproduce your issue. Would it be possible to share a trimmed down project that I can use to reproduce your issue? I did this in my test function: response.WriteString(MagickNET.Version);.

PetterKnudsen98 commented 8 months ago

This repo should reproduce it, at least it does for me.

image

dlemstra commented 8 months ago

Does it only fail in a unit test or also when running on Azure?

PetterKnudsen98 commented 8 months ago

It fails locally and in azure, I only named the function "Test", its not an actual unit test (my bad, bad naming).

dlemstra commented 8 months ago

It works on my machine and in Azure? afbeelding Maybe this is related to your IDE, I am using Visual Studio.

PetterKnudsen98 commented 8 months ago

What OS are you testing on? I though Visual Studio wasn't available on Linux? I'm using JetBrains Rider. I tested the code above on an ubuntu VM, heres the full exception.

{
    "ClassName": "System.TypeInitializationException",
    "Message": "The type initializer for 'NativeMagickNET' threw an exception.",
    "Data": null,
    "InnerException": {
        "ClassName": "System.DllNotFoundException",
        "Message": "Unable to load shared library 'Magick.Native-Q16-x64.dll' or one of its dependencies. In order to help diagnose loading problems, consider setting the LD_DEBUG environment variable: libMagick.Native-Q16-x64.dll: cannot open shared object file: No such file or directory",
        "Data": null,
        "InnerException": null,
        "HelpURL": null,
        "StackTraceString": "   at ImageMagick.Environment.NativeMethods.X64.Environment_Initialize()\n   at ImageMagick.Environment.NativeEnvironment.Initialize() in /_/src/Magick.NET/Native/Helpers/Environment.cs:line 65\n   at ImageMagick.Environment.Initialize() in /_/src/Magick.NET/Helpers/Environment.cs:line 21\n   at ImageMagick.MagickNET.NativeMagickNET..cctor() in /_/src/Magick.NET/Native/Magick.cs:line 103",
        "RemoteStackTraceString": null,
        "RemoteStackIndex": 0,
        "ExceptionMethod": null,
        "HResult": -2146233052,
        "Source": "Magick.NET-Q16-AnyCPU",
        "WatsonBuckets": null,
        "TypeLoadClassName": null,
        "TypeLoadAssemblyName": null,
        "TypeLoadMessageArg": null,
        "TypeLoadResourceID": 0
    },
    "HelpURL": null,
    "StackTraceString": "   at ImageMagick.MagickNET.NativeMagickNET.get_ImageMagickVersion() in /_/src/Magick.NET/Native/Magick.cs:line 162\n   at ImageMagick.MagickNET.get_ImageMagickVersion() in /_/src/Magick.NET/MagickNET.cs:line 150\n   at Company.FunctionAppTest.TestFunction.Test(HttpRequest req) in /home/petter/RiderProjects/magickTest/Company.FunctionAppTest/TestFunction.cs:line 21",
    "RemoteStackTraceString": null,
    "RemoteStackIndex": 0,
    "ExceptionMethod": null,
    "HResult": -2146233036,
    "Source": "Magick.NET-Q16-AnyCPU",
    "WatsonBuckets": null,
    "TypeName": "NativeMagickNET"
}
dlemstra commented 8 months ago

I am on Windows and publishing the application through Visual Studio on Azure. I suspect that Rider packages it differently. Not sure when I have time for this but I will setup VM and use rider there and check what they do. What you could do is go to your nuget cache and copy the .targets file from the netstandard20 folder to netstandard21 folder. (you need to create it) And then set the copy linux variable again. Does that work?

dlemstra commented 6 months ago

Did my "patch" work for you @PetterKnudsen98?