1 // Copyright (c) Microsoft. All rights reserved.
2 // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3 //-----------------------------------------------------------------------
4 // </copyright>
5 // <summary>Wraps a task element.</summary>
6 //-----------------------------------------------------------------------
7 
8 using System.Collections.Generic;
9 using Microsoft.Build.Construction;
10 using Microsoft.Build.Collections;
11 using Microsoft.Build.Shared;
12 using System.Diagnostics;
13 using System;
14 using Microsoft.Build.BackEnd;
15 
16 namespace Microsoft.Build.Execution
17 {
18     /// <summary>
19     /// Wraps a task element
20     /// </summary>
21     /// <remarks>
22     /// This is an immutable class
23     /// </remarks>
24     [DebuggerDisplay("Name={_name} Condition={_condition} ContinueOnError={_continueOnError} MSBuildRuntime={MSBuildRuntime} MSBuildArchitecture={MSBuildArchitecture} #Parameters={_parameters.Count} #Outputs={_outputs.Count}")]
25     public sealed class ProjectTaskInstance : ProjectTargetInstanceChild, INodePacketTranslatable
26     {
27         /// <summary>
28         /// Name of the task, possibly qualified, as it appears in the project
29         /// </summary>
30         private string _name;
31 
32         /// <summary>
33         /// Condition on the task, if any
34         /// May be empty string
35         /// </summary>
36         private string _condition;
37 
38         /// <summary>
39         /// Continue on error on the task, if any
40         /// May be empty string
41         /// </summary>
42         private string _continueOnError;
43 
44         /// <summary>
45         /// Runtime on the task, if any
46         /// May be empty string
47         /// </summary>
48         private string _msbuildRuntime;
49 
50         /// <summary>
51         /// Architecture on the task, if any
52         /// May be empty string
53         /// </summary>
54         private string _msbuildArchitecture;
55 
56         /// <summary>
57         /// Unordered set of task parameter names and unevaluated values.
58         /// This is a dead, read-only collection.
59         /// </summary>
60         private CopyOnWriteDictionary<string, Tuple<string, ElementLocation>> _parameters;
61 
62         /// <summary>
63         /// Output properties and items below this task. This is an ordered collection
64         /// as one may depend on another.
65         /// This is a dead, read-only collection.
66         /// </summary>
67         private List<ProjectTaskInstanceChild> _outputs;
68 
69         /// <summary>
70         /// Location of this element
71         /// </summary>
72         private ElementLocation _location;
73 
74         /// <summary>
75         /// Location of the condition, if any
76         /// </summary>
77         private ElementLocation _conditionLocation;
78 
79         /// <summary>
80         /// Location of the continueOnError attribute, if any
81         /// </summary>
82         private ElementLocation _continueOnErrorLocation;
83 
84         /// <summary>
85         /// Location of the MSBuildRuntime attribute, if any
86         /// </summary>
87         private ElementLocation _msbuildRuntimeLocation;
88 
89         /// <summary>
90         /// Location of the MSBuildArchitecture attribute, if any
91         /// </summary>
92         private ElementLocation _msbuildArchitectureLocation;
93 
94         /// <summary>
95         /// Constructor called by Evaluator.
96         /// All parameters are in the unevaluated state.
97         /// Locations other than the main location may be null.
98         /// </summary>
ProjectTaskInstance( ProjectTaskElement element, IList<ProjectTaskInstanceChild> outputs )99         internal ProjectTaskInstance
100             (
101             ProjectTaskElement element,
102             IList<ProjectTaskInstanceChild> outputs
103             )
104         {
105             ErrorUtilities.VerifyThrowInternalNull(element, "element");
106             ErrorUtilities.VerifyThrowInternalNull(outputs, "outputs");
107 
108             // These are all immutable
109             _name = element.Name;
110             _condition = element.Condition;
111             _continueOnError = element.ContinueOnError;
112             _msbuildArchitecture = element.MSBuildArchitecture;
113             _msbuildRuntime = element.MSBuildRuntime;
114             _location = element.Location;
115             _conditionLocation = element.ConditionLocation;
116             _continueOnErrorLocation = element.ContinueOnErrorLocation;
117             _msbuildRuntimeLocation = element.MSBuildRuntimeLocation;
118             _msbuildArchitectureLocation = element.MSBuildArchitectureLocation;
119             _parameters = element.ParametersForEvaluation;
120             _outputs = new List<ProjectTaskInstanceChild>(outputs);
121         }
122 
123         /// <summary>
124         ///     Creates a new task instance directly.  Used for generating instances on-the-fly.
125         /// </summary>
126         /// <param name="name">The task name.</param>
127         /// <param name="location">The location for this task.</param>
128         /// <param name="condition">The unevaluated condition.</param>
129         /// <param name="continueOnError">The unevaluated continue on error.</param>
130         /// <param name="msbuildRuntime">The MSBuild runtime.</param>
131         /// <param name="msbuildArchitecture">The MSBuild architecture.</param>
ProjectTaskInstance( string name, ElementLocation location, string condition, string continueOnError, string msbuildRuntime, string msbuildArchitecture )132         internal ProjectTaskInstance(
133             string name,
134             ElementLocation location,
135             string condition,
136             string continueOnError,
137             string msbuildRuntime,
138             string msbuildArchitecture
139         ) : this(
140             name,
141             condition,
142             continueOnError,
143             msbuildRuntime,
144             msbuildArchitecture,
145             new CopyOnWriteDictionary<string, Tuple<string, ElementLocation>>(8, StringComparer.OrdinalIgnoreCase),
146             new List<ProjectTaskInstanceChild>(),
147             location,
148             condition == string.Empty ? null : ElementLocation.EmptyLocation,
149             continueOnError == string.Empty ? null : ElementLocation.EmptyLocation,
150             msbuildRuntime == string.Empty ? null : ElementLocation.EmptyLocation,
151             msbuildArchitecture == string.Empty ? null : ElementLocation.EmptyLocation)
152         {
153         }
154 
ProjectTaskInstance( string name, string condition, string continueOnError, string msbuildRuntime, string msbuildArchitecture, CopyOnWriteDictionary<string, Tuple<string, ElementLocation>> parameters, List<ProjectTaskInstanceChild> outputs, ElementLocation location, ElementLocation conditionLocation, ElementLocation continueOnErrorElementLocation, ElementLocation msbuildRuntimeLocation, ElementLocation msbuildArchitectureLocation)155         internal ProjectTaskInstance
156             (
157             string name,
158             string condition,
159             string continueOnError,
160             string msbuildRuntime,
161             string msbuildArchitecture,
162             CopyOnWriteDictionary<string, Tuple<string, ElementLocation>> parameters,
163             List<ProjectTaskInstanceChild> outputs,
164             ElementLocation location,
165             ElementLocation conditionLocation,
166             ElementLocation continueOnErrorElementLocation,
167             ElementLocation msbuildRuntimeLocation,
168             ElementLocation msbuildArchitectureLocation)
169         {
170             ErrorUtilities.VerifyThrowArgumentLength(name, "name");
171             ErrorUtilities.VerifyThrowArgumentNull(condition, "condition");
172             ErrorUtilities.VerifyThrowArgumentNull(continueOnError, "continueOnError");
173 
174             _name = name;
175             _condition = condition;
176             _continueOnError = continueOnError;
177             _msbuildRuntime = msbuildRuntime;
178             _msbuildArchitecture = msbuildArchitecture;
179             _location = location;
180             _conditionLocation = conditionLocation;
181             _continueOnErrorLocation = continueOnErrorElementLocation;
182             _msbuildArchitectureLocation = msbuildArchitectureLocation;
183             _msbuildRuntimeLocation = msbuildRuntimeLocation;
184             _parameters = parameters;
185             _outputs = outputs;
186         }
187 
ProjectTaskInstance()188         private ProjectTaskInstance()
189         {
190         }
191 
192         /// <summary>
193         /// Name of the task, possibly qualified, as it appears in the project
194         /// </summary>
195         public string Name
196         {
197             get { return _name; }
198         }
199 
200         /// <summary>
201         /// Unevaluated condition on the task
202         /// May be empty string.
203         /// </summary>
204         public override string Condition
205         {
206             get { return _condition; }
207         }
208 
209         /// <summary>
210         /// Unevaluated ContinueOnError on the task.
211         /// May be empty string.
212         /// </summary>
213         public string ContinueOnError
214         {
215             get { return _continueOnError; }
216         }
217 
218         /// <summary>
219         /// Unevaluated MSBuildRuntime on the task.
220         /// May be empty string.
221         /// </summary>
222         public string MSBuildRuntime
223         {
224             get { return _msbuildRuntime; }
225         }
226 
227         /// <summary>
228         /// Unevaluated MSBuildArchitecture on the task.
229         /// May be empty string.
230         /// </summary>
231         public string MSBuildArchitecture
232         {
233             get { return _msbuildArchitecture; }
234         }
235 
236         /// <summary>
237         /// Read-only dead unordered set of task parameter names and unevaluated values.
238         /// Condition and ContinueOnError, which have their own properties, are not included in this collection.
239         /// </summary>
240         public IDictionary<string, string> Parameters
241         {
242             get
243             {
244                 Dictionary<string, string> filteredParameters = new Dictionary<string, string>(_parameters.Count, StringComparer.OrdinalIgnoreCase);
245                 foreach (KeyValuePair<string, Tuple<string, ElementLocation>> parameter in _parameters)
246                 {
247                     filteredParameters[parameter.Key] = parameter.Value.Item1;
248                 }
249 
250                 return filteredParameters;
251             }
252         }
253 
254         internal IDictionary<string, Tuple<string, ElementLocation>> TestGetParameters => _parameters;
255 
256         /// <summary>
257         /// Ordered set of output property and item objects.
258         /// This is a read-only dead collection.
259         /// </summary>
260         public IList<ProjectTaskInstanceChild> Outputs
261         {
262             get { return _outputs; }
263         }
264 
265         /// <summary>
266         /// Location of the ContinueOnError attribute, if any
267         /// </summary>
268         public ElementLocation ContinueOnErrorLocation
269         {
270             get { return _continueOnErrorLocation; }
271         }
272 
273         /// <summary>
274         /// Location of the MSBuildRuntime attribute, if any
275         /// </summary>
276         public ElementLocation MSBuildRuntimeLocation
277         {
278             get { return _msbuildRuntimeLocation; }
279         }
280 
281         /// <summary>
282         /// Location of the MSBuildArchitecture attribute, if any
283         /// </summary>
284         public ElementLocation MSBuildArchitectureLocation
285         {
286             get { return _msbuildArchitectureLocation; }
287         }
288 
289         /// <summary>
290         /// Location of the original element
291         /// </summary>
292         public override ElementLocation Location
293         {
294             get { return _location; }
295         }
296 
297         /// <summary>
298         /// Location of the condition, if any
299         /// </summary>
300         public override ElementLocation ConditionLocation
301         {
302             get { return _conditionLocation; }
303         }
304 
305         /// <summary>
306         /// Retrieves the parameters dictionary as used during the build.
307         /// </summary>
308         internal IDictionary<string, Tuple<string, ElementLocation>> ParametersForBuild
309         {
310             get { return _parameters; }
311         }
312 
313         /// <summary>
314         /// Returns the value of a named parameter, or null if there is no such parameter.
315         /// </summary>
316         /// <param name="parameterName">The name of the parameter to retrieve.</param>
317         /// <returns>The parameter value, or null if it does not exist.</returns>
GetParameter(string parameterName)318         internal string GetParameter(string parameterName)
319         {
320             Tuple<string, ElementLocation> parameterValue = null;
321             if (_parameters.TryGetValue(parameterName, out parameterValue))
322             {
323                 return parameterValue.Item1;
324             }
325 
326             return null;
327         }
328 
329         /// <summary>
330         /// Sets the unevaluated value for the specified parameter.
331         /// </summary>
332         /// <param name="parameterName">The name of the parameter to set.</param>
333         /// <param name="unevaluatedValue">The unevaluated value for the parameter.</param>
SetParameter(string parameterName, string unevaluatedValue)334         internal void SetParameter(string parameterName, string unevaluatedValue)
335         {
336             _parameters[parameterName] = new Tuple<string, ElementLocation>(unevaluatedValue, ElementLocation.EmptyLocation);
337         }
338 
339         /// <summary>
340         /// Adds an output item to the task.
341         /// </summary>
342         /// <param name="taskOutputParameterName">The name of the parameter on the task which produces the output.</param>
343         /// <param name="itemName">The item which will receive the output.</param>
344         /// <param name="condition">The condition.</param>
AddOutputItem(string taskOutputParameterName, string itemName, string condition)345         internal void AddOutputItem(string taskOutputParameterName, string itemName, string condition)
346         {
347             ErrorUtilities.VerifyThrowArgumentLength(taskOutputParameterName, "taskOutputParameterName");
348             ErrorUtilities.VerifyThrowArgumentLength(itemName, "itemName");
349             _outputs.Add(new ProjectTaskOutputItemInstance(itemName, taskOutputParameterName, condition ?? String.Empty, ElementLocation.EmptyLocation, ElementLocation.EmptyLocation, ElementLocation.EmptyLocation, condition == null ? null : ElementLocation.EmptyLocation));
350         }
351 
352         /// <summary>
353         /// Adds an output property to the task.
354         /// </summary>
355         /// <param name="taskOutputParameterName">The name of the parameter on the task which produces the output.</param>
356         /// <param name="propertyName">The property which will receive the output.</param>
357         /// <param name="condition">The condition.</param>
AddOutputProperty(string taskOutputParameterName, string propertyName, string condition)358         internal void AddOutputProperty(string taskOutputParameterName, string propertyName, string condition)
359         {
360             ErrorUtilities.VerifyThrowArgumentLength(taskOutputParameterName, "taskOutputParameterName");
361             ErrorUtilities.VerifyThrowArgumentLength(propertyName, "propertyName");
362             _outputs.Add(new ProjectTaskOutputPropertyInstance(propertyName, taskOutputParameterName, condition ?? String.Empty, ElementLocation.EmptyLocation, ElementLocation.EmptyLocation, ElementLocation.EmptyLocation, condition == null ? null : ElementLocation.EmptyLocation));
363         }
364 
INodePacketTranslatable.Translate(INodePacketTranslator translator)365         void INodePacketTranslatable.Translate(INodePacketTranslator translator)
366         {
367             if (translator.Mode == TranslationDirection.WriteToStream)
368             {
369                 var typeName = this.GetType().FullName;
370                 translator.Translate(ref typeName);
371             }
372 
373             translator.Translate(ref _name);
374             translator.Translate(ref _condition);
375             translator.Translate(ref _continueOnError);
376             translator.Translate(ref _msbuildRuntime);
377             translator.Translate(ref _msbuildArchitecture);
378             translator.Translate(ref _outputs, ProjectTaskInstanceChild.FactoryForDeserialization);
379             translator.Translate(ref _location, ElementLocation.FactoryForDeserialization);
380             translator.Translate(ref _conditionLocation, ElementLocation.FactoryForDeserialization);
381             translator.Translate(ref _continueOnErrorLocation, ElementLocation.FactoryForDeserialization);
382             translator.Translate(ref _msbuildRuntimeLocation, ElementLocation.FactoryForDeserialization);
383             translator.Translate(ref _msbuildArchitectureLocation, ElementLocation.FactoryForDeserialization);
384 
385             IDictionary<string, Tuple<string, ElementLocation>> localParameters = _parameters;
386             translator.TranslateDictionary(
387                 ref localParameters,
388                 ParametersKeyTranslator,
389                 ParametersValueTranslator,
390                 count => new CopyOnWriteDictionary<string, Tuple<string, ElementLocation>>(count));
391 
392             if (translator.Mode == TranslationDirection.ReadFromStream && localParameters != null)
393             {
394                 _parameters = (CopyOnWriteDictionary<string, Tuple<string, ElementLocation>>) localParameters;
395             }
396         }
397 
ParametersKeyTranslator(ref string key, INodePacketTranslator translator)398         private static void ParametersKeyTranslator(ref string key, INodePacketTranslator translator)
399         {
400             translator.Translate(ref key);
401         }
402 
ParametersValueTranslator(ref Tuple<string, ElementLocation> value, INodePacketTranslator translator)403         private static void ParametersValueTranslator(ref Tuple<string, ElementLocation> value, INodePacketTranslator translator)
404         {
405             if (translator.Mode == TranslationDirection.WriteToStream)
406             {
407                 var item1 = value.Item1;
408                 var item2 = value.Item2;
409 
410                 translator.Translate(ref item1);
411                 translator.Translate(ref item2, ElementLocation.FactoryForDeserialization);
412             }
413             else
414             {
415                 var item1 = default(string);
416                 var item2 = default(ElementLocation);
417 
418                 translator.Translate(ref item1);
419                 translator.Translate(ref item2, ElementLocation.FactoryForDeserialization);
420 
421                 value = Tuple.Create(item1, item2);
422             }
423         }
424 
FactoryForDeserialization(INodePacketTranslator translator)425         internal new static ProjectTaskInstance FactoryForDeserialization(INodePacketTranslator translator)
426         {
427             return translator.FactoryForDeserializingTypeWithName<ProjectTaskInstance>();
428         }
429     }
430 }
431