Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions documentation/general/decouple-vs-and-net-sdk.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,11 @@ To mitigate this we will be fixing the `build-server shutdown` command to be rel
Based on the value of the `RoslynCompilerType` property, the SDK sets property `RoslynTasksAssembly` to a full path to a [Roslyn build task DLL][roslyn-build-task],
and the SDK targets use `$(RoslynTasksAssembly)` to load the build task.

The SDK also sets `RoslynTargetsPath` to the directory path of the roslyn tasks assembly. This property is used by some targets
but it should be avoided if possible because the tasks assembly name can change as well, not just the directory path.
The SDK also sets the following properties:
- `RoslynTargetsPath` to the directory path of the roslyn tasks assembly. This property is used by some targets
but it should be avoided if possible because the tasks assembly name can change as well, not just the directory path.
- `RoslynAssembliesPath` to the directory path of other roslyn assemblies (like `Microsoft.CodeAnalysis.dll`).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ericstj this value can point to assemblies that target net472 or .NET core. Will the API compat tool be okay with both of these?

Copy link
Member

@ericstj ericstj Aug 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would expect it to point to the correct framework for the MSBuild host process. So if MSBuild is running as netfx, point to netfx assemblies. If MSBuild is running as core, point to core assemblies.

If you think there is a scenario for cross-framework execution (IE: if MSBuild might allow a core-taskhost in netfx build process the future) then we should also have specific properties that always point at core and netfx.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently the property should point to the matching TFM assemblies. I can clarify the docs.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting. Why did you pick this? My intuition is that RoslynAssembliesPath would point to the assemblies that would be used by the build task.

Copy link
Member Author

@jjonescz jjonescz Aug 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I picked it so that ApiCompat (currently the only consumer of RoslynAssembliesPath) works. And I would expect other consumers behave similarly in this regard - it's not possible to load netcore MS.CA.dll from custom netfx build task; our bridge compiler build task can work only because it starts an executable; if it loaded roslyn as a library it wouldn't work either. And I assume people will use RoslynAssembliesPath for the library DLLs, not the executables.

In builds using .NET Framework MSBuild, the path is set to the Roslyn directory that ships with MSBuild (no .NET Framework Roslyn assemblies ship with the .NET SDK).

These values are recognized for property `RoslynCompilerType`:
- `Core`: use the compiler that comes with the .NET SDK
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -336,9 +336,15 @@ Copyright (c) .NET Foundation. All rights reserved.
</Otherwise>
</Choose>

Copy link
Member

@ericstj ericstj Aug 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we set RoslynAssembliesPath when RoslynCompilerType is not Core as well? That way we establish this as a general contract provided by roslyn/SDK. It's possible that others may wish to use that property.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely, that was my intention, thanks.

<!-- NOTE: Compiler toolset packages should set the following properties on their own, hence we set them only for RoslynCompilerType being Core or Framework. -->
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 I opened a follow-up in roslyn to set RoslynAssembliesPath by the toolset packages as well: dotnet/roslyn#80025

<PropertyGroup Condition="'$(RoslynCompilerType)' == 'Core' or '$(RoslynCompilerType)' == 'Framework'">
<!-- In Full Framework MSBuild, RoslynTargetsPath originally points to the directory with Full Framework Roslyn assemblies. -->
<RoslynAssembliesPath Condition="'$(RoslynAssembliesPath)' == '' and '$(MSBuildRuntimeType)' != 'Core'">$(RoslynTargetsPath)</RoslynAssembliesPath>
</PropertyGroup>
<PropertyGroup Condition="'$(RoslynCompilerType)' == 'Core'">
<RoslynTargetsPath Condition="'$(MSBuildRuntimeType)' == 'Core'">$(MSBuildThisFileDirectory)..\..\..\Roslyn</RoslynTargetsPath>
<RoslynTasksAssembly Condition="'$(MSBuildRuntimeType)' == 'Core'">$(MSBuildThisFileDirectory)..\..\..\Roslyn\Microsoft.Build.Tasks.CodeAnalysis.dll</RoslynTasksAssembly>
<RoslynAssembliesPath Condition="'$(RoslynAssembliesPath)' == '' and '$(MSBuildRuntimeType)' == 'Core'">$(MSBuildThisFileDirectory)..\..\..\Roslyn\bincore</RoslynAssembliesPath>
<RoslynTargetsPath Condition="'$(MSBuildRuntimeType)' != 'Core'">$(MSBuildThisFileDirectory)..\..\..\Roslyn\binfx</RoslynTargetsPath>
<RoslynTasksAssembly Condition="'$(MSBuildRuntimeType)' != 'Core'">$(MSBuildThisFileDirectory)..\..\..\Roslyn\binfx\Microsoft.Build.Tasks.CodeAnalysis.Sdk.dll</RoslynTasksAssembly>
<CSharpCoreTargetsPath>$(MSBuildThisFileDirectory)..\..\..\Roslyn\Microsoft.CSharp.Core.targets</CSharpCoreTargetsPath>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public ValidatePackageTargetIntegrationTests(ITestOutputHelper log) : base(log)
return (log, validator);
}

[RequiresMSBuildVersionFact("17.0.0.32901", Skip = "https://github.com/dotnet/sdk/issues/23533")]
[RequiresMSBuildVersionFact("17.0.0.32901")]
public void InvalidPackage()
{
var testAsset = _testAssetsManager
Expand All @@ -46,10 +46,10 @@ public void InvalidPackage()

// No failures while running the package validation on a simple assembly.
Assert.Equal(1, result.ExitCode);
Assert.Contains("error CP0002: Member 'PackageValidationTestProject.Program.SomeAPINotIn6_0()' exists on lib/netstandard2.0/PackageValidationTestProject.dll but not on lib/net6.0/PackageValidationTestProject.dll", result.StdOut);
Assert.Contains("error CP0002: Member 'void PackageValidationTestProject.Program.SomeAPINotInCore()' exists on lib/netstandard2.0/PackageValidationTestProject.dll but not on lib/net8.0/PackageValidationTestProject.dll", result.StdOut);
}

[RequiresMSBuildVersionFact("17.0.0.32901", Skip = "https://github.com/dotnet/sdk/issues/23533")]
[RequiresMSBuildVersionFact("17.0.0.32901")]
public void ValidatePackageTargetRunsSuccessfully()
{
var testAsset = _testAssetsManager
Expand All @@ -63,7 +63,7 @@ public void ValidatePackageTargetRunsSuccessfully()
Assert.Equal(0, result.ExitCode);
}

[RequiresMSBuildVersionFact("17.0.0.32901", Skip = "https://github.com/dotnet/sdk/issues/23533")]
[RequiresMSBuildVersionFact("17.0.0.32901")]
public void ValidatePackageTargetRunsSuccessfullyWithBaselineCheck()
{
var testAsset = _testAssetsManager
Expand All @@ -83,7 +83,7 @@ public void ValidatePackageTargetRunsSuccessfullyWithBaselineCheck()
Assert.Equal(0, result.ExitCode);
}

[RequiresMSBuildVersionFact("17.0.0.32901", Skip = "https://github.com/dotnet/sdk/issues/23533")]
[RequiresMSBuildVersionFact("17.0.0.32901")]
public void ValidatePackageTargetRunsSuccessfullyWithBaselineVersion()
{
var testAsset = _testAssetsManager
Expand All @@ -102,7 +102,7 @@ public void ValidatePackageTargetRunsSuccessfullyWithBaselineVersion()
Assert.Equal(0, result.ExitCode);
}

[RequiresMSBuildVersionFact("17.0.0.32901", Skip = "https://github.com/dotnet/sdk/issues/23533")]
[RequiresMSBuildVersionFact("17.0.0.32901")]
public void ValidatePackageTargetFailsWithBaselineVersion()
{
var testAsset = _testAssetsManager
Expand All @@ -119,11 +119,11 @@ public void ValidatePackageTargetFailsWithBaselineVersion()
.Execute($"-p:PackageVersion=2.0.0;AddBreakingChange=true;PackageValidationBaselinePath={packageValidationBaselinePath}");

Assert.Equal(1, result.ExitCode);
Assert.Contains("error CP0002: Member 'PackageValidationTestProject.Program.SomeApiNotInLatestVersion()' exists on [Baseline] lib/net6.0/PackageValidationTestProject.dll but not on lib/net6.0/PackageValidationTestProject.dll", result.StdOut);
Assert.Contains("error CP0002: Member 'PackageValidationTestProject.Program.SomeApiNotInLatestVersion()' exists on [Baseline] lib/netstandard2.0/PackageValidationTestProject.dll but not on lib/netstandard2.0/PackageValidationTestProject.dll", result.StdOut);
Assert.Contains("error CP0002: Member 'void PackageValidationTestProject.Program.SomeApiNotInLatestVersion()' exists on [Baseline] lib/net8.0/PackageValidationTestProject.dll but not on lib/net8.0/PackageValidationTestProject.dll", result.StdOut);
Assert.Contains("error CP0002: Member 'void PackageValidationTestProject.Program.SomeApiNotInLatestVersion()' exists on [Baseline] lib/netstandard2.0/PackageValidationTestProject.dll but not on lib/netstandard2.0/PackageValidationTestProject.dll", result.StdOut);
}

[RequiresMSBuildVersionFact("17.0.0.32901", Skip = "https://github.com/dotnet/sdk/issues/23533")]
[RequiresMSBuildVersionFact("17.0.0.32901")]
public void ValidatePackageTargetWithIncorrectBaselinePackagePath()
{
var testAsset = _testAssetsManager
Expand Down Expand Up @@ -291,7 +291,7 @@ public void ValidateMissingReferencesIsOnlyLoggedWhenRunningWithReferences(bool
Assert.Contains(log.warnings, e => e.Contains("CP1003"));
}

[RequiresMSBuildVersionFact("17.0.0.32901", Skip = "https://github.com/dotnet/sdk/issues/23533")]
[RequiresMSBuildVersionFact("17.0.0.32901")]
public void ValidateReferencesAreRespectedForPlatformSpecificTFMs()
{
TestProject testProject = CreateTestProject("public class MyType { }", $"netstandard2.0;{ToolsetInfo.CurrentTargetFramework}-windows");
Expand All @@ -310,10 +310,10 @@ public void ValidateReferencesAreRespectedForPlatformSpecificTFMs()

validator.Validate(new PackageValidatorOption(package));

Assert.Contains(log.warnings, e => e.Contains("CP1003"));
Assert.DoesNotContain(log.warnings, e => e.Contains("CP1003"));
}

[RequiresMSBuildVersionFact("17.0.0.32901", Skip = "https://github.com/dotnet/sdk/issues/23533")]
[RequiresMSBuildVersionFact("17.0.0.32901")]
public void ValidatePackageTargetFailsWithBaselineVersionInStrictMode()
{
var testAsset = _testAssetsManager
Expand All @@ -330,11 +330,11 @@ public void ValidatePackageTargetFailsWithBaselineVersionInStrictMode()
.Execute($"-p:PackageVersion=2.0.0;ForceStrictModeBaselineValidationProblem=true;EnableStrictModeForBaselineValidation=true;PackageValidationBaselinePath={packageValidationBaselinePath}");

Assert.Equal(1, result.ExitCode);
Assert.Contains("error CP0002: Member 'PackageValidationTestProject.Program.SomeApiOnlyInLatestVersion()' exists on lib/net6.0/PackageValidationTestProject.dll but not on [Baseline] lib/net6.0/PackageValidationTestProject.dll", result.StdOut);
Assert.Contains("error CP0002: Member 'PackageValidationTestProject.Program.SomeApiOnlyInLatestVersion()' exists on lib/netstandard2.0/PackageValidationTestProject.dll but not on [Baseline] lib/netstandard2.0/PackageValidationTestProject.dll", result.StdOut);
Assert.Contains("error CP0002: Member 'void PackageValidationTestProject.Program.SomeApiOnlyInLatestVersion()' exists on lib/net8.0/PackageValidationTestProject.dll but not on [Baseline] lib/net8.0/PackageValidationTestProject.dll", result.StdOut);
Assert.Contains("error CP0002: Member 'void PackageValidationTestProject.Program.SomeApiOnlyInLatestVersion()' exists on lib/netstandard2.0/PackageValidationTestProject.dll but not on [Baseline] lib/netstandard2.0/PackageValidationTestProject.dll", result.StdOut);
}

[RequiresMSBuildVersionFact("17.0.0.32901", Skip = "https://github.com/dotnet/sdk/issues/23533")]
[RequiresMSBuildVersionFact("17.0.0.32901")]
public void ValidatePackageTargetSucceedsWithBaselineVersionNotInStrictMode()
{
var testAsset = _testAssetsManager
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,4 @@
<EnablePackageValidation>true</EnablePackageValidation>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="$(RepoRoot)src\Compatibility\ApiCompat\Microsoft.DotNet.ApiCompat.Task\Microsoft.DotNet.ApiCompat.Task.csproj" />
<PackageReference Include="NuGet.Frameworks" Version="6.0.0-preview.1.66" />
<PackageReference Include="NuGet.Packaging" Version="6.0.0-preview.1.66" />
<PackageReference Include="Microsoft.Bcl.HashCode" Version="1.1.1" />
<PackageReference Include="Microsoft.CodeAnalysis" Version="4.0.0-1.21277.15" />
</ItemGroup>

<Import Project="$(RepoRoot)src\Tasks\Microsoft.NET.Build.Tasks\targets\Microsoft.NET.ApiCompat.targets" />

<PropertyGroup>
<DotNetPackageValidationTaskAssembly Condition="'$(MSBuildRuntimeType)' != 'Core'">$(MSBuildThisFileDirectory)\bin\$(Configuration)\net472\Microsoft.DotNet.ApiCompat.Task.dll</DotNetPackageValidationTaskAssembly>
<DotNetPackageValidationTaskAssembly Condition="'$(MSBuildRuntimeType)' == 'Core'">$(MSBuildThisFileDirectory)\bin\$(Configuration)\net8.0\Microsoft.DotNet.ApiCompat.Task.dll</DotNetPackageValidationTaskAssembly>
</PropertyGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ namespace PackageValidationTestProject
{
public class Program
{
#if ForceValidationProblem && !NET6_0
public void SomeAPINotIn6_0()
#if ForceValidationProblem && !NET
public void SomeAPINotInCore()
{
}
#endif
Expand Down