diff --git a/documentation/general/dotnet-run-file.md b/documentation/general/dotnet-run-file.md index 3e0db6afaadf..1eaac4b6232c 100644 --- a/documentation/general/dotnet-run-file.md +++ b/documentation/general/dotnet-run-file.md @@ -120,6 +120,35 @@ and it is not a DLL path, built-in command, or a NuGet tool (e.g., `dotnet watch even if a valid `watch` file-based app exists in the current directory; one can use `dotnet ./watch` to run the file-based app). +### `dnx` + +The `dnx` launcher also supports running file-based apps whose target path is valid according to the same rules as `dotnet run`: +the file must exist and either have a `.cs` extension or start with `#!`. +The target path must also be explicit: it must be fully qualified or contain a directory separator, +such as `./app`, `../app`, or `some/directory/app`. +This requirement avoids ambiguity with NuGet tool package IDs, so a bare `dnx app` always retains the existing tool execution behavior +even if a file named `app` exists in the current directory. + +```ps1 +dnx ./some/path.cs arg0 arg1 +``` + +This is equivalent to `dotnet run --file ./some/path.cs -- arg0 arg1`. +All arguments after the target path are passed to the app verbatim, including a literal `--` +and arguments that have the same names as `dnx` tool options. + +For file-based apps, `dnx` starts the `dotnet` host with the target directory as its working directory so SDK resolution, +including the search for `global.json`, starts from the target directory rather than the directory from which `dnx` was invoked. +After the SDK CLI has started, `dnx` restores the original working directory before building and running the app. +Consequently, implicit build files such as `Directory.Build.props` are still discovered relative to the file-based app, +while the running app observes the directory from which the user invoked `dnx` as its current working directory. + +For example, `cd /x/ && dnx /y/file.cs` searches for `global.json` and implicit build files from `/y/`, +but runs the app with `/x/` as its current working directory. + +If the first argument is not a valid file-based app target path, `dnx` retains its existing NuGet tool execution behavior: +it uses the newest installed SDK regardless of `global.json`. + ### Other commands Commands `dotnet restore file.cs` and `dotnet build file.cs` are needed for IDE support and hence work for file-based programs. diff --git a/src/Cli/Microsoft.DotNet.Cli.Definitions/CommandDefinitionStrings.resx b/src/Cli/Microsoft.DotNet.Cli.Definitions/CommandDefinitionStrings.resx index fbd774fbc257..f3702cae837b 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Definitions/CommandDefinitionStrings.resx +++ b/src/Cli/Microsoft.DotNet.Cli.Definitions/CommandDefinitionStrings.resx @@ -1057,9 +1057,15 @@ The default is to publish a framework-dependent application. The target framework to run for. The target framework must also be specified in the project file. + + Treat the first application argument as the file-based app to run. A relative file path is resolved against the directory specified by --working-directory. + The target runtime to run for. + + The working directory to use when running the application. This option overrides the working directory from a launch profile. + .NET SDK Command @@ -1551,4 +1557,16 @@ If command is specified without the argument, it lists all the template packages Display the command schema as JSON. + + Run a file-based app or execute a tool package without permanently installing it. + + + A qualified path to a file-based app, or a package reference in the form of a package identifier like 'dotnetsay' or package identifier and version separated by '@' like 'dotnetsay@2.1.7'. + + + FILE_OR_PACKAGE + + + Arguments forwarded to the file-based app or tool. + diff --git a/src/Cli/Microsoft.DotNet.Cli.Definitions/Commands/Dnx/DnxCommandDefinition.cs b/src/Cli/Microsoft.DotNet.Cli.Definitions/Commands/Dnx/DnxCommandDefinition.cs index 6616e89fe870..ff6a6e62d90e 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Definitions/Commands/Dnx/DnxCommandDefinition.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Definitions/Commands/Dnx/DnxCommandDefinition.cs @@ -8,8 +8,12 @@ namespace Microsoft.DotNet.Cli.Commands.Dnx; internal sealed class DnxCommandDefinition : ToolExecuteCommandDefinitionBase { public DnxCommandDefinition() - : base("dnx") + : base("dnx", CommandDefinitionStrings.DnxPackageOrFileArgumentName) { + Description = CommandDefinitionStrings.DnxCommandDescription; + PackageIdentityArgument.Description = CommandDefinitionStrings.DnxPackageOrFileArgumentDescription; + PackageIdentityArgument.HelpName = CommandDefinitionStrings.DnxPackageOrFileArgumentName; + CommandArgument.Description = CommandDefinitionStrings.DnxArgumentsDescription; Hidden = true; } } diff --git a/src/Cli/Microsoft.DotNet.Cli.Definitions/Commands/Run/RunCommandDefinition.cs b/src/Cli/Microsoft.DotNet.Cli.Definitions/Commands/Run/RunCommandDefinition.cs index fd646939939b..bc7334ba44b0 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Definitions/Commands/Run/RunCommandDefinition.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Definitions/Commands/Run/RunCommandDefinition.cs @@ -29,6 +29,17 @@ internal sealed class RunCommandDefinition : Command HelpName = CommandDefinitionStrings.CommandOptionFileHelpName, }; + public readonly Option FileModeOption = new("--file-mode") + { + Description = CommandDefinitionStrings.RunFileModeOptionDescription, + Hidden = true, + }; + + public readonly Option WorkingDirectoryOption = new("--working-directory") + { + Description = CommandDefinitionStrings.RunWorkingDirectoryOptionDescription, + }; + public readonly Option?> PropertyOption = CommonOptions.CreatePropertyOption(); public readonly Option LaunchProfileOption = new("--launch-profile", "-lp") @@ -107,6 +118,8 @@ public RunCommandDefinition() Options.Add(FrameworkOption); Options.Add(ProjectOption); Options.Add(FileOption); + Options.Add(FileModeOption); + Options.Add(WorkingDirectoryOption); Options.Add(PropertyOption); Options.Add(LaunchProfileOption); Options.Add(NoLaunchProfileOption); diff --git a/src/Cli/Microsoft.DotNet.Cli.Definitions/Commands/Tool/ToolExecuteCommandDefinition.cs b/src/Cli/Microsoft.DotNet.Cli.Definitions/Commands/Tool/ToolExecuteCommandDefinition.cs index 4447d91a68b4..f3dabd013984 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Definitions/Commands/Tool/ToolExecuteCommandDefinition.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Definitions/Commands/Tool/ToolExecuteCommandDefinition.cs @@ -16,7 +16,7 @@ public ToolExecuteCommandDefinition() internal abstract class ToolExecuteCommandDefinitionBase : Command { - public readonly Argument PackageIdentityArgument = CommonArguments.CreateRequiredPackageIdentityArgument("dotnetsay", "2.1.7"); + public readonly Argument PackageIdentityArgument; public readonly Argument> CommandArgument = new("commandArguments") { @@ -34,9 +34,13 @@ internal abstract class ToolExecuteCommandDefinitionBase : Command public readonly NuGetRestoreOptions RestoreOptions = new(forward: true); - public ToolExecuteCommandDefinitionBase(string name) + public ToolExecuteCommandDefinitionBase(string name, string packageIdentityArgumentName = CommonArguments.PackageIdArgumentName) : base(name, CommandDefinitionStrings.ToolExecuteCommandDescription) { + PackageIdentityArgument = CommonArguments.CreateRequiredPackageIdentityArgument( + "dotnetsay", + "2.1.7", + packageIdentityArgumentName); Arguments.Add(PackageIdentityArgument); Arguments.Add(CommandArgument); diff --git a/src/Cli/Microsoft.DotNet.Cli.Definitions/Common/CommonArguments.cs b/src/Cli/Microsoft.DotNet.Cli.Definitions/Common/CommonArguments.cs index 07b7edc76507..55eff6116c92 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Definitions/Common/CommonArguments.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Definitions/Common/CommonArguments.cs @@ -22,8 +22,11 @@ internal static class CommonArguments IsDynamic = true }; - public static Argument CreateRequiredPackageIdentityArgument(string examplePackage = "Newtonsoft.Json", string exampleVersion = "13.0.3") => - new(PackageIdArgumentName) + public static Argument CreateRequiredPackageIdentityArgument( + string examplePackage = "Newtonsoft.Json", + string exampleVersion = "13.0.3", + string argumentName = PackageIdArgumentName) => + new(argumentName) { Description = string.Format(CommandDefinitionStrings.PackageIdentityArgumentDescription, examplePackage, exampleVersion), CustomParser = argumentResult => ParsePackageIdentityWithVersionSeparator(argumentResult.Tokens[0]?.Value)!.Value, diff --git a/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.cs.xlf b/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.cs.xlf index 0276a19fcffc..9c22a7bf7ceb 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.cs.xlf +++ b/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.cs.xlf @@ -1067,6 +1067,26 @@ Pokud je příkaz zadán bez argumentu, zobrazí seznam všech nainstalovaných Pokud je k dispozici, zabrání zobrazení šablon, které jsou součástí sady SDK. + + Arguments forwarded to the file-based app or tool. + Arguments forwarded to the file-based app or tool. + + + + Run a file-based app or execute a tool package without permanently installing it. + Run a file-based app or execute a tool package without permanently installing it. + + + + A qualified path to a file-based app, or a package reference in the form of a package identifier like 'dotnetsay' or package identifier and version separated by '@' like 'dotnetsay@2.1.7'. + A qualified path to a file-based app, or a package reference in the form of a package identifier like 'dotnetsay' or package identifier and version separated by '@' like 'dotnetsay@2.1.7'. + + + + FILE_OR_PACKAGE + FILE_OR_PACKAGE + + .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. Testovací příkaz .NET pro Microsoft.Testing.Platform (vyjádřen výslovný souhlas prostřednictvím souboru global.json). Podporuje jenom Microsoft.Testing.Platform a nepodporuje VSTest. Další informace najdete na https://aka.ms/dotnet-test. @@ -1790,6 +1810,11 @@ Ve výchozím nastavení je publikována aplikace závislá na architektuře.Konfigurace pro spuštění. Výchozí možností pro většinu projektů je Debug. + + Treat the first application argument as the file-based app to run. A relative file path is resolved against the directory specified by --working-directory. + Treat the first application argument as the file-based app to run. A relative file path is resolved against the directory specified by --working-directory. + + The target framework to run for. The target framework must also be specified in the project file. Cílová architektura pro spuštění. Cílová architektura musí být určená také v souboru projektu. @@ -1800,6 +1825,11 @@ Ve výchozím nastavení je publikována aplikace závislá na architektuře.Cílový modul runtime pro běh + + The working directory to use when running the application. This option overrides the working directory from a launch profile. + The working directory to use when running the application. This option overrides the working directory from a launch profile. + + RUNTIME_IDENTIFIER RUNTIME_IDENTIFIER diff --git a/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.de.xlf b/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.de.xlf index c64a8bbdf95a..57cb52a97e20 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.de.xlf +++ b/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.de.xlf @@ -1067,6 +1067,26 @@ Wenn der Befehl ohne Argument angegeben wird, werden alle installierten Vorlagen Falls vorhanden, wird verhindert, dass im SDK gebündelte Vorlagen präsentiert werden. + + Arguments forwarded to the file-based app or tool. + Arguments forwarded to the file-based app or tool. + + + + Run a file-based app or execute a tool package without permanently installing it. + Run a file-based app or execute a tool package without permanently installing it. + + + + A qualified path to a file-based app, or a package reference in the form of a package identifier like 'dotnetsay' or package identifier and version separated by '@' like 'dotnetsay@2.1.7'. + A qualified path to a file-based app, or a package reference in the form of a package identifier like 'dotnetsay' or package identifier and version separated by '@' like 'dotnetsay@2.1.7'. + + + + FILE_OR_PACKAGE + FILE_OR_PACKAGE + + .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. .NET-Testbefehl für Microsoft.Testing.Platform (über die Datei „global.json“ aktiviert). Dies unterstützt ausschließlich Microsoft.Testing.Platform und nicht VSTest. Weitere Informationen finden Sie unter https://aka.ms/dotnet-test. @@ -1790,6 +1810,11 @@ Standardmäßig wird eine Framework-abhängige Anwendung veröffentlicht.Die Konfiguration für die Ausführung. Der Standardwert für die meisten Projekte ist "Debug". + + Treat the first application argument as the file-based app to run. A relative file path is resolved against the directory specified by --working-directory. + Treat the first application argument as the file-based app to run. A relative file path is resolved against the directory specified by --working-directory. + + The target framework to run for. The target framework must also be specified in the project file. Das Zielframework für die Ausführung. Das Zielframework muss auch in der Projektdatei angegeben werden. @@ -1800,6 +1825,11 @@ Standardmäßig wird eine Framework-abhängige Anwendung veröffentlicht.Die Zielruntime, für die die Ausführung erfolgt. + + The working directory to use when running the application. This option overrides the working directory from a launch profile. + The working directory to use when running the application. This option overrides the working directory from a launch profile. + + RUNTIME_IDENTIFIER RUNTIME_IDENTIFIER diff --git a/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.es.xlf b/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.es.xlf index cbf0b6307624..c4f60f504033 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.es.xlf +++ b/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.es.xlf @@ -1067,6 +1067,26 @@ Si el comando se especifica sin el argumento, muestra todos los paquetes de plan Si está presente, impide que se presenten las plantillas agrupadas en el SDK. + + Arguments forwarded to the file-based app or tool. + Arguments forwarded to the file-based app or tool. + + + + Run a file-based app or execute a tool package without permanently installing it. + Run a file-based app or execute a tool package without permanently installing it. + + + + A qualified path to a file-based app, or a package reference in the form of a package identifier like 'dotnetsay' or package identifier and version separated by '@' like 'dotnetsay@2.1.7'. + A qualified path to a file-based app, or a package reference in the form of a package identifier like 'dotnetsay' or package identifier and version separated by '@' like 'dotnetsay@2.1.7'. + + + + FILE_OR_PACKAGE + FILE_OR_PACKAGE + + .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. Comando de prueba de .NET para Microsoft.Testing.Platform (activado mediante el archivo “global.json”). Solo es compatible con Microsoft.Testing.Platform y no con VSTest. Consulte https://aka.ms/dotnet-test para obtener más información. @@ -1790,6 +1810,11 @@ El valor predeterminado es publicar una aplicación dependiente del marco.La configuración para la que se ejecuta. El valor predeterminado para la mayoría de los proyectos es "Debug". + + Treat the first application argument as the file-based app to run. A relative file path is resolved against the directory specified by --working-directory. + Treat the first application argument as the file-based app to run. A relative file path is resolved against the directory specified by --working-directory. + + The target framework to run for. The target framework must also be specified in the project file. La plataforma de destino para la que se ejecuta. La plataforma de destino se debe especificar en el archivo de proyecto. @@ -1800,6 +1825,11 @@ El valor predeterminado es publicar una aplicación dependiente del marco.El entorno de ejecución de destino en el que se ejecuta. + + The working directory to use when running the application. This option overrides the working directory from a launch profile. + The working directory to use when running the application. This option overrides the working directory from a launch profile. + + RUNTIME_IDENTIFIER RUNTIME_IDENTIFIER diff --git a/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.fr.xlf b/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.fr.xlf index 299d93490b0e..a6f3f47a71a2 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.fr.xlf +++ b/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.fr.xlf @@ -1067,6 +1067,26 @@ Si la commande est spécifiée sans l’argument, elle répertorie tous les pack S'il est présent, empêche la présentation des modèles regroupés dans le SDK. + + Arguments forwarded to the file-based app or tool. + Arguments forwarded to the file-based app or tool. + + + + Run a file-based app or execute a tool package without permanently installing it. + Run a file-based app or execute a tool package without permanently installing it. + + + + A qualified path to a file-based app, or a package reference in the form of a package identifier like 'dotnetsay' or package identifier and version separated by '@' like 'dotnetsay@2.1.7'. + A qualified path to a file-based app, or a package reference in the form of a package identifier like 'dotnetsay' or package identifier and version separated by '@' like 'dotnetsay@2.1.7'. + + + + FILE_OR_PACKAGE + FILE_OR_PACKAGE + + .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. Commande de test .NET pour Microsoft.Testing.Platform (activée via le fichier « global.json »). Cela prend uniquement en charge Microsoft.Testing.Platform et ne prend pas en charge VSTest. Pour plus d'informations, consultez https://aka.ms/dotnet-test. @@ -1790,6 +1810,11 @@ La valeur par défaut est de publier une application dépendante du framework.Configuration pour laquelle l'exécution est effectuée. La valeur par défaut pour la plupart des projets est 'Debug'. + + Treat the first application argument as the file-based app to run. A relative file path is resolved against the directory specified by --working-directory. + Treat the first application argument as the file-based app to run. A relative file path is resolved against the directory specified by --working-directory. + + The target framework to run for. The target framework must also be specified in the project file. Framework cible pour lequel l'exécution est effectuée. Le framework cible doit également être spécifié dans le fichier projet. @@ -1800,6 +1825,11 @@ La valeur par défaut est de publier une application dépendante du framework.Runtime cible à exécuter. + + The working directory to use when running the application. This option overrides the working directory from a launch profile. + The working directory to use when running the application. This option overrides the working directory from a launch profile. + + RUNTIME_IDENTIFIER RUNTIME_IDENTIFIER diff --git a/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.it.xlf b/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.it.xlf index 3c656a18508d..86820b1fd333 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.it.xlf +++ b/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.it.xlf @@ -1067,6 +1067,26 @@ Se il comando è specificato senza l'argomento, vengono elencati tutti i pacchet Se presente, impedisce la presentazione dei modelli forniti in bundle nell'SDK. + + Arguments forwarded to the file-based app or tool. + Arguments forwarded to the file-based app or tool. + + + + Run a file-based app or execute a tool package without permanently installing it. + Run a file-based app or execute a tool package without permanently installing it. + + + + A qualified path to a file-based app, or a package reference in the form of a package identifier like 'dotnetsay' or package identifier and version separated by '@' like 'dotnetsay@2.1.7'. + A qualified path to a file-based app, or a package reference in the form of a package identifier like 'dotnetsay' or package identifier and version separated by '@' like 'dotnetsay@2.1.7'. + + + + FILE_OR_PACKAGE + FILE_OR_PACKAGE + + .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. Comando di test .NET per Microsoft.Testing.Platform (attivato tramite il file 'global.json'). Supporta solo Microsoft.Testing.Platform e non VSTest. Per altre informazioni, vedere https://aka.ms/dotnet-test. @@ -1790,6 +1810,11 @@ Per impostazione predefinita, viene generato un pacchetto dipendente dal framewo Configurazione da usare per l'esecuzione. L'impostazione predefinita per la maggior parte dei progetti è 'Debug'. + + Treat the first application argument as the file-based app to run. A relative file path is resolved against the directory specified by --working-directory. + Treat the first application argument as the file-based app to run. A relative file path is resolved against the directory specified by --working-directory. + + The target framework to run for. The target framework must also be specified in the project file. Framework di destinazione da usare per l'esecuzione. Il framework di destinazione deve essere specificato anche nel file di progetto. @@ -1800,6 +1825,11 @@ Per impostazione predefinita, viene generato un pacchetto dipendente dal framewo Runtime di destinazione per l'esecuzione. + + The working directory to use when running the application. This option overrides the working directory from a launch profile. + The working directory to use when running the application. This option overrides the working directory from a launch profile. + + RUNTIME_IDENTIFIER RUNTIME_IDENTIFIER diff --git a/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.ja.xlf b/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.ja.xlf index aa06b0e29ce3..097ece8f49eb 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.ja.xlf +++ b/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.ja.xlf @@ -1067,6 +1067,26 @@ If command is specified without the argument, it lists all the template packages SDK にバンドルされているテンプレートがある場合にそれらが表示されないようにします。 + + Arguments forwarded to the file-based app or tool. + Arguments forwarded to the file-based app or tool. + + + + Run a file-based app or execute a tool package without permanently installing it. + Run a file-based app or execute a tool package without permanently installing it. + + + + A qualified path to a file-based app, or a package reference in the form of a package identifier like 'dotnetsay' or package identifier and version separated by '@' like 'dotnetsay@2.1.7'. + A qualified path to a file-based app, or a package reference in the form of a package identifier like 'dotnetsay' or package identifier and version separated by '@' like 'dotnetsay@2.1.7'. + + + + FILE_OR_PACKAGE + FILE_OR_PACKAGE + + .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. Microsoft.Testing.Platform 用の .NET テスト コマンド ('global.json' ファイルでオプトイン済み)。これは Microsoft.Testing.Platform のみをサポートしており、VSTest には対応していません。詳細については、https://aka.ms/dotnet-test をご覧ください。 @@ -1790,6 +1810,11 @@ The default is to publish a framework-dependent application. 実行する対象の構成。大部分のプロジェクトで、既定値は 'Debug' です。 + + Treat the first application argument as the file-based app to run. A relative file path is resolved against the directory specified by --working-directory. + Treat the first application argument as the file-based app to run. A relative file path is resolved against the directory specified by --working-directory. + + The target framework to run for. The target framework must also be specified in the project file. 実行する対象のターゲット フレームワーク。ターゲット フレームワークはプロジェクト ファイルでも指定する必要があります。 @@ -1800,6 +1825,11 @@ The default is to publish a framework-dependent application. 実行対象のターゲット ランタイム。 + + The working directory to use when running the application. This option overrides the working directory from a launch profile. + The working directory to use when running the application. This option overrides the working directory from a launch profile. + + RUNTIME_IDENTIFIER RUNTIME_IDENTIFIER diff --git a/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.ko.xlf b/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.ko.xlf index 90ae3d7d10a9..3a87b5d31e68 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.ko.xlf +++ b/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.ko.xlf @@ -1067,6 +1067,26 @@ If command is specified without the argument, it lists all the template packages 있는 경우 SDK에 번들로 제공되는 템플릿이 표시되지 않도록 합니다. + + Arguments forwarded to the file-based app or tool. + Arguments forwarded to the file-based app or tool. + + + + Run a file-based app or execute a tool package without permanently installing it. + Run a file-based app or execute a tool package without permanently installing it. + + + + A qualified path to a file-based app, or a package reference in the form of a package identifier like 'dotnetsay' or package identifier and version separated by '@' like 'dotnetsay@2.1.7'. + A qualified path to a file-based app, or a package reference in the form of a package identifier like 'dotnetsay' or package identifier and version separated by '@' like 'dotnetsay@2.1.7'. + + + + FILE_OR_PACKAGE + FILE_OR_PACKAGE + + .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. Microsoft.Testing.Platform용 .NET 테스트 명령입니다('global.json' 파일을 통해 옵트인). 이는 Microsoft.Testing.Platform만 지원하며 VSTest를 지원하지 않습니다. 자세한 내용은 https://aka.ms/dotnet-test를 참조하세요. @@ -1790,6 +1810,11 @@ The default is to publish a framework-dependent application. 실행할 구성입니다. 대부분의 프로젝트에서 기본값은 'Debug'입니다. + + Treat the first application argument as the file-based app to run. A relative file path is resolved against the directory specified by --working-directory. + Treat the first application argument as the file-based app to run. A relative file path is resolved against the directory specified by --working-directory. + + The target framework to run for. The target framework must also be specified in the project file. 실행할 대상 프레임워크입니다. 대상 프레임워크는 프로젝트 파일에도 지정되어야 합니다. @@ -1800,6 +1825,11 @@ The default is to publish a framework-dependent application. 실행할 대상 런타임입니다. + + The working directory to use when running the application. This option overrides the working directory from a launch profile. + The working directory to use when running the application. This option overrides the working directory from a launch profile. + + RUNTIME_IDENTIFIER RUNTIME_IDENTIFIER diff --git a/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.pl.xlf b/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.pl.xlf index c294c184b6d5..d77c49a5e314 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.pl.xlf +++ b/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.pl.xlf @@ -1067,6 +1067,26 @@ Jeśli polecenie zostanie określone bez argumentu, zostanie wyświetlona lista Jeśli istnieją, uniemożliwia prezentowanie szablonów dołączonych do zestawu SDK. + + Arguments forwarded to the file-based app or tool. + Arguments forwarded to the file-based app or tool. + + + + Run a file-based app or execute a tool package without permanently installing it. + Run a file-based app or execute a tool package without permanently installing it. + + + + A qualified path to a file-based app, or a package reference in the form of a package identifier like 'dotnetsay' or package identifier and version separated by '@' like 'dotnetsay@2.1.7'. + A qualified path to a file-based app, or a package reference in the form of a package identifier like 'dotnetsay' or package identifier and version separated by '@' like 'dotnetsay@2.1.7'. + + + + FILE_OR_PACKAGE + FILE_OR_PACKAGE + + .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. Polecenie testowe platformy .NET dla elementu Microsoft.Testing.Platform (aktywowane przez plik „global.json”). Obsługuje tylko Microsoft.Testing.Platform i nie obsługuje VSTest. Aby uzyskać więcej informacji, zobacz https://aka.ms/dotnet-test. @@ -1790,6 +1810,11 @@ Domyślnie publikowana jest aplikacja zależna od struktury. Konfiguracja, którą należy uruchomić. W przypadku większości projektów ustawienie domyślne to „Debugowanie”. + + Treat the first application argument as the file-based app to run. A relative file path is resolved against the directory specified by --working-directory. + Treat the first application argument as the file-based app to run. A relative file path is resolved against the directory specified by --working-directory. + + The target framework to run for. The target framework must also be specified in the project file. Platforma docelowa uruchomienia. Platforma docelowa musi być również określona w pliku projektu. @@ -1800,6 +1825,11 @@ Domyślnie publikowana jest aplikacja zależna od struktury. Docelowe środowisko uruchomieniowe na potrzeby przebiegu. + + The working directory to use when running the application. This option overrides the working directory from a launch profile. + The working directory to use when running the application. This option overrides the working directory from a launch profile. + + RUNTIME_IDENTIFIER RUNTIME_IDENTIFIER diff --git a/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.pt-BR.xlf b/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.pt-BR.xlf index 1026e78b095f..91558919e7fa 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.pt-BR.xlf +++ b/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.pt-BR.xlf @@ -1067,6 +1067,26 @@ Se o comando for especificado sem o argumento, ele listará todos os pacotes de Se presente, impede que os modelos agrupados no SDK sejam apresentados. + + Arguments forwarded to the file-based app or tool. + Arguments forwarded to the file-based app or tool. + + + + Run a file-based app or execute a tool package without permanently installing it. + Run a file-based app or execute a tool package without permanently installing it. + + + + A qualified path to a file-based app, or a package reference in the form of a package identifier like 'dotnetsay' or package identifier and version separated by '@' like 'dotnetsay@2.1.7'. + A qualified path to a file-based app, or a package reference in the form of a package identifier like 'dotnetsay' or package identifier and version separated by '@' like 'dotnetsay@2.1.7'. + + + + FILE_OR_PACKAGE + FILE_OR_PACKAGE + + .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. Comando de Teste .NET para Microsoft.Testing.Platform (aceito por meio do arquivo 'global.json'). Isso dá suporte apenas a Microsoft.Testing.Platform e não dá suporte a VSTest. Para obter mais informações, confira https://aka.ms/dotnet-test. @@ -1790,6 +1810,11 @@ O padrão é publicar uma aplicação dependente de framework. A configuração para a qual a execução ocorrerá. O padrão para a maioria dos projetos é 'Debug'. + + Treat the first application argument as the file-based app to run. A relative file path is resolved against the directory specified by --working-directory. + Treat the first application argument as the file-based app to run. A relative file path is resolved against the directory specified by --working-directory. + + The target framework to run for. The target framework must also be specified in the project file. A estrutura de destino para a qual a execução ocorrerá. A estrutura de destino também precisa ser especificada no arquivo de projeto. @@ -1800,6 +1825,11 @@ O padrão é publicar uma aplicação dependente de framework. O runtime de destino a ser executado. + + The working directory to use when running the application. This option overrides the working directory from a launch profile. + The working directory to use when running the application. This option overrides the working directory from a launch profile. + + RUNTIME_IDENTIFIER RUNTIME_IDENTIFIER diff --git a/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.ru.xlf b/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.ru.xlf index 6fac99d59539..556ecc2348c6 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.ru.xlf +++ b/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.ru.xlf @@ -1067,6 +1067,26 @@ If command is specified without the argument, it lists all the template packages При наличии этого параметра шаблоны, входящие в пакет SDK, не будут представлены. + + Arguments forwarded to the file-based app or tool. + Arguments forwarded to the file-based app or tool. + + + + Run a file-based app or execute a tool package without permanently installing it. + Run a file-based app or execute a tool package without permanently installing it. + + + + A qualified path to a file-based app, or a package reference in the form of a package identifier like 'dotnetsay' or package identifier and version separated by '@' like 'dotnetsay@2.1.7'. + A qualified path to a file-based app, or a package reference in the form of a package identifier like 'dotnetsay' or package identifier and version separated by '@' like 'dotnetsay@2.1.7'. + + + + FILE_OR_PACKAGE + FILE_OR_PACKAGE + + .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. Тестовая команда .NET для Microsoft.Testing.Platform (предоставлено согласие с помощью файла global.json). Поддерживается только Microsoft.Testing.Platform, VSTest не поддерживается. Дополнительные сведения см. на странице https://aka.ms/dotnet-test. @@ -1790,6 +1810,11 @@ The default is to publish a framework-dependent application. Конфигурация для запуска. По умолчанию для большинства проектов используется "Debug". + + Treat the first application argument as the file-based app to run. A relative file path is resolved against the directory specified by --working-directory. + Treat the first application argument as the file-based app to run. A relative file path is resolved against the directory specified by --working-directory. + + The target framework to run for. The target framework must also be specified in the project file. Целевая платформа для запуска. Целевая платформа также должна быть указана в файле проекта. @@ -1800,6 +1825,11 @@ The default is to publish a framework-dependent application. Целевая среда выполнения. + + The working directory to use when running the application. This option overrides the working directory from a launch profile. + The working directory to use when running the application. This option overrides the working directory from a launch profile. + + RUNTIME_IDENTIFIER RUNTIME_IDENTIFIER diff --git a/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.tr.xlf b/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.tr.xlf index e06607f3baed..f7951a7f8081 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.tr.xlf +++ b/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.tr.xlf @@ -1067,6 +1067,26 @@ Eğer komut bağımsız değişken olmadan belirtilirse yüklü tüm şablon pak Varsa, SDK'da paketlenmiş şablonların sunumlarını önler. + + Arguments forwarded to the file-based app or tool. + Arguments forwarded to the file-based app or tool. + + + + Run a file-based app or execute a tool package without permanently installing it. + Run a file-based app or execute a tool package without permanently installing it. + + + + A qualified path to a file-based app, or a package reference in the form of a package identifier like 'dotnetsay' or package identifier and version separated by '@' like 'dotnetsay@2.1.7'. + A qualified path to a file-based app, or a package reference in the form of a package identifier like 'dotnetsay' or package identifier and version separated by '@' like 'dotnetsay@2.1.7'. + + + + FILE_OR_PACKAGE + FILE_OR_PACKAGE + + .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. Microsoft.Testing.Platform için .NET Test Komutu ('global.json' dosyasıyla seçildi). Bu sadece Microsoft.Testing.Platform'u destekler, VSTest'i desteklemez. Daha fazla bilgi için bkz. https://aka.ms/dotnet-test. @@ -1790,6 +1810,11 @@ Varsayılan durum, çerçeveye bağımlı bir uygulama yayımlamaktır. Çalıştırılacak yapılandırma. Çoğu proje için varsayılan, ‘Hata Ayıklama’ seçeneğidir. + + Treat the first application argument as the file-based app to run. A relative file path is resolved against the directory specified by --working-directory. + Treat the first application argument as the file-based app to run. A relative file path is resolved against the directory specified by --working-directory. + + The target framework to run for. The target framework must also be specified in the project file. Çalıştırılacak hedef çerçeve. Hedef çerçevenin proje dosyasında da belirtilmesi gerekir. @@ -1800,6 +1825,11 @@ Varsayılan durum, çerçeveye bağımlı bir uygulama yayımlamaktır. Çalıştırılacağı hedef çalışma zamanı. + + The working directory to use when running the application. This option overrides the working directory from a launch profile. + The working directory to use when running the application. This option overrides the working directory from a launch profile. + + RUNTIME_IDENTIFIER RUNTIME_IDENTIFIER diff --git a/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.zh-Hans.xlf b/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.zh-Hans.xlf index edf2922fbb34..2702f951bd65 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.zh-Hans.xlf +++ b/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.zh-Hans.xlf @@ -1067,6 +1067,26 @@ If command is specified without the argument, it lists all the template packages 如果存在,则阻止显示捆绑在 SDK 中的模板。 + + Arguments forwarded to the file-based app or tool. + Arguments forwarded to the file-based app or tool. + + + + Run a file-based app or execute a tool package without permanently installing it. + Run a file-based app or execute a tool package without permanently installing it. + + + + A qualified path to a file-based app, or a package reference in the form of a package identifier like 'dotnetsay' or package identifier and version separated by '@' like 'dotnetsay@2.1.7'. + A qualified path to a file-based app, or a package reference in the form of a package identifier like 'dotnetsay' or package identifier and version separated by '@' like 'dotnetsay@2.1.7'. + + + + FILE_OR_PACKAGE + FILE_OR_PACKAGE + + .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. 适用于 Microsoft.Testing.Platform 的 .NET 测试命令(已通过 "global.json" 文件选择加入)。此命令仅支持 Microsoft.Testing.Platform,不支持 VSTest。有关详细信息,请参阅 https://aka.ms/dotnet-test。 @@ -1790,6 +1810,11 @@ The default is to publish a framework-dependent application. 要运行的配置。大多数项目的默认值是 "Debug"。 + + Treat the first application argument as the file-based app to run. A relative file path is resolved against the directory specified by --working-directory. + Treat the first application argument as the file-based app to run. A relative file path is resolved against the directory specified by --working-directory. + + The target framework to run for. The target framework must also be specified in the project file. 要运行的目标框架。必须在项目文件中指定目标框架。 @@ -1800,6 +1825,11 @@ The default is to publish a framework-dependent application. 要为其运行的目标运行时。 + + The working directory to use when running the application. This option overrides the working directory from a launch profile. + The working directory to use when running the application. This option overrides the working directory from a launch profile. + + RUNTIME_IDENTIFIER RUNTIME_IDENTIFIER diff --git a/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.zh-Hant.xlf b/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.zh-Hant.xlf index 550dc2834a21..bcff963650b0 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.zh-Hant.xlf +++ b/src/Cli/Microsoft.DotNet.Cli.Definitions/xlf/CommandDefinitionStrings.zh-Hant.xlf @@ -1067,6 +1067,26 @@ If command is specified without the argument, it lists all the template packages 如果存在,則會防止顯示 SDK 中套件組合的範本。 + + Arguments forwarded to the file-based app or tool. + Arguments forwarded to the file-based app or tool. + + + + Run a file-based app or execute a tool package without permanently installing it. + Run a file-based app or execute a tool package without permanently installing it. + + + + A qualified path to a file-based app, or a package reference in the form of a package identifier like 'dotnetsay' or package identifier and version separated by '@' like 'dotnetsay@2.1.7'. + A qualified path to a file-based app, or a package reference in the form of a package identifier like 'dotnetsay' or package identifier and version separated by '@' like 'dotnetsay@2.1.7'. + + + + FILE_OR_PACKAGE + FILE_OR_PACKAGE + + .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. .NET 測試命令,適用於 Microsoft.Testing.Platform (透過 'global.json' 檔案選擇加入)。此命令僅支援 Microsoft.Testing.Platform,不支援 VSTest。如需詳細資訊,請參閱 https://aka.ms/dotnet-test。 @@ -1790,6 +1810,11 @@ The default is to publish a framework-dependent application. 要為其執行的組態。大部分的專案預設為「偵錯」。 + + Treat the first application argument as the file-based app to run. A relative file path is resolved against the directory specified by --working-directory. + Treat the first application argument as the file-based app to run. A relative file path is resolved against the directory specified by --working-directory. + + The target framework to run for. The target framework must also be specified in the project file. 要為其執行的目標架構。該目標架構也必須在專案檔中指定。 @@ -1800,6 +1825,11 @@ The default is to publish a framework-dependent application. 測試標的之目標執行階段。 + + The working directory to use when running the application. This option overrides the working directory from a launch profile. + The working directory to use when running the application. This option overrides the working directory from a launch profile. + + RUNTIME_IDENTIFIER RUNTIME_IDENTIFIER diff --git a/src/Cli/dotnet/Commands/Run/AotRunCommand.cs b/src/Cli/dotnet/Commands/Run/AotRunCommand.cs index b45c8864e610..7f10f3e581a1 100644 --- a/src/Cli/dotnet/Commands/Run/AotRunCommand.cs +++ b/src/Cli/dotnet/Commands/Run/AotRunCommand.cs @@ -61,6 +61,7 @@ internal static int Execute( out string? entryPointFileFullPath, out string[]? applicationArguments, out IReadOnlyDictionary? environmentVariables, + out string? workingDirectoryOverride, out string fallbackReason)) { throw CreateManagedFallbackException(fallbackReason); @@ -149,6 +150,7 @@ internal static int Execute( { throw CreateManagedFallbackException("no eligible cached launch contract was found"); } + workingDirectory = workingDirectoryOverride ?? workingDirectory; var launchEnvironment = new Dictionary(StringComparer.Ordinal); if (profileResult.Profile is not ExecutableLaunchProfile) @@ -284,12 +286,14 @@ private static bool TryGetEligibleInvocationInputs( [NotNullWhen(true)] out string? entryPointFileFullPath, [NotNullWhen(true)] out string[]? applicationArguments, [NotNullWhen(true)] out IReadOnlyDictionary? environmentVariables, + out string? workingDirectoryOverride, out string fallbackReason) { noBuild = parseResult.HasOption(definition.NoBuildOption); entryPointFileFullPath = null; applicationArguments = null; environmentVariables = null; + workingDirectoryOverride = null; fallbackReason = string.Empty; if (GetUnsupportedOption(parseResult, definition) is { } unsupportedOption) @@ -299,6 +303,21 @@ private static bool TryGetEligibleInvocationInputs( } string[] parsedApplicationArguments = parseResult.GetValue(definition.ApplicationArguments) ?? []; + bool fileMode = parseResult.GetValue(definition.FileModeOption); + string? workingDirectory = parseResult.GetValue(definition.WorkingDirectoryOption); + if (workingDirectory is not null) + { + try + { + workingDirectoryOverride = Path.GetFullPath(workingDirectory, currentDirectory); + } + catch (Exception exception) when (exception is ArgumentException or NotSupportedException or SecurityException) + { + fallbackReason = "the working directory could not be normalized"; + return false; + } + } + if (!CommonRunHelpers.TrySplitApplicationArgumentsAtDoubleDash( parseResult, parsedApplicationArguments, @@ -316,7 +335,14 @@ private static bool TryGetEligibleInvocationInputs( } string? entryPointPath = parseResult.GetValue(definition.FileOption); - if (string.IsNullOrEmpty(entryPointPath)) + if (fileMode) + { + (entryPointPath, applicationArguments) = CommonRunHelpers.ProcessFileModeArguments( + parsedApplicationArguments, + currentDirectory, + workingDirectoryOverride); + } + else if (string.IsNullOrEmpty(entryPointPath)) { string? projectFilePath; try @@ -359,7 +385,9 @@ UnauthorizedAccessException or try { - entryPointFileFullPath = Path.GetFullPath(entryPointPath, currentDirectory); + entryPointFileFullPath = Path.GetFullPath( + entryPointPath, + fileMode ? workingDirectoryOverride ?? currentDirectory : currentDirectory); } catch (Exception exception) when (exception is ArgumentException or NotSupportedException or SecurityException) { @@ -378,7 +406,7 @@ UnauthorizedAccessException or return false; } - applicationArguments = argumentsAfterDoubleDash; + applicationArguments ??= argumentsAfterDoubleDash; environmentVariables = parseResult.GetValue(definition.EnvOption) ?? new Dictionary(StringComparer.OrdinalIgnoreCase); fallbackReason = string.Empty; @@ -391,6 +419,8 @@ UnauthorizedAccessException or .FirstOrDefault(optionResult => !optionResult.Implicit && optionResult.Option != definition.FileOption + && optionResult.Option != definition.FileModeOption + && optionResult.Option != definition.WorkingDirectoryOption && optionResult.Option != definition.LaunchProfileOption && optionResult.Option != definition.NoLaunchProfileOption && optionResult.Option != definition.NoBuildOption diff --git a/src/Cli/dotnet/Commands/Run/Api/RunApiCommand.cs b/src/Cli/dotnet/Commands/Run/Api/RunApiCommand.cs index cd44483ba352..24ab9ef725fa 100644 --- a/src/Cli/dotnet/Commands/Run/Api/RunApiCommand.cs +++ b/src/Cli/dotnet/Commands/Run/Api/RunApiCommand.cs @@ -128,7 +128,8 @@ public override RunApiOutput Execute() msbuildArgs: msbuildArgs, applicationArgs: [], readCodeFromStdin: false, - environmentVariables: ReadOnlyDictionary.Empty); + environmentVariables: ReadOnlyDictionary.Empty, + workingDirectory: null); var result = runCommand.ReadLaunchProfileSettings(); var targetCommand = (Utils.Command)runCommand.GetTargetCommand(result.Profile, buildCommand.CreateProjectInstance, cachedRunProperties: null, runPropertiesFromEvaluation: false, logger: null); diff --git a/src/Cli/dotnet/Commands/Run/CommonRunHelpers.cs b/src/Cli/dotnet/Commands/Run/CommonRunHelpers.cs index 03ddbed9e888..d4a69298221f 100644 --- a/src/Cli/dotnet/Commands/Run/CommonRunHelpers.cs +++ b/src/Cli/dotnet/Commands/Run/CommonRunHelpers.cs @@ -14,6 +14,28 @@ namespace Microsoft.DotNet.Cli.Commands.Run; /// internal static class CommonRunHelpers { + /// + /// Resolves the first application argument as a file-based app entry point for file mode. + /// + /// Application arguments whose first value is the entry-point path. + /// The current directory. + /// The optional working directory used as the base for a relative entry-point path. + /// The full entry-point path and remaining application arguments. + internal static (string EntryPointPath, string[] ApplicationArguments) ProcessFileModeArguments( + string[] applicationArguments, + string currentDirectory, + string? workingDirectory) + { + if (applicationArguments is not [{ } filePath, ..]) + { + throw new GracefulException(CliCommandStrings.InvalidFilePath, string.Empty); + } + + return ( + Path.GetFullPath(filePath, workingDirectory ?? currentDirectory), + applicationArguments[1..]); + } + /// /// Finds the only project in a directory. /// diff --git a/src/Cli/dotnet/Commands/Run/RunCommand.cs b/src/Cli/dotnet/Commands/Run/RunCommand.cs index cb3b6dce4b36..bcd25edf93a4 100644 --- a/src/Cli/dotnet/Commands/Run/RunCommand.cs +++ b/src/Cli/dotnet/Commands/Run/RunCommand.cs @@ -56,6 +56,7 @@ public class RunCommand public string[] ApplicationArgs { get; set; } public bool NoRestore { get; } public bool NoCache { get; } + public string? WorkingDirectory { get; } /// /// Parsed structure representing the MSBuild arguments that will be used to build the project. @@ -119,7 +120,8 @@ public RunCommand( MSBuildArgs msbuildArgs, string[] applicationArgs, bool readCodeFromStdin, - IReadOnlyDictionary environmentVariables) + IReadOnlyDictionary environmentVariables, + string? workingDirectory) { Debug.Assert(projectFileFullPath is null ^ entryPointFileFullPath is null); Debug.Assert(!readCodeFromStdin || entryPointFileFullPath is not null); @@ -139,6 +141,7 @@ public RunCommand( NoCache = noCache; MSBuildArgs = SetupSilentBuildArgs(msbuildArgs); EnvironmentVariables = environmentVariables; + WorkingDirectory = workingDirectory; } public int Execute() @@ -234,7 +237,8 @@ public int Execute() } internal ICommand GetTargetCommand(LaunchProfile? launchSettings, Func? projectFactory, RunProperties? cachedRunProperties, bool runPropertiesFromEvaluation, FacadeLogger? logger) - => launchSettings switch + { + ICommand command = launchSettings switch { null => GetTargetCommandForProject(launchSettings: null, projectFactory, cachedRunProperties, runPropertiesFromEvaluation, logger), ProjectLaunchProfile projectSettings => GetTargetCommandForProject(projectSettings, projectFactory, cachedRunProperties, runPropertiesFromEvaluation, logger), @@ -242,6 +246,11 @@ internal ICommand GetTargetCommand(LaunchProfile? launchSettings, Func throw new InvalidOperationException() }; + return WorkingDirectory is null + ? command + : command.WorkingDirectory(WorkingDirectory); + } + /// /// Checks if target framework selection and device selection are needed. /// Uses a single RunCommandSelector instance for both operations, re-evaluating @@ -802,6 +811,13 @@ public static RunCommand FromParseResult(ParseResult parseResult) string? projectOption = parseResult.GetValue(definition.ProjectOption); string? fileOption = parseResult.GetValue(definition.FileOption); + bool fileMode = parseResult.GetValue(definition.FileModeOption); + string? workingDirectory = parseResult.GetValue(definition.WorkingDirectoryOption); + + if (workingDirectory is not null) + { + workingDirectory = Path.GetFullPath(workingDirectory); + } if (projectOption != null && fileOption != null) { @@ -809,6 +825,14 @@ public static RunCommand FromParseResult(ParseResult parseResult) } string[] args = [.. nonLoggerArgs]; + if (fileMode) + { + (fileOption, args) = CommonRunHelpers.ProcessFileModeArguments( + args, + Directory.GetCurrentDirectory(), + workingDirectory); + } + string? projectFilePath = DiscoverProjectFilePath( filePath: fileOption, projectFileOrDirectoryPath: projectOption, @@ -905,7 +929,8 @@ public static RunCommand FromParseResult(ParseResult parseResult) msbuildArgs: msbuildArgs, applicationArgs: args, readCodeFromStdin: readCodeFromStdin, - environmentVariables: parseResult.GetValue(definition.EnvOption) ?? ImmutableDictionary.Empty + environmentVariables: parseResult.GetValue(definition.EnvOption) ?? ImmutableDictionary.Empty, + workingDirectory: workingDirectory ); return command; diff --git a/src/Layout/redist/dnx b/src/Layout/redist/dnx index b580e52192a7..511e7a05f0c3 100755 --- a/src/Layout/redist/dnx +++ b/src/Layout/redist/dnx @@ -15,6 +15,33 @@ done DIR="$(cd "$(dirname "$SCRIPT")" && pwd -P)" DOTNET="$DIR/dotnet" + +case "$1" in + /*|*/*) + case "$1" in + /*) DNX_FILE="$1" ;; + *) DNX_FILE="$PWD/$1" ;; + esac + + if [ -f "$DNX_FILE" ]; then + case "$DNX_FILE" in + *.[cC][sS]) IS_FILE_BASED_APP=true ;; + *) [ "$(dd if="$DNX_FILE" bs=2 count=1 2>/dev/null)" = "#!" ] && IS_FILE_BASED_APP=true ;; + esac + fi + ;; +esac + +if [ "$IS_FILE_BASED_APP" = true ]; then + DNX_WORKING_DIRECTORY="$PWD" + + if ! cd "$(dirname "$DNX_FILE")"; then + exit 1 + fi + + exec "$DOTNET" run --file-mode --working-directory "$DNX_WORKING_DIRECTORY" -- "$@" +fi + SDK_VERSION=$("$DOTNET" --list-sdks | tail -n 1 | cut -d' ' -f1) if [ -z "$SDK_VERSION" ]; then echo "Error: dnx requires a .NET SDK to be installed, but none was found." >&2 diff --git a/src/Layout/redist/dnx.cmd b/src/Layout/redist/dnx.cmd index 94156380edde..8609c2d76cfd 100644 --- a/src/Layout/redist/dnx.cmd +++ b/src/Layout/redist/dnx.cmd @@ -6,6 +6,16 @@ setlocal enableextensions set "DOTNET=%~dp0dotnet.exe" +set "DNX_PATH=%~1" +if "%DNX_PATH:\=%"=="%DNX_PATH%" if "%DNX_PATH:/=%"=="%DNX_PATH%" goto run_tool +if not exist "%~f1" goto run_tool +if exist "%~f1\*" goto run_tool +if /I "%~x1"==".cs" goto run_file + +:check_shebang +for /f "delims=" %%i in ('findstr /n "^" "%~f1" 2^>nul ^| findstr /b /l /c:"1:#!"') do goto run_file + +:run_tool set "SDK_VERSION=" for /f "tokens=1" %%i in ('"%DOTNET%" --list-sdks') do ( set "SDK_VERSION=%%i" @@ -21,3 +31,13 @@ set "SDK_PATH=%~dp0sdk\%SDK_VERSION%\dotnet.dll" "%DOTNET%" exec "%SDK_PATH%" dnx %* endlocal & exit /b %ERRORLEVEL% + +:run_file +set "DNX_WORKING_DIRECTORY=%CD%" +pushd "%~dp1" || exit /b 1 + +"%DOTNET%" run --file-mode --working-directory "%DNX_WORKING_DIRECTORY%" -- %* +set "EXIT_CODE=%ERRORLEVEL%" + +popd +endlocal & exit /b %EXIT_CODE% diff --git a/test/dotnet-aot.Tests/AotRunCommandTests.cs b/test/dotnet-aot.Tests/AotRunCommandTests.cs index 4ea521a005cd..d0f78a58075b 100644 --- a/test/dotnet-aot.Tests/AotRunCommandTests.cs +++ b/test/dotnet-aot.Tests/AotRunCommandTests.cs @@ -89,13 +89,16 @@ public void EligibleSyntheticNoBuildProducesLaunchInvocation() public void EligiblePositionalNoBuildProducesLaunchInvocation() { var fixture = CreateFixture(); + string workingDirectory = Path.Join(fixture.TestDirectory, "working"); + Directory.CreateDirectory(workingDirectory); string? originalDotnetRoot = NativeEntryPoint.DotnetRoot; try { NativeEntryPoint.DotnetRoot = fixture.TestDirectory; var parseResult = Parser.Parse([ "run", - fixture.EntryPointPath, + "--working-directory", workingDirectory, + Path.GetRelativePath(fixture.TestDirectory, fixture.EntryPointPath), "--no-build", "--no-launch-profile", "--", "arg one", "--flag", @@ -115,7 +118,50 @@ public void EligiblePositionalNoBuildProducesLaunchInvocation() Assert.IsNotNull(invocation); Assert.AreEqual(fixture.LaunchArtifacts.AppHost, invocation.Command); Assert.AreEqual("\"arg one\" --flag", invocation.CommandArguments); - Assert.AreEqual(fixture.TestDirectory, invocation.WorkingDirectory); + Assert.AreEqual(workingDirectory, invocation.WorkingDirectory); + } + finally + { + NativeEntryPoint.DotnetRoot = originalDotnetRoot; + DeleteFixture(fixture); + } + } + + [TestMethod] + public void FileModeResolvesFromAndRunsInWorkingDirectory() + { + var fixture = CreateFixture(); + string callerDirectory = Path.Join(fixture.TestDirectory, "caller"); + Directory.CreateDirectory(callerDirectory); + string relativeEntryPointPath = Path.GetRelativePath(callerDirectory, fixture.EntryPointPath); + string? originalDotnetRoot = NativeEntryPoint.DotnetRoot; + try + { + NativeEntryPoint.DotnetRoot = fixture.TestDirectory; + var parseResult = Parser.Parse([ + "run", + "--file-mode", + "--working-directory", callerDirectory, + "--no-build", + "--no-launch-profile", + "--", relativeEntryPointPath, "--help", "arg", + ]); + AotRunInvocation? invocation = null; + + int exitCode = AotRunCommand.Execute( + parseResult, + value => + { + invocation = value; + return 17; + }, + fixture.TestDirectory); + + Assert.AreEqual(17, exitCode); + Assert.IsNotNull(invocation); + Assert.AreEqual(fixture.LaunchArtifacts.AppHost, invocation.Command); + Assert.AreEqual("--help arg", invocation.CommandArguments); + Assert.AreEqual(callerDirectory, invocation.WorkingDirectory); } finally { diff --git a/test/dotnet.Tests/CommandTests/Run/RunCommandTests.cs b/test/dotnet.Tests/CommandTests/Run/RunCommandTests.cs index 1b26c4cb744b..79ee1493fbb8 100644 --- a/test/dotnet.Tests/CommandTests/Run/RunCommandTests.cs +++ b/test/dotnet.Tests/CommandTests/Run/RunCommandTests.cs @@ -18,7 +18,8 @@ private static string EnvironmentVariableReference(string name) private static RunCommand CreateRunCommand( string projectPath, bool noLaunchProfileArguments = false, - string[]? applicationArgs = null) + string[]? applicationArgs = null, + string? workingDirectory = null) => new( noBuild: true, projectFileFullPath: projectPath, @@ -34,7 +35,8 @@ private static RunCommand CreateRunCommand( msbuildArgs: MSBuildArgs.FromOtherArgs([]), applicationArgs: applicationArgs ?? [], readCodeFromStdin: false, - environmentVariables: new Dictionary()); + environmentVariables: new Dictionary(), + workingDirectory: workingDirectory); [TestMethod] public void EnvironmentVariableExpansion_Project() @@ -97,6 +99,32 @@ public void Executable_DefaultWorkingDirectory() Assert.AreEqual("", command.StartInfo.Arguments); } + [TestMethod] + public void Executable_WorkingDirectoryOptionOverridesLaunchProfile() + { + string root = TestAssetsManager.CreateTestDirectory().Path; + string projectDirectory = Path.Combine(root, "project"); + string optionWorkingDirectory = Path.Combine(root, "option"); + var model = new ExecutableLaunchProfile + { + ExecutablePath = "executable", + WorkingDirectory = Path.Combine(root, "profile"), + EnvironmentVariables = [], + }; + + var runCommand = CreateRunCommand( + Path.Combine(projectDirectory, "myproj.csproj"), + workingDirectory: optionWorkingDirectory); + var command = (Command)runCommand.GetTargetCommand( + model, + projectFactory: null, + cachedRunProperties: null, + runPropertiesFromEvaluation: false, + logger: null); + + Assert.AreEqual(optionWorkingDirectory, command.StartInfo.WorkingDirectory); + } + [TestMethod] public void Executable_NoLaunchProfileArguments() { @@ -168,4 +196,30 @@ public void Project_CachedRunPropertiesApplicationArguments(string? cachedArgume Assert.AreEqual(expectedArguments, command.StartInfo.Arguments); } + + [TestMethod] + public void Project_WorkingDirectoryOptionOverridesRunWorkingDirectory() + { + string root = TestAssetsManager.CreateTestDirectory().Path; + string optionWorkingDirectory = Path.Combine(root, "option"); + var runCommand = CreateRunCommand( + Path.Combine(root, "myproj.csproj"), + workingDirectory: optionWorkingDirectory); + var runProperties = new RunProperties( + Command: "executable", + Arguments: null, + WorkingDirectory: Path.Combine(root, "msbuild"), + RuntimeIdentifier: string.Empty, + DefaultAppHostRuntimeIdentifier: string.Empty, + TargetFrameworkVersion: string.Empty); + + var command = (Command)runCommand.GetTargetCommand( + launchSettings: null, + projectFactory: null, + cachedRunProperties: runProperties, + runPropertiesFromEvaluation: false, + logger: null); + + Assert.AreEqual(optionWorkingDirectory, command.StartInfo.WorkingDirectory); + } } diff --git a/test/dotnet.Tests/CommandTests/Run/RunFileTests_General.cs b/test/dotnet.Tests/CommandTests/Run/RunFileTests_General.cs index f7b114456b2e..614fe8910c08 100644 --- a/test/dotnet.Tests/CommandTests/Run/RunFileTests_General.cs +++ b/test/dotnet.Tests/CommandTests/Run/RunFileTests_General.cs @@ -164,6 +164,129 @@ Release config """); } + [TestMethod] + [DataRow("Program.cs", false)] + [DataRow("app", true)] + public void FilePath_WithDnx_Args(string fileName, bool addShebang) + { + var testInstance = TestAssetsManager.CreateTestDirectory(); + File.WriteAllText( + Path.Join(testInstance.Path, fileName), + addShebang ? $"#!/usr/bin/env dotnet{Environment.NewLine}{s_program}" : s_program); + + string dnxPath = Path.Join( + Path.GetDirectoryName(SdkTestContext.Current.ToolsetUnderTest.DotNetHostPath), + OperatingSystem.IsWindows() ? "dnx.cmd" : "dnx"); + + new RunExeCommand(Log, dnxPath, Path.Join(".", fileName), "arg0", "--version", "1.2.3", "--help", "--", "arg") + .WithWorkingDirectory(testInstance.Path) + .Execute() + .Should().Pass() + .And.HaveStdOut($""" + echo args:arg0;--version;1.2.3;--help;--;arg + Hello from {Path.GetFileNameWithoutExtension(fileName)} + """); + } + + [TestMethod] + public void FilePath_WithDnx_Help() + { + string dnxPath = Path.Join( + Path.GetDirectoryName(SdkTestContext.Current.ToolsetUnderTest.DotNetHostPath), + OperatingSystem.IsWindows() ? "dnx.cmd" : "dnx"); + + new RunExeCommand(Log, dnxPath, "--help") + .Execute() + .Should().Pass() + .And.HaveStdOutContaining("Run a file-based app or execute a tool package") + .And.HaveStdOutContaining("") + .And.HaveStdOutContaining("Arguments forwarded to the file-based app or tool."); + } + + [TestMethod] + public void FilePath_WithDnx_RequiresQualifiedPath() + { + var testInstance = TestAssetsManager.CreateTestDirectory(); + var packageSource = Path.Join(testInstance.Path, "packages"); + Directory.CreateDirectory(packageSource); + + const string fileName = "DefinitelyNotARealToolPackage"; + File.WriteAllText( + Path.Join(testInstance.Path, fileName), + """ + #!/usr/bin/env dotnet + Console.WriteLine("file app ran"); + """); + + string dnxPath = Path.Join( + Path.GetDirectoryName(SdkTestContext.Current.ToolsetUnderTest.DotNetHostPath), + OperatingSystem.IsWindows() ? "dnx.cmd" : "dnx"); + + new RunExeCommand(Log, dnxPath, fileName, "--version", "1.0.0", "--source", packageSource) + .WithWorkingDirectory(testInstance.Path) + .Execute() + .Should().Fail() + .And.NotHaveStdOutContaining("file app ran"); + } + + [TestMethod] + public void FilePath_WithDnx_Context() + { + var testInstance = TestAssetsManager.CreateTestDirectory(); + var workingDirectory = Path.Join(testInstance.Path, "working"); + var sourceDirectory = Path.Join(testInstance.Path, "source"); + Directory.CreateDirectory(workingDirectory); + Directory.CreateDirectory(sourceDirectory); + + File.WriteAllText( + Path.Join(sourceDirectory, "global.json"), + $$"""{ "sdk": { "version": "{{SdkTestContext.Current.ToolsetUnderTest.SdkVersion}}", "rollForward": "disable" } }"""); + File.WriteAllText(Path.Join(workingDirectory, "global.json"), """{ "sdk": { "version": "999.0.0" } }"""); + File.WriteAllText( + Path.Join(sourceDirectory, "Directory.Build.props"), + """ + + + $(DefineConstants);SOURCE_DIRECTORY_BUILD_PROPS + + + """); + File.WriteAllText( + Path.Join(sourceDirectory, "app"), + $$""" + #!/usr/bin/env dotnet + {{s_program}} + Console.WriteLine("cwd:" + Environment.CurrentDirectory); + #if SOURCE_DIRECTORY_BUILD_PROPS + Console.WriteLine("source Directory.Build.props"); + #endif + """); + + string dnxPath = Path.Join( + Path.GetDirectoryName(SdkTestContext.Current.ToolsetUnderTest.DotNetHostPath), + OperatingSystem.IsWindows() ? "dnx.cmd" : "dnx"); + + new RunExeCommand(Log, dnxPath, Path.Join("..", "source", "app"), "arg0", "arg1") + .WithWorkingDirectory(workingDirectory) + .Execute() + .Should().Pass() + .And.HaveStdOut($""" + echo args:arg0;arg1 + Hello from app + cwd:{workingDirectory} + source Directory.Build.props + """); + + string sourceGlobalJsonPath = Path.Join(sourceDirectory, "global.json"); + File.WriteAllText(sourceGlobalJsonPath, """{ "sdk": { "version": "999.0.0" } }"""); + + new RunExeCommand(Log, dnxPath, Path.Join("..", "source", "app")) + .WithWorkingDirectory(workingDirectory) + .Execute() + .Should().Fail() + .And.HaveStdErrContaining(sourceGlobalJsonPath); + } + /// /// Casing of the argument is used for the output binary name. /// @@ -210,6 +333,29 @@ public void FilePath_OutsideWorkDir() .And.HaveStdOut("Hello from Program"); } + [TestMethod] + public void WorkingDirectoryOption() + { + var testInstance = TestAssetsManager.CreateTestDirectory(); + string sourceDirectory = Path.Join(testInstance.Path, "source"); + string workingDirectory = Path.Join(testInstance.Path, "working"); + Directory.CreateDirectory(sourceDirectory); + Directory.CreateDirectory(workingDirectory); + File.WriteAllText( + Path.Join(sourceDirectory, "Program.cs"), + """Console.WriteLine(Environment.CurrentDirectory);"""); + + new DotnetCommand( + Log, + "run", + "--working-directory", "working", + Path.Join("source", "Program.cs")) + .WithWorkingDirectory(testInstance.Path) + .Execute() + .Should().Pass() + .And.HaveStdOut(workingDirectory); + } + /// /// dotnet run --project file.cs fails. ///