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

Assembly version mismatch, caused by netstandard1.6 support #34

Closed
iainnicol opened this issue Oct 17, 2018 · 10 comments · Fixed by #38
Closed

Assembly version mismatch, caused by netstandard1.6 support #34

iainnicol opened this issue Oct 17, 2018 · 10 comments · Fixed by #38

Comments

@iainnicol
Copy link

iainnicol commented Oct 17, 2018

Support for netstandard1.6 causes assembly binding trouble, at least when support for netstandard2.0 would be preferred.

Steps to reproduce

Suppose you have two F# (or C#) projects, namely a library libfoo and an application appbar. libfoo targets netstandard2.0, and appbar targets .NET Framework 4.7.2.

Also, suppose libfoo uses FParsec. In particular, libfoo depends upon the FParsec NuGet package, version 1.0.4-RC3. Also, libfoo must reference FParsec types, even if in a trivial way, to cause libfoo to load the FParsec assemblies.

A suitable example libfoo.fs follows:

module libfoo =
    let p : FParsec.Primitives.Parser<unit, unit> = Unchecked.defaultof<_>
    let nonConst () : int =
        System.Environment.MachineName.Length

Finally, suppose appbar depends upon libfoo. In particular, appbar must load libfoo, by calling into libfoo.nonConst(). The last being non-constant is important; otherwise the compiler could optimize away the loading of the libfoo assembly.

Expected result

appbar calls libfoo.nonConst, and successfully receives a return value.

Actual result

appbar calling libfoo.nonConst causes an exception:

FileLoadException: Could not load file or assembly 'FParsecCS, Version=1.0.4.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)

Environment

I am using recent Visual Studio 2017 (v15.8.7), and NuGet (v4.8.1).

Non-causes and non-fixes

Many things can be changed or added, and still trigger this bug:

  • Adding missing (or removing) AutoGenerateBindingRedirects to appbar's project file (.csproj or .fsproj) does not make this problem go away.
  • Changing FParsec.dll and FParsecCS.dll's AssemblyVersion doesn't help. Whilst FParsec's AssemblyFileVersion/FileVersion is set as expected to e.g. 1.0.4.0, the AssemblyVersion tends to be set to 1.0.0.0. This surprised me. Regardless, changing AssemblyVersion to match AssemblyFileVersion/FileVersion does not solve the bug.
  • downgrading libfoo's netstandard version to 1.6 doesn't help
  • downgrading appbar's .NET Framework version to 4.7.1 doesn't help
  • switching either or both of libfoo and appbar's languages, to C# from F#, doesn't help
  • switching libfoo and appbar's project files (.csproj or .fsproj) between the "old style" (e.g. VS2015), and the modern style (MSBuild Project SDK) doesn't help

Also, this issue affects every version of FParsec with netstandard1.6 support, namely from 1.0.3 beta upwards.

Also, FWIW, I needed two projects, the libfoo and appbar: I could not reproduce the issue with only one project.

Workarounds

Now, this issue can be avoided by changing libfoo to target .NET Framework (e.g. v4.7.2), or changing appbar to target netcoreapp (v1.0, v1.1, v2.0 or v2.1). Unfortunately, this is not always feasible.

The above fact suggests to me that this could be a bug in the .NET Framework's support of netstandard.

Nonetheless, it may be easier and faster to work around this issue in FParsec. In particular, the issue can be fixed if FParsec targets netstandard2.0 instead of netstandard1.6. Weirdly, and in support of my supposition this is a .NET Framework bug, it is not enough for FParsec to target netstandard2.0 in addition to netstandard1.6

Next steps

Would you be willing to target netstandard2.0 instead of netstandard1.6? And if so, would you prefer a pull request, or to implement the change yourself?

Moreover, please ping me if you have trouble building an unmodified FParsec with the recent version of Visual Studio I am using. In particular, as a separate issue, there are some changes which might be required to the way FSharp.Core is referenced.

@humhei
Copy link

humhei commented Nov 11, 2018

Should multiple target frameworks worked?
image

@enricosada
Copy link
Collaborator

enricosada commented Feb 7, 2019

can repro in FsAutocomplete|

fusion log

*** Assembly Binder Log Entry  (2019-02-07 @ 11:02:12) ***

The operation failed.
Bind result: hr = 0x80131040. No description available.

Assembly manager loaded from:  C:\Windows\Microsoft.NET\Framework\v4.0.30319\clr.dll
Running under executable  E:\github\fsharp\FsAutoComplete\src\FsAutoComplete\bin\Debug\net461\fsautocomplete.exe
--- A detailed error log follows. 

=== Pre-bind state information ===
LOG: DisplayName = FParsecCS, Version=1.0.3.0, Culture=neutral, PublicKeyToken=null
 (Fully-specified)
LOG: Appbase = file:///E:/github/fsharp/FsAutoComplete/src/FsAutoComplete/bin/Debug/net461/
LOG: Initial PrivatePath = NULL
LOG: Dynamic Base = NULL
LOG: Cache Base = NULL
LOG: AppName = fsautocomplete.exe
Calling assembly : FSharpLint.Core, Version=0.10.1.0, Culture=neutral, PublicKeyToken=null.
===
LOG: This bind starts in default load context.
LOG: Using application configuration file: E:\github\fsharp\FsAutoComplete\src\FsAutoComplete\bin\Debug\net461\fsautocomplete.exe.config
LOG: Using host configuration file: 
LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework\v4.0.30319\config\machine.config.
LOG: Policy not being applied to reference at this time (private, custom, partial, or location-based assembly bind).
LOG: Attempting download of new URL file:///E:/github/fsharp/FsAutoComplete/src/FsAutoComplete/bin/Debug/net461/FParsecCS.DLL.
LOG: Assembly download was successful. Attempting setup of file: E:\github\fsharp\FsAutoComplete\src\FsAutoComplete\bin\Debug\net461\FParsecCS.dll
LOG: Entering run-from-source setup phase.
LOG: Assembly Name is: FParsecCS, Version=1.0.0.0, Culture=neutral, PublicKeyToken=40ccfc0a09edbb5d
WRN: Comparing the assembly name resulted in the mismatch: PUBLIC KEY TOKEN
ERR: The assembly reference did not match the assembly definition found.
ERR: Run-from-source setup phase failed with hr = 0x80131040.
ERR: Failed to complete setup of assembly (hr = 0x80131040). Probing terminated.

enricosada added a commit to enricosada/FSharpLint that referenced this issue Feb 10, 2019
```
Error Message:
 System.TypeInitializationException : The type initializer for 'FSharpLint.Framework.Configuration' threw an exception.
  ----> System.TypeInitializationException : The type initializer for '<StartupCode$FSharpLint-Core>.$Configuration' threw an exception.
  ----> System.IO.FileLoadException : Could not load file or assembly 'FParsecCS, Version=1.0.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)
```

That's caused by bad packaging of FParsec

ref stephan-tolksdorf/fparsec#34
@iainnicol
Copy link
Author

I see @enricosada has some success with dual targetting as a workaround.

Another workaround is to handle AppDomain.AssemblyResolve. If asked for any version of FParsecCS, Assembly.Load("FParsecCS") without specifying a version. Ditto for "FParsec". Blunt, but it works.

@enricosada
Copy link
Collaborator

enricosada commented Feb 13, 2019

Multitargeting works because consumer projects will load the right assembly (net or netstandard) in all the project references chain.

That mean netstandard fparsec and net fparsec are never mixed (=> no error)

I’ll send a PR to fix this here, adding netstandard2.0 too
I think should be good if is fixed for 1.0 RTM

@iainnicol
Copy link
Author

@enricosada Thanks. Also, aaaaah! I had tried multitargeting netstandard1.6 and netstandard2.0, which did not work. But I did not try also targeting netfx.

I am curious whether you agree this is ultimately a .NET Framework bug, even though it is one you can work around in FParsec by multitargeting. As I understand it, net472 should support netstandard2.0 just as well as netcoreapp2.0 does.

@enricosada
Copy link
Collaborator

sent pr #38 to add netstandard2.0 support

enricosada added a commit that referenced this issue Mar 2, 2019
…#38)

create a modern package, use .net sdk, cleanup infra, add ci.

## Supported frameworks

add support for `netstandard2.0`

drop support for discontinued `net40-client` target framework.
it's not used anymore in .NET >= 4.5
ref https://docs.microsoft.com/en-us/dotnet/framework/deployment/client-profile

drop support for `netstandard1.6`, it's useful only in .NET Core v1.
replaced by `netstandard2.0`

drop support for `.NETPortable`( `portable-net45+win8+wp8+wpa81` ) target framework.
the PCL profiles are replaced by .NET Standard.
- the compiler defines `PCL` is not used anymore. The code is not yet removed
  because it's a big change, but because the compiler define is not set, the
  code is unused

## Dependencies

pin `FSharp.Core` to be >= v4.3.4 for all target framework to simplify deps

## Projects

add `global.json` to pin .net core sdk version to make build deterministic

upgrade form FSharp.NET.Sdk style (deprecated in .NET Core Sdk 1.0) to .NET Sdk fsproj

cleanup shared props and fsproj, using conventions of .NET Sdk:

- refactor common properties in an automatically imported `Directory.Build.props`
- move props near usage in projects
- remove now useless FParsec.Samples.Common.targets
- upgrade tests from netcoreapp2.0 to netcoreapp2.1

remove old VS11 projects, new projects works in VS

## Build script

update `pack.ps1` powershell script, to use `dotnet` commands instead of msbuild/nuget
nupkgs are generated in `~/bin/nupkg`

## CI

Add appveyor:
- run `pack.ps1`
- version suffix autogenerated by buildId/branch/PR. no suffix if is a tag (so just tag to generate a stable version)
- generated nupkgs are in the artifacts tab of appveyor, ready to download

Add travis:
- osx/ubuntu
- run `dotnet build` on both `Release` and `Release-LowTrust` configurations
- run tests only if `netcoreapp`. It can run also `net45` test using mono but i am too lazy to write the if in the travis script (but the matrix is there)

## Issues

fix #34 (support `netstandard` and `net` tfms)
fix #21 (just run `dotnet` commands, but replacing the powershell is easy too)
@Arshia001
Copy link

Arshia001 commented Jan 4, 2020

I'm having the same problem, though I suspect it has little to do with targeting 1.6 vs 2.0. This is from a decompilation of the FParsecCS assembly:

[assembly: AssemblyFileVersion("1.0.3.0")]
[assembly: AssemblyInformationalVersion("1.0.3")]
[assembly: AssemblyProduct("FParsec")]
[assembly: AssemblyTitle("FParsecCS")]
[assembly: AssemblyVersion("1.0.0.0")]

Notice it says 1.0.0.0 on the last line. ILSpy also recognizes the assembly as FParsecCS(1.0.0.0), which is probably why it'd fail to load in .net framework. I suspect the update to 2.0 somehow fixed whatever caused this, since that version is fixed in 1.1.0-RC. Here's the question though: given 1.1.0's status as not final, and that the 1.0.3 version is essentially unusable (for my specific scenario) given this error, is it safe to use 1.1.0-RC in production?

@stephan-tolksdorf
Copy link
Owner

I've just released the final 1.1.0 version, which codewise is identical to the RC release.

@brian-reichle
Copy link

I'm getting a very similar issue with FParsec 1.1.1, the issue seems to come down to the FParsec package having assemblies with different version numbers for different target frameworks (net45 = 1.0.0.0, netstandard2.0 = 1.1.1.0).

Was it intentional that the net45 assembly has a different version number? It makes it difficult to consume a netstandard2.0 library that uses FParsec, from a net472 assembly.

@stephan-tolksdorf
Copy link
Owner

The net45 assembly uses the fixed version number 1.0.0.0 because it is strongly signed, while the netstandard2.0 is not. You'll find discussions on the signing in comments on older issues.

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

Successfully merging a pull request may close this issue.

6 participants