Skip to content

Migrate fsproj To .NET CLI

Enrico Sada edited this page Jun 17, 2016 · 8 revisions

OBSOLETE

DOESNT WORK WITH PREVIEW1 WITHOUT CHANGES, is not updated, but the procedure is the same

SEE SUAVE AS EXAMPLE

The idea is not to convert everything to project.json

It's possibile to:

Contents

  1. Convert existing fsproj to project.json
  2. Create a test project to smoke check the library
  3. add NET Core framework
  4. Fix all build errors
  5. add NET Core framework to test project
  6. add Appveyor build using .NET CLI
  7. add Travis build using .NET CLI

Summary

The idea is to:

  • convert fsproj to .NET CLI and project.json
  • build the project with .NET CLI using framework .NET 4.6
  • create a smoke test to check .NET 4.6 works
  • add .NET Core (netstandard1.5) framework
  • fix errors and recheck with same test project

This project use FsCheck as an example

Other sources are:

Older migrations, using deprecated dnxcore50 framework (but .NET Full => .NET Core errors are the same)

## Convert existing fsproj to project.json

The project.json it's parallel to .fsproj, you can add it without changing the fsproj

Create the project.json is the same directory of fsproj

  1. cd in the fsproj directory

  2. exec dotnet new --lang f# to create a new project with project.json ( 37bbdcf )

    Expected:

    Created new F# project in e:\github\FsCheck\src\FsCheck.
    
  3. Remove the generated file Program.fs (the main of console app) ( 781e72c )

    Remove also the "Program.fs", from the property compileFiles in project.json

    The useful files are:

    • Nuget.config contains the dev nuget feed of .NET cli and F#
    • project.json the project file, like the old .fsproj
  4. If it's a library, remove the "emitEntryPoint": true from project.json ( a762f8d )

    The emitEntryPoint property specify if a project is a console app (true) or a library (false or not specified)

Add the source files in project.json

Add all the files from fsproj inside the compileFiles property in project.json ( 99de1c2 )

The source files are these with xml element <Compile />

<Compile Include="Utils.fs" />

to property compileFiles in project.json

"Utils.fs",

TIP If a file has \\ in path, use / instead, less problem with escaping json
NOTE All path are relative to project.json, like msbuild .fsproj
NOTE The <Link> msbuild property is not supported by project.json, it use the file system tree, ignore it

[OPTIONAL] Add the .NET 4.6 framework

