C#, Mono, Nuget, Nunit, T4, CI, Oh My!

In all my experiences, I have had no need to run C# code on linux. This has recently changed as the demographics for my side project, Pdoxcl2Sharp has expanded to include linux. Being the ambitious developer that I am, I decided that not only can people use the code on linux, but also develop the code without any problems. Turns out there are a lot of problems. This is my story.

Pdoxcl2Sharp makes use of Nuget for dependencies, Nunit for tests, T4 for templates, and Travis-CI for continuous integration. Each one of these is a roadblock in development.

Mono

Make sure you have a recent version of Mono. If you’re on Ubuntu 14.04+ you are good to go just sudo apt-get install mono-devel mono-gmcs. Many aren’t on 14.04 yet, so you can add a PPA. I used directhex/monoxide (sudo add-apt-repository ppa:directhex/monoxide), but there others; Nancy uses badgerports.org. If this isn’t your style you can take the route that F# projects take and download the latest MDK.

All of these options will install xbuild, which is an msbuild replacement. Simply call xbuild <proj> to build. Before we can build successfully there are requirements we are missing: dependencies.

Nuget

Nuget is great for dependency management, but if you don’t have the latest version it can be annoying. I didn’t realize it but Nuget can only restore on linux recently, and I didn’t have a recent version. I kept getting build errors telling me I didn’t set Visual Studio to the correct options. I didn’t think to immediately download a new version of Nuget, as I thought dependency managers wouldn’t need to be updated! I was thrown off because I keep Nuget inside a .nuget folder inside my source control and it turns out when Visual Studio updates Nuget it doesn’t the version in the source control, only its internal version. The solution ended up being quite easy.

Nunit

An important of any workflow are tests. Setting up Nunit is impossibly easy: sudo apt-get nunit-console. After the build succeeds, run the tests by invoking nunit-console <Proj.Test.dll>

Travis-CI

A build server will have a clean slate and in order for Nuget to restore packages it must access the outside word. Well, by default many machines don’t trust where Nuget gets it’s sources, so you have to add them explicitly.

sudo mozroots --import --machine --sync
yes | sudo certmgr -ssl -m https://go.microsoft.com
yes | sudo certmgr -ssl -m https://nugetgallery.blob.core.windows.net
yes | sudo certmgr -ssl -m https://nuget.org

The command yes is piped to the other commands because they normally wait for user input.

T4

I’ve saved the best (worst?) for last. I still don’t have the perfect solution, so I will take suggestions. The goal is have a cross platform way using xbuild, msbuild, and Visual Studios. If there is only one T4 and it is relatively small, you might just be better off committing the autogenerated code to source control. This way there are no added dependencies – you just git a weird feeling in your stomach.

If you have a much larger T4 like my project, EU4.Savegame, where committing the autogenerated content will be an extra 5000 lines to keep track of, it may be better off generating the content on each build. I will warn you that if your T4 template contains the import name="CustomProj.dll then this solution will result in Visual Studio doing no-op on save. To accomplish this, follow these steps:

<Target Name="BeforeBuild">
  <Exec 
    Condition=" '$(OS)' != 'Windows_NT'"
     WorkingDirectory="$(SolutionDir)\dependencies"
     Command="mono $(SolutionDir)dependencies\t4\TextTransform.exe \
              -o $(MSBuildProjectDirectory)\ParseTemplate.cs \
               $(MSBuildProjectDirectory)\ParseTemplate.tt" />
  <Exec 
    Condition=" '$(OS)' == 'Windows_NT'"
     WorkingDirectory="$(SolutionDir)\dependencies"
     Command="$(SolutionDir)dependencies\t4\TextTransform.exe \
              -o $(MSBuildProjectDirectory)\ParseTemplate.cs \
              -r $(FrameworkDir)$(FrameworkVersion)\System.Linq.dll \
              $(MSBuildProjectDirectory)\ParseTemplate.tt" />
</Target>

Conclusion

Honestly the hardest thing about getting a workflow going on linux was finding the information. I hope this will provide enough info for others to start developing C# on linux!

Comments

If you'd like to leave a comment, please email [email protected]