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 } }