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