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
}
}