未验证 提交 c64367af 编写于 作者: E Elinor Fung 提交者: GitHub

Update error on failure to execute when SDK could not be resolved (#163)

上级 651050ed
......@@ -531,7 +531,7 @@ int fx_muxer_t::execute(
int result = command_line::parse_args_for_mode(mode, host_info, argc, argv, &new_argoff, app_candidate, opts);
if (static_cast<StatusCode>(result) == AppArgNotRunnable)
{
return handle_cli(host_info, argc, argv);
return handle_cli(host_info, argc, argv, app_candidate);
}
if (!result)
......@@ -964,7 +964,8 @@ int fx_muxer_t::handle_exec_host_command(
int fx_muxer_t::handle_cli(
const host_startup_info_t& host_info,
int argc,
const pal::char_t* argv[])
const pal::char_t* argv[],
const pal::string_t& app_candidate)
{
// Check for commands that don't depend on the CLI SDK to be loaded
if (pal::strcasecmp(_X("--list-sdks"), argv[1]) == 0)
......@@ -979,10 +980,11 @@ int fx_muxer_t::handle_cli(
}
//
// Did not exececute the app or run other commands, so try the CLI SDK dotnet.dll
// Did not execute the app or run other commands, so try the CLI SDK dotnet.dll
//
auto sdk_dotnet = sdk_resolver::from_nearest_global_file().resolve(host_info.dotnet_root);
sdk_resolver resolver = sdk_resolver::from_nearest_global_file();
auto sdk_dotnet = resolver.resolve(host_info.dotnet_root, false /*print_errors*/);
if (sdk_dotnet.empty())
{
assert(argc > 1);
......@@ -1000,6 +1002,13 @@ int fx_muxer_t::handle_cli(
return StatusCode::Success;
}
trace::error(_X("Could not execute because the application was not found or a compatible .NET Core SDK is not installed."));
trace::error(_X("Possible reasons for this include:"));
trace::error(_X(" * You intended to execute a .NET Core program:"));
trace::error(_X(" The application '%s' does not exist."), app_candidate.c_str());
trace::error(_X(" * You intended to execute a .NET Core SDK command:"));
resolver.print_resolution_error(host_info.dotnet_root, _X(" "));
return StatusCode::LibHostSdkFindFailure;
}
......@@ -1022,16 +1031,16 @@ int fx_muxer_t::handle_cli(
trace::verbose(_X("Using .NET Core SDK dll=[%s]"), sdk_dotnet.c_str());
int new_argoff;
pal::string_t app_candidate;
pal::string_t sdk_app_candidate;
opt_map_t opts;
int result = command_line::parse_args_for_sdk_command(host_info, new_argv.size(), new_argv.data(), &new_argoff, app_candidate, opts);
int result = command_line::parse_args_for_sdk_command(host_info, new_argv.size(), new_argv.data(), &new_argoff, sdk_app_candidate, opts);
if (!result)
{
// Transform dotnet [exec] [--additionalprobingpath path] [--depsfile file] [dll] [args] -> dotnet [dll] [args]
result = handle_exec_host_command(
pal::string_t{} /*host_command*/,
host_info,
app_candidate,
sdk_app_candidate,
opts,
new_argv.size(),
new_argv.data(),
......
......@@ -54,5 +54,6 @@ private:
static int handle_cli(
const host_startup_info_t& host_info,
int argc,
const pal::char_t* argv[]);
const pal::char_t* argv[],
const pal::string_t& app_candidate);
};
......@@ -46,15 +46,17 @@ pal::string_t const& sdk_resolver::global_file_path() const
return global_file;
}
pal::string_t sdk_resolver::resolve(const pal::string_t& dotnet_root) const
pal::string_t sdk_resolver::resolve(const pal::string_t& dotnet_root, bool print_errors) const
{
auto requested = version.is_empty() ? pal::string_t{} : version.as_str();
trace::verbose(
_X("Resolving SDKs with version = '%s', rollForward = '%s', allowPrerelease = %s"),
requested.empty() ? _X("latest") : requested.c_str(),
to_policy_name(roll_forward),
allow_prerelease ? _X("true") : _X("false"));
if (trace::is_enabled())
{
auto requested = version.is_empty() ? pal::string_t{} : version.as_str();
trace::verbose(
_X("Resolving SDKs with version = '%s', rollForward = '%s', allowPrerelease = %s"),
requested.empty() ? _X("latest") : requested.c_str(),
to_policy_name(roll_forward),
allow_prerelease ? _X("true") : _X("false"));
}
pal::string_t resolved_sdk_path;
fx_ver_t resolved_version;
......@@ -78,27 +80,44 @@ pal::string_t sdk_resolver::resolve(const pal::string_t& dotnet_root) const
return resolved_sdk_path;
}
if (!requested.empty())
if (print_errors)
print_resolution_error(dotnet_root, _X(""));
return {};
}
void sdk_resolver::print_resolution_error(const pal::string_t& dotnet_root, const pal::char_t *prefix) const
{
bool sdk_exists = false;
const pal::char_t *no_sdk_message = _X("It was not possible to find any installed .NET Core SDKs.");
if (!version.is_empty())
{
pal::string_t requested = version.as_str();
if (!global_file.empty())
{
trace::error(_X("A compatible installed .NET Core SDK for global.json version [%s] from [%s] was not found"), requested.c_str(), global_file.c_str());
trace::error(_X("Install the [%s] .NET Core SDK or update [%s] with an installed .NET Core SDK:"), requested.c_str(), global_file.c_str());
trace::error(_X("%sA compatible installed .NET Core SDK for global.json version [%s] from [%s] was not found."), prefix, requested.c_str(), global_file.c_str());
trace::error(_X("%sInstall the [%s] .NET Core SDK or update [%s] with an installed .NET Core SDK:"), prefix, requested.c_str(), global_file.c_str());
}
else
{
trace::error(_X("A compatible installed .NET Core SDK version [%s] was not found"), requested.c_str());
trace::error(_X("Install the [%s] .NET Core SDK or create a global.json file with an installed .NET Core SDK:"), requested.c_str());
trace::error(_X("%sA compatible installed .NET Core SDK version [%s] was not found."), prefix, requested.c_str());
trace::error(_X("%sInstall the [%s] .NET Core SDK or create a global.json file with an installed .NET Core SDK:"), prefix, requested.c_str());
}
sdk_exists = sdk_info::print_all_sdks(dotnet_root, pal::string_t{prefix}.append(_X(" ")));
if (!sdk_exists)
trace::error(_X("%s %s"), prefix, no_sdk_message);
}
else
{
trace::error(_X("%s%s"), prefix, no_sdk_message);
}
if (requested.empty() || !sdk_info::print_all_sdks(dotnet_root, _X(" ")))
if (!sdk_exists)
{
trace::error(_X(" It was not possible to find any installed .NET Core SDKs"));
trace::error(_X(" Did you mean to run .NET Core SDK commands? Install a .NET Core SDK from:"));
trace::error(_X(" %s"), DOTNET_CORE_DOWNLOAD_URL);
trace::error(_X("%sInstall a .NET Core SDK from:"), prefix);
trace::error(_X("%s %s"), prefix, DOTNET_CORE_DOWNLOAD_URL);
}
return {};
}
sdk_resolver sdk_resolver::from_nearest_global_file(bool allow_prerelease)
......
......@@ -40,7 +40,9 @@ public:
pal::string_t const& global_file_path() const;
pal::string_t resolve(const pal::string_t& dotnet_root) const;
pal::string_t resolve(const pal::string_t& dotnet_root, bool print_errors = true) const;
void print_resolution_error(const pal::string_t& dotnet_root, const pal::char_t *prefix) const;
static sdk_resolver from_nearest_global_file(bool allow_prerelease = true);
......
......@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.DotNet.Cli.Build;
using System;
using System.IO;
using Xunit;
......@@ -12,22 +13,16 @@ public class DotnetArgValidation : IClassFixture<DotnetArgValidation.SharedTestS
{
private SharedTestState sharedTestState;
public DotnetArgValidation(DotnetArgValidation.SharedTestState fixture)
public DotnetArgValidation(DotnetArgValidation.SharedTestState sharedState)
{
sharedTestState = fixture;
sharedTestState = sharedState;
}
[Fact]
public void Muxer_Exec_With_Missing_App_Assembly_Fails()
public void MuxerExec_MissingAppAssembly_Fails()
{
var fixture = sharedTestState.PortableAppFixture
.Copy();
var dotnet = fixture.BuiltDotnet;
string assemblyName = Path.Combine(GetNonexistentAndUnnormalizedPath(), "foo.dll");
dotnet.Exec("exec", assemblyName)
sharedTestState.BuiltDotNet.Exec("exec", assemblyName)
.CaptureStdOut()
.CaptureStdErr()
.Execute(fExpectedToFail: true)
......@@ -36,16 +31,10 @@ public void Muxer_Exec_With_Missing_App_Assembly_Fails()
}
[Fact]
public void Muxer_Exec_With_Missing_App_Assembly_And_Bad_Extension_Fails()
public void MuxerExec_MissingAppAssembly_BadExtension_Fails()
{
var fixture = sharedTestState.PortableAppFixture
.Copy();
var dotnet = fixture.BuiltDotnet;
string assemblyName = Path.Combine(GetNonexistentAndUnnormalizedPath(), "foo.xzy");
dotnet.Exec("exec", assemblyName)
sharedTestState.BuiltDotNet.Exec("exec", assemblyName)
.CaptureStdOut()
.CaptureStdErr()
.Execute(fExpectedToFail: true)
......@@ -54,19 +43,14 @@ public void Muxer_Exec_With_Missing_App_Assembly_And_Bad_Extension_Fails()
}
[Fact]
public void Muxer_Exec_With_Bad_Extension_Fails()
public void MuxerExec_BadExtension_Fails()
{
var fixture = sharedTestState.PortableAppFixture
.Copy();
var dotnet = fixture.BuiltDotnet;
// Get a valid file name, but not exe or dll
string fxDir = Path.Combine(fixture.SdkDotnet.BinPath, "shared", "Microsoft.NETCore.App");
string fxDir = Path.Combine(sharedTestState.RepoDirectories.DotnetSDK, "shared", "Microsoft.NETCore.App");
fxDir = new DirectoryInfo(fxDir).GetDirectories()[0].FullName;
string assemblyName = Path.Combine(fxDir, "Microsoft.NETCore.App.deps.json");
dotnet.Exec("exec", assemblyName)
sharedTestState.BuiltDotNet.Exec("exec", assemblyName)
.CaptureStdOut()
.CaptureStdErr()
.Execute(fExpectedToFail: true)
......@@ -75,14 +59,9 @@ public void Muxer_Exec_With_Bad_Extension_Fails()
}
[Fact]
public void Detect_Missing_Argument_Value()
public void MissingArgumentValue_Fails()
{
var fixture = sharedTestState.PortableAppFixture
.Copy();
var dotnet = fixture.BuiltDotnet;
dotnet.Exec("--fx-version")
sharedTestState.BuiltDotNet.Exec("--fx-version")
.CaptureStdOut()
.CaptureStdErr()
.Execute(fExpectedToFail: true)
......@@ -90,29 +69,51 @@ public void Detect_Missing_Argument_Value()
.And.HaveStdErrContaining($"Failed to parse supported options or their values:");
}
[Fact]
public void InvalidFileOrCommand_NoSDK_ListsPossibleIssues()
{
string fileName = "NonExistent";
sharedTestState.BuiltDotNet.Exec(fileName)
.WorkingDirectory(sharedTestState.BaseDirectory)
.CaptureStdOut()
.CaptureStdErr()
.Execute(fExpectedToFail: true)
.Should().Fail()
.And.HaveStdErrContaining($"The application '{fileName}' does not exist")
.And.HaveStdErrContaining($"It was not possible to find any installed .NET Core SDKs");
}
// Return a non-exisitent path that contains a mix of / and \
private string GetNonexistentAndUnnormalizedPath()
{
return Path.Combine(sharedTestState.PortableAppFixture.SdkDotnet.BinPath, @"x\y/");
return Path.Combine(sharedTestState.RepoDirectories.DotnetSDK, @"x\y/");
}
public class SharedTestState : IDisposable
{
public RepoDirectoriesProvider RepoDirectories { get; }
public TestProjectFixture PortableAppFixture { get; }
public DotNetCli BuiltDotNet { get; }
public string BaseDirectory { get; }
public SharedTestState()
{
RepoDirectories = new RepoDirectoriesProvider();
BuiltDotNet = new DotNetCli(RepoDirectories.BuiltDotnet);
BaseDirectory = SharedFramework.CalculateUniqueTestDirectory(Path.Combine(TestArtifact.TestArtifactsPath, "argValidation"));
PortableAppFixture = new TestProjectFixture("PortableApp", RepoDirectories)
.EnsureRestored(RepoDirectories.CorehostPackages)
.BuildProject();
// Create an empty global.json file
Directory.CreateDirectory(BaseDirectory);
File.WriteAllText(Path.Combine(BaseDirectory, "global.json"), "{}");
}
public void Dispose()
{
PortableAppFixture.Dispose();
if (!TestArtifact.PreserveTestRuns() && Directory.Exists(BaseDirectory))
{
Directory.Delete(BaseDirectory, true);
}
}
}
}
......
......@@ -388,7 +388,7 @@ public void SdkLookup_Negative_Version()
.Execute(fExpectedToFail: true)
.Should().Fail()
.And.HaveStdErrContaining("It was not possible to find any installed .NET Core SDKs")
.And.HaveStdErrContaining("Did you mean to run .NET Core SDK commands? Install a .NET Core SDK from");
.And.HaveStdErrContaining("Install a .NET Core SDK from");
// Add SDK versions
AddAvailableSdkVersions(_exeSdkBaseDir, "9999.0.4");
......@@ -423,7 +423,7 @@ public void SdkLookup_Negative_Version()
public void SdkLookup_Must_Pick_The_Highest_Semantic_Version()
{
WriteEmptyGlobalJson();
// Add SDK versions
AddAvailableSdkVersions(_exeSdkBaseDir, "9999.0.0", "9999.0.3-dummy.9", "9999.0.3-dummy.10");
......@@ -1291,7 +1291,7 @@ private void WriteGlobalJson(string contents)
{
File.WriteAllText(Path.Combine(_currentWorkingDir, "global.json"), contents);
}
private void WriteEmptyGlobalJson() => WriteGlobalJson("{}");
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册