Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
7 changes: 1 addition & 6 deletions documentation/general/dotnet-run-file.md
Original file line number Diff line number Diff line change
Expand Up @@ -215,18 +215,13 @@ The directives are processed as follows:
where `{0}` is the directive's value and `{1}` is determined by its extension.
The mapping can be customized by setting the MSBuild property `FileBasedProgramsItemMapping`
which is by default set to `.cs=Compile;.resx=EmbeddedResource;.json=None;.razor=Content`.
(The mapping customization is currently gated under a feature flag that can be enabled by setting the MSBuild property `ExperimentalFileBasedProgramEnableItemMapping=true`.)

It is an error if the value is empty.

Relative paths are resolved relative to the file containing the directive.

This directive is currently gated under a feature flag that can be enabled by setting the MSBuild property `ExperimentalFileBasedProgramEnableIncludeDirective=true`.

- Each `#:exclude` is injected similarly to `#:include` but with `Remove="{0}"` instead of `Include="{0}"`.

This directive is currently gated under a feature flag that can be enabled by setting the MSBuild property `ExperimentalFileBasedProgramEnableExcludeDirective=true`.

- Other directive kinds result in an error, reserving them for future use.

Directive values support MSBuild variables (like `$(..)`) normally as they are translated literally and left to MSBuild engine to process.
Expand All @@ -245,7 +240,7 @@ and can do that efficiently by stopping the search when it sees the first "C# to

For a given `dotnet run file.cs`, we include directives from the current entry point file (`file.cs`) and all other non-entry-point C# files,
specifically from all `Compile` items included in the project, no matter whether the `Compile` items are specified in some MSBuild code or inferred from `#:include`.
(Processing directives from other files is currently gated under a feature flag that can be enabled by setting the MSBuild property `ExperimentalFileBasedProgramEnableTransitiveDirectives=true`.)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Was turning this single paragraph into two paragraphs intended?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

No, will revert, thanks.

The order in which other files are processed is currently unspecified (can change across SDK versions) but deterministic (stable in a given SDK version).
We do not limit these directives to appear only in entry point files because it allows:
- a non-entry-point file like `Util.cs` to be self-contained and have all the `#:package`s it needs specified in it,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -598,11 +598,6 @@ public enum IncludeOrExcludeKind
/// </summary>
public sealed class IncludeOrExclude(in ParseInfo info) : Named(info)
{
public const string ExperimentalFileBasedProgramEnableIncludeDirective = nameof(ExperimentalFileBasedProgramEnableIncludeDirective);
public const string ExperimentalFileBasedProgramEnableExcludeDirective = nameof(ExperimentalFileBasedProgramEnableExcludeDirective);
public const string ExperimentalFileBasedProgramEnableTransitiveDirectives = nameof(ExperimentalFileBasedProgramEnableTransitiveDirectives);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

do want to call out that I think the editor still needs changes to handle transitive directives properly. So today it will see #: and assume the file is an entry point (depending on the order things load in).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I am comfortable with moving forward on that change and just making sure we expedite it properly. But may want to extract this bit to separate PR, if our goal was to only remove experimental flags for these, for scenarios where we expect editor tooling to work.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@DamianEdwards what would you like to do about the transitive feature flag given IDE doesn't implement support for that yet?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@RikkiGibson what's the plan for integrating your entry-point detection changes? Is that the change you're referring to when you said:

I am comfortable with moving forward on that change and just making sure we expedite it properly.

I am concerned about missing the boat here a bit as the next train is 10.0.400 (or 11 previews) and that's not until August+. It would be super unfortunate if we're able to get the editor experience changes done well before then but we can't enable it by default in the SDK.

That said, pulling the removal of flag on the transitive directive feature in the SDK into a separate PR seems like the prudent thing to do given the current state.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

public const string ExperimentalFileBasedProgramEnableItemMapping = nameof(ExperimentalFileBasedProgramEnableItemMapping);

public const string MappingPropertyName = "FileBasedProgramsItemMapping";

public static string DefaultMappingString => ".cs=Compile;.resx=EmbeddedResource;.json=None;.razor=Content";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
const Microsoft.DotNet.FileBasedPrograms.CSharpDirective.IncludeOrExclude.ExperimentalFileBasedProgramEnableExcludeDirective = "ExperimentalFileBasedProgramEnableExcludeDirective" -> string!
const Microsoft.DotNet.FileBasedPrograms.CSharpDirective.IncludeOrExclude.ExperimentalFileBasedProgramEnableIncludeDirective = "ExperimentalFileBasedProgramEnableIncludeDirective" -> string!
const Microsoft.DotNet.FileBasedPrograms.CSharpDirective.IncludeOrExclude.ExperimentalFileBasedProgramEnableItemMapping = "ExperimentalFileBasedProgramEnableItemMapping" -> string!
const Microsoft.DotNet.FileBasedPrograms.CSharpDirective.IncludeOrExclude.ExperimentalFileBasedProgramEnableTransitiveDirectives = "ExperimentalFileBasedProgramEnableTransitiveDirectives" -> string!
const Microsoft.DotNet.FileBasedPrograms.CSharpDirective.IncludeOrExclude.MappingPropertyName = "FileBasedProgramsItemMapping" -> string!
Microsoft.DotNet.FileBasedPrograms.CSharpDirective
Microsoft.DotNet.FileBasedPrograms.CSharpDirective.CSharpDirective(in Microsoft.DotNet.FileBasedPrograms.CSharpDirective.ParseInfo info) -> void
Expand Down
4 changes: 0 additions & 4 deletions src/Microsoft.DotNet.ProjectTools/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,4 @@ Make the profile names distinct.</value>
<value>File included via #:include directive (or Compile item) not found: {0}</value>
<comment>{Locked="#:include"}{Locked="Compile"}. {0} is file path.</comment>
</data>
<data name="ExperimentalFeatureDisabled" xml:space="preserve">
<value>This is an experimental feature, set MSBuild property '{0}' to 'true' to enable it.</value>
<comment>{Locked="MSBuild"}{Locked="true"}. {0} is MSBuild property name.</comment>
</data>
</root>
59 changes: 4 additions & 55 deletions src/Microsoft.DotNet.ProjectTools/VirtualProjectBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -221,12 +221,10 @@ private ImmutableArray<CSharpDirective> EvaluateDirectives(

internal ImmutableArray<(string Extension, string ItemType)> GetItemMapping(ProjectInstance project, ErrorReporter reportError)
{
return MSBuildUtilities.ConvertStringToBool(project.GetPropertyValue(CSharpDirective.IncludeOrExclude.ExperimentalFileBasedProgramEnableItemMapping))
? CSharpDirective.IncludeOrExclude.ParseMapping(
project.GetPropertyValue(CSharpDirective.IncludeOrExclude.MappingPropertyName),
EntryPointSourceFile,
reportError)
: CSharpDirective.IncludeOrExclude.DefaultMapping;
return CSharpDirective.IncludeOrExclude.ParseMapping(
project.GetPropertyValue(CSharpDirective.IncludeOrExclude.MappingPropertyName),
EntryPointSourceFile,
reportError);
}

public static ProjectInstance CreateProjectInstance(
Expand Down Expand Up @@ -276,8 +274,6 @@ internal void CreateProjectInstance(
evaluatedDirectives,
addGlobalProperties);

CheckDirectives(project, evaluatedDirectives, reportError);

return;
}

Expand Down Expand Up @@ -325,8 +321,6 @@ internal void CreateProjectInstance(
evaluatedDirectives = evaluatedDirectiveBuilder.ToImmutable();
_evaluatedDirectives = (directivesOriginal, evaluatedDirectives);

CheckDirectives(project, evaluatedDirectives, reportError);

bool TryGetNextFileToProcess()
{
while (filesToProcess.TryDequeue(out var filePath))
Expand Down Expand Up @@ -399,51 +393,6 @@ ProjectRootElement CreateProjectRootElement(string projectFileText, ProjectColle
}
}

private void CheckDirectives(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

So some version of this check, is going to return, when #:ref support merges behind experimental flag? The churn doesn't specifically worry me, just wanted to understand what to expect.

@jjonescz jjonescz Apr 15, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

yes, and since I just merged the #:ref change, I'm going to merge base branch back here and this diff should become smaller

ProjectInstance project,
ImmutableArray<CSharpDirective> directives,
ErrorReporter reportError)
{
bool? includeEnabled = null;
bool? excludeEnabled = null;
bool? transitiveEnabled = null;

foreach (var directive in directives)
{
if (directive is CSharpDirective.IncludeOrExclude includeOrExcludeDirective)
{
if (includeOrExcludeDirective.Kind == CSharpDirective.IncludeOrExcludeKind.Include)
{
CheckFlagEnabled(ref includeEnabled, CSharpDirective.IncludeOrExclude.ExperimentalFileBasedProgramEnableIncludeDirective, directive);
}
else
{
Debug.Assert(includeOrExcludeDirective.Kind == CSharpDirective.IncludeOrExcludeKind.Exclude);
CheckFlagEnabled(ref excludeEnabled, CSharpDirective.IncludeOrExclude.ExperimentalFileBasedProgramEnableExcludeDirective, directive);
}
}

if (directive.Info.SourceFile.Path != EntryPointSourceFile.Path)
{
CheckFlagEnabled(ref transitiveEnabled, CSharpDirective.IncludeOrExclude.ExperimentalFileBasedProgramEnableTransitiveDirectives, directive);
}
}

void CheckFlagEnabled(ref bool? flag, string flagName, CSharpDirective directive)
{
bool value = flag ??= MSBuildUtilities.ConvertStringToBool(project.GetPropertyValue(flagName));

if (!value)
{
reportError(
directive.Info.SourceFile.Text,
directive.Info.SourceFile.Path,
directive.Info.Span,
string.Format(Resources.ExperimentalFeatureDisabled, flagName));
}
}
}

internal static void WriteProjectFile(
TextWriter writer,
ImmutableArray<CSharpDirective> directives,
Expand Down
5 changes: 0 additions & 5 deletions src/Microsoft.DotNet.ProjectTools/xlf/Resources.cs.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 0 additions & 5 deletions src/Microsoft.DotNet.ProjectTools/xlf/Resources.de.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 0 additions & 5 deletions src/Microsoft.DotNet.ProjectTools/xlf/Resources.es.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 0 additions & 5 deletions src/Microsoft.DotNet.ProjectTools/xlf/Resources.fr.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 0 additions & 5 deletions src/Microsoft.DotNet.ProjectTools/xlf/Resources.it.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 0 additions & 5 deletions src/Microsoft.DotNet.ProjectTools/xlf/Resources.ja.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 0 additions & 5 deletions src/Microsoft.DotNet.ProjectTools/xlf/Resources.ko.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 0 additions & 5 deletions src/Microsoft.DotNet.ProjectTools/xlf/Resources.pl.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 0 additions & 5 deletions src/Microsoft.DotNet.ProjectTools/xlf/Resources.pt-BR.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 0 additions & 5 deletions src/Microsoft.DotNet.ProjectTools/xlf/Resources.ru.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 0 additions & 5 deletions src/Microsoft.DotNet.ProjectTools/xlf/Resources.tr.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading