MaxRev-Dev / gdal.netcore

GDAL 3.x C#/F# bindings for .NET apps
MIT License
161 stars 36 forks source link

How to use library when packaging into a single file exe? #130

Closed mht2953658596 closed 6 months ago

mht2953658596 commented 6 months ago

Describe the bug I have successfully used MaxRev.Gdal.Core in my console application, and when I run it using 'dotnet run', the code runs smoothly.

"I want to mention that I attempted to package the files into a single executable, a standalone exe file. I used the command dotnet publish --configuration Release -r win-x64 -p:PublishSingleFile=true -:IncludeNativeLibrariesForSelfExtract=true -p:CopyOutputSymbolsToPublishDirectory=false --self-contained. It generated an exe file in my '\bin\Release\net8.0\win-x64\publish' directory. However, when I double-clicked to run it, I encountered the following error."

"D:\test\data\fvc_20190801_xlht.tif"
Unhandled exception. System.TypeInitializationException: The type initializer for 'GisTools.TiffTools' threw an exception.
 ---> System.IO.FileNotFoundException: Can not find proj.db. Tried to search in D:\code_space\CsharpProjects\ConsoleApp\MyConsoleApp\bin\Release\net8.0\win-x64\publish\runtimes\win-x64\native\maxrev.gdal.core.libshared, D:\code_space\CsharpProjects\ConsoleApp\MyConsoleApp\bin\Release\net8.0\win-x64\publish\maxrev.gdal.core.libshared, D:\code_space\CsharpProjects\ConsoleApp\MyConsoleApp\bin\Release\net8.0\win-x64\runtimes\win-x64\native\maxrev.gdal.core.libshared, D:\code_space\CsharpProjects\ConsoleApp\MyConsoleApp\bin\Release\net8.0\win-x64\publish, D:\code_space\CsharpProjects\ConsoleApp\MyConsoleApp\bin\Release\net8.0\win-x64\publish\runtimes\win-x64\native
   at MaxRev.Gdal.Core.Proj.Configure(String[] )
   at GisTools.TiffTools..cctor()
   --- End of inner exception stack trace ---
   at GisTools.TiffTools.GetTiffInfo(String)
   at MyConsoleApp.Program.Main(String[]) in D:\code_space\CsharpProjects\ConsoleApp\MyConsoleApp\Program.cs:line 30

Expected behavior There is the code which using MaxRev.Gdal.Core

public static Dictionary<string, object> GetTiffInfo(string tiffPath)
{
    using (var ds = Gdal.Open(tiffPath, Access.GA_ReadOnly))
    {
        if (ds == null)
        {
            throw new FileNotFoundException("File not found", tiffPath);
        }

        double[] adfGeoTransform = new double[6];
        ds.GetGeoTransform(adfGeoTransform);

        var info = new Dictionary<string, object>
        {
            { "Filename", tiffPath },
            { "Width", ds.RasterXSize },
            { "Height", ds.RasterYSize },
            { "BandCount", ds.RasterCount },
            { "CoordinateSystem", ds.GetProjectionRef() },
            { "UpperLeftCorner", new double[] { adfGeoTransform[0], adfGeoTransform[3] } },
            { "PixelWidth", adfGeoTransform[1] },
            { "PixelHeight", adfGeoTransform[5] }
        };

        return info;
    }
}

Environment information:

  • OS (version): [ Windows 11]
  • Package version (core): 3.8.3.286]

Additional context

None
mht2953658596 commented 6 months ago

Translation:Then I adjusted my .csproj file.

MyConsoleApp.csproj

<Project Sdk="Microsoft.NET.Sdk">

  <ItemGroup>
    <ProjectReference Include="..\GisTools\GisTools.csproj" />
  </ItemGroup>

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <PublishTrimmed>true</PublishTrimmed>
    <SelfContained>true</SelfContained>
    <PublishReadyToRun>true</PublishReadyToRun>
    <PublishReadyToRunShowWarnings>true</PublishReadyToRunShowWarnings>
    <PublishReadyToRunShowWarnings>true</PublishReadyToRunShowWarnings>
  </PropertyGroup>

</Project>

GisTools.csproj


<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Library</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="MaxRev.Gdal.Core" Version="3.8.3.286" />
    <PackageReference Include="MaxRev.Gdal.LinuxRuntime.Minimal" Version="3.8.3.286" />
    <PackageReference Include="MaxRev.Gdal.WindowsRuntime.Minimal" Version="3.8.3.259" />
    <PackageReference Include="NumSharp" Version="0.30.0" />
  </ItemGroup>

</Project>

i used this command dotnet publish --configuration Release -r win-x64 -p:IncludeNativeLibrariesForSelfExtract=true -p:CopyOutputSymbolsToPublishDirectory=false --self-contained

I noticed that not only MyConsoleApp.exe was generated in the \bin\Release\net8.0\win-x64\publish directory, but also many other files. I guess these files are dependent on GDAL. If I move this exe file alone, running it will report a dependency missing error. Is there a way to generate only one exe single file with all dependencies in this exe file? Then running this exe file in the \bin\Release\net8.0\win-x64\publish directory can run normally. Is there a way to generate only a single executable file, give this file to others, and others can directly use it, instead of giving a directory, which is quite cumbersome.

MaxRev-Dev commented 6 months ago

I haven't tried to build a single file, but from my experience with dynamic libraries and the search paths - this library won't work in single file builds. I mean, you need to ship everything that you have in the publish folder.

If you want to build and distribute a single file, consider using the official package. It will still require the GDAL installation to be present on the host system.

I suggest using a normal publishing approach - where all the DLLs are published in a single directory. Then you can create an archive and distribute it.