Skip to content

Commit

Permalink
Merge branch 'main' into feat-markdig-extension-override2
Browse files Browse the repository at this point in the history
  • Loading branch information
yufeih authored Apr 1, 2024
2 parents 93ee11d + aa982be commit f25547f
Show file tree
Hide file tree
Showing 12 changed files with 229 additions and 26 deletions.
4 changes: 2 additions & 2 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="HtmlAgilityPack" Version="1.11.59" />
<PackageVersion Include="HtmlAgilityPack" Version="1.11.60" />
<PackageVersion Include="ICSharpCode.Decompiler" Version="8.2.0.7535" />
<PackageVersion Include="IgnoresAccessChecksToGenerator" Version="0.7.0" />
<PackageVersion Include="Jint" Version="3.0.1" />
Expand Down Expand Up @@ -37,7 +37,7 @@
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageVersion Include="PublicApiGenerator" Version="11.1.0" />
<PackageVersion Include="Verify.DiffPlex" Version="2.3.0" />
<PackageVersion Include="Verify.Xunit" Version="23.5.2" />
<PackageVersion Include="Verify.Xunit" Version="23.6.0" />
<PackageVersion Include="xunit.runner.visualstudio" Version="2.5.7" />
<PackageVersion Include="xunit" Version="2.7.0" />
</ItemGroup>
Expand Down
3 changes: 3 additions & 0 deletions docs/reference/docfx-json-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

The `docfx.json` file indicates that the directory is the root of a docfx project.

