diff --git a/src/Cake.Cli/Cake.Cli.csproj b/src/Cake.Cli/Cake.Cli.csproj new file mode 100644 index 0000000000..7ab9b4723e --- /dev/null +++ b/src/Cake.Cli/Cake.Cli.csproj @@ -0,0 +1,25 @@ + + + Cake.Cli + netstandard2.0;net5.0 + Library + AnyCpu + true + + + + + The Cake CLI library. + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Cake.Cli/Features/InfoFeature.cs b/src/Cake.Cli/Features/InfoFeature.cs new file mode 100644 index 0000000000..70b0abacb4 --- /dev/null +++ b/src/Cake.Cli/Features/InfoFeature.cs @@ -0,0 +1,79 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Cake.Core; + +namespace Cake.Cli +{ + /// + /// Represents a feature that writes information about Cake to the console. + /// + public interface ICakeInfoFeature + { + /// + /// Runs the feature. + /// + /// The console to write to. + void Run(IConsole console); + } + + /// + /// Writes information about Cake to the console. + /// + public sealed class InfoFeature : ICakeInfoFeature + { + private readonly IVersionResolver _resolver; + + /// + /// Initializes a new instance of the class. + /// + /// The version resolver. + public InfoFeature(IVersionResolver resolver) + { + _resolver = resolver; + } + + /// + public void Run(IConsole console) + { + var version = _resolver.GetVersion(); + var product = _resolver.GetProductVersion(); + + console.WriteLine(); + console.WriteLine(@" +## #;;'"); + console.WriteLine(@" #;;# .+;;;;+,"); + console.WriteLine(@" '+;;#;,+';;;;;'#."); + console.WriteLine(@" ++'''';;;;;;;;;;# ;#;"); + console.WriteLine(@" ##';;;;++'+#;;;;;'. `#:"); + console.WriteLine(@" ;# '+'';;;;;;;;;'#` #."); + console.WriteLine(@" `#, .'++;;;;;':..........#"); + console.WriteLine(@" '+ `.........';;;;':.........#"); + console.WriteLine(@" #..................+;;;;;':........#"); + console.WriteLine(@" #..................#';;;;;'+''''''.#"); + console.WriteLine(@" #.......,:;''''''''##';;;;;'+'''''#,"); + console.WriteLine(@" #''''''''''''''''''###';;;;;;+''''#"); + console.WriteLine(@" #''''''''''''''''''####';;;;;;#'''#"); + console.WriteLine(@" #''''''''''''''''''#####';;;;;;#''#"); + console.WriteLine(@" #''''''''''''''''''######';;;;;;#'#"); + console.WriteLine(@" #''''''''''''''''''#######';;;;;;##"); + console.WriteLine(@" #''''''''''''''''''########';;;;;;#"); + console.WriteLine(@" #''''''''''''++####+;#######';;;;;;#"); + console.WriteLine(@" #+####':,` ,#####';;;;;;'"); + console.WriteLine(@" +##'''''+."); + + console.ForegroundColor = System.ConsoleColor.Yellow; + console.WriteLine(@" ___ _ ___ _ _ _ "); + console.WriteLine(@" / __\__ _| | _____ / __\_ _(_) | __| |"); + console.WriteLine(@" / / / _` | |/ / _ \/__\// | | | | |/ _` |"); + console.WriteLine(@"/ /___ (_| | < __/ \/ \ |_| | | | (_| |"); + console.WriteLine(@"\____/\__,_|_|\_\___\_____/\__,_|_|_|\__,_|"); + console.ResetColor(); + + console.WriteLine(); + console.WriteLine(@"Version: {0}", version); + console.WriteLine(@"Details: {0}", string.Join("\n ", product.Split('/'))); + console.WriteLine(); + } + } +} diff --git a/src/Cake.Cli/Features/VersionFeature.cs b/src/Cake.Cli/Features/VersionFeature.cs new file mode 100644 index 0000000000..fc71345b5c --- /dev/null +++ b/src/Cake.Cli/Features/VersionFeature.cs @@ -0,0 +1,49 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using Cake.Core; + +namespace Cake.Cli +{ + /// + /// Represents a feature that writes the Cake version to the console. + /// + public interface ICakeVersionFeature + { + /// + /// Writes the Cake version to the console. + /// + /// The console to write to. + void Run(IConsole console); + } + + /// + /// Writes the Cake version to the console. + /// + public sealed class VersionFeature : ICakeVersionFeature + { + private readonly IVersionResolver _resolver; + + /// + /// Initializes a new instance of the class. + /// + /// The version resolver. + public VersionFeature(IVersionResolver resolver) + { + _resolver = resolver; + } + + /// + public void Run(IConsole console) + { + if (console is null) + { + throw new ArgumentNullException(nameof(console)); + } + + console.WriteLine(_resolver.GetVersion()); + } + } +} diff --git a/src/Cake/Features/Introspection/VersionResolver.cs b/src/Cake.Cli/Features/VersionResolver.cs similarity index 62% rename from src/Cake/Features/Introspection/VersionResolver.cs rename to src/Cake.Cli/Features/VersionResolver.cs index 1a9dfad55a..cc91351749 100644 --- a/src/Cake/Features/Introspection/VersionResolver.cs +++ b/src/Cake.Cli/Features/VersionResolver.cs @@ -3,20 +3,37 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; +using System.Reflection; -namespace Cake.Features.Introspection +namespace Cake.Cli { + /// + /// Represents a version resolver. + /// public interface IVersionResolver { + /// + /// Gets the version. + /// + /// The version. string GetVersion(); + + /// + /// Gets the product version. + /// + /// The product version. string GetProductVersion(); } + /// + /// The Cake version resolver. + /// public sealed class VersionResolver : IVersionResolver { + /// public string GetVersion() { - var assembly = typeof(Program).Assembly; + var assembly = Assembly.GetEntryAssembly(); var version = FileVersionInfo.GetVersionInfo(assembly.Location).Comments; if (string.IsNullOrWhiteSpace(version)) @@ -27,9 +44,10 @@ public string GetVersion() return version; } + /// public string GetProductVersion() { - var assembly = typeof(Program).Assembly; + var assembly = Assembly.GetEntryAssembly(); var version = FileVersionInfo.GetVersionInfo(assembly.Location).ProductVersion; if (string.IsNullOrWhiteSpace(version)) diff --git a/src/Cake.Cli/Hosts/BuildScriptHost.cs b/src/Cake.Cli/Hosts/BuildScriptHost.cs new file mode 100644 index 0000000000..ba7e6c9151 --- /dev/null +++ b/src/Cake.Cli/Hosts/BuildScriptHost.cs @@ -0,0 +1,82 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Cake.Core; +using Cake.Core.Diagnostics; +using Cake.Core.Scripting; + +namespace Cake.Cli +{ + /// + /// The script host used to execute Cake scripts. + /// + public sealed class BuildScriptHost : BuildScriptHost + { + /// + /// Initializes a new instance of the class. + /// + /// The engine. + /// The execution strategy. + /// The context. + /// The report printer. + /// The log. + public BuildScriptHost( + ICakeEngine engine, + IExecutionStrategy executionStrategy, + ICakeContext context, + ICakeReportPrinter reportPrinter, + ICakeLog log) : base(engine, executionStrategy, context, reportPrinter, log) + { + } + } + + /// + /// The script host used to execute Cake scripts. + /// + /// The context type. + public class BuildScriptHost : ScriptHost + where TContext : ICakeContext + { + private readonly ICakeReportPrinter _reportPrinter; + private readonly ICakeLog _log; + private readonly IExecutionStrategy _executionStrategy; + private readonly TContext _context; + + /// + /// Initializes a new instance of the class. + /// + /// The engine. + /// The execution strategy. + /// The context. + /// The report printer. + /// The log. + public BuildScriptHost( + ICakeEngine engine, + IExecutionStrategy executionStrategy, + TContext context, + ICakeReportPrinter reportPrinter, + ICakeLog log) : base(engine, context) + { + _executionStrategy = executionStrategy; + _context = context; + _reportPrinter = reportPrinter; + _log = log; + } + + /// + public override async Task RunTargetAsync(string target) + { + Settings.SetTarget(target); + + var report = await Engine.RunTargetAsync(_context, _executionStrategy, Settings).ConfigureAwait(false); + if (report != null && !report.IsEmpty) + { + _reportPrinter.Write(report); + } + + return report; + } + } +} \ No newline at end of file diff --git a/src/Cake/Features/Building/Hosts/DescriptionScriptHost.cs b/src/Cake.Cli/Hosts/DescriptionScriptHost.cs similarity index 95% rename from src/Cake/Features/Building/Hosts/DescriptionScriptHost.cs rename to src/Cake.Cli/Hosts/DescriptionScriptHost.cs index 1f8f6fb3b1..a6845ed13e 100644 --- a/src/Cake/Features/Building/Hosts/DescriptionScriptHost.cs +++ b/src/Cake.Cli/Hosts/DescriptionScriptHost.cs @@ -8,12 +8,12 @@ using Cake.Core; using Cake.Core.Scripting; -namespace Cake.Features.Building.Hosts +namespace Cake.Cli { /// /// The script host used for showing task descriptions. /// - public sealed class DescriptionScriptHost : ScriptHost + public class DescriptionScriptHost : ScriptHost { private readonly IConsole _console; private readonly Dictionary _descriptions; diff --git a/src/Cake/Features/Building/Hosts/DryRunExecutionStrategy.cs b/src/Cake.Cli/Hosts/DryRunExecutionStrategy.cs similarity index 98% rename from src/Cake/Features/Building/Hosts/DryRunExecutionStrategy.cs rename to src/Cake.Cli/Hosts/DryRunExecutionStrategy.cs index aab1a5a0b0..e8331f2334 100644 --- a/src/Cake/Features/Building/Hosts/DryRunExecutionStrategy.cs +++ b/src/Cake.Cli/Hosts/DryRunExecutionStrategy.cs @@ -7,7 +7,7 @@ using Cake.Core; using Cake.Core.Diagnostics; -namespace Cake.Features.Building.Hosts +namespace Cake.Cli { internal sealed class DryRunExecutionStrategy : IExecutionStrategy { diff --git a/src/Cake/Features/Building/Hosts/DryRunScriptHost.cs b/src/Cake.Cli/Hosts/DryRunScriptHost.cs similarity index 67% rename from src/Cake/Features/Building/Hosts/DryRunScriptHost.cs rename to src/Cake.Cli/Hosts/DryRunScriptHost.cs index e1b618dcce..92baff08ae 100644 --- a/src/Cake/Features/Building/Hosts/DryRunScriptHost.cs +++ b/src/Cake.Cli/Hosts/DryRunScriptHost.cs @@ -8,15 +8,13 @@ using Cake.Core.Diagnostics; using Cake.Core.Scripting; -namespace Cake.Features.Building.Hosts +namespace Cake.Cli { /// /// The script host used to dry run Cake scripts. /// - public sealed class DryRunScriptHost : ScriptHost + public sealed class DryRunScriptHost : DryRunScriptHost { - private readonly ICakeLog _log; - /// /// Initializes a new instance of the class. /// @@ -24,6 +22,27 @@ public sealed class DryRunScriptHost : ScriptHost /// The context. /// The log. public DryRunScriptHost(ICakeEngine engine, ICakeContext context, ICakeLog log) + : base(engine, context, log) + { + } + } + + /// + /// The script host used to dry run Cake scripts. + /// + /// The context. + public class DryRunScriptHost : ScriptHost + where TContext : ICakeContext + { + private readonly ICakeLog _log; + + /// + /// Initializes a new instance of the class. + /// + /// The engine. + /// The context. + /// The log. + public DryRunScriptHost(ICakeEngine engine, TContext context, ICakeLog log) : base(engine, context) { _log = log ?? throw new ArgumentNullException(nameof(log)); diff --git a/src/Cake/Features/Building/Hosts/TreeScriptHost.cs b/src/Cake.Cli/Hosts/TreeScriptHost.cs similarity index 99% rename from src/Cake/Features/Building/Hosts/TreeScriptHost.cs rename to src/Cake.Cli/Hosts/TreeScriptHost.cs index 76a5b7b322..b31048c201 100644 --- a/src/Cake/Features/Building/Hosts/TreeScriptHost.cs +++ b/src/Cake.Cli/Hosts/TreeScriptHost.cs @@ -10,7 +10,7 @@ using Cake.Core.Graph; using Cake.Core.Scripting; -namespace Cake.Features.Building.Hosts +namespace Cake.Cli { /// /// The script host used for showing task descriptions. diff --git a/src/Cake/Infrastructure/Converters/FilePathConverter.cs b/src/Cake.Cli/Infrastructure/FilePathConverter.cs similarity index 51% rename from src/Cake/Infrastructure/Converters/FilePathConverter.cs rename to src/Cake.Cli/Infrastructure/FilePathConverter.cs index c5697d1415..f8932244f2 100644 --- a/src/Cake/Infrastructure/Converters/FilePathConverter.cs +++ b/src/Cake.Cli/Infrastructure/FilePathConverter.cs @@ -7,10 +7,14 @@ using System.Globalization; using Cake.Core.IO; -namespace Cake.Infrastructure.Converters +namespace Cake.Cli { + /// + /// A type converter for . + /// public sealed class FilePathConverter : TypeConverter { + /// public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { if (value is string stringValue) @@ -21,4 +25,21 @@ public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo c throw new NotSupportedException("Can't convert value to file path."); } } + + /// + /// A type converter for . + /// + public sealed class DirectoryPathConverter : TypeConverter + { + /// + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + { + if (value is string stringValue) + { + return new DirectoryPath(stringValue); + } + + throw new NotSupportedException("Can't convert value to file path."); + } + } } diff --git a/src/Cake/Infrastructure/Converters/VerbosityConverter.cs b/src/Cake.Cli/Infrastructure/VerbosityConverter.cs similarity index 87% rename from src/Cake/Infrastructure/Converters/VerbosityConverter.cs rename to src/Cake.Cli/Infrastructure/VerbosityConverter.cs index e229ea5648..7ee38940c6 100644 --- a/src/Cake/Infrastructure/Converters/VerbosityConverter.cs +++ b/src/Cake.Cli/Infrastructure/VerbosityConverter.cs @@ -9,12 +9,18 @@ using Cake.Core; using Cake.Core.Diagnostics; -namespace Cake.Infrastructure.Converters +namespace Cake.Cli { + /// + /// A type converter for . + /// public sealed class VerbosityConverter : TypeConverter { private readonly Dictionary _lookup; + /// + /// Initializes a new instance of the class. + /// public VerbosityConverter() { _lookup = new Dictionary(StringComparer.OrdinalIgnoreCase) @@ -32,6 +38,7 @@ public VerbosityConverter() }; } + /// public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { if (value is string stringValue) diff --git a/src/Cake.Core/Diagnostics/CakeDebugger.cs b/src/Cake.Core/Diagnostics/CakeDebugger.cs index 8e9ed49987..263a1b0660 100644 --- a/src/Cake.Core/Diagnostics/CakeDebugger.cs +++ b/src/Cake.Core/Diagnostics/CakeDebugger.cs @@ -5,15 +5,26 @@ namespace Cake.Core.Diagnostics { - internal sealed class CakeDebugger : ICakeDebugger + /// + /// The Cake debugger. + /// + public sealed class CakeDebugger : ICakeDebugger { private readonly ICakeLog _log; + /// + /// Initializes a new instance of the class. + /// + /// The log. public CakeDebugger(ICakeLog log) { _log = log; } + /// + /// Waits for a debugger to attach. + /// + /// The timeout. public void WaitForAttach(TimeSpan timeout) { var processId = Process.GetCurrentProcess().Id; diff --git a/src/Cake.Core/Graph/CakeGraph.cs b/src/Cake.Core/Graph/CakeGraph.cs index 7e29e2e973..fcfbad6b30 100644 --- a/src/Cake.Core/Graph/CakeGraph.cs +++ b/src/Cake.Core/Graph/CakeGraph.cs @@ -11,7 +11,7 @@ namespace Cake.Core.Graph /// /// Represents the Cake task graph. /// - internal sealed class CakeGraph + public sealed class CakeGraph { private readonly List _nodes; private readonly List _edges; diff --git a/src/Cake.Core/Graph/CakeGraphBuilder.cs b/src/Cake.Core/Graph/CakeGraphBuilder.cs index f91f7d9c09..0aa695fea3 100644 --- a/src/Cake.Core/Graph/CakeGraphBuilder.cs +++ b/src/Cake.Core/Graph/CakeGraphBuilder.cs @@ -10,7 +10,7 @@ namespace Cake.Core.Graph /// /// Responsible for building the Cake task graph. /// - internal static class CakeGraphBuilder + public static class CakeGraphBuilder { /// /// Builds a from the specified tasks. diff --git a/src/Cake.Core/Graph/CakeGraphEdge.cs b/src/Cake.Core/Graph/CakeGraphEdge.cs index f464f2a473..b54169a7c6 100644 --- a/src/Cake.Core/Graph/CakeGraphEdge.cs +++ b/src/Cake.Core/Graph/CakeGraphEdge.cs @@ -7,7 +7,7 @@ namespace Cake.Core.Graph /// /// Represents an edge in a . /// - internal sealed class CakeGraphEdge + public sealed class CakeGraphEdge { /// /// Gets the start node of the edge. diff --git a/src/Cake.Core/Modules/CoreModule.cs b/src/Cake.Core/Modules/CoreModule.cs index 0f612804fb..693bd4d5f1 100644 --- a/src/Cake.Core/Modules/CoreModule.cs +++ b/src/Cake.Core/Modules/CoreModule.cs @@ -33,6 +33,7 @@ public void Register(ICakeContainerRegistrar registrar) registrar.RegisterType().As().Singleton(); registrar.RegisterType().As().Singleton(); registrar.RegisterType().As().As().Singleton(); + registrar.RegisterType().As().Singleton(); // Environment registrar.RegisterType().As().Singleton(); diff --git a/src/Cake.Core/Scripting/IScriptHost.cs b/src/Cake.Core/Scripting/IScriptHost.cs index 8f650cb55a..c61660b21f 100644 --- a/src/Cake.Core/Scripting/IScriptHost.cs +++ b/src/Cake.Core/Scripting/IScriptHost.cs @@ -25,6 +25,11 @@ public interface IScriptHost /// The registered tasks. IReadOnlyList Tasks { get; } + /// + /// Gets the execution settings. + /// + ExecutionSettings Settings { get; } + /// /// Registers a new task. /// diff --git a/src/Cake.Core/Scripting/ScriptHost.cs b/src/Cake.Core/Scripting/ScriptHost.cs index d8161541f9..c8c9544474 100644 --- a/src/Cake.Core/Scripting/ScriptHost.cs +++ b/src/Cake.Core/Scripting/ScriptHost.cs @@ -36,16 +36,8 @@ public abstract class ScriptHost : IScriptHost /// The context. protected ScriptHost(ICakeEngine engine, ICakeContext context) { - if (engine == null) - { - throw new ArgumentNullException(nameof(engine)); - } - if (context == null) - { - throw new ArgumentNullException(nameof(context)); - } - Engine = engine; - Context = context; + Engine = engine ?? throw new ArgumentNullException(nameof(engine)); + Context = context ?? throw new ArgumentNullException(nameof(context)); Settings = new ExecutionSettings(); } diff --git a/src/Cake.Frosting.Example/Cake.Frosting.Example.csproj b/src/Cake.Frosting.Example/Cake.Frosting.Example.csproj index 7d8884ead1..c73cadaa6a 100644 --- a/src/Cake.Frosting.Example/Cake.Frosting.Example.csproj +++ b/src/Cake.Frosting.Example/Cake.Frosting.Example.csproj @@ -5,6 +5,7 @@ Exe 7 true + $(MSBuildProjectDirectory).. diff --git a/src/Cake.Frosting.Example/Program.cs b/src/Cake.Frosting.Example/Program.cs index 4bb5c1a948..0331c75490 100644 --- a/src/Cake.Frosting.Example/Program.cs +++ b/src/Cake.Frosting.Example/Program.cs @@ -2,28 +2,60 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace Cake.Frosting.Example +using System.Threading.Tasks; +using Cake.Core; +using Cake.Core.Diagnostics; +using Cake.Frosting; + +public static class Program { - public class Program + public static int Main(string[] args) { - public static int Main(string[] args) - { - // Create the host. - var host = new CakeHostBuilder() - .UseStartup() - .WithArguments(args) - .Build(); - - // Run the host. - return host.Run(); - } + return new CakeHost() + .UseContext() + .Run(args); + } +} + +public class BuildContext : FrostingContext +{ + public bool Delay { get; set; } + + public BuildContext(ICakeContext context) + : base(context) + { + Delay = context.Arguments.HasArgument("delay"); } +} - public class Startup : IFrostingStartup +[TaskName("Hello")] +public sealed class HelloTask : FrostingTask +{ + public override void Run(BuildContext context) + { + context.Log.Information("Hello"); + } +} + +[TaskName("World")] +[IsDependentOn(typeof(HelloTask))] +public sealed class WorldTask : AsyncFrostingTask +{ + // Tasks can be asynchronous + public override async Task RunAsync(BuildContext context) { - public void Configure(ICakeServices services) + if (context.Delay) { - services.UseContext(); + context.Log.Information("Waiting..."); + await Task.Delay(1500); } + + context.Log.Information("World"); } +} + +[TaskName("Default")] +[IsDependentOn(typeof(WorldTask))] +public class DefaultTask : FrostingTask +{ } \ No newline at end of file diff --git a/src/Cake.Frosting.Example/Settings.cs b/src/Cake.Frosting.Example/Settings.cs deleted file mode 100644 index d000f2f4bb..0000000000 --- a/src/Cake.Frosting.Example/Settings.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Cake.Core; - -namespace Cake.Frosting.Example -{ - public class Settings : FrostingContext - { - public bool Magic { get; set; } - - public Settings(ICakeContext context) - : base(context) - { - // You could also use a CakeLifeTime - // to provide a Setup method to setup the context. - Magic = context.Arguments.HasArgument("magic"); - } - } -} \ No newline at end of file diff --git a/src/Cake.Frosting.Example/Tasks.cs b/src/Cake.Frosting.Example/Tasks.cs deleted file mode 100644 index 8e3f75a94c..0000000000 --- a/src/Cake.Frosting.Example/Tasks.cs +++ /dev/null @@ -1,47 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Threading.Tasks; -using Cake.Core; -using Cake.Core.Diagnostics; - -namespace Cake.Frosting.Example -{ - public sealed class Hello : FrostingTask - { - } - - [Dependency(typeof(Hello))] - public sealed class World : AsyncFrostingTask - { - // Tasks can be asynchronous - public override async Task RunAsync(Settings context) - { - context.Log.Information("About to do something expensive"); - await Task.Delay(1500); - context.Log.Information("Done"); - } - } - - [Dependency(typeof(World))] - public sealed class Magic : FrostingTask - { - public override bool ShouldRun(Settings context) - { - // Don't run this task on OSX. - return context.Environment.Platform.Family != PlatformFamily.OSX; - } - - public override void Run(Settings context) - { - context.Log.Information("Value is: {0}", context.Magic); - } - } - - [TaskName("Default")] - [Dependency(typeof(Magic))] - public class DefaultTask : FrostingTask - { - } -} diff --git a/src/Cake.Frosting.Template/Cake.Frosting.Template.csproj b/src/Cake.Frosting.Template/Cake.Frosting.Template.csproj index 205829e861..ff838c4e3e 100644 --- a/src/Cake.Frosting.Template/Cake.Frosting.Template.csproj +++ b/src/Cake.Frosting.Template/Cake.Frosting.Template.csproj @@ -1,4 +1,4 @@ - + Template @@ -6,23 +6,25 @@ Cake.Frosting templates for the .NET SDK. Cake.Frosting templates for the .NET SDK. netcoreapp3.1 - true false content - NU5110;NU5111;NU5128 + $(NoWarn);NU5110;NU5111;NU5128 + true - - - + + + + + diff --git a/src/Cake.Frosting.Template/build.ps1 b/src/Cake.Frosting.Template/build.ps1 deleted file mode 100644 index 90d183fc2c..0000000000 --- a/src/Cake.Frosting.Template/build.ps1 +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/env pwsh -$DotNetInstallerUri = 'https://dot.net/v1/dotnet-install.ps1'; -$DotNetUnixInstallerUri = 'https://dot.net/v1/dotnet-install.sh' -$DotNetChannel = 'LTS' -$PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent - -[string] $DotNetVersion= '' -foreach($line in Get-Content (Join-Path $PSScriptRoot 'build.config')) -{ - if ($line -like 'DOTNET_VERSION=*') { - $DotNetVersion =$line.SubString(15) - } -} - - -if ([string]::IsNullOrEmpty($DotNetVersion)) { - 'Failed to parse .NET Core SDK Version' - exit 1 -} - -$DotNetInstallerUri = "https://dot.net/v1/dotnet-install.ps1"; - -# Make sure tools folder exists -$PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent -$ToolPath = Join-Path $PSScriptRoot "tools" -if (!(Test-Path $ToolPath)) { - Write-Verbose "Creating tools directory..." - New-Item -Path $ToolPath -Type directory | out-null -} - -########################################################################### -# INSTALL .NET CORE CLI -########################################################################### - -Function Remove-PathVariable([string]$VariableToRemove) -{ - $path = [Environment]::GetEnvironmentVariable("PATH", "User") - $newItems = $path.Split(';') | Where-Object { $_.ToString() -inotlike $VariableToRemove } - [Environment]::SetEnvironmentVariable("PATH", [System.String]::Join(';', $newItems), "User") - $path = [Environment]::GetEnvironmentVariable("PATH", "Process") - $newItems = $path.Split(';') | Where-Object { $_.ToString() -inotlike $VariableToRemove } - [Environment]::SetEnvironmentVariable("PATH", [System.String]::Join(';', $newItems), "Process") -} - -# Get .NET Core CLI path if installed. -$FoundDotNetCliVersion = $null; -if (Get-Command dotnet -ErrorAction SilentlyContinue) { - $FoundDotNetCliVersion = dotnet --version; -} - -if($FoundDotNetCliVersion -ne $DotNetVersion) { - $InstallPath = Join-Path $PSScriptRoot ".dotnet" - if (!(Test-Path $InstallPath)) { - mkdir -Force $InstallPath | Out-Null; - } - (New-Object System.Net.WebClient).DownloadFile($DotNetInstallerUri, "$InstallPath\dotnet-install.ps1"); - & $InstallPath\dotnet-install.ps1 -Version $DotNetVersion -InstallDir $InstallPath; - - Remove-PathVariable "$InstallPath" - $env:PATH = "$InstallPath;$env:PATH" - $env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 - $env:DOTNET_CLI_TELEMETRY_OPTOUT=1 -} - -########################################################################### -# RUN BUILD SCRIPT -########################################################################### - -dotnet run --project build/Build.csproj -- $args -exit $LASTEXITCODE; \ No newline at end of file diff --git a/src/Cake.Frosting.Template/build.sh b/src/Cake.Frosting.Template/build.sh deleted file mode 100755 index bd9974b730..0000000000 --- a/src/Cake.Frosting.Template/build.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env bash -# Define varibles -SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) -source $SCRIPT_DIR/build.config - -if [ "$DOTNET_VERSION" = "" ]; then - echo "An error occured while parsing .NET Core SDK version." - exit 1 -fi - -########################################################################### -# INSTALL .NET CORE CLI -########################################################################### - -export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 -export DOTNET_CLI_TELEMETRY_OPTOUT=1 -export DOTNET_SYSTEM_NET_HTTP_USESOCKETSHTTPHANDLER=0 -export DOTNET_ROLL_FORWARD_ON_NO_CANDIDATE_FX=2 - -DOTNET_INSTALLED_VERSION=$(dotnet --version 2>&1) - -if [ "$DOTNET_VERSION" != "$DOTNET_INSTALLED_VERSION" ]; then - echo "Installing .NET CLI..." - if [ ! -d "$SCRIPT_DIR/.dotnet" ]; then - mkdir "$SCRIPT_DIR/.dotnet" - fi - curl -Lsfo "$SCRIPT_DIR/.dotnet/dotnet-install.sh" https://dot.net/v1/dotnet-install.sh - bash "$SCRIPT_DIR/.dotnet/dotnet-install.sh" --version $DOTNET_VERSION --install-dir .dotnet --no-path - export PATH="$SCRIPT_DIR/.dotnet":$PATH - export DOTNET_ROOT="$SCRIPT_DIR/.dotnet" -fi - -########################################################################### -# RUN BUILD SCRIPT -########################################################################### - -echo "Running build script.." -dotnet run --project ./build/Build.csproj -- "$@" diff --git a/src/Cake.Frosting.Template/templates/cakefrosting/build.ps1 b/src/Cake.Frosting.Template/templates/cakefrosting/build.ps1 new file mode 100644 index 0000000000..a7077190ae --- /dev/null +++ b/src/Cake.Frosting.Template/templates/cakefrosting/build.ps1 @@ -0,0 +1,2 @@ +dotnet run --project build/Build.csproj -- $args +exit $LASTEXITCODE; \ No newline at end of file diff --git a/src/Cake.Frosting.Template/templates/cakefrosting/build.sh b/src/Cake.Frosting.Template/templates/cakefrosting/build.sh new file mode 100644 index 0000000000..dfd6b854b7 --- /dev/null +++ b/src/Cake.Frosting.Template/templates/cakefrosting/build.sh @@ -0,0 +1 @@ +dotnet run --project ./build/Build.csproj -- "$@" diff --git a/src/Cake.Frosting.Template/templates/cakefrosting/build/Build.csproj b/src/Cake.Frosting.Template/templates/cakefrosting/build/Build.csproj index bdae2f0327..f4e23a563b 100644 --- a/src/Cake.Frosting.Template/templates/cakefrosting/build/Build.csproj +++ b/src/Cake.Frosting.Template/templates/cakefrosting/build/Build.csproj @@ -2,9 +2,7 @@ Exe netcoreapp3.1 - true - - $(MSBuildProjectDirectory) + $(MSBuildProjectDirectory).. diff --git a/src/Cake.Frosting.Template/templates/cakefrosting/build/Context.cs b/src/Cake.Frosting.Template/templates/cakefrosting/build/Context.cs deleted file mode 100644 index 2e70b09a50..0000000000 --- a/src/Cake.Frosting.Template/templates/cakefrosting/build/Context.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Cake.Core; -using Cake.Frosting; - -public class Context : FrostingContext -{ - public Context(ICakeContext context) - : base(context) - { - } -} \ No newline at end of file diff --git a/src/Cake.Frosting.Template/templates/cakefrosting/build/Lifetime.cs b/src/Cake.Frosting.Template/templates/cakefrosting/build/Lifetime.cs deleted file mode 100644 index 999f1e85ad..0000000000 --- a/src/Cake.Frosting.Template/templates/cakefrosting/build/Lifetime.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Cake.Common.Diagnostics; -using Cake.Core; -using Cake.Frosting; - -public sealed class Lifetime : FrostingLifetime -{ - public override void Setup(Context context) - { - context.Information("Setting things up..."); - } - - public override void Teardown(Context context, ITeardownContext info) - { - context.Information("Tearing things down..."); - } -} \ No newline at end of file diff --git a/src/Cake.Frosting.Template/templates/cakefrosting/build/Program.cs b/src/Cake.Frosting.Template/templates/cakefrosting/build/Program.cs index a5c08a1052..76a13189a0 100644 --- a/src/Cake.Frosting.Template/templates/cakefrosting/build/Program.cs +++ b/src/Cake.Frosting.Template/templates/cakefrosting/build/Program.cs @@ -1,24 +1,55 @@ using Cake.Core; using Cake.Frosting; -public class Program : IFrostingStartup +public static class Program { public static int Main(string[] args) { - // Create the host. - var host = new CakeHostBuilder() - .WithArguments(args) - .UseStartup() - .Build(); + return new CakeHost() + .UseContext() + .Run(args); + } +} + +public class Settings : FrostingContext +{ + public bool Delay { get; set; } + + public Settings(ICakeContext context) + : base(context) + { + Delay = context.Arguments.HasArgument("delay"); + } +} - // Run the host. - return host.Run(); +[TaskName("Hello")] +public sealed class HelloTask : FrostingTask +{ + public override void Run(Settings context) + { + context.Log.Information("Hello"); } +} - public void Configure(ICakeServices services) +[TaskName("World")] +[IsDependentOn(typeof(HelloTask))] +public sealed class WorldTask : AsyncFrostingTask +{ + // Tasks can be asynchronous + public override async Task RunAsync(Settings context) { - services.UseContext(); - services.UseLifetime(); - services.UseWorkingDirectory(".."); + if (context.Delay) + { + context.Log.Information("Waiting..."); + await Task.Delay(1500); + } + + context.Log.Information("World"); } +} + +[TaskName("Default")] +[IsDependentOn(typeof(WorldTask))] +public class DefaultTask : FrostingTask +{ } \ No newline at end of file diff --git a/src/Cake.Frosting.Template/templates/cakefrosting/build/Tasks/Default.cs b/src/Cake.Frosting.Template/templates/cakefrosting/build/Tasks/Default.cs deleted file mode 100644 index 7d2ab0b182..0000000000 --- a/src/Cake.Frosting.Template/templates/cakefrosting/build/Tasks/Default.cs +++ /dev/null @@ -1,6 +0,0 @@ -using Cake.Frosting; - -[Dependency(typeof(Hello))] -public sealed class Default : FrostingTask -{ -} \ No newline at end of file diff --git a/src/Cake.Frosting.Template/templates/cakefrosting/build/Tasks/Hello.cs b/src/Cake.Frosting.Template/templates/cakefrosting/build/Tasks/Hello.cs deleted file mode 100644 index e01777b78e..0000000000 --- a/src/Cake.Frosting.Template/templates/cakefrosting/build/Tasks/Hello.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Cake.Common.Diagnostics; -using Cake.Frosting; - -[TaskName("Hello")] -public sealed class Hello : FrostingTask -{ - public override void Run(Context context) - { - context.Information("Hello World"); - } -} \ No newline at end of file diff --git a/src/Cake.Frosting.Tests/Asserts/ExecptionAsserts.cs b/src/Cake.Frosting.Tests/Asserts/ExecptionAsserts.cs deleted file mode 100644 index 56adc8c596..0000000000 --- a/src/Cake.Frosting.Tests/Asserts/ExecptionAsserts.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; - -// ReSharper disable once CheckNamespace -namespace Xunit -{ - public class AssertEx - { - public static void IsArgumentNullException(Exception exception, string parameterName) - { - Assert.IsType(exception); - Assert.Equal(parameterName, ((ArgumentNullException)exception).ParamName); - } - } -} diff --git a/src/Cake.Frosting.Tests/CakeHostTests.cs b/src/Cake.Frosting.Tests/CakeHostTests.cs new file mode 100644 index 0000000000..8fb0d43772 --- /dev/null +++ b/src/Cake.Frosting.Tests/CakeHostTests.cs @@ -0,0 +1,297 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using Cake.Core; +using Cake.Core.Diagnostics; +using Cake.Core.Packaging; +using Cake.Testing; +using Microsoft.Extensions.DependencyInjection; +using NSubstitute; +using Xunit; + +namespace Cake.Frosting.Tests +{ + public sealed partial class CakeHostTests + { + [Fact] + public void Should_Set_Working_Directory_From_Options_If_Set() + { + // Given + var fixture = new CakeHostFixture(); + fixture.RegisterTask(); + fixture.Host.UseWorkingDirectory("./Foo"); + fixture.FileSystem.CreateDirectory("/Working/Foo"); + + // When + fixture.Run(); + + // Then + Assert.Equal("/Working/Foo", fixture.Environment.WorkingDirectory.FullPath); + } + + [Fact] + public void Should_Prefer_Working_Directory_From_Options_Over_Configuration() + { + // Given + var fixture = new CakeHostFixture(); + fixture.RegisterTask(); + fixture.FileSystem.CreateDirectory("/Working/Foo"); + fixture.FileSystem.CreateDirectory("/Working/Bar"); + fixture.Host.UseWorkingDirectory("./Foo"); + + // When + fixture.Run("-w", "./Bar"); + + // Then + Assert.Equal("/Working/Bar", fixture.Environment.WorkingDirectory.FullPath); + } + + [Fact] + public void Should_Call_Setup_On_Registered_Setup_Lifetime() + { + // Given + var fixture = new CakeHostFixture(); + fixture.RegisterTask(); + + var lifetime = new FakeLifetime(); + fixture.Host.ConfigureServices(services => services.AddSingleton(lifetime)); + + // When + var result = fixture.Run("--target", "dummytask"); + + // Then + Assert.Equal(0, result); + Assert.Equal(1, lifetime.SetupCount); + } + + [Fact] + public void Should_Call_Teardown_On_Registered_Teardown_Lifetime() + { + // Given + var fixture = new CakeHostFixture(); + fixture.RegisterTask(); + + var lifetime = new FakeLifetime(); + fixture.Host.ConfigureServices(services => services.AddSingleton(lifetime)); + + // When + var result = fixture.Run("--target", "dummytask"); + + // Then + Assert.Equal(0, result); + Assert.Equal(1, lifetime.TeardownCount); + } + + [Fact] + public void Should_Call_Setup_On_Registered_Task_Lifetime() + { + // Given + var fixture = new CakeHostFixture(); + fixture.RegisterTask(); + + var lifetime = new FakeTaskLifetime(); + fixture.Host.ConfigureServices(services => services.AddSingleton(lifetime)); + + // When + var result = fixture.Run("--target", "dummytask"); + + // Then + Assert.Equal(0, result); + Assert.Equal(1, lifetime.SetupCount); + } + + [Fact] + public void Should_Call_Teardown_On_Registered_Task_Lifetime() + { + // Given + var fixture = new CakeHostFixture(); + fixture.RegisterTask(); + + var lifetime = new FakeTaskLifetime(); + fixture.Host.ConfigureServices(services => services.AddSingleton(lifetime)); + + // When + var result = fixture.Run("--target", "dummytask"); + + // Then + Assert.Equal(0, result); + Assert.Equal(1, lifetime.TeardownCount); + } + + [Fact] + public void Should_Execute_Tasks() + { + // Given + var fixture = new CakeHostFixture(); + fixture.Strategy = Substitute.For(); + fixture.RegisterTask(); + + // When + var result = fixture.Run("--target", "dummytask"); + + // Then + fixture.Strategy + .Received(1) + .ExecuteAsync(Arg.Is(t => t.Name == "DummyTask"), Arg.Any()); + } + + [Fact] + public void Should_Not_Abort_Build_If_Task_That_Is_ContinueOnError_Throws() + { + // Given + var fixture = new CakeHostFixture(); + fixture.RegisterTask(); + + // When + var result = fixture.Run("--target", "ContinueOnErrorTask"); + + // Then + Assert.Equal(0, result); + } + + [Fact] + public void Should_Abort_Build_If_Task_That_Is_Not_ContinueOnError_Throws() + { + // Given + var fixture = new CakeHostFixture(); + fixture.RegisterTask(); + + // When + var result = fixture.Run("--target", "ThrowingTask"); + + // Then + Assert.NotEqual(0, result); + } + + [Fact] + public void Should_Execute_Tasks_In_Correct_Order() + { + // Given + var fixture = new CakeHostFixture(); + fixture.RegisterTask(); + fixture.RegisterTask(); + fixture.RegisterTask(); + fixture.Strategy = Substitute.For(); + + // When + fixture.Run("--target", "UnitTestsTask"); + + // Then + Received.InOrder(() => + { + fixture.Strategy.ExecuteAsync(Arg.Is(t => t.Name == "CleanTask"), Arg.Any()); + fixture.Strategy.ExecuteAsync(Arg.Is(t => t.Name == "BuildTask"), Arg.Any()); + fixture.Strategy.ExecuteAsync(Arg.Is(t => t.Name == "UnitTestsTask"), Arg.Any()); + }); + } + + [Fact] + public void Should_Throw_If_Dependency_Is_Not_A_Valid_Task() + { + // Given + var fixture = new CakeHostFixture(); + fixture.RegisterTask(); + fixture.Strategy = Substitute.For(); + + // When + var result = fixture.Run("--target", "InvalidDependencyTask"); + + // Then + Assert.NotEqual(0, result); + fixture.Log.Received(1).Error("Error: {0}", "The dependency 'DateTime' is not a valid task."); + } + + [Fact] + public void Should_Return_Zero_On_Success() + { + // Given + var fixture = new CakeHostFixture(); + fixture.RegisterTask(); + + // When + var result = fixture.Run("--target", "dummytask"); + + // Then + Assert.Equal(0, result); + } + + [Fact] + public void Should_Execute_OnError_Method_If_Run_Failed() + { + // Given + var fixture = new CakeHostFixture(); + fixture.RegisterTask(); + + // When + fixture.Run("--target", "OnErrorRunFailedTask"); + + // Then + fixture.Log.Received(1).Error("OnError: {0}", "An exception"); + } + + [Fact] + public void Should_Execute_OnError_Method_If_RunAsync_Failed() + { + // Given + var fixture = new CakeHostFixture(); + fixture.RegisterTask(); + + // When + fixture.Run("--target", "OnErrorRunAsyncFailedTask"); + + // Then + fixture.Log.Received(1).Error("OnError: {0}", "An exception"); + } + + [Fact] + public void Should_Not_Execute_OnError_Method_If_Run_Completed() + { + // Given + var fixture = new CakeHostFixture(); + fixture.RegisterTask(); + + // When + fixture.Run("--target", "OnErrorRunCompletedTask"); + + // Then + fixture.Log.DidNotReceive().Error("OnError: {0}", "An exception"); + } + + [Fact] + public void Should_Execute_Finally_Method_After_All_Methods() + { + // Given + var fixture = new CakeHostFixture(); + fixture.RegisterTask(); + + // When + fixture.Run("--target", "FinallyTask"); + + // Then + Received.InOrder(() => + { + fixture.Log.Information("Run method called"); + fixture.Log.Information("OnError method called"); + fixture.Log.Information("Finally method called"); + }); + } + + [Fact] + public void Should_Install_Tools() + { + // Given + var fixture = new CakeHostFixture(); + fixture.Host.ConfigureServices(s => s.UseTool(new Uri("foo:?package=Bar"))); + fixture.RegisterTask(); + + // When + var result = fixture.Run("--target", "dummytask"); + + // Then + fixture.Installer.Received(1).Install( + Arg.Is(p => p.OriginalString == "foo:?package=Bar")); + } + } +} diff --git a/src/Cake.Frosting.Tests/Data/DummyModule.cs b/src/Cake.Frosting.Tests/Data/DummyModule.cs deleted file mode 100644 index 0a15b0b158..0000000000 --- a/src/Cake.Frosting.Tests/Data/DummyModule.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Cake.Core.Composition; - -namespace Cake.Frosting.Tests.Data -{ - public class DummyModule : ICakeModule - { - public sealed class DummyModuleSentinel - { - } - - public void Register(ICakeContainerRegistrar registrar) - { - registrar.RegisterType(); - } - } -} diff --git a/src/Cake.Frosting.Tests/Data/DummyPackageInstaller.cs b/src/Cake.Frosting.Tests/Data/DummyPackageInstaller.cs deleted file mode 100644 index 7ebc1e7722..0000000000 --- a/src/Cake.Frosting.Tests/Data/DummyPackageInstaller.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Collections.Generic; -using Cake.Core.IO; -using Cake.Core.Packaging; - -namespace Cake.Frosting.Tests.Data -{ - public class DummyPackageInstaller : IPackageInstaller - { - public bool CanInstall(PackageReference package, PackageType type) - { - return true; - } - - public IReadOnlyCollection Install(PackageReference package, PackageType type, DirectoryPath path) - { - return new List(); - } - } -} diff --git a/src/Cake.Frosting.Tests/Data/DummyStartup.cs b/src/Cake.Frosting.Tests/Data/DummyStartup.cs deleted file mode 100644 index cdda2b4a0e..0000000000 --- a/src/Cake.Frosting.Tests/Data/DummyStartup.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Cake.Core.Composition; - -namespace Cake.Frosting.Tests.Data -{ - public sealed class DummyStartup : IFrostingStartup - { - public sealed class DummyStartupSentinel - { - } - - public void Configure(ICakeServices services) - { - services.RegisterType(); - } - } -} diff --git a/src/Cake.Frosting.Tests/Data/Tasks/NoContinueOnErrorTask.cs b/src/Cake.Frosting.Tests/Data/Tasks/NoContinueOnErrorTask.cs deleted file mode 100644 index 82ac81d6e8..0000000000 --- a/src/Cake.Frosting.Tests/Data/Tasks/NoContinueOnErrorTask.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using Cake.Core; - -namespace Cake.Frosting.Tests.Data.Tasks -{ - [TaskName("No-Continue-On-Error")] - public sealed class NoContinueOnErrorTask : FrostingTask - { - public override void Run(ICakeContext context) - { - throw new InvalidOperationException(); - } - } -} diff --git a/src/Cake.Frosting.Tests/Extensions/CakeEngineExtensions.cs b/src/Cake.Frosting.Tests/Extensions/CakeEngineExtensions.cs deleted file mode 100644 index 940160b915..0000000000 --- a/src/Cake.Frosting.Tests/Extensions/CakeEngineExtensions.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Linq; -using Cake.Core; - -// ReSharper disable once CheckNamespace -namespace Cake.Frosting.Testing -{ - public static class CakeEngineExtensions - { - public static bool IsTaskRegistered(this ICakeEngine engine, string name) - { - return engine.Tasks.Any(e => e.Name.Equals(name, StringComparison.OrdinalIgnoreCase)); - } - } -} diff --git a/src/Cake.Frosting.Tests/Fakes/FakeLifetime.cs b/src/Cake.Frosting.Tests/Fakes/FakeLifetime.cs index 89ba358b98..b185ced0c9 100644 --- a/src/Cake.Frosting.Tests/Fakes/FakeLifetime.cs +++ b/src/Cake.Frosting.Tests/Fakes/FakeLifetime.cs @@ -4,25 +4,21 @@ using Cake.Core; -namespace Cake.Frosting.Tests.Fakes +namespace Cake.Frosting.Tests { public sealed class FakeLifetime : FrostingLifetime { - public bool CalledSetup { get; private set; } - public bool CalledTeardown { get; private set; } + public int SetupCount { get; set; } + public int TeardownCount { get; set; } public override void Setup(ICakeContext context) { - CalledSetup = true; + SetupCount++; } public override void Teardown(ICakeContext context, ITeardownContext info) { - CalledTeardown = true; - } - - public sealed class WithoutOverrides : FrostingLifetime - { + TeardownCount++; } } -} \ No newline at end of file +} diff --git a/src/Cake.Frosting.Tests/Fakes/FakeTaskLifetime.cs b/src/Cake.Frosting.Tests/Fakes/FakeTaskLifetime.cs index f642339e20..a1bec3b5af 100644 --- a/src/Cake.Frosting.Tests/Fakes/FakeTaskLifetime.cs +++ b/src/Cake.Frosting.Tests/Fakes/FakeTaskLifetime.cs @@ -4,25 +4,21 @@ using Cake.Core; -namespace Cake.Frosting.Tests.Fakes +namespace Cake.Frosting.Tests { public sealed class FakeTaskLifetime : FrostingTaskLifetime { - public bool CalledSetup { get; private set; } - public bool CalledTeardown { get; private set; } + public int SetupCount { get; set; } + public int TeardownCount { get; set; } public override void Setup(ICakeContext context, ITaskSetupContext info) { - CalledSetup = true; + SetupCount++; } public override void Teardown(ICakeContext context, ITaskTeardownContext info) { - CalledTeardown = true; - } - - public sealed class WithoutOverrides : FrostingTaskLifetime - { + TeardownCount++; } } -} \ No newline at end of file +} diff --git a/src/Cake.Frosting.Tests/Fakes/NullConsole.cs b/src/Cake.Frosting.Tests/Fakes/NullConsole.cs deleted file mode 100644 index bc70b429cc..0000000000 --- a/src/Cake.Frosting.Tests/Fakes/NullConsole.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using Cake.Core; - -namespace Cake.Frosting.Tests.Fakes -{ - internal sealed class NullConsole : IConsole - { - public void Write(string format, params object[] arg) - { - } - - public void WriteLine(string format, params object[] arg) - { - } - - public void WriteError(string format, params object[] arg) - { - } - - public void WriteErrorLine(string format, params object[] arg) - { - } - - public void ResetColor() - { - } - - public ConsoleColor ForegroundColor { get; set; } - public ConsoleColor BackgroundColor { get; set; } - public bool SupportAnsiEscapeCodes => false; - } -} diff --git a/src/Cake.Frosting.Tests/Fixtures/CakeHostBuilderFixture.cs b/src/Cake.Frosting.Tests/Fixtures/CakeHostBuilderFixture.cs deleted file mode 100644 index 029fdb8c30..0000000000 --- a/src/Cake.Frosting.Tests/Fixtures/CakeHostBuilderFixture.cs +++ /dev/null @@ -1,70 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Cake.Core; -using Cake.Core.Composition; -using Cake.Core.Diagnostics; -using Cake.Core.IO; -using Cake.Frosting.Tests.Fakes; -using Cake.Testing; -using NSubstitute; - -namespace Cake.Frosting.Tests.Fixtures -{ - public class CakeHostBuilderFixture - { - public CakeHostBuilder Builder { get; set; } - - public FakeFileSystem FileSystem { get; set; } - public ICakeEnvironment Environment { get; set; } - public ICakeEngine Engine { get; set; } - public ICakeLog Log { get; set; } - public ICakeDataService Data { get; set; } - public IExecutionStrategy Strategy { get; set; } - public IToolInstaller Installer { get; set; } - public CakeHostOptions Options { get; set; } - - public CakeHostBuilderFixture() - { - Builder = new CakeHostBuilder(); - Environment = FakeEnvironment.CreateUnixEnvironment(); - - FileSystem = new FakeFileSystem(Environment); - FileSystem.CreateDirectory("/Working"); - - Log = Substitute.For(); - Data = Substitute.For(); - Engine = new CakeEngine(Data, Log); - Installer = Substitute.For(); - Options = new CakeHostOptions(); - } - - public ICakeHost Build() - { - // Replace registrations with more suitable ones. - Builder.ConfigureServices(s => s.RegisterType().As()); - Builder.ConfigureServices(s => s.RegisterInstance(Environment).As()); - Builder.ConfigureServices(s => s.RegisterInstance(FileSystem).As()); - Builder.ConfigureServices(s => s.RegisterInstance(Engine).As()); - Builder.ConfigureServices(s => s.RegisterInstance(Log).As()); - Builder.ConfigureServices(s => s.RegisterInstance(Installer).As()); - Builder.ConfigureServices(s => s.RegisterInstance(Options).As()); - - if (Strategy != null) - { - Builder.ConfigureServices(services => services.RegisterInstance(Strategy).As()); - } - - Builder.ConfigureServices(s => s.RegisterInstance(Options).AsSelf().Singleton()); - - return Builder.Build(); - } - - public int Run() - { - return Build().Run(); - } - } -} diff --git a/src/Cake.Frosting.Tests/Fixtures/CakeHostBuilderFixtureExtensions.cs b/src/Cake.Frosting.Tests/Fixtures/CakeHostBuilderFixtureExtensions.cs deleted file mode 100644 index 8ba1ad164d..0000000000 --- a/src/Cake.Frosting.Tests/Fixtures/CakeHostBuilderFixtureExtensions.cs +++ /dev/null @@ -1,60 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Cake.Core; -using Cake.Core.Composition; -using Cake.Frosting.Tests.Data; -using Cake.Frosting.Tests.Fakes; -using NSubstitute; - -namespace Cake.Frosting.Tests.Fixtures -{ - internal static class CakeHostBuilderFixtureExtensions - { - public static CakeHostBuilderFixture RegisterDefaultTask(this CakeHostBuilderFixture fixture) - { - fixture.RegisterTask(); - fixture.Options.Target = typeof(DummyTask).Name; - return fixture; - } - - public static CakeHostBuilderFixture RegisterTask(this CakeHostBuilderFixture fixture) - where T : IFrostingTask - { - fixture.Builder.ConfigureServices(services => services.RegisterType().As()); - return fixture; - } - - public static FakeLifetime RegisterLifetimeSubstitute(this CakeHostBuilderFixture fixture) - { - var lifetime = new FakeLifetime(); - return fixture.RegisterLifetimeSubstitute(lifetime); - } - - public static T RegisterLifetimeSubstitute(this CakeHostBuilderFixture fixture, T lifetime) - where T : class, IFrostingLifetime - { - fixture.Builder.ConfigureServices(s => s.RegisterInstance(lifetime).As()); - return lifetime; - } - - public static FakeTaskLifetime RegisterTaskLifetimeSubstitute(this CakeHostBuilderFixture fixture) - { - var lifetime = new FakeTaskLifetime(); - return fixture.RegisterTaskLifetimeSubstitute(lifetime); - } - - public static T RegisterTaskLifetimeSubstitute(this CakeHostBuilderFixture fixture, T lifetime) - where T : class, IFrostingTaskLifetime - { - fixture.Builder.ConfigureServices(s => s.RegisterInstance(lifetime).As()); - return lifetime; - } - - public static void UseExecutionStrategySubstitute(this CakeHostBuilderFixture fixture) - { - fixture.Strategy = Substitute.For(); - } - } -} diff --git a/src/Cake.Frosting.Tests/Fixtures/CakeHostFixture.cs b/src/Cake.Frosting.Tests/Fixtures/CakeHostFixture.cs new file mode 100644 index 0000000000..1d81522b7c --- /dev/null +++ b/src/Cake.Frosting.Tests/Fixtures/CakeHostFixture.cs @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Cake.Core; +using Cake.Core.Diagnostics; +using Cake.Core.IO; +using Cake.Testing; +using Microsoft.Extensions.DependencyInjection; +using NSubstitute; + +namespace Cake.Frosting.Tests +{ + public sealed class CakeHostFixture + { + public CakeHost Host { get; set; } + public FakeEnvironment Environment { get; set; } + public FakeFileSystem FileSystem { get; set; } + public FakeConsole Console { get; set; } + public ICakeLog Log { get; set; } + public IExecutionStrategy Strategy { get; set; } + public IToolInstaller Installer { get; set; } + + public CakeHostFixture() + { + Host = new CakeHost(); + Environment = FakeEnvironment.CreateUnixEnvironment(); + Console = new FakeConsole(); + Log = Substitute.For(); + Installer = Substitute.For(); + + FileSystem = new FakeFileSystem(Environment); + FileSystem.CreateDirectory("/Working"); + + Host.ConfigureServices(services => services.AddSingleton(FileSystem)); + Host.ConfigureServices(services => services.AddSingleton(Environment)); + Host.ConfigureServices(services => services.AddSingleton(Console)); + Host.ConfigureServices(services => services.AddSingleton(Log)); + Host.ConfigureServices(services => services.AddSingleton(Installer)); + } + + public void RegisterTask() + where T : class, IFrostingTask + { + Host.ConfigureServices(services => services.AddSingleton()); + } + + public int Run(params string[] args) + { + if (Strategy != null) + { + Host.ConfigureServices(services => services.AddSingleton(Strategy)); + } + + return Host.Run(args); + } + } +} diff --git a/src/Cake.Frosting.Tests/Data/Tasks/BuildTask.cs b/src/Cake.Frosting.Tests/Tasks/BuildTask.cs similarity index 75% rename from src/Cake.Frosting.Tests/Data/Tasks/BuildTask.cs rename to src/Cake.Frosting.Tests/Tasks/BuildTask.cs index 0146d021da..3f5c9ae0ed 100644 --- a/src/Cake.Frosting.Tests/Data/Tasks/BuildTask.cs +++ b/src/Cake.Frosting.Tests/Tasks/BuildTask.cs @@ -4,10 +4,9 @@ using Cake.Core; -namespace Cake.Frosting.Tests.Data.Tasks +namespace Cake.Frosting.Tests { - [TaskName("Build")] - [Dependency(typeof(CleanTask))] + [IsDependentOn(typeof(CleanTask))] public sealed class BuildTask : FrostingTask { } diff --git a/src/Cake.Frosting.Tests/Data/Tasks/CleanTask.cs b/src/Cake.Frosting.Tests/Tasks/CleanTask.cs similarity index 82% rename from src/Cake.Frosting.Tests/Data/Tasks/CleanTask.cs rename to src/Cake.Frosting.Tests/Tasks/CleanTask.cs index 82c63e0830..57b0a35ba1 100644 --- a/src/Cake.Frosting.Tests/Data/Tasks/CleanTask.cs +++ b/src/Cake.Frosting.Tests/Tasks/CleanTask.cs @@ -4,9 +4,8 @@ using Cake.Core; -namespace Cake.Frosting.Tests.Data.Tasks +namespace Cake.Frosting.Tests { - [TaskName("Clean")] public sealed class CleanTask : FrostingTask { } diff --git a/src/Cake.Frosting.Tests/Data/Tasks/ContinueOnErrorTask.cs b/src/Cake.Frosting.Tests/Tasks/ContinueOnErrorTask.cs similarity index 67% rename from src/Cake.Frosting.Tests/Data/Tasks/ContinueOnErrorTask.cs rename to src/Cake.Frosting.Tests/Tasks/ContinueOnErrorTask.cs index 42288429e1..804fcd279c 100644 --- a/src/Cake.Frosting.Tests/Data/Tasks/ContinueOnErrorTask.cs +++ b/src/Cake.Frosting.Tests/Tasks/ContinueOnErrorTask.cs @@ -5,10 +5,9 @@ using System; using Cake.Core; -namespace Cake.Frosting.Tests.Data.Tasks +namespace Cake.Frosting.Tests { [ContinueOnError] - [TaskName("Continue-On-Error")] public sealed class ContinueOnErrorTask : FrostingTask { public override void Run(ICakeContext context) @@ -16,4 +15,12 @@ public override void Run(ICakeContext context) throw new InvalidOperationException(); } } + + public sealed class ThrowingTask : FrostingTask + { + public override void Run(ICakeContext context) + { + throw new InvalidOperationException(); + } + } } diff --git a/src/Cake.Frosting.Tests/Data/DummyTask.cs b/src/Cake.Frosting.Tests/Tasks/DummyTask.cs similarity index 74% rename from src/Cake.Frosting.Tests/Data/DummyTask.cs rename to src/Cake.Frosting.Tests/Tasks/DummyTask.cs index 450ea455fb..16ef2aa90f 100644 --- a/src/Cake.Frosting.Tests/Data/DummyTask.cs +++ b/src/Cake.Frosting.Tests/Tasks/DummyTask.cs @@ -4,9 +4,12 @@ using Cake.Core; -namespace Cake.Frosting.Tests.Data +namespace Cake.Frosting.Tests { public sealed class DummyTask : FrostingTask { + public override void Run(ICakeContext context) + { + } } } diff --git a/src/Cake.Frosting.Tests/Data/Tasks/FinallyTask.cs b/src/Cake.Frosting.Tests/Tasks/FinallyTask.cs similarity index 92% rename from src/Cake.Frosting.Tests/Data/Tasks/FinallyTask.cs rename to src/Cake.Frosting.Tests/Tasks/FinallyTask.cs index 8f992cb55a..7b9924c275 100644 --- a/src/Cake.Frosting.Tests/Data/Tasks/FinallyTask.cs +++ b/src/Cake.Frosting.Tests/Tasks/FinallyTask.cs @@ -6,9 +6,8 @@ using Cake.Core; using Cake.Core.Diagnostics; -namespace Cake.Frosting.Tests.Data.Tasks +namespace Cake.Frosting.Tests { - [TaskName("Finally")] public sealed class FinallyTask : FrostingTask { public override void Run(ICakeContext context) diff --git a/src/Cake.Frosting.Tests/Data/Tasks/InvalidDependencyTask.cs b/src/Cake.Frosting.Tests/Tasks/InvalidDependencyTask.cs similarity index 81% rename from src/Cake.Frosting.Tests/Data/Tasks/InvalidDependencyTask.cs rename to src/Cake.Frosting.Tests/Tasks/InvalidDependencyTask.cs index 9ca93bdceb..94be68e2a1 100644 --- a/src/Cake.Frosting.Tests/Data/Tasks/InvalidDependencyTask.cs +++ b/src/Cake.Frosting.Tests/Tasks/InvalidDependencyTask.cs @@ -5,9 +5,9 @@ using System; using Cake.Core; -namespace Cake.Frosting.Tests.Data.Tasks +namespace Cake.Frosting.Tests { - [Dependency(typeof(DateTime))] + [IsDependentOn(typeof(DateTime))] public sealed class InvalidDependencyTask : FrostingTask { } diff --git a/src/Cake.Frosting.Tests/Data/Tasks/OnErrorRunAsyncFailedTask.cs b/src/Cake.Frosting.Tests/Tasks/OnErrorRunAsyncFailedTask.cs similarity index 71% rename from src/Cake.Frosting.Tests/Data/Tasks/OnErrorRunAsyncFailedTask.cs rename to src/Cake.Frosting.Tests/Tasks/OnErrorRunAsyncFailedTask.cs index dbc804f0eb..dd69f69f57 100644 --- a/src/Cake.Frosting.Tests/Data/Tasks/OnErrorRunAsyncFailedTask.cs +++ b/src/Cake.Frosting.Tests/Tasks/OnErrorRunAsyncFailedTask.cs @@ -7,19 +7,18 @@ using Cake.Core; using Cake.Core.Diagnostics; -namespace Cake.Frosting.Tests.Data.Tasks +namespace Cake.Frosting.Tests { - [TaskName("On-Error-RunAsync-Failed")] public sealed class OnErrorRunAsyncFailedTask : AsyncFrostingTask { public override Task RunAsync(ICakeContext context) { - throw new InvalidOperationException("On test exception"); + throw new InvalidOperationException("An exception"); } public override void OnError(Exception exception, ICakeContext context) { - context.Log.Error("An error has occurred. {0}", exception.Message); + context.Log.Error("OnError: {0}", exception.Message); } } } \ No newline at end of file diff --git a/src/Cake.Frosting.Tests/Data/Tasks/OnErrorRunCompletedTask.cs b/src/Cake.Frosting.Tests/Tasks/OnErrorRunCompletedTask.cs similarity index 78% rename from src/Cake.Frosting.Tests/Data/Tasks/OnErrorRunCompletedTask.cs rename to src/Cake.Frosting.Tests/Tasks/OnErrorRunCompletedTask.cs index 0b4644ba37..7317219df2 100644 --- a/src/Cake.Frosting.Tests/Data/Tasks/OnErrorRunCompletedTask.cs +++ b/src/Cake.Frosting.Tests/Tasks/OnErrorRunCompletedTask.cs @@ -6,9 +6,8 @@ using Cake.Core; using Cake.Core.Diagnostics; -namespace Cake.Frosting.Tests.Data.Tasks +namespace Cake.Frosting.Tests { - [TaskName("On-Error-Run-Completed")] public sealed class OnErrorRunCompletedTask : FrostingTask { public override void Run(ICakeContext context) @@ -17,7 +16,7 @@ public override void Run(ICakeContext context) public override void OnError(Exception exception, ICakeContext context) { - context.Log.Error("OnErrorRunCompletedTask Exception"); + context.Log.Error("OnError: {0}", exception.Message); } } } \ No newline at end of file diff --git a/src/Cake.Frosting.Tests/Data/Tasks/OnErrorRunFailedTask.cs b/src/Cake.Frosting.Tests/Tasks/OnErrorRunFailedTask.cs similarity index 69% rename from src/Cake.Frosting.Tests/Data/Tasks/OnErrorRunFailedTask.cs rename to src/Cake.Frosting.Tests/Tasks/OnErrorRunFailedTask.cs index 4ed64a2492..fac264d101 100644 --- a/src/Cake.Frosting.Tests/Data/Tasks/OnErrorRunFailedTask.cs +++ b/src/Cake.Frosting.Tests/Tasks/OnErrorRunFailedTask.cs @@ -6,19 +6,18 @@ using Cake.Core; using Cake.Core.Diagnostics; -namespace Cake.Frosting.Tests.Data.Tasks +namespace Cake.Frosting.Tests { - [TaskName("On-Error-Run-Failed")] public sealed class OnErrorRunFailedTask : FrostingTask { public override void Run(ICakeContext context) { - throw new InvalidOperationException("On test exception"); + throw new InvalidOperationException("An exception"); } public override void OnError(Exception exception, ICakeContext context) { - context.Log.Error("An error has occurred. {0}", exception.Message); + context.Log.Error("OnError: {0}", exception.Message); } } } \ No newline at end of file diff --git a/src/Cake.Frosting.Tests/Data/Tasks/UnitTestsTask.cs b/src/Cake.Frosting.Tests/Tasks/UnitTestsTask.cs similarity index 73% rename from src/Cake.Frosting.Tests/Data/Tasks/UnitTestsTask.cs rename to src/Cake.Frosting.Tests/Tasks/UnitTestsTask.cs index b6e5a8c0ad..53c73cb7cf 100644 --- a/src/Cake.Frosting.Tests/Data/Tasks/UnitTestsTask.cs +++ b/src/Cake.Frosting.Tests/Tasks/UnitTestsTask.cs @@ -4,10 +4,9 @@ using Cake.Core; -namespace Cake.Frosting.Tests.Data.Tasks +namespace Cake.Frosting.Tests { - [TaskName("Run-Unit-Tests")] - [Dependency(typeof(BuildTask))] + [IsDependentOn(typeof(BuildTask))] public sealed class UnitTestsTask : FrostingTask { } diff --git a/src/Cake.Frosting.Tests/Unit/CakeHostBuilderTests.cs b/src/Cake.Frosting.Tests/Unit/CakeHostBuilderTests.cs deleted file mode 100644 index f82e30d650..0000000000 --- a/src/Cake.Frosting.Tests/Unit/CakeHostBuilderTests.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Cake.Core.Composition; -using Cake.Frosting.Tests.Fixtures; -using NSubstitute; -using Xunit; - -namespace Cake.Frosting.Tests.Unit -{ - public sealed class CakeHostBuilderTests - { - public sealed class TheConfigureServicesMethod - { - [Fact] - public void Should_Replace_Default_Registrations() - { - // Given - var fixture = new CakeHostBuilderFixture(); - var host = Substitute.For(); - fixture.Builder.ConfigureServices(services => services.RegisterInstance(host).As()); - - // When - var result = fixture.Build(); - - // Then - Assert.Same(host, result); - } - } - } -} diff --git a/src/Cake.Frosting.Tests/Unit/CakeHostTests.cs b/src/Cake.Frosting.Tests/Unit/CakeHostTests.cs deleted file mode 100644 index 1b92202cef..0000000000 --- a/src/Cake.Frosting.Tests/Unit/CakeHostTests.cs +++ /dev/null @@ -1,414 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using Cake.Core; -using Cake.Core.Diagnostics; -using Cake.Core.Packaging; -using Cake.Frosting.Testing; -using Cake.Frosting.Tests.Data.Tasks; -using Cake.Frosting.Tests.Fakes; -using Cake.Frosting.Tests.Fixtures; -using Cake.Testing; -using NSubstitute; -using Xunit; - -namespace Cake.Frosting.Tests.Unit -{ - public sealed class CakeHostTests - { - [Fact] - public void Should_Set_Log_Verbosity_From_Options() - { - // Given - var fixture = new CakeHostBuilderFixture(); - fixture.RegisterDefaultTask(); - fixture.Options.Verbosity = Verbosity.Diagnostic; - - // When - fixture.Run(); - - // Then - Assert.Equal(Verbosity.Diagnostic, fixture.Log.Verbosity); - } - - [Fact] - public void Should_Set_Working_Directory_From_Options_If_Set() - { - // Given - var fixture = new CakeHostBuilderFixture(); - fixture.RegisterDefaultTask(); - fixture.FileSystem.CreateDirectory("/Working/Foo"); - fixture.Options.WorkingDirectory = "./Foo"; - - // When - fixture.Run(); - - // Then - Assert.Equal("/Working/Foo", fixture.Environment.WorkingDirectory.FullPath); - } - - [Fact] - public void Should_Use_Working_Directory_From_Service_Configuration_If_Set() - { - // Given - var fixture = new CakeHostBuilderFixture(); - fixture.RegisterDefaultTask(); - fixture.FileSystem.CreateDirectory("/Working/Foo"); - fixture.Builder.ConfigureServices(s => s.UseWorkingDirectory("./Foo")); - - // When - fixture.Run(); - - // Then - Assert.Equal("/Working/Foo", fixture.Environment.WorkingDirectory.FullPath); - } - - [Fact] - public void Should_Prefer_Working_Directory_From_Options_Over_Configuration() - { - // Given - var fixture = new CakeHostBuilderFixture(); - fixture.RegisterDefaultTask(); - fixture.FileSystem.CreateDirectory("/Working/Foo"); - fixture.FileSystem.CreateDirectory("/Working/Bar"); - fixture.Options.WorkingDirectory = "./Bar"; - fixture.Builder.ConfigureServices(s => s.UseWorkingDirectory("./Foo")); - - // When - fixture.Run(); - - // Then - Assert.Equal("/Working/Bar", fixture.Environment.WorkingDirectory.FullPath); - } - - [Fact] - public void Should_Register_Tasks_With_Engine() - { - // Given - var fixture = new CakeHostBuilderFixture(); - fixture.RegisterDefaultTask(); - - // When - fixture.Run(); - - // Then - Assert.True(fixture.Engine.IsTaskRegistered("DummyTask")); - } - - [Fact] - public void Should_Call_Setup_On_Registered_Lifetime() - { - // Given - var fixture = new CakeHostBuilderFixture(); - var lifetime = fixture.RegisterDefaultTask() - .RegisterLifetimeSubstitute(); - - // When - fixture.Run(); - - // Then - Assert.True(lifetime.CalledSetup); - } - - [Fact] - public void Should_Not_Call_Setup_On_Registered_Lifetime_If_Not_Overridden() - { - // Given - var fixture = new CakeHostBuilderFixture(); - fixture.RegisterDefaultTask(); - fixture.RegisterLifetimeSubstitute(new FakeLifetime.WithoutOverrides()); - fixture.UseExecutionStrategySubstitute(); - - // When - fixture.Run(); - - // Then - fixture.Strategy.Received(0).PerformSetup( - Arg.Any>(), - Arg.Any()); - } - - [Fact] - public void Should_Call_Teardown_On_Registered_Lifetime() - { - // Given - var fixture = new CakeHostBuilderFixture(); - var lifetime = fixture.RegisterDefaultTask() - .RegisterLifetimeSubstitute(); - - // When - fixture.Run(); - - // Then - Assert.True(lifetime.CalledTeardown); - } - - [Fact] - public void Should_Not_Call_Teardown_On_Registered_Lifetime_If_Not_Overridden() - { - // Given - var fixture = new CakeHostBuilderFixture(); - fixture.RegisterDefaultTask(); - fixture.RegisterLifetimeSubstitute(new FakeLifetime.WithoutOverrides()); - fixture.UseExecutionStrategySubstitute(); - - // When - fixture.Run(); - - // Then - fixture.Strategy.Received(0).PerformTeardown( - Arg.Any>(), - Arg.Any()); - } - - [Fact] - public void Should_Call_Setup_On_Registered_Task_Lifetime() - { - // Given - var fixture = new CakeHostBuilderFixture(); - var lifetime = fixture.RegisterDefaultTask() - .RegisterTaskLifetimeSubstitute(); - - // When - fixture.Run(); - - // Then - Assert.True(lifetime.CalledSetup); - } - - [Fact] - public void Should_Not_Call_Setup_On_Registered_Task_Lifetime_If_Not_Overridden() - { - // Given - var fixture = new CakeHostBuilderFixture(); - fixture.RegisterDefaultTask(); - fixture.RegisterTaskLifetimeSubstitute(new FakeTaskLifetime.WithoutOverrides()); - fixture.UseExecutionStrategySubstitute(); - - // When - fixture.Run(); - - // Then - fixture.Strategy.Received(0).PerformTaskSetup( - Arg.Any>(), - Arg.Any()); - } - - [Fact] - public void Should_Call_Teardown_On_Registered_Task_Lifetime() - { - // Given - var fixture = new CakeHostBuilderFixture(); - var lifetime = fixture.RegisterDefaultTask() - .RegisterTaskLifetimeSubstitute(); - - // When - fixture.Run(); - - // Then - Assert.True(lifetime.CalledTeardown); - } - - [Fact] - public void Should_Not_Call_Teardown_On_Registered_Task_Lifetime_If_Not_Overridden() - { - // Given - var fixture = new CakeHostBuilderFixture(); - fixture.RegisterDefaultTask(); - fixture.RegisterTaskLifetimeSubstitute(new FakeTaskLifetime.WithoutOverrides()); - fixture.UseExecutionStrategySubstitute(); - - // When - fixture.Run(); - - // Then - fixture.Strategy.Received(0).PerformTaskTeardown( - Arg.Any>(), - Arg.Any()); - } - - [Fact] - public void Should_Execute_Tasks() - { - // Given - var fixture = new CakeHostBuilderFixture(); - fixture.RegisterDefaultTask(); - fixture.UseExecutionStrategySubstitute(); - - // When - fixture.Run(); - - // Then - fixture.Strategy.Received(1).ExecuteAsync(Arg.Is(t => t.Name == "DummyTask"), Arg.Any()); - } - - [Fact] - public void Should_Not_Abort_Build_If_Task_That_Is_ContinueOnError_Throws() - { - // Given - var fixture = new CakeHostBuilderFixture(); - fixture.RegisterTask(); - fixture.Options.Target = "Continue-On-Error"; - - // When - var result = fixture.Run(); - - // Then - Assert.Equal(0, result); - } - - [Fact] - public void Should_Abort_Build_If_Task_That_Is_Not_ContinueOnError_Throws() - { - // Given - var fixture = new CakeHostBuilderFixture(); - fixture.RegisterTask(); - fixture.Options.Target = "No-Continue-On-Error"; - - // When - var result = fixture.Run(); - - // Then - Assert.NotEqual(0, result); - } - - [Fact] - public void Should_Execute_Tasks_In_Correct_Order() - { - // Given - var fixture = new CakeHostBuilderFixture(); - fixture.RegisterTask(); - fixture.RegisterTask(); - fixture.RegisterTask(); - fixture.UseExecutionStrategySubstitute(); - fixture.Options.Target = "Run-Unit-Tests"; - - // When - fixture.Run(); - - // Then - Received.InOrder(() => - { - fixture.Strategy.ExecuteAsync(Arg.Is(t => t.Name == "Clean"), Arg.Any()); - fixture.Strategy.ExecuteAsync(Arg.Is(t => t.Name == "Build"), Arg.Any()); - fixture.Strategy.ExecuteAsync(Arg.Is(t => t.Name == "Run-Unit-Tests"), Arg.Any()); - }); - } - - [Fact] - public void Should_Throw_If_Dependency_Is_Not_A_Valid_Task() - { - // Given - var fixture = new CakeHostBuilderFixture(); - fixture.RegisterTask(); - fixture.UseExecutionStrategySubstitute(); - fixture.Options.Target = "InvalidDependencyTask"; - - // When - var result = fixture.Run(); - - // Then - Assert.Equal(1, result); - fixture.Log.Received(1).Write( - Verbosity.Quiet, LogLevel.Error, - "Error: {0}", "The dependency 'DateTime' is not a valid task."); - } - - [Fact] - public void Should_Return_Zero_On_Success() - { - // Given - var fixture = new CakeHostBuilderFixture(); - fixture.RegisterDefaultTask(); - - // When - var result = fixture.Run(); - - // Then - Assert.Equal(0, result); - } - - [Fact] - public void Should_Execute_OnError_Method_If_Run_Failed() - { - // Given - var fixture = new CakeHostBuilderFixture(); - fixture.RegisterTask(); - fixture.Options.Target = "On-Error-Run-Failed"; - - // When - fixture.Run(); - - // Then - fixture.Log.Received(1).Error("An error has occurred. {0}", "On test exception"); - } - - [Fact] - public void Should_Execute_OnError_Method_If_RunAsync_Failed() - { - // Given - var fixture = new CakeHostBuilderFixture(); - fixture.RegisterTask(); - fixture.Options.Target = "On-Error-RunAsync-Failed"; - - // When - fixture.Run(); - - // Then - fixture.Log.Received(1).Error("An error has occurred. {0}", "On test exception"); - } - - [Fact] - public void Should_Not_Execute_OnError_Method_If_Run_Completed() - { - // Given - var fixture = new CakeHostBuilderFixture(); - fixture.RegisterTask(); - fixture.Options.Target = "On-Error-Run-Completed"; - - // When - fixture.Run(); - - // Then - fixture.Log.DidNotReceive().Error("OnErrorRunCompletedTask Exception"); - } - - [Fact] - public void Should_Execute_Finally_Method_After_All_Methods() - { - // Given - var fixture = new CakeHostBuilderFixture(); - fixture.RegisterTask(); - fixture.Options.Target = "Finally"; - - // When - fixture.Run(); - - // Then - Received.InOrder(() => - { - fixture.Log.Information("Run method called"); - fixture.Log.Information("OnError method called"); - fixture.Log.Information("Finally method called"); - }); - } - - [Fact] - public void Should_Install_Tools() - { - // Given - var fixture = new CakeHostBuilderFixture(); - fixture.Builder.ConfigureServices(s => s.UseTool(new Uri("foo:?package=Bar"))); - fixture.RegisterDefaultTask(); - - // When - fixture.Run(); - - // Then - fixture.Installer.Received(1).Install(Arg.Is( - p => p.OriginalString == "foo:?package=Bar")); - } - } -} diff --git a/src/Cake.Frosting.Tests/Unit/CakeOptionsTests.cs b/src/Cake.Frosting.Tests/Unit/CakeOptionsTests.cs deleted file mode 100644 index e8ff568717..0000000000 --- a/src/Cake.Frosting.Tests/Unit/CakeOptionsTests.cs +++ /dev/null @@ -1,80 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Cake.Core.Diagnostics; -using Xunit; - -namespace Cake.Frosting.Tests.Unit -{ - public sealed class CakeOptionsTests - { - public sealed class TheConstructor - { - [Fact] - public void Should_Set_Arguments_To_Empty_Dictionary() - { - // Given - var options = new CakeHostOptions(); - - // When - var result = options.Arguments; - - // Then - Assert.NotNull(result); - } - - [Fact] - public void Should_Set_Working_Directory_To_Default_Value() - { - // Given - var options = new CakeHostOptions(); - - // When - var result = options.WorkingDirectory; - - // Then - Assert.Null(result); - } - - [Fact] - public void Should_Set_Target_To_Default_Value() - { - // Given - var options = new CakeHostOptions(); - - // When - var result = options.Target; - - // Then - Assert.Equal("Default", result); - } - - [Fact] - public void Should_Set_Verbosity_To_Default_Value() - { - // Given - var options = new CakeHostOptions(); - - // When - var result = options.Verbosity; - - // Then - Assert.Equal(Verbosity.Normal, result); - } - - [Fact] - public void Should_Set_Command_To_Default_Value() - { - // Given - var options = new CakeHostOptions(); - - // When - var result = options.Command; - - // Then - Assert.Equal(CakeHostCommand.Run, result); - } - } - } -} diff --git a/src/Cake.Frosting.Tests/Unit/Extensions/CakeHostBuilderExtensionsTests.cs b/src/Cake.Frosting.Tests/Unit/Extensions/CakeHostBuilderExtensionsTests.cs deleted file mode 100644 index cdb27b4437..0000000000 --- a/src/Cake.Frosting.Tests/Unit/Extensions/CakeHostBuilderExtensionsTests.cs +++ /dev/null @@ -1,195 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Cake.Core.Diagnostics; -using Cake.Frosting.Tests.Data; -using NSubstitute; -using Xunit; - -namespace Cake.Frosting.Tests.Unit.Extensions -{ - public sealed class CakeHostBuilderExtensionsTests - { - public sealed class TheUseStartupExtensionMethod - { - [Fact] - public void Should_Throw_If_Builder_Reference_Is_Null() - { - // Given - ICakeHostBuilder builder = null; - - // When - var result = Record.Exception(() => builder.UseStartup()); - - // Then - AssertEx.IsArgumentNullException(result, "builder"); - } - - [Fact] - public void Should_Run_Startup() - { - // Given - var builder = Substitute.For(); - var services = Substitute.For(); - builder.ConfigureServices(Arg.Invoke(services)); - - // When - builder.UseStartup(); - - // Then - services.RegisterType(typeof(DummyStartup.DummyStartupSentinel)); - } - } - - public sealed class TheWithArgumentsMethod - { - [Fact] - public void Should_Throw_If_Builder_Reference_Is_Null() - { - // Given - ICakeHostBuilder builder = null; - - // When - var result = Record.Exception(() => builder.WithArguments(new string[] { })); - - // Then - AssertEx.IsArgumentNullException(result, "builder"); - } - - [Theory] - [InlineData("--v=quiet", Verbosity.Quiet)] - [InlineData("--v=minimal", Verbosity.Minimal)] - [InlineData("--v=normal", Verbosity.Normal)] - [InlineData("--v=verbose", Verbosity.Verbose)] - [InlineData("--v=diagnostic", Verbosity.Diagnostic)] - [InlineData("--v=q", Verbosity.Quiet)] - [InlineData("--v=m", Verbosity.Minimal)] - [InlineData("--v=n", Verbosity.Normal)] - [InlineData("--v=v", Verbosity.Verbose)] - [InlineData("--v=d", Verbosity.Diagnostic)] - [InlineData("--verbosity=quiet", Verbosity.Quiet)] - [InlineData("--verbosity=minimal", Verbosity.Minimal)] - [InlineData("--verbosity=normal", Verbosity.Normal)] - [InlineData("--verbosity=verbose", Verbosity.Verbose)] - [InlineData("--verbosity=diagnostic", Verbosity.Diagnostic)] - [InlineData("--verbosity=q", Verbosity.Quiet)] - [InlineData("--verbosity=m", Verbosity.Minimal)] - [InlineData("--verbosity=n", Verbosity.Normal)] - [InlineData("--verbosity=v", Verbosity.Verbose)] - [InlineData("--verbosity=d", Verbosity.Diagnostic)] - [InlineData("-v=quiet", Verbosity.Quiet)] - [InlineData("-v=minimal", Verbosity.Minimal)] - [InlineData("-v=normal", Verbosity.Normal)] - [InlineData("-v=verbose", Verbosity.Verbose)] - [InlineData("-v=diagnostic", Verbosity.Diagnostic)] - [InlineData("-v=q", Verbosity.Quiet)] - [InlineData("-v=m", Verbosity.Minimal)] - [InlineData("-v=n", Verbosity.Normal)] - [InlineData("-v=v", Verbosity.Verbose)] - [InlineData("-v=d", Verbosity.Diagnostic)] - [InlineData("-verbosity=quiet", Verbosity.Quiet)] - [InlineData("-verbosity=minimal", Verbosity.Minimal)] - [InlineData("-verbosity=normal", Verbosity.Normal)] - [InlineData("-verbosity=verbose", Verbosity.Verbose)] - [InlineData("-verbosity=diagnostic", Verbosity.Diagnostic)] - [InlineData("-verbosity=q", Verbosity.Quiet)] - [InlineData("-verbosity=m", Verbosity.Minimal)] - [InlineData("-verbosity=n", Verbosity.Normal)] - [InlineData("-verbosity=v", Verbosity.Verbose)] - [InlineData("-verbosity=d", Verbosity.Diagnostic)] - public void Should_Parse_Verbosity(string args, Verbosity expected) - { - // Given - var builder = Substitute.For(); - var services = Substitute.For(); - builder.ConfigureServices(Arg.Invoke(services)); - - // When - builder.WithArguments(new[] { args }); - - // Then - services.Received(1).RegisterInstance( - Arg.Is(o => o.Verbosity == expected)); - } - - [Theory] - [InlineData("--help")] - [InlineData("--h")] - [InlineData("-help")] - [InlineData("-h")] - public void Should_Parse_Show_Help(string args) - { - // Given - var builder = Substitute.For(); - var services = Substitute.For(); - builder.ConfigureServices(Arg.Invoke(services)); - - // When - builder.WithArguments(new[] { args }); - - // Then - services.Received(1).RegisterInstance( - Arg.Is(o => o.Command == CakeHostCommand.Help)); - } - - [Theory] - [InlineData("--version")] - [InlineData("-version")] - public void Should_Parse_Show_Version(string args) - { - // Given - var builder = Substitute.For(); - var services = Substitute.For(); - builder.ConfigureServices(Arg.Invoke(services)); - - // When - builder.WithArguments(new[] { args }); - - // Then - services.Received(1).RegisterInstance( - Arg.Is(o => o.Command == CakeHostCommand.Version)); - } - - [Theory] - [InlineData("--target=Test1", "Test1")] - [InlineData("--t=Test2", "Test2")] - [InlineData("-target=Test1", "Test1")] - [InlineData("-t=Test2", "Test2")] - public void Should_Parse_Target(string args, string expected) - { - // Given - var builder = Substitute.For(); - var services = Substitute.For(); - builder.ConfigureServices(Arg.Invoke(services)); - - // When - builder.WithArguments(new[] { args }); - - // Then - services.Received(1).RegisterInstance( - Arg.Is(o => o.Target == expected)); - } - - [Theory] - [InlineData("--w=Test1", "Test1")] - [InlineData("--working=Test2", "Test2")] - [InlineData("-w=Test1", "Test1")] - [InlineData("-working=Test2", "Test2")] - public void Should_Parse_Working_Directory(string args, string expected) - { - // Given - var builder = Substitute.For(); - var services = Substitute.For(); - builder.ConfigureServices(Arg.Invoke(services)); - - // When - builder.WithArguments(new[] { args }); - - // Then - services.Received(1).RegisterInstance( - Arg.Is(o => o.WorkingDirectory.FullPath == expected)); - } - } - } -} diff --git a/src/Cake.Frosting.Tests/Unit/Extensions/CakeServicesExtensionsTests.cs b/src/Cake.Frosting.Tests/Unit/Extensions/CakeServicesExtensionsTests.cs deleted file mode 100644 index 90b1f8cf72..0000000000 --- a/src/Cake.Frosting.Tests/Unit/Extensions/CakeServicesExtensionsTests.cs +++ /dev/null @@ -1,269 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Reflection; -using Cake.Core.Composition; -using Cake.Core.Packaging; -using Cake.Frosting.Tests.Data; -using NSubstitute; -using Xunit; - -namespace Cake.Frosting.Tests.Unit.Extensions -{ - public sealed class CakeServicesExtensionsTests - { - public sealed class TheUseContextExtensionMethod - { - [Fact] - public void Should_Throw_If_Services_Reference_Is_Null() - { - // Given - ICakeServices services = null; - - // When - var result = Record.Exception(() => services.UseContext()); - - // Then - AssertEx.IsArgumentNullException(result, "services"); - } - - [Fact] - public void Should_Register_The_Context() - { - // Given - var services = Substitute.For(); - var builder = Substitute.For(); - services.RegisterType(Arg.Any()).Returns(builder); // Return a builder object when registering - builder.AsSelf().Returns(builder); // Return same builder object when chaining - builder.As(Arg.Any()).Returns(builder); // Return same builder object when chaining - - // When - services.UseContext(); - - // Then - Received.InOrder(() => - { - services.RegisterType(); - builder.AsSelf(); - builder.As(); - builder.Singleton(); - }); - } - } - - public sealed class TheUseLifetimeExtensionMethod - { - [Fact] - public void Should_Throw_If_Services_Reference_Is_Null() - { - // Given - ICakeServices services = null; - - // When - var result = Record.Exception(() => services.UseLifetime()); - - // Then - AssertEx.IsArgumentNullException(result, "services"); - } - - [Fact] - public void Should_Register_The_Lifetime() - { - // Given - var services = Substitute.For(); - var builder = Substitute.For(); - services.RegisterType(Arg.Any()).Returns(builder); // Return a builder object when registering - builder.As(Arg.Any()).Returns(builder); // Return same builder object when chaining - - // When - services.UseLifetime(); - - // Then - Received.InOrder(() => - { - services.RegisterType(); - builder.As(); - builder.Singleton(); - }); - } - } - - public sealed class TheUseTaskLifetimeExtensionMethod - { - [Fact] - public void Should_Throw_If_Services_Reference_Is_Null() - { - // Given - ICakeServices services = null; - - // When - var result = Record.Exception(() => services.UseTaskLifetime()); - - // Then - AssertEx.IsArgumentNullException(result, "services"); - } - - [Fact] - public void Should_Register_The_Lifetime() - { - // Given - var services = Substitute.For(); - var builder = Substitute.For(); - services.RegisterType(Arg.Any()).Returns(builder); // Return a builder object when registering - builder.As(Arg.Any()).Returns(builder); // Return same builder object when chaining - - // When - services.UseTaskLifetime(); - - // Then - Received.InOrder(() => - { - services.RegisterType(); - builder.As(); - builder.Singleton(); - }); - } - } - - public sealed class TheUseAssemblyExtensionMethod - { - [Fact] - public void Should_Throw_If_Services_Reference_Is_Null() - { - // Given - ICakeServices services = null; - var assembly = typeof(DateTime).GetTypeInfo().Assembly; - - // When - var result = Record.Exception(() => services.UseAssembly(assembly)); - - // Then - AssertEx.IsArgumentNullException(result, "services"); - } - - [Fact] - public void Should_Register_The_Assembly() - { - // Given - var services = Substitute.For(); - var builder = Substitute.For(); - services.RegisterInstance(Arg.Any()).Returns(builder); // Return a builder object when registering - builder.As(Arg.Any()).Returns(builder); // Return same builder object when chaining - - // When - services.UseAssembly(typeof(DateTime).GetTypeInfo().Assembly); - - // Then - Received.InOrder(() => - { - services.RegisterInstance(Arg.Any()); - builder.Singleton(); - }); - } - } - - public sealed class TheUseModuleExtensionMethod - { - [Fact] - public void Should_Throw_If_Services_Reference_Is_Null() - { - // Given - ICakeServices services = null; - - // When - var result = Record.Exception(() => services.UseModule()); - - // Then - AssertEx.IsArgumentNullException(result, "services"); - } - - [Fact] - public void Should_Create_Module_And_Call_Registration() - { - // Given - var services = Substitute.For(); - - // When - services.UseModule(); - - // Then - services.Received(1).RegisterType(typeof(DummyModule.DummyModuleSentinel)); - } - } - - public sealed class TheUsePackageInstallerExtensionMethod - { - [Fact] - public void Should_Throw_If_Services_Reference_Is_Null() - { - // Given - ICakeServices services = null; - - // When - var result = Record.Exception(() => services.UsePackageInstaller()); - - // Then - AssertEx.IsArgumentNullException(result, "services"); - } - - [Fact] - public void Should_Register_The_Package_Installer() - { - // Given - var services = Substitute.For(); - var builder = Substitute.For(); - services.RegisterType(Arg.Any()).Returns(builder); // Return a builder object when registering - builder.As(Arg.Any()).Returns(builder); // Return same builder object when chaining - - // When - services.UsePackageInstaller(); - - // Then - Received.InOrder(() => - { - services.RegisterType(); - builder.As(); - builder.Singleton(); - }); - } - } - - public sealed class TheUseToolExtensionMethod - { - [Fact] - public void Should_Throw_If_Services_Reference_Is_Null() - { - // Given - ICakeServices services = null; - - // When - var result = Record.Exception(() => services.UseTool(new Uri("nuget:?package=Foo"))); - - // Then - AssertEx.IsArgumentNullException(result, "services"); - } - - [Fact] - public void Should_Register_The_PackageReference() - { - // Given - var services = Substitute.For(); - var builder = Substitute.For(); - services.RegisterInstance(Arg.Any()).Returns(builder); // Return a builder object when registering - builder.As(Arg.Any()).Returns(builder); // Return same builder object when chaining - - // When - services.UseTool(new Uri("nuget:?package=Foo")); - - // Then - Received.InOrder(() => - { - services.RegisterInstance(Arg.Is( - r => r.OriginalString == "nuget:?package=Foo")); - }); - } - } - } -} diff --git a/src/Cake.Frosting/Abstractions/ICakeHost.cs b/src/Cake.Frosting/Abstractions/ICakeHost.cs deleted file mode 100644 index f39aaf6993..0000000000 --- a/src/Cake.Frosting/Abstractions/ICakeHost.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -// ReSharper disable once CheckNamespace -namespace Cake.Frosting -{ - /// - /// Represents a configured Cake host. - /// - public interface ICakeHost - { - /// - /// Runs the configured Cake host. - /// - /// An exit code indicating success or failure. - int Run(); - } -} \ No newline at end of file diff --git a/src/Cake.Frosting/Abstractions/ICakeHostBuilder.cs b/src/Cake.Frosting/Abstractions/ICakeHostBuilder.cs deleted file mode 100644 index 8aeaac8707..0000000000 --- a/src/Cake.Frosting/Abstractions/ICakeHostBuilder.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; - -// ReSharper disable once CheckNamespace -namespace Cake.Frosting -{ - /// - /// Represents a builder for . - /// - public interface ICakeHostBuilder - { - /// - /// Adds a delegate for configuring additional services for the host. - /// - /// A delegate for configuring the . - /// The same instance so that multiple calls can be chained. - ICakeHostBuilder ConfigureServices(Action configureServices); - - /// - /// Builds the required services and an using the specified options. - /// - /// The built . - ICakeHost Build(); - } -} \ No newline at end of file diff --git a/src/Cake.Frosting/Abstractions/ICakeServices.cs b/src/Cake.Frosting/Abstractions/ICakeServices.cs deleted file mode 100644 index cffeaf1798..0000000000 --- a/src/Cake.Frosting/Abstractions/ICakeServices.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Cake.Core.Composition; - -// ReSharper disable once CheckNamespace -namespace Cake.Frosting -{ - /// - /// Represents a collection of service registrations. - /// - /// - public interface ICakeServices : ICakeContainerRegistrar - { - } -} \ No newline at end of file diff --git a/src/Cake.Frosting/Abstractions/ICakeTaskFinder.cs b/src/Cake.Frosting/Abstractions/ICakeTaskFinder.cs deleted file mode 100644 index a36bfe08da..0000000000 --- a/src/Cake.Frosting/Abstractions/ICakeTaskFinder.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.Reflection; - -// ReSharper disable once CheckNamespace -namespace Cake.Frosting -{ - /// - /// Represents Frosting's task finder mechanism. - /// - public interface ICakeTaskFinder - { - /// - /// Gets all task types present in the provided assemblies. - /// - /// The assemblies. - /// An array of task types. - Type[] GetTasks(IEnumerable assemblies); - } -} diff --git a/src/Cake.Frosting/Annotations/ContinueOnErrorAttribute.cs b/src/Cake.Frosting/Annotations/ContinueOnErrorAttribute.cs index 965e91b09a..37ab2c6eb7 100644 --- a/src/Cake.Frosting/Annotations/ContinueOnErrorAttribute.cs +++ b/src/Cake.Frosting/Annotations/ContinueOnErrorAttribute.cs @@ -4,7 +4,6 @@ using System; -// ReSharper disable once CheckNamespace namespace Cake.Frosting { /// diff --git a/src/Cake.Frosting/Annotations/DependencyAttribute.cs b/src/Cake.Frosting/Annotations/DependencyAttribute.cs index 850cc42bac..8afe6bfd49 100644 --- a/src/Cake.Frosting/Annotations/DependencyAttribute.cs +++ b/src/Cake.Frosting/Annotations/DependencyAttribute.cs @@ -4,15 +4,14 @@ using System; -// ReSharper disable once CheckNamespace namespace Cake.Frosting { /// /// Represents a dependency. /// - /// + /// [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] - public sealed class DependencyAttribute : Attribute + public sealed class DependencyAttribute : Attribute, ITaskDependency { /// /// Gets the depdendency task type. diff --git a/src/Cake.Frosting/Annotations/IsDependeeOfAttribute.cs b/src/Cake.Frosting/Annotations/IsDependeeOfAttribute.cs new file mode 100644 index 0000000000..a55870a415 --- /dev/null +++ b/src/Cake.Frosting/Annotations/IsDependeeOfAttribute.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace Cake.Frosting +{ + /// + /// Represents a reverse dependency. + /// + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + public sealed class IsDependeeOfAttribute : Attribute, IReverseTaskDependency + { + /// + /// Gets the reverse dependency task type. + /// + /// The reverse dependency task type. + public Type Task { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The reverse dependency type. + public IsDependeeOfAttribute(Type type) + { + Task = type; + } + } +} diff --git a/src/Cake.Frosting/Annotations/IsDependentOnAttribute.cs b/src/Cake.Frosting/Annotations/IsDependentOnAttribute.cs new file mode 100644 index 0000000000..08cb51b52c --- /dev/null +++ b/src/Cake.Frosting/Annotations/IsDependentOnAttribute.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace Cake.Frosting +{ + /// + /// Represents a dependency. + /// + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + public sealed class IsDependentOnAttribute : Attribute, ITaskDependency + { + /// + /// Gets the dependency task type. + /// + /// The dependency task type. + public Type Task { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The dependency type. + public IsDependentOnAttribute(Type type) + { + Task = type; + } + } +} diff --git a/src/Cake.Frosting/Annotations/ReverseDependencyAttribute.cs b/src/Cake.Frosting/Annotations/ReverseDependencyAttribute.cs new file mode 100644 index 0000000000..e02e871302 --- /dev/null +++ b/src/Cake.Frosting/Annotations/ReverseDependencyAttribute.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace Cake.Frosting +{ + /// + /// Represents a reverse dependency. + /// + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + public sealed class ReverseDependencyAttribute : Attribute, IReverseTaskDependency + { + /// + /// Gets the reverse dependency task type. + /// + /// The reverse dependency task type. + public Type Task { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The reverse dependency type. + public ReverseDependencyAttribute(Type type) + { + Task = type; + } + } +} diff --git a/src/Cake.Frosting/Annotations/TaskDescriptionAttribute.cs b/src/Cake.Frosting/Annotations/TaskDescriptionAttribute.cs new file mode 100644 index 0000000000..1129ed7d5f --- /dev/null +++ b/src/Cake.Frosting/Annotations/TaskDescriptionAttribute.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace Cake.Frosting +{ + /// + /// Represents a task description. + /// + /// + [AttributeUsage(AttributeTargets.Class, Inherited = false)] + public sealed class TaskDescriptionAttribute : Attribute + { + /// + /// Gets the task description. + /// + public string Description { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The task description. + public TaskDescriptionAttribute(string description) + { + Description = description; + } + } +} diff --git a/src/Cake.Frosting/Annotations/TaskNameAttribute.cs b/src/Cake.Frosting/Annotations/TaskNameAttribute.cs index 9694d689ac..b748c7bf2e 100644 --- a/src/Cake.Frosting/Annotations/TaskNameAttribute.cs +++ b/src/Cake.Frosting/Annotations/TaskNameAttribute.cs @@ -4,7 +4,6 @@ using System; -// ReSharper disable once CheckNamespace namespace Cake.Frosting { /// @@ -15,15 +14,14 @@ namespace Cake.Frosting public sealed class TaskNameAttribute : Attribute { /// - /// Gets the name of the task. + /// Gets the task name. /// - /// The name. public string Name { get; } /// /// Initializes a new instance of the class. /// - /// The name of the task. + /// The task name. public TaskNameAttribute(string name) { Name = name; diff --git a/src/Cake.Frosting/AsyncFrostingTask.cs b/src/Cake.Frosting/AsyncFrostingTask.cs index 29cdff57c5..13eb3b90ce 100644 --- a/src/Cake.Frosting/AsyncFrostingTask.cs +++ b/src/Cake.Frosting/AsyncFrostingTask.cs @@ -3,10 +3,8 @@ // See the LICENSE file in the project root for more information. using System; -using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; using Cake.Core; -using Cake.Frosting.Internal; namespace Cake.Frosting { @@ -55,6 +53,10 @@ public virtual bool ShouldRun(T context) /// The context. public virtual void OnError(Exception exception, T context) { + if (exception is null) + { + throw new ArgumentNullException(nameof(exception)); + } } /// @@ -65,35 +67,51 @@ public virtual void Finally(T context) { } - [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:ElementsMustBeDocumented", Justification = "Explicit implementation.")] + /// Task IFrostingTask.RunAsync(ICakeContext context) { - Guard.ArgumentNotNull(context, nameof(context)); + if (context is null) + { + throw new ArgumentNullException(nameof(context)); + } return RunAsync((T)context); } - [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:ElementsMustBeDocumented", Justification = "Explicit implementation.")] + /// bool IFrostingTask.ShouldRun(ICakeContext context) { - Guard.ArgumentNotNull(context, nameof(context)); + if (context is null) + { + throw new ArgumentNullException(nameof(context)); + } return ShouldRun((T)context); } - [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:ElementsMustBeDocumented", Justification = "Explicit implementation.")] + /// void IFrostingTask.OnError(Exception exception, ICakeContext context) { - Guard.ArgumentNotNull(exception, nameof(exception)); - Guard.ArgumentNotNull(context, nameof(context)); + if (exception is null) + { + throw new ArgumentNullException(nameof(exception)); + } + + if (context is null) + { + throw new ArgumentNullException(nameof(context)); + } OnError(exception, (T)context); } - [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:ElementsMustBeDocumented", Justification = "Explicit implementation.")] + /// void IFrostingTask.Finally(ICakeContext context) { - Guard.ArgumentNotNull(context, nameof(context)); + if (context is null) + { + throw new ArgumentNullException(nameof(context)); + } Finally((T)context); } diff --git a/src/Cake.Frosting/Cake.Frosting.csproj b/src/Cake.Frosting/Cake.Frosting.csproj index fde2234469..23fb3e0622 100644 --- a/src/Cake.Frosting/Cake.Frosting.csproj +++ b/src/Cake.Frosting/Cake.Frosting.csproj @@ -8,7 +8,12 @@ + + + + + diff --git a/src/Cake.Frosting/CakeHost.cs b/src/Cake.Frosting/CakeHost.cs index 72ee2c98d7..ac747d80b5 100644 --- a/src/Cake.Frosting/CakeHost.cs +++ b/src/Cake.Frosting/CakeHost.cs @@ -4,123 +4,168 @@ using System; using System.Collections.Generic; -using System.Linq; +using System.Reflection; +using Cake.Cli; +using Cake.Common.Modules; using Cake.Core; +using Cake.Core.Configuration; using Cake.Core.Diagnostics; -using Cake.Core.IO; -using Cake.Core.Packaging; +using Cake.Core.Modules; using Cake.Frosting.Internal; -using Cake.Frosting.Internal.Commands; -using Cake.Frosting.Internal.Composition; +using Cake.NuGet; +using Microsoft.Extensions.DependencyInjection; +using Spectre.Cli; namespace Cake.Frosting { - internal sealed class CakeHost : ICakeHost + /// + /// The Cake host. + /// + public sealed class CakeHost { - private readonly CakeHostOptions _options; - private readonly IFileSystem _fileSystem; - private readonly IFrostingContext _context; - private readonly IEnumerable _tasks; - private readonly IFrostingLifetime _lifetime; - private readonly IFrostingTaskLifetime _taskLifetime; - private readonly ICakeEnvironment _environment; - private readonly ICakeEngine _engine; - private readonly ICakeLog _log; - private readonly IToolInstaller _installer; - private readonly List _tools; - private readonly CommandFactory _commandFactory; - private readonly WorkingDirectory _workingDirectory; - private readonly EngineInitializer _engineInitializer; - - // ReSharper disable once NotAccessedField.Local - private readonly Container _container; - - public CakeHost(IFrostingContext context, Container container, CakeHostOptions options, - IFileSystem fileSystem, ICakeEnvironment environment, ICakeEngine engine, ICakeLog log, - IToolInstaller installer, IEnumerable tools, - EngineInitializer engineInitializer, CommandFactory commandFactory, - WorkingDirectory workingDirectory = null, IEnumerable tasks = null, - IFrostingLifetime lifetime = null, IFrostingTaskLifetime taskLifetime = null) + private readonly IServiceCollection _services; + private readonly List _assemblies; + + /// + /// Initializes a new instance of the class. + /// + public CakeHost() + { + _services = CreateServiceCollection(); + _assemblies = new List + { + Assembly.GetEntryAssembly() + }; + } + + /// + /// Creates a . + /// + /// The created . + public static CakeHost Create() + { + return new CakeHost(); + } + + /// + /// Registers an assembly which will be used to find tasks. + /// + /// The assembly. + /// The same instance so that multiple calls can be chained. + public CakeHost AddAssembly(Assembly assembly) + { + _assemblies.Add(assembly); + return this; + } + + /// + /// Adds a delegate for configuring additional services for the host. + /// + /// A delegate for configuring the . + /// The same instance so that multiple calls can be chained. + public CakeHost ConfigureServices(Action services) { - Guard.ArgumentNotNull(context, nameof(context)); - Guard.ArgumentNotNull(container, nameof(container)); - Guard.ArgumentNotNull(options, nameof(options)); - Guard.ArgumentNotNull(fileSystem, nameof(fileSystem)); - Guard.ArgumentNotNull(environment, nameof(environment)); - Guard.ArgumentNotNull(engine, nameof(engine)); - Guard.ArgumentNotNull(log, nameof(log)); - Guard.ArgumentNotNull(engineInitializer, nameof(engineInitializer)); - Guard.ArgumentNotNull(commandFactory, nameof(commandFactory)); - - // Mandatory arguments. - _context = context; - _container = container; - _options = options; - _fileSystem = fileSystem; - _environment = environment; - _engine = engine; - _log = log; - _installer = installer; - _tools = new List(tools ?? Enumerable.Empty()); - _engineInitializer = engineInitializer; - _commandFactory = commandFactory; - - // Optional arguments. - _workingDirectory = workingDirectory; - _tasks = tasks; - _lifetime = lifetime; - _taskLifetime = taskLifetime; + services(_services); + return this; } - public int Run() + /// + /// Runs the build with the specified arguments. + /// + /// The arguments. + /// The exit code. + public int Run(IEnumerable args) { - try + RegisterTasks(_assemblies); + + // Register all the user's registrations + var registrar = new TypeRegistrar(); + registrar.RegisterInstance(typeof(IServiceCollection), _services); + + // Run the application + var app = new CommandApp(registrar); + app.Configure(config => { - // Update the log verbosity. - _log.Verbosity = _options.Verbosity; + config.ValidateExamples(); - // Set the working directory. - _environment.WorkingDirectory = GetWorkingDirectory(); - _log.Debug("Working directory: {0}", _environment.WorkingDirectory.FullPath); + // Top level examples. + config.AddExample(new[] { string.Empty }); + config.AddExample(new[] { "--verbosity", "quiet" }); + config.AddExample(new[] { "--tree" }); + }); - // Install tools. - if (_tools.Count > 0) - { - _log.Verbose("Installing tools..."); - foreach (var tool in _tools) - { - _installer.Install(tool); - } - } + return app.Run(args); + } - // Initialize the engine and register everything. - _engineInitializer.Initialize(_engine, _context, _tasks, _lifetime, _taskLifetime); + private ServiceCollection CreateServiceCollection() + { + var services = new ServiceCollection(); - // Get the command and execute. - var command = _commandFactory.GetCommand(_options); - var result = command.ExecuteAsync(_engine, _options).GetAwaiter().GetResult(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); - // Return success. - return result ? 0 : 1; - } - catch (Exception exception) + services.AddSingleton>(); + services.AddSingleton>(); + services.AddSingleton(); + services.AddSingleton(); + + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + + services.AddSingleton(); + + services.UseModule(); + services.UseModule(); + services.UseModule(); + + services.AddSingleton(); + services.AddSingleton(f => f.GetService()); + + return services; + } + + private void RegisterTasks(IEnumerable assemblies) + { + // Find tasks in registered assemblies. + var tasks = GetTasks(assemblies); + if (tasks.Length > 0) { - ErrorHandler.OutputError(_log, exception); - return ErrorHandler.GetExitCode(exception); + foreach (var task in tasks) + { + _services.AddSingleton(typeof(IFrostingTask), task); + } } } - private DirectoryPath GetWorkingDirectory() + private static Type[] GetTasks(IEnumerable assemblies) { - var workingDirectory = _options.WorkingDirectory ?? _workingDirectory?.Path ?? "."; - workingDirectory = workingDirectory.MakeAbsolute(_environment); - - if (!_fileSystem.Exist(workingDirectory)) + var result = new List(); + foreach (var assembly in assemblies) { - throw new FrostingException($"The working directory '{workingDirectory.FullPath}' does not exist."); + if (assembly == null) + { + continue; + } + + foreach (var type in assembly.GetExportedTypes()) + { + var info = type.GetTypeInfo(); + if (typeof(IFrostingTask).IsAssignableFrom(type) && info.IsClass && !info.IsAbstract) + { + result.Add(type); + } + } } - return workingDirectory; + return result.ToArray(); } } -} \ No newline at end of file +} diff --git a/src/Cake.Frosting/CakeHostBuilder.cs b/src/Cake.Frosting/CakeHostBuilder.cs deleted file mode 100644 index ae1695f212..0000000000 --- a/src/Cake.Frosting/CakeHostBuilder.cs +++ /dev/null @@ -1,98 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.Reflection; -using Cake.Core.Composition; -using Cake.Core.Modules; -using Cake.Frosting.Internal; -using Cake.Frosting.Internal.Composition; -using Cake.NuGet; - -namespace Cake.Frosting -{ - /// - /// A builder for . - /// - /// - public sealed class CakeHostBuilder : ICakeHostBuilder - { - private readonly CakeServices _registrations; - - /// - /// Initializes a new instance of the class. - /// - public CakeHostBuilder() - { - _registrations = new CakeServices(); - } - - /// - public ICakeHostBuilder ConfigureServices(Action configureServices) - { - try - { - configureServices(_registrations); - return this; - } - catch (Exception exception) - { - return new ErrorCakeHostBuilder(exception); - } - } - - /// - public ICakeHost Build() - { - try - { - // Create the "base" container with the minimum - // stuff registered to run Cake at all. - var registrar = new CakeServices(); - registrar.RegisterModule(new CoreModule()); - registrar.RegisterModule(new Module()); - registrar.RegisterModule(new NuGetModule()); - var container = registrar.Build(); - - // Add custom registrations to the container. - container.Update(_registrations); - - // Find and register tasks with the container. - RegisterTasks(container); - - // Resolve the application and run it. - return container.Resolve(); - } - catch (Exception exception) - { - return new ErrorCakeHost(exception); - } - } - - private static void RegisterTasks(Container container) - { - // Create a child scope to not affect the underlying - // container in case the ICakeTaskFinder references - // something that is later replaced. - using (var scope = container.CreateChildScope()) - { - // Find tasks in registered assemblies. - var assemblies = scope.Resolve>(); - var finder = scope.Resolve(); - var tasks = finder.GetTasks(assemblies); - - if (tasks.Length > 0) - { - var registrar = new CakeServices(); - foreach (var task in tasks) - { - registrar.RegisterType(task).As().Singleton(); - } - container.Update(registrar); - } - } - } - } -} diff --git a/src/Cake.Frosting/CakeHostBuilderExtensions.cs b/src/Cake.Frosting/CakeHostBuilderExtensions.cs deleted file mode 100644 index 3bc13d888a..0000000000 --- a/src/Cake.Frosting/CakeHostBuilderExtensions.cs +++ /dev/null @@ -1,48 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Cake.Frosting.Internal; -using Cake.Frosting.Internal.Arguments; - -namespace Cake.Frosting -{ - /// - /// Contains extension methods for . - /// - public static class CakeHostBuilderExtensions - { - /// - /// Specify the startup type to be used by the Cake host. - /// - /// The startup type. - /// The to configure. - /// The same instance so that multiple calls can be chained. - public static ICakeHostBuilder UseStartup(this ICakeHostBuilder builder) - where TStartup : IFrostingStartup, new() - { - Guard.ArgumentNotNull(builder, nameof(builder)); - - return builder.ConfigureServices(services => - { - var startup = new TStartup(); - startup.Configure(services); - }); - } - - /// - /// Specify the arguments to be used when building the host. - /// The arguments will be translated into a instance. - /// - /// The builder. - /// The arguments to parse. - /// The same instance so that multiple calls can be chained. - public static ICakeHostBuilder WithArguments(this ICakeHostBuilder builder, string[] args) - { - Guard.ArgumentNotNull(builder, nameof(builder)); - - return builder.ConfigureServices(services => - services.RegisterInstance(ArgumentParser.Parse(args)).AsSelf().Singleton()); - } - } -} \ No newline at end of file diff --git a/src/Cake.Frosting/CakeHostCommand.cs b/src/Cake.Frosting/CakeHostCommand.cs deleted file mode 100644 index c5b0d934af..0000000000 --- a/src/Cake.Frosting/CakeHostCommand.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace Cake.Frosting -{ - /// - /// Represents a command for the . - /// - public enum CakeHostCommand - { - /// - /// Runs the build script. - /// - Run = 0, - - /// - /// Performs a dry run of the build script. - /// - DryRun = 1, - - /// - /// Shows version information. - /// - Version = 2, - - /// - /// Shows help. - /// - Help = 3, - } -} diff --git a/src/Cake.Frosting/CakeHostOptions.cs b/src/Cake.Frosting/CakeHostOptions.cs deleted file mode 100644 index 4f3acaac15..0000000000 --- a/src/Cake.Frosting/CakeHostOptions.cs +++ /dev/null @@ -1,59 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using Cake.Core.Diagnostics; -using Cake.Core.IO; - -namespace Cake.Frosting -{ - /// - /// The options that tells how Cake should be executed. - /// - public sealed class CakeHostOptions - { - /// - /// Gets the arguments. - /// - /// The arguments. - public IDictionary Arguments { get; } - - /// - /// Gets or sets the working directory. - /// - /// The working directory. - public DirectoryPath WorkingDirectory { get; set; } - - /// - /// Gets or sets the target to run. - /// - /// The target to run. - public string Target { get; set; } - - /// - /// Gets or sets the output verbosity. - /// - /// The output verbosity. - public Verbosity Verbosity { get; set; } - - /// - /// Gets or sets the command to execute. - /// - /// The command to execute. - public CakeHostCommand Command { get; set; } - - /// - /// Initializes a new instance of the class. - /// - public CakeHostOptions() - { - Arguments = new Dictionary(StringComparer.OrdinalIgnoreCase); - WorkingDirectory = null; - Target = "Default"; - Verbosity = Verbosity.Normal; - Command = CakeHostCommand.Run; - } - } -} diff --git a/src/Cake.Frosting/CakeServices.cs b/src/Cake.Frosting/CakeServices.cs deleted file mode 100644 index b095f0a0c2..0000000000 --- a/src/Cake.Frosting/CakeServices.cs +++ /dev/null @@ -1,53 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using Cake.Core.Composition; -using Cake.Frosting.Internal.Composition; -using Cake.Frosting.Internal.Composition.Activators; - -namespace Cake.Frosting -{ - internal sealed class CakeServices : ICakeServices - { - public ContainerBuilder Builder { get; } - - public CakeServices() - : this(null) - { - } - - public CakeServices(ContainerBuilder builder) - { - Builder = builder ?? new ContainerBuilder(); - } - - public void RegisterModule(ICakeModule module) - { - module.Register(this); - } - - public ICakeRegistrationBuilder RegisterType(Type type) - { - var registration = new ComponentRegistration(type); - registration.Activator = new ReflectionActivator(type); - Builder.Register(registry => registry.Register(registration)); - return new RegistrationBuilder(registration); - } - - public ICakeRegistrationBuilder RegisterInstance(T instance) where T : class - { - var registration = new ComponentRegistration(typeof(T)); - registration.Singleton = true; - registration.Activator = new InstanceActivator(instance); - Builder.Register(registry => registry.Register(registration)); - return new RegistrationBuilder(registration); - } - - public Container Build() - { - return Builder.Build(); - } - } -} diff --git a/src/Cake.Frosting/CakeServicesExtensions.cs b/src/Cake.Frosting/CakeServicesExtensions.cs deleted file mode 100644 index 83162aacce..0000000000 --- a/src/Cake.Frosting/CakeServicesExtensions.cs +++ /dev/null @@ -1,157 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Reflection; -using Cake.Core.Composition; -using Cake.Core.IO; -using Cake.Core.Packaging; -using Cake.Frosting.Internal; - -namespace Cake.Frosting -{ - /// - /// Contains extensions for . - /// - public static class CakeServicesExtensions - { - /// - /// Registers the specified context type. - /// Only the last registration will be used. - /// - /// The type of the context to register. - /// The service registration collection. - /// The same instance so that multiple calls can be chained. - public static ICakeServices UseContext(this ICakeServices services) - where TContext : IFrostingContext - { - Guard.ArgumentNotNull(services, nameof(services)); - - services.RegisterType().AsSelf().As().Singleton(); - return services; - } - - /// - /// Registers the specified lifetime type. - /// Only the last registration will be used. - /// - /// The type of the lifetime. - /// The service registration collection. - /// The same instance so that multiple calls can be chained. - public static ICakeServices UseLifetime(this ICakeServices services) - where TLifetime : IFrostingLifetime - { - Guard.ArgumentNotNull(services, nameof(services)); - - services.RegisterType().As().Singleton(); - return services; - } - - /// - /// Registers the specified task lifetime type. - /// Only the last registration will be used. - /// - /// The type of the lifetime. - /// The service registration collection. - /// The same instance so that multiple calls can be chained. - public static ICakeServices UseTaskLifetime(this ICakeServices services) - where TLifetime : IFrostingTaskLifetime - { - Guard.ArgumentNotNull(services, nameof(services)); - - services.RegisterType().As().Singleton(); - return services; - } - - /// - /// Registers the specified . - /// - /// The service registration collection. - /// The assembly to register. - /// The same instance so that multiple calls can be chained. - public static ICakeServices UseAssembly(this ICakeServices services, Assembly assembly) - { - Guard.ArgumentNotNull(services, nameof(services)); - - services.RegisterInstance(assembly).Singleton(); - return services; - } - - /// - /// Registers the specified module type. - /// - /// The type of the module. - /// The service registration collection. - /// The same instance so that multiple calls can be chained. - public static ICakeServices UseModule(this ICakeServices services) - where TModule : ICakeModule, new() - { - Guard.ArgumentNotNull(services, nameof(services)); - - var module = new TModule(); - module.Register(services); - return services; - } - - /// - /// Sets the relative working directory to be used when running the build. - /// - /// The service registration collection. - /// The working directory path. - /// The same instance so that multiple calls can be chained. - public static ICakeServices UseWorkingDirectory(this ICakeServices services, DirectoryPath path) - { - Guard.ArgumentNotNull(services, nameof(services)); - - services.RegisterInstance(new WorkingDirectory(path)).AsSelf().Singleton(); - return services; - } - - /// - /// Add or replace a setting in the configuration. - /// - /// The service registration collection. - /// The key of the setting to add or replace. - /// The value of the setting to add or replace. - /// The same instance so that multiple calls can be chained. - public static ICakeServices UseSetting(this ICakeServices services, string key, string value) - { - Guard.ArgumentNotNull(services, nameof(services)); - - var info = new ConfigurationSetting(key, value); - services.RegisterInstance(info).AsSelf().Singleton(); - return services; - } - - /// - /// Registers a specific tool for installation. - /// - /// The type of the package installer. - /// The service registration collection. - /// The same instance so that multiple calls can be chained. - public static ICakeServices UsePackageInstaller(this ICakeServices services) - where TPackageInstaller : IPackageInstaller - { - Guard.ArgumentNotNull(services, nameof(services)); - - services.RegisterType().As().Singleton(); - return services; - } - - /// - /// Registers a specific tool for installation. - /// - /// The service registration collection. - /// The tool URI. - /// The same instance so that multiple calls can be chained. - public static ICakeServices UseTool(this ICakeServices services, Uri uri) - { - Guard.ArgumentNotNull(services, nameof(services)); - - var package = new PackageReference(uri.OriginalString); - services.RegisterInstance(package).Singleton(); - return services; - } - } -} \ No newline at end of file diff --git a/src/Cake.Frosting/Extensions/CakeHostExtensions.cs b/src/Cake.Frosting/Extensions/CakeHostExtensions.cs new file mode 100644 index 0000000000..f632703f07 --- /dev/null +++ b/src/Cake.Frosting/Extensions/CakeHostExtensions.cs @@ -0,0 +1,183 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using Cake.Core.Composition; +using Cake.Core.IO; +using Cake.Core.Packaging; + +namespace Cake.Frosting +{ + /// + /// Contains extension methods for . + /// + public static class CakeHostExtensions + { + /// + /// Specify the startup type to be used by the Cake host. + /// + /// The startup type. + /// The to configure. + /// The same instance so that multiple calls can be chained. + public static CakeHost UseStartup(this CakeHost host) + where TStartup : IFrostingStartup, new() + { + if (host is null) + { + throw new ArgumentNullException(nameof(host)); + } + + return host.ConfigureServices(services => + { + var startup = new TStartup(); + startup.Configure(services); + }); + } + + /// + /// Registers the specified context type. + /// Only the last registration will be used. + /// + /// The type of the context to register. + /// The to configure. + /// The same instance so that multiple calls can be chained. + public static CakeHost UseContext(this CakeHost host) + where TContext : class, IFrostingContext + { + return host.ConfigureServices(services => services.UseContext()); + } + + /// + /// Registers the specified lifetime type. + /// Only the last registration will be used. + /// + /// The type of the lifetime. + /// The to configure. + /// The same instance so that multiple calls can be chained. + public static CakeHost UseLifetime(this CakeHost host) + where TLifetime : class, IFrostingLifetime + { + return host.ConfigureServices(services => services.UseLifetime()); + } + + /// + /// Registers a setup action. + /// + /// The setup action. + /// The service collection. + /// The same instance so that multiple calls can be chained. + public static CakeHost UseSetup(this CakeHost host) + where TSetup : class, IFrostingSetup + { + return host.ConfigureServices(services => services.UseSetup()); + } + + /// + /// Registers a teardown action. + /// + /// The teardown action. + /// The service collection. + /// The same instance so that multiple calls can be chained. + public static CakeHost UseTeardown(this CakeHost host) + where TTeardown : class, IFrostingTeardown + { + return host.ConfigureServices(services => services.UseTeardown()); + } + + /// + /// Registers the specified task lifetime type. + /// Only the last registration will be used. + /// + /// The type of the lifetime. + /// The to configure. + /// The same instance so that multiple calls can be chained. + public static CakeHost UseTaskLifetime(this CakeHost host) + where TLifetime : class, IFrostingTaskLifetime + { + return host.ConfigureServices(services => services.UseTaskLifetime()); + } + + /// + /// Registers a task setup action. + /// + /// The task setup action. + /// The to configure. + /// The same instance so that multiple calls can be chained. + public static CakeHost UseTaskSetup(this CakeHost host) + where TSetup : class, IFrostingTaskSetup + { + return host.ConfigureServices(services => services.UseTaskSetup()); + } + + /// + /// Registers a tasl teardown action. + /// + /// The task teardown action. + /// The to configure. + /// The same instance so that multiple calls can be chained. + public static CakeHost UseTaskTeardown(this CakeHost host) + where TTeardown : class, IFrostingTaskTeardown + { + return host.ConfigureServices(services => services.UseTaskTeardown()); + } + + /// + /// Registers the specified module type. + /// + /// The type of the module. + /// The to configure. + /// The same instance so that multiple calls can be chained. + public static CakeHost UseModule(this CakeHost host) + where TModule : ICakeModule, new() + { + return host.ConfigureServices(services => services.UseModule()); + } + + /// + /// Registers a specific tool for installation. + /// + /// The type of the package installer. + /// The to configure. + /// The same instance so that multiple calls can be chained. + public static CakeHost UsePackageInstaller(this CakeHost host) + where TPackageInstaller : class, IPackageInstaller + { + return host.ConfigureServices(services => services.UsePackageInstaller()); + } + + /// + /// Registers a specific tool for installation. + /// + /// The to configure. + /// The tool URI. + /// The same instance so that multiple calls can be chained. + public static CakeHost UseTool(this CakeHost host, Uri uri) + { + return host.ConfigureServices(services => services.UseTool(uri)); + } + + /// + /// Sets the relative working directory to be used when running the build. + /// + /// The to configure. + /// The working directory path. + /// The same instance so that multiple calls can be chained. + public static CakeHost UseWorkingDirectory(this CakeHost host, DirectoryPath path) + { + return host.ConfigureServices(services => services.UseWorkingDirectory(path)); + } + + /// + /// Sets the specified Cake setting to the specified value. + /// + /// The to configure. + /// The setting key. + /// The setting value. + /// The same instance so that multiple calls can be chained. + public static CakeHost UseCakeSetting(this CakeHost host, string key, string value) + { + return host.ConfigureServices(services => services.UseCakeSetting(key, value)); + } + } +} diff --git a/src/Cake.Frosting/Extensions/ServiceCollectionExtensions.cs b/src/Cake.Frosting/Extensions/ServiceCollectionExtensions.cs new file mode 100644 index 0000000000..c6b5073877 --- /dev/null +++ b/src/Cake.Frosting/Extensions/ServiceCollectionExtensions.cs @@ -0,0 +1,182 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using Cake.Core.Composition; +using Cake.Core.IO; +using Cake.Core.Packaging; +using Cake.Frosting.Internal; +using Microsoft.Extensions.DependencyInjection; + +namespace Cake.Frosting +{ + /// + /// Contains extensions for . + /// + public static class ServiceCollectionExtensions + { + /// + /// Registers the specified context type. + /// Only the last registration will be used. + /// + /// The type of the context to register. + /// The service collection. + /// The same instance so that multiple calls can be chained. + public static IServiceCollection UseContext(this IServiceCollection services) + where TContext : class, IFrostingContext + { + services.AddSingleton(); + return services; + } + + /// + /// Registers the specified lifetime type. + /// Only the last registration will be used. + /// + /// The type of the lifetime. + /// The service collection. + /// The same instance so that multiple calls can be chained. + public static IServiceCollection UseLifetime(this IServiceCollection services) + where TLifetime : class, IFrostingLifetime + { + services.AddSingleton(); + services.AddSingleton(s => s.GetRequiredService()); + services.AddSingleton(s => s.GetRequiredService()); + return services; + } + + /// + /// Registers a setup action. + /// + /// The setup action. + /// The service collection. + /// The same instance so that multiple calls can be chained. + public static IServiceCollection UseSetup(this IServiceCollection services) + where TSetup : class, IFrostingSetup + { + return services.AddSingleton(); + } + + /// + /// Registers a teardown action. + /// + /// The teardown action. + /// The service collection. + /// The same instance so that multiple calls can be chained. + public static IServiceCollection UseTeardown(this IServiceCollection services) + where TTeardown : class, IFrostingTeardown + { + return services.AddSingleton(); + } + + /// + /// Registers the specified task lifetime type. + /// Only the last registration will be used. + /// + /// The type of the lifetime. + /// The service collection. + /// The same instance so that multiple calls can be chained. + public static IServiceCollection UseTaskLifetime(this IServiceCollection services) + where TLifetime : class, IFrostingTaskLifetime + { + services.AddSingleton(); + services.AddSingleton(s => s.GetRequiredService()); + services.AddSingleton(s => s.GetRequiredService()); + return services; + } + + /// + /// Registers a task setup action. + /// + /// The task setup action. + /// The service collection. + /// The same instance so that multiple calls can be chained. + public static IServiceCollection UseTaskSetup(this IServiceCollection services) + where TSetup : class, IFrostingTaskSetup + { + return services.AddSingleton(); + } + + /// + /// Registers a tasl teardown action. + /// + /// The task teardown action. + /// The service collection. + /// The same instance so that multiple calls can be chained. + public static IServiceCollection UseTaskTeardown(this IServiceCollection services) + where TTeardown : class, IFrostingTaskTeardown + { + return services.AddSingleton(); + } + + /// + /// Registers the specified module type. + /// + /// The type of the module. + /// The service collection. + /// The same instance so that multiple calls can be chained. + public static IServiceCollection UseModule(this IServiceCollection services) + where TModule : ICakeModule, new() + { + var module = new TModule(); + + var adapter = new ServiceCollectionAdapter(); + module.Register(adapter); + adapter.Transfer(services); + + return services; + } + + /// + /// Registers a specific tool for installation. + /// + /// The type of the package installer. + /// The service collection. + /// The same instance so that multiple calls can be chained. + public static IServiceCollection UsePackageInstaller(this IServiceCollection services) + where TPackageInstaller : class, IPackageInstaller + { + services.AddSingleton(); + return services; + } + + /// + /// Registers a specific tool for installation. + /// + /// The service collection. + /// The tool URI. + /// The same instance so that multiple calls can be chained. + public static IServiceCollection UseTool(this IServiceCollection services, Uri uri) + { + var package = new PackageReference(uri.OriginalString); + services.AddSingleton(package); + return services; + } + + /// + /// Sets the relative working directory to be used when running the build. + /// + /// The service collection. + /// The working directory path. + /// The same instance so that multiple calls can be chained. + public static IServiceCollection UseWorkingDirectory(this IServiceCollection services, DirectoryPath path) + { + services.AddSingleton(new WorkingDirectory(path)); + return services; + } + + /// + /// Sets the specified Cake setting to the specified value. + /// + /// The service collection. + /// The setting key. + /// The setting value. + /// The same instance so that multiple calls can be chained. + public static IServiceCollection UseCakeSetting(this IServiceCollection services, string key, string value) + { + services.AddSingleton(new FrostingConfigurationValue(key, value)); + return services; + } + } +} diff --git a/src/Cake.Frosting/FrostingException.cs b/src/Cake.Frosting/FrostingException.cs index c4c3af03d1..35030b6fb2 100644 --- a/src/Cake.Frosting/FrostingException.cs +++ b/src/Cake.Frosting/FrostingException.cs @@ -9,7 +9,7 @@ namespace Cake.Frosting /// /// Represent errors that occur during execution of Cake. /// - /// + /// public sealed class FrostingException : Exception { /// diff --git a/src/Cake.Frosting/FrostingLifetime.cs b/src/Cake.Frosting/FrostingLifetime.cs index 9dedc5e43a..d21c8d82ed 100644 --- a/src/Cake.Frosting/FrostingLifetime.cs +++ b/src/Cake.Frosting/FrostingLifetime.cs @@ -2,9 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Diagnostics.CodeAnalysis; +using System; using Cake.Core; -using Cake.Frosting.Internal; namespace Cake.Frosting { @@ -29,9 +28,7 @@ public abstract class FrostingLifetime : IFrostingLifetime /// If setup fails, no tasks will be executed but teardown will be performed. /// /// The context. - public virtual void Setup(TContext context) - { - } + public abstract void Setup(TContext context); /// /// This method is executed after all tasks have been run. @@ -39,23 +36,31 @@ public virtual void Setup(TContext context) /// /// The context. /// The teardown information. - public virtual void Teardown(TContext context, ITeardownContext info) - { - } + public abstract void Teardown(TContext context, ITeardownContext info); - [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:ElementsMustBeDocumented", Justification = "Explicit implementation.")] - void IFrostingLifetime.Setup(ICakeContext context) + /// + void IFrostingSetup.Setup(ICakeContext context) { - Guard.ArgumentNotNull(context, nameof(context)); + if (context is null) + { + throw new ArgumentNullException(nameof(context)); + } Setup((TContext)context); } - [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:ElementsMustBeDocumented", Justification = "Explicit implementation.")] - void IFrostingLifetime.Teardown(ICakeContext context, ITeardownContext info) + /// + void IFrostingTeardown.Teardown(ICakeContext context, ITeardownContext info) { - Guard.ArgumentNotNull(context, nameof(context)); - Guard.ArgumentNotNull(info, nameof(info)); + if (context is null) + { + throw new ArgumentNullException(nameof(context)); + } + + if (info is null) + { + throw new ArgumentNullException(nameof(info)); + } Teardown((TContext)context, info); } diff --git a/src/Cake.Frosting/FrostingSetup.cs b/src/Cake.Frosting/FrostingSetup.cs new file mode 100644 index 0000000000..b13886ab91 --- /dev/null +++ b/src/Cake.Frosting/FrostingSetup.cs @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using Cake.Core; +using Cake.Frosting; + +namespace Cake.Frosting +{ + /// + /// Base class for setup logic. + /// + public abstract class FrostingSetup : FrostingSetup + { + } + + /// + /// Base class for setup logic. + /// + /// The build context type. + public abstract class FrostingSetup : IFrostingSetup + where TContext : ICakeContext + { + /// + /// This method is executed before any tasks are run. + /// If setup fails, no tasks will be executed but teardown will be performed. + /// + /// The context. + public abstract void Setup(TContext context); + + void IFrostingSetup.Setup(ICakeContext context) + { + if (context is null) + { + throw new ArgumentNullException(nameof(context)); + } + + Setup((TContext)context); + } + } +} diff --git a/src/Cake.Frosting/FrostingTask.cs b/src/Cake.Frosting/FrostingTask.cs index 69b8e0836e..7e892f97e0 100644 --- a/src/Cake.Frosting/FrostingTask.cs +++ b/src/Cake.Frosting/FrostingTask.cs @@ -3,10 +3,8 @@ // See the LICENSE file in the project root for more information. using System; -using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; using Cake.Core; -using Cake.Frosting.Internal; namespace Cake.Frosting { @@ -63,36 +61,52 @@ public virtual void Finally(T context) { } - [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:ElementsMustBeDocumented", Justification = "Explicit implementation.")] + /// Task IFrostingTask.RunAsync(ICakeContext context) { - Guard.ArgumentNotNull(context, nameof(context)); + if (context is null) + { + throw new ArgumentNullException(nameof(context)); + } Run((T)context); return Task.CompletedTask; } - [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:ElementsMustBeDocumented", Justification = "Explicit implementation.")] + /// bool IFrostingTask.ShouldRun(ICakeContext context) { - Guard.ArgumentNotNull(context, nameof(context)); + if (context is null) + { + throw new ArgumentNullException(nameof(context)); + } return ShouldRun((T)context); } - [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:ElementsMustBeDocumented", Justification = "Explicit implementation.")] + /// void IFrostingTask.OnError(Exception exception, ICakeContext context) { - Guard.ArgumentNotNull(exception, nameof(exception)); - Guard.ArgumentNotNull(context, nameof(context)); + if (exception is null) + { + throw new ArgumentNullException(nameof(exception)); + } + + if (context is null) + { + throw new ArgumentNullException(nameof(context)); + } OnError(exception, (T)context); } - [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:ElementsMustBeDocumented", Justification = "Explicit implementation.")] + /// void IFrostingTask.Finally(ICakeContext context) { - Guard.ArgumentNotNull(context, nameof(context)); + if (context is null) + { + throw new ArgumentNullException(nameof(context)); + } Finally((T)context); } diff --git a/src/Cake.Frosting/FrostingTaskLifetime.cs b/src/Cake.Frosting/FrostingTaskLifetime.cs index 09200a26c9..103ae868f1 100644 --- a/src/Cake.Frosting/FrostingTaskLifetime.cs +++ b/src/Cake.Frosting/FrostingTaskLifetime.cs @@ -2,9 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Diagnostics.CodeAnalysis; using Cake.Core; -using Cake.Frosting.Internal; namespace Cake.Frosting { @@ -19,7 +17,7 @@ public abstract class FrostingTaskLifetime : FrostingTaskLifetime /// /// Base class for the lifetime for a task. /// - /// The build script context type. + /// The build context type. /// public abstract class FrostingTaskLifetime : IFrostingTaskLifetime where TContext : ICakeContext @@ -30,9 +28,7 @@ public abstract class FrostingTaskLifetime : IFrostingTaskLifetime /// /// The context. /// The setup information. - public virtual void Setup(TContext context, ITaskSetupContext info) - { - } + public abstract void Setup(TContext context, ITaskSetupContext info); /// /// This method is executed after each task have been run. @@ -40,24 +36,36 @@ public virtual void Setup(TContext context, ITaskSetupContext info) /// /// The context. /// The teardown information. - public virtual void Teardown(TContext context, ITaskTeardownContext info) - { - } + public abstract void Teardown(TContext context, ITaskTeardownContext info); - [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:ElementsMustBeDocumented", Justification = "Explicit implementation.")] - void IFrostingTaskLifetime.Setup(ICakeContext context, ITaskSetupContext info) + /// + void IFrostingTaskSetup.Setup(ICakeContext context, ITaskSetupContext info) { - Guard.ArgumentNotNull(context, nameof(context)); - Guard.ArgumentNotNull(info, nameof(info)); + if (context is null) + { + throw new System.ArgumentNullException(nameof(context)); + } + + if (info is null) + { + throw new System.ArgumentNullException(nameof(info)); + } Setup((TContext)context, info); } - [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:ElementsMustBeDocumented", Justification = "Explicit implementation.")] - void IFrostingTaskLifetime.Teardown(ICakeContext context, ITaskTeardownContext info) + /// + void IFrostingTaskTeardown.Teardown(ICakeContext context, ITaskTeardownContext info) { - Guard.ArgumentNotNull(context, nameof(context)); - Guard.ArgumentNotNull(info, nameof(info)); + if (context is null) + { + throw new System.ArgumentNullException(nameof(context)); + } + + if (info is null) + { + throw new System.ArgumentNullException(nameof(info)); + } Teardown((TContext)context, info); } diff --git a/src/Cake.Frosting/FrostingTaskSetup.cs b/src/Cake.Frosting/FrostingTaskSetup.cs new file mode 100644 index 0000000000..4d82f3f1e4 --- /dev/null +++ b/src/Cake.Frosting/FrostingTaskSetup.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using Cake.Core; +using Cake.Frosting; + +namespace Cake.Frosting +{ + /// + /// Base class for the setup logic of a task. + /// + public abstract class FrostingTaskSetup : FrostingTaskSetup + { + } + + /// + /// Base class for the setup logic of a task. + /// + /// The build context type. + public abstract class FrostingTaskSetup : IFrostingTaskSetup + where TContext : ICakeContext + { + /// + /// This method is executed before any tasks are run. + /// If setup fails, no tasks will be executed but teardown will be performed. + /// + /// The context. + /// The setup information. + public abstract void Setup(TContext context, ITaskSetupContext info); + + void IFrostingTaskSetup.Setup(ICakeContext context, ITaskSetupContext info) + { + if (context is null) + { + throw new ArgumentNullException(nameof(context)); + } + + Setup((TContext)context, info); + } + } +} diff --git a/src/Cake.Frosting/FrostingTaskTeardown.cs b/src/Cake.Frosting/FrostingTaskTeardown.cs new file mode 100644 index 0000000000..146fdc55f1 --- /dev/null +++ b/src/Cake.Frosting/FrostingTaskTeardown.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using Cake.Core; +using Cake.Frosting; + +namespace Cake.Frosting +{ + /// + /// Base class for the teardown logic of a task. + /// + public abstract class FrostingTaskTeardown : FrostingTaskTeardown + { + } + + /// + /// Base class for the teardown logic of a task. + /// + /// The build context type. + public abstract class FrostingTaskTeardown : IFrostingTaskTeardown + where TContext : ICakeContext + { + /// + /// This method is executed after each task have been run. + /// If a task setup action or a task fails with or without recovery, the specified task teardown action will still be executed. + /// + /// The context. + /// The teardown information. + public abstract void Teardown(TContext context, ITaskTeardownContext info); + + void IFrostingTaskTeardown.Teardown(ICakeContext context, ITaskTeardownContext info) + { + if (context is null) + { + throw new ArgumentNullException(nameof(context)); + } + + Teardown((TContext)context, info); + } + } +} diff --git a/src/Cake.Frosting/FrostingTeardown.cs b/src/Cake.Frosting/FrostingTeardown.cs new file mode 100644 index 0000000000..3a4e6c26a0 --- /dev/null +++ b/src/Cake.Frosting/FrostingTeardown.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using Cake.Core; +using Cake.Frosting; + +namespace Cake.Frosting +{ + /// + /// Base class for teardown logic. + /// + public abstract class FrostingTeardown : FrostingTeardown + { + } + + /// + /// Base class for teardown logic. + /// + /// The build context type. + public abstract class FrostingTeardown : IFrostingTeardown + where TContext : ICakeContext + { + /// + /// This method is executed before any tasks are run. + /// If setup fails, no tasks will be executed but teardown will be performed. + /// + /// The context. + /// The teardown information. + public abstract void Teardown(TContext context, ITeardownContext info); + + void IFrostingTeardown.Teardown(ICakeContext context, ITeardownContext info) + { + if (context is null) + { + throw new ArgumentNullException(nameof(context)); + } + + Teardown((TContext)context, info); + } + } +} diff --git a/src/Cake.Frosting/Abstractions/IFrostingContext.cs b/src/Cake.Frosting/IFrostingContext.cs similarity index 91% rename from src/Cake.Frosting/Abstractions/IFrostingContext.cs rename to src/Cake.Frosting/IFrostingContext.cs index 4d016c7eab..85d7d2b47e 100644 --- a/src/Cake.Frosting/Abstractions/IFrostingContext.cs +++ b/src/Cake.Frosting/IFrostingContext.cs @@ -4,7 +4,6 @@ using Cake.Core; -// ReSharper disable once CheckNamespace namespace Cake.Frosting { /// diff --git a/src/Cake.Frosting/Abstractions/IFrostingLifetime.cs b/src/Cake.Frosting/IFrostingLifetime.cs similarity index 77% rename from src/Cake.Frosting/Abstractions/IFrostingLifetime.cs rename to src/Cake.Frosting/IFrostingLifetime.cs index 8ba155f4bb..72de900fa3 100644 --- a/src/Cake.Frosting/Abstractions/IFrostingLifetime.cs +++ b/src/Cake.Frosting/IFrostingLifetime.cs @@ -4,13 +4,12 @@ using Cake.Core; -// ReSharper disable once CheckNamespace namespace Cake.Frosting { /// - /// Represents the Setup/Teardown logic for a Cake run. + /// Represents setup logic. /// - public interface IFrostingLifetime + public interface IFrostingSetup { /// /// This method is executed before any tasks are run. @@ -18,7 +17,13 @@ public interface IFrostingLifetime /// /// The context. void Setup(ICakeContext context); + } + /// + /// Represents teardown logic. + /// + public interface IFrostingTeardown + { /// /// This method is executed after all tasks have been run. /// If a setup action or a task fails with or without recovery, the specified teardown action will still be executed. @@ -27,4 +32,11 @@ public interface IFrostingLifetime /// The teardown information. void Teardown(ICakeContext context, ITeardownContext info); } + + /// + /// Represents the Setup/Teardown logic for a Cake run. + /// + public interface IFrostingLifetime : IFrostingSetup, IFrostingTeardown + { + } } diff --git a/src/Cake.Frosting/Abstractions/IFrostingStartup.cs b/src/Cake.Frosting/IFrostingStartup.cs similarity index 83% rename from src/Cake.Frosting/Abstractions/IFrostingStartup.cs rename to src/Cake.Frosting/IFrostingStartup.cs index 0e1cb8cfd0..c0771b064e 100644 --- a/src/Cake.Frosting/Abstractions/IFrostingStartup.cs +++ b/src/Cake.Frosting/IFrostingStartup.cs @@ -2,7 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -// ReSharper disable once CheckNamespace +using Microsoft.Extensions.DependencyInjection; + namespace Cake.Frosting { /// @@ -14,6 +15,6 @@ public interface IFrostingStartup /// Configures services used by Cake. /// /// The services to configure. - void Configure(ICakeServices services); + void Configure(IServiceCollection services); } } \ No newline at end of file diff --git a/src/Cake.Frosting/Abstractions/IFrostingTask.cs b/src/Cake.Frosting/IFrostingTask.cs similarity index 97% rename from src/Cake.Frosting/Abstractions/IFrostingTask.cs rename to src/Cake.Frosting/IFrostingTask.cs index 3061159d26..2517b4107f 100644 --- a/src/Cake.Frosting/Abstractions/IFrostingTask.cs +++ b/src/Cake.Frosting/IFrostingTask.cs @@ -6,7 +6,6 @@ using System.Threading.Tasks; using Cake.Core; -// ReSharper disable once CheckNamespace namespace Cake.Frosting { /// diff --git a/src/Cake.Frosting/Abstractions/IFrostingTaskLifetime.cs b/src/Cake.Frosting/IFrostingTaskLifetime.cs similarity index 77% rename from src/Cake.Frosting/Abstractions/IFrostingTaskLifetime.cs rename to src/Cake.Frosting/IFrostingTaskLifetime.cs index f76a585f55..73d7b283ca 100644 --- a/src/Cake.Frosting/Abstractions/IFrostingTaskLifetime.cs +++ b/src/Cake.Frosting/IFrostingTaskLifetime.cs @@ -4,13 +4,12 @@ using Cake.Core; -// ReSharper disable once CheckNamespace namespace Cake.Frosting { /// - /// Represents the lifetime for all tasks. + /// Represents a task setup action. /// - public interface IFrostingTaskLifetime + public interface IFrostingTaskSetup { /// /// This method is executed before each task is run. @@ -19,7 +18,13 @@ public interface IFrostingTaskLifetime /// The context. /// The setup information. void Setup(ICakeContext context, ITaskSetupContext info); + } + /// + /// Represents a task teardown action. + /// + public interface IFrostingTaskTeardown + { /// /// This method is executed after each task have been run. /// If a task setup action or a task fails with or without recovery, the specified task teardown action will still be executed. @@ -28,4 +33,11 @@ public interface IFrostingTaskLifetime /// The teardown information. void Teardown(ICakeContext context, ITaskTeardownContext info); } + + /// + /// Represents the lifetime for all tasks. + /// + public interface IFrostingTaskLifetime : IFrostingTaskSetup, IFrostingTaskTeardown + { + } } \ No newline at end of file diff --git a/src/Cake.Frosting/Abstractions/IToolInstaller.cs b/src/Cake.Frosting/IToolInstaller.cs similarity index 94% rename from src/Cake.Frosting/Abstractions/IToolInstaller.cs rename to src/Cake.Frosting/IToolInstaller.cs index 5b172a0866..26ed662e6d 100644 --- a/src/Cake.Frosting/Abstractions/IToolInstaller.cs +++ b/src/Cake.Frosting/IToolInstaller.cs @@ -4,7 +4,6 @@ using Cake.Core.Packaging; -// ReSharper disable once CheckNamespace namespace Cake.Frosting { /// diff --git a/src/Cake.Frosting/Internal/Arguments/ArgumentParser.cs b/src/Cake.Frosting/Internal/Arguments/ArgumentParser.cs deleted file mode 100644 index ea05e7610a..0000000000 --- a/src/Cake.Frosting/Internal/Arguments/ArgumentParser.cs +++ /dev/null @@ -1,143 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using Cake.Core; -using Cake.Core.Diagnostics; -using Cake.Core.IO; - -namespace Cake.Frosting.Internal.Arguments -{ - internal static class ArgumentParser - { - public static CakeHostOptions Parse(IEnumerable args) - { - if (args == null) - { - throw new ArgumentNullException(nameof(args)); - } - - var options = new CakeHostOptions(); - - foreach (var argument in args) - { - ParseOption(argument.UnQuote(), options); - } - - return options; - } - - private static void ParseOption(string arg, CakeHostOptions options) - { - if (!IsOption(arg)) - { - throw new FrostingException($"Encountered invalid option '{arg}'"); - } - - string name, value; - - var nameIndex = arg.StartsWith("--") ? 2 : 1; - var separatorIndex = arg.IndexOfAny(new[] { '=' }); - if (separatorIndex < 0) - { - name = arg.Substring(nameIndex); - value = string.Empty; - } - else - { - name = arg.Substring(nameIndex, separatorIndex - nameIndex); - value = arg.Substring(separatorIndex + 1); - } - - ParseOption(name, value.UnQuote(), options); - } - - private static void ParseOption(string name, string value, CakeHostOptions options) - { - if (name.Equals("working", StringComparison.OrdinalIgnoreCase) || - name.Equals("w", StringComparison.OrdinalIgnoreCase)) - { - options.WorkingDirectory = new DirectoryPath(value); - } - - if (name.Equals("verbosity", StringComparison.OrdinalIgnoreCase) - || name.Equals("v", StringComparison.OrdinalIgnoreCase)) - { - Verbosity verbosity; - if (!VerbosityParser.TryParse(value, out verbosity)) - { - throw new CakeException($"The value '{value}' is not a valid verbosity."); - } - options.Verbosity = verbosity; - } - - if (name.Equals("target", StringComparison.OrdinalIgnoreCase) || - name.Equals("t", StringComparison.OrdinalIgnoreCase)) - { - options.Target = value; - } - - if (name.Equals("help", StringComparison.OrdinalIgnoreCase) || - name.Equals("h", StringComparison.OrdinalIgnoreCase)) - { - if (ParseBooleanValue(value)) - { - options.Command = CakeHostCommand.Help; - } - } - - if (name.Equals("dryrun", StringComparison.OrdinalIgnoreCase) || - name.Equals("d", StringComparison.OrdinalIgnoreCase)) - { - if (ParseBooleanValue(value)) - { - options.Command = CakeHostCommand.DryRun; - } - } - - if (name.Equals("version", StringComparison.OrdinalIgnoreCase)) - { - if (ParseBooleanValue(value)) - { - options.Command = CakeHostCommand.Version; - } - } - - if (options.Arguments.ContainsKey(name)) - { - throw new CakeException($"More than one argument called '{name}' was encountered."); - } - - options.Arguments.Add(name, value); - } - - private static bool IsOption(string arg) - { - if (string.IsNullOrWhiteSpace(arg)) - { - return false; - } - return arg.StartsWith("--") || arg.StartsWith("-"); - } - - private static bool ParseBooleanValue(string value) - { - value = (value ?? string.Empty).UnQuote(); - if (string.IsNullOrWhiteSpace(value)) - { - return true; - } - if (value.Equals("true", StringComparison.OrdinalIgnoreCase)) - { - return true; - } - if (value.Equals("false", StringComparison.OrdinalIgnoreCase)) - { - return false; - } - throw new CakeException($"Argument value '{value}' is not a valid boolean value."); - } - } -} \ No newline at end of file diff --git a/src/Cake.Frosting/Internal/Arguments/VerbosityParser.cs b/src/Cake.Frosting/Internal/Arguments/VerbosityParser.cs deleted file mode 100644 index 07dd71633c..0000000000 --- a/src/Cake.Frosting/Internal/Arguments/VerbosityParser.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using Cake.Core.Diagnostics; - -namespace Cake.Frosting.Internal.Arguments -{ - internal static class VerbosityParser - { - private static readonly Dictionary Lookup; - - static VerbosityParser() - { - Lookup = new Dictionary(StringComparer.OrdinalIgnoreCase) - { - { "q", Verbosity.Quiet }, - { "quiet", Verbosity.Quiet }, - { "m", Verbosity.Minimal }, - { "minimal", Verbosity.Minimal }, - { "n", Verbosity.Normal }, - { "normal", Verbosity.Normal }, - { "v", Verbosity.Verbose }, - { "verbose", Verbosity.Verbose }, - { "d", Verbosity.Diagnostic }, - { "diagnostic", Verbosity.Diagnostic } - }; - } - - public static bool TryParse(string value, out Verbosity verbosity) - { - return Lookup.TryGetValue(value, out verbosity); - } - } -} diff --git a/src/Cake.Frosting/Internal/Commands/Command.cs b/src/Cake.Frosting/Internal/Commands/Command.cs deleted file mode 100644 index a674607e32..0000000000 --- a/src/Cake.Frosting/Internal/Commands/Command.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Threading.Tasks; -using Cake.Core; - -namespace Cake.Frosting.Internal.Commands -{ - internal abstract class Command - { - public abstract Task ExecuteAsync(ICakeEngine engine, CakeHostOptions options); - } -} diff --git a/src/Cake.Frosting/Internal/Commands/CommandFactory.cs b/src/Cake.Frosting/Internal/Commands/CommandFactory.cs deleted file mode 100644 index e929a39a46..0000000000 --- a/src/Cake.Frosting/Internal/Commands/CommandFactory.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Cake.Frosting.Internal.Composition; - -namespace Cake.Frosting.Internal.Commands -{ - internal sealed class CommandFactory - { - private readonly Container _scope; - - public CommandFactory(Container scope) - { - _scope = scope; - } - - public Command GetCommand(CakeHostOptions options) - { - switch (options.Command) - { - case CakeHostCommand.Help: - return _scope.Resolve(); - case CakeHostCommand.DryRun: - return _scope.Resolve(); - case CakeHostCommand.Version: - return _scope.Resolve(); - case CakeHostCommand.Run: - return _scope.Resolve(); - default: - return new ErrorDecoratorCommand(_scope.Resolve()); - } - } - } -} diff --git a/src/Cake.Frosting/Internal/Commands/DefaultCommand.cs b/src/Cake.Frosting/Internal/Commands/DefaultCommand.cs new file mode 100644 index 0000000000..2a7cde020c --- /dev/null +++ b/src/Cake.Frosting/Internal/Commands/DefaultCommand.cs @@ -0,0 +1,154 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Linq; +using Cake.Cli; +using Cake.Core; +using Cake.Core.Diagnostics; +using Cake.Core.IO; +using Cake.Core.Packaging; +using Microsoft.Extensions.DependencyInjection; +using Spectre.Cli; + +namespace Cake.Frosting.Internal +{ + internal sealed class DefaultCommand : Command + { + private readonly IServiceCollection _services; + + public DefaultCommand(IServiceCollection services) + { + _services = services ?? throw new ArgumentNullException(nameof(services)); + } + + public override int Execute(CommandContext context, DefaultCommandSettings settings) + { + // Register arguments + var arguments = new CakeArguments(context.Remaining.Parsed); + _services.AddSingleton(arguments); + + var provider = _services.BuildServiceProvider(); + + try + { + if (settings.Version) + { + // Show version + var console = provider.GetRequiredService(); + provider.GetRequiredService().Run(console); + return 0; + } + else if (settings.Info) + { + // Show information + var console = provider.GetRequiredService(); + provider.GetRequiredService().Run(console); + return 0; + } + + // Install tools + InstallTools(provider); + + // Run + var runner = GetFrostingEngine(provider, settings); + + // Set the working directory + SetWorkingDirectory(provider, settings); + + if (settings.Exclusive) + { + runner.Settings.UseExclusiveTarget(); + } + + runner.Run(settings.Target, settings.Verbosity, settings.WorkingDirectory); + } + catch (Exception ex) + { + LogException(provider.GetService(), ex); + return -1; + } + + return 0; + } + + private static int LogException(ICakeLog log, T ex) + where T : Exception + { + log = log ?? new CakeBuildLog( + new CakeConsole(new CakeEnvironment(new CakePlatform(), new CakeRuntime()))); + + if (log.Verbosity == Verbosity.Diagnostic) + { + log.Error("Error: {0}", ex); + } + else + { + log.Error("Error: {0}", ex.Message); + if (ex is AggregateException aex) + { + foreach (var exception in aex.Flatten().InnerExceptions) + { + log.Error("\t{0}", exception.Message); + } + } + } + + return 1; + } + + private void InstallTools(ServiceProvider provider) + { + var installer = provider.GetRequiredService(); + var tools = provider.GetServices(); + var log = provider.GetService(); + + // Install tools. + if (tools.Any()) + { + log.Verbose("Installing tools..."); + foreach (var tool in tools) + { + installer.Install(tool); + } + } + } + + private void SetWorkingDirectory(ServiceProvider provider, DefaultCommandSettings settings) + { + var fileSystem = provider.GetRequiredService(); + var environment = provider.GetRequiredService(); + + var directory = settings.WorkingDirectory ?? provider.GetService()?.Path; + directory = directory?.MakeAbsolute(environment) ?? environment.WorkingDirectory; + + if (!fileSystem.Exist(directory)) + { + throw new FrostingException($"The working directory '{directory.FullPath}' does not exist."); + } + + environment.WorkingDirectory = directory; + } + + private IFrostingEngine GetFrostingEngine(ServiceProvider provider, DefaultCommandSettings settings) + { + if (settings.DryRun) + { + return provider.GetRequiredService(); + } + else if (settings.Tree) + { + return provider.GetRequiredService(); + } + else if (settings.Descriptions) + { + return provider.GetRequiredService(); + } + else + { + return provider.GetRequiredService(); + } + } + } +} diff --git a/src/Cake.Frosting/Internal/Commands/DefaultCommandSettings.cs b/src/Cake.Frosting/Internal/Commands/DefaultCommandSettings.cs new file mode 100644 index 0000000000..9e1f994170 --- /dev/null +++ b/src/Cake.Frosting/Internal/Commands/DefaultCommandSettings.cs @@ -0,0 +1,55 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.ComponentModel; +using Cake.Cli; +using Cake.Core.Diagnostics; +using Cake.Core.IO; +using Spectre.Cli; + +namespace Cake.Frosting.Internal +{ + internal sealed class DefaultCommandSettings : CommandSettings + { + [CommandOption("--target|-t ")] + [DefaultValue("Default")] + [Description("Target task to invoke.")] + public string Target { get; set; } + + [CommandOption("-e|--exclusive")] + [Description("Executes the target task without any dependencies.")] + public bool Exclusive { get; set; } + + [CommandOption("--working|-w ")] + [TypeConverter(typeof(DirectoryPathConverter))] + [Description("Sets the working directory")] + public DirectoryPath WorkingDirectory { get; set; } + + [CommandOption("--verbosity|-v ")] + [TypeConverter(typeof(VerbosityConverter))] + [DefaultValue(Verbosity.Normal)] + [Description("Specifies the amount of information to be displayed.\n(Quiet, Minimal, Normal, Verbose, Diagnostic)")] + public Verbosity Verbosity { get; set; } + + [CommandOption("--dryrun|--noop|--whatif")] + [Description("Performs a dry run.")] + public bool DryRun { get; set; } + + [CommandOption("--tree")] + [Description("Shows the task dependency tree.")] + public bool Tree { get; set; } + + [CommandOption("--descriptions")] + [Description("Shows task descriptions.")] + public bool Descriptions { get; set; } + + [CommandOption("--version")] + [Description("Displays version information.")] + public bool Version { get; set; } + + [CommandOption("--info")] + [Description("Displays additional information about Cake.")] + public bool Info { get; set; } + } +} diff --git a/src/Cake.Frosting/Internal/Commands/DryRunCommand.cs b/src/Cake.Frosting/Internal/Commands/DryRunCommand.cs deleted file mode 100644 index 5da5a1716b..0000000000 --- a/src/Cake.Frosting/Internal/Commands/DryRunCommand.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Threading.Tasks; -using Cake.Core; -using Cake.Core.Diagnostics; - -namespace Cake.Frosting.Internal.Commands -{ - internal sealed class DryRunCommand : Command - { - private readonly IFrostingContext _context; - private readonly ICakeLog _log; - private readonly ExecutionSettings _executionSettings; - - public DryRunCommand(IFrostingContext context, ICakeLog log) - { - _context = context; - _log = log; - _executionSettings = new ExecutionSettings(); - } - - public override async Task ExecuteAsync(ICakeEngine engine, CakeHostOptions options) - { - _executionSettings.SetTarget(options.Target); - - _log.Information("Performing dry run..."); - _log.Information("Target is: {0}", options.Target); - _log.Information(string.Empty); - - var strategy = new DryRunExecutionStrategy(_log); - await engine.RunTargetAsync(_context, strategy, _executionSettings).ConfigureAwait(false); - - _log.Information(string.Empty); - _log.Information("This was a dry run."); - _log.Information("No tasks were actually executed."); - - return true; - } - } -} diff --git a/src/Cake.Frosting/Internal/Commands/DryRunExecutionStrategy.cs b/src/Cake.Frosting/Internal/Commands/DryRunExecutionStrategy.cs deleted file mode 100644 index 5a4410f217..0000000000 --- a/src/Cake.Frosting/Internal/Commands/DryRunExecutionStrategy.cs +++ /dev/null @@ -1,81 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Threading.Tasks; -using Cake.Core; -using Cake.Core.Diagnostics; - -namespace Cake.Frosting.Internal.Commands -{ - internal sealed class DryRunExecutionStrategy : IExecutionStrategy - { - private readonly ICakeLog _log; - private int _counter; - - public DryRunExecutionStrategy(ICakeLog log) - { - _log = log ?? throw new ArgumentNullException(nameof(log)); - _counter = 1; - } - - public void PerformSetup(Action action, ISetupContext context) - { - } - - public void PerformTeardown(Action action, ITeardownContext teardownContext) - { - } - - public Task ExecuteAsync(CakeTask task, ICakeContext context) - { - if (task != null) - { - _log.Information("{0}. {1}", _counter, task.Name); - _counter++; - } - - return Task.CompletedTask; - } - - public void Skip(CakeTask task, CakeTaskCriteria criteria) - { - } - - public void ReportErrors(Action action, Exception exception) - { - } - - public void HandleErrors(Action action, Exception exception, ICakeContext context) - { - } - - public void InvokeFinally(Action action) - { - } - - public void PerformTaskSetup(Action action, ITaskSetupContext taskSetupContext) - { - } - - public void PerformTaskTeardown(Action action, ITaskTeardownContext taskTeardownContext) - { - } - - public Task ReportErrorsAsync(Func action, Exception exception) - { - return Task.CompletedTask; - } - - public Task HandleErrorsAsync(Func action, Exception exception, ICakeContext context) - { - return Task.CompletedTask; - } - - public Task InvokeFinallyAsync(Func action) - { - return Task.CompletedTask; - } - } -} diff --git a/src/Cake.Frosting/Internal/Commands/ErrorDecoratorCommand.cs b/src/Cake.Frosting/Internal/Commands/ErrorDecoratorCommand.cs deleted file mode 100644 index 9314f35d6e..0000000000 --- a/src/Cake.Frosting/Internal/Commands/ErrorDecoratorCommand.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Threading.Tasks; -using Cake.Core; - -namespace Cake.Frosting.Internal.Commands -{ - internal sealed class ErrorDecoratorCommand : Command - { - private readonly Command _command; - - public ErrorDecoratorCommand(Command command) - { - _command = command; - } - - public override async Task ExecuteAsync(ICakeEngine engine, CakeHostOptions options) - { - await _command.ExecuteAsync(engine, options).ConfigureAwait(false); - return false; - } - } -} diff --git a/src/Cake.Frosting/Internal/Commands/HelpCommand.cs b/src/Cake.Frosting/Internal/Commands/HelpCommand.cs deleted file mode 100644 index a57e34b288..0000000000 --- a/src/Cake.Frosting/Internal/Commands/HelpCommand.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Reflection; -using System.Threading.Tasks; -using Cake.Core; - -namespace Cake.Frosting.Internal.Commands -{ - internal sealed class HelpCommand : Command - { - private readonly IConsole _console; - - public HelpCommand(IConsole console) - { - _console = console; - } - - public override Task ExecuteAsync(ICakeEngine engine, CakeHostOptions options) - { - _console.Write("Cake.Frosting ("); - _console.ForegroundColor = ConsoleColor.Yellow; - _console.Write(typeof(HelpCommand).GetTypeInfo().Assembly.GetName().Version.ToString(3)); - _console.ResetColor(); - _console.WriteLine(")"); - - _console.WriteLine("Usage:"); - _console.WriteLine(" dotnet {0}.dll [options]", typeof(HelpCommand).GetTypeInfo().Assembly.GetName().Name); - _console.WriteLine(); - _console.WriteLine("Options:"); - _console.WriteLine(" --target|-t Sets the build target"); - _console.WriteLine(" --working|-w Sets the working directory"); - _console.WriteLine(" --verbosity|-v Sets the verbosity"); - _console.WriteLine(" --dryrun|-r Performs a dry run"); - _console.WriteLine(" --version Displays Cake.Frosting version number"); - _console.WriteLine(" --help|-h Show help"); - _console.WriteLine(); - - return Task.FromResult(true); - } - } -} diff --git a/src/Cake.Frosting/Internal/Commands/RunCommand.cs b/src/Cake.Frosting/Internal/Commands/RunCommand.cs deleted file mode 100644 index abe3fac86b..0000000000 --- a/src/Cake.Frosting/Internal/Commands/RunCommand.cs +++ /dev/null @@ -1,40 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Threading.Tasks; -using Cake.Core; - -namespace Cake.Frosting.Internal.Commands -{ - internal sealed class RunCommand : Command - { - private readonly IFrostingContext _context; - private readonly IExecutionStrategy _strategy; - private readonly ICakeReportPrinter _printer; - private readonly ExecutionSettings _executionSettings; - - public RunCommand( - IFrostingContext context, - IExecutionStrategy strategy, - ICakeReportPrinter printer) - { - _context = context; - _strategy = strategy; - _printer = printer; - _executionSettings = new ExecutionSettings(); - } - - public override async Task ExecuteAsync(ICakeEngine engine, CakeHostOptions options) - { - _executionSettings.SetTarget(options.Target); - var report = await engine.RunTargetAsync(_context, _strategy, _executionSettings).ConfigureAwait(false); - if (report != null && !report.IsEmpty) - { - _printer.Write(report); - } - - return true; - } - } -} diff --git a/src/Cake.Frosting/Internal/Commands/VersionCommand.cs b/src/Cake.Frosting/Internal/Commands/VersionCommand.cs deleted file mode 100644 index a511d1e830..0000000000 --- a/src/Cake.Frosting/Internal/Commands/VersionCommand.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using System.Threading.Tasks; -using Cake.Core; - -namespace Cake.Frosting.Internal.Commands -{ - internal sealed class VersionCommand : Command - { - private readonly IConsole _console; - - public VersionCommand(IConsole console) - { - _console = console; - } - - public override Task ExecuteAsync(ICakeEngine engine, CakeHostOptions options) - { - _console.Write(typeof(HelpCommand).GetTypeInfo().Assembly.GetName().Version.ToString(3)); - return Task.FromResult(true); - } - } -} diff --git a/src/Cake.Frosting/Internal/Composition/Activator.cs b/src/Cake.Frosting/Internal/Composition/Activator.cs deleted file mode 100644 index 424e0a757a..0000000000 --- a/src/Cake.Frosting/Internal/Composition/Activator.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace Cake.Frosting.Internal.Composition -{ - internal abstract class Activator - { - public abstract object Activate(Container container); - - public abstract Activator CreateCopy(); - } -} \ No newline at end of file diff --git a/src/Cake.Frosting/Internal/Composition/Activators/CachingActivator.cs b/src/Cake.Frosting/Internal/Composition/Activators/CachingActivator.cs deleted file mode 100644 index 5ecb85db5d..0000000000 --- a/src/Cake.Frosting/Internal/Composition/Activators/CachingActivator.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace Cake.Frosting.Internal.Composition.Activators -{ - internal class CachingActivator : Activator - { - private readonly Activator _activator; - private object _result; - - public CachingActivator(Activator activator) - { - _activator = activator; - _result = null; - } - - public override object Activate(Container container) - { - return _result ?? (_result = _activator.Activate(container)); - } - - public override Activator CreateCopy() - { - return new CachingActivator(_activator.CreateCopy()); - } - } -} \ No newline at end of file diff --git a/src/Cake.Frosting/Internal/Composition/Activators/InstanceActivator.cs b/src/Cake.Frosting/Internal/Composition/Activators/InstanceActivator.cs deleted file mode 100644 index 4ae31f84d9..0000000000 --- a/src/Cake.Frosting/Internal/Composition/Activators/InstanceActivator.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace Cake.Frosting.Internal.Composition.Activators -{ - internal sealed class InstanceActivator : Activator - { - private readonly object _instance; - - public InstanceActivator(object instance) - { - _instance = instance; - } - - public override object Activate(Container container) - { - return _instance; - } - - public override Activator CreateCopy() - { - return new InstanceActivator(_instance); - } - } -} \ No newline at end of file diff --git a/src/Cake.Frosting/Internal/Composition/Activators/ReflectionActivator.cs b/src/Cake.Frosting/Internal/Composition/Activators/ReflectionActivator.cs deleted file mode 100644 index 4c0d27e4fb..0000000000 --- a/src/Cake.Frosting/Internal/Composition/Activators/ReflectionActivator.cs +++ /dev/null @@ -1,81 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.Reflection; - -namespace Cake.Frosting.Internal.Composition.Activators -{ - internal sealed class ReflectionActivator : Activator - { - private readonly Type _type; - private readonly ConstructorInfo _constructor; - private readonly List _parameters; - - public ReflectionActivator(Type type) - { - _type = type; - _constructor = GetGreediestConstructor(type); - _parameters = new List(); - - var parameters = _constructor.GetParameters(); - foreach (var parameter in parameters) - { - _parameters.Add(parameter); - } - } - - public override object Activate(Container container) - { - var parameters = new object[_parameters.Count]; - for (var i = 0; i < _parameters.Count; i++) - { - var parameter = _parameters[i]; - if (parameter.ParameterType == typeof(Container)) - { - parameters[i] = container; - } - else - { - var resolved = container.Resolve(parameter.ParameterType); - if (resolved == null) - { - if (!parameter.IsOptional) - { - throw new InvalidOperationException($"Could not find registration for '{parameter.ParameterType.FullName}'."); - } - parameters[i] = null; - } - else - { - parameters[i] = resolved; - } - } - } - return _constructor.Invoke(parameters); - } - - public override Activator CreateCopy() - { - return new ReflectionActivator(_type); - } - - private static ConstructorInfo GetGreediestConstructor(Type type) - { - ConstructorInfo current = null; - var count = -1; - foreach (var constructor in type.GetTypeInfo().GetConstructors()) - { - var parameters = constructor.GetParameters(); - if (parameters.Length > count) - { - count = parameters.Length; - current = constructor; - } - } - return current; - } - } -} \ No newline at end of file diff --git a/src/Cake.Frosting/Internal/Composition/ComponentRegistration.cs b/src/Cake.Frosting/Internal/Composition/ComponentRegistration.cs deleted file mode 100644 index cc153777b8..0000000000 --- a/src/Cake.Frosting/Internal/Composition/ComponentRegistration.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; - -namespace Cake.Frosting.Internal.Composition -{ - internal sealed class ComponentRegistration - { - public Type ImplementationType { get; } - - public List RegistrationTypes { get; } - - public bool Singleton { get; set; } - - public Activator Activator { get; set; } - - public ComponentRegistration(Type type) - { - ImplementationType = type; - RegistrationTypes = new List(); - Singleton = true; - Activator = null; - } - - public ComponentRegistration CreateCopy() - { - var registration = new ComponentRegistration(ImplementationType); - registration.RegistrationTypes.AddRange(RegistrationTypes); - registration.Singleton = Singleton; - registration.Activator = Activator?.CreateCopy(); - return registration; - } - } -} \ No newline at end of file diff --git a/src/Cake.Frosting/Internal/Composition/ComponentRegistry.cs b/src/Cake.Frosting/Internal/Composition/ComponentRegistry.cs deleted file mode 100644 index 399b5e3eef..0000000000 --- a/src/Cake.Frosting/Internal/Composition/ComponentRegistry.cs +++ /dev/null @@ -1,75 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.Linq; -using Cake.Frosting.Internal.Composition.Activators; - -namespace Cake.Frosting.Internal.Composition -{ - internal sealed class ComponentRegistry : IDisposable - { - private readonly Dictionary> _registrations; - - public ComponentRegistry() - { - _registrations = new Dictionary>(); - } - - public ComponentRegistry CreateCopy() - { - var registry = new ComponentRegistry(); - foreach (var registration in _registrations.SelectMany(p => p.Value)) - { - registry.Register(registration.CreateCopy()); - } - return registry; - } - - public void Dispose() - { - foreach (var registration in _registrations) - { - registration.Value.Clear(); - } - _registrations.Clear(); - } - - public void Register(ComponentRegistration registration) - { - var types = new HashSet(registration.RegistrationTypes); - if (types.Count == 0) - { - // Make sure that each registration have at least one registration type. - registration.RegistrationTypes.Add(registration.ImplementationType); - types.Add(registration.ImplementationType); - } - - if (registration.Singleton) - { - // Cache singletons after first resolve. - registration.Activator = new CachingActivator(registration.Activator); - } - - foreach (var type in types) - { - if (!_registrations.ContainsKey(type)) - { - _registrations.Add(type, new HashSet()); - } - _registrations[type].Add(registration); - } - } - - public ICollection GetRegistrations(Type type) - { - if (_registrations.ContainsKey(type)) - { - return _registrations[type]; - } - return new List(); - } - } -} \ No newline at end of file diff --git a/src/Cake.Frosting/Internal/Composition/Container.cs b/src/Cake.Frosting/Internal/Composition/Container.cs deleted file mode 100644 index 906d7c8664..0000000000 --- a/src/Cake.Frosting/Internal/Composition/Container.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; - -namespace Cake.Frosting.Internal.Composition -{ - internal sealed class Container : IDisposable - { - public ComponentRegistry Registry { get; } - - public Container() - : this(null) - { - } - - public Container(ComponentRegistry registry) - { - Registry = registry ?? new ComponentRegistry(); - } - - public void Dispose() - { - Registry.Dispose(); - } - - public Container CreateChildScope() - { - return new Container(Registry.CreateCopy()); - } - - public object Resolve(ComponentRegistration registration) - { - return registration?.Activator.Activate(this); - } - } -} \ No newline at end of file diff --git a/src/Cake.Frosting/Internal/Composition/ContainerBuilder.cs b/src/Cake.Frosting/Internal/Composition/ContainerBuilder.cs deleted file mode 100644 index 7cc5603e2b..0000000000 --- a/src/Cake.Frosting/Internal/Composition/ContainerBuilder.cs +++ /dev/null @@ -1,40 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; - -namespace Cake.Frosting.Internal.Composition -{ - internal sealed class ContainerBuilder - { - private readonly Queue> _registry; - - public ContainerBuilder() - { - _registry = new Queue>(); - } - - public void Register(Action action) - { - _registry.Enqueue(action); - } - - public Container Build() - { - var container = new Container(); - Update(container); - return container; - } - - public void Update(Container container) - { - while (_registry.Count > 0) - { - var action = _registry.Dequeue(); - action(container.Registry); - } - } - } -} diff --git a/src/Cake.Frosting/Internal/Composition/ContainerExtensions.cs b/src/Cake.Frosting/Internal/Composition/ContainerExtensions.cs deleted file mode 100644 index bda880d685..0000000000 --- a/src/Cake.Frosting/Internal/Composition/ContainerExtensions.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; - -// ReSharper disable once CheckNamespace -namespace Cake.Frosting.Internal.Composition -{ - internal static class ContainerExtensions - { - public static T Resolve(this Container container) - { - return (T)container.Resolve(typeof(T)); - } - - public static object Resolve(this Container container, Type type) - { - return ContainerResolver.Resolve(container, type); - } - - public static void Update(this Container container, CakeServices services) - { - services.Builder.Update(container); - } - } -} diff --git a/src/Cake.Frosting/Internal/Composition/ContainerResolver.cs b/src/Cake.Frosting/Internal/Composition/ContainerResolver.cs deleted file mode 100644 index 64239ebab4..0000000000 --- a/src/Cake.Frosting/Internal/Composition/ContainerResolver.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; - -namespace Cake.Frosting.Internal.Composition -{ - internal static class ContainerResolver - { - public static object Resolve(Container container, Type type) - { - var isEnumerable = false; - if (type.GetTypeInfo().IsGenericType) - { - if (type.GetGenericTypeDefinition() == typeof(IEnumerable<>)) - { - isEnumerable = true; - type = type.GenericTypeArguments[0]; - } - } - - var registrations = container.Registry.GetRegistrations(type); - if (registrations != null) - { - if (isEnumerable) - { - var result = Array.CreateInstance(type, registrations.Count); - for (var index = 0; index < registrations.Count; index++) - { - var registration = registrations.ElementAt(index); - result.SetValue(container.Resolve(registration), index); - } - return result; - } - } - - return container.Resolve(registrations?.LastOrDefault()); - } - } -} diff --git a/src/Cake.Frosting/Internal/Composition/RegistrationBuilder.cs b/src/Cake.Frosting/Internal/Composition/RegistrationBuilder.cs deleted file mode 100644 index e65f87d015..0000000000 --- a/src/Cake.Frosting/Internal/Composition/RegistrationBuilder.cs +++ /dev/null @@ -1,48 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Reflection; -using Cake.Core.Composition; - -namespace Cake.Frosting.Internal.Composition -{ - internal class RegistrationBuilder : ICakeRegistrationBuilder - { - private readonly ComponentRegistration _registration; - - public RegistrationBuilder(ComponentRegistration registration) - { - _registration = registration; - } - - public ICakeRegistrationBuilder As(Type type) - { - if (!type.GetTypeInfo().IsAssignableFrom(_registration.ImplementationType)) - { - throw new InvalidOperationException("Invalid registration."); - } - _registration.RegistrationTypes.Add(type); - return this; - } - - public ICakeRegistrationBuilder AsSelf() - { - _registration.RegistrationTypes.Add(_registration.ImplementationType); - return this; - } - - public ICakeRegistrationBuilder Singleton() - { - _registration.Singleton = true; - return this; - } - - public ICakeRegistrationBuilder Transient() - { - _registration.Singleton = false; - return this; - } - } -} \ No newline at end of file diff --git a/src/Cake.Frosting/Internal/Configuration.cs b/src/Cake.Frosting/Internal/Configuration.cs deleted file mode 100644 index d7e56d6614..0000000000 --- a/src/Cake.Frosting/Internal/Configuration.cs +++ /dev/null @@ -1,56 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using Cake.Core; -using Cake.Core.Configuration; - -namespace Cake.Frosting.Internal -{ - internal sealed class Configuration : ICakeConfiguration - { - private readonly CakeConfigurationProvider _provider; - private readonly CakeHostOptions _options; - private readonly ICakeEnvironment _environment; - private readonly IEnumerable _values; - private readonly object _lock; - private ICakeConfiguration _configuration; - - public Configuration( - CakeConfigurationProvider provider, - CakeHostOptions options, - ICakeEnvironment environment, - IEnumerable values) - { - _provider = provider; - _options = options; - _environment = environment; - _values = values; - _lock = new object(); - _configuration = null; - } - - public string GetValue(string key) - { - lock (_lock) - { - if (_configuration == null) - { - var arguments = new Dictionary(_options.Arguments, StringComparer.OrdinalIgnoreCase); - if (_values != null) - { - // Add additional configuration values. - foreach (var value in _values) - { - arguments[value.Key] = value.Value; - } - } - _configuration = _provider.CreateConfiguration(_environment.WorkingDirectory, arguments); - } - return _configuration.GetValue(key); - } - } - } -} diff --git a/src/Cake.Frosting/Internal/DefaultConsole.cs b/src/Cake.Frosting/Internal/DefaultConsole.cs deleted file mode 100644 index 0ed77a2e4a..0000000000 --- a/src/Cake.Frosting/Internal/DefaultConsole.cs +++ /dev/null @@ -1,59 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using Cake.Core; - -namespace Cake.Frosting.Internal -{ - internal sealed class DefaultConsole : IConsole - { - /// - public ConsoleColor ForegroundColor - { - get => Console.ForegroundColor; - set => Console.ForegroundColor = value; - } - - /// - public ConsoleColor BackgroundColor - { - get => Console.BackgroundColor; - set => Console.BackgroundColor = value; - } - - /// - public bool SupportAnsiEscapeCodes => false; - - /// - public void Write(string format, params object[] arg) - { - Console.Write(format, arg); - } - - /// - public void WriteLine(string format, params object[] arg) - { - Console.WriteLine(format, arg); - } - - /// - public void WriteError(string format, params object[] arg) - { - Console.Error.Write(format, arg); - } - - /// - public void WriteErrorLine(string format, params object[] arg) - { - Console.Error.WriteLine(format, arg); - } - - /// - public void ResetColor() - { - Console.ResetColor(); - } - } -} \ No newline at end of file diff --git a/src/Cake.Frosting/Internal/Diagnostics/CakeLog.cs b/src/Cake.Frosting/Internal/Diagnostics/CakeLog.cs deleted file mode 100644 index 658192e0a4..0000000000 --- a/src/Cake.Frosting/Internal/Diagnostics/CakeLog.cs +++ /dev/null @@ -1,99 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using Cake.Core; -using Cake.Core.Diagnostics; -using Cake.Frosting.Internal.Diagnostics.Formatting; - -namespace Cake.Frosting.Internal.Diagnostics -{ - internal sealed class CakeLog : ICakeLog - { - private readonly IConsole _console; - private readonly object _lock; - private readonly IDictionary _palettes; - - public Verbosity Verbosity { get; set; } - - public CakeLog(IConsole console, Verbosity verbosity = Verbosity.Normal) - { - _console = console; - _lock = new object(); - _palettes = CreatePalette(); - Verbosity = verbosity; - } - - public void Write(Verbosity verbosity, LogLevel level, string format, params object[] args) - { - if (verbosity > Verbosity) - { - return; - } - lock (_lock) - { - try - { - var palette = _palettes[level]; - var tokens = FormatParser.Parse(format); - foreach (var token in tokens) - { - SetPalette(token, palette); - if (level > LogLevel.Error) - { - _console.Write("{0}", token.Render(args)); - } - else - { - _console.WriteError("{0}", token.Render(args)); - } - } - } - finally - { - _console.ResetColor(); - if (level > LogLevel.Error) - { - _console.WriteLine(); - } - else - { - _console.WriteErrorLine(); - } - } - } - } - - private void SetPalette(FormatToken token, ConsolePalette palette) - { - var property = token as PropertyToken; - if (property != null) - { - _console.BackgroundColor = palette.ArgumentBackground; - _console.ForegroundColor = palette.ArgumentForeground; - } - else - { - _console.BackgroundColor = palette.Background; - _console.ForegroundColor = palette.Foreground; - } - } - - private IDictionary CreatePalette() - { - var background = _console.BackgroundColor; - var palette = new Dictionary - { - { LogLevel.Fatal, new ConsolePalette(ConsoleColor.Magenta, ConsoleColor.White, ConsoleColor.DarkMagenta, ConsoleColor.White) }, - { LogLevel.Error, new ConsolePalette(ConsoleColor.DarkRed, ConsoleColor.White, ConsoleColor.Red, ConsoleColor.White) }, - { LogLevel.Warning, new ConsolePalette(background, ConsoleColor.Yellow, background, ConsoleColor.Yellow) }, - { LogLevel.Information, new ConsolePalette(background, ConsoleColor.White, ConsoleColor.DarkBlue, ConsoleColor.White) }, - { LogLevel.Verbose, new ConsolePalette(background, ConsoleColor.Gray, background, ConsoleColor.White) }, - { LogLevel.Debug, new ConsolePalette(background, ConsoleColor.DarkGray, background, ConsoleColor.Gray) } - }; - return palette; - } - } -} \ No newline at end of file diff --git a/src/Cake.Frosting/Internal/Diagnostics/ConsolePalette.cs b/src/Cake.Frosting/Internal/Diagnostics/ConsolePalette.cs deleted file mode 100644 index f6d0b854e8..0000000000 --- a/src/Cake.Frosting/Internal/Diagnostics/ConsolePalette.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; - -namespace Cake.Frosting.Internal.Diagnostics -{ - internal sealed class ConsolePalette - { - public ConsoleColor Background { get; set; } - - public ConsoleColor Foreground { get; set; } - - public ConsoleColor ArgumentBackground { get; set; } - - public ConsoleColor ArgumentForeground { get; set; } - - public ConsolePalette(ConsoleColor background, ConsoleColor foreground, - ConsoleColor argumentBackground, ConsoleColor argumentForeground) - { - Background = background; - Foreground = foreground; - ArgumentBackground = argumentBackground; - ArgumentForeground = argumentForeground; - } - } -} \ No newline at end of file diff --git a/src/Cake.Frosting/Internal/Diagnostics/Formatting/FormatParser.cs b/src/Cake.Frosting/Internal/Diagnostics/Formatting/FormatParser.cs deleted file mode 100644 index 677ae7f7be..0000000000 --- a/src/Cake.Frosting/Internal/Diagnostics/Formatting/FormatParser.cs +++ /dev/null @@ -1,130 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Text; - -namespace Cake.Frosting.Internal.Diagnostics.Formatting -{ - internal static class FormatParser - { - public static IEnumerable Parse(string format) - { - var reader = new StringReader(format); - while (true) - { - var current = reader.Peek(); - if (current == -1) - { - break; - } - var character = (char)current; - if (character == '{') - { - yield return ParseProperty(reader); - } - else - { - yield return ParseText(reader); - } - } - } - - private static FormatToken ParseProperty(TextReader reader) - { - reader.Read(); // Consume - if (reader.Peek() == -1) - { - return new LiteralToken("{"); - } - if ((char)reader.Peek() == '{') - { - reader.Read(); - return new LiteralToken("{{"); - } - var builder = new StringBuilder(); - while (true) - { - var current = reader.Peek(); - if (current == -1) - { - break; - } - - var character = (char)current; - if (character == '}') - { - reader.Read(); - - var accumulated = builder.ToString(); - var parts = accumulated.Split(new[] { ':' }, StringSplitOptions.None); - if (parts.Length > 1) - { - var name = parts[0]; - var format = string.Join(string.Empty, parts.Skip(1)); - var positional = IsNumeric(name); - if (!positional) - { - throw new FormatException("Input string was not in a correct format."); - } - var position = int.Parse(name, CultureInfo.InvariantCulture); - return new PropertyToken(position, format); - } - else - { - var positional = IsNumeric(accumulated); - if (!positional) - { - throw new FormatException("Input string was not in a correct format."); - } - var position = int.Parse(accumulated, CultureInfo.InvariantCulture); - return new PropertyToken(position, null); - } - } - builder.Append((char)reader.Read()); - } - return new LiteralToken(builder.ToString()); - } - - private static FormatToken ParseText(TextReader reader) - { - var builder = new StringBuilder(); - while (true) - { - var current = reader.Peek(); - if (current == -1) - { - break; - } - var character = (char)current; - if (character == '{') - { - break; - } - builder.Append((char)reader.Read()); - } - return new LiteralToken(builder.ToString()); - } - - private static bool IsNumeric(string value) - { - if (string.IsNullOrWhiteSpace(value)) - { - return false; - } - foreach (var character in value) - { - if (!char.IsDigit(character)) - { - return false; - } - } - return true; - } - } -} \ No newline at end of file diff --git a/src/Cake.Frosting/Internal/Diagnostics/Formatting/FormatToken.cs b/src/Cake.Frosting/Internal/Diagnostics/Formatting/FormatToken.cs deleted file mode 100644 index 6b69a379aa..0000000000 --- a/src/Cake.Frosting/Internal/Diagnostics/Formatting/FormatToken.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace Cake.Frosting.Internal.Diagnostics.Formatting -{ - internal abstract class FormatToken - { - public abstract string Render(object[] args); - } -} \ No newline at end of file diff --git a/src/Cake.Frosting/Internal/Diagnostics/Formatting/LiteralToken.cs b/src/Cake.Frosting/Internal/Diagnostics/Formatting/LiteralToken.cs deleted file mode 100644 index b5d410da36..0000000000 --- a/src/Cake.Frosting/Internal/Diagnostics/Formatting/LiteralToken.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace Cake.Frosting.Internal.Diagnostics.Formatting -{ - internal sealed class LiteralToken : FormatToken - { - public string Text { get; } - - public LiteralToken(string text) - { - Text = text; - } - - public override string Render(object[] args) - { - return Text; - } - } -} \ No newline at end of file diff --git a/src/Cake.Frosting/Internal/Diagnostics/Formatting/PropertyToken.cs b/src/Cake.Frosting/Internal/Diagnostics/Formatting/PropertyToken.cs deleted file mode 100644 index 39b0e1d939..0000000000 --- a/src/Cake.Frosting/Internal/Diagnostics/Formatting/PropertyToken.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Globalization; - -namespace Cake.Frosting.Internal.Diagnostics.Formatting -{ - internal sealed class PropertyToken : FormatToken - { - public string Format { get; } - - public int Position { get; } - - public PropertyToken(int position, string format) - { - Position = position; - Format = format; - } - - public override string Render(object[] args) - { - var value = args[Position]; - if (!string.IsNullOrWhiteSpace(Format)) - { - var formattable = value as IFormattable; - if (formattable != null) - { - return formattable.ToString(Format, CultureInfo.InvariantCulture); - } - } - return value == null ? "[NULL]" : value.ToString(); - } - } -} \ No newline at end of file diff --git a/src/Cake.Frosting/Internal/EngineInitializer.cs b/src/Cake.Frosting/Internal/EngineInitializer.cs deleted file mode 100644 index 2ed521bb36..0000000000 --- a/src/Cake.Frosting/Internal/EngineInitializer.cs +++ /dev/null @@ -1,117 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Collections.Generic; -using System.Reflection; -using Cake.Core; -using Cake.Core.Diagnostics; - -namespace Cake.Frosting.Internal -{ - internal sealed class EngineInitializer - { - private readonly ICakeLog _log; - - public EngineInitializer(ICakeLog log) - { - _log = log; - } - - public void Initialize(ICakeEngine engine, IFrostingContext context, IEnumerable tasks, - IFrostingLifetime lifetime, IFrostingTaskLifetime taskLifetime) - { - if (tasks != null) - { - foreach (var task in tasks) - { - var taskName = TaskNameHelper.GetTaskName(task); - _log.Debug("Registering task: {0}", taskName); - - // Get the task's context type. - if (!task.HasCompatibleContext(context)) - { - const string format = "Task cannot be used since the context isn't convertible to {0}."; - _log.Warning(format, task.GetContextType().FullName); - } - else - { - // Register task with the Cake engine. - var cakeTask = engine.RegisterTask(taskName); - - // Is the run method overridden? - if (task.IsRunOverridden(context)) - { - cakeTask.Does(task.RunAsync); - } - - // Is the criteria method overridden? - if (task.IsShouldRunOverridden(context)) - { - cakeTask.WithCriteria(task.ShouldRun); - } - - // Continue on error? - if (task.IsContinueOnError()) - { - cakeTask.ContinueOnError(); - } - - // Is the on error method overridden? - if (task.IsOnErrorOverridden(context)) - { - cakeTask.OnError(exception => task.OnError(exception, context)); - } - - // Is the finally method overridden? - if (task.IsFinallyOverridden(context)) - { - cakeTask.Finally(() => task.Finally(context)); - } - - // Add dependencies - var attributes = task.GetType().GetTypeInfo().GetCustomAttributes(); - foreach (var dependency in attributes) - { - var dependencyName = TaskNameHelper.GetTaskName(dependency); - if (!typeof(IFrostingTask).IsAssignableFrom(dependency.Task)) - { - throw new FrostingException($"The dependency '{dependencyName}' is not a valid task."); - } - cakeTask.IsDependentOn(dependencyName); - } - } - } - } - - if (lifetime != null) - { - _log.Debug("Registering lifetime: {0}", lifetime.GetType().Name); - - if (lifetime.IsSetupOverridden(context)) - { - engine.RegisterSetupAction(info => lifetime.Setup(context)); - } - if (lifetime.IsTeardownOverridden(context)) - { - engine.RegisterTeardownAction(info => lifetime.Teardown(context, info)); - } - } - - if (taskLifetime != null) - { - _log.Debug("Registering task lifetime: {0}", taskLifetime.GetType().Name); - - if (taskLifetime.IsSetupOverridden(context)) - { - engine.RegisterTaskSetupAction(info => taskLifetime.Setup(context, info)); - } - - if (taskLifetime.IsTeardownOverridden(context)) - { - engine.RegisterTaskTeardownAction(info => taskLifetime.Teardown(context, info)); - } - } - } - } -} \ No newline at end of file diff --git a/src/Cake.Frosting/Internal/Engines/FrostingDescriptionRunner.cs b/src/Cake.Frosting/Internal/Engines/FrostingDescriptionRunner.cs new file mode 100644 index 0000000000..86e3b144a8 --- /dev/null +++ b/src/Cake.Frosting/Internal/Engines/FrostingDescriptionRunner.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using Cake.Cli; +using Cake.Core; +using Cake.Core.Diagnostics; + +namespace Cake.Frosting.Internal +{ + internal sealed class FrostingDescriptionRunner : FrostingEngine + { + public FrostingDescriptionRunner(DescriptionScriptHost host, + ICakeEngine engine, IFrostingContext context, ICakeLog log, + IEnumerable tasks, + IFrostingSetup setup = null, IFrostingTeardown teardown = null, + IFrostingTaskSetup taskSetup = null, IFrostingTaskTeardown taskTeardown = null) + : base(host, engine, context, log, tasks, setup, teardown, taskSetup, taskTeardown) + { + } + } +} diff --git a/src/Cake.Frosting/Internal/Engines/FrostingDryRunner.cs b/src/Cake.Frosting/Internal/Engines/FrostingDryRunner.cs new file mode 100644 index 0000000000..1a466b7093 --- /dev/null +++ b/src/Cake.Frosting/Internal/Engines/FrostingDryRunner.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using Cake.Cli; +using Cake.Core; +using Cake.Core.Diagnostics; + +namespace Cake.Frosting.Internal +{ + internal sealed class FrostingDryRunner : FrostingEngine> + { + public FrostingDryRunner(DryRunScriptHost host, + ICakeEngine engine, IFrostingContext context, ICakeLog log, + IEnumerable tasks, + IFrostingSetup setup = null, IFrostingTeardown teardown = null, + IFrostingTaskSetup taskSetup = null, IFrostingTaskTeardown taskTeardown = null) + : base(host, engine, context, log, tasks, setup, teardown, taskSetup, taskTeardown) + { + } + } +} diff --git a/src/Cake.Frosting/Internal/Engines/FrostingRunner.cs b/src/Cake.Frosting/Internal/Engines/FrostingRunner.cs new file mode 100644 index 0000000000..e30a3139be --- /dev/null +++ b/src/Cake.Frosting/Internal/Engines/FrostingRunner.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using Cake.Cli; +using Cake.Core; +using Cake.Core.Diagnostics; + +namespace Cake.Frosting.Internal +{ + internal sealed class FrostingRunner : FrostingEngine> + { + public FrostingRunner(BuildScriptHost host, + ICakeEngine engine, IFrostingContext context, ICakeLog log, + IEnumerable tasks, + IFrostingSetup setup = null, IFrostingTeardown teardown = null, + IFrostingTaskSetup taskSetup = null, IFrostingTaskTeardown taskTeardown = null) + : base(host, engine, context, log, tasks, setup, teardown, taskSetup, taskTeardown) + { + } + } +} diff --git a/src/Cake.Frosting/Internal/Engines/FrostingTreeRunner.cs b/src/Cake.Frosting/Internal/Engines/FrostingTreeRunner.cs new file mode 100644 index 0000000000..bba22de94d --- /dev/null +++ b/src/Cake.Frosting/Internal/Engines/FrostingTreeRunner.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using Cake.Cli; +using Cake.Core; +using Cake.Core.Diagnostics; + +namespace Cake.Frosting.Internal +{ + internal sealed class FrostingTreeRunner : FrostingEngine + { + public FrostingTreeRunner(TreeScriptHost host, + ICakeEngine engine, IFrostingContext context, ICakeLog log, + IEnumerable tasks, + IFrostingSetup setup = null, IFrostingTeardown teardown = null, + IFrostingTaskSetup taskSetup = null, IFrostingTaskTeardown taskTeardown = null) + : base(host, engine, context, log, tasks, setup, teardown, taskSetup, taskTeardown) + { + } + } +} diff --git a/src/Cake.Frosting/Internal/ErrorCakeHost.cs b/src/Cake.Frosting/Internal/ErrorCakeHost.cs deleted file mode 100644 index 7aa361533b..0000000000 --- a/src/Cake.Frosting/Internal/ErrorCakeHost.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using Cake.Core.Diagnostics; -using Cake.Frosting.Internal.Diagnostics; - -namespace Cake.Frosting.Internal -{ - internal sealed class ErrorCakeHost : ICakeHost - { - private readonly ICakeLog _log; - private readonly Exception _exception; - - public ErrorCakeHost(Exception exception) - : this(null, exception) - { - } - - public ErrorCakeHost(ICakeLog log, Exception exception) - { - _log = log ?? new CakeLog(new DefaultConsole(), Verbosity.Verbose); - _exception = exception; - } - - public int Run() - { - ErrorHandler.OutputError(_log, _exception); - return ErrorHandler.GetExitCode(_exception); - } - } -} diff --git a/src/Cake.Frosting/Internal/ErrorCakeHostBuilder.cs b/src/Cake.Frosting/Internal/ErrorCakeHostBuilder.cs deleted file mode 100644 index 2085276ab4..0000000000 --- a/src/Cake.Frosting/Internal/ErrorCakeHostBuilder.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using Cake.Core.Diagnostics; -using Cake.Frosting.Internal.Diagnostics; - -namespace Cake.Frosting.Internal -{ - internal sealed class ErrorCakeHostBuilder : ICakeHostBuilder - { - private readonly ICakeLog _log; - private readonly Exception _exception; - - public ErrorCakeHostBuilder(Exception exception) - { - _log = new CakeLog(new DefaultConsole(), Verbosity.Verbose); - _exception = exception; - } - - public ICakeHostBuilder ConfigureServices(Action configureServices) - { - return this; - } - - public ICakeHost Build() - { - return new ErrorCakeHost(_log, _exception); - } - } -} \ No newline at end of file diff --git a/src/Cake.Frosting/Internal/ErrorHandler.cs b/src/Cake.Frosting/Internal/ErrorHandler.cs deleted file mode 100644 index 248d63d19e..0000000000 --- a/src/Cake.Frosting/Internal/ErrorHandler.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using Cake.Core.Diagnostics; - -namespace Cake.Frosting.Internal -{ - internal static class ErrorHandler - { - public static int OutputError(ICakeLog log, Exception exception) - { - if (log.Verbosity == Verbosity.Diagnostic) - { - log.Error("Error: {0}", exception); - } - else - { - log.Error("Error: {0}", exception.Message); - } - return 1; - } - - public static int GetExitCode(Exception exception) - { - return 1; - } - } -} diff --git a/src/Cake.Frosting/Internal/Extensions/FrostingLifetimeExtensions.cs b/src/Cake.Frosting/Internal/Extensions/FrostingLifetimeExtensions.cs index cf85bbc608..5b6e48aae8 100644 --- a/src/Cake.Frosting/Internal/Extensions/FrostingLifetimeExtensions.cs +++ b/src/Cake.Frosting/Internal/Extensions/FrostingLifetimeExtensions.cs @@ -2,10 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Reflection; using Cake.Core; -// ReSharper disable once CheckNamespace namespace Cake.Frosting.Internal { internal static class FrostingLifetimeExtensions diff --git a/src/Cake.Frosting/Internal/Extensions/FrostingTaskExtensions.cs b/src/Cake.Frosting/Internal/Extensions/FrostingTaskExtensions.cs index b18d5d2619..000710fa6b 100644 --- a/src/Cake.Frosting/Internal/Extensions/FrostingTaskExtensions.cs +++ b/src/Cake.Frosting/Internal/Extensions/FrostingTaskExtensions.cs @@ -3,14 +3,51 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using System.Reflection; using Cake.Core; -// ReSharper disable once CheckNamespace namespace Cake.Frosting.Internal { internal static class FrostingTaskExtensions { + public static string GetTaskName(this IFrostingTask task) + { + if (task is null) + { + throw new ArgumentNullException(nameof(task)); + } + + return task.GetType().GetTaskName(); + } + + public static string GetTaskDescription(this IFrostingTask task) + { + if (task is null) + { + throw new ArgumentNullException(nameof(task)); + } + + var attribute = task.GetType().GetCustomAttribute(); + return attribute != null ? attribute.Description : string.Empty; + } + + public static IEnumerable GetDependencies(this IFrostingTask task) + { + var result = new List(); + result.AddRange(task.GetType().GetCustomAttributes()); + result.AddRange(task.GetType().GetCustomAttributes()); + return result; + } + + public static IEnumerable GetReverseDependencies(this IFrostingTask task) + { + var result = new List(); + result.AddRange(task.GetType().GetCustomAttributes()); + result.AddRange(task.GetType().GetCustomAttributes()); + return result; + } + public static bool IsRunOverridden(this IFrostingTask task, IFrostingContext context) { if (task.IsFrostingTask()) diff --git a/src/Cake.Frosting/Internal/Extensions/FrostingTaskLifetimeExtensions.cs b/src/Cake.Frosting/Internal/Extensions/FrostingTaskLifetimeExtensions.cs index 678fdf06c3..236d5d232b 100644 --- a/src/Cake.Frosting/Internal/Extensions/FrostingTaskLifetimeExtensions.cs +++ b/src/Cake.Frosting/Internal/Extensions/FrostingTaskLifetimeExtensions.cs @@ -2,10 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Reflection; using Cake.Core; -// ReSharper disable once CheckNamespace namespace Cake.Frosting.Internal { internal static class FrostingTaskLifetimeExtensions diff --git a/src/Cake.Frosting/Internal/Extensions/MethodInfoExtensions.cs b/src/Cake.Frosting/Internal/Extensions/MethodInfoExtensions.cs index 83c1cf8f12..ce50fc4b3e 100644 --- a/src/Cake.Frosting/Internal/Extensions/MethodInfoExtensions.cs +++ b/src/Cake.Frosting/Internal/Extensions/MethodInfoExtensions.cs @@ -4,7 +4,6 @@ using System.Reflection; -// ReSharper disable once CheckNamespace namespace Cake.Frosting.Internal { internal static class MethodInfoExtensions diff --git a/src/Cake.Frosting/Internal/Guard.cs b/src/Cake.Frosting/Internal/Extensions/TaskNameHelper.cs similarity index 54% rename from src/Cake.Frosting/Internal/Guard.cs rename to src/Cake.Frosting/Internal/Extensions/TaskNameHelper.cs index 3eb4a56dc2..f6bd6c9fb0 100644 --- a/src/Cake.Frosting/Internal/Guard.cs +++ b/src/Cake.Frosting/Internal/Extensions/TaskNameHelper.cs @@ -6,14 +6,16 @@ namespace Cake.Frosting.Internal { - internal static class Guard + internal static class TaskNameHelper { - public static void ArgumentNotNull(object value, string name) + public static string GetTaskName(this ITaskDependency dependency) { - if (value == null) + if (dependency is null) { - throw new ArgumentNullException(name); + throw new ArgumentNullException(nameof(dependency)); } + + return dependency.Task.GetTaskName(); } } } diff --git a/src/Cake.Frosting/Internal/Extensions/TypeExtensions.cs b/src/Cake.Frosting/Internal/Extensions/TypeExtensions.cs new file mode 100644 index 0000000000..b458b64742 --- /dev/null +++ b/src/Cake.Frosting/Internal/Extensions/TypeExtensions.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Reflection; + +namespace Cake.Frosting.Internal +{ + internal static class TypeExtensions + { + public static string GetTaskName(this Type task) + { + if (task is null) + { + throw new ArgumentNullException(nameof(task)); + } + + var attribute = task.GetCustomAttribute(); + return attribute != null ? attribute.Name : task.Name; + } + } +} diff --git a/src/Cake.Frosting/Internal/FrostingConfiguration.cs b/src/Cake.Frosting/Internal/FrostingConfiguration.cs new file mode 100644 index 0000000000..e980056cb8 --- /dev/null +++ b/src/Cake.Frosting/Internal/FrostingConfiguration.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using Cake.Core.Configuration; + +namespace Cake.Frosting.Internal +{ + internal sealed class FrostingConfiguration : ICakeConfiguration + { + private readonly Dictionary _lookup; + + public FrostingConfiguration(IEnumerable values) + { + if (values is null) + { + throw new ArgumentNullException(nameof(values)); + } + + _lookup = new Dictionary(StringComparer.OrdinalIgnoreCase); + foreach (var value in values) + { + _lookup[value.Key] = value.Value; + } + } + + public string GetValue(string key) + { + _lookup.TryGetValue(key, out var value); + return value; + } + } +} diff --git a/src/Cake.Frosting/Internal/ConfigurationSetting.cs b/src/Cake.Frosting/Internal/FrostingConfigurationValue.cs similarity index 55% rename from src/Cake.Frosting/Internal/ConfigurationSetting.cs rename to src/Cake.Frosting/Internal/FrostingConfigurationValue.cs index 25cd0a3871..8ce56f32a2 100644 --- a/src/Cake.Frosting/Internal/ConfigurationSetting.cs +++ b/src/Cake.Frosting/Internal/FrostingConfigurationValue.cs @@ -2,18 +2,19 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; + namespace Cake.Frosting.Internal { - internal sealed class ConfigurationSetting + internal sealed class FrostingConfigurationValue { public string Key { get; } - public string Value { get; } - public ConfigurationSetting(string key, string value) + public FrostingConfigurationValue(string key, string value) { - Key = key; - Value = value; + Key = key ?? throw new ArgumentNullException(nameof(key)); + Value = value ?? throw new ArgumentNullException(nameof(value)); } } } diff --git a/src/Cake.Frosting/Internal/FrostingEngine.cs b/src/Cake.Frosting/Internal/FrostingEngine.cs new file mode 100644 index 0000000000..c62fb16c0d --- /dev/null +++ b/src/Cake.Frosting/Internal/FrostingEngine.cs @@ -0,0 +1,182 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using Cake.Core; +using Cake.Core.Diagnostics; +using Cake.Core.IO; +using Cake.Core.Scripting; + +namespace Cake.Frosting.Internal +{ + internal interface IFrostingEngine + { + ExecutionSettings Settings { get; } + CakeReport Run(string target, Verbosity verbosity, DirectoryPath workingDirectory); + } + + internal abstract class FrostingEngine : IFrostingEngine + where THost : IScriptHost + { + private readonly List _tasks; + private readonly IFrostingContext _context; + private readonly ICakeLog _log; + private readonly IFrostingSetup _setup; + private readonly IFrostingTeardown _teardown; + private readonly IFrostingTaskSetup _taskSetup; + private readonly IFrostingTaskTeardown _taskTeardown; + private readonly THost _host; + private readonly ICakeEngine _engine; + + public ExecutionSettings Settings => _host.Settings; + + protected FrostingEngine( + THost host, + ICakeEngine engine, IFrostingContext context, ICakeLog log, + IEnumerable tasks, + IFrostingSetup setup = null, + IFrostingTeardown teardown = null, + IFrostingTaskSetup taskSetup = null, + IFrostingTaskTeardown taskTeardown = null) + { + _host = host; + _engine = engine; + _context = context; + _log = log; + _setup = setup; + _teardown = teardown; + _taskSetup = taskSetup; + _taskTeardown = taskTeardown; + _tasks = new List(tasks ?? Array.Empty()); + } + + public CakeReport Run(string target, Verbosity verbosity, DirectoryPath workingDirectory) + { + _log.Verbosity = verbosity; + + ConfigureTasks(); + ConfigureLifetime(); + ConfigureTaskLifetime(); + + return _host.RunTarget(target); + } + + private void ConfigureTaskLifetime() + { + if (_taskSetup != null) + { + _log.Debug("Registering task setup: {0}", _taskSetup.GetType().Name); + _engine.RegisterTaskSetupAction(info => _taskSetup.Setup(_context, info)); + } + + if (_taskTeardown != null) + { + _log.Debug("Registering task setup: {0}", _taskTeardown.GetType().Name); + _engine.RegisterTaskTeardownAction(info => _taskTeardown.Teardown(_context, info)); + } + } + + private void ConfigureLifetime() + { + if (_setup != null) + { + _log.Debug("Registering setup: {0}", _setup.GetType().Name); + _engine.RegisterSetupAction(info => _setup.Setup(_context)); + } + + if (_teardown != null) + { + _log.Debug("Registering teardown: {0}", _teardown.GetType().Name); + _engine.RegisterTeardownAction(info => _teardown.Teardown(_context, info)); + } + } + + private void ConfigureTasks() + { + if (_tasks == null) + { + return; + } + + foreach (var task in _tasks) + { + var name = task.GetTaskName(); + _log.Debug("Registering task: {0}", name); + + // Get the task's context type. + if (!task.HasCompatibleContext(_context)) + { + const string format = "Task cannot be used since the context isn't convertible to {0}."; + _log.Warning(format, task.GetContextType().FullName); + } + else + { + // Register task with the Cake engine. + var cakeTask = _engine.RegisterTask(name); + + var description = task.GetTaskDescription(); + if (!string.IsNullOrWhiteSpace(description)) + { + cakeTask.Description(description); + } + + // Is the run method overridden? + if (task.IsRunOverridden(_context)) + { + cakeTask.Does(task.RunAsync); + } + + // Is the criteria method overridden? + if (task.IsShouldRunOverridden(_context)) + { + cakeTask.WithCriteria(task.ShouldRun); + } + + // Continue on error? + if (task.IsContinueOnError()) + { + cakeTask.ContinueOnError(); + } + + // Is the on error method overridden? + if (task.IsOnErrorOverridden(_context)) + { + cakeTask.OnError(exception => task.OnError(exception, _context)); + } + + // Is the finally method overridden? + if (task.IsFinallyOverridden(_context)) + { + cakeTask.Finally(() => task.Finally(_context)); + } + + // Add dependencies + foreach (var dependency in task.GetDependencies()) + { + var dependencyName = dependency.GetTaskName(); + if (!typeof(IFrostingTask).IsAssignableFrom(dependency.Task)) + { + throw new FrostingException($"The dependency '{dependencyName}' is not a valid task."); + } + + cakeTask.IsDependentOn(dependencyName); + } + + // Add reverse dependencies + foreach (var dependee in task.GetReverseDependencies()) + { + var dependeeName = dependee.GetType().GetTaskName(); + if (!typeof(IFrostingTask).IsAssignableFrom(dependee.Task)) + { + throw new FrostingException($"The reverse dependency '{dependeeName}' is not a valid task."); + } + + cakeTask.IsDependeeOf(dependeeName); + } + } + } + } + } +} diff --git a/src/Cake.Frosting.Tests/Data/DummyTaskLifetime.cs b/src/Cake.Frosting/Internal/IReverseTaskDependency.cs similarity index 66% rename from src/Cake.Frosting.Tests/Data/DummyTaskLifetime.cs rename to src/Cake.Frosting/Internal/IReverseTaskDependency.cs index 704d821cc3..2c4046d183 100644 --- a/src/Cake.Frosting.Tests/Data/DummyTaskLifetime.cs +++ b/src/Cake.Frosting/Internal/IReverseTaskDependency.cs @@ -2,9 +2,12 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace Cake.Frosting.Tests.Data +using System; + +namespace Cake.Frosting { - public sealed class DummyTaskLifetime : FrostingTaskLifetime + internal interface IReverseTaskDependency { + Type Task { get; } } -} \ No newline at end of file +} diff --git a/src/Cake.Frosting.Tests/Data/DummyLifetime.cs b/src/Cake.Frosting/Internal/ITaskDependency.cs similarity index 67% rename from src/Cake.Frosting.Tests/Data/DummyLifetime.cs rename to src/Cake.Frosting/Internal/ITaskDependency.cs index 72a0a38d11..700ce980cf 100644 --- a/src/Cake.Frosting.Tests/Data/DummyLifetime.cs +++ b/src/Cake.Frosting/Internal/ITaskDependency.cs @@ -2,9 +2,12 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace Cake.Frosting.Tests.Data +using System; + +namespace Cake.Frosting { - public sealed class DummyLifetime : FrostingLifetime + internal interface ITaskDependency { + Type Task { get; } } } diff --git a/src/Cake.Frosting/Internal/Module.cs b/src/Cake.Frosting/Internal/Module.cs deleted file mode 100644 index 2b84484fc6..0000000000 --- a/src/Cake.Frosting/Internal/Module.cs +++ /dev/null @@ -1,57 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Reflection; -using Cake.Core; -using Cake.Core.Composition; -using Cake.Core.Configuration; -using Cake.Core.Diagnostics; -using Cake.Frosting.Internal; -using Cake.Frosting.Internal.Commands; -using Cake.Frosting.Internal.Diagnostics; - -// ReSharper disable once CheckNamespace -namespace Cake.Frosting -{ - internal class Module : ICakeModule - { - public void Register(ICakeContainerRegistrar registry) - { - // Register the entry assembly. - registry.RegisterInstance(Assembly.GetEntryAssembly()); - - // Core services - registry.RegisterType().As().Singleton(); - registry.RegisterType().As().Singleton(); - registry.RegisterType().Singleton(); - registry.RegisterType().As().Singleton(); - - // Logging - registry.RegisterType().As().Singleton(); - registry.RegisterType().As().Singleton(); - - // Configuration - registry.RegisterType().AsSelf().Singleton(); - registry.RegisterType().As().Singleton(); - - // Commands - registry.RegisterType().AsSelf().Singleton(); - registry.RegisterType().AsSelf().Singleton(); - registry.RegisterType().AsSelf().Singleton(); - registry.RegisterType().AsSelf().Singleton(); - registry.RegisterType().AsSelf().Singleton(); - - // Misc - registry.RegisterType().As().Singleton(); - registry.RegisterType().As().Singleton(); - - // Tooling - registry.RegisterType().As().Singleton(); - - // Register default stuff. - registry.RegisterType().AsSelf().As().Singleton(); - registry.RegisterType().AsSelf().Singleton(); - } - } -} diff --git a/src/Cake.Frosting/Internal/RawArguments.cs b/src/Cake.Frosting/Internal/RawArguments.cs deleted file mode 100644 index 495ea672ee..0000000000 --- a/src/Cake.Frosting/Internal/RawArguments.cs +++ /dev/null @@ -1,46 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using Cake.Core; - -namespace Cake.Frosting.Internal -{ - internal sealed class RawArguments : ICakeArguments - { - private readonly Dictionary _arguments; - - /// - /// Gets the arguments. - /// - /// The arguments. - public IReadOnlyDictionary Arguments => _arguments; - - /// - /// Initializes a new instance of the class. - /// - /// The options. - public RawArguments(CakeHostOptions options) - { - _arguments = new Dictionary( - (options ?? new CakeHostOptions()).Arguments ?? new Dictionary(), - StringComparer.OrdinalIgnoreCase); - } - - /// - public bool HasArgument(string name) - { - return _arguments.ContainsKey(name); - } - - /// - public ICollection GetArguments(string name) - { - return _arguments.ContainsKey(name) - ? new[] { _arguments[name] } - : Array.Empty(); - } - } -} diff --git a/src/Cake.Frosting/Internal/ReportPrinter.cs b/src/Cake.Frosting/Internal/ReportPrinter.cs deleted file mode 100644 index 51e8423323..0000000000 --- a/src/Cake.Frosting/Internal/ReportPrinter.cs +++ /dev/null @@ -1,105 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using Cake.Core; -using Cake.Core.Diagnostics; - -namespace Cake.Frosting.Internal -{ - internal sealed class ReportPrinter : ICakeReportPrinter - { - private readonly IConsole _console; - private readonly ICakeLog _log; - - public ReportPrinter(IConsole console, ICakeLog log) - { - _console = console; - _log = log; - } - - public void Write(CakeReport report) - { - if (report == null) - { - throw new ArgumentNullException(nameof(report)); - } - - try - { - var maxTaskNameLength = 29; - foreach (var item in report) - { - if (item.TaskName.Length > maxTaskNameLength) - { - maxTaskNameLength = item.TaskName.Length; - } - } - - maxTaskNameLength++; - string lineFormat = "{0,-" + maxTaskNameLength + "}{1,-20}"; - _console.ForegroundColor = ConsoleColor.Green; - - // Write header. - _console.WriteLine(); - _console.WriteLine(lineFormat, "Task", "Duration"); - _console.WriteLine(new string('-', 20 + maxTaskNameLength)); - - // Write task status. - foreach (var item in report) - { - if (ShouldWriteTask(item)) - { - _console.ForegroundColor = GetItemForegroundColor(item); - _console.WriteLine(lineFormat, item.TaskName, FormatDuration(item)); - } - } - - // Write footer. - _console.ForegroundColor = ConsoleColor.Green; - _console.WriteLine(new string('-', 20 + maxTaskNameLength)); - _console.WriteLine(lineFormat, "Total:", FormatTime(GetTotalTime(report))); - } - finally - { - _console.ResetColor(); - } - } - - private bool ShouldWriteTask(CakeReportEntry item) - { - if (item.ExecutionStatus == CakeTaskExecutionStatus.Delegated) - { - return _log.Verbosity >= Verbosity.Verbose; - } - return true; - } - - private static string FormatDuration(CakeReportEntry item) - { - return item.ExecutionStatus == CakeTaskExecutionStatus.Skipped - ? "Skipped" : FormatTime(item.Duration); - } - - private static ConsoleColor GetItemForegroundColor(CakeReportEntry item) - { - return item.ExecutionStatus == CakeTaskExecutionStatus.Executed - ? ConsoleColor.Green : ConsoleColor.Gray; - } - - private static string FormatTime(TimeSpan time) - { - return time.ToString("c", CultureInfo.InvariantCulture); - } - - private static TimeSpan GetTotalTime(IEnumerable entries) - { - return entries.Select(i => i.Duration) - .Aggregate(TimeSpan.Zero, (t1, t2) => t1 + t2); - } - } -} \ No newline at end of file diff --git a/src/Cake.Frosting/Internal/ServiceCollectionAdapter.cs b/src/Cake.Frosting/Internal/ServiceCollectionAdapter.cs new file mode 100644 index 0000000000..b006b1bbbf --- /dev/null +++ b/src/Cake.Frosting/Internal/ServiceCollectionAdapter.cs @@ -0,0 +1,64 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using Cake.Core.Composition; +using Microsoft.Extensions.DependencyInjection; + +namespace Cake.Frosting.Internal +{ + internal sealed class ServiceCollectionAdapter : ICakeContainerRegistrar + { + private readonly List _registrations; + + public ServiceCollectionAdapter() + { + _registrations = new List(); + } + + public ICakeRegistrationBuilder RegisterInstance(TImplementation instance) + where TImplementation : class + { + var registration = new ServiceRegistration(typeof(TImplementation)) + { + Instance = instance, + Lifetime = ServiceLifetime.Singleton, + ServiceType = typeof(TImplementation), + }; + + _registrations.Add(registration); + return registration; + } + + public ICakeRegistrationBuilder RegisterType(Type type) + { + var registration = new ServiceRegistration(type) + { + Lifetime = ServiceLifetime.Transient, + ServiceType = type, + }; + + _registrations.Add(registration); + return registration; + } + + public void Transfer(IServiceCollection services) + { + foreach (var registration in _registrations) + { + if (registration.Instance != null) + { + var descriptor = ServiceDescriptor.Describe(registration.ServiceType, f => registration.Instance, registration.Lifetime); + services.Add(descriptor); + } + else + { + var descriptor = ServiceDescriptor.Describe(registration.ServiceType, registration.ImplementationType, registration.Lifetime); + services.Add(descriptor); + } + } + } + } +} diff --git a/src/Cake.Frosting/Internal/ServiceRegistration.cs b/src/Cake.Frosting/Internal/ServiceRegistration.cs new file mode 100644 index 0000000000..4e1906f5f5 --- /dev/null +++ b/src/Cake.Frosting/Internal/ServiceRegistration.cs @@ -0,0 +1,47 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using Cake.Core.Composition; +using Microsoft.Extensions.DependencyInjection; + +namespace Cake.Frosting.Internal +{ + internal sealed class ServiceRegistration : ICakeRegistrationBuilder + { + public Type ImplementationType { get; } + public object Instance { get; set; } + public Type ServiceType { get; set; } + public ServiceLifetime Lifetime { get; set; } + + public ServiceRegistration(Type implementationType) + { + ImplementationType = implementationType; + } + + public ICakeRegistrationBuilder As(Type type) + { + ServiceType = type; + return this; + } + + public ICakeRegistrationBuilder AsSelf() + { + ServiceType = ImplementationType; + return this; + } + + public ICakeRegistrationBuilder Singleton() + { + Lifetime = ServiceLifetime.Singleton; + return this; + } + + public ICakeRegistrationBuilder Transient() + { + Lifetime = ServiceLifetime.Transient; + return this; + } + } +} diff --git a/src/Cake.Frosting/Internal/TaskFinder.cs b/src/Cake.Frosting/Internal/TaskFinder.cs deleted file mode 100644 index e421ad81cf..0000000000 --- a/src/Cake.Frosting/Internal/TaskFinder.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.Reflection; - -namespace Cake.Frosting.Internal -{ - internal sealed class TaskFinder : ICakeTaskFinder - { - public Type[] GetTasks(IEnumerable assemblies) - { - var result = new List(); - foreach (var assembly in assemblies) - { - if (assembly == null) - { - continue; - } - - foreach (var type in assembly.GetExportedTypes()) - { - var info = type.GetTypeInfo(); - if (typeof(IFrostingTask).IsAssignableFrom(type) && info.IsClass && !info.IsAbstract) - { - result.Add(type); - } - } - } - return result.ToArray(); - } - } -} diff --git a/src/Cake.Frosting/Internal/TaskNameHelper.cs b/src/Cake.Frosting/Internal/TaskNameHelper.cs deleted file mode 100644 index e95a8ef38b..0000000000 --- a/src/Cake.Frosting/Internal/TaskNameHelper.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Reflection; - -namespace Cake.Frosting.Internal -{ - internal static class TaskNameHelper - { - public static string GetTaskName(IFrostingTask task) - { - return GetTaskName(task.GetType()); - } - - public static string GetTaskName(DependencyAttribute attribute) - { - return GetTaskName(attribute.Task); - } - - public static string GetTaskName(Type task) - { - var attribute = task.GetTypeInfo().GetCustomAttribute(); - return attribute != null ? attribute.Name : task.Name; - } - } -} diff --git a/src/Cake.Frosting/Internal/TypeRegistrar.cs b/src/Cake.Frosting/Internal/TypeRegistrar.cs new file mode 100644 index 0000000000..feb783445e --- /dev/null +++ b/src/Cake.Frosting/Internal/TypeRegistrar.cs @@ -0,0 +1,38 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Diagnostics; +using Microsoft.Extensions.DependencyInjection; +using Spectre.Cli; + +namespace Cake.Frosting.Internal +{ + internal sealed class TypeRegistrar : ITypeRegistrar + { + private readonly IServiceCollection _collection; + + public TypeRegistrar() + { + _collection = new ServiceCollection(); + } + + public ITypeResolver Build() + { + return new TypeResolver(_collection.BuildServiceProvider()); + } + + [DebuggerStepThrough] + public void Register(Type service, Type implementation) + { + _collection.AddSingleton(service, implementation); + } + + [DebuggerStepThrough] + public void RegisterInstance(Type service, object implementation) + { + _collection.AddSingleton(service, implementation); + } + } +} diff --git a/src/Cake.Frosting/Internal/TypeResolver.cs b/src/Cake.Frosting/Internal/TypeResolver.cs new file mode 100644 index 0000000000..04c4054da0 --- /dev/null +++ b/src/Cake.Frosting/Internal/TypeResolver.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using Spectre.Cli; + +namespace Cake.Frosting.Internal +{ + internal sealed class TypeResolver : ITypeResolver + { + private readonly IServiceProvider _provider; + + public TypeResolver(IServiceProvider provider) + { + _provider = provider; + } + + public object Resolve(Type type) + { + return _provider.GetService(type); + } + } +} diff --git a/src/Cake.Frosting/Internal/WorkingDirectory.cs b/src/Cake.Frosting/Internal/WorkingDirectory.cs index 30d0ff549d..6c5271d7d2 100644 --- a/src/Cake.Frosting/Internal/WorkingDirectory.cs +++ b/src/Cake.Frosting/Internal/WorkingDirectory.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using Cake.Core.IO; namespace Cake.Frosting.Internal @@ -12,9 +13,7 @@ internal sealed class WorkingDirectory public WorkingDirectory(DirectoryPath path) { - Guard.ArgumentNotNull(path, nameof(path)); - - Path = path; + Path = path ?? throw new ArgumentNullException(nameof(path)); } } } diff --git a/src/Cake.Tests/Fakes/FakeVersionResolver.cs b/src/Cake.Tests/Fakes/FakeVersionResolver.cs index e9df38787e..422d4fe756 100644 --- a/src/Cake.Tests/Fakes/FakeVersionResolver.cs +++ b/src/Cake.Tests/Fakes/FakeVersionResolver.cs @@ -1,4 +1,4 @@ -using Cake.Features.Introspection; +using Cake.Cli; namespace Cake.Tests.Fakes { diff --git a/src/Cake.Tests/Unit/Features/BuildFeatureTests.cs b/src/Cake.Tests/Unit/Features/BuildFeatureTests.cs index 7186021392..fbc7a81778 100644 --- a/src/Cake.Tests/Unit/Features/BuildFeatureTests.cs +++ b/src/Cake.Tests/Unit/Features/BuildFeatureTests.cs @@ -1,6 +1,7 @@ using System; +using Cake.Cli; +using Cake.Core; using Cake.Features.Building; -using Cake.Features.Building.Hosts; using Cake.Tests.Fixtures; using Xunit; diff --git a/src/Cake.Tests/Unit/Features/InfoFeatureTests.cs b/src/Cake.Tests/Unit/Features/InfoFeatureTests.cs index a2e17a7c4e..d477ac45d0 100644 --- a/src/Cake.Tests/Unit/Features/InfoFeatureTests.cs +++ b/src/Cake.Tests/Unit/Features/InfoFeatureTests.cs @@ -1,4 +1,4 @@ -using Cake.Features.Introspection; +using Cake.Cli; using Cake.Testing; using Cake.Tests.Fakes; using Xunit; @@ -7,31 +7,16 @@ namespace Cake.Tests.Unit.Features { public sealed class InfoFeatureTests { - [Fact] - public void Should_Return_Success() - { - // Given - var console = new FakeConsole(); - var resolver = new FakeVersionResolver("1.2.3", "3.2.1"); - var feature = new InfoFeature(console, resolver); - - // When - var result = feature.Run(); - - // Then - Assert.Equal(0, result); - } - [Fact] public void Should_Output_Version_To_Console() { // Given var console = new FakeConsole(); var resolver = new FakeVersionResolver("1.2.3", "3.2.1"); - var feature = new InfoFeature(console, resolver); + var feature = new InfoFeature(resolver); // When - var result = feature.Run(); + feature.Run(console); // Then Assert.Contains("Version: 1.2.3", console.Messages); diff --git a/src/Cake.Tests/Unit/Features/VersionFeatureTests.cs b/src/Cake.Tests/Unit/Features/VersionFeatureTests.cs index 6567874a7b..552377f316 100644 --- a/src/Cake.Tests/Unit/Features/VersionFeatureTests.cs +++ b/src/Cake.Tests/Unit/Features/VersionFeatureTests.cs @@ -1,4 +1,4 @@ -using Cake.Features.Introspection; +using Cake.Cli; using Cake.Testing; using Cake.Tests.Fakes; using Xunit; @@ -7,31 +7,16 @@ namespace Cake.Tests.Unit.Features { public sealed class VersionFeatureTests { - [Fact] - public void Should_Return_Success() - { - // Given - var console = new FakeConsole(); - var resolver = new FakeVersionResolver("1.2.3", "3.2.1"); - var feature = new VersionFeature(console, resolver); - - // When - var result = feature.Run(); - - // Then - Assert.Equal(0, result); - } - [Fact] public void Should_Output_Version_To_Console() { // Given var console = new FakeConsole(); var resolver = new FakeVersionResolver("1.2.3", "3.2.1"); - var feature = new VersionFeature(console, resolver); + var feature = new VersionFeature(resolver); // When - var result = feature.Run(); + feature.Run(console); // Then Assert.Contains("1.2.3", console.Messages); diff --git a/src/Cake.Tests/Unit/ProgramTests.cs b/src/Cake.Tests/Unit/ProgramTests.cs index 06a0889b23..7ae78de811 100644 --- a/src/Cake.Tests/Unit/ProgramTests.cs +++ b/src/Cake.Tests/Unit/ProgramTests.cs @@ -1,8 +1,10 @@ +using System.Linq; using System.Threading.Tasks; using Autofac; +using Cake.Cli; +using Cake.Core; using Cake.Core.Diagnostics; using Cake.Features.Building; -using Cake.Features.Introspection; using Cake.Tests.Fixtures; using NSubstitute; using Spectre.Cli; @@ -103,14 +105,14 @@ public async Task The_Version_Option_Should_Call_Version_Feature(params string[] { // Given var fixture = new ProgramFixture(); - var feature = Substitute.For(); + var feature = Substitute.For(); fixture.Overrides.Add(builder => builder.RegisterInstance(feature)); // When var result = await fixture.Run(args); // Then - feature.Received(1).Run(); + feature.Received(1).Run(fixture.Console); } [Theory] @@ -119,14 +121,14 @@ public async Task The_Info_Option_Should_Call_Info_Feature(params string[] args) { // Given var fixture = new ProgramFixture(); - var feature = Substitute.For(); + var feature = Substitute.For(); fixture.Overrides.Add(builder => builder.RegisterInstance(feature)); // When var result = await fixture.Run(args); // Then - feature.Received(1).Run(); + feature.Received(1).Run(fixture.Console); } } } diff --git a/src/Cake.sln b/src/Cake.sln index 50c028d729..acb0d1d5a5 100644 --- a/src/Cake.sln +++ b/src/Cake.sln @@ -15,6 +15,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{A01118B7 ..\build.cake = ..\build.cake ..\build.ps1 = ..\build.ps1 ..\build.sh = ..\build.sh + Shared.msbuild = Shared.msbuild EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "nuspec", "nuspec", "{B38B35F5-812A-4E4C-B267-71254A7EEF17}" @@ -54,7 +55,18 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cake.Frosting.Example", "Ca EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cake.Frosting.Tests", "Cake.Frosting.Tests\Cake.Frosting.Tests.csproj", "{26F4E738-122C-428D-A014-62A357F39023}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cake.Frosting.Template", "Cake.Frosting.Template\Cake.Frosting.Template.csproj", "{E5AE09CE-30D3-4C5D-97A7-A2B95C6898B8}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cake.Frosting.Template", "Cake.Frosting.Template\Cake.Frosting.Template.csproj", "{E5AE09CE-30D3-4C5D-97A7-A2B95C6898B8}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cake.Cli", "Cake.Cli\Cake.Cli.csproj", "{B155E9B1-5F3E-4AA2-B0E1-A9F43601F5CE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{83848400-2E27-4B57-B733-FAF357ED1AE6}" + ProjectSection(SolutionItems) = preProject + ..\build\credentials.cake = ..\build\credentials.cake + ..\build\packages.cake = ..\build\packages.cake + ..\build\parameters.cake = ..\build\parameters.cake + ..\build\paths.cake = ..\build\paths.cake + ..\build\version.cake = ..\build\version.cake + EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -118,6 +130,10 @@ Global {E5AE09CE-30D3-4C5D-97A7-A2B95C6898B8}.Debug|Any CPU.Build.0 = Debug|Any CPU {E5AE09CE-30D3-4C5D-97A7-A2B95C6898B8}.Release|Any CPU.ActiveCfg = Release|Any CPU {E5AE09CE-30D3-4C5D-97A7-A2B95C6898B8}.Release|Any CPU.Build.0 = Release|Any CPU + {B155E9B1-5F3E-4AA2-B0E1-A9F43601F5CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B155E9B1-5F3E-4AA2-B0E1-A9F43601F5CE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B155E9B1-5F3E-4AA2-B0E1-A9F43601F5CE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B155E9B1-5F3E-4AA2-B0E1-A9F43601F5CE}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -131,6 +147,7 @@ Global {2502455D-AEAC-4AD7-B29C-B1D3AFFF9E84} = {8615F41E-65C7-4BE5-AFD4-C9C78912AF80} {15A49638-46DE-4361-B86E-4873BA5EF136} = {8615F41E-65C7-4BE5-AFD4-C9C78912AF80} {26F4E738-122C-428D-A014-62A357F39023} = {8615F41E-65C7-4BE5-AFD4-C9C78912AF80} + {83848400-2E27-4B57-B733-FAF357ED1AE6} = {A01118B7-C6FC-4B0B-8B5C-F580F31FE57D} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {35585E1D-D23E-40C8-A01E-2E5FF5B41083} diff --git a/src/Cake/Cake.csproj b/src/Cake/Cake.csproj index 509bd3cad9..3c42478d6e 100644 --- a/src/Cake/Cake.csproj +++ b/src/Cake/Cake.csproj @@ -21,6 +21,7 @@ + @@ -32,6 +33,5 @@ - \ No newline at end of file diff --git a/src/Cake/Commands/DefaultCommand.cs b/src/Cake/Commands/DefaultCommand.cs index 27e00a06ee..0b1b045ca4 100644 --- a/src/Cake/Commands/DefaultCommand.cs +++ b/src/Cake/Commands/DefaultCommand.cs @@ -3,11 +3,11 @@ // See the LICENSE file in the project root for more information. using System; +using Cake.Cli; using Cake.Core; using Cake.Core.Diagnostics; using Cake.Features.Bootstrapping; using Cake.Features.Building; -using Cake.Features.Introspection; using Spectre.Cli; namespace Cake.Commands @@ -16,21 +16,24 @@ public sealed class DefaultCommand : Command { private readonly IBuildFeature _builder; private readonly IBootstrapFeature _bootstrapper; - private readonly IVersionFeature _version; - private readonly IInfoFeature _info; + private readonly ICakeVersionFeature _version; + private readonly ICakeInfoFeature _info; + private readonly IConsole _console; private readonly ICakeLog _log; public DefaultCommand( IBuildFeature builder, IBootstrapFeature bootstrapper, - IVersionFeature version, - IInfoFeature info, + ICakeVersionFeature version, + ICakeInfoFeature info, + IConsole console, ICakeLog log) { _builder = builder; _bootstrapper = bootstrapper; _version = version; _info = info; + _console = console; _log = log; } @@ -43,11 +46,13 @@ public override int Execute(CommandContext context, DefaultCommandSettings setti if (settings.ShowVersion) { - return _version.Run(); + _version.Run(_console); + return 0; } else if (settings.ShowInfo) { - return _info.Run(); + _info.Run(_console); + return 0; } // Get the build host type. @@ -79,7 +84,8 @@ public override int Execute(CommandContext context, DefaultCommandSettings setti } } - private static int LogException(ICakeLog log, T ex) where T : Exception + private static int LogException(ICakeLog log, T ex) + where T : Exception { log = log ?? new CakeBuildLog( new CakeConsole(new CakeEnvironment(new CakePlatform(), new CakeRuntime()))); @@ -99,6 +105,7 @@ private static int LogException(ICakeLog log, T ex) where T : Exception } } } + return 1; } diff --git a/src/Cake/Commands/DefaultCommandSettings.cs b/src/Cake/Commands/DefaultCommandSettings.cs index f0dcb5d492..6cc434ebc0 100644 --- a/src/Cake/Commands/DefaultCommandSettings.cs +++ b/src/Cake/Commands/DefaultCommandSettings.cs @@ -3,9 +3,9 @@ // See the LICENSE file in the project root for more information. using System.ComponentModel; +using Cake.Cli; using Cake.Core.Diagnostics; using Cake.Core.IO; -using Cake.Infrastructure.Converters; using Spectre.Cli; namespace Cake.Commands @@ -29,7 +29,7 @@ public sealed class DefaultCommandSettings : CommandSettings public bool Debug { get; set; } [CommandOption("-e|--exclusive")] - [Description("Execute a single task without any dependencies.")] + [Description("Executes the target task without any dependencies.")] public bool Exclusive { get; set; } [CommandOption("--dryrun|--noop|--whatif")] @@ -44,11 +44,11 @@ public sealed class DefaultCommandSettings : CommandSettings [Description("Skips bootstrapping when running build.")] public bool SkipBootstrap { get; set; } - [CommandOption("--showdescription|--description")] - [Description("Shows description about tasks.")] + [CommandOption("--description|--showdescription")] + [Description("Shows task descriptions.")] public bool ShowDescription { get; set; } - [CommandOption("--showtree|--tree")] + [CommandOption("--tree|--showtree")] [Description("Shows the task dependency tree.")] public bool ShowTree { get; set; } diff --git a/src/Cake/Features/Building/BuildFeature.cs b/src/Cake/Features/Building/BuildFeature.cs index b8e5c6c7ac..60d2537fa8 100644 --- a/src/Cake/Features/Building/BuildFeature.cs +++ b/src/Cake/Features/Building/BuildFeature.cs @@ -6,13 +6,12 @@ using System.Linq; using System.Threading; using Autofac; +using Cake.Cli; using Cake.Core; using Cake.Core.Composition; using Cake.Core.Diagnostics; using Cake.Core.IO; using Cake.Core.Scripting; -using Cake.Features.Bootstrapping; -using Cake.Features.Building.Hosts; using Cake.Infrastructure; using Cake.Infrastructure.Composition; using Cake.Infrastructure.Scripting; diff --git a/src/Cake/Features/Building/Hosts/BuildScriptHost.cs b/src/Cake/Features/Building/Hosts/BuildScriptHost.cs deleted file mode 100644 index 2e78557466..0000000000 --- a/src/Cake/Features/Building/Hosts/BuildScriptHost.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Threading.Tasks; -using Cake.Core; -using Cake.Core.Diagnostics; -using Cake.Core.Scripting; - -namespace Cake.Features.Building.Hosts -{ - /// - /// The script host used to execute Cake scripts. - /// - public sealed class BuildScriptHost : ScriptHost - { - private readonly ICakeReportPrinter _reportPrinter; - private readonly ICakeLog _log; - - /// - /// Initializes a new instance of the class. - /// - /// The engine. - /// The context. - /// The report printer. - /// The log. - public BuildScriptHost( - ICakeEngine engine, - ICakeContext context, - ICakeReportPrinter reportPrinter, - ICakeLog log) : base(engine, context) - { - _reportPrinter = reportPrinter; - _log = log; - } - - /// - public override async Task RunTargetAsync(string target) - { - Settings.SetTarget(target); - - var strategy = new DefaultExecutionStrategy(_log); - var report = await Engine.RunTargetAsync(Context, strategy, Settings).ConfigureAwait(false); - if (report != null && !report.IsEmpty) - { - _reportPrinter.Write(report); - } - - return report; - } - } -} \ No newline at end of file diff --git a/src/Cake/Features/Introspection/InfoFeature.cs b/src/Cake/Features/Introspection/InfoFeature.cs deleted file mode 100644 index 51b9d27ef5..0000000000 --- a/src/Cake/Features/Introspection/InfoFeature.cs +++ /dev/null @@ -1,68 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Cake.Core; - -namespace Cake.Features.Introspection -{ - public interface IInfoFeature - { - int Run(); - } - - public sealed class InfoFeature : IInfoFeature - { - private readonly IConsole _console; - private readonly IVersionResolver _resolver; - - public InfoFeature(IConsole console, IVersionResolver resolver) - { - _console = console; - _resolver = resolver; - } - - public int Run() - { - var version = _resolver.GetVersion(); - var product = _resolver.GetProductVersion(); - - _console.WriteLine(); - _console.WriteLine(@" +## #;;'"); - _console.WriteLine(@" #;;# .+;;;;+,"); - _console.WriteLine(@" '+;;#;,+';;;;;'#."); - _console.WriteLine(@" ++'''';;;;;;;;;;# ;#;"); - _console.WriteLine(@" ##';;;;++'+#;;;;;'. `#:"); - _console.WriteLine(@" ;# '+'';;;;;;;;;'#` #."); - _console.WriteLine(@" `#, .'++;;;;;':..........#"); - _console.WriteLine(@" '+ `.........';;;;':.........#"); - _console.WriteLine(@" #..................+;;;;;':........#"); - _console.WriteLine(@" #..................#';;;;;'+''''''.#"); - _console.WriteLine(@" #.......,:;''''''''##';;;;;'+'''''#,"); - _console.WriteLine(@" #''''''''''''''''''###';;;;;;+''''#"); - _console.WriteLine(@" #''''''''''''''''''####';;;;;;#'''#"); - _console.WriteLine(@" #''''''''''''''''''#####';;;;;;#''#"); - _console.WriteLine(@" #''''''''''''''''''######';;;;;;#'#"); - _console.WriteLine(@" #''''''''''''''''''#######';;;;;;##"); - _console.WriteLine(@" #''''''''''''''''''########';;;;;;#"); - _console.WriteLine(@" #''''''''''''++####+;#######';;;;;;#"); - _console.WriteLine(@" #+####':,` ,#####';;;;;;'"); - _console.WriteLine(@" +##'''''+."); - - _console.ForegroundColor = System.ConsoleColor.Yellow; - _console.WriteLine(@" ___ _ ___ _ _ _ "); - _console.WriteLine(@" / __\__ _| | _____ / __\_ _(_) | __| |"); - _console.WriteLine(@" / / / _` | |/ / _ \/__\// | | | | |/ _` |"); - _console.WriteLine(@"/ /___ (_| | < __/ \/ \ |_| | | | (_| |"); - _console.WriteLine(@"\____/\__,_|_|\_\___\_____/\__,_|_|_|\__,_|"); - _console.ResetColor(); - - _console.WriteLine(); - _console.WriteLine(@"Version: {0}", version); - _console.WriteLine(@"Details: {0}", string.Join("\n ", product.Split('/'))); - _console.WriteLine(); - - return 0; - } - } -} diff --git a/src/Cake/Features/Introspection/VersionFeature.cs b/src/Cake/Features/Introspection/VersionFeature.cs deleted file mode 100644 index bbb24b20b0..0000000000 --- a/src/Cake/Features/Introspection/VersionFeature.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Cake.Core; - -namespace Cake.Features.Introspection -{ - public interface IVersionFeature - { - int Run(); - } - - public sealed class VersionFeature : IVersionFeature - { - private readonly IConsole _console; - private readonly IVersionResolver _resolver; - - public VersionFeature(IConsole console, IVersionResolver resolver) - { - _console = console; - _resolver = resolver; - } - - public int Run() - { - _console.WriteLine(_resolver.GetVersion()); - return 0; - } - } -} diff --git a/src/Cake/Infrastructure/ContainerConfigurator.cs b/src/Cake/Infrastructure/ContainerConfigurator.cs index a08221b147..a3d9e8e53d 100644 --- a/src/Cake/Infrastructure/ContainerConfigurator.cs +++ b/src/Cake/Infrastructure/ContainerConfigurator.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Cake.Cli; using Cake.Common.Modules; using Cake.Core; using Cake.Core.Composition; @@ -9,7 +10,6 @@ using Cake.Core.Diagnostics; using Cake.Core.Modules; using Cake.Core.Scripting; -using Cake.Features.Building.Hosts; using Cake.Infrastructure.Scripting; using Cake.NuGet; using Spectre.Cli; diff --git a/src/Cake/Program.cs b/src/Cake/Program.cs index 671eb8152b..e66450d7de 100644 --- a/src/Cake/Program.cs +++ b/src/Cake/Program.cs @@ -5,16 +5,15 @@ using System; using System.Threading.Tasks; using Autofac; +using Cake.Cli; using Cake.Commands; using Cake.Core; using Cake.Core.Diagnostics; using Cake.Core.IO; using Cake.Features.Bootstrapping; using Cake.Features.Building; -using Cake.Features.Introspection; using Cake.Infrastructure; using Cake.Infrastructure.Composition; -using Cake.Infrastructure.Converters; using Spectre.Cli; namespace Cake @@ -77,8 +76,8 @@ private ITypeRegistrar BuildTypeRegistrar() // Features builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().SingleInstance(); - builder.RegisterType().As().SingleInstance(); - builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); // Core builder.RegisterType().As().SingleInstance();