Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MSVC compatibility #963

Open
ed10vi opened this issue Oct 17, 2023 · 11 comments
Open

MSVC compatibility #963

ed10vi opened this issue Oct 17, 2023 · 11 comments

Comments

@ed10vi
Copy link

ed10vi commented Oct 17, 2023

Would be nice to be able to import GTK libs built with gvsbuild.

Would it be possible modifying Resolve in ImportResover with something like this?

if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
    if (!NativeLibrary.TryLoad(osDependentLibraryName, assembly, searchPath, out TargetLibraryPointer))
        TargetLibraryPointer = NativeLibrary.Load(osDependentLibraryName.Substring(3), assembly, searchPath);
}
else
{
    TargetLibraryPointer = NativeLibrary.Load(osDependentLibraryName, assembly, searchPath);
}

Or

if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows) || !NativeLibrary.TryLoad(osDependentLibraryName.Substring(3), assembly, searchPath, out TargetLibraryPointer))
    TargetLibraryPointer = NativeLibrary.Load(osDependentLibraryName, assembly, searchPath);
@badcel
Copy link
Member

badcel commented Oct 17, 2023

Hi,

I think I would not like to add some code which "tries" to find some binary based on some heuristic like Substring(3). If a user want's to use a different binary for some reason this should be some additionaly API call to make it easier to understand why some binary is used which is not explicitly defined in the gir file. So I think we could add some API to allow a user to define their own DllImportResolver. E.g. for Gio something like:

using System;
using System.Runtime.InteropServices;

namespace Gio;

public static class Module
{
    private static bool IsInitialized;
    private static DllImportResolver? CustomDllImportResolver;

    /// <summary>
    /// Initialize the <c>Gio</c> module.
    /// </summary>
    /// <remarks>
    /// <para>
    /// Calling this method is necessary to correctly initialize the bindings
    /// and should be done before using anything else in the <see cref="Gio" />
    /// namespace.
    /// </para>
    /// <para>
    /// Calling this method will also initialize the modules this module
    /// depends on:
    /// </para>
    /// <list type="table">
    /// <item><description><see cref="GObject.Module" /></description></item>
    /// </list>
    /// </remarks>
    public static void Initialize()
    {
        if (IsInitialized)
            return;

        GObject.Module.Initialize();

        NativeLibrary.SetDllImportResolver(typeof(Module).Assembly, CustomDllImportResolver ?? Internal.ImportResolver.Resolve);
        Internal.TypeRegistration.RegisterTypes();

        IsInitialized = true;
    }

    /// <summary>
    /// Set a custom DllImportResolver. This disables the automatic loading of native binaries for
    /// Gio. If the given DllImportResolver receives the library name "Gio" it has to return a pointer
    /// to the desired native Gio binary.
    /// </summary>
    /// <remarks>This method must be called before any application is created and Module.Initialize is called.</remarks>
    /// <param name="customDllImportResolver">Custom DllImportResolver to use.</param>
    public static void SetCustomDllImportResolver(DllImportResolver customDllImportResolver)
    {
        if (IsInitialized)
            throw new Exception("Can't set a custom DllImportResolver after initialization is done");

        CustomDllImportResolver = customDllImportResolver;
    }
}

As there is a Module.cs for every project this would need to be added to every project. In case we are going to touch this code I think I would like to remove the method Internal.ImportResolver.RegisterAsDllImportResolver and include the code in each Module.cs like shown above.

May I ask where you get your binaries from and why you use them over the MSYS2 ones?

@cameronwhite do you have some thoughts on this?

@cameronwhite
Copy link
Contributor

Yeah I think we probably need some more info on what the naming conventions are for these libraries. Maybe the substring is being used to a strip a "lib" prefix or something like that?

@ed10vi
Copy link
Author

ed10vi commented Oct 18, 2023

May I ask where you get your binaries from and why you use them over the MSYS2 ones?

I'm building them with gvsbuild just because is the recommended by gtk4-rs wich I'm already using

Yeah I think we probably need some more info on what the naming conventions are for these libraries. Maybe the substring is being used to a strip a "lib" prefix or something like that?

Meson doc says about name_prefix:

The string that will be used as the prefix for the target output filename by overriding the default (only used for libraries). By default this is lib on all platforms and compilers, except for MSVC shared libraries where it is omitted to follow convention, and Cygwin shared libraries where it is cyg.

@badcel
Copy link
Member

badcel commented Oct 18, 2023

Could you give the concrete name of the created GTK binary / DLL?

If you already build GTK yourself you probably build its dependencies, too?

In this case you could consider building your own set of GirCore binaries which match exactly your self compiled binaries.

The instructions are in the readme. You would probably only need to update the gir files to the ones created by your build.

The correct binary name would then be taken automatically from your own gir file.

@ed10vi
Copy link
Author

ed10vi commented Oct 18, 2023

Could you give the concrete name of the created GTK binary / DLL?

This is the output of ls -R in the build output directory