> [!IMPORTANT]
> All relative paths specified in config file are relative to the location of `docfx.json`
```json
{
"build": { },
Expand Down
39 changes: 31 additions & 8 deletions src/Docfx.Build/XRefMaps/XRefMapDownloader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,13 +111,25 @@ protected static IXRefContainer DownloadFromLocal(Uri uri)
private static IXRefContainer ReadLocalFile(string filePath)
{
Logger.LogVerbose($"Reading from file: {filePath}");
if (".zip".Equals(Path.GetExtension(filePath), StringComparison.OrdinalIgnoreCase))

switch (Path.GetExtension(filePath).ToLowerInvariant())
{
return XRefArchive.Open(filePath, XRefArchiveMode.Read);
}
case ".zip":
return XRefArchive.Open(filePath, XRefArchiveMode.Read);

using var sr = File.OpenText(filePath);
return YamlUtility.Deserialize<XRefMap>(sr);
case ".json":
{
using var stream = File.OpenText(filePath);
return JsonUtility.Deserialize<XRefMap>(stream);
}

case ".yml":
default:
{
using var sr = File.OpenText(filePath);
return YamlUtility.Deserialize<XRefMap>(sr);
}
}
}

protected static async Task<XRefMap> DownloadFromWebAsync(Uri uri)
Expand All @@ -134,10 +146,21 @@ protected static async Task<XRefMap> DownloadFromWebAsync(Uri uri)
};

using var stream = await httpClient.GetStreamAsync(uri);
using var sr = new StreamReader(stream, bufferSize: 81920); // Default :1024 byte
var map = YamlUtility.Deserialize<XRefMap>(sr);

return map;
switch (Path.GetExtension(uri.AbsolutePath).ToLowerInvariant())
{
case ".json":
{
using var sr = new StreamReader(stream, bufferSize: 81920); // Default :1024 byte
return JsonUtility.Deserialize<XRefMap>(sr);
}
case ".yml":
default:
{
using var sr = new StreamReader(stream, bufferSize: 81920); // Default :1024 byte
return YamlUtility.Deserialize<XRefMap>(sr);
}
}
}

public static void UpdateHref(XRefMap map, Uri uri)
Expand Down
6 changes: 6 additions & 0 deletions src/Docfx.Build/XRefMaps/XRefMapRedirection.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Text.Json.Serialization;
using Newtonsoft.Json;
using YamlDotNet.Serialization;

namespace Docfx.Build.Engine;

public class XRefMapRedirection
{
[YamlMember(Alias = "uidPrefix")]
[JsonProperty("uidPrefix")]
[JsonPropertyName("uidPrefix")]
public string UidPrefix { get; set; }

[YamlMember(Alias = "href")]
[JsonProperty("Href")]
[JsonPropertyName("href")]
public string Href { get; set; }
}
16 changes: 8 additions & 8 deletions src/Docfx.Dotnet/CompilationHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public static bool CheckDiagnostics(this Compilation compilation, bool errorAsWa
return errorCount > 0;
}

public static Compilation CreateCompilationFromCSharpFiles(IEnumerable<string> files, IDictionary<string, string> msbuildProperties)
public static Compilation CreateCompilationFromCSharpFiles(IEnumerable<string> files, IDictionary<string, string> msbuildProperties, MetadataReference[] references)
{
var parserOption = GetCSharpParseOptions(msbuildProperties);
var syntaxTrees = files.Select(path => CS.CSharpSyntaxTree.ParseText(File.ReadAllText(path), parserOption, path: path));
Expand All @@ -63,7 +63,7 @@ public static Compilation CreateCompilationFromCSharpFiles(IEnumerable<string> f
assemblyName: null,
options: new CS.CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, xmlReferenceResolver: XmlFileResolver.Default),
syntaxTrees: syntaxTrees,
references: GetDefaultMetadataReferences("C#"));
references: GetDefaultMetadataReferences("C#").Concat(references));
}

public static Compilation CreateCompilationFromCSharpCode(string code, IDictionary<string, string> msbuildProperties, string? name = null, params MetadataReference[] references)
Expand All @@ -75,10 +75,10 @@ public static Compilation CreateCompilationFromCSharpCode(string code, IDictiona
name,
options: new CS.CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, xmlReferenceResolver: XmlFileResolver.Default),
syntaxTrees: [syntaxTree],
references: GetDefaultMetadataReferences("C#").Concat(references));
references: GetDefaultMetadataReferences("C#").Concat(references ?? []));
}

public static Compilation CreateCompilationFromVBFiles(IEnumerable<string> files, IDictionary<string, string> msbuildProperties)
public static Compilation CreateCompilationFromVBFiles(IEnumerable<string> files, IDictionary<string, string> msbuildProperties, MetadataReference[] references)
{
var parserOption = GetVisualBasicParseOptions(msbuildProperties);
var syntaxTrees = files.Select(path => VB.VisualBasicSyntaxTree.ParseText(File.ReadAllText(path), parserOption, path: path));
Expand All @@ -87,7 +87,7 @@ public static Compilation CreateCompilationFromVBFiles(IEnumerable<string> files
assemblyName: null,
options: new VB.VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary, globalImports: GetVBGlobalImports(), xmlReferenceResolver: XmlFileResolver.Default),
syntaxTrees: syntaxTrees,
references: GetDefaultMetadataReferences("VB"));
references: GetDefaultMetadataReferences("VB").Concat(references));
}

public static Compilation CreateCompilationFromVBCode(string code, IDictionary<string, string> msbuildProperties, string? name = null, params MetadataReference[] references)
Expand All @@ -99,19 +99,19 @@ public static Compilation CreateCompilationFromVBCode(string code, IDictionary<s
name,
options: new VB.VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary, globalImports: GetVBGlobalImports(), xmlReferenceResolver: XmlFileResolver.Default),
syntaxTrees: [syntaxTree],
references: GetDefaultMetadataReferences("VB").Concat(references));
references: GetDefaultMetadataReferences("VB").Concat(references ?? []));
}

public static (Compilation, IAssemblySymbol) CreateCompilationFromAssembly(string assemblyPath, IEnumerable<string>? references = null)
public static (Compilation, IAssemblySymbol) CreateCompilationFromAssembly(string assemblyPath, params MetadataReference[] references)
{
var metadataReference = CreateMetadataReference(assemblyPath);
var compilation = CS.CSharpCompilation.Create(
assemblyName: null,
options: new CS.CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary),
syntaxTrees: s_assemblyBootstrap,
references: GetReferenceAssemblies(assemblyPath)
.Concat(references ?? Enumerable.Empty<string>())
.Select(CreateMetadataReference)
.Concat(references ?? [])
.Append(metadataReference));

var assembly = (IAssemblySymbol)compilation.GetAssemblyOrModuleSymbol(metadataReference)!;
Expand Down
18 changes: 14 additions & 4 deletions src/Docfx.Dotnet/DotnetApiCatalog.Compile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ partial class DotnetApiCatalog
_ => LoggerVerbosity.Quiet,
});

var workspace = MSBuildWorkspace.Create(msbuildProperties);
using var workspace = MSBuildWorkspace.Create(msbuildProperties);
workspace.WorkspaceFailed += (sender, e) => Logger.LogWarning($"{e.Diagnostic}");

if (files.TryGetValue(FileType.NotSupported, out var unsupportedFiles))
Expand Down Expand Up @@ -88,26 +88,36 @@ await LoadCompilationFromProject(project.AbsolutePath) is { } compilation)
assemblies.Add((compilation.Assembly, compilation));
}

var references = config.References ?? [];
var metadataReferences = references.Select(assemblyPath =>
{
var documentation = XmlDocumentationProvider.CreateFromFile(Path.ChangeExtension(assemblyPath, ".xml"));
return MetadataReference.CreateFromFile(assemblyPath, documentation: documentation);
}).ToArray();

// LoadCompilationFrom C# source files
if (files.TryGetValue(FileType.CSSourceCode, out var csFiles))
{
var compilation = CompilationHelper.CreateCompilationFromCSharpFiles(csFiles.Select(f => f.NormalizedPath), msbuildProperties);
var compilation = CompilationHelper.CreateCompilationFromCSharpFiles(csFiles.Select(f => f.NormalizedPath), msbuildProperties, metadataReferences);
hasCompilationError |= compilation.CheckDiagnostics(config.AllowCompilationErrors);
assemblies.Add((compilation.Assembly, compilation));
}

// LoadCompilationFrom VB source files
if (files.TryGetValue(FileType.VBSourceCode, out var vbFiles))
{
var compilation = CompilationHelper.CreateCompilationFromVBFiles(vbFiles.Select(f => f.NormalizedPath), msbuildProperties);
var compilation = CompilationHelper.CreateCompilationFromVBFiles(vbFiles.Select(f => f.NormalizedPath), msbuildProperties, metadataReferences);
hasCompilationError |= compilation.CheckDiagnostics(config.AllowCompilationErrors);
assemblies.Add((compilation.Assembly, compilation));
}

// Load Compilation from assembly files
if (files.TryGetValue(FileType.Assembly, out var assemblyFiles))
{
foreach (var assemblyFile in assemblyFiles)
{
Logger.LogInfo($"Loading assembly {assemblyFile.NormalizedPath}");
var (compilation, assembly) = CompilationHelper.CreateCompilationFromAssembly(assemblyFile.NormalizedPath, config.References);
var (compilation, assembly) = CompilationHelper.CreateCompilationFromAssembly(assemblyFile.NormalizedPath, metadataReferences);
hasCompilationError |= compilation.CheckDiagnostics(config.AllowCompilationErrors);
assemblies.Add((assembly, compilation));
}
Expand Down
10 changes: 10 additions & 0 deletions test/Docfx.Build.Tests/TestData/xrefmap.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"references": [
{
"fullName": "str",
"href": "https://docs.python.org/3.5/library/stdtypes.html#str",
"name": "str",
"uid": "str"
}
]
}
15 changes: 15 additions & 0 deletions test/Docfx.Build.Tests/XRefMapDownloaderTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Net;
using FluentAssertions;
using Xunit;

namespace Docfx.Build.Engine.Tests;
Expand Down Expand Up @@ -35,4 +36,18 @@ public async Task ReadLocalXRefMapWithFallback()
Assert.NotNull(xrefSpec);
Assert.Equal("https://docs.python.org/3.5/library/stdtypes.html#str", xrefSpec.Href);
}

[Fact]
public async Task ReadLocalXRefMapJsonFileTest()
{
// Arrange
var path = Path.Combine(Directory.GetCurrentDirectory(), "TestData", "xrefmap.json");

XRefMapDownloader downloader = new XRefMapDownloader();
var xrefMap = await downloader.DownloadAsync(new Uri(path)) as XRefMap;

// Assert
xrefMap.Should().NotBeNull();
xrefMap.References.Should().HaveCount(1);
}
}
74 changes: 74 additions & 0 deletions test/Docfx.Build.Tests/XRefMapSerializationTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Text;
using Docfx.Common;
using Docfx.Plugins;
using FluentAssertions;
using Xunit;
using Xunit.Abstractions;

namespace Docfx.Build.Engine.Tests;

public class XRefMapSerializationTest
{
[Fact]
public void XRefMapSerializationRoundTripTest()
{
var model = new XRefMap
{
BaseUrl = "http://localhost",
Sorted = true,
HrefUpdated = null,
Redirections = new List<XRefMapRedirection>
{
new XRefMapRedirection
{
Href = "Dummy",
UidPrefix = "Dummy"
},
},
References = new List<XRefSpec>
{
new XRefSpec(new Dictionary<string,object>
{
["Additional1"] = "Dummy",
})
{
Uid = "Dummy",
Name = "Dummy",
Href = "Dummy",
CommentId ="Dummy",
IsSpec = true,
},
},
Others = new Dictionary<string, object>
{
["Other1"] = "Dummy",
}
};

// Arrange
var jsonResult = RoundtripByNewtonsoftJson(model);
var yamlResult = RoundtripWithYamlDotNet(model);

// Assert
jsonResult.Should().BeEquivalentTo(model);
yamlResult.Should().BeEquivalentTo(model);
}

private static T RoundtripByNewtonsoftJson<T>(T model)
{
var json = JsonUtility.Serialize(model);
return JsonUtility.Deserialize<T>(new StringReader(json));
}

private static T RoundtripWithYamlDotNet<T>(T model)
{
var sb = new StringBuilder();
using var sw = new StringWriter(sb);
YamlUtility.Serialize(sw, model);
var json = sb.ToString();
return YamlUtility.Deserialize<T>(new StringReader(json));
}
}
30 changes: 28 additions & 2 deletions test/Docfx.Dotnet.Tests/GenerateMetadataFromCSUnitTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ public class GenerateMetadataFromCSUnitTest
{
private static readonly Dictionary<string, string> EmptyMSBuildProperties = new();

private static MetadataItem Verify(string code, ExtractMetadataConfig config = null, IDictionary<string, string> msbuildProperties = null)
private static MetadataItem Verify(string code, ExtractMetadataConfig config = null, IDictionary<string, string> msbuildProperties = null, MetadataReference[] references = null)
{
var compilation = CompilationHelper.CreateCompilationFromCSharpCode(code, msbuildProperties ?? EmptyMSBuildProperties, "test.dll");
var compilation = CompilationHelper.CreateCompilationFromCSharpCode(code, msbuildProperties ?? EmptyMSBuildProperties, "test.dll", references);
var extensionMethods = compilation.Assembly.FindExtensionMethods(new(new(), new())).ToArray();
return compilation.Assembly.GenerateMetadataItem(compilation, config, extensionMethods: extensionMethods);
}
Expand Down Expand Up @@ -3771,4 +3771,30 @@ public void F1() {}
Assert.Empty(foo.Items);
}
}

[Fact]
public void TestGenerateMetadataWithReference()
{
string code = @"
namespace Test
{
public class Foo
{
public TupleLibrary.XmlTasks Tasks{ get;set; }
}
}
";
var references = new string[] { "TestData/TupleLibrary.dll" }.Select(assemblyPath =>
{
var documentation = XmlDocumentationProvider.CreateFromFile(Path.ChangeExtension(assemblyPath, ".xml"));
return MetadataReference.CreateFromFile(assemblyPath, documentation: documentation);
}).ToArray();

// Act
var output = Verify(code, references: references);

// Assert
Assert.Contains("TupleLibrary", output.References.Keys);
Assert.Contains("TupleLibrary.XmlTasks", output.References.Keys);
}
}
Loading

0 comments on commit f25547f

Please sign in to comment.