Add #:item directive support#53712
Add #:item directive support#53712stevefan1999-personal wants to merge 2 commits intodotnet:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Adds support for a new #:item file-level directive in file-based apps (dotnet run file.cs) so users can inject arbitrary MSBuild items (e.g., EmbeddedResource, Protobuf) into the generated project, and updates conversion/docs/tests accordingly.
Changes:
- Introduces parsing and project-file emission for
#:item <ItemType> <Include>directives (with optional quoting for the include). - Evaluates
#:itemincludes to full paths during virtual project evaluation and disables runfile caching when item directives are present. - Extends
dotnet project convertand test suite/documentation to recognize and emit#:item.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| test/dotnet.Tests/CommandTests/Run/RunFileTests_Directives.cs | Adds end-to-end and API-level tests validating #:item behavior (embedded resource scenario). |
| test/dotnet.Tests/CommandTests/Project/Convert/DotnetProjectConvertTests.cs | Adds conversion tests for #:item and updates directive validation coverage. |
| src/Microsoft.DotNet.ProjectTools/VirtualProjectBuilder.cs | Evaluates #:item includes to full paths and emits items into generated project files. |
| src/Cli/Microsoft.DotNet.FileBasedPrograms/InternalAPI.Unshipped.txt | Records the newly added CSharpDirective.Item internal API surface. |
| src/Cli/Microsoft.DotNet.FileBasedPrograms/FileLevelDirectiveHelpers.cs | Adds directive parsing support and relaxes quote blocking specifically for #:item. |
| src/Cli/dotnet/Commands/Run/VirtualProjectBuildingCommand.cs | Prevents caching/reuse when #:item directives are present. |
| src/Cli/dotnet/Commands/Project/Convert/ProjectConvertCommand.cs | Adjusts conversion to relativize item includes for output projects. |
| documentation/general/dotnet-run-file.md | Documents #:item syntax and behavior. |
|
|
||
| if (includeText.Length == 0) | ||
| { | ||
| context.ReportError(string.Format(FileBasedProgramsResources.InvalidDirectiveName, context.DirectiveKind, " ")); |
There was a problem hiding this comment.
For #:item, when the item type is present but the include is missing (#:item Foo), this reports InvalidDirectiveName, whose message suggests an optional value. Since #:item requires an include, this error is misleading for this specific case; consider reporting a dedicated “missing include/value” error (similar to PropertyDirectiveMissingParts) so the user gets actionable guidance.
| context.ReportError(string.Format(FileBasedProgramsResources.InvalidDirectiveName, context.DirectiveKind, " ")); | |
| context.ReportError($"The '{context.DirectiveKind}' directive requires both an item type and an include value."); |
| if (includeText.Length >= 2 && includeText[0] == '"' && includeText[^1] == '"') | ||
| { |
There was a problem hiding this comment.
#:item accepts quoted includes, but currently any unbalanced quote (e.g., #:item Protobuf "foo.proto or foo".proto) will be treated as a literal quote in the include and silently flow into MSBuild. This makes it easy to produce surprising item includes; consider validating that if includeText contains a double-quote then it must be properly wrapped (or otherwise report an error) rather than partially stripping/accepting it.
| if (includeText.Length >= 2 && includeText[0] == '"' && includeText[^1] == '"') | |
| { | |
| if (includeText.Contains('"')) | |
| { | |
| if (includeText.Length < 2 || includeText[0] != '"' || includeText[^1] != '"' || includeText[1..^1].Contains('"')) | |
| { | |
| context.ReportError(string.Format(FileBasedProgramsResources.InvalidDirectiveName, context.DirectiveKind, " ")); | |
| return null; | |
| } |
| if (directive is CSharpDirective.Item item && | ||
| Path.IsPathFullyQualified(item.Include)) | ||
| { | ||
| item = item.WithInclude(Path.GetRelativePath(relativeTo: targetDirectory, path: item.Include)); | ||
| result.Add(item); | ||
| continue; | ||
| } |
There was a problem hiding this comment.
UpdateDirectives rewrites #:item includes to be relative to targetDirectory, which implies the referenced file will exist in the converted output. However, FindIncludedItems() only copies items whose item types come from the include-mapping (Compile/EmbeddedResource/None/Content by default); any #:item using a custom item type (e.g., Protobuf) won’t be copied, so the converted project will reference a missing file. Consider extending the conversion copy list to include file paths referenced by CSharpDirective.Item directives (or enumerating all project items that are files), so conversion reliably produces a buildable project for arbitrary item types.
Fixes #49820 and #52319