jcurl / RJCP.DLL.SerialPortStream

SerialPortStream is an independent implementation of System.IO.Ports.SerialPort and SerialStream for better reliability and maintainability. Default branch is 2.x and now has support for Mono with help of a C library.
Microsoft Public License
624 stars 197 forks source link

Assembly 'RJCP.SerialPortStream' in NuGet /netstandard1.5 folder misses PublicKeyToken; version 2.1.3 #55

Closed Amarok79 closed 6 years ago

Amarok79 commented 6 years ago

Hello!

I encountered a problem when using your library in a .NET Standard 2.0 library of myself that is then used in a .NET 4.7.1 console app. The issue arises from a PublicKeyToken mismatch in the different assemblies you deploy with your NuGet package.

For instance, your net40 and net45 assemblies have a PublicKeyToken of 5f5e7b70c6a74deb, whereas your netstandard1.5 assembly has none.

Now, a .NET Standard 2.0 library will generally bind to your netstandard1.5 assembly, which has no PublicKeyToken. When I then use this .NET Standard 2.0 library in another .NET 4.7.1 console application - which is by the way a common use case - then this console application will bind to the net45 version of RJCP.SerialPortStream, which has a PublicKeyToken.

In such situation, compilation works fine, but at runtime, I get a System.IO.FileLoadException: 'Could not load file or assembly 'RJCP.SerialPortStream, Version=2.1.3.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)' exception, which is caused by the different PublicKeyTokens.

I would expect to have all assembly variants in a NuGet package to have the same PublicKeyToken, meaning your .NET Standard 1.5 assembly should be strong-signed too.

jcurl commented 6 years ago

Thanks for the detailed report. I'll have to investigate this as using NET Core is not the primary target. If you have some experience with strong signing NET Core binaries, I'd be glad to hear. Then I can investigate an update.

It also sounds like your NET 4.7.1 project also references the project.

Please be aware, that the two assemblies are not the same (NetStandard 1.5 and NetFx 4.0). It might be that if I get them both signed, there might still be a type load error.

So I fear that might not be as simple as just described.

Amarok79 commented 6 years ago

I'll have to investigate this as using NET Core is not the primary target. If you have some experience with strong signing NET Core binaries, I'd be glad to hear. Then I can investigate an update.

Well, I don't target .NET Core as runtime. I just use a .NET Standard class library because .NET Standard is intended to provide target-independent libraries that then can run on different runtimes like Mono, .NET Core or .NET FX. My primary target is .NET FX 4.7.1. I just have chosen .NET Standard in my current side-project to learn about this new stuff.

That said, I also a kind of newcomer to .NET Standard, but I have time to dig into it. Hopefully I'll get together a PR.

It also sounds like your NET 4.7.1 project also references the project.

Yes and no. Basically, my application (.NET 4.7.1) references your package indirectly as transitive dependency. And because of that it picks the net45 assembly from the package, because that matches best.

I also tried to explicitly add your package to my application, making it a direct dependency, but that has the same effect. I then tried to override package target resolution via <PackageTargetFallback/>, but also without luck. It's possible that there exist some other mechanism that I don't know yet.

Nevertheless, having assemblies with different identities (for different targets) in the same NuGet package, isn't going to work. So, I think that's the root cause of my troubles.

Please be aware, that the two assemblies are not the same (NetStandard 1.5 and NetFx 4.0). It might be that if I get them both signed, there might still be a type load error.

Indeed, that can be a problem. But, in practice it won't. First, because your net45 and netstandard15 assemblies seem to be compatible (at least the public API). In my app project I enforce loading of net45 library by handling AppDomain.CurrentDomain.AssemblyResolve event. That means, I use net45 assembly at runtime, although compilation was done against netstandard1.5. And it works fine, but this is a hack, not working for unit tests, etc.

The net40 version of course might break, but net40 isn't compatible with .NET Standard at all and therefore I think this target won't be picked in scenarios similar to mine; you already provide better matching targets like net45 and netstandard1.5.

This all is a bit complicated. I still believe that fixing the strong-naming will make all problem go away. So, I'm going to investigate there.

tmatthey commented 6 years ago

My 2 Cents. Maybe this could be resolved by using the new XML-based MSBuild/csproj format? It allows to combine all targets into one project ("netstandard1.5;net40;net45") and build, sign and pack all DLL's in one go from Visual Studio.

jcurl commented 6 years ago

I'm using .NET Core 1.0.4. I get the following build error.

======================================================================
== Building SerialPortStream for netstandard1.5
======================================================================
  Restoring packages for C:\Users\jcurl\Documents\Programming\serialportstream\code\SerialPortStream-netstandard15.csproj...
  Lock file has not changed. Skipping lock file write. Path: C:\Users\jcurl\Documents\Programming\serialportstream\code\obj\project.assets.json
  Restore completed in 617.36 ms for C:\Users\jcurl\Documents\Programming\serialportstream\code\SerialPortStream-netstandard15.csproj.

  NuGet Config files used:
      C:\Users\jcurl\AppData\Roaming\NuGet\NuGet.Config

  Feeds used:
      https://api.nuget.org/v3/index.json
      C:\Users\jcurl\rjnubld
Microsoft (R) Build Engine version 15.1.1012.6693
Copyright (C) Microsoft Corporation. All rights reserved.

C:\Program Files\dotnet\sdk\1.0.4\Microsoft.Common.CurrentVersion.targets(3032,5): error : PFX signing not supported on .NET Core [C:\Users\jcurl\Documents\Programming\serialportstream\code\SerialPortStream-netstandard15.csproj]

Build FAILED.

C:\Program Files\dotnet\sdk\1.0.4\Microsoft.Common.CurrentVersion.targets(3032,5): error : PFX signing not supported on .NET Core [C:\Users\jcurl\Documents\Programming\serialportstream\code\SerialPortStream-netstandard15.csproj]
    0 Warning(s)
    1 Error(s)

Time Elapsed 00:00:00.93

Which version are you using?

Amarok79 commented 6 years ago

Ohh. My fault. As I tested my PR locally, I replaced you PFX with a SNK, because I didn't had a PFX at hand and it didn't come to my mind that PFX wouldn't be supported in .NET Core... but that's the case.

I would suggest to switch to SNK in your projects because they are supported by .NET FX and .NET Core toolset.

I found some resources for PFX to SNK conversion:

Hope this helps.

jcurl commented 6 years ago

Alright. Got it working, but I'll have to update the patchset first. NuSpec needs updating, the SNK should be used instead. I'll see if I can use the SNK for everything first. Saves me having to carry around two forms of the same key.

jcurl commented 6 years ago

Can you please test the attached nuget packages? I'll look at your other ticket before making a release.

serialportstream.zip

jcurl commented 6 years ago

By the way, just saw Amarok.SerialPortStream. Are you sure that you solved the problem there?

Projects\SerialPortTest\packages\Amarok.SerialPortStream.2.1.4\lib>sn -T net40\RJCP.SerialPortStream.dll

Microsoft (R) .NET Framework Strong Name Utility  Version 4.0.30319.0
Copyright (c) Microsoft Corporation.  All rights reserved.

net40\RJCP.SerialPortStream.dll does not represent a strongly named assembly

SerialPortTest\packages\Amarok.SerialPortStream.2.1.4\lib>sn -T net45\RJCP.SerialPortStream.dll

Microsoft (R) .NET Framework Strong Name Utility  Version 4.0.30319.0
Copyright (c) Microsoft Corporation.  All rights reserved.

net45\RJCP.SerialPortStream.dll does not represent a strongly named assembly

SerialPortTest\packages\Amarok.SerialPortStream.2.1.4\lib>sn -T netstandard1.5\RJCP.SerialPortStream.dll

Microsoft (R) .NET Framework Strong Name Utility  Version 4.0.30319.0
Copyright (c) Microsoft Corporation.  All rights reserved.

netstandard1.5\RJCP.SerialPortStream.dll does not represent a strongly named assembly

Doesn't look like your library does any signing.

Amarok79 commented 6 years ago

Alright. Got it working, but I'll have to update the patchset first. NuSpec needs updating, the SNK should be used instead. I'll see if I can use the SNK for everything first. Saves me having to carry around two forms of the same key.

I only use SNKs all the time. I was surprised as I looked at your repository that PFX was supported too ;-) Didn't know that.

Amarok79 commented 6 years ago

By the way, just saw Amarok.SerialPortStream. Are you sure that you solved the problem there?

This is just there to get going with my work. To keep it simple I removed signing entirely, as I don't need it at the moment.

The description isn't correct; it doesn't solve the strong-name conflict, it removes strong-name entirely.

Amarok79 commented 6 years ago

Can you please test the attached nuget packages? I'll look at your other ticket before making a release.

Tested it. Works!

jcurl commented 6 years ago

Integrated in branch v2.x