Log target build error.
[mono.git] / mcs / class / Microsoft.Build / Microsoft.Build.Evaluation / ProjectCollection.cs
1 //
2 // ProjectCollection.cs
3 //
4 // Author:
5 //   Leszek Ciesielski (skolima@gmail.com)
6 //   Rolf Bjarne Kvinge (rolf@xamarin.com)
7 //   Atsushi Enomoto (atsushi@xamarin.com)
8 //
9 // (C) 2011 Leszek Ciesielski
10 // Copyright (C) 2011,2013 Xamarin Inc.
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31
32 using Microsoft.Build.Construction;
33 using Microsoft.Build.Execution;
34 using Microsoft.Build.Framework;
35 using Microsoft.Build.Logging;
36 using Microsoft.Build.Utilities;
37 using System;
38 using System.Collections.Generic;
39 using System.Collections.ObjectModel;
40 using System.IO;
41 using System.Linq;
42 using System.Xml;
43 using System.Reflection;
44 using System.Globalization;
45
46 namespace Microsoft.Build.Evaluation
47 {
48         public class ProjectCollection : IDisposable
49         {
50                 public delegate void ProjectAddedEventHandler (object target, ProjectAddedToProjectCollectionEventArgs args);
51                 
52                 public class ProjectAddedToProjectCollectionEventArgs : EventArgs
53                 {
54                         public ProjectAddedToProjectCollectionEventArgs (ProjectRootElement project)
55                         {
56                                 if (project == null)
57                                         throw new ArgumentNullException ("project");
58                                 ProjectRootElement = project;
59                         }
60                         
61                         public ProjectRootElement ProjectRootElement { get; private set; }
62                 }
63
64                 // static members
65
66                 static readonly ProjectCollection global_project_collection;
67
68                 static ProjectCollection ()
69                 {
70                         #if NET_4_5
71                         global_project_collection = new ProjectCollection (new ReadOnlyDictionary<string, string> (new Dictionary<string, string> ()));
72                         #else
73                         global_project_collection = new ProjectCollection (new Dictionary<string, string> ());
74                         #endif
75                 }
76
77                 public static string Escape (string unescapedString)
78                 {
79                         return Mono.XBuild.Utilities.MSBuildUtils.Escape (unescapedString);
80                 }
81
82                 public static string Unescape (string escapedString)
83                 {
84                         return Mono.XBuild.Utilities.MSBuildUtils.Unescape (escapedString);
85                 }
86
87                 public static ProjectCollection GlobalProjectCollection {
88                         get { return global_project_collection; }
89                 }
90
91                 // semantic model part
92
93                 public ProjectCollection ()
94                         : this (null)
95                 {
96                 }
97
98                 public ProjectCollection (IDictionary<string, string> globalProperties)
99                 : this (globalProperties, null, ToolsetDefinitionLocations.Registry | ToolsetDefinitionLocations.ConfigurationFile)
100                 {
101                 }
102
103                 public ProjectCollection (ToolsetDefinitionLocations toolsetDefinitionLocations)
104                 : this (null, null, toolsetDefinitionLocations)
105                 {
106                 }
107
108                 public ProjectCollection (IDictionary<string, string> globalProperties, IEnumerable<ILogger> loggers,
109                                 ToolsetDefinitionLocations toolsetDefinitionLocations)
110                         : this (globalProperties, loggers, null, toolsetDefinitionLocations, 1, false)
111                 {
112                 }
113
114                 public ProjectCollection (IDictionary<string, string> globalProperties,
115                                 IEnumerable<ILogger> loggers, IEnumerable<ForwardingLoggerRecord> remoteLoggers,
116                                 ToolsetDefinitionLocations toolsetDefinitionLocations,
117                                 int maxNodeCount, bool onlyLogCriticalEvents)
118                 {
119                         global_properties = globalProperties ?? new Dictionary<string, string> ();
120                         this.loggers = loggers != null ? loggers.ToList () : new List<ILogger> ();
121                         toolset_locations = toolsetDefinitionLocations;
122                         MaxNodeCount = maxNodeCount;
123                         OnlyLogCriticalEvents = onlyLogCriticalEvents;
124
125                         LoadDefaultToolsets ();
126                 }
127                 
128                 [MonoTODO ("not fired yet")]
129                 public event ProjectAddedEventHandler ProjectAdded;
130                 [MonoTODO ("not fired yet")]
131                 public event EventHandler<ProjectChangedEventArgs> ProjectChanged;
132                 [MonoTODO ("not fired yet")]
133                 public event EventHandler<ProjectCollectionChangedEventArgs> ProjectCollectionChanged;
134                 [MonoTODO ("not fired yet")]
135                 public event EventHandler<ProjectXmlChangedEventArgs> ProjectXmlChanged;
136
137                 public void AddProject (Project project)
138                 {
139                         this.loaded_projects.Add (project);
140                         if (ProjectAdded != null)
141                                 ProjectAdded (this, new ProjectAddedToProjectCollectionEventArgs (project.Xml));
142                 }
143
144                 public int Count {
145                         get { return loaded_projects.Count; }
146                 }
147
148                 string default_tools_version;
149                 public string DefaultToolsVersion {
150                         get { return default_tools_version; }
151                         set {
152                                 if (GetToolset (value) == null)
153                                         throw new InvalidOperationException (string.Format ("Toolset '{0}' does not exist", value));
154                                 default_tools_version = value;
155                         }
156                 }
157
158                 public void Dispose ()
159                 {
160                         Dispose (true);
161                         GC.SuppressFinalize (this);
162                 }
163
164                 protected virtual void Dispose (bool disposing)
165                 {
166                         if (disposing) {
167                         }
168                 }
169
170                 public ICollection<Project> GetLoadedProjects (string fullPath)
171                 {
172                         return LoadedProjects.Where (p => p.FullPath != null && Path.GetFullPath (p.FullPath) == Path.GetFullPath (fullPath)).ToList ();
173                 }
174
175                 readonly IDictionary<string, string> global_properties;
176
177                 public IDictionary<string, string> GlobalProperties {
178                         get { return global_properties; }
179                 }
180
181                 readonly List<Project> loaded_projects = new List<Project> ();
182                 
183                 public Project LoadProject (string fileName)
184                 {
185                         return LoadProject (fileName, DefaultToolsVersion);
186                 }
187                 
188                 public Project LoadProject (string fileName, string toolsVersion)
189                 {
190                         return LoadProject (fileName, null, toolsVersion);
191                 }
192                 
193                 public Project LoadProject (string fileName, IDictionary<string,string> globalProperties, string toolsVersion)
194                 {
195                         var ret = new Project (fileName, globalProperties, toolsVersion);
196                         loaded_projects.Add (ret);
197                         return ret;
198                 }
199                 
200                 // These methods somehow don't add the project to ProjectCollection...
201                 public Project LoadProject (XmlReader xmlReader)
202                 {
203                         return LoadProject (xmlReader, DefaultToolsVersion);
204                 }
205                 
206                 public Project LoadProject (XmlReader xmlReader, string toolsVersion)
207                 {
208                         return LoadProject (xmlReader, null, toolsVersion);
209                 }
210                 
211                 public Project LoadProject (XmlReader xmlReader, IDictionary<string,string> globalProperties, string toolsVersion)
212                 {
213                         return new Project (xmlReader, globalProperties, toolsVersion);
214                 }
215                 
216                 public ICollection<Project> LoadedProjects {
217                         get { return loaded_projects; }
218                 }
219
220                 readonly List<ILogger> loggers = new List<ILogger> ();
221                 [MonoTODO]
222                 public ICollection<ILogger> Loggers {
223                         get { return loggers; }
224                 }
225
226                 [MonoTODO]
227                 public bool OnlyLogCriticalEvents { get; set; }
228
229                 [MonoTODO]
230                 public bool SkipEvaluation { get; set; }
231
232                 readonly ToolsetDefinitionLocations toolset_locations;
233                 public ToolsetDefinitionLocations ToolsetLocations {
234                         get { return toolset_locations; }
235                 }
236
237                 readonly List<Toolset> toolsets = new List<Toolset> ();
238                 // so what should we do without ToolLocationHelper in Microsoft.Build.Utilities.dll? There is no reference to it in this dll.
239                 public ICollection<Toolset> Toolsets {
240                         // For ConfigurationFile and None, they cannot be added externally.
241                         get { return (ToolsetLocations & ToolsetDefinitionLocations.Registry) != 0 ? toolsets : toolsets.ToList (); }
242                 }
243                 
244                 public Toolset GetToolset (string toolsVersion)
245                 {
246                         return Toolsets.FirstOrDefault (t => t.ToolsVersion == toolsVersion);
247                 }
248
249                 //FIXME: should also support config file, depending on ToolsetLocations
250                 void LoadDefaultToolsets ()
251                 {
252                         AddToolset (new Toolset ("2.0",
253                                 ToolLocationHelper.GetPathToDotNetFramework (TargetDotNetFrameworkVersion.Version20), this, null));
254                         AddToolset (new Toolset ("3.0",
255                                 ToolLocationHelper.GetPathToDotNetFramework (TargetDotNetFrameworkVersion.Version30), this, null));
256                         AddToolset (new Toolset ("3.5",
257                                 ToolLocationHelper.GetPathToDotNetFramework (TargetDotNetFrameworkVersion.Version35), this, null));
258 #if NET_4_0
259                         AddToolset (new Toolset ("4.0",
260                                 ToolLocationHelper.GetPathToDotNetFramework (TargetDotNetFrameworkVersion.Version40), this, null));
261 #endif
262 #if NET_4_5
263                         AddToolset (new Toolset ("12.0",
264                                 ToolLocationHelper.GetMSBuildInstallPath ("12.0"), this, ToolLocationHelper.GetPathToDotNetFramework (TargetDotNetFrameworkVersion.Version40)));
265 #endif
266                         default_tools_version = toolsets.First ().ToolsVersion;
267                 }
268                 
269                 [MonoTODO ("not verified at all")]
270                 public void AddToolset (Toolset toolset)
271                 {
272                         toolsets.Add (toolset);
273                 }
274                 
275                 [MonoTODO ("not verified at all")]
276                 public void RemoveAllToolsets ()
277                 {
278                         toolsets.Clear ();
279                 }
280                 
281                 [MonoTODO ("not verified at all")]
282                 public void RegisterLogger (ILogger logger)
283                 {
284                         loggers.Add (logger);
285                 }
286                 
287                 [MonoTODO ("not verified at all")]
288                 public void RegisterLoggers (IEnumerable<ILogger> loggers)
289                 {
290                         foreach (var logger in loggers)
291                                 this.loggers.Add (logger);
292                 }
293
294                 public void UnloadAllProjects ()
295                 {
296                         throw new NotImplementedException ();
297                 }
298
299                 [MonoTODO ("Not verified at all")]
300                 public void UnloadProject (Project project)
301                 {
302                         this.loaded_projects.Remove (project);
303                 }
304
305                 [MonoTODO ("Not verified at all")]
306                 public void UnloadProject (ProjectRootElement projectRootElement)
307                 {
308                         foreach (var proj in loaded_projects.Where (p => p.Xml == projectRootElement).ToArray ())
309                                 UnloadProject (proj);
310                 }
311
312                 public static Version Version {
313                         get { throw new NotImplementedException (); }
314                 }
315
316                 // Execution part
317
318                 [MonoTODO]
319                 public bool DisableMarkDirty { get; set; }
320
321                 [MonoTODO]
322                 public HostServices HostServices { get; set; }
323
324                 [MonoTODO]
325                 public bool IsBuildEnabled { get; set; }
326                 
327                 internal string BuildStartupDirectory { get; set; }
328                 
329                 internal int MaxNodeCount { get; private set; }
330                 
331                 Stack<string> ongoing_imports = new Stack<string> ();
332                 
333                 internal Stack<string> OngoingImports {
334                         get { return ongoing_imports; }
335                 }
336                 
337                 // common part
338                 internal static IEnumerable<EnvironmentProjectProperty> GetWellKnownProperties (Project project)
339                 {
340                         Func<string,string,EnvironmentProjectProperty> create = (name, value) => new EnvironmentProjectProperty (project, name, value, true);
341                         return GetWellKnownProperties (create);
342                 }
343                 
344                 internal static IEnumerable<ProjectPropertyInstance> GetWellKnownProperties (ProjectInstance project)
345                 {
346                         Func<string,string,ProjectPropertyInstance> create = (name, value) => new ProjectPropertyInstance (name, true, value);
347                         return GetWellKnownProperties (create);
348                 }
349                 
350                 static IEnumerable<T> GetWellKnownProperties<T> (Func<string,string,T> create)
351                 {
352                         var ext = Environment.GetEnvironmentVariable ("MSBuildExtensionsPath") ?? DefaultExtensionsPath;
353                         yield return create ("MSBuildExtensionsPath", ext);
354                         var ext32 = Environment.GetEnvironmentVariable ("MSBuildExtensionsPath32") ?? DefaultExtensionsPath;
355                         yield return create ("MSBuildExtensionsPath32", ext32);
356                         var ext64 = Environment.GetEnvironmentVariable ("MSBuildExtensionsPath64") ?? DefaultExtensionsPath;
357                         yield return create ("MSBuildExtensionsPath64", ext64);
358                 }
359
360                 static string extensions_path;
361                 internal static string DefaultExtensionsPath {
362                         get {
363                                 if (extensions_path == null) {
364                                         // NOTE: code from mcs/tools/gacutil/driver.cs
365                                         PropertyInfo gac = typeof (System.Environment).GetProperty (
366                                                         "GacPath", BindingFlags.Static | BindingFlags.NonPublic);
367
368                                         if (gac != null) {
369                                                 MethodInfo get_gac = gac.GetGetMethod (true);
370                                                 string gac_path = (string) get_gac.Invoke (null, null);
371                                                 extensions_path = Path.GetFullPath (Path.Combine (
372                                                                         gac_path, Path.Combine ("..", "xbuild")));
373                                         }
374                                 }
375                                 return extensions_path;
376                         }
377                 }
378                 
379                 internal IEnumerable<ReservedProjectProperty> GetReservedProperties (Toolset toolset, Project project)
380                 {
381                         Func<string,Func<string>,ReservedProjectProperty> create = (name, value) => new ReservedProjectProperty (project, name, value);
382                         return GetReservedProperties<ReservedProjectProperty> (toolset, project.Xml, create, () => project.FullPath);
383                 }
384                 
385                 internal IEnumerable<ProjectPropertyInstance> GetReservedProperties (Toolset toolset, ProjectInstance project, ProjectRootElement xml)
386                 {
387                         Func<string,Func<string>,ProjectPropertyInstance> create = (name, value) => new ProjectPropertyInstance (name, true, null, value);
388                         return GetReservedProperties<ProjectPropertyInstance> (toolset, xml, create, () => project.FullPath);
389                 }
390                 
391                 // seealso http://msdn.microsoft.com/en-us/library/ms164309.aspx
392                 IEnumerable<T> GetReservedProperties<T> (Toolset toolset, ProjectRootElement project, Func<string,Func<string>,T> create, Func<string> projectFullPath)
393                 {
394                         yield return create ("MSBuildBinPath", () => toolset.ToolsPath);
395                         // FIXME: add MSBuildLastTaskResult
396                         // FIXME: add MSBuildNodeCount
397                         // FIXME: add MSBuildProgramFiles32
398                         yield return create ("MSBuildProjectDefaultTargets", () => project.DefaultTargets);
399                         yield return create ("MSBuildProjectDirectory", () => project.DirectoryPath + Path.DirectorySeparatorChar);
400                         yield return create ("MSBuildProjectDirectoryNoRoot", () => project.DirectoryPath.Substring (Path.GetPathRoot (project.DirectoryPath).Length));
401                         yield return create ("MSBuildProjectExtension", () => Path.GetExtension (project.FullPath));
402                         yield return create ("MSBuildProjectFile", () => Path.GetFileName (project.FullPath));
403                         yield return create ("MSBuildProjectFullPath", () => project.FullPath);
404                         yield return create ("MSBuildProjectName", () => Path.GetFileNameWithoutExtension (project.FullPath));
405                         yield return create ("MSBuildStartupDirectory", () => BuildStartupDirectory);
406                         yield return create ("MSBuildThisFile", () => Path.GetFileName (GetEvaluationTimeThisFile (projectFullPath)));
407                         yield return create ("MSBuildThisFileFullPath", () => GetEvaluationTimeThisFile (projectFullPath));
408                         yield return create ("MSBuildThisFileName", () => Path.GetFileNameWithoutExtension (GetEvaluationTimeThisFile (projectFullPath)));
409                         yield return create ("MSBuildThisFileExtension", () => Path.GetExtension (GetEvaluationTimeThisFile (projectFullPath)));
410
411                         yield return create ("MSBuildThisFileDirectory", () => Path.GetDirectoryName (GetEvaluationTimeThisFileDirectory (projectFullPath)));
412                         yield return create ("MSBuildThisFileDirectoryNoRoot", () => {
413                                 string dir = GetEvaluationTimeThisFileDirectory (projectFullPath) + Path.DirectorySeparatorChar;
414                                 return dir.Substring (Path.GetPathRoot (dir).Length);
415                                 });
416                         yield return create ("MSBuildToolsPath", () => toolset.ToolsPath);
417                         yield return create ("MSBuildToolsVersion", () => toolset.ToolsVersion);
418                 }
419                 
420                 // These are required for reserved property, represents dynamically changing property values.
421                 // This should resolve to either the project file path or that of the imported file.
422                 internal string GetEvaluationTimeThisFileDirectory (Func<string> nonImportingTimeFullPath)
423                 {
424                         var file = GetEvaluationTimeThisFile (nonImportingTimeFullPath);
425                         var dir = Path.IsPathRooted (file) ? Path.GetDirectoryName (file) : Directory.GetCurrentDirectory ();
426                         return dir + Path.DirectorySeparatorChar;
427                 }
428
429                 internal string GetEvaluationTimeThisFile (Func<string> nonImportingTimeFullPath)
430                 {
431                         return OngoingImports.Count > 0 ? OngoingImports.Peek () : (nonImportingTimeFullPath () ?? string.Empty);
432                 }
433                 
434                 static readonly char [] item_target_sep = {';'};
435                 
436                 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)
437                 {
438                         var includes = expandString (include).Trim ().Split (item_target_sep, StringSplitOptions.RemoveEmptyEntries);
439                         var excludes = expandString (exclude).Trim ().Split (item_target_sep, StringSplitOptions.RemoveEmptyEntries);
440                         
441                         if (includes.Length == 0)
442                                 yield break;
443                         if (includes.Length == 1 && includes [0].IndexOf ('*') < 0 && excludes.Length == 0) {
444                                 // for most case - shortcut.
445                                 var item = creator (includes [0]);
446                                 yield return item;
447                         } else {
448                                 var ds = new Microsoft.Build.BuildEngine.DirectoryScanner () {
449                                         BaseDirectory = new DirectoryInfo (directory),
450                                         Includes = includes.Where (s => !string.IsNullOrWhiteSpace (s)).Select (i => taskItemCreator (i)).ToArray (),
451                                         Excludes = excludes.Where (s => !string.IsNullOrWhiteSpace (s)).Select (e => taskItemCreator (e)).ToArray (),
452                                 };
453                                 ds.Scan ();
454                                 foreach (var taskItem in ds.MatchedItems) {
455                                         if (isDuplicate (taskItem))
456                                                 continue; // skip duplicate
457                                         var item = creator (taskItem.ItemSpec);
458                                         string recurse = taskItem.GetMetadata ("RecursiveDir");
459                                         assignRecurse (item, recurse);
460                                         yield return item;
461                                 }
462                         }
463                 }
464                 
465                 static readonly char [] path_sep = {Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar};
466                 
467                 internal static string GetWellKnownMetadata (string name, string file, Func<string,string> getFullPath, string recursiveDir)
468                 {
469                         switch (name.ToLower (CultureInfo.InvariantCulture)) {
470                         case "fullpath":
471                                 return getFullPath (file);
472                         case "rootdir":
473                                 return Path.GetPathRoot (getFullPath (file));
474                         case "filename":
475                                 return Path.GetFileNameWithoutExtension (file);
476                         case "extension":
477                                 return Path.GetExtension (file);
478                         case "relativedir":
479                                         var idx = file.LastIndexOfAny (path_sep);
480                                         return idx < 0 ? string.Empty : file.Substring (0, idx + 1);
481                         case "directory":
482                                         var fp = getFullPath (file);
483                                         return Path.GetDirectoryName (fp).Substring (Path.GetPathRoot (fp).Length);
484                         case "recursivedir":
485                                 return recursiveDir;
486                         case "identity":
487                                 return file;
488                         case "modifiedtime":
489                                 return new FileInfo (getFullPath (file)).LastWriteTime.ToString ("yyyy-MM-dd HH:mm:ss.fffffff");
490                         case "createdtime":
491                                 return new FileInfo (getFullPath (file)).CreationTime.ToString ("yyyy-MM-dd HH:mm:ss.fffffff");
492                         case "accessedtime":
493                                 return new FileInfo (getFullPath (file)).LastAccessTime.ToString ("yyyy-MM-dd HH:mm:ss.fffffff");
494                         }
495                         return null;
496                 }
497         }
498 }