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.Collections.Generic; 7 using System.Xml; 8 using System.Globalization; 9 10 namespace Microsoft.Build.Shared 11 { 12 /// <summary> 13 /// Contains the names of the known attributes in the XML project file. 14 /// </summary> 15 internal static class XMakeAttributes 16 { 17 internal const string condition = "Condition"; 18 internal const string executeTargets = "ExecuteTargets"; 19 internal const string name = "Name"; 20 internal const string msbuildVersion = "MSBuildVersion"; 21 internal const string xmlns = "xmlns"; 22 internal const string defaultTargets = "DefaultTargets"; 23 internal const string initialTargets = "InitialTargets"; 24 internal const string treatAsLocalProperty = "TreatAsLocalProperty"; 25 internal const string dependsOnTargets = "DependsOnTargets"; 26 internal const string beforeTargets = "BeforeTargets"; 27 internal const string afterTargets = "AfterTargets"; 28 internal const string include = "Include"; 29 internal const string exclude = "Exclude"; 30 internal const string remove = "Remove"; 31 internal const string update = "Update"; 32 internal const string keepMetadata = "KeepMetadata"; 33 internal const string removeMetadata = "RemoveMetadata"; 34 internal const string keepDuplicates = "KeepDuplicates"; 35 internal const string inputs = "Inputs"; 36 internal const string outputs = "Outputs"; 37 internal const string keepDuplicateOutputs = "KeepDuplicateOutputs"; 38 internal const string assemblyName = "AssemblyName"; 39 internal const string assemblyFile = "AssemblyFile"; 40 internal const string taskName = "TaskName"; 41 internal const string continueOnError = "ContinueOnError"; 42 internal const string project = "Project"; 43 internal const string taskParameter = "TaskParameter"; 44 internal const string itemName = "ItemName"; 45 internal const string propertyName = "PropertyName"; 46 internal const string sdk = "Sdk"; 47 internal const string sdkName = "Name"; 48 internal const string sdkVersion = "Version"; 49 internal const string sdkMinimumVersion = "MinimumVersion"; 50 internal const string toolsVersion = "ToolsVersion"; 51 internal const string runtime = "Runtime"; 52 internal const string msbuildRuntime = "MSBuildRuntime"; 53 internal const string architecture = "Architecture"; 54 internal const string msbuildArchitecture = "MSBuildArchitecture"; 55 internal const string taskFactory = "TaskFactory"; 56 internal const string parameterType = "ParameterType"; 57 internal const string required = "Required"; 58 internal const string output = "Output"; 59 internal const string defaultValue = "DefaultValue"; 60 internal const string evaluate = "Evaluate"; 61 internal const string label = "Label"; 62 internal const string returns = "Returns"; 63 64 // Obsolete 65 internal const string requiredRuntime = "RequiredRuntime"; 66 internal const string requiredPlatform = "RequiredPlatform"; 67 68 internal struct ContinueOnErrorValues 69 { 70 internal const string errorAndContinue = "ErrorAndContinue"; 71 internal const string errorAndStop = "ErrorAndStop"; 72 internal const string warnAndContinue = "WarnAndContinue"; 73 } 74 75 internal struct MSBuildRuntimeValues 76 { 77 internal const string clr2 = "CLR2"; 78 internal const string clr4 = "CLR4"; 79 internal const string currentRuntime = "CurrentRuntime"; 80 internal const string any = "*"; 81 } 82 83 internal struct MSBuildArchitectureValues 84 { 85 internal const string x86 = "x86"; 86 internal const string x64 = "x64"; 87 internal const string currentArchitecture = "CurrentArchitecture"; 88 internal const string any = "*"; 89 } 90 91 ///////////////////////////////////////////////////////////////////////////////////////////// 92 // If we ever add a new MSBuild namespace (or change this one) we must update the registry key 93 // we set during install to disable the XSL debugger from working on MSBuild format files. 94 ///////////////////////////////////////////////////////////////////////////////////////////// 95 internal const string defaultXmlNamespace = "http://schemas.microsoft.com/developer/msbuild/2003"; 96 97 /// <summary> 98 /// Returns true if and only if the specified attribute is one of the attributes that the engine specifically recognizes 99 /// on a task and treats in a special way. 100 /// </summary> 101 /// <param name="attribute"></param> 102 /// <returns>true, if given attribute is a reserved task attribute</returns> IsSpecialTaskAttribute( string attribute )103 internal static bool IsSpecialTaskAttribute 104 ( 105 string attribute 106 ) 107 { 108 // Currently the known "special" attributes for a task are: 109 // Condition, ContinueOnError 110 // 111 // We want to match case-sensitively on all of them 112 return ((attribute == condition) || 113 (attribute == continueOnError) || 114 (attribute == msbuildRuntime) || 115 (attribute == msbuildArchitecture) || 116 (attribute == xmlns)); 117 } 118 119 /// <summary> 120 /// Checks if the specified attribute is a reserved task attribute with incorrect casing. 121 /// </summary> 122 /// <param name="attribute"></param> 123 /// <returns>true, if the given attribute is reserved and badly cased</returns> IsBadlyCasedSpecialTaskAttribute(string attribute)124 internal static bool IsBadlyCasedSpecialTaskAttribute(string attribute) 125 { 126 return (!IsSpecialTaskAttribute(attribute) && 127 ((String.Compare(attribute, condition, StringComparison.OrdinalIgnoreCase) == 0) || 128 (String.Compare(attribute, continueOnError, StringComparison.OrdinalIgnoreCase) == 0) || 129 (String.Compare(attribute, msbuildRuntime, StringComparison.OrdinalIgnoreCase) == 0) || 130 (String.Compare(attribute, msbuildArchitecture, StringComparison.OrdinalIgnoreCase) == 0))); 131 } 132 133 /// <summary> 134 /// Indicates if the specified attribute cannot be used for batching targets. 135 /// </summary> 136 /// <param name="attribute"></param> 137 /// <returns>true, if a target cannot batch on the given attribute</returns> IsNonBatchingTargetAttribute(string attribute)138 internal static bool IsNonBatchingTargetAttribute(string attribute) 139 { 140 return ((attribute == name) || 141 (attribute == condition) || 142 (attribute == dependsOnTargets) || 143 (attribute == beforeTargets) || 144 (attribute == afterTargets)); 145 } 146 147 /// <summary> 148 /// Returns true if the given string is a valid member of the MSBuildRuntimeValues set 149 /// </summary> IsValidMSBuildRuntimeValue(string runtime)150 internal static bool IsValidMSBuildRuntimeValue(string runtime) 151 { 152 return (runtime == null || 153 XMakeAttributes.MSBuildRuntimeValues.clr2.Equals(runtime, StringComparison.OrdinalIgnoreCase) || 154 XMakeAttributes.MSBuildRuntimeValues.clr4.Equals(runtime, StringComparison.OrdinalIgnoreCase) || 155 XMakeAttributes.MSBuildRuntimeValues.currentRuntime.Equals(runtime, StringComparison.OrdinalIgnoreCase) || 156 XMakeAttributes.MSBuildRuntimeValues.any.Equals(runtime, StringComparison.OrdinalIgnoreCase)); 157 } 158 159 /// <summary> 160 /// Returns true if the given string is a valid member of the MSBuildArchitectureValues set 161 /// </summary> IsValidMSBuildArchitectureValue(string architecture)162 internal static bool IsValidMSBuildArchitectureValue(string architecture) 163 { 164 return (architecture == null || 165 XMakeAttributes.MSBuildArchitectureValues.x86.Equals(architecture, StringComparison.OrdinalIgnoreCase) || 166 XMakeAttributes.MSBuildArchitectureValues.x64.Equals(architecture, StringComparison.OrdinalIgnoreCase) || 167 XMakeAttributes.MSBuildArchitectureValues.currentArchitecture.Equals(architecture, StringComparison.OrdinalIgnoreCase) || 168 XMakeAttributes.MSBuildArchitectureValues.any.Equals(architecture, StringComparison.OrdinalIgnoreCase)); 169 } 170 171 /// <summary> 172 /// Compares two members of MSBuildRuntimeValues, returning true if they count as a match, and false otherwise. 173 /// </summary> RuntimeValuesMatch(string runtimeA, string runtimeB)174 internal static bool RuntimeValuesMatch(string runtimeA, string runtimeB) 175 { 176 ErrorUtilities.VerifyThrow(runtimeA != String.Empty && runtimeB != String.Empty, "We should never get an empty string passed to this method"); 177 178 if (runtimeA == null || runtimeB == null) 179 { 180 // neither one cares, or only one cares, so they match by default. 181 return true; 182 } 183 184 if (runtimeA.Equals(runtimeB, StringComparison.OrdinalIgnoreCase)) 185 { 186 // if they are equal, of course they match 187 return true; 188 } 189 190 if (runtimeA.Equals(MSBuildRuntimeValues.any, StringComparison.OrdinalIgnoreCase) || runtimeB.Equals(MSBuildRuntimeValues.any, StringComparison.OrdinalIgnoreCase)) 191 { 192 // one or both explicitly don't care -- still a match. 193 return true; 194 } 195 196 if ((runtimeA.Equals(MSBuildRuntimeValues.currentRuntime, StringComparison.OrdinalIgnoreCase) && runtimeB.Equals(MSBuildRuntimeValues.clr4, StringComparison.OrdinalIgnoreCase)) || 197 (runtimeA.Equals(MSBuildRuntimeValues.clr4, StringComparison.OrdinalIgnoreCase) && runtimeB.Equals(MSBuildRuntimeValues.currentRuntime, StringComparison.OrdinalIgnoreCase))) 198 { 199 // CLR4 is the current runtime, so this is also a match. 200 return true; 201 } 202 203 // if none of the above is true, then it doesn't match ... 204 return false; 205 } 206 207 /// <summary> 208 /// Given two MSBuildRuntime values, returns the concrete result of merging the two. If the merge fails, the merged runtime 209 /// string is returned null, and the return value of the method is false. Otherwise, if the merge succeeds, the method returns 210 /// true with the merged runtime value. E.g.: 211 /// "CLR4" + "CLR2" = null (false) 212 /// "CLR2" + "don't care" = "CLR2" (true) 213 /// "current runtime" + "CLR4" = "CLR4" (true) 214 /// "current runtime" + "don't care" = "CLR4" (true) 215 /// If both specify "don't care", then defaults to the current runtime -- CLR4. 216 /// A null or empty string is interpreted as "don't care". 217 /// </summary> TryMergeRuntimeValues(string runtimeA, string runtimeB, out string mergedRuntime)218 internal static bool TryMergeRuntimeValues(string runtimeA, string runtimeB, out string mergedRuntime) 219 { 220 ErrorUtilities.VerifyThrow(runtimeA != String.Empty && runtimeB != String.Empty, "We should never get an empty string passed to this method"); 221 222 // set up the defaults 223 if (runtimeA == null) 224 { 225 runtimeA = MSBuildRuntimeValues.any; 226 } 227 228 if (runtimeB == null) 229 { 230 runtimeB = MSBuildRuntimeValues.any; 231 } 232 233 // if they're equal, then there's no problem -- just return the equivalent runtime. 234 if (runtimeA.Equals(runtimeB, StringComparison.OrdinalIgnoreCase)) 235 { 236 if (runtimeA.Equals(MSBuildRuntimeValues.currentRuntime, StringComparison.OrdinalIgnoreCase) || 237 runtimeA.Equals(MSBuildRuntimeValues.any, StringComparison.OrdinalIgnoreCase)) 238 { 239 mergedRuntime = MSBuildRuntimeValues.clr4; 240 } 241 else 242 { 243 mergedRuntime = runtimeA; 244 } 245 246 return true; 247 } 248 249 // if both A and B are one of CLR4, don't care, or current, then the end result will be CLR4 no matter what. 250 if ( 251 ( 252 runtimeA.Equals(MSBuildRuntimeValues.clr4, StringComparison.OrdinalIgnoreCase) || 253 runtimeA.Equals(MSBuildRuntimeValues.currentRuntime, StringComparison.OrdinalIgnoreCase) || 254 runtimeA.Equals(MSBuildRuntimeValues.any, StringComparison.OrdinalIgnoreCase) 255 ) && 256 ( 257 runtimeB.Equals(MSBuildRuntimeValues.clr4, StringComparison.OrdinalIgnoreCase) || 258 runtimeB.Equals(MSBuildRuntimeValues.currentRuntime, StringComparison.OrdinalIgnoreCase) || 259 runtimeB.Equals(MSBuildRuntimeValues.any, StringComparison.OrdinalIgnoreCase) 260 ) 261 ) 262 { 263 mergedRuntime = MSBuildRuntimeValues.clr4; 264 return true; 265 } 266 267 // If A doesn't care, then it's B -- and we can say B straight out, because if B were one of the 268 // special cases (current runtime or don't care) then it would already have been caught in the 269 // previous clause. 270 if (runtimeA.Equals(MSBuildRuntimeValues.any, StringComparison.OrdinalIgnoreCase)) 271 { 272 mergedRuntime = runtimeB; 273 return true; 274 } 275 276 // And vice versa 277 if (runtimeB.Equals(MSBuildRuntimeValues.any, StringComparison.OrdinalIgnoreCase)) 278 { 279 mergedRuntime = runtimeA; 280 return true; 281 } 282 283 // and now we've run out of things that it could be -- all the remaining options are non-matches. 284 mergedRuntime = null; 285 return false; 286 } 287 288 /// <summary> 289 /// Compares two members of MSBuildArchitectureValues, returning true if they count as a match, and false otherwise. 290 /// </summary> ArchitectureValuesMatch(string architectureA, string architectureB)291 internal static bool ArchitectureValuesMatch(string architectureA, string architectureB) 292 { 293 ErrorUtilities.VerifyThrow(architectureA != String.Empty && architectureB != String.Empty, "We should never get an empty string passed to this method"); 294 295 if (architectureA == null || architectureB == null) 296 { 297 // neither one cares, or only one cares, so they match by default. 298 return true; 299 } 300 301 if (architectureA.Equals(architectureB, StringComparison.OrdinalIgnoreCase)) 302 { 303 // if they are equal, of course they match 304 return true; 305 } 306 307 if (architectureA.Equals(MSBuildArchitectureValues.any, StringComparison.OrdinalIgnoreCase) || architectureB.Equals(MSBuildArchitectureValues.any, StringComparison.OrdinalIgnoreCase)) 308 { 309 // one or both explicitly don't care -- still a match. 310 return true; 311 } 312 313 string currentArchitecture = GetCurrentMSBuildArchitecture(); 314 315 if ((architectureA.Equals(MSBuildArchitectureValues.currentArchitecture, StringComparison.OrdinalIgnoreCase) && architectureB.Equals(currentArchitecture, StringComparison.OrdinalIgnoreCase)) || 316 (architectureA.Equals(currentArchitecture, StringComparison.OrdinalIgnoreCase) && architectureB.Equals(MSBuildArchitectureValues.currentArchitecture, StringComparison.OrdinalIgnoreCase))) 317 { 318 return true; 319 } 320 321 // if none of the above is true, then it doesn't match ... 322 return false; 323 } 324 325 /// <summary> 326 /// Given an MSBuildRuntime value that may be non-explicit -- e.g. "CurrentRuntime" or "Any" -- 327 /// return the specific MSBuildRuntime value that it would map to in this case. If it does not map 328 /// to any known runtime, just return it as is -- maybe someone else knows what to do with it; if 329 /// not, they'll certainly have more context on logging or throwing the error. 330 /// </summary> GetExplicitMSBuildRuntime(string runtime)331 internal static string GetExplicitMSBuildRuntime(string runtime) 332 { 333 if (runtime == null || 334 MSBuildRuntimeValues.any.Equals(runtime, StringComparison.OrdinalIgnoreCase) || 335 MSBuildRuntimeValues.currentRuntime.Equals(runtime, StringComparison.OrdinalIgnoreCase)) 336 { 337 // Default to CLR4. 338 return MSBuildRuntimeValues.clr4; 339 } 340 else 341 { 342 // either it's already a valid, specific runtime, or we don't know what to do with it. Either way, return. 343 return runtime; 344 } 345 } 346 347 /// <summary> 348 /// Given two MSBuildArchitecture values, returns the concrete result of merging the two. If the merge fails, the merged architecture 349 /// string is returned null, and the return value of the method is false. Otherwise, if the merge succeeds, the method returns 350 /// true with the merged architecture value. E.g.: 351 /// "x86" + "x64" = null (false) 352 /// "x86" + "don't care" = "x86" (true) 353 /// "current architecture" + "x86" = "x86" (true) on a 32-bit process, and null (false) on a 64-bit process 354 /// "current architecture" + "don't care" = "x86" (true) on a 32-bit process, and "x64" (true) on a 64-bit process 355 /// A null or empty string is interpreted as "don't care". 356 /// If both specify "don't care", then defaults to whatever the current process architecture is. 357 /// </summary> TryMergeArchitectureValues(string architectureA, string architectureB, out string mergedArchitecture)358 internal static bool TryMergeArchitectureValues(string architectureA, string architectureB, out string mergedArchitecture) 359 { 360 ErrorUtilities.VerifyThrow(architectureA != String.Empty && architectureB != String.Empty, "We should never get an empty string passed to this method"); 361 362 // set up the defaults 363 if (architectureA == null) 364 { 365 architectureA = MSBuildArchitectureValues.any; 366 } 367 368 if (architectureB == null) 369 { 370 architectureB = MSBuildArchitectureValues.any; 371 } 372 373 string currentArchitecture = GetCurrentMSBuildArchitecture(); 374 375 // if they're equal, then there's no problem -- just return the equivalent runtime. 376 if (architectureA.Equals(architectureB, StringComparison.OrdinalIgnoreCase)) 377 { 378 if (architectureA.Equals(MSBuildArchitectureValues.currentArchitecture, StringComparison.OrdinalIgnoreCase) || 379 architectureA.Equals(MSBuildArchitectureValues.any, StringComparison.OrdinalIgnoreCase)) 380 { 381 mergedArchitecture = currentArchitecture; 382 } 383 else 384 { 385 mergedArchitecture = architectureA; 386 } 387 388 return true; 389 } 390 391 // if both A and B are one of CLR4, don't care, or current, then the end result will be CLR4 no matter what. 392 if ( 393 ( 394 architectureA.Equals(currentArchitecture, StringComparison.OrdinalIgnoreCase) || 395 architectureA.Equals(MSBuildArchitectureValues.currentArchitecture, StringComparison.OrdinalIgnoreCase) || 396 architectureA.Equals(MSBuildArchitectureValues.any, StringComparison.OrdinalIgnoreCase) 397 ) && 398 ( 399 architectureB.Equals(currentArchitecture, StringComparison.OrdinalIgnoreCase) || 400 architectureB.Equals(MSBuildArchitectureValues.currentArchitecture, StringComparison.OrdinalIgnoreCase) || 401 architectureB.Equals(MSBuildArchitectureValues.any, StringComparison.OrdinalIgnoreCase) 402 ) 403 ) 404 { 405 mergedArchitecture = currentArchitecture; 406 return true; 407 } 408 409 // If A doesn't care, then it's B -- and we can say B straight out, because if B were one of the 410 // special cases (current runtime or don't care) then it would already have been caught in the 411 // previous clause. 412 if (architectureA.Equals(MSBuildArchitectureValues.any, StringComparison.OrdinalIgnoreCase)) 413 { 414 mergedArchitecture = architectureB; 415 return true; 416 } 417 418 // And vice versa 419 if (architectureB.Equals(MSBuildArchitectureValues.any, StringComparison.OrdinalIgnoreCase)) 420 { 421 mergedArchitecture = architectureA; 422 return true; 423 } 424 425 // and now we've run out of things that it could be -- all the remaining options are non-matches. 426 mergedArchitecture = null; 427 return false; 428 } 429 430 /// <summary> 431 /// Returns the MSBuildArchitecture value corresponding to the current process' architecture. 432 /// </summary> 433 /// <comments> 434 /// Revisit if we ever run on something other than Intel. 435 /// </comments> GetCurrentMSBuildArchitecture()436 internal static string GetCurrentMSBuildArchitecture() 437 { 438 string currentArchitecture = (IntPtr.Size == sizeof(Int64)) ? MSBuildArchitectureValues.x64 : MSBuildArchitectureValues.x86; 439 return currentArchitecture; 440 } 441 442 /// <summary> 443 /// Given an MSBuildArchitecture value that may be non-explicit -- e.g. "CurrentArchitecture" or "Any" -- 444 /// return the specific MSBuildArchitecture value that it would map to in this case. If it does not map 445 /// to any known architecture, just return it as is -- maybe someone else knows what to do with it; if 446 /// not, they'll certainly have more context on logging or throwing the error. 447 /// </summary> GetExplicitMSBuildArchitecture(string architecture)448 internal static string GetExplicitMSBuildArchitecture(string architecture) 449 { 450 if (architecture == null || 451 MSBuildArchitectureValues.any.Equals(architecture, StringComparison.OrdinalIgnoreCase) || 452 MSBuildArchitectureValues.currentArchitecture.Equals(architecture, StringComparison.OrdinalIgnoreCase)) 453 { 454 string currentArchitecture = GetCurrentMSBuildArchitecture(); 455 return currentArchitecture; 456 } 457 else 458 { 459 // either it's already a valid, specific architecture, or we don't know what to do with it. Either way, return. 460 return architecture; 461 } 462 } 463 } 464 } 465