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

View File

@@ -4,7 +4,7 @@ using Ionic.BZip2;
// 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

View File

@@ -2,7 +2,7 @@ using System;
using System.Linq;
using System.Xml;
namespace Squirrel.Core
namespace Squirrel
{
internal static class ContentType
{
@@ -25,16 +25,21 @@ namespace Squirrel.Core
k.GetAttribute("Extension").ToLowerInvariant(),
k.GetAttribute("ContentType").ToLowerInvariant()));
elements
var toAdd = elements
.Where(x => existingTypes.All(t => t.Item2 != x.Item2.ToLowerInvariant()))
.Select(element => {
var ret = doc.CreateElement(element.Item1, typesElement.NamespaceURI);
var ext = doc.CreateAttribute("Extension"); ext.Value = element.Item2;
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;
}).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.RegularExpressions;
using Ionic.Zip;
using ReactiveUIMicro;
using Splat;
namespace Squirrel.Core
namespace Squirrel
{
public interface IDeltaPackageBuilder
{
@@ -69,8 +69,9 @@ namespace Squirrel.Core
var newLibDir = tempInfo.GetDirectories().First(x => x.Name.ToLowerInvariant() == "lib");
newLibDir.GetAllFilesRecursively()
.ForEach(libFile => createDeltaForSingleFile(libFile, tempInfo, baseLibFiles));
foreach (var libFile in newLibDir.GetAllFilesRecursively()) {
createDeltaForSingleFile(libFile, tempInfo, baseLibFiles);
}
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.IO;
using System.Linq;
using System.Reactive.Linq;
using System.Text;
using System.Text.RegularExpressions;
using NuGet;
using ReactiveUIMicro;
using Squirrel.Core.Extensions;
using Splat;
using System.Runtime.Serialization;
using System.Threading.Tasks;
using System.Collections.Concurrent;
namespace Squirrel.Core
namespace Squirrel
{
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 = fileSystemFactory.GetDirectoryInfo(releasePackagesDir);
var packagesDir = new DirectoryInfo(releasePackagesDir);
// 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()) {
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
// place
var tempFile = fileSystemFactory.CreateTempFile();
try {
if (entries.Count > 0) WriteReleaseFile(entries, tempFile.Item2);
} finally {
tempFile.Item2.Dispose();
var entries = entriesQueue.ToList();
var tempFile = Path.GetTempFileName();
using (var of = File.OpenWrite(tempFile)) {
if (entries.Count > 0) WriteReleaseFile(entries, of);
}
var target = Path.Combine(packagesDir.FullName, "RELEASES");
@@ -176,7 +175,7 @@ namespace Squirrel.Core
File.Delete(target);
}
fileSystemFactory.GetFileInfo(tempFile.Item1).MoveTo(target);
File.Move(tempFile, target);
}
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 Ionic.Zip;
using NuGet;
using ReactiveUIMicro;
using Squirrel.Core.Extensions;
using Splat;
namespace Squirrel.Core
namespace Squirrel
{
internal static class FrameworkTargetVersion
{

View File

@@ -4,21 +4,18 @@ using System.ComponentModel;
using System.Diagnostics.Contracts;
using System.IO;
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.Runtime.InteropServices;
using System.Security.AccessControl;
using System.Security.Cryptography;
using System.Security.Principal;
using System.Threading;
using ReactiveUIMicro;
using Splat;
using System.Text;
using System.Threading.Tasks;
using System.Collections.Concurrent;
namespace Squirrel.Core
namespace Squirrel
{
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(to));
@@ -67,11 +64,11 @@ namespace Squirrel.Core
Log().Warn("The file {0} does not exist", from);
// TODO: should we fail this operation?
return Observable.Return(Unit.Default);
return;
}
// 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)
@@ -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;
@@ -147,17 +150,15 @@ namespace Squirrel.Core
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));
scheduler = scheduler ?? RxApp.TaskpoolScheduler;
Log().Info("Starting to delete folder: {0}", directoryPath);
if (!Directory.Exists(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
@@ -177,34 +178,25 @@ namespace Squirrel.Core
Log().Warn(message, ex);
}
var fileOperations = files.MapReduce(file =>
Observable.Start(() => {
Log().Debug("Now deleting file: {0}", file);
File.SetAttributes(file, FileAttributes.Normal);
File.Delete(file);
}, scheduler))
.Select(_ => Unit.Default);
var fileOperations = files.ForEachAsync(file => {
File.SetAttributes(file, FileAttributes.Normal);
File.Delete(file);
});
var directoryOperations =
dirs.MapReduce(dir => DeleteDirectory(dir, scheduler)
.Retry(3))
.Select(_ => Unit.Default);
dirs.ForEachAsync(async dir => await DeleteDirectory(dir));
return fileOperations
.Merge(directoryOperations, scheduler)
.ToList() // still feeling a bit icky
.Select(_ => {
Log().Debug("Now deleting folder: {0}", directoryPath);
File.SetAttributes(directoryPath, FileAttributes.Normal);
await Task.WhenAll(fileOperations, directoryOperations);
try {
Directory.Delete(directoryPath, false);
} catch (Exception ex) {
var message = String.Format("DeleteDirectory: could not delete - {0}", directoryPath);
Log().ErrorException(message, ex);
}
return Unit.Default;
});
Log().Debug("Now deleting folder: {0}", directoryPath);
File.SetAttributes(directoryPath, FileAttributes.Normal);
try {
Directory.Delete(directoryPath, false);
} catch (Exception ex) {
var message = String.Format("DeleteDirectory: could not delete - {0}", directoryPath);
Log().ErrorException(message, ex);
}
}
public static Tuple<string, Stream> CreateTempFile()
@@ -251,9 +243,11 @@ namespace Squirrel.Core
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)]
@@ -271,6 +265,7 @@ namespace Squirrel.Core
}
}
/*
public sealed class SingleGlobalInstance : IDisposable
{
readonly static object gate = 42;
@@ -331,4 +326,29 @@ namespace Squirrel.Core
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>
<package id="Microsoft.Web.Xdt" version="2.1.1" targetFramework="net45" />
<package id="NuGet.Core" version="2.8.2" targetFramework="net45" />
<package id="Splat" version="1.4.0" targetFramework="net45" />
</packages>