SharpDevelop ships with two custom MSBuild tasks for Mono, one for Mcs
and the other for Gmcs, both of which live in the
ICSharpCode.Build.Tasks assembly. With the recent Mono release,
1.1.9, the command line options and compiler error message formats are
the same for Mcs and Gmcs, earlier versions differed. This means
that the Mcs and Gmcs classes do not do much apart from determine the
name of the compiler to use. The MonoCompilerTask class does the
hard work of compiling the code and reporting error messages back.
Simple C# Console Project
HelloMonoWorld.csproj:
<Project DefaultTargets="Build"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<OutputType>Exe</OutputType>
<AssemblyName>HelloMonoWorld</AssemblyName>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<OutputPath>bin</OutputPath>
</PropertyGroup>
<ItemGroup>
<Compile Include="Main.cs" />
<Compile Include="AssemblyInfo.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
</Project>
Main.cs:
using System;
namespace HelloMonoWorld
{
class MainClass
{
public static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
}
Running MSBuild, passing the .csproj as a command line argument, will
build the console app using Microsoft's Csc and targets the .NET
Framework 2.0.
Using the Mcs Task Directly
Let us modify the project file so it calls the Mcs task directly.
I am assuming that the ICSharpCode.Build.Tasks.dll exists in the same
folder as the project file.
<Project DefaultTargets="Build"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<OutputType>Exe</OutputType>
<AssemblyName>HelloMonoWorld</AssemblyName>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<OutputPath>bin</OutputPath>
</PropertyGroup>
<ItemGroup>
<Compile Include="Main.cs" />
<Compile Include="AssemblyInfo.cs" />
</ItemGroup>
<UsingTask TaskName="ICSharpCode.Build.Tasks.Mcs"
AssemblyFile="ICSharpCode.Build.Tasks.dll"/>
<Target Name="Build">
<Mcs
OutputAssembly="$(OutputPath)\$(AssemblyName).exe"
References="@(ReferencePath)"
Sources="@(Compile)"
TargetType="$(OutputType)"/>
</Target>
</Project>
The project file above is a bit limited, it does not have standard
targets such as "Rebuild" and "Clean". We could write everything
ourselves, but instead why not use the standard .targets that Microsoft
provides and just override the bare minimum?
Mono.Mcs.targets
So what is the bare minimum .targets file we need in order to get MSBuild to use our custom Mono tasks?
First let us look at the modified project file:
<Project DefaultTargets="Build"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<OutputType>Exe</OutputType>
<AssemblyName>HelloMonoWorld</AssemblyName>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<OutputPath>bin</OutputPath>
</PropertyGroup>
<ItemGroup>
<Compile Include="Main.cs" />
<Compile Include="AssemblyInfo.cs" />
</ItemGroup>
<Import Project="Mono.Mcs.targets" />
</Project>
The Mono.Mcs.targets file, which is based on the Microsoft.CSharp.targets file, overrides the CoreCompile target:
<Project DefaultTargets="Build"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<UsingTask TaskName="ICSharpCode.Build.Tasks.Mcs"
AssemblyFile="ICSharpCode.Build.Tasks.dll"/>
<PropertyGroup>
<MSBuildAllProjects>$(MSBuildAllProjects);Mono.Mcs.targets</MSBuildAllProjects>
<DefaultLanguageSourceExtension>.cs</DefaultLanguageSourceExtension>
<Language>C#</Language>
</PropertyGroup>
<Target Name="CreateManifestResourceNames"/>
<PropertyGroup>
<DebugSymbols Condition=" '$(DebugType)' == 'none' ">false</DebugSymbols>
<DebugType Condition=" '$(DebugType)' == 'none' "></DebugType>
</PropertyGroup>
<ItemGroup>
<DocFileItem Include="$(DocumentationFile)" Condition="'$(DocumentationFile)'!=''">
<InProject>false</InProject>
</DocFileItem>
</ItemGroup>
<PropertyGroup>
<CoreCompileDependsOn>_ComputeNonExistentFileProperty</CoreCompileDependsOn>
</PropertyGroup>
<Target
Name="CoreCompile"
Inputs="$(MSBuildAllProjects);
@(Compile);
@(ManifestResourceWithNoCulture);
$(ApplicationIcon);
$(AssemblyOriginatorKeyFile);
@(ManifestNonResxWithNoCultureOnDisk);
@(ReferencePath);
@(CompiledLicenseFile)"
Outputs="@(DocFileItem);
@(IntermediateAssembly);
$(NonExistentFile)"
DependsOnTargets="$(CoreCompileDependsOn)"
>
<Mcs
AdditionalLibPaths="$(AdditionalLibPaths)"
AddModules="@(AddModules)"
AllowUnsafeBlocks="$(AllowUnsafeBlocks)"
CheckForOverflowUnderflow="$(CheckForOverflowUnderflow)"
CodePage="$(CodePage)"
DebugType="$(DebugType)"
DefineConstants="$(DefineConstants)"
DelaySign="$(DelaySign)"
DisabledWarnings="$(NoWarn)"
DocumentationFile="@(DocFileItem)"
EmitDebugInformation="$(DebugSymbols)"
KeyContainer="$(KeyContainerName)"
KeyFile="$(KeyOriginatorFile)"
LangVersion="$(LangVersion)"
MainEntryPoint="$(StartupObject)"
NoConfig="true"
NoLogo="$(NoLogo)"
NoStandardLib="$(NoStdLib)"
Optimize="$(Optimize)"
OutputAssembly="@(IntermediateAssembly)"
References="@(ReferencePath)"
Resources="@(ManifestResourceWithNoCulture);@(ManifestNonResxWithNoCultureOnDisk);@(CompiledLicenseFile)"
ResponseFiles="$(CompilerResponseFile)"
Sources="@(Compile)"
TargetType="$(OutputType)"
ToolPath="$(McsToolPath)"
TreatWarningsAsErrors="$(TreatWarningsAsErrors)"
WarningLevel="$(WarningLevel)"
Win32Icon="$(ApplicationIcon)"
Win32Resource="$(Win32Resource)" />
</Target>
<Import Project="$(MSBuildBinPath)\Microsoft.Common.targets" />
</Project>
The differences between this is and the standard
Microsoft.CSharp.targets file is that a few properties have been
removed (NoWarn and UseHostCompilerIfAvailable) and the
CreateManifestResourceNames target has essentially been removed, the
target is defined, but it does nothing. This targets file is good
enough to compile our console application, as long as we do not add any
embedded resources to it.
SharpDevelop
How does this compare with the .targets files that ship with
SharpDevelop? Since SharpDevelop can target more than just Mono,
it supports the older .NET frameworks too, things are a little more
complicated. This will be covered in another post.