diff --git a/TestAssets/TestProjects/TestApp/project.json b/TestAssets/TestProjects/TestApp/project.json
index 51b830d57..64ba761af 100644
--- a/TestAssets/TestProjects/TestApp/project.json
+++ b/TestAssets/TestProjects/TestApp/project.json
@@ -13,10 +13,5 @@
"frameworks": {
"dnxcore50": { }
- },
-
- "scripts": {
- "prepublish" : ["echo prepublish_output ?%publish:ProjectPath%? ?%publish:Configuration%? ?%publish:OutputPath%? ?%publish:Framework%? ?%publish:Runtime%?"],
- "postpublish" : ["echo postpublish_output ?%publish:ProjectPath%? ?%publish:Configuration%? ?%publish:OutputPath%? ?%publish:Framework%? ?%publish:Runtime%?"]
}
}
diff --git a/TestAssets/TestProjects/TestAppWithScripts/Program.cs b/TestAssets/TestProjects/TestAppWithScripts/Program.cs
new file mode 100644
index 000000000..7bcd4fbd3
--- /dev/null
+++ b/TestAssets/TestProjects/TestAppWithScripts/Program.cs
@@ -0,0 +1,17 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Diagnostics;
+
+namespace TestApp
+{
+ public class Program
+ {
+ public static int Main(string[] args)
+ {
+ Console.WriteLine("Hello World");
+ return 0;
+ }
+ }
+}
diff --git a/TestAssets/TestProjects/TestAppWithScripts/TestApp.xproj b/TestAssets/TestProjects/TestAppWithScripts/TestApp.xproj
new file mode 100644
index 000000000..4cef17daa
--- /dev/null
+++ b/TestAssets/TestProjects/TestAppWithScripts/TestApp.xproj
@@ -0,0 +1,20 @@
+
+
+
+ 14.0
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+
+
+
+
+ 58808bbc-371e-47d6-a3d0-4902145eda4e
+ TestApp
+ ..\..\artifacts\obj\$(MSBuildProjectName)
+ ..\..\artifacts\bin\$(MSBuildProjectName)\
+
+
+
+ 2.0
+
+
+
diff --git a/TestAssets/TestProjects/TestAppWithScripts/echoscript.cmd b/TestAssets/TestProjects/TestAppWithScripts/echoscript.cmd
new file mode 100644
index 000000000..a996a370f
--- /dev/null
+++ b/TestAssets/TestProjects/TestAppWithScripts/echoscript.cmd
@@ -0,0 +1,2 @@
+@echo off
+echo %*
\ No newline at end of file
diff --git a/TestAssets/TestProjects/TestAppWithScripts/echoscript.sh b/TestAssets/TestProjects/TestAppWithScripts/echoscript.sh
new file mode 100644
index 000000000..ef9907913
--- /dev/null
+++ b/TestAssets/TestProjects/TestAppWithScripts/echoscript.sh
@@ -0,0 +1 @@
+echo $@
\ No newline at end of file
diff --git a/TestAssets/TestProjects/TestAppWithScripts/project.json b/TestAssets/TestProjects/TestAppWithScripts/project.json
new file mode 100644
index 000000000..8df726e4d
--- /dev/null
+++ b/TestAssets/TestProjects/TestAppWithScripts/project.json
@@ -0,0 +1,22 @@
+{
+ "version": "1.0.0-*",
+ "compilationOptions": {
+ "emitEntryPoint": true
+ },
+
+ "dependencies": {
+ "NETStandard.Library": "1.0.0-rc2-23811"
+ },
+
+ "frameworks": {
+ "dnxcore50": { }
+ },
+
+ "scripts": {
+ "prepublish" : ["echoscript prepublish_output ?%publish:ProjectPath%? ?%publish:Configuration%? ?%publish:OutputPath%? ?%publish:TargetFramework%? ?%publish:Runtime%?"],
+ "postpublish" : ["echoscript postpublish_output ?%publish:ProjectPath%? ?%publish:Configuration%? ?%publish:OutputPath%? ?%publish:TargetFramework%? ?%publish:Runtime%?"],
+
+ "precompile" : ["echoscript precompile_output ?%compile:ProjectPath%? ?%compile:Configuration%? ?%compile:OutputPath%? ?%compile:TargetFramework%? ?%compile:Runtime%?"],
+ "postcompile" : ["echoscript postcompile_output ?%compile:ProjectPath%? ?%compile:Configuration%? ?%compile:OutputPath%? ?%compile:TargetFramework%? ?%compile:Runtime%?"]
+ }
+}
diff --git a/src/Microsoft.DotNet.Cli.Utils/ArgumentEscaper.cs b/src/Microsoft.DotNet.Cli.Utils/ArgumentEscaper.cs
index 30835d159..4a5d0f4fe 100644
--- a/src/Microsoft.DotNet.Cli.Utils/ArgumentEscaper.cs
+++ b/src/Microsoft.DotNet.Cli.Utils/ArgumentEscaper.cs
@@ -86,8 +86,10 @@ namespace Microsoft.DotNet.Cli.Utils
{
var sb = new StringBuilder();
- var quoted = ShouldSurroundWithQuotes(arg);
- if (quoted) sb.Append("\"");
+ var needsQuotes = ShouldSurroundWithQuotes(arg);
+ var isQuoted = needsQuotes || IsSurroundedWithQuotes(arg);
+
+ if (needsQuotes) sb.Append("\"");
for (int i = 0; i < arg.Length; ++i)
{
@@ -101,13 +103,21 @@ namespace Microsoft.DotNet.Cli.Utils
}
// Escape any backslashes at the end of the arg
+ // when the argument is also quoted.
// This ensures the outside quote is interpreted as
// an argument delimiter
- if (i == arg.Length)
+ if (i == arg.Length && isQuoted)
{
sb.Append('\\', 2 * backslashCount);
}
+ // At then end of the arg, which isn't quoted,
+ // just add the backslashes, no need to escape
+ else if (i == arg.Length)
+ {
+ sb.Append('\\', backslashCount);
+ }
+
// Escape any preceding backslashes and the quote
else if (arg[i] == '"')
{
@@ -123,7 +133,7 @@ namespace Microsoft.DotNet.Cli.Utils
}
}
- if (quoted) sb.Append("\"");
+ if (needsQuotes) sb.Append("\"");
return sb.ToString();
}
@@ -149,22 +159,14 @@ namespace Microsoft.DotNet.Cli.Utils
if (quoted) sb.Append("^\"");
+ // Prepend every character with ^
+ // This is harmless when passing through cmd
+ // and ensures cmd metacharacters are not interpreted
+ // as such
foreach (var character in argument)
{
-
- if (character == '"')
- {
-
- sb.Append('^');
- sb.Append('"');
- sb.Append('^');
- sb.Append(character);
- }
- else
- {
- sb.Append("^");
- sb.Append(character);
- }
+ sb.Append("^");
+ sb.Append(character);
}
if (quoted) sb.Append("^\"");
@@ -172,35 +174,27 @@ namespace Microsoft.DotNet.Cli.Utils
return sb.ToString();
}
- ///
- /// Prepare as single argument to
- /// roundtrip properly through cmd.
- ///
- /// This prefixes every character with the '^' character to force cmd to
- /// interpret the argument string literally. An alternative option would
- /// be to do this only for cmd metacharacters.
- ///
- /// See here for more info:
- /// http://blogs.msdn.com/b/twistylittlepassagesallalike/archive/2011/04/23/everyone-quotes-arguments-the-wrong-way.aspx
- ///
- ///
- ///
internal static bool ShouldSurroundWithQuotes(string argument)
{
// Don't quote already quoted strings
- if (argument.StartsWith("\"", StringComparison.Ordinal) &&
- argument.EndsWith("\"", StringComparison.Ordinal))
+ if (IsSurroundedWithQuotes(argument))
{
return false;
}
// Only quote if whitespace exists in the string
- if (argument.Contains(" ") || argument.Contains("\t") || argument.Contains("\n"))
- {
- return true;
- }
+ return ArgumentContainsWhitespace(argument);
+ }
- return true;
+ internal static bool IsSurroundedWithQuotes(string argument)
+ {
+ return argument.StartsWith("\"", StringComparison.Ordinal) &&
+ argument.EndsWith("\"", StringComparison.Ordinal);
+ }
+
+ internal static bool ArgumentContainsWhitespace(string argument)
+ {
+ return argument.Contains(" ") || argument.Contains("\t") || argument.Contains("\n");
}
}
}
diff --git a/src/Microsoft.DotNet.Cli.Utils/Command.cs b/src/Microsoft.DotNet.Cli.Utils/Command.cs
index 34a2c62c2..1480c2c7f 100644
--- a/src/Microsoft.DotNet.Cli.Utils/Command.cs
+++ b/src/Microsoft.DotNet.Cli.Utils/Command.cs
@@ -8,6 +8,7 @@ using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using NuGet.Frameworks;
+using Microsoft.DotNet.ProjectModel;
namespace Microsoft.DotNet.Cli.Utils
{
@@ -40,9 +41,9 @@ namespace Microsoft.DotNet.Cli.Utils
ResolutionStrategy = commandSpec.ResolutionStrategy;
}
- public static Command CreateDotNet(string commandName, IEnumerable args, NuGetFramework framework = null, bool useComSpec = false)
+ public static Command CreateDotNet(string commandName, IEnumerable args, NuGetFramework framework = null)
{
- return Create("dotnet", new[] { commandName }.Concat(args), framework, useComSpec);
+ return Create("dotnet", new[] { commandName }.Concat(args), framework);
}
///
@@ -55,9 +56,23 @@ namespace Microsoft.DotNet.Cli.Utils
///
///
///
- public static Command Create(string commandName, IEnumerable args, NuGetFramework framework = null, bool useComSpec = false)
+ public static Command Create(string commandName, IEnumerable args, NuGetFramework framework = null)
{
- var commandSpec = CommandResolver.TryResolveCommandSpec(commandName, args, framework, useComSpec);
+ var commandSpec = CommandResolver.TryResolveCommandSpec(commandName, args, framework);
+
+ if (commandSpec == null)
+ {
+ throw new CommandUnknownException(commandName);
+ }
+
+ var command = new Command(commandSpec);
+
+ return command;
+ }
+
+ public static Command CreateForScript(string commandName, IEnumerable args, Project project, string[] inferredExtensionList)
+ {
+ var commandSpec = CommandResolver.TryResolveScriptCommandSpec(commandName, args, project, inferredExtensionList);
if (commandSpec == null)
{
@@ -197,6 +212,8 @@ namespace Microsoft.DotNet.Cli.Utils
public string CommandName => _process.StartInfo.FileName;
+ public string CommandArgs => _process.StartInfo.Arguments;
+
private string FormatProcessInfo(ProcessStartInfo info)
{
if (string.IsNullOrWhiteSpace(info.Arguments))
diff --git a/src/Microsoft.DotNet.Cli.Utils/CommandResolutionStrategy.cs b/src/Microsoft.DotNet.Cli.Utils/CommandResolutionStrategy.cs
index aecec2211..83589d989 100644
--- a/src/Microsoft.DotNet.Cli.Utils/CommandResolutionStrategy.cs
+++ b/src/Microsoft.DotNet.Cli.Utils/CommandResolutionStrategy.cs
@@ -8,6 +8,9 @@
//command loaded from the same directory as the executing assembly
BaseDirectory,
+ //command loaded from the same directory as a project.json file
+ ProjectLocal,
+
//command loaded from path
Path,
diff --git a/src/Microsoft.DotNet.Cli.Utils/CommandResolver.cs b/src/Microsoft.DotNet.Cli.Utils/CommandResolver.cs
index 030322c63..f62486969 100644
--- a/src/Microsoft.DotNet.Cli.Utils/CommandResolver.cs
+++ b/src/Microsoft.DotNet.Cli.Utils/CommandResolver.cs
@@ -13,52 +13,60 @@ namespace Microsoft.DotNet.Cli.Utils
{
internal static class CommandResolver
{
- public static CommandSpec TryResolveCommandSpec(string commandName, IEnumerable args, NuGetFramework framework = null, bool useComSpec = false)
+ public static CommandSpec TryResolveCommandSpec(string commandName, IEnumerable args, NuGetFramework framework = null)
{
- return ResolveFromRootedCommand(commandName, args, useComSpec) ??
- ResolveFromProjectDependencies(commandName, args, framework, useComSpec) ??
- ResolveFromProjectTools(commandName, args, useComSpec) ??
- ResolveFromAppBase(commandName, args, useComSpec) ??
- ResolveFromPath(commandName, args, useComSpec);
+ return ResolveFromRootedCommand(commandName, args) ??
+ ResolveFromProjectDependencies(commandName, args, framework) ??
+ ResolveFromProjectTools(commandName, args) ??
+ ResolveFromAppBase(commandName, args) ??
+ ResolveFromPath(commandName, args);
+ }
+
+ public static CommandSpec TryResolveScriptCommandSpec(string commandName, IEnumerable args, Project project, string[] inferredExtensionList)
+ {
+ return ResolveFromRootedCommand(commandName, args) ??
+ ResolveFromProjectPath(commandName, args, project, inferredExtensionList) ??
+ ResolveFromAppBase(commandName, args) ??
+ ResolveFromPath(commandName, args);
}
- private static CommandSpec ResolveFromPath(string commandName, IEnumerable args, bool useComSpec = false)
+ private static CommandSpec ResolveFromPath(string commandName, IEnumerable args)
{
var commandPath = Env.GetCommandPath(commandName);
return commandPath == null
? null
- : CreateCommandSpecPreferringExe(commandName, args, commandPath, CommandResolutionStrategy.Path, useComSpec);
+ : CreateCommandSpecPreferringExe(commandName, args, commandPath, CommandResolutionStrategy.Path);
}
- private static CommandSpec ResolveFromAppBase(string commandName, IEnumerable args, bool useComSpec = false)
+ private static CommandSpec ResolveFromAppBase(string commandName, IEnumerable args)
{
- var commandPath = Env.GetCommandPathFromAppBase(PlatformServices.Default.Application.ApplicationBasePath, commandName);
+ var commandPath = Env.GetCommandPathFromRootPath(PlatformServices.Default.Application.ApplicationBasePath, commandName);
return commandPath == null
? null
- : CreateCommandSpecPreferringExe(commandName, args, commandPath, CommandResolutionStrategy.BaseDirectory, useComSpec);
+ : CreateCommandSpecPreferringExe(commandName, args, commandPath, CommandResolutionStrategy.BaseDirectory);
+ }
+
+ private static CommandSpec ResolveFromProjectPath(string commandName, IEnumerable args, Project project, string[] inferredExtensionList)
+ {
+ var commandPath = Env.GetCommandPathFromRootPath(project.ProjectDirectory, commandName, inferredExtensionList);
+ return commandPath == null
+ ? null
+ : CreateCommandSpecPreferringExe(commandName, args, commandPath, CommandResolutionStrategy.ProjectLocal);
}
- private static CommandSpec ResolveFromRootedCommand(string commandName, IEnumerable args, bool useComSpec = false)
+ private static CommandSpec ResolveFromRootedCommand(string commandName, IEnumerable args)
{
if (Path.IsPathRooted(commandName))
{
- if (useComSpec)
- {
- return CreateComSpecCommandSpec(commandName, args, CommandResolutionStrategy.Path);
- }
- else
- {
- var escapedArgs = ArgumentEscaper.EscapeAndConcatenateArgArrayForProcessStart(args);
- return new CommandSpec(commandName, escapedArgs, CommandResolutionStrategy.Path);
- }
-
+ var escapedArgs = ArgumentEscaper.EscapeAndConcatenateArgArrayForProcessStart(args);
+ return new CommandSpec(commandName, escapedArgs, CommandResolutionStrategy.Path);
}
return null;
}
public static CommandSpec ResolveFromProjectDependencies(string commandName, IEnumerable args,
- NuGetFramework framework, bool useComSpec = false)
+ NuGetFramework framework)
{
if (framework == null) return null;
@@ -72,7 +80,7 @@ namespace Microsoft.DotNet.Cli.Utils
var depsPath = projectContext.GetOutputPaths(Constants.DefaultConfiguration).RuntimeFiles.Deps;
- return ConfigureCommandFromPackage(commandName, args, commandPackage, projectContext, depsPath, useComSpec);
+ return ConfigureCommandFromPackage(commandName, args, commandPackage, projectContext, depsPath);
}
private static ProjectContext GetProjectContext(NuGetFramework framework)
@@ -101,7 +109,7 @@ namespace Microsoft.DotNet.Cli.Utils
e == FileNameSuffixes.DotNet.DynamicLib));
}
- public static CommandSpec ResolveFromProjectTools(string commandName, IEnumerable args, bool useComSpec = false)
+ public static CommandSpec ResolveFromProjectTools(string commandName, IEnumerable args)
{
var context = GetProjectContext(FrameworkConstants.CommonFrameworks.DnxCore50);
@@ -147,7 +155,7 @@ namespace Microsoft.DotNet.Cli.Utils
}
private static CommandSpec ConfigureCommandFromPackage(string commandName, IEnumerable args,
- PackageDescription commandPackage, ProjectContext projectContext, string depsPath = null, bool useComSpec = false)
+ PackageDescription commandPackage, ProjectContext projectContext, string depsPath = null)
{
var files = commandPackage.Library.Files;
@@ -157,11 +165,11 @@ namespace Microsoft.DotNet.Cli.Utils
var packageDir = Path.Combine(packageRoot, packagePath);
- return ConfigureCommandFromPackage(commandName, args, files, packageDir, depsPath, useComSpec);
+ return ConfigureCommandFromPackage(commandName, args, files, packageDir, depsPath);
}
private static CommandSpec ConfigureCommandFromPackage(string commandName, IEnumerable args,
- IEnumerable files, string packageDir, string depsPath = null, bool useComSpec = false)
+ IEnumerable files, string packageDir, string depsPath = null)
{
var fileName = string.Empty;
@@ -192,24 +200,18 @@ namespace Microsoft.DotNet.Cli.Utils
fileName = Path.Combine(packageDir, commandPath);
}
- if (useComSpec)
- {
- return CreateComSpecCommandSpec(fileName, args, CommandResolutionStrategy.NugetPackage);
- }
- else
- {
- var escapedArgs = ArgumentEscaper.EscapeAndConcatenateArgArrayForProcessStart(args);
- return new CommandSpec(fileName, escapedArgs, CommandResolutionStrategy.NugetPackage);
- }
+ var escapedArgs = ArgumentEscaper.EscapeAndConcatenateArgArrayForProcessStart(args);
+ return new CommandSpec(fileName, escapedArgs, CommandResolutionStrategy.NugetPackage);
}
private static CommandSpec CreateCommandSpecPreferringExe(
string commandName,
IEnumerable args,
string commandPath,
- CommandResolutionStrategy resolutionStrategy,
- bool useComSpec = false)
+ CommandResolutionStrategy resolutionStrategy)
{
+ var useComSpec = false;
+
if (PlatformServices.Default.Runtime.OperatingSystemPlatform == Platform.Windows &&
Path.GetExtension(commandPath).Equals(".cmd", StringComparison.OrdinalIgnoreCase))
{
@@ -228,7 +230,7 @@ namespace Microsoft.DotNet.Cli.Utils
if (useComSpec)
{
- return CreateComSpecCommandSpec(commandPath, args, resolutionStrategy);
+ return CreateCmdCommandSpec(commandPath, args, resolutionStrategy);
}
else
{
@@ -237,14 +239,14 @@ namespace Microsoft.DotNet.Cli.Utils
}
}
- private static CommandSpec CreateComSpecCommandSpec(
+ private static CommandSpec CreateCmdCommandSpec(
string command,
IEnumerable args,
CommandResolutionStrategy resolutionStrategy)
{
- // To prevent Command Not Found, comspec gets passed in as
- // the command already in some cases
var comSpec = Environment.GetEnvironmentVariable("ComSpec");
+
+ // Handle the case where ComSpec is already the command
if (command.Equals(comSpec, StringComparison.OrdinalIgnoreCase))
{
command = args.FirstOrDefault();
diff --git a/src/Microsoft.DotNet.Cli.Utils/Env.cs b/src/Microsoft.DotNet.Cli.Utils/Env.cs
index 66144b02d..6fdac60f1 100644
--- a/src/Microsoft.DotNet.Cli.Utils/Env.cs
+++ b/src/Microsoft.DotNet.Cli.Utils/Env.cs
@@ -66,14 +66,14 @@ namespace Microsoft.DotNet.Cli.Utils
return commandPath;
}
- public static string GetCommandPathFromAppBase(string appBase, string commandName, params string[] extensions)
+ public static string GetCommandPathFromRootPath(string rootPath, string commandName, params string[] extensions)
{
if (!extensions.Any())
{
extensions = Env.ExecutableExtensions.ToArray();
}
- var commandPath = extensions.Select(e => Path.Combine(appBase, commandName + e))
+ var commandPath = extensions.Select(e => Path.Combine(rootPath, commandName + e))
.FirstOrDefault(File.Exists);
return commandPath;
diff --git a/src/Microsoft.DotNet.Cli.Utils/ScriptExecutor.cs b/src/Microsoft.DotNet.Cli.Utils/ScriptExecutor.cs
index 6a986756e..f28bef08b 100644
--- a/src/Microsoft.DotNet.Cli.Utils/ScriptExecutor.cs
+++ b/src/Microsoft.DotNet.Cli.Utils/ScriptExecutor.cs
@@ -17,82 +17,47 @@ namespace Microsoft.DotNet.Cli.Utils
public static ICommand CreateCommandForScript(Project project, string scriptCommandLine, Func getVariable)
{
- // Preserve quotation marks around arguments since command is about to be passed to a shell. May need
- // the quotes to ensure the shell groups arguments correctly.
+ var scriptArguments = ParseScriptArguments(project, scriptCommandLine, getVariable);
+ if (scriptArguments == null)
+ {
+ throw new Exception($"ScriptExecutor: failed to parse script \"{scriptCommandLine}\"");
+ }
+
+ var inferredExtensions = DetermineInferredScriptExtensions();
+
+ return Command
+ .CreateForScript(scriptArguments.First(), scriptArguments.Skip(1), project, inferredExtensions)
+ .WorkingDirectory(project.ProjectDirectory);
+ }
+
+ private static IEnumerable ParseScriptArguments(Project project, string scriptCommandLine, Func getVariable)
+ {
var scriptArguments = CommandGrammar.Process(
scriptCommandLine,
GetScriptVariable(project, getVariable),
- preserveSurroundingQuotes: true);
+ preserveSurroundingQuotes: false);
- // Ensure the array won't be empty and the elements won't be null or empty strings.
scriptArguments = scriptArguments.Where(argument => !string.IsNullOrEmpty(argument)).ToArray();
if (scriptArguments.Length == 0)
{
return null;
}
- var useComSpec = false;
+ return scriptArguments;
+ }
- if (PlatformServices.Default.Runtime.OperatingSystemPlatform == Platform.Windows)
+ private static string[] DetermineInferredScriptExtensions()
+ {
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
- // Only forward slashes are used in script blocks. Replace with backslashes to correctly
- // locate the script. The directory separator is platform-specific.
- scriptArguments[0] = scriptArguments[0].Replace(
- Path.AltDirectorySeparatorChar,
- Path.DirectorySeparatorChar);
-
- // Command-lines on Windows are executed via "cmd /S /C" in order to support batch files, &&,
- // built-in commands like echo, et cetera. /S allows quoting the command as well as the arguments.
- // ComSpec is Windows-specific, and contains the full path to cmd.exe
- var comSpec = Environment.GetEnvironmentVariable("ComSpec");
- if (!string.IsNullOrEmpty(comSpec))
- {
- useComSpec = true;
-
- scriptArguments = new string[] { comSpec }
- .Concat(scriptArguments)
- .ToArray();
- }
+ return new string[] { "", ".cmd" };
}
else
{
- // Special-case a script name that, perhaps with added .sh, matches an existing file.
- var surroundWithQuotes = false;
- var scriptCandidate = scriptArguments[0];
- if (scriptCandidate.StartsWith("\"", StringComparison.Ordinal) &&
- scriptCandidate.EndsWith("\"", StringComparison.Ordinal))
- {
- // Strip surrounding quotes; they were required in project.json to keep the script name
- // together but confuse File.Exists() e.g. "My Script", lacking ./ prefix and .sh suffix.
- surroundWithQuotes = true;
- scriptCandidate = scriptCandidate.Substring(1, scriptCandidate.Length - 2);
- }
-
- if (!scriptCandidate.EndsWith(".sh", StringComparison.Ordinal))
- {
- scriptCandidate = scriptCandidate + ".sh";
- }
-
- if (File.Exists(Path.Combine(project.ProjectDirectory, scriptCandidate)))
- {
- // scriptCandidate may be a path relative to the project root. If so, likely will not be found
- // in the $PATH; add ./ to let bash know where to look.
- var prefix = Path.IsPathRooted(scriptCandidate) ? string.Empty : "./";
- var quote = surroundWithQuotes ? "\"" : string.Empty;
- scriptArguments[0] = $"{ quote }{ prefix }{ scriptCandidate }{ quote }";
- }
-
- // Always use /usr/bin/env bash -c in order to support redirection and so on; similar to Windows case.
- // Unlike Windows, must escape quotation marks within the newly-quoted string.
- scriptArguments = new[] { "/usr/bin/env", "bash", "-c", "\"" }
- .Concat(scriptArguments.Select(argument => argument.Replace("\"", "\\\"")))
- .Concat(new[] { "\"" })
- .ToArray();
+ return new string[] { "", ".sh" };
}
-
- return Command.Create(scriptArguments.FirstOrDefault(), scriptArguments.Skip(1), useComSpec: useComSpec)
- .WorkingDirectory(project.ProjectDirectory);
}
+
private static Func WrapVariableDictionary(IDictionary contextVariables)
{
return key =>
diff --git a/src/dotnet/commands/dotnet-compile/ManagedCompiler.cs b/src/dotnet/commands/dotnet-compile/ManagedCompiler.cs
index 40280dd62..d1d284f2e 100644
--- a/src/dotnet/commands/dotnet-compile/ManagedCompiler.cs
+++ b/src/dotnet/commands/dotnet-compile/ManagedCompiler.cs
@@ -135,7 +135,8 @@ namespace Microsoft.DotNet.Tools.Compiler
// Run pre-compile event
var contextVariables = new Dictionary()
{
- { "compile:TargetFramework", context.TargetFramework.DotNetFrameworkName },
+ { "compile:TargetFramework", context.TargetFramework.GetShortFolderName() },
+ { "compile:FullTargetFramework", context.TargetFramework.DotNetFrameworkName },
{ "compile:Configuration", args.ConfigValue },
{ "compile:OutputFile", outputName },
{ "compile:OutputDir", outputPath.TrimEnd('\\', '/') },
diff --git a/src/dotnet/commands/dotnet-publish/PublishCommand.cs b/src/dotnet/commands/dotnet-publish/PublishCommand.cs
index 112712794..ce3acfc60 100644
--- a/src/dotnet/commands/dotnet-publish/PublishCommand.cs
+++ b/src/dotnet/commands/dotnet-publish/PublishCommand.cs
@@ -96,7 +96,8 @@ namespace Microsoft.DotNet.Tools.Publish
{ "publish:ProjectPath", context.ProjectDirectory },
{ "publish:Configuration", configuration },
{ "publish:OutputPath", outputPath },
- { "publish:Framework", context.TargetFramework.Framework },
+ { "publish:TargetFramework", context.TargetFramework.GetShortFolderName() },
+ { "publish:FullTargetFramework", context.TargetFramework.DotNetFrameworkName },
{ "publish:Runtime", context.RuntimeIdentifier },
};
diff --git a/test/ArgumentForwardingTests/ArgumentForwardingTests/ArgumentForwardingTests.cs b/test/ArgumentForwardingTests/ArgumentForwardingTests/ArgumentForwardingTests.cs
index adde47bf9..4b4bb1614 100644
--- a/test/ArgumentForwardingTests/ArgumentForwardingTests/ArgumentForwardingTests.cs
+++ b/test/ArgumentForwardingTests/ArgumentForwardingTests/ArgumentForwardingTests.cs
@@ -94,16 +94,12 @@ namespace Microsoft.DotNet.Tests.ArgumentForwarding
[InlineData("\"abc\"\t\td\te")]
[InlineData(@"a\\b d""e f""g h")]
[InlineData(@"\ \\ \\\")]
- [InlineData(@"a\""b c d")]
[InlineData(@"a\\""b c d")]
- [InlineData(@"a\\\""b c d")]
[InlineData(@"a\\\\""b c d")]
[InlineData(@"a\\\\""b c d")]
[InlineData(@"a\\\\""b c"" d e")]
[InlineData(@"a""b c""d e""f g""h i""j k""l")]
[InlineData(@"a b c""def")]
- [InlineData(@"""\a\"" \\""\\\ b c")]
- [InlineData(@"a\""b \\ cd ""\e f\"" \\""\\\")]
public void TestArgumentForwardingCmd(string testUserArgument)
{
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
@@ -153,6 +149,29 @@ namespace Microsoft.DotNet.Tests.ArgumentForwarding
}
}
+ [Theory]
+ [InlineData(@"a\""b c d")]
+ [InlineData(@"a\\\""b c d")]
+ [InlineData(@"""\a\"" \\""\\\ b c")]
+ [InlineData(@"a\""b \\ cd ""\e f\"" \\""\\\")]
+ public void TestArgumentForwardingCmdFailsWithUnbalancedQuote(string testArgString)
+ {
+ if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ return;
+ }
+
+ // Get Baseline Argument Evaluation via Reflector
+ // This does not need to be different for cmd because
+ // it only establishes what the string[] args should be
+ var rawEvaluatedArgument = RawEvaluateArgumentString(testArgString);
+
+ // Escape and Re-Evaluate the rawEvaluatedArgument
+ var escapedEvaluatedRawArgument = EscapeAndEvaluateArgumentStringCmd(rawEvaluatedArgument);
+
+ rawEvaluatedArgument.Length.Should().NotBe(escapedEvaluatedRawArgument.Length);
+ }
+
///
/// EscapeAndEvaluateArgumentString returns a representation of string[] args
/// when rawEvaluatedArgument is passed as an argument to a process using
diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/TestCommand.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/TestCommand.cs
index f1672a62a..ef78f1018 100644
--- a/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/TestCommand.cs
+++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/Commands/TestCommand.cs
@@ -24,7 +24,7 @@ namespace Microsoft.DotNet.Tools.Test.Utilities
if (!Path.IsPathRooted(_command))
{
_command = Env.GetCommandPath(_command) ??
- Env.GetCommandPathFromAppBase(AppContext.BaseDirectory, _command);
+ Env.GetCommandPathFromRootPath(AppContext.BaseDirectory, _command);
}
Console.WriteLine($"Executing - {_command} {args}");
@@ -43,7 +43,7 @@ namespace Microsoft.DotNet.Tools.Test.Utilities
Console.WriteLine($"Executing (Captured Output) - {_command} {args}");
var commandPath = Env.GetCommandPath(_command, ".exe", ".cmd", "") ??
- Env.GetCommandPathFromAppBase(AppContext.BaseDirectory, _command, ".exe", ".cmd", "");
+ Env.GetCommandPathFromRootPath(AppContext.BaseDirectory, _command, ".exe", ".cmd", "");
var stdOut = new StreamForwarder();
var stdErr = new StreamForwarder();
diff --git a/test/ScriptExecutorTests/ScriptExecutorTests.cs b/test/ScriptExecutorTests/ScriptExecutorTests.cs
new file mode 100644
index 000000000..f0ed16ab4
--- /dev/null
+++ b/test/ScriptExecutorTests/ScriptExecutorTests.cs
@@ -0,0 +1,155 @@
+using System;
+using System.IO;
+using System.Collections.Generic;
+using Microsoft.DotNet.Cli.Utils;
+using Microsoft.DotNet.ProjectModel;
+using Microsoft.DotNet.Tools.Test.Utilities;
+using System.Runtime.InteropServices;
+using Xunit;
+using FluentAssertions;
+using NuGet.Frameworks;
+
+namespace Microsoft.DotNet.Cli.Utils.ScriptExecutorTests
+{
+ public class ScriptExecutorTests : TestBase
+ {
+ private static readonly string s_testProjectRoot = Path.Combine(AppContext.BaseDirectory, "TestAssets/TestProjects");
+
+ private TempDirectory _root;
+
+ public ScriptExecutorTests()
+ {
+ _root = Temp.CreateDirectory();
+ }
+
+ [Fact]
+ public void Test_Project_Local_Script_is_Resolved()
+ {
+ var sourceTestProjectPath = Path.Combine(s_testProjectRoot, "TestApp");
+ var binTestProjectPath = _root.CopyDirectory(sourceTestProjectPath).Path;
+
+ var project = ProjectContext.Create(binTestProjectPath, NuGetFramework.Parse("dnxcore50")).ProjectFile;
+
+ CreateTestFile("some.script", binTestProjectPath);
+ var scriptCommandLine = "some.script";
+
+ var command = ScriptExecutor.CreateCommandForScript(project, scriptCommandLine, new Dictionary());
+
+ command.Should().NotBeNull();
+ command.ResolutionStrategy.Should().Be(CommandResolutionStrategy.ProjectLocal);
+ }
+
+ [Fact]
+ public void Test_Nonexistent_Project_Local_Script_is_not_Resolved()
+ {
+ var sourceTestProjectPath = Path.Combine(s_testProjectRoot, "TestApp");
+ var binTestProjectPath = _root.CopyDirectory(sourceTestProjectPath).Path;
+
+ var project = ProjectContext.Create(binTestProjectPath, NuGetFramework.Parse("dnxcore50")).ProjectFile;
+
+ var scriptCommandLine = "nonexistent.script";
+
+ Action action = () => ScriptExecutor.CreateCommandForScript(project, scriptCommandLine, new Dictionary());
+ action.ShouldThrow();
+ }
+
+ [Fact]
+ public void Test_Extension_Inference_in_Resolution_for_Project_Local_Scripts()
+ {
+ var extensionList = new string[] {".cmd", ".sh"};
+
+ var expectedExtension = default(string);
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ expectedExtension = ".cmd";
+ }
+ else
+ {
+ expectedExtension = ".sh";
+ }
+
+ var sourceTestProjectPath = Path.Combine(s_testProjectRoot, "TestApp");
+ var binTestProjectPath = _root.CopyDirectory(sourceTestProjectPath).Path;
+
+ var project = ProjectContext.Create(binTestProjectPath, NuGetFramework.Parse("dnxcore50")).ProjectFile;
+
+ foreach (var extension in extensionList)
+ {
+ CreateTestFile("uniquescriptname" + extension, binTestProjectPath);
+ }
+
+ // Don't include extension
+ var scriptCommandLine = "uniquescriptname";
+
+ var command = ScriptExecutor.CreateCommandForScript(project, scriptCommandLine, new Dictionary());
+
+ command.Should().NotBeNull();
+ command.ResolutionStrategy.Should().Be(CommandResolutionStrategy.ProjectLocal);
+ command.CommandArgs.Should().Contain(scriptCommandLine + expectedExtension);
+ }
+
+ [Fact]
+ public void Test_Script_Exe_Files_Dont_Use_Cmd_or_Sh()
+ {
+ var sourceTestProjectPath = Path.Combine(s_testProjectRoot, "TestApp");
+ var binTestProjectPath = _root.CopyDirectory(sourceTestProjectPath).Path;
+
+ var project = ProjectContext.Create(binTestProjectPath, NuGetFramework.Parse("dnxcore50")).ProjectFile;
+
+ CreateTestFile("some.exe", binTestProjectPath);
+ var scriptCommandLine = "some.exe";
+
+ var command = ScriptExecutor.CreateCommandForScript(project, scriptCommandLine, new Dictionary());
+
+ command.Should().NotBeNull();
+ command.ResolutionStrategy.Should().Be(CommandResolutionStrategy.ProjectLocal);
+
+ Path.GetFileName(command.CommandName).Should().NotBe("cmd.exe");
+ Path.GetFileName(command.CommandName).Should().NotBe("sh");
+ }
+
+ [Fact]
+ public void Test_Script_Cmd_Files_Use_CmdExe()
+ {
+ if (! RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ return;
+ }
+
+ var sourceTestProjectPath = Path.Combine(s_testProjectRoot, "TestApp");
+ var binTestProjectPath = _root.CopyDirectory(sourceTestProjectPath).Path;
+
+ var project = ProjectContext.Create(binTestProjectPath, NuGetFramework.Parse("dnxcore50")).ProjectFile;
+
+ CreateTestFile("some.cmd", binTestProjectPath);
+ var scriptCommandLine = "some.cmd";
+
+ var command = ScriptExecutor.CreateCommandForScript(project, scriptCommandLine, new Dictionary());
+
+ command.Should().NotBeNull();
+ command.ResolutionStrategy.Should().Be(CommandResolutionStrategy.ProjectLocal);
+
+ Path.GetFileName(command.CommandName).Should().Be("cmd.exe");
+ }
+
+ [Fact]
+ public void Test_Script_Builtins_Fail()
+ {
+ var sourceTestProjectPath = Path.Combine(s_testProjectRoot, "TestApp");
+ var binTestProjectPath = _root.CopyDirectory(sourceTestProjectPath).Path;
+
+ var project = ProjectContext.Create(binTestProjectPath, NuGetFramework.Parse("dnxcore50")).ProjectFile;
+
+ var scriptCommandLine = "echo";
+
+ Action action = () => ScriptExecutor.CreateCommandForScript(project, scriptCommandLine, new Dictionary());
+ action.ShouldThrow();
+ }
+
+ private void CreateTestFile(string filename, string directory)
+ {
+ string path = Path.Combine(directory, filename);
+ File.WriteAllText(path, "echo hello");
+ }
+ }
+}
diff --git a/test/ScriptExecutorTests/ScriptExecutorTests.xproj b/test/ScriptExecutorTests/ScriptExecutorTests.xproj
new file mode 100644
index 000000000..8b66a40d6
--- /dev/null
+++ b/test/ScriptExecutorTests/ScriptExecutorTests.xproj
@@ -0,0 +1,19 @@
+
+
+
+ 14.0.23107
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+
+
+
+ 833ffee1-7eed-4f51-8dfd-946d48833333
+ Microsoft.DotNet.Cli.Utils.ScriptExecutorTests
+ ..\..\artifacts\obj\$(MSBuildProjectName)
+ ..\..\artifacts\bin\$(MSBuildProjectName)\
+
+
+
+ 2.0
+
+
+
\ No newline at end of file
diff --git a/test/ScriptExecutorTests/project.json b/test/ScriptExecutorTests/project.json
new file mode 100644
index 000000000..22bda6b8e
--- /dev/null
+++ b/test/ScriptExecutorTests/project.json
@@ -0,0 +1,26 @@
+{
+ "version": "1.0.0-*",
+
+ "dependencies": {
+ "NETStandard.Library": "1.0.0-rc2-23805",
+
+ "Microsoft.DotNet.ProjectModel": { "target": "project" },
+ "Microsoft.DotNet.Cli.Utils": { "target": "project" },
+ "Microsoft.DotNet.Tools.Tests.Utilities": { "target": "project" },
+
+ "xunit": "2.1.0",
+ "dotnet-test-xunit": "1.0.0-dev-48273-16"
+ },
+
+ "frameworks": {
+ "dnxcore50": {
+ "imports": "portable-net45+win8"
+ }
+ },
+
+ "content": [
+ "../../TestAssets/TestProjects/TestApp/**/*"
+ ],
+
+ "testRunner": "xunit"
+}
diff --git a/test/dotnet-publish.Tests/Microsoft.DotNet.Tools.Publish.Tests.cs b/test/dotnet-publish.Tests/Microsoft.DotNet.Tools.Publish.Tests.cs
index a9d465ee5..ac61d0d42 100644
--- a/test/dotnet-publish.Tests/Microsoft.DotNet.Tools.Publish.Tests.cs
+++ b/test/dotnet-publish.Tests/Microsoft.DotNet.Tools.Publish.Tests.cs
@@ -214,18 +214,15 @@ namespace Microsoft.DotNet.Tools.Publish.Tests
}
[Fact]
- [ActiveIssue(982)]
public void PublishScriptsRun()
{
// create unique directories in the 'temp' folder
var root = Temp.CreateDirectory();
- var testAppDir = root.CreateDirectory("TestApp");
- var testLibDir = root.CreateDirectory("TestLibrary");
+ var testAppDir = root.CreateDirectory("TestAppWithScripts");
//copy projects to the temp dir
- CopyProjectToTempDir(Path.Combine(_testProjectsRoot, "TestApp"), testAppDir);
- CopyProjectToTempDir(Path.Combine(_testProjectsRoot, "TestLibrary"), testLibDir);
+ CopyProjectToTempDir(Path.Combine(_testProjectsRoot, "TestAppWithScripts"), testAppDir);
// run publish
var testProject = GetProjectPath(testAppDir);
diff --git a/test/dotnet-publish.Tests/project.json b/test/dotnet-publish.Tests/project.json
index db7d665b1..75ab3dd6b 100644
--- a/test/dotnet-publish.Tests/project.json
+++ b/test/dotnet-publish.Tests/project.json
@@ -23,6 +23,7 @@
"content": [
"../../TestAssets/TestProjects/TestApp/**/*",
+ "../../TestAssets/TestProjects/TestAppWithScripts/**/*",
"../../TestAssets/TestProjects/TestLibrary/**/*",
"../../TestAssets/TestProjects/CompileFail/**/*",
"../../TestAssets/TestProjects/TestBindingRedirectGeneration/**/*",