-
Notifications
You must be signed in to change notification settings - Fork 831
Description
Recent PRs in the Type Provider SDK repo (e.g. fsprojects/FSharp.TypeProviders.SDK#139) are a big step towards completing our type provider story for .NET Core With this work, properly written type providers using the latest version of the TPSDK can now always produce target code for either .NET Core or .NET Framework – i.e. they properly adjust for the target reference assemblies being used by the compilation. This applies to both generative and erasing type providers.
One final piece of the type provider puzzle is to have the F# compiler load an appropriate design-time DLL depending on whether the compiler is running in .NET Core or .NET Framework.
Currently, the compiler looks for design time DLL alongside the referenced runtime DLLs. (For simple type providers, these DLLs are the same)
We will do something like what is specified her https://docs.microsoft.com/en-us/nuget/schema/analyzers-conventions,
Rough proposal
When a referenced assembly as the usual TypeProviderAssembly attribute, indicating it wants design-time type provider component “MyDesignTime.dll”, then
- When executing using .NET Core the compiler looks in this order
...\typeproviders\fsharpNN\netcoreapp2.0\ARCH\MyDesignTime.dll
...\typeproviders\fsharpNN\netcoreapp2.0\MyDesignTime.dll
...\typeproviders\fsharpNN\netstandard2.0\ARCH\MyDesignTime.dll
...\typeproviders\fsharpNN\netstandard2.0\MyDesignTime.dll
MyDesignTime.dll
- When executing using .NET Framework the compiler looks in this order
...\typeproviders\fsharpNN\net461\ARCH\MyDesignTime.dll
...\typeproviders\fsharpNN\net461\MyDesignTime.dll
...\typeproviders\fsharpNN\netstandard2.0\ARCH\MyDesignTime.dll
...\typeproviders\fsharpNN\netstandard2.0\MyDesignTime.dll
MyDesignTime.dll
relative to the location of the runtime DLL, which is presumed to be in a nuget package.
-
When we use
...we mean a recursive upwards directory search looking for a directory namestypeproviders, stopping when we find a directory namepackages -
WHen we use
fsharpNNwe mean a successive search backwards forfsharp42,fsharp41etc. Putting a TPDTC infsharp41means the TPDTC is suitable to load into F# 4.1 tooling and later, and has the right to minimally assume FSharp.Core 4.4.1.0
Some examples:
The idea is that a package can contain a type provider design-time for both .NET Core and .NET Framework. This will allow it to load into both compiler, F# Interactive and tools running .NET Framework OR .NET Core.
Example 1 - the ultimate simplest form
Going forward, the very simplest type providers will eventually use a single .NET Standard 2.0 DLL for both the runtime library and the design-time component. Layout:
lib\netstandard2.0\MyRuntimeAndDesignTime.dll
As of today however you will also need to ship facades for netstandard.dll, System.Runtime.dll and System.Reflection.dll alongside this component and as such you should either
- adopt one of the layouts below to ensure facade extra files don't end up in the
libdirectory, OR - put the facades in a well-known relative location (not under the lib directory in the package) and added AssemblyResolve events to go and find them
Example 2 - FSharp.Data
FSharp.Data would lay out as follows:
lib\net45\FSharp.Data.dll
lib\netstandard2.0\FSharp.Data.dll
lib\portable-whatever\FSharp.Data.dll
typeproviders\fsharp40\net461\FSharp.Data.DesignTime.dll
typeproviders\fsharp40\netcoreapp2.0\FSharp.Data.DesignTime.dll
Here we are assuming FSharp.Data wants to use different design time DLLs for the two host tooling contexts we care about. Example 3 deals with the case where FSHarp.Data wants to use a single DLL.
Example 3 - FSharp.Data (with .NET Standard design-time DLL)
A layout like this may also be feasible if shipping facades to create a .NET Standard 2.0 TPDTC
lib\net45\FSharp.Data.dll
lib\netstandard2.0\FSharp.Data.dll
lib\portable-whatever\FSharp.Data.dll
typeproviders\fsharp41\netstandard2.0\FSharp.Data.DesignTime.dll
See note on facades above.
Example 4 - type providers with 32/64-bit dependencies
A Python type provider may have different dependencies for 32 and 64-bit for both runtime and design-time (the directory names may not be exactly right)
lib\netstandard2.0\x86\FPython.Runtime.dll
lib\netstandard2.0\x64\FPython.Runtime.dll
typeproviders\fsharp41\netstandard2.0\x86\FPython.Runtime.dll
typeproviders\fsharp41\netstandard2.0\x86\cpython32.dll # some 32-bit DLL needed to run at design-time
typeproviders\fsharp41\netstandard2.0\x64\FPython.Runtime.dll
typeproviders\fsharp41\netstandard2.0\x64\cpython64.dll # some 64-bit DLL needed to run at design-time
plus facades as mentioned above
Non Example 5
Going forward, we should not be happy with type provider packages that look like this - these will be unusable when the compiler executes using .NET Core
lib\net45\MyTypeProvider.dll
or even
lib\net45\MyTypeProvider.dll
typeproviders\fsharp40\net45\MyTypeProvider.DesignTime.dll
Again if your TPDTC component is a solitary NET Framework component then it will be unusable when a host tool executes using .NET Core.
Remember, we must be able to load type provider design-time DLLs into many different tooling contexts, notable
- FsAutoComplete.exe .NET Framework running 64-bit
- fsc.exe running on .NET Core 2.0 as either 64-bit or 32-bit
- fsi.exe running on .NET Framework as 32-bit
- fsiAnyCpu.exe running on .NET Framework as either 64-bit or 32-bit
- fsc.exe running on .NET Framework as either 64-bit or 32-bit
- devenv.exe running on .NET Framework 32-bit
and any context that uses FSharp.Compiler.Service.dll as either netstandard 2.0 or a .NET Framework component.
Note the above is effectively a proposed package architecture for any cross-generating compiler plugins. If we ever support other kinds of compiler plugins then we could follow a similar pattern.