Roslyn 似乎忽略了一些元数据引用
Roslyn seems to ignore some metadata references
在一些集成测试的准备阶段,我必须动态生成一些引用其他程序集的程序集并将它们刷新到磁盘。我猜这个任务的明显选择是 Roslyn。
Roslyn 编译成功完成并将发出的程序集保存到磁盘。当我使用 ILSPy 检查结果时,我发现某些程序集引用未包含在内。
虚拟class生成代码:
public static string GenerateEmptyPublicClass([NotNull] string @namespace, [NotNull] string className)
{
if (@namespace == null) throw new ArgumentNullException(nameof(@namespace));
if (className == null) throw new ArgumentNullException(nameof(className));
var classDeclaration = SyntaxFactory.ClassDeclaration(className).AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword));
var namespaceDeclaration = SyntaxFactory.NamespaceDeclaration(SyntaxFactory.ParseName(@namespace)).NormalizeWhitespace();
namespaceDeclaration = namespaceDeclaration.AddMembers(classDeclaration);
return namespaceDeclaration.NormalizeWhitespace().ToFullString();
}
汇编准备代码:
blic static void GenerateAssembly([NotNull] this string sourceCode, [NotNull] string assemblyFilePath,
[NotNull] params string[] referencedAssemblyPaths)
{
if (sourceCode == null) throw new ArgumentNullException(nameof(sourceCode));
if (assemblyFilePath == null) throw new ArgumentNullException(nameof(assemblyFilePath));
var assemblyFileName = Path.GetFileName(assemblyFilePath);
var outputDirectory = Path.GetDirectoryName(assemblyFilePath);
Directory.CreateDirectory(outputDirectory);
var syntaxTree = CSharpSyntaxTree.ParseText(sourceCode);
var referencedAssemblyMetadata =
referencedAssemblyPaths.Select(x => MetadataReference.CreateFromFile(x).WithProperties(new MetadataReferenceProperties()));
var compilationOptions = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary);
var compilation = CSharpCompilation.Create(assemblyFileName, new[] {syntaxTree}, referencedAssemblyMetadata, compilationOptions);
using (var fs = File.Create(assemblyFilePath))
{
var emitResult = compilation.Emit(fs);
if (!emitResult.Success)
{
var failures = emitResult.Diagnostics.Where(x => x.IsWarningAsError || x.Severity == DiagnosticSeverity.Error);
var errorReport = failures.Select(x => $"{x.Id}: {x.GetMessage()}, {x.Location}");
throw new InvalidOperationException($"Failed to compile source code {sourceCode}. Report: {errorReport}");
}
fs.Flush();
}
}
为简单起见,我想生成两个程序集:
- B 仅依赖于 netstandard.dll
- A 同时引用了 netstandard.dll 和 B
代码如下:
var emptyClassSourceCode = RoslynAssemblyGenerator.GenerateEmptyPublicClass("DummyNamespace", "DummyClass");
var standardAssemblyLocation = Path.Combine(Path.GetDirectoryName(Common.ExecutingAssemblyFullPath), "Resources", "netstandard.dll");
// A references B
var aPath = Path.Combine(AssemblyGenerationPath, "A.dll");
var bPath = Path.Combine(AssemblyGenerationPath, "B.dll");
emptyClassSourceCode.GenerateAssembly(bPath, standardAssemblyLocation);
emptyClassSourceCode.GenerateAssembly(aPath, bPath, standardAssemblyLocation);
B 按预期生成,但 A 未引用 B:
想不通我错过了什么,为什么 B 没有被 A 引用。
正如 PetSerAl 在评论中提到的,为了引用程序集,我们不仅需要指明程序集位置,还需要实际使用它的元数据。
在一些集成测试的准备阶段,我必须动态生成一些引用其他程序集的程序集并将它们刷新到磁盘。我猜这个任务的明显选择是 Roslyn。 Roslyn 编译成功完成并将发出的程序集保存到磁盘。当我使用 ILSPy 检查结果时,我发现某些程序集引用未包含在内。
虚拟class生成代码:
public static string GenerateEmptyPublicClass([NotNull] string @namespace, [NotNull] string className)
{
if (@namespace == null) throw new ArgumentNullException(nameof(@namespace));
if (className == null) throw new ArgumentNullException(nameof(className));
var classDeclaration = SyntaxFactory.ClassDeclaration(className).AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword));
var namespaceDeclaration = SyntaxFactory.NamespaceDeclaration(SyntaxFactory.ParseName(@namespace)).NormalizeWhitespace();
namespaceDeclaration = namespaceDeclaration.AddMembers(classDeclaration);
return namespaceDeclaration.NormalizeWhitespace().ToFullString();
}
汇编准备代码:
blic static void GenerateAssembly([NotNull] this string sourceCode, [NotNull] string assemblyFilePath,
[NotNull] params string[] referencedAssemblyPaths)
{
if (sourceCode == null) throw new ArgumentNullException(nameof(sourceCode));
if (assemblyFilePath == null) throw new ArgumentNullException(nameof(assemblyFilePath));
var assemblyFileName = Path.GetFileName(assemblyFilePath);
var outputDirectory = Path.GetDirectoryName(assemblyFilePath);
Directory.CreateDirectory(outputDirectory);
var syntaxTree = CSharpSyntaxTree.ParseText(sourceCode);
var referencedAssemblyMetadata =
referencedAssemblyPaths.Select(x => MetadataReference.CreateFromFile(x).WithProperties(new MetadataReferenceProperties()));
var compilationOptions = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary);
var compilation = CSharpCompilation.Create(assemblyFileName, new[] {syntaxTree}, referencedAssemblyMetadata, compilationOptions);
using (var fs = File.Create(assemblyFilePath))
{
var emitResult = compilation.Emit(fs);
if (!emitResult.Success)
{
var failures = emitResult.Diagnostics.Where(x => x.IsWarningAsError || x.Severity == DiagnosticSeverity.Error);
var errorReport = failures.Select(x => $"{x.Id}: {x.GetMessage()}, {x.Location}");
throw new InvalidOperationException($"Failed to compile source code {sourceCode}. Report: {errorReport}");
}
fs.Flush();
}
}
为简单起见,我想生成两个程序集:
- B 仅依赖于 netstandard.dll
- A 同时引用了 netstandard.dll 和 B
代码如下:
var emptyClassSourceCode = RoslynAssemblyGenerator.GenerateEmptyPublicClass("DummyNamespace", "DummyClass");
var standardAssemblyLocation = Path.Combine(Path.GetDirectoryName(Common.ExecutingAssemblyFullPath), "Resources", "netstandard.dll");
// A references B
var aPath = Path.Combine(AssemblyGenerationPath, "A.dll");
var bPath = Path.Combine(AssemblyGenerationPath, "B.dll");
emptyClassSourceCode.GenerateAssembly(bPath, standardAssemblyLocation);
emptyClassSourceCode.GenerateAssembly(aPath, bPath, standardAssemblyLocation);
B 按预期生成,但 A 未引用 B:
想不通我错过了什么,为什么 B 没有被 A 引用。
正如 PetSerAl 在评论中提到的,为了引用程序集,我们不仅需要指明程序集位置,还需要实际使用它的元数据。