diff --git a/src/CreateReleasePackage/CreateReleasePackage.csproj b/src/CreateReleasePackage/CreateReleasePackage.csproj index 9fa08efa..53fd5b91 100644 --- a/src/CreateReleasePackage/CreateReleasePackage.csproj +++ b/src/CreateReleasePackage/CreateReleasePackage.csproj @@ -9,7 +9,7 @@ Properties CreateReleasePackage CreateReleasePackage - v4.0 + v4.5 512 ..\ true @@ -28,6 +28,7 @@ false false true + AnyCPU @@ -38,6 +39,7 @@ DEBUG;TRACE prompt 4 + false AnyCPU @@ -47,6 +49,7 @@ TRACE prompt 4 + false diff --git a/src/CreateReleasePackage/app.config b/src/CreateReleasePackage/app.config index 118c7c04..50db73bb 100644 --- a/src/CreateReleasePackage/app.config +++ b/src/CreateReleasePackage/app.config @@ -1,19 +1,19 @@ - + - - + + - - + + - - + + - \ No newline at end of file + diff --git a/src/SampleUpdatingApp/Properties/Resources.Designer.cs b/src/SampleUpdatingApp/Properties/Resources.Designer.cs index b4c28fe6..afe705c9 100644 --- a/src/SampleUpdatingApp/Properties/Resources.Designer.cs +++ b/src/SampleUpdatingApp/Properties/Resources.Designer.cs @@ -1,17 +1,17 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.17626 +// Runtime Version:4.0.30319.34003 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ -namespace SampleUpdatingApp.Properties -{ - - +namespace SampleUpdatingApp.Properties { + using System; + + /// /// A strongly-typed resource class, for looking up localized strings, etc. /// @@ -22,48 +22,40 @@ namespace SampleUpdatingApp.Properties [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources - { - + internal class Resources { + private static global::System.Resources.ResourceManager resourceMan; - + private static global::System.Globalization.CultureInfo resourceCulture; - + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() - { + internal Resources() { } - + /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager - { - get - { - if ((resourceMan == null)) - { + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SampleUpdatingApp.Properties.Resources", typeof(Resources).Assembly); resourceMan = temp; } return resourceMan; } } - + /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture - { - get - { + internal static global::System.Globalization.CultureInfo Culture { + get { return resourceCulture; } - set - { + set { resourceCulture = value; } } diff --git a/src/SampleUpdatingApp/Properties/Settings.Designer.cs b/src/SampleUpdatingApp/Properties/Settings.Designer.cs index 9aeeb9ed..7becbdbe 100644 --- a/src/SampleUpdatingApp/Properties/Settings.Designer.cs +++ b/src/SampleUpdatingApp/Properties/Settings.Designer.cs @@ -1,28 +1,24 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.17626 +// Runtime Version:4.0.30319.34003 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ -namespace SampleUpdatingApp.Properties -{ - - +namespace SampleUpdatingApp.Properties { + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] - internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase - { - + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "12.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); - - public static Settings Default - { - get - { + + public static Settings Default { + get { return defaultInstance; } } diff --git a/src/SampleUpdatingApp/SampleUpdatingApp.csproj b/src/SampleUpdatingApp/SampleUpdatingApp.csproj index 60401c2b..e9ae1aac 100644 --- a/src/SampleUpdatingApp/SampleUpdatingApp.csproj +++ b/src/SampleUpdatingApp/SampleUpdatingApp.csproj @@ -9,12 +9,13 @@ Properties SampleUpdatingApp SampleUpdatingApp - v4.0 + v4.5 512 {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 4 ..\ true + AnyCPU @@ -25,6 +26,7 @@ DEBUG;TRACE prompt 4 + false AnyCPU @@ -34,6 +36,7 @@ TRACE prompt 4 + false diff --git a/src/SampleUpdatingApp/app.config b/src/SampleUpdatingApp/app.config index cdd95f8d..b0100b8b 100644 --- a/src/SampleUpdatingApp/app.config +++ b/src/SampleUpdatingApp/app.config @@ -1,24 +1,24 @@ - + - + - - + + - - + + - - + + - \ No newline at end of file + diff --git a/src/Squirrel.Client/InstallManager.cs b/src/Squirrel.Client/InstallManager.cs index fb7faee9..371f21b0 100644 --- a/src/Squirrel.Client/InstallManager.cs +++ b/src/Squirrel.Client/InstallManager.cs @@ -67,7 +67,7 @@ public IObservable> ExecuteInstall(string currentAssemblyDir, IPack return updateUsingDeltas; } - Task> executeInstall( + async Task> executeInstall( string currentAssemblyDir, IPackage bundledPackageMetadata, bool ignoreDeltaUpdates = false, @@ -83,7 +83,13 @@ Task> executeInstall( var realCopyFileProgress = new Subject(); var realApplyProgress = new Subject(); - var eigenUpdateObs = Observable.Using(() => new UpdateManager(currentAssemblyDir, bundledPackageMetadata.Id, fxVersion, TargetRootDirectory), eigenUpdater => { + List ret = null; + + using (var eigenUpdater = new UpdateManager( + currentAssemblyDir, + bundledPackageMetadata.Id, + fxVersion, + TargetRootDirectory)) { // The real update takes longer than the eigenupdate because we're // downloading from the Internet instead of doing everything // locally, so give it more weight @@ -95,57 +101,65 @@ Task> executeInstall( .Select(x => (int) x) .Subscribe(progress); - var updateInfoObs = eigenUpdater.CheckForUpdate(ignoreDeltaUpdates, eigenCheckProgress); - - return updateInfoObs.SelectMany(updateInfo => { - log.Info("The checking of releases completed - and there was much rejoicing"); + var updateInfo = await eigenUpdater.CheckForUpdate(ignoreDeltaUpdates, eigenCheckProgress); - if (!updateInfo.ReleasesToApply.Any()) { + log.Info("The checking of releases completed - and there was much rejoicing"); - var rootDirectory = TargetRootDirectory ?? Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); + if (!updateInfo.ReleasesToApply.Any()) { - var version = updateInfo.CurrentlyInstalledVersion; - var releaseFolder = String.Format("app-{0}", version.Version); - var absoluteFolder = Path.Combine(rootDirectory, version.PackageName, releaseFolder); + var rootDirectory = TargetRootDirectory ?? Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); - if (!Directory.Exists(absoluteFolder)) { - log.Warn("executeInstall: the directory {0} doesn't exist - cannot find the current app?!!?"); - } else { - return Observable.Return( - Directory.GetFiles(absoluteFolder, "*.exe", SearchOption.TopDirectoryOnly).ToList()); - } - } + var version = updateInfo.CurrentlyInstalledVersion; + var releaseFolder = String.Format("app-{0}", version.Version); + var absoluteFolder = Path.Combine(rootDirectory, version.PackageName, releaseFolder); - foreach (var u in updateInfo.ReleasesToApply) { - log.Info("HEY! We should be applying update {0}", u.Filename); + if (!Directory.Exists(absoluteFolder)) { + log.Warn("executeInstall: the directory {0} doesn't exist - cannot find the current app?!!?"); + } else { + return Directory.GetFiles(absoluteFolder, "*.exe", SearchOption.TopDirectoryOnly) + .ToList(); } + } - return eigenUpdater.DownloadReleases(updateInfo.ReleasesToApply, eigenCopyFileProgress) - .Do(_ => log.Info("The downloading of releases completed - and there was much rejoicing")) - .SelectMany(_ => eigenUpdater.ApplyReleases(updateInfo, eigenApplyProgress)) - .Do(_ => log.Info("The applying of releases completed - and there was much rejoicing")); - }); - }); - - return eigenUpdateObs.SelectMany(ret => { - var updateUrl = bundledPackageMetadata.ProjectUrl != null ? bundledPackageMetadata.ProjectUrl.ToString() : null; - updateUrl = null; //XXX REMOVE ME - if (updateUrl == null) { - realCheckProgress.OnNext(100); realCheckProgress.OnCompleted(); - realCopyFileProgress.OnNext(100); realCopyFileProgress.OnCompleted(); - realApplyProgress.OnNext(100); realApplyProgress.OnCompleted(); + foreach (var u in updateInfo.ReleasesToApply) { + log.Info("HEY! We should be applying update {0}", u.Filename); + } - return Observable.Return(ret); + await eigenUpdater.DownloadReleases(updateInfo.ReleasesToApply, eigenCopyFileProgress); + + log.Info("The downloading of releases completed - and there was much rejoicing"); + + ret = await eigenUpdater.ApplyReleases(updateInfo, eigenApplyProgress); + + log.Info("The applying of releases completed - and there was much rejoicing"); + } + + var updateUrl = bundledPackageMetadata.ProjectUrl != null ? bundledPackageMetadata.ProjectUrl.ToString() : null; + updateUrl = null; //XXX REMOVE ME + if (updateUrl == null) { + realCheckProgress.OnNext(100); realCheckProgress.OnCompleted(); + realCopyFileProgress.OnNext(100); realCopyFileProgress.OnCompleted(); + realApplyProgress.OnNext(100); realApplyProgress.OnCompleted(); + + return ret; + } + + using(var realUpdater = new UpdateManager( + updateUrl, + bundledPackageMetadata.Id, + fxVersion, + TargetRootDirectory)) { + try { + var updateInfo = await realUpdater.CheckForUpdate(progress: realCheckProgress); + await realUpdater.DownloadReleases(updateInfo.ReleasesToApply, realCopyFileProgress); + await realUpdater.ApplyReleases(updateInfo, realApplyProgress); + } catch (Exception ex) { + log.ErrorException("Failed to update to latest remote version", ex); + return new List(); } + } - return Observable.Using(() => new UpdateManager(updateUrl, bundledPackageMetadata.Id, fxVersion, TargetRootDirectory), realUpdater => { - return realUpdater.CheckForUpdate(progress: realCheckProgress) - .SelectMany(x => realUpdater.DownloadReleases(x.ReleasesToApply, realCopyFileProgress).Select(_ => x)) - .SelectMany(x => realUpdater.ApplyReleases(x, realApplyProgress)) - .Select(_ => ret) - .LoggedCatch(this, Observable.Return(new List()), "Failed to update to latest remote version"); - }); - }).ToTask(); + return ret; } public IObservable ExecuteUninstall(Version version = null) diff --git a/src/Squirrel.Client/Squirrel.Client.csproj b/src/Squirrel.Client/Squirrel.Client.csproj index 71d934a6..5250eb90 100644 --- a/src/Squirrel.Client/Squirrel.Client.csproj +++ b/src/Squirrel.Client/Squirrel.Client.csproj @@ -10,11 +10,12 @@ Properties Squirrel.Client Squirrel.Client - v4.0 + v4.5 512 ..\..\src\ true 0 + true @@ -58,6 +59,7 @@ Full Build 0 + false pdbonly @@ -66,6 +68,7 @@ TRACE prompt 4 + false @@ -138,12 +141,8 @@ - - Designer - - - Designer - + + Designer diff --git a/src/Squirrel.Client/UpdateManager.cs b/src/Squirrel.Client/UpdateManager.cs index 4c56d48f..af221da6 100644 --- a/src/Squirrel.Client/UpdateManager.cs +++ b/src/Squirrel.Client/UpdateManager.cs @@ -192,6 +192,11 @@ IObservable downloadReleases(IEnumerable releasesToDownload, } public IObservable> ApplyReleases(UpdateInfo updateInfo, IObserver progress = null) + { + return acquireUpdateLock().SelectMany(_ => applyReleases(updateInfo, progress).ToObservable()); + } + + async Task> applyReleases(UpdateInfo updateInfo, IObserver progress = null) { progress = progress ?? new Subject(); @@ -199,24 +204,25 @@ public IObservable> ApplyReleases(UpdateInfo updateInfo, IObserver< // once the entire operation has completed, even though we technically // could do it after DownloadUpdates finishes. We do this so that if // we get interrupted / killed during this operation, we'll start over - return Observable.Using(_ => acquireUpdateLock().ToTask(), (dontcare, ct) => { - var obs = cleanDeadVersions(updateInfo.CurrentlyInstalledVersion != null ? updateInfo.CurrentlyInstalledVersion.Version : null) - .Do(_ => progress.OnNext(10)) - .SelectMany(_ => createFullPackagesFromDeltas(updateInfo.ReleasesToApply, updateInfo.CurrentlyInstalledVersion)) - .Do(_ => progress.OnNext(50)) - .Select(release => installPackageToAppDir(updateInfo, release)) - .Do(_ => progress.OnNext(95)) - .SelectMany(ret => UpdateLocalReleasesFile().Select(_ => ret)) - .Finally(() => progress.OnCompleted()) - .PublishLast(); - - obs.Connect(); - - // NB: This overload of Using is high as a kite. - var tcs = new TaskCompletionSource>>(); - tcs.SetResult(obs); - return tcs.Task; - }); + + var ret = default(List); + try { + await cleanDeadVersions(updateInfo.CurrentlyInstalledVersion != null ? updateInfo.CurrentlyInstalledVersion.Version : null); + progress.OnNext(10); + + var release = await createFullPackagesFromDeltas(updateInfo.ReleasesToApply, updateInfo.CurrentlyInstalledVersion); + progress.OnNext(50); + + ret = await Observable.Start(() => installPackageToAppDir(updateInfo, release), RxApp.TaskpoolScheduler); + progress.OnNext(95); + + await UpdateLocalReleasesFile(); + progress.OnNext(100); + } finally { + progress.OnCompleted(); + } + + return ret; } public IObservable UpdateLocalReleasesFile() diff --git a/src/Squirrel.Client/app.config b/src/Squirrel.Client/app.config index c5c407bd..e5eb4927 100644 --- a/src/Squirrel.Client/app.config +++ b/src/Squirrel.Client/app.config @@ -1,4 +1,4 @@ - + - \ No newline at end of file + diff --git a/src/Squirrel.Core/ReactiveUIMicro/Rx-Shim/AwaitableAsyncSubject.cs b/src/Squirrel.Core/ReactiveUIMicro/Rx-Shim/AwaitableAsyncSubject.cs new file mode 100644 index 00000000..f1021f3d --- /dev/null +++ b/src/Squirrel.Core/ReactiveUIMicro/Rx-Shim/AwaitableAsyncSubject.cs @@ -0,0 +1,342 @@ +#define HAS_AWAIT + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Disposables; +using System.Reactive.Subjects; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading; + +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. + +/* This file is substantially copied from http://rx.codeplex.com/SourceControl/changeset/view/ef6a42709f49#Rx.NET/System.Reactive.Linq/Reactive/Subjects/AsyncSubject.cs + * Check LICENSE in this folder for licensing information */ + +namespace ReactiveUIMicro +{ + /// + /// Represents the result of an asynchronous operation. + /// The last value before the OnCompleted notification, or the error received through OnError, is sent to all subscribed observers. + /// + /// The type of the elements processed by the subject. + public sealed class AwaitableAsyncSubject : ISubject, IDisposable , INotifyCompletion + { + private readonly object _gate = new object(); + + private ImmutableList> _observers; + private bool _isDisposed; + private bool _isStopped; + private T _value; + private bool _hasValue; + private Exception _exception; + + /// + /// Creates a subject that can only receive one value and that value is cached for all future observations. + /// + public AwaitableAsyncSubject() + { + _observers = new ImmutableList>(); + } + + /// + /// Notifies all subscribed observers about the end of the sequence, also causing the last received value to be sent out (if any). + /// + public void OnCompleted() + { + var os = default(IObserver[]); + + var v = default(T); + var hv = false; + lock (_gate) + { + CheckDisposed(); + + if (!_isStopped) + { + os = _observers.Data; + _observers = new ImmutableList>(); + _isStopped = true; + v = _value; + hv = _hasValue; + } + } + + if (os != null) + { + if (hv) + { + foreach (var o in os) + { + o.OnNext(v); + o.OnCompleted(); + } + } + else + foreach (var o in os) + o.OnCompleted(); + } + } + + /// + /// Notifies all subscribed observers about the exception. + /// + /// The exception to send to all observers. + /// is null. + public void OnError(Exception error) + { + if (error == null) + throw new ArgumentNullException("error"); + + var os = default(IObserver[]); + lock (_gate) + { + CheckDisposed(); + + if (!_isStopped) + { + os = _observers.Data; + _observers = new ImmutableList>(); + _isStopped = true; + _exception = error; + } + } + + if (os != null) + foreach (var o in os) + o.OnError(error); + } + + /// + /// Sends a value to the subject. The last value received before successful termination will be sent to all subscribed and future observers. + /// + /// The value to store in the subject. + public void OnNext(T value) + { + lock (_gate) + { + CheckDisposed(); + + if (!_isStopped) + { + _value = value; + _hasValue = true; + } + } + } + + /// + /// Subscribes an observer to the subject. + /// + /// Observer to subscribe to the subject. + /// Disposable object that can be used to unsubscribe the observer from the subject. + /// is null. + public IDisposable Subscribe(IObserver observer) + { + if (observer == null) + throw new ArgumentNullException("observer"); + + var ex = default(Exception); + var v = default(T); + var hv = false; + + lock (_gate) + { + CheckDisposed(); + + if (!_isStopped) + { + _observers = _observers.Add(observer); + return new Subscription(this, observer); + } + + ex = _exception; + hv = _hasValue; + v = _value; + } + + if (ex != null) + observer.OnError(ex); + else if (hv) + { + observer.OnNext(v); + observer.OnCompleted(); + } + else + observer.OnCompleted(); + + return Disposable.Empty; + } + + class Subscription : IDisposable + { + private readonly AwaitableAsyncSubject _subject; + private IObserver _observer; + + public Subscription(AwaitableAsyncSubject subject, IObserver observer) + { + _subject = subject; + _observer = observer; + } + + public void Dispose() + { + if (_observer != null) + { + lock (_subject._gate) + { + if (!_subject._isDisposed && _observer != null) + { + _subject._observers = _subject._observers.Remove(_observer); + _observer = null; + } + } + } + } + } + + void CheckDisposed() + { + if (_isDisposed) + throw new ObjectDisposedException(string.Empty); + } + + /// + /// Unsubscribe all observers and release resources. + /// + public void Dispose() + { + lock (_gate) + { + _isDisposed = true; + _observers = null; + _exception = null; + _value = default(T); + } + } + +#if HAS_AWAIT + /// + /// Gets an awaitable object for the current AsyncSubject. + /// + /// Object that can be awaited. + public AwaitableAsyncSubject GetAwaiter() + { + return this; + } + + /// + /// Specifies a callback action that will be invoked when the subject completes. + /// + /// Callback action that will be invoked when the subject completes. + /// is null. + public void OnCompleted(Action continuation) + { + if (continuation == null) + throw new ArgumentNullException("continuation"); + + OnCompleted(continuation, true); + } +#endif + + private void OnCompleted(Action continuation, bool originalContext) + { + // + // [OK] Use of unsafe Subscribe: this type's Subscribe implementation is safe. + // + this.Subscribe/*Unsafe*/(new AwaitObserver(continuation, originalContext)); + } + + class AwaitObserver : IObserver + { +#if HAS_AWAIT + private readonly SynchronizationContext _context; +#endif + private readonly Action _callback; + + public AwaitObserver(Action callback, bool originalContext) + { +#if HAS_AWAIT + if (originalContext) + _context = SynchronizationContext.Current; +#else + System.Diagnostics.Debug.Assert(!originalContext); +#endif + + _callback = callback; + } + + public void OnCompleted() + { + InvokeOnOriginalContext(); + } + + public void OnError(Exception error) + { + InvokeOnOriginalContext(); + } + + public void OnNext(T value) + { + } + + private void InvokeOnOriginalContext() + { +#if HAS_AWAIT + if (_context != null) + { + // + // No need for OperationStarted and OperationCompleted calls here; + // this code is invoked through await support and will have a way + // to observe its start/complete behavior, either through returned + // Task objects or the async method builder's interaction with the + // SynchronizationContext object. + // + _context.Post(c => ((Action)c)(), _callback); + } + else +#endif + { + _callback(); + } + } + } + + /// + /// Gets whether the AsyncSubject has completed. + /// + public bool IsCompleted + { + get + { + return _isStopped; + } + } + + /// + /// Gets the last element of the subject, potentially blocking until the subject completes successfully or exceptionally. + /// + /// The last element of the subject. Throws an InvalidOperationException if no element was received. + /// The source sequence is empty. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Await pattern for C# and VB compilers.")] + public T GetResult() + { + if (!_isStopped) + { + var e = new ManualResetEvent(false); + OnCompleted(() => e.Set(), false); + e.WaitOne(); + } + + if (_exception != null) { + throw new InvalidOperationException("AwaitableAsyncSubject.GetResult() is rethrowing an inner exception", _exception); + } + + if (!_hasValue) + throw new InvalidOperationException("AwaitableAsyncSubject.GetResult() completed without having a result. That's bad."); + + return _value; + } + } +} \ No newline at end of file diff --git a/src/Squirrel.Core/ReactiveUIMicro/Rx-Shim/GetAwaiter.cs b/src/Squirrel.Core/ReactiveUIMicro/Rx-Shim/GetAwaiter.cs new file mode 100644 index 00000000..c3d1a903 --- /dev/null +++ b/src/Squirrel.Core/ReactiveUIMicro/Rx-Shim/GetAwaiter.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Disposables; +using System.Reactive.Subjects; +using System.Text; +using System.Threading; + +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. + +/* This file is substantially copied from http://rx.codeplex.com/SourceControl/changeset/view/ef6a42709f49#Rx.NET/System.Reactive.Linq/Reactive/Linq/QueryLanguage.Awaiter.cs + * Check LICENSE in this folder for licensing information */ + +namespace ReactiveUIMicro +{ + public static class ObservableAwaiter + { + public static AwaitableAsyncSubject GetAwaiter(this IObservable source) + { + var s = new AwaitableAsyncSubject(); + source.SubscribeSafe(s); + return s; + } + + public static AwaitableAsyncSubject GetAwaiter(this IConnectableObservable source) + { + var s = new AwaitableAsyncSubject(); + source.SubscribeSafe(s); + source.Connect(); + return s; + } + + public static AwaitableAsyncSubject RunAsync(this IObservable source, CancellationToken cancellationToken) + { + var s = new AwaitableAsyncSubject(); + + var cancel = new Action(() => s.OnError(new OperationCanceledException())); + if (cancellationToken.IsCancellationRequested) + { + cancel(); + return s; + } + + var d = source.SubscribeSafe(s); + cancellationToken.Register(d.Dispose); + cancellationToken.Register(cancel); + + return s; + } + + public static AwaitableAsyncSubject RunAsync(this IConnectableObservable source, CancellationToken cancellationToken) + { + var s = new AwaitableAsyncSubject(); + + var cancel = new Action(() => s.OnError(new OperationCanceledException())); + if (cancellationToken.IsCancellationRequested) + { + cancel(); + return s; + } + + var d = new CompositeDisposable(source.SubscribeSafe(s), source.Connect()); + cancellationToken.Register(d.Dispose); + cancellationToken.Register(cancel); + + return s; + } + } +} \ No newline at end of file diff --git a/src/Squirrel.Core/ReactiveUIMicro/Rx-Shim/ImmutableList.cs b/src/Squirrel.Core/ReactiveUIMicro/Rx-Shim/ImmutableList.cs new file mode 100644 index 00000000..72a8e987 --- /dev/null +++ b/src/Squirrel.Core/ReactiveUIMicro/Rx-Shim/ImmutableList.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. + +/* This file is substantially copied from http://rx.codeplex.com/SourceControl/changeset/view/ef6a42709f49#Rx.NET/System.Reactive.Core/Reactive/Internal/ImmutableList.cs + * Check LICENSE in this folder for licensing information */ + +namespace ReactiveUIMicro +{ + internal class ImmutableList + { + T[] data; + + public ImmutableList() + { + data = new T[0]; + } + + public ImmutableList(T[] data) + { + this.data = data; + } + + public ImmutableList Add(T value) + { + var newData = new T[data.Length + 1]; + Array.Copy(data, newData, data.Length); + newData[data.Length] = value; + return new ImmutableList(newData); + } + + public ImmutableList Remove(T value) + { + var i = IndexOf(value); + if (i < 0) + return this; + var newData = new T[data.Length - 1]; + Array.Copy(data, 0, newData, 0, i); + Array.Copy(data, i + 1, newData, i, data.Length - i - 1); + return new ImmutableList(newData); + } + + public int IndexOf(T value) + { + for (var i = 0; i < data.Length; ++i) + if (data[i].Equals(value)) + return i; + return -1; + } + + public T[] Data + { + get { return data; } + } + } +} + diff --git a/src/Squirrel.Core/ReactiveUIMicro/Rx-Shim/LICENSE b/src/Squirrel.Core/ReactiveUIMicro/Rx-Shim/LICENSE new file mode 100644 index 00000000..a208c748 --- /dev/null +++ b/src/Squirrel.Core/ReactiveUIMicro/Rx-Shim/LICENSE @@ -0,0 +1,15 @@ +Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +Microsoft Open Technologies would like to thank its contributors, a list +of whom are at http://rx.codeplex.com/wikipage?title=Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); you +may not use this file except in compliance with the License. You may +obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied. See the License for the specific language governing permissions +and limitations under the License. \ No newline at end of file diff --git a/src/Squirrel.Core/Squirrel.Core.csproj b/src/Squirrel.Core/Squirrel.Core.csproj index 0a4e101a..b1478f4d 100644 --- a/src/Squirrel.Core/Squirrel.Core.csproj +++ b/src/Squirrel.Core/Squirrel.Core.csproj @@ -10,11 +10,12 @@ Properties Squirrel.Core Squirrel.Core - v4.0 + v4.5 512 ..\..\src\ true 0 + true @@ -58,6 +59,7 @@ Full Build 0 + false pdbonly @@ -67,6 +69,7 @@ prompt 4 true + false @@ -149,6 +152,9 @@ + + + @@ -158,6 +164,7 @@ + diff --git a/src/Squirrel.Core/app.config b/src/Squirrel.Core/app.config index c5c407bd..e5eb4927 100644 --- a/src/Squirrel.Core/app.config +++ b/src/Squirrel.Core/app.config @@ -1,4 +1,4 @@ - + - \ No newline at end of file + diff --git a/src/Squirrel.Tests/Squirrel.Tests.csproj b/src/Squirrel.Tests/Squirrel.Tests.csproj index e763feb3..542f501d 100644 --- a/src/Squirrel.Tests/Squirrel.Tests.csproj +++ b/src/Squirrel.Tests/Squirrel.Tests.csproj @@ -10,11 +10,12 @@ Properties Squirrel.Tests Squirrel.Tests - v4.0 + v4.5 512 ..\..\src\ true 0 + true @@ -58,6 +59,7 @@ %28none%29 0 true + false pdbonly @@ -66,6 +68,7 @@ TRACE prompt 4 + false diff --git a/src/Squirrel.Tests/app.config b/src/Squirrel.Tests/app.config index c5c407bd..e5eb4927 100644 --- a/src/Squirrel.Tests/app.config +++ b/src/Squirrel.Tests/app.config @@ -1,4 +1,4 @@ - + - \ No newline at end of file + diff --git a/src/Squirrel.Updater/App.config b/src/Squirrel.Updater/App.config index 4c77df1f..f80039e2 100644 --- a/src/Squirrel.Updater/App.config +++ b/src/Squirrel.Updater/App.config @@ -1,17 +1,17 @@ - + - + - - + + - - + + diff --git a/src/Squirrel.Updater/Squirrel.Updater.csproj b/src/Squirrel.Updater/Squirrel.Updater.csproj index 0d03833d..d3eb1fcb 100644 --- a/src/Squirrel.Updater/Squirrel.Updater.csproj +++ b/src/Squirrel.Updater/Squirrel.Updater.csproj @@ -9,7 +9,7 @@ Properties Squirrel.Updater Squirrel.Updater - v4.0 + v4.5 512 ..\ @@ -24,6 +24,7 @@ DEBUG;TRACE prompt 4 + false AnyCPU @@ -33,6 +34,7 @@ TRACE prompt 4 + false diff --git a/src/Squirrel.WiXUi/Squirrel.WiXUi.csproj b/src/Squirrel.WiXUi/Squirrel.WiXUi.csproj index 8aa786fb..04e06ad8 100644 --- a/src/Squirrel.WiXUi/Squirrel.WiXUi.csproj +++ b/src/Squirrel.WiXUi/Squirrel.WiXUi.csproj @@ -9,10 +9,11 @@ Properties Squirrel.WiXUi Squirrel.WiXUi - v4.0 + v4.5 512 ..\ true + true @@ -23,6 +24,7 @@ prompt 4 649 + false pdbonly @@ -32,6 +34,7 @@ prompt 4 649 + false diff --git a/src/Squirrel.WiXUiClient/Squirrel.WiXUiClient.csproj b/src/Squirrel.WiXUiClient/Squirrel.WiXUiClient.csproj index 5f5975f5..aea80e29 100644 --- a/src/Squirrel.WiXUiClient/Squirrel.WiXUiClient.csproj +++ b/src/Squirrel.WiXUiClient/Squirrel.WiXUiClient.csproj @@ -9,10 +9,11 @@ Properties Squirrel.WiXUiClient Squirrel.WiXUiClient - v4.0 + v4.5 512 ..\ true + true @@ -22,6 +23,7 @@ DEBUG;TRACE prompt 4 + false pdbonly @@ -30,6 +32,7 @@ TRACE prompt 4 + false diff --git a/src/SquirrelIAppUpdateTestTarget/Properties/Resources.Designer.cs b/src/SquirrelIAppUpdateTestTarget/Properties/Resources.Designer.cs index bd2a5593..f0762530 100644 --- a/src/SquirrelIAppUpdateTestTarget/Properties/Resources.Designer.cs +++ b/src/SquirrelIAppUpdateTestTarget/Properties/Resources.Designer.cs @@ -1,17 +1,17 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.17626 +// Runtime Version:4.0.30319.34003 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ -namespace SquirrelIAppUpdateTestTarget.Properties -{ - - +namespace SquirrelIAppUpdateTestTarget.Properties { + using System; + + /// /// A strongly-typed resource class, for looking up localized strings, etc. /// @@ -22,48 +22,40 @@ namespace SquirrelIAppUpdateTestTarget.Properties [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources - { - + internal class Resources { + private static global::System.Resources.ResourceManager resourceMan; - + private static global::System.Globalization.CultureInfo resourceCulture; - + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() - { + internal Resources() { } - + /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager - { - get - { - if ((resourceMan == null)) - { + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SquirrelIAppUpdateTestTarget.Properties.Resources", typeof(Resources).Assembly); resourceMan = temp; } return resourceMan; } } - + /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture - { - get - { + internal static global::System.Globalization.CultureInfo Culture { + get { return resourceCulture; } - set - { + set { resourceCulture = value; } } diff --git a/src/SquirrelIAppUpdateTestTarget/Properties/Settings.Designer.cs b/src/SquirrelIAppUpdateTestTarget/Properties/Settings.Designer.cs index b1d344ea..de306b5f 100644 --- a/src/SquirrelIAppUpdateTestTarget/Properties/Settings.Designer.cs +++ b/src/SquirrelIAppUpdateTestTarget/Properties/Settings.Designer.cs @@ -1,28 +1,24 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.17626 +// Runtime Version:4.0.30319.34003 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ -namespace SquirrelIAppUpdateTestTarget.Properties -{ - - +namespace SquirrelIAppUpdateTestTarget.Properties { + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] - internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase - { - + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "12.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); - - public static Settings Default - { - get - { + + public static Settings Default { + get { return defaultInstance; } } diff --git a/src/SquirrelIAppUpdateTestTarget/SquirrelIAppUpdateTestTarget.csproj b/src/SquirrelIAppUpdateTestTarget/SquirrelIAppUpdateTestTarget.csproj index 0f3a2cb5..a242a30a 100644 --- a/src/SquirrelIAppUpdateTestTarget/SquirrelIAppUpdateTestTarget.csproj +++ b/src/SquirrelIAppUpdateTestTarget/SquirrelIAppUpdateTestTarget.csproj @@ -9,12 +9,13 @@ Properties SquirrelIAppUpdateTestTarget SquirrelIAppUpdateTestTarget - v4.0 + v4.5 512 {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 4 ..\ true + AnyCPU @@ -25,6 +26,7 @@ DEBUG;TRACE prompt 4 + false AnyCPU @@ -34,6 +36,7 @@ TRACE prompt 4 + false diff --git a/src/SquirrelIAppUpdateTestTarget/app.config b/src/SquirrelIAppUpdateTestTarget/app.config index 118c7c04..50db73bb 100644 --- a/src/SquirrelIAppUpdateTestTarget/app.config +++ b/src/SquirrelIAppUpdateTestTarget/app.config @@ -1,19 +1,19 @@ - + - - + + - - + + - - + + - \ No newline at end of file +