[xbuild] Add hooks for extending .sln builds.
[mono.git] / mcs / tools / xbuild / SolutionParser.cs
index 65bdb80294e483d6e9d937f95ec359556356260f..67bce1c6afb0519ab3f4d138f1cbbc1f32b20a8d 100644 (file)
@@ -93,7 +93,7 @@ namespace Mono.XBuild.CommandLine {
                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 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>.*)\"");
@@ -114,6 +114,8 @@ namespace Mono.XBuild.CommandLine {
                public void ParseSolution (string file, Project p, RaiseWarningHandler RaiseWarning)
                {
                        this.RaiseWarning = RaiseWarning;
+                       EmitBeforeImports (p, file);
+
                        AddGeneralSettings (file, p);
 
                        StreamReader reader = new StreamReader (file);
@@ -133,6 +135,7 @@ namespace Mono.XBuild.CommandLine {
                        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) {
@@ -146,10 +149,14 @@ namespace Mono.XBuild.CommandLine {
                                        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;
                                }
@@ -158,9 +165,7 @@ namespace Mono.XBuild.CommandLine {
                                                StringComparison.InvariantCultureIgnoreCase) == 0)
                                        websiteProjectInfos.Add (new Guid (m.Groups[4].Value), projectInfo);
                                else
-                                       projectInfos.Add (new Guid (m.Groups[4].Value), projectInfo);
-
-                               projectInfo.Guid = new Guid (m.Groups [4].Value);
+                                       projectInfos.Add (projectInfo.Guid, projectInfo);
 
                                Match projectSectionMatch = projectDependenciesRegex.Match (m.Groups[6].Value);
                                while (projectSectionMatch.Success) {
@@ -220,6 +225,12 @@ namespace Mono.XBuild.CommandLine {
                                        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.Include));
+                                                       continue;
+                                               }
                                        }
 
                                        if (info == null || !hasGuid) {
@@ -229,10 +240,16 @@ namespace Mono.XBuild.CommandLine {
                                                string fullpath = Path.GetFullPath (Path.Combine (projectDir, bi.Include.Replace ('\\', Path.DirectorySeparatorChar)));
                                                info = projectInfos.Values.FirstOrDefault (pi => pi.FileName == fullpath);
 
-                                               if (info == null)
-                                                       RaiseWarning (0, String.Format (
-                                                                       "{0}: ProjectReference '{1}' not found, neither by guid '{2}' nor by project file name '{3}'.",
-                                                                       filename, bi.Include, projectReferenceGuid, 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.Include));
+                                                       else
+                                                               RaiseWarning (0, String.Format (
+                                                                               "{0}: ProjectReference '{1}' not found, neither by guid '{2}' nor by project file name '{3}'.",
+                                                                               filename, bi.Include, projectReferenceGuid.Replace ("{", "").Replace ("}", ""), fullpath));
+                                               }
 
                                        }
 
@@ -290,6 +307,8 @@ namespace Mono.XBuild.CommandLine {
                        AddWebsiteProperties (p, websiteProjectInfos, projectInfos);
                        AddValidateSolutionConfiguration (p);
 
+                       EmitAfterImports (p, file);
+
                        AddGetFrameworkPathTarget (p);
                        AddWebsiteTargets (p, websiteProjectInfos, projectInfos, infosByLevel, solutionTargets);
                        AddProjectTargets (p, solutionTargets, projectInfos);
@@ -298,7 +317,6 @@ namespace Mono.XBuild.CommandLine {
 
                 string GetSlnFileVersion (StreamReader reader)
                 {
-                        string strVersion = null;
                         string strInput = null;
                         Match match;
 
@@ -320,6 +338,30 @@ namespace Mono.XBuild.CommandLine {
                         return null;
                 }
 
+               void EmitBeforeImports (Project p, string file)
+               {
+#if NET_4_0
+                       p.AddNewImport ("$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\SolutionFile\\ImportBefore\\*",
+                                       "'$(ImportByWildcardBeforeSolution)' != 'false' and " +
+                                       "Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\SolutionFile\\ImportBefore')");
+#endif
+
+                       string before_filename = Path.Combine (Path.GetDirectoryName (file), "before." + Path.GetFileName (file) + ".targets");
+                       p.AddNewImport (before_filename, String.Format ("Exists ('{0}')", before_filename));
+               }
+
+               void EmitAfterImports (Project p, string file)
+               {
+#if NET_4_0
+                       p.AddNewImport ("$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\SolutionFile\\ImportAfter\\*",
+                                       "'$(ImportByWildcardAfterSolution)' != 'false' and " +
+                                       "Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\SolutionFile\\ImportAfter')");
+#endif
+
+                       string after_filename = Path.Combine (Path.GetDirectoryName (file), "after." + Path.GetFileName (file) + ".targets");
+                       p.AddNewImport (after_filename, String.Format ("Exists ('{0}')", after_filename));
+               }
+
                void AddGeneralSettings (string solutionFile, Project p)
                {
                        p.DefaultTargets = "Build";
@@ -736,20 +778,10 @@ namespace Mono.XBuild.CommandLine {
                                        string target_name = GetTargetNameForProject (project.Name, buildTarget);
                                        Target target = p.Targets.AddNewTarget (target_name);
                                        target.Condition = "'$(CurrentSolutionConfigurationContents)' != ''"; 
-
-                                       if (project.Dependencies.Count > 0) {
-                                               StringBuilder dependencies = new StringBuilder ();
-                                               foreach (ProjectInfo dependentInfo in project.Dependencies.Values) {
-                                                       if (dependencies.Length > 0)
-                                                               dependencies.Append (";");
-                                                       if (IsBuildTargetName (dependentInfo.Name))
-                                                               dependencies.Append ("Solution:");
-                                                       dependencies.Append (dependentInfo.Name);
-                                                       if (buildTarget != "Build")
-                                                               dependencies.Append (":" + buildTarget);
-                                               }
-                                               target.DependsOnTargets = dependencies.ToString ();
-                                       }
+                                       if (project.Dependencies.Count > 0)
+                                               target.DependsOnTargets = String.Join (";",
+                                                               project.Dependencies.Values.Select (
+                                                                       di => GetTargetNameForProject (di.Name, buildTarget)).ToArray ());
 
                                        foreach (TargetInfo targetInfo in solutionTargets) {
                                                BuildTask task = null;