diff options
Diffstat (limited to 'mdoc/Mono.Documentation/Updater/Frameworks')
5 files changed, 359 insertions, 0 deletions
diff --git a/mdoc/Mono.Documentation/Updater/Frameworks/AssemblySet.cs b/mdoc/Mono.Documentation/Updater/Frameworks/AssemblySet.cs new file mode 100644 index 00000000..524e39a7 --- /dev/null +++ b/mdoc/Mono.Documentation/Updater/Frameworks/AssemblySet.cs @@ -0,0 +1,111 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Mono.Cecil; + +namespace Mono.Documentation.Updater.Frameworks +{ + /// <summary> + /// Represents a set of assemblies that we want to document + /// </summary> + class AssemblySet : IDisposable + { + readonly DefaultAssemblyResolver resolver = new Frameworks.UwpResolver (); + HashSet<string> assemblyPaths = new HashSet<string> (); + HashSet<string> assemblySearchPaths = new HashSet<string> (); + HashSet<string> forwardedTypes = new HashSet<string> (); + IEnumerable<string> importPaths; + public IEnumerable<DocumentationImporter> Importers { get; private set; } + + public AssemblySet (IEnumerable<string> paths) : this ("Default", paths, new string[0], null) { } + + public AssemblySet (string name, IEnumerable<string> paths, IEnumerable<string> resolverSearchPaths, IEnumerable<string> imports) + { + Name = name; + + foreach (var path in paths) + assemblyPaths.Add (path); + + // add default search paths + var assemblyDirectories = paths + .Where (p => p.Contains (Path.DirectorySeparatorChar)) + .Select (p => Path.GetDirectoryName (p)); + + foreach (var searchPath in resolverSearchPaths.Union (assemblyDirectories)) + assemblySearchPaths.Add (searchPath); + + char oppositeSeparator = Path.DirectorySeparatorChar == '/' ? '\\' : '/'; + Func<string, string> sanitize = p => + p.Replace (oppositeSeparator, Path.DirectorySeparatorChar); + + foreach (var searchPath in assemblySearchPaths.Select (sanitize)) + resolver.AddSearchDirectory (searchPath); + + this.importPaths = imports; + if (this.importPaths != null) + { + this.Importers = this.importPaths.Select (p => MDocUpdater.Instance.GetImporter (p, supportsEcmaDoc: false)); + } + else + this.Importers = new DocumentationImporter[0]; + } + + public string Name { get; private set; } + + public IEnumerable<AssemblyDefinition> Assemblies { get { return this.LoadAllAssemblies ().Where(a => a != null); } } + public IEnumerable<string> AssemblyPaths { get { return this.assemblyPaths; } } + + /// <summary>Adds all subdirectories to the search directories for the resolver to look in.</summary> + public void RecurseSearchDirectories() + { + var directories = resolver + .GetSearchDirectories () + .Select(d => new DirectoryInfo (d)) + .Where (d => d.Exists) + .Select(d => d.FullName) + .ToDictionary(d => d, d => d); + + var subdirs = directories.Keys + .SelectMany (d => Directory.GetDirectories (d, ".", SearchOption.AllDirectories)) + .Where (d => !directories.ContainsKey (d)); + + foreach (var dir in subdirs) + resolver.AddSearchDirectory (dir); + } + + /// <returns><c>true</c>, if in set was contained in the set of assemblies, <c>false</c> otherwise.</returns> + /// <param name="name">An assembly file name</param> + public bool Contains (string name) + { + return assemblyPaths.Any (p => Path.GetFileName (p) == name); + } + + /// <summary>Tells whether an already enumerated AssemblyDefinition, contains the type.</summary> + /// <param name="name">Type name</param> + public bool ContainsForwardedType (string name) + { + return forwardedTypes.Contains (name); + } + + public void Dispose () => resolver.Dispose (); + + public override string ToString () + { + return string.Format ("[AssemblySet: Name={0}, Assemblies={1}]", Name, assemblyPaths.Count); + } + + IEnumerable<AssemblyDefinition> LoadAllAssemblies () + { + foreach (var path in this.assemblyPaths) { + var assembly = MDocUpdater.Instance.LoadAssembly (path, this.resolver); + if (assembly != null) { + foreach (var type in assembly.MainModule.ExportedTypes.Where (t => t.IsForwarder).Select (t => t.FullName)) + forwardedTypes.Add (type); + } + yield return assembly; + } + } + } +} diff --git a/mdoc/Mono.Documentation/Updater/Frameworks/FrameworkEntry.cs b/mdoc/Mono.Documentation/Updater/Frameworks/FrameworkEntry.cs new file mode 100644 index 00000000..c6684d51 --- /dev/null +++ b/mdoc/Mono.Documentation/Updater/Frameworks/FrameworkEntry.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Mono.Cecil; +using Mono.Cecil.Rocks; + +namespace Mono.Documentation.Updater.Frameworks +{ + class FrameworkEntry + { + SortedSet<FrameworkTypeEntry> types = new SortedSet<FrameworkTypeEntry> (); + + List<FrameworkEntry> allframeworks; + + public FrameworkEntry (List<FrameworkEntry> frameworks) + { + allframeworks = frameworks; + if (allframeworks == null) + allframeworks = new List<FrameworkEntry> (0); + } + + public string Name { get; set; } + + public IEnumerable<DocumentationImporter> Importers { get; set; } + + public ISet<FrameworkTypeEntry> Types { get { return this.types; } } + + public IEnumerable<FrameworkEntry> Frameworks { get { return this.allframeworks; } } + + public static readonly FrameworkEntry Empty = new EmptyFrameworkEntry () { Name = "Empty" }; + + public virtual FrameworkTypeEntry ProcessType (TypeDefinition type) + { + + var entry = types.FirstOrDefault (t => t.Name.Equals (type.FullName)); + if (entry == null) { + var docid = DocCommentId.GetDocCommentId (type); + entry = new FrameworkTypeEntry (this) { Id = docid, Name = type.FullName, Namespace = type.Namespace }; + types.Add (entry); + } + return entry; + } + + public override string ToString () => this.Name; + + class EmptyFrameworkEntry : FrameworkEntry + { + public EmptyFrameworkEntry () : base (null) { } + public override FrameworkTypeEntry ProcessType (TypeDefinition type) { return FrameworkTypeEntry.Empty; } + } + } +} diff --git a/mdoc/Mono.Documentation/Updater/Frameworks/FrameworkIndex.cs b/mdoc/Mono.Documentation/Updater/Frameworks/FrameworkIndex.cs new file mode 100644 index 00000000..e392f585 --- /dev/null +++ b/mdoc/Mono.Documentation/Updater/Frameworks/FrameworkIndex.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Xml; +using System.Xml.Linq; + +using Mono.Cecil; + +namespace Mono.Documentation.Updater.Frameworks +{ + + class FrameworkIndex + { + List<FrameworkEntry> frameworks = new List<FrameworkEntry> (); + string path; + + public FrameworkIndex (string pathToFrameworks) + { + path = pathToFrameworks; + } + + public IList<FrameworkEntry> Frameworks { + get { + return this.frameworks; + } + } + + public FrameworkEntry StartProcessingAssembly (AssemblyDefinition assembly, IEnumerable<DocumentationImporter> importers) + { + if (string.IsNullOrWhiteSpace (this.path)) + return FrameworkEntry.Empty; + + string assemblyPath = assembly.MainModule.FileName; + var frameworksDirectory = this.path.EndsWith ("frameworks.xml", StringComparison.OrdinalIgnoreCase) + ? Path.GetDirectoryName (this.path) : this.path; + string relativePath = assemblyPath.Replace (frameworksDirectory, string.Empty); + string shortPath = Path.GetDirectoryName (relativePath); + if (shortPath.StartsWith (Path.DirectorySeparatorChar.ToString (), StringComparison.InvariantCultureIgnoreCase)) + shortPath = shortPath.Substring (1, shortPath.Length - 1); + + + var entry = frameworks.FirstOrDefault (f => f.Name.Equals (shortPath)); + if (entry == null) { + entry = new FrameworkEntry (frameworks) { Name = shortPath, Importers = importers }; + frameworks.Add (entry); + } + return entry; + } + + /// <summary>Writes the framework indices to disk.</summary> + /// <param name="path">The folder where one file for every FrameworkEntry will be written.</param> + public void WriteToDisk (string path) + { + if (string.IsNullOrWhiteSpace (this.path)) + return; + + string outputPath = Path.Combine (path, "FrameworksIndex"); + if (!Directory.Exists (outputPath)) + Directory.CreateDirectory (outputPath); + + foreach (var fx in this.frameworks) { + + XDocument doc = new XDocument ( + new XElement("Framework", + new XAttribute ("Name", fx.Name), + fx.Types + .GroupBy(t => t.Namespace) + .Select(g => new XElement("Namespace", + new XAttribute("Name", g.Key), + g.Select (t => new XElement ("Type", + new XAttribute ("Name", t.Name), + new XAttribute("Id", t.Id), + t.Members.Select (m => + new XElement ("Member", + new XAttribute ("Id", m))))))))); + + // now save the document + string filePath = Path.Combine (outputPath, fx.Name + ".xml"); + + if (File.Exists (filePath)) + File.Delete (filePath); + + var settings = new XmlWriterSettings { Indent = true }; + using (var writer = XmlWriter.Create (filePath, settings)) { + doc.WriteTo (writer); + } + } + } + } +} diff --git a/mdoc/Mono.Documentation/Updater/Frameworks/FrameworkTypeEntry.cs b/mdoc/Mono.Documentation/Updater/Frameworks/FrameworkTypeEntry.cs new file mode 100644 index 00000000..67bcee86 --- /dev/null +++ b/mdoc/Mono.Documentation/Updater/Frameworks/FrameworkTypeEntry.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using Mono.Cecil; +using Mono.Cecil.Rocks; + +namespace Mono.Documentation.Updater.Frameworks +{ + class FrameworkTypeEntry : IComparable<FrameworkTypeEntry> + { + SortedSet<string> members = new SortedSet<string> (); + SortedSet<string> memberscsharpsig = new SortedSet<string> (); + + ILFullMemberFormatter formatter = new ILFullMemberFormatter (); + + FrameworkEntry fx; + + public static FrameworkTypeEntry Empty = new EmptyTypeEntry (FrameworkEntry.Empty) { Name = "Empty" }; + + public FrameworkTypeEntry (FrameworkEntry fx) + { + this.fx = fx; + } + + public string Id { get; set; } + public string Name { get; set; } + public string Namespace { get; set; } + public FrameworkEntry Framework { get { return fx; } } + + public ISet<string> Members { + get { + return this.members; + } + } + + public virtual void ProcessMember (MemberReference member) + { + var resolvedMember = member.Resolve (); + if (resolvedMember != null) { + var docid = DocCommentId.GetDocCommentId (resolvedMember); + members.Add (docid); + } + else + members.Add (member.FullName); + + // this is for lookup purposes + try { + memberscsharpsig.Add(formatter.GetDeclaration(member)); + } + catch {} + } + + public bool ContainsCSharpSig (string sig) + { + return memberscsharpsig.Contains (sig); + } + + public override string ToString () => $"{this.Name} in {this.fx.Name}"; + + public int CompareTo (FrameworkTypeEntry other) + { + if (other == null) return -1; + if (this.Name == null) return 1; + + return string.Compare (this.Name, other.Name, StringComparison.CurrentCulture); + } + + public override bool Equals (object obj) + { + FrameworkTypeEntry other = obj as FrameworkTypeEntry; + if (other == null) return false; + return this.Name.Equals (other.Name); + } + + class EmptyTypeEntry : FrameworkTypeEntry + { + public EmptyTypeEntry (FrameworkEntry fx) : base (fx) { } + public override void ProcessMember (MemberReference member) { } + } + } +} diff --git a/mdoc/Mono.Documentation/Updater/Frameworks/UwpResolver.cs b/mdoc/Mono.Documentation/Updater/Frameworks/UwpResolver.cs new file mode 100644 index 00000000..4dbbec82 --- /dev/null +++ b/mdoc/Mono.Documentation/Updater/Frameworks/UwpResolver.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Microsoft.Win32; +using Mono.Cecil; + +namespace Mono.Documentation.Updater.Frameworks +{ + /// <summary>Mono.Cecil resolver for the windows universal platform</summary> + class UwpResolver : DefaultAssemblyResolver + { + public override AssemblyDefinition Resolve (AssemblyNameReference name) + { + var ver = name.Version; + if (ver.Major == 255 && ver.Minor == 255 && ver.Revision == 255 && name.Name == "mscorlib") + { + var v = new Version (4, 5, 0); + var anr = new AssemblyNameReference (name.Name, v); + return base.Resolve (anr); + } + else + return base.Resolve (name); + } + } +}
\ No newline at end of file |