dotnet-installer/src/dotnet/commands/dotnet-repl-csi/Program.cs
Eric Erhardt 44483ddc98 Remove System.CommandLine dependency.
Also removed the dependency on Microsoft.Extensions.CommandLineUtils.Sources NuGet package and instead just checking the source files into our repo as internal classes.

Fix #2526
2016-04-20 18:47:37 -05:00

184 lines
8 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.DotNet.Cli.CommandLine;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.ProjectModel;
using NuGet.Frameworks;
namespace Microsoft.DotNet.Tools.Repl.Csi
{
public sealed class ReplCsiCommand
{
public static int Run(string[] args)
{
DebugHelper.HandleDebugSwitch(ref args);
var app = new CommandLineApplication(throwOnUnexpectedArg: false);
app.Name = "dotnet repl csi";
app.FullName = "C# REPL";
app.Description = "C# REPL for the .NET platform";
app.HelpOption("-h|--help");
var script = app.Argument("<SCRIPT>", "The .csx file to run. Defaults to interactive mode");
var framework = app.Option("-f|--framework <FRAMEWORK>", "Compile a specific framework", CommandOptionType.SingleValue);
var configuration = app.Option("-c|--configuration <CONFIGURATION>", "Configuration under which to build", CommandOptionType.SingleValue);
var preserveTemporary = app.Option("-t|--preserve-temporary", "Preserve the temporary directory containing the compiled project", CommandOptionType.NoValue);
var project = app.Option("-p|--project <PROJECT>", "The path to the project to run. Can be a path to a project.json or a project directory", CommandOptionType.SingleValue);
app.OnExecute(() => Run(script.Value, framework.Value(), configuration.Value(), preserveTemporary.HasValue(), project.Value(), app.RemainingArguments));
return app.Execute(args);
}
private static ProjectContext GetProjectContext(string targetFramework, string projectPath)
{
// Selecting the target framework for the project is done in the same manner as dotnet run. If a target framework
// was specified, we attempt to create a context for that framework (and error out if the framework is unsupported).
// Otherwise, we pick the first context supported by the project.
var contexts = ProjectContext.CreateContextForEachFramework(Path.GetFullPath(projectPath));
var context = contexts.First();
if (targetFramework != null)
{
var framework = NuGetFramework.Parse(targetFramework);
context = contexts.FirstOrDefault(c => c.TargetFramework.Equals(framework));
}
return context;
}
private static int CompileProject(ProjectContext projectContext, string configuration, out string tempOutputDir)
{
tempOutputDir = Path.Combine(projectContext.ProjectDirectory, "bin", ".dotnetrepl", Guid.NewGuid().ToString("N"));
Reporter.Output.WriteLine($"Compiling {projectContext.RootProject.Identity.Name.Yellow()} for {projectContext.TargetFramework.DotNetFrameworkName.Yellow()} to use with the {"C# REPL".Yellow()} environment.");
return Build.BuildCommand.Run(new[]
{
$"--output",
$"{tempOutputDir}",
$"--framework",
$"{projectContext.TargetFramework}",
$"--configuration",
$"{configuration}",
$"{projectContext.ProjectDirectory}"
});
}
private static IEnumerable<string> GetRuntimeDependencies(ProjectContext projectContext, string buildConfiguration)
{
// We collect the full list of runtime dependencies here and pass them back so they can be
// referenced by the REPL environment when seeding the context. It appears that we need to
// explicitly list the dependencies as they may not exist in the output directory (as is the
// for library projects) or they may not exist anywhere on the path (e.g. they may only exist
// in the nuget package that was downloaded for the compilation) or they may be specific to a
// specific target framework.
var runtimeDependencies = new HashSet<string>();
var projectExporter = projectContext.CreateExporter(buildConfiguration);
var projectDependencies = projectExporter.GetDependencies();
foreach (var projectDependency in projectDependencies)
{
var runtimeAssemblies = projectDependency.RuntimeAssemblyGroups;
foreach (var runtimeAssembly in runtimeAssemblies.GetDefaultAssets())
{
var runtimeAssemblyPath = runtimeAssembly.ResolvedPath;
runtimeDependencies.Add(runtimeAssemblyPath);
}
}
return runtimeDependencies;
}
private static string CreateResponseFile(ProjectContext projectContext, string buildConfiguration, string tempOutputDir)
{
var outputFileName = projectContext.ProjectFile.Name;
var outputFilePath = Path.Combine(tempOutputDir, $"{outputFileName}.dll");
var projectResponseFilePath = Path.Combine(tempOutputDir, $"dotnet-repl.{outputFileName}{Constants.ResponseFileSuffix}");
var runtimeDependencies = GetRuntimeDependencies(projectContext, buildConfiguration);
using (var fileStream = new FileStream(projectResponseFilePath, FileMode.Create))
{
using (var streamWriter = new StreamWriter(fileStream))
{
streamWriter.WriteLine($"/r:\"{outputFilePath}\"");
foreach (var projectDependency in runtimeDependencies)
{
streamWriter.WriteLine($"/r:\"{projectDependency}\"");
}
}
}
return projectResponseFilePath;
}
private static int Run(string script, string targetFramework, string buildConfiguration, bool preserveTemporaryOutput, string projectPath, IEnumerable<string> remainingArguments)
{
var csiArgs = new List<string>();
if (buildConfiguration == null)
{
buildConfiguration = Constants.DefaultConfiguration;
}
string tempOutputDir = null;
try
{
if (!string.IsNullOrWhiteSpace(projectPath))
{
var projectContext = GetProjectContext(targetFramework, projectPath);
if (projectContext == null)
{
Reporter.Error.WriteLine($"Unrecognized framework: {targetFramework.First()}".Red());
}
var compileResult = CompileProject(projectContext, buildConfiguration, out tempOutputDir);
if (compileResult != 0)
{
Reporter.Error.WriteLine($"Project compilation failed. Exiting REPL".Red());
return compileResult;
}
string responseFile = CreateResponseFile(projectContext, buildConfiguration, tempOutputDir);
csiArgs.Add($"@{responseFile}");
}
if (string.IsNullOrEmpty(script) && !remainingArguments.Any())
{
csiArgs.Add("-i");
}
else
{
csiArgs.Add(script);
}
csiArgs.AddRange(remainingArguments);
return Command.Create("csi", csiArgs)
.ForwardStdOut()
.ForwardStdErr()
.Execute()
.ExitCode;
}
finally
{
if ((tempOutputDir != null) && !preserveTemporaryOutput)
{
Directory.Delete(tempOutputDir, recursive: true);
}
}
}
}
}