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

Help debugging F# project #123

Closed
NatElkins opened this issue Dec 14, 2019 · 15 comments
Closed

Help debugging F# project #123

NatElkins opened this issue Dec 14, 2019 · 15 comments
Labels
General Enhancement New feature or request

Comments

@NatElkins
Copy link

NatElkins commented Dec 14, 2019

Hi, I think I've seen in various comments that non-csproj files are not supported. That said, I don't think there's any fundamental reason why F# shouldn't be able to be supported. I'm trying to debug the reason why fsproj files don't work on my machine, I'm hoping someone can point me in the right direction.

Steps to repro:

  1. mkdir repro
  2. cd repro
  3. dotnet new console -lang F#
  4. dotnet package add Buildalyzer
  5. pwd <-- copy this.
  6. Open the generated Program.fs. Replace with the following:
open System
open Buildalyzer

[<EntryPoint>]
let main argv =
    let amo = AnalyzerManagerOptions(LogWriter=Console.Out)
    let manager = AnalyzerManager(amo)
    let analyzer = manager.GetProject("/path/from/running/pwd/repro.fsproj")
    let overallSuccess = analyzer.Build().OverallSuccess
    printfn "Overall Success: %b" overallSuccess
    0 // return an integer exit code
  1. dotnet run

After doing this, here's what I see:

nathaniel@nelknet-linux:~/Projects/buildalyzer-repro$ dotnet run

Started process 5338: "dotnet" --info
.NET Core SDK (reflecting any global.json):
 Version:   3.0.101
 Commit:    bc5f8df0f5
Runtime Environment:
 OS Name:     ubuntu
 OS Version:  18.04
 OS Platform: Linux
 RID:         ubuntu.18.04-x64
 Base Path:   /usr/share/dotnet/sdk/3.0.101/
Host (useful for support):
  Version: 3.0.1
  Commit:  19942e7199
.NET Core SDKs installed:
  2.2.402 [/usr/share/dotnet/sdk]
  3.0.101 [/usr/share/dotnet/sdk]
.NET Core runtimes installed:
  Microsoft.AspNetCore.All 2.2.8 [/usr/share/dotnet/shared/Microsoft.AspNetCore.All]
  Microsoft.AspNetCore.App 2.2.8 [/usr/share/dotnet/shared/Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 3.0.1 [/usr/share/dotnet/shared/Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 2.2.8 [/usr/share/dotnet/shared/Microsoft.NETCore.App]
  Microsoft.NETCore.App 3.0.1 [/usr/share/dotnet/shared/Microsoft.NETCore.App]
To install additional .NET Core runtimes or SDKs:
  https://aka.ms/dotnet-download
Process 5338 exited with code 0


Started process 5357: "dotnet" "/usr/share/dotnet/sdk/3.0.101/MSBuild.dll" /noconsolelogger /restore /target:Clean;Build /property:ProvideCommandLineArgs="true";GenerateResourceMSBuildArchitecture="CurrentArchitecture";DesignTimeBuild="true";BuildProjectReferences="false";SkipCompilerExecution="true";DisableRarCache="true";AutoGenerateBindingRedirects="false";CopyBuildOutputToOutputDirectory="false";CopyOutputSymbolsToOutputDirectory="false";SkipCopyBuildProduct="true";AddModules="false";UseCommonOutputDirectory="true";GeneratePackageOnBuild="false";NonExistentFile="__NonExistentSubDir__/__NonExistentFile__";SolutionDir="/home/nathaniel/Projects/buildalyzer-repro" /l:BuildalyzerLogger,"/home/nathaniel/Projects/buildalyzer-repro/bin/Debug/netcoreapp3.0/Buildalyzer.Logger.dll";106;False "/home/nathaniel/Projects/buildalyzer-repro/buildalyzer-repro.fsproj"
Microsoft (R) Build Engine version 16.3.2+e481bbf88 for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.
The specified task executable location "/usr/share/dotnet/sdk/3.0.101/fsc.exe" is invalid.Process 5357 exited with code 1

Overall Success: false

It seems to be failing to find /usr/share/dotnet/sdk/3.0.101/fsc.exe. That's because it doesn't exist. The actual path should be /usr/share/dotnet/sdk/3.0.101/FSharp/fsc.exe.

Do you know what is causing it to get the wrong path? Or how it can be corrected?

Thank you!

@daveaglick daveaglick added the General Enhancement New feature or request label Jan 11, 2020
@daveaglick
Copy link
Collaborator

Interesting, thanks a lot for the detailed step-by-step repro. That was very helpful since I don't know the first thing about F#.

The fact that MSBuild figures out this is an F# project looks for fsc.exe is probably a good sign (even if it's looking in the wrong place). If I had to guess I'd say there's a special build property that it's expecting to be set regarding SDK locations - the dotnet command and Visual Studio set several "hidden" properties that MSBuild sometimes relies on.

@daveaglick
Copy link
Collaborator

The funny thing is that MSBuild finds FSharp.Build.dll under the /FSharp folder just fine:
image

@daveaglick
Copy link
Collaborator

It's failing on this line in Microsoft.FSharp.targets:
image

because it can't find the Fsc task assembly. That assembly gets registered here in Microsoft.FSharp.targets:
image

That clearly registers FSharp.Build.dll as the assembly that contains the Fsc task (and that's confirmed by the binlog output in my previous comment). So it finds that assembly but then...? I don't yet understand why it's talking about a "task executable" of fsc.exe (maybe that's an internal error thrown by the Fsc task and not from MSBuild?). Still digging. Don't mind all the comments, it's just my version of rubber-ducky debugging :).

@daveaglick
Copy link
Collaborator

Okay, after a lot of trial and error and spelunking through the FSharp targets files here's what you have to do:

EnvironmentOptions options = new EnvironmentOptions
{
    DesignTime = false
};
analyzer.SetEnvironmentVariable("DOTNET_HOST_PATH", @"C:\Program Files\dotnet\dotnet.exe");

There were multiple problems - F# does not support design-time builds (at least not with the same properties as C# does) and setting those properties confuses it. Additionally the DOTNET_HOST_PATH needs to be set for the FSharp paths to calculate correctly.

I'll make it so both these get set out of the box for F# projects.

@jonsequitur
Copy link

dotnet-try also uses Buildalyzer. @brettfo Do you recall whether we did anything special for this?

@daveaglick
Copy link
Collaborator

It's funny, @mholo65 also just reported that F# works in dotnet-depends. I was definitely able to reproduce the problem with the minimum steps in the original post and was able to create a failing test case, so I wonder if the F# failures are recent (maybe with changes to the targets that shipped with 3.x SDK).

daveaglick added a commit that referenced this issue Jan 11, 2020
@daveaglick
Copy link
Collaborator

Thankfully the test case is working now, so we hopefully should know if F# support fails again in the future. I'll close this out, but please do follow-up @jonsequitur if you discover you were adding some special sauce - I could bake that in if so.

@NatElkins
Copy link
Author

Thank you for looking into this!

@daveaglick
Copy link
Collaborator

@NatElkins No problem - the latest is up on NuGet now so give it a try and let me know if F# projects are working for you.

@brettfo
Copy link

brettfo commented Jan 13, 2020

@daveaglick F# should support a regular design time build; if it doesn't can you file an issue on the dotnet/fsharp repo? I'd like F# to be able to do what C# can. The F# code @jonsequitur was referring to doesn't use Buildalyzer in that location, but in something entirely different that I'm almost ashamed to admit exists, if you're ultimately just trying to get the list of command line options that are eventually passed to fsc.exe (e.g., source files and references), I have a terrible (experimental) hack here that abuses MSBuild to get this information. The interesting bits are FSharp.Compiler.LanguageServer.DesignTime.proj and it's accompanying .targets file. State.fs mentioned above essentially invokes MSBuild twice and scrapes well-known output. The first command run is dotnet build path/to/FSharp.Compiler.LanguageServer.DesignTime.proj /p:ProjectFile=path/to/my-project.fsproj. This results in output like the following:

DetectedTargetFramework=net472
DetectedTargetFramework=netcoreapp2.1

Then the same command is executed, but this time with the additional TargetFramework variable: dotnet build path/to/FSharp.Compiler.LanguageServer.DesignTime.proj /p:ProjectFile=path/to/my-project.fsproj /p:TargetFramework=netcoreapp2.1. This then results in the command line args that would eventually be passed to fsc.exe:

DetectedCommandLineArg=Program.fs
DetectedCommandLineArgs=-r:path/to/assembly.dll
...

The command line arguments can then be separated into directives beginning with the - character (e.g., -r:path/to/assembly.dll) and everything else, which is likely a source file.

@bjorkstromm
Copy link
Contributor

@daveaglick nevermind my comment about it working for me (as it was with an older SDK). I've now verified that dotnet-depends is also broken when using .NET Core SDK 3.0 or 3.1. I'm seeing:
image

Verified that it works as expected with .NET Core SDK 2.1.

@NatElkins NatElkins reopened this Jan 13, 2020
@NatElkins
Copy link
Author

Oops closed by accident.

BTW, are reference assemblies a prerequisite of design time builds? If so, F# doesn't support reference assemblies. If not, ignore.

@daveaglick
Copy link
Collaborator

@brettfo That use of the DesignTime project is really something. Part crazy hack, part utterly brilliant. I think Buildalyzer can get at the full set of command-line args now, but without design-time support it's likely still not as performant as what you're doing.

Speaking of which, I tracked down the problems I had with design-time builds via MSBuild to setting SkipCompilerExecution to true. It wouldn't surprise me if VS passes some slightly different set of MSBuild properties for FSharp design-time builds - the properties I'm using to trigger a design-time build in C# are mostly reverse-engineered since they're not well documented. As for SkipCompilerExecution, turning it on in an FSharp project results in errors related to copying:

Started process 34492: "dotnet" "C:\Program Files\dotnet\sdk\3.1.100\MSBuild.dll" /noconsolelogger /restore /target:Clean;Build /property:ProvideCommandLineArgs="true";GenerateResourceMSBuildArchitecture="CurrentArchitecture";DesignTimeBuild="true";BuildProjectReferences="false";SkipCompilerExecution="true";DisableRarCache="true";AutoGenerateBindingRedirects="false";CopyBuildOutputToOutputDirectory="false";CopyOutputSymbolsToOutputDirectory="false";SkipCopyBuildProduct="true";AddModules="false";UseCommonOutputDirectory="true";GeneratePackageOnBuild="false";NonExistentFile="__NonExistentSubDir__\__NonExistentFile__";SolutionDir="C:\Code\daveaglick\Buildalyzer\tests\projects\FSharpProject" /l:BuildalyzerLogger,"C:\Code\daveaglick\Buildalyzer\tests\Buildalyzer.Tests\bin\Debug\netcoreapp2.1\Buildalyzer.Logger.dll";2668;True "C:\Code\daveaglick\Buildalyzer\tests\projects\FSharpProject\FSharpProject.fsproj"
Microsoft (R) Build Engine version 16.4.0+e901037fe for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.
Could not copy the file "C:\Code\daveaglick\Buildalyzer\tests\projects\FSharpProject\obj\Debug\netcoreapp3.1\FSharpProject.exe" because it was not found.Process 34492 exited with code 1

In the binlog it looks like the _CopySourceItemsToOutputDirectory is to blame, though it's getting the files to copy from CopyFilesToOutputDirectory:

image

Those targets exist in Microsot.Common.CurrentVersion.targets so I'm not totally clear on why they don't cause problems for C# design-time builds but do for F#. I kind of lost the trail at that point so I'm not even sure it's really an F# problem.

All that said, I'd love to get F# design-time working in Buildalyzer so it's equivalent with C#. Just not sure where to continue looking.

@daveaglick
Copy link
Collaborator

@mholo65 I'm assuming that failure is with the current version of dotnet-depends (without the most recent Buildalyzer)? Whenever you get a chance to update the Buildalyzer version let me know if it continues to have problems.

@daveaglick
Copy link
Collaborator

Since this is pretty old and there have been ongoing improvements, I'm going to assume this issue is either resolved or no longer relevant. Please comment or open a new issue if that's incorrect and you're still having trouble.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
General Enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

5 participants