[xbuild] Use the env var $MSBuildExtensionsPath before trying other paths.
[mono.git] / mcs / class / Microsoft.Build.Engine / Microsoft.Build.BuildEngine / Import.cs
index 3f41bd9c51a7aa66c7ef637ad84957b69f2447f3..9088babcfecf8444dc178ef46234374432af271f 100644 (file)
@@ -31,12 +31,19 @@ using System;
 using System.IO;
 using System.Xml;
 
+using Microsoft.Build.Framework;
+using Mono.XBuild.Utilities;
+
 namespace Microsoft.Build.BuildEngine {
        public class Import {
                XmlElement      importElement;
                Project         project;
                ImportedProject originalProject;
                string          evaluatedProjectPath;
+
+               static string DotConfigExtensionsPath = Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData),
+                                                               Path.Combine ("xbuild", "tasks"));
+               const string MacOSXExternalXBuildDir = "/Library/Frameworks/Mono.framework/External/xbuild";
        
                internal Import (XmlElement importElement, Project project, ImportedProject originalProject)
                {
@@ -50,42 +57,131 @@ namespace Microsoft.Build.BuildEngine {
                        this.originalProject = originalProject;
 
                        if (ProjectPath == String.Empty)
-                               throw new InvalidProjectFileException ("Project attribute must be specified.");
+                               throw new InvalidProjectFileException ("The required attribute \"Project\" is missing from element <Import>.");
+
+                       if (ConditionParser.ParseAndEvaluate (Condition, project)) {
+                               evaluatedProjectPath = EvaluateProjectPath (ProjectPath);
+                               evaluatedProjectPath = GetFullPath ();
+                               if (EvaluatedProjectPath == String.Empty)
+                                       throw new InvalidProjectFileException ("The required attribute \"Project\" is missing from element <Import>.");
+                       }
                }
 
-               internal void Evaluate ()
+               // FIXME: condition
+               internal void Evaluate (bool ignoreMissingImports)
                {
-                       string file;
-                       OldExpression exp;
-
-                       exp = new OldExpression (project);
-                       exp.ParseSource (ProjectPath);
+                       string filename = evaluatedProjectPath;
+                       // NOTE: it's a hack to transform Microsoft.CSharp.Targets to Microsoft.CSharp.targets
+                       if (Path.HasExtension (filename))
+                               filename = Path.ChangeExtension (filename, Path.GetExtension (filename));
                        
-                       file = evaluatedProjectPath = (string) exp.ConvertTo (typeof (string));
-
-                       if (Path.IsPathRooted (EvaluatedProjectPath) == false) {
-                               string dir;
-                               if (originalProject == null)
-                                       dir = Path.GetDirectoryName (project.FullFileName);
-                               else
-                                       dir = Path.GetDirectoryName (originalProject.FullFileName);
-                               file = Path.Combine (dir, EvaluatedProjectPath);
-                       }
-
-                       // FIXME: loggers anybody?
-                       if (!File.Exists (file)) {
-                               Console.WriteLine ("Imported file {0} doesn't exist.", file);
-                               return;
+                       if (!File.Exists (filename)) {
+                               if (ignoreMissingImports) {
+                                       project.LogWarning (project.FullFileName, "Could not find project file {0}, to import. Ignoring.", filename);
+                                       return;
+                               } else {
+                                       throw new InvalidProjectFileException (String.Format ("Imported project: \"{0}\" does not exist.", filename));
+                               }
                        }
                        
                        ImportedProject importedProject = new ImportedProject ();
-                       importedProject.Load (file);
-                       // FIXME: UGLY HACK
+                       importedProject.Load (filename);
+
                        project.ProcessElements (importedProject.XmlDocument.DocumentElement, importedProject);
                }
+
+               string EvaluateProjectPath (string file)
+               {
+                       string ret;
+                       if (EvaluateAsMSBuildExtensionsPath (file, "MSBuildExtensionsPath", out ret) ||
+                               EvaluateAsMSBuildExtensionsPath (file, "MSBuildExtensionsPath32", out ret) ||
+                               EvaluateAsMSBuildExtensionsPath (file, "MSBuildExtensionsPath64", out ret))
+                               return ret;
+
+                       return EvaluatePath (file);
+               }
+
+               bool EvaluateAsMSBuildExtensionsPath (string file, string property_name, out string epath)
+               {
+                       epath = null;
+                       string property_ref = String.Format ("$({0})", property_name);
+                       if (file.IndexOf (property_ref) < 0)
+                               return false;
+
+                       // This is a *HACK* to support multiple paths for
+                       // MSBuildExtensionsPath property. Normally it would
+                       // get resolved to a single value, but here we special
+                       // case it and try ~/.config/xbuild/tasks and any
+                       // paths specified in the env var $MSBuildExtensionsPath .
+                       //
+                       // The property itself will resolve to the default
+                       // location though, so you get in any other part of the
+                       // project.
+
+                       string envvar = Environment.GetEnvironmentVariable (property_name);
+                       envvar = String.Join (":", new string [] {
+                                               (envvar ?? String.Empty),
+                                               // For mac osx, look in the 'External' dir on macosx,
+                                               // see bug #663180
+                                               MSBuildUtils.RunningOnMac ? MacOSXExternalXBuildDir : String.Empty,
+                                               DotConfigExtensionsPath});
+
+                       string [] paths = envvar.Split (new char [] {':'}, StringSplitOptions.RemoveEmptyEntries);
+                       foreach (string path in paths) {
+                               if (!Directory.Exists (path)) {
+                                       project.ParentEngine.LogMessage (MessageImportance.Low, "Extension path '{0}' not found, ignoring.", path);
+                                       continue;
+                               }
+
+                               string pfile = Path.GetFullPath (file.Replace ("\\", "/").Replace (
+                                                       property_ref, path + Path.DirectorySeparatorChar));
+
+                               var evaluated_path = EvaluatePath (pfile);
+                               if (File.Exists (evaluated_path)) {
+                                       project.ParentEngine.LogMessage (MessageImportance.Low,
+                                               "{0}: Importing project {1} from extension path {2}", project.FullFileName, evaluated_path, path);
+                                       epath = pfile;
+                                       return true;
+                               }
+                               project.ParentEngine.LogMessage (MessageImportance.Low,
+                                               "{0}: Couldn't find project {1} for extension path {2}", project.FullFileName, evaluated_path, path);
+                       }
+
+                       return false;
+               }
+
+               string EvaluatePath (string path)
+               {
+                       var exp = new Expression ();
+                       exp.Parse (path, ParseOptions.Split);
+                       return (string) exp.ConvertTo (project, typeof (string));
+               }
+
+               string GetFullPath ()
+               {
+                       string file = EvaluatedProjectPath;
+
+                       if (!Path.IsPathRooted (EvaluatedProjectPath)) {
+                               string dir = null;
+                               if (originalProject == null) {
+                                       if (project.FullFileName != String.Empty) // Path.GetDirectoryName throws exception on String.Empty
+                                               dir = Path.GetDirectoryName (project.FullFileName);
+                               } else {
+                                       if (originalProject.FullFileName != String.Empty)
+                                               dir = Path.GetDirectoryName (originalProject.FullFileName);
+                               }
+                               if (dir != null)
+                                       file = Path.Combine (dir, EvaluatedProjectPath);
+                       }
+                       
+                       return MSBuildUtils.FromMSBuildPath (file);
+               }
                
                public string Condition {
-                       get { return importElement.GetAttribute ("Condition"); }
+                       get {
+                               string s = importElement.GetAttribute ("Condition");
+                               return s == String.Empty ? null : s;
+                       }
                }
                
                public string EvaluatedProjectPath {