Filtering dlls:

  • adwaita-1-0.dll
  • asprintf.dll
  • cairo-2.dll
  • cairo-gobject-2.dll
  • cairo-script-interpreter-2.dll
  • epoxy-0.dll
  • ffi-8.dll
  • fontconfig-1.dll
  • freetype-6.dll
  • fribidi-0.dll
  • gdk_pixbuf-2.0-0.dll
  • gettextlib-0.21.0.dll
  • gettextpo.dll
  • gettextsrc-0.21.0.dll
  • gio-2.0-0.dll
  • glib-2.0-0.dll
  • gmodule-2.0-0.dll
  • gobject-2.0-0.dll
  • graphene-1.0-0.dll
  • gthread-2.0-0.dll
  • gtk-4-1.dll
  • harfbuzz-cairo.dll
  • harfbuzz.dll
  • harfbuzz-gobject.dll
  • harfbuzz-subset.dll
  • iconv.dll
  • intl.dll
  • jpeg62.dll
  • libexpat.dll
  • libpng16.dll
  • libxml2.dll
  • pango-1.0-0.dll
  • pangocairo-1.0-0.dll
  • pangoft2-1.0-0.dll
  • pangowin32-1.0-0.dll
  • pcre2-16.dll
  • pcre2-32.dll
  • pcre2-8.dll
  • pcre2-posix.dll
  • pixman-1-0.dll
  • pkgconf-4.dll
  • rsvg-2.0-vs17.dll
  • textstyle.dll
  • tiff.dll
  • turbojpeg.dll
  • zlib1.dll

In this case you could consider building your own set of GirCore binaries which match exactly your self compiled binaries.

I'll give it a try

@cameronwhite
Copy link
Contributor

I think we should try to support this if it's not too much effort, that way the default Nuget packages are usable with either the MSVC or GNU (msys) toolchain rather than requiring MSYS

GtkSharp seems like it had some handling for this: https://github.com/GtkSharp/GtkSharp/blob/develop/Source/Libs/Shared/GLibrary.cs#L23

@badcel
Copy link
Member

badcel commented Oct 18, 2023

I don't want to hardcode some library names like in GTK sharp.

I'm fine with adding some hook to allow to exchange the library loading like shown above. But using this method would mean leaving any supported terrain as it means hooking the bindings up to some binary nobody knows which version it is or how it got compiled and so on. I think it is a bad idea to allow this to happen automatically.

If using msvc means building GTK manually I would even strongly recommend building GirCore manually, too, to keep both in sync. This allows to benefit from GirCore improvements without the need to update to newer GTK versions.

@ed10vi
Copy link
Author

ed10vi commented Oct 19, 2023

In this case you could consider building your own set of GirCore binaries which match exactly your self compiled binaries.

My build wasn't generating gir files so I modified them manually and all works.

The problem now is that it's not multiplatform

@badcel
Copy link
Member

badcel commented Oct 19, 2023

Yeah the distribution of software is a bit tricky. Currently there is no support from GirCore as no C binaries are distributed.

I'm not even sure of the current distribution model is going to stay or if there will be platform dependent nugets some day as the sources of the different platforms move at different speeds.

It gets even more complicated if you mix custom builds in the mix.

If you are planing to create some enterprise application please be aware that this is not recommended currently due to the early state of the code. Meaning there are currently no api stability guarantees.

If you don't plan to distribute your application as a flatpak you can even have different library versions depending on your operating system. In this case it could be better to create custom GirCore binaries for your distribution. The distribution usually ship the Gir files in some package.

Otherwise you risk using some binaries which are older than the ones the bindings were build for. If you use some unavailable api the program will crash at runtime. So to have a stable program it makes sense to have some set of APIs you know will be there:

  • GirCore targets gnome flatpak sdk, msys2 and brew. The last two unfortunately move faster than the gnome sdk so we hold updates back. As long as there is no major version bump the APIs should be backwards compatible and it should just work.
  • If you can ensure that your windows / Linux / Mac versions are more or less identical (different patch level) you should be able to safely exchange the Gir files with the ones from your platform and create one set of multi platform binaries.
  • If you can't ensure this, the "safest" way is probably to have different bindings per platform. This would require to tell the build system to create different application binaries depending on the operating system they are build for. The big benefit is that you get compiler errors if you use APIs which are not available.

Which platforms do you want to support? How do you do this using the rust bindings? Because there you would have the same problem. The bindings need to match the available versions of GTK, GObject, etc which can vary widely depending on your environment.

@ed10vi
Copy link
Author

ed10vi commented Oct 20, 2023

Most apps I do are for internal use, and we have a very limited OS versions.

When I use gtk-rs I build a different version for windows, and ship the gtklibs with it. Probably the difference here is that is the linker at buildtime who finds them, not at runtime.

Edit: Sorry. It really is multiplatform since Linux girs are untouched. It's working perfectly

Also had to modify src/Libs/cairo-1.0/Internal/CairoImportResolver.cs

@badcel
Copy link
Member

badcel commented Oct 20, 2023

Yep.

The nuget packages create the illusion that it should "just work". But they are build for certain versions of the libraries and this must be kept in mind.

I hope that we could help solving your immediate problem / question?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants