From c7668c237086fde88b81583729bbc7ff2787c795 Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Thu, 9 Apr 2026 12:13:01 +0200 Subject: [PATCH 1/9] Remove feature flags for `#:include` and `#:exclude` --- documentation/general/dotnet-run-file.md | 6 +- .../FileLevelDirectiveHelpers.cs | 3 - .../InternalAPI.Unshipped.txt | 3 - .../Resources.resx | 4 - .../VirtualProjectBuilder.cs | 49 ---- .../xlf/Resources.cs.xlf | 5 - .../xlf/Resources.de.xlf | 5 - .../xlf/Resources.es.xlf | 5 - .../xlf/Resources.fr.xlf | 5 - .../xlf/Resources.it.xlf | 5 - .../xlf/Resources.ja.xlf | 5 - .../xlf/Resources.ko.xlf | 5 - .../xlf/Resources.pl.xlf | 5 - .../xlf/Resources.pt-BR.xlf | 5 - .../xlf/Resources.ru.xlf | 5 - .../xlf/Resources.tr.xlf | 5 - .../xlf/Resources.zh-Hans.xlf | 5 - .../xlf/Resources.zh-Hant.xlf | 5 - .../Convert/DotnetProjectConvertTests.cs | 16 -- .../CommandTests/Run/RunFileTests.cs | 211 +++++------------- 20 files changed, 58 insertions(+), 299 deletions(-) diff --git a/documentation/general/dotnet-run-file.md b/documentation/general/dotnet-run-file.md index 9636bc0ff88b..35fa9217be9f 100644 --- a/documentation/general/dotnet-run-file.md +++ b/documentation/general/dotnet-run-file.md @@ -221,12 +221,8 @@ The directives are processed as follows: 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. @@ -245,7 +241,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`.) + 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, diff --git a/src/Cli/Microsoft.DotNet.FileBasedPrograms/FileLevelDirectiveHelpers.cs b/src/Cli/Microsoft.DotNet.FileBasedPrograms/FileLevelDirectiveHelpers.cs index 89413dc860cc..1ee7b206c5f3 100644 --- a/src/Cli/Microsoft.DotNet.FileBasedPrograms/FileLevelDirectiveHelpers.cs +++ b/src/Cli/Microsoft.DotNet.FileBasedPrograms/FileLevelDirectiveHelpers.cs @@ -598,9 +598,6 @@ public enum IncludeOrExcludeKind /// 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); public const string ExperimentalFileBasedProgramEnableItemMapping = nameof(ExperimentalFileBasedProgramEnableItemMapping); public const string MappingPropertyName = "FileBasedProgramsItemMapping"; diff --git a/src/Cli/Microsoft.DotNet.FileBasedPrograms/InternalAPI.Unshipped.txt b/src/Cli/Microsoft.DotNet.FileBasedPrograms/InternalAPI.Unshipped.txt index 8beab97ae92c..1b464c020ef9 100644 --- a/src/Cli/Microsoft.DotNet.FileBasedPrograms/InternalAPI.Unshipped.txt +++ b/src/Cli/Microsoft.DotNet.FileBasedPrograms/InternalAPI.Unshipped.txt @@ -1,7 +1,4 @@ -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 diff --git a/src/Microsoft.DotNet.ProjectTools/Resources.resx b/src/Microsoft.DotNet.ProjectTools/Resources.resx index 117e6b5642f2..5c3d076e48a7 100644 --- a/src/Microsoft.DotNet.ProjectTools/Resources.resx +++ b/src/Microsoft.DotNet.ProjectTools/Resources.resx @@ -162,8 +162,4 @@ Make the profile names distinct. File included via #:include directive (or Compile item) not found: {0} {Locked="#:include"}{Locked="Compile"}. {0} is file path. - - This is an experimental feature, set MSBuild property '{0}' to 'true' to enable it. - {Locked="MSBuild"}{Locked="true"}. {0} is MSBuild property name. - diff --git a/src/Microsoft.DotNet.ProjectTools/VirtualProjectBuilder.cs b/src/Microsoft.DotNet.ProjectTools/VirtualProjectBuilder.cs index b4adf03ea70c..e2c6308d5135 100644 --- a/src/Microsoft.DotNet.ProjectTools/VirtualProjectBuilder.cs +++ b/src/Microsoft.DotNet.ProjectTools/VirtualProjectBuilder.cs @@ -276,8 +276,6 @@ internal void CreateProjectInstance( evaluatedDirectives, addGlobalProperties); - CheckDirectives(project, evaluatedDirectives, reportError); - return; } @@ -325,8 +323,6 @@ internal void CreateProjectInstance( evaluatedDirectives = evaluatedDirectiveBuilder.ToImmutable(); _evaluatedDirectives = (directivesOriginal, evaluatedDirectives); - CheckDirectives(project, evaluatedDirectives, reportError); - bool TryGetNextFileToProcess() { while (filesToProcess.TryDequeue(out var filePath)) @@ -399,51 +395,6 @@ ProjectRootElement CreateProjectRootElement(string projectFileText, ProjectColle } } - private void CheckDirectives( - ProjectInstance project, - ImmutableArray 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 directives, diff --git a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.cs.xlf b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.cs.xlf index c27bf12ff8da..5ea201219f60 100644 --- a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.cs.xlf +++ b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.cs.xlf @@ -26,11 +26,6 @@ Nastavte odlišné názvy profilů. Nelze určit cestu k dočasnému adresáři. Zvažte konfiguraci proměnné prostředí TEMP v systému Windows nebo místní datové složky aplikace v systému Unix. - - This is an experimental feature, set MSBuild property '{0}' to 'true' to enable it. - Toto je experimentální funkce. Pokud ji chcete povolit, nastavte vlastnost MSBuild {0} na hodnotu true. - {Locked="MSBuild"}{Locked="true"}. {0} is MSBuild property name. - File included via #:include directive (or Compile item) not found: {0} Soubor zahrnutý prostřednictvím direktivy #:include (nebo položky Compile) nebyl nalezen: {0} diff --git a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.de.xlf b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.de.xlf index be6f488dfaf6..89a14e9282cf 100644 --- a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.de.xlf +++ b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.de.xlf @@ -26,11 +26,6 @@ Erstellen Sie eindeutige Profilnamen. Ein temporärer Verzeichnispfad kann nicht ermittelt werden. Erwägen Sie, die TEMP-Umgebungsvariable unter Windows oder den lokalen App-Datenordner unter Unix zu konfigurieren. - - This is an experimental feature, set MSBuild property '{0}' to 'true' to enable it. - Dies ist eine experimentelle Funktion. Legen Sie die MSBuild-Eigenschaft „{0}“ auf „true“ fest, um sie zu aktivieren. - {Locked="MSBuild"}{Locked="true"}. {0} is MSBuild property name. - File included via #:include directive (or Compile item) not found: {0} Die über die #:include-Anweisung (oder das Compile-Element) eingebundene Datei wurde nicht gefunden: {0} diff --git a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.es.xlf b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.es.xlf index 417c200b9dec..a45244ad1996 100644 --- a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.es.xlf +++ b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.es.xlf @@ -26,11 +26,6 @@ Defina nombres de perfiles distintos. No se puede determinar una ruta de acceso temporal al directorio. Considere la posibilidad de configurar la variable de entorno TEMP en Windows o la carpeta de datos de la aplicación local en Unix. - - This is an experimental feature, set MSBuild property '{0}' to 'true' to enable it. - Se trata de una característica experimental, establezca la propiedad "{0}" de MSBuild en "true" para habilitarla. - {Locked="MSBuild"}{Locked="true"}. {0} is MSBuild property name. - File included via #:include directive (or Compile item) not found: {0} No se encuentra el archivo incluido mediante la directiva #:include (o elemento Compile): {0} diff --git a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.fr.xlf b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.fr.xlf index b9afff50e4be..56d5f4507a0b 100644 --- a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.fr.xlf +++ b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.fr.xlf @@ -26,11 +26,6 @@ Faites en sorte que les noms de profil soient distincts. Impossible de déterminer un chemin d’accès pour le répertoire temporaire. Nous vous recommandons de configurer la variable d’environnement TEMP sous Windows ou le dossier des données d’application locale sous Unix. - - This is an experimental feature, set MSBuild property '{0}' to 'true' to enable it. - Il s’agit d’une fonctionnalité expérimentale, définissez la propriété MSBuild « {0} » sur « true » pour l’activer. - {Locked="MSBuild"}{Locked="true"}. {0} is MSBuild property name. - File included via #:include directive (or Compile item) not found: {0} Fichier inclus via la directive #:include (ou élément Compile) introuvable : {0} diff --git a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.it.xlf b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.it.xlf index b81b4e946977..e2c70ec1c9a8 100644 --- a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.it.xlf +++ b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.it.xlf @@ -26,11 +26,6 @@ Rendi distinti i nomi profilo. Non è possibile determinare un percorso per la directory temporanea. Considerare la configurazione della variabile di ambiente TEMP in Windows o della cartella dei dati locali dell'app in Unix. - - This is an experimental feature, set MSBuild property '{0}' to 'true' to enable it. - Questa è una funzionalità sperimentale, impostare la proprietà MSBuild "{0}" su "true" per abilitarla. - {Locked="MSBuild"}{Locked="true"}. {0} is MSBuild property name. - File included via #:include directive (or Compile item) not found: {0} File incluso tramite direttiva #:include (o elemento Compile) non trovato: {0} diff --git a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.ja.xlf b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.ja.xlf index 0e0a34c9824b..30ea3f04d6ae 100644 --- a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.ja.xlf +++ b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.ja.xlf @@ -26,11 +26,6 @@ Make the profile names distinct. 一時ディレクトリ パスを特定できません。Windows で TEMP 環境変数を構成するか、Unix でローカル アプリ データ フォルダーを構成することを検討してください。 - - This is an experimental feature, set MSBuild property '{0}' to 'true' to enable it. - これは試験的な機能です。有効にするには、MSBuild プロパティ '{0}' を 'true' に設定してください。 - {Locked="MSBuild"}{Locked="true"}. {0} is MSBuild property name. - File included via #:include directive (or Compile item) not found: {0} #:include ディレクティブ (または Compile 項目) を介して含められているファイルが見つかりません: {0} diff --git a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.ko.xlf b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.ko.xlf index 047b32c0f0e0..e85d485b04c6 100644 --- a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.ko.xlf +++ b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.ko.xlf @@ -26,11 +26,6 @@ Make the profile names distinct. 임시 디렉터리 경로를 확인할 수 없습니다. Windows에서는 TEMP 환경 변수를, Unix에서는 로컬 앱 데이터 폴더를 설정하는 것이 좋습니다. - - This is an experimental feature, set MSBuild property '{0}' to 'true' to enable it. - 이 기능은 실험적인 기능입니다. 사용하려면 MSBuild 속성 '{0}'을(를) 'true'로 설정하세요. - {Locked="MSBuild"}{Locked="true"}. {0} is MSBuild property name. - File included via #:include directive (or Compile item) not found: {0} #:include 지시문(또는 Compile 항목)을 통해 포함된 파일을 찾을 수 없음: {0} diff --git a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.pl.xlf b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.pl.xlf index cb88c3488035..34b6e91e8875 100644 --- a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.pl.xlf +++ b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.pl.xlf @@ -26,11 +26,6 @@ Rozróżnij nazwy profilów. Nie można określić tymczasowej ścieżki katalogu. Rozważ skonfigurowanie zmiennej środowiskowej TEMP w systemie Windows lub folderze danych aplikacji lokalnej w systemie Unix. - - This is an experimental feature, set MSBuild property '{0}' to 'true' to enable it. - To funkcja eksperymentalna, ustaw właściwość MSBuild „{0}” na wartość „true”, aby ją włączyć. - {Locked="MSBuild"}{Locked="true"}. {0} is MSBuild property name. - File included via #:include directive (or Compile item) not found: {0} Nie znaleziono pliku dołączonego za pomocą dyrektywy #:include (lub elementu Compile): {0} diff --git a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.pt-BR.xlf b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.pt-BR.xlf index 904cd0a49833..3ef349070949 100644 --- a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.pt-BR.xlf +++ b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.pt-BR.xlf @@ -26,11 +26,6 @@ Diferencie os nomes dos perfis. Não é possível determinar um caminho de diretório temporário. Considere configurar a variável de ambiente TEMP no Windows ou a pasta de dados do aplicativo local no Unix. - - This is an experimental feature, set MSBuild property '{0}' to 'true' to enable it. - Esse é um recurso experimental, defina a propriedade "{0}" do MSBuild como "true" para habilitá-lo. - {Locked="MSBuild"}{Locked="true"}. {0} is MSBuild property name. - File included via #:include directive (or Compile item) not found: {0} Arquivo incluído por meio da diretiva #:include (ou item de Compile) não encontrado: {0} diff --git a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.ru.xlf b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.ru.xlf index 11a9bdc7fae9..df4550afc339 100644 --- a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.ru.xlf +++ b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.ru.xlf @@ -26,11 +26,6 @@ Make the profile names distinct. Не удалось определить путь к временному каталогу. Рассмотрите возможность настроить переменную среды TEMP в Windows или папку локальных данных приложений в Unix. - - This is an experimental feature, set MSBuild property '{0}' to 'true' to enable it. - Это экспериментальная функция. Чтобы включить ее, присвойте свойству MSBuild "{0}" значение true. - {Locked="MSBuild"}{Locked="true"}. {0} is MSBuild property name. - File included via #:include directive (or Compile item) not found: {0} Файл, включенный через директиву #:include (или элемент Compile), не найден: {0} diff --git a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.tr.xlf b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.tr.xlf index d9db5826f9cd..7dde1f92fcfc 100644 --- a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.tr.xlf +++ b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.tr.xlf @@ -26,11 +26,6 @@ Profil adlarının birbirinden farklı olmasını sağlayın. Geçici dizin yolu saptanamıyor. Windows'da TEMP ortam değişkenini veya Unix'te yerel uygulama verileri klasörünü yapılandırmayı göz önünde bulundurun. - - This is an experimental feature, set MSBuild property '{0}' to 'true' to enable it. - Bu deneysel bir özelliktir, etkinleştirmek için '{0}' MSBuild özelliğini 'true' değerine ayarlayın. - {Locked="MSBuild"}{Locked="true"}. {0} is MSBuild property name. - File included via #:include directive (or Compile item) not found: {0} #:include yönergesiyle (veya Compile öğesi) eklenen dosya bulunamadı: {0} diff --git a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.zh-Hans.xlf b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.zh-Hans.xlf index 5eefc4c634b9..3bb783438b0b 100644 --- a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.zh-Hans.xlf +++ b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.zh-Hans.xlf @@ -26,11 +26,6 @@ Make the profile names distinct. 无法确定临时目录路径。请考虑在 Windows 上配置 TEMP 环境变量,或在 Unix 上配置本地应用数据文件夹。 - - This is an experimental feature, set MSBuild property '{0}' to 'true' to enable it. - 这是一项实验性功能,将 MSBuild 属性 '{0}' 设置为 'true' 以启用它。 - {Locked="MSBuild"}{Locked="true"}. {0} is MSBuild property name. - File included via #:include directive (or Compile item) not found: {0} 找不到通过 #:include 指令(或 Compile 项)包含的文件: {0} diff --git a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.zh-Hant.xlf b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.zh-Hant.xlf index eb15ea99c933..289859ddd39b 100644 --- a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.zh-Hant.xlf +++ b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.zh-Hant.xlf @@ -26,11 +26,6 @@ Make the profile names distinct. 無法判斷暫存 目錄路徑。考慮在 Windows 上或 Unix 上的本機應用程式資料資料資料夾上設定 TEMP 環境變數。 - - This is an experimental feature, set MSBuild property '{0}' to 'true' to enable it. - 這是實驗性功能,將 MSBuild 屬性 '{0}' 設定為 'true' 以啟用它。 - {Locked="MSBuild"}{Locked="true"}. {0} is MSBuild property name. - File included via #:include directive (or Compile item) not found: {0} 找不到透過 #:include 指示詞 (或 Compile 項目) 包含的檔案: {0} diff --git a/test/dotnet.Tests/CommandTests/Project/Convert/DotnetProjectConvertTests.cs b/test/dotnet.Tests/CommandTests/Project/Convert/DotnetProjectConvertTests.cs index 26b00a68e870..9779e5fcf489 100644 --- a/test/dotnet.Tests/CommandTests/Project/Convert/DotnetProjectConvertTests.cs +++ b/test/dotnet.Tests/CommandTests/Project/Convert/DotnetProjectConvertTests.cs @@ -1237,15 +1237,6 @@ public void Directives_IncludeExclude() { var testInstance = _testAssetsManager.CreateTestDirectory(); - File.WriteAllText(Path.Join(testInstance.Path, "Directory.Build.props"), """ - - - true - true - - - """); - VerifyConversion( baseDirectory: testInstance.Path, evaluateDirectives: true, @@ -1290,8 +1281,6 @@ public void Directives_IncludeExclude_FilesCopied() { var testInstance = _testAssetsManager.CreateTestDirectory(); File.WriteAllText(Path.Join(testInstance.Path, "Program.cs"), """ - #:property ExperimentalFileBasedProgramEnableIncludeDirective=true - #:property ExperimentalFileBasedProgramEnableExcludeDirective=true #:include **/*.cs #:include *.json #:exclude my.json @@ -2179,7 +2168,6 @@ public void DeleteSource_WithIncludeDirective() // Create entry point file with #:include directive File.WriteAllText(Path.Join(testInstance.Path, "Program.cs"), """ - #:property ExperimentalFileBasedProgramEnableIncludeDirective=true #:include Util.cs Console.WriteLine("Test"); """); @@ -2209,7 +2197,6 @@ public void DeleteSource_WithIncludeDirective_NotDeleted() // Create entry point file with #:include directive File.WriteAllText(Path.Join(testInstance.Path, "Program.cs"), """ - #:property ExperimentalFileBasedProgramEnableIncludeDirective=true #:include Util.cs Console.WriteLine("Test"); """); @@ -2240,7 +2227,6 @@ public void DeleteSource_WithIncludeDirective_MultipleFiles() // Create entry point file with multiple #:include directives File.WriteAllText(Path.Join(testInstance.Path, "Program.cs"), """ - #:property ExperimentalFileBasedProgramEnableIncludeDirective=true #:include Util.cs #:include Helper.cs #:include config.json @@ -2278,8 +2264,6 @@ public void DeleteSource_WithIncludeDirective_Transitive() // Create entry point file with #:include directive File.WriteAllText(Path.Join(testInstance.Path, "Program.cs"), """ - #:property ExperimentalFileBasedProgramEnableIncludeDirective=true - #:property ExperimentalFileBasedProgramEnableTransitiveDirectives=true #:include Util.cs Console.WriteLine("Test"); """); diff --git a/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs b/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs index cab95ebf77b3..3fb161bb2323 100644 --- a/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs +++ b/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs @@ -923,9 +923,6 @@ public static class B """); File.WriteAllText(Path.Join(testInstance.Path, "Directory.Build.props"), """ - - true - @@ -1653,14 +1650,6 @@ public void BinaryLog_EvaluationData_MultiFile() { var testInstance = _testAssetsManager.CreateTestDirectory(); - File.WriteAllText(Path.Join(testInstance.Path, "Directory.Build.props"), """ - - - true - - - """); - File.WriteAllText(Path.Join(testInstance.Path, "Program.cs"), $""" #!/usr/bin/env dotnet @@ -1826,15 +1815,6 @@ public void MissingShebangWarning() { var testInstance = _testAssetsManager.CreateTestDirectory(); - File.WriteAllText(Path.Join(testInstance.Path, "Directory.Build.props"), """ - - - true - true - - - """); - // Single-file program without shebang should NOT produce CA2266 // (the warning only fires when there are multiple files via #:include). File.WriteAllText(Path.Join(testInstance.Path, "Program.cs"), """ @@ -3247,15 +3227,6 @@ public void IncludeDirective( { var testInstance = _testAssetsManager.CreateTestDirectory(); - File.WriteAllText(Path.Join(testInstance.Path, "Directory.Build.props"), """ - - - true - true - - - """); - File.WriteAllText(Path.Join(testInstance.Path, "Program.cs"), $""" #!/usr/bin/env dotnet #:include {includePattern} @@ -3278,14 +3249,6 @@ public void IncludeDirective_WorkingDirectory() { var testInstance = _testAssetsManager.CreateTestDirectory(); - File.WriteAllText(Path.Join(testInstance.Path, "Directory.Build.props"), """ - - - true - - - """); - var srcDir = Path.Join(testInstance.Path, "src"); Directory.CreateDirectory(srcDir); @@ -3365,15 +3328,6 @@ public void IncludeDirective_Transitive() Directory.CreateDirectory(Path.Join(testInstance.Path, "dir1/dir2")); Directory.CreateDirectory(Path.Join(testInstance.Path, "dir3")); - File.WriteAllText(Path.Join(testInstance.Path, "dir1/Directory.Build.props"), """ - - - true - true - - - """); - var a = """ B.M(); """; @@ -3483,14 +3437,6 @@ public void IncludeDirective_FileNotFound() { var testInstance = _testAssetsManager.CreateTestDirectory(); - File.WriteAllText(Path.Join(testInstance.Path, "Directory.Build.props"), """ - - - true - - - """); - var programPath = Path.Join(testInstance.Path, "A.cs"); File.WriteAllText(programPath, """ @@ -3516,14 +3462,6 @@ public void IncludeDirective_UpToDate_Glob(string glob) { var testInstance = _testAssetsManager.CreateTestDirectory(); - File.WriteAllText(Path.Join(testInstance.Path, "Directory.Build.props"), """ - - - true - - - """); - var programPath = Path.Join(testInstance.Path, "Program.cs"); File.WriteAllText(programPath, $""" #!/usr/bin/env dotnet @@ -3583,14 +3521,6 @@ public void IncludeDirective_UpToDate_NoGlob() { var testInstance = _testAssetsManager.CreateTestDirectory(); - File.WriteAllText(Path.Join(testInstance.Path, "Directory.Build.props"), """ - - - true - - - """); - var programPath = Path.Join(testInstance.Path, "Program.cs"); File.WriteAllText(programPath, $""" #!/usr/bin/env dotnet @@ -3648,15 +3578,6 @@ public void IncludeDirective_UpToDate_ProjectReference() { var testInstance = _testAssetsManager.CreateTestDirectory(); - File.WriteAllText(Path.Join(testInstance.Path, "Directory.Build.props"), """ - - - true - true - - - """); - var libDir = Path.Join(testInstance.Path, "Lib"); Directory.CreateDirectory(libDir); @@ -3716,77 +3637,15 @@ class UtilClass Build(testInstance, BuildLevel.All, expectedOutput: expectedOutput, workDir: appDir); } - [Fact] - public void IncludeDirective_FeatureFlags() - { - var testInstance = _testAssetsManager.CreateTestDirectory(); - - var programPath = Path.Join(testInstance.Path, "Program.cs"); - File.WriteAllText(programPath, $""" - #!/usr/bin/env dotnet - #:include *.cs - {s_programDependingOnUtil} - """); - - var utilPath = Path.Join(testInstance.Path, "Util.cs"); - File.WriteAllText(utilPath, $""" - #:exclude Other.cs - {s_util} - """); - - new DotnetCommand(Log, "run", "Program.cs") - .WithWorkingDirectory(testInstance.Path) - .Execute() - .Should().Fail() - .And.HaveStdErr($""" - {DirectiveError(programPath, 2, Resources.ExperimentalFeatureDisabled, CSharpDirective.IncludeOrExclude.ExperimentalFileBasedProgramEnableIncludeDirective)} - - {CliCommandStrings.RunCommandException} - """); - - new DotnetCommand(Log, "run", "Program.cs") - .WithWorkingDirectory(testInstance.Path) - .WithEnvironmentVariable(CSharpDirective.IncludeOrExclude.ExperimentalFileBasedProgramEnableIncludeDirective, "true") - .Execute() - .Should().Fail() - .And.HaveStdErr($""" - {DirectiveError(utilPath, 1, Resources.ExperimentalFeatureDisabled, CSharpDirective.IncludeOrExclude.ExperimentalFileBasedProgramEnableExcludeDirective)} - - {CliCommandStrings.RunCommandException} - """); - - new DotnetCommand(Log, "run", "Program.cs") - .WithWorkingDirectory(testInstance.Path) - .WithEnvironmentVariable(CSharpDirective.IncludeOrExclude.ExperimentalFileBasedProgramEnableIncludeDirective, "true") - .WithEnvironmentVariable(CSharpDirective.IncludeOrExclude.ExperimentalFileBasedProgramEnableExcludeDirective, "true") - .Execute() - .Should().Fail() - .And.HaveStdErr($""" - {DirectiveError(utilPath, 1, Resources.ExperimentalFeatureDisabled, CSharpDirective.IncludeOrExclude.ExperimentalFileBasedProgramEnableTransitiveDirectives)} - - {CliCommandStrings.RunCommandException} - """); - - new DotnetCommand(Log, "run", "Program.cs") - .WithWorkingDirectory(testInstance.Path) - .WithEnvironmentVariable(CSharpDirective.IncludeOrExclude.ExperimentalFileBasedProgramEnableIncludeDirective, "true") - .WithEnvironmentVariable(CSharpDirective.IncludeOrExclude.ExperimentalFileBasedProgramEnableExcludeDirective, "true") - .WithEnvironmentVariable(CSharpDirective.IncludeOrExclude.ExperimentalFileBasedProgramEnableTransitiveDirectives, "true") - .Execute() - .Should().Pass() - .And.HaveStdOut("Hello, String from Util"); - } - [Fact] public void IncludeDirective_CustomMapping() { var testInstance = _testAssetsManager.CreateTestDirectory(); - File.WriteAllText(Path.Join(testInstance.Path, "Directory.Build.props"), """ + File.WriteAllText(Path.Join(testInstance.Path, "Directory.Build.props"), $""" - true - true + <{CSharpDirective.IncludeOrExclude.ExperimentalFileBasedProgramEnableItemMapping}>true """); @@ -3840,16 +3699,68 @@ public void IncludeDirective_CustomMapping() .And.HaveStdOut("Hello, String from Util"); } + /// + /// When is not enabled, + /// FileBasedProgramsItemMapping property is ignored and the default mapping is used. + /// + [Theory, PairwiseData] + public void IncludeDirective_CustomMapping_FeatureFlag( + [CombinatorialValues(null, "", "false", "off")] string? disabledFlagValue, + [CombinatorialValues("true", "on")] string enabledFlagValue) + { + var testInstance = _testAssetsManager.CreateTestDirectory(); + + if (disabledFlagValue is not null) + { + File.WriteAllText(Path.Join(testInstance.Path, "Directory.Build.props"), $""" + + + <{CSharpDirective.IncludeOrExclude.ExperimentalFileBasedProgramEnableItemMapping}>{disabledFlagValue} + + + """); + } + + File.WriteAllText(Path.Join(testInstance.Path, "Program.cs"), $""" + #!/usr/bin/env dotnet + #:property FileBasedProgramsItemMapping=.cs=Content + #:include *.cs + {s_programDependingOnUtil} + """); + + File.WriteAllText(Path.Join(testInstance.Path, "Util.cs"), s_util); + + new DotnetCommand(Log, "run", "Program.cs") + .WithWorkingDirectory(testInstance.Path) + .Execute() + .Should().Pass() + .And.HaveStdOut("Hello, String from Util"); + + File.WriteAllText(Path.Join(testInstance.Path, "Directory.Build.props"), $""" + + + <{CSharpDirective.IncludeOrExclude.ExperimentalFileBasedProgramEnableItemMapping}>{enabledFlagValue} + + + """); + + new DotnetCommand(Log, "run", "Program.cs") + .WithWorkingDirectory(testInstance.Path) + .Execute() + .Should().Fail() + // Program.cs(8,31): error CS0103: The name 'Util' does not exist in the current context + .And.HaveStdOutContaining("Program.cs(8,31): error CS0103"); + } + [Fact] public void IncludeDirective_CustomMapping_ParseErrors() { var testInstance = _testAssetsManager.CreateTestDirectory(); - File.WriteAllText(Path.Join(testInstance.Path, "Directory.Build.props"), """ + File.WriteAllText(Path.Join(testInstance.Path, "Directory.Build.props"), $""" - true - true + <{CSharpDirective.IncludeOrExclude.ExperimentalFileBasedProgramEnableItemMapping}>true """); @@ -5925,14 +5836,6 @@ public void Api_Evaluation() { var testInstance = _testAssetsManager.CreateTestDirectory(); - File.WriteAllText(Path.Join(testInstance.Path, "Directory.Build.props"), """ - - - true - - - """); - var programPath = Path.Join(testInstance.Path, "A.cs"); File.WriteAllText(programPath, """ #:property P1=cs From 6be0b8070d449b81f0b3ac0b90ab797f0fd2728a Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Thu, 9 Apr 2026 15:01:10 +0200 Subject: [PATCH 2/9] Fixup a test --- test/dotnet.Tests/CommandTests/Run/RunFileTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs b/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs index 3fb161bb2323..b4d0bdeefe81 100644 --- a/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs +++ b/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs @@ -3284,7 +3284,6 @@ static class B { public static string M() => "Hello from B"; } new DirectoryInfo(testInstance.Path) .Should().HaveSubtree(""" - Directory.Build.props src/ src/A.cs src/A/ From c3185b6bd0b36276a2b0066ebbf006068b8f6a07 Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Fri, 10 Apr 2026 12:28:44 +0200 Subject: [PATCH 3/9] Remove opt in for the mapping property too --- documentation/general/dotnet-run-file.md | 1 - .../FileLevelDirectiveHelpers.cs | 2 - .../InternalAPI.Unshipped.txt | 1 - .../VirtualProjectBuilder.cs | 10 ++- .../CommandTests/Run/RunFileTests.cs | 69 ------------------- 5 files changed, 4 insertions(+), 79 deletions(-) diff --git a/documentation/general/dotnet-run-file.md b/documentation/general/dotnet-run-file.md index 35fa9217be9f..4878e2e6bae2 100644 --- a/documentation/general/dotnet-run-file.md +++ b/documentation/general/dotnet-run-file.md @@ -215,7 +215,6 @@ 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. diff --git a/src/Cli/Microsoft.DotNet.FileBasedPrograms/FileLevelDirectiveHelpers.cs b/src/Cli/Microsoft.DotNet.FileBasedPrograms/FileLevelDirectiveHelpers.cs index 1ee7b206c5f3..469aa2d3e96e 100644 --- a/src/Cli/Microsoft.DotNet.FileBasedPrograms/FileLevelDirectiveHelpers.cs +++ b/src/Cli/Microsoft.DotNet.FileBasedPrograms/FileLevelDirectiveHelpers.cs @@ -598,8 +598,6 @@ public enum IncludeOrExcludeKind /// public sealed class IncludeOrExclude(in ParseInfo info) : Named(info) { - public const string ExperimentalFileBasedProgramEnableItemMapping = nameof(ExperimentalFileBasedProgramEnableItemMapping); - public const string MappingPropertyName = "FileBasedProgramsItemMapping"; public static string DefaultMappingString => ".cs=Compile;.resx=EmbeddedResource;.json=None;.razor=Content"; diff --git a/src/Cli/Microsoft.DotNet.FileBasedPrograms/InternalAPI.Unshipped.txt b/src/Cli/Microsoft.DotNet.FileBasedPrograms/InternalAPI.Unshipped.txt index 1b464c020ef9..04e898a70ef1 100644 --- a/src/Cli/Microsoft.DotNet.FileBasedPrograms/InternalAPI.Unshipped.txt +++ b/src/Cli/Microsoft.DotNet.FileBasedPrograms/InternalAPI.Unshipped.txt @@ -1,4 +1,3 @@ -const Microsoft.DotNet.FileBasedPrograms.CSharpDirective.IncludeOrExclude.ExperimentalFileBasedProgramEnableItemMapping = "ExperimentalFileBasedProgramEnableItemMapping" -> 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 diff --git a/src/Microsoft.DotNet.ProjectTools/VirtualProjectBuilder.cs b/src/Microsoft.DotNet.ProjectTools/VirtualProjectBuilder.cs index e2c6308d5135..00555a11cb6e 100644 --- a/src/Microsoft.DotNet.ProjectTools/VirtualProjectBuilder.cs +++ b/src/Microsoft.DotNet.ProjectTools/VirtualProjectBuilder.cs @@ -221,12 +221,10 @@ private ImmutableArray 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( diff --git a/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs b/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs index b4d0bdeefe81..60c12c5e67bd 100644 --- a/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs +++ b/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs @@ -3641,14 +3641,6 @@ public void IncludeDirective_CustomMapping() { var testInstance = _testAssetsManager.CreateTestDirectory(); - File.WriteAllText(Path.Join(testInstance.Path, "Directory.Build.props"), $""" - - - <{CSharpDirective.IncludeOrExclude.ExperimentalFileBasedProgramEnableItemMapping}>true - - - """); - var programPath = Path.Join(testInstance.Path, "Program.cs"); File.WriteAllText(programPath, $""" #!/usr/bin/env dotnet @@ -3698,72 +3690,11 @@ public void IncludeDirective_CustomMapping() .And.HaveStdOut("Hello, String from Util"); } - /// - /// When is not enabled, - /// FileBasedProgramsItemMapping property is ignored and the default mapping is used. - /// - [Theory, PairwiseData] - public void IncludeDirective_CustomMapping_FeatureFlag( - [CombinatorialValues(null, "", "false", "off")] string? disabledFlagValue, - [CombinatorialValues("true", "on")] string enabledFlagValue) - { - var testInstance = _testAssetsManager.CreateTestDirectory(); - - if (disabledFlagValue is not null) - { - File.WriteAllText(Path.Join(testInstance.Path, "Directory.Build.props"), $""" - - - <{CSharpDirective.IncludeOrExclude.ExperimentalFileBasedProgramEnableItemMapping}>{disabledFlagValue} - - - """); - } - - File.WriteAllText(Path.Join(testInstance.Path, "Program.cs"), $""" - #!/usr/bin/env dotnet - #:property FileBasedProgramsItemMapping=.cs=Content - #:include *.cs - {s_programDependingOnUtil} - """); - - File.WriteAllText(Path.Join(testInstance.Path, "Util.cs"), s_util); - - new DotnetCommand(Log, "run", "Program.cs") - .WithWorkingDirectory(testInstance.Path) - .Execute() - .Should().Pass() - .And.HaveStdOut("Hello, String from Util"); - - File.WriteAllText(Path.Join(testInstance.Path, "Directory.Build.props"), $""" - - - <{CSharpDirective.IncludeOrExclude.ExperimentalFileBasedProgramEnableItemMapping}>{enabledFlagValue} - - - """); - - new DotnetCommand(Log, "run", "Program.cs") - .WithWorkingDirectory(testInstance.Path) - .Execute() - .Should().Fail() - // Program.cs(8,31): error CS0103: The name 'Util' does not exist in the current context - .And.HaveStdOutContaining("Program.cs(8,31): error CS0103"); - } - [Fact] public void IncludeDirective_CustomMapping_ParseErrors() { var testInstance = _testAssetsManager.CreateTestDirectory(); - File.WriteAllText(Path.Join(testInstance.Path, "Directory.Build.props"), $""" - - - <{CSharpDirective.IncludeOrExclude.ExperimentalFileBasedProgramEnableItemMapping}>true - - - """); - var programPath = Path.Join(testInstance.Path, "Program.cs"); File.WriteAllText(programPath, """ #:property FileBasedProgramsItemMapping=x From 7fdf23b6f85e4015f1481fbc9c0a5dacea2021f7 Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Wed, 15 Apr 2026 10:36:08 +0200 Subject: [PATCH 4/9] Revert paragraph split --- documentation/general/dotnet-run-file.md | 1 - 1 file changed, 1 deletion(-) diff --git a/documentation/general/dotnet-run-file.md b/documentation/general/dotnet-run-file.md index 83a19586a407..49a4946936d5 100644 --- a/documentation/general/dotnet-run-file.md +++ b/documentation/general/dotnet-run-file.md @@ -261,7 +261,6 @@ 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`. - 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, From d1a16bc1be617ba671f95d20e6860213432f56f4 Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Wed, 15 Apr 2026 10:57:05 +0200 Subject: [PATCH 5/9] Revert resource string removal --- src/Microsoft.DotNet.ProjectTools/Resources.resx | 4 ++++ src/Microsoft.DotNet.ProjectTools/xlf/Resources.cs.xlf | 5 +++++ src/Microsoft.DotNet.ProjectTools/xlf/Resources.de.xlf | 5 +++++ src/Microsoft.DotNet.ProjectTools/xlf/Resources.es.xlf | 5 +++++ src/Microsoft.DotNet.ProjectTools/xlf/Resources.fr.xlf | 5 +++++ src/Microsoft.DotNet.ProjectTools/xlf/Resources.it.xlf | 5 +++++ src/Microsoft.DotNet.ProjectTools/xlf/Resources.ja.xlf | 5 +++++ src/Microsoft.DotNet.ProjectTools/xlf/Resources.ko.xlf | 5 +++++ src/Microsoft.DotNet.ProjectTools/xlf/Resources.pl.xlf | 5 +++++ src/Microsoft.DotNet.ProjectTools/xlf/Resources.pt-BR.xlf | 5 +++++ src/Microsoft.DotNet.ProjectTools/xlf/Resources.ru.xlf | 5 +++++ src/Microsoft.DotNet.ProjectTools/xlf/Resources.tr.xlf | 5 +++++ src/Microsoft.DotNet.ProjectTools/xlf/Resources.zh-Hans.xlf | 5 +++++ src/Microsoft.DotNet.ProjectTools/xlf/Resources.zh-Hant.xlf | 5 +++++ 14 files changed, 69 insertions(+) diff --git a/src/Microsoft.DotNet.ProjectTools/Resources.resx b/src/Microsoft.DotNet.ProjectTools/Resources.resx index 5c3d076e48a7..117e6b5642f2 100644 --- a/src/Microsoft.DotNet.ProjectTools/Resources.resx +++ b/src/Microsoft.DotNet.ProjectTools/Resources.resx @@ -162,4 +162,8 @@ Make the profile names distinct. File included via #:include directive (or Compile item) not found: {0} {Locked="#:include"}{Locked="Compile"}. {0} is file path. + + This is an experimental feature, set MSBuild property '{0}' to 'true' to enable it. + {Locked="MSBuild"}{Locked="true"}. {0} is MSBuild property name. + diff --git a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.cs.xlf b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.cs.xlf index 5ea201219f60..c27bf12ff8da 100644 --- a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.cs.xlf +++ b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.cs.xlf @@ -26,6 +26,11 @@ Nastavte odlišné názvy profilů. Nelze určit cestu k dočasnému adresáři. Zvažte konfiguraci proměnné prostředí TEMP v systému Windows nebo místní datové složky aplikace v systému Unix. + + This is an experimental feature, set MSBuild property '{0}' to 'true' to enable it. + Toto je experimentální funkce. Pokud ji chcete povolit, nastavte vlastnost MSBuild {0} na hodnotu true. + {Locked="MSBuild"}{Locked="true"}. {0} is MSBuild property name. + File included via #:include directive (or Compile item) not found: {0} Soubor zahrnutý prostřednictvím direktivy #:include (nebo položky Compile) nebyl nalezen: {0} diff --git a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.de.xlf b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.de.xlf index 89a14e9282cf..be6f488dfaf6 100644 --- a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.de.xlf +++ b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.de.xlf @@ -26,6 +26,11 @@ Erstellen Sie eindeutige Profilnamen. Ein temporärer Verzeichnispfad kann nicht ermittelt werden. Erwägen Sie, die TEMP-Umgebungsvariable unter Windows oder den lokalen App-Datenordner unter Unix zu konfigurieren. + + This is an experimental feature, set MSBuild property '{0}' to 'true' to enable it. + Dies ist eine experimentelle Funktion. Legen Sie die MSBuild-Eigenschaft „{0}“ auf „true“ fest, um sie zu aktivieren. + {Locked="MSBuild"}{Locked="true"}. {0} is MSBuild property name. + File included via #:include directive (or Compile item) not found: {0} Die über die #:include-Anweisung (oder das Compile-Element) eingebundene Datei wurde nicht gefunden: {0} diff --git a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.es.xlf b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.es.xlf index a45244ad1996..417c200b9dec 100644 --- a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.es.xlf +++ b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.es.xlf @@ -26,6 +26,11 @@ Defina nombres de perfiles distintos. No se puede determinar una ruta de acceso temporal al directorio. Considere la posibilidad de configurar la variable de entorno TEMP en Windows o la carpeta de datos de la aplicación local en Unix. + + This is an experimental feature, set MSBuild property '{0}' to 'true' to enable it. + Se trata de una característica experimental, establezca la propiedad "{0}" de MSBuild en "true" para habilitarla. + {Locked="MSBuild"}{Locked="true"}. {0} is MSBuild property name. + File included via #:include directive (or Compile item) not found: {0} No se encuentra el archivo incluido mediante la directiva #:include (o elemento Compile): {0} diff --git a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.fr.xlf b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.fr.xlf index 56d5f4507a0b..b9afff50e4be 100644 --- a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.fr.xlf +++ b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.fr.xlf @@ -26,6 +26,11 @@ Faites en sorte que les noms de profil soient distincts. Impossible de déterminer un chemin d’accès pour le répertoire temporaire. Nous vous recommandons de configurer la variable d’environnement TEMP sous Windows ou le dossier des données d’application locale sous Unix. + + This is an experimental feature, set MSBuild property '{0}' to 'true' to enable it. + Il s’agit d’une fonctionnalité expérimentale, définissez la propriété MSBuild « {0} » sur « true » pour l’activer. + {Locked="MSBuild"}{Locked="true"}. {0} is MSBuild property name. + File included via #:include directive (or Compile item) not found: {0} Fichier inclus via la directive #:include (ou élément Compile) introuvable : {0} diff --git a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.it.xlf b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.it.xlf index e2c70ec1c9a8..b81b4e946977 100644 --- a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.it.xlf +++ b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.it.xlf @@ -26,6 +26,11 @@ Rendi distinti i nomi profilo. Non è possibile determinare un percorso per la directory temporanea. Considerare la configurazione della variabile di ambiente TEMP in Windows o della cartella dei dati locali dell'app in Unix. + + This is an experimental feature, set MSBuild property '{0}' to 'true' to enable it. + Questa è una funzionalità sperimentale, impostare la proprietà MSBuild "{0}" su "true" per abilitarla. + {Locked="MSBuild"}{Locked="true"}. {0} is MSBuild property name. + File included via #:include directive (or Compile item) not found: {0} File incluso tramite direttiva #:include (o elemento Compile) non trovato: {0} diff --git a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.ja.xlf b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.ja.xlf index 30ea3f04d6ae..0e0a34c9824b 100644 --- a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.ja.xlf +++ b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.ja.xlf @@ -26,6 +26,11 @@ Make the profile names distinct. 一時ディレクトリ パスを特定できません。Windows で TEMP 環境変数を構成するか、Unix でローカル アプリ データ フォルダーを構成することを検討してください。 + + This is an experimental feature, set MSBuild property '{0}' to 'true' to enable it. + これは試験的な機能です。有効にするには、MSBuild プロパティ '{0}' を 'true' に設定してください。 + {Locked="MSBuild"}{Locked="true"}. {0} is MSBuild property name. + File included via #:include directive (or Compile item) not found: {0} #:include ディレクティブ (または Compile 項目) を介して含められているファイルが見つかりません: {0} diff --git a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.ko.xlf b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.ko.xlf index e85d485b04c6..047b32c0f0e0 100644 --- a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.ko.xlf +++ b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.ko.xlf @@ -26,6 +26,11 @@ Make the profile names distinct. 임시 디렉터리 경로를 확인할 수 없습니다. Windows에서는 TEMP 환경 변수를, Unix에서는 로컬 앱 데이터 폴더를 설정하는 것이 좋습니다. + + This is an experimental feature, set MSBuild property '{0}' to 'true' to enable it. + 이 기능은 실험적인 기능입니다. 사용하려면 MSBuild 속성 '{0}'을(를) 'true'로 설정하세요. + {Locked="MSBuild"}{Locked="true"}. {0} is MSBuild property name. + File included via #:include directive (or Compile item) not found: {0} #:include 지시문(또는 Compile 항목)을 통해 포함된 파일을 찾을 수 없음: {0} diff --git a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.pl.xlf b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.pl.xlf index 34b6e91e8875..cb88c3488035 100644 --- a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.pl.xlf +++ b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.pl.xlf @@ -26,6 +26,11 @@ Rozróżnij nazwy profilów. Nie można określić tymczasowej ścieżki katalogu. Rozważ skonfigurowanie zmiennej środowiskowej TEMP w systemie Windows lub folderze danych aplikacji lokalnej w systemie Unix. + + This is an experimental feature, set MSBuild property '{0}' to 'true' to enable it. + To funkcja eksperymentalna, ustaw właściwość MSBuild „{0}” na wartość „true”, aby ją włączyć. + {Locked="MSBuild"}{Locked="true"}. {0} is MSBuild property name. + File included via #:include directive (or Compile item) not found: {0} Nie znaleziono pliku dołączonego za pomocą dyrektywy #:include (lub elementu Compile): {0} diff --git a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.pt-BR.xlf b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.pt-BR.xlf index 3ef349070949..904cd0a49833 100644 --- a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.pt-BR.xlf +++ b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.pt-BR.xlf @@ -26,6 +26,11 @@ Diferencie os nomes dos perfis. Não é possível determinar um caminho de diretório temporário. Considere configurar a variável de ambiente TEMP no Windows ou a pasta de dados do aplicativo local no Unix. + + This is an experimental feature, set MSBuild property '{0}' to 'true' to enable it. + Esse é um recurso experimental, defina a propriedade "{0}" do MSBuild como "true" para habilitá-lo. + {Locked="MSBuild"}{Locked="true"}. {0} is MSBuild property name. + File included via #:include directive (or Compile item) not found: {0} Arquivo incluído por meio da diretiva #:include (ou item de Compile) não encontrado: {0} diff --git a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.ru.xlf b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.ru.xlf index df4550afc339..11a9bdc7fae9 100644 --- a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.ru.xlf +++ b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.ru.xlf @@ -26,6 +26,11 @@ Make the profile names distinct. Не удалось определить путь к временному каталогу. Рассмотрите возможность настроить переменную среды TEMP в Windows или папку локальных данных приложений в Unix. + + This is an experimental feature, set MSBuild property '{0}' to 'true' to enable it. + Это экспериментальная функция. Чтобы включить ее, присвойте свойству MSBuild "{0}" значение true. + {Locked="MSBuild"}{Locked="true"}. {0} is MSBuild property name. + File included via #:include directive (or Compile item) not found: {0} Файл, включенный через директиву #:include (или элемент Compile), не найден: {0} diff --git a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.tr.xlf b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.tr.xlf index 7dde1f92fcfc..d9db5826f9cd 100644 --- a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.tr.xlf +++ b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.tr.xlf @@ -26,6 +26,11 @@ Profil adlarının birbirinden farklı olmasını sağlayın. Geçici dizin yolu saptanamıyor. Windows'da TEMP ortam değişkenini veya Unix'te yerel uygulama verileri klasörünü yapılandırmayı göz önünde bulundurun. + + This is an experimental feature, set MSBuild property '{0}' to 'true' to enable it. + Bu deneysel bir özelliktir, etkinleştirmek için '{0}' MSBuild özelliğini 'true' değerine ayarlayın. + {Locked="MSBuild"}{Locked="true"}. {0} is MSBuild property name. + File included via #:include directive (or Compile item) not found: {0} #:include yönergesiyle (veya Compile öğesi) eklenen dosya bulunamadı: {0} diff --git a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.zh-Hans.xlf b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.zh-Hans.xlf index 3bb783438b0b..5eefc4c634b9 100644 --- a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.zh-Hans.xlf +++ b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.zh-Hans.xlf @@ -26,6 +26,11 @@ Make the profile names distinct. 无法确定临时目录路径。请考虑在 Windows 上配置 TEMP 环境变量,或在 Unix 上配置本地应用数据文件夹。 + + This is an experimental feature, set MSBuild property '{0}' to 'true' to enable it. + 这是一项实验性功能,将 MSBuild 属性 '{0}' 设置为 'true' 以启用它。 + {Locked="MSBuild"}{Locked="true"}. {0} is MSBuild property name. + File included via #:include directive (or Compile item) not found: {0} 找不到通过 #:include 指令(或 Compile 项)包含的文件: {0} diff --git a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.zh-Hant.xlf b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.zh-Hant.xlf index 289859ddd39b..eb15ea99c933 100644 --- a/src/Microsoft.DotNet.ProjectTools/xlf/Resources.zh-Hant.xlf +++ b/src/Microsoft.DotNet.ProjectTools/xlf/Resources.zh-Hant.xlf @@ -26,6 +26,11 @@ Make the profile names distinct. 無法判斷暫存 目錄路徑。考慮在 Windows 上或 Unix 上的本機應用程式資料資料資料夾上設定 TEMP 環境變數。 + + This is an experimental feature, set MSBuild property '{0}' to 'true' to enable it. + 這是實驗性功能,將 MSBuild 屬性 '{0}' 設定為 'true' 以啟用它。 + {Locked="MSBuild"}{Locked="true"}. {0} is MSBuild property name. + File included via #:include directive (or Compile item) not found: {0} 找不到透過 #:include 指示詞 (或 Compile 項目) 包含的檔案: {0} From 2b0724d2a4c2d2dd0c595c0440ea6bee1bedb5d5 Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Wed, 15 Apr 2026 11:34:55 +0200 Subject: [PATCH 6/9] Fixup after merge --- test/dotnet.Tests/CommandTests/Run/RunFileTests.cs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs b/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs index 4328aff9ad7f..d6dd6d026cf3 100644 --- a/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs +++ b/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs @@ -3647,7 +3647,6 @@ public void RefDirective_WithInclude() <{CSharpDirective.Ref.ExperimentalFileBasedProgramEnableRefDirective}>true - <{CSharpDirective.IncludeOrExclude.ExperimentalFileBasedProgramEnableIncludeDirective}>true """); @@ -3827,8 +3826,6 @@ public void RefDirective_DuplicateRefFromIncludedFiles() <{CSharpDirective.Ref.ExperimentalFileBasedProgramEnableRefDirective}>true - <{CSharpDirective.IncludeOrExclude.ExperimentalFileBasedProgramEnableIncludeDirective}>true - <{CSharpDirective.IncludeOrExclude.ExperimentalFileBasedProgramEnableTransitiveDirectives}>true """); @@ -3886,8 +3883,6 @@ public void RefDirective_DuplicateRefFromIncludedFiles_Subdirectories() <{CSharpDirective.Ref.ExperimentalFileBasedProgramEnableRefDirective}>true - <{CSharpDirective.IncludeOrExclude.ExperimentalFileBasedProgramEnableIncludeDirective}>true - <{CSharpDirective.IncludeOrExclude.ExperimentalFileBasedProgramEnableTransitiveDirectives}>true """); @@ -3952,8 +3947,6 @@ public void RefDirective_IncludeAndRefSameFile() <{CSharpDirective.Ref.ExperimentalFileBasedProgramEnableRefDirective}>true - <{CSharpDirective.IncludeOrExclude.ExperimentalFileBasedProgramEnableIncludeDirective}>true - <{CSharpDirective.IncludeOrExclude.ExperimentalFileBasedProgramEnableTransitiveDirectives}>true """); From 232b4f221f4836e54f7b9fd0857669c9875c8473 Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Wed, 15 Apr 2026 13:25:59 +0200 Subject: [PATCH 7/9] Fixup after merge --- src/Microsoft.DotNet.ProjectTools/VirtualProjectBuilder.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Microsoft.DotNet.ProjectTools/VirtualProjectBuilder.cs b/src/Microsoft.DotNet.ProjectTools/VirtualProjectBuilder.cs index b22e24c0d5fc..64797417230f 100644 --- a/src/Microsoft.DotNet.ProjectTools/VirtualProjectBuilder.cs +++ b/src/Microsoft.DotNet.ProjectTools/VirtualProjectBuilder.cs @@ -307,6 +307,8 @@ internal void CreateProjectInstance( evaluatedDirectives, addGlobalProperties); + CheckDirectives(project, evaluatedDirectives, reportError); + return; } @@ -354,6 +356,8 @@ internal void CreateProjectInstance( evaluatedDirectives = evaluatedDirectiveBuilder.ToImmutable(); _evaluatedDirectives = (directivesOriginal, evaluatedDirectives); + CheckDirectives(project, evaluatedDirectives, reportError); + bool TryGetNextFileToProcess() { while (filesToProcess.TryDequeue(out var filePath)) From 242509c93528bce796465c2bd583a108f6e2a525 Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Thu, 16 Apr 2026 11:10:13 +0200 Subject: [PATCH 8/9] Re-introduce a feature flag for transitive directives --- documentation/general/dotnet-run-file.md | 1 + .../FileLevelDirectiveHelpers.cs | 1 + .../InternalAPI.Unshipped.txt | 1 + .../VirtualProjectBuilder.cs | 6 ++ .../Convert/DotnetProjectConvertTests.cs | 8 +++ .../CommandTests/Run/RunFileTests.cs | 60 +++++++++++++++++++ 6 files changed, 77 insertions(+) diff --git a/documentation/general/dotnet-run-file.md b/documentation/general/dotnet-run-file.md index 49a4946936d5..dfcb07822795 100644 --- a/documentation/general/dotnet-run-file.md +++ b/documentation/general/dotnet-run-file.md @@ -261,6 +261,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`.) 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, diff --git a/src/Cli/Microsoft.DotNet.FileBasedPrograms/FileLevelDirectiveHelpers.cs b/src/Cli/Microsoft.DotNet.FileBasedPrograms/FileLevelDirectiveHelpers.cs index 2303d48b4c2e..65fd17f44bdb 100644 --- a/src/Cli/Microsoft.DotNet.FileBasedPrograms/FileLevelDirectiveHelpers.cs +++ b/src/Cli/Microsoft.DotNet.FileBasedPrograms/FileLevelDirectiveHelpers.cs @@ -693,6 +693,7 @@ public enum IncludeOrExcludeKind /// public sealed class IncludeOrExclude(in ParseInfo info) : Named(info) { + public const string ExperimentalFileBasedProgramEnableTransitiveDirectives = nameof(ExperimentalFileBasedProgramEnableTransitiveDirectives); public const string MappingPropertyName = "FileBasedProgramsItemMapping"; public static string DefaultMappingString => ".cs=Compile;.resx=EmbeddedResource;.json=None;.razor=Content"; diff --git a/src/Cli/Microsoft.DotNet.FileBasedPrograms/InternalAPI.Unshipped.txt b/src/Cli/Microsoft.DotNet.FileBasedPrograms/InternalAPI.Unshipped.txt index cb463f26bf99..d3a08eaa3a3e 100644 --- a/src/Cli/Microsoft.DotNet.FileBasedPrograms/InternalAPI.Unshipped.txt +++ b/src/Cli/Microsoft.DotNet.FileBasedPrograms/InternalAPI.Unshipped.txt @@ -1,3 +1,4 @@ +const Microsoft.DotNet.FileBasedPrograms.CSharpDirective.IncludeOrExclude.ExperimentalFileBasedProgramEnableTransitiveDirectives = "ExperimentalFileBasedProgramEnableTransitiveDirectives" -> string! const Microsoft.DotNet.FileBasedPrograms.CSharpDirective.IncludeOrExclude.MappingPropertyName = "FileBasedProgramsItemMapping" -> string! const Microsoft.DotNet.FileBasedPrograms.CSharpDirective.Ref.ExperimentalFileBasedProgramEnableRefDirective = "ExperimentalFileBasedProgramEnableRefDirective" -> string! Microsoft.DotNet.FileBasedPrograms.CSharpDirective diff --git a/src/Microsoft.DotNet.ProjectTools/VirtualProjectBuilder.cs b/src/Microsoft.DotNet.ProjectTools/VirtualProjectBuilder.cs index 64797417230f..50f18abe9265 100644 --- a/src/Microsoft.DotNet.ProjectTools/VirtualProjectBuilder.cs +++ b/src/Microsoft.DotNet.ProjectTools/VirtualProjectBuilder.cs @@ -437,6 +437,7 @@ private void CheckDirectives( ErrorReporter reportError) { bool? refEnabled = null; + bool? transitiveEnabled = null; foreach (var directive in directives) { @@ -444,6 +445,11 @@ private void CheckDirectives( { CheckFlagEnabled(ref refEnabled, CSharpDirective.Ref.ExperimentalFileBasedProgramEnableRefDirective, directive); } + + if (directive.Info.SourceFile.Path != EntryPointSourceFile.Path) + { + CheckFlagEnabled(ref transitiveEnabled, CSharpDirective.IncludeOrExclude.ExperimentalFileBasedProgramEnableTransitiveDirectives, directive); + } } void CheckFlagEnabled(ref bool? flag, string flagName, CSharpDirective directive) diff --git a/test/dotnet.Tests/CommandTests/Project/Convert/DotnetProjectConvertTests.cs b/test/dotnet.Tests/CommandTests/Project/Convert/DotnetProjectConvertTests.cs index 2f9696aaf9a9..1d384e3d5344 100644 --- a/test/dotnet.Tests/CommandTests/Project/Convert/DotnetProjectConvertTests.cs +++ b/test/dotnet.Tests/CommandTests/Project/Convert/DotnetProjectConvertTests.cs @@ -2715,6 +2715,14 @@ public void DeleteSource_WithIncludeDirective_Transitive() { var testInstance = _testAssetsManager.CreateTestDirectory(); + File.WriteAllText(Path.Join(testInstance.Path, "Directory.Build.props"), """ + + + true + + + """); + // Create entry point file with #:include directive File.WriteAllText(Path.Join(testInstance.Path, "Program.cs"), """ #:include Util.cs diff --git a/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs b/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs index d6dd6d026cf3..312fe70aa99c 100644 --- a/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs +++ b/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs @@ -997,6 +997,9 @@ public static class B """); File.WriteAllText(Path.Join(testInstance.Path, "Directory.Build.props"), """ + + true + @@ -3826,6 +3829,7 @@ public void RefDirective_DuplicateRefFromIncludedFiles() <{CSharpDirective.Ref.ExperimentalFileBasedProgramEnableRefDirective}>true + <{CSharpDirective.IncludeOrExclude.ExperimentalFileBasedProgramEnableTransitiveDirectives}>true """); @@ -3883,6 +3887,7 @@ public void RefDirective_DuplicateRefFromIncludedFiles_Subdirectories() <{CSharpDirective.Ref.ExperimentalFileBasedProgramEnableRefDirective}>true + <{CSharpDirective.IncludeOrExclude.ExperimentalFileBasedProgramEnableTransitiveDirectives}>true """); @@ -3947,6 +3952,7 @@ public void RefDirective_IncludeAndRefSameFile() <{CSharpDirective.Ref.ExperimentalFileBasedProgramEnableRefDirective}>true + <{CSharpDirective.IncludeOrExclude.ExperimentalFileBasedProgramEnableTransitiveDirectives}>true """); @@ -4082,6 +4088,14 @@ public void IncludeDirective_Transitive() Directory.CreateDirectory(Path.Join(testInstance.Path, "dir1/dir2")); Directory.CreateDirectory(Path.Join(testInstance.Path, "dir3")); + File.WriteAllText(Path.Join(testInstance.Path, "dir1/Directory.Build.props"), $""" + + + <{CSharpDirective.IncludeOrExclude.ExperimentalFileBasedProgramEnableTransitiveDirectives}>true + + + """); + var a = """ B.M(); """; @@ -4332,6 +4346,14 @@ public void IncludeDirective_UpToDate_ProjectReference() { var testInstance = _testAssetsManager.CreateTestDirectory(); + File.WriteAllText(Path.Join(testInstance.Path, "Directory.Build.props"), $""" + + + <{CSharpDirective.IncludeOrExclude.ExperimentalFileBasedProgramEnableTransitiveDirectives}>true + + + """); + var libDir = Path.Join(testInstance.Path, "Lib"); Directory.CreateDirectory(libDir); @@ -4391,6 +4413,44 @@ class UtilClass Build(testInstance, BuildLevel.All, expectedOutput: expectedOutput, workDir: appDir); } + /// + /// Transitive directives (directives in non-entry-point files) are gated behind a feature flag. + /// + [Fact] + public void IncludeDirective_FeatureFlags() + { + var testInstance = _testAssetsManager.CreateTestDirectory(); + + File.WriteAllText(Path.Join(testInstance.Path, "Program.cs"), """ + #!/usr/bin/env dotnet + #:include Util.cs + Console.WriteLine(Util.M()); + """); + + var utilPath = Path.Join(testInstance.Path, "Util.cs"); + File.WriteAllText(utilPath, """ + #:property DefineConstants=MY_CONST + static class Util { public static string M() => "Hello from Util"; } + """); + + new DotnetCommand(Log, "run", "Program.cs") + .WithWorkingDirectory(testInstance.Path) + .Execute() + .Should().Fail() + .And.HaveStdErr($""" + {DirectiveError(utilPath, 1, Resources.ExperimentalFeatureDisabled, CSharpDirective.IncludeOrExclude.ExperimentalFileBasedProgramEnableTransitiveDirectives)} + + {CliCommandStrings.RunCommandException} + """); + + new DotnetCommand(Log, "run", "Program.cs") + .WithWorkingDirectory(testInstance.Path) + .WithEnvironmentVariable(CSharpDirective.IncludeOrExclude.ExperimentalFileBasedProgramEnableTransitiveDirectives, "true") + .Execute() + .Should().Pass() + .And.HaveStdOut("Hello from Util"); + } + [Fact] public void IncludeDirective_CustomMapping() { From efe50eb3fc5665e796bc65c9463065215cc5e30c Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Thu, 16 Apr 2026 11:16:03 +0200 Subject: [PATCH 9/9] Improve --- .../Project/Convert/DotnetProjectConvertTests.cs | 14 +++----------- test/dotnet.Tests/CommandTests/Run/RunFileTests.cs | 4 ++-- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/test/dotnet.Tests/CommandTests/Project/Convert/DotnetProjectConvertTests.cs b/test/dotnet.Tests/CommandTests/Project/Convert/DotnetProjectConvertTests.cs index 1d384e3d5344..b06c4aae98f6 100644 --- a/test/dotnet.Tests/CommandTests/Project/Convert/DotnetProjectConvertTests.cs +++ b/test/dotnet.Tests/CommandTests/Project/Convert/DotnetProjectConvertTests.cs @@ -448,8 +448,7 @@ public void RefDirective_DuplicateFolderName_ViaInclude() <{CSharpDirective.Ref.ExperimentalFileBasedProgramEnableRefDirective}>true - true - true + <{CSharpDirective.IncludeOrExclude.ExperimentalFileBasedProgramEnableTransitiveDirectives}>true """); @@ -2715,16 +2714,9 @@ public void DeleteSource_WithIncludeDirective_Transitive() { var testInstance = _testAssetsManager.CreateTestDirectory(); - File.WriteAllText(Path.Join(testInstance.Path, "Directory.Build.props"), """ - - - true - - - """); - // Create entry point file with #:include directive - File.WriteAllText(Path.Join(testInstance.Path, "Program.cs"), """ + File.WriteAllText(Path.Join(testInstance.Path, "Program.cs"), $""" + #:property {CSharpDirective.IncludeOrExclude.ExperimentalFileBasedProgramEnableTransitiveDirectives}=true #:include Util.cs Console.WriteLine("Test"); """); diff --git a/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs b/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs index 312fe70aa99c..401dae81a289 100644 --- a/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs +++ b/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs @@ -995,10 +995,10 @@ public static class B public static string M() => "String from Util"; } """); - File.WriteAllText(Path.Join(testInstance.Path, "Directory.Build.props"), """ + File.WriteAllText(Path.Join(testInstance.Path, "Directory.Build.props"), $""" - true + <{CSharpDirective.IncludeOrExclude.ExperimentalFileBasedProgramEnableTransitiveDirectives}>true