Skip to content

Commit

Permalink
Implemented debugging support for roslyn script sessions
Browse files Browse the repository at this point in the history
  • Loading branch information
bjorkstromm committed May 12, 2016
1 parent d3346d2 commit f75166c
Show file tree
Hide file tree
Showing 28 changed files with 689 additions and 39 deletions.
2 changes: 2 additions & 0 deletions src/Cake.Tests/Cake.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
<Compile Include="Extensions\StreamReaderExtensions.cs" />
<Compile Include="Extensions\StringExtensions.cs" />
<Compile Include="Fixtures\ArgumentParserFixture.cs" />
<Compile Include="Fixtures\DebugCommandFixture.cs" />
<Compile Include="Fixtures\CakeApplicationFixture.cs" />
<Compile Include="Fixtures\MonoScriptProcessorFixture.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
Expand All @@ -79,6 +80,7 @@
<Compile Include="Unit\CakeReportPrinterTests.cs" />
<Compile Include="Unit\Diagnostics\CakeBuildLogTests.cs" />
<Compile Include="Unit\Diagnostics\FormatParserTests.cs" />
<Compile Include="Unit\Commands\DebugCommandTests.cs" />
<Compile Include="Unit\Scripting\BuildScriptHostTests.cs">
<SubType>Code</SubType>
</Compile>
Expand Down
47 changes: 47 additions & 0 deletions src/Cake.Tests/Fixtures/DebugCommandFixture.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using Cake.Core;
using Cake.Core.Diagnostics;
using Cake.Core.Scripting;
using Cake.Commands;
using Cake.Diagnostics;
using Cake.Scripting;
using NSubstitute;

namespace Cake.Tests.Fixtures
{
internal sealed class DebugCommandFixture
{
public IScriptRunner ScriptRunner { get; set; }
public ICakeLog Log { get; set; }
public IDebugger Debugger { get; set; }
public CakeOptions Options { get; set; }

public DebugCommandFixture()
{
ScriptRunner = Substitute.For<IScriptRunner>();
Log = Substitute.For<ICakeLog>();
Debugger = Substitute.For<IDebugger>();
Options = new CakeOptions
{
Script = "build.cake"
};
}

public DebugCommand CreateCommand()
{
var context = Substitute.For<ICakeContext>();
var engine = Substitute.For<ICakeEngine>();
var printer = Substitute.For<ICakeReportPrinter>();

context.Log.Returns(Log);

var host = new BuildScriptHost(engine, context, printer, Log);

return new DebugCommand(ScriptRunner, Debugger, host);
}

public bool Execute()
{
return CreateCommand().Execute(Options);
}
}
}
32 changes: 32 additions & 0 deletions src/Cake.Tests/Unit/Arguments/ArgumentParserTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,22 @@ public void Can_Parse_Version(string input)
// Then
Assert.Equal(true, result.ShowVersion);
}

[Theory]
[InlineData("-debug")]
[InlineData("-d")]
public void Can_Parse_Debug(string input)
{
// Given
var fixture = new ArgumentParserFixture();
var parser = new ArgumentParser(fixture.Log, fixture.VerbosityParser);

// When
var result = parser.Parse(new[] { "build.csx", input });

// Then
Assert.Equal(true, result.PerformDebug);
}
}

public sealed class WithTwoDashLongArguments
Expand Down Expand Up @@ -467,6 +483,22 @@ public void Can_Parse_Version(string input)
// Then
Assert.Equal(true, result.ShowVersion);
}

[Theory]
[InlineData("--debug")]
[InlineData("-d")]
public void Can_Parse_Debug(string input)
{
// Given
var fixture = new ArgumentParserFixture();
var parser = new ArgumentParser(fixture.Log, fixture.VerbosityParser);

// When
var result = parser.Parse(new[] { "build.csx", input });

// Then
Assert.Equal(true, result.PerformDebug);
}
}
}
}
Expand Down
61 changes: 61 additions & 0 deletions src/Cake.Tests/Unit/Commands/DebugCommandTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using Cake.Core.Diagnostics;
using Cake.Scripting;
using Cake.Tests.Fixtures;
using NSubstitute;
using Xunit;

namespace Cake.Tests.Unit.Scripting
{
public sealed class DebugCommandTests
{
public sealed class TheExecuteMethod
{
[Fact]
public void Should_Throw_If_Options_Is_Null()
{
// Given
var fixture = new DebugCommandFixture();
fixture.Options = null;

// When
var result = Record.Exception(() => fixture.Execute());

// Then
Assert.IsArgumentNullException(result, "options");
}

[Fact]
public void Should_Proxy_Call_To_ScriptRunner()
{
// Given
var fixture = new DebugCommandFixture();
var runner = fixture.ScriptRunner;
var options = fixture.Options;

// When
fixture.Execute();

// Then
runner.Received(1).Run(Arg.Any<BuildScriptHost>(), options.Script, options.Arguments);
}

[Fact]
public void Should_Report_Correct_Process_Id()
{
// Given
var fixture = new DebugCommandFixture();
var debugger = fixture.Debugger;
var log = fixture.Log;
var pid = 1234567;

debugger.GetProcessId().Returns(pid);

// When
fixture.Execute();

// Then
log.Received(1).Information(Verbosity.Quiet, Arg.Any<string>(), pid);
}
}
}
}
6 changes: 6 additions & 0 deletions src/Cake/Arguments/ArgumentParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,12 @@ private bool ParseOption(string name, string value, CakeOptions options)
options.ShowVersion = true;
}

if (name.Equals("debug", StringComparison.OrdinalIgnoreCase) ||
name.Equals("d", StringComparison.OrdinalIgnoreCase))
{
options.PerformDebug = true;
}

if (options.Arguments.ContainsKey(name))
{
_log.Error("Multiple arguments with the same name ({0}).", name);
Expand Down
2 changes: 2 additions & 0 deletions src/Cake/Autofac/CakeModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ protected override void Load(ContainerBuilder builder)
builder.RegisterType<ScriptRunner>().As<IScriptRunner>().SingleInstance();
builder.RegisterType<CakeBuildLog>().As<ICakeLog>().As<IVerbosityAwareLog>().SingleInstance();
builder.RegisterType<VerbosityParser>().SingleInstance();
builder.RegisterType<CakeDebugger>().As<IDebugger>().SingleInstance();

// Register script hosts.
builder.RegisterType<BuildScriptHost>().SingleInstance();
Expand All @@ -64,6 +65,7 @@ protected override void Load(ContainerBuilder builder)

// Register commands.
builder.RegisterType<BuildCommand>().AsSelf().InstancePerDependency();
builder.RegisterType<DebugCommand>().AsSelf().InstancePerDependency();
builder.RegisterType<DescriptionCommand>().AsSelf().InstancePerDependency();
builder.RegisterType<DryRunCommand>().AsSelf().InstancePerDependency();
builder.RegisterType<HelpCommand>().AsSelf().InstancePerDependency();
Expand Down
15 changes: 13 additions & 2 deletions src/Cake/Autofac/ScriptingModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,19 @@ protected override void Load(ContainerBuilder builder)
{
// Roslyn
builder.RegisterType<RoslynScriptEngine>().As<IScriptEngine>().SingleInstance();
builder.RegisterType<RoslynScriptSessionFactory>().SingleInstance();
builder.RegisterType<RoslynNightlyScriptSessionFactory>().SingleInstance();

if (_options.Arguments.ContainsKey("debug"))
{
// Debug
builder.RegisterType<DebugRoslynScriptSessionFactory>().As<RoslynScriptSessionFactory>().SingleInstance();
builder.RegisterType<DebugRoslynNightlyScriptSessionFactory>().As<RoslynNightlyScriptSessionFactory>().SingleInstance();
}
else
{
// Default
builder.RegisterType<DefaultRoslynScriptSessionFactory>().As<RoslynScriptSessionFactory>().SingleInstance();
builder.RegisterType<DefaultRoslynNightlyScriptSessionFactory>().As<RoslynNightlyScriptSessionFactory>().SingleInstance();
}
}
}
}
Expand Down
11 changes: 11 additions & 0 deletions src/Cake/Cake.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@
<Compile Include="CakeReportPrinter.cs" />
<Compile Include="Commands\BuildCommand.cs" />
<Compile Include="Commands\CommandFactory.cs" />
<Compile Include="Commands\DebugCommand.cs" />
<Compile Include="Commands\DescriptionCommand.cs" />
<Compile Include="Commands\DryRunCommand.cs" />
<Compile Include="Commands\ErrorCommandDecorator.cs" />
Expand All @@ -146,10 +147,12 @@
<Compile Include="Commands\VersionCommand.cs" />
<Compile Include="Diagnostics\CakeBuildLog.cs" />
<Compile Include="Diagnostics\ConsolePalette.cs" />
<Compile Include="Diagnostics\CakeDebugger.cs" />
<Compile Include="Diagnostics\Formatting\FormatParser.cs" />
<Compile Include="Diagnostics\Formatting\FormatToken.cs" />
<Compile Include="Diagnostics\Formatting\LiteralToken.cs" />
<Compile Include="Diagnostics\Formatting\PropertyToken.cs" />
<Compile Include="Diagnostics\IDebugger.cs" />
<Compile Include="Diagnostics\IVerbosityAware.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
Expand All @@ -168,12 +171,20 @@
<Compile Include="Scripting\Mono\CodeGen\Parsing\ScriptBlock.cs" />
<Compile Include="Scripting\Mono\CodeGen\Parsing\ScriptParser.cs" />
<Compile Include="Scripting\Mono\CodeGen\MonoScriptProcessor.cs" />
<Compile Include="Scripting\Roslyn\Nightly\DefaultRoslynNightlyScriptSession.cs" />
<Compile Include="Scripting\Roslyn\Nightly\DebugRoslynNightlyScriptSession.cs" />
<Compile Include="Scripting\Roslyn\Nightly\RoslynNightlyScriptSession.cs" />
<Compile Include="Scripting\Roslyn\Nightly\DebugRoslynNightlyScriptSessionFactory.cs" />
<Compile Include="Scripting\Roslyn\Nightly\DefaultRoslynNightlyScriptSessionFactory.cs" />
<Compile Include="Scripting\Roslyn\Nightly\RoslynNightlyScriptSessionFactory.cs" />
<Compile Include="Scripting\Roslyn\RoslynCodeGenerator.cs" />
<Compile Include="Scripting\Roslyn\Stable\RoslynScriptSession.cs" />
<Compile Include="Scripting\Roslyn\Stable\DebugRoslynScriptSession.cs" />
<Compile Include="Scripting\Roslyn\Stable\DefaultRoslynScriptSession.cs" />
<Compile Include="Scripting\Roslyn\RoslynScriptEngine.cs" />
<Compile Include="Scripting\Roslyn\Stable\RoslynScriptSessionFactory.cs" />
<Compile Include="Scripting\Roslyn\Stable\DebugRoslynScriptSessionFactory.cs" />
<Compile Include="Scripting\Roslyn\Stable\DefaultRoslynScriptSessionFactory.cs" />
<Compile Include="Scripting\Mono\MonoScriptSession.cs" />
<Compile Include="Scripting\Mono\MonoScriptEngine.cs" />
<Compile Include="Scripting\Mono\CodeGen\MonoCodeGenerator.cs" />
Expand Down
4 changes: 4 additions & 0 deletions src/Cake/CakeApplication.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ private ICommand CreateCommand(CakeOptions options)
{
return _commandFactory.CreateDescriptionCommand();
}
if (options.PerformDebug)
{
return _commandFactory.CreateDebugCommand();
}

return _commandFactory.CreateBuildCommand();
}
Expand Down
8 changes: 8 additions & 0 deletions src/Cake/CakeOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ public IDictionary<string, string> Arguments
/// </value>
public bool PerformDryRun { get; set; }

/// <summary>
/// Gets or sets a value indicating whether to debug script.
/// </summary>
/// <value>
/// <c>true</c> if a debug session should be started; otherwise, <c>false</c>.
/// </value>
public bool PerformDebug { get; set; }

/// <summary>
/// Gets or sets a value indicating whether to show help.
/// </summary>
Expand Down
8 changes: 8 additions & 0 deletions src/Cake/Commands/CommandFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,22 @@
internal sealed class CommandFactory : ICommandFactory
{
private readonly BuildCommand.Factory _buildCommandFactory;
private readonly DebugCommand.Factory _debugCommandFactory;
private readonly DescriptionCommand.Factory _descriptionCommandFactory;
private readonly DryRunCommand.Factory _dryRunCommandFactory;
private readonly HelpCommand.Factory _helpCommandFactory;
private readonly VersionCommand.Factory _versionCommandFactory;

public CommandFactory(
BuildCommand.Factory buildCommandFactory,
DebugCommand.Factory debugCommandFactory,
DescriptionCommand.Factory descriptionCommandFactory,
DryRunCommand.Factory dryRunCommandFactory,
HelpCommand.Factory helpCommandFactory,
VersionCommand.Factory versionCommandFactory)
{
_buildCommandFactory = buildCommandFactory;
_debugCommandFactory = debugCommandFactory;
_descriptionCommandFactory = descriptionCommandFactory;
_dryRunCommandFactory = dryRunCommandFactory;
_helpCommandFactory = helpCommandFactory;
Expand All @@ -27,6 +30,11 @@ public ICommand CreateBuildCommand()
return _buildCommandFactory();
}

public ICommand CreateDebugCommand()
{
return _debugCommandFactory();
}

public ICommand CreateDescriptionCommand()
{
return _descriptionCommandFactory();
Expand Down
52 changes: 52 additions & 0 deletions src/Cake/Commands/DebugCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using System;
using System.Threading;
using Cake.Core.Diagnostics;
using Cake.Core.Scripting;
using Cake.Diagnostics;
using Cake.Scripting;

namespace Cake.Commands
{
/// <summary>
/// A command that builds and debugs a build script.
/// </summary>
internal sealed class DebugCommand : ICommand
{
private readonly IScriptRunner _scriptRunner;
private readonly IDebugger _debugger;
private readonly ICakeLog _log;
private readonly BuildScriptHost _host;

// Delegate factory used by Autofac.
public delegate DebugCommand Factory();

public DebugCommand(IScriptRunner scriptRunner, IDebugger debugger, BuildScriptHost host)
{
_scriptRunner = scriptRunner;
_debugger = debugger;
_host = host;
_log = _host.Context.Log;
}

public bool Execute(CakeOptions options)
{
if (options == null)
{
throw new ArgumentNullException("options");
}

var message = "Attach debugger to process {0} to continue";
var pid = _debugger.GetProcessId();

_log.Debug("Performing debug...");
_log.Information(Verbosity.Quiet, message, pid);

_debugger.WaitForAttach(Timeout.InfiniteTimeSpan);

_log.Debug("Debugger attached");

_scriptRunner.Run(_host, options.Script, options.Arguments);
return true;
}
}
}
1 change: 1 addition & 0 deletions src/Cake/Commands/HelpCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public bool Execute(CakeOptions options)
_console.WriteLine(" -verbosity=value Specifies the amount of information to be displayed.");
_console.WriteLine(" ({0})",
string.Join(", ", Enum.GetNames(typeof(Verbosity))));
_console.WriteLine(" -debug Performs a debug.");
_console.WriteLine(" -showdescription Shows description about tasks.");
_console.WriteLine(" -dryrun Performs a dry run.");
_console.WriteLine(" -version Displays version information.");
Expand Down
Loading

0 comments on commit f75166c

Please sign in to comment.