Home: project.assets.json should indicate "analyzer" assets like "compile" and "runtime"

Created on 8 Dec 2017  路  20Comments  路  Source: NuGet/Home

project.assets.json doesn't actually indicate which analyzer references a project should use.

Unlike "compile" and "runtime", the SDK is just pattern matching on all files in the package to find analyzers: https://github.com/dotnet/sdk/blob/634680ab0ae045ba45977b6293b46f95a0385aac/src/Tasks/Microsoft.NET.Build.Tasks/ResolvePackageDependencies.cs#L251

"analyzer" assets should work like "compile" and "runtime" and the SDK should resolve them the same way. The current mechanism blocks respecting PrivateAssets and ExcludeAssets=analyzers

cc @emgarten

Analyzers Epic Restore Backlog 2 PackageReference Feature

Most helpful comment

@levhaikin PrivateAssets=all only applies if I am the one with a direct dependency on an analyzer package. And yes, that works.

But if I'm referencing a package that should propagate to my consumers, but that package itself has an analyzer dependency, I should be able to suppress that using ExcludeAssets="analyzers", but it doesn't work.

All 20 comments

Analyzers were added without work being done on the NuGet side originally. We should add it to project.assets.json so that the SDK can switch over to using that instead of reading packages directly to find the analyzers folder.

Related to this, is there a spec for how analyzer assets should be selected based on project language?

Current implementations are here:
https://github.com/NuGet/NuGet.BuildTasks/blob/1d8af3499f94a32c1d128a42faceade39c1f4337/src/Microsoft.NuGet.Build.Tasks/ResolveNuGetPackageAssets.cs#L444-L470
https://github.com/dotnet/sdk/blob/634680ab0ae045ba45977b6293b46f95a0385aac/src/Tasks/Microsoft.NET.Build.Tasks/ResolvePackageDependencies.cs#L251-L272

I believe they are semantically equivalent, but both have some strange behavior if I'm reading correctly.

  • Leading "analyzers" is matched without separator

    • So "analyzersBanana/myanalyzer.dll" would match

  • Leading "analyzers" is case-sensitive.

    • Is that how the others like "lib" and "ref" are matched too?

  • C# projects matches ("*/cs/*" or not "*/vb/*") and VB matches ("*/vb/*" or not "*/cs/*")?

    • This seems to ignore the possibility of other languages gaining analyzers. e.g. If there were F# analyzers, they would match VB and C# projects.

  • Looking for language in any path component seems like a hammer vs how other nuget assets are matched.

cc @jinujoseph @natidea @jasonmalinowski

Adding @jmarolf too.

This seems to ignores the possibility of other languages gaining analyzers. e.g. If there were F# analyzers, they would match VB and C# projects.

I think that was just the code trying to be tricky and deal with the "no language either" thing at the same time.

cc @davidfowl

This seems pretty broken. It means that analyzers don't respect the PrivateAssets or flags specified on the PacakgeReference (I see a few people suffering from this already today).

Yes, it is pretty broken. See two linked sdk bugs that are blocked on this.

I have to run this powershell on my builds as a workaround using the parameters -path $(Build.SourcesDirectory) -packageName $(Build.DefinitionName)

Param(
   [string]$path,
   [string]$packageName
)

Add-Type -Assembly System.IO.Compression.FileSystem

Function Remove-RoslynAnalyzers([string] $filePath, [string] $projectName)
{
    if ((Test-Path -Path $filePath) -eq $false)
    {
        Write-Host "$filePath was not found"

        return
    }

    Write-Host "Scanning $filePath"

    $ZipStream = [System.IO.Compression.ZipFile]::Open("$filePath", 'Update')
    $ZipItem = $ZipStream.GetEntry("$projectName.nuspec")

    if ($ZipItem -eq $null)
    {
        Write-Host "$projectName.nuspec was not found in the zip"

        return
    }

    [xml]$nuspec = $null
    $reader = $null

    $reader = New-Object System.IO.StreamReader($ZipItem.Open())

    try
    {
        [xml]$nuspec = $reader.ReadToEnd()
    }
    finally
    {
        if ($null -ne $reader) 
        { 
            $reader.Close() 
            $reader.Dispose() 
        }
    }

    Clean-NuSpec $nuspec

    # Re-open the file this time with streamwriter
    $writer = [System.IO.StreamWriter]($ZipItem).Open()

    try
    {
        # If needed, zero out the file -- in case the new file is shorter than the old one
        $writer.BaseStream.SetLength(0)

        # Insert the $text to the file and close
        $writer.Write($nuspec.OuterXml)
    }
    finally
    {
        $writer.Flush()
        $writer.Close()
    }

    # Write the changes and close the zip file
    $ZipStream.Dispose()
}

