2015-10-28 02:21:00 -07:00
|
|
|
using System;
|
2015-10-13 14:31:29 -07:00
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.IO;
|
|
|
|
using System.Linq;
|
2015-10-28 02:21:00 -07:00
|
|
|
using System.Runtime.InteropServices;
|
2015-10-21 12:59:44 -07:00
|
|
|
using Microsoft.Extensions.Internal;
|
2015-10-13 14:31:29 -07:00
|
|
|
using Microsoft.Extensions.ProjectModel.Graph;
|
|
|
|
using Microsoft.Extensions.ProjectModel.Resolution;
|
|
|
|
using NuGet.Frameworks;
|
|
|
|
|
|
|
|
namespace Microsoft.Extensions.ProjectModel
|
|
|
|
{
|
|
|
|
public class ProjectContextBuilder
|
|
|
|
{
|
2015-10-29 01:35:23 -07:00
|
|
|
private Project Project { get; set; }
|
2015-10-13 14:31:29 -07:00
|
|
|
|
2015-10-29 01:35:23 -07:00
|
|
|
private LockFile LockFile { get; set; }
|
2015-10-13 14:31:29 -07:00
|
|
|
|
2015-10-29 01:35:23 -07:00
|
|
|
private GlobalSettings GlobalSettings { get; set; }
|
2015-10-13 14:31:29 -07:00
|
|
|
|
2015-10-29 01:35:23 -07:00
|
|
|
private NuGetFramework TargetFramework { get; set; }
|
2015-10-13 14:31:29 -07:00
|
|
|
|
2015-10-29 01:35:23 -07:00
|
|
|
private IEnumerable<string> RuntimeIdentifiers { get; set; } = Enumerable.Empty<string>();
|
2015-10-13 14:31:29 -07:00
|
|
|
|
2015-10-29 01:35:23 -07:00
|
|
|
private string RootDirectory { get; set; }
|
2015-10-13 14:31:29 -07:00
|
|
|
|
2015-10-29 01:35:23 -07:00
|
|
|
private string ProjectDirectory { get; set; }
|
2015-10-13 14:31:29 -07:00
|
|
|
|
2015-10-29 01:35:23 -07:00
|
|
|
private string PackagesDirectory { get; set; }
|
2015-10-28 02:21:00 -07:00
|
|
|
|
2015-10-29 01:35:23 -07:00
|
|
|
private string ReferenceAssembliesPath { get; set; }
|
|
|
|
|
|
|
|
public ProjectContextBuilder WithLockFile(LockFile lockFile)
|
|
|
|
{
|
|
|
|
LockFile = lockFile;
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public ProjectContextBuilder WithProject(Project project)
|
|
|
|
{
|
|
|
|
Project = project;
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public ProjectContextBuilder WithProjectDirectory(string projectDirectory)
|
|
|
|
{
|
|
|
|
ProjectDirectory = projectDirectory;
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public ProjectContextBuilder WithTargetFramework(NuGetFramework targetFramework)
|
|
|
|
{
|
|
|
|
TargetFramework = targetFramework;
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public ProjectContextBuilder WithTargetFramework(string targetFramework)
|
|
|
|
{
|
|
|
|
TargetFramework = NuGetFramework.Parse(targetFramework);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public ProjectContextBuilder WithRuntimeIdentifiers(IEnumerable<string> runtimeIdentifiers)
|
|
|
|
{
|
|
|
|
RuntimeIdentifiers = runtimeIdentifiers;
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public ProjectContextBuilder WithReferenceAssembliesPath(string referenceAssembliesPath)
|
|
|
|
{
|
|
|
|
ReferenceAssembliesPath = referenceAssembliesPath;
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public ProjectContextBuilder WithPackagesDirectory(string packagesDirectory)
|
|
|
|
{
|
|
|
|
PackagesDirectory = packagesDirectory;
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public ProjectContextBuilder WithRootDirectory(string rootDirectory)
|
|
|
|
{
|
|
|
|
RootDirectory = rootDirectory;
|
|
|
|
return this;
|
|
|
|
}
|
2015-10-13 14:31:29 -07:00
|
|
|
|
|
|
|
public ProjectContext Build()
|
|
|
|
{
|
|
|
|
ProjectDirectory = Project?.ProjectDirectory ?? ProjectDirectory;
|
|
|
|
|
|
|
|
if (GlobalSettings == null)
|
|
|
|
{
|
2015-10-21 12:59:44 -07:00
|
|
|
RootDirectory = ProjectRootResolver.ResolveRootDirectory(ProjectDirectory);
|
|
|
|
|
2015-10-13 14:31:29 -07:00
|
|
|
GlobalSettings globalSettings;
|
2015-10-21 12:59:44 -07:00
|
|
|
if (GlobalSettings.TryGetGlobalSettings(RootDirectory, out globalSettings))
|
2015-10-13 14:31:29 -07:00
|
|
|
{
|
|
|
|
GlobalSettings = globalSettings;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
RootDirectory = GlobalSettings?.DirectoryPath ?? RootDirectory;
|
2015-10-29 01:35:23 -07:00
|
|
|
PackagesDirectory = PackagesDirectory ?? PackageDependencyProvider.ResolvePackagesPath(RootDirectory, GlobalSettings);
|
2015-10-28 02:21:00 -07:00
|
|
|
ReferenceAssembliesPath = ReferenceAssembliesPath ?? GetDefaultReferenceAssembliesPath();
|
2015-10-13 14:31:29 -07:00
|
|
|
|
|
|
|
LockFileLookup lockFileLookup = null;
|
|
|
|
|
|
|
|
EnsureProjectLoaded();
|
|
|
|
|
|
|
|
var projectLockJsonPath = Path.Combine(ProjectDirectory, LockFile.FileName);
|
|
|
|
|
|
|
|
if (LockFile == null && File.Exists(projectLockJsonPath))
|
|
|
|
{
|
|
|
|
LockFile = LockFileReader.Read(projectLockJsonPath);
|
|
|
|
}
|
|
|
|
|
|
|
|
var validLockFile = true;
|
|
|
|
string lockFileValidationMessage = null;
|
|
|
|
|
|
|
|
if (LockFile != null)
|
|
|
|
{
|
2015-10-24 04:32:26 -07:00
|
|
|
validLockFile = LockFile.IsValidForProject(Project, out lockFileValidationMessage);
|
2015-10-13 14:31:29 -07:00
|
|
|
|
|
|
|
lockFileLookup = new LockFileLookup(LockFile);
|
|
|
|
}
|
|
|
|
|
2015-10-21 12:59:44 -07:00
|
|
|
var libraries = new Dictionary<LibraryKey, LibraryDescription>();
|
2015-10-13 14:31:29 -07:00
|
|
|
var projectResolver = new ProjectDependencyProvider();
|
|
|
|
|
|
|
|
var mainProject = projectResolver.GetDescription(TargetFramework, Project);
|
|
|
|
|
|
|
|
// Add the main project
|
2015-10-21 12:59:44 -07:00
|
|
|
libraries.Add(new LibraryKey(mainProject.Identity.Name), mainProject);
|
2015-10-13 14:31:29 -07:00
|
|
|
|
|
|
|
LockFileTarget target = null;
|
|
|
|
if (lockFileLookup != null)
|
|
|
|
{
|
|
|
|
target = SelectTarget(LockFile);
|
|
|
|
if (target != null)
|
|
|
|
{
|
|
|
|
var packageResolver = new PackageDependencyProvider(PackagesDirectory);
|
|
|
|
ScanLibraries(target, lockFileLookup, libraries, packageResolver, projectResolver);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-28 02:21:00 -07:00
|
|
|
var frameworkReferenceResolver = new FrameworkReferenceResolver(ReferenceAssembliesPath);
|
2015-10-13 14:31:29 -07:00
|
|
|
var referenceAssemblyDependencyResolver = new ReferenceAssemblyDependencyResolver(frameworkReferenceResolver);
|
2015-10-28 02:21:00 -07:00
|
|
|
bool requiresFrameworkAssemblies;
|
2015-10-13 14:31:29 -07:00
|
|
|
|
|
|
|
// Resolve the dependencies
|
2015-10-28 02:21:00 -07:00
|
|
|
ResolveDependencies(libraries, referenceAssemblyDependencyResolver, out requiresFrameworkAssemblies);
|
2015-10-13 14:31:29 -07:00
|
|
|
|
2015-10-17 07:34:04 -07:00
|
|
|
var diagnostics = new List<DiagnosticMessage>();
|
2015-10-13 14:31:29 -07:00
|
|
|
|
2015-10-17 07:34:04 -07:00
|
|
|
// REVIEW: Should this be in NuGet (possibly stored in the lock file?)
|
|
|
|
if (LockFile == null)
|
|
|
|
{
|
|
|
|
diagnostics.Add(new DiagnosticMessage(
|
|
|
|
ErrorCodes.NU1009,
|
2015-11-07 02:24:53 -08:00
|
|
|
$"The expected lock file doesn't exist. Please run \"dotnet restore\" to generate a new lock file.",
|
2015-10-17 07:34:04 -07:00
|
|
|
Path.Combine(Project.ProjectDirectory, LockFile.FileName),
|
|
|
|
DiagnosticMessageSeverity.Error));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!validLockFile)
|
|
|
|
{
|
|
|
|
diagnostics.Add(new DiagnosticMessage(
|
|
|
|
ErrorCodes.NU1006,
|
2015-11-07 02:24:53 -08:00
|
|
|
$"{lockFileValidationMessage}. Please run \"dotnet restore\" to generate a new lock file.",
|
2015-10-17 07:34:04 -07:00
|
|
|
Path.Combine(Project.ProjectDirectory, LockFile.FileName),
|
|
|
|
DiagnosticMessageSeverity.Warning));
|
|
|
|
}
|
|
|
|
|
2015-11-07 23:53:05 -08:00
|
|
|
if (requiresFrameworkAssemblies)
|
2015-10-28 02:21:00 -07:00
|
|
|
{
|
|
|
|
var frameworkInfo = Project.GetTargetFramework(TargetFramework);
|
2015-11-07 23:53:05 -08:00
|
|
|
|
|
|
|
if (string.IsNullOrEmpty(ReferenceAssembliesPath))
|
|
|
|
{
|
|
|
|
// If there was an attempt to use reference assemblies but they were not installed
|
|
|
|
// report an error
|
|
|
|
diagnostics.Add(new DiagnosticMessage(
|
|
|
|
ErrorCodes.DOTNET1012,
|
|
|
|
$"The reference assemblies directory was not specified. You can set the location using the DOTNET_REFERENCE_ASSEMBLIES_PATH environment variable.",
|
|
|
|
filePath: Project.ProjectFilePath,
|
|
|
|
severity: DiagnosticMessageSeverity.Error,
|
|
|
|
startLine: frameworkInfo.Line,
|
|
|
|
startColumn: frameworkInfo.Column
|
|
|
|
));
|
|
|
|
}
|
|
|
|
else if (!frameworkReferenceResolver.IsInstalled(TargetFramework))
|
|
|
|
{
|
|
|
|
// If there was an attempt to use reference assemblies but they were not installed
|
|
|
|
// report an error
|
|
|
|
diagnostics.Add(new DiagnosticMessage(
|
|
|
|
ErrorCodes.DOTNET1011,
|
|
|
|
$"Framework not installed: {TargetFramework.DotNetFrameworkName} in {ReferenceAssembliesPath}",
|
|
|
|
filePath: Project.ProjectFilePath,
|
|
|
|
severity: DiagnosticMessageSeverity.Error,
|
|
|
|
startLine: frameworkInfo.Line,
|
|
|
|
startColumn: frameworkInfo.Column
|
|
|
|
));
|
|
|
|
}
|
2015-10-28 02:21:00 -07:00
|
|
|
}
|
|
|
|
|
2015-10-17 07:34:04 -07:00
|
|
|
// Create a library manager
|
2015-11-08 07:38:42 -08:00
|
|
|
var libraryManager = new LibraryManager(libraries.Values.ToList(), diagnostics, Project.ProjectFilePath);
|
2015-10-13 14:31:29 -07:00
|
|
|
|
|
|
|
return new ProjectContext(
|
|
|
|
GlobalSettings,
|
|
|
|
mainProject,
|
|
|
|
TargetFramework,
|
|
|
|
target?.RuntimeIdentifier,
|
|
|
|
PackagesDirectory,
|
|
|
|
libraryManager);
|
|
|
|
}
|
|
|
|
|
2015-10-28 02:21:00 -07:00
|
|
|
private void ResolveDependencies(Dictionary<LibraryKey, LibraryDescription> libraries,
|
|
|
|
ReferenceAssemblyDependencyResolver referenceAssemblyDependencyResolver,
|
|
|
|
out bool requiresFrameworkAssemblies)
|
2015-10-13 14:31:29 -07:00
|
|
|
{
|
2015-10-28 02:21:00 -07:00
|
|
|
requiresFrameworkAssemblies = false;
|
|
|
|
|
2015-10-13 14:31:29 -07:00
|
|
|
foreach (var library in libraries.Values.ToList())
|
|
|
|
{
|
|
|
|
if (Equals(library.Identity.Type, LibraryType.Package) &&
|
|
|
|
!Directory.Exists(library.Path))
|
|
|
|
{
|
|
|
|
// If the package path doesn't exist then mark this dependency as unresolved
|
|
|
|
library.Resolved = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
library.Framework = library.Framework ?? TargetFramework;
|
|
|
|
foreach (var dependency in library.Dependencies)
|
|
|
|
{
|
2015-10-21 12:59:44 -07:00
|
|
|
var keyType = dependency.Target == LibraryType.ReferenceAssembly ? LibraryType.ReferenceAssembly : LibraryType.Unspecified;
|
|
|
|
var key = new LibraryKey(dependency.Name, keyType);
|
|
|
|
|
2015-10-13 14:31:29 -07:00
|
|
|
LibraryDescription dep;
|
2015-10-21 12:59:44 -07:00
|
|
|
if (!libraries.TryGetValue(key, out dep))
|
2015-10-13 14:31:29 -07:00
|
|
|
{
|
2015-10-17 00:04:20 -07:00
|
|
|
if (Equals(LibraryType.ReferenceAssembly, dependency.Target))
|
2015-10-13 14:31:29 -07:00
|
|
|
{
|
2015-10-28 02:21:00 -07:00
|
|
|
requiresFrameworkAssemblies = true;
|
|
|
|
|
2015-10-13 14:31:29 -07:00
|
|
|
dep = referenceAssemblyDependencyResolver.GetDescription(dependency, TargetFramework) ??
|
2015-10-17 07:34:04 -07:00
|
|
|
UnresolvedDependencyProvider.GetDescription(dependency, TargetFramework);
|
2015-10-13 14:31:29 -07:00
|
|
|
|
|
|
|
dep.Framework = TargetFramework;
|
2015-10-21 12:59:44 -07:00
|
|
|
libraries[key] = dep;
|
2015-10-13 14:31:29 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-10-17 07:34:04 -07:00
|
|
|
dep = UnresolvedDependencyProvider.GetDescription(dependency, TargetFramework);
|
2015-10-21 12:59:44 -07:00
|
|
|
libraries[key] = dep;
|
2015-10-13 14:31:29 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-20 01:43:37 -07:00
|
|
|
dep.RequestedRanges.Add(dependency);
|
2015-10-16 22:50:44 -07:00
|
|
|
dep.Parents.Add(library);
|
2015-10-13 14:31:29 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-21 12:59:44 -07:00
|
|
|
private void ScanLibraries(LockFileTarget target, LockFileLookup lockFileLookup, Dictionary<LibraryKey, LibraryDescription> libraries, PackageDependencyProvider packageResolver, ProjectDependencyProvider projectResolver)
|
2015-10-13 14:31:29 -07:00
|
|
|
{
|
|
|
|
foreach (var library in target.Libraries)
|
|
|
|
{
|
2015-10-24 04:32:26 -07:00
|
|
|
LibraryDescription description = null;
|
|
|
|
var type = LibraryType.Unspecified;
|
|
|
|
|
2015-10-13 14:31:29 -07:00
|
|
|
if (string.Equals(library.Type, "project"))
|
|
|
|
{
|
|
|
|
var projectLibrary = lockFileLookup.GetProject(library.Name);
|
|
|
|
|
2015-10-24 04:32:26 -07:00
|
|
|
if (projectLibrary != null)
|
|
|
|
{
|
|
|
|
var path = Path.GetFullPath(Path.Combine(ProjectDirectory, projectLibrary.Path));
|
2015-10-13 14:31:29 -07:00
|
|
|
|
2015-10-24 04:32:26 -07:00
|
|
|
description = projectResolver.GetDescription(library.Name, path, library);
|
|
|
|
}
|
2015-10-13 14:31:29 -07:00
|
|
|
|
2015-10-24 04:32:26 -07:00
|
|
|
type = LibraryType.Project;
|
2015-10-13 14:31:29 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
var packageEntry = lockFileLookup.GetPackage(library.Name, library.Version);
|
|
|
|
|
2015-10-24 04:32:26 -07:00
|
|
|
if (packageEntry != null)
|
|
|
|
{
|
|
|
|
description = packageResolver.GetDescription(packageEntry, library);
|
|
|
|
}
|
2015-10-13 14:31:29 -07:00
|
|
|
|
2015-10-24 04:32:26 -07:00
|
|
|
type = LibraryType.Package;
|
2015-10-13 14:31:29 -07:00
|
|
|
}
|
2015-10-24 04:32:26 -07:00
|
|
|
|
|
|
|
description = description ?? UnresolvedDependencyProvider.GetDescription(new LibraryRange(library.Name, type), target.TargetFramework);
|
|
|
|
|
|
|
|
libraries.Add(new LibraryKey(library.Name), description);
|
2015-10-13 14:31:29 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-28 02:21:00 -07:00
|
|
|
public static string GetDefaultReferenceAssembliesPath()
|
|
|
|
{
|
|
|
|
// Allow setting the reference assemblies path via an environment variable
|
|
|
|
var referenceAssembliesPath = Environment.GetEnvironmentVariable("DOTNET_REFERENCE_ASSEMBLIES_PATH");
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(referenceAssembliesPath))
|
|
|
|
{
|
|
|
|
return referenceAssembliesPath;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
|
|
|
{
|
|
|
|
// There is no reference assemblies path outside of windows
|
|
|
|
// The enviorment variable can be used to specify one
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
// References assemblies are in %ProgramFiles(x86)% on
|
|
|
|
// 64 bit machines
|
|
|
|
var programFiles = Environment.GetEnvironmentVariable("ProgramFiles(x86)");
|
|
|
|
|
|
|
|
if (string.IsNullOrEmpty(programFiles))
|
|
|
|
{
|
|
|
|
// On 32 bit machines they are in %ProgramFiles%
|
|
|
|
programFiles = Environment.GetEnvironmentVariable("ProgramFiles");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (string.IsNullOrEmpty(programFiles))
|
|
|
|
{
|
|
|
|
// Reference assemblies aren't installed
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return Path.Combine(
|
|
|
|
programFiles,
|
|
|
|
"Reference Assemblies", "Microsoft", "Framework");
|
|
|
|
}
|
|
|
|
|
2015-10-13 14:31:29 -07:00
|
|
|
private void EnsureProjectLoaded()
|
|
|
|
{
|
|
|
|
if (Project == null)
|
|
|
|
{
|
|
|
|
Project project;
|
2015-10-17 22:42:50 -07:00
|
|
|
if (ProjectReader.TryGetProject(ProjectDirectory, out project))
|
2015-10-13 14:31:29 -07:00
|
|
|
{
|
|
|
|
Project = project;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
throw new InvalidOperationException($"Unable to resolve project from {ProjectDirectory}");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private LockFileTarget SelectTarget(LockFile lockFile)
|
|
|
|
{
|
|
|
|
foreach (var runtimeIdentifier in RuntimeIdentifiers)
|
|
|
|
{
|
|
|
|
foreach (var scanTarget in lockFile.Targets)
|
|
|
|
{
|
|
|
|
if (Equals(scanTarget.TargetFramework, TargetFramework) && string.Equals(scanTarget.RuntimeIdentifier, runtimeIdentifier, StringComparison.Ordinal))
|
|
|
|
{
|
|
|
|
return scanTarget;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach (var scanTarget in lockFile.Targets)
|
|
|
|
{
|
|
|
|
if (Equals(scanTarget.TargetFramework, TargetFramework) && string.IsNullOrEmpty(scanTarget.RuntimeIdentifier))
|
|
|
|
{
|
|
|
|
return scanTarget;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
2015-10-21 12:59:44 -07:00
|
|
|
|
|
|
|
private struct LibraryKey
|
|
|
|
{
|
|
|
|
public LibraryKey(string name) : this(name, LibraryType.Unspecified)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
public LibraryKey(string name, LibraryType libraryType)
|
|
|
|
{
|
|
|
|
Name = name;
|
|
|
|
LibraryType = libraryType;
|
|
|
|
}
|
|
|
|
|
|
|
|
public string Name { get; }
|
|
|
|
public LibraryType LibraryType { get; }
|
|
|
|
|
|
|
|
public override bool Equals(object obj)
|
|
|
|
{
|
|
|
|
var otherKey = (LibraryKey)obj;
|
|
|
|
|
|
|
|
return string.Equals(otherKey.Name, Name, StringComparison.Ordinal) &&
|
|
|
|
otherKey.LibraryType.Equals(LibraryType);
|
|
|
|
}
|
|
|
|
|
|
|
|
public override int GetHashCode()
|
|
|
|
{
|
|
|
|
var combiner = new HashCodeCombiner();
|
|
|
|
combiner.Add(Name);
|
|
|
|
combiner.Add(LibraryType);
|
|
|
|
|
|
|
|
return combiner.CombinedHash;
|
|
|
|
}
|
|
|
|
}
|
2015-10-13 14:31:29 -07:00
|
|
|
}
|
|
|
|
}
|