Let's add back the .NET 4.6 references

  1. Move the dependencies property inside netstandard1.5 ( a542f4f )

    "dependencies": {
      "Microsoft.FSharp.Core.netcore": "1.0.0-alpha-160316",
      "Microsoft.NETCore.App": {
        "type": "platform",
        "version": "1.0.0-rc2-23925"
      }
    },

    into framework.netstandard1.5

    "frameworks": {
      "netstandard1.5": {
        "dependencies": {
          "Microsoft.FSharp.Core.netcore": "1.0.0-alpha-160316",
          "Microsoft.NETCore.App": {
            "type": "platform",
            "version": "1.0.0-rc2-23925"
          }
        },
        "imports": [
          "portable-net45+win8",
          "dnxcore50"
        ]
      }
    }
    

    NOTE project.json doesnt support comments

  2. In project.json add net46 inside frameworks ( 0cc9c8a )

    Now frameworks is like

    "frameworks": {
        "net46": { },
        "netstandard1.5": {
  3. Add all framework (GAC) reference of the .fsproj to project.json ( c9e8d64 )

    Only framework assemblies (no nugets) Except mscorlib, FSharp.Core add all <Reference /> to frameworkAssemblies under net46

    An example:

    From

    <Reference Include="System" />
    <Reference Include="System.Core" />
    <Reference Include="System.Xml" />

    To

    "net46": { 
        "frameworkAssemblies": {
            "System": "",
            "System.Core": "",
            "System.Xml": ""
        }
    }
  4. Add FSharp.Core and nuget packages ( 84daf2f )

    This example use the FSharp.Core nuget package, not the GAC version You can use the GAC version inside frameworkAssemblies, but it's better use the nuget package

    Add a dependency property under net46 with all packages and version

    An example:

    "net46": { 
        "dependencies": {
            "FSharp.Core": "4.0.0.1"
        },
        "frameworkAssemblies": {

    Add also the used nuget packages

    NOTE the order of properties or packages doesnt matter, but specific version yes
    TIP the fsproj or nuget packages.config or paket.lock contains the exact version used

  5. The .NET 4.6 version is ready, build it!

    • cd in the project directory

    • dotnet restore should restore all packages

      Expected output (and exit code 0):

      log  : Restoring packages for e:\github\FsCheck\src\FsCheck\project.json...
      info : Committing restore...
      log  : Writing lock file to disk. Path: e:\github\FsCheck\src\FsCheck\project.lock.json
      log  : e:\github\FsCheck\src\FsCheck\project.json
      log  : Restore completed in 2201ms.
      
      NuGet Config files used:
         e:\github\FsCheck\src\FsCheck\NuGet.Config
         C:\Users\e.sada\AppData\Roaming\NuGet\NuGet.Config
      
      Feeds used:
         https://dotnet.myget.org/F/dotnet-core/api/v3/index.json
         https://api.nuget.org/v3/index.json
         https://www.myget.org/F/fsharp-daily/api/v3/index.json
      
    • dotnet build --framework net46 to compile the project using only the .NET 4.6 framework

      NOTE dotnet build build all frameworks inside project.json NOTE to build only net46 use --framework, like dotnet build --framework net46

      Expected output (and exit code 0):

      Compiling FsCheck for .NETFramework,Version=v4.6
      
      Compilation succeeded.
          0 Warning(s)
          0 Error(s)
      
      Time elapsed 00:00:13.6352964
      
    • add ignore of project.lock.json (in .gitignore), it should never go inside repository ( 4bfa34c )

    • Check if file <projectdir>/bin/Debug/net46/<projectdirname>.dll exists

      NOTE the name of assembly is always the name of directory with a project.json project
      TIP if you want to change the name, create a dir with the new name and reference files with relative path in project.json

## [Optional] Create a test project to smoke check the library

Let's create a test project to smoke test the library.
It's not the full suite of test, only to check it's working

TIP It's possibile to move it in another directory (like <root>/test ) when the migration is done
TIP You can use also dotnet test with xunit, see the page dotnet-test

  1. Create in the parent directory a new directory <projectname>.Dotnetcli.Tests

  2. cd inside that directory

  3. dotnet new --lang f# to create the project ( 4409899 )

  4. remove dependencies and change framework to net46

  5. Add a project reference to the project ( 0fd7fc1 )

    In the project.json of test project, add the reference to project (in the example is FsCheck)

    "frameworks": {
        "net46" : { 
            "dependencies": {
                "FsCheck":  { "version": "1.0.0", "type": "build" }
            }
        }
    }
  6. Build it!

    • in the test project directory

    • dotnet restore

    • dotnet build

      The expected output ( see the FsCheck project is rebuilt if needed ):

      Project FsCheck (.NETFramework,Version=v4.6) was previously compiled. Skipping compilation.
      Compiling FsCheck.Dotnetcli.Tests for .NETFramework,Version=v4.6
      
      Compilation succeeded.
         0 Warning(s)
         0 Error(s)
      
      Time elapsed 00:00:05.3692070
      
    • Run it! with some arguments a and b

      The built binary is in the bin/<configuration>/<framework>/<os>/<projectname>.exe

      TIP you can execute the .exe directly or use dotnet run --framework net46 -- a b

      Execute:

      .\bin\Debug\net46\win7-x64\FsCheck.Dotnetcli.Tests.exe a b

      Output:

      Hello World!
      [|"a"; "b"|]
      
    • Add your specific tests ( 303519c )

    • Build and run the test project to check it's working

## add NET Core framework
  1. Move back to the project directory

  2. If Library, fix in project.json the dependencies of .NET Core framework (netstandard1.5 alias) ( ec177ae )

    Instead of Microsoft.NETCore.App use the NETStandard.Library

    NOTE the version of NETStandard.Library is 1.5 not 1.0 (1.5 like netstandard1.5)

    "frameworks": {
      "net46": { 
        ...
      },
      "netstandard1.5": {
        "dependencies": {
          "Microsoft.FSharp.Core.netcore": "1.0.0-alpha-160316",
          "NETStandard.Library": "1.5.0-rc2-23925"
        },
        "imports": [
          "portable-net45+win8",
          "dnxcore50"
        ]
      }
  3. Copy the net46 nuget dependencies in the dependancies node

    NOTE it's ok the FSharp.Core package name is different TIP if a package doesnt support netcore, maybe there is a dev built package, ask package team TIP You can add additional nuget feeds inside Nuget.config

  4. The project.json is changed, so redo dotnet restore

  5. Now when can try to build it, do dotnet build to build all configuration

    TIP dotnet build build all frameworks
    TIP to build only netstandard1.5 use --framework, like dotnet build --framework netstandard1.5

  6. Now the build should fail with errors, it's ok

    • maybe some reference are missing (for example System.Xml is not in standard library anymore)
    • .NET Desktop <-> .NET Core differences
## Fix all build errors

See more info in the Migration-NETCore-Common-Issues

  1. Fix all missing packages

    You can check all packages used in project.json.lock

    • add packages in the dependancies under netstandard1.5 in project.json
    • dotnet restore required because project.json changed
    • dotnet build
    • GOTO 10

    TIP version suffix is usually the same of NETStandard.Library, so in this example is -rc2-23925 NOTE check the dotnet restore output

    For example in another conversion (Argu) adding "System.Xml.XmlDocument": "4.0.0-rc2-23811", but got the warning

    warn : Dependency specified was System.Xml.XmlDocument (>= 4.0.0-rc2-23811) but 
    ended up with System.Xml.XmlDocument 4.0.1-rc2-23811.
    

    That mean you can change the version inside project.json to 4.0.1-rc2-23811 directly

  2. Fix all code errors ( e983b06 )

    NOTE You can use compiler directive NETSTANDARD1_5 to #if the code for framework netstandard1.5 NOTE each framework define an upper case alias, so net46 define NET46 compiler directive

    If you change only the source files (.fs), you can do dotnet build only

    For big missing features, it's easier to #ifdef out the not supported code

    #if NETSTANDARD1_5
       //netcore
    #else
       //else
    #endif

    TIP F# 4.0 syntax help #if !NETSTANDARD1_5 TIP F# 4.0 syntax help #if PCL || NETSTANDARD1_5

  3. If there strange errors, f# netcore is beta, so submit a bug report in the Microsoft/visualfshar repo

## add NET Core framework to test project
  1. Add netstandard1.5 framework and dependancies ( 5ce86a0 )

"frameworks": { "netstandard1.5" : { "dependencies": { "FsCheck": { "version": "1.0.0", "type": "build" }, "Microsoft.NETCore.App": { "type": "platform", "version": "1.0.0-rc2-23925" } }, "imports": [ "portable-net45+win8", "dnxcore50" ] }, "net46" : {


1. `cd` in test project directory
1. `dotnet restore`
1. `dotnet build` to check it's ok (it' optional because `dotnet run` build too)
1. `dotnet run --framework netstandard1.5 -- a b` run with arguments `a` `b`

<a name="#appveyor" />
## Add Appveyor build using .NET CLI

See https://github.com/enricosada/fsharp-dotnet-cli-samples/blob/master/appveyor.yml for a working version 

the script download an install the binaries in a subdirectory `.dotnetcli`, and add it to `PATH`

<a name="#travisci" />
## Add Travisci build using .NET CLI

See https://github.com/enricosada/fsharp-dotnet-cli-samples/blob/master/.travis.yml for a working version 

the script download an install the binaries in a subdirectory `.dotnetcli`, and add it to `PATH`
Clone this wiki locally