Add more platform support to CSFML NuGet
As stated in issue #149 from @DemoXinMC
Additionally, we need to look at ways to provide CI and automated building of the CSFML NuGet package to cover more platforms (Mac OS is the glaring omission) as well as avoid having to manually package CSFML updates.
What is missing from the current NuGet package (in order of most important from my point of view):
- MacOS builds
- ARM and ARM64 build for linux and windows
- linux-x64 rid references (need to check if it's possible)
MacOS
The missing MacOS target in the CSFML NuGet package is currently causing problems for SFML.NET on mac, because usually when you build your project the dependencies of the project are resolved and the correct binaries for the platform are extracted to the build directory allowing for a nice and tight development loop. But because of the omission of the MacOS binaries all user that build for MacOS need to download the CSFML MacOS binaries from the SFML website and then copy them to the build directory each time they clean build. This is a subpar experience compare for the linux and windows user experience.
ARM and ARM64 build for Linux and Windows
This would enable users to create projects for Single Board Computer (Like Raspberry Pi) with SFML.NET and also we could explore running on Chromebooks since they can now install linux app. Same thing for Windows because Microsoft is starting to push the Windows on ARM again with their new Surface Lineup (Surface Pro X). And it seems that ARM is getting more and more popular for non-smartphone usage and there's even rumors of the next mac running on arm... So I think it's worth starting to support ARM as a first citizen for SFML (CSFML)
linux-x64
This one I'm not sure how feasible it is, can we support the linux-x64 RID in the NuGet package? this would mean that any linux x64 platform can work with the package even if they are not based on :
- alpine-x64
- debian-x64
- fedora-x64
This would mean we can generate a set of "universal" linux x64 shared objects for CSFML (which might not be possible?)
CI and automated building of the CSFML NuGet package
We could check for using GitHub Actions to automatically CI and Build the CSFML NuGet Package (and maybe even do an automatic package release?). I've personally checked/started working on a workflow file, but I've hit some build problems.
So, the big issue to be discussed here is CI and packaging of NuGet builds.
Travis CI is a great, free-to-open-source build system that offers support for Windows, Mac OS, and Linux builds. The large drawback to Travis CI is that it's really intended as a test environment, and therefore does NOT store build artifacts after job completion. This leaves us with the issue of how to aggregate builds from separate jobs (each OS we're building on is a separate job). My current thought is to create a GitHub Pages repo that Travis can upload builds to, with a Travis connection itself, triggering a "build" (packaging a NuGet package and uploading it) when new builds of CSFML are pushed by CSFML's Travis.
Regarding ARM/ARM64, I'm unsure what the P/Invoke system looks like for ARM and ARM64. I'm inclined to say that they are the same as normal P/Invoking, but I do know that Android and iOS both require some changes in order to use P/Invoke on those platforms. Ideally, I'd really like to get SFML.NET able to target these platforms as well, though I think that both are in the "experimental" phase for SFML itself. Do we have working ARM/ARM64 SFML? Cross-compilation for Android/iOS could easily be included in the Mac OS/*nix jobs and those artifacts uploaded as well.
I think it would then be really interesting to explore Github Actions considering the shortcomings of Travis CI. Github Actions are also free-to-open-source, with Windows, Mac OS and Linux. It can be use for both a test environment and build/release because it allows to upload and aggregate build artifacts, and even support publishing NuGets.
Regarding the ARM stuff for the P/Invoke that should be handled by DotNet. And for the state of SFML on ARM maybe @eXpl0it3r has a better overview of the whole SFML project to say how's the support (is it experimental, is it supported?)
Currently on macOS, if we install CSFML with brew and run a downstream dotnet application that depends on SFML.NET (managed-doom in my case), we get:
$ DYLD_PRINT_LIBRARIES=1 LD_LIBRARY_PATH=/usr/local/Cellar/csfml/2.5/lib dotnet run -p ManagedDoom
/snip
dyld: loaded: /usr/local/Cellar/csfml/2.5/lib/libcsfml-window.dylib
dyld: loaded: /usr/local/opt/sfml/lib/libsfml-window.2.5.dylib
dyld: loaded: /usr/local/opt/sfml/lib/libsfml-system.2.5.dylib
dyld: unloaded: /usr/local/Cellar/csfml/2.5/lib/libcsfml-window.dylib
dyld: unloaded: /usr/local/opt/sfml/lib/libsfml-window.2.5.dylib
dyld: unloaded: /usr/local/opt/sfml/lib/libsfml-system.2.5.dylib
System.DllNotFoundException: Unable to load shared library 'csfml-window' or one of its dependencies. In order to help diagnose loading problems, consider setting the DYLD_PRINT_LIBRARIES environment variable: dlopen(libcsfml-window, 1): image not found
at SFML.Window.VideoMode.sfVideoMode_getDesktopMode()
at SFML.Window.VideoMode.get_DesktopMode()
at ManagedDoom.ConfigUtilities.GetDefaultVideoMode() in /Users/am11/projects/managed-doom/ManagedDoom/src/ConfigUtilities.cs:line 40
at ManagedDoom.Config..ctor() in /Users/am11/projects/managed-doom/ManagedDoom/src/Config.cs:line 127
at ManagedDoom.Config..ctor(String path) in /Users/am11/projects/managed-doom/ManagedDoom/src/Config.cs:line 141
at ManagedDoom.DoomApplication..ctor(CommandLineArgs args) in /Users/am11/projects/managed-doom/ManagedDoom/src/DoomApplication.cs:line 68
at ManagedDoom.Program.Main(String[] args) in /Users/am11/projects/managed-doom/ManagedDoom/src/Program.cs:line 35
it seems like a case of missing RPATH in CSFML (also reported here https://stackoverflow.com/q/54156107/863980), which will fix the library loading. There is a workaround available to patch up the rpath using install_name_tool utility. It would be nice if that gets fixed in this repo so the downstream (brew formula) folks won't need to patch up.
Note this install dependency with package manager and run dotnet app workflow is considered reasonable for Unix-likes (also encouraged from Linux distros official package maintainers) in .NET community, instead of bundling our own separate builds of those binaries inside the nuget packages.
e.g. due to the same reasons (respecting the package maintainers' build configuration / finetune choices for the OS), why dotnet team does not (force) bundle their own build of libgdiplus with drawing library. Today, we are supposed to brew install mono-libgdiplus on macOS before using BCL's System.Drawing on macOS with .NET Core 3x (and upcoming .NET 5). Same guideline is for Linux distros (use native package manager to install these dependencies).
I tried a lot of things for macOS in #158, but in the end couldn't get the RPATH issue resolved. I hope with the links from #161 and some additional knowledge I might look into this some more.
With #155 I've added GitHub Action support, but only generic building. We could have a second YAML file for NuGet building. With Docker and the different base images, we can easily build for different distros, not sure whether this is possible using GitHub Action as well.
Not sure if I did that correctly but the problem persists on MacOS
Error:
System.DllNotFoundException: Unable to load shared library 'csfml-system' or one of its dependencies. In order to help diagnose loading problems, consider setting the DYLD_PRINT_LIBRARIES environment variable:
dlopen(/usr/local/share/dotnet/shared/Microsoft.NETCore.App/7.0.0/csfml-system.dylib, 0x0001): tried: '/usr/local/share/dotnet/shared/Microsoft.NETCore.App/7.0.0/csfml-system.dylib' (no such file), '/System/Volumes/Preboot/Cryptexes/OS/usr/local/share/dotnet/shared/Microsoft.NETCore.App/7.0.0/csfml-system.dylib' (no such file), '/usr/local/share/dotnet/shared/Microsoft.NETCore.App/7.0.0/csfml-system.dylib' (no such file)
dlopen(/Users/daniel/RiderProjects/KeyOverlay/KeyOverlay/bin/Debug/net7.0/csfml-system.dylib, 0x0001): tried: '/Users/daniel/RiderProjects/KeyOverlay/KeyOverlay/bin/Debug/net7.0/csfml-system.dylib' (mach-o file, but is an incompatible architecture (have 'x86_64', need 'arm64')), '/System/Volumes/Preboot/Cryptexes/OS/Users/daniel/RiderProjects/KeyOverlay/KeyOverlay/bin/Debug/net7.0/csfml-system.dylib' (no such file), '/Users/daniel/RiderProjects/KeyOverlay/KeyOverlay/bin/Debug/net7.0/csfml-system.dylib' (mach-o file, but is an incompatible architecture (have 'x86_64', need 'arm64'))
dlopen(csfml-system.dylib, 0x0001): tried: 'csfml-system.dylib' (mach-o file, but is an incompatible architecture (have 'x86_64', need 'arm64')), '/System/Volumes/Preboot/Cryptexes/OScsfml-system.dylib' (no such file), '/usr/lib/csfml-system.dylib' (no such file, not in dyld cache), 'csfml-system.dylib' (mach-o file, but is an incompatible architecture (have 'x86_64', need 'arm64')), '/usr/local/lib/csfml-system.dylib' (no such file), '/usr/lib/csfml-system.dylib' (no such file, not in dyld cache)
dlopen(/usr/local/share/dotnet/shared/Microsoft.NETCore.App/7.0.0/libcsfml-system.dylib, 0x0001): tried: '/usr/local/share/dotnet/shared/Microsoft.NETCore.App/7.0.0/libcsfml-system.dylib' (no such file), '/System/Volumes/Preboot/Cryptexes/OS/usr/local/share/dotnet/shared/Microsoft.NETCore.App/7.0.0/libcsfml-system.dylib' (no such file), '/usr/local/share/dotnet/shared/Microsoft.NETCore.App/7.0.0/libcsfml-system.dylib' (no such file)
dlopen(/Users/daniel/RiderProjects/KeyOverlay/KeyOverlay/bin/Debug/net7.0/libcsfml-system.dylib, 0x0001): tried: '/Users/daniel/RiderProjects/KeyOverlay/KeyOverlay/bin/Debug/net7.0/libcsfml-system.dylib' (no such file), '/System/Volumes/Preboot/Cryptexes/OS/Users/daniel/RiderProjects/KeyOverlay/KeyOverlay/bin/Debug/net7.0/libcsfml-system.dylib' (no such file), '/Users/daniel/RiderProjects/KeyOverlay/KeyOverlay/bin/Debug/net7.0/libcsfml-system.dylib' (no such file)
dlopen(libcsfml-system.dylib, 0x0001): tried: 'libcsfml-system.dylib' (no such file), '/System/Volumes/Preboot/Cryptexes/OSlibcsfml-system.dylib' (no such file), '/usr/lib/libcsfml-system.dylib' (no such file, not in dyld cache), 'libcsfml-system.dylib' (no such file), '/usr/local/lib/libcsfml-system.dylib' (no such file), '/usr/lib/libcsfml-system.dylib' (no such file, not in dyld cache)
dlopen(/usr/local/share/dotnet/shared/Microsoft.NETCore.App/7.0.0/csfml-system, 0x0001): tried: '/usr/local/share/dotnet/shared/Microsoft.NETCore.App/7.0.0/csfml-system' (no such file), '/System/Volumes/Preboot/Cryptexes/OS/usr/local/share/dotnet/shared/Microsoft.NETCore.App/7.0.0/csfml-system' (no such file), '/usr/local/share/dotnet/shared/Microsoft.NETCore.App/7.0.0/csfml-system' (no such file)
dlopen(/Users/daniel/RiderProjects/KeyOverlay/KeyOverlay/bin/Debug/net7.0/csfml-system, 0x0001): tried: '/Users/daniel/RiderProjects/KeyOverlay/KeyOverlay/bin/Debug/net7.0/csfml-system' (no such file), '/System/Volumes/Preboot/Cryptexes/OS/Users/daniel/RiderProjects/KeyOverlay/KeyOverlay/bin/Debug/net7.0/csfml-system' (no such file), '/Users/daniel/RiderProjects/KeyOverlay/KeyOverlay/bin/Debug/net7.0/csfml-system' (no such file)
dlopen(csfml-system, 0x0001): tried: 'csfml-system' (no such file), '/System/Volumes/Preboot/Cryptexes/OScsfml-system' (no such file), '/usr/lib/csfml-system' (no such file, not in dyld cache), 'csfml-system' (no such file), '/usr/local/lib/csfml-system' (no such file), '/usr/lib/csfml-system' (no such file, not in dyld cache)
dlopen(/usr/local/share/dotnet/shared/Microsoft.NETCore.App/7.0.0/libcsfml-system, 0x0001): tried: '/usr/local/share/dotnet/shared/Microsoft.NETCore.App/7.0.0/libcsfml-system' (no such file), '/System/Volumes/Preboot/Cryptexes/OS/usr/local/share/dotnet/shared/Microsoft.NETCore.App/7.0.0/libcsfml-system' (no such file), '/usr/local/share/dotnet/shared/Microsoft.NETCore.App/7.0.0/libcsfml-system' (no such file)
dlopen(/Users/daniel/RiderProjects/KeyOverlay/KeyOverlay/bin/Debug/net7.0/libcsfml-system, 0x0001): tried: '/Users/daniel/RiderProjects/KeyOverlay/KeyOverlay/bin/Debug/net7.0/libcsfml-system' (no such file), '/System/Volumes/Preboot/Cryptexes/OS/Users/daniel/RiderProjects/KeyOverlay/KeyOverlay/bin/Debug/net7.0/libcsfml-system' (no such file), '/Users/daniel/RiderProjects/KeyOverlay/KeyOverlay/bin/Debug/net7.0/libcsfml-system' (no such file)
dlopen(libcsfml-system, 0x0001): tried: 'libcsfml-system' (no such file), '/System/Volumes/Preboot/Cryptexes/OSlibcsfml-system' (no such file), '/usr/lib/libcsfml-system' (no such file, not in dyld cache), 'libcsfml-system' (no such file), '/usr/local/lib/libcsfml-system' (no such file), '/usr/lib/libcsfml-system' (no such file, not in dyld cache)
at SFML.System.Clock.sfClock_create()
at SFML.System.Clock..ctor()
at KeyOverlay.AppWindow..ctor(String configFileName) in /Users/daniel/RiderProjects/KeyOverlay/KeyOverlay/AppWindow.cs:line 32
at KeyOverlay.Program.Main(String[] args) in /Users/daniel/RiderProjects/KeyOverlay/KeyOverlay/Program.cs:line 14
[1] 49618 abort ./KeyOverlay
Well it took a lot of tinkering and adding some not so nice workarounds with rpath resolving, but it's finally working! 🥳
Please give the newly published package a try: https://www.nuget.org/packages/CSFML/2.5.2
~~I'm still having the exact same problem when building with SFML.Net with CSFML 2.5.2 on MacOS. Should i do something more other than just trying to build the project? There are few instances of tried: 'csfml-system.dylib' (mach-o file, but is an incompatible architecture (have 'x86_64', need 'arm64')) in the exception, could that be the issue?~~
Edit: I deleted all the previous build files and now there is no mention of architecture in the exception, the rest is still the same and the problem persists. Are there some extra steps to do after building the app with SFML.Net?
Make sure you delete the nuget cache, otherwise you'll just pull in the previous version.
On macOS the cache should be at ~/.nuget/packages/csfml
Same thing happens after deleting nuget cache and a clean build. When i have a bit more free time i'll try to create an empty project to test if this is an issue with my project or any project.
Make sure it's a .NET Core/5/6/7, for .NET Framework you still need to manually copy DLLs
The target is .net7
Ah note, that CSFML 2.x is only available for x64, so ensure that you're using dotnet for x64 and not arm64
Just tried again with a new .NET 6 project. Add the SFML.NET NuGet package, then update the CSFML NuGet package by installing the 2.5.2 version explicitly. Build and run.
Seems to work fine on my end.
I made a publish and it gave me o lot of errors of
error NETSDK1152: Found multiple publish output files with the same relative path:
which i fixed by adding <ErrorOnDuplicatePublishOutputFiles>false</ErrorOnDuplicatePublishOutputFiles> property group to the project file which seems to helped with this issue, but now I get a similar error to before just with csfml-graphics this time.
I created a new project in which i create a new RenderWindow
_window = new RenderWindow(new VideoMode(400, 400),
"test", Styles.Default);
and I just clear and display the window and the same csfml-graphics issue appeared.
Maybe the problem is in the code itself or the output type? I have no idea at this point
Did you use any additional arguments when building for MacOs?
I just build the project and run it within the IDE (JetBrains Rider). When you publish application, you need to ensure that the correct contents is packaged, so don't expect this to just work for everything. Rather just test if building and running on its own works.
I still get the same exception. When building with rider all the csfml files are sitting inside `runtimes/osx-64/native' since there are many runtimes, and the program cannot find csfml-system dylibs and when i build through the terminal and get all the files on one folder the problem appears to be with csfml-graphics
dlopen(/Users/daniel/RiderProjects/KeyOverlay/KeyOverlay/bin/Debug/net7.0/osx-x64/csfml-graphics.dylib, 0x0001): tried: '/Users/daniel/RiderProjects/KeyOverlay/KeyOverlay/bin/Debug/net7.0/osx-x64/csfml-graphics.dylib' (no such file), '/System/Volumes/Preboot/Cryptexes/OS/Users/daniel/RiderProjects/KeyOverlay/KeyOverlay/bin/Debug/net7.0/osx-x64/csfml-graphics.dylib' (no such file), '/Users/daniel/RiderProjects/KeyOverlay/KeyOverlay/bin/Debug/net7.0/osx-x64/csfml-graphics.dylib' (no such file)
dlopen(csfml-graphics.dylib, 0x0001): tried: 'csfml-graphics.dylib' (no such file), '/System/Volumes/Preboot/Cryptexes/OScsfml-graphics.dylib' (no such file), '/usr/lib/csfml-graphics.dylib' (no such file, not in dyld cache), 'csfml-graphics.dylib' (no such file), '/usr/local/lib/csfml-graphics.dylib' (no such file), '/usr/lib/csfml-graphics.dylib' (no such file, not in dyld cache)
dlopen(/Users/daniel/RiderProjects/KeyOverlay/KeyOverlay/bin/Debug/net7.0/osx-x64/libcsfml-graphics.dylib, 0x0001): tried: '/Users/daniel/RiderProjects/KeyOverlay/KeyOverlay/bin/Debug/net7.0/osx-x64/libcsfml-graphics.dylib' (no such file), '/System/Volumes/Preboot/Cryptexes/OS/Users/daniel/RiderProjects/KeyOverlay/KeyOverlay/bin/Debug/net7.0/osx-x64/libcsfml-graphics.dylib' (no such file), '/Users/daniel/RiderProjects/KeyOverlay/KeyOverlay/bin/Debug/net7.0/osx-x64/libcsfml-graphics.dylib' (no such file)
dlopen(libcsfml-graphics.dylib, 0x0001): tried: 'libcsfml-graphics.dylib' (no such file), '/System/Volumes/Preboot/Cryptexes/OSlibcsfml-graphics.dylib' (no such file), '/usr/lib/libcsfml-graphics.dylib' (no such file, not in dyld cache), 'libcsfml-graphics.dylib' (no such file), '/usr/local/lib/libcsfml-graphics.dylib' (no such file), '/usr/lib/libcsfml-graphics.dylib' (no such file, not in dyld cache)
dlopen(/Users/daniel/RiderProjects/KeyOverlay/KeyOverlay/bin/Debug/net7.0/osx-x64/csfml-graphics, 0x0001): tried: '/Users/daniel/RiderProjects/KeyOverlay/KeyOverlay/bin/Debug/net7.0/osx-x64/csfml-graphics' (no such file), '/System/Volumes/Preboot/Cryptexes/OS/Users/daniel/RiderProjects/KeyOverlay/KeyOverlay/bin/Debug/net7.0/osx-x64/csfml-graphics' (no such file), '/Users/daniel/RiderProjects/KeyOverlay/KeyOverlay/bin/Debug/net7.0/osx-x64/csfml-graphics' (no such file)
dlopen(csfml-graphics, 0x0001): tried: 'csfml-graphics' (no such file), '/System/Volumes/Preboot/Cryptexes/OScsfml-graphics' (no such file), '/usr/lib/csfml-graphics' (no such file, not in dyld cache), 'csfml-graphics' (no such file), '/usr/local/lib/csfml-graphics' (no such file), '/usr/lib/csfml-graphics' (no such file, not in dyld cache)
dlopen(/Users/daniel/RiderProjects/KeyOverlay/KeyOverlay/bin/Debug/net7.0/osx-x64/libcsfml-graphics, 0x0001): Library not loaded: @rpath/freetype.framework/Versions/A/freetype
Referenced from: <0EA01235-77CF-343C-883F-0BB46E209553> /Users/daniel/RiderProjects/KeyOverlay/KeyOverlay/bin/Debug/net7.0/osx-x64/libsfml-graphics.2.5.dylib
Reason: tried: '/Users/daniel/RiderProjects/KeyOverlay/KeyOverlay/bin/Debug/net7.0/osx-x64/freetype.framework/Versions/A/freetype' (no such file), '/Users/daniel/RiderProjects/KeyOverlay/KeyOverlay/bin/Debug/net7.0/osx-x64/freetype.framework/Versions/A/freetype' (no such file), '/Users/daniel/RiderProjects/KeyOverlay/KeyOverlay/bin/Debug/net7.0/osx-x64/freetype.framework/Versions/A/freetype' (no such file), '/Library/Frameworks/freetype.framework/Versions/A/freetype' (no such file), '/System/Library/Frameworks/freetype.framework/Versions/A/freetype' (no such file, not in dyld cache)
dlopen(libcsfml-graphics, 0x0001): tried: 'libcsfml-graphics' (no such file), '/System/Volumes/Preboot/Cryptexes/OSlibcsfml-graphics' (no such file), '/usr/lib/libcsfml-graphics' (no such file, not in dyld cache), 'libcsfml-graphics' (no such file), '/usr/local/lib/libcsfml-graphics' (no such file), '/usr/lib/libcsfml-graphics' (no such file, not in dyld cache)
at SFML.Graphics.RenderWindow.sfRenderWindow_createUnicode(VideoMode Mode, IntPtr Title, Styles Style, ContextSettings& Params)
at SFML.Graphics.RenderWindow..ctor(VideoMode mode, String title, Styles style, ContextSettings settings)
at SFML.Graphics.RenderWindow..ctor(VideoMode mode, String title, Styles style)
at KeyOverlay.AppWindow..ctor(String configFileName) in /Users/daniel/RiderProjects/KeyOverlay/KeyOverlay/AppWindow.cs:line 40
at KeyOverlay.Program.Main(String[] args) in /Users/daniel/RiderProjects/KeyOverlay/KeyOverlay/Program.cs:line 14
Same with the test project.
SFML-Test2.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace>SFML_Test2</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CSFML" Version="2.5.2" />
<PackageReference Include="SFML.Net" Version="2.5.0" />
</ItemGroup>
</Project>
Program.cs
using SFML.Graphics;
using SFML.Window;
namespace SFML_Test2;
internal static class Program
{
static void Main(string[] args)
{
Console.WriteLine("Press ESC key to close window");
var window = new SimpleWindow();
window.Run();
Console.WriteLine("All done");
}
}
internal class SimpleWindow
{
public void Run()
{
var mode = new VideoMode(800, 600);
var window = new RenderWindow(mode, "SFML works!");
window.KeyPressed += Window_KeyPressed;
var circle = new CircleShape(100f)
{
FillColor = Color.Blue
};
// Start the game loop
while (window.IsOpen)
{
// Process events
window.DispatchEvents();
window.Draw(circle);
// Finally, display the rendered frame on screen
window.Display();
}
}
/// <summary>
/// Function called when a key is pressed
/// </summary>
private static void Window_KeyPressed(object? sender, KeyEventArgs e)
{
var window = (Window)sender;
if (e.Code == Keyboard.Key.Escape)
{
window.Close();
}
}
}
Build directory:
dotnet --version output:
SDK Version: 6.0.408
I think one of the crucial points is also the SFML-Test2.deps.json file in the output directory, which lists all the links to the native libraries, including for osx-64:
I can give .NET 7 a try and see if the maybe changed something majorly with native library loading.
Just set everything up the same way with .NET 7 and it works there as well.
Okay, thanks I'm gonna try it out tommorow
OKAY I got it working. I downloaded the x64 dotnet SDK after i found out i only had the arm64 one and then set rider to use the x64 sdk to build the program.
Ah, I thought you already caught that when I wrote earlier
Ah note, that CSFML 2.x is only available for x64, so ensure that you're using dotnet for x64 and not arm64
Good to hear, that you got it working 🙂
Yeah I misunderstood what that was referring to. Thanks a lot for your help!