public FilePath GetPackageInstallPath()

in CodeAnalysis/Xamarin.Interactive.CodeAnalysis/NuGet/InteractivePackageManager.cs [169:307]


        public FilePath GetPackageInstallPath (InteractivePackage package)
            => project.GetInstalledPath (package.Identity);

        /// <summary>
        /// Install a NuGet package. Returns all newly installed packages.
        /// </summary>
        public async Task<IReadOnlyCollection<InteractivePackage>> InstallPackageAsync (
            InteractivePackage package,
            SourceRepository sourceRepository,
            CancellationToken cancellationToken)
        {
            if (package == null)
                throw new ArgumentNullException (nameof (package));
            if (!package.Identity.HasVersion)
                throw new ArgumentException ("PackageIdentity.Version must be set");

            // TODO: File upstream issue about exception if primary source repo is offline.
            //       Shouldn't secondary source repos kick in? Our current work around is to
            //       pass the source repo from search to install, but that's not perfect.
            sourceRepository = sourceRepository ?? SourceRepositories [0];

            project.ResetInstallationContext ();

            // Just need to apply one fixup here
            if (PackageIdComparer.Equals (package.Identity.Id, FixedXamarinFormsPackageIdentity.Id) &&
                package.Identity.Version != FixedXamarinFormsPackageIdentity.Version) {
                Log.Warning (
                    TAG,
                    $"Replacing requested Xamarin.Forms version {package.Identity.Version} with " +
                    $"required version {FixedXamarinFormsPackageIdentity.Version}.");
                package = package.WithVersion (
                    FixedXamarinFormsPackageIdentity.Version,
                    overwriteRange: true);
            }

            if (PackageIdComparer.Equals (package.Identity.Id, IntegrationPackageId)) {
                Log.Warning (TAG, $"Refusing to add integration NuGet package {IntegrationPackageId}.");
                return Array.Empty<InteractivePackage> ();
            }

            var resolutionContext = new ResolutionContext (
                DependencyBehavior.Lowest, // IDEs only use Highest if upgrading
                includePrelease: true,
                includeUnlisted: true,
                versionConstraints: VersionConstraints.None);

            // Although there is a single repo associated with the package being installed,
            // dependency resolution will also look into the secondary sources. In some cases,
            // this can greatly slow down installation. For the primary case of searching for
            // packages in nuget.org, prevent the package manager from using secondary sources
            // for resolution.
            //
            // It is important to pass an empty enumerable, because if we pass null, the package
            // manager will determine secondary sources based on the NuGet configuration.
            var secondarySources =
                sourceRepository == SourceRepositories [0]
                ? Enumerable.Empty<SourceRepository> ()
                : SourceRepositories.Where (r => r != sourceRepository).ToArray ();

            // There does not appear to be a way to hook into or override functionality of the
            // NuGetPackageManager or PackageResolver classes. In order to mess with package
            // resolution, we need to either write a lot of code, proxy the sources, or intercede
            // via preview installation actions.
            //
            // Here we do the latter, though it is not the best general-purpose approach. It works
            // fine for replacing one single package that we know a LOT about. If that package's
            // dependencies continually changed, we'd be better off with another approach.
            var previewInstallActions = await packageManager.PreviewInstallPackageAsync (
                project,
                package.Identity,
                resolutionContext,
                projectContext,
                sourceRepository,
                secondarySources,
                cancellationToken);

            var installActions = new List<NuGetProjectAction> ();
            foreach (var action in previewInstallActions) {
                // If the installed package has a dependency on Xamarin.Forms, make sure the version
                // that gets installed is our preferred version. Force it to install from the primary
                // source repository, because we can't assume that version is available everywhere.
                //
                // TODO: Consider adding a search or something to see if we can use the specified source
                //       instead. Could be handy if nuget.org is down or the user is offline and using
                //       a local repo.
                if (action.PackageIdentity.Id == FixedXamarinFormsPackageIdentity.Id)
                    installActions.Add (NuGetProjectAction.CreateInstallProjectAction (
                        FixedXamarinFormsPackageIdentity,
                        SourceRepositories [0],
                        action.Project));
                else
                    installActions.Add (action);
            }

            // We follow the modern behavior of .NET Core and do not actually install packages anywhere.
            // Instead, we ultimately reference them out of the user's global package cache (by default,
            // ~/.nuget/packages). Our NuGetProject implementation simply collects package assembly
            // references (and potentially other necessary files) and populates them back into the
            // InteractiveInstallationContext.
            using (var sourceCacheContext = new SourceCacheContext ())
                await packageManager.ExecuteNuGetProjectActionsAsync (
                    project,
                    installActions,
                    projectContext,
                    sourceCacheContext,
                    cancellationToken);

            // Identify which packages were not already noted as installed, or have been upgraded now
            var newlyInstalledPackages = new List<InteractivePackage> ();
            foreach (var newPackage in project.InstallationContext.InstalledPackages) {
                InteractivePackage finalNewPackage;
                var foundInstalledMatch = installedPackages.TryGetValue (
                    newPackage,
                    out finalNewPackage);

                if (!foundInstalledMatch ||
                    newPackage.Identity.Version > finalNewPackage.Identity.Version) {

                    // Make sure we have a reference to a matching explicit InteractivePackage if it
                    // exists, so that we can persist the original SupportedVersionRange
                    if (!foundInstalledMatch)
                        finalNewPackage = PackageIdComparer.Equals (package, newPackage)
                            ? package
                            : newPackage;

                    finalNewPackage = newPackage
                        .WithIsExplicit (finalNewPackage.IsExplicit)
                        .WithSupportedVersionRange (finalNewPackage.SupportedVersionRange);

                    newlyInstalledPackages.Add (finalNewPackage);
                    installedPackages = installedPackages
                        .Remove (finalNewPackage)
                        .Add (finalNewPackage);
                    UpdateInstalledPackages ();
                }
            }

            return newlyInstalledPackages;
        }