1 //------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //------------------------------------------------------------
5 namespace Microsoft.Build.Tasks.Xaml
8 using System.Collections.Generic;
10 using System.Security;
12 using System.Xaml.Schema;
14 using System.Xml.Linq;
15 using Microsoft.Build.Utilities;
16 using System.Reflection;
17 using System.Globalization;
18 using System.Diagnostics.CodeAnalysis;
21 using System.ComponentModel;
22 using System.CodeDom.Compiler;
24 using Microsoft.Build.Framework;
27 internal static class XamlBuildTaskServices
30 internal const string ClrNamespaceUriNamespacePart = "clr-namespace:";
31 internal const string ClrNamespaceUriAssemblyPart = "assembly=";
32 internal const string XamlExtension = ".xaml";
35 //internal static XName SchemaTypeName = XamlSchemaTypeResolver.Default.GetTypeReference(typeof(SchemaType)).Name;
36 const string UnknownExceptionErrorCode = "XC1000";
37 // Update this value if any changes are made to it in System.Xaml\KnownStrings.cs
38 const string serializerReferenceNamePrefix = "__ReferenceID";
40 internal static string SerializerReferenceNamePrefix
41 { get { return serializerReferenceNamePrefix; } }
43 static string _private = String.Empty;
44 internal static string PrivateModifier
45 { get { return _private; } }
47 static string _public = String.Empty;
48 internal static string PublicModifier
49 { get { return _public; } }
51 static string _internal = String.Empty;
52 internal static string InternalModifier
53 { get { return _internal; } }
55 static string _protected = String.Empty;
56 internal static string ProtectedModifier
57 { get { return _protected; } }
59 static string _protectedInternal = String.Empty;
60 internal static string ProtectedInternalModifier
61 { get { return _protectedInternal; } }
63 static string _protectedAndInternal = String.Empty;
64 internal static string ProtectedAndInternalModifier
65 { get { return _protectedAndInternal; } }
67 static string _publicClass = String.Empty;
68 internal static string PublicClassModifier
69 { get { return _publicClass; } }
71 static string _internalClass = String.Empty;
72 internal static string InternalClassModifier
73 { get { return _internalClass; } }
75 static string _fileNotLoaded = String.Empty;
76 internal static string FileNotLoaded
78 get { return _fileNotLoaded; }
81 internal static void PopulateModifiers(CodeDomProvider codeDomProvider)
83 TypeConverter memberAttributesConverter = codeDomProvider.GetConverter(typeof(MemberAttributes));
84 if (memberAttributesConverter != null)
86 if (memberAttributesConverter.CanConvertTo(typeof(string)))
90 _private = memberAttributesConverter.ConvertToInvariantString(MemberAttributes.Private).ToUpperInvariant();
91 _public = memberAttributesConverter.ConvertToInvariantString(MemberAttributes.Public).ToUpperInvariant();
92 _protected = memberAttributesConverter.ConvertToInvariantString(MemberAttributes.Family).ToUpperInvariant();
93 _internal = memberAttributesConverter.ConvertToInvariantString(MemberAttributes.Assembly).ToUpperInvariant();
94 _protectedInternal = memberAttributesConverter.ConvertToInvariantString(MemberAttributes.FamilyOrAssembly).ToUpperInvariant();
95 _protectedAndInternal = memberAttributesConverter.ConvertToInvariantString(MemberAttributes.FamilyAndAssembly).ToUpperInvariant();
97 catch (NotSupportedException)
103 TypeConverter typeAttributesConverter = codeDomProvider.GetConverter(typeof(TypeAttributes));
104 if (typeAttributesConverter != null)
106 if (typeAttributesConverter.CanConvertTo(typeof(string)))
110 _internalClass = typeAttributesConverter.ConvertToInvariantString(TypeAttributes.NotPublic).ToUpperInvariant();
111 _publicClass = typeAttributesConverter.ConvertToInvariantString(TypeAttributes.Public).ToUpperInvariant();
113 catch (NotSupportedException)
120 internal static bool IsPublic(string classModifier)
122 if (!string.IsNullOrEmpty(classModifier))
124 if (string.Equals(classModifier, InternalClassModifier, StringComparison.OrdinalIgnoreCase))
128 else if (string.Equals(classModifier, PublicClassModifier, StringComparison.OrdinalIgnoreCase))
134 throw FxTrace.Exception.AsError(
135 new InvalidOperationException(SR.ClassModifierNotSupported(classModifier)));
141 internal static MemberVisibility GetMemberVisibility(string memberModifier)
143 if (!string.IsNullOrEmpty(memberModifier))
145 if (string.Equals(memberModifier, XamlBuildTaskServices.PrivateModifier, StringComparison.OrdinalIgnoreCase))
147 return MemberVisibility.Private;
149 else if (string.Equals(memberModifier, XamlBuildTaskServices.PublicModifier, StringComparison.OrdinalIgnoreCase))
151 return MemberVisibility.Public;
153 else if (string.Equals(memberModifier, XamlBuildTaskServices.ProtectedModifier, StringComparison.OrdinalIgnoreCase))
155 return MemberVisibility.Family;
157 else if (string.Equals(memberModifier, XamlBuildTaskServices.InternalModifier, StringComparison.OrdinalIgnoreCase))
159 return MemberVisibility.Assembly;
161 else if (string.Equals(memberModifier, XamlBuildTaskServices.ProtectedInternalModifier, StringComparison.OrdinalIgnoreCase))
163 return MemberVisibility.FamilyOrAssembly;
165 else if (string.Equals(memberModifier, XamlBuildTaskServices.ProtectedAndInternalModifier, StringComparison.OrdinalIgnoreCase))
167 return MemberVisibility.FamilyAndAssembly;
171 throw FxTrace.Exception.AsError(new InvalidOperationException(SR.FieldModifierNotSupported(memberModifier)));
174 // Public is only the default modifier for properties, not for fields.
175 // But we explicitly set the default modifier for fields (in ClassImporter), so if the
176 // modifier is null or empty, it must be a property, and so we return public.
177 // This is consistent with Dev10.
178 return MemberVisibility.Public;
181 public static Assembly ReflectionOnlyAssemblyResolve(object sender, ResolveEventArgs args)
183 string[] parts = args.Name.Split(',');
184 foreach (var asm in AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies())
186 AssemblyName assemblyName = asm.GetName();
188 if (assemblyName.Name.Equals(parts[0], StringComparison.OrdinalIgnoreCase))
194 // The fact that the file can not be found in the referenced assembly list means that
195 // CLR will throw a FileLoadException once this event handler returns.
196 // Since the FileLoadException's FileName property is set to null (due to the fact there is no path),
197 // we are storing it in a static variable so that we can print a pretty error message later on.
199 _fileNotLoaded = args.Name;
204 public static AppDomain CreateAppDomain(string friendlyName, string buildTaskPath)
206 if (buildTaskPath == null)
208 throw FxTrace.Exception.AsError(new LoggableException(new InvalidOperationException(SR.BuildTaskPathMustNotBeNull)));
211 // Enable shadow copying in the Designer appdomain, so that we can continue to rebuild
212 // projects in the solution even if designer has loaded the assemblies that were referenced in current project
213 AppDomainSetup appDomainSetup = new AppDomainSetup();
214 appDomainSetup.ShadowCopyFiles = "true";
215 appDomainSetup.ApplicationBase = buildTaskPath;
216 appDomainSetup.LoaderOptimization = LoaderOptimization.MultiDomainHost;
218 // Create appdomain with fulltrust.
219 return AppDomain.CreateDomain(
221 AppDomain.CurrentDomain.Evidence,
223 new NamedPermissionSet("FullTrust"));
226 internal static IList<Assembly> Load(IList<ITaskItem> referenceAssemblies, bool isDesignTime)
228 List<string> systemReferences = new List<string>();
229 List<string> nonSystemReferences = new List<string>();
231 CategorizeReferenceAssemblies(referenceAssemblies, out systemReferences, out nonSystemReferences);
233 IList<Assembly> assemblies = new List<Assembly>();
234 foreach (string item in systemReferences)
236 assemblies.Add(Load(item));
239 foreach (string item in nonSystemReferences)
243 assemblies.Add(Load(item));
245 catch (FileNotFoundException)
247 // file not found on P2P references is allowed.
248 // The design time build can run before the DLL's are present
256 bool mscorlibFound = false;
258 foreach (Assembly asm in assemblies)
260 // here we want to check if the assembly is mscorlib.dll.
261 // for the current codebase, this check would have worked:
262 // if (asm == typeof(Object).Assembly), but we
263 // prefer a check that will continue to work when LMR is used
264 if (asm.GetReferencedAssemblies().Length == 0)
266 mscorlibFound = true;
272 assemblies.Add(typeof(Object).Assembly);
278 [SuppressMessage(FxCop.Category.Reliability, FxCop.Rule.AvoidCallingProblematicMethods,
279 Justification = "Using LoadFile to avoid loading through Fusion and load the exact assembly the developer specified")]
280 internal static Assembly Load(string reference)
282 if (reference.EndsWith("mscorlib.dll", StringComparison.OrdinalIgnoreCase))
284 return typeof(Object).Assembly;
286 string fullPath = Path.GetFullPath(reference);
289 return Assembly.ReflectionOnlyLoadFrom(fullPath);
291 catch (FileNotFoundException e)
293 if (e.FileName == null)
295 throw FxTrace.Exception.AsError((new FileNotFoundException(e.Message, fullPath)));
302 internal static void LogException(TaskLoggingHelper buildLogger, string message)
304 LogException(buildLogger, message, null, 0, 0);
307 internal static void LogException(TaskLoggingHelper buildLogger, string message, string fileName, int lineNumber, int linePosition)
309 string errorCode, logMessage;
310 ExtractErrorCodeAndMessage(buildLogger, message, out errorCode, out logMessage);
311 buildLogger.LogError(null, errorCode, null, fileName, lineNumber, linePosition, 0, 0, logMessage, null);
314 static void ExtractErrorCodeAndMessage(TaskLoggingHelper buildLogger, string message, out string errorCode, out string logMessage)
316 errorCode = buildLogger.ExtractMessageCode(message, out logMessage);
317 if (string.IsNullOrEmpty(errorCode))
319 errorCode = UnknownExceptionErrorCode;
320 logMessage = SR.UnknownBuildError(message);
324 internal static bool IsClrNamespaceUri(string nsName, out int nsIndex, out int assemblyIndex)
326 if (nsName.StartsWith(ClrNamespaceUriNamespacePart, StringComparison.Ordinal))
328 int semicolonIndex = nsName.IndexOf(';');
329 if (semicolonIndex == -1 || nsName.Trim().EndsWith(";", StringComparison.Ordinal))
331 nsIndex = ClrNamespaceUriNamespacePart.Length;
337 int equalsIndex = nsName.IndexOf('=', semicolonIndex);
338 if (equalsIndex != -1)
340 int start = ClrNamespaceUriNamespacePart.Length;
341 int assemblyStart = semicolonIndex + 1;
342 if (equalsIndex - semicolonIndex == ClrNamespaceUriAssemblyPart.Length)
344 for (int i = 0; i < ClrNamespaceUriAssemblyPart.Length; i++)
346 if (nsName[assemblyStart + i] != ClrNamespaceUriAssemblyPart[i])
355 assemblyIndex = assemblyStart + ClrNamespaceUriAssemblyPart.Length;
366 internal static string UpdateClrNamespaceUriWithLocalAssembly(string @namespace, string localAssemblyName)
368 return UpdateClrNamespaceUriWithLocalAssembly(@namespace, localAssemblyName, null);
371 internal static string UpdateClrNamespaceUriWithLocalAssembly(string @namespace, string localAssemblyName, string realAssemblyName)
373 int nsIndex, assemblyIndex;
374 if (IsClrNamespaceUri(@namespace, out nsIndex, out assemblyIndex))
376 // If assembly portion of namespace does not exist, assume that this is part of the local assembly
377 if (assemblyIndex == -1)
379 return string.Format(CultureInfo.InvariantCulture, "{0};{1}{2}",
380 @namespace.TrimEnd(' ', ';'), XamlBuildTaskServices.ClrNamespaceUriAssemblyPart, localAssemblyName);
382 else if (!string.IsNullOrEmpty(realAssemblyName) &&
383 localAssemblyName != realAssemblyName &&
384 @namespace.Substring(assemblyIndex) == realAssemblyName)
386 return string.Format(CultureInfo.InvariantCulture, "{0}{1}",
387 @namespace.Substring(0, assemblyIndex), localAssemblyName);
393 internal static string GetFullTypeName(XamlType xamlType)
395 string typeName = GetFullTypeNameWithoutNamespace(xamlType);
396 typeName = xamlType.PreferredXamlNamespace + ":" + typeName;
400 private static string GetFullTypeNameWithoutNamespace(XamlType xamlType)
402 string typeName = string.Empty;
403 if (xamlType != null)
405 typeName = xamlType.Name;
406 bool firstTypeArg = true;
407 if (xamlType.TypeArguments != null && xamlType.TypeArguments.Count > 0)
410 foreach (XamlType typeArg in xamlType.TypeArguments)
418 firstTypeArg = false;
420 typeName += typeArg.Name;
428 internal static XamlType GetXamlTypeFromString(string typeName, NamespaceTable namespaceTable, XamlSchemaContext xsc)
430 XamlTypeName xamlTypeName = XamlTypeName.Parse(typeName, namespaceTable);
431 XamlType xamlType = xsc.GetXamlType(xamlTypeName);
432 if (xamlType == null)
434 xamlType = GetXamlTypeFromXamlTypeName(xamlTypeName, namespaceTable, xsc);
439 static XamlType GetXamlTypeFromXamlTypeName(XamlTypeName xamlTypeName, NamespaceTable namespaceTable, XamlSchemaContext xsc)
441 IList<XamlType> typeArgs = null;
442 if (xamlTypeName.TypeArguments.Count > 0)
444 typeArgs = new List<XamlType>();
445 foreach (var typeArg in xamlTypeName.TypeArguments)
447 typeArgs.Add(GetXamlTypeFromXamlTypeName(typeArg, namespaceTable, xsc));
450 return new XamlType(xamlTypeName.Namespace, xamlTypeName.Name, typeArgs, xsc);
453 internal static bool TryGetClrTypeName(XamlType xamlType, string rootNamespace, out string clrTypeName)
456 return TryGetClrTypeName(xamlType, rootNamespace, out clrTypeName, out isLocal);
459 internal static bool TryGetClrTypeName(XamlType xamlType, string rootNamespace, out string clrTypeName, out bool isLocal)
461 if (!xamlType.IsUnknown)
464 clrTypeName = xamlType.UnderlyingType != null ? xamlType.UnderlyingType.FullName : null;
465 return (clrTypeName != null);
470 return TryGetClrTypeNameFromLocalType(xamlType, rootNamespace, out clrTypeName);
474 internal static bool TryExtractClrNs(string @namespace, out string clrNs)
476 int nsIndex, assemblyIndex;
477 if (XamlBuildTaskServices.IsClrNamespaceUri(@namespace, out nsIndex, out assemblyIndex))
479 clrNs = (assemblyIndex == -1)
480 ? @namespace.Substring(nsIndex).TrimEnd(' ', ';')
481 : @namespace.Substring(
483 assemblyIndex - XamlBuildTaskServices.ClrNamespaceUriAssemblyPart.Length - nsIndex - 1).TrimEnd(' ', ';');
493 static bool TryGetClrTypeNameFromLocalType(XamlType xamlType, string rootNamespace, out string clrTypeName)
496 // This means that either we have a type in the base hierarchy or type arguments
497 // that is invalid or it's in the local (current) project.
498 string @namespace = xamlType.PreferredXamlNamespace;
499 string name = xamlType.Name;
502 if (@namespace != null && TryExtractClrNs(@namespace, out clrNs))
504 if (!String.IsNullOrEmpty(rootNamespace) &&
505 !String.IsNullOrEmpty(clrNs) &&
506 clrNs.StartsWith(rootNamespace, StringComparison.OrdinalIgnoreCase))
508 if (clrNs.Length > rootNamespace.Length)
510 clrNs = clrNs.Substring(rootNamespace.Length + 1);
514 clrNs = string.Empty;
517 if (string.IsNullOrEmpty(clrNs))
523 clrTypeName = string.Format(CultureInfo.InvariantCulture, "{0}.{1}", clrNs, name);
529 // This could be an open generic with local type-args. Or it could be a known type arg
530 // that we didn't resolve earlier because it was an arg to a local open generic.
531 // Either way we should try to re-resolve it here.
532 string qualifiedName = name;
533 if (xamlType.TypeArguments != null && xamlType.TypeArguments.Count > 0)
535 qualifiedName = name + "`" + xamlType.TypeArguments.Count;
537 XamlType resolvedType = xamlType.SchemaContext.GetXamlType(new XamlTypeName(@namespace, qualifiedName));
538 if (resolvedType != null && resolvedType.UnderlyingType != null)
540 clrTypeName = resolvedType.UnderlyingType.FullName;
548 // Returns true if type namespace is clr-namespace and populates assemblyName
549 // Else returns false with assemblyName null;
550 internal static bool GetTypeNameInAssemblyOrNamespace(XamlType type, string localAssemblyName, string realAssemblyName,
551 out string typeName, out string assemblyName, out string ns)
553 int assemblyIndex, nsIndex;
554 string typeNs = type.PreferredXamlNamespace;
555 typeName = GetFullTypeNameWithoutNamespace(type);
556 if (IsClrNamespaceUri(typeNs, out nsIndex, out assemblyIndex))
558 assemblyName = assemblyIndex > -1 ? typeNs.Substring(assemblyIndex) : String.Empty;
559 if ((!string.IsNullOrEmpty(localAssemblyName) && assemblyName.Contains(localAssemblyName)) || string.IsNullOrEmpty(assemblyName))
561 assemblyName = realAssemblyName;
563 int nsLength = typeNs.IndexOf(';') - nsIndex;
566 ns = typeNs.Substring(nsIndex, nsLength);
570 ns = typeNs.Substring(nsIndex);
582 internal static string GetTypeName(XamlType type, string localAssemblyName, string realAssemblyName)
584 string typeName, assemblyName, ns;
585 if (GetTypeNameInAssemblyOrNamespace(type, localAssemblyName, realAssemblyName, out typeName, out assemblyName, out ns))
587 return ns + "." + typeName;
591 return ns + ":" + typeName;
595 // Returns false if the type is known else returns true.
596 internal static bool GetUnresolvedLeafTypeArg(XamlType type, ref IList<XamlType> unresolvedLeafTypeList)
598 if (unresolvedLeafTypeList != null && type != null && type.IsUnknown)
602 unresolvedLeafTypeList.Add(type);
606 bool hasUnknownChildren = false;
607 foreach (XamlType typeArg in type.TypeArguments)
609 hasUnknownChildren |= GetUnresolvedLeafTypeArg(typeArg, ref unresolvedLeafTypeList);
611 if (!hasUnknownChildren)
613 unresolvedLeafTypeList.Add(type);
624 [SuppressMessage(FxCop.Category.Reliability, FxCop.Rule.AvoidCallingProblematicMethods,
625 Justification = "Using LoadFrom to avoid loading through Fusion and load from the exact path specified")]
626 internal static IEnumerable<T> GetXamlBuildTaskExtensions<T>(IList<Tuple<string, string, string>> extensionNames, TaskLoggingHelper logger, string currentProjectDirectory) where T : class
628 List<T> extensionsLoaded = new List<T>();
630 if (extensionNames == null)
632 return extensionsLoaded;
635 foreach (Tuple<string, string, string> extensionEntry in extensionNames)
637 Assembly assembly = null;
638 string assemblyName = extensionEntry.Item2;
639 string assemblyFile = extensionEntry.Item3;
640 if (!string.IsNullOrEmpty(assemblyName))
644 // try to load using the assembly name
645 assembly = Assembly.Load(assemblyName);
653 logger.LogWarning(SR.UnresolvedExtensionAssembly(assemblyName));
660 if (Path.IsPathRooted(assemblyFile))
662 // if the path is absolute, we just load from the given location
663 assembly = Assembly.LoadFrom(assemblyFile);
667 // if the path is relative, we load from
668 // current project folder + provided relative path
669 assembly = Assembly.LoadFrom(Path.Combine(currentProjectDirectory, assemblyFile));
678 logger.LogWarning(SR.UnresolvedExtensionAssembly(assemblyFile));
682 if (assembly == null)
687 Type extensionType = assembly.GetType(extensionEntry.Item1);
689 if (extensionType == null || extensionType.GetInterface(typeof(T).FullName) == null)
691 string assemblylocationInfo = assemblyFile != "" ? assemblyFile : assemblyName;
692 logger.LogWarning(SR.UnresolvedExtension(extensionEntry.Item1, assemblylocationInfo));
699 extension = Activator.CreateInstance(extensionType) as T;
708 logger.LogWarning(SR.ExceptionThrownDuringConstruction(extensionEntry.Item1,
709 e.GetType().ToString(),
711 e.InnerException != null ? e.InnerException.GetType().ToString() : "null",
712 e.InnerException != null ? e.InnerException.Message : "null"));
717 if (extension != null)
719 extensionsLoaded.Add(extension);
722 return extensionsLoaded;
725 internal static IList<Tuple<string, string, string>> GetXamlBuildTaskExtensionNames(ITaskItem[] xamlBuildTypeGenerationExtensionsNames)
727 List<Tuple<string, string, string>> extensionNames = new List<Tuple<string, string, string>>();
728 if (xamlBuildTypeGenerationExtensionsNames != null)
732 foreach (ITaskItem taskItem in xamlBuildTypeGenerationExtensionsNames)
734 assemblyFile = taskItem.GetMetadata("AssemblyFile");
735 assemblyName = taskItem.GetMetadata("AssemblyName");
736 if (assemblyName != "" && assemblyFile != "")
738 throw FxTrace.Exception.AsError(new LoggableException(SR.BothAssemblyNameAndFileSpecified));
740 if (assemblyName == "" && assemblyFile == "")
742 throw FxTrace.Exception.AsError(new LoggableException(SR.AssemblyNameOrFileNotSpecified));
745 extensionNames.Add(new Tuple<string, string, string>(taskItem.ItemSpec, assemblyName, assemblyFile));
748 return extensionNames;
751 internal static IList<string> GetReferences(IList<ITaskItem> referenceAssemblies)
753 IList<string> references = new List<string>();
754 foreach (var reference in referenceAssemblies)
756 references.Add(reference.ItemSpec);
762 private static void CategorizeReferenceAssemblies(IList<ITaskItem> referenceAssemblies, out List<string> systemItems, out List<string> nonSystemItems)
764 List<string> systemList = new List<string>();
765 List<string> nonSystemList = new List<string>();
766 foreach (ITaskItem item in referenceAssemblies)
768 string resolvedFrom = item.GetMetadata("ResolvedFrom");
769 string isSystemReference = item.GetMetadata("IsSystemReference");
770 string asmName = Path.GetFileName(item.ItemSpec);
772 bool isMsCorLib = asmName.Equals("mscorlib.dll", StringComparison.OrdinalIgnoreCase);
773 bool isManagedSystemMetadata = !String.IsNullOrEmpty(isSystemReference)
774 && isSystemReference.Equals("True", StringComparison.OrdinalIgnoreCase);
775 bool isNativeSystemMetadata = resolvedFrom != null
776 && (resolvedFrom == "GetSDKReferenceFiles" || resolvedFrom == "{TargetFrameworkDirectory}");
778 if (isManagedSystemMetadata || isNativeSystemMetadata || isMsCorLib)
780 systemList.Add(item.ItemSpec);
784 nonSystemList.Add(item.ItemSpec);
788 systemItems = systemList;
789 nonSystemItems = nonSystemList;