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

FSharp.Compiler.Service uses latest TargetFramework when compiling scripts rather than environment. #14289

Open
roboz0r opened this issue Nov 10, 2022 · 11 comments
Labels
Bug Impact-Medium (Internal MS Team use only) Describes an issue with moderate impact on existing code.
Milestone

Comments

@roboz0r
Copy link

roboz0r commented Nov 10, 2022

Not sure if this is a bug or a feature request.

I am using FSharp.Compiler.Service to compile user created F# scripts. Up until today, the code was working as expected however after I updated to .NET 7 the script compilation succeeds but attempting to load/reflect into the script fails with:

Could not load file or assembly 'System.Runtime, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. The system cannot find the file specified.

After some investigation what seems to be happening is when FSharpChecker.Compile is given an fsx file, it implicitly creates an fsproj file with the TargetFramework=net7.0 (based on the latest SDK installed?). The rest of my solution is targeting net6.0 so it can't find the correct version of System.Runtime.

Repro steps

A concise repro is not simple. The key parameters are described below.

    let checker = FSharpChecker.Create()

    // https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/compiler-options
    let compilerArgs (scriptPath:string) (dllPath) = 
        [|
            "-a"; scriptPath
            $"-o:%s{dllPath}"
            "--targetprofile:netcore"
            "--target:library"
            "--debug:full"
            $"--compilertool:%s{Assembly.GetExecutingAssembly().DirectoryName}"
        |]

    let tryCompile scriptPath dllPath =
        async {
            let compilerArgs = compilerArgs scriptPath loadCtx.DllFullName
            let! errors, retCode = checker.Compile(compilerArgs)
            // omitted a bunch of code that handles copying of reference assemblies
            let assembly = // ... code loads the dll into its own AssemblyLoadContext
            let types = assembly.GetTypes() // calling this method throws the exception

As an attempted workaround I placed a global.json into the runtime directory:

{
    "sdk": {
      "version": "6.0.0",
      "rollForward": "latestMinor"
    }
}

Then with some scripts I get a compiler error. To cause this behaviour the script must have some dependencies to trigger the creation of a .fsproj on disk. #r "nuget: FSharp.Data, 5.0.2" seems to be sufficient for this. Simple scripts produce the same System.Runtime exception from above.

C:\Path\To\main.fsx (2,1)-(2,31) parse error C:\Program Files\dotnet\sdk\6.0.306\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.TargetFrameworkInference.targets(144,5): error NETSDK1045: The current .NET SDK does not support targeting .NET 7.0.  Either target .NET 6.0 or lower, or use a version of the .NET SDK that supports .NET 7.0. [C:\Path\To\bin\Projects\31412--0815da43-918d-4e85-baef-19d36d3e76bf\Project.fsproj]

Expected behavior

My initially expected behavior is that selecting a particular version of FSharp.Compiler.Service (I tried with 41.0.6 and 41.0.7) corresponding to FSharp.Core 6.0.6 and 6.0.7 would set the TargetFramework=net6.0 and FSharp.Compiler.Service 42.7.1 would set the TargetFramework=net7.0.

I would have also expected the compiler to consider the presence of global.json and set the TargetFramework based on that.

I searched extensively to see if there was some way to instruct FSharpChecker on what TargetFramework to use e.g. with compiler args. Possibly there could be an overload to Compile or new method added to FSharpChecker which provides some direct control over the implicitly generated fsproj file.

Known workarounds

None tested. Expect that uninstalling the latest .NET so that only .NET 6 SDKs were present on the system would succeed. My update to .NET 7 happened as part of automatic updates to Visual Studio.

Related information

Provide any related information (optional):

  • Operating system: Windows 10
  • dotnet --list-sdks
    5.0.104 [C:\Program Files\dotnet\sdk]
    6.0.111 [C:\Program Files\dotnet\sdk]
    6.0.202 [C:\Program Files\dotnet\sdk]
    6.0.203 [C:\Program Files\dotnet\sdk]
    6.0.306 [C:\Program Files\dotnet\sdk]
    7.0.100 [C:\Program Files\dotnet\sdk]
  • Editing Tools: Visual Studio 17.4.0
@vzarytovskii
Copy link
Member

hm, @KevinRansom does this ring a bell to you?

@TheAngryByrd
Copy link
Contributor

Regarding that dll missing error, we were getting a similar problem over in ProjInfo. (Just pointing it out as it might help diagnosing what's going on here)

@KevinRansom
Copy link
Member

looking

@abelbraaksma
Copy link
Contributor

abelbraaksma commented Nov 10, 2022

We are noticing something similar with homebrew, which apparently uninstalls the old sdk version by default (scary, but true). That doesn't seem to be the case here, but it gives the same kind of errors. The big difference being that dotnet --list-sdks will give you just .NET 7 (thanks to @peterfaria-lula for mentioning).

@KevinRansom
Copy link
Member

Yes, this is definitely an issue.

The behavior generating default references does not do what is expected. I did a bunch of cleanup around that code a month or two ago, I may have broken something. Although it may also have been broken a lot longer than that.

Here is a complete straightforward repro:
FCS_14289_Repro.zip

It produces this output:

scriptPath: C:\Users\codec\source\repos\FCS_14289_Repro\FCS_14289_Repro\script\repro_14289.fsx
dllPath:    C:\Users\codec\source\repos\FCS_14289_Repro\FCS_14289_Repro\bin\Debug\net6.0\repro_14289.dll
References:    System.Runtime, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
References:    FSharp.Core, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
References:    netstandard, Version=2.1.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51

System.Runtime should be 6.0.0.0 because of the presence of the global.json file with this specification:

{
  "sdk": {
    "version": "6.0.402"
  }
}

@abelbraaksma
Copy link
Contributor

Before 7.0 hit the market, I noticed that if you have "version": "6.0.0" that it always does a rollforward, picking up .NET 7 RC1 etc. My fix at the time was to use a specific version in your local global.json file with "version": "6.0.402", or even an older, but "legal/available" version.

Not sure it helps now, but to me it kinda confirms that this was broken longer ago. I blamed it on the RC status and never gave it much thought.

@KevinRansom
Copy link
Member

Okay I have improved the repro slightly to make the issue more clear:
FCS_14289_Repro.zip
This produces this output:
Note: It produces a reference to System.Runtime, 7.0.0.0 while definitely running on the net6.0 coreclr on a system with net 7.0.100 sdk installed**:

System.Private.CoreLib:   C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.11\System.Private.CoreLib.dll
scriptPath: C:\Users\codec\source\repos\FCS_14289_Repro\FCS_14289_Repro\script\repro_14289.fsx
dllPath:    C:\Users\codec\source\repos\FCS_14289_Repro\FCS_14289_Repro\bin\Debug\net6.0\repro_14289.dll
References:    System.Runtime, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
References:    FSharp.Core, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
References:    netstandard, Version=2.1.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51

@KevinRansom KevinRansom added Impact-High (Internal MS Team use only) Describes an issue with extreme impact on existing code. Regression Impact-Medium (Internal MS Team use only) Describes an issue with moderate impact on existing code. and removed Needs-Triage Regression Impact-High (Internal MS Team use only) Describes an issue with extreme impact on existing code. labels Nov 10, 2022
@KevinRansom
Copy link
Member

It may not be a regression then, indeed it may never have worked entirely as expected. It would be hard to see in an environment where we are not transitioning between two tfms net6.0 ==> net7.0.

However it is a feature that was designed and is supposed to work. I'll mark this as impact-medium and will work on finding a decent fix

@roboz0r
Copy link
Author

roboz0r commented Nov 11, 2022

Thanks for looking into this and producing a proper repro. I got to 11pm last night and couldn't handle creating a new project.

@albertwoo
Copy link

When I use FSharpChecker.Create(keepAssemblyContents = true).ParseAndCheckFileInProject for my target parsing project which is running in dotnet 7, I also got some error:

FSharp.Compiler.Diagnostics.FSharpDiagnostics

internal error: One or more errors occurred. (Assembly: The system type 'System.Array' was required but no referenced system DLL contained this type, full path: System.Array)

@sritharans
Copy link

@KevinRansom There seems to be a workaround to this issue, using a solution posted here:
#14250

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug Impact-Medium (Internal MS Team use only) Describes an issue with moderate impact on existing code.
Projects
Status: New
Development

No branches or pull requests

7 participants