diff --git a/src/Microsoft.Extensions.ProjectModel.Sources/DotNet/DotNetDependencyProvider.cs b/src/Microsoft.Extensions.ProjectModel.Sources/DotNet/DotNetDependencyProvider.cs new file mode 100644 index 0000000000..429a2a6a68 --- /dev/null +++ b/src/Microsoft.Extensions.ProjectModel.Sources/DotNet/DotNetDependencyProvider.cs @@ -0,0 +1,98 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.IO; +using Microsoft.DotNet.ProjectModel; +using Microsoft.Extensions.ProjectModel.Resolution; +using Microsoft.DotNet.Cli.Utils; + +namespace Microsoft.Extensions.ProjectModel +{ + internal class DotNetDependencyProvider + { + private ProjectContext _context; + private List _packageDependencies; + private List _resolvedReferences; + private string _configuration; + + public DotNetDependencyProvider(ProjectContext context, string configuration = Constants.DefaultConfiguration) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + _configuration = configuration; + _context = context; + DiscoverDependencies(); + } + + public IEnumerable GetPackageDependencies() + { + return _packageDependencies; + } + + public IEnumerable GetResolvedReferences() + { + return _resolvedReferences; + } + + private void DiscoverDependencies() + { + var exporter = _context.CreateExporter(_configuration); + + if (exporter == null) + { + throw new InvalidOperationException($"Couldn't create a library exporter for configuration {_configuration}"); + } + + var framework = _context.TargetFramework; + if (framework == null) + { + throw new InvalidOperationException("Couldn't resolve dependencies when target framework is not specified."); + } + + var exports = exporter.GetAllExports(); + var nugetPackages = new Dictionary(StringComparer.OrdinalIgnoreCase); + _resolvedReferences = new List(); + + foreach (var export in exports) + { + var library = export.Library; + + var description = new DependencyDescription( + library.Identity.Name, + library.Identity.Version.ToString(), + export.Library.Path, + framework.DotNetFrameworkName, + library.Identity.Type.Value, + library.Resolved); + + foreach (var dependency in export.Library.Dependencies) + { + var dep = new Dependency(dependency.Name, version: string.Empty); + description.AddDependency(dep); + } + + var itemSpec = $"{framework.DotNetFrameworkName}/{library.Identity.Name}/{library.Identity.Version.ToString()}"; + nugetPackages[itemSpec] = description; + + if (library.Resolved) + { + foreach (var asset in export.CompilationAssemblies) + { + var resolvedRef = new ResolvedReference( + name: Path.GetFileNameWithoutExtension(asset.FileName), + resolvedPath: asset.ResolvedPath); + _resolvedReferences.Add(resolvedRef); + } + } + } + + _packageDependencies = nugetPackages.Values.ToList(); + } + } +} diff --git a/src/Microsoft.Extensions.ProjectModel.Sources/DotNet/DotNetProjectContext.cs b/src/Microsoft.Extensions.ProjectModel.Sources/DotNet/DotNetProjectContext.cs index 7de51a2b53..b2b9851ea8 100644 --- a/src/Microsoft.Extensions.ProjectModel.Sources/DotNet/DotNetProjectContext.cs +++ b/src/Microsoft.Extensions.ProjectModel.Sources/DotNet/DotNetProjectContext.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.IO; using Microsoft.DotNet.ProjectModel; +using Microsoft.Extensions.ProjectModel.Resolution; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using NuGet.Frameworks; @@ -17,6 +18,10 @@ namespace Microsoft.Extensions.ProjectModel private readonly OutputPaths _paths; private readonly Lazy _rawProject; private readonly CommonCompilerOptions _compilerOptions; + private readonly Lazy _dependencyProvider; + + private IEnumerable _packageDependencies; + private IEnumerable _compilationAssemblies; public DotNetProjectContext(ProjectContext projectContext, string configuration, string outputPath) { @@ -49,6 +54,8 @@ namespace Microsoft.Extensions.ProjectModel // Workaround https://github.com/dotnet/cli/issues/3164 IsClassLibrary = !(_compilerOptions.EmitEntryPoint ?? projectContext.ProjectFile.GetCompilerOptions(null, configuration).EmitEntryPoint.GetValueOrDefault()); + + _dependencyProvider = new Lazy(() => new DotNetDependencyProvider(_projectContext)); } public bool IsClassLibrary { get; } @@ -79,6 +86,32 @@ namespace Microsoft.Extensions.ProjectModel public IEnumerable EmbededItems => _compilerOptions.EmbedInclude.ResolveFiles(); + public IEnumerable PackageDependencies + { + get + { + if (_packageDependencies == null) + { + _packageDependencies = _dependencyProvider.Value.GetPackageDependencies(); + } + + return _packageDependencies; + } + } + + public IEnumerable CompilationAssemblies + { + get + { + if (_compilationAssemblies == null) + { + _compilationAssemblies = _dependencyProvider.Value.GetResolvedReferences(); + } + + return _compilationAssemblies; + } + } + /// /// Returns string values of top-level keys in the project.json file /// diff --git a/src/Microsoft.Extensions.ProjectModel.Sources/IProjectContext.cs b/src/Microsoft.Extensions.ProjectModel.Sources/IProjectContext.cs index 163c033bcb..ce7731fac6 100644 --- a/src/Microsoft.Extensions.ProjectModel.Sources/IProjectContext.cs +++ b/src/Microsoft.Extensions.ProjectModel.Sources/IProjectContext.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; +using Microsoft.Extensions.ProjectModel.Resolution; using NuGet.Frameworks; namespace Microsoft.Extensions.ProjectModel @@ -25,5 +26,7 @@ namespace Microsoft.Extensions.ProjectModel IEnumerable CompilationItems { get; } IEnumerable EmbededItems { get; } string FindProperty(string propertyName); + IEnumerable PackageDependencies { get;} + IEnumerable CompilationAssemblies { get; } } } \ No newline at end of file diff --git a/src/Microsoft.Extensions.ProjectModel.Sources/MsBuild/MsBuildProjectContext.cs b/src/Microsoft.Extensions.ProjectModel.Sources/MsBuild/MsBuildProjectContext.cs index 4e9a6f89c0..dc2369065e 100644 --- a/src/Microsoft.Extensions.ProjectModel.Sources/MsBuild/MsBuildProjectContext.cs +++ b/src/Microsoft.Extensions.ProjectModel.Sources/MsBuild/MsBuildProjectContext.cs @@ -7,6 +7,7 @@ using System.IO; using Microsoft.Build.Execution; using NuGet.Frameworks; using System.Linq; +using Microsoft.Extensions.ProjectModel.Resolution; namespace Microsoft.Extensions.ProjectModel { @@ -16,23 +17,28 @@ namespace Microsoft.Extensions.ProjectModel private const string EmbedItemName = "EmbeddedResource"; private const string FullPathMetadataName = "FullPath"; - private readonly ProjectInstance _project; - private readonly string _name; + private readonly MsBuildProjectDependencyProvider _dependencyProvider; + private IEnumerable _packageDependencies; + private IEnumerable _compilationAssemblies; + + protected ProjectInstance Project { get; } + protected string Name { get; } public MsBuildProjectContext(string name, string configuration, ProjectInstance project) { - _project = project; + Project = project; Configuration = configuration; - _name = name; + Name = name; + _dependencyProvider = new MsBuildProjectDependencyProvider(Project); } public string FindProperty(string propertyName) { - return _project.GetProperty(propertyName)?.EvaluatedValue; + return Project.GetProperty(propertyName)?.EvaluatedValue; } - public string ProjectName => FindProperty("ProjectName") ?? _name; + public string ProjectName => FindProperty("ProjectName") ?? Name; public string Configuration { get; } public NuGetFramework TargetFramework @@ -69,14 +75,41 @@ namespace Microsoft.Extensions.ProjectModel } public string AssemblyFullPath => FindProperty("TargetPath"); public string Platform => FindProperty("Platform"); - public string ProjectFullPath => _project.FullPath; + public string ProjectFullPath => Project.FullPath; public string RootNamespace => FindProperty("RootNamespace") ?? ProjectName; public string TargetDirectory => FindProperty("TargetDir"); public IEnumerable CompilationItems - => _project.GetItems(CompileItemName).Select(i => i.GetMetadataValue(FullPathMetadataName)); + => Project.GetItems(CompileItemName).Select(i => i.GetMetadataValue(FullPathMetadataName)); public IEnumerable EmbededItems - => _project.GetItems(EmbedItemName).Select(i => i.GetMetadataValue(FullPathMetadataName)); + => Project.GetItems(EmbedItemName).Select(i => i.GetMetadataValue(FullPathMetadataName)); + + public IEnumerable PackageDependencies + { + get + { + if (_packageDependencies == null) + { + _packageDependencies = _dependencyProvider.GetPackageDependencies(); + } + + return _packageDependencies; + } + } + + public IEnumerable CompilationAssemblies + { + get + { + if (_compilationAssemblies == null) + { + _compilationAssemblies = _dependencyProvider.GetResolvedReferences(); + } + + return _compilationAssemblies; + } + } + } } \ No newline at end of file diff --git a/src/Microsoft.Extensions.ProjectModel.Sources/MsBuild/MsBuildProjectContextBuilder.cs b/src/Microsoft.Extensions.ProjectModel.Sources/MsBuild/MsBuildProjectContextBuilder.cs index 20d1f6cdc4..8033dd0012 100644 --- a/src/Microsoft.Extensions.ProjectModel.Sources/MsBuild/MsBuildProjectContextBuilder.cs +++ b/src/Microsoft.Extensions.ProjectModel.Sources/MsBuild/MsBuildProjectContextBuilder.cs @@ -20,10 +20,10 @@ namespace Microsoft.Extensions.ProjectModel { internal class MsBuildProjectContextBuilder { - private string _configuration; - private IFileInfo _fileInfo; - private string[] _buildTargets; - private Dictionary _globalProperties = new Dictionary(); + protected string _configuration; + protected IFileInfo _fileInfo; + protected string[] _buildTargets; + protected Dictionary _globalProperties = new Dictionary(); private MsBuildContext _msbuildContext; public MsBuildProjectContextBuilder() @@ -186,7 +186,7 @@ namespace Microsoft.Extensions.ProjectModel protected virtual void Initialize() { - WithBuildTargets(new[] { "ResolveReferences" }); + WithBuildTargets(new[] { "ResolveReferences", "ResolvePackageDependenciesDesignTime" }); WithProperty("_ResolveReferenceDependencies", "true"); } diff --git a/src/Microsoft.Extensions.ProjectModel.Sources/MsBuild/MsBuildProjectDependencyProvider.cs b/src/Microsoft.Extensions.ProjectModel.Sources/MsBuild/MsBuildProjectDependencyProvider.cs new file mode 100644 index 0000000000..8c4345e158 --- /dev/null +++ b/src/Microsoft.Extensions.ProjectModel.Sources/MsBuild/MsBuildProjectDependencyProvider.cs @@ -0,0 +1,126 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Microsoft.Build.Execution; +using Microsoft.Extensions.ProjectModel.Resolution; + +namespace Microsoft.Extensions.ProjectModel +{ + internal class MsBuildProjectDependencyProvider + { + private const string PackageDependencyItemType = "_DependenciesDesignTime"; + private const string ResolvedReferenceItemType = "ReferencePath"; + + private readonly ProjectInstance _projectInstance; + public MsBuildProjectDependencyProvider(ProjectInstance projectInstance) + { + if (projectInstance == null) + { + throw new ArgumentNullException(nameof(projectInstance)); + } + _projectInstance = projectInstance; + } + + public IEnumerable GetPackageDependencies() + { + var packageItems = _projectInstance.GetItems(PackageDependencyItemType); + var packageInfo = new Dictionary(StringComparer.OrdinalIgnoreCase); + if (packageItems != null) + { + foreach (var packageItem in packageItems) + { + var packageDependency = CreateDependencyDescriptionFromItem(packageItem); + if (packageDependency != null) + { + packageInfo[packageItem.EvaluatedInclude] = packageDependency; + } + } + + // 2nd pass to populate dependencies; + + PopulateDependencies(packageInfo, packageItems); + } + + return packageInfo.Values; + } + + + public IEnumerable GetResolvedReferences() + { + var refItems = _projectInstance.GetItems(ResolvedReferenceItemType); + + var resolvedReferences = refItems + ?.Select(refItem => CreateResolvedReferenceFromProjectItem(refItem)) + .Where(resolvedReference => resolvedReference != null); + + return resolvedReferences; + } + + private static ResolvedReference CreateResolvedReferenceFromProjectItem(ProjectItemInstance item) + { + var resolvedPath = item.EvaluatedInclude; + + if (string.IsNullOrEmpty(resolvedPath)) + { + return null; + } + + var name = Path.GetFileNameWithoutExtension(resolvedPath); + return new ResolvedReference(name, resolvedPath); + } + + private static DependencyDescription CreateDependencyDescriptionFromItem(ProjectItemInstance item) + { + // For type == Target, we do not get Name in the metadata. This is a special node where the dependencies are + // the direct dependencies of the project. + var itemSpec = item.EvaluatedInclude; + var name = item.HasMetadata("Name") ? item.GetMetadataValue("Name") : itemSpec; + + if (string.IsNullOrEmpty(name)) + { + return null; + } + + var version = item.GetMetadataValue("Version"); + var path = item.GetMetadataValue("Path"); + var type = item.GetMetadataValue("Type"); + var resolved = item.GetMetadataValue("Resolved"); + + bool isResolved; + isResolved = bool.TryParse(resolved, out isResolved) ? isResolved : false; + var framework = itemSpec.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries).First(); + + return new DependencyDescription(name, version, path, framework, type, isResolved); + } + + private static void PopulateDependencies(Dictionary dependencies, ICollection items) + { + foreach (var item in items) + { + var depSpecs = item.GetMetadataValue("Dependencies") + ?.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries); + DependencyDescription currentDescription = null; + if (depSpecs == null || !dependencies.TryGetValue(item.EvaluatedInclude, out currentDescription)) + { + return; + } + + var prefix = item.EvaluatedInclude.Split('/').FirstOrDefault(); + foreach (var depSpec in depSpecs) + { + var spec = $"{prefix}/{depSpec}"; + DependencyDescription dependency = null; + if (dependencies.TryGetValue(spec, out dependency)) + { + var dep = new Dependency(dependency.Name, dependency.Version); + currentDescription.AddDependency(dep); + } + } + } + } + } +} diff --git a/src/Microsoft.Extensions.ProjectModel.Sources/Resolution/Dependency.cs b/src/Microsoft.Extensions.ProjectModel.Sources/Resolution/Dependency.cs new file mode 100644 index 0000000000..7722da1762 --- /dev/null +++ b/src/Microsoft.Extensions.ProjectModel.Sources/Resolution/Dependency.cs @@ -0,0 +1,26 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Microsoft.Extensions.ProjectModel.Resolution +{ + public class Dependency + { + public Dependency(string name, string version) + { + if (string.IsNullOrEmpty(name)) + { + throw new ArgumentNullException(nameof(name)); + } + + Name = name; + Version = version; + } + public string Name { get; } + public string Version { get; } + } +} diff --git a/src/Microsoft.Extensions.ProjectModel.Sources/Resolution/DependencyDescription.cs b/src/Microsoft.Extensions.ProjectModel.Sources/Resolution/DependencyDescription.cs new file mode 100644 index 0000000000..25dc5249f7 --- /dev/null +++ b/src/Microsoft.Extensions.ProjectModel.Sources/Resolution/DependencyDescription.cs @@ -0,0 +1,51 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Microsoft.Extensions.ProjectModel.Resolution +{ + public class DependencyDescription + { + private readonly List _dependencies; + + public DependencyDescription(string name, string version, string path, string framework, string type, bool isResolved) + { + if (string.IsNullOrEmpty(name)) + { + throw new ArgumentNullException(nameof(name)); + } + + if (string.IsNullOrEmpty(framework)) + { + throw new ArgumentNullException(nameof(framework)); + } + + Name = name; + Version = version; + TargetFramework = framework; + Resolved = isResolved; + Path = path; + DependencyType dt; + Type = Enum.TryParse(type, ignoreCase: true , result: out dt) ? dt : DependencyType.Unknown; + + _dependencies = new List(); + } + + public string TargetFramework { get; } + public string Name { get; } + public string Path { get; } + public string Version { get; } + public DependencyType Type { get; } + public bool Resolved { get; } + public IEnumerable Dependencies => _dependencies; + + public void AddDependency(Dependency dependency) + { + _dependencies.Add(dependency); + } + } +} diff --git a/src/Microsoft.Extensions.ProjectModel.Sources/Resolution/DependencyType.cs b/src/Microsoft.Extensions.ProjectModel.Sources/Resolution/DependencyType.cs new file mode 100644 index 0000000000..4f49efa9b1 --- /dev/null +++ b/src/Microsoft.Extensions.ProjectModel.Sources/Resolution/DependencyType.cs @@ -0,0 +1,20 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Microsoft.Extensions.ProjectModel.Resolution +{ + public enum DependencyType + { + Target, + Package, + Assembly, + Project, + AnalyzerAssembly, + Unknown + } +} diff --git a/src/Microsoft.Extensions.ProjectModel.Sources/Resolution/ResolvedReference.cs b/src/Microsoft.Extensions.ProjectModel.Sources/Resolution/ResolvedReference.cs new file mode 100644 index 0000000000..4833d86071 --- /dev/null +++ b/src/Microsoft.Extensions.ProjectModel.Sources/Resolution/ResolvedReference.cs @@ -0,0 +1,21 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Microsoft.Extensions.ProjectModel.Resolution +{ + public class ResolvedReference + { + public ResolvedReference(string name,string resolvedPath) + { + Name = name; + ResolvedPath = resolvedPath; + } + public string ResolvedPath { get; } + public string Name { get; } + } +} diff --git a/test/Microsoft.Extensions.ProjectModel.Tests/DotNet/DotNetDependencyProviderTests.cs b/test/Microsoft.Extensions.ProjectModel.Tests/DotNet/DotNetDependencyProviderTests.cs new file mode 100644 index 0000000000..ccb96e4303 --- /dev/null +++ b/test/Microsoft.Extensions.ProjectModel.Tests/DotNet/DotNetDependencyProviderTests.cs @@ -0,0 +1,88 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.IO; +using System.Linq; +using Microsoft.DotNet.Cli.Utils; +using Microsoft.DotNet.ProjectModel; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.Extensions.ProjectModel.DotNet +{ + public class DotNetDependencyProviderTests + { + private const string projectJson = @" +{ + ""buildOptions"": { + }, + ""dependencies"": { + ""Microsoft.AspNetCore.Mvc"": ""1.0.0-*"", + }, + ""frameworks"": { + ""netcoreapp1.0"": { + ""dependencies"": { + ""Microsoft.NETCore.App"": { + ""version"": ""1.0.0"", + ""type"": ""platform"" + } + } + } + }, +} +"; + private readonly ITestOutputHelper _output; + + public DotNetDependencyProviderTests(ITestOutputHelper output) + { + _output = output; + } + + [Fact] + public void BuildProjectDependencies() + { + using (var fileProvider = new TemporaryFileProvider()) + { + Directory.CreateDirectory(Path.Combine(fileProvider.Root, "demo")); + fileProvider.Add($"demo{Path.DirectorySeparatorChar}project.json", projectJson); + fileProvider.Add($"demo{Path.DirectorySeparatorChar}First.cs", "namespace demo { class First{} }"); + + var muxer = new Muxer().MuxerPath; + + var result = Command + .Create(muxer, new[] { "restore", Path.Combine(fileProvider.Root, "demo") }) + .OnErrorLine(l => _output.WriteLine(l)) + .OnOutputLine(l => _output.WriteLine(l)) + .Execute(); + + Assert.Equal(0, result.ExitCode); + var oldContext = ProjectContext + .CreateContextForEachFramework(Path.Combine(fileProvider.Root, "demo", "project.json")) + .First(); + + var context = new DotNetProjectContext(oldContext, "Debug", Path.Combine(fileProvider.Root, "demo", "bin")); + + var home = Environment.GetEnvironmentVariable("USERPROFILE") + ?? Environment.GetEnvironmentVariable("HOME"); + var nugetPackageRoot = Path.Combine(home, ".nuget", "packages"); + var expectedPackagePath = Path.Combine(nugetPackageRoot, "Microsoft.AspNetCore.Mvc", "1.0.0"); + var expectedReferencePath = Path.Combine(expectedPackagePath, "lib", "netstandard1.6", "Microsoft.AspNetCore.Mvc.dll"); + + var assembly = context + .CompilationAssemblies + .Where(asm => asm.Name.Equals("Microsoft.AspNetCore.Mvc", StringComparison.OrdinalIgnoreCase)) + .First(); + + Assert.Equal(expectedReferencePath, assembly.ResolvedPath); + + var mvcPackage = context + .PackageDependencies + .Where(package => package.Name.Equals("Microsoft.AspNetCore.Mvc", StringComparison.OrdinalIgnoreCase)) + .First(); + + Assert.Equal(expectedPackagePath, mvcPackage.Path); + } + } + } +} diff --git a/test/Microsoft.Extensions.ProjectModel.Tests/MsBuild/MsBuildProjectContextBuilderTest.cs b/test/Microsoft.Extensions.ProjectModel.Tests/MsBuild/MsBuildProjectContextBuilderTest.cs index 1f840b9be6..4dfeab0d99 100644 --- a/test/Microsoft.Extensions.ProjectModel.Tests/MsBuild/MsBuildProjectContextBuilderTest.cs +++ b/test/Microsoft.Extensions.ProjectModel.Tests/MsBuild/MsBuildProjectContextBuilderTest.cs @@ -102,6 +102,7 @@ namespace Microsoft.Extensions.ProjectModel "); + _files.Add("One.cs", "public class Abc {}"); _files.Add("Two.cs", "public class Abc2 {}"); _files.Add("Excluded.cs", "public class Abc {}"); diff --git a/test/Microsoft.Extensions.ProjectModel.Tests/MsBuild/MsBuildProjectDependencyProviderTests.cs b/test/Microsoft.Extensions.ProjectModel.Tests/MsBuild/MsBuildProjectDependencyProviderTests.cs new file mode 100644 index 0000000000..9caea9c562 --- /dev/null +++ b/test/Microsoft.Extensions.ProjectModel.Tests/MsBuild/MsBuildProjectDependencyProviderTests.cs @@ -0,0 +1,171 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Linq; +using Microsoft.Extensions.ProjectModel.Tests; +using Xunit; +using Xunit.Abstractions; +using System.IO; +using Microsoft.DotNet.Cli.Utils; +using NuGet.Frameworks; + +namespace Microsoft.Extensions.ProjectModel.MsBuild +{ + public class MsBuildProjectDependencyProviderTests: IClassFixture + { + private const string NugetConfigTxt = @" + + + + + + + + +"; + + private const string RootProjectTxt = @" + + + + + Microsoft.TestProject + TestProject + Library + netcoreapp1.0 + bin\$(Configuration) + + + + + + + + + 1.0.0-* + + + 1.0.0-* + + + 1.0.1 + + + + + + + + + + +"; + + private const string LibraryProjectTxt = @" + + + + + Microsoft.Library + Library1 + Library + netcoreapp1.0 + bin\$(Configuration) + + + + + + + + + 1.0.0-* + + + 1.0.1 + + + + +"; + private readonly MsBuildFixture _fixture; + private readonly ITestOutputHelper _output; + + public MsBuildProjectDependencyProviderTests(MsBuildFixture fixture, ITestOutputHelper output) + { + _fixture = fixture; + _output = output; + } + + [Fact(Skip = "CI doesn't yet have a new enough version of .NET Core SDK")] + public void BuildDependenciesForProject() + { + using (var fileProvider = new TemporaryFileProvider()) + { + Directory.CreateDirectory(Path.Combine(fileProvider.Root, "Root")); + Directory.CreateDirectory(Path.Combine(fileProvider.Root, "Library1")); + // TODO remove when SDK becomes available on other feeds + fileProvider.Add("NuGet.config", NugetConfigTxt); + + // Add Root Project + fileProvider.Add($"Root{Path.DirectorySeparatorChar}test.csproj", RootProjectTxt); + fileProvider.Add($"Root{Path.DirectorySeparatorChar}One.cs", "public class Abc {}"); + fileProvider.Add($"Root{Path.DirectorySeparatorChar}Two.cs", "public class Abc2 {}"); + fileProvider.Add($"Root{Path.DirectorySeparatorChar}Excluded.cs", "public class Abc {}"); + + // Add Class Library project + fileProvider.Add($"Library1{Path.DirectorySeparatorChar}library.csproj", LibraryProjectTxt); + fileProvider.Add($"Library1{Path.DirectorySeparatorChar}Three.cs", "public class Abc3 {}"); + + var testContext = _fixture.GetMsBuildContext(); + + var muxer = Path.Combine(testContext.ExtensionsPath, "../..", "dotnet.exe"); + var result = Command + .Create(muxer, new[] { "restore3", Path.Combine(fileProvider.Root, "Library1","Library1.csproj") }) + .OnErrorLine(l => _output.WriteLine(l)) + .OnOutputLine(l => _output.WriteLine(l)) + .Execute(); + + Assert.Equal(0, result.ExitCode); + + result = Command + .Create(muxer, new[] { "restore3", Path.Combine(fileProvider.Root, "Root", "test.csproj") }) + .OnErrorLine(l => _output.WriteLine(l)) + .OnOutputLine(l => _output.WriteLine(l)) + .Execute(); + + Assert.Equal(0, result.ExitCode); + + var builder = new MsBuildProjectContextBuilder() + .AsDesignTimeBuild() + .UseMsBuild(testContext) + .WithTargetFramework(FrameworkConstants.CommonFrameworks.NetCoreApp10) + .WithConfiguration("Debug") + .WithProjectFile(fileProvider.GetFileInfo(Path.Combine("Root", "test.csproj"))); + + var context = builder.Build(); + + var compilationAssemblies = context.CompilationAssemblies; + var lib1Dll = compilationAssemblies + .Where(assembly => assembly.Name.Equals("Library1", StringComparison.OrdinalIgnoreCase)) + .FirstOrDefault(); + + Assert.NotNull(lib1Dll); + var expectedPath = Path.Combine(fileProvider.Root, "Root", "bin", "Debug", "netcoreapp1.0", "lib1.dll"); + Assert.Equal(expectedPath, lib1Dll.ResolvedPath, ignoreCase: true); + + // This reference doesn't resolve so should not be available here. + Assert.False(compilationAssemblies.Any(assembly => assembly.Name.Equals("xyz", StringComparison.OrdinalIgnoreCase))); + + var packageDependencies = context.PackageDependencies; + var mvcPackage = packageDependencies + .Where(p => p.Name.Equals("Microsoft.AspNetCore.Mvc", StringComparison.OrdinalIgnoreCase)) + .FirstOrDefault(); + Assert.NotNull(mvcPackage); + + Assert.True(mvcPackage.Dependencies.Any(dependency => dependency.Name.Equals("Microsoft.Extensions.DependencyInjection", StringComparison.OrdinalIgnoreCase))); + } + } + } +}