Merge pull request #662 from tannergooding/ReplSeedProjectContext

Adding support for seeding the repl with a project context.
This commit is contained in:
Piotr Puszkiewicz 2015-12-30 11:35:43 -08:00
commit 7481f10e45
6 changed files with 217 additions and 27 deletions

View file

@ -33,5 +33,7 @@ namespace Microsoft.DotNet.Cli.Utils
RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? "osx.10.10-x64" : "ubuntu.14.04-x64";
public static readonly string StaticLibSuffix = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".lib" : ".a" ;
public static readonly string ResponseFileSuffix = ".rsp";
}
}

View file

@ -7,7 +7,7 @@
"dependencies": {
"NETStandard.Library": "1.0.0-rc2-23616",
"Microsoft.Net.Compilers.netcore": "1.2.0-beta-20151224-03",
"Microsoft.Net.Compilers.netcore": "1.2.0-beta1-20151228-02",
"Microsoft.DotNet.Cli.Utils": {
"type": "build",

View file

@ -2,9 +2,14 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Text;
using Microsoft.Dnx.Runtime.Common.CommandLine;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.ProjectModel;
using NuGet.Frameworks;
namespace Microsoft.DotNet.Tools.Repl.Csi
{
@ -14,27 +19,166 @@ namespace Microsoft.DotNet.Tools.Repl.Csi
{
DebugHelper.HandleDebugSwitch(ref args);
var app = new CommandLineApplication();
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.");
app.OnExecute(() => Run(script.Value));
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 int Run(string scriptOpt)
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(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 CommandResult 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.");
// --temp-output is actually the intermediate output folder and can be the same as --output for our temporary compilation (`dotnet run` can be seen doing the same)
return Command.Create($"dotnet-compile", $"--output \"{tempOutputDir}\" --temp-output \"{tempOutputDir}\" --framework \"{projectContext.TargetFramework}\" --configuration \"{configuration}\" \"{projectContext.ProjectDirectory}\"")
.ForwardStdOut(onlyIfVerbose: true)
.ForwardStdErr()
.Execute();
}
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.RuntimeAssemblies;
foreach (var runtimeAssembly in runtimeAssemblies)
{
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}{Constants.DynamicLibSuffix}");
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 corerun = Path.Combine(AppContext.BaseDirectory, Constants.HostExecutableName);
var csiExe = Path.Combine(AppContext.BaseDirectory, "csi.exe");
var csiArgs = string.IsNullOrEmpty(scriptOpt) ? "-i" : scriptOpt;
var result = Command.Create(csiExe, csiArgs)
.ForwardStdOut()
.ForwardStdErr()
.Execute();
return result.ExitCode;
var csiArgs = new StringBuilder();
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.ExitCode != 0)
{
return compileResult.ExitCode;
}
string responseFile = CreateResponseFile(projectContext, buildConfiguration, tempOutputDir);
csiArgs.Append($"@\"{responseFile}\" ");
}
if (string.IsNullOrEmpty(script) && !remainingArguments.Any())
{
csiArgs.Append("-i");
}
else
{
csiArgs.Append(script);
}
foreach (string remainingArgument in remainingArguments)
{
csiArgs.Append($" {remainingArgument}");
}
return Command.Create(csiExe, csiArgs.ToString())
.ForwardStdOut()
.ForwardStdErr()
.Execute()
.ExitCode;
}
finally
{
if ((tempOutputDir != null) && !preserveTemporaryOutput)
{
Directory.Delete(tempOutputDir, recursive: true);
}
}
}
}
}

View file

@ -7,7 +7,7 @@
"dependencies": {
"NETStandard.Library": "1.0.0-rc2-23616",
"Microsoft.Net.CSharp.Interactive.netcore": "1.2.0-beta-20151106-02",
"Microsoft.Net.CSharp.Interactive.netcore": "1.2.0-beta1-20151228-02",
"Microsoft.DotNet.Cli.Utils": {
"type": "build",

View file

@ -2,6 +2,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using Microsoft.Dnx.Runtime.Common.CommandLine;
using Microsoft.DotNet.Cli.Utils;
@ -9,38 +10,81 @@ namespace Microsoft.DotNet.Tools.Repl
{
public sealed class Program
{
private const string DefaultReplLanguage = "csharp";
private const string AppName = "dotnet repl";
private const string AppFullName = ".NET interactive REPL";
private const string AppDescription = "Interactive REPL for the .NET platform";
private static readonly string AppHelpText = $@"{AppFullName}
Usage: {AppName} [language] [arguments]
Languages:
csharp|csi Launches the C# REPL (default)
Arguments:
[arguments] Arguments to pass to the target REPL
Options:
-h|--help Show help information
";
public static int Main(string[] args)
{
DebugHelper.HandleDebugSwitch(ref args);
var app = new CommandLineApplication();
app.Name = "dotnet repl";
app.FullName = ".NET interactive REPL";
app.Description = "Interactive REPL for the .NET platform";
app.HelpOption("-h|--help");
var language = app.Argument("<LANGUAGE>", "The interactive programming language, defaults to csharp");
var app = new CommandLineApplication(throwOnUnexpectedArg: false) {
Name = AppName,
FullName = AppFullName,
Description = AppDescription
};
app.OnExecute(() => Run(language.Value));
var language = app.Argument("[language]", "The interactive programming language, defaults to csharp");
var help = app.Option("-h|--help", "Show help information", CommandOptionType.NoValue);
app.OnExecute(() => Run(language.Value, help.HasValue(), app.RemainingArguments));
return app.Execute(args);
}
private static int Run(string languageOpt)
private static void ShowHelp()
{
Console.WriteLine(AppHelpText);
}
private static int Run(string language, bool help, List<string> remainingArguments)
{
if (language == null)
{
if (help)
{
ShowHelp();
return 0;
}
language = DefaultReplLanguage;
}
string replName;
if ((languageOpt == null) || (languageOpt == "csharp"))
if (language.Equals("csharp") || language.Equals("csi"))
{
replName = "csi";
}
else
{
Reporter.Error.WriteLine($"Unrecognized language: {languageOpt}".Red());
Reporter.Error.WriteLine($"Unrecognized language: {language}".Red());
return -1;
}
var command = Command.Create($"dotnet-repl-{replName}", string.Empty)
if (help)
{
remainingArguments.Add("--help");
}
return Command.Create($"dotnet-repl-{replName}", remainingArguments)
.ForwardStdOut()
.ForwardStdErr();
var result = command.Execute();
return result.ExitCode;
.ForwardStdErr()
.Execute()
.ExitCode;
}
}
}

View file

@ -7,7 +7,7 @@
"dependencies": {
"NETStandard.Library": "1.0.0-rc2-23616",
"Microsoft.Net.Compilers.netcore": "1.2.0-beta-20151224-03",
"Microsoft.Net.Compilers.netcore": "1.2.0-beta1-20151228-02",
"System.CommandLine" : "0.1.0-d111815-3",
"Microsoft.DotNet.ProjectModel": "1.0.0-*",