Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 33 additions & 3 deletions src/Cli/dotnet/Commands/Test/MTP/SolutionAndProjectUtility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Microsoft.Build.Evaluation;
using Microsoft.Build.Evaluation.Context;
using Microsoft.Build.Execution;
using Microsoft.Build.Framework;
using Microsoft.DotNet.Cli.Commands.Run;
using Microsoft.DotNet.Cli.Extensions;
using Microsoft.DotNet.Cli.Utils;
Expand Down Expand Up @@ -556,6 +557,7 @@ private static (string? device, string? runtimeIdentifier) SelectDeviceForTfm(
runProperties = DeployAndGetRunProperties(
project,
logger,
AnalyzeStandardTestMSBuildArgs(buildOptions.MSBuildArgs),
buildOptions.EnvironmentVariables,
out runtimeEnvironmentVariables);

Expand Down Expand Up @@ -603,6 +605,7 @@ private static (string? device, string? runtimeIdentifier) SelectDeviceForTfm(
static RunProperties DeployAndGetRunProperties(
ProjectInstance project,
FacadeLogger? logger,
MSBuildArgs msbuildArgs,
IReadOnlyDictionary<string, string> environmentVariables,
out IReadOnlyDictionary<string, string> runtimeEnvironmentVariables)
{
Expand All @@ -618,20 +621,19 @@ static RunProperties DeployAndGetRunProperties(
// NOTE: BuildManager is singleton.
lock (s_buildLock)
{
var loggers = logger is null ? null : new[] { logger };
if (project.Targets.ContainsKey(Constants.DeployToDevice))
{
// Deploy on a fresh ProjectInstance to avoid accumulating state (existing item
// groups) that would leak into the ComputeRunArguments build below, which has to
// build the original instance since the run properties are read back from it.
// Same reason as dotnet run, see RunCommandSelector.OpenProjectIfNeeded.
if (!project.DeepCopy().Build([Constants.DeployToDevice], loggers))
if (!project.DeepCopy().Build([Constants.DeployToDevice], CreateBuildLoggers(msbuildArgs, logger)))
{
throw new GracefulException(CliCommandStrings.RunCommandDeployFailed);
}
}

if (!project.Build(s_computeRunArgumentsTarget, loggers))
if (!project.Build(s_computeRunArgumentsTarget, CreateBuildLoggers(msbuildArgs, logger)))
{
throw new GracefulException(CliCommandStrings.RunCommandEvaluationExceptionBuildFailed, s_computeRunArgumentsTarget[0]);
}
Expand All @@ -644,6 +646,34 @@ static RunProperties DeployAndGetRunProperties(
}
}

/// <summary>
/// Gets the loggers to attach to an in-process <see cref="ProjectInstance"/> build.
/// A console logger is attached (unless <c>-noConsoleLogger</c> was passed) so that MSBuild errors are
/// actually reported to the user: the binary logger only forwards events to binlogs, and it is only
/// created when <c>-bl</c> was passed, so without a console logger these builds fail silently and the user
/// is only told to "fix the errors and warnings" without any error being printed anywhere.
/// This mirrors what <c>dotnet run</c> does in <c>RunCommand.InvokeRunArgumentsTarget</c>.
/// </summary>
/// <remarks>
/// A fresh console logger is created for each build to avoid disposal issues when calling
/// <see cref="ProjectInstance.Build(string[], IEnumerable{ILogger})"/> multiple times.
/// </remarks>
private static IEnumerable<ILogger> CreateBuildLoggers(MSBuildArgs msbuildArgs, FacadeLogger? binaryLogger)
{
if (binaryLogger is not null)
{
yield return binaryLogger;
}

if (!LoggerUtility.HasNoConsoleLoggerArgument(msbuildArgs.OtherMSBuildArgs))
{
// These builds only compute run arguments and deploy, so keep them quiet - at this verbosity
// MSBuild still reports errors and warnings.
yield return CommonRunHelpers.GetConsoleLogger(
msbuildArgs.CloneWithExplicitArgs([$"--verbosity:{LoggerVerbosity.Quiet.ToString().ToLowerInvariant()}", .. msbuildArgs.OtherMSBuildArgs]));
}
}

private static LaunchProfile? TryGetLaunchProfileSettings(string projectDirectory, string projectNameWithoutExtension, string appDesignerFolder, BuildOptions buildOptions, string? profileName)
{
if (buildOptions.NoLaunchProfile)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@
Overwrite="true" />
</Target>

<Target Name="_FailComputeRunArguments"
BeforeTargets="ComputeRunArguments"
Condition="'$(FailComputeRunArguments)' == 'true'">
<Error Text="ComputeRunArguments failed as requested." />
</Target>

<Target Name="_LogRuntimeEnvironmentVariableDuringBuild"
BeforeTargets="_MTPBuild"
Condition="'@(RuntimeEnvironmentVariable)' != ''">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -608,7 +608,33 @@ public void ItFailsWhenDeployToDeviceTargetFails()
"-p:FailDeployToDevice=true");

result.Should().Fail()
.And.HaveStdErrContaining(CliCommandStrings.RunCommandDeployFailed);
.And.HaveStdErrContaining(CliCommandStrings.RunCommandDeployFailed)
// The MSBuild error itself must be reported, otherwise the user is told to fix errors
// that were never printed anywhere.
.And.HaveStdOutContaining("DeployToDevice failed as requested.");
}

[TestMethod]
public void ItFailsWhenComputeRunArgumentsTargetFails()
{
var testInstance = TestAssetsManager.CopyTestAsset("DotnetTestDevices", identifier: "ComputeRunArgumentsFailure")
.WithSource();

var result = new DotnetTestCommand(Log, disableNewOutput: false)
.WithWorkingDirectory(testInstance.Path)
.WithEnvironmentVariable("DOTNET_CLI_UI_LANGUAGE", "en-US")
.Execute(
"--framework",
ToolsetInfo.CurrentTargetFramework,
"--device",
"test-device-1",
"-p:FailComputeRunArguments=true");

result.Should().Fail()
.And.HaveStdErrContaining(string.Format(CliCommandStrings.RunCommandEvaluationExceptionBuildFailed, "ComputeRunArguments"))
// The MSBuild error itself must be reported, otherwise the user is told to fix errors
// that were never printed anywhere.
.And.HaveStdOutContaining("ComputeRunArguments failed as requested.");
}

[TestMethod]
Expand Down
Loading