Closed dsyme closed 5 years ago
This table can be used to record the current state of which "fsprojects" libraries are PCL. However in many cases moving to .NET Core will not be immediately possible due to non-portability or dependencies.
:white_check_mark: FSharp.Core.Fluent :white_check_mark: FSharp.Control.AsyncSeq :white_check_mark: FSharp.Collections.ParallelSeq :white_check_mark: FSharp.Quotations.Evaluator :white_check_mark: FSharp.Control.Reactive :white_check_mark: Tamarin
:white_check_mark: Unquote :white_check_mark: FParsec
:boom: FSharp.Compiler.CodeDom :boom: [ ] FSharp.Desktop.UI
Here are the approximate steps to add a PCL Portable build of an otherwise unremarkable F# library called FSharp.Foo
targeting .NET 4.x.
The aim is to make a nuget package containing both your existing .NET 4.x DLL and a portable DLL containing essentially the same functionality.
These notes were prepared on projects using FAKE and/or Paket. They should be basically valid with other build tools.
Note that we copy, edit and prepare project files directly. If you're not comfortable doing that then stop reading.
1 - Create directory src\FSharp.Foo.Portable
and copy src\FSharp.Foo\FSharp.Foo.fsproj
to src\FSharp.Foo.Portable\FSharp.Foo.Portable.fsproj
2 - Edit the portable project file to contain these magic lines, replacing corresponding existing lines
<PropertyGroup>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkProfile>Profile7</TargetFrameworkProfile>
<TargetProfile>netcore</TargetProfile>
<TargetFSharpCoreVersion>3.3.1.0</TargetFSharpCoreVersion>
</PropertyGroup>
...
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<OutputPath>..\..\bin\portable</OutputPath>
...
<DocumentationFile>..\..\bin\portable\FSharp.Collections.ParallelSeq.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
...
<OutputPath>..\..\bin\portable</OutputPath>
...
<DocumentationFile>..\..\bin\portable\FSharp.Collections.ParallelSeq.xml</DocumentationFile>
</PropertyGroup>
<Reference Include="FSharp.Core">
<Name>FSharp.Core</Name>
<AssemblyName>FSharp.Core.dll</AssemblyName>
<HintPath>$(MSBuildExtensionsPath32)\..\Reference Assemblies\Microsoft\FSharp\.NETCore\$(TargetFSharpCoreVersion)\FSharp.Core.dll</HintPath>
</Reference>
....
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.Portable.FSharp.Targets" />
Also change the file-references in the project file to be links referring across a directory, e.g. like this, e.g.
<Compile Include="Tools.fs">
becomes
<Compile Include="../FSharp.Foo/Tools.fs">
<Link>Tools.fs</Link>
</Compile>
3 - Add the portable project to the solution, either using IDE tooling or like this
4 - Build your project. If it doesn't build then your project may not be portable. Or you may need to use "reshaped reflection" if your project uses reflection, for example these helpers taken from FSharp.Core. There may be a better version of these helpers around for you to use.
5 - If you're game, try adjusting to Profile78 or Profile259. These are "even more portable" profiles, and may be necessary if you want your project consumable by most Xamarin tooling. The TargetFSharpCoreVersion
changes as follows (and you must change the moniker in the nuget package).
<TargetFSharpCoreVersion>3.78.3.1</TargetFSharpCoreVersion>
<TargetFSharpCoreVersion>3.259.3.1</TargetFSharpCoreVersion>
6 - Add the portable outputs to the nuget package using the correct moniker, like this if using nuget.exe and like this if using paket.exe. The monikers to use in the subdirectory in the nuget packages are
Profile7 - portable-net45+netcore45+MonoAndroid1+MonoTouch1
Profile78 - portable-net45+netcore45+wp8+MonoAndroid1+MonoTouch1
Profile259 - portable-net45+netcore45+wpa81+wp8+MonoAndroid1+MonoTouch1
7 - Check your nuget package. Try using it from a new F# portable profile 7 library (which should be able to refer to either profile 78 or profile 259 libraries too)
8 - Push your new nuget package
Note that it may be possible (and much nicer) to remove the .NET 4.x version of your package after you've made your PCL package. However test that path carefully and ensure that your portable version doesn't contain any less functionality
Don't forget fparsec. I think it is already converted to PCL. Though again without .NET native support we won't be able to run them as UWP apps :(
@ReverseBlade This thread only deals with "fsprojects" projects.
It would be great to make a more complete table, e.g. of everything under http://fsharp.org/community/projects. Also of the major C# libraries that F# community work sits on (e.g. Math.NET Numerics).
FParsec already is, although I have to admit I'm struggling to understand the relationships between NuGet monikers, profiles, etc. I've followed The Don Guide in sentiment rather than exactly, and ended up with Profile259 assemblies, although it is fiddly, and i'm not looking forward to trying this with a large many project codebase (Freya).
I do have a PR for FSharp.Control.Reactive that makes it Profile259. It needs a second pair of eyes. https://github.com/fsprojects/FSharp.Control.Reactive/pull/65
Here's the background research that I did on this topic, to save anyone else the effort:
FYI - Unquote has builds targeting .NET 4.0, .NET 4.5, and Portable Profile 259.
@stephen-swensen Great!
How do PCLs interact w/ .Net Core/CoreCLR?
THis is non-authoritative, but basically the important thing is
Profile259 => Profile78 => Profile7 => ".NET Standard 1.5+" => .NET Core 1.x+ and .NET Framework 4.5x+
So making something into a PCL 259/78/7 means it can be referenced from a .NET Standard library, and can be sued on both .NET Core and ,NET Framework.
Likewise making something into a .NET Standard library means it can be used on both .NET Core and ,NET Framework.
That's not authoritative, just my understanding of how things work. Eventually I'd expect most F# libraries to just be .NET Standard 1.something.
I believe currently the most 'ideal' profile is profile 151 at the moment.
Why can't the compiler handle this complexity? It could make it the 'most compatible' by default?
@haf The F# compiler (fsc.ex) doesn't really change much, the caller just points the compiler at different sets of reference binaries.
The complexity and churn here is unfortunate and really a question for the .NET designers. They've iterated on this multiple times, only recently settling on .NET Standard as the way of binary code sharing - and there will be another iteration on that.
@dsyme That's what I mean; can't the compiler than know about those sets without heavy-handed xml generated by opaque tooling. I'd like to specify a single input the to the compiler; e.g. a list of target "profiles": ["netcore15"; "most-portable-pcl"; "full-framework"]
?
@haf the tooling just resolve the path of assemblies (from gac or packages).
now all references are passed as normal file references, no more magic. there is a switch for --simpleresolution
in fcs.
For example the response file of the compiler (the arguments) to build net46
:
--temp-output:e:\temp\test78\obj\Debug\net46\
--out:e:\temp\test78\bin\Debug\net46\test78.dll
--define:DEBUG
--define:TRACE
--define:NET46
--optimize:False
--debug-type:portable
--output-name:test78
--file-version:1.0.0.0
--version:1.0.0.0
--informational-version:1.0.0
--target-framework:.NETFramework,Version=v4.6
--reference:C:\Users\e.sada\.nuget\packages\FSharp.Core\4.0.1.7-alpha\lib\net40\FSharp.Core.dll
--reference:C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6\mscorlib.dll
--reference:C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6\System.dll
--reference:C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6\System.Core.dll
--reference:C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6\Microsoft.CSharp.dll
e:\temp\test78\Library.fs
running fsc @responsefile.rsp
is going to build the lib
but for netstandard the list of referenced assemblies change too.. so something (the tooling) should choose. be that project.json
with dotnet core sdk, msbuild
or a bat file running fsc
The idea is netstandard is an upgraded version of pcl, so no more intersection of api (115, 259, etc), but an incremental api surface (netstandard1.0
-> netstandard1.6
), see https://gist.github.com/davidfowl/8939f305567e1755412d6dc0b8baf1b7
We should gradually make more "fsprojects" and other F# community libraries available as portable libraries (profile 7, 78 or 259). Portable libraries are consumable by both .NET Core and .NET Framework projects, as well as Windows apps and so on. Looking ahead it seems pretty important that we maximize the reach of F# libraries where possible. This will be a fairly long process but it will be worth it.
For example, we made FSharp.Control.AsyncSeq available as both a .NET 4.5 and Profile7 PCL in this PR. We could have made it only a Profile7 but decided to do both for the moment.