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 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.
("*/cs/*" or not "*/vb/*")
and VB matches ("*/vb/*" or not "*/cs/*")
?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?
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.