Skip to content
Draft
Show file tree
Hide file tree
Changes from 9 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
30 changes: 30 additions & 0 deletions documentation/general/dotnet-run-file.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ which are [ignored][ignored-directives] by the C# language but recognized by the
#:property TargetFramework=net11.0
#:property LangVersion=preview
#:package System.CommandLine@2.0.0-*
#:package Microsoft.Build@17.0.0 ExcludeAssets=runtime PrivateAssets=all
#:project ../MyLibrary
#:ref ../lib/lib.cs
#:include ./**/*.cs
Expand All @@ -190,6 +191,31 @@ The value is required for `#:property`, optional for `#:package`/`#:sdk`, and di
The name must be separated from the kind of the directive by whitespace
and any leading and trailing white space is not considered part of the name and value.

The remainder of a directive (after the kind) is split into whitespace-separated tokens.
Whitespace inside a value is not allowed unless the value is enclosed in double quotes (`"`).
A value is written either bare or wrapped entirely in double quotes. A quoted value is lexed as a
regular C# string literal (the same way `#r`/`#load` directives lex their argument), so its escape
sequences are decoded, e.g., `#:property Description="Hello World"` sets the value to `Hello World`,
`#:property Path="a\\b"` sets it to `a\b`, and `#:property Text="a\"b"` sets it to `a"b`. Verbatim
(`@"..."`) and raw (`"""..."""`) string literals are not supported. Quotes can only enclose a whole
value, so `#:property A=B` and `#:property A="B"` are allowed, but `#:property A=B"C"` is an error.
It is an error if a quote is left unterminated.

Because a bare value keeps a backslash literal while a quoted value follows C# escape rules, a Windows
path is simplest written bare (`#:project C:\src\lib`) or with forward slashes if quoting is needed
(`#:project "C:/src/my lib"`); quoting a backslash path requires escaping it (`"C:\\src\\my lib"`).

For backward compatibility, a directive whose value contains no double quotes is still accepted in a
*legacy mode*: the entire remainder after the name and separator is taken verbatim as a single value
(including any internal whitespace), matching how these directives behaved before quoting and metadata
were supported. Analyzer [CA2267](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2267)
flags such legacy directives and offers a code fix to rewrite them into the quoted form.

`#:package`, `#:project`, and `#:ref` directives can specify additional MSBuild item metadata as trailing `Name=Value` tokens,
e.g., `#:package Microsoft.Build@17.0.0 ExcludeAssets=runtime PrivateAssets=all`.
Each metadata name must be a valid XML element name; each metadata value can be quoted to contain whitespace.
The other directive kinds do not support trailing metadata and it is an error to specify extra tokens for them.

The directives are processed as follows:

- The name and value of the first `#:sdk` is injected into `<Project Sdk="{0}/{1}">` (or just `<Project Sdk="{0}">` if it has no value),
Expand All @@ -201,13 +227,16 @@ The directives are processed as follows:

- Each `#:package` is injected as `<PackageReference Include="{0}" Version="{1}">` (or without the `Version` attribute if it has no value) in an `<ItemGroup>`.
It is an error if its name is empty (the value, i.e., package version, is allowed to be empty, but that results in empty `Version=""`).
Any trailing `Name=Value` metadata is injected as child elements, e.g.,
`<PackageReference Include="{0}" Version="{1}"><ExcludeAssets>runtime</ExcludeAssets></PackageReference>`.

It is valid to have a `#:package` directive without a version.
That's useful when central package management (CPM) is used.
NuGet will report an appropriate error if the version is missing and CPM is not enabled.

- Each `#:project` is injected as `<ProjectReference Include="{0}" />` in an `<ItemGroup>`.
It is an error if the value is empty.
Any trailing `Name=Value` metadata is injected as child elements of the `<ProjectReference>`.
If the path points to an existing directory, a project file is found inside that directory and its path is used instead
(because `ProjectReference` items don't support directory paths).
An error is reported if zero or more than one projects are found in the directory, just like `dotnet reference add` would do.
Expand All @@ -216,6 +245,7 @@ The directives are processed as follows:
A virtual project is created for the referenced file (e.g., `lib.cs` produces a virtual `lib.cs.csproj`),
and a `<ProjectReference Include="lib.cs.csproj" />` is injected in an `<ItemGroup>`.
It is an error if the name is empty or if the referenced file does not exist.
Any trailing `Name=Value` metadata is injected as child elements of the `<ProjectReference>`.
Unlike `#:project`, `#:ref` points to a `.cs` file (not a `.csproj` file or directory).

The referenced file is itself a file-based program with its own virtual project (defaulting to `OutputType=Exe`).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,8 +158,24 @@
<value>Duplicate directives are not supported: {0}</value>
<comment>{0} is the directive type and name.</comment>
</data>
<data name="QuoteInDirective" xml:space="preserve">
<value>Directives currently cannot contain double quotes (").</value>
<data name="UnterminatedQuoteInDirective" xml:space="preserve">
<value>Unterminated double quote (") in directive.</value>
</data>
<data name="InvalidQuoteInDirective" xml:space="preserve">
<value>Double quotes (") in a directive must enclose an entire value, for example: 'Name="a b"' or '"a b"'.</value>
<comment>{Locked="Name=&quot;a b&quot;"}{Locked="&quot;a b&quot;"}</comment>
</data>
<data name="InvalidDirectiveMetadata" xml:space="preserve">
<value>Directive metadata must be in the form 'Name=Value'. Invalid metadata: '{0}'.</value>
<comment>{Locked="'Name=Value'"}{0} is the offending metadata text.</comment>
</data>
<data name="DirectiveMetadataInvalidName" xml:space="preserve">
<value>Invalid directive metadata name '{0}': {1}</value>
<comment>{0} is the metadata name. {1} is the inner exception message.</comment>
</data>
<data name="UnexpectedDirectiveText" xml:space="preserve">
<value>The '{0}' directive has unexpected content. To include whitespace in a value, enclose it in double quotes (").</value>
<comment>{0} is the directive kind like 'property' or 'sdk'.</comment>
</data>
<data name="InvalidProjectDirective" xml:space="preserve">
<value>The '#:project' directive is invalid: {0}</value>
Expand Down
Loading
Loading