internal int WhoWins(CanonicalName negative, CanonicalName positive) { if (positive == negative) { return(0); } foreach (var p in Priorities) { int pos = 0; int neg = 0; foreach (var key in GetCanonicalNames(p).Where(each => this[p, each, null].IsTrue())) { pos = Math.Max(positive.MatchQuality(key), pos); neg = Math.Max(negative.MatchQuality(key), neg); } if (pos == neg) { continue; } return(pos - neg); } // didn't find a rule that can distinguish. // if the packages differ by version only, use that to decide if (positive.DiffersOnlyByVersion(negative)) { return(((long)(ulong)positive.Version - (long)(ulong)negative.Version) > 0 ? 1 : -1); } // nothing to decide with! return(0); }
public Task InstallPackage(CanonicalName canonicalName, bool? autoUpgrade, bool? force, bool? download, bool? pretend, CanonicalName replacingPackage) { var response = Event<GetResponseInterface>.RaiseFirst(); if (CancellationRequested) { response.OperationCanceled("install-package"); return FinishedSynchronously; } double[] overallProgress = {0.0}; double[] eachTaskIsWorth = {0.0}; int[] lastProgress = {0}; Package currentPackageInstalling = null; var numberOfPackagesToInstall = 0; var numberOfPackagesToDownload = 0; CurrentTask.Events += new IndividualProgress(percentage => { overallProgress[0] += ((percentage - lastProgress[0])*eachTaskIsWorth[0])/100; lastProgress[0] = percentage; // ReSharper disable PossibleNullReferenceException ... this is what I really want. :[ // ReSharper disable AccessToModifiedClosure response.InstallingPackageProgress(currentPackageInstalling.CanonicalName, percentage, (int)(overallProgress[0]*100)); // ReSharper restore AccessToModifiedClosure // ReSharper restore PossibleNullReferenceException }); var unwantedPackages = new List<Package>(); if( null != replacingPackage ) { if( replacingPackage.DiffersOnlyByVersion(canonicalName)) { unwantedPackages.AddRange(SearchForPackages(replacingPackage)); } } using (var manualResetEvent = new ManualResetEvent(true)) { try { lock (_manualResetEvents) { _manualResetEvents.Add(manualResetEvent); } var packagesTriedToDownloadThisTask = new List<Package>(); if (canonicalName.IsPartial) { response.Error("Invalid Canonical Name", "InstallPackage", "Canonical name '{0}' is not a complete canonical name".format(canonicalName)); } var package = SearchForPackages(canonicalName).FirstOrDefault(); if (package == null) { response.UnknownPackage(canonicalName); return FinishedSynchronously; } if (package.IsBlocked) { response.PackageBlocked(canonicalName); return FinishedSynchronously; } var installedPackages = package.InstalledPackages.ToArray(); // is the user authorized to install this? if (null != replacingPackage) { if (replacingPackage.DiffersOnlyByVersion(canonicalName)) { if (!Event<CheckForPermission>.RaiseFirst(PermissionPolicy.UpdatePackage)) { return FinishedSynchronously; } } } else { if( package.LatestInstalledThatUpdatesToThis != null ) { if (!Event<CheckForPermission>.RaiseFirst(PermissionPolicy.UpdatePackage)) { return FinishedSynchronously; } } else { if (!Event<CheckForPermission>.RaiseFirst(PermissionPolicy.InstallPackage)) { return FinishedSynchronously; } } } // if this is an explicit update or upgrade, // - check to see if there is a compatible package already installed that is marked do-not-update // fail if so. if (null != replacingPackage && unwantedPackages.Any( each => each.IsBlocked )) { response.PackageBlocked(canonicalName); return FinishedSynchronously; } // mark the package as the client requested. package.PackageSessionData.DoNotSupercede = (false == autoUpgrade); package.PackageSessionData.UpgradeAsNeeded = (true == autoUpgrade); package.PackageSessionData.IsWanted = true; // the resolve-acquire-install-loop do { // if the world changes, this will get set somewhere between here and the // other end of the do-loop. manualResetEvent.Reset(); if (CancellationRequested) { response.OperationCanceled("install-package"); return FinishedSynchronously; } IEnumerable<Package> installGraph; try { UpdateDependencyFlags(); installGraph = GenerateInstallGraph(package).ToArray(); } catch (OperationCompletedBeforeResultException) { // we encountered an unresolvable condition in the install graph. // messages should have already been sent. response.FailedPackageInstall(canonicalName, package.LocalLocations.FirstOrDefault(), "One or more dependencies are unable to be resolved."); return FinishedSynchronously; } // seems like a good time to check if we're supposed to bail... if (CancellationRequested) { response.OperationCanceled("install-package"); return FinishedSynchronously; } if (download == false && pretend == true) { // we can just return a bunch of foundpackage messages, since we're not going to be // actually installing anything, nor trying to download anything. foreach (var p in installGraph) { response.PackageInformation(p); } return FinishedSynchronously; } // we've got an install graph. // let's see if we've got all the files var missingFiles = (from p in installGraph where !p.HasLocalLocation select p).ToArray(); if (download == true) { // we want to try downloading all the files that we're missing, regardless if we've tried before. // unless we've already tried in this task once. foreach (var p in missingFiles.Where(packagesTriedToDownloadThisTask.Contains)) { packagesTriedToDownloadThisTask.Add(p); p.PackageSessionData.CouldNotDownload = false; } } if (numberOfPackagesToInstall != installGraph.Count() || numberOfPackagesToDownload != missingFiles.Count()) { // recalculate the rest of the install progress based on the new install graph. numberOfPackagesToInstall = installGraph.Count(); numberOfPackagesToDownload = missingFiles.Count(); eachTaskIsWorth[0] = (1.0 - overallProgress[0])/(numberOfPackagesToInstall + numberOfPackagesToDownload); } if (missingFiles.Any()) { // we've got some packages to install that don't have files. foreach (var p in missingFiles.Where(p => !p.PackageSessionData.HasRequestedDownload)) { SessionData.Current.RequireRemoteFile(p.CanonicalName, p.RemotePackageLocations, PackageManagerSettings.CoAppPackageCache, false,(rrfState) => { Updated(); //shake loose anything that might be waiting for this. return rrfState.LocalLocation; }); p.PackageSessionData.HasRequestedDownload = true; } } else { // check to see if this package requires trust. bool ok = true; foreach (var pkg in installGraph.Where(pkg => pkg.RequiresTrustedPublisher && !TrustedPublishers.ContainsIgnoreCase(pkg.PublicKeyToken))) { response.FailedPackageInstall(pkg.CanonicalName, pkg.LocalLocations.FirstOrDefault(), "Package requires a trusted publisher key of '{0}'.".format(pkg.PublicKeyToken)); ok = false; } if( ok == false) { return FinishedSynchronously; } if (pretend == true) { // we can just return a bunch of found-package messages, since we're not going to be // actually installing anything, and everything we needed is downloaded. foreach (var p in installGraph) { response.PackageInformation(p); } return FinishedSynchronously; } var failed = false; // no missing files? Check // complete install graph? Check foreach (var p in installGraph) { currentPackageInstalling = p; // seems like a good time to check if we're supposed to bail... if (CancellationRequested) { response.OperationCanceled("install-package"); return FinishedSynchronously; } var validLocation = currentPackageInstalling.PackageSessionData.LocalValidatedLocation; try { if (!currentPackageInstalling.IsInstalled) { if (string.IsNullOrEmpty(validLocation)) { // can't find a valid location response.FailedPackageInstall(currentPackageInstalling.CanonicalName, currentPackageInstalling.LocalLocations.FirstOrDefault(), "Can not find local valid package"); currentPackageInstalling.PackageSessionData.PackageFailedInstall = true; } else { lastProgress[0] = 0; // GS01: We should put a softer lock here to keep the client aware that packages // are being installed on other threads... lock (typeof (MSIBase)) { if (Engine.DoesTheServiceNeedARestart) { // something has changed where we need restart the service before we can continue. // and the one place we don't wanna be when we issue a shutdown in in Install :) ... Engine.RestartService(); response.OperationCanceled("install-package"); return FinishedSynchronously; } // install progress is now handled by the delegate at the beginning of this function. currentPackageInstalling.Install(); } overallProgress[0] += ((100 - lastProgress[0])*eachTaskIsWorth[0])/100; response.InstallingPackageProgress(currentPackageInstalling.CanonicalName, 100, (int)(overallProgress[0]*100)); response.InstalledPackage(currentPackageInstalling.CanonicalName); Signals.InstalledPackage(currentPackageInstalling.CanonicalName); } } } catch (Exception e) /* (PackageInstallFailedException pife) */ { Logger.Error("FAILED INSTALL"); Logger.Error(e); response.FailedPackageInstall(currentPackageInstalling.CanonicalName, validLocation, "Package failed to install."); currentPackageInstalling.PackageSessionData.PackageFailedInstall = true; if (!currentPackageInstalling.PackageSessionData.AllowedToSupercede) { throw new OperationCompletedBeforeResultException(); // user specified packge as critical. } failed = true; break; } } if (!failed) { if( unwantedPackages.Any()) { foreach (Package eachPkg in unwantedPackages) { eachPkg.IsWanted = false; } } else { var olderpkgs = package.InstalledPackages.Where(each => each.IsWanted && package.IsNewerThan(each)).ToArray(); if( olderpkgs.Length > 0 ) { //anthing older? if( olderpkgs.Length > 1) { // hmm. more than one. // is there just a single thing we're updating? olderpkgs = olderpkgs.Where(package.IsAnUpdateFor).ToArray(); } // if we can get down to one, let's unwant that. if (olderpkgs.Length == 1) { ((Package)olderpkgs[0]).IsWanted = false; } } } // W00T ... We did it! // check for restart required... if (Engine.DoesTheServiceNeedARestart) { // something has changed where we need restart the service before we can continue. // and the one place we don't wanna be when we issue a shutdown in in Install :) ... response.Restarting(); Engine.RestartService(); return FinishedSynchronously; } return FinishedSynchronously; } // otherwise, let's run it thru again. maybe it'll come together. } //---------------------------------------------------------------------------- // wait until either the manualResetEvent is set, but check every second or so // to see if the client has cancelled the operation. while (!manualResetEvent.WaitOne(500)) { if (CancellationRequested) { response.OperationCanceled("install-package"); return FinishedSynchronously; } // we can also use this opportunity to update progress on any outstanding download tasks. overallProgress[0] += missingFiles.Sum(missingFile => ((missingFile.PackageSessionData.DownloadProgressDelta*eachTaskIsWorth[0])/100)); } } while (true); } catch (OperationCompletedBeforeResultException) { // can't continue with options given. return FinishedSynchronously; } finally { // remove manualResetEvent from the mre list lock (_manualResetEvents) { _manualResetEvents.Remove(manualResetEvent); } } } }
internal static FourPartVersion GetCurrentPackageVersion(CanonicalName canonicalName) { var activePkg = PackageManagerImpl.Instance.InstalledPackages.Where(each => canonicalName.DiffersOnlyByVersion(each.CanonicalName)) .OrderBy(each => each, new Toolkit.Extensions.Comparer<Package>((packageA, packageB) => GeneralPackageSettings.Instance.WhoWins(packageA, packageB))) .FirstOrDefault(); return activePkg == null ? 0 : activePkg.Version; }
internal static FourPartVersion GetCurrentPackageVersion(CanonicalName canonicalName) { var installedVersionsOfPackage = PackageManagerImpl.Instance.InstalledPackages.Where(each => canonicalName.DiffersOnlyByVersion(each.CanonicalName)).OrderByDescending(each => each.CanonicalName.Version); var latestPackage = installedVersionsOfPackage.FirstOrDefault(); // clean as we go... if (latestPackage == null) { PackageManagerSettings.PerPackageSettings[canonicalName.GeneralName, "CurrentVersion"].Value = null; return 0; } // is there a version set? FourPartVersion ver = (ulong)PackageManagerSettings.PerPackageSettings[canonicalName.GeneralName, "CurrentVersion"].LongValue; // if not (or it's not set to an installed package), let's set it to the latest version of the package. if (ver == 0 || installedVersionsOfPackage.FirstOrDefault(p => p.CanonicalName.Version == ver) == null) { latestPackage.SetPackageCurrent(); return latestPackage.CanonicalName.Version; } return ver; }
internal int WhoWins(CanonicalName negative, CanonicalName positive) { if( positive == negative ) { return 0; } foreach( var p in Priorities) { int pos = 0; int neg = 0; foreach (var key in GetCanonicalNames(p).Where(each=> this[p, each, null].IsTrue())) { pos = Math.Max(positive.MatchQuality(key), pos); neg = Math.Max(negative.MatchQuality(key), neg); } if( pos == neg) { continue; } return pos - neg; } // didn't find a rule that can distinguish. // if the packages differ by version only, use that to decide if( positive.DiffersOnlyByVersion(negative) ) { return (((long)(ulong)positive.Version - (long)(ulong)negative.Version) > 0 ? 1 : -1); } // nothing to decide with! return 0; }
internal static FourPartVersion GetCurrentPackageVersion(CanonicalName canonicalName) { var activePkg = PackageManagerImpl.Instance.InstalledPackages.Where(each => canonicalName.DiffersOnlyByVersion(each.CanonicalName)) .OrderBy(each => each, new Toolkit.Extensions.Comparer <Package>((packageA, packageB) => GeneralPackageSettings.Instance.WhoWins(packageA, packageB))) .FirstOrDefault(); return(activePkg == null ? 0 : activePkg.Version); }