I recently started working on the F# integration with #Develop. #Develop is a free and open source IDE base that in many respects is very similar to Visual Studio. I actually found getting the basic integration going fairly easy, I have something that allows you to edit text files and build them. But as there’s not yet any colouring or auto-completion there no signification advantage over using that to any other text editor, so I’m not going to release it just yet, but watch this space.
However the first step in integrating F# into #Develop was to create an msbuild provider so F# project files could be built by msbuild. I believe this may be useful in their own right, so I'm presenting it here.
This is the C# source required, note that the MonoCompilerTask is a class provided by #Develop ICSharpCode.Build.Tasks.dll assembly (the source could have been written in F# but I hope to one to integrate this code into existing #Develop libraries this meant it had to be written in C#):
using System.IO;
using System.CodeDom.Compiler;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using Microsoft.Build.Tasks;
namespace ICSharpCode.Build.Tasks
{
public sealed class Fsc : MonoCompilerTask
{
MonoCSharpCompilerResultsParser parser = new MonoCSharpCompilerResultsParser();
string keyContainer, keyFile;
int fileAlignment;
public int FileAlignment {
get {
return fileAlignment;
}
set {
fileAlignment = value;
}
}
public string KeyContainer {
get {
return keyContainer;
}
set {
keyContainer = value;
}
}
public string KeyFile {
get {
return keyFile;
}
set {
keyFile = value;
}
}
protected override string ToolName {
get {
return "fsc.exe";
}
}
protected override string GenerateFullPathToTool()
{
//TODO be cleverer
return Path.Combine(@"C:\Program Files\FSharp-1.9.2.5\bin\", ToolName) ;
}
protected override string GenerateCommandLineCommands()
{
CompilerCommandLineArguments commandLine = new CompilerCommandLineArguments();
if (((OutputAssembly == null) && (Sources != null)) && ((Sources.Length > 0))) {
OutputAssembly = new TaskItem(Path.GetFileNameWithoutExtension(this.Sources[0].ItemSpec));
if (string.Equals(this.TargetType, "library", StringComparison.InvariantCultureIgnoreCase)) {
OutputAssembly.ItemSpec += ".dll";
} else if (string.Equals(this.TargetType, "module", StringComparison.InvariantCultureIgnoreCase)) {
OutputAssembly.ItemSpec += ".netmodule";
} else {
OutputAssembly.ItemSpec += ".exe";
}
}
commandLine.AppendSwitch("-g");
if (Optimize) {
commandLine.AppendSwitch("-O3");
}
commandLine.AppendSwitchIfNotNull("--keyfile ", this.KeyFile);
if (Resources != null) {
foreach (ITaskItem item in Resources) {
commandLine.AppendSwitchIfNotNull("--resource ", item);
}
}
if (FileAlignment > 0) {
AppendIntegerSwitch(commandLine, "--base-address ", FileAlignment);
}
commandLine.AppendSwitchIfNotNull("-o ", this.OutputAssembly);
if (string.Equals(this.TargetType, "library", StringComparison.InvariantCultureIgnoreCase)) {
commandLine.AppendSwitch("--target-dll");
}
else if (string.Equals(this.TargetType, "winexe", StringComparison.InvariantCultureIgnoreCase))
{
commandLine.AppendSwitch("--target-winexe");
}
else if (string.Equals(this.TargetType, "module", StringComparison.InvariantCultureIgnoreCase))
{
commandLine.AppendSwitch("--target-module");
}
if(References != null){
foreach (ITaskItem reference in References) {
commandLine.AppendFileNameIfNotNull("-r ", reference);
}
}
commandLine.AppendFileNamesIfNotNull(this.Sources, " ");
Console.WriteLine(commandLine.ToString());
return commandLine.ToString();
}
protected override CompilerError ParseLine(string line)
{
return parser.ParseLine(line);
}
}
}
Next an xml .target file is required to map the parameters the command should receive to our classes properties:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<UsingTask TaskName="ICSharpCode.Build.Tasks.Fsc" AssemblyFile="$(SharpDevelopBinPath)\ICSharpCode.Build.Tasks.dll"/>
<PropertyGroup>
<MSBuildAllProjects>$(MSBuildAllProjects);$(SharpDevelopBinPath)\SharpDevelop.Build.FSharp.targets</MSBuildAllProjects>
<DefaultLanguageSourceExtension>.fs</DefaultLanguageSourceExtension>
<Language>F#</Language>
</PropertyGroup>
<Target
Name="CoreCompile"
Inputs="$(MSBuildAllProjects);
@(Compile);
@(ReferencePath);
@(ManifestResourceWithNoCulture);
$(ApplicationIcon);
$(AssemblyOriginatorKeyFile);
@(ManifestNonResxWithNoCultureOnDisk);
@(ReferencePath);
@(CompiledLicenseFile)"
Outputs="@(DocFileItem);
@(IntermediateAssembly);
$(NonExistentFile)"
DependsOnTargets="$(CoreCompileDependsOn)"
>
<Fsc
DebugType="$(DebugType)"
EmitDebugInformation="$(DebugSymbols)"
FileAlignment="$(FileAlignment)"
KeyContainer="$(KeyContainerName)"
KeyFile="$(KeyOriginatorFile)"
Optimize="$(Optimize)"
OutputAssembly="@(IntermediateAssembly)"
Resources="@(ManifestResourceWithNoCulture);@(ManifestNonResxWithNoCultureOnDisk);@(CompiledLicenseFile)"
Sources="@(Compile)"
References="@(ReferencePath)"
TargetType="$(OutputType)"
/>
</Target>
<Import Project="$(SharpDevelopBinPath)\SharpDevelop.Build.Common.targets" />
</Project>
This will allow you to build F# MSbuild scripts of the following style (note the “import” tag which is very important as it tells msbuild were to find the .target file):
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProjectGuid>{5B3B2169-3435-437E-8860-155386CFA6BD}</ProjectGuid>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<OutputType>Exe</OutputType>
<RootNamespace>test</RootNamespace>
<AssemblyName>test</AssemblyName>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<OutputPath>bin\Debug\</OutputPath>
<DebugSymbols>True</DebugSymbols>
<DebugType>Full</DebugType>
<Optimize>False</Optimize>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<OutputPath>bin\Release\</OutputPath>
<DebugSymbols>False</DebugSymbols>
<DebugType>None</DebugType>
<Optimize>True</Optimize>
</PropertyGroup>
<Import Project="$(SharpDevelopBinPath)\SharpDevelop.Build.FSharp.Targets" />
<ItemGroup>
<Compile Include="Main.fs" />
</ItemGroup>
</Project>
You can download the source files here.