--- /dev/null
+//
+// Main.cs: Main program file of command line utility.
+//
+// Author:
+// Marek Sieradzki (marek.sieradzki@gmail.com)
+// Miguel de Icaza (miguel@ximian.com)
+// Marek Safar (marek.safar@seznam.cz)
+//
+// (C) 2005 Marek Sieradzki
+// Copyright 2009 Novell, Inc (http://www.novell.com)
+// Copyright 2011 Xamarin Inc (http://www.xamarin.com).
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+using Microsoft.Build.Utilities;
+using Microsoft.Build.Exceptions;
+using Microsoft.Build.Construction;
+using System.Xml;
+using System.Xml.Schema;
+using System.Linq;
+using System.Collections.Generic;
+
+#if NET_2_0
+
+using System;
+using System.Collections;
+using System.IO;
+using System.Reflection;
+using System.Text;
+using Microsoft.Build.Evaluation;
+using Microsoft.Build.Execution;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Logging;
+using Mono.XBuild.Framework;
+
+class MonoTODOAttribute : Attribute
+{
+}
+
+namespace Mono.XBuild.CommandLine {
+ public class MainClass {
+
+ Parameters parameters;
+ string[] args;
+ string defaultSchema;
+
+ ProjectCollection project_collection;
+ ProjectRootElement project;
+ ConsoleReportPrinter printer;
+
+
+ public static void Main (string[] args)
+ {
+ MainClass mc = new MainClass ();
+ mc.args = args;
+ mc.Execute ();
+ }
+
+ public MainClass ()
+ {
+ string binPath = ToolLocationHelper.GetPathToDotNetFramework (TargetDotNetFrameworkVersion.Version20);
+ defaultSchema = Path.Combine (binPath, "Microsoft.Build.xsd");
+ parameters = new Parameters ();
+ }
+
+ public void Execute ()
+ {
+ bool result = false;
+ bool show_stacktrace = false;
+
+ try {
+ parameters.ParseArguments (args);
+ show_stacktrace = (parameters.LoggerVerbosity == LoggerVerbosity.Detailed ||
+ parameters.LoggerVerbosity == LoggerVerbosity.Diagnostic);
+
+ if (!parameters.NoLogo)
+ ErrorUtilities.ShowVersion (false);
+
+ project_collection = new ProjectCollection ();
+ if (!String.IsNullOrEmpty (parameters.ToolsVersion)) {
+ if (project_collection.GetToolset (parameters.ToolsVersion) == null)
+ ErrorUtilities.ReportError (0, new InvalidToolsetDefinitionException ("Toolset " + parameters.ToolsVersion + " was not found").Message);
+
+ project_collection.DefaultToolsVersion = parameters.ToolsVersion;
+ }
+
+ foreach (var p in parameters.Properties)
+ project_collection.GlobalProperties.Add (p.Key, p.Value);
+
+ if (!parameters.NoConsoleLogger) {
+ printer = new ConsoleReportPrinter ();
+ ConsoleLogger cl = new ConsoleLogger (parameters.LoggerVerbosity,
+ printer.Print, printer.SetForeground, printer.ResetColor);
+
+ cl.Parameters = parameters.ConsoleLoggerParameters;
+ cl.Verbosity = parameters.LoggerVerbosity;
+ project_collection.RegisterLogger (cl);
+ }
+
+ if (parameters.FileLoggerParameters != null) {
+ for (int i = 0; i < parameters.FileLoggerParameters.Length; i ++) {
+ string fl_params = parameters.FileLoggerParameters [i];
+ if (fl_params == null)
+ continue;
+
+ var fl = new FileLogger ();
+ if (fl_params.Length == 0 && i > 0)
+ fl.Parameters = String.Format ("LogFile=msbuild{0}.log", i);
+ else
+ fl.Parameters = fl_params;
+ project_collection.RegisterLogger (fl);
+ }
+ }
+
+ foreach (LoggerInfo li in parameters.Loggers) {
+ Assembly assembly;
+ if (li.InfoType == LoadInfoType.AssemblyFilename)
+ assembly = Assembly.LoadFrom (li.Filename);
+ else
+ assembly = Assembly.Load (li.AssemblyName);
+ ILogger logger = (ILogger)Activator.CreateInstance (assembly.GetType (li.ClassName));
+ logger.Parameters = li.Parameters;
+ project_collection.RegisterLogger (logger);
+ }
+
+ string projectFile = parameters.ProjectFile;
+ if (!File.Exists (projectFile)) {
+ ErrorUtilities.ReportError (0, String.Format ("Project file '{0}' not found.", projectFile));
+ return;
+ }
+
+ XmlReaderSettings settings = new XmlReaderSettings ();
+ if (parameters.Validate) {
+ settings.ValidationType = ValidationType.Schema;
+ if (parameters.ValidationSchema == null)
+ using (var xsdxml = XmlReader.Create (defaultSchema))
+ settings.Schemas.Add (XmlSchema.Read (xsdxml, null));
+ else
+ using (var xsdxml = XmlReader.Create (parameters.ValidationSchema))
+ settings.Schemas.Add (XmlSchema.Read (xsdxml, null));
+ }
+
+ var projectInstances = new List<ProjectInstance> ();
+ if (string.Equals (Path.GetExtension (projectFile), ".sln", StringComparison.OrdinalIgnoreCase)) {
+ var parser = new SolutionParser ();
+ var root = ProjectRootElement.Create ();
+ parser.ParseSolution (projectFile, project_collection, root, LogWarning);
+ projectInstances.Add (new ProjectInstance (root, null, null, project_collection));
+ } else {
+ project = ProjectRootElement.Create (XmlReader.Create (projectFile, settings), project_collection);
+ var pi = new ProjectInstance (project, parameters.Properties, parameters.ToolsVersion, project_collection);
+ projectInstances.Add (pi);
+ }
+ foreach (var projectInstance in projectInstances) {
+ var targets = parameters.Targets.Length == 0 ? projectInstance.DefaultTargets.ToArray () : parameters.Targets;
+ result = projectInstance.Build (targets, parameters.Loggers.Count > 0 ? parameters.Loggers : project_collection.Loggers);
+ if (!result)
+ break;
+ }
+ }
+
+ catch (InvalidProjectFileException ipfe) {
+ ErrorUtilities.ReportError (0, show_stacktrace ? ipfe.ToString () : ipfe.Message);
+ }
+
+ catch (InternalLoggerException ile) {
+ ErrorUtilities.ReportError (0, show_stacktrace ? ile.ToString () : ile.Message);
+ }
+
+ catch (CommandLineException cle) {
+ ErrorUtilities.ReportError(cle.ErrorCode, show_stacktrace ? cle.ToString() : cle.Message);
+ }
+ finally {
+ //if (project_collection != null)
+ // project_collection.UnregisterAllLoggers ();
+
+ Environment.Exit (result ? 0 : 1);
+ }
+ }
+
+ void LogWarning (int errorNumber, string message)
+ {
+ Console.Error.WriteLine ("Warning {0}: {1}", errorNumber, message);
+ }
+ }
+
+ // code from mcs/report.cs
+ class ConsoleReportPrinter
+ {
+ string prefix, postfix;
+ bool color_supported;
+ TextWriter writer;
+ string [] colorPrefixes;
+
+ public ConsoleReportPrinter ()
+ : this (Console.Out)
+ {
+ }
+
+ public ConsoleReportPrinter (TextWriter writer)
+ {
+ this.writer = writer;
+
+ string term = Environment.GetEnvironmentVariable ("TERM");
+ bool xterm_colors = false;
+
+ color_supported = false;
+ switch (term){
+ case "xterm":
+ case "rxvt":
+ case "rxvt-unicode":
+ if (Environment.GetEnvironmentVariable ("COLORTERM") != null){
+ xterm_colors = true;
+ }
+ break;
+
+ case "xterm-color":
+ case "xterm-256color":
+ xterm_colors = true;
+ break;
+ }
+ if (!xterm_colors)
+ return;
+
+ if (!(UnixUtils.isatty (1) && UnixUtils.isatty (2)))
+ return;
+
+ color_supported = true;
+ PopulateColorPrefixes ();
+ postfix = "\x001b[0m";
+ }
+
+ void PopulateColorPrefixes ()
+ {
+ colorPrefixes = new string [16];
+
+ colorPrefixes [(int)ConsoleColor.Black] = GetForeground ("black");
+ colorPrefixes [(int)ConsoleColor.DarkBlue] = GetForeground ("blue");
+ colorPrefixes [(int)ConsoleColor.DarkGreen] = GetForeground ("green");
+ colorPrefixes [(int)ConsoleColor.DarkCyan] = GetForeground ("cyan");
+ colorPrefixes [(int)ConsoleColor.DarkRed] = GetForeground ("red");
+ colorPrefixes [(int)ConsoleColor.DarkMagenta] = GetForeground ("magenta");
+ colorPrefixes [(int)ConsoleColor.DarkYellow] = GetForeground ("yellow");
+ colorPrefixes [(int)ConsoleColor.DarkGray] = GetForeground ("grey");
+
+ colorPrefixes [(int)ConsoleColor.Gray] = GetForeground ("brightgrey");
+ colorPrefixes [(int)ConsoleColor.Blue] = GetForeground ("brightblue");
+ colorPrefixes [(int)ConsoleColor.Green] = GetForeground ("brightgreen");
+ colorPrefixes [(int)ConsoleColor.Cyan] = GetForeground ("brightcyan");
+ colorPrefixes [(int)ConsoleColor.Red] = GetForeground ("brightred");
+ colorPrefixes [(int)ConsoleColor.Magenta] = GetForeground ("brightmagenta");
+ colorPrefixes [(int)ConsoleColor.Yellow] = GetForeground ("brightyellow");
+
+ colorPrefixes [(int)ConsoleColor.White] = GetForeground ("brightwhite");
+ }
+
+ public void SetForeground (ConsoleColor color)
+ {
+ if (color_supported)
+ prefix = colorPrefixes [(int)color];
+ }
+
+ public void ResetColor ()
+ {
+ prefix = "\x001b[0m";
+ }
+
+ static int NameToCode (string s)
+ {
+ switch (s) {
+ case "black":
+ return 0;
+ case "red":
+ return 1;
+ case "green":
+ return 2;
+ case "yellow":
+ return 3;
+ case "blue":
+ return 4;
+ case "magenta":
+ return 5;
+ case "cyan":
+ return 6;
+ case "grey":
+ case "white":
+ return 7;
+ }
+ return 7;
+ }
+
+ //
+ // maps a color name to its xterm color code
+ //
+ static string GetForeground (string s)
+ {
+ string highcode;
+
+ if (s.StartsWith ("bright")) {
+ highcode = "1;";
+ s = s.Substring (6);
+ } else
+ highcode = "";
+
+ return "\x001b[" + highcode + (30 + NameToCode (s)).ToString () + "m";
+ }
+
+ static string GetBackground (string s)
+ {
+ return "\x001b[" + (40 + NameToCode (s)).ToString () + "m";
+ }
+
+ string FormatText (string txt)
+ {
+ if (prefix != null && color_supported)
+ return prefix + txt + postfix;
+
+ return txt;
+ }
+
+ public void Print (string message)
+ {
+ writer.WriteLine (FormatText (message));
+ }
+
+ }
+
+ class UnixUtils {
+ [System.Runtime.InteropServices.DllImport ("libc", EntryPoint="isatty")]
+ extern static int _isatty (int fd);
+
+ public static bool isatty (int fd)
+ {
+ try {
+ return _isatty (fd) == 1;
+ } catch {
+ return false;
+ }
+ }
+ }
+
+}
+
+#endif
--- /dev/null
+//
+// SolutionParser.cs: Generates a project file from a solution file.
+//
+// Author:
+// Jonathan Chambers (joncham@gmail.com)
+// Ankit Jain <jankit@novell.com>
+// Lluis Sanchez Gual <lluis@novell.com>
+//
+// (C) 2009 Jonathan Chambers
+// Copyright 2008, 2009 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.IO;
+using Microsoft.Build.Evaluation;
+using Microsoft.Build.Execution;
+using Microsoft.Build.Construction;
+using Microsoft.Build.Exceptions;
+
+namespace Mono.XBuild.CommandLine {
+ class ProjectInfo {
+ public string Name;
+ public string FileName;
+ public Guid Guid;
+
+ public ProjectInfo (string name, string fileName)
+ {
+ Name = name;
+ FileName = fileName;
+ }
+
+ public Dictionary<TargetInfo, TargetInfo> TargetMap = new Dictionary<TargetInfo, TargetInfo> ();
+ public Dictionary<Guid, ProjectInfo> Dependencies = new Dictionary<Guid, ProjectInfo> ();
+ public Dictionary<string, ProjectSection> ProjectSections = new Dictionary<string, ProjectSection> ();
+ public List<string> AspNetConfigurations = new List<string> ();
+ }
+
+ class ProjectSection {
+ public string Name;
+ public Dictionary<string, string> Properties = new Dictionary<string, string> ();
+
+ public ProjectSection (string name)
+ {
+ Name = name;
+ }
+ }
+
+ struct TargetInfo {
+ public string Configuration;
+ public string Platform;
+ public bool Build;
+
+ public TargetInfo (string configuration, string platform)
+ : this (configuration, platform, false)
+ {
+ }
+
+ public TargetInfo (string configuration, string platform, bool build)
+ {
+ Configuration = configuration;
+ Platform = platform;
+ Build = build;
+ }
+ }
+
+
+ internal delegate void RaiseWarningHandler (int errorNumber, string message);
+
+ class SolutionParser {
+ static string[] buildTargets = new string[] { "Build", "Clean", "Rebuild", "Publish" };
+
+ static string guidExpression = "{[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}}";
+
+ static Regex slnVersionRegex = new Regex (@"Microsoft Visual Studio Solution File, Format Version (\d?\d.\d\d)");
+ static Regex projectRegex = new Regex ("Project\\(\"(" + guidExpression + ")\"\\) = \"(.*?)\", \"(.*?)\", \"(" + guidExpression + ")\"(\\s*?)((\\s*?)ProjectSection\\((.*?)\\) = (.*?)EndProjectSection(\\s*?))*(\\s*?)(EndProject)?", RegexOptions.Singleline);
+ static Regex projectDependenciesRegex = new Regex ("ProjectSection\\((.*?)\\) = \\w*(.*?)EndProjectSection", RegexOptions.Singleline);
+ static Regex projectDependencyRegex = new Regex ("\\s*(" + guidExpression + ") = (" + guidExpression + ")");
+ static Regex projectSectionPropertiesRegex = new Regex ("\\s*(?<name>.*) = \"(?<value>.*)\"");
+
+ static Regex globalRegex = new Regex ("Global(.*)EndGlobal", RegexOptions.Singleline);
+ static Regex globalSectionRegex = new Regex ("GlobalSection\\((.*?)\\) = \\w*(.*?)EndGlobalSection", RegexOptions.Singleline);
+
+ static Regex solutionConfigurationRegex = new Regex ("\\s*(.*?)\\|(.*?) = (.*?)\\|(.+)");
+ static Regex projectConfigurationActiveCfgRegex = new Regex ("\\s*(" + guidExpression + ")\\.(.+?)\\|(.+?)\\.ActiveCfg = (.+?)\\|(.+)");
+ static Regex projectConfigurationBuildRegex = new Regex ("\\s*(" + guidExpression + ")\\.(.*?)\\|(.*?)\\.Build\\.0 = (.*?)\\|(.+)");
+
+ static string solutionFolderGuid = "{2150E333-8FDC-42A3-9474-1A3956D46DE8}";
+ static string vcprojGuid = "{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}";
+ static string websiteProjectGuid = "{E24C65DC-7377-472B-9ABA-BC803B73C61A}";
+
+ RaiseWarningHandler RaiseWarning;
+
+ public void ParseSolution (string file, ProjectCollection projects, ProjectRootElement p, RaiseWarningHandler RaiseWarning)
+ {
+ this.RaiseWarning = RaiseWarning;
+ EmitBeforeImports (p, file);
+
+ AddGeneralSettings (file, p);
+
+ StreamReader reader = new StreamReader (file);
+ string slnVersion = GetSlnFileVersion (reader);
+ if (slnVersion == "11.00")
+ projects.DefaultToolsVersion = "4.0";
+ else if (slnVersion == "10.00")
+ projects.DefaultToolsVersion = "3.5";
+ else
+ projects.DefaultToolsVersion = "2.0";
+
+ string line = reader.ReadToEnd ();
+ line = line.Replace ("\r\n", "\n");
+ string solutionDir = Path.GetDirectoryName (file);
+
+ List<TargetInfo> solutionTargets = new List<TargetInfo> ();
+ Dictionary<Guid, ProjectInfo> projectInfos = new Dictionary<Guid, ProjectInfo> ();
+ Dictionary<Guid, ProjectInfo> websiteProjectInfos = new Dictionary<Guid, ProjectInfo> ();
+ List<ProjectInfo>[] infosByLevel = null;
+ Dictionary<Guid, ProjectInfo> unsupportedProjectInfos = new Dictionary<Guid, ProjectInfo> ();
+
+ Match m = projectRegex.Match (line);
+ while (m.Success) {
+ ProjectInfo projectInfo = new ProjectInfo (m.Groups[2].Value,
+ Path.GetFullPath (Path.Combine (solutionDir,
+ m.Groups [3].Value.Replace ('\\', Path.DirectorySeparatorChar))));
+ if (String.Compare (m.Groups [1].Value, solutionFolderGuid,
+ StringComparison.InvariantCultureIgnoreCase) == 0) {
+ // Ignore solution folders
+ m = m.NextMatch ();
+ continue;
+ }
+
+ projectInfo.Guid = new Guid (m.Groups [4].Value);
+
+ if (String.Compare (m.Groups [1].Value, vcprojGuid,
+ StringComparison.InvariantCultureIgnoreCase) == 0) {
+ // Ignore vcproj
+ RaiseWarning (0, string.Format("Ignoring vcproj '{0}'.", projectInfo.Name));
+
+ unsupportedProjectInfos [projectInfo.Guid] = projectInfo;
+ m = m.NextMatch ();
+ continue;
+ }
+
+ if (String.Compare (m.Groups [1].Value, websiteProjectGuid,
+ StringComparison.InvariantCultureIgnoreCase) == 0)
+ websiteProjectInfos.Add (new Guid (m.Groups[4].Value), projectInfo);
+ else
+ projectInfos.Add (projectInfo.Guid, projectInfo);
+
+ Match projectSectionMatch = projectDependenciesRegex.Match (m.Groups[6].Value);
+ while (projectSectionMatch.Success) {
+ string section_name = projectSectionMatch.Groups [1].Value;
+ if (String.Compare (section_name, "ProjectDependencies") == 0) {
+ Match projectDependencyMatch = projectDependencyRegex.Match (projectSectionMatch.Value);
+ while (projectDependencyMatch.Success) {
+ // we might not have projectInfo available right now, so
+ // set it to null, and fill it in later
+ projectInfo.Dependencies [new Guid (projectDependencyMatch.Groups[1].Value)] = null;
+ projectDependencyMatch = projectDependencyMatch.NextMatch ();
+ }
+ } else {
+ ProjectSection section = new ProjectSection (section_name);
+ Match propertiesMatch = projectSectionPropertiesRegex.Match (
+ projectSectionMatch.Groups [2].Value);
+ while (propertiesMatch.Success) {
+ section.Properties [propertiesMatch.Groups ["name"].Value] =
+ propertiesMatch.Groups ["value"].Value;
+
+ propertiesMatch = propertiesMatch.NextMatch ();
+ }
+
+ projectInfo.ProjectSections [section_name] = section;
+ }
+ projectSectionMatch = projectSectionMatch.NextMatch ();
+ }
+ m = m.NextMatch ();
+ }
+
+ foreach (ProjectInfo projectInfo in projectInfos.Values) {
+ string filename = projectInfo.FileName;
+ string projectDir = Path.GetDirectoryName (filename);
+
+ if (!File.Exists (filename)) {
+ RaiseWarning (0, String.Format ("Project file {0} referenced in the solution file, " +
+ "not found. Ignoring.", filename));
+ continue;
+ }
+
+ Project currentProject;
+ try {
+ currentProject = new Project (filename, null, null, projects, ProjectLoadSettings.IgnoreMissingImports);
+ } catch (InvalidProjectFileException e) {
+ RaiseWarning (0, e.Message);
+ continue;
+ }
+
+ foreach (var bi in currentProject.GetItems ("ProjectReference")) {
+ ProjectInfo info = null;
+ string projectReferenceGuid = bi.GetMetadataValue ("Project");
+ bool hasGuid = !String.IsNullOrEmpty (projectReferenceGuid);
+
+ // try to resolve the ProjectReference by GUID
+ // and fallback to project filename
+
+ if (hasGuid) {
+ Guid guid = new Guid (projectReferenceGuid);
+ projectInfos.TryGetValue (guid, out info);
+ if (info == null && unsupportedProjectInfos.TryGetValue (guid, out info)) {
+ RaiseWarning (0, String.Format (
+ "{0}: ProjectReference '{1}' is of an unsupported type. Ignoring.",
+ filename, bi.EvaluatedInclude));
+ continue;
+ }
+ }
+
+ if (info == null || !hasGuid) {
+ // Project not found by guid or guid not available
+ // Try to find by project file
+
+ string fullpath = Path.GetFullPath (Path.Combine (projectDir, bi.EvaluatedInclude.Replace ('\\', Path.DirectorySeparatorChar)));
+ info = projectInfos.Values.FirstOrDefault (pi => pi.FileName == fullpath);
+
+ if (info == null) {
+ if (unsupportedProjectInfos.Values.Any (pi => pi.FileName == fullpath))
+ RaiseWarning (0, String.Format (
+ "{0}: ProjectReference '{1}' is of an unsupported type. Ignoring.",
+ filename, bi.EvaluatedInclude));
+ else
+ RaiseWarning (0, String.Format (
+ "{0}: ProjectReference '{1}' not found, neither by guid '{2}' nor by project file name '{3}'.",
+ filename, bi.EvaluatedInclude, projectReferenceGuid.Replace ("{", "").Replace ("}", ""), fullpath));
+ }
+
+ }
+
+ if (info != null)
+ projectInfo.Dependencies [info.Guid] = info;
+ }
+ }
+
+ // fill in the project info for deps found in the .sln file
+ foreach (ProjectInfo projectInfo in projectInfos.Values) {
+ List<Guid> missingInfos = new List<Guid> ();
+ foreach (KeyValuePair<Guid, ProjectInfo> dependency in projectInfo.Dependencies) {
+ if (dependency.Value == null)
+ missingInfos.Add (dependency.Key);
+ }
+
+ foreach (Guid guid in missingInfos) {
+ ProjectInfo info;
+ if (projectInfos.TryGetValue (guid, out info))
+ projectInfo.Dependencies [guid] = info;
+ else
+ projectInfo.Dependencies.Remove (guid);
+ }
+ }
+
+ Match globalMatch = globalRegex.Match (line);
+ Match globalSectionMatch = globalSectionRegex.Match (globalMatch.Groups[1].Value);
+ while (globalSectionMatch.Success) {
+ string sectionType = globalSectionMatch.Groups[1].Value;
+ switch (sectionType) {
+ case "SolutionConfigurationPlatforms":
+ ParseSolutionConfigurationPlatforms (globalSectionMatch.Groups[2].Value, solutionTargets);
+ break;
+ case "ProjectConfigurationPlatforms":
+ ParseProjectConfigurationPlatforms (globalSectionMatch.Groups[2].Value,
+ projectInfos, websiteProjectInfos);
+ break;
+ case "SolutionProperties":
+ ParseSolutionProperties (globalSectionMatch.Groups[2].Value);
+ break;
+ case "NestedProjects":
+ break;
+ case "MonoDevelopProperties":
+ break;
+ default:
+ RaiseWarning (0, string.Format("Don't know how to handle GlobalSection {0}, Ignoring.", sectionType));
+ break;
+ }
+ globalSectionMatch = globalSectionMatch.NextMatch ();
+ }
+
+ int num_levels = AddBuildLevels (p, solutionTargets, projectInfos, ref infosByLevel);
+
+ AddCurrentSolutionConfigurationContents (p, solutionTargets, projectInfos, websiteProjectInfos);
+ AddProjectReferences (p, projectInfos);
+ AddWebsiteProperties (p, websiteProjectInfos, projectInfos);
+ AddValidateSolutionConfiguration (p);
+
+ EmitAfterImports (p, file);
+
+ AddGetFrameworkPathTarget (p);
+ AddWebsiteTargets (p, websiteProjectInfos, projectInfos, infosByLevel, solutionTargets);
+ AddProjectTargets (p, solutionTargets, projectInfos);
+ AddSolutionTargets (p, num_levels, websiteProjectInfos.Values);
+ }
+
+ string GetSlnFileVersion (StreamReader reader)
+ {
+ string strInput = null;
+ Match match;
+
+ strInput = reader.ReadLine();
+ if (strInput == null)
+ return null;
+
+ match = slnVersionRegex.Match(strInput);
+ if (!match.Success) {
+ strInput = reader.ReadLine();
+ if (strInput == null)
+ return null;
+ match = slnVersionRegex.Match (strInput);
+ }
+
+ if (match.Success)
+ return match.Groups[1].Value;
+
+ return null;
+ }
+
+ void EmitBeforeImports (ProjectRootElement p, string file)
+ {
+#if NET_4_0
+ p.AddImportGroup ().AddImport ("$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\SolutionFile\\ImportBefore\\*").Condition =
+ "'$(ImportByWildcardBeforeSolution)' != 'false' and " +
+ "Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\SolutionFile\\ImportBefore')";
+#endif
+
+ string before_filename = Path.Combine (Path.GetDirectoryName (file), "before." + Path.GetFileName (file) + ".targets");
+ p.AddImportGroup ().AddImport (before_filename).Condition = String.Format ("Exists ('{0}')", before_filename);
+ }
+
+ void EmitAfterImports (ProjectRootElement p, string file)
+ {
+#if NET_4_0
+ p.AddImportGroup ().AddImport ("$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\SolutionFile\\ImportAfter\\*").Condition =
+ "'$(ImportByWildcardAfterSolution)' != 'false' and " +
+ "Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\SolutionFile\\ImportAfter')";
+#endif
+
+ string after_filename = Path.Combine (Path.GetDirectoryName (file), "after." + Path.GetFileName (file) + ".targets");
+ p.AddImportGroup ().AddImport (after_filename).Condition = String.Format ("Exists ('{0}')", after_filename);
+ }
+
+ void AddGeneralSettings (string solutionFile, ProjectRootElement p)
+ {
+ p.DefaultTargets = "Build";
+ p.InitialTargets = "ValidateSolutionConfiguration";
+ //p.AddUsingTask ("CreateTemporaryVCProject", null, "Microsoft.Build.Tasks, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
+ //p.AddUsingTask ("ResolveVCProjectOutput", null, "Microsoft.Build.Tasks, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
+
+ string solutionFilePath = Path.GetFullPath (solutionFile);
+ var solutionPropertyGroup = p.CreatePropertyGroupElement ();
+ //solutionPropertyGroup.ChildrenReversed = true;
+ solutionPropertyGroup.AddProperty ("SolutionDir", Path.GetDirectoryName (solutionFilePath) + Path.DirectorySeparatorChar);
+ solutionPropertyGroup.AddProperty ("SolutionExt", Path.GetExtension (solutionFile));
+ solutionPropertyGroup.AddProperty ("SolutionFileName", Path.GetFileName (solutionFile));
+ solutionPropertyGroup.AddProperty ("SolutionName", Path.GetFileNameWithoutExtension (solutionFile));
+ solutionPropertyGroup.AddProperty ("SolutionPath", solutionFilePath);
+ }
+
+ void ParseSolutionConfigurationPlatforms (string section, List<TargetInfo> solutionTargets)
+ {
+ Match solutionConfigurationPlatform = solutionConfigurationRegex.Match (section);
+ while (solutionConfigurationPlatform.Success) {
+ string solutionConfiguration = solutionConfigurationPlatform.Groups[1].Value;
+ string solutionPlatform = solutionConfigurationPlatform.Groups[2].Value;
+ solutionTargets.Add (new TargetInfo (solutionConfiguration, solutionPlatform));
+ solutionConfigurationPlatform = solutionConfigurationPlatform.NextMatch ();
+ }
+ }
+
+ // ignores the website projects, in the websiteProjectInfos
+ void ParseProjectConfigurationPlatforms (string section, Dictionary<Guid, ProjectInfo> projectInfos,
+ Dictionary<Guid, ProjectInfo> websiteProjectInfos)
+ {
+ List<Guid> missingGuids = new List<Guid> ();
+ Match projectConfigurationPlatform = projectConfigurationActiveCfgRegex.Match (section);
+ while (projectConfigurationPlatform.Success) {
+ Guid guid = new Guid (projectConfigurationPlatform.Groups[1].Value);
+ ProjectInfo projectInfo;
+ if (!projectInfos.TryGetValue (guid, out projectInfo)) {
+ if (!missingGuids.Contains (guid)) {
+ if (!websiteProjectInfos.ContainsKey (guid))
+ // ignore website projects
+ RaiseWarning (0, string.Format("Failed to find project {0}", guid));
+ missingGuids.Add (guid);
+ }
+ projectConfigurationPlatform = projectConfigurationPlatform.NextMatch ();
+ continue;
+ }
+ string solConf = projectConfigurationPlatform.Groups[2].Value;
+ string solPlat = projectConfigurationPlatform.Groups[3].Value;
+ string projConf = projectConfigurationPlatform.Groups[4].Value;
+ string projPlat = projectConfigurationPlatform.Groups[5].Value;
+ // hack, what are they doing here?
+ if (projPlat == "Any CPU")
+ projPlat = "AnyCPU";
+ projectInfo.TargetMap.Add (new TargetInfo (solConf, solPlat), new TargetInfo (projConf, projPlat));
+ projectConfigurationPlatform = projectConfigurationPlatform.NextMatch ();
+ }
+ Match projectConfigurationPlatformBuild = projectConfigurationBuildRegex.Match (section);
+ while (projectConfigurationPlatformBuild.Success) {
+ Guid guid = new Guid (projectConfigurationPlatformBuild.Groups[1].Value);
+ ProjectInfo projectInfo;
+ if (!projectInfos.TryGetValue (guid, out projectInfo)) {
+ if (!missingGuids.Contains (guid)) {
+ RaiseWarning (0, string.Format("Failed to find project {0}", guid));
+ missingGuids.Add (guid);
+ }
+ projectConfigurationPlatformBuild = projectConfigurationPlatformBuild.NextMatch ();
+ continue;
+ }
+ string solConf = projectConfigurationPlatformBuild.Groups[2].Value;
+ string solPlat = projectConfigurationPlatformBuild.Groups[3].Value;
+ string projConf = projectConfigurationPlatformBuild.Groups[4].Value;
+ string projPlat = projectConfigurationPlatformBuild.Groups[5].Value;
+ // hack, what are they doing here?
+ if (projPlat == "Any CPU")
+ projPlat = "AnyCPU";
+ projectInfo.TargetMap[new TargetInfo (solConf, solPlat)] = new TargetInfo (projConf, projPlat, true);
+ projectConfigurationPlatformBuild = projectConfigurationPlatformBuild.NextMatch ();
+ }
+ }
+
+ void ParseSolutionProperties (string section)
+ {
+ }
+
+ void AddCurrentSolutionConfigurationContents (ProjectRootElement p, List<TargetInfo> solutionTargets,
+ Dictionary<Guid, ProjectInfo> projectInfos,
+ Dictionary<Guid, ProjectInfo> websiteProjectInfos)
+ {
+ TargetInfo default_target_info = new TargetInfo ("Debug", "Any CPU");
+ if (solutionTargets.Count > 0) {
+ bool found = false;
+ foreach (TargetInfo tinfo in solutionTargets) {
+ if (String.Compare (tinfo.Platform, "Mixed Platforms") == 0) {
+ default_target_info = tinfo;
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ default_target_info = solutionTargets [0];
+ }
+
+ AddDefaultSolutionConfiguration (p, default_target_info);
+
+ foreach (TargetInfo solutionTarget in solutionTargets) {
+ var platformPropertyGroup = p.AddPropertyGroup ();
+ platformPropertyGroup.Condition = string.Format (
+ " ('$(Configuration)' == '{0}') and ('$(Platform)' == '{1}') ",
+ solutionTarget.Configuration,
+ solutionTarget.Platform
+ );
+
+ StringBuilder solutionConfigurationContents = new StringBuilder ();
+ solutionConfigurationContents.Append ("<SolutionConfiguration xmlns=\"\">");
+ foreach (KeyValuePair<Guid, ProjectInfo> projectInfo in projectInfos) {
+ AddProjectConfigurationItems (projectInfo.Key, projectInfo.Value, solutionTarget, solutionConfigurationContents);
+ }
+ solutionConfigurationContents.Append ("</SolutionConfiguration>");
+
+ platformPropertyGroup.AddProperty ("CurrentSolutionConfigurationContents",
+ solutionConfigurationContents.ToString ());
+ }
+ }
+
+ void AddProjectReferences (ProjectRootElement p, Dictionary<Guid, ProjectInfo> projectInfos)
+ {
+ var big = p.AddItemGroup ();
+ foreach (KeyValuePair<Guid, ProjectInfo> pair in projectInfos)
+ big.AddItem ("ProjectReference", pair.Value.FileName);
+ }
+
+ void AddProjectConfigurationItems (Guid guid, ProjectInfo projectInfo, TargetInfo solutionTarget,
+ StringBuilder solutionConfigurationContents)
+ {
+ foreach (KeyValuePair<TargetInfo, TargetInfo> targetInfo in projectInfo.TargetMap) {
+ if (solutionTarget.Configuration == targetInfo.Key.Configuration &&
+ solutionTarget.Platform == targetInfo.Key.Platform) {
+ solutionConfigurationContents.AppendFormat (
+ "<ProjectConfiguration Project=\"{0}\">{1}|{2}</ProjectConfiguration>",
+ guid.ToString ("B").ToUpper (), targetInfo.Value.Configuration, targetInfo.Value.Platform);
+ }
+ }
+ }
+
+ void AddDefaultSolutionConfiguration (ProjectRootElement p, TargetInfo target)
+ {
+ var configurationPropertyGroup = p.AddPropertyGroup ();
+ //configurationPropertyGroup.PropertiesReversed = true;
+ configurationPropertyGroup.Condition = " '$(Configuration)' == '' ";
+ configurationPropertyGroup.AddProperty ("Configuration", target.Configuration);
+
+ var platformPropertyGroup = p.AddPropertyGroup ();
+ //platformPropertyGroup.PropertiesReversed = true;
+ platformPropertyGroup.Condition = " '$(Platform)' == '' ";
+ platformPropertyGroup.AddProperty ("Platform", target.Platform);
+
+ // emit default for AspNetConfiguration also
+ var aspNetConfigurationPropertyGroup = p.AddPropertyGroup ();
+ //aspNetConfigurationPropertyGroup.PropertiesReversed = true;
+ aspNetConfigurationPropertyGroup.Condition = " ('$(AspNetConfiguration)' == '') ";
+ aspNetConfigurationPropertyGroup.AddProperty ("AspNetConfiguration", "$(Configuration)");
+ }
+
+ void AddWarningForMissingProjectConfiguration (ProjectTargetElement target, string slnConfig, string slnPlatform, string projectName)
+ {
+ var task = target.AddTask ("Warning");
+ task.SetParameter ("Text",
+ String.Format ("The project configuration for project '{0}' corresponding " +
+ "to the solution configuration '{1}|{2}' was not found in the solution file.",
+ projectName, slnConfig, slnPlatform));
+ task.Condition = String.Format ("('$(Configuration)' == '{0}') and ('$(Platform)' == '{1}')",
+ slnConfig, slnPlatform);
+
+ }
+
+ // Website project methods
+
+ void AddWebsiteProperties (ProjectRootElement p, Dictionary<Guid, ProjectInfo> websiteProjectInfos,
+ Dictionary<Guid, ProjectInfo> projectInfos)
+ {
+ var propertyGroupByConfig = new Dictionary<string, ProjectPropertyGroupElement> ();
+ foreach (KeyValuePair<Guid, ProjectInfo> infoPair in websiteProjectInfos) {
+ ProjectInfo info = infoPair.Value;
+ string projectGuid = infoPair.Key.ToString ();
+
+ ProjectSection section;
+ if (!info.ProjectSections.TryGetValue ("WebsiteProperties", out section)) {
+ RaiseWarning (0, String.Format ("Website project '{0}' does not have the required project section: WebsiteProperties. Ignoring project.", info.Name));
+ return;
+ }
+
+ //parse project references
+ string [] ref_guids = null;
+ string references;
+ if (section.Properties.TryGetValue ("ProjectReferences", out references)) {
+ ref_guids = references.Split (new char [] {';'}, StringSplitOptions.RemoveEmptyEntries);
+ for (int i = 0; i < ref_guids.Length; i ++) {
+ // "{guid}|foo.dll"
+ ref_guids [i] = ref_guids [i].Split ('|') [0];
+
+ Guid r_guid = new Guid (ref_guids [i]);
+ ProjectInfo ref_info;
+ if (projectInfos.TryGetValue (r_guid, out ref_info))
+ // ignore if not found
+ info.Dependencies [r_guid] = ref_info;
+ }
+ }
+
+ foreach (KeyValuePair<string, string> pair in section.Properties) {
+ //looking for -- ConfigName.AspNetCompiler.PropName
+ string [] parts = pair.Key.Split ('.');
+ if (parts.Length != 3 || String.Compare (parts [1], "AspNetCompiler") != 0)
+ continue;
+
+ string config = parts [0];
+ string propertyName = parts [2];
+
+ ProjectPropertyGroupElement bpg;
+ if (!propertyGroupByConfig.TryGetValue (config, out bpg)) {
+ bpg = p.AddPropertyGroup ();
+ //bpg.PropertiesReversed = true;
+ bpg.Condition = String.Format (" '$(AspNetConfiguration)' == '{0}' ", config);
+ propertyGroupByConfig [config] = bpg;
+ }
+
+ bpg.AddProperty (String.Format ("Project_{0}_AspNet{1}", projectGuid, propertyName),
+ pair.Value);
+
+ if (!info.AspNetConfigurations.Contains (config))
+ info.AspNetConfigurations.Add (config);
+ }
+ }
+ }
+
+ // For WebSite projects
+ // The main "Build" target:
+ // 1. builds all non-website projects
+ // 2. calls target for website project
+ // - gets target path for the referenced projects
+ // - Resolves dependencies, satellites etc for the
+ // referenced project assemblies, and copies them
+ // to bin/ folder
+ void AddWebsiteTargets (ProjectRootElement p, Dictionary<Guid, ProjectInfo> websiteProjectInfos,
+ Dictionary<Guid, ProjectInfo> projectInfos, List<ProjectInfo>[] infosByLevel,
+ List<TargetInfo> solutionTargets)
+ {
+ foreach (ProjectInfo w_info in websiteProjectInfos.Values) {
+ // gets a linear list of dependencies
+ List<ProjectInfo> depInfos = new List<ProjectInfo> ();
+ foreach (List<ProjectInfo> pinfos in infosByLevel) {
+ foreach (ProjectInfo pinfo in pinfos)
+ if (w_info.Dependencies.ContainsKey (pinfo.Guid))
+ depInfos.Add (pinfo);
+ }
+
+ foreach (string buildTarget in new string [] {"Build", "Rebuild"})
+ AddWebsiteTarget (p, w_info, projectInfos, depInfos, solutionTargets, buildTarget);
+
+ // clean/publish are not supported for website projects
+ foreach (string buildTarget in new string [] {"Clean", "Publish"})
+ AddWebsiteUnsupportedTarget (p, w_info, depInfos, buildTarget);
+ }
+ }
+
+ void AddWebsiteTarget (ProjectRootElement p, ProjectInfo webProjectInfo,
+ Dictionary<Guid, ProjectInfo> projectInfos, List<ProjectInfo> depInfos,
+ List<TargetInfo> solutionTargets, string buildTarget)
+ {
+ string w_guid = webProjectInfo.Guid.ToString ().ToUpper ();
+
+ var target = p.AddTarget (GetTargetNameForProject (webProjectInfo.Name, buildTarget));
+ target.Condition = "'$(CurrentSolutionConfigurationContents)' != ''";
+ target.DependsOnTargets = GetWebsiteDependsOnTarget (depInfos, buildTarget);
+
+ // this item collects all the references
+ string final_ref_item = String.Format ("Project_{0}_References{1}", w_guid,
+ buildTarget != "Build" ? "_" + buildTarget : String.Empty);
+
+ foreach (TargetInfo targetInfo in solutionTargets) {
+ int ref_num = 0;
+ foreach (ProjectInfo depInfo in depInfos) {
+ TargetInfo projectTargetInfo;
+ if (!depInfo.TargetMap.TryGetValue (targetInfo, out projectTargetInfo))
+ // Ignore, no config, so no target path
+ continue;
+
+ // GetTargetPath from the referenced project
+ AddWebsiteMSBuildTaskForReference (target, depInfo, projectTargetInfo, targetInfo,
+ final_ref_item, ref_num);
+ ref_num ++;
+ }
+ }
+
+ // resolve the references
+ AddWebsiteResolveAndCopyReferencesTasks (target, webProjectInfo, final_ref_item, w_guid);
+ }
+
+ // emits the MSBuild task to GetTargetPath for the referenced project
+ void AddWebsiteMSBuildTaskForReference (ProjectTargetElement target, ProjectInfo depInfo, TargetInfo projectTargetInfo,
+ TargetInfo solutionTargetInfo, string final_ref_item, int ref_num)
+ {
+ var task = target.AddTask ("MSBuild");
+ task.SetParameter ("Projects", depInfo.FileName);
+ task.SetParameter ("Targets", "GetTargetPath");
+
+ task.SetParameter ("Properties", string.Format ("Configuration={0}; Platform={1}; BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)", projectTargetInfo.Configuration, projectTargetInfo.Platform));
+ task.Condition = string.Format (" ('$(Configuration)' == '{0}') and ('$(Platform)' == '{1}') ", solutionTargetInfo.Configuration, solutionTargetInfo.Platform);
+
+ string ref_item = String.Format ("{0}_{1}",
+ final_ref_item, ref_num);
+
+ task.AddOutputItem ("TargetOutputs", ref_item);
+
+ task = target.AddTask ("CreateItem");
+ task.SetParameter ("Include", String.Format ("@({0})", ref_item));
+ task.SetParameter ("AdditionalMetadata", String.Format ("Guid={{{0}}}",
+ depInfo.Guid.ToString ().ToUpper ()));
+ task.AddOutputItem ("Include", final_ref_item);
+ }
+
+ void AddWebsiteResolveAndCopyReferencesTasks (ProjectTargetElement target, ProjectInfo webProjectInfo,
+ string final_ref_item, string w_guid)
+ {
+ var task = target.AddTask ("ResolveAssemblyReference");
+ task.SetParameter ("Assemblies", String.Format ("@({0}->'%(FullPath)')", final_ref_item));
+ task.SetParameter ("TargetFrameworkDirectories", "$(TargetFrameworkPath)");
+ task.SetParameter ("SearchPaths", "{RawFileName};{TargetFrameworkDirectory};{GAC}");
+ task.SetParameter ("FindDependencies", "true");
+ task.SetParameter ("FindSatellites", "true");
+ task.SetParameter ("FindRelatedFiles", "true");
+ task.Condition = String.Format ("Exists ('%({0}.Identity)')", final_ref_item);
+
+ string copylocal_item = String.Format ("{0}_CopyLocalFiles", final_ref_item);
+ task.AddOutputItem ("CopyLocalFiles", copylocal_item);
+
+ // Copy the references
+ task = target.AddTask ("Copy");
+ task.SetParameter ("SourceFiles", String.Format ("@({0})", copylocal_item));
+ task.SetParameter ("DestinationFiles", String.Format (
+ "@({0}->'$(Project_{1}_AspNetPhysicalPath)\\Bin\\%(DestinationSubDirectory)%(Filename)%(Extension)')",
+ copylocal_item, w_guid));
+
+ // AspNetConfiguration, is config for the website project, useful
+ // for overriding from command line
+ StringBuilder cond = new StringBuilder ();
+ foreach (string config in webProjectInfo.AspNetConfigurations) {
+ if (cond.Length > 0)
+ cond.Append (" or ");
+ cond.AppendFormat (" ('$(AspNetConfiguration)' == '{0}') ", config);
+ }
+ task.Condition = cond.ToString ();
+
+ task = target.AddTask ("Message");
+ cond = new StringBuilder ();
+ foreach (string config in webProjectInfo.AspNetConfigurations) {
+ if (cond.Length > 0)
+ cond.Append (" and ");
+ cond.AppendFormat (" ('$(AspNetConfiguration)' != '{0}') ", config);
+ }
+ task.Condition = cond.ToString ();
+ task.SetParameter ("Text", "Skipping as the '$(AspNetConfiguration)' configuration is " +
+ "not supported by this website project.");
+ }
+
+ void AddWebsiteUnsupportedTarget (ProjectRootElement p, ProjectInfo webProjectInfo, List<ProjectInfo> depInfos,
+ string buildTarget)
+ {
+ var target = p.AddTarget (GetTargetNameForProject (webProjectInfo.Name, buildTarget));
+ target.DependsOnTargets = GetWebsiteDependsOnTarget (depInfos, buildTarget);
+
+ var task = target.AddTask ("Message");
+ task.SetParameter ("Text", String.Format (
+ "Target '{0}' not support for website projects", buildTarget));
+ }
+
+ string GetWebsiteDependsOnTarget (List<ProjectInfo> depInfos, string buildTarget)
+ {
+ StringBuilder deps = new StringBuilder ();
+ foreach (ProjectInfo pinfo in depInfos) {
+ if (deps.Length > 0)
+ deps.Append (";");
+ deps.Append (GetTargetNameForProject (pinfo.Name, buildTarget));
+ }
+ deps.Append (";GetFrameworkPath");
+ return deps.ToString ();
+ }
+
+ void AddGetFrameworkPathTarget (ProjectRootElement p)
+ {
+ var t = p.AddTarget ("GetFrameworkPath");
+ var task = t.AddTask ("GetFrameworkPath");
+ task.AddOutputProperty ("Path", "TargetFrameworkPath");
+ }
+
+ void AddValidateSolutionConfiguration (ProjectRootElement p)
+ {
+ var t = p.AddTarget ("ValidateSolutionConfiguration");
+ var task = t.AddTask ("Warning");
+ task.SetParameter ("Text", "On windows, an environment variable 'Platform' is set to MCD sometimes, and this overrides the Platform property" +
+ " for Mono msbuild, which could be an invalid Platform for this solution file. And so you are getting the following error." +
+ " You could override it by either setting the environment variable to nothing, as\n" +
+ " set Platform=\n" +
+ "Or explicity specify its value on the command line, as\n" +
+ " msbuild Foo.sln /p:Platform=Release");
+ task.Condition = "('$(CurrentSolutionConfigurationContents)' == '') and ('$(SkipInvalidConfigurations)' != 'true')" +
+ " and '$(Platform)' == 'MCD' and '$(OS)' == 'Windows_NT'";
+
+ task = t.AddTask ("Error");
+ task.SetParameter ("Text", "Invalid solution configuration and platform: \"$(Configuration)|$(Platform)\".");
+ task.Condition = "('$(CurrentSolutionConfigurationContents)' == '') and ('$(SkipInvalidConfigurations)' != 'true')";
+ task = t.AddTask ("Warning");
+ task.SetParameter ("Text", "Invalid solution configuration and platform: \"$(Configuration)|$(Platform)\".");
+ task.Condition = "('$(CurrentSolutionConfigurationContents)' == '') and ('$(SkipInvalidConfigurations)' == 'true')";
+ task = t.AddTask ("Message");
+ task.SetParameter ("Text", "Building solution configuration \"$(Configuration)|$(Platform)\".");
+ task.Condition = "'$(CurrentSolutionConfigurationContents)' != ''";
+ }
+
+ void AddProjectTargets (ProjectRootElement p, List<TargetInfo> solutionTargets, Dictionary<Guid, ProjectInfo> projectInfos)
+ {
+ foreach (KeyValuePair<Guid, ProjectInfo> projectInfo in projectInfos) {
+ ProjectInfo project = projectInfo.Value;
+ foreach (string buildTarget in buildTargets) {
+ string target_name = GetTargetNameForProject (project.Name, buildTarget);
+ bool is_build_or_rebuild = buildTarget == "Build" || buildTarget == "Rebuild";
+ var target = p.AddTarget (target_name);
+ target.Condition = "'$(CurrentSolutionConfigurationContents)' != ''";
+
+ if (is_build_or_rebuild)
+ target.Outputs = "@(CollectedBuildOutput)";
+ if (project.Dependencies.Count > 0)
+ target.DependsOnTargets = String.Join (";",
+ project.Dependencies.Values.Select (
+ di => GetTargetNameForProject (di.Name, buildTarget)).ToArray ());
+
+ foreach (TargetInfo targetInfo in solutionTargets) {
+ ProjectTaskElement task = null;
+ TargetInfo projectTargetInfo;
+ if (!project.TargetMap.TryGetValue (targetInfo, out projectTargetInfo)) {
+ AddWarningForMissingProjectConfiguration (target, targetInfo.Configuration,
+ targetInfo.Platform, project.Name);
+ continue;
+ }
+ if (projectTargetInfo.Build) {
+ task = target.AddTask ("MSBuild");
+ task.SetParameter ("Projects", project.FileName);
+ task.SetParameter ("ToolsVersion", "$(ProjectToolsVersion)");
+ if (is_build_or_rebuild)
+ task.AddOutputItem ("TargetOutputs", "CollectedBuildOutput");
+
+ if (buildTarget != "Build")
+ task.SetParameter ("Targets", buildTarget);
+ task.SetParameter ("Properties", string.Format ("Configuration={0}; Platform={1}; BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)", projectTargetInfo.Configuration, projectTargetInfo.Platform));
+ } else {
+ task = target.AddTask ("Message");
+ task.SetParameter ("Text", string.Format ("Project \"{0}\" is disabled for solution configuration \"{1}|{2}\".", project.Name, targetInfo.Configuration, targetInfo.Platform));
+ }
+ task.Condition = string.Format (" ('$(Configuration)' == '{0}') and ('$(Platform)' == '{1}') ", targetInfo.Configuration, targetInfo.Platform);
+ }
+ }
+ }
+ }
+
+
+ string GetTargetNameForProject (string projectName, string buildTarget)
+ {
+ //FIXME: hack
+ projectName = projectName.Replace ("\\", "/").Replace (".", "_");
+ string target_name = projectName +
+ (buildTarget == "Build" ? string.Empty : ":" + buildTarget);
+
+ if (IsBuildTargetName (projectName))
+ target_name = "Solution:" + target_name;
+
+ return target_name;
+ }
+
+ bool IsBuildTargetName (string name)
+ {
+ foreach (string tgt in buildTargets)
+ if (name == tgt)
+ return true;
+ return false;
+ }
+
+ // returns number of levels
+ int AddBuildLevels (ProjectRootElement p, List<TargetInfo> solutionTargets, Dictionary<Guid, ProjectInfo> projectInfos,
+ ref List<ProjectInfo>[] infosByLevel)
+ {
+ infosByLevel = TopologicalSort<ProjectInfo> (projectInfos.Values);
+
+ foreach (TargetInfo targetInfo in solutionTargets) {
+ var big = p.AddItemGroup ();
+ big.Condition = String.Format (" ('$(Configuration)' == '{0}') and ('$(Platform)' == '{1}') ",
+ targetInfo.Configuration, targetInfo.Platform);
+
+ //FIXME: every level has projects that can be built in parallel.
+ // levels are ordered on the basis of the dependency graph
+
+ for (int i = 0; i < infosByLevel.Length; i ++) {
+ string build_level = String.Format ("BuildLevel{0}", i);
+ string skip_level = String.Format ("SkipLevel{0}", i);
+ string missing_level = String.Format ("MissingConfigLevel{0}", i);
+
+ foreach (ProjectInfo projectInfo in infosByLevel [i]) {
+ TargetInfo projectTargetInfo;
+ if (!projectInfo.TargetMap.TryGetValue (targetInfo, out projectTargetInfo)) {
+ // missing project config
+ big.AddItem (missing_level, projectInfo.Name);
+ continue;
+ }
+
+ if (projectTargetInfo.Build) {
+ var item = big.AddItem (build_level, projectInfo.FileName);
+ item.AddMetadata ("Configuration", projectTargetInfo.Configuration);
+ item.AddMetadata ("Platform", projectTargetInfo.Platform);
+ } else {
+ // build disabled
+ big.AddItem (skip_level, projectInfo.Name);
+ }
+ }
+ }
+ }
+
+ return infosByLevel.Length;
+ }
+
+ void AddSolutionTargets (ProjectRootElement p, int num_levels, IEnumerable<ProjectInfo> websiteProjectInfos)
+ {
+ foreach (string buildTarget in buildTargets) {
+ var t = p.AddTarget (buildTarget);
+ bool is_build_or_rebuild = buildTarget == "Build" || buildTarget == "Rebuild";
+
+ t.Condition = "'$(CurrentSolutionConfigurationContents)' != ''";
+ if (is_build_or_rebuild)
+ t.Outputs = "@(CollectedBuildOutput)";
+
+ ProjectTaskElement task = null;
+ for (int i = 0; i < num_levels; i ++) {
+ string level_str = String.Format ("BuildLevel{0}", i);
+ task = t.AddTask ("MSBuild");
+ task.SetParameter ("Condition", String.Format ("'@({0})' != ''", level_str));
+ task.SetParameter ("Projects", String.Format ("@({0})", level_str));
+ task.SetParameter ("ToolsVersion", "$(ProjectToolsVersion)");
+ task.SetParameter ("Properties",
+ string.Format ("Configuration=%(Configuration); Platform=%(Platform); BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)"));
+ if (buildTarget != "Build")
+ task.SetParameter ("Targets", buildTarget);
+ //FIXME: change this to BuildInParallel=true, when parallel
+ // build support gets added
+ task.SetParameter ("RunEachTargetSeparately", "true");
+ if (is_build_or_rebuild)
+ task.AddOutputItem ("TargetOutputs", "CollectedBuildOutput");
+
+ level_str = String.Format ("SkipLevel{0}", i);
+ task = t.AddTask ("Message");
+ task.Condition = String.Format ("'@({0})' != ''", level_str);
+ task.SetParameter ("Text",
+ String.Format ("The project '%({0}.Identity)' is disabled for solution " +
+ "configuration '$(Configuration)|$(Platform)'.", level_str));
+
+ level_str = String.Format ("MissingConfigLevel{0}", i);
+ task = t.AddTask ("Warning");
+ task.Condition = String.Format ("'@({0})' != ''", level_str);
+ task.SetParameter ("Text",
+ String.Format ("The project configuration for project '%({0}.Identity)' " +
+ "corresponding to the solution configuration " +
+ "'$(Configuration)|$(Platform)' was not found.", level_str));
+ }
+
+ // "build" website projects also
+ StringBuilder w_targets = new StringBuilder ();
+ foreach (ProjectInfo info in websiteProjectInfos) {
+ if (w_targets.Length > 0)
+ w_targets.Append (";");
+ w_targets.Append (GetTargetNameForProject (info.Name, buildTarget));
+ }
+
+ task = t.AddTask ("CallTarget");
+ task.SetParameter ("Targets", w_targets.ToString ());
+ task.SetParameter ("RunEachTargetSeparately", "true");
+ }
+ }
+
+ // Sorts the ProjectInfo dependency graph, to obtain
+ // a series of build levels with projects. Projects
+ // in each level can be run parallel (no inter-dependency).
+ static List<T>[] TopologicalSort<T> (IEnumerable<T> items) where T: ProjectInfo
+ {
+ IList<T> allItems;
+ allItems = items as IList<T>;
+ if (allItems == null)
+ allItems = new List<T> (items);
+
+ bool[] inserted = new bool[allItems.Count];
+ bool[] triedToInsert = new bool[allItems.Count];
+ int[] levels = new int [allItems.Count];
+
+ int maxdepth = 0;
+ for (int i = 0; i < allItems.Count; ++i) {
+ int d = Insert<T> (i, allItems, levels, inserted, triedToInsert);
+ if (d > maxdepth)
+ maxdepth = d;
+ }
+
+ // Separate out the project infos by build level
+ List<T>[] infosByLevel = new List<T>[maxdepth];
+ for (int i = 0; i < levels.Length; i ++) {
+ int level = levels [i] - 1;
+ if (infosByLevel [level] == null)
+ infosByLevel [level] = new List<T> ();
+
+ infosByLevel [level].Add (allItems [i]);
+ }
+
+ return infosByLevel;
+ }
+
+ // returns level# for the project
+ static int Insert<T> (int index, IList<T> allItems, int[] levels, bool[] inserted, bool[] triedToInsert)
+ where T: ProjectInfo
+ {
+ if (inserted [index])
+ return levels [index];
+
+ if (triedToInsert[index])
+ throw new InvalidOperationException (String.Format (
+ "Cyclic dependency involving project {0} found in the project dependency graph",
+ allItems [index].Name));
+
+ triedToInsert[index] = true;
+ ProjectInfo insertItem = allItems[index];
+
+ int maxdepth = 0;
+ foreach (ProjectInfo dependency in insertItem.Dependencies.Values) {
+ for (int j = 0; j < allItems.Count; ++j) {
+ ProjectInfo checkItem = allItems [j];
+ if (dependency.FileName == checkItem.FileName) {
+ int d = Insert (j, allItems, levels, inserted, triedToInsert);
+ maxdepth = d > maxdepth ? d : maxdepth;
+ break;
+ }
+ }
+ }
+ levels [index] = maxdepth + 1;
+ inserted [index] = true;
+
+ return levels [index];
+ }
+
+ public static IEnumerable<string> GetAllProjectFileNames (string solutionFile)
+ {
+ StreamReader reader = new StreamReader (solutionFile);
+ string line = reader.ReadToEnd ();
+ line = line.Replace ("\r\n", "\n");
+ string soln_dir = Path.GetDirectoryName (solutionFile);
+
+ Match m = projectRegex.Match (line);
+ while (m.Success) {
+ if (String.Compare (m.Groups [1].Value, solutionFolderGuid,
+ StringComparison.InvariantCultureIgnoreCase) != 0)
+ yield return Path.Combine (soln_dir, m.Groups [3].Value).Replace ("\\", "/");
+
+ m = m.NextMatch ();
+ }
+ }
+ }
+}