183 lines
7.3 KiB
C#
183 lines
7.3 KiB
C#
// 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 System.Linq;
|
|
using System.Reflection.Metadata;
|
|
using System.Reflection.PortableExecutable;
|
|
using Microsoft.DotNet.ProjectModel.Graph;
|
|
using NuGet.Frameworks;
|
|
using NuGet.Packaging;
|
|
|
|
namespace Microsoft.DotNet.ProjectModel.Resolution
|
|
{
|
|
public class PackageDependencyProvider
|
|
{
|
|
private readonly VersionFolderPathResolver _packagePathResolver;
|
|
private readonly FrameworkReferenceResolver _frameworkReferenceResolver;
|
|
|
|
public PackageDependencyProvider(string packagesPath, FrameworkReferenceResolver frameworkReferenceResolver)
|
|
{
|
|
_packagePathResolver = new VersionFolderPathResolver(packagesPath);
|
|
_frameworkReferenceResolver = frameworkReferenceResolver;
|
|
}
|
|
|
|
public PackageDescription GetDescription(NuGetFramework targetFramework, LockFilePackageLibrary package, LockFileTargetLibrary targetLibrary)
|
|
{
|
|
// If a NuGet dependency is supposed to provide assemblies but there is no assembly compatible with
|
|
// current target framework, we should mark this dependency as unresolved
|
|
var containsAssembly = package.Files
|
|
.Any(x => x.StartsWith($"ref{Path.DirectorySeparatorChar}") ||
|
|
x.StartsWith($"lib{Path.DirectorySeparatorChar}"));
|
|
|
|
var compatible = targetLibrary.FrameworkAssemblies.Any() ||
|
|
targetLibrary.CompileTimeAssemblies.Any() ||
|
|
targetLibrary.RuntimeAssemblies.Any() ||
|
|
!containsAssembly;
|
|
|
|
var dependencies = new List<LibraryRange>(targetLibrary.Dependencies.Count + targetLibrary.FrameworkAssemblies.Count);
|
|
PopulateDependencies(dependencies, targetLibrary, targetFramework);
|
|
|
|
var path = _packagePathResolver.GetInstallPath(package.Name, package.Version);
|
|
|
|
// Remove place holders
|
|
targetLibrary.CompileTimeAssemblies = targetLibrary.CompileTimeAssemblies.Where(item => !IsPlaceholderFile(item.Path)).ToList();
|
|
targetLibrary.RuntimeAssemblies = targetLibrary.RuntimeAssemblies.Where(item => !IsPlaceholderFile(item.Path)).ToList();
|
|
|
|
// If the package's compile time assemblies is for a portable profile then, read the assembly metadata
|
|
// and turn System.* references into reference assembly dependencies
|
|
PopulateLegacyPortableDependencies(targetFramework, dependencies, path, targetLibrary);
|
|
|
|
var packageDescription = new PackageDescription(
|
|
path,
|
|
package,
|
|
targetLibrary,
|
|
dependencies,
|
|
compatible);
|
|
|
|
return packageDescription;
|
|
}
|
|
|
|
private void PopulateLegacyPortableDependencies(NuGetFramework targetFramework, List<LibraryRange> dependencies, string packagePath, LockFileTargetLibrary targetLibrary)
|
|
{
|
|
var seen = new HashSet<string>();
|
|
|
|
foreach (var assembly in targetLibrary.CompileTimeAssemblies)
|
|
{
|
|
// (ref/lib)/{tfm}/{assembly}
|
|
var pathParts = assembly.Path.Split(Path.DirectorySeparatorChar);
|
|
|
|
if (pathParts.Length != 3)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var assemblyTargetFramework = NuGetFramework.Parse(pathParts[1]);
|
|
|
|
if (!assemblyTargetFramework.IsPCL)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var assemblyPath = Path.Combine(packagePath, assembly.Path);
|
|
|
|
foreach (var dependency in GetDependencies(assemblyPath))
|
|
{
|
|
if (seen.Add(dependency))
|
|
{
|
|
string path;
|
|
Version version;
|
|
|
|
// If there exists a reference assembly on the requested framework with the same name then turn this into a
|
|
// framework assembly dependency
|
|
if (_frameworkReferenceResolver.TryGetAssembly(dependency, targetFramework, out path, out version))
|
|
{
|
|
dependencies.Add(new LibraryRange(dependency,
|
|
LibraryType.ReferenceAssembly,
|
|
LibraryDependencyType.Build));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static IEnumerable<string> GetDependencies(string path)
|
|
{
|
|
using (var peReader = new PEReader(File.OpenRead(path)))
|
|
{
|
|
var metadataReader = peReader.GetMetadataReader();
|
|
|
|
foreach (var assemblyReferenceHandle in metadataReader.AssemblyReferences)
|
|
{
|
|
var assemblyReference = metadataReader.GetAssemblyReference(assemblyReferenceHandle);
|
|
|
|
yield return metadataReader.GetString(assemblyReference.Name);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void PopulateDependencies(
|
|
List<LibraryRange> dependencies,
|
|
LockFileTargetLibrary targetLibrary,
|
|
NuGetFramework targetFramework)
|
|
{
|
|
foreach (var dependency in targetLibrary.Dependencies)
|
|
{
|
|
dependencies.Add(new LibraryRange(
|
|
dependency.Id,
|
|
dependency.VersionRange,
|
|
LibraryType.Unspecified,
|
|
LibraryDependencyType.Default));
|
|
}
|
|
|
|
if (!targetFramework.IsPackageBased)
|
|
{
|
|
// Only add framework assemblies for non-package based frameworks.
|
|
foreach (var frameworkAssembly in targetLibrary.FrameworkAssemblies)
|
|
{
|
|
dependencies.Add(new LibraryRange(
|
|
frameworkAssembly,
|
|
LibraryType.ReferenceAssembly,
|
|
LibraryDependencyType.Default));
|
|
}
|
|
}
|
|
}
|
|
|
|
public static bool IsPlaceholderFile(string path)
|
|
{
|
|
return string.Equals(Path.GetFileName(path), "_._", StringComparison.Ordinal);
|
|
}
|
|
|
|
public static string ResolvePackagesPath(string rootDirectory, GlobalSettings settings)
|
|
{
|
|
// Order
|
|
// 1. global.json { "packages": "..." }
|
|
// 2. EnvironmentNames.PackagesStore environment variable
|
|
// 3. NuGet.config repositoryPath (maybe)?
|
|
// 4. {DefaultLocalRuntimeHomeDir}\packages
|
|
|
|
if (!string.IsNullOrEmpty(settings?.PackagesPath))
|
|
{
|
|
return Path.Combine(rootDirectory, settings.PackagesPath);
|
|
}
|
|
|
|
var runtimePackages = Environment.GetEnvironmentVariable(EnvironmentNames.PackagesStore);
|
|
|
|
if (!string.IsNullOrEmpty(runtimePackages))
|
|
{
|
|
return runtimePackages;
|
|
}
|
|
|
|
var profileDirectory = Environment.GetEnvironmentVariable("USERPROFILE");
|
|
|
|
if (string.IsNullOrEmpty(profileDirectory))
|
|
{
|
|
profileDirectory = Environment.GetEnvironmentVariable("HOME");
|
|
}
|
|
|
|
return Path.Combine(profileDirectory, ".nuget", "packages");
|
|
}
|
|
}
|
|
}
|