/// <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)); }
/// <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); } }
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); }
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); }