Function Clean-NuSpec([xml]$xml)
{
    $namespace = "http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd"

    [System.Xml.XmlNamespaceManager] $nsMgr = New-Object System.Xml.XmlNamespaceManager($nuspec.NameTable)
    $nsMgr.AddNamespace("ns", "http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd");

    Remove-Package $nuspec "CodeCracker.CSharp" $nsMgr

    [string]$analyzersPath = "//ns:package/ns:metadata/ns:dependencies/ns:group/ns:dependency[contains(@id,'.Analyzers')]"

    $nodes = $nuspec.SelectNodes($analyzersPath, $nsMgr)

    foreach ($node in $nodes)
    {
        [string]$id = $node.Attributes["id"].Value

        Write-Host "Removing $id"

        $node.ParentNode.RemoveChild($node)
    }
}

Function Remove-Package([xml]$xml, [string] $name, [System.Xml.XmlNamespaceManager] $nsMgr)
{
    [string]$path = "//ns:package/ns:metadata/ns:dependencies/ns:group/ns:dependency[@id='$name']"

    $nodes = $xml.SelectNodes($path, $nsMgr)

    foreach ($node in $nodes)
    {
        [string]$id = $node.Attributes["id"].Value

        Write-Host "Removing $id"

        $node.ParentNode.RemoveChild($node)
    }
}

$items = Get-ChildItem $path -Recurse -Filter "$packageName.*.nupkg"

foreach ($item in $items)
{
     Remove-RoslynAnalyzers $item.FullName $packageName | out-null
}

@rrelyea I see this is both a pri 0 and on the backlog. Any chance we can re-triage this?

See also https://github.com/dotnet/sdk/issues/968#issuecomment-410319090 that notes that PrivateAssets="all" can prevent analyzers from flowing, if you can afford to prevent all other package assets from flowing.

Is there a workaround for the problem we're seeing in #7426? Specifically, we want to suppress chaining in analyzers as a transitive dependency through our package.

I just tried adding ExcludeAssets="analyzers" to my PackageReference and it _still_ didn't exclude the analyzers. Is there no way to turn off analyzers brought in by a transitive dependency?

have you tried <PrivateAssets>all</PrivateAssets>?

e.g.:
<ItemGroup> <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.6.3" > <PrivateAssets>all</PrivateAssets> </PackageReference> <PackageReference Include="StyleCop.Analyzers" Version="1.1.1-beta.61" > <PrivateAssets>all</PrivateAssets> </PackageReference> </ItemGroup>

@levhaikin PrivateAssets=all only applies if I am the one with a direct dependency on an analyzer package. And yes, that works.

But if I'm referencing a package that should propagate to my consumers, but that package itself has an analyzer dependency, I should be able to suppress that using ExcludeAssets="analyzers", but it doesn't work.

I see.

Any news on this?

I'm just here posting my interest in seeing this bug resolved. It's BONKERS. (finally, a proper scenario in which to deploy that word).

The analyzers are like a virus.

Everything we do is Async. We shouldn't be forced into adding "Async" onto every method name on 100+ projects in a solution just because we use a nuget package with threading analyzers (StreamJsonRpc in this case).

We are also hitting this in corefx now. Seems like this issue is stale?

Here I get the same issue, any update on this?

This is a confounding issue to encounter where everything the docs say to do has no effect and eventually after asking experts on twitter and nothing working either, you end up finding this bug. Now I'm considering republishing a package that I don't own so that I can get rid of the transitive analyzer OR creating custom MSBuild tasks to forcibly rip analyzers out prior to compiling. Send help.

Seeing a spike on reports on this. Can we add it to an agenda in a weekly sync in the new year and see if we can get this on the roadmap?

Was this page helpful?
0 / 5 - 0 ratings