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 using System;
5 using System.Collections;
6 using System.Text;
7 using System.IO;
8 
9 using Microsoft.Build.Framework;
10 using Microsoft.Build.BuildEngine.Shared;
11 
12 namespace Microsoft.Build.BuildEngine
13 {
14     /// <summary>
15     /// This class is a container for build results travelling from the engine to the node
16     /// </summary>
17     internal class BuildResult
18     {
19         #region Constructions
20 
BuildResult()21         internal BuildResult()
22         {
23             // used for serialization
24         }
25 
26         /// <summary>
27         /// Constructor
28         /// </summary>
BuildResult( IDictionary outputsByTarget, Hashtable resultByTarget, bool evaluationResult, int handleId, int requestId, int projectId, bool useResultCache, string defaultTargets, string initialTargets, int totalTime, int engineTime, int taskTime )29         internal BuildResult
30         (
31             IDictionary outputsByTarget, Hashtable resultByTarget, bool evaluationResult,
32             int handleId, int requestId, int projectId, bool useResultCache,
33             string defaultTargets, string initialTargets,
34             int totalTime, int engineTime, int taskTime
35         )
36         {
37             this.outputsByTarget = outputsByTarget;
38             this.resultByTarget = resultByTarget;
39             this.handleId = handleId;
40             this.requestId = requestId;
41             this.projectId = projectId;
42             this.flags = (byte)((evaluationResult ? 1 : 0 ) | (useResultCache ? 2 : 0));
43             this.defaultTargets = defaultTargets;
44             this.initialTargets = initialTargets;
45             this.totalTime = totalTime;
46             this.engineTime = engineTime;
47             this.taskTime = taskTime;
48         }
49 
50         /// <summary>
51         /// Copy constructor
52         /// </summary>
BuildResult(BuildResult buildResultToCopy, bool deepCopy)53         internal BuildResult
54             (BuildResult buildResultToCopy, bool deepCopy)
55         {
56             ErrorUtilities.VerifyThrowArgumentNull(buildResultToCopy, "Cannot have a null build result passed in");
57             this.flags = buildResultToCopy.flags;
58             this.handleId = buildResultToCopy.handleId;
59             this.requestId = buildResultToCopy.requestId;
60             this.projectId = buildResultToCopy.projectId;
61             this.outputsByTarget = new Hashtable();
62             this.defaultTargets = buildResultToCopy.defaultTargets;
63             this.initialTargets = buildResultToCopy.initialTargets;
64             this.resultByTarget = new Hashtable(buildResultToCopy.resultByTarget, StringComparer.OrdinalIgnoreCase);
65 
66             if (buildResultToCopy.outputsByTarget == null)
67             {
68                 return;
69             }
70 
71             if (deepCopy)
72             {
73                 // Copy all the old data
74                 foreach (DictionaryEntry entry in buildResultToCopy.outputsByTarget)
75                 {
76                     // Make deep copies of all the target outputs before
77                     // passing them back
78                     BuildItem[] originalArray = (BuildItem[])entry.Value;
79                     BuildItem[] itemArray = new BuildItem[originalArray.Length];
80                     for (int i = 0; i < originalArray.Length; i++)
81                     {
82                         if (!originalArray[i].IsUninitializedItem)
83                         {
84                             itemArray[i] = originalArray[i].Clone();
85                             itemArray[i].CloneVirtualMetadata();
86                         }
87                         else
88                         {
89                             itemArray[i] = new BuildItem(null, originalArray[i].FinalItemSpecEscaped);
90                         }
91                     }
92 
93                     this.outputsByTarget.Add(entry.Key, itemArray);
94                 }
95             }
96             else
97             {
98                 // Create a new hashtable but point at the same data
99                 foreach (DictionaryEntry entry in buildResultToCopy.outputsByTarget)
100                 {
101                     this.outputsByTarget.Add(entry.Key, entry.Value);
102                 }
103             }
104         }
105 
106         #endregion
107 
108         #region Properties
109         internal IDictionary OutputsByTarget
110         {
111             get
112             {
113                 return this.outputsByTarget;
114             }
115         }
116 
117         internal Hashtable ResultByTarget
118         {
119             get
120             {
121                 return this.resultByTarget;
122             }
123         }
124 
125         internal bool EvaluationResult
126         {
127             get
128             {
129                 return ((this.flags & 1) == 0 ? false : true) ;
130             }
131         }
132 
133         internal int HandleId
134         {
135             get
136             {
137                 return this.handleId;
138             }
139             set
140             {
141                 this.handleId = value;
142             }
143         }
144 
145         internal int RequestId
146         {
147             get
148             {
149                 return this.requestId;
150             }
151             set
152             {
153                 this.requestId = value;
154             }
155         }
156 
157         internal int ProjectId
158         {
159             get
160             {
161                 return this.projectId;
162             }
163         }
164 
165         internal bool UseResultCache
166         {
167             get
168             {
169                 return ((this.flags & 2) == 0 ? false : true);
170             }
171         }
172 
173         internal string DefaultTargets
174         {
175             get
176             {
177                 return this.defaultTargets;
178             }
179         }
180 
181         internal string InitialTargets
182         {
183             get
184             {
185                 return this.initialTargets;
186             }
187         }
188 
189         /// <summary>
190         /// Total time spent on the build request measured from the time it is received to the time build
191         /// result is created. This number will be 0 if the result was in the cache.
192         /// </summary>
193         internal int TotalTime
194         {
195             get
196             {
197                 return this.totalTime;
198             }
199         }
200 
201         /// <summary>
202         /// Total time spent in the engine working on the build request. This number will be 0 if the result
203         /// was in the cache.
204         /// </summary>
205         internal int EngineTime
206         {
207             get
208             {
209                 return this.engineTime;
210             }
211         }
212 
213         /// <summary>
214         /// Total time spent in the running tasks for the build request. This number will be 0 if the result
215         /// was in the cache.
216         /// </summary>
217         internal int TaskTime
218         {
219             get
220             {
221                 return this.taskTime;
222             }
223         }
224         #endregion
225 
226         #region Methods
227 
228         /// <summary>
229         /// BuildItems are passed around internally, including across the wire. Before passing these
230         /// to tasks, they need to be converted into TaskItems using this method.
231         /// </summary>
ConvertToTaskItems()232         internal void ConvertToTaskItems()
233         {
234             // If outputsByTarget was null then we dont have to re-create anything as nothing was passed over
235             if (null != outputsByTarget)
236             {
237                 string[] keys = new string[outputsByTarget.Count];
238                 outputsByTarget.Keys.CopyTo(keys, 0);
239                 for (int key_index = 0; key_index < keys.Length; key_index++)
240                 {
241                     object key = keys[key_index];
242                     BuildItem[] originalArray = (BuildItem[])outputsByTarget[key];
243                     outputsByTarget[key] = BuildItem.ConvertBuildItemArrayToTaskItems(originalArray);
244                 }
245             }
246         }
247         #endregion
248 
249         #region Data
250         private IDictionary outputsByTarget;
251         private Hashtable resultByTarget;
252         private byte flags;
253         private int handleId;
254         private int requestId;
255         private int projectId;
256         private string defaultTargets;
257         private string initialTargets;
258 
259         // Timing data related to the execution of the request
260         private int totalTime;
261         private int engineTime;
262         private int taskTime;
263 
264         #endregion
265 
266         #region CustomSerializationToStream
WriteToStream(BinaryWriter writer)267         internal void WriteToStream(BinaryWriter writer)
268         {
269             ErrorUtilities.VerifyThrow(resultByTarget != null, "resultsByTarget cannot be null");
270             #region OutputsByTarget
271             if (outputsByTarget == null)
272             {
273                 writer.Write((byte)0);
274             }
275             else
276             {
277                 writer.Write((byte)1);
278                 writer.Write((Int32)outputsByTarget.Count);
279                 foreach (string key in outputsByTarget.Keys)
280                 {
281                     writer.Write(key);
282                     if (outputsByTarget[key] == null)
283                     {
284                         writer.Write((byte)0);
285                     }
286                     else
287                     {
288                         writer.Write((byte)1);
289                         BuildItem[] items = ((BuildItem[])outputsByTarget[key]);
290                         writer.Write((Int32)items.Length);
291                         foreach (BuildItem item in items)
292                         {
293                             if (item == null)
294                             {
295                                 writer.Write((byte)0);
296                             }
297                             writer.Write((byte)1);
298                             item.WriteToStream(writer);
299                         }
300                     }
301                 }
302             }
303             #endregion
304             #region ResultByTarget
305             //Write Number of HashItems
306             writer.Write((Int32)resultByTarget.Count);
307             foreach (string key in resultByTarget.Keys)
308             {
309                 writer.Write(key);
310                 writer.Write((Int32)resultByTarget[key]);
311             }
312             #endregion
313             writer.Write((byte)flags);
314             writer.Write((Int32)handleId);
315             writer.Write((Int32)requestId);
316             writer.Write((Int32)projectId);
317             #region DefaultTargets
318             if (defaultTargets == null)
319             {
320                 writer.Write((byte)0);
321             }
322             else
323             {
324                 writer.Write((byte)1);
325                 writer.Write(defaultTargets);
326             }
327             #endregion
328             #region InitialTargets
329             if (initialTargets == null)
330             {
331                 writer.Write((byte)0);
332             }
333             else
334             {
335                 writer.Write((byte)1);
336                 writer.Write(initialTargets);
337             }
338             #endregion
339             #region Timing data
340             writer.Write((Int32)totalTime);
341             writer.Write((Int32)engineTime);
342             writer.Write((Int32)taskTime);
343             #endregion
344         }
345 
346 
CreateFromStream(BinaryReader reader)347         internal static BuildResult CreateFromStream(BinaryReader reader)
348         {
349             BuildResult buildResult = new BuildResult();
350             #region OutputsByTarget
351             if (reader.ReadByte() == 0)
352             {
353                 buildResult.outputsByTarget = null;
354             }
355             else
356             {
357                 int numberOfElements = reader.ReadInt32();
358                 buildResult.outputsByTarget = new Hashtable(numberOfElements, StringComparer.OrdinalIgnoreCase);
359                 for (int i = 0; i < numberOfElements; i++)
360                 {
361                     string key = reader.ReadString();
362                     BuildItem[] value = null;
363                     if (reader.ReadByte() != 0)
364                     {
365                         int sizeOfArray = reader.ReadInt32();
366                         value = new BuildItem[sizeOfArray];
367                         for (int j = 0; j < sizeOfArray; j++)
368                         {
369                             BuildItem itemToAdd = null;
370                             if (reader.ReadByte() != 0)
371                             {
372                                 itemToAdd = new BuildItem(null, string.Empty);
373                                 itemToAdd.CreateFromStream(reader);
374                             }
375                             value[j] = itemToAdd;
376                         }
377                     }
378                     buildResult.outputsByTarget.Add(key, value);
379                 }
380             }
381             #endregion
382             #region ResultsByTarget
383             //Write Number of HashItems
384             int numberOfHashKeyValuePairs = reader.ReadInt32();
385             buildResult.resultByTarget = new Hashtable(numberOfHashKeyValuePairs, StringComparer.OrdinalIgnoreCase);
386             for (int i = 0; i < numberOfHashKeyValuePairs; i++)
387             {
388                 string key = reader.ReadString();
389                 int value = reader.ReadInt32();
390                 buildResult.resultByTarget.Add(key, (Target.BuildState)value);
391             }
392             #endregion
393             buildResult.flags = reader.ReadByte();
394             buildResult.handleId = reader.ReadInt32();
395             buildResult.requestId = reader.ReadInt32();
396             buildResult.projectId = reader.ReadInt32();
397             #region DefaultTargets
398             if (reader.ReadByte() == 0)
399             {
400                 buildResult.defaultTargets = null;
401             }
402             else
403             {
404                 buildResult.defaultTargets = reader.ReadString();
405             }
406             #endregion
407             #region InitialTargets
408             if (reader.ReadByte() == 0)
409             {
410                 buildResult.initialTargets = null;
411             }
412             else
413             {
414                 buildResult.initialTargets = reader.ReadString();
415             }
416             #endregion
417             #region Timing data
418             buildResult.totalTime = reader.ReadInt32();
419             buildResult.engineTime = reader.ReadInt32();
420             buildResult.taskTime = reader.ReadInt32();
421             #endregion
422             return buildResult;
423         }
424         #endregion
425     }
426 }
427