Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
58aeab6
Add telemetry logger support for API-based MSBuild usage
Copilot Sep 30, 2025
0b8fb6f
Add tests for ProjectInstanceExtensions telemetry logger creation
Copilot Sep 30, 2025
c37c61a
Fix telemetry logger to use distributed logging pattern with central …
Copilot Sep 30, 2025
1dc7f54
Fix ForwardingLoggerRecord to use central logger instance with forwar…
Copilot Sep 30, 2025
c201734
Share telemetry central logger instance between ProjectCollection and…
Copilot Sep 30, 2025
6d409d6
Add integration test for telemetry logger receiving events from API-b…
Copilot Sep 30, 2025
51f1399
Add telemetry logger support to VirtualProjectBuildingCommand
Copilot Oct 1, 2025
9343a69
Add telemetry logger support to PackageAddCommand for project evaluation
Copilot Oct 1, 2025
65f60f0
Add BannedApiAnalyzer to prevent direct MSBuild API usage without tel…
Copilot Nov 4, 2025
102371a
Scope BannedApiAnalyzer to src/Cli and fix all ProjectCollection viol…
Copilot Nov 4, 2025
3cd34a8
Use version property for BannedApiAnalyzers and update copilot-instru…
Copilot Nov 7, 2025
3093c60
Remove formatting-only changes to reduce PR noise
Copilot Nov 20, 2025
31e17b7
Restore BuildFinished event registration outside telemetry check
Copilot Nov 20, 2025
6eb6064
Revert accidental removal of functional code in TestCommand and telem…
Copilot Nov 20, 2025
243ad7f
react to changes post-rebase
baronfel Nov 21, 2025
151ef1a
Add unification plan document
baronfel Nov 23, 2025
bb53470
update local build instructions for copilot
baronfel Nov 23, 2025
ef5751b
big giant refactor towards more explicit types
baronfel Nov 24, 2025
5825147
Update some RunCommand infrastructure
baronfel Nov 25, 2025
6de6502
Tweak
baronfel Nov 25, 2025
ea3fc88
a lot more consolidation
baronfel Nov 25, 2025
ea27e91
finally plumb through centralizing on DotNetProject
baronfel Nov 29, 2025
4540b6a
update runcommand target framework selection to use evaluator
baronfel Nov 30, 2025
d3b9d3d
react to rebase
baronfel Dec 5, 2025
d2a96eb
prevent some duplicate loads when using the raw methods of the evaluator
baronfel Dec 5, 2025
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
Prev Previous commit
Next Next commit
a lot more consolidation
  • Loading branch information
baronfel committed Dec 5, 2025
commit ea3fc888cae23446892dcc21a0e911a766748b1f
8 changes: 5 additions & 3 deletions eng/Analyzers.props
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
<PackageReference Include="StyleCop.Analyzers" PrivateAssets="all" Condition="'$(EnableStyleCopAnalyzer)' == 'true'" />
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" PrivateAssets="all" Condition="$(MSBuildProjectDirectory.Contains('src\Cli')) OR $(MSBuildProjectDirectory.Contains('src/Cli'))" />
</ItemGroup>

<ItemGroup Condition="'$(DotNetBuildSourceOnly)' != 'true' AND ($(MSBuildProjectDirectory.Contains('src\Cli')) OR $(MSBuildProjectDirectory.Contains('src/Cli')))">

<ItemGroup Condition="'$(DotNetBuildSourceOnly)' != 'true'
AND ($(MSBuildProjectDirectory.Contains('src\Cli')) OR $(MSBuildProjectDirectory.Contains('src/Cli')))
AND (!$(MSBuildProjectDirectory.Contains('FileBasedPrograms')))">
<AdditionalFiles Include="$(MSBuildThisFileDirectory)../BannedSymbols.txt" />
</ItemGroup>
</Project>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace Microsoft.DotNet.Cli.Utils.Extensions;

internal static class MSBuildProjectExtensions
{
public static bool IsConditionalOnFramework(this ProjectElement el, string framework)
public static bool IsConditionalOnFramework(this ProjectElement el, string? framework)
{
if (!TryGetFrameworkConditionString(framework, out string? conditionStr))
{
Expand Down Expand Up @@ -72,12 +72,12 @@ public static bool IsUniformItemElementType(this ProjectItemGroupElement group,
return group.Items.All((it) => it.ItemType == projectItemElementType);
}

public static IEnumerable<ProjectItemElement> FindExistingItemsWithCondition(this ProjectRootElement root, string framework, string include)
public static IEnumerable<ProjectItemElement> FindExistingItemsWithCondition(this ProjectRootElement root, string? framework, string include)
{
return root.Items.Where((el) => el.IsConditionalOnFramework(framework) && el.HasInclude(include));
}

public static bool HasExistingItemWithCondition(this ProjectRootElement root, string framework, string include)
public static bool HasExistingItemWithCondition(this ProjectRootElement root, string? framework, string include)
{
return root.FindExistingItemsWithCondition(framework, include).Count() != 0;
}
Expand Down Expand Up @@ -113,7 +113,7 @@ private static IEnumerable<string> SplitSemicolonDelimitedValues(string combined
}


private static bool TryGetFrameworkConditionString(string framework, out string? condition)
private static bool TryGetFrameworkConditionString(string? framework, out string? condition)
{
if (string.IsNullOrEmpty(framework))
{
Expand Down
4 changes: 2 additions & 2 deletions src/Cli/dotnet/CliCompletion.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public static IEnumerable<CompletionItem> ProjectReferencesFromProjectFile(Compl
{
try
{
return GetMSBuildProject()?.GetProjectToProjectReferences().Select(r => ToCompletionItem(r.Include)) ?? Empty<CompletionItem>();
return GetMSBuildProject()?.ProjectReferences().Select(r => ToCompletionItem(r.EvaluatedInclude)) ?? Empty<CompletionItem>();
}
catch (Exception)
{
Expand All @@ -69,7 +69,7 @@ public static IEnumerable<CompletionItem> ConfigurationsFromProjectFileOrDefault
{
using var evaluator = DotNetProjectEvaluatorFactory.CreateForCommand();
return MsbuildProject.FromFileOrDirectory(
evaluator.ProjectCollection,
evaluator,
Directory.GetCurrentDirectory(), interactive: false);
}
catch (Exception e)
Expand Down
110 changes: 24 additions & 86 deletions src/Cli/dotnet/CommandFactory/CommandResolution/MSBuildProject.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#nullable disable

using Microsoft.Build.Evaluation;
using Microsoft.DotNet.Cli.MSBuildEvaluation;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Cli.Utils.Extensions;
using NuGet.Frameworks;
Expand All @@ -15,62 +13,20 @@ internal class MSBuildProject : IProject
{
private static readonly NuGetFramework s_toolPackageFramework = FrameworkConstants.CommonFrameworks.NetCoreApp10;

private readonly Project _project;
private readonly DotNetProject _project;
private readonly string _msBuildExePath;

public string DepsJsonPath
{
get
{
return _project
.AllEvaluatedProperties
.FirstOrDefault(p => p.Name.Equals("ProjectDepsFilePath"))
.EvaluatedValue;
}
}
public string? DepsJsonPath => _project.GetPropertyValue("ProjectDepsFilePath");

public string RuntimeConfigJsonPath
{
get
{
return _project
.AllEvaluatedProperties
.FirstOrDefault(p => p.Name.Equals("ProjectRuntimeConfigFilePath"))
.EvaluatedValue;
}
}
public string? RuntimeConfigJsonPath => _project.GetPropertyValue("ProjectRuntimeConfigFilePath");

public string FullOutputPath
{
get
{
return _project
.AllEvaluatedProperties
.FirstOrDefault(p => p.Name.Equals("TargetDir"))
.EvaluatedValue;
}
}
public string? FullOutputPath => _project.GetPropertyValue("TargetDir");

public string ProjectRoot { get; }

public NuGetFramework DotnetCliToolTargetFramework
{
get
{
var frameworkString = _project
.AllEvaluatedProperties
.FirstOrDefault(p => p.Name.Equals("DotnetCliToolTargetFramework"))
?.EvaluatedValue;

if (string.IsNullOrEmpty(frameworkString))
{
return s_toolPackageFramework;
}

return NuGetFramework.Parse(frameworkString);
}
}

public NuGetFramework DotnetCliToolTargetFramework => _project.GetPropertyValue("DotnetCliToolTargetFramework") is string s && !string.IsNullOrEmpty(s)
? NuGetFramework.Parse(s)
: s_toolPackageFramework;

public Dictionary<string, string> EnvironmentVariables
{
Expand All @@ -83,20 +39,10 @@ public Dictionary<string, string> EnvironmentVariables
}
}

public string ToolDepsJsonGeneratorProject
{
get
{
var generatorProject = _project
.AllEvaluatedProperties
.FirstOrDefault(p => p.Name.Equals("ToolDepsJsonGeneratorProject"))
?.EvaluatedValue;

return generatorProject;
}
}
public string? ToolDepsJsonGeneratorProject => _project.GetPropertyValue("ToolDepsJsonGeneratorProject");

public MSBuildProject(
internal MSBuildProject(
DotNetProjectEvaluator evaluator,
string msBuildProjectPath,
NuGetFramework framework,
string configuration,
Expand All @@ -107,7 +53,7 @@ public MSBuildProject(

var globalProperties = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
{ "MSBuildExtensionsPath", Path.GetDirectoryName(msBuildExePath) }
{ "MSBuildExtensionsPath", Path.GetDirectoryName(msBuildExePath)! }
};

if (framework != null)
Expand All @@ -125,19 +71,17 @@ public MSBuildProject(
globalProperties.Add("Configuration", configuration);
}

_project = ProjectCollection.GlobalProjectCollection.LoadProject(
_project = evaluator.LoadProject(
msBuildProjectPath,
globalProperties,
null);
globalProperties);

_msBuildExePath = msBuildExePath;
}

public IEnumerable<SingleProjectInfo> GetTools()
{
var toolsReferences = _project.AllEvaluatedItems.Where(i => i.ItemType.Equals("DotNetCliToolReference"));
var toolsReferences = _project.GetItems("DotNetCliToolReference");
var tools = toolsReferences.Select(t => new SingleProjectInfo(t.EvaluatedInclude, t.GetMetadataValue("Version"), []));

return tools;
}

Expand All @@ -147,11 +91,11 @@ public LockFile GetLockFile()
GetLockFilePathFromIntermediateBaseOutputPath();

return new LockFileFormat()
.ReadWithLock(lockFilePath)
.ReadWithLock(lockFilePath!)
.Result;
}

public bool TryGetLockFile(out LockFile lockFile)
public bool TryGetLockFile(out LockFile? lockFile)
{
lockFile = null;

Expand All @@ -174,21 +118,15 @@ public bool TryGetLockFile(out LockFile lockFile)
return true;
}

private string GetLockFilePathFromProjectLockFileProperty()
{
return _project
.AllEvaluatedProperties
.Where(p => p.Name.Equals("ProjectAssetsFile"))
.Select(p => p.EvaluatedValue)
.FirstOrDefault(p => Path.IsPathRooted(p) && File.Exists(p));
}
private string? GetLockFilePathFromProjectLockFileProperty() => _project.ProjectAssetsFile;

private string GetLockFilePathFromIntermediateBaseOutputPath()
private string? GetLockFilePathFromIntermediateBaseOutputPath()
{
var intermediateOutputPath = _project
.AllEvaluatedProperties
.FirstOrDefault(p => p.Name.Equals("BaseIntermediateOutputPath"))
.EvaluatedValue;
var intermediateOutputPath = _project.GetPropertyValue("BaseIntermediateOutputPath");
if (string.IsNullOrEmpty(intermediateOutputPath))
{
return null;
}
return Path.Combine(intermediateOutputPath, "project.assets.json");
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#nullable disable

using Microsoft.Build.Exceptions;
using Microsoft.DotNet.Cli.MSBuildEvaluation;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Cli.Utils.Extensions;
using NuGet.Frameworks;
Expand All @@ -16,7 +14,9 @@ internal class ProjectFactory(IEnvironmentProvider environment)

private readonly IEnvironmentProvider _environment = environment;

public IProject GetProject(
private readonly DotNetProjectEvaluator _evaluator = new();

public IProject? GetProject(
string projectDirectory,
NuGetFramework framework,
string configuration,
Expand All @@ -26,7 +26,7 @@ public IProject GetProject(
return GetMSBuildProj(projectDirectory, framework, configuration, outputPath);
}

private IProject GetMSBuildProj(string projectDirectory, NuGetFramework framework, string configuration, string outputPath)
private IProject? GetMSBuildProj(string projectDirectory, NuGetFramework framework, string configuration, string outputPath)
{
var msBuildExePath = _environment.GetEnvironmentVariable(Constants.MSBUILD_EXE_PATH);

Expand All @@ -39,7 +39,7 @@ private IProject GetMSBuildProj(string projectDirectory, NuGetFramework framewor
ProjectFactoryName,
msBuildExePath));

string msBuildProjectPath = GetMSBuildProjPath(projectDirectory);
var msBuildProjectPath = GetMSBuildProjPath(projectDirectory);

Reporter.Verbose.WriteLine(string.Format(
CliStrings.MSBuildProjectPath,
Expand All @@ -53,17 +53,17 @@ private IProject GetMSBuildProj(string projectDirectory, NuGetFramework framewor

try
{
return new MSBuildProject(msBuildProjectPath, framework, configuration, outputPath, msBuildExePath);
return new MSBuildProject(_evaluator, msBuildProjectPath, framework, configuration, outputPath, msBuildExePath);
}
catch (InvalidProjectFileException ex)
catch (Exception ex)
{
Reporter.Verbose.WriteLine(ex.ToString().Red());

return null;
}
}

private static string GetMSBuildProjPath(string projectDirectory)
private static string? GetMSBuildProjPath(string projectDirectory)
{
IEnumerable<string> projectFiles = Directory
.GetFiles(projectDirectory, "*.*proj")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using MSBuildProject = Microsoft.Build.Evaluation.Project;
using Microsoft.DotNet.Cli.MSBuildEvaluation;

namespace Microsoft.DotNet.Cli.Commands.New.MSBuildEvaluation;

Expand Down Expand Up @@ -41,7 +41,10 @@ internal enum DotNetLanguage { NotEvaluated, CSharp, VB, FSharp }

internal string? ProjectPath { get; }

public MSBuildProject? EvaluatedProject { get; protected set; }
/// <summary>
/// The evaluated MSBuild project. Null if evaluation did not succeed (so Status will be Failed/NoProjectFound/NoRestore).
/// </summary>
public DotNetProject? EvaluatedProject { get; protected set; }

public string? ErrorMessage { get; protected set; }

Expand Down
Loading