dotnet-installer/src/dotnet/commands/dotnet-run/RunCommand.cs

290 lines
10 KiB
C#

// 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.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.Build.Execution;
using Microsoft.Build.Exceptions;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Tools;
using Microsoft.DotNet.Tools.MSBuild;
using Microsoft.DotNet.Tools.Run.LaunchSettings;
namespace Microsoft.DotNet.Tools.Run
{
public partial class RunCommand
{
public string Configuration { get; private set; }
public string Framework { get; private set; }
public bool NoBuild { get; private set; }
public string Project { get; private set; }
public IReadOnlyCollection<string> Args { get; private set; }
public bool NoRestore { get; private set; }
public IEnumerable<string> RestoreArgs { get; private set; }
private List<string> _args;
private bool ShouldBuild => !NoBuild;
public string LaunchProfile { get; private set; }
public bool NoLaunchProfile { get; private set; }
private bool UseLaunchProfile => !NoLaunchProfile;
public int Start()
{
Initialize();
if (ShouldBuild)
{
EnsureProjectIsBuilt();
}
try
{
ICommand runCommand = GetRunCommand();
int launchSettingsApplicationResult = ApplyLaunchProfileSettingsIfNeeded(ref runCommand);
if (launchSettingsApplicationResult != 0)
{
return launchSettingsApplicationResult;
}
return runCommand
.Execute()
.ExitCode;
}
catch (InvalidProjectFileException e)
{
throw new GracefulException(
string.Format(LocalizableStrings.RunCommandSpecifiecFileIsNotAValidProject, Project),
e);
}
}
public RunCommand(string configuration,
string framework,
bool noBuild,
string project,
string launchProfile,
bool noLaunchProfile,
bool noRestore,
IEnumerable<string> restoreArgs,
IReadOnlyCollection<string> args)
{
Configuration = configuration;
Framework = framework;
NoBuild = noBuild;
Project = project;
LaunchProfile = launchProfile;
NoLaunchProfile = noLaunchProfile;
Args = args;
RestoreArgs = restoreArgs;
NoRestore = noRestore;
}
public RunCommand MakeNewWithReplaced(string configuration = null,
string framework = null,
bool? noBuild = null,
string project = null,
string launchProfile = null,
bool? noLaunchProfile = null,
bool? noRestore = null,
IEnumerable<string> restoreArgs = null,
IReadOnlyCollection<string> args = null)
{
return new RunCommand(
configuration ?? this.Configuration,
framework ?? this.Framework,
noBuild ?? this.NoBuild,
project ?? this.Project,
launchProfile ?? this.LaunchProfile,
noLaunchProfile ?? this.NoLaunchProfile,
noRestore ?? this.NoRestore,
restoreArgs ?? this.RestoreArgs,
args ?? this.Args
);
}
private int ApplyLaunchProfileSettingsIfNeeded(ref ICommand runCommand)
{
if (UseLaunchProfile)
{
var buildPathContainer = File.Exists(Project) ? Path.GetDirectoryName(Project) : Project;
var launchSettingsPath = Path.Combine(buildPathContainer, "Properties", "launchSettings.json");
if (File.Exists(launchSettingsPath))
{
Reporter.Output.WriteLine(string.Format(LocalizableStrings.UsingLaunchSettingsFromMessage, launchSettingsPath));
string profileName = string.IsNullOrEmpty(LaunchProfile) ? LocalizableStrings.DefaultLaunchProfileDisplayName : LaunchProfile;
try
{
var launchSettingsFileContents = File.ReadAllText(launchSettingsPath);
var applyResult = LaunchSettingsManager.TryApplyLaunchSettings(launchSettingsFileContents, ref runCommand, LaunchProfile);
if (!applyResult.Success)
{
//Error that the launch profile couldn't be applied
Reporter.Error.WriteLine(string.Format(LocalizableStrings.RunCommandExceptionCouldNotApplyLaunchSettings, profileName, applyResult.FailureReason).Bold().Red());
}
}
catch (IOException ex)
{
Reporter.Error.WriteLine(string.Format(LocalizableStrings.RunCommandExceptionCouldNotApplyLaunchSettings, profileName).Bold().Red());
Reporter.Error.WriteLine(ex.Message.Bold().Red());
return -1;
}
}
else if (!string.IsNullOrEmpty(LaunchProfile))
{
//Error that the launch profile couldn't be found
Reporter.Error.WriteLine(LocalizableStrings.RunCommandExceptionCouldNotLocateALaunchSettingsFile.Bold().Red());
}
}
return 0;
}
private void EnsureProjectIsBuilt()
{
var restoreArgs = GetRestoreArguments();
var buildResult =
new RestoringCommand(
restoreArgs.Prepend(Project),
restoreArgs,
new [] { Project },
NoRestore
).Execute();
if (buildResult != 0)
{
Reporter.Error.WriteLine();
throw new GracefulException(LocalizableStrings.RunCommandException);
}
}
private List<string> GetRestoreArguments()
{
List<string> args = new List<string>()
{
"-nologo"
};
if (!RestoreArgs.Any(a => a.StartsWith("-verbosity:")))
{
args.Add("-verbosity:quiet");
}
args.AddRange(RestoreArgs);
return args;
}
private ICommand GetRunCommand()
{
var globalProperties = new Dictionary<string, string>
{
// This property disables default item globbing to improve performance
// This should be safe because we are not evaluating items, only properties
{ Constants.EnableDefaultItems, "false" },
{ Constants.MSBuildExtensionsPath, AppContext.BaseDirectory }
};
if (!string.IsNullOrWhiteSpace(Configuration))
{
globalProperties.Add("Configuration", Configuration);
}
if (!string.IsNullOrWhiteSpace(Framework))
{
globalProperties.Add("TargetFramework", Framework);
}
var project = new ProjectInstance(Project, globalProperties, null);
string runProgram = project.GetPropertyValue("RunCommand");
if (string.IsNullOrEmpty(runProgram))
{
ThrowUnableToRunError(project);
}
string runArguments = project.GetPropertyValue("RunArguments");
string runWorkingDirectory = project.GetPropertyValue("RunWorkingDirectory");
string fullArguments = runArguments;
if (_args.Any())
{
fullArguments += " " + ArgumentEscaper.EscapeAndConcatenateArgArrayForProcessStart(_args);
}
CommandSpec commandSpec = new CommandSpec(runProgram, fullArguments, CommandResolutionStrategy.None);
return Command.Create(commandSpec)
.WorkingDirectory(runWorkingDirectory);
}
private void ThrowUnableToRunError(ProjectInstance project)
{
string targetFrameworks = project.GetPropertyValue("TargetFrameworks");
if (!string.IsNullOrEmpty(targetFrameworks))
{
string targetFramework = project.GetPropertyValue("TargetFramework");
if (string.IsNullOrEmpty(targetFramework))
{
var framework = "--framework";
throw new GracefulException(LocalizableStrings.RunCommandExceptionUnableToRunSpecifyFramework, framework);
}
}
string outputType = project.GetPropertyValue("OutputType");
throw new GracefulException(
string.Format(
LocalizableStrings.RunCommandExceptionUnableToRun,
"dotnet run",
"OutputType",
outputType));
}
private void Initialize()
{
if (string.IsNullOrWhiteSpace(Project))
{
Project = Directory.GetCurrentDirectory();
}
if (Directory.Exists(Project))
{
Project = FindSingleProjectInDirectory(Project);
}
if (Args == null)
{
_args = new List<string>();
}
else
{
_args = new List<string>(Args);
}
}
private static string FindSingleProjectInDirectory(string directory)
{
string[] projectFiles = Directory.GetFiles(directory, "*.*proj");
if (projectFiles.Length == 0)
{
var project = "--project";
throw new GracefulException(LocalizableStrings.RunCommandExceptionNoProjects, directory, project);
}
else if (projectFiles.Length > 1)
{
throw new GracefulException(LocalizableStrings.RunCommandExceptionMultipleProjects, directory);
}
return projectFiles[0];
}
}
}