From b34419702828cbedf3a1b472d4167c46d7c35154 Mon Sep 17 00:00:00 2001 From: Akshita Date: Tue, 10 Sep 2019 20:40:26 -0700 Subject: [PATCH 1/2] use msbuild to get the nuget packages path --- .../NuGetPackagePathResolverTests.cs | 31 --------- .../Commands/LoadExtensionFromNuGetPackage.cs | 25 ------- .../Commands/LoadExtensionInDirectory.cs | 21 ++++++ Microsoft.DotNet.Interactive/KernelBase.cs | 25 +++---- .../NuGetPackagePathResolver.cs | 34 ---------- .../Kernel/CSharpKernelTests.cs | 13 +--- .../PackageRestoreContextTests.cs | 10 +++ .../Kernel/CSharpKernelExtensions.cs | 4 +- .../Packaging/PackageRestoreContext.cs | 68 ++++++++++++++++++- 9 files changed, 114 insertions(+), 117 deletions(-) delete mode 100644 Microsoft.DotNet.Interactive.Tests/NuGetPackagePathResolverTests.cs delete mode 100644 Microsoft.DotNet.Interactive/Commands/LoadExtensionFromNuGetPackage.cs create mode 100644 Microsoft.DotNet.Interactive/Commands/LoadExtensionInDirectory.cs delete mode 100644 Microsoft.DotNet.Interactive/NuGetPackagePathResolver.cs diff --git a/Microsoft.DotNet.Interactive.Tests/NuGetPackagePathResolverTests.cs b/Microsoft.DotNet.Interactive.Tests/NuGetPackagePathResolverTests.cs deleted file mode 100644 index f6fc401d9..000000000 --- a/Microsoft.DotNet.Interactive.Tests/NuGetPackagePathResolverTests.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Xunit; -using MLS.Agent.Tools; -using Microsoft.DotNet.Interactive; -using FluentAssertions; -using System.Linq; -using MLS.Agent.Tools.Tests; -using System.IO; -using System.Runtime.InteropServices.ComTypes; -using System.ComponentModel.DataAnnotations; - -namespace Microsoft.DotNet.Interactive.Tests -{ - public class NuGetPackagePathResolverTests - { - [Fact] - public void Can_find_path_of_nuget_package() - { - var firstPackageRef = new NugetPackageReference("first", "2.0.0"); - var secondPackageRef = new NugetPackageReference("second", "3.0.0"); - var directory = new InMemoryDirectoryAccessor() - { - ($"{firstPackageRef.PackageName}/{firstPackageRef.PackageVersion}/lib/netstandard2.0/{firstPackageRef.PackageName}.dll", ""), - ($"{secondPackageRef.PackageName}/{secondPackageRef.PackageVersion}/lib/netstandard2.0/{secondPackageRef.PackageName}.dll", "") - }; - - NuGetPackagePathResolver.TryGetNuGetPackageBasePath(firstPackageRef, directory.GetAllFilesRecursively().Select(file => directory.GetFullyQualifiedFilePath(file)), out var nugetPackageDirectory); - - nugetPackageDirectory.GetFullyQualifiedRoot().FullName.Should().Be(directory.GetFullyQualifiedPath(new RelativeDirectoryPath($"{firstPackageRef.PackageName}/{firstPackageRef.PackageVersion}")).FullName); - } - } -} \ No newline at end of file diff --git a/Microsoft.DotNet.Interactive/Commands/LoadExtensionFromNuGetPackage.cs b/Microsoft.DotNet.Interactive/Commands/LoadExtensionFromNuGetPackage.cs deleted file mode 100644 index abe34f7dc..000000000 --- a/Microsoft.DotNet.Interactive/Commands/LoadExtensionFromNuGetPackage.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System; -using System.Collections.Generic; -using System.IO; -using Newtonsoft.Json; - -namespace Microsoft.DotNet.Interactive.Commands -{ - public class LoadExtensionFromNuGetPackage : KernelCommandBase - { - public LoadExtensionFromNuGetPackage(NugetPackageReference nugetPackageReference, IEnumerable metadataReferences) - { - NugetPackageReference = nugetPackageReference ?? throw new ArgumentNullException(nameof(nugetPackageReference)); - MetadataReferences = metadataReferences ?? throw new ArgumentNullException(nameof(metadataReferences)); - } - - [JsonIgnore] - public NugetPackageReference NugetPackageReference { get; } - - [JsonIgnore] - public IEnumerable MetadataReferences { get; } - } -} \ No newline at end of file diff --git a/Microsoft.DotNet.Interactive/Commands/LoadExtensionInDirectory.cs b/Microsoft.DotNet.Interactive/Commands/LoadExtensionInDirectory.cs new file mode 100644 index 000000000..591b180ee --- /dev/null +++ b/Microsoft.DotNet.Interactive/Commands/LoadExtensionInDirectory.cs @@ -0,0 +1,21 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.IO; +using MLS.Agent.Tools; +using Newtonsoft.Json; + +namespace Microsoft.DotNet.Interactive.Commands +{ + public class LoadExtensionsInDirectory : KernelCommandBase + { + public LoadExtensionsInDirectory(IDirectoryAccessor directory) + { + Directory = directory; + } + + public IDirectoryAccessor Directory { get; } + } +} \ No newline at end of file diff --git a/Microsoft.DotNet.Interactive/KernelBase.cs b/Microsoft.DotNet.Interactive/KernelBase.cs index da00195fc..d3315c551 100644 --- a/Microsoft.DotNet.Interactive/KernelBase.cs +++ b/Microsoft.DotNet.Interactive/KernelBase.cs @@ -83,37 +83,34 @@ private void AddDirectiveMiddlewareAndCommonCommandHandlers() context, next), - LoadExtensionFromNuGetPackage loadCSharpExtension => - HandleLoadExtensionFromNuGetPackage( - loadCSharpExtension, - context, + LoadExtensionsInDirectory loadExtensionsInDirectory => + HandleLoadExtensionsInDirectory( + loadExtensionsInDirectory, + context, next), _ => next(command, context) }); } - private async Task HandleLoadExtensionFromNuGetPackage( - LoadExtensionFromNuGetPackage loadExtensionFromNuGetPackage, + private async Task HandleLoadExtensionsInDirectory( + LoadExtensionsInDirectory loadExtensionsInDirectory, KernelInvocationContext invocationContext, KernelPipelineContinuation next) { - loadExtensionFromNuGetPackage.Handler = async context => + loadExtensionsInDirectory.Handler = async context => { if (context.HandlingKernel is IExtensibleKernel extensibleKernel) { - if (NuGetPackagePathResolver.TryGetNuGetPackageBasePath(loadExtensionFromNuGetPackage.NugetPackageReference, loadExtensionFromNuGetPackage.MetadataReferences, out var nugetPackageDirectory)) - { - await extensibleKernel.LoadExtensionsInDirectory(nugetPackageDirectory, context); - } + await extensibleKernel.LoadExtensionsInDirectory(loadExtensionsInDirectory.Directory, context); } else { - context.Publish(new CommandFailed($"Kernel {context.HandlingKernel.Name} doesn't support loading extensions", loadExtensionFromNuGetPackage)); + context.Publish(new CommandFailed($"Kernel {context.HandlingKernel.Name} doesn't support loading extensions", loadExtensionsInDirectory)); } }; - await next(loadExtensionFromNuGetPackage, invocationContext); + await next(loadExtensionsInDirectory, invocationContext); } private async Task HandleLoadExtension( @@ -307,7 +304,7 @@ internal virtual async Task HandleInternalAsync( await command.InvokeAsync(context); } - private readonly ConcurrentQueue _commandQueue = + private readonly ConcurrentQueue _commandQueue = new ConcurrentQueue(); public Task SendAsync( diff --git a/Microsoft.DotNet.Interactive/NuGetPackagePathResolver.cs b/Microsoft.DotNet.Interactive/NuGetPackagePathResolver.cs deleted file mode 100644 index 4f7e65838..000000000 --- a/Microsoft.DotNet.Interactive/NuGetPackagePathResolver.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using MLS.Agent.Tools; -using System.Collections.Generic; -using System.IO; -using System.Linq; - -namespace Microsoft.DotNet.Interactive -{ - public class NuGetPackagePathResolver - { - public static bool TryGetNuGetPackageBasePath(NugetPackageReference nugetPackage, IEnumerable metadataReferences, out IDirectoryAccessor nugetPackageDirectory) - { - var nugetPackageAssembly = metadataReferences.FirstOrDefault(file => string.Compare(Path.GetFileNameWithoutExtension(file.Name), nugetPackage.PackageName) == 0); - if (nugetPackageAssembly != null) - { - var directory = nugetPackageAssembly.Directory; - while (directory != null && directory.Parent != null && directory.Parent.Name.ToLower().CompareTo(nugetPackage.PackageName.ToLower()) != 0) - { - directory = directory.Parent; - } - - nugetPackageDirectory = new FileSystemDirectoryAccessor(directory); - return true; - } - else - { - nugetPackageDirectory = null; - return false; - } - } - } -} \ No newline at end of file diff --git a/WorkspaceServer.Tests/Kernel/CSharpKernelTests.cs b/WorkspaceServer.Tests/Kernel/CSharpKernelTests.cs index 3345886de..a0ee9f416 100644 --- a/WorkspaceServer.Tests/Kernel/CSharpKernelTests.cs +++ b/WorkspaceServer.Tests/Kernel/CSharpKernelTests.cs @@ -555,7 +555,7 @@ public void ScriptState_is_not_null_prior_to_receiving_code_submissions() } [Fact] - public async Task Should_load_extension_in_nuget_package() + public async Task Should_load_extension_in_directory() { var directory = Create.EmptyWorkspace().Directory; @@ -564,8 +564,6 @@ public async Task Should_load_extension_in_nuget_package() directory.Subdirectory($"{nugetPackageName}/2.0.0")) .CreateFiles(); - var nugetPackageDll = nugetPackageDirectory.GetFullyQualifiedFilePath($"lib/netstandard2.0/{nugetPackageName}.dll"); - var extensionsDir = (FileSystemDirectoryAccessor) nugetPackageDirectory.GetDirectoryAccessorForRelativePath(new RelativeDirectoryPath("interactive-extensions/dotnet/cs")); @@ -575,13 +573,8 @@ public async Task Should_load_extension_in_nuget_package() var kernel = CreateKernel(); - await kernel.SendAsync( - new LoadExtensionFromNuGetPackage( - new NugetPackageReference(nugetPackageName), - new List - { - nugetPackageDll - })); + await kernel.SendAsync(new LoadExtensionsInDirectory(nugetPackageDirectory)); + KernelEvents.Should() .ContainSingle(e => e.Value is ExtensionLoaded && diff --git a/WorkspaceServer.Tests/PackageRestoreContextTests.cs b/WorkspaceServer.Tests/PackageRestoreContextTests.cs index f57b0b233..0bbee3d99 100644 --- a/WorkspaceServer.Tests/PackageRestoreContextTests.cs +++ b/WorkspaceServer.Tests/PackageRestoreContextTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using FluentAssertions; +using System.IO; using System.Threading.Tasks; using WorkspaceServer.Packaging; using Xunit; @@ -28,5 +29,14 @@ public async Task Returns_failure_if_package_installation_fails() result.Succeeded.Should().BeFalse(); result.DetailedErrors.Should().NotBeEmpty(); } + + [Fact] + public async Task Can_get_path_to_nuget_package() + { + PackageRestoreContext packageRestoreContext = new PackageRestoreContext(); + await packageRestoreContext.AddPackage("fluentAssertions", "5.7.0"); + var path = await packageRestoreContext.GetDirectoryForPackage("fluentassertions"); + path.FullName.Should().EndWith("fluentassertions" + Path.DirectorySeparatorChar + "5.7.0"); + } } } \ No newline at end of file diff --git a/WorkspaceServer/Kernel/CSharpKernelExtensions.cs b/WorkspaceServer/Kernel/CSharpKernelExtensions.cs index 9cf2b2647..620444851 100644 --- a/WorkspaceServer/Kernel/CSharpKernelExtensions.cs +++ b/WorkspaceServer/Kernel/CSharpKernelExtensions.cs @@ -13,6 +13,7 @@ using Microsoft.DotNet.Interactive.Commands; using Microsoft.DotNet.Interactive.Events; using Microsoft.DotNet.Interactive.Rendering; +using MLS.Agent.Tools; using WorkspaceServer.Packaging; namespace WorkspaceServer.Kernel @@ -103,7 +104,8 @@ public static CSharpKernel UseNugetDirective(this CSharpKernel kernel, INativeAs context.Publish(new DisplayedValueProduced($"Successfully added reference to package {package.PackageName}, version {result.InstalledVersion}", context.Command)); context.Publish(new NuGetPackageAdded(addPackage, package)); - await pipelineContext.HandlingKernel.SendAsync(new LoadExtensionFromNuGetPackage(package, result.References.Select(reference => new FileInfo(reference.Display)))); + var nugetPackageDirectory = new FileSystemDirectoryAccessor(await restoreContext.GetDirectoryForPackage(package.PackageName)); + await pipelineContext.HandlingKernel.SendAsync(new LoadExtensionsInDirectory(nugetPackageDirectory)); } else { diff --git a/WorkspaceServer/Packaging/PackageRestoreContext.cs b/WorkspaceServer/Packaging/PackageRestoreContext.cs index 20f43b3ff..5f656b37d 100644 --- a/WorkspaceServer/Packaging/PackageRestoreContext.cs +++ b/WorkspaceServer/Packaging/PackageRestoreContext.cs @@ -1,6 +1,7 @@  using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Threading.Tasks; using Clockwise; @@ -12,7 +13,6 @@ namespace WorkspaceServer.Packaging { public partial class PackageRestoreContext { - private readonly AsyncLazy _lazyPackage; public PackageRestoreContext() @@ -30,8 +30,9 @@ private async Task CreatePackage() packageBuilder.CreateUsingDotnet("console"); packageBuilder.TrySetLanguageVersion("8.0"); packageBuilder.AddPackageReference("Newtonsoft.Json"); - var package = (Package) packageBuilder.GetPackage(); + var package = (Package)packageBuilder.GetPackage(); await package.CreateRoslynWorkspaceForRunAsync(new Budget()); + AddDirectoryProps(package); return package; } @@ -66,5 +67,68 @@ public async Task> GetAllReferences() var currentWorkspace = await package.CreateRoslynWorkspaceForRunAsync(new Budget()); return currentWorkspace.CurrentSolution.Projects.First().MetadataReferences; } + + public async Task GetDirectoryForPackage(string packageName) + { + var package = await _lazyPackage.ValueAsync(); + var nugetPathsFile = package.Directory.GetFiles("*.nuget.paths").Single(); + var nugetPackagePaths = File.ReadAllText(Path.Combine(package.Directory.FullName, nugetPathsFile.FullName)).Split(',','\r', '\n').Where(t => !string.IsNullOrWhiteSpace(t)).ToArray(); + var pathsDictionary = nugetPackagePaths + .Select((v, i) => new { Index = i, Value = v }) + .GroupBy(p => p.Index / 2) + .ToDictionary(g => g.First().Value, g => g.Last().Value, StringComparer.OrdinalIgnoreCase); + + return new DirectoryInfo(pathsDictionary[packageName.ToLower()]); + } + + private void AddDirectoryProps(Package package) + { + const string generatePathsPropertyTarget = +@" + + + + + + true + + + + + + "; + + const string computePackageRootsTarget = +@" + + + + Pkg$([System.String]::Copy('%(ResolvedCompileFileDefinitions.NugetPackageId)').Replace('.','_')) + $(%(AddedNuGetPackage.PackageRootProperty)) + + + + + "; + + const string writePackageRootsToDiskTarget = +@" + + + + + + + "; + + string directoryPropsContent = +$@" +{generatePathsPropertyTarget} +{computePackageRootsTarget} +{writePackageRootsToDiskTarget} +"; + + File.WriteAllText(Path.Combine(package.Directory.FullName, "Directory.Build.props"), directoryPropsContent); + } } } From 6273ec5bf6335d254b7aeaa5f5a2dcde2f4bb0ff Mon Sep 17 00:00:00 2001 From: Akshita Date: Wed, 11 Sep 2019 13:27:40 -0700 Subject: [PATCH 2/2] Add another test and correctly name the file --- ...InDirectory.cs => LoadExtensionsInDirectory.cs} | 0 .../PackageRestoreContextTests.cs | 14 +++++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) rename Microsoft.DotNet.Interactive/Commands/{LoadExtensionInDirectory.cs => LoadExtensionsInDirectory.cs} (100%) diff --git a/Microsoft.DotNet.Interactive/Commands/LoadExtensionInDirectory.cs b/Microsoft.DotNet.Interactive/Commands/LoadExtensionsInDirectory.cs similarity index 100% rename from Microsoft.DotNet.Interactive/Commands/LoadExtensionInDirectory.cs rename to Microsoft.DotNet.Interactive/Commands/LoadExtensionsInDirectory.cs diff --git a/WorkspaceServer.Tests/PackageRestoreContextTests.cs b/WorkspaceServer.Tests/PackageRestoreContextTests.cs index 0bbee3d99..c3b5f55e0 100644 --- a/WorkspaceServer.Tests/PackageRestoreContextTests.cs +++ b/WorkspaceServer.Tests/PackageRestoreContextTests.cs @@ -33,10 +33,22 @@ public async Task Returns_failure_if_package_installation_fails() [Fact] public async Task Can_get_path_to_nuget_package() { - PackageRestoreContext packageRestoreContext = new PackageRestoreContext(); + var packageRestoreContext = new PackageRestoreContext(); await packageRestoreContext.AddPackage("fluentAssertions", "5.7.0"); var path = await packageRestoreContext.GetDirectoryForPackage("fluentassertions"); path.FullName.Should().EndWith("fluentassertions" + Path.DirectorySeparatorChar + "5.7.0"); + path.Exists.Should().BeTrue(); + } + + [Fact] + public async Task Can_get_path_to_nuget_package_when_multiple_packages_are_added() + { + var packageRestoreContext = new PackageRestoreContext(); + await packageRestoreContext.AddPackage("fluentAssertions", "5.7.0"); + await packageRestoreContext.AddPackage("htmlagilitypack", "1.11.12"); + var path = await packageRestoreContext.GetDirectoryForPackage("htmlagilitypack"); + path.FullName.Should().EndWith("htmlagilitypack" + Path.DirectorySeparatorChar + "1.11.12"); + path.Exists.Should().BeTrue(); } } } \ No newline at end of file