X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Ftools%2Fmdoc%2FMono.Documentation%2Fmonodocer.cs;h=b1e410d18eafba9c0fa86a7bc4f8fc0c78e7edad;hb=1dc0dba379942113012ca404a9b931a337620f5d;hp=50e7d3e287cc9da3e18734eb6ea9a980bd59a529;hpb=e1849e070703c17113eea43e475431d5bd224c53;p=mono.git diff --git a/mcs/tools/mdoc/Mono.Documentation/monodocer.cs b/mcs/tools/mdoc/Mono.Documentation/monodocer.cs index 50e7d3e287c..b1e410d18ea 100644 --- a/mcs/tools/mdoc/Mono.Documentation/monodocer.cs +++ b/mcs/tools/mdoc/Mono.Documentation/monodocer.cs @@ -133,6 +133,9 @@ class MDocUpdater : MDocCommand List assemblies; readonly DefaultAssemblyResolver assemblyResolver = new DefaultAssemblyResolver(); + string apistyle = string.Empty; + bool isClassicRun; + bool multiassembly; bool delete; bool show_exceptions; bool no_assembly_versions, ignore_missing_types; @@ -278,6 +281,12 @@ class MDocUpdater : MDocCommand { "preserve", "Do not delete members that don't exist in the assembly, but rather mark them as preserved.", v => PreserveTag = "true" }, + { "multiassembly", + "Allow types to be in multiple assemblies.", + v => multiassembly = true }, + { "api-style=", + "Denotes the apistyle. Currently, only `classic` and `unified` are supported. `classic` set of assemblies should be run first, immediately followed by 'unified' assemblies with the `dropns` parameter.", + v => apistyle = v.ToLowerInvariant ()}, }; var assemblies = Parse (p, args, "update", "[OPTIONS]+ ASSEMBLIES", @@ -286,7 +295,17 @@ class MDocUpdater : MDocCommand return; if (assemblies.Count == 0) Error ("No assemblies specified."); - + + // validation for the api-style parameter + if (apistyle == "classic") + isClassicRun = true; + else if (apistyle == "unified") { + if (!droppedAssemblies.Any ()) + Error ("api-style 'unified' must also supply the 'dropns' parameter with at least one assembly and dropped namespace."); + } else if (!string.IsNullOrWhiteSpace (apistyle)) + Error ("api-style '{0}' is not currently supported", apistyle); + + foreach (var dir in assemblies .Where (a => a.Contains (Path.DirectorySeparatorChar)) .Select (a => Path.GetDirectoryName (a))) @@ -449,8 +468,10 @@ class MDocUpdater : MDocCommand } if (r == null) continue; - c.Remove (n); - c.InsertBefore (n, r); + if (c [n.Name] != null) { + c.RemoveNamedItem (n.Name); + c.InsertBefore (n, r); + } } } @@ -676,12 +697,6 @@ class MDocUpdater : MDocCommand XmlElement td = StubType(type, output); if (td == null) return null; - - System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo (DocUtils.PathCombine (dest, type.Namespace)); - if (!dir.Exists) { - dir.Create(); - Console.WriteLine("Namespace Directory Created: " + type.Namespace); - } } return reltypefile; } @@ -771,7 +786,13 @@ class MDocUpdater : MDocCommand private void AddIndexAssembly (AssemblyDefinition assembly, XmlElement parent) { - XmlElement index_assembly = parent.OwnerDocument.CreateElement("Assembly"); + XmlElement index_assembly = null; + if (multiassembly) + index_assembly = (XmlElement)parent.SelectSingleNode ("Assembly[@Name='"+ assembly.Name.Name +"']"); + + if (index_assembly == null) + index_assembly = parent.OwnerDocument.CreateElement ("Assembly"); + index_assembly.SetAttribute ("Name", assembly.Name.Name); index_assembly.SetAttribute ("Version", assembly.Name.Version.ToString()); @@ -846,7 +867,8 @@ class MDocUpdater : MDocCommand XmlElement index_types = WriteElement(index.DocumentElement, "Types"); XmlElement index_assemblies = WriteElement(index.DocumentElement, "Assemblies"); - index_assemblies.RemoveAll (); + if (!multiassembly) + index_assemblies.RemoveAll (); HashSet goodfiles = new HashSet (StringComparer.OrdinalIgnoreCase); @@ -1010,32 +1032,49 @@ class MDocUpdater : MDocCommand XmlElement e = doc.SelectSingleNode("/Type") as XmlElement; string assemblyName = doc.SelectSingleNode ("/Type/AssemblyInfo/AssemblyName").InnerText; AssemblyDefinition assembly = assemblies.FirstOrDefault (a => a.Name.Name == assemblyName); - if (e != null && !no_assembly_versions && assembly != null && assemblyName != null && UpdateAssemblyVersions(e, assembly, GetAssemblyVersions(assemblyName), false)) { + + Action saveDoc = () => { using (TextWriter writer = OpenWrite (typefile.FullName, FileMode.Truncate)) WriteXml(doc.DocumentElement, writer); + }; + + if (e != null && !no_assembly_versions && assembly != null && assemblyName != null && UpdateAssemblyVersions (e, assembly, GetAssemblyVersions(assemblyName), false)) { + saveDoc (); goodfiles.Add (relTypeFile); continue; } Action actuallyDelete = () => { string newname = typefile.FullName + ".remove"; - try { System.IO.File.Delete (newname); } catch (Exception ex) { Warning ("Unable to delete existing file: {0}", newname); } - try { typefile.MoveTo (newname); } catch (Exception ex) { Warning ("Unable to rename to: {0}", newname); } + try { System.IO.File.Delete (newname); } catch (Exception) { Warning ("Unable to delete existing file: {0}", newname); } + try { typefile.MoveTo (newname); } catch (Exception) { Warning ("Unable to rename to: {0}", newname); } Console.WriteLine ("Class no longer present; file renamed: " + Path.Combine (nsdir.Name, typefile.Name)); }; if (string.IsNullOrWhiteSpace (PreserveTag)) { // only do this if there was not a -preserve - using (TextWriter writer = OpenWrite (typefile.FullName, FileMode.Truncate)) - WriteXml (doc.DocumentElement, writer); - + saveDoc (); + var unifiedAssemblyNode = doc.SelectSingleNode ("/Type/AssemblyInfo[@apistyle='unified']"); - var classicAssemblyNode = doc.SelectSingleNode ("/Type/AssemblyInfo[@apistyle='classic']"); + var classicAssemblyNode = doc.SelectSingleNode ("/Type/AssemblyInfo[not(@apistyle) or @apistyle='classic']"); + var unifiedMembers = doc.SelectNodes ("//Member[@apistyle='unified']|//Member/AssemblyInfo[@apistyle='unified']"); + var classicMembers = doc.SelectNodes ("//Member[@apistyle='classic']|//Member/AssemblyInfo[@apistyle='classic']"); bool isUnifiedRun = HasDroppedAnyNamespace (); bool isClassicOrNormalRun = !isUnifiedRun; + + Action removeStyles = (x, style) => { + var styledNodes = doc.SelectNodes("//*[@apistyle='"+ style.ToString ().ToLowerInvariant () +"']"); + if (styledNodes != null && styledNodes.Count > 0) { + foreach(var node in styledNodes.Cast ()) { + node.ParentNode.RemoveChild (node); + } + } + saveDoc (); + }; if (isClassicOrNormalRun) { - if (unifiedAssemblyNode != null) { + if (unifiedAssemblyNode != null || unifiedMembers.Count > 0) { Warning ("*** this type is marked as unified, not deleting during this run: {0}", typefile.FullName); // if truly removed from both assemblies, it will be removed fully during the unified run + removeStyles (doc, ApiStyle.Classic); continue; } else { // we should be safe to delete here because it was not marked as a unified assembly @@ -1043,7 +1082,7 @@ class MDocUpdater : MDocCommand } } if (isUnifiedRun) { - if (classicAssemblyNode != null) { + if (classicAssemblyNode != null || classicMembers.Count > 0) { Warning ("*** this type is marked as classic, not deleting {0}", typefile.FullName); continue; } else { @@ -1146,6 +1185,16 @@ class MDocUpdater : MDocCommand foreach (DocsNodeInfo info in docEnum.GetDocumentationMembers (basefile, type)) { XmlElement oldmember = info.Node; MemberReference oldmember2 = info.Member; + + if (info.Member != null && info.Node != null) { + // Check for an error condition where the xml MemberName doesn't match the matched member + var memberName = GetMemberName (info.Member); + var memberAttribute = info.Node.Attributes ["MemberName"]; + if (memberAttribute == null || (memberAttribute.Value != memberName && memberAttribute.Value.Split (',').Length != memberName.Split (',').Length)) { + oldmember.SetAttribute ("MemberName", memberName); + } + } + string sig = oldmember2 != null ? memberFormatters [0].GetDeclaration (oldmember2) : null; // Interface implementations and overrides are deleted from the docs @@ -1226,10 +1275,10 @@ class MDocUpdater : MDocCommand XmlElement mm = MakeMember(basefile, new DocsNodeInfo (null, m)); if (mm == null) continue; - if (MDocUpdater.SwitchingToMagicTypes) { + if (MDocUpdater.SwitchingToMagicTypes || MDocUpdater.HasDroppedNamespace (m)) { // this is a unified style API that obviously doesn't exist in the classic API. Let's mark // it with apistyle="unified", so that it's not displayed for classic style APIs - mm.SetAttribute ("apistyle", "unified"); + mm.AddApiStyle (ApiStyle.Unified); } members.AppendChild( mm ); @@ -1340,8 +1389,10 @@ class MDocUpdater : MDocCommand bool unifiedRun = HasDroppedNamespace (type); - var classicAssemblyInfo = member.SelectSingleNode ("AssemblyInfo[@apistyle='classic']"); - bool nodeIsClassic = classicAssemblyInfo != null; + var classicAssemblyInfo = member.SelectSingleNode ("AssemblyInfo[not(@apistyle) or @apistyle='classic']"); + bool nodeIsClassic = classicAssemblyInfo != null || member.HasApiStyle (ApiStyle.Classic); + var unifiedAssemblyInfo = member.SelectSingleNode ("AssemblyInfo[@apistyle='unified']"); + bool nodeIsUnified = unifiedAssemblyInfo != null || member.HasApiStyle (ApiStyle.Unified); Action actuallyDelete = () => { todelete.Add (member); @@ -1356,14 +1407,16 @@ class MDocUpdater : MDocCommand Warning (message, signature); } else if (unifiedRun && nodeIsClassic) { // this is a unified run, and the member doesn't exist, but is marked as being in the classic assembly. - Warning ("Not removing '{0}' since it's still in the classic assembly.", signature); + member.RemoveApiStyle (ApiStyle.Unified); + member.AddApiStyle (ApiStyle.Classic); + Warning ("Not removing '{0}' since it's still in the classic assembly.", signature); } else if (unifiedRun && !nodeIsClassic) { // unified run, and the node is not classic, which means it doesn't exist anywhere. actuallyDelete (); - } else { - if (!nodeIsClassic) { + } else { + if (!isClassicRun || (isClassicRun && !nodeIsClassic && !nodeIsUnified)) { // regular codepath (ie. not classic/unified) actuallyDelete (); - } else { + } else { // this is a classic run Warning ("Removing classic from '{0}' ... will be removed in the unified run if not present there.", signature); member.RemoveApiStyle (ApiStyle.Classic); if (classicAssemblyInfo != null) { @@ -1509,28 +1562,15 @@ class MDocUpdater : MDocCommand type); } - string assemblyInfoNodeFilter = MDocUpdater.HasDroppedNamespace (type) ? "[@apistyle='unified']" : "[not(@apistyle) or @apistyle='classic']"; - - AddXmlNode( - root.SelectNodes ("AssemblyInfo" + assemblyInfoNodeFilter).Cast ().ToArray (), - x => x.SelectSingleNode("AssemblyName").InnerText == type.Module.Assembly.Name.Name, - x => WriteElementText(x, "AssemblyName", type.Module.Assembly.Name.Name), - () => { - XmlElement ass = WriteElement(root, "AssemblyInfo", forceNewElement:true); - - if (MDocUpdater.HasDroppedNamespace (type)) ass.SetAttribute ("apistyle", "unified"); - - + AddAssemblyNameToNode (root, type); - return ass; - }, - type); - - foreach(var ass in root.SelectNodes ("AssemblyInfo" + assemblyInfoNodeFilter).Cast ()) + string assemblyInfoNodeFilter = MDocUpdater.HasDroppedNamespace (type) ? "[@apistyle='unified']" : "[not(@apistyle) or @apistyle='classic']"; + Func assemblyFilter = x => x.SelectSingleNode ("AssemblyName").InnerText == type.Module.Assembly.Name.Name; + foreach(var ass in root.SelectNodes ("AssemblyInfo" + assemblyInfoNodeFilter).Cast ().Where (assemblyFilter)) { WriteElementText(ass, "AssemblyName", type.Module.Assembly.Name.Name); if (!no_assembly_versions) { - UpdateAssemblyVersions (root, type, true); + UpdateAssemblyVersions (ass, type, true); } else { var versions = ass.SelectNodes ("AssemblyVersion").Cast ().ToList (); @@ -1628,6 +1668,43 @@ class MDocUpdater : MDocCommand NormalizeWhitespace(root); } + /// Adds an AssemblyInfo with AssemblyName node to an XmlElement. + /// The assembly that was either added, or was already present + XmlElement AddAssemblyNameToNode (XmlElement root, TypeDefinition type) + { + return AddAssemblyNameToNode (root, type.Module); + } + + /// Adds an AssemblyInfo with AssemblyName node to an XmlElement. + /// The assembly that was either added, or was already present + XmlElement AddAssemblyNameToNode (XmlElement root, ModuleDefinition module) + { + Func assemblyFilter = x => { + var existingName = x.SelectSingleNode ("AssemblyName"); + + bool apiStyleMatches = true; + string currentApiStyle = x.GetAttribute ("apistyle"); + if ((HasDroppedNamespace (module) && !string.IsNullOrWhiteSpace (currentApiStyle) && currentApiStyle != "unified") || + (isClassicRun && (string.IsNullOrWhiteSpace (currentApiStyle) || currentApiStyle != "classic"))) { + apiStyleMatches = false; + } + return apiStyleMatches && (existingName == null || (existingName != null && existingName.InnerText == module.Assembly.Name.Name)); + }; + + return AddAssemblyXmlNode ( + root.SelectNodes ("AssemblyInfo").Cast ().ToArray (), + assemblyFilter, x => WriteElementText (x, "AssemblyName", module.Assembly.Name.Name), + () => { + XmlElement ass = WriteElement (root, "AssemblyInfo", forceNewElement: true); + + if (MDocUpdater.HasDroppedNamespace (module)) + ass.AddApiStyle (ApiStyle.Unified); + if (isClassicRun) + ass.AddApiStyle (ApiStyle.Classic); + return ass; + }, module); + } + static readonly string[] TypeNodeOrder = { "TypeSignature", "MemberOfLibrary", @@ -1683,7 +1760,13 @@ class MDocUpdater : MDocCommand WriteElementText(me, "MemberType", GetMemberType(mi)); if (!no_assembly_versions) { - UpdateAssemblyVersions (me, mi, true); + if (!multiassembly) + UpdateAssemblyVersions (me, mi, true); + else { + var node = AddAssemblyNameToNode (me, mi.Module); + + UpdateAssemblyVersionForAssemblyInfo (node, me, new[] { GetAssemblyVersion (mi.Module.Assembly) }, add: true); + } } else { ClearElement (me, "AssemblyInfo"); @@ -1717,6 +1800,25 @@ class MDocUpdater : MDocCommand AddXmlNode (relevant, valueMatches, setValue, makeNewNode, type.Module); } + static XmlElement AddAssemblyXmlNode (XmlElement[] relevant, Func valueMatches, Action setValue, Func makeNewNode, ModuleDefinition module) + { + bool isUnified = MDocUpdater.HasDroppedNamespace (module); + XmlElement thisAssemblyNode = relevant.FirstOrDefault (valueMatches); + if (thisAssemblyNode == null) { + thisAssemblyNode = makeNewNode (); + } + setValue (thisAssemblyNode); + + if (isUnified) { + thisAssemblyNode.AddApiStyle (ApiStyle.Unified); + + foreach (var otherNodes in relevant.Where (n => n != thisAssemblyNode && n.DoesNotHaveApiStyle (ApiStyle.Unified))) { + otherNodes.AddApiStyle (ApiStyle.Classic); + } + } + return thisAssemblyNode; + } + /// Adds an xml node, reusing the node if it's available /// The existing set of nodes /// Checks to see if the node's value matches what you're trying to write. @@ -2276,12 +2378,18 @@ class MDocUpdater : MDocCommand n.ParentNode.RemoveChild(n); } - private static bool UpdateAssemblyVersions (XmlElement root, MemberReference member, bool add) + private bool UpdateAssemblyVersions (XmlElement root, MemberReference member, bool add) { TypeDefinition type = member as TypeDefinition; if (type == null) type = member.DeclaringType as TypeDefinition; - return UpdateAssemblyVersions(root, type.Module.Assembly, new string[]{ GetAssemblyVersion (type.Module.Assembly) }, add); + + var versions = new string[] { GetAssemblyVersion (type.Module.Assembly) }; + + if (root.LocalName == "AssemblyInfo") + return UpdateAssemblyVersionForAssemblyInfo (root, root.ParentNode as XmlElement, versions, add: true); + else + return UpdateAssemblyVersions (root, type.Module.Assembly, versions, add); } private static string GetAssemblyVersion (AssemblyDefinition assembly) @@ -2289,8 +2397,11 @@ class MDocUpdater : MDocCommand return assembly.Name.Version.ToString(); } - private static bool UpdateAssemblyVersions(XmlElement root, AssemblyDefinition assembly, string[] assemblyVersions, bool add) + private bool UpdateAssemblyVersions(XmlElement root, AssemblyDefinition assembly, string[] assemblyVersions, bool add) { + if (multiassembly) + return false; + XmlElement av = (XmlElement) root.SelectSingleNode ("AssemblyVersions"); if (av != null) { // AssemblyVersions is not part of the spec @@ -2307,7 +2418,7 @@ class MDocUpdater : MDocCommand e = root.OwnerDocument.CreateElement("AssemblyInfo"); if (MDocUpdater.HasDroppedNamespace (assembly)) { - e.SetAttribute ("apistyle", "unified"); + e.AddApiStyle (ApiStyle.Unified); } root.AppendChild(e); @@ -2316,13 +2427,16 @@ class MDocUpdater : MDocCommand var thatNode = (XmlElement) root.SelectSingleNode (thatNodeFilter); if (MDocUpdater.HasDroppedNamespace (assembly) && thatNode != null) { // there's a classic node, we should add apistyles - e.SetAttribute ("apistyle", "unified"); - thatNode.SetAttribute ("apistyle", "classic"); + e.AddApiStyle (ApiStyle.Unified); + thatNode.AddApiStyle (ApiStyle.Classic); } - List matches = e.SelectNodes ("AssemblyVersion").Cast() - .Where(v => Array.IndexOf (assemblyVersions, v.InnerText) >= 0) - .ToList (); + return UpdateAssemblyVersionForAssemblyInfo (e, root, assemblyVersions, add); + } + + static bool UpdateAssemblyVersionForAssemblyInfo (XmlElement e, XmlElement root, string[] assemblyVersions, bool add) + { + List matches = e.SelectNodes ("AssemblyVersion").Cast ().Where (v => Array.IndexOf (assemblyVersions, v.InnerText) >= 0).ToList (); // matches.Count > 0 && add: ignore -- already present if (matches.Count > 0 && !add) { foreach (XmlNode c in matches) @@ -2941,21 +3055,63 @@ static class DocUtils { string styleString = style.ToString ().ToLowerInvariant (); return element.GetAttribute ("apistyle") == styleString; } + public static bool HasApiStyle(this XmlNode node, ApiStyle style) + { + var attribute = node.Attributes ["apistyle"]; + return attribute != null && attribute.Value == style.ToString ().ToLowerInvariant (); + } public static void AddApiStyle(this XmlElement element, ApiStyle style) { string styleString = style.ToString ().ToLowerInvariant (); var existingValue = element.GetAttribute ("apistyle"); if (string.IsNullOrWhiteSpace (existingValue) || existingValue != styleString) { element.SetAttribute ("apistyle", styleString); } + + // Propagate the API style up to the membernode if necessary + if (element.LocalName == "AssemblyInfo" && element.ParentNode != null && element.ParentNode.LocalName == "Member") { + var member = element.ParentNode; + var unifiedAssemblyNode = member.SelectSingleNode ("AssemblyInfo[@apistyle='unified']"); + var classicAssemblyNode = member.SelectSingleNode ("AssemblyInfo[not(@apistyle) or @apistyle='classic']"); + + var parentAttribute = element.ParentNode.Attributes ["apistyle"]; + Action removeStyle = () => element.ParentNode.Attributes.Remove (parentAttribute); + Action propagateStyle = () => { + if (parentAttribute == null) { + // if it doesn't have the attribute, then add it + parentAttribute = element.OwnerDocument.CreateAttribute ("apistyle"); + parentAttribute.Value = styleString; + element.ParentNode.Attributes.Append (parentAttribute); + } + }; + + if ((style == ApiStyle.Classic && unifiedAssemblyNode != null) || (style == ApiStyle.Unified && classicAssemblyNode != null)) + removeStyle (); + else + propagateStyle (); + } + } + public static void AddApiStyle (this XmlNode node, ApiStyle style) + { + string styleString = style.ToString ().ToLowerInvariant (); + var existingAttribute = node.Attributes ["apistyle"]; + if (existingAttribute == null) { + existingAttribute = node.OwnerDocument.CreateAttribute ("apistyle"); + node.Attributes.Append (existingAttribute); + } + existingAttribute.Value = styleString; } public static void RemoveApiStyle (this XmlElement element, ApiStyle style) { - element.RemoveAttribute (style.ToString ().ToLowerInvariant ()); + string styleString = style.ToString ().ToLowerInvariant (); + string existingValue = element.GetAttribute ("apistyle"); + if (string.IsNullOrWhiteSpace (existingValue) || existingValue == styleString) { + element.RemoveAttribute ("apistyle"); + } } public static void RemoveApiStyle (this XmlNode node, ApiStyle style) { var styleAttribute = node.Attributes ["apistyle"]; - if (styleAttribute != null) { + if (styleAttribute != null && styleAttribute.Value == style.ToString ().ToLowerInvariant ()) { node.Attributes.Remove (styleAttribute); } } @@ -3259,7 +3415,7 @@ class DocumentationEnumerator { string docName = member.MemberName; - string[] docTypeParams = GetTypeParameters (docName); + string[] docTypeParams = GetTypeParameters (docName, member.TypeParameters); // If we're using 'magic types', then we might get false positives ... in those cases, we keep searching MemberReference likelyCandidate = null; @@ -3369,7 +3525,7 @@ class DocumentationEnumerator { return likelyCandidate; } - static string[] GetTypeParameters (string docName) + static string[] GetTypeParameters (string docName, IEnumerable knownParameters) { if (docName [docName.Length-1] != '>') return null; @@ -3386,7 +3542,12 @@ class DocumentationEnumerator { } while (--i >= 0); types.Reverse (); - return types.ToArray (); + var arrayTypes = types.ToArray (); + + if (knownParameters != null && knownParameters.Any () && arrayTypes.Length != knownParameters.Count ()) + return knownParameters.ToArray (); + else + return arrayTypes; } protected static IEnumerable GetReflectionMembers (TypeDefinition type, string docName)