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

type provider ResolutionFolder is empty #1868

Closed
zanaptak opened this issue Jul 30, 2019 · 7 comments
Closed

type provider ResolutionFolder is empty #1868

zanaptak opened this issue Jul 30, 2019 · 7 comments

Comments

@zanaptak
Copy link
Contributor

zanaptak commented Jul 30, 2019

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
@alfonsogarciacaro
Copy link
Member

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.

@alfonsogarciacaro
Copy link
Member

Maybe it has do with the options we're passing to the F# compiler?

let getFullProjectOpts (define: string[]) (rootDir: string) (projFile: string) =
let projFile = Path.GetFullPath(projFile)
if not(File.Exists(projFile)) then
failwith ("File does not exist: " + projFile)
let projRefs, mainProj = retryGetCrackedProjects define projFile
let fableLibraryPath, pkgRefs =
copyFableLibraryAndPackageSources rootDir mainProj.PackageReferences
let projOpts =
let sourceFiles =
let pkgSources = pkgRefs |> List.collect getSourcesFromFsproj
let refSources = projRefs |> List.collect (fun x -> x.SourceFiles)
pkgSources @ refSources @ mainProj.SourceFiles |> List.toArray |> removeFilesInObjFolder

@ncave
Copy link
Collaborator

ncave commented Jul 30, 2019

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

@zanaptak
Copy link
Contributor Author

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

@ncave
Copy link
Collaborator

ncave commented Aug 3, 2019

@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.

@zanaptak
Copy link
Contributor Author

zanaptak commented Aug 3, 2019

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.

@zanaptak
Copy link
Contributor Author

zanaptak commented Aug 6, 2019

Works great after the update, thanks. 👍

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

No branches or pull requests

3 participants