Cut up Core into something we can use

This commit is contained in:
Paul Betts
2014-07-28 09:18:38 +02:00
parent 8fa1cf703c
commit 9d239d85bd
12 changed files with 18595 additions and 72 deletions

BIN
ext/Ionic.Zip.dll Normal file

Binary file not shown.

18151
ext/Ionic.Zip.xml Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -4,7 +4,7 @@ using Ionic.BZip2;
// Adapted from https://github.com/LogosBible/bsdiff.net/blob/master/src/bsdiff/BinaryPatchUtility.cs // Adapted from https://github.com/LogosBible/bsdiff.net/blob/master/src/bsdiff/BinaryPatchUtility.cs
namespace Squirrel.Core namespace Squirrel
{ {
/* /*
The original bsdiff.c source code (http://www.daemonology.net/bsdiff/) is The original bsdiff.c source code (http://www.daemonology.net/bsdiff/) is

View File

@@ -2,7 +2,7 @@ using System;
using System.Linq; using System.Linq;
using System.Xml; using System.Xml;
namespace Squirrel.Core namespace Squirrel
{ {
internal static class ContentType internal static class ContentType
{ {
@@ -25,16 +25,21 @@ namespace Squirrel.Core
k.GetAttribute("Extension").ToLowerInvariant(), k.GetAttribute("Extension").ToLowerInvariant(),
k.GetAttribute("ContentType").ToLowerInvariant())); k.GetAttribute("ContentType").ToLowerInvariant()));
elements var toAdd = elements
.Where(x => existingTypes.All(t => t.Item2 != x.Item2.ToLowerInvariant())) .Where(x => existingTypes.All(t => t.Item2 != x.Item2.ToLowerInvariant()))
.Select(element => { .Select(element => {
var ret = doc.CreateElement(element.Item1, typesElement.NamespaceURI); var ret = doc.CreateElement(element.Item1, typesElement.NamespaceURI);
var ext = doc.CreateAttribute("Extension"); ext.Value = element.Item2; var ext = doc.CreateAttribute("Extension"); ext.Value = element.Item2;
var ct = doc.CreateAttribute("ContentType"); ct.Value = element.Item3; var ct = doc.CreateAttribute("ContentType"); ct.Value = element.Item3;
new[] { ext, ct }.ForEach(x => ret.Attributes.Append(x));
ret.Attributes.Append(ext);
ret.Attributes.Append(ct);
return ret; return ret;
}).ForEach(x => typesElement.AppendChild(x)); });
foreach (var v in toAdd) typesElement.AppendChild(v);
} }
} }
} }

View File

@@ -6,9 +6,9 @@ using System.Linq;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Ionic.Zip; using Ionic.Zip;
using ReactiveUIMicro; using Splat;
namespace Squirrel.Core namespace Squirrel
{ {
public interface IDeltaPackageBuilder public interface IDeltaPackageBuilder
{ {
@@ -69,8 +69,9 @@ namespace Squirrel.Core
var newLibDir = tempInfo.GetDirectories().First(x => x.Name.ToLowerInvariant() == "lib"); var newLibDir = tempInfo.GetDirectories().First(x => x.Name.ToLowerInvariant() == "lib");
newLibDir.GetAllFilesRecursively() foreach (var libFile in newLibDir.GetAllFilesRecursively()) {
.ForEach(libFile => createDeltaForSingleFile(libFile, tempInfo, baseLibFiles)); createDeltaForSingleFile(libFile, tempInfo, baseLibFiles);
}
ReleasePackage.addDeltaFilesToContentTypes(tempInfo.FullName); ReleasePackage.addDeltaFilesToContentTypes(tempInfo.FullName);

281
src/EnumerableExtensions.cs Normal file
View File

@@ -0,0 +1,281 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt
using System.Collections.Generic;
namespace System.Linq
{
internal static class EnumerableExtensions
{
/// <summary>
/// Enumerates the sequence and invokes the given action for each value in the sequence.
/// </summary>
/// <typeparam name="TSource">Source sequence element type.</typeparam>
/// <param name="source">Source sequence.</param>
/// <param name="onNext">Action to invoke for each element.</param>
public static void ForEach<TSource>(this IEnumerable<TSource> source, Action<TSource> onNext)
{
if (source == null)
throw new ArgumentNullException("source");
if (onNext == null)
throw new ArgumentNullException("onNext");
foreach (var item in source) onNext(item);
}
/// <summary>
/// Returns the elements with the maximum key value by using the default comparer to compare key values.
/// </summary>
/// <typeparam name="TSource">Source sequence element type.</typeparam>
/// <typeparam name="TKey">Key type.</typeparam>
/// <param name="source">Source sequence.</param>
/// <param name="keySelector">Key selector used to extract the key for each element in the sequence.</param>
/// <returns>List with the elements that share the same maximum key value.</returns>
public static IList<TSource> MaxBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
if (source == null)
throw new ArgumentNullException("source");
if (keySelector == null)
throw new ArgumentNullException("keySelector");
return MaxBy(source, keySelector, Comparer<TKey>.Default);
}
/// <summary>
/// Returns the elements with the minimum key value by using the specified comparer to compare key values.
/// </summary>
/// <typeparam name="TSource">Source sequence element type.</typeparam>
/// <typeparam name="TKey">Key type.</typeparam>
/// <param name="source">Source sequence.</param>
/// <param name="keySelector">Key selector used to extract the key for each element in the sequence.</param>
/// <param name="comparer">Comparer used to determine the maximum key value.</param>
/// <returns>List with the elements that share the same maximum key value.</returns>
public static IList<TSource> MaxBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer)
{
if (source == null)
throw new ArgumentNullException("source");
if (keySelector == null)
throw new ArgumentNullException("keySelector");
if (comparer == null)
throw new ArgumentNullException("comparer");
return ExtremaBy(source, keySelector, (key, minValue) => comparer.Compare(key, minValue));
}
private static IList<TSource> ExtremaBy<TSource, TKey>(IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TKey, TKey, int> compare)
{
var result = new List<TSource>();
using (var e = source.GetEnumerator())
{
if (!e.MoveNext())
throw new InvalidOperationException("Source sequence doesn't contain any elements.");
var current = e.Current;
var resKey = keySelector(current);
result.Add(current);
while (e.MoveNext())
{
var cur = e.Current;
var key = keySelector(cur);
var cmp = compare(key, resKey);
if (cmp == 0)
{
result.Add(cur);
}
else if (cmp > 0)
{
result = new List<TSource> { cur };
resKey = key;
}
}
}
return result;
}
/// <summary>
/// Lazily invokes an action for each value in the sequence.
/// </summary>
/// <typeparam name="TSource">Source sequence element type.</typeparam>
/// <param name="source">Source sequence.</param>
/// <param name="onNext">Action to invoke for each element.</param>
/// <returns>Sequence exhibiting the specified side-effects upon enumeration.</returns>
public static IEnumerable<TSource> Do<TSource>(this IEnumerable<TSource> source, Action<TSource> onNext)
{
if (source == null)
throw new ArgumentNullException("source");
if (onNext == null)
throw new ArgumentNullException("onNext");
return DoHelper(source, onNext, _ => { }, () => { });
}
/// <summary>
/// Lazily invokes an action for each value in the sequence, and executes an action for successful termination.
/// </summary>
/// <typeparam name="TSource">Source sequence element type.</typeparam>
/// <param name="source">Source sequence.</param>
/// <param name="onNext">Action to invoke for each element.</param>
/// <param name="onCompleted">Action to invoke on successful termination of the sequence.</param>
/// <returns>Sequence exhibiting the specified side-effects upon enumeration.</returns>
public static IEnumerable<TSource> Do<TSource>(this IEnumerable<TSource> source, Action<TSource> onNext, Action onCompleted)
{
if (source == null)
throw new ArgumentNullException("source");
if (onNext == null)
throw new ArgumentNullException("onNext");
if (onCompleted == null)
throw new ArgumentNullException("onCompleted");
return DoHelper(source, onNext, _ => { }, onCompleted);
}
/// <summary>
/// Lazily invokes an action for each value in the sequence, and executes an action upon exceptional termination.
/// </summary>
/// <typeparam name="TSource">Source sequence element type.</typeparam>
/// <param name="source">Source sequence.</param>
/// <param name="onNext">Action to invoke for each element.</param>
/// <param name="onError">Action to invoke on exceptional termination of the sequence.</param>
/// <returns>Sequence exhibiting the specified side-effects upon enumeration.</returns>
public static IEnumerable<TSource> Do<TSource>(this IEnumerable<TSource> source, Action<TSource> onNext, Action<Exception> onError)
{
if (source == null)
throw new ArgumentNullException("source");
if (onNext == null)
throw new ArgumentNullException("onNext");
if (onError == null)
throw new ArgumentNullException("onError");
return DoHelper(source, onNext, onError, () => { });
}
/// <summary>
/// Lazily invokes an action for each value in the sequence, and executes an action upon successful or exceptional termination.
/// </summary>
/// <typeparam name="TSource">Source sequence element type.</typeparam>
/// <param name="source">Source sequence.</param>
/// <param name="onNext">Action to invoke for each element.</param>
/// <param name="onError">Action to invoke on exceptional termination of the sequence.</param>
/// <param name="onCompleted">Action to invoke on successful termination of the sequence.</param>
/// <returns>Sequence exhibiting the specified side-effects upon enumeration.</returns>
public static IEnumerable<TSource> Do<TSource>(this IEnumerable<TSource> source, Action<TSource> onNext, Action<Exception> onError, Action onCompleted)
{
if (source == null)
throw new ArgumentNullException("source");
if (onNext == null)
throw new ArgumentNullException("onNext");
if (onError == null)
throw new ArgumentNullException("onError");
if (onCompleted == null)
throw new ArgumentNullException("onCompleted");
return DoHelper(source, onNext, onError, onCompleted);
}
private static IEnumerable<TSource> DoHelper<TSource>(this IEnumerable<TSource> source, Action<TSource> onNext, Action<Exception> onError, Action onCompleted)
{
using (var e = source.GetEnumerator())
{
while (true)
{
var current = default(TSource);
try
{
if (!e.MoveNext())
break;
current = e.Current;
}
catch (Exception ex)
{
onError(ex);
throw;
}
onNext(current);
yield return current;
}
onCompleted();
}
}
/// <summary>
/// Returns the source sequence prefixed with the specified value.
/// </summary>
/// <typeparam name="TSource">Source sequence element type.</typeparam>
/// <param name="source">Source sequence.</param>
/// <param name="values">Values to prefix the sequence with.</param>
/// <returns>Sequence starting with the specified prefix value, followed by the source sequence.</returns>
public static IEnumerable<TSource> StartWith<TSource>(this IEnumerable<TSource> source, params TSource[] values)
{
if (source == null)
throw new ArgumentNullException("source");
return source.StartWith_(values);
}
static IEnumerable<TSource> StartWith_<TSource>(this IEnumerable<TSource> source, params TSource[] values)
{
foreach (var x in values)
yield return x;
foreach (var item in source)
yield return item;
}
/// <summary>
/// Returns elements with a distinct key value by using the default equality comparer to compare key values.
/// </summary>
/// <typeparam name="TSource">Source sequence element type.</typeparam>
/// <typeparam name="TKey">Key type.</typeparam>
/// <param name="source">Source sequence.</param>
/// <param name="keySelector">Key selector.</param>
/// <returns>Sequence that contains the elements from the source sequence with distinct key values.</returns>
public static IEnumerable<TSource> Distinct<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
if (source == null)
throw new ArgumentNullException("source");
if (keySelector == null)
throw new ArgumentNullException("keySelector");
return source.Distinct_(keySelector, EqualityComparer<TKey>.Default);
}
/// <summary>
/// Returns elements with a distinct key value by using the specified equality comparer to compare key values.
/// </summary>
/// <typeparam name="TSource">Source sequence element type.</typeparam>
/// <typeparam name="TKey">Key type.</typeparam>
/// <param name="source">Source sequence.</param>
/// <param name="keySelector">Key selector.</param>
/// <param name="comparer">Comparer used to compare key values.</param>
/// <returns>Sequence that contains the elements from the source sequence with distinct key values.</returns>
public static IEnumerable<TSource> Distinct<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
{
if (source == null)
throw new ArgumentNullException("source");
if (keySelector == null)
throw new ArgumentNullException("keySelector");
if (comparer == null)
throw new ArgumentNullException("comparer");
return source.Distinct_(keySelector, comparer);
}
static IEnumerable<TSource> Distinct_<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
{
var set = new HashSet<TKey>(comparer);
foreach (var item in source)
{
var key = keySelector(item);
if (set.Add(key))
yield return item;
}
}
}
}

17
src/PackageExtensions.cs Normal file
View File

@@ -0,0 +1,17 @@
using System;
using NuGet;
namespace Squirrel
{
public static class PackageExtensions
{
public static string ExtractTitle(this IPackage package)
{
if (package == null)
return String.Empty;
var title = package.Title;
return !String.IsNullOrWhiteSpace(title) ? title : package.Id;
}
}
}

View File

@@ -3,15 +3,15 @@ using System.Collections.Generic;
using System.Diagnostics.Contracts; using System.Diagnostics.Contracts;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reactive.Linq;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using NuGet; using NuGet;
using ReactiveUIMicro; using Splat;
using Squirrel.Core.Extensions;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using System.Threading.Tasks;
using System.Collections.Concurrent;
namespace Squirrel.Core namespace Squirrel
{ {
public interface IReleaseEntry public interface IReleaseEntry
{ {
@@ -150,25 +150,24 @@ namespace Squirrel.Core
} }
} }
public static void BuildReleasesFile(string releasePackagesDir, IFileSystemFactory fileSystemFactory = null) public static void BuildReleasesFile(string releasePackagesDir)
{ {
fileSystemFactory = fileSystemFactory ?? AnonFileSystem.Default; var packagesDir = new DirectoryInfo(releasePackagesDir);
var packagesDir = fileSystemFactory.GetDirectoryInfo(releasePackagesDir);
// Generate release entries for all of the local packages // Generate release entries for all of the local packages
var entries = packagesDir.GetFiles("*.nupkg").MapReduce(x => Observable.Start(() => { var entriesQueue = new ConcurrentQueue<ReleaseEntry>();
Parallel.ForEach(packagesDir.GetFiles("*.nupkg"), x => {
using (var file = x.OpenRead()) { using (var file = x.OpenRead()) {
return GenerateFromFile(file, x.Name); entriesQueue.Enqueue(GenerateFromFile(file, x.Name));
} }
}, RxApp.TaskpoolScheduler)).First(); });
// Write the new RELEASES file to a temp file then move it into // Write the new RELEASES file to a temp file then move it into
// place // place
var tempFile = fileSystemFactory.CreateTempFile(); var entries = entriesQueue.ToList();
try { var tempFile = Path.GetTempFileName();
if (entries.Count > 0) WriteReleaseFile(entries, tempFile.Item2); using (var of = File.OpenWrite(tempFile)) {
} finally { if (entries.Count > 0) WriteReleaseFile(entries, of);
tempFile.Item2.Dispose();
} }
var target = Path.Combine(packagesDir.FullName, "RELEASES"); var target = Path.Combine(packagesDir.FullName, "RELEASES");
@@ -176,7 +175,7 @@ namespace Squirrel.Core
File.Delete(target); File.Delete(target);
} }
fileSystemFactory.GetFileInfo(tempFile.Item1).MoveTo(target); File.Move(tempFile, target);
} }
static bool filenameIsDeltaFile(string filename) static bool filenameIsDeltaFile(string filename)

49
src/ReleaseExtensions.cs Normal file
View File

@@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace Squirrel
{
public static class VersionExtensions
{
public static Version ToVersion(this IReleasePackage package)
{
return package.InputPackageFile.ToVersion();
}
public static Version ToVersion(this string fileName)
{
var parts = (new FileInfo(fileName)).Name
.Replace(".nupkg", "").Replace("-delta", "")
.Split('.', '-').Reverse();
var numberRegex = new Regex(@"^\d+$");
var versionFields = parts
.Where(x => numberRegex.IsMatch(x))
.Select(Int32.Parse)
.Reverse()
.ToArray();
if (versionFields.Length < 2 || versionFields.Length > 4)
{
return null;
}
switch (versionFields.Length)
{
case 2:
return new Version(versionFields[0], versionFields[1]);
case 3:
return new Version(versionFields[0], versionFields[1], versionFields[2]);
case 4:
return new Version(versionFields[0], versionFields[1], versionFields[2], versionFields[3]);
}
return null;
}
}
}

View File

@@ -11,10 +11,9 @@ using System.Text.RegularExpressions;
using System.Xml; using System.Xml;
using Ionic.Zip; using Ionic.Zip;
using NuGet; using NuGet;
using ReactiveUIMicro; using Splat;
using Squirrel.Core.Extensions;
namespace Squirrel.Core namespace Squirrel
{ {
internal static class FrameworkTargetVersion internal static class FrameworkTargetVersion
{ {

View File

@@ -4,21 +4,18 @@ using System.ComponentModel;
using System.Diagnostics.Contracts; using System.Diagnostics.Contracts;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reactive;
using System.Reactive.Concurrency;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Security.AccessControl; using System.Security.AccessControl;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Security.Principal; using System.Security.Principal;
using System.Threading; using System.Threading;
using ReactiveUIMicro; using Splat;
using System.Text; using System.Text;
using System.Threading.Tasks;
using System.Collections.Concurrent;
namespace Squirrel.Core namespace Squirrel
{ {
public static class Utility public static class Utility
{ {
@@ -58,7 +55,7 @@ namespace Squirrel.Core
} }
} }
public static IObservable<Unit> CopyToAsync(string from, string to) public static async Task CopyToAsync(string from, string to)
{ {
Contract.Requires(!String.IsNullOrEmpty(from) && File.Exists(from)); Contract.Requires(!String.IsNullOrEmpty(from) && File.Exists(from));
Contract.Requires(!String.IsNullOrEmpty(to)); Contract.Requires(!String.IsNullOrEmpty(to));
@@ -67,11 +64,11 @@ namespace Squirrel.Core
Log().Warn("The file {0} does not exist", from); Log().Warn("The file {0} does not exist", from);
// TODO: should we fail this operation? // TODO: should we fail this operation?
return Observable.Return(Unit.Default); return;
} }
// XXX: SafeCopy // XXX: SafeCopy
return Observable.Start(() => File.Copy(from, to, true), RxApp.TaskpoolScheduler); await Task.Run(() => File.Copy(from, to, true));
} }
public static void Retry(this Action block, int retries = 2) public static void Retry(this Action block, int retries = 2)
@@ -105,14 +102,20 @@ namespace Squirrel.Core
} }
} }
public static IObservable<IList<TRet>> MapReduce<T, TRet>(this IObservable<T> This, Func<T, IObservable<TRet>> selector, int degreeOfParallelism = 4) public static Task ForEachAsync<T>(this IEnumerable<T> source, Action<T> body, int degreeOfParallelism = 4)
{ {
return This.Select(x => Observable.Defer(() => selector(x))).Merge(degreeOfParallelism).ToList(); return ForEachAsync(source, x => Task.Run(() => body(x)), degreeOfParallelism);
} }
public static IObservable<IList<TRet>> MapReduce<T, TRet>(this IEnumerable<T> This, Func<T, IObservable<TRet>> selector, int degreeOfParallelism = 4) public static Task ForEachAsync<T>(this IEnumerable<T> source, Func<T, Task> body, int degreeOfParallelism = 4)
{ {
return This.ToObservable().Select(x => Observable.Defer(() => selector(x))).Merge(degreeOfParallelism).ToList(); return Task.WhenAll(
from partition in Partitioner.Create(source).GetPartitions(degreeOfParallelism)
select Task.Run(async () => {
using (partition)
while (partition.MoveNext())
await body(partition.Current);
}));
} }
static string directoryChars; static string directoryChars;
@@ -147,17 +150,15 @@ namespace Squirrel.Core
DeleteDirectory(tempDir.FullName).Wait()); DeleteDirectory(tempDir.FullName).Wait());
} }
public static IObservable<Unit> DeleteDirectory(string directoryPath, IScheduler scheduler = null) public static async Task DeleteDirectory(string directoryPath)
{ {
Contract.Requires(!String.IsNullOrEmpty(directoryPath)); Contract.Requires(!String.IsNullOrEmpty(directoryPath));
scheduler = scheduler ?? RxApp.TaskpoolScheduler;
Log().Info("Starting to delete folder: {0}", directoryPath); Log().Info("Starting to delete folder: {0}", directoryPath);
if (!Directory.Exists(directoryPath)) { if (!Directory.Exists(directoryPath)) {
Log().Warn("DeleteDirectory: does not exist - {0}", directoryPath); Log().Warn("DeleteDirectory: does not exist - {0}", directoryPath);
return Observable.Return(Unit.Default); return;
} }
// From http://stackoverflow.com/questions/329355/cannot-delete-directory-with-directory-deletepath-true/329502#329502 // From http://stackoverflow.com/questions/329355/cannot-delete-directory-with-directory-deletepath-true/329502#329502
@@ -177,34 +178,25 @@ namespace Squirrel.Core
Log().Warn(message, ex); Log().Warn(message, ex);
} }
var fileOperations = files.MapReduce(file => var fileOperations = files.ForEachAsync(file => {
Observable.Start(() => { File.SetAttributes(file, FileAttributes.Normal);
Log().Debug("Now deleting file: {0}", file); File.Delete(file);
File.SetAttributes(file, FileAttributes.Normal); });
File.Delete(file);
}, scheduler))
.Select(_ => Unit.Default);
var directoryOperations = var directoryOperations =
dirs.MapReduce(dir => DeleteDirectory(dir, scheduler) dirs.ForEachAsync(async dir => await DeleteDirectory(dir));
.Retry(3))
.Select(_ => Unit.Default);
return fileOperations await Task.WhenAll(fileOperations, directoryOperations);
.Merge(directoryOperations, scheduler)
.ToList() // still feeling a bit icky
.Select(_ => {
Log().Debug("Now deleting folder: {0}", directoryPath);
File.SetAttributes(directoryPath, FileAttributes.Normal);
try { Log().Debug("Now deleting folder: {0}", directoryPath);
Directory.Delete(directoryPath, false); File.SetAttributes(directoryPath, FileAttributes.Normal);
} catch (Exception ex) {
var message = String.Format("DeleteDirectory: could not delete - {0}", directoryPath); try {
Log().ErrorException(message, ex); Directory.Delete(directoryPath, false);
} } catch (Exception ex) {
return Unit.Default; var message = String.Format("DeleteDirectory: could not delete - {0}", directoryPath);
}); Log().ErrorException(message, ex);
}
} }
public static Tuple<string, Stream> CreateTempFile() public static Tuple<string, Stream> CreateTempFile()
@@ -251,9 +243,11 @@ namespace Squirrel.Core
Log().Error("safeDeleteFileAtNextReboot: failed - {0} - {1}", name, lastError); Log().Error("safeDeleteFileAtNextReboot: failed - {0} - {1}", name, lastError);
} }
static IRxUIFullLogger Log() static IFullLogger logger;
static IFullLogger Log()
{ {
return LogManager.GetLogger(typeof(Utility)); return logger ??
(logger = Locator.CurrentMutable.GetService<ILogManager>().GetLogger(typeof(Utility)));
} }
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
@@ -271,6 +265,7 @@ namespace Squirrel.Core
} }
} }
/*
public sealed class SingleGlobalInstance : IDisposable public sealed class SingleGlobalInstance : IDisposable
{ {
readonly static object gate = 42; readonly static object gate = 42;
@@ -331,4 +326,29 @@ namespace Squirrel.Core
lockScheduler.Dispose(); lockScheduler.Dispose();
} }
} }
*/
public static class Disposable
{
public static IDisposable Create(Action action)
{
return new AnonDisposable(action);
}
class AnonDisposable : IDisposable
{
static readonly Action dummyBlock = (() => { });
Action block;
public AnonDisposable(Action b)
{
block = b;
}
public void Dispose()
{
Interlocked.Exchange(ref block, dummyBlock)();
}
}
}
} }

View File

@@ -2,4 +2,5 @@
<packages> <packages>
<package id="Microsoft.Web.Xdt" version="2.1.1" targetFramework="net45" /> <package id="Microsoft.Web.Xdt" version="2.1.1" targetFramework="net45" />
<package id="NuGet.Core" version="2.8.2" targetFramework="net45" /> <package id="NuGet.Core" version="2.8.2" targetFramework="net45" />
<package id="Splat" version="1.4.0" targetFramework="net45" />
</packages> </packages>