static void ProcessHtmlFiles(string siteDir, bool test) { string files = "*.html"; if (test) { //files = @"\api\Au.AaaDocFX*"; //files = @"\api\Au.ARegex.Replace"; files = @"\api\Au.AAcc.Find"; files = @"\api\Au.ADialog.Show"; files = @"\articles\Wildcard expression"; files += ".html"; } foreach (var f in AFile.EnumDirectory(siteDir, FEFlags.AndSubdirectories | FEFlags.NeedRelativePaths)) { if (f.IsDirectory) { continue; } var name = f.Name; if (!name.Like(files, true) || name.Ends(@"\toc.html")) { continue; } var file = f.FullPath; //if(test) AOutput.Write($"<><c 0xff>{file}</c>"); var s = File.ReadAllText(file); bool modified = ProcessHtmlFile(ref s, name.Starts(@"\api"), siteDir); if (modified) { File.WriteAllText(!test ? file : file.Remove(file.Length - 1), s); } } ProcessJs(siteDir); }
static void _CreateRef(string dbFile, string dir1, string dir2) { AFile.Delete(dbFile); using var d = new ASqlite(dbFile); using var trans = d.Transaction(); d.Execute("CREATE TABLE ref (name TEXT PRIMARY KEY, data BLOB)"); using var statInsert = d.Statement("INSERT OR REPLACE INTO ref VALUES (?, ?)"); _AddDir(dir1, "WindowsBase", "System.Drawing"); _AddDir(dir2); trans.Commit(); d.Execute("VACUUM"); AOutput.Write("Created " + dbFile); void _AddDir(string dir, params string[] skip) { foreach (var f in AFile.EnumDirectory(dir)) { if (f.IsDirectory) { continue; } if (!f.Name.Ends(".dll", true)) { continue; } var asmName = f.Name.RemoveSuffix(4); if (skip.Contains(asmName)) { continue; } _AddFile(asmName, f.FullPath); //break; } } void _AddFile(string asmName, string asmFile) { //AOutput.Write(asmName); statInsert.Bind(1, asmName); statInsert.Bind(2, File.ReadAllBytes(asmFile)); statInsert.Step(); statInsert.Reset(); } }
/// <summary> /// Creates SQLite databases containing design-time assemblies and XML documentation files of a .NET Core runtime. The SDK must be installed. /// </summary> /// <remarks> /// Shows a list dialog. /// If selected All, creates for all runtime versions starting from 3.1, with names ref.version.db (eg ref.3.1.0.db) and doc.version.db, in AFolders.ThisAppBS. /// Else creates only for the selected runtime version, with names ref.db and doc.db, in dataDir, and sets to copy to AFolders.ThisAppBS when opening next time after process restarts. /// We ship and at run time load databases of single version, named ref.db and doc.db. In the future should allow to download and use multiple versions. /// Also this function allows users to create databases from SDKs installed on their PC, but currently this feature is not exposed. Would need to add UI and exception handling. /// ref.db contains dlls from 'dotnet\packs' folder. They contain only metadata of public API, not all code like dlls in the 'dotnet\shared' folder. /// Why need it when we can load PortableExecutableReference from 'dotnet\shared' folder? Because: /// 1. They are big and may add 100 MB of process memory. We need to load all, because cannot know which are actually used in various stages of compilation. /// 2. When loading from dll files, Windows Defender makes it as slow as 2.5 s or more, unless the files already are in OS file buffers. /// 3. Better compatibility. See https://github.com/dotnet/standard/blob/master/docs/history/evolution-of-design-time-assemblies.md /// doc.db contains XML documentation files of .NET Core assemblies. From the same 'dotnet\packs' folder. /// Why need it: /// 1. Else users would have to download whole .NET Core SDK. Now need only runtimes. /// 2. Parsed XML files can use eg 200 MB of process memory. Now we get doc of a single type/method/etc from database only when need; all other data is not in memory. /// /// Need to run this after changing Core version of C# projects (<TargetFramework>netcoreapp3.1</TargetFramework>). Also update COREVER2 etc in AppHost.cpp. /// </remarks> public static void CreateRefAndDoc(string dataDir = @"Q:\app\Au\Other\Data") { Cursor.Current = Cursors.WaitCursor; if (0 == AFolders.NetRuntimeBS.RegexReplace(@"(?i)\\shared\\(Microsoft\.NETCore\.App)\\.+", @"\packs\$1.Ref", out var refDir, 1)) { throw new AuException(); } //AOutput.Write(refDir); var a = new List <string>(); foreach (var f in AFile.EnumDirectory(refDir, FEFlags.UseRawPath)) //for each version { if (!f.IsDirectory) { continue; } var s = f.Name; int v1 = s.ToInt(0, out int ne), v2 = s.ToInt(ne + 1); if (v1 < 3 || v2 < 1) { continue; //must be 3.1 or later } a.Add(s); } a.Add("All"); int i = ADialog.ShowList(a, "Create database", "For runtime") - 1; if (i < 0) { return; } int n = a.Count - 1; if (i < n) { _CreateRefAndDoc(refDir, a[i], false, dataDir); } else { for (i = 0; i < n; i++) { _CreateRefAndDoc(refDir, a[i], true, dataDir); } } AOutput.Write("CreateRefAndDoc done."); Cursor.Current = Cursors.Arrow; }
static void _CreateDoc(string dbFile, string dir1, string dir2) { AFile.Delete(dbFile); using var d = new ASqlite(dbFile, sql: "PRAGMA page_size = 8192;"); //8192 makes file smaller by 2-3 MB. using var trans = d.Transaction(); d.Execute("CREATE TABLE doc (name TEXT PRIMARY KEY, xml TEXT)"); using var statInsert = d.Statement("INSERT INTO doc VALUES (?, ?)"); using var statDupl = d.Statement("SELECT xml FROM doc WHERE name=?"); var haveRefs = new List <string>(); var uniq = new Dictionary <string, string>(); //name -> asmName //using var textFile = File.CreateText(Path.ChangeExtension(dbFile, "txt")); //test. Compresses almost 2 times better than db. _AddDir(dir1, "WindowsBase"); _AddDir(dir2); statInsert.BindAll(".", string.Join("\n", haveRefs)).Step(); trans.Commit(); d.Execute("VACUUM"); AOutput.Write("Created " + dbFile); void _AddDir(string dir, params string[] skip) { foreach (var f in AFile.EnumDirectory(dir)) { if (f.IsDirectory) { continue; } if (!f.Name.Ends(".xml", true)) { continue; } var asmName = f.Name.RemoveSuffix(4); if (skip.Contains(asmName)) { continue; } if (!AFile.ExistsAsFile(dir + asmName + ".dll")) { AOutput.Write("<><c 0x808080>" + f.Name + "</c>"); continue; } _AddFile(asmName, f.FullPath); //break; } } void _AddFile(string asmName, string xmlFile) { //AOutput.Write(asmName); haveRefs.Add(asmName); var xr = AExtXml.LoadElem(xmlFile); foreach (var e in xr.Descendants("member")) { var name = e.Attr("name"); //remove <remarks> and <example>. Does not save much space, because .NET xmls don't have it. foreach (var v in e.Descendants("remarks").ToArray()) { v.Remove(); } foreach (var v in e.Descendants("example").ToArray()) { v.Remove(); } using var reader = e.CreateReader(); reader.MoveToContent(); var xml = reader.ReadInnerXml(); //AOutput.Write(name, xml); //textFile.WriteLine(name); textFile.WriteLine(xml); textFile.WriteLine("\f"); if (uniq.TryGetValue(name, out var prevRef)) { if (!statDupl.Bind(1, name).Step()) { throw new AuException(); } var prev = statDupl.GetText(0); if (xml != prev && asmName != "System.Linq") { AOutput.Write($"<>\t{name} already defined in {prevRef}\r\n<c 0xc000>{prev}</c>\r\n<c 0xff0000>{xml}</c>"); } statDupl.Reset(); } else { statInsert.BindAll(name, xml).Step(); uniq.Add(name, asmName); } statInsert.Reset(); } } }