Hello,
here is first attempt to implement <msbuild> task for MSBuild interation. To
be a little usable for us, NAnt users, I made it atleast little compatible
with <solution> task. So its not a simple wrapper around msbuild.exe. Mainly
it accept fileset of projects, which msbuild itself do not. Property names
are compatible with solution task as well.
I also implement function to distinguish between vs2003/vs2005 projects, but
I'm not sure if it'd be useful.
For now, I place it to NAnt.DotNet project. Maybe other location is more
appropriate?
No testcases yet, but they should be next step. Let me know, if you like
this approach. More attibutes/element will be needed as well, I think. Ideas
are welcomed :-)
Almost forgot:
you need to add
<task name="msbuild">
<attribute name="exename">MSBuild</attribute>
</task>
to NAnt.exe.config under <framework name="net-2.0">.
Regards,
Martin Aliger
PS: I'll refactor to follow nant's conding standarts lately. Sorry for
inconvenience. This is just a preview...
Note: MSBuild from net 2.0 Beta 2 will not compile solutions from VS2005
final. Took me hour to figure :)
// NAnt - A .NET build tool
// Copyright (C) 2001-2004 Gerry Shaw
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Martin Aliger ([EMAIL PROTECTED])
using System;
using System.IO;
using System.Collections;
using System.Reflection;
using System.Globalization;
using NAnt.Core;
using NAnt.Core.Types;
using NAnt.Core.Attributes;
namespace NAnt.DotNet.Functions
{
/// <summary>
/// Functions to return information for MSBuild system.
/// </summary>
[FunctionSet("msbuild", "MSBuild")]
public class MsbuildFunctions : FunctionSetBase
{
#region Public Instance Constructors
/// <exclude/>
public MsbuildFunctions(Project project, PropertyDictionary
properties) : base(project, properties) {}
#endregion Public Instance Constructors
#region Public Instance Methods
/// <summary>
/// Test whether project is VS2005 project and could be built
using <msbuild>
/// </summary>
/// <param name="project">The name or path of the project file
(csproj, vbproj, ...).</param>
/// <returns>
/// True, if it is msbuild project, False otherwise.
/// </returns>
[Function("is-msbuild-project")]
public bool IsMsbuildProject(string project)
{
using(StreamReader str = new
StreamReader(File.Open(project,FileMode.Open,FileAccess.Read,FileShare.ReadWrite)))
{
System.Xml.XmlDocument doc = new
System.Xml.XmlDocument();
doc.Load(str);
string ns =
doc.NameTable.Get("http://schemas.microsoft.com/developer/msbuild/2003");
return ns!=null;
}
}
#endregion Public Static Methods
}
}
// NAnt - A .NET build tool
// Copyright (C) 2001-2002 Gerry Shaw
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Martin Aliger ([EMAIL PROTECTED])
using System;
using System.Globalization;
using System.IO;
using System.Text.RegularExpressions;
using System.Text;
using System.Collections;
using System.Collections.Specialized;
using System.CodeDom.Compiler;
using NAnt.Core;
using NAnt.Core.Tasks;
using NAnt.Core.Attributes;
using NAnt.Core.Types;
using NAnt.Core.Util;
using NAnt.DotNet.Types;
namespace NAnt.DotNet.Tasks
{
//TODO: write documentation
/// <summary>
/// Runs MSBuild system.
/// </summary>
/// <remarks>
/// <note>
/// </note>
/// </remarks>
/// <example>
/// </example>
[TaskName("msbuild")]
[ProgramLocation(LocationType.FrameworkDir)]
public class MsbuildTask : ExternalProgramBase {
#region Private Instance Fields
private StringBuilder _arguments = null;
private FileSet _projects = new FileSet();
private FileInfo _solutionFile;
private string _configuration;
private DirectoryInfo _outputDir;
#endregion Private Instance Fields
#region Private Static Fields
#endregion Private Static Fields
#region Public Instance Properties
/// <summary>
/// The projects to build.
/// </summary>
[BuildElement("projects", Required=false)]
public FileSet Projects
{
get { return _projects; }
set { _projects = value; }
}
/// <summary>
/// The name of the VS.NET solution file to build.
/// </summary>
/// <remarks>
/// <para>
/// The <see cref="Projects" /> can be used instead to supply a
list
/// of Visual Studio.NET projects that should be built.
/// </para>
/// </remarks>
[TaskAttribute("solutionfile", Required=false)]
public FileInfo SolutionFile
{
get { return _solutionFile; }
set { _solutionFile = value; }
}
/// <summary>
/// The name of the solution configuration to build.
/// </summary>
/// <remarks>
/// <para>
/// Generally <c>release</c> or <c>debug</c>. Not
case-sensitive.
/// </para>
/// </remarks>
[TaskAttribute("configuration", Required=false)] //changed to
non-required
[StringValidator(AllowEmpty=false)]
public string Configuration
{
get { return _configuration; }
set { _configuration =
StringUtils.ConvertEmptyToNull(value); }
}
#endregion Public Instance Properties
#region Override implementation of ExternalProgramBase
/// <summary>
/// Gets the command line arguments for the external program.
/// </summary>
/// <value>
/// The command line arguments for the external program.
/// </value>
public override string ProgramArguments
{
get { return _arguments.ToString(); }
}
/// <summary>
/// The directory where compiled targets will be placed. This
/// overrides path settings contained in the solution/project.
/// </summary>
[TaskAttribute("outputdir", Required=false)]
public DirectoryInfo OutputDir
{
get { return _outputDir; }
set { _outputDir = value; }
}
/// <summary>
/// Starts the external process and captures its output.
/// </summary>
protected override void ExecuteTask()
{
Log(Level.Info, "Starting VS2005 solution build.");
FileInfo l_solution = null;
using (TempFileCollection tfc = new
TempFileCollection())
{
if (SolutionFile != null)
{
if (!SolutionFile.Exists)
{
throw new
BuildException(string.Format(CultureInfo.InvariantCulture,
"Couldn't find solution
file '{0}'.", SolutionFile.FullName),
Location);
}
l_solution = SolutionFile;
}
if (Projects.FileNames.Count > 0)
{
if(l_solution!=null)
{
throw new
BuildException(string.Format(CultureInfo.InvariantCulture,
"Both solution and
projects are specified. Specify just one of those"),
Location);
}
Log(Level.Verbose, "Included projects:"
);
foreach (string projectFile in
Projects.FileNames)
{
Log(Level.Verbose, " - " +
projectFile);
}
Log(Level.Verbose, "Creating temporary
solution to hold all projects.");
l_solution=CreateTempSolution(tfc,Projects.FileNames);
}
_arguments=new StringBuilder();
_arguments.Append( "/nologo ");
//_arguments.Append( "/noconsolelogger ");
if (!Verbose)
{
_arguments.Append("/verbosity:normal ");
}
else
{
_arguments.Append("/verbosity:detailed
"); //diagnostic
}
if(_outputDir!=null)
{
_arguments.Append("/property:OutDir=\"").Append(_outputDir.FullName).Append("\"
");
}
if(_configuration!=null)
{
_arguments.Append("/property:Configuration=").Append(_configuration).Append("
");
}
_arguments.Append("\"").Append(l_solution.FullName).Append("\" ");
Log(Level.Info, "Starting MSBuild");
base.ExecuteTask();
}
}
#endregion Override implementation of ExternalProgramBase
#region Internal Instance Methods
private string ReadProjectGuid(string project)
{
using(StreamReader str = new
StreamReader(File.Open(project,FileMode.Open,FileAccess.Read,FileShare.ReadWrite)))
{
System.Xml.XmlDocument doc = new
System.Xml.XmlDocument();
doc.Load(str);
System.Xml.XmlNamespaceManager nsmgr = new
System.Xml.XmlNamespaceManager(doc.NameTable);
nsmgr.AddNamespace("vs2005","http://schemas.microsoft.com/developer/msbuild/2003");
System.Xml.XmlNode node =
doc.SelectSingleNode("//vs2005:ProjectGuid",nsmgr);
if(node==null)
throw new
BuildException(string.Format(CultureInfo.InvariantCulture,
"Unable to read ProjectGuid
from project '{0}'.", project),
Location);
return node.InnerText;
}
}
private FileInfo CreateTempSolution(TempFileCollection tfc,
StringCollection projects)
{
string tempFile = tfc.AddExtension("sln",false);
using(StreamWriter str = File.CreateText(tempFile))
{
str.WriteLine("Microsoft Visual Studio Solution
File, Format Version 9.00");
str.WriteLine("# NAnt generated");
string solguid = Guid.NewGuid().ToString("B");
Hashtable l_projects = new Hashtable();
foreach(string project in projects)
{
string pg = ReadProjectGuid(project);
l_projects[project]=pg;
str.WriteLine("Project(\"{0}\") =
\"{3}\", \"{2}\", \"{1}\"",
solguid,pg,project,Path.GetFileNameWithoutExtension(project));
str.WriteLine("EndProject");
}
str.WriteLine("Global");
str.WriteLine("\tGlobalSection(SolutionConfigurationPlatforms) = preSolution");
str.WriteLine("\t\tDebug|Any CPU = Debug|Any
CPU");
str.WriteLine("\t\tRelease|Any CPU =
Release|Any CPU");
str.WriteLine("\tEndGlobalSection");
str.WriteLine("\tGlobalSection(ProjectConfigurationPlatforms) = postSolution");
foreach(string project in projects)
{
string pg = (string)l_projects[project];
str.WriteLine("\t\t{0}.Debug|Any
CPU.ActiveCfg = Debug|Any CPU",pg);
str.WriteLine("\t\t{0}.Debug|Any
CPU.Build.0 = Debug|Any CPU",pg);
str.WriteLine("\t\t{0}.Release|Any
CPU.ActiveCfg = Release|Any CPU",pg);
str.WriteLine("\t\t{0}.Release|Any
CPU.Build.0 = Release|Any CPU",pg);
}
str.WriteLine("EndGlobalSection");
}
return new FileInfo(tempFile);
}
#endregion Internal Instance Methods
}
}