Merge pull request #900 from Blewzman/FixAggregateExceptionGetBaseException
[mono.git] / mcs / class / Microsoft.Build / Microsoft.Build.Evaluation / ProjectCollection.cs
index 348a844ad40e4f4574507cf6eed4b38e0e6f9460..ab3ee06638909f6d645195d96d662e817cb55271 100644 (file)
@@ -4,9 +4,10 @@
 // Author:
 //   Leszek Ciesielski (skolima@gmail.com)
 //   Rolf Bjarne Kvinge (rolf@xamarin.com)
+//   Atsushi Enomoto (atsushi@xamarin.com)
 //
 // (C) 2011 Leszek Ciesielski
-// Copyright (C) 2011 Xamarin Inc.
+// Copyright (C) 2011,2013 Xamarin Inc.
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
 //
 
 using Microsoft.Build.Construction;
+using Microsoft.Build.Execution;
 using Microsoft.Build.Framework;
 using Microsoft.Build.Logging;
+using Microsoft.Build.Utilities;
 using System;
 using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.IO;
+using System.Linq;
+using System.Xml;
+using System.Reflection;
+using System.Globalization;
 
 namespace Microsoft.Build.Evaluation
 {
        public class ProjectCollection : IDisposable
        {
+               public delegate void ProjectAddedEventHandler (object target, ProjectAddedToProjectCollectionEventArgs args);
+               
+               public class ProjectAddedToProjectCollectionEventArgs : EventArgs
+               {
+                       public ProjectAddedToProjectCollectionEventArgs (ProjectRootElement project)
+                       {
+                               if (project == null)
+                                       throw new ArgumentNullException ("project");
+                               ProjectRootElement = project;
+                       }
+                       
+                       public ProjectRootElement ProjectRootElement { get; private set; }
+               }
+
+               // static members
+
+               static readonly ProjectCollection global_project_collection;
+
+               static ProjectCollection ()
+               {
+                       #if NET_4_5
+                       global_project_collection = new ProjectCollection (new ReadOnlyDictionary<string, string> (new Dictionary<string, string> ()));
+                       #else
+                       global_project_collection = new ProjectCollection (new Dictionary<string, string> ());
+                       #endif
+               }
+
+               public static string Escape (string unescapedString)
+               {
+                       return Mono.XBuild.Utilities.MSBuildUtils.Escape (unescapedString);
+               }
+
+               public static string Unescape (string escapedString)
+               {
+                       return Mono.XBuild.Utilities.MSBuildUtils.Unescape (escapedString);
+               }
+
+               public static ProjectCollection GlobalProjectCollection {
+                       get { return global_project_collection; }
+               }
+
+               // semantic model part
+
                public ProjectCollection ()
+                       : this (null)
                {
                }
 
@@ -54,7 +107,7 @@ namespace Microsoft.Build.Evaluation
 
                public ProjectCollection (IDictionary<string, string> globalProperties, IEnumerable<ILogger> loggers,
                                ToolsetDefinitionLocations toolsetDefinitionLocations)
-               : this (globalProperties, loggers, null, toolsetDefinitionLocations, 1, false)
+                       : this (globalProperties, loggers, null, toolsetDefinitionLocations, 1, false)
                {
                }
 
@@ -63,16 +116,43 @@ namespace Microsoft.Build.Evaluation
                                ToolsetDefinitionLocations toolsetDefinitionLocations,
                                int maxNodeCount, bool onlyLogCriticalEvents)
                {
-                       throw new NotImplementedException ();
+                       global_properties = globalProperties ?? new Dictionary<string, string> ();
+                       this.loggers = loggers != null ? loggers.ToList () : new List<ILogger> ();
+                       toolset_locations = toolsetDefinitionLocations;
+                       MaxNodeCount = maxNodeCount;
+                       OnlyLogCriticalEvents = onlyLogCriticalEvents;
+
+                       LoadDefaultToolsets ();
                }
+               
+               [MonoTODO ("not fired yet")]
+               public event ProjectAddedEventHandler ProjectAdded;
+               [MonoTODO ("not fired yet")]
+               public event EventHandler<ProjectChangedEventArgs> ProjectChanged;
+               [MonoTODO ("not fired yet")]
+               public event EventHandler<ProjectCollectionChangedEventArgs> ProjectCollectionChanged;
+               [MonoTODO ("not fired yet")]
+               public event EventHandler<ProjectXmlChangedEventArgs> ProjectXmlChanged;
 
-               public static string Escape (string unescapedString)
+               public void AddProject (Project project)
                {
-                       return unescapedString;
+                       this.loaded_projects.Add (project);
+                       if (ProjectAdded != null)
+                               ProjectAdded (this, new ProjectAddedToProjectCollectionEventArgs (project.Xml));
                }
 
-               public static ProjectCollection GlobalProjectCollection {
-                       get { return globalProjectCollection; }
+               public int Count {
+                       get { return loaded_projects.Count; }
+               }
+
+               string default_tools_version;
+               public string DefaultToolsVersion {
+                       get { return default_tools_version; }
+                       set {
+                               if (GetToolset (value) == null)
+                                       throw new InvalidOperationException (string.Format ("Toolset '{0}' does not exist", value));
+                               default_tools_version = value;
+                       }
                }
 
                public void Dispose ()
@@ -87,19 +167,127 @@ namespace Microsoft.Build.Evaluation
                        }
                }
 
-               static ProjectCollection globalProjectCollection = new ProjectCollection ();
-
                public ICollection<Project> GetLoadedProjects (string fullPath)
                {
-                       throw new NotImplementedException ();
+                       return LoadedProjects.Where (p => p.FullPath != null && Path.GetFullPath (p.FullPath) == Path.GetFullPath (fullPath)).ToList ();
+               }
+
+               readonly IDictionary<string, string> global_properties;
+
+               public IDictionary<string, string> GlobalProperties {
+                       get { return global_properties; }
                }
 
+               readonly List<Project> loaded_projects = new List<Project> ();
+               
+               public Project LoadProject (string fileName)
+               {
+                       return LoadProject (fileName, DefaultToolsVersion);
+               }
+               
+               public Project LoadProject (string fileName, string toolsVersion)
+               {
+                       return LoadProject (fileName, null, toolsVersion);
+               }
+               
+               public Project LoadProject (string fileName, IDictionary<string,string> globalProperties, string toolsVersion)
+               {
+                       var ret = new Project (fileName, globalProperties, toolsVersion);
+                       loaded_projects.Add (ret);
+                       return ret;
+               }
+               
+               // These methods somehow don't add the project to ProjectCollection...
+               public Project LoadProject (XmlReader xmlReader)
+               {
+                       return LoadProject (xmlReader, DefaultToolsVersion);
+               }
+               
+               public Project LoadProject (XmlReader xmlReader, string toolsVersion)
+               {
+                       return LoadProject (xmlReader, null, toolsVersion);
+               }
+               
+               public Project LoadProject (XmlReader xmlReader, IDictionary<string,string> globalProperties, string toolsVersion)
+               {
+                       return new Project (xmlReader, globalProperties, toolsVersion);
+               }
+               
+               public ICollection<Project> LoadedProjects {
+                       get { return loaded_projects; }
+               }
+
+               readonly List<ILogger> loggers = new List<ILogger> ();
+               [MonoTODO]
+               public ICollection<ILogger> Loggers {
+                       get { return loggers; }
+               }
+
+               [MonoTODO]
+               public bool OnlyLogCriticalEvents { get; set; }
+
+               [MonoTODO]
+               public bool SkipEvaluation { get; set; }
+
+               readonly ToolsetDefinitionLocations toolset_locations;
                public ToolsetDefinitionLocations ToolsetLocations {
-                       get { throw new NotImplementedException (); }
+                       get { return toolset_locations; }
                }
 
+               readonly List<Toolset> toolsets = new List<Toolset> ();
+               // so what should we do without ToolLocationHelper in Microsoft.Build.Utilities.dll? There is no reference to it in this dll.
                public ICollection<Toolset> Toolsets {
-                       get { throw new NotImplementedException (); }
+                       // For ConfigurationFile and None, they cannot be added externally.
+                       get { return (ToolsetLocations & ToolsetDefinitionLocations.Registry) != 0 ? toolsets : toolsets.ToList (); }
+               }
+               
+               public Toolset GetToolset (string toolsVersion)
+               {
+                       return Toolsets.FirstOrDefault (t => t.ToolsVersion == toolsVersion);
+               }
+
+               //FIXME: should also support config file, depending on ToolsetLocations
+               void LoadDefaultToolsets ()
+               {
+                       AddToolset (new Toolset ("2.0",
+                               ToolLocationHelper.GetPathToDotNetFramework (TargetDotNetFrameworkVersion.Version20), this, null));
+                       AddToolset (new Toolset ("3.0",
+                               ToolLocationHelper.GetPathToDotNetFramework (TargetDotNetFrameworkVersion.Version30), this, null));
+                       AddToolset (new Toolset ("3.5",
+                               ToolLocationHelper.GetPathToDotNetFramework (TargetDotNetFrameworkVersion.Version35), this, null));
+#if NET_4_0
+                       AddToolset (new Toolset ("4.0",
+                               ToolLocationHelper.GetPathToDotNetFramework (TargetDotNetFrameworkVersion.Version40), this, null));
+#endif
+#if XBUILD_12
+                       AddToolset (new Toolset ("12.0", ToolLocationHelper.GetPathToBuildTools ("12.0"), this, null));
+#endif
+                       default_tools_version = toolsets.First ().ToolsVersion;
+               }
+               
+               [MonoTODO ("not verified at all")]
+               public void AddToolset (Toolset toolset)
+               {
+                       toolsets.Add (toolset);
+               }
+               
+               [MonoTODO ("not verified at all")]
+               public void RemoveAllToolsets ()
+               {
+                       toolsets.Clear ();
+               }
+               
+               [MonoTODO ("not verified at all")]
+               public void RegisterLogger (ILogger logger)
+               {
+                       loggers.Add (logger);
+               }
+               
+               [MonoTODO ("not verified at all")]
+               public void RegisterLoggers (IEnumerable<ILogger> loggers)
+               {
+                       foreach (var logger in loggers)
+                               this.loggers.Add (logger);
                }
 
                public void UnloadAllProjects ()
@@ -107,18 +295,203 @@ namespace Microsoft.Build.Evaluation
                        throw new NotImplementedException ();
                }
 
+               [MonoTODO ("Not verified at all")]
                public void UnloadProject (Project project)
                {
-                       throw new NotImplementedException ();
+                       this.loaded_projects.Remove (project);
                }
 
+               [MonoTODO ("Not verified at all")]
                public void UnloadProject (ProjectRootElement projectRootElement)
                {
-                       throw new NotImplementedException ();
+                       foreach (var proj in loaded_projects.Where (p => p.Xml == projectRootElement).ToArray ())
+                               UnloadProject (proj);
                }
 
                public static Version Version {
                        get { throw new NotImplementedException (); }
                }
+
+               // Execution part
+
+               [MonoTODO]
+               public bool DisableMarkDirty { get; set; }
+
+               [MonoTODO]
+               public HostServices HostServices { get; set; }
+
+               [MonoTODO]
+               public bool IsBuildEnabled { get; set; }
+               
+               internal string BuildStartupDirectory { get; set; }
+               
+               internal int MaxNodeCount { get; private set; }
+               
+               Stack<string> ongoing_imports = new Stack<string> ();
+               
+               internal Stack<string> OngoingImports {
+                       get { return ongoing_imports; }
+               }
+               
+               // common part
+               internal static IEnumerable<EnvironmentProjectProperty> GetWellKnownProperties (Project project)
+               {
+                       Func<string,string,EnvironmentProjectProperty> create = (name, value) => new EnvironmentProjectProperty (project, name, value, true);
+                       return GetWellKnownProperties (create);
+               }
+               
+               internal static IEnumerable<ProjectPropertyInstance> GetWellKnownProperties (ProjectInstance project)
+               {
+                       Func<string,string,ProjectPropertyInstance> create = (name, value) => new ProjectPropertyInstance (name, true, value);
+                       return GetWellKnownProperties (create);
+               }
+               
+               static IEnumerable<T> GetWellKnownProperties<T> (Func<string,string,T> create)
+               {
+                       var ext = Environment.GetEnvironmentVariable ("MSBuildExtensionsPath") ?? DefaultExtensionsPath;
+                       yield return create ("MSBuildExtensionsPath", ext);
+                       var ext32 = Environment.GetEnvironmentVariable ("MSBuildExtensionsPath32") ?? DefaultExtensionsPath;
+                       yield return create ("MSBuildExtensionsPath32", ext32);
+                       var ext64 = Environment.GetEnvironmentVariable ("MSBuildExtensionsPath64") ?? DefaultExtensionsPath;
+                       yield return create ("MSBuildExtensionsPath64", ext64);
+               }
+
+               static string extensions_path;
+               internal static string DefaultExtensionsPath {
+                       get {
+                               if (extensions_path == null) {
+                                       // NOTE: code from mcs/tools/gacutil/driver.cs
+                                       PropertyInfo gac = typeof (System.Environment).GetProperty (
+                                                       "GacPath", BindingFlags.Static | BindingFlags.NonPublic);
+
+                                       if (gac != null) {
+                                               MethodInfo get_gac = gac.GetGetMethod (true);
+                                               string gac_path = (string) get_gac.Invoke (null, null);
+                                               extensions_path = Path.GetFullPath (Path.Combine (
+                                                                       gac_path, Path.Combine ("..", "xbuild")));
+                                       }
+                               }
+                               return extensions_path;
+                       }
+               }
+               
+               internal IEnumerable<ReservedProjectProperty> GetReservedProperties (Toolset toolset, Project project)
+               {
+                       Func<string,Func<string>,ReservedProjectProperty> create = (name, value) => new ReservedProjectProperty (project, name, value);
+                       return GetReservedProperties<ReservedProjectProperty> (toolset, project.Xml, create, () => project.FullPath);
+               }
+               
+               internal IEnumerable<ProjectPropertyInstance> GetReservedProperties (Toolset toolset, ProjectInstance project, ProjectRootElement xml)
+               {
+                       Func<string,Func<string>,ProjectPropertyInstance> create = (name, value) => new ProjectPropertyInstance (name, true, null, value);
+                       return GetReservedProperties<ProjectPropertyInstance> (toolset, xml, create, () => project.FullPath);
+               }
+               
+               // seealso http://msdn.microsoft.com/en-us/library/ms164309.aspx
+               IEnumerable<T> GetReservedProperties<T> (Toolset toolset, ProjectRootElement project, Func<string,Func<string>,T> create, Func<string> projectFullPath)
+               {
+                       yield return create ("MSBuildBinPath", () => toolset.ToolsPath);
+                       // FIXME: add MSBuildLastTaskResult
+                       // FIXME: add MSBuildNodeCount
+                       // FIXME: add MSBuildProgramFiles32
+                       yield return create ("MSBuildProjectDefaultTargets", () => project.DefaultTargets);
+                       yield return create ("MSBuildProjectDirectory", () => project.DirectoryPath + Path.DirectorySeparatorChar);
+                       yield return create ("MSBuildProjectDirectoryNoRoot", () => project.DirectoryPath.Substring (Path.GetPathRoot (project.DirectoryPath).Length));
+                       yield return create ("MSBuildProjectExtension", () => Path.GetExtension (project.FullPath));
+                       yield return create ("MSBuildProjectFile", () => Path.GetFileName (project.FullPath));
+                       yield return create ("MSBuildProjectFullPath", () => project.FullPath);
+                       yield return create ("MSBuildProjectName", () => Path.GetFileNameWithoutExtension (project.FullPath));
+                       yield return create ("MSBuildStartupDirectory", () => BuildStartupDirectory);
+                       yield return create ("MSBuildThisFile", () => Path.GetFileName (GetEvaluationTimeThisFile (projectFullPath)));
+                       yield return create ("MSBuildThisFileFullPath", () => GetEvaluationTimeThisFile (projectFullPath));
+                       yield return create ("MSBuildThisFileName", () => Path.GetFileNameWithoutExtension (GetEvaluationTimeThisFile (projectFullPath)));
+                       yield return create ("MSBuildThisFileExtension", () => Path.GetExtension (GetEvaluationTimeThisFile (projectFullPath)));
+
+                       yield return create ("MSBuildThisFileDirectory", () => Path.GetDirectoryName (GetEvaluationTimeThisFileDirectory (projectFullPath)));
+                       yield return create ("MSBuildThisFileDirectoryNoRoot", () => {
+                               string dir = GetEvaluationTimeThisFileDirectory (projectFullPath) + Path.DirectorySeparatorChar;
+                               return dir.Substring (Path.GetPathRoot (dir).Length);
+                               });
+                       yield return create ("MSBuildToolsPath", () => toolset.ToolsPath);
+                       yield return create ("MSBuildToolsVersion", () => toolset.ToolsVersion);
+               }
+               
+               // These are required for reserved property, represents dynamically changing property values.
+               // This should resolve to either the project file path or that of the imported file.
+               internal string GetEvaluationTimeThisFileDirectory (Func<string> nonImportingTimeFullPath)
+               {
+                       var file = GetEvaluationTimeThisFile (nonImportingTimeFullPath);
+                       var dir = Path.IsPathRooted (file) ? Path.GetDirectoryName (file) : Directory.GetCurrentDirectory ();
+                       return dir + Path.DirectorySeparatorChar;
+               }
+
+               internal string GetEvaluationTimeThisFile (Func<string> nonImportingTimeFullPath)
+               {
+                       return OngoingImports.Count > 0 ? OngoingImports.Peek () : (nonImportingTimeFullPath () ?? string.Empty);
+               }
+               
+               static readonly char [] item_target_sep = {';'};
+               
+               internal static IEnumerable<T> GetAllItems<T> (Func<string,string> expandString, string include, string exclude, Func<string,T> creator, Func<string,ITaskItem> taskItemCreator, string directory, Action<T,string> assignRecurse, Func<ITaskItem,bool> isDuplicate)
+               {
+                       var includes = expandString (include).Trim ().Split (item_target_sep, StringSplitOptions.RemoveEmptyEntries);
+                       var excludes = expandString (exclude).Trim ().Split (item_target_sep, StringSplitOptions.RemoveEmptyEntries);
+                       
+                       if (includes.Length == 0)
+                               yield break;
+                       if (includes.Length == 1 && includes [0].IndexOf ('*') < 0 && excludes.Length == 0) {
+                               // for most case - shortcut.
+                               var item = creator (includes [0]);
+                               yield return item;
+                       } else {
+                               var ds = new Microsoft.Build.BuildEngine.DirectoryScanner () {
+                                       BaseDirectory = new DirectoryInfo (directory),
+                                       Includes = includes.Where (s => !string.IsNullOrWhiteSpace (s)).Select (i => taskItemCreator (i)).ToArray (),
+                                       Excludes = excludes.Where (s => !string.IsNullOrWhiteSpace (s)).Select (e => taskItemCreator (e)).ToArray (),
+                               };
+                               ds.Scan ();
+                               foreach (var taskItem in ds.MatchedItems) {
+                                       if (isDuplicate (taskItem))
+                                               continue; // skip duplicate
+                                       var item = creator (taskItem.ItemSpec);
+                                       string recurse = taskItem.GetMetadata ("RecursiveDir");
+                                       assignRecurse (item, recurse);
+                                       yield return item;
+                               }
+                       }
+               }
+               
+               static readonly char [] path_sep = {Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar};
+               
+               internal static string GetWellKnownMetadata (string name, string file, Func<string,string> getFullPath, string recursiveDir)
+               {
+                       switch (name.ToLower (CultureInfo.InvariantCulture)) {
+                       case "fullpath":
+                               return getFullPath (file);
+                       case "rootdir":
+                               return Path.GetPathRoot (getFullPath (file));
+                       case "filename":
+                               return Path.GetFileNameWithoutExtension (file);
+                       case "extension":
+                               return Path.GetExtension (file);
+                       case "relativedir":
+                                       var idx = file.LastIndexOfAny (path_sep);
+                                       return idx < 0 ? string.Empty : file.Substring (0, idx + 1);
+                       case "directory":
+                                       var fp = getFullPath (file);
+                                       return Path.GetDirectoryName (fp).Substring (Path.GetPathRoot (fp).Length);
+                       case "recursivedir":
+                               return recursiveDir;
+                       case "identity":
+                               return file;
+                       case "modifiedtime":
+                               return new FileInfo (getFullPath (file)).LastWriteTime.ToString ("yyyy-MM-dd HH:mm:ss.fffffff");
+                       case "createdtime":
+                               return new FileInfo (getFullPath (file)).CreationTime.ToString ("yyyy-MM-dd HH:mm:ss.fffffff");
+                       case "accessedtime":
+                               return new FileInfo (getFullPath (file)).LastAccessTime.ToString ("yyyy-MM-dd HH:mm:ss.fffffff");
+                       }
+                       return null;
+               }
        }
 }