5 // Atsushi Enomoto (atsushi@xamarin.com)
7 // Copyright (C) 2013 Xamarin Inc. (http://www.xamarin.com)
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:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
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.
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;
38 namespace Microsoft.Build.Internal
40 class BuildEngine4 : IBuildEngine4
42 public BuildEngine4 (BuildSubmission submission)
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);
51 BuildSubmission submission;
52 ProjectInstance project;
53 ProjectTaskInstance current_task;
54 EventSource event_source;
56 public ProjectCollection Projects {
57 get { return submission.BuildManager.OngoingBuildParameters.ProjectCollection; }
61 // While we are not faced to implement those features, there are some modern task execution requirements.
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.)
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.
72 public void BuildProject (Func<bool> checkCancel, BuildResult result, ProjectInstance project, IEnumerable<string> targetNames, IDictionary<string,string> globalProperties, IDictionary<string,string> targetOutputs, string toolsVersion)
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);
79 // null targets -> success. empty targets -> success(!)
80 if (request.TargetNames == null)
81 result.OverallResult = BuildResultCode.Success;
83 foreach (var targetName in request.TargetNames.Where (t => t != null)) {
87 ProjectTargetInstance target;
88 var targetResult = new TargetResult ();
90 // FIXME: check skip condition
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);
97 foreach (var c in target.Children.OfType<ProjectPropertyGroupTaskInstance> ()) {
98 if (!project.EvaluateCondition (c.Condition))
100 throw new NotImplementedException ();
102 foreach (var c in target.Children.OfType<ProjectItemGroupTaskInstance> ()) {
103 if (!project.EvaluateCondition (c.Condition))
105 throw new NotImplementedException ();
107 foreach (var c in target.Children.OfType<ProjectOnErrorInstance> ()) {
108 if (!project.EvaluateCondition (c.Condition))
110 throw new NotImplementedException ();
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))
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))
131 result.AddResultsForTarget (targetName, targetResult);
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;
139 #region IBuildEngine4 implementation
141 // task objects are not in use anyways though...
143 class TaskObjectRegistration
145 public TaskObjectRegistration (object key, object obj, RegisteredTaskObjectLifetime lifetime, bool allowEarlyCollection)
150 AllowEarlyCollection = allowEarlyCollection;
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; }
158 List<TaskObjectRegistration> task_objects = new List<TaskObjectRegistration> ();
160 public object GetRegisteredTaskObject (object key, RegisteredTaskObjectLifetime lifetime)
162 var reg = task_objects.FirstOrDefault (t => t.Key == key && t.Lifetime == lifetime);
163 return reg != null ? reg.Object : null;
166 public void RegisterTaskObject (object key, object obj, RegisteredTaskObjectLifetime lifetime, bool allowEarlyCollection)
168 task_objects.Add (new TaskObjectRegistration (key, obj, lifetime, allowEarlyCollection));
171 public object UnregisterTaskObject (object key, RegisteredTaskObjectLifetime lifetime)
173 var reg = task_objects.FirstOrDefault (t => t.Key == key && t.Lifetime == lifetime);
175 task_objects.Remove (reg);
181 #region IBuildEngine3 implementation
183 public BuildEngineResult BuildProjectFilesInParallel (string[] projectFileNames, string[] targetNames, IDictionary[] globalProperties, IList<string>[] removeGlobalProperties, string[] toolsVersion, bool returnTargetOutputs)
185 throw new NotImplementedException ();
188 public void Reacquire ()
190 throw new NotImplementedException ();
195 throw new NotImplementedException ();
200 #region IBuildEngine2 implementation
202 public bool BuildProjectFile (string projectFileName, string[] targetNames, IDictionary globalProperties, IDictionary targetOutputs, string toolsVersion)
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;
216 public bool BuildProjectFilesInParallel (string[] projectFileNames, string[] targetNames, IDictionary[] globalProperties, IDictionary[] targetOutputsPerProject, string[] toolsVersion, bool useResultsCache, bool unloadProjectsOnCompletion)
218 throw new NotImplementedException ();
221 public bool IsRunningMultipleNodes {
223 throw new NotImplementedException ();
227 ProjectInstance GetProjectInstance (string projectFileName, string toolsVersion)
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);
236 throw new InvalidOperationException (string.Format ("Project '{0}' is not loaded", projectFileName));
237 return submission.BuildManager.GetProjectInstanceForBuild (project);
242 #region IBuildEngine implementation
244 public bool BuildProjectFile (string projectFileName, string[] targetNames, IDictionary globalProperties, IDictionary targetOutputs)
246 return BuildProjectFile (projectFileName, targetNames, globalProperties, targetOutputs, Projects.DefaultToolsVersion);
249 public void LogCustomEvent (CustomBuildEventArgs e)
251 event_source.FireCustomEventRaised (this, e);
254 public void LogErrorEvent (BuildErrorEventArgs e)
256 event_source.FireErrorRaised (this, e);
259 public void LogMessageEvent (BuildMessageEventArgs e)
261 event_source.FireMessageRaised (this, e);
264 public void LogWarningEvent (BuildWarningEventArgs e)
266 event_source.FireWarningRaised (this, e);
269 public int ColumnNumberOfTaskNode {
270 get { return current_task.Location.Column; }
273 public bool ContinueOnError {
275 switch (current_task.ContinueOnError) {
277 case "WarnAndContinue":
278 case "ErrorAndContinue":
285 public int LineNumberOfTaskNode {
286 get { return current_task.Location.Line; }
289 public string ProjectFileOfTaskNode {
290 get { return current_task.FullPath; }