Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Clarify project directive name properties
  • Loading branch information
jjonescz committed Nov 5, 2025
commit 8987eb13627040a7c189d43ee0b6ee2d12061677
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ public static ImmutableArray<CSharpDirective> EvaluateDirectives(
.Select(d => d is CSharpDirective.Project p
? (project is null
? p
: p.WithName(project.ExpandString(p.Name)))
: p.WithName(project.ExpandString(p.Name), CSharpDirective.Project.NameKind.Expanded))
.ResolveProjectPath(sourceFile, diagnostics)
: d)
.ToImmutableArray();
Expand Down Expand Up @@ -490,23 +490,24 @@ public Project(in ParseInfo info, string name) : base(info)
{
Name = name;
OriginalName = name;
UnresolvedName = name;
}

/// <summary>
/// Preserved across <see cref="WithName"/> calls.
/// Preserved across <see cref="WithName"/> calls, i.e.,
/// this is the original directive text as entered by the user.
/// </summary>
public string OriginalName { get; init; }

/// <summary>
/// Preserved across <see cref="ResolveProjectPath"/> calls.
/// This is the <see cref="OriginalName"/> with MSBuild <c>$(..)</c> vars expanded (via <see cref="ProjectInstance.ExpandString"/>).
/// </summary>
/// <remarks>
/// When MSBuild <c>$(..)</c> vars are expanded via <see cref="WithName"/>,
/// the <see cref="UnresolvedName"/> will be expanded, but not resolved
/// (i.e., can be pointing to project directory or file).
/// </remarks>
public string UnresolvedName { get; init; }
public string? ExpandedName { get; init; }

/// <summary>
/// This is the <see cref="ExpandedName"/> resolved via <see cref="ResolveProjectPath"/>
/// (i.e., this is a file path if the original text pointed to a directory).
/// </summary>
public string? ResolvedName { get; init; }

public static new Project? Parse(in ParseContext context)
{
Expand All @@ -520,15 +521,32 @@ public Project(in ParseInfo info, string name) : base(info)
return new Project(context.Info, directiveText);
}

public Project WithName(string name, bool preserveUnresolvedName = false)
public enum NameKind
{
return name == Name && (preserveUnresolvedName || UnresolvedName == name)
? this
: new Project(Info, name)
{
OriginalName = OriginalName,
UnresolvedName = preserveUnresolvedName ? UnresolvedName : name,
};
/// <summary>
/// Change <see cref="Named.Name"/> and <see cref="ExpandedName"/>.
/// </summary>
Expanded = 1,

/// <summary>
/// Change <see cref="Named.Name"/> and <see cref="ResolvedName"/>.
/// </summary>
Resolved = 2,

/// <summary>
/// Change only <see cref="Named.Name"/>.
/// </summary>
Final = 3,
}

public Project WithName(string name, NameKind kind)
{
return new Project(Info, name)
{
OriginalName = OriginalName,
ExpandedName = kind == NameKind.Expanded ? name : ExpandedName,
ResolvedName = kind == NameKind.Resolved ? name : ResolvedName,
};
}

/// <summary>
Expand Down Expand Up @@ -563,7 +581,7 @@ public Project ResolveProjectPath(SourceFile sourceFile, DiagnosticBag diagnosti
diagnostics.AddError(sourceFile, Info.Span, string.Format(FileBasedProgramsResources.InvalidProjectDirective, e.Message), e);
}

return WithName(directiveText, preserveUnresolvedName: true);
return WithName(directiveText, NameKind.Resolved);
}

// https://github.com/dotnet/sdk/issues/51487: Delete copies of methods from MsbuildProject and MSBuildUtilities from the source package, sharing the original method(s) under src/Cli instead.
Expand Down
13 changes: 8 additions & 5 deletions src/Cli/dotnet/Commands/Project/Convert/ProjectConvertCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System.Collections.Immutable;
using System.CommandLine;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using Microsoft.Build.Evaluation;
using Microsoft.DotNet.Cli.Commands.Run;
Expand Down Expand Up @@ -184,10 +185,12 @@ ImmutableArray<CSharpDirective> UpdateDirectives(ImmutableArray<CSharpDirective>
// while also pointing to the project file rather than a directory).
if (directive is CSharpDirective.Project project)
{
Debug.Assert(project.ExpandedName != null && project.ResolvedName != null && project.ResolvedName == project.Name);

if (Path.IsPathFullyQualified(project.Name))
{
// If the path is absolute and has no `$(..)` vars, just keep it.
if (project.UnresolvedName == project.OriginalName)
if (project.ExpandedName == project.OriginalName)
{
result.Add(project);
continue;
Expand All @@ -203,21 +206,21 @@ ImmutableArray<CSharpDirective> UpdateDirectives(ImmutableArray<CSharpDirective>
// The `OriginalName` is absolute if there are no `$(..)` vars at the start.
if (!Path.IsPathFullyQualified(project.OriginalName))
{
project = project.WithName(Path.GetRelativePath(relativeTo: targetDirectory, path: project.Name));
project = project.WithName(Path.GetRelativePath(relativeTo: targetDirectory, path: project.Name), CSharpDirective.Project.NameKind.Final);
result.Add(project);
continue;
}
}

// If the original path is to a directory, just append the resolved file name
// but preserve the variables from the original, e.g., `../$(..)/Directory/Project.csproj`.
if (Directory.Exists(Path.Combine(sourceDirectory, project.UnresolvedName)))
if (Directory.Exists(Path.Combine(sourceDirectory, project.ExpandedName)))
{
var projectFileName = Path.GetFileName(project.Name);
project = project.WithName(Path.Join(project.OriginalName, projectFileName));
project = project.WithName(Path.Join(project.OriginalName, projectFileName), CSharpDirective.Project.NameKind.Final);
}

project = project.WithName(Path.GetRelativePath(relativeTo: targetDirectory, path: Path.Combine(sourceDirectory, project.Name)));
project = project.WithName(Path.GetRelativePath(relativeTo: targetDirectory, path: Path.Combine(sourceDirectory, project.Name)), CSharpDirective.Project.NameKind.Final);
result.Add(project);
continue;
}
Expand Down
5 changes: 0 additions & 5 deletions src/Cli/dotnet/Commands/Run/VirtualProjectBuildingCommand.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Frozen;
using System.Collections.Immutable;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Security;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
using System.Xml;
using Microsoft.Build.Construction;
using Microsoft.Build.Definition;
Expand All @@ -20,8 +17,6 @@
using Microsoft.Build.Logging;
using Microsoft.Build.Logging.SimpleErrorLogger;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using Microsoft.DotNet.Cli.Commands.Clean.FileBasedAppArtifacts;
using Microsoft.DotNet.Cli.Commands.Restore;
Expand Down
Loading