vpenades / SharpGLTF

glTF reader and writer for .NET Standard
MIT License
467 stars 75 forks source link

Consider strong-naming assemblies #48

Closed diredev closed 3 years ago

diredev commented 4 years ago

Would you mind strong-naming the SharpGLTF assemblies?

This would allow users like me that have to develop on the old .NET Framework, where strong-named assemblies cannot reference assemblies that aren't strong-named too. At the moment our application refuses to load the assembly at all.

See also Microsoft's recommendations here.

vpenades commented 4 years ago

I've been considering it for a while, actually. My biggest concern is this:

"The downside to strong naming is that the .NET Framework on Windows enables strict loading of assemblies once an assembly is strong named."

This is specially problematic with large projects that might reference different versions of the library. Usually the compiler picks the latest version.

Right now, it would not be very difficult to strong name SharpGLTF because it does not depend in any other library... so please, give me some time to think about it.

promontis commented 4 years ago

@diredev which strong-named assemblies do you use?

diredev commented 4 years ago

It's a company-internal library used to convert into various formats. One of the converters uses SharpGLTF to, well, create GLTF, GLB and OBJ files. The main library is strong-named and therefore requires the same of all plugins libraries too...

It should be noted that I do have some control over the strong-naming of the libraries, but changing this is guaranteed to break a few applications.

promontis commented 4 years ago

@diredev if you can I would advise to remove the strong names of your libraries, if possible. The whole strong naming thing is flawed to be honest, and to make things worse, it is an on/off switch for all the dependencies as well 😱

vpenades commented 4 years ago

Thinking about the dependencies, I forgot that SharpGLTF has one dependency, and not a trivial one; it depends on System.Text.Json 4.7.1

If I make SharpGLTF strong named, I will be enforcing everybody using SharpGLTF to use System.Text.Json 4.7.1 , which can be problematic if you also need to consume other libraries that depend on a different version of System.Text.Json, which is very likely.

This is a similar problem that happened with many projects using different versions of Newtonsoft.Json, as reported here or here.

Possible solutions:

Certainly this is a common problem shared by everybody deploying and consuming nuget packages, and it's sad to believe that these are the only solutions.

A while ago I found there's some efforts to allow consuming non signed nuget packages on projects that require it, like StrongNamer or Nivot.StrongNaming , I haven't tried them myself, so I can't tell if they really solve the problem and how robust they're... StrongNamer has +500.000 downloads, so it's probably well tested... @diredev if you try this solution, let me know if it worked for you... if not, I will consider other options.

diredev commented 4 years ago

First of all, let me rant about the strong-naming debate a bit:

The .NET framework will, under normal circumstances, only load a single instance of any one assembly. If the reference is without strong-name it will just load whichever version it finds in the output directory. For strong-named dependencies on the other hand, it will search for a specific version in the directory (or the Global Assembly Cache) instead. This is bound to lead to problems when an application references multiple different versions of the same assembly, as only one version can be copied to the out directory...

To work around these issues, Microsoft allows us to configure Binding Redirects on the app.config level to tell the framework which version to load, which it will then use for all dependent libraries. Managing these redirects by hand was a huge pain on larger projects.

The issues were so widespread that Microsoft chose to automatically fill the app.config with Redirects to the highest referenced version when using NuGet, fixing many of the beforementioned issues, but also (permanently) filling the app.config of the project. Nowadays (starting with Framework 4.5.1) these entries are automatically added to the config at compile time, taking the whole dependency-tree into account.

It should also be noted that when talking about versions, it's always the AssemblyVersion - not the file or package version - that matters. No Binding Redirects are required at all as long as all libraries reference the same AssemblyVersion. Microsoft recommends that the AssemblyVersion should be changed only on new major (breaking) releases of a project, greatly reducing the number of conflicts.

You also do not force your dependencies onto other projects by strong-naming your assembly, but may need (automatic) Binding Redirects added when multiple versions of your own assembly are referenced (unless the AssemblyVersion stays the same). Note that, since System.Text.Json is strong-named itself, the Framework will already apply the stricter loading mechanism to it. E.g. referencing SharpGLTF.Core 1.0.0-alpha17 in one project and alpha15 in another will automatically add a Binding Redirect for System.Text.Json to the app.config.

Nowadays the issues people have with strong-named assemblies stem mostly from problematic behavior on the developers part or them not understanding AssemblyVersions. E.g. the Newtonsoft.Json issues you've linked are caused by them seemingly introducing a breaking change without changing the AssemblyVersion. The Framework assumes them to be compatible though, and tries to load the latest available file-version from the Global Assembly Cache, which caused errors.

TLDR: Most of the discussion and controversy around strong-naming seems, IMHO, outdated and filled with confusion. A well-maintained library has nothing to loose from being strong-named if one follows Microsoft's advice to commit the key-file and keep the AssemblyVersion mostly fixed.

It should be noted however, that, as correctly explained in the HeartySoft Blog you've linked, adding a strong-name will break dependent applications (once), as two libraries aren't considered the same when their strong-names differ. But that's why we have Alpha versions, right ;-)


I've also tried the StrongNamer on our project and the result seems promising. Deploying the whole thing seems manageable but rather awkward to me, as it would require incorporating the modified binaries into my own NuGet package.

vpenades commented 4 years ago

@diredev Thanks, I would like to test some case scenarios with strong named nugets and see what happens... I'm busy right now so I don't know when I'll be able to do the tests.

I'm particularly worried about games using the library:

Game development is a field that usually don't care too much about strong naming, and although it looks like Visual Studio is handling the versioning through BindingRedirects, there's more environments to take into account:

For example, Unity has its own build pipeline that does cross compiling to cpp, and then to native, and I have no clue how it handles strong named assemblies, I don't think if it even has the concept of binding redirects.

Monogame is stuck at Net 4.5.0 when it targets PlayStation and Nintendo... This is even worse than with Unity, because console development toolchains are usually very restrictive.

And then there's Xamarin/Mono for targeting Android/iOS... more cross compiling.

So, before making the nuget packages strong named, I must be 100% sure that developers using the library in all these environments are going to be able to use the library seamlessly. If there's the slightest problem with strong naming.... I might end publishing strong named versions of the library with the .StrongName suffix.

It's goog that StrongNamer has been useful... at least it will buy me some time to do the tests and figure out what to do.

tlgkccampbell commented 3 years ago

I'm looking into incorporating SharpGLTF into my framework and this is a bit of a stumbling block for me, since my framework assemblies are all strongly named. I'm probably going to solve this by distributing my own strongly named build of SharpGLTF, but obviously it would be much more convenient for me if I could get them directly from the source, so I wanted to voice my support for this issue.

vpenades commented 3 years ago

Ok, I can do this; next version, alpha 20, which is due to be released in a few weeks, I can make it strong named, and see what happens when users update.

If there's no complaints, it will stay strong named. If there's complaints, I'll move it back to weak named, and I'll consider publishing strong named versions.

promontis commented 3 years ago

@vpenades I would recommend publishing a strong name version and a non-strong name version, so that people still stuck with strong naming can use that library.

@tlgkccampbell Why do you use strong naming? Nobody uses strong naming anymore... https://github.com/dotnet/corefx/blob/master/Documentation/project-docs/strong-name-signing.md.

vpenades commented 3 years ago

@promontis If possible, I would like to avoid maintaining two versions, I will do it if having a strong named only assembly proves unsuccessful.

The link you mentioned, states two contradictory things:

One one side, it states that the main reason to strong name assemblies has been lost, and as they say, newer runtimes like net core don't pay too much attention whether the assembly has been strong named or not.

On the other, the link still recommends strong naming the assemblies for the sake of backwards compatibility.

Now from a practical standpoint, my feeling is to choose the path that causes less harm.

So, which harm would make to keep the assemblies from being strong named?

Obviously those requiring strong named assemblies would not be able to use the library.

And, which harm would make strong naming the assemblies?

To begin with, the library has minimal third party dependencies, System.Text.Json and nothing else, so it would not introduce any evil third party dependency into any project like it used to happen with Newtonsoft.Json.

Also it seems the only runtime in which conflicts may occur, is NetFramework, which is already fading away.

So I believe (correct me if I am wrong), for most users consuming the library, whether it's strong named or not, it will be harmless and transparent.

On my side, by strong naming SharpGLTF it would mean that if, in the future, I require a third party library (like a draco decoder, or an Imaging library), and it happens to not be strong named, I would have a problem, and I would have to approach the maintainer of that library to ask him to strong name his project, spreading the evil. This really looks like a fight between reds and blues, because there's no other technical explanation.

Finally

Looking around, I've noticed that ImageSharp has gone strong named recently too. It could be interesting to know the percentage of packages at nuget.org that have strong names.

Personally, I would have liked that strong naming would be removed or never existed to begin with, and trust me I don't like strong naming the assembles, but it's the imperfect world we live.

tlgkccampbell commented 3 years ago

@promontis My framework has existed in some form since 2010, when strong naming was necessary for certain sandboxing scenarios that I was exploring at the time. Removing the strong names now would be a breaking change, and the honest truth is that being able to use the official SharpGLTF packages is not a sufficiently compelling benefit to make me do that. I had considered doing so when I transitioned to .NET Core, but Microsoft's official guidance is that developers of publicly-distributed .NET Core libraries should strong name their assemblies, so I did not.

I appreciate that the developers of SharpGLTF have their own problems to consider, and I'll adapt to whatever the ultimate resolution is. But that's the scenario I'm in.

vpenades commented 3 years ago

Ok, I think I've been able to create the nuget packages with strong naming.

@tlgkccampbell , @promontis , would you consider trying the strong signed nuget packages to see if you find any issues?

I've published the preview packages here at github, the latest versions can be found here.

If things go well, next version alpha 20 will be strong signed.

tlgkccampbell commented 3 years ago

@vpenades I can confirm that the package you posted works for my needs.

vpenades commented 3 years ago

I'm about to release Alpha20 shortly, This new release will be strong named.

I took special care trying to avoid dependencies as much as possible, and this new release multitargets netstandard and netcore3.1 ... which means that the netcore 3.1 compiled assemblies are completely free of dependencies, I hope this will minimize any conflicts with other strong named dependencies.

@promontis I would be glad if you can check this release when available, and report if you've had any issues with it.

@tlgkccampbell, @diredev, this release will serve a sort of public test bed for strong naming incompatibilities, if there's lots of complaints, I'll roll back the strong naming for Alpha21 and I will consider other solutions like having a .Signed assembly (which Microsoft does not recomend)

vpenades commented 3 years ago

Since Alpha20 is strong name is been out in a while and no major issues appeared, I think I can consider this issue settled and closed.

@tlgkccampbell I've seen you still have your ultraviolet.sharpgltf.core visible at nuget.org, if you are to switch back to using sharpgltf now that it's strong named, I would suggest to make that package invisible.

tlgkccampbell commented 3 years ago

I've issued a new release of Ultraviolet which directly references SharpGLTF and, as requested, marked my fork as deprecated and unlisted. Thank you for your work.