From 9209b41db8ebae466517714a8a4ced33991c01b9 Mon Sep 17 00:00:00 2001 From: Alex Abenoja Date: Thu, 12 Jan 2017 14:03:58 -0700 Subject: [PATCH] Initial spike --- .gitignore | 275 +++++++++++++ .paket/paket.bootstrapper.exe | Bin 0 -> 33840 bytes build.cake | 47 +++ build.ps1 | 189 +++++++++ paket.dependencies | 7 + paket.lock | 19 + .../Cake.Parallel.Tests.csproj | 137 +++++++ .../ParallelGraphExtensionTests.cs | 97 +++++ .../Properties/AssemblyInfo.cs | 36 ++ src/Cake.Parallel.Tests/paket.references | 4 + src/Cake.Parallel.sln | 28 ++ src/Cake.Parallel/Cake.Parallel.Module.csproj | 70 ++++ src/Cake.Parallel/CakeGraph.cs | 80 ++++ src/Cake.Parallel/ParallelCakeEngine.cs | 381 ++++++++++++++++++ src/Cake.Parallel/ParallelCakeModule.cs | 13 + src/Cake.Parallel/ParallelGraphBuilder.cs | 37 ++ src/Cake.Parallel/ParallelGraphExtensions.cs | 45 +++ src/Cake.Parallel/Properties/AssemblyInfo.cs | 39 ++ src/Cake.Parallel/paket.references | 1 + tools/packages.config | 4 + 20 files changed, 1509 insertions(+) create mode 100644 .gitignore create mode 100644 .paket/paket.bootstrapper.exe create mode 100644 build.cake create mode 100644 build.ps1 create mode 100644 paket.dependencies create mode 100644 paket.lock create mode 100644 src/Cake.Parallel.Tests/Cake.Parallel.Tests.csproj create mode 100644 src/Cake.Parallel.Tests/ParallelGraphExtensionTests.cs create mode 100644 src/Cake.Parallel.Tests/Properties/AssemblyInfo.cs create mode 100644 src/Cake.Parallel.Tests/paket.references create mode 100644 src/Cake.Parallel.sln create mode 100644 src/Cake.Parallel/Cake.Parallel.Module.csproj create mode 100644 src/Cake.Parallel/CakeGraph.cs create mode 100644 src/Cake.Parallel/ParallelCakeEngine.cs create mode 100644 src/Cake.Parallel/ParallelCakeModule.cs create mode 100644 src/Cake.Parallel/ParallelGraphBuilder.cs create mode 100644 src/Cake.Parallel/ParallelGraphExtensions.cs create mode 100644 src/Cake.Parallel/Properties/AssemblyInfo.cs create mode 100644 src/Cake.Parallel/paket.references create mode 100644 tools/packages.config diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..64e4aff --- /dev/null +++ b/.gitignore @@ -0,0 +1,275 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ +**/Properties/launchSettings.json + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignoreable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +tools/** +!tools/packages.config diff --git a/.paket/paket.bootstrapper.exe b/.paket/paket.bootstrapper.exe new file mode 100644 index 0000000000000000000000000000000000000000..60d1d179ca5b275cf366670e20a6a4ce2730ac25 GIT binary patch literal 33840 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%0AoCke-T9dki<8F;s6 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 + { + if (StartProcess(".paket/paket.bootstrapper.exe") != 0) + Error("Unable to fetch paket.exe"); + + paketPath = ".paket/paket.exe"; + }); + +Task("paket-restore") + .IsDependentOn("bootstrap-paket") + .Does(() => + { + if (StartProcess(paketPath, "restore") != 0) + Error("Paket restore failed"); + }); + +Task("compile") + .IsDependentOn("paket-restore") + .Does(() => + { + DotNetBuild("./src/Cake.Parallel.sln"); + }); + +Task("xUnit") + .IsDependentOn("compile") + .Does(() => + { + XUnit2("./src/Cake.Parallel.Tests/bin/**/Cake.Parallel.Tests.dll"); + }); + +Task("Default") + .IsDependentOn("xUnit") + .Does(() => + { + Information("It works!"); + }); + +RunTarget(target); diff --git a/build.ps1 b/build.ps1 new file mode 100644 index 0000000..44de579 --- /dev/null +++ b/build.ps1 @@ -0,0 +1,189 @@ +########################################################################## +# This is the Cake bootstrapper script for PowerShell. +# This file was downloaded from https://github.com/cake-build/resources +# Feel free to change this file to fit your needs. +########################################################################## + +<# + +.SYNOPSIS +This is a Powershell script to bootstrap a Cake build. + +.DESCRIPTION +This Powershell script will download NuGet if missing, restore NuGet tools (including Cake) +and execute your Cake build script with the parameters you provide. + +.PARAMETER Script +The build script to execute. +.PARAMETER Target +The build script target to run. +.PARAMETER Configuration +The build configuration to use. +.PARAMETER Verbosity +Specifies the amount of information to be displayed. +.PARAMETER Experimental +Tells Cake to use the latest Roslyn release. +.PARAMETER WhatIf +Performs a dry run of the build script. +No tasks will be executed. +.PARAMETER Mono +Tells Cake to use the Mono scripting engine. +.PARAMETER SkipToolPackageRestore +Skips restoring of packages. +.PARAMETER ScriptArgs +Remaining arguments are added here. + +.LINK +http://cakebuild.net + +#> + +[CmdletBinding()] +Param( + [string]$Script = "build.cake", + [string]$Target = "Default", + [ValidateSet("Release", "Debug")] + [string]$Configuration = "Release", + [ValidateSet("Quiet", "Minimal", "Normal", "Verbose", "Diagnostic")] + [string]$Verbosity = "Verbose", + [switch]$Experimental, + [Alias("DryRun","Noop")] + [switch]$WhatIf, + [switch]$Mono, + [switch]$SkipToolPackageRestore, + [Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)] + [string[]]$ScriptArgs +) + +[Reflection.Assembly]::LoadWithPartialName("System.Security") | Out-Null +function MD5HashFile([string] $filePath) +{ + if ([string]::IsNullOrEmpty($filePath) -or !(Test-Path $filePath -PathType Leaf)) + { + return $null + } + + [System.IO.Stream] $file = $null; + [System.Security.Cryptography.MD5] $md5 = $null; + try + { + $md5 = [System.Security.Cryptography.MD5]::Create() + $file = [System.IO.File]::OpenRead($filePath) + return [System.BitConverter]::ToString($md5.ComputeHash($file)) + } + finally + { + if ($file -ne $null) + { + $file.Dispose() + } + } +} + +Write-Host "Preparing to run build script..." + +if(!$PSScriptRoot){ + $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent +} + +$TOOLS_DIR = Join-Path $PSScriptRoot "tools" +$NUGET_EXE = Join-Path $TOOLS_DIR "nuget.exe" +$CAKE_EXE = Join-Path $TOOLS_DIR "Cake/Cake.exe" +$NUGET_URL = "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe" +$PACKAGES_CONFIG = Join-Path $TOOLS_DIR "packages.config" +$PACKAGES_CONFIG_MD5 = Join-Path $TOOLS_DIR "packages.config.md5sum" + +# Should we use mono? +$UseMono = ""; +if($Mono.IsPresent) { + Write-Verbose -Message "Using the Mono based scripting engine." + $UseMono = "-mono" +} + +# Should we use the new Roslyn? +$UseExperimental = ""; +if($Experimental.IsPresent -and !($Mono.IsPresent)) { + Write-Verbose -Message "Using experimental version of Roslyn." + $UseExperimental = "-experimental" +} + +# Is this a dry run? +$UseDryRun = ""; +if($WhatIf.IsPresent) { + $UseDryRun = "-dryrun" +} + +# Make sure tools folder exists +if ((Test-Path $PSScriptRoot) -and !(Test-Path $TOOLS_DIR)) { + Write-Verbose -Message "Creating tools directory..." + New-Item -Path $TOOLS_DIR -Type directory | out-null +} + +# Make sure that packages.config exist. +if (!(Test-Path $PACKAGES_CONFIG)) { + Write-Verbose -Message "Downloading packages.config..." + try { (New-Object System.Net.WebClient).DownloadFile("http://cakebuild.net/download/bootstrapper/packages", $PACKAGES_CONFIG) } catch { + Throw "Could not download packages.config." + } +} + +# Try find NuGet.exe in path if not exists +if (!(Test-Path $NUGET_EXE)) { + Write-Verbose -Message "Trying to find nuget.exe in PATH..." + $existingPaths = $Env:Path -Split ';' | Where-Object { (![string]::IsNullOrEmpty($_)) -and (Test-Path $_) } + $NUGET_EXE_IN_PATH = Get-ChildItem -Path $existingPaths -Filter "nuget.exe" | Select -First 1 + if ($NUGET_EXE_IN_PATH -ne $null -and (Test-Path $NUGET_EXE_IN_PATH.FullName)) { + Write-Verbose -Message "Found in PATH at $($NUGET_EXE_IN_PATH.FullName)." + $NUGET_EXE = $NUGET_EXE_IN_PATH.FullName + } +} + +# Try download NuGet.exe if not exists +if (!(Test-Path $NUGET_EXE)) { + Write-Verbose -Message "Downloading NuGet.exe..." + try { + (New-Object System.Net.WebClient).DownloadFile($NUGET_URL, $NUGET_EXE) + } catch { + Throw "Could not download NuGet.exe." + } +} + +# Save nuget.exe path to environment to be available to child processed +$ENV:NUGET_EXE = $NUGET_EXE + +# Restore tools from NuGet? +if(-Not $SkipToolPackageRestore.IsPresent) { + Push-Location + Set-Location $TOOLS_DIR + + # Check for changes in packages.config and remove installed tools if true. + [string] $md5Hash = MD5HashFile($PACKAGES_CONFIG) + if((!(Test-Path $PACKAGES_CONFIG_MD5)) -Or + ($md5Hash -ne (Get-Content $PACKAGES_CONFIG_MD5 ))) { + Write-Verbose -Message "Missing or changed package.config hash..." + Remove-Item * -Recurse -Exclude packages.config,nuget.exe + } + + Write-Verbose -Message "Restoring tools from NuGet..." + $NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$TOOLS_DIR`"" + + if ($LASTEXITCODE -ne 0) { + Throw "An error occured while restoring NuGet tools." + } + else + { + $md5Hash | Out-File $PACKAGES_CONFIG_MD5 -Encoding "ASCII" + } + Write-Verbose -Message ($NuGetOutput | out-string) + Pop-Location +} + +# Make sure that Cake has been installed. +if (!(Test-Path $CAKE_EXE)) { + Throw "Could not find Cake.exe at $CAKE_EXE" +} + +# Start Cake +Write-Host "Running build script..." +Invoke-Expression "& `"$CAKE_EXE`" `"$Script`" -target=`"$Target`" -configuration=`"$Configuration`" -verbosity=`"$Verbosity`" $UseMono $UseDryRun $UseExperimental $ScriptArgs" +exit $LASTEXITCODE \ No newline at end of file diff --git a/paket.dependencies b/paket.dependencies new file mode 100644 index 0000000..b70e5d6 --- /dev/null +++ b/paket.dependencies @@ -0,0 +1,7 @@ +framework: net461 +source http://nuget.org/api/v2 + +nuget Cake.Core 0.15.0 +nuget Cake.Testing +nuget Shouldly +nuget xunit \ No newline at end of file diff --git a/paket.lock b/paket.lock new file mode 100644 index 0000000..1b0a8b7 --- /dev/null +++ b/paket.lock @@ -0,0 +1,19 @@ +FRAMEWORK: NET461 +NUGET + remote: http://www.nuget.org/api/v2 + Cake.Core (0.15) + Cake.Testing (0.15) + Cake.Core (>= 0.15) + Shouldly (2.8.2) + xunit (2.1) + xunit.assert (2.1) + xunit.core (2.1) + xunit.abstractions (2.0) + xunit.assert (2.1) + xunit.core (2.1) + xunit.extensibility.core (2.1) + xunit.extensibility.execution (2.1) + xunit.extensibility.core (2.1) + xunit.abstractions (2.0) + xunit.extensibility.execution (2.1) + xunit.extensibility.core (2.1) diff --git a/src/Cake.Parallel.Tests/Cake.Parallel.Tests.csproj b/src/Cake.Parallel.Tests/Cake.Parallel.Tests.csproj new file mode 100644 index 0000000..220d6aa --- /dev/null +++ b/src/Cake.Parallel.Tests/Cake.Parallel.Tests.csproj @@ -0,0 +1,137 @@ + + + + + Debug + AnyCPU + {E9425295-5158-4AF4-9982-8ED8978704A6} + Library + Properties + Cake.Parallel.Tests + Cake.Parallel.Tests + v4.6.1 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + {f7b7373b-3c5a-4061-9bfd-8b82b16db5b8} + Cake.Parallel.Module + + + + + + + + + ..\..\packages\Cake.Core\lib\net45\Cake.Core.dll + True + True + + + + + + + + + ..\..\packages\Cake.Testing\lib\net45\Cake.Testing.dll + True + True + + + + + + + + + ..\..\packages\Shouldly\lib\net451\Shouldly.dll + True + True + + + + + + + + + ..\..\packages\xunit.abstractions\lib\net35\xunit.abstractions.dll + True + True + + + + + + + + + ..\..\packages\xunit.assert\lib\portable-net45+win8+wp8+wpa81\xunit.assert.dll + True + True + + + + + + + + + ..\..\packages\xunit.extensibility.core\lib\portable-net45+win8+wp8+wpa81\xunit.core.dll + True + True + + + + + + + + + ..\..\packages\xunit.extensibility.execution\lib\net45\xunit.execution.desktop.dll + True + True + + + + + \ No newline at end of file diff --git a/src/Cake.Parallel.Tests/ParallelGraphExtensionTests.cs b/src/Cake.Parallel.Tests/ParallelGraphExtensionTests.cs new file mode 100644 index 0000000..9b6a5e6 --- /dev/null +++ b/src/Cake.Parallel.Tests/ParallelGraphExtensionTests.cs @@ -0,0 +1,97 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Cake.Core; +using Cake.Parallel.Module; +using Shouldly; +using Xunit; + +namespace Cake.Parallel.Tests +{ + public class ParallelGraphExtensionTests + { + private readonly CakeGraph _graph; + private readonly List _tasks; + private readonly List _taskResults; + + public ParallelGraphExtensionTests() + { + _taskResults = new List(); + _tasks = new List(defineTasks()); + _graph = ParallelGraphBuilder.Build(_tasks); + } + + [Fact] + public void Throws_On_Circular_References() + { + Should.Throw(() => + { + _graph.Traverse("circ-c", _ => {}); + }); + } + + [Fact] + public async Task Should_Execute_Dependencies_Asynchronously() + { + await _graph.Traverse("e", nodeName => + { + var task = (ActionTask)_tasks.First(_ => _.Name == nodeName); + task.Actions.ForEach(action => action(null)); + }); + _taskResults.ShouldNotBeEmpty(); + } + + private IEnumerable defineTasks() + { + var circularTaskA = new ActionTask("circ-a"); + new CakeTaskBuilder(circularTaskA) + .IsDependentOn("circ-b"); + yield return circularTaskA; + + var circularTaskB = new ActionTask("circ-b"); + new CakeTaskBuilder(circularTaskB) + .IsDependentOn("circ-c"); + yield return circularTaskB; + + var circularTaskC = new ActionTask("circ-c"); + new CakeTaskBuilder(circularTaskC) + .IsDependentOn("circ-a"); + yield return circularTaskC; + + var taskA = new ActionTask("a"); + new CakeTaskBuilder(taskA) + .Does(() => + { + Thread.Sleep(1000); + _taskResults.Add("a"); + }); + yield return taskA; + + var taskB = new ActionTask("b"); + new CakeTaskBuilder(taskB) + .IsDependentOn("a") + .Does(() => _taskResults.Add("b")); + yield return taskB; + + var taskC = new ActionTask("c"); + new CakeTaskBuilder(taskC) + .IsDependentOn("a") + .Does(() => _taskResults.Add("c")); + yield return taskC; + + var taskD = new ActionTask("d"); + new CakeTaskBuilder(taskD) + .Does(() => _taskResults.Add("d")); + yield return taskD; + + var taskE = new ActionTask("e"); + new CakeTaskBuilder(taskE) + .IsDependentOn("b") + .IsDependentOn("c") + .IsDependentOn("d") + .Does(() => _taskResults.Add("e")); + yield return taskE; + } + } +} diff --git a/src/Cake.Parallel.Tests/Properties/AssemblyInfo.cs b/src/Cake.Parallel.Tests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..d0f47e9 --- /dev/null +++ b/src/Cake.Parallel.Tests/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Cake.Parallel.Tests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Extend Health, Inc.")] +[assembly: AssemblyProduct("Cake.Parallel.Tests")] +[assembly: AssemblyCopyright("Copyright © Extend Health, Inc. 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("e9425295-5158-4af4-9982-8ed8978704a6")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/Cake.Parallel.Tests/paket.references b/src/Cake.Parallel.Tests/paket.references new file mode 100644 index 0000000..dbe6efc --- /dev/null +++ b/src/Cake.Parallel.Tests/paket.references @@ -0,0 +1,4 @@ +xunit +Cake.Core +Cake.Testing +Shouldly \ No newline at end of file diff --git a/src/Cake.Parallel.sln b/src/Cake.Parallel.sln new file mode 100644 index 0000000..cf8a25c --- /dev/null +++ b/src/Cake.Parallel.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25123.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cake.Parallel.Module", "Cake.Parallel\Cake.Parallel.Module.csproj", "{F7B7373B-3C5A-4061-9BFD-8B82B16DB5B8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cake.Parallel.Tests", "Cake.Parallel.Tests\Cake.Parallel.Tests.csproj", "{E9425295-5158-4AF4-9982-8ED8978704A6}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F7B7373B-3C5A-4061-9BFD-8B82B16DB5B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F7B7373B-3C5A-4061-9BFD-8B82B16DB5B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F7B7373B-3C5A-4061-9BFD-8B82B16DB5B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F7B7373B-3C5A-4061-9BFD-8B82B16DB5B8}.Release|Any CPU.Build.0 = Release|Any CPU + {E9425295-5158-4AF4-9982-8ED8978704A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E9425295-5158-4AF4-9982-8ED8978704A6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E9425295-5158-4AF4-9982-8ED8978704A6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E9425295-5158-4AF4-9982-8ED8978704A6}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/src/Cake.Parallel/Cake.Parallel.Module.csproj b/src/Cake.Parallel/Cake.Parallel.Module.csproj new file mode 100644 index 0000000..e2bf43d --- /dev/null +++ b/src/Cake.Parallel/Cake.Parallel.Module.csproj @@ -0,0 +1,70 @@ + + + + + Debug + AnyCPU + {F7B7373B-3C5A-4061-9BFD-8B82B16DB5B8} + Library + Properties + Cake.Parallel.Module + Cake.Parallel.Module + v4.6.1 + 512 + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + ..\..\packages\Cake.Core\lib\net45\Cake.Core.dll + True + True + + + + + \ No newline at end of file diff --git a/src/Cake.Parallel/CakeGraph.cs b/src/Cake.Parallel/CakeGraph.cs new file mode 100644 index 0000000..37c4359 --- /dev/null +++ b/src/Cake.Parallel/CakeGraph.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Cake.Core; + +namespace Cake.Parallel.Module +{ + public class CakeGraph + { + private readonly List _nodes; + private readonly List _edges; + + public CakeGraph() + { + _nodes = new List(); + _edges = new List(); + } + + public IReadOnlyList Nodes => _nodes; + + public IReadOnlyList Edges => _edges; + + public void Add(string node) + { + if (node == null) + { + throw new ArgumentNullException(nameof(node)); + } + if (_nodes.Any(x => x == node)) + { + throw new CakeException("Node has already been added to graph."); + } + _nodes.Add(node); + } + + public void Connect(string start, string end) + { + if (start.Equals(end, StringComparison.OrdinalIgnoreCase)) + { + throw new CakeException("Reflexive edges in graph are not allowed."); + } + if (_edges.Any(x => x.Start.Equals(end, StringComparison.OrdinalIgnoreCase) + && x.End.Equals(start, StringComparison.OrdinalIgnoreCase))) + { + throw new CakeException("Unidirectional edges in graph are not allowed."); + } + if (_edges.Any(x => x.Start.Equals(start, StringComparison.OrdinalIgnoreCase) + && x.End.Equals(end, StringComparison.OrdinalIgnoreCase))) + { + return; + } + if (_nodes.All(x => !x.Equals(start, StringComparison.OrdinalIgnoreCase))) + { + _nodes.Add(start); + } + if (_nodes.All(x => !x.Equals(end, StringComparison.OrdinalIgnoreCase))) + { + _nodes.Add(end); + } + _edges.Add(new CakeGraphEdge(start, end)); + } + + public bool Exist(string name) + { + return _nodes.Any(x => x.Equals(name, StringComparison.OrdinalIgnoreCase)); + } + } + + public class CakeGraphEdge + { + public string Start { get; set; } + public string End { get; set; } + + public CakeGraphEdge(string start, string end) + { + Start = start; + End = end; + } + } +} diff --git a/src/Cake.Parallel/ParallelCakeEngine.cs b/src/Cake.Parallel/ParallelCakeEngine.cs new file mode 100644 index 0000000..57f3594 --- /dev/null +++ b/src/Cake.Parallel/ParallelCakeEngine.cs @@ -0,0 +1,381 @@ +// 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. + +// originally copied from https://github.com/cake-build/cake/blob/b5de9ae6219510330f6926b9e779307be88ff669/src/Cake.Core/CakeEngine.cs + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Linq; +using System.Reflection; +using Cake.Core; +using Cake.Core.Diagnostics; + +namespace Cake.Parallel.Module +{ + public class ParallelCakeEngine : ICakeEngine + { + private readonly List _tasks; + private readonly ICakeLog _logger; + private Action _setupAction; + private Action _teardownAction; + private Action _taskSetupAction; + private Action _taskTeardownAction; + + public ParallelCakeEngine(ICakeLog logger) + { + logger.Warning("Using experimental shit here"); + + _logger = logger; + _tasks = new List(); + } + + public CakeTaskBuilder RegisterTask(string name) + { + if (_tasks.Any(x => x.Name.Equals(name, StringComparison.OrdinalIgnoreCase))) + { + const string format = "Another task with the name '{0}' has already been added."; + throw new CakeException(string.Format(CultureInfo.InvariantCulture, format, name)); + } + + var task = new ActionTask(name); + _tasks.Add(task); + + return new CakeTaskBuilder(task); + } + + public void RegisterSetupAction(Action action) + { + _setupAction = action; + } + + public void RegisterTeardownAction(Action action) + { + _teardownAction = action; + } + + public CakeReport RunTarget(ICakeContext context, IExecutionStrategy strategy, string target) + { + if (target == null) + { + throw new ArgumentNullException(nameof(target)); + } + if (strategy == null) + { + throw new ArgumentNullException(nameof(strategy)); + } + + var graph = ParallelGraphBuilder.Build(_tasks); + + // Make sure target exist. + if (!graph.Exist(target)) + { + const string format = "The target '{0}' was not found."; + throw new CakeException(string.Format(CultureInfo.InvariantCulture, format, target)); + } + + // This isn't pretty, but we need to keep track of exceptions thrown + // while running a setup action, or a task. We do this since we don't + // want to throw teardown exceptions if an exception was thrown previously. + var exceptionWasThrown = false; + Exception thrownException = null; + + try + { + performSetup(strategy, context); + + var report = new CakeReport(); + + var targetTask = graph.Traverse(target, taskName => + { + var task = _tasks.FirstOrDefault(_ => _.Name.Equals(taskName, StringComparison.OrdinalIgnoreCase)); + Debug.Assert(task != null, "Node should not be null"); + + var isTarget = task.Name.Equals(target, StringComparison.OrdinalIgnoreCase); + + if (shouldExecuteTask(context, task, isTarget)) + { + executeTask(context, strategy, task, report); + } + else + { + skipTask(context, strategy, task, report); + } + }); + + var stopwatch = Stopwatch.StartNew(); + targetTask.Wait(); + _logger.Information($"All tasks completed in {stopwatch.Elapsed}"); + + return report; + } + catch (Exception ex) + { + exceptionWasThrown = true; + thrownException = ex; + throw; + } + finally + { + performTeardown(strategy, context, exceptionWasThrown, thrownException); + } + } + + public void RegisterTaskSetupAction(Action action) + { + _taskSetupAction = action; + } + + public void RegisterTaskTeardownAction(Action action) + { + _taskTeardownAction = action; + } + + public event EventHandler Setup; + public event EventHandler Teardown; + public event EventHandler TaskSetup; + public event EventHandler TaskTeardown; + + private void performSetup(IExecutionStrategy strategy, ICakeContext context) + { + publishEvent(Setup, new SetupEventArgs(context)); + if (_setupAction != null) + { + strategy.PerformSetup(_setupAction, context); + } + } + + private void performTeardown(IExecutionStrategy strategy, ICakeContext context, bool exceptionWasThrown, Exception thrownException) + { + var teardownContext = new TeardownContext(context, thrownException); + publishEvent(Teardown, new TeardownEventArgs(teardownContext)); + if (_teardownAction != null) + { + try + { + strategy.PerformTeardown(_teardownAction, teardownContext); + } + catch (Exception ex) + { + _logger.Error("An error occurred in the custom teardown action."); + if (!exceptionWasThrown) + { + // If no other exception was thrown, we throw this one. + throw; + } + _logger.Error("Teardown error: {0}", ex.ToString()); + } + } + } + + private void publishEvent(EventHandler eventHandler, T eventArgs) where T : EventArgs + { + if (eventHandler != null) + { + foreach (var @delegate in eventHandler.GetInvocationList()) + { + var handler = (EventHandler) @delegate; + try + { + handler(this, eventArgs); + } + catch (Exception e) + { + _logger.Error($"An error occurred in the event handler {handler.GetMethodInfo().Name}: {e.Message}"); + throw; + } + } + } + } + + private bool shouldExecuteTask(ICakeContext context, CakeTask task, bool isTarget) + { + foreach (var criteria in task.Criterias) + { + if (!criteria(context)) + { + if (!isTarget) return false; + + throw new CakeException($"Could not reach target '{task.Name}' since it was skipped due to a criteria"); + } + } + return true; + } + + private void executeTask(ICakeContext context, IExecutionStrategy strategy, CakeTask task, CakeReport report) + { + _logger.Verbose($"Starting task {task.Name}"); + var stopwatch = Stopwatch.StartNew(); + + performTaskSetup(context, strategy, task, false); + + var execptionWasThrown = false; + try + { + strategy.Execute(task, context); + } + catch (Exception exception) + { + execptionWasThrown = true; + _logger.Error($"An error occurred when executing task '{task.Name}'."); + + if (task.ErrorReporter != null) + { + reportErrors(strategy, task.ErrorReporter, exception); + } + + if (task.ErrorHandler != null) + { + handleErrors(strategy, task.ErrorHandler, exception); + } + else + { + throw; + } + } + finally + { + strategy.InvokeFinally(task.FinallyHandler); + + performTaskTeardown(context, strategy, task, stopwatch.Elapsed, false, execptionWasThrown); + } + + if (isDelegatedTask(task)) + { + report.AddDelegated(task.Name, stopwatch.Elapsed); + } + else + { + report.Add(task.Name, stopwatch.Elapsed); + } + } + + private void skipTask(ICakeContext context, IExecutionStrategy strategy, CakeTask task, CakeReport report) + { + performTaskSetup(context, strategy, task, true); + strategy.Skip(task); + performTaskTeardown(context, strategy, task, TimeSpan.Zero, true, false); + + report.AddSkipped(task.Name); + } + + private bool isDelegatedTask(CakeTask task) + { + var actionTask = task as ActionTask; + + return actionTask != null && !actionTask.Actions.Any(); + } + + private void reportErrors(IExecutionStrategy strategy, Action errorReporter, Exception taskException) + { + try + { + strategy.ReportErrors(errorReporter, taskException); + } + catch { } + } + + private void handleErrors(IExecutionStrategy strategy, Action errorHandler, Exception exception) + { + try + { + strategy.HandleErrors(errorHandler, exception); + } + catch (Exception errorHandlerException) + { + if (errorHandlerException != exception) + { + _logger.Error($"Error: {exception.Message}"); + } + throw; + } + } + + public void performTaskSetup(ICakeContext context, IExecutionStrategy strategy, CakeTask task, bool skipped) + { + var taskSetupContext = new TaskSetupContext(context, task); + publishEvent(TaskSetup, new TaskSetupEventArgs(taskSetupContext)); + + if (_taskSetupAction != null) + { + try + { + strategy.PerformTaskSetup(_taskSetupAction, taskSetupContext); + } + catch + { + performTaskTeardown(context, strategy, task, TimeSpan.Zero, skipped, true); + throw; + } + } + } + + public void performTaskTeardown(ICakeContext context, IExecutionStrategy strategy, CakeTask task, TimeSpan duration, bool skipped, bool exceptionWasThrown) + { + var taskTeardownContext = new TaskTeardownContext(context, task, duration, skipped); + publishEvent(TaskTeardown, new TaskTeardownEventArgs(taskTeardownContext)); + + if (_taskTeardownAction != null) + { + try + { + strategy.PerformTaskTeardown(_taskTeardownAction, taskTeardownContext); + } + catch (Exception ex) + { + _logger.Error($"An error occurred in the custom task teardown action ({task.Name})."); + if (!exceptionWasThrown) + { + // If no other exception was thrown, we throw this one. + throw; + } + _logger.Error($"Task Teardown error ({task.Name}): {ex}"); + } + } + } + + public IReadOnlyList Tasks { get; } + } + + public class SetupEventArgs : EventArgs + { + public ICakeContext Context { get; } + + public SetupEventArgs(ICakeContext context) + { + Context = context; + } + } + + public class TeardownEventArgs : EventArgs + { + public ITeardownContext TeardownContext { get; } + + public TeardownEventArgs(ITeardownContext teardownContext) + { + TeardownContext = teardownContext; + } + } + + public class TaskSetupEventArgs : EventArgs + { + public ITaskSetupContext TaskSetupContext { get; } + + public TaskSetupEventArgs(ITaskSetupContext taskSetupContext) + { + TaskSetupContext = taskSetupContext; + } + } + + public class TaskTeardownEventArgs : EventArgs + { + public ITaskTeardownContext TaskTeardownContext { get; } + + public TaskTeardownEventArgs(ITaskTeardownContext taskTeardownContext) + { + TaskTeardownContext = taskTeardownContext; + } + } +} diff --git a/src/Cake.Parallel/ParallelCakeModule.cs b/src/Cake.Parallel/ParallelCakeModule.cs new file mode 100644 index 0000000..2418005 --- /dev/null +++ b/src/Cake.Parallel/ParallelCakeModule.cs @@ -0,0 +1,13 @@ +using Cake.Core; +using Cake.Core.Composition; + +namespace Cake.Parallel.Module +{ + public class ParallelCakeModule : ICakeModule + { + public void Register(ICakeContainerRegistry registry) + { + registry.RegisterType().As().Singleton(); + } + } +} diff --git a/src/Cake.Parallel/ParallelGraphBuilder.cs b/src/Cake.Parallel/ParallelGraphBuilder.cs new file mode 100644 index 0000000..1e19eaf --- /dev/null +++ b/src/Cake.Parallel/ParallelGraphBuilder.cs @@ -0,0 +1,37 @@ +// 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.Globalization; +using Cake.Core; + +namespace Cake.Parallel.Module +{ + public class ParallelGraphBuilder + { + public static CakeGraph Build(List tasks) + { + var graph = new CakeGraph(); + foreach (var task in tasks) + { + graph.Add(task.Name); + } + foreach (var task in tasks) + { + foreach (var dependency in task.Dependencies) + { + if (!graph.Exist(dependency)) + { + const string format = "Task '{0}' is dependent on task '{1}' which does not exist."; + var message = string.Format(CultureInfo.InvariantCulture, format, task.Name, dependency); + throw new CakeException(message); + } + + graph.Connect(dependency, task.Name); + } + } + return graph; + } + } +} diff --git a/src/Cake.Parallel/ParallelGraphExtensions.cs b/src/Cake.Parallel/ParallelGraphExtensions.cs new file mode 100644 index 0000000..0e10cf7 --- /dev/null +++ b/src/Cake.Parallel/ParallelGraphExtensions.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Cake.Core; + +namespace Cake.Parallel.Module +{ + public static class ParallelGraphExtensions + { + public static Task Traverse(this CakeGraph graph, string target, Action executeTask) + { + if (!graph.Exist(target)) return Task.CompletedTask; + if (graph.hasCircularReferences(target)) throw new CakeException("Graph contains circular references."); + + return graph.traverse(target, executeTask); + } + + private static Task traverse(this CakeGraph graph, string nodeName, Action executeTask) + { + var dependentTasks = graph.Edges + .Where(_ => _.End.Equals(nodeName, StringComparison.OrdinalIgnoreCase)) + .Select(_ => graph.traverse(_.Start, executeTask)) + .ToArray(); + + if (!dependentTasks.Any()) return Task.Factory.StartNew(() => executeTask(nodeName)); + + return Task.Factory.ContinueWhenAll(dependentTasks, _ => executeTask(nodeName)); + } + + private static bool hasCircularReferences(this CakeGraph graph, string nodeName, HashSet visited = null) + { + visited = visited ?? new HashSet(); + + if (visited.Contains(nodeName)) return true; + + visited.Add(nodeName); + var dependencies = graph.Edges + .Where(_ => _.End.Equals(nodeName, StringComparison.OrdinalIgnoreCase)) + .Select(_ => _.Start); + + return dependencies.Any(dependency => graph.hasCircularReferences(dependency, visited)); + } + } +} diff --git a/src/Cake.Parallel/Properties/AssemblyInfo.cs b/src/Cake.Parallel/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..59e37f0 --- /dev/null +++ b/src/Cake.Parallel/Properties/AssemblyInfo.cs @@ -0,0 +1,39 @@ +using System.Reflection; +using System.Runtime.InteropServices; +using Cake.Core.Annotations; +using Cake.Parallel.Module; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Cake.Parallel")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Extend Health, Inc.")] +[assembly: AssemblyProduct("Cake.Parallel")] +[assembly: AssemblyCopyright("Copyright © Extend Health, Inc. 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("f7b7373b-3c5a-4061-9bfd-8b82b16db5b8")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] + +[assembly: CakeModule(typeof(ParallelCakeModule))] diff --git a/src/Cake.Parallel/paket.references b/src/Cake.Parallel/paket.references new file mode 100644 index 0000000..8f30419 --- /dev/null +++ b/src/Cake.Parallel/paket.references @@ -0,0 +1 @@ +Cake.Core \ No newline at end of file diff --git a/tools/packages.config b/tools/packages.config new file mode 100644 index 0000000..85fc75c --- /dev/null +++ b/tools/packages.config @@ -0,0 +1,4 @@ + + + +