/// <summary>
        /// Get an InteractivePackage with populated assembly references from a restored NuGet package.
        /// </summary>
        /// <param name="inputPackages">Input to RestorePackagesAsync, used to ensure the returned package
        /// has the same SupportedVersionRange as the requested package, and to determine if this is a
        /// user-specified package or a dependency.</param>
        static InteractivePackage GetInteractivePackageFromReader(
            PackageReaderBase packageReader,
            InteractiveNuGetProject project,
            IEnumerable <InteractivePackage> inputPackages)
        {
            ImmutableList <FilePath> assemblyReferences = null;
            var fx = project.TargetFramework;
            var packageIdentity = packageReader.GetIdentity();

            if (packageReader
                .GetSupportedFrameworks()
                .Any(f => DefaultCompatibilityProvider.Instance.IsCompatible(fx, f)))
            {
                assemblyReferences =
                    project.GetPackageAssemblyReferences(packageReader, packageIdentity);
            }

            var originalInputPackage = inputPackages.FirstOrDefault(
                p => PackageIdComparer.Equals(p.Identity, packageIdentity));

            // Persist original VersionRange to what gets in the installed package list so that
            // the same original version range string gets written to the manifest on save
            return(new InteractivePackage(
                       packageIdentity,
                       isExplicit: originalInputPackage?.IsExplicit == true,
                       assemblyReferences: assemblyReferences,
                       supportedVersionRange: originalInputPackage?.SupportedVersionRange));
        }
Пример #2
0
        /// <summary>
        /// Install a NuGet package and make it available for use in the <see cref="EvaluationService"/>.
        /// </summary>
        public async Task InstallAsync(
            InteractivePackageDescription packageDescription,
            CancellationToken cancellationToken = default)
        {
            if (packageDescription.PackageId == null)
            {
                throw new ArgumentException(
                          $"{nameof (packageDescription.PackageId)} property cannot be null",
                          nameof(packageDescription));
            }

            var package = packageDescription.ToInteractivePackage();

            var installedPackages = await packageManager.InstallPackageAsync(
                package,
                packageDescription.GetSourceRepository(),
                cancellationToken);

            // TODO: Should probably alert user that the package is already installed.
            //       Should we add a fresh #r for the package in case that's what they're trying to get?
            //       A feel good thing?
            if (installedPackages.Count == 0)
            {
                return;
            }

            var agent = await getAgentConnectionHandler(false, cancellationToken);

            foreach (var installedPackage in installedPackages)
            {
                ReferencePackageInWorkspace(installedPackage);
                await LoadPackageIntegrationsAsync(agent, installedPackage, cancellationToken);
            }

            // TODO: Figure out metapackages. Install Microsoft.AspNet.SignalR, for example,
            //       and no #r submission gets generated, so all the workspace reference stuff
            //       above fails to bring in references to dependnet assemblies automatically.
            //       User must type them out themselves.
            //
            //       This was busted in our NuGet 2.x code as well.
            package = installedPackages.FirstOrDefault(
                p => PackageIdComparer.Equals(p, package));

            // TODO: Same issue as installedPackages.Count == 0. What do we want to tell user?
            //       probably they tried to install a package they already had installed, and
            //       maybe it bumped a shared dep (which is why installedPackages is non-empty).
            if (package == null)
            {
                return;
            }

            if (await ReferenceTopLevelPackageAsync(package, cancellationToken))
            {
                evaluationService.OutdateAllCodeCells();
                await evaluationService.EvaluateAllAsync(cancellationToken);
            }
        }
Пример #3
0
        public async Task CanInstall(FrameworkName targetFramework, PackageInstallData data)
        {
            var project = CreatePackageManager(targetFramework);
            await project.InstallPackageAsync(
                new InteractivePackage (new PackageIdentity(data.Id, data.VersionToInstall)),
                sourceRepository : null, // use default
                cancellationToken : CancellationToken.None);

            Assert.Collection(
                project.InstalledPackages.Where(
                    p => PackageIdComparer.Equals(p.Identity.Id, data.Id)),
                package => {
                Assert.True(package.IsExplicit);

                Assert.Equal(data.ExpectedInstalled, package.Identity.Version);
                Assert.Equal(new VersionRange(data.ExpectedInstalled), package.SupportedVersionRange);
            });
        }
        /// <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);
        }
Пример #5
0
        async Task LoadPackageIntegrationsAsync(
            AgentType agentType,
            TargetCompilationConfiguration targetCompilationConfiguration,
            IEvaluationContextManager evaluationContextManager,
            InteractivePackage package,
            CancellationToken cancellationToken)
        {
            // Forms is special-cased because we own it and load the extension from our framework.
            if (PackageIdComparer.Equals(package.Identity.Id, "Xamarin.Forms"))
            {
                await WorkspaceConfiguration.LoadFormsAgentExtensions(
                    package.Identity.Version.Version,
                    targetCompilationConfiguration,
                    evaluationContextManager,
                    dependencyResolver);
            }

            var assembliesToLoadOnAgent = new List <ResolvedAssembly> ();

            // Integration assemblies are not expected to be in a TFM directory—we look for them in
            // the `xamarin.interactive` folder inside the NuGet package.
            var packagePath = packageManager.GetPackageInstallPath(package);

            var interactivePath = packagePath.Combine("xamarin.interactive");

            if (interactivePath.DirectoryExists)
            {
                var interactiveAssemblies = interactivePath.EnumerateFiles("*.dll");
                foreach (var interactiveReference in interactiveAssemblies)
                {
                    var resolvedAssembly = dependencyResolver.ResolveWithoutReferences(interactiveReference);

                    if (HasIntegration(resolvedAssembly))
                    {
                        assembliesToLoadOnAgent.Add(resolvedAssembly);
                    }
                }
            }

            if (assembliesToLoadOnAgent.Count > 0)
            {
                var includePeImage = targetCompilationConfiguration.IncludePEImagesInDependencyResolution;

                var assembliesToLoad = assembliesToLoadOnAgent.Select(dep => {
                    var peImage = includePeImage
                       ? GetFileBytes(dep.Path)
                       : null;
                    var syms = includePeImage
                        ? GetDebugSymbolsFromAssemblyPath(dep.Path)
                        : null;
                    return(new AssemblyDefinition(
                               dep.AssemblyName,
                               dep.Path,
                               peImage: peImage,
                               debugSymbols: syms
                               ));
                }).ToArray();

                await evaluationContextManager.LoadAssembliesAsync(
                    targetCompilationConfiguration.EvaluationContextId,
                    assembliesToLoad,
                    cancellationToken);
            }

            await getAgentConnectionHandler(true, cancellationToken);
        }