Skip to content

CopyRefAssembly Could not extract the MVID from reference assembly #13216

@nojaf

Description

@nojaf

Following up on #12334, I've noticed that a project B (csproj) cannot consume a reference assembly of a project A (fsproj) because the mvid cannot be read from the assembly.

Project structure:

A.fsproj

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net7.0</TargetFramework>
    <GenerateDocumentationFile>true</GenerateDocumentationFile>
    <DotnetFscCompilerPath>C:\Users\nojaf\Projects\fsharp\artifacts\bin\fsc\Debug\net6.0\fsc.dll</DotnetFscCompilerPath>
    <OtherFlags>--refout:obj\Debug\net7.0\refint\A.dll</OtherFlags>
    <ProduceReferenceAssembly>true</ProduceReferenceAssembly>
  </PropertyGroup>

  <ItemGroup>
    <Compile Include="Library.fs" />
  </ItemGroup>

</Project>

B.csproj

<Project Sdk="Microsoft.NET.Sdk">

  <ItemGroup>
    <ProjectReference Include="..\A\A.fsproj" />
    
  </ItemGroup>

  <PropertyGroup>
    <TargetFramework>net7.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <ProduceReferenceAssembly>true</ProduceReferenceAssembly>
  </PropertyGroup>

</Project>

Expected behavior

dotnet build of B.csproj will find the reference assembly of project A that was specified using the refout flag.

Actual behavior

In the MSBuild log we saw the following:

Could not extract the MVID from "obj\Debug\net7.0\refint\A.dll". Are you sure it is a reference assembly?

This warning was logged by CopyRefAssembly,

https://github.com/dotnet/roslyn/blob/b7838e90db7c7f1de309f8ef314e0aa8bfc6dab1/src/Compilers/Core/MSBuildTask/CopyRefAssembly.cs#L56

The main problem is that the mvid guid is empty.

The current Number of Sections inside the COFF Header is 3, whereas compared to a C# reference assembly this is 4. The .mvid section is missing from the F# generation.

The MSBuild task uses this section to read that guid.

https://github.com/dotnet/roslyn/blob/b7838e90db7c7f1de309f8ef314e0aa8bfc6dab1/src/Compilers/Core/MSBuildTask/MvidReader.cs#L90-L137

This entire section could be added around

let numSections =
if hasEntryPointStub then 3 // .text, .sdata, .reloc
else 2 // .text, .sdata

Similar to .text, .sdata, .reloc I would assume. Conditional logic could be implemented using options.referenceAssemblyOnly.

Known workarounds

Besides adding the additional section, it is possible to extract the guid currently from the Module metadata table.
We were able to read it using:

#r "nuget: System.Reflection.Metadata"

open System.IO
open System.Reflection.PortableExecutable

let refDll = @"C:\Users\nojaf\Projects\reference-assemblies-sample\A\obj\Debug\net7.0\refint\A.dll"
let embeddedReader = new PEReader(File.OpenRead refDll)
let sourceReader = embeddedReader.GetMetadataReader()
let loc = sourceReader.GetModuleDefinition().Mvid
let mvid = sourceReader.GetGuid(loc)

So, from a certain point of view, the mvid is there. Just not in the way that CopyRefAssembly reads it.

Related information

Provide any related information (optional):

  • Operating system: Win11
  • .NET Runtime kind: .NET 7 preview 4
  • local compiler, main at 5b1a3ae

//cc @dsyme @vzarytovskii @baronfel @jaredpar

Metadata

Metadata

Assignees

No one assigned

    Labels

    Area-BuildEverything related to building F# compiler, tooling and VS extension.Feature Improvement

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions