diff --git a/Directory.Packages.props b/Directory.Packages.props index a2b0a46067b0..b2bb43864d8d 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -114,6 +114,7 @@ + diff --git a/eng/Signing.props b/eng/Signing.props index 0a8d0fdad4d7..41bba1d7a2b8 100644 --- a/eng/Signing.props +++ b/eng/Signing.props @@ -64,6 +64,7 @@ + diff --git a/eng/common/vmr-sync.ps1 b/eng/common/vmr-sync.ps1 index 97302f3205be..76e2f9d08fe8 100755 --- a/eng/common/vmr-sync.ps1 +++ b/eng/common/vmr-sync.ps1 @@ -103,7 +103,7 @@ Set-StrictMode -Version Latest Highlight 'Installing .NET, preparing the tooling..' . .\eng\common\tools.ps1 $dotnetRoot = InitializeDotNetCli -install:$true -$darc = Get-Darc +$darc = Get-Darc "1.1.0-beta.25514.2" $dotnet = "$dotnetRoot\dotnet.exe" Highlight "Starting the synchronization of VMR.." diff --git a/eng/common/vmr-sync.sh b/eng/common/vmr-sync.sh index 44239e331c0c..c038012a55aa 100755 --- a/eng/common/vmr-sync.sh +++ b/eng/common/vmr-sync.sh @@ -164,7 +164,7 @@ set -e highlight 'Installing .NET, preparing the tooling..' source "./eng/common/tools.sh" InitializeDotNetCli true -GetDarc +GetDarc "1.1.0-beta.25514.2" dotnetDir=$( cd ./.dotnet/; pwd -P ) dotnet=$dotnetDir/dotnet diff --git a/src/Cli/dotnet/Commands/CliCommandStrings.resx b/src/Cli/dotnet/Commands/CliCommandStrings.resx index aa6b906e6ec0..21d0dda1c0ae 100644 --- a/src/Cli/dotnet/Commands/CliCommandStrings.resx +++ b/src/Cli/dotnet/Commands/CliCommandStrings.resx @@ -1739,6 +1739,21 @@ 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 + + + Type to search + + + 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 ba57cb7e44f4..09cfbe91720f 100644 --- a/src/Cli/dotnet/Commands/Run/RunCommand.cs +++ b/src/Cli/dotnet/Commands/Run/RunCommand.cs @@ -19,6 +19,7 @@ using Microsoft.DotNet.Cli.Extensions; using Microsoft.DotNet.Cli.Utils; using Microsoft.DotNet.Cli.Utils.Extensions; +using Microsoft.DotNet.FileBasedPrograms; namespace Microsoft.DotNet.Cli.Commands.Run; @@ -56,8 +57,11 @@ public class RunCommand /// /// Parsed structure representing the MSBuild arguments that will be used to build the project. + /// + /// Note: This property has a private setter and is mutated within the class when framework selection modifies it. + /// This mutability is necessary to allow the command to update MSBuild arguments after construction based on framework selection. /// - public MSBuildArgs MSBuildArgs { get; } + public MSBuildArgs MSBuildArgs { get; private set; } public bool Interactive { get; } /// @@ -124,6 +128,18 @@ public int Execute() return 1; } + // Pre-run evaluation: Handle target framework selection for multi-targeted projects + if (ProjectFileFullPath is not null && !TrySelectTargetFrameworkIfNeeded()) + { + return 1; + } + + // For file-based projects, check for multi-targeting before building + if (EntryPointFileFullPath is not null && !TrySelectTargetFrameworkForFileBasedProject()) + { + return 1; + } + Func? projectFactory = null; RunProperties? cachedRunProperties = null; VirtualProjectBuildingCommand? virtualCommand = null; @@ -182,6 +198,100 @@ 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); + if (TargetFrameworkSelector.TrySelectTargetFramework( + ProjectFileFullPath, + globalProperties, + Interactive, + out string? selectedFramework)) + { + ApplySelectedFramework(selectedFramework); + return true; + } + + return false; + } + + /// + /// Checks if target framework selection is needed for file-based projects. + /// Parses directives from the source file to detect multi-targeting. + /// + /// True if we can continue, false if we should exit + private bool TrySelectTargetFrameworkForFileBasedProject() + { + Debug.Assert(EntryPointFileFullPath is not null); + + var globalProperties = CommonRunHelpers.GetGlobalPropertiesFromArgs(MSBuildArgs); + + // If a framework is already specified via --framework, no need to check + if (globalProperties.TryGetValue("TargetFramework", out var existingFramework) && !string.IsNullOrWhiteSpace(existingFramework)) + { + return true; + } + + // Get frameworks from source file directives + var frameworks = GetTargetFrameworksFromSourceFile(EntryPointFileFullPath); + if (frameworks is null || frameworks.Length == 0) + { + return true; // Not multi-targeted + } + + // Use TargetFrameworkSelector to handle multi-target selection (or single framework selection) + if (TargetFrameworkSelector.TrySelectTargetFramework(frameworks, Interactive, out string? selectedFramework)) + { + ApplySelectedFramework(selectedFramework); + return true; + } + + return false; + } + + /// + /// Parses a source file to extract target frameworks from directives. + /// + /// Array of frameworks if TargetFrameworks is specified, null otherwise + private static string[]? GetTargetFrameworksFromSourceFile(string sourceFilePath) + { + var sourceFile = SourceFile.Load(sourceFilePath); + var directives = FileLevelDirectiveHelpers.FindDirectives(sourceFile, reportAllErrors: false, DiagnosticBag.Ignore()); + + var targetFrameworksDirective = directives.OfType() + .FirstOrDefault(p => string.Equals(p.Name, "TargetFrameworks", StringComparison.OrdinalIgnoreCase)); + + if (targetFrameworksDirective is null) + { + return null; + } + + return targetFrameworksDirective.Value.Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + } + + /// + /// Applies the selected target framework to MSBuildArgs if a framework was provided. + /// + /// The framework to apply, or null if no framework selection was needed + private void ApplySelectedFramework(string? selectedFramework) + { + // If selectedFramework is null, it means no framework selection was needed + // (e.g., user already specified --framework, or single-target project) + if (selectedFramework is not null) + { + var additionalProperties = new ReadOnlyDictionary( + new Dictionary { { "TargetFramework", selectedFramework } }); + MSBuildArgs = MSBuildArgs.CloneWithAdditionalProperties(additionalProperties); + } + } + internal void ApplyLaunchSettingsProfileToCommand(ICommand targetCommand, ProjectLaunchSettingsModel? launchSettings) { if (launchSettings == null) @@ -431,7 +541,8 @@ static ProjectInstance EvaluateProject(string? projectFilePath, Func + /// 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 + string targetFrameworks; + try + { + using var collection = new ProjectCollection(globalProperties: globalProperties); + var project = collection.LoadProject(projectFilePath); + targetFrameworks = project.GetPropertyValue("TargetFrameworks"); + } + catch (InvalidProjectFileException) + { + // Invalid project file, return true to continue for normal error handling + return true; + } + + // If there's no TargetFrameworks property or only one framework, no selection needed + if (string.IsNullOrWhiteSpace(targetFrameworks)) + { + return true; + } + + // parse the TargetFrameworks property and make sure to account for any additional whitespace + // users may have added for formatting reasons. + var frameworks = targetFrameworks.Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + + return TrySelectTargetFramework(frameworks, isInteractive, out selectedFramework); + } + + /// + /// Handles target framework selection when given an array of frameworks. + /// If there's only one framework, selects it automatically. + /// If there are multiple frameworks, prompts the user (interactive) or shows an error (non-interactive). + /// + /// Array of target frameworks to choose from + /// Whether we're running in interactive mode (can prompt user) + /// The selected target framework, or null if selection was cancelled + /// True if we should continue, false if we should exit with error + public static bool TrySelectTargetFramework(string[] frameworks, bool isInteractive, out string? selectedFramework) + { + // If there's only one framework in the TargetFrameworks, we do need to pick it to force the subsequent builds/evaluations + // to act against the correct 'view' of the project + if (frameworks.Length == 1) + { + selectedFramework = frameworks[0]; + 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(); + selectedFramework = null; + 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) + .EnableSearch() + .SearchPlaceholderText(CliCommandStrings.RunCommandSearchPlaceholderText); + + 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 a4ed38fb3289..0270eee341c1 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.cs.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.cs.xlf @@ -2657,6 +2657,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í... @@ -2667,6 +2672,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. @@ -2714,11 +2724,26 @@ 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"} + + Type to search + Type to search + + + + 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 3edecf5eb775..a7b5754496b3 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.de.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.de.xlf @@ -2657,6 +2657,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... @@ -2667,6 +2672,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. @@ -2714,11 +2724,26 @@ 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"} + + Type to search + Type to search + + + + 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 25f8f46501af..6181b0041e9b 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.es.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.es.xlf @@ -2657,6 +2657,11 @@ El valor predeterminado es publicar una aplicación dependiente del marco.Ejecutar comando de .NET + + Available target frameworks: + Available target frameworks: + + Building... Compilando... @@ -2667,6 +2672,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. @@ -2714,11 +2724,26 @@ 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"} + + Type to search + Type to search + + + + 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 43d8e78cf478..ace7519eeb54 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.fr.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.fr.xlf @@ -2657,6 +2657,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... @@ -2667,6 +2672,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. @@ -2714,11 +2724,26 @@ 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"} + + Type to search + Type to search + + + + 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 2fd688bba6af..4b2fa0bd75c2 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.it.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.it.xlf @@ -2657,6 +2657,11 @@ Per impostazione predefinita, viene generato un pacchetto dipendente dal framewo Comando esecuzione .NET + + Available target frameworks: + Available target frameworks: + + Building... Compilazione... @@ -2667,6 +2672,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. @@ -2714,11 +2724,26 @@ 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"} + + Type to search + Type to search + + + + 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 b087bf0b8ea2..770f97d13c58 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ja.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ja.xlf @@ -2658,6 +2658,11 @@ The default is to publish a framework-dependent application. .NET Run コマンド + + Available target frameworks: + Available target frameworks: + + Building... ビルドしています... @@ -2668,6 +2673,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. ビルドに失敗しました。ビルド エラーを修正して、もう一度実行してください。 @@ -2715,11 +2725,26 @@ 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"} + + Type to search + Type to search + + + + 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 f441615c4f20..9b4645aa1eb9 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ko.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ko.xlf @@ -2657,6 +2657,11 @@ The default is to publish a framework-dependent application. .NET 실행 명령 + + Available target frameworks: + Available target frameworks: + + Building... 빌드하는 중... @@ -2667,6 +2672,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. 빌드하지 못했습니다. 빌드 오류를 수정하고 다시 실행하세요. @@ -2714,11 +2724,26 @@ 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"} + + Type to search + Type to search + + + + 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 ccdd390a647f..86137f2d874f 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pl.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pl.xlf @@ -2657,6 +2657,11 @@ Domyślnie publikowana jest aplikacja zależna od struktury. Uruchamianie polecenia platformy .NET + + Available target frameworks: + Available target frameworks: + + Building... Trwa kompilowanie... @@ -2667,6 +2672,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. @@ -2714,11 +2724,26 @@ 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"} + + Type to search + Type to search + + + + 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 f6c9c85dffdb..112717418361 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pt-BR.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pt-BR.xlf @@ -2657,6 +2657,11 @@ O padrão é publicar uma aplicação dependente de framework. Comando Run do .NET + + Available target frameworks: + Available target frameworks: + + Building... Compilando... @@ -2667,6 +2672,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. @@ -2714,11 +2724,26 @@ 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"} + + Type to search + Type to search + + + + 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 bb20ad91e059..8d933a41c596 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ru.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ru.xlf @@ -2657,6 +2657,11 @@ The default is to publish a framework-dependent application. Команда .NET Run + + Available target frameworks: + Available target frameworks: + + Building... Сборка… @@ -2667,6 +2672,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. Ошибка сборки. Устраните ошибки сборки и повторите попытку. @@ -2714,11 +2724,26 @@ 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"} + + Type to search + Type to search + + + + 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 f268875b2834..7587dc54d0b7 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.tr.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.tr.xlf @@ -2657,6 +2657,11 @@ Varsayılan durum, çerçeveye bağımlı bir uygulama yayımlamaktır. .NET Run Komutu + + Available target frameworks: + Available target frameworks: + + Building... Derleniyor... @@ -2667,6 +2672,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. @@ -2714,11 +2724,26 @@ 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"} + + Type to search + Type to search + + + + 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 b95583b5088e..e1197828c1d4 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hans.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hans.xlf @@ -2657,6 +2657,11 @@ The default is to publish a framework-dependent application. .NET 运行命令 + + Available target frameworks: + Available target frameworks: + + Building... 正在生成... @@ -2667,6 +2672,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. 生成失败。请修复生成错误并重新运行。 @@ -2714,11 +2724,26 @@ 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"} + + Type to search + Type to search + + + + 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 a1fd6f561922..5d848f84c897 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hant.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hant.xlf @@ -2657,6 +2657,11 @@ The default is to publish a framework-dependent application. .NET 執行命令 + + Available target frameworks: + Available target frameworks: + + Building... 正在建置... @@ -2667,6 +2672,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. 建置失敗。請修正建置錯誤後,再執行一次。 @@ -2714,11 +2724,26 @@ 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"} + + Type to search + Type to search + + + + 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 3cdb3b20beeb..65df2389a343 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..33c83c564103 --- /dev/null +++ b/test/dotnet.Tests/CommandTests/Run/GivenDotnetRunSelectsTargetFramework.cs @@ -0,0 +1,210 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Text.RegularExpressions; +using Microsoft.DotNet.Cli.Commands; + +namespace Microsoft.DotNet.Cli.Run.Tests; + +/// +/// Integration tests for target framework selection in dotnet run +/// +public partial 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() + .And.HaveStdErrContaining(string.Format(CliCommandStrings.RunCommandExceptionUnableToRunSpecifyFramework, "--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 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() + .And.HaveStdErrContaining(string.Format(CliCommandStrings.RunCommandExceptionUnableToRunSpecifyFramework, "--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() + .And.HaveStdErrContaining(string.Format(CliCommandStrings.RunCommandExceptionUnableToRunSpecifyFramework, "--framework")); + } + + [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) + { + // 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(); + + new DotnetCommand(Log, "run") + .WithWorkingDirectory(testInstance.Path) + .Execute("--framework", targetFramework) + .Should().Pass() + .And.HaveStdOutContaining($"Target Framework: {expectedMoniker}"); + } + + [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() + .And.HaveStdErrContaining(string.Format(CliCommandStrings.RunCommandExceptionUnableToRunSpecifyFramework, "--framework")); + } + + [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() + .And.HaveStdErrContaining(string.Format(CliCommandStrings.RunCommandExceptionUnableToRunSpecifyFramework, "--framework")); + } + + [Fact] + public void ItAutoSelectsSingleFrameworkInTargetFrameworksProperty() + { + // Reuse the DotnetRunMultiTarget project and modify it to have only one framework + var testInstance = _testAssetsManager.CopyTestAsset("DotnetRunMultiTarget") + .WithSource(); + + // Read the existing .csproj file + var projectPath = Path.Combine(testInstance.Path, "DotnetRunMultiTarget.csproj"); + var projectContent = File.ReadAllText(projectPath); + + // Replace TargetFrameworks with a single framework + projectContent = TargetFrameworksRegex() + .Replace(projectContent, $"{ToolsetInfo.CurrentTargetFramework}"); + File.WriteAllText(projectPath, projectContent); + + // Run without specifying --framework - it should auto-select the single framework + var result = new DotnetCommand(Log, "run") + .WithWorkingDirectory(testInstance.Path) + .Execute(); + + result.Should().Pass() + .And.HaveStdOutContaining($"Target Framework: {ToolsetInfo.CurrentTargetFrameworkMoniker}"); + } + + [GeneratedRegex(@".*?")] + private static partial Regex TargetFrameworksRegex(); +} diff --git a/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs b/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs index 1eebfddaef95..e24fa4aee6c3 100644 --- a/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs +++ b/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs @@ -1706,9 +1706,9 @@ class C; new DotnetCommand(Log, "run", "lib.cs") .WithWorkingDirectory(testInstance.Path) - .Execute() + .Execute("--no-interactive") .Should().Fail() - .And.HaveStdErr(string.Format(CliCommandStrings.RunCommandExceptionUnableToRunSpecifyFramework, "--framework")); + .And.HaveStdErrContaining(string.Format(CliCommandStrings.RunCommandExceptionUnableToRunSpecifyFramework, "--framework")); new DotnetCommand(Log, "run", "lib.cs", "--framework", ToolsetInfo.CurrentTargetFramework) .WithWorkingDirectory(testInstance.Path) @@ -1833,7 +1833,7 @@ public void Build_Exe_MultiTarget() .WithWorkingDirectory(testInstance.Path) .Execute() .Should().Fail() - .And.HaveStdErr(string.Format(CliCommandStrings.RunCommandExceptionUnableToRunSpecifyFramework, "--framework")); + .And.HaveStdErrContaining(string.Format(CliCommandStrings.RunCommandExceptionUnableToRunSpecifyFramework, "--framework")); new DotnetCommand(Log, "run", "exe.cs", "--framework", ToolsetInfo.CurrentTargetFramework) .WithWorkingDirectory(testInstance.Path)