Skip to content

Conversation

jjonescz
Copy link
Member

@jjonescz jjonescz commented Oct 2, 2025

Closes #49286.

@jjonescz jjonescz added the Area-run-file Items related to the "dotnet run <file>" effort label Oct 2, 2025
@jjonescz jjonescz force-pushed the sprint-project-vars branch from 9ad9ae2 to bc17aea Compare October 2, 2025 08:52
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

Adds support for MSBuild variable expansion inside #:project directives and updates tests and documentation accordingly.

  • Introduces directive evaluation pass (EvaluateDirectives) to expand MSBuild variables and resolve project paths.
  • Extends CSharpDirective.Project to preserve original and unresolved names for later path adjustments during project conversion.
  • Adds tests covering variable usage and updates documentation about variable handling limitations.

Reviewed Changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
RunTelemetryTests.cs Adapts tests to new CSharpDirective.Project constructor.
RunFileTests.cs Adds tests for variable-based project references and malformed variable syntax.
DotnetProjectConvertTests.cs Adds test cases for variable-containing paths; updates expectations for cross-platform separators.
VirtualProjectBuildingCommand.cs Adds directive evaluation, caching of source file, and enhanced project directive handling.
ProjectConvertCommand.cs Integrates directive evaluation and updates path rewrite logic to preserve variable intent.
dotnet-run-file.md Documents variable support and caveats for #:project directives.

Co-authored-by: Copilot <[email protected]>
@jjonescz jjonescz marked this pull request as ready for review October 2, 2025 14:49
@jjonescz jjonescz requested a review from a team October 2, 2025 14:49
@RikkiGibson
Copy link
Member

The linked issue has milestone 10.0.2xx, was this PR meant to target release/10.0.2xx branch?

@jjonescz jjonescz changed the base branch from release/10.0.1xx to release/10.0.2xx October 2, 2025 18:36
@jjonescz
Copy link
Member Author

jjonescz commented Oct 2, 2025

Definitely, I was worried there might be changes missing in the 2xx branch, but looks like it's fairly up to date wrt file-based app PRs, so I can retarget now.

@jjonescz

This comment was marked as outdated.

@jjonescz jjonescz mentioned this pull request Oct 3, 2025
@jjonescz jjonescz added this to the 10.0.2xx milestone Oct 3, 2025
@dotnet dotnet deleted a comment from azure-pipelines bot Oct 3, 2025
@jjonescz
Copy link
Member Author

jjonescz commented Oct 8, 2025

@RikkiGibson @333fred @MiYanni for reviews, thanks

result.Add(directive);
// If the path is absolute and it has some `$(..)` vars in it,
// turn it into a relative path (it might be in the form `$(ProjectDir)/../Lib`
// and we don't want that to be turned into an absolute path in the converted project).
Copy link
Member

Choose a reason for hiding this comment

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

Is there an ability to be more precise here? For example, what if I have #:project C:\git\$(Config)\$(Config).csproj. Are we able to detect if the path starts with an absolute path, and is only dependent on variables for subpath elements?

Copy link
Member Author

Choose a reason for hiding this comment

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

Well, in theory, those subpath elements could be something like $("name of the file's directory") which can change during conversion. But that also applies to relative paths where we preserve variables, and I think we shouldn't care about such edge cases. So yeah, I guess we can be more precise, thanks.

Copy link
Member Author

@jjonescz jjonescz Oct 9, 2025

Choose a reason for hiding this comment

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

Note that we could probably somehow evaluate the variables against the conversion output directory and be even more precise here, but that seemed like an overkill to me.

public sealed class Project(in ParseInfo info) : Named(info)
public sealed class Project : Named
{
[SetsRequiredMembers]
Copy link
Member

Choose a reason for hiding this comment

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

Is there a parameterless constructor somewhere I'm missing? If not, please remove required members from the type, this isn't serving any purpose.

Copy link
Member Author

@jjonescz jjonescz Oct 8, 2025

Choose a reason for hiding this comment

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

Good catch, thanks, this is a leftover from some earlier state of the code edit: I though could remove the SetsRequiredMembers too but looks like that has to stay due to the required base property Name - but I removed the required modifiers, those seemed unnecessary.

However, in `#:project` directives, variables might not be preserved during [grow up](#grow-up),
because there is additional processing of those directives that makes it technically challenging to preserve variables in all cases
(project directive values need to be resolved to be relative to the target directory
and also to point to a project file rather than a directory).
Copy link
Member

@RikkiGibson RikkiGibson Oct 8, 2025

Choose a reason for hiding this comment

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

I am wondering if it would be helpful to attempt to process the project path in two ways

  1. CLI-like. dotnet add MyProject.csproj reference OtherProject.csproj, etc. MSBuild variables are not respected, a relative path to a directory or project is expected. This is probably the 90% case.
  2. MSBuild-like. A project file path is expected. Variables are expanded. Full paths are expected (e.g. $(MSBuildThisFileDirectory)/path/to/project.csproj).

Essentially, we first try to resolve as (1), as if it is the last argument to dotnet add MyProject.csproj reference OtherProject.csproj, and, if we don't find it, just pass the path as written into the virtual project as case (2). I am speculating there is not a need to include both the convenience features of (1) and the expressiveness of (2). It's not something that either ordinary projects or the CLI can do today.

This would mean that the virtual project content can depend on the presence of the referenced projects on disk. I'm not sure if that introduces any major problems or not.

It's possible this is the wrong approach and would just make things more confusing, not less, but, thought I would submit it in hopes of it helping to resolve the technical challenges.

Copy link
Member Author

Choose a reason for hiding this comment

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

That's interesting, at first glance this seems to definitely simplify some things - we wouldn't need to expand the paths against MSBuild project during dotnet run. But consider that during dotnet project convert, if we get something like #:project $(MSBuildThisFileDirectory)/path/to/project.csproj, we still need to evaluate it ourselves so we can rewrite it to make it correct relative to the conversion output directory. So the logic for expanding the paths probably needs to remain.

We could skip searching for project file in the directory in case there are MSBuild variables, but we would still need to do that for non-variable paths, and that logic is actually the same, so that wouldn't simplify our lives much probably.

So I'm not sure this would resolve any technical challenges after all? (If we want to support conversion of the variable paths.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-run-file Items related to the "dotnet run <file>" effort
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants