Can I prevent NuGet from downgrading (parameterized) references in my project files? - f#

This happens sometimes, not always and I can't describe precisely what circumstances are needed to replicate it:
I have a test project in F# 4.0, that naturally references FSharp.Core 4.4.0.0
The test project uses FsUnit which is added through NuGet
On -ReInstall or update/restore, the FSharp.Core reference gets downgraded to FSharp.Core 4.0.0.1
Essentially the following line in the .fsproj file:
<Reference Include="FSharp.Core, Version=$(TargetFSharpCoreVersion), Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<Private>True</Private>
</Reference>
gets replaced with:
<Reference Include="FSharp.Core">
<HintPath>..\packages\FSharp.Core.4.0.0.1\lib\net40\FSharp.Core.dll</HintPath>
<Private>True</Private>
</Reference>
I would very much like to prevent that from happening, especially on the TeamCity build-server, which currently has different test results from local builds as a result of this behavior.
I tried playing with -DependencyVersion:HighestPatch, but this appears to have no effect here.

Related

Project doesn't compile after upgrading from Visual Studio 2017 to 2019

I can't understand the warnings and errors from Visual Studio 2019. It seems that all the references to the packages of the project are no longer working after the upgrade from Visual Studio 2017.
There are the initial lines of the log when I compile the solution.
1>------ Build started: Project: ClientServerUpload, Configuration: Debug Any CPU ------
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin\Microsoft.Common.CurrentVersion.targets(2106,5): warning MSB3245: Could not resolve this reference. Could not locate the assembly "System.Text.Encoding.CodePages". Check to make sure the assembly exists on disk. If this reference is required by your code, you may get compilation errors.
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin\Microsoft.Common.CurrentVersion.targets(2106,5): warning MSB3243: No way to resolve conflict between "System.Text.Encoding.CodePages, Version=4.1.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" and "System.Text.Encoding.CodePages". Choosing "System.Text.Encoding.CodePages, Version=4.1.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" arbitrarily.
1> No way to resolve conflict between "FSharp.Core, Version=4.4.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" and "FSharp.Core, Version=4.3.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a". Choosing "FSharp.Core, Version=4.4.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" arbitrarily.
1> No way to resolve conflict between "FSharp.Core, Version=4.4.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" and "FSharp.Core, Version=4.4.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a". Choosing "FSharp.Core, Version=4.4.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" arbitrarily.
1> Consider app.config remapping of assembly "FSharp.Core, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" from Version "4.4.3.0" [] to Version "4.7.0.0" [\\mypath\packages\FSharp.Core.4.7.0\lib\net45\FSharp.Core.dll] to solve conflict and get rid of warning.
There are many other lines after the above, but anyway I can't see what is going wrong. For example, I pick the first message, about "System.Text.Encoding.CodePages". There a warning saying "Could not locate the assembly" and another about a conflict between Version=4.1.3.0 and a reference with no version. But from where does it look at Version=4.1.3.0? In the pacakge.config there is a line:
<package id="System.Text.Encoding.CodePages" version="4.7.0" targetFramework="net461" />
and if I edit the .proj file I see:
<Reference Include="System.Text.Encoding.CodePages"> <HintPath>..\packages\System.Text.Encoding.CodePages.4.7.0\lib\net461\System.Text.Encoding.CodePages.dll</HintPath>
</Reference>
and the HintPath is indeed there. Similar doubts for all the other messages and errors.
Only another example, why is it searching FSharp.Core, Version=4.4.1.0?
Again I have
<ItemGroup>
<Reference Include="FSharp.Core">
<HintPath>..\packages\FSharp.Core.4.7.0\lib\net45\FSharp.Core.dll</HintPath>
</Reference>
in the proj file and in the packages.config
<package id="FSharp.Core" version="4.7.0" targetFramework="net461" />
All the configurations I'm aware of seem correct to me, and the whole solution was completely fine with Visual Studio 2017. The project was built with WebSharper template, I don't know if/how this is relevant, however I also reinstalled WebSharper VSIX, after upgrading to Visual Studio 2019, and I didn't receive any error.
Aside from deleting and restoring all the packages, looking at the .proj file, cleaning and rebuilding the solution, what other options do I have? At what else should I look? I know I can add remapping to the Web.config, but I don't think it is the solution (not even a workaround). For your info, after many hours I managed to get a release with many (strange) lines of remapping and by manually (!) copying some files (including FSharp.Core) from the packages to the bin folder, that is clearly not an acceptable way to proceed.
I seem to recall something similar when I upgraded an F# project from VS2015 to 2017.
The solution was to update the version of the F# runtime (FSharp.Core.dll) in the project properties.
Looking at the .fsproj for a .NET Core 3.0 F# project: the F# runtime is not actually listed, so must be an implicit dependency: maybe removing it would also work.
Step 0
I've restored an old commit of my project to better describe the original situation and to track all the steps to resolve it.
Before the following 2 operations, I had to nuget upgrade from FSharp.Core 4.6 to 4.7, for a problem specific to WebSharper: the Scripts were not produced.
Step 1
I had to edit the .proj file to delete some reference lines, sort of duplicated.
Here is an example for FSharp.Core, but I did the same for many others (I don't know what exactly originated this problem but removing those lines seems to have done the trick for me)
<Reference Include="FSharp.Core">
<HintPath>..\packages\FSharp.Core.4.7.0\lib\net45\FSharp.Core.dll</HintPath>
</Reference>
<Reference Include="FSharp.Core" />
In the above case I deleted the last line (and all the other similar lines without a version reference under a tag containing the version of the same package)
Step 2
Then I copied and pasted all the <dependentAssembly> from the Visual Studio output to the Web.config. Again I don't understand wht they are many more than the ones needed for Visual Studio 2017, but these 2 operations apparently solved my issue.

Cannot resolve dependency to assembly FSharp.Core 4.4.1.0 when using VS 2017

I have been developing in VS 2015 and F# 4.0 (4.4.0.0) for quite some time.
With the release of VS 2017, I want to open solutions in the newest VS for development work, but still for a while keep the projects as VS 2015, F# 4.0, .NET 4.5.2. The build server will also have to use VS 2015 for a while.
As far as I can remember, this kind of scenario has not been problematic in earlier VS version upgrades, but then I don't think I used F# at that time.
I opened the solution and tried to compile. I get this error in a C# application project. (There are other C# applications, and at least one references an F# library.)
Unknown build error, 'Cannot resolve dependency to assembly 'FSharp.Core, Version=4.4.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' because it has not been preloaded. When using the ReflectionOnly APIs, dependent assemblies must be pre-loaded or loaded on demand through the ReflectionOnlyAssemblyResolve event.
All my F# projects in the solution are 4.0 (4.4.0.0). I double checked.
Why is this happening?
I searched for "4.4.1.0", and discovered that the "obj" folder of the C# project had a .exe.config file that differed from the app.config. It had this extra information that is not in the app.config of the project.
<runtime>
...
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="FSharp.Core" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.4.1.0" newVersion="4.4.1.0" />
</dependentAssembly>
</assemblyBinding>
Why is this appended automatically, and why only in this particular C# project?
I tried to copy that section to the app.config of the project, and change it to 4.4.0.0 in both places, but that didn't work. Also tried to use "4.4.1.0" as upper limit of old version, and have "4.4.0.0" as new version, but still didn't work. Same compiler error.
Then I removed that section, and I referenced FSharp.Core 4.4.0.0 in the C# project. That finally got rid of the compile error.
The I ran the program. It crashed with this exception.
Unhandled exception: Could not load file or assembly 'FSharp.Core, Version=4.4.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)
I reinserted the section with the redirect, and now the program runs fine.
Just to sum up, I added a reference to FSharp.Core 4.0, and the redirect looks like this
<bindingRedirect oldVersion="0.0.0.0-4.4.1.0" newVersion="4.4.0.0" />
With these modifications, the solution still works as expected also in VS 2015.
I had the same problem, maybe this is helpful for someone:
In my case, the cause was that some of my C# projects with transitive dependencies on FSharp.Core were referencing the runtime's assembly installed on my system directly, instead of using the NuGet package. I.e. the reference didn't have a hint path pointing to the NuGet packages folder, and thus was picking the assembly from C:\Program Files\FSharp\... from the F# SDK. I solved this by removing the reference and reinstalling the FSharp.Core NuGet package.
So this:
<Reference Include="FSharp.Core, Version=4.3.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
Turns into:
<Reference Include="FSharp.Core, Version=4.5.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\FSharp.Core.4.5.2\lib\net45\FSharp.Core.dll</HintPath>
<Private>True</Private>
</Reference>
Check the assembly references of the assembly in the message. For me I had a reference to assembly X which had a reference to Y. Because Y was missing, I got this error. By referencing Y, the error was resolved for me.

F# How to setup FAKE project that can use FsUnit

I'm trying to setup a basic FAKE F# project that can run FsUnit but I cannot figure out how to solve the Method not found: 'Void FsUnit.TopLevelOperators.should(Microsoft.FSharp.Core.FSharpFunc`2<!!0,!!1>, !!0, System.Object)' errors.
I have read the following posts that seem to be related, but I'm apparently still not grokking it:
Main github issue
FSharp.Core packaging guidelines
FsUnit unable to test portable library (SO)
Another github issue
I have created a JunkTest library project with the following setup:
paket.dependencies
source https://www.nuget.org/api/v2
nuget FAKE
nuget FSharp.Core
nuget FsUnit
nuget NUnit
nuget NUnit.Console
paket.references
FSharp.Core
FsUnit
NUnit
JunkTest.fs
module JunkTest
open FsUnit
open NUnit.Framework
[<Test>]
let ``Example Test`` () =
1 |> should equal 1 // this does not work
//Assert.That(1, Is.EqualTo(1)) // this works (NUnit)
build.fsx (relevant part)
Target "Test" (fun _ ->
!! (buildDir + "JunkTest.dll")
|> NUnit3 (fun p ->
{p with OutputDir = "TestResults" }
)
)
Output
I see that FSharp.Core.dll is being copied from the local packages directory: Copying file from "c:\Users\dangets\code\exercism\fsharp\dgt\packages\FSharp.Core\lib\net40\FSharp.Core.dll" to "c:\Users\dangets\code\exercism\fsharp\dgt\build\FSharp.Core.dll".
And the nunit3-console execution: c:\Users\dangets\code\exercism\fsharp\dgt\packages\NUnit.ConsoleRunner\tools\nunit3-console.exe "--noheader" "--output=TestResults" "c:\Users\dangets\code\exercism\fsharp\dgt\build\JunkTest.dll"
I have tried to add a app.config file with the in the test project root directory with the following but it doesn't seem to solve the issue (NOTE I am not using Visual Studio - do I need to do anything special for the project to include the app.config file?):
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="FSharp.Core" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.3.1.0" newVersion="4.3.1.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
Any and all help is appreciated.
EDIT: The solution was that I was not properly setting up the App.config file to get included in the build. All of the answers that said "just add this to your App.config file" didn't help me because VSCode doesn't add this to the fsproj file automatically.
The part that I added is:
<None Include="App.config" />
In the ItemGroup that contains the other <Compile Include=Foo.fs> lines.
This happens because of FSharp.Core version mismatch. See, your application references one version of FSharp.Core and FsUnit references another version. This means that the FSharpFunc<_,_> type is going to be different (coming from different assemblies) for you and FsUnit, which in turn means that the should function exported by FsUnit is not the same function that your code is looking for, because it has a parameter of a different type.
This is where the bindingRedirect comes in. You're absolutely correctly added it to app.config, but from your question about whether you're doing it correctly, I get a suspicion that you might not. The thing with app.config is, it's not actually the program configuration. Rather, it's the source code for program configuration. At compile time, this file gets copied to bin\Debug\Your.Project.dll.config, and only then it will get picked up at runtime. If you didn't add this file to the fsproj project file (which, I suspect, might be the case), then it's not getting copied to the right place during build, and thus isn't getting picked up at runtime.
Another reason for it still not working may be that you've specified an incorrect version of FSharp.Core in your app.config file. Which brings me to the next point.
Crafting that file by hand is a bit fragile: when you upgrade FSharp.Core to a new version (or Paket does it for you), you may forget to fix it in app.config and even if you don't, it's a bit of a hassle. But Paket can help you with that: if you add the redirects: on options to your paket.dependencies file, Paket will add the bindingRedirect cruft to your app.config automatically:
source https://www.nuget.org/api/v2
nuget FAKE
nuget FSharp.Core redirects: on
nuget FsUnit
nuget NUnit
nuget NUnit.Console
This sounds like an FSharp.Core version mismatch.
The NuGet package you're using ships with FSharp.Core 4.4 (not 4.3.1). I recommend modifying your binding redirect to use 4.4:
<bindingRedirect oldVersion="0.0.0.0-4.3.1.0" newVersion="4.4.0.0" />

F#'s Scaffold and FsUnit/FsCheck

I'm struggling to get started with F# on Linux using ProjectScaffold.
Specifically: I can't get a project to work with FsUnit/FsCheck/xunit. I
have F# 3.1 and mono 3.12.1 and I'm on Linux (Ubuntu) x64.
I start "MyProject" with:
$ git clone --depth=1 git#github.com:fsprojects/ProjectScaffold.git
$ cd ProjectScaffold && ./build.sh
Then I add a bit of code to "src/MyProject/Library.fs":
module MyProject.X
let four = 4
And then two tests to "tests/MyProject.Tests/Tests.fs":
module MyProject.Tests.X
open Xunit
open FsUnit.Xunit
open FsCheck
open FsCheck.Xunit
open MyProject.X
[<Fact>]
let ``Two plus two is four.`` () =
2 + 2 |> should equal four
[<Property>]
let ``Sorting a sorted list is idempotent.`` (l: int list) =
let s = List.sort l
s = List.sort s
This code works on Visual Studio where I manually added FsCheck, FsUnit, anx xunit 1.9.2 (it fails with later version
for some reason). My test project on Windows/Visual Studio has this config file:
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="FsCheck" version="1.0.4" targetFramework="net45" />
<package id="FsCheck.Xunit" version="1.0.4" targetFramework="net45" />
<package id="FsUnit.xUnit" version="1.3.0.1" targetFramework="net45" />
<package id="xunit" version="1.9.2" targetFramework="net45" />
<package id="xunit.runner.visualstudio" version="2.0.0" targetFramework="net45" />
</packages>
So I edit paket.dependencies to add these packages and remove Nunit:
source https://nuget.org/api/v2
nuget FSharp.Formatting 2.8.0
nuget FSharpVSPowerTools.Core 1.7.0
nuget FAKE
nuget FsCheck 1.0.4
nuget FsCheck.Xunit 1.0.4
nuget FsUnit.xUnit 1.3.0.1
nuget xunit 1.9.2
nuget SourceLink.Fake
github fsharp/FAKE modules/Octokit/Octokit.fsx
Then:
$ mono .paket/paket.exe install
...and it fails because NUnit is referenced somewhere, so I delete the references in tests/MyProject.Tests/paket.references and
$ mono .paket/paket.exe install
works, but
$ ./build.sh
fails, at it cannot find references to FsCheck et al. So I assume that I need to add the references manually, so tests/MyProject.Tests/paket.references is now:
FsCheck
FsCheck.Xunit
FsUnit.xUnit
xunit
...built build.sh fails again: it cannot find FsCheck. I could not find in the paket doc how to add a local dependency (MyProject.Tests should reference MyProject), it might be done automatically.
I had this problem and it took a while for me to figure out a fix. For the project file in the Tests directory, I had to change this:
<Reference Include="FsUnit.NUnit">
<HintPath>..\..\packages\FsUnit.1.3.0.1\Lib\Net40\FsUnit.NUnit.dll</HintPath>
<Private>True</Private>
</Reference>
To this:
<Reference Include="FsUnit.NUnit">
<HintPath>..\..\packages\FsUnit\Lib\Net40\FsUnit.NUnit.dll</HintPath>
<Private>True</Private>
</Reference>
Similarly, for NUnit:
<Reference Include="nunit.framework">
<HintPath>..\..\packages\NUnit.2.6.3\lib\nunit.framework.dll</HintPath>
<Private>True</Private>
</Reference>
To:
<Reference Include="nunit.framework">
<HintPath>..\..\packages\NUnit\lib\nunit.framework.dll</HintPath>
<Private>True</Private>
</Reference>
The issue is that on mono, the packages don't have the version in the path but under Visual Studio they do. Once I found this fix, I created two .fsproj files for the tests and I modified the build.sh script to swap the mono-compatible one in when under mono via:
#!/bin/bash
if test "$OS" = "Windows_NT"
then
# no changes in here
else
# fix test fsproj file
mv tests/ProjectName.Tests/ProjectName.Tests.fsproj tests/ProjectName.Tests/ProjectName.Tests.fsproj.vs
mv tests/ProjectName.Tests/ProjectName.Tests.fsproj.mono tests/ProjectName.Tests/ProjectName.Tests.fsproj
# leave the script logic for mono in place
# put project files back to avoid git noticing the swap
mv tests/ProjectName.Tests/ProjectName.Tests.fsproj tests/ProjectName.Tests/ProjectName.Tests.fsproj.mono
mv tests/ProjectName.Tests/ProjectName.Tests.fsproj.vs tests/ProjectName.Tests/ProjectName.Tests.fsproj
fi
Once I made these changes the project works fine under both Visual Studio as well as mono.
I am not quite sure, I understand you: Do you have two fsproj files, one for the production code and one for the unit tests? And are you referencing FsCheck in the fsproj?
Plus, I remember an issue, where if you compile against an older version of .net and reference an assembly compiled against a newer version of .net, it will behave as though there was no reference.

How does one use the WebGreaseTask MSBuild Task from WebGrease?

In the documentation for WebGrease, it talks about using an MSBuild task to do minification at build time. However, when I add the task to my project file, it fails.
Here are the lines I've added to my project
<UsingTask TaskName="WebGreaseTask" AssemblyName="WebGrease, Version=1.6.5135.21930, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"/>
<Target AfterTargets="DotLessBuildTasksDotNet" Name="Minify">
<WebGreaseTask />
</Target>
But when I try to build, I get the following error:
The "WebGreaseTask" task could not be loaded from the assembly
WebGrease, Version=1.6.5135.21930, Culture=neutral,
PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL. Could not
load file or assembly 'WebGrease, Version=1.6.5135.21930,
Culture=neutral, PublicKeyToken=31bf3856ad364e35' or one of its
dependencies. The system cannot find the file specified. Confirm that
the declaration is correct, that the assembly and all its
dependencies are available, and that the task contains a public class
that implements Microsoft.Build.Framework.ITask.
As far as I know, that's the correct assembly name, as I'm using the same reference in my project:
<Reference Include="WebGrease, Version=1.6.5135.21930, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\WebGrease.1.6.0\lib\WebGrease.dll</HintPath>
</Reference>
And all of the code there is working fine.
I want to do the minification at compile time, which I believe is the purpose of the build task. I know there are a plethora of bundling and minification helpers out there, but I'd like to avoid pulling in more packages than I have to.
Edit:
I've tried the various NuGet commands, but those don't affect the UsingTask node in any way, and don't cause the build to work either. Still at a loss on this error.
Looking in the source code there is a class https://webgrease.codeplex.com/SourceControl/latest#WebGrease/WebGrease.Build/WebGreaseTask.cs which looks like the thing you need, but after decompiling my local WebGrease nuget package, I don't see this class or WebGrease.Build assembly there at all. As the error points out it can't find a class that implements ITask and this class exactly implements it.

Resources