workaround threading by Task isse. Also fix regression on "no target found" case...
[mono.git] / mcs / class / Microsoft.Build / Microsoft.Build.Internal / BuildEngine4.cs
1 //
2 // BuildEngine4.cs
3 //
4 // Author:
5 //   Atsushi Enomoto (atsushi@xamarin.com)
6 //
7 // Copyright (C) 2013 Xamarin Inc. (http://www.xamarin.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28 using System;
29 using System.Collections;
30 using System.Collections.Generic;
31 using Microsoft.Build.BuildEngine;
32 using Microsoft.Build.Execution;
33 using Microsoft.Build.Framework;
34 using Microsoft.Build.Evaluation;
35 using System.Linq;
36 using System.IO;
37
38 namespace Microsoft.Build.Internal
39 {
40         class BuildEngine4 : IBuildEngine4
41         {
42                 public BuildEngine4 (BuildSubmission submission)
43                 {
44                         this.submission = submission;
45                         event_source = new EventSource ();
46                         if (submission.BuildManager.OngoingBuildParameters.Loggers != null)
47                                 foreach (var l in submission.BuildManager.OngoingBuildParameters.Loggers)
48                                         l.Initialize (event_source);
49                 }
50
51                 BuildSubmission submission;
52                 ProjectInstance project;
53                 ProjectTaskInstance current_task;
54                 EventSource event_source;
55                 
56                 public ProjectCollection Projects {
57                         get { return submission.BuildManager.OngoingBuildParameters.ProjectCollection; }
58                 }
59
60                 // FIXME:
61                 // While we are not faced to implement those features, there are some modern task execution requirements.
62                 //
63                 // This will have to be available for "out of process" nodes (see NodeAffinity).
64                 // NodeAffinity is set per project file at BuildManager.HostServices.
65                 // When NodeAffinity is set to OutOfProc, it should probably launch different build host
66                 // that runs separate build tasks. (.NET has MSBuildTaskHost.exe which I guess is about that.)
67                 //
68                 // Also note that the complete implementation has to support LoadInSeparateAppDomainAttribute
69                 // (which is most likely derived from AppDomainIsolatedBuildTask) that marks a task to run
70                 // in separate AppDomain.
71                 //
72                 public void BuildProject (Func<bool> checkCancel, BuildResult result, ProjectInstance project, IEnumerable<string> targetNames, IDictionary<string,string> globalProperties, IDictionary<string,string> targetOutputs, string toolsVersion)
73                 {
74                         var request = submission.BuildRequest;
75                         var parameters = submission.BuildManager.OngoingBuildParameters;
76                         this.project = project;
77                         var buildTaskFactory = new BuildTaskFactory (BuildTaskDatabase.GetDefaultTaskDatabase (parameters.GetToolset (toolsVersion)), submission.BuildRequest.ProjectInstance.TaskDatabase);
78                         
79                         // null targets -> success. empty targets -> success(!)
80                         if (request.TargetNames == null)
81                                 result.OverallResult = BuildResultCode.Success;
82                         else {
83                                 foreach (var targetName in request.TargetNames.Where (t => t != null)) {
84                                         if (checkCancel ())
85                                                 break;
86
87                                         ProjectTargetInstance target;
88                                         var targetResult = new TargetResult ();
89                                         
90                                         // FIXME: check skip condition
91                                         if (false)
92                                                 targetResult.Skip ();
93                                         // null key is allowed and regarded as blind success(!) (as long as it could retrieve target)
94                                         else if (!request.ProjectInstance.Targets.TryGetValue (targetName, out target))
95                                                 targetResult.Failure (null);
96                                         else {
97                                                 foreach (var c in target.Children.OfType<ProjectPropertyGroupTaskInstance> ()) {
98                                                         if (!project.EvaluateCondition (c.Condition))
99                                                                 continue;
100                                                         throw new NotImplementedException ();
101                                                 }
102                                                 foreach (var c in target.Children.OfType<ProjectItemGroupTaskInstance> ()) {
103                                                         if (!project.EvaluateCondition (c.Condition))
104                                                                 continue;
105                                                         throw new NotImplementedException ();
106                                                 }
107                                                 foreach (var c in target.Children.OfType<ProjectOnErrorInstance> ()) {
108                                                         if (!project.EvaluateCondition (c.Condition))
109                                                                 continue;
110                                                         throw new NotImplementedException ();
111                                                 }
112                                                 foreach (var c in target.Children.OfType<ProjectTaskInstance> ()) {
113                                                         var host = request.HostServices == null ? null : request.HostServices.GetHostObject (request.ProjectFullPath, targetName, c.Name);
114                                                         if (!project.EvaluateCondition (c.Condition))
115                                                                 continue;
116                                                         current_task = c;
117                                                         
118                                                         var factoryIdentityParameters = new Dictionary<string,string> ();
119                                                         factoryIdentityParameters ["MSBuildRuntime"] = c.MSBuildRuntime;
120                                                         factoryIdentityParameters ["MSBuildArchitecture"] = c.MSBuildArchitecture;
121                                                         var task = buildTaskFactory.GetTask (c.Name, factoryIdentityParameters, this);
122                                                         task.HostObject = host;
123                                                         task.BuildEngine = this;                                                        
124                                                         if (!task.Execute ()) {
125                                                                 targetResult.Failure (null);
126                                                                 if (!project.EvaluateCondition (c.ContinueOnError))
127                                                                         break;
128                                                         }
129                                                 }
130                                         }
131                                         result.AddResultsForTarget (targetName, targetResult);
132                                 }
133                                 
134                                 // FIXME: check .NET behavior, whether cancellation always results in failure.
135                                 result.OverallResult = checkCancel () ? BuildResultCode.Failure : result.ResultsByTarget.Select (p => p.Value).Any (r => r.ResultCode == TargetResultCode.Failure) ? BuildResultCode.Failure : BuildResultCode.Success;
136                         }
137                 }
138                 
139                 #region IBuildEngine4 implementation
140                 
141                 // task objects are not in use anyways though...
142                 
143                 class TaskObjectRegistration
144                 {
145                         public TaskObjectRegistration (object key, object obj, RegisteredTaskObjectLifetime lifetime, bool allowEarlyCollection)
146                         {
147                                 Key = key;
148                                 Object = obj;
149                                 Lifetime = lifetime;
150                                 AllowEarlyCollection = allowEarlyCollection;
151                         }
152                         public object Key { get; private set; }
153                         public object Object { get; private set; }
154                         public RegisteredTaskObjectLifetime Lifetime { get; private set; }
155                         public bool AllowEarlyCollection { get; private set; }
156                 }
157                 
158                 List<TaskObjectRegistration> task_objects = new List<TaskObjectRegistration> ();
159
160                 public object GetRegisteredTaskObject (object key, RegisteredTaskObjectLifetime lifetime)
161                 {
162                         var reg = task_objects.FirstOrDefault (t => t.Key == key && t.Lifetime == lifetime);
163                         return reg != null ? reg.Object : null;
164                 }
165
166                 public void RegisterTaskObject (object key, object obj, RegisteredTaskObjectLifetime lifetime, bool allowEarlyCollection)
167                 {
168                         task_objects.Add (new TaskObjectRegistration (key, obj, lifetime, allowEarlyCollection));
169                 }
170
171                 public object UnregisterTaskObject (object key, RegisteredTaskObjectLifetime lifetime)
172                 {
173                         var reg = task_objects.FirstOrDefault (t => t.Key == key && t.Lifetime == lifetime);
174                         if (reg != null)
175                                 task_objects.Remove (reg);
176                         return reg.Object;
177                 }
178
179                 #endregion
180
181                 #region IBuildEngine3 implementation
182
183                 public BuildEngineResult BuildProjectFilesInParallel (string[] projectFileNames, string[] targetNames, IDictionary[] globalProperties, IList<string>[] removeGlobalProperties, string[] toolsVersion, bool returnTargetOutputs)
184                 {
185                         throw new NotImplementedException ();
186                 }
187
188                 public void Reacquire ()
189                 {
190                         throw new NotImplementedException ();
191                 }
192
193                 public void Yield ()
194                 {
195                         throw new NotImplementedException ();
196                 }
197
198                 #endregion
199
200                 #region IBuildEngine2 implementation
201
202                 public bool BuildProjectFile (string projectFileName, string[] targetNames, IDictionary globalProperties, IDictionary targetOutputs, string toolsVersion)
203                 {
204                         var proj = GetProjectInstance (projectFileName, toolsVersion);
205                         var globalPropertiesThatMakeSense = new Dictionary<string,string> ();
206                         foreach (DictionaryEntry p in globalProperties)
207                                 globalPropertiesThatMakeSense [(string) p.Key] = (string) p.Value;
208                         var result = new BuildResult ();
209                         var outputs = new Dictionary<string, string> ();
210                         BuildProject (() => false, result, proj, targetNames, globalPropertiesThatMakeSense, outputs, toolsVersion);
211                         foreach (var p in outputs)
212                                 targetOutputs [p.Key] = p.Value;
213                         return result.OverallResult == BuildResultCode.Success;
214                 }
215
216                 public bool BuildProjectFilesInParallel (string[] projectFileNames, string[] targetNames, IDictionary[] globalProperties, IDictionary[] targetOutputsPerProject, string[] toolsVersion, bool useResultsCache, bool unloadProjectsOnCompletion)
217                 {
218                         throw new NotImplementedException ();
219                 }
220
221                 public bool IsRunningMultipleNodes {
222                         get {
223                                 throw new NotImplementedException ();
224                         }
225                 }
226                 
227                 ProjectInstance GetProjectInstance (string projectFileName, string toolsVersion)
228                 {
229                         string fullPath = Path.GetFullPath (projectFileName);
230                         if (submission.BuildRequest.ProjectFullPath == fullPath)
231                                 return submission.BuildRequest.ProjectInstance;
232                         // FIXME: could also be filtered by global properties
233                         // http://msdn.microsoft.com/en-us/library/microsoft.build.evaluation.projectcollection.getloadedprojects.aspx
234                         var project = Projects.GetLoadedProjects (projectFileName).FirstOrDefault (p => p.ToolsVersion == toolsVersion);
235                         if (project == null)
236                                 throw new InvalidOperationException (string.Format ("Project '{0}' is not loaded", projectFileName));
237                         return submission.BuildManager.GetProjectInstanceForBuild (project);
238                 }
239
240                 #endregion
241
242                 #region IBuildEngine implementation
243
244                 public bool BuildProjectFile (string projectFileName, string[] targetNames, IDictionary globalProperties, IDictionary targetOutputs)
245                 {
246                         return BuildProjectFile (projectFileName, targetNames, globalProperties, targetOutputs, Projects.DefaultToolsVersion);
247                 }
248
249                 public void LogCustomEvent (CustomBuildEventArgs e)
250                 {
251                         event_source.FireCustomEventRaised (this, e);
252                 }
253
254                 public void LogErrorEvent (BuildErrorEventArgs e)
255                 {
256                         event_source.FireErrorRaised (this, e);
257                 }
258
259                 public void LogMessageEvent (BuildMessageEventArgs e)
260                 {
261                         event_source.FireMessageRaised (this, e);
262                 }
263
264                 public void LogWarningEvent (BuildWarningEventArgs e)
265                 {
266                         event_source.FireWarningRaised (this, e);
267                 }
268
269                 public int ColumnNumberOfTaskNode {
270                         get { return current_task.Location.Column; }
271                 }
272
273                 public bool ContinueOnError {
274                         get {
275                                 switch (current_task.ContinueOnError) {
276                                 case "true":
277                                 case "WarnAndContinue":
278                                 case "ErrorAndContinue":
279                                         return true;
280                                 }
281                                 return false;
282                         }
283                 }
284
285                 public int LineNumberOfTaskNode {
286                         get { return current_task.Location.Line; }
287                 }
288
289                 public string ProjectFileOfTaskNode {
290                         get { return current_task.FullPath; }
291                 }
292
293                 #endregion
294         }
295 }
296