From 48a663adc508c8c9d68cd66017de15242535f658 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Wed, 22 Oct 2025 14:51:31 -0500 Subject: [PATCH 1/8] [dotnet-cli] prompt for target framework using `Spectre.Console` Implements the first part of the spec mentioned in: https://github.com/dotnet/sdk/blob/522c88a6abfc4a011556f839d15844d07ba62cd9/documentation/specs/dotnet-run-for-maui.md?plain=1#L35-L46 Add interactive target framework selection to `dotnet run` When running a multi-targeted project without specifying `--framework`, `dotnet run` now: * Prompts interactively (using `Spectre.Console`) to select a framework with arrow keys * Shows a formatted error list in non-interactive mode with available frameworks * Handles selection early before project build/evaluation * Removes redundant multi-TFM error checking from `ThrowUnableToRunError()` * Adds a few unit tests to validate these changes. This is currently WIP, as it introduces a "pre-built" `Spectre.Console` that I will need to setup in source-build in a PRs elsewhere. --- Directory.Packages.props | 1 + .../dotnet/Commands/CliCommandStrings.resx | 12 + src/Cli/dotnet/Commands/Run/RunCommand.cs | 52 +++- .../Commands/Run/TargetFrameworkSelector.cs | 100 +++++++ .../Commands/xlf/CliCommandStrings.cs.xlf | 20 ++ .../Commands/xlf/CliCommandStrings.de.xlf | 20 ++ .../Commands/xlf/CliCommandStrings.es.xlf | 20 ++ .../Commands/xlf/CliCommandStrings.fr.xlf | 20 ++ .../Commands/xlf/CliCommandStrings.it.xlf | 20 ++ .../Commands/xlf/CliCommandStrings.ja.xlf | 20 ++ .../Commands/xlf/CliCommandStrings.ko.xlf | 20 ++ .../Commands/xlf/CliCommandStrings.pl.xlf | 20 ++ .../Commands/xlf/CliCommandStrings.pt-BR.xlf | 20 ++ .../Commands/xlf/CliCommandStrings.ru.xlf | 20 ++ .../Commands/xlf/CliCommandStrings.tr.xlf | 20 ++ .../xlf/CliCommandStrings.zh-Hans.xlf | 20 ++ .../xlf/CliCommandStrings.zh-Hant.xlf | 20 ++ src/Cli/dotnet/dotnet.csproj | 1 + .../DotnetRunMultiTarget.csproj | 8 + .../DotnetRunMultiTarget/Program.cs | 17 ++ .../GivenDotnetRunSelectsTargetFramework.cs | 256 ++++++++++++++++++ 21 files changed, 696 insertions(+), 11 deletions(-) create mode 100644 src/Cli/dotnet/Commands/Run/TargetFrameworkSelector.cs create mode 100644 test/TestAssets/TestProjects/DotnetRunMultiTarget/DotnetRunMultiTarget.csproj create mode 100644 test/TestAssets/TestProjects/DotnetRunMultiTarget/Program.cs create mode 100644 test/dotnet.Tests/CommandTests/Run/GivenDotnetRunSelectsTargetFramework.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index 788923bf48d6..f3872d8af5b9 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -113,6 +113,7 @@ + diff --git a/src/Cli/dotnet/Commands/CliCommandStrings.resx b/src/Cli/dotnet/Commands/CliCommandStrings.resx index 6ca03e2f5fe6..5a01a54c293b 100644 --- a/src/Cli/dotnet/Commands/CliCommandStrings.resx +++ b/src/Cli/dotnet/Commands/CliCommandStrings.resx @@ -1790,6 +1790,18 @@ The current OutputType is '{2}'. Unable to run your project Your project targets multiple frameworks. Specify which framework to run using '{0}'. + + Select the target framework to run: + + + Move up and down to reveal more frameworks + + + Available target frameworks: + + + Example + Warning NETSDK1174: The abbreviation of -p for --project is deprecated. Please use --project. {Locked="--project"} diff --git a/src/Cli/dotnet/Commands/Run/RunCommand.cs b/src/Cli/dotnet/Commands/Run/RunCommand.cs index 33ec7b5e3219..5d26d32ba5e6 100644 --- a/src/Cli/dotnet/Commands/Run/RunCommand.cs +++ b/src/Cli/dotnet/Commands/Run/RunCommand.cs @@ -55,7 +55,7 @@ public class RunCommand /// /// Parsed structure representing the MSBuild arguments that will be used to build the project. /// - public MSBuildArgs MSBuildArgs { get; } + public MSBuildArgs MSBuildArgs { get; private set; } public bool Interactive { get; } /// @@ -122,6 +122,12 @@ public int Execute() return 1; } + // Pre-run evaluation: Handle target framework selection for multi-targeted projects + if (ProjectFileFullPath is not null && !TrySelectTargetFrameworkIfNeeded()) + { + return 1; + } + Func? projectFactory = null; RunProperties? cachedRunProperties = null; VirtualProjectBuildingCommand? virtualCommand = null; @@ -180,6 +186,40 @@ public int Execute() } } + /// + /// Checks if target framework selection is needed for multi-targeted projects. + /// If needed and we're in interactive mode, prompts the user to select a framework. + /// If needed and we're in non-interactive mode, shows an error. + /// + /// True if we can continue, false if we should exit + private bool TrySelectTargetFrameworkIfNeeded() + { + Debug.Assert(ProjectFileFullPath is not null); + + var globalProperties = CommonRunHelpers.GetGlobalPropertiesFromArgs(MSBuildArgs); + + // Check if we're in an interactive terminal + bool isInteractive = !Console.IsInputRedirected && !Console.IsOutputRedirected; + + if (TargetFrameworkSelector.TrySelectTargetFramework( + ProjectFileFullPath, + globalProperties, + isInteractive, + out string? selectedFramework)) + { + // If a framework was selected, add it to MSBuildArgs + if (selectedFramework is not null) + { + var additionalProperties = new ReadOnlyDictionary( + new Dictionary { { "TargetFramework", selectedFramework } }); + MSBuildArgs = MSBuildArgs.CloneWithAdditionalProperties(additionalProperties); + } + return true; + } + + return false; + } + internal void ApplyLaunchSettingsProfileToCommand(ICommand targetCommand, ProjectLaunchSettingsModel? launchSettings) { if (launchSettings == null) @@ -499,16 +539,6 @@ static void InvokeRunArgumentsTarget(ProjectInstance project, bool noBuild, Faca internal static void ThrowUnableToRunError(ProjectInstance project) { - string targetFrameworks = project.GetPropertyValue("TargetFrameworks"); - if (!string.IsNullOrEmpty(targetFrameworks)) - { - string targetFramework = project.GetPropertyValue("TargetFramework"); - if (string.IsNullOrEmpty(targetFramework)) - { - throw new GracefulException(CliCommandStrings.RunCommandExceptionUnableToRunSpecifyFramework, "--framework"); - } - } - throw new GracefulException( string.Format( CliCommandStrings.RunCommandExceptionUnableToRun, diff --git a/src/Cli/dotnet/Commands/Run/TargetFrameworkSelector.cs b/src/Cli/dotnet/Commands/Run/TargetFrameworkSelector.cs new file mode 100644 index 000000000000..a9a13cc16199 --- /dev/null +++ b/src/Cli/dotnet/Commands/Run/TargetFrameworkSelector.cs @@ -0,0 +1,100 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Build.Evaluation; +using Microsoft.DotNet.Cli.Utils; +using Spectre.Console; + +namespace Microsoft.DotNet.Cli.Commands.Run; + +internal static class TargetFrameworkSelector +{ + /// + /// Evaluates the project to determine if target framework selection is needed. + /// If the project has multiple target frameworks and none was specified, prompts the user to select one. + /// + /// Path to the project file + /// Global properties for MSBuild evaluation + /// Whether we're running in interactive mode (can prompt user) + /// The selected target framework, or null if not needed + /// True if we should continue, false if we should exit with error + public static bool TrySelectTargetFramework( + string projectFilePath, + Dictionary globalProperties, + bool isInteractive, + out string? selectedFramework) + { + selectedFramework = null; + + // If a framework is already specified, no need to prompt + if (globalProperties.TryGetValue("TargetFramework", out var existingFramework) && !string.IsNullOrWhiteSpace(existingFramework)) + { + return true; + } + + // Evaluate the project to get TargetFrameworks + using var collection = new ProjectCollection(globalProperties: globalProperties); + var project = collection.LoadProject(projectFilePath); + + string targetFrameworks = project.GetPropertyValue("TargetFrameworks"); + + // If there's no TargetFrameworks property or only one framework, no selection needed + if (string.IsNullOrWhiteSpace(targetFrameworks)) + { + return true; + } + + var frameworks = targetFrameworks.Split(';', StringSplitOptions.RemoveEmptyEntries); + + // If there's only one framework, no selection needed + if (frameworks.Length <= 1) + { + return true; + } + + if (isInteractive) + { + selectedFramework = PromptForTargetFramework(frameworks); + return selectedFramework != null; + } + else + { + Reporter.Error.WriteLine(string.Format(CliCommandStrings.RunCommandExceptionUnableToRunSpecifyFramework, "--framework")); + Reporter.Error.WriteLine(); + Reporter.Error.WriteLine(CliCommandStrings.RunCommandAvailableTargetFrameworks); + Reporter.Error.WriteLine(); + + for (int i = 0; i < frameworks.Length; i++) + { + Reporter.Error.WriteLine($" {i + 1}. {frameworks[i]}"); + } + + Reporter.Error.WriteLine(); + Reporter.Error.WriteLine($"{CliCommandStrings.RunCommandExampleText}: dotnet run --framework {frameworks[0]}"); + Reporter.Error.WriteLine(); + return false; + } + } + + /// + /// Prompts the user to select a target framework from the available options using Spectre.Console. + /// + private static string? PromptForTargetFramework(string[] frameworks) + { + try + { + var prompt = new SelectionPrompt() + .Title($"[cyan]{Markup.Escape(CliCommandStrings.RunCommandSelectTargetFrameworkPrompt)}[/]") + .PageSize(10) + .MoreChoicesText($"[grey]({Markup.Escape(CliCommandStrings.RunCommandMoreFrameworksText)})[/]") + .AddChoices(frameworks); + + return Spectre.Console.AnsiConsole.Prompt(prompt); + } + catch (Exception) + { + // If Spectre.Console fails (e.g., terminal doesn't support it), return null + return null; + } + } +} diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.cs.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.cs.xlf index d1d8709624e6..f987124ae574 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.cs.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.cs.xlf @@ -2702,6 +2702,11 @@ Ve výchozím nastavení je publikována aplikace závislá na architektuře.Příkaz rozhraní .NET pro spuštění + + Available target frameworks: + Available target frameworks: + + Building... Sestavování... @@ -2712,6 +2717,11 @@ Ve výchozím nastavení je publikována aplikace závislá na architektuře.Spuštění cíle {0} ke zjištění příkazů spuštění pro tento projekt se nezdařilo. Opravte chyby a upozornění a spusťte je znovu. {0} is the name of an MSBuild target + + Example + Example + + The build failed. Fix the build errors and run again. Sestavení se nepovedlo. Opravte v sestavení chyby a spusťte ho znovu. @@ -2759,11 +2769,21 @@ Your project targets multiple frameworks. Specify which framework to run using ' Cílem projektu je více architektur. Pomocí parametru {0} určete, která architektura se má spustit. + + Move up and down to reveal more frameworks + Move up and down to reveal more frameworks + + Warning NETSDK1174: The abbreviation of -p for --project is deprecated. Please use --project. Upozornění NETSDK1174: Zkratka -p pro --project je zastaralá. Použijte prosím --project. {Locked="--project"} + + Select the target framework to run: + Select the target framework to run: + + '{0}' is not a valid project file. {0} není platný soubor projektu. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.de.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.de.xlf index 2a39befb9d3c..a394a608bdd8 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.de.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.de.xlf @@ -2702,6 +2702,11 @@ Standardmäßig wird eine Framework-abhängige Anwendung veröffentlicht..NET-Befehl "Run" + + Available target frameworks: + Available target frameworks: + + Building... Buildvorgang wird ausgeführt... @@ -2712,6 +2717,11 @@ Standardmäßig wird eine Framework-abhängige Anwendung veröffentlicht.Das {0} Ziel ausführen, um zu ermitteln, dass ein Fehler bei den Ausführungsbefehlen für dieses Projekt aufgetreten ist. Beheben Sie die Fehler und Warnungen, und führen Sie dies erneut aus. {0} is the name of an MSBuild target + + Example + Example + + The build failed. Fix the build errors and run again. Fehler beim Buildvorgang. Beheben Sie die Buildfehler, und versuchen Sie es anschließend noch mal. @@ -2759,11 +2769,21 @@ Your project targets multiple frameworks. Specify which framework to run using ' Ihr Projekt verwendet mehrere Zielframeworks. Geben Sie über "{0}" an, welches Framework ausgeführt werden soll. + + Move up and down to reveal more frameworks + Move up and down to reveal more frameworks + + Warning NETSDK1174: The abbreviation of -p for --project is deprecated. Please use --project. Warnung NETSDK1174: Die Abkürzung von „-p“ für „--project“ ist veraltet. Verwenden Sie „--project“. {Locked="--project"} + + Select the target framework to run: + Select the target framework to run: + + '{0}' is not a valid project file. "{0}" ist keine gültige Projektdatei. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.es.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.es.xlf index 84f4aabb9069..3c89d8542134 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.es.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.es.xlf @@ -2702,6 +2702,11 @@ El valor predeterminado es publicar una aplicación dependiente del marco.Ejecutar comando de .NET + + Available target frameworks: + Available target frameworks: + + Building... Compilando... @@ -2712,6 +2717,11 @@ El valor predeterminado es publicar una aplicación dependiente del marco.Error al ejecutar el destino {0} para detectar comandos de ejecución para este proyecto. Corrija los errores y advertencias y vuelva a ejecutarlo. {0} is the name of an MSBuild target + + Example + Example + + The build failed. Fix the build errors and run again. No se pudo llevar a cabo la compilación. Corrija los errores de compilación y vuelva a ejecutar el proyecto. @@ -2759,11 +2769,21 @@ Your project targets multiple frameworks. Specify which framework to run using ' Su proyecto tiene como destino varias plataformas. Especifique la que quiere usar mediante "{0}". + + Move up and down to reveal more frameworks + Move up and down to reveal more frameworks + + Warning NETSDK1174: The abbreviation of -p for --project is deprecated. Please use --project. Advertencia NETSDK1174: La abreviatura de -p para --project está en desuso. Use --project. {Locked="--project"} + + Select the target framework to run: + Select the target framework to run: + + '{0}' is not a valid project file. "{0}" no es un archivo de proyecto válido. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.fr.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.fr.xlf index 6a637323a60e..a3c02245fd64 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.fr.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.fr.xlf @@ -2702,6 +2702,11 @@ La valeur par défaut est de publier une application dépendante du framework.Commande d'exécution .NET + + Available target frameworks: + Available target frameworks: + + Building... Génération... @@ -2712,6 +2717,11 @@ La valeur par défaut est de publier une application dépendante du framework.L’exécution de la {0} cible pour découvrir les commandes d’exécution a échoué pour ce projet. Corrigez les erreurs et les avertissements, puis réexécutez. {0} is the name of an MSBuild target + + Example + Example + + The build failed. Fix the build errors and run again. La build a échoué. Corrigez les erreurs de la build et réexécutez-la. @@ -2759,11 +2769,21 @@ Your project targets multiple frameworks. Specify which framework to run using ' Votre projet cible plusieurs frameworks. Spécifiez le framework à exécuter à l'aide de '{0}'. + + Move up and down to reveal more frameworks + Move up and down to reveal more frameworks + + Warning NETSDK1174: The abbreviation of -p for --project is deprecated. Please use --project. AVERTISSEMENT NETSDK1174 : l’abréviation de-p pour--project est déconseillée. Veuillez utiliser--project. {Locked="--project"} + + Select the target framework to run: + Select the target framework to run: + + '{0}' is not a valid project file. '{0}' n'est pas un fichier projet valide. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.it.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.it.xlf index 1f255f43c794..5c1a45d07d69 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.it.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.it.xlf @@ -2702,6 +2702,11 @@ Per impostazione predefinita, viene generato un pacchetto dipendente dal framewo Comando esecuzione .NET + + Available target frameworks: + Available target frameworks: + + Building... Compilazione... @@ -2712,6 +2717,11 @@ Per impostazione predefinita, viene generato un pacchetto dipendente dal framewo L'esecuzione della destinazione {0} per individuare i comandi di esecuzione non è riuscita per questo progetto. Correggere gli errori e gli avvisi ed eseguire di nuovo. {0} is the name of an MSBuild target + + Example + Example + + The build failed. Fix the build errors and run again. La compilazione non è riuscita. Correggere gli errori di compilazione e ripetere l'esecuzione. @@ -2759,11 +2769,21 @@ Your project targets multiple frameworks. Specify which framework to run using ' Il progetto è destinato a più framework. Specificare il framework da eseguire con '{0}'. + + Move up and down to reveal more frameworks + Move up and down to reveal more frameworks + + Warning NETSDK1174: The abbreviation of -p for --project is deprecated. Please use --project. Avviso NETSDK1174: l'abbreviazione di -p per --project è deprecata. Usare --project. {Locked="--project"} + + Select the target framework to run: + Select the target framework to run: + + '{0}' is not a valid project file. '{0}' non è un file di progetto valido. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ja.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ja.xlf index 7aa8108b6954..24de7a1dca1d 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ja.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ja.xlf @@ -2702,6 +2702,11 @@ The default is to publish a framework-dependent application. .NET Run コマンド + + Available target frameworks: + Available target frameworks: + + Building... ビルドしています... @@ -2712,6 +2717,11 @@ The default is to publish a framework-dependent application. このプロジェクトで実行コマンドを検出するための {0} ターゲットの実行に失敗しました。エラーと警告を修正して、もう一度実行してください。 {0} is the name of an MSBuild target + + Example + Example + + The build failed. Fix the build errors and run again. ビルドに失敗しました。ビルド エラーを修正して、もう一度実行してください。 @@ -2759,11 +2769,21 @@ Your project targets multiple frameworks. Specify which framework to run using ' プロジェクトは複数のフレームワークを対象としています。'{0}' を使用して、実行するフレームワークを指定してください。 + + Move up and down to reveal more frameworks + Move up and down to reveal more frameworks + + Warning NETSDK1174: The abbreviation of -p for --project is deprecated. Please use --project. 警告 NETSDK1174: --project の省略形である -p は推奨されていません。--project を使用してください。 {Locked="--project"} + + Select the target framework to run: + Select the target framework to run: + + '{0}' is not a valid project file. '{0}' は有効なプロジェクト ファイルではありません。 diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ko.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ko.xlf index 6544ed0d7051..0db06a3686de 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ko.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ko.xlf @@ -2702,6 +2702,11 @@ The default is to publish a framework-dependent application. .NET 실행 명령 + + Available target frameworks: + Available target frameworks: + + Building... 빌드하는 중... @@ -2712,6 +2717,11 @@ The default is to publish a framework-dependent application. 이 프로젝트에 대해 실행 명령을 검색하기 위해 {0} 대상을 실행하지 못했습니다. 오류 및 경고를 수정하고 다시 실행합니다. {0} is the name of an MSBuild target + + Example + Example + + The build failed. Fix the build errors and run again. 빌드하지 못했습니다. 빌드 오류를 수정하고 다시 실행하세요. @@ -2759,11 +2769,21 @@ Your project targets multiple frameworks. Specify which framework to run using ' 프로젝트에서 여러 프레임워크를 대상으로 합니다. '{0}'을(를) 사용하여 실행할 프레임워크를 지정하세요. + + Move up and down to reveal more frameworks + Move up and down to reveal more frameworks + + Warning NETSDK1174: The abbreviation of -p for --project is deprecated. Please use --project. 경고 NETSDK1174: --project에 대한 약어 -p는 더 이상 사용되지 않습니다. --project를 사용하세요. {Locked="--project"} + + Select the target framework to run: + Select the target framework to run: + + '{0}' is not a valid project file. '{0}'은(는) 유효한 프로젝트 파일이 아닙니다. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pl.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pl.xlf index 6e7de9021244..637bf0bfd014 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pl.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pl.xlf @@ -2702,6 +2702,11 @@ Domyślnie publikowana jest aplikacja zależna od struktury. Uruchamianie polecenia platformy .NET + + Available target frameworks: + Available target frameworks: + + Building... Trwa kompilowanie... @@ -2712,6 +2717,11 @@ Domyślnie publikowana jest aplikacja zależna od struktury. Uruchomienie obiektu docelowego {0} w celu odnalezienia poleceń przebiegu dla tego projektu nie powiodło się. Usuń błędy i ostrzeżenia, a następnie uruchom ponownie. {0} is the name of an MSBuild target + + Example + Example + + The build failed. Fix the build errors and run again. Kompilacja nie powiodła się. Napraw błędy kompilacji i uruchom ją ponownie. @@ -2759,11 +2769,21 @@ Your project targets multiple frameworks. Specify which framework to run using ' Projekt ma wiele platform docelowych. Określ platformę do uruchomienia przy użyciu elementu „{0}”. + + Move up and down to reveal more frameworks + Move up and down to reveal more frameworks + + Warning NETSDK1174: The abbreviation of -p for --project is deprecated. Please use --project. Ostrzeżenie NETSDK1174: Skrót -p dla polecenia --project jest przestarzały. Użyj polecenia --project. {Locked="--project"} + + Select the target framework to run: + Select the target framework to run: + + '{0}' is not a valid project file. „{0}” nie jest prawidłowym plikiem projektu. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pt-BR.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pt-BR.xlf index 032d2caf149f..389393572dce 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pt-BR.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pt-BR.xlf @@ -2702,6 +2702,11 @@ O padrão é publicar uma aplicação dependente de framework. Comando Run do .NET + + Available target frameworks: + Available target frameworks: + + Building... Compilando... @@ -2712,6 +2717,11 @@ O padrão é publicar uma aplicação dependente de framework. Falha na execução do destino {0} para descobrir comandos de execução para este projeto. Corrija os erros e avisos e execute novamente. {0} is the name of an MSBuild target + + Example + Example + + The build failed. Fix the build errors and run again. Ocorreu uma falha no build. Corrija os erros de build e execute novamente. @@ -2759,11 +2769,21 @@ Your project targets multiple frameworks. Specify which framework to run using ' Ele tem diversas estruturas como destino. Especifique que estrutura executar usando '{0}'. + + Move up and down to reveal more frameworks + Move up and down to reveal more frameworks + + Warning NETSDK1174: The abbreviation of -p for --project is deprecated. Please use --project. Aviso NETSDK1174: a abreviação de-p para--project é preterida. Use --project. {Locked="--project"} + + Select the target framework to run: + Select the target framework to run: + + '{0}' is not a valid project file. '{0}' não é um arquivo de projeto válido. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ru.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ru.xlf index 05edfdd693ef..9b2052d1b9d6 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ru.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ru.xlf @@ -2702,6 +2702,11 @@ The default is to publish a framework-dependent application. Команда .NET Run + + Available target frameworks: + Available target frameworks: + + Building... Сборка… @@ -2712,6 +2717,11 @@ The default is to publish a framework-dependent application. Не удалось запустить цель {0} для обнаружения команд выполнения для этого проекта. Исправьте ошибки и предупреждения и повторите попытку. {0} is the name of an MSBuild target + + Example + Example + + The build failed. Fix the build errors and run again. Ошибка сборки. Устраните ошибки сборки и повторите попытку. @@ -2759,11 +2769,21 @@ Your project targets multiple frameworks. Specify which framework to run using ' Проект предназначен для нескольких платформ. Укажите платформу, для которой следует запустить проект, с помощью "{0}". + + Move up and down to reveal more frameworks + Move up and down to reveal more frameworks + + Warning NETSDK1174: The abbreviation of -p for --project is deprecated. Please use --project. Предупреждение NETSDK1174: сокращение "-p" для "--project" не рекомендуется. Используйте "--project". {Locked="--project"} + + Select the target framework to run: + Select the target framework to run: + + '{0}' is not a valid project file. "{0}" не является допустимым файлом проекта. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.tr.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.tr.xlf index 228d3a11bd02..3dae95e5e3e0 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.tr.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.tr.xlf @@ -2702,6 +2702,11 @@ Varsayılan durum, çerçeveye bağımlı bir uygulama yayımlamaktır. .NET Run Komutu + + Available target frameworks: + Available target frameworks: + + Building... Derleniyor... @@ -2712,6 +2717,11 @@ Varsayılan durum, çerçeveye bağımlı bir uygulama yayımlamaktır. Çalıştırma komutlarını bulmak için {0} hedefini çalıştırma bu proje için başarısız oldu. Hataları ve uyarıları düzeltip yeniden çalıştırın. {0} is the name of an MSBuild target + + Example + Example + + The build failed. Fix the build errors and run again. Derleme başarısız oldu. Derleme hatalarını düzeltip yeniden çalıştırın. @@ -2759,11 +2769,21 @@ Your project targets multiple frameworks. Specify which framework to run using ' Projeniz birden fazla Framework'ü hedefliyor. '{0}' kullanarak hangi Framework'ün çalıştırılacağını belirtin. + + Move up and down to reveal more frameworks + Move up and down to reveal more frameworks + + Warning NETSDK1174: The abbreviation of -p for --project is deprecated. Please use --project. Uyarı NETSDK1174: --project için -p kısaltması kullanımdan kaldırıldı. Lütfen --project kullanın. {Locked="--project"} + + Select the target framework to run: + Select the target framework to run: + + '{0}' is not a valid project file. '{0}' geçerli bir proje dosyası değil. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hans.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hans.xlf index 8dce87da752d..076fec45f2ba 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hans.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hans.xlf @@ -2702,6 +2702,11 @@ The default is to publish a framework-dependent application. .NET 运行命令 + + Available target frameworks: + Available target frameworks: + + Building... 正在生成... @@ -2712,6 +2717,11 @@ The default is to publish a framework-dependent application. 为此项目运行 {0} 目标以发现运行命令失败。请修复错误和警告,然后再次运行。 {0} is the name of an MSBuild target + + Example + Example + + The build failed. Fix the build errors and run again. 生成失败。请修复生成错误并重新运行。 @@ -2759,11 +2769,21 @@ Your project targets multiple frameworks. Specify which framework to run using ' 你的项目面向多个框架。请指定要使用“{0}”运行的框架。 + + Move up and down to reveal more frameworks + Move up and down to reveal more frameworks + + Warning NETSDK1174: The abbreviation of -p for --project is deprecated. Please use --project. 警告 NETSDK1174: 已弃用使用缩写“-p”来代表“--project”。请使用“--project”。 {Locked="--project"} + + Select the target framework to run: + Select the target framework to run: + + '{0}' is not a valid project file. “{0}”不是有效的项目文件。 diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hant.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hant.xlf index 4402fa7bc229..a053b1da5e26 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hant.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hant.xlf @@ -2702,6 +2702,11 @@ The default is to publish a framework-dependent application. .NET 執行命令 + + Available target frameworks: + Available target frameworks: + + Building... 正在建置... @@ -2712,6 +2717,11 @@ The default is to publish a framework-dependent application. 執行 {0} 目標以探索對此專案的執行命令失敗。修正錯誤和警告,然後重新執行。 {0} is the name of an MSBuild target + + Example + Example + + The build failed. Fix the build errors and run again. 建置失敗。請修正建置錯誤後,再執行一次。 @@ -2759,11 +2769,21 @@ Your project targets multiple frameworks. Specify which framework to run using ' 您的專案以多重架構為目標。請使用 '{0}' 指定要執行的架構。 + + Move up and down to reveal more frameworks + Move up and down to reveal more frameworks + + Warning NETSDK1174: The abbreviation of -p for --project is deprecated. Please use --project. 警告 NETSDK1174: --project 已取代縮寫 -p。請使用 --project。 {Locked="--project"} + + Select the target framework to run: + Select the target framework to run: + + '{0}' is not a valid project file. '{0}' 並非有效的專案名稱。 diff --git a/src/Cli/dotnet/dotnet.csproj b/src/Cli/dotnet/dotnet.csproj index 2e63f9a3c5a8..36265a85a975 100644 --- a/src/Cli/dotnet/dotnet.csproj +++ b/src/Cli/dotnet/dotnet.csproj @@ -59,6 +59,7 @@ + diff --git a/test/TestAssets/TestProjects/DotnetRunMultiTarget/DotnetRunMultiTarget.csproj b/test/TestAssets/TestProjects/DotnetRunMultiTarget/DotnetRunMultiTarget.csproj new file mode 100644 index 000000000000..fa9bbe9e7439 --- /dev/null +++ b/test/TestAssets/TestProjects/DotnetRunMultiTarget/DotnetRunMultiTarget.csproj @@ -0,0 +1,8 @@ + + + + Exe + net8.0;net9.0;$(CurrentTargetFramework) + + + diff --git a/test/TestAssets/TestProjects/DotnetRunMultiTarget/Program.cs b/test/TestAssets/TestProjects/DotnetRunMultiTarget/Program.cs new file mode 100644 index 000000000000..9c2839e43ffd --- /dev/null +++ b/test/TestAssets/TestProjects/DotnetRunMultiTarget/Program.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace DotNetRunMultiTarget +{ + class Program + { + static void Main(string[] args) + { + Console.WriteLine("Hello from multi-targeted app!"); + Console.WriteLine($"Target Framework: {AppContext.TargetFrameworkName}"); + Console.WriteLine($"Runtime: {System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription}"); + } + } +} diff --git a/test/dotnet.Tests/CommandTests/Run/GivenDotnetRunSelectsTargetFramework.cs b/test/dotnet.Tests/CommandTests/Run/GivenDotnetRunSelectsTargetFramework.cs new file mode 100644 index 000000000000..67c244d42cd8 --- /dev/null +++ b/test/dotnet.Tests/CommandTests/Run/GivenDotnetRunSelectsTargetFramework.cs @@ -0,0 +1,256 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.DotNet.Cli.Utils; + +namespace Microsoft.DotNet.Cli.Run.Tests; + +/// +/// Integration tests for target framework selection in dotnet run +/// +public class GivenDotnetRunSelectsTargetFramework : SdkTest +{ + public GivenDotnetRunSelectsTargetFramework(ITestOutputHelper log) : base(log) + { + } + + [Fact] + public void ItRunsMultiTFMProjectWhenFrameworkIsSpecified() + { + var testInstance = _testAssetsManager.CopyTestAsset( + "NETFrameworkReferenceNETStandard20", + testAssetSubdirectory: TestAssetSubdirectories.DesktopTestProjects) + .WithSource(); + + string projectDirectory = Path.Combine(testInstance.Path, "MultiTFMTestApp"); + + new DotnetCommand(Log, "run") + .WithWorkingDirectory(projectDirectory) + .Execute("--framework", ToolsetInfo.CurrentTargetFramework) + .Should().Pass() + .And.HaveStdOutContaining("This string came from the test library!"); + } + + [Fact] + public void ItFailsInNonInteractiveMode_WhenMultiTFMProjectHasNoFrameworkSpecified() + { + var testInstance = _testAssetsManager.CopyTestAsset( + "NETFrameworkReferenceNETStandard20", + testAssetSubdirectory: TestAssetSubdirectories.DesktopTestProjects) + .WithSource(); + + string projectDirectory = Path.Combine(testInstance.Path, "MultiTFMTestApp"); + + var result = new DotnetCommand(Log, "run") + .WithWorkingDirectory(projectDirectory) + .WithEnvironmentVariable("DOTNET_CLI_UI_LANGUAGE", "en-US") + .Execute("--no-interactive"); + + result.Should().Fail(); + + if (!TestContext.IsLocalized()) + { + result.Should().HaveStdErrContaining("Unable to run your project"); + result.Should().HaveStdErrContaining("multiple frameworks"); + result.Should().HaveStdErrContaining("--framework"); + } + } + + [Fact] + public void ItRunsWithShortFormFrameworkOption() + { + var testInstance = _testAssetsManager.CopyTestAsset( + "NETFrameworkReferenceNETStandard20", + testAssetSubdirectory: TestAssetSubdirectories.DesktopTestProjects) + .WithSource(); + + string projectDirectory = Path.Combine(testInstance.Path, "MultiTFMTestApp"); + + new DotnetCommand(Log, "run") + .WithWorkingDirectory(projectDirectory) + .Execute("-f", ToolsetInfo.CurrentTargetFramework) + .Should().Pass() + .And.HaveStdOutContaining("This string came from the test library!"); + } + + [Fact] + public void ItRunsWithFrameworkPropertySyntax() + { + var testInstance = _testAssetsManager.CopyTestAsset( + "NETFrameworkReferenceNETStandard20", + testAssetSubdirectory: TestAssetSubdirectories.DesktopTestProjects) + .WithSource(); + + string projectDirectory = Path.Combine(testInstance.Path, "MultiTFMTestApp"); + + new DotnetCommand(Log, "run") + .WithWorkingDirectory(projectDirectory) + .Execute("-p:TargetFramework=" + ToolsetInfo.CurrentTargetFramework) + .Should().Pass() + .And.HaveStdOutContaining("This string came from the test library!"); + } + + [Fact] + public void ItPrefersExplicitFrameworkOptionOverProperty() + { + var testInstance = _testAssetsManager.CopyTestAsset( + "NETFrameworkReferenceNETStandard20", + testAssetSubdirectory: TestAssetSubdirectories.DesktopTestProjects) + .WithSource(); + + string projectDirectory = Path.Combine(testInstance.Path, "MultiTFMTestApp"); + + // Pass both --framework and -p:TargetFramework + // The --framework option should take precedence + new DotnetCommand(Log, "run") + .WithWorkingDirectory(projectDirectory) + .Execute( + "--framework", ToolsetInfo.CurrentTargetFramework, + "-p:TargetFramework=net462") + .Should().Pass() + .And.HaveStdOutContaining("This string came from the test library!"); + } + + [Fact] + public void ItShowsErrorMessageWithAvailableFrameworks_InNonInteractiveMode() + { + var testInstance = _testAssetsManager.CopyTestAsset( + "NETFrameworkReferenceNETStandard20", + testAssetSubdirectory: TestAssetSubdirectories.DesktopTestProjects) + .WithSource(); + + string projectDirectory = Path.Combine(testInstance.Path, "MultiTFMTestApp"); + + var result = new DotnetCommand(Log, "run") + .WithWorkingDirectory(projectDirectory) + .WithEnvironmentVariable("DOTNET_CLI_UI_LANGUAGE", "en-US") + .Execute("--no-interactive"); + + result.Should().Fail(); + + if (!TestContext.IsLocalized()) + { + // Should show the "Unable to run your project" message + result.Should().HaveStdErrContaining("Unable to run your project"); + result.Should().HaveStdErrContaining("Your project targets multiple frameworks"); + + // Should show available frameworks + result.Should().HaveStdErrContaining("Available target frameworks:"); + + // Should show example command + result.Should().HaveStdErrContaining("Example:"); + result.Should().HaveStdErrContaining("dotnet run --framework"); + } + } + + [Fact] + public void ItFailsForMultiTargetedAppWithoutFramework_InNonInteractiveMode() + { + var testInstance = _testAssetsManager.CopyTestAsset("DotnetRunMultiTarget") + .WithSource(); + + var result = new DotnetCommand(Log, "run") + .WithWorkingDirectory(testInstance.Path) + .WithEnvironmentVariable("DOTNET_CLI_UI_LANGUAGE", "en-US") + .Execute("--no-interactive"); + + result.Should().Fail(); + + if (!TestContext.IsLocalized()) + { + result.Should().HaveStdErrContaining("Unable to run your project"); + result.Should().HaveStdErrContaining("multiple frameworks"); + } + } + + [Theory] + [InlineData("net8.0", ".NETCoreApp,Version=v8.0")] + [InlineData("net9.0", ".NETCoreApp,Version=v9.0")] + [InlineData(ToolsetInfo.CurrentTargetFramework, ToolsetInfo.CurrentTargetFrameworkMoniker)] + public void ItRunsDifferentFrameworksInMultiTargetedApp(string targetFramework, string expectedMoniker) + { + var testInstance = _testAssetsManager.CopyTestAsset("DotnetRunMultiTarget") + .WithSource(); + + new DotnetCommand(Log, "run") + .WithWorkingDirectory(testInstance.Path) + .Execute("--framework", targetFramework) + .Should().Pass() + .And.HaveStdOutContaining($"Target Framework: {expectedMoniker}"); + } + + [Fact] + public void ItRunsSingleTFMProjectWithoutFrameworkSpecification() + { + var testInstance = _testAssetsManager.CopyTestAsset("HelloWorld") + .WithSource(); + + new DotnetCommand(Log, "run") + .WithWorkingDirectory(testInstance.Path) + .Execute() + .Should().Pass() + .And.HaveStdOutContaining("Hello World!"); + } + + [Fact] + public void ItRunsProjectWithOnlyTargetFrameworkProperty() + { + // Projects with only TargetFramework (not TargetFrameworks) should run without needing --framework + var testInstance = _testAssetsManager.CopyTestAsset("MSBuildTestApp") + .WithSource(); + + new DotnetCommand(Log, "run") + .WithWorkingDirectory(testInstance.Path) + .Execute() + .Should().Pass() + .And.HaveStdOutContaining("Hello World!"); + } + + [Fact] + public void ItTreatsEmptyFrameworkSpecificationAsNotSpecified() + { + var testInstance = _testAssetsManager.CopyTestAsset( + "NETFrameworkReferenceNETStandard20", + testAssetSubdirectory: TestAssetSubdirectories.DesktopTestProjects) + .WithSource(); + + string projectDirectory = Path.Combine(testInstance.Path, "MultiTFMTestApp"); + + var result = new DotnetCommand(Log, "run") + .WithWorkingDirectory(projectDirectory) + .WithEnvironmentVariable("DOTNET_CLI_UI_LANGUAGE", "en-US") + .Execute("-p:TargetFramework=", "--no-interactive"); // Empty string + + result.Should().Fail(); + + if (!TestContext.IsLocalized()) + { + result.Should().HaveStdErrContaining("Unable to run your project"); + result.Should().HaveStdErrContaining("multiple frameworks"); + } + } + + [Fact] + public void ItTreatsWhitespaceFrameworkSpecificationAsNotSpecified() + { + var testInstance = _testAssetsManager.CopyTestAsset( + "NETFrameworkReferenceNETStandard20", + testAssetSubdirectory: TestAssetSubdirectories.DesktopTestProjects) + .WithSource(); + + string projectDirectory = Path.Combine(testInstance.Path, "MultiTFMTestApp"); + + var result = new DotnetCommand(Log, "run") + .WithWorkingDirectory(projectDirectory) + .WithEnvironmentVariable("DOTNET_CLI_UI_LANGUAGE", "en-US") + .Execute("-p:TargetFramework= ", "--no-interactive"); // Whitespace + + result.Should().Fail(); + + if (!TestContext.IsLocalized()) + { + result.Should().HaveStdErrContaining("Unable to run your project"); + result.Should().HaveStdErrContaining("multiple frameworks"); + } + } +} From f5b4ff4f0b2d5a14c74008c100f45ac99b45aa97 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Thu, 23 Oct 2025 14:17:14 -0500 Subject: [PATCH 2/8] Fix for `.sln` file passed in --- .../dotnet/Commands/Run/TargetFrameworkSelector.cs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/Cli/dotnet/Commands/Run/TargetFrameworkSelector.cs b/src/Cli/dotnet/Commands/Run/TargetFrameworkSelector.cs index a9a13cc16199..92fd65d6b5cc 100644 --- a/src/Cli/dotnet/Commands/Run/TargetFrameworkSelector.cs +++ b/src/Cli/dotnet/Commands/Run/TargetFrameworkSelector.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.Build.Evaluation; +using Microsoft.Build.Exceptions; using Microsoft.DotNet.Cli.Utils; using Spectre.Console; @@ -33,8 +34,17 @@ public static bool TrySelectTargetFramework( } // Evaluate the project to get TargetFrameworks - using var collection = new ProjectCollection(globalProperties: globalProperties); - var project = collection.LoadProject(projectFilePath); + Microsoft.Build.Evaluation.Project project; + try + { + using var collection = new ProjectCollection(globalProperties: globalProperties); + project = collection.LoadProject(projectFilePath); + } + catch (InvalidProjectFileException) + { + // Invalid project file, return true to continue for normal error handling + return true; + } string targetFrameworks = project.GetPropertyValue("TargetFrameworks"); From 0c1445eb0e7300c4f32ece8a2ff2a5ac4e8c1d3b Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Thu, 23 Oct 2025 14:21:38 -0500 Subject: [PATCH 3/8] Use `Interactive` property --- src/Cli/dotnet/Commands/Run/RunCommand.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Cli/dotnet/Commands/Run/RunCommand.cs b/src/Cli/dotnet/Commands/Run/RunCommand.cs index 5d26d32ba5e6..21e11ba32257 100644 --- a/src/Cli/dotnet/Commands/Run/RunCommand.cs +++ b/src/Cli/dotnet/Commands/Run/RunCommand.cs @@ -197,14 +197,10 @@ private bool TrySelectTargetFrameworkIfNeeded() Debug.Assert(ProjectFileFullPath is not null); var globalProperties = CommonRunHelpers.GetGlobalPropertiesFromArgs(MSBuildArgs); - - // Check if we're in an interactive terminal - bool isInteractive = !Console.IsInputRedirected && !Console.IsOutputRedirected; - if (TargetFrameworkSelector.TrySelectTargetFramework( ProjectFileFullPath, globalProperties, - isInteractive, + Interactive, out string? selectedFramework)) { // If a framework was selected, add it to MSBuildArgs From 78afad32a0bdaac1f7de7c74d7fce734599d6398 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Thu, 23 Oct 2025 15:56:40 -0500 Subject: [PATCH 4/8] Update GivenDotnetRunSelectsTargetFramework.cs So it doesn't require `mono` --- .../Run/GivenDotnetRunSelectsTargetFramework.cs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/test/dotnet.Tests/CommandTests/Run/GivenDotnetRunSelectsTargetFramework.cs b/test/dotnet.Tests/CommandTests/Run/GivenDotnetRunSelectsTargetFramework.cs index 67c244d42cd8..73ed4d09d46e 100644 --- a/test/dotnet.Tests/CommandTests/Run/GivenDotnetRunSelectsTargetFramework.cs +++ b/test/dotnet.Tests/CommandTests/Run/GivenDotnetRunSelectsTargetFramework.cs @@ -93,22 +93,18 @@ public void ItRunsWithFrameworkPropertySyntax() [Fact] public void ItPrefersExplicitFrameworkOptionOverProperty() { - var testInstance = _testAssetsManager.CopyTestAsset( - "NETFrameworkReferenceNETStandard20", - testAssetSubdirectory: TestAssetSubdirectories.DesktopTestProjects) + var testInstance = _testAssetsManager.CopyTestAsset("DotnetRunMultiTarget") .WithSource(); - string projectDirectory = Path.Combine(testInstance.Path, "MultiTFMTestApp"); - // Pass both --framework and -p:TargetFramework // The --framework option should take precedence new DotnetCommand(Log, "run") - .WithWorkingDirectory(projectDirectory) + .WithWorkingDirectory(testInstance.Path) .Execute( "--framework", ToolsetInfo.CurrentTargetFramework, - "-p:TargetFramework=net462") + "-p:TargetFramework=net8.0") .Should().Pass() - .And.HaveStdOutContaining("This string came from the test library!"); + .And.HaveStdOutContaining("Hello from multi-targeted app!"); } [Fact] From ea80a04f7f58fd105477f1fb00fe2b7d605d35b9 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Fri, 24 Oct 2025 10:34:59 -0500 Subject: [PATCH 5/8] Remove duplicative tests There are other tests that do this --- .../GivenDotnetRunSelectsTargetFramework.cs | 27 ------------------- 1 file changed, 27 deletions(-) diff --git a/test/dotnet.Tests/CommandTests/Run/GivenDotnetRunSelectsTargetFramework.cs b/test/dotnet.Tests/CommandTests/Run/GivenDotnetRunSelectsTargetFramework.cs index 73ed4d09d46e..b666485933d2 100644 --- a/test/dotnet.Tests/CommandTests/Run/GivenDotnetRunSelectsTargetFramework.cs +++ b/test/dotnet.Tests/CommandTests/Run/GivenDotnetRunSelectsTargetFramework.cs @@ -175,33 +175,6 @@ public void ItRunsDifferentFrameworksInMultiTargetedApp(string targetFramework, .And.HaveStdOutContaining($"Target Framework: {expectedMoniker}"); } - [Fact] - public void ItRunsSingleTFMProjectWithoutFrameworkSpecification() - { - var testInstance = _testAssetsManager.CopyTestAsset("HelloWorld") - .WithSource(); - - new DotnetCommand(Log, "run") - .WithWorkingDirectory(testInstance.Path) - .Execute() - .Should().Pass() - .And.HaveStdOutContaining("Hello World!"); - } - - [Fact] - public void ItRunsProjectWithOnlyTargetFrameworkProperty() - { - // Projects with only TargetFramework (not TargetFrameworks) should run without needing --framework - var testInstance = _testAssetsManager.CopyTestAsset("MSBuildTestApp") - .WithSource(); - - new DotnetCommand(Log, "run") - .WithWorkingDirectory(testInstance.Path) - .Execute() - .Should().Pass() - .And.HaveStdOutContaining("Hello World!"); - } - [Fact] public void ItTreatsEmptyFrameworkSpecificationAsNotSpecified() { From 36e5271ccd6a820319d551caf0ad5ed7eb3be47d Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Fri, 24 Oct 2025 10:35:07 -0500 Subject: [PATCH 6/8] Better assertion --- .../CommandTests/Run/GivenDotnetRunSelectsTargetFramework.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/dotnet.Tests/CommandTests/Run/GivenDotnetRunSelectsTargetFramework.cs b/test/dotnet.Tests/CommandTests/Run/GivenDotnetRunSelectsTargetFramework.cs index b666485933d2..b54ad99fdb83 100644 --- a/test/dotnet.Tests/CommandTests/Run/GivenDotnetRunSelectsTargetFramework.cs +++ b/test/dotnet.Tests/CommandTests/Run/GivenDotnetRunSelectsTargetFramework.cs @@ -104,7 +104,7 @@ public void ItPrefersExplicitFrameworkOptionOverProperty() "--framework", ToolsetInfo.CurrentTargetFramework, "-p:TargetFramework=net8.0") .Should().Pass() - .And.HaveStdOutContaining("Hello from multi-targeted app!"); + .And.HaveStdOutContaining($"Target Framework: {ToolsetInfo.CurrentTargetFrameworkMoniker}"); } [Fact] From 6f66408d9f488c058f4a6a6bf2c85a41c0ffeabe Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Fri, 24 Oct 2025 10:40:56 -0500 Subject: [PATCH 7/8] Skip previous TFMs on arm64, x64 passes on these --- .../Run/GivenDotnetRunSelectsTargetFramework.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/test/dotnet.Tests/CommandTests/Run/GivenDotnetRunSelectsTargetFramework.cs b/test/dotnet.Tests/CommandTests/Run/GivenDotnetRunSelectsTargetFramework.cs index b54ad99fdb83..04cc9e1ed837 100644 --- a/test/dotnet.Tests/CommandTests/Run/GivenDotnetRunSelectsTargetFramework.cs +++ b/test/dotnet.Tests/CommandTests/Run/GivenDotnetRunSelectsTargetFramework.cs @@ -1,7 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.DotNet.Cli.Utils; +using System.Runtime.InteropServices; namespace Microsoft.DotNet.Cli.Run.Tests; @@ -165,6 +165,13 @@ public void ItFailsForMultiTargetedAppWithoutFramework_InNonInteractiveMode() [InlineData(ToolsetInfo.CurrentTargetFramework, ToolsetInfo.CurrentTargetFrameworkMoniker)] public void ItRunsDifferentFrameworksInMultiTargetedApp(string targetFramework, string expectedMoniker) { + // Skip net8.0 and net9.0 on arm64 as they may not be available on CI + if (RuntimeInformation.ProcessArchitecture == Architecture.Arm64 && + (targetFramework == "net8.0" || targetFramework == "net9.0")) + { + return; + } + var testInstance = _testAssetsManager.CopyTestAsset("DotnetRunMultiTarget") .WithSource(); From ae1b1d71d5c1841a38bc3d5173bc2cc4f4248da4 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Fri, 24 Oct 2025 11:04:27 -0500 Subject: [PATCH 8/8] Remove ItPrefersExplicitFrameworkOptionOverProperty This test fails after I made it check TF at runtime. This was not even the existing behavior, I think we can remove this test. --- .../Run/GivenDotnetRunSelectsTargetFramework.cs | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/test/dotnet.Tests/CommandTests/Run/GivenDotnetRunSelectsTargetFramework.cs b/test/dotnet.Tests/CommandTests/Run/GivenDotnetRunSelectsTargetFramework.cs index 04cc9e1ed837..3775daee0173 100644 --- a/test/dotnet.Tests/CommandTests/Run/GivenDotnetRunSelectsTargetFramework.cs +++ b/test/dotnet.Tests/CommandTests/Run/GivenDotnetRunSelectsTargetFramework.cs @@ -90,23 +90,6 @@ public void ItRunsWithFrameworkPropertySyntax() .And.HaveStdOutContaining("This string came from the test library!"); } - [Fact] - public void ItPrefersExplicitFrameworkOptionOverProperty() - { - var testInstance = _testAssetsManager.CopyTestAsset("DotnetRunMultiTarget") - .WithSource(); - - // Pass both --framework and -p:TargetFramework - // The --framework option should take precedence - new DotnetCommand(Log, "run") - .WithWorkingDirectory(testInstance.Path) - .Execute( - "--framework", ToolsetInfo.CurrentTargetFramework, - "-p:TargetFramework=net8.0") - .Should().Pass() - .And.HaveStdOutContaining($"Target Framework: {ToolsetInfo.CurrentTargetFrameworkMoniker}"); - } - [Fact] public void ItShowsErrorMessageWithAvailableFrameworks_InNonInteractiveMode() {