Fable: type provider ResolutionFolder is empty

Created on 30 Jul 2019  路  7Comments  路  Source: fable-compiler/Fable

Description

I have a type provider (as do others) for use with Fable. It erases to strings, and works great for the most part.

However, there is one problem: during the Fable compilation process, the type provider's ResolutionFolder configuration property, which is supposed to be the .fsproj directory, does not get set.

Type providers are somehow instantiated with a TypeProviderConfig object like so: type MyTypeProvider (config:TypeProviderConfig) as this = ...

When compiled by Fable (or F# compiler services, or however they are interacting), the config object has the other properties set but not the ResolutionFolder. Consequently, at compilation time we have no way to find source data files with paths relative to project root, we can only find absolute paths.

Here is some debug logging from my type provider to illustrate this further.

At design time in VS Code it sets the ResolutionFolder:

Creating TypeProviderForNamespaces Zanaptak.TypedCssClasses.TypeProvider+CssClassesTypeProvider [0]
TypeProviderConfig.IsHostedExecution = false
TypeProviderConfig.IsInvalidationSupported = true
TypeProviderConfig.TemporaryFolder = C:\Users\zaphod\AppData\Local\Temp\
TypeProviderConfig.ResolutionFolder = c:\src\testTypedCss\src
TypeProviderConfig.RuntimeAssembly = c:\src\TypedCssClasses\src\bin\Debug\netstandard2.0\Zanaptak.TypedCssClasses.dll
TypeProviderConfig.ReferencedAssemblies count = 123
    C:\Users\zaphod\.nuget\packages\fsharp.core\4.6.2\lib\netstandard1.6\FSharp.Core.dll
    C:\Users\zaphod\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\Microsoft.Win32.Primitives.dll
    C:\Users\zaphod\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\mscorlib.dll
    C:\Users\zaphod\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\netstandard.dll
    C:\Users\zaphod\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.AppContext.dll
    ...
Environment.CommandLine = C:\Users\zaphod\.vscode\extensions\ionide.ionide-fsharp-4.0.6\bin_netcore\fsautocomplete.backgroundservices.dll 23332
Environment.CurrentDirectory = c:\src\testTypedCss

If I build with dotnet build .\src\App.fsproj, it sets the ResolutionFolder (also changing current working dir):

Creating TypeProviderForNamespaces Zanaptak.TypedCssClasses.TypeProvider+CssClassesTypeProvider [0]
TypeProviderConfig.IsHostedExecution = false
TypeProviderConfig.IsInvalidationSupported = false
TypeProviderConfig.TemporaryFolder = C:\Users\zaphod\AppData\Local\Temp\
TypeProviderConfig.ResolutionFolder = C:\src\testTypedCss\src
TypeProviderConfig.RuntimeAssembly = C:\src\TypedCssClasses\src\bin\Debug\netstandard2.0\Zanaptak.TypedCssClasses.dll
TypeProviderConfig.ReferencedAssemblies count = 123
    C:\Users\zaphod\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\netstandard.dll
    C:\Users\zaphod\.nuget\packages\fsharp.core\4.6.2\lib\netstandard1.6\FSharp.Core.dll
    C:\Users\zaphod\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\Microsoft.Win32.Primitives.dll
    C:\Users\zaphod\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\mscorlib.dll
    C:\Users\zaphod\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.AppContext.dll
    ...
Environment.CommandLine = "C:\Program Files\dotnet\sdk\3.0.100-preview5-011568\FSharp\fsc.exe" @C:\Users\zaphod\AppData\Local\Temp\tmp748d72f3b53a4861ad2f55279ffa7343.rsp
Environment.CurrentDirectory = C:\src\testTypedCss\src

If I build with webpack which runs Fable.Cli.dll, the folder is empty string (not null, in case it matters):

Creating TypeProviderForNamespaces Zanaptak.TypedCssClasses.TypeProvider+CssClassesTypeProvider [0]
TypeProviderConfig.IsHostedExecution = false
TypeProviderConfig.IsInvalidationSupported = false
TypeProviderConfig.TemporaryFolder = C:\Users\zaphod\AppData\Local\Temp\
TypeProviderConfig.ResolutionFolder is empty string
TypeProviderConfig.RuntimeAssembly = C:/src/TypedCssClasses/src/bin/Debug/netstandard2.0/Zanaptak.TypedCssClasses.dll
TypeProviderConfig.ReferencedAssemblies count = 120
    C:/Users/zaphod/.nuget/packages/netstandard.library/2.0.3/build/netstandard2.0/ref/netstandard.dll
    C:/Users/zaphod/.nuget/packages/fsharp.core/4.6.2/lib/netstandard1.6/FSharp.Core.dll
    C:/Users/zaphod/.nuget/packages/netstandard.library/2.0.3/build/netstandard2.0/ref/System.Xml.XPath.XDocument.dll
    C:/Users/zaphod/.nuget/packages/netstandard.library/2.0.3/build/netstandard2.0/ref/System.Xml.XPath.dll
    C:/Users/zaphod/.nuget/packages/netstandard.library/2.0.3/build/netstandard2.0/ref/System.Xml.XmlSerializer.dll
    ...
Environment.CommandLine = C:\src\testTypedCss\node_modules\fable-compiler\bin\fable-cli\Fable.Cli.dll start-stdin --silent undefined
Environment.CurrentDirectory = C:\src\testTypedCss

I started to poke around the Fable codebase but quickly got lost so I'm asking here. Do you think it might be possible to figure out how to set this property correctly? I can also provide a test project if necessary if someone is willing and able to dig further.

Related information

  • Fable version: 2.3.14
  • Operating system: Windows 10

Most helpful comment

Can Fable run a standalone fsx script outside of a project? If so, then possibly it should be conditional based on script vs. project context.

But at least for a structured project it should be the project dir like FCS. That will be consistent with:

  • IDE behavior
  • dotnet build behavior (if I have a .NET Giraffe project and a Fable project, a TP in either should resolve paths consistently as project-relative)
  • the tutorial doc:

Accessing Project-Local or Script-Local Resources
Each instance of a type provider can be given a TypeProviderConfig value during construction. This value contains the "resolution folder" for the provider (that is, the project folder for the compilation or the directory that contains a script), the list of referenced assemblies, and other information.

According to above, it is understood that project context and script context receive different configuration, with script context being the script directory. I have confirmed this by running dotnet fsi dir1/dir2/file.fsx, and my TP in file.fsx received the dir1/dir2 path, not the original current directory of the FSI launch. I suspect your example of FSI using current directory is either launching without a script, or if with a script, then it has probably previously chdir'd to the script dir.

In summary:

  • If compiling a project, set it to the project directory.
  • If compiling a script file without a project context, set it to the script directory.

All 7 comments

Interesting, didn't know about that property. Fable contains almost no code specific for type providers, as they're basically resolved by the FSharp.Compiler.Service. If it works with VS Code it should work with Fable too. Maybe we're using an older version but I cannot see any recent fix related to this in dotnet/fsharp repo.

@zanaptak Yes, a test project would help, if you have one.

@ncave @alfonsogarciacaro I have added a test project, clone my repo and check the TestWithFable instructions.

@zanaptak @alfonsogarciacaro

Looks like the ResolutionFolder property is set from the TcConfigBuilder.implicitIncludeDir property. Currently we're not setting this property, and the initial value is empty string.

Looks like FSI sets it to the current folder:

tcConfigB.implicitIncludeDir <- Directory.GetCurrentDirectory()

but FCS sets it to the project folder:

tcConfigB.implicitIncludeDir <- projectOptions.ProjectDirectory

Let me know which one you think is better, current dir or project path, and I'll make the change.

Can Fable run a standalone fsx script outside of a project? If so, then possibly it should be conditional based on script vs. project context.

But at least for a structured project it should be the project dir like FCS. That will be consistent with:

  • IDE behavior
  • dotnet build behavior (if I have a .NET Giraffe project and a Fable project, a TP in either should resolve paths consistently as project-relative)
  • the tutorial doc:

Accessing Project-Local or Script-Local Resources
Each instance of a type provider can be given a TypeProviderConfig value during construction. This value contains the "resolution folder" for the provider (that is, the project folder for the compilation or the directory that contains a script), the list of referenced assemblies, and other information.

According to above, it is understood that project context and script context receive different configuration, with script context being the script directory. I have confirmed this by running dotnet fsi dir1/dir2/file.fsx, and my TP in file.fsx received the dir1/dir2 path, not the original current directory of the FSI launch. I suspect your example of FSI using current directory is either launching without a script, or if with a script, then it has probably previously chdir'd to the script dir.

In summary:

  • If compiling a project, set it to the project directory.
  • If compiling a script file without a project context, set it to the script directory.

Works great after the update, thanks. 馃憤

Was this page helpful?
0 / 5 - 0 ratings

Related issues

krauthaufen picture krauthaufen  路  3Comments

funlambda picture funlambda  路  4Comments

tomcl picture tomcl  路  4Comments

rommsen picture rommsen  路  3Comments

ncave picture ncave  路  3Comments