2015-11-16 11:21:57 -08:00
// 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.
2015-10-13 14:31:29 -07:00
using System ;
using System.Collections.Generic ;
using System.IO ;
using System.Linq ;
2016-10-27 18:46:43 -07:00
using Microsoft.DotNet.Internal.ProjectModel.Files ;
using Microsoft.DotNet.Internal.ProjectModel.Utilities ;
2016-03-31 16:25:52 -07:00
using Newtonsoft.Json ;
using Newtonsoft.Json.Linq ;
2015-10-13 14:31:29 -07:00
using NuGet.Frameworks ;
2016-09-23 15:30:53 -07:00
using NuGet.LibraryModel ;
2015-10-13 14:31:29 -07:00
using NuGet.Versioning ;
2016-10-27 18:46:43 -07:00
namespace Microsoft.DotNet.Internal.ProjectModel
2015-10-13 14:31:29 -07:00
{
2016-10-27 18:46:43 -07:00
internal class ProjectReader : IProjectReader
2015-10-13 14:31:29 -07:00
{
2016-04-11 19:25:28 -07:00
public static bool TryGetProject ( string path , out Project project , ProjectReaderSettings settings = null )
2015-10-17 22:42:50 -07:00
{
project = null ;
string projectPath = null ;
if ( string . Equals ( Path . GetFileName ( path ) , Project . FileName , StringComparison . OrdinalIgnoreCase ) )
{
projectPath = path ;
path = Path . GetDirectoryName ( path ) ;
}
else if ( ! HasProjectFile ( path ) )
{
return false ;
}
else
{
projectPath = Path . Combine ( path , Project . FileName ) ;
}
// Assume the directory name is the project name if none was specified
var projectName = PathUtility . GetDirectoryName ( Path . GetFullPath ( path ) ) ;
projectPath = Path . GetFullPath ( projectPath ) ;
if ( ! File . Exists ( projectPath ) )
{
return false ;
}
try
{
using ( var stream = File . OpenRead ( projectPath ) )
{
var reader = new ProjectReader ( ) ;
2016-04-11 19:25:28 -07:00
project = reader . ReadProject ( stream , projectName , projectPath , settings ) ;
2015-10-17 22:42:50 -07:00
}
}
catch ( Exception ex )
{
throw FileFormatException . Create ( ex , projectPath ) ;
}
return true ;
}
2016-04-11 19:25:28 -07:00
public static Project GetProject ( string projectPath , ProjectReaderSettings settings = null )
2016-08-22 21:29:14 -07:00
{
return new ProjectReader ( ) . ReadProject ( projectPath , settings ) ;
}
public Project ReadProject ( string projectPath , ProjectReaderSettings settings )
2015-10-13 14:31:29 -07:00
{
2016-04-28 09:15:41 -05:00
projectPath = ProjectPathHelper . NormalizeProjectFilePath ( projectPath ) ;
2016-03-01 17:42:44 -08:00
var name = Path . GetFileName ( Path . GetDirectoryName ( projectPath ) ) ;
2016-01-04 12:36:46 -08:00
2016-03-01 17:42:44 -08:00
using ( var stream = new FileStream ( projectPath , FileMode . Open , FileAccess . Read , FileShare . Read ) )
2015-10-13 14:31:29 -07:00
{
2016-08-22 21:29:14 -07:00
return ReadProject ( stream , name , projectPath , settings ) ;
2015-10-13 14:31:29 -07:00
}
}
2016-04-11 19:25:28 -07:00
public Project ReadProject ( Stream stream , string projectName , string projectPath , ProjectReaderSettings settings = null )
2015-10-13 14:31:29 -07:00
{
2015-12-09 00:47:57 -08:00
settings = settings ? ? new ProjectReaderSettings ( ) ;
2015-10-13 14:31:29 -07:00
var project = new Project ( ) ;
2015-10-15 15:09:37 -07:00
var reader = new StreamReader ( stream ) ;
2016-04-22 15:01:56 -07:00
JObject rawProject ;
using ( var jsonReader = new JsonTextReader ( reader ) )
{
rawProject = JObject . Load ( jsonReader ) ;
// Try to read another token to ensure we're at the end of the document.
// This will no-op if we are, and throw a JsonReaderException if there is additional content (which is what we want)
jsonReader . Read ( ) ;
}
2016-03-31 16:25:52 -07:00
2015-10-13 14:31:29 -07:00
if ( rawProject = = null )
{
throw FileFormatException . Create (
"The JSON file can't be deserialized to a JSON object." ,
projectPath ) ;
}
// Meta-data properties
2016-03-31 16:25:52 -07:00
project . Name = rawProject . Value < string > ( "name" ) ? ? projectName ;
2015-10-13 14:31:29 -07:00
project . ProjectFilePath = Path . GetFullPath ( projectPath ) ;
2016-03-31 16:25:52 -07:00
var version = rawProject . Value < string > ( "version" ) ;
2015-10-13 14:31:29 -07:00
if ( version = = null )
{
project . Version = new NuGetVersion ( "1.0.0" ) ;
}
else
{
try
{
2016-01-03 08:18:25 -08:00
var buildVersion = settings . VersionSuffix ;
2015-10-15 15:09:37 -07:00
project . Version = SpecifySnapshot ( version , buildVersion ) ;
2015-10-13 14:31:29 -07:00
}
catch ( Exception ex )
{
throw FileFormatException . Create ( ex , version , project . ProjectFilePath ) ;
}
}
2016-01-03 08:18:25 -08:00
var fileVersion = settings . AssemblyFileVersion ;
2015-10-13 14:31:29 -07:00
if ( string . IsNullOrWhiteSpace ( fileVersion ) )
{
project . AssemblyFileVersion = project . Version . Version ;
}
else
{
try
{
var simpleVersion = project . Version . Version ;
project . AssemblyFileVersion = new Version ( simpleVersion . Major ,
simpleVersion . Minor ,
simpleVersion . Build ,
int . Parse ( fileVersion ) ) ;
}
catch ( FormatException ex )
{
throw new FormatException ( "The assembly file version is invalid: " + fileVersion , ex ) ;
}
}
2016-03-31 16:25:52 -07:00
project . Description = rawProject . Value < string > ( "description" ) ;
project . Copyright = rawProject . Value < string > ( "copyright" ) ;
project . Title = rawProject . Value < string > ( "title" ) ;
project . EntryPoint = rawProject . Value < string > ( "entryPoint" ) ;
project . TestRunner = rawProject . Value < string > ( "testRunner" ) ;
project . Authors =
rawProject . Value < JToken > ( "authors" ) ? . Values < string > ( ) . ToArray ( ) ? ? EmptyArray < string > . Value ;
project . Language = rawProject . Value < string > ( "language" ) ;
2015-10-17 22:42:50 -07:00
2015-10-17 08:05:35 -07:00
// REVIEW: Move this to the dependencies node?
2016-03-31 16:25:52 -07:00
project . EmbedInteropTypes = rawProject . Value < bool > ( "embedInteropTypes" ) ;
2015-10-13 14:31:29 -07:00
2016-09-23 15:30:53 -07:00
project . Dependencies = new List < ProjectLibraryDependency > ( ) ;
2016-10-27 18:46:43 -07:00
2016-09-23 15:30:53 -07:00
project . Tools = new List < ProjectLibraryDependency > ( ) ;
2016-10-27 18:46:43 -07:00
project . Runtimes = new List < string > ( ) ;
2015-10-13 14:31:29 -07:00
// Project files
project . Files = new ProjectFilesCollection ( rawProject , project . ProjectDirectory , project . ProjectFilePath ) ;
2016-04-30 19:09:43 -07:00
AddProjectFilesCollectionDiagnostics ( rawProject , project ) ;
2015-10-13 14:31:29 -07:00
2016-03-31 16:25:52 -07:00
var commands = rawProject . Value < JToken > ( "commands" ) as JObject ;
2015-10-13 14:31:29 -07:00
if ( commands ! = null )
{
2016-03-31 16:25:52 -07:00
foreach ( var command in commands )
2015-10-13 14:31:29 -07:00
{
2016-03-31 16:25:52 -07:00
var commandValue = command . Value . Type = = JTokenType . String ? command . Value . Value < string > ( ) : null ;
if ( commandValue ! = null )
2015-10-13 14:31:29 -07:00
{
2016-03-31 16:25:52 -07:00
project . Commands [ command . Key ] = commandValue ;
2015-10-13 14:31:29 -07:00
}
}
}
2016-03-31 16:25:52 -07:00
var scripts = rawProject . Value < JToken > ( "scripts" ) as JObject ;
2015-10-13 14:31:29 -07:00
if ( scripts ! = null )
{
2016-03-31 16:25:52 -07:00
foreach ( var script in scripts )
2015-10-13 14:31:29 -07:00
{
2016-03-31 16:25:52 -07:00
var stringValue = script . Value . Type = = JTokenType . String ? script . Value . Value < string > ( ) : null ;
2015-10-13 14:31:29 -07:00
if ( stringValue ! = null )
{
2016-03-31 16:25:52 -07:00
project . Scripts [ script . Key ] = new string [ ] { stringValue } ;
2015-10-13 14:31:29 -07:00
continue ;
}
2016-03-31 16:25:52 -07:00
var arrayValue =
script . Value . Type = = JTokenType . Array ? script . Value . Values < string > ( ) . ToArray ( ) : null ;
2015-10-13 14:31:29 -07:00
if ( arrayValue ! = null )
{
2016-03-31 16:25:52 -07:00
project . Scripts [ script . Key ] = arrayValue ;
2015-10-13 14:31:29 -07:00
continue ;
}
throw FileFormatException . Create (
string . Format ( "The value of a script in {0} can only be a string or an array of strings" , Project . FileName ) ,
2016-03-31 16:25:52 -07:00
script . Value ,
2015-10-13 14:31:29 -07:00
project . ProjectFilePath ) ;
}
}
2016-04-11 19:25:28 -07:00
project . PackOptions = GetPackOptions ( rawProject , project ) ? ? new PackOptions ( ) ;
project . RuntimeOptions = GetRuntimeOptions ( rawProject ) ? ? new RuntimeOptions ( ) ;
project . PublishOptions = GetPublishInclude ( rawProject , project ) ;
BuildTargetFrameworksAndConfigurations ( project , rawProject ) ;
2015-10-13 14:31:29 -07:00
PopulateDependencies (
project . ProjectFilePath ,
project . Dependencies ,
rawProject ,
"dependencies" ,
isGacOrFrameworkReference : false ) ;
2016-01-04 12:36:46 -08:00
PopulateDependencies (
project . ProjectFilePath ,
project . Tools ,
rawProject ,
"tools" ,
isGacOrFrameworkReference : false ) ;
2016-10-27 18:46:43 -07:00
PopulateRuntimes ( project . Runtimes , rawProject ) ;
2016-10-26 22:23:40 +00:00
2016-03-31 16:25:52 -07:00
JToken runtimeOptionsToken ;
if ( rawProject . TryGetValue ( "runtimeOptions" , out runtimeOptionsToken ) )
{
var runtimeOptions = runtimeOptionsToken as JObject ;
if ( runtimeOptions = = null )
{
throw FileFormatException . Create ( "The runtimeOptions must be an object" , runtimeOptionsToken ) ;
}
project . RawRuntimeOptions = runtimeOptions . ToString ( ) ;
}
2015-10-13 14:31:29 -07:00
return project ;
}
private static NuGetVersion SpecifySnapshot ( string version , string snapshotValue )
{
if ( version . EndsWith ( "-*" ) )
{
if ( string . IsNullOrEmpty ( snapshotValue ) )
{
version = version . Substring ( 0 , version . Length - 2 ) ;
}
else
{
version = version . Substring ( 0 , version . Length - 1 ) + snapshotValue ;
}
}
2015-10-15 15:09:37 -07:00
return new NuGetVersion ( version ) ;
2015-10-13 14:31:29 -07:00
}
private static void PopulateDependencies (
string projectPath ,
2016-09-23 15:30:53 -07:00
IList < ProjectLibraryDependency > results ,
2016-03-31 16:25:52 -07:00
JObject settings ,
2015-10-13 14:31:29 -07:00
string propertyName ,
bool isGacOrFrameworkReference )
{
2016-03-31 16:25:52 -07:00
var dependencies = settings . Value < JToken > ( propertyName ) as JObject ;
2015-10-13 14:31:29 -07:00
if ( dependencies ! = null )
{
2016-03-31 16:25:52 -07:00
foreach ( var dependency in dependencies )
2015-10-13 14:31:29 -07:00
{
2016-03-31 16:25:52 -07:00
if ( string . IsNullOrEmpty ( dependency . Key ) )
2015-10-13 14:31:29 -07:00
{
throw FileFormatException . Create (
"Unable to resolve dependency ''." ,
2016-03-31 16:25:52 -07:00
dependency . Key ,
2015-10-13 14:31:29 -07:00
projectPath ) ;
}
2016-03-31 16:25:52 -07:00
var dependencyValue = dependency . Value ;
2015-10-15 15:09:37 -07:00
var dependencyTypeValue = LibraryDependencyType . Default ;
2016-09-26 21:40:11 -07:00
var dependencyIncludeFlagsValue = LibraryIncludeFlags . All ;
var dependencyExcludeFlagsValue = LibraryIncludeFlags . None ;
var suppressParentFlagsValue = LibraryIncludeFlagUtils . DefaultSuppressParent ;
2016-09-26 13:47:55 -07:00
var target = isGacOrFrameworkReference ? LibraryDependencyTarget . Reference : LibraryDependencyTarget . All ;
2016-03-31 16:25:52 -07:00
string dependencyVersionAsString = null ;
2015-10-13 14:31:29 -07:00
2016-03-31 16:25:52 -07:00
if ( dependencyValue . Type = = JTokenType . Object )
2015-10-13 14:31:29 -07:00
{
// "dependencies" : { "Name" : { "version": "1.0", "type": "build", "target": "project" } }
2016-03-31 16:25:52 -07:00
dependencyVersionAsString = dependencyValue . Value < string > ( "version" ) ;
2015-10-13 14:31:29 -07:00
2016-03-31 16:25:52 -07:00
var type = dependencyValue . Value < string > ( "type" ) ;
2015-10-13 14:31:29 -07:00
if ( type ! = null )
{
2016-09-23 15:30:53 -07:00
dependencyTypeValue = LibraryDependencyType . Parse ( new [ ] { type } ) ;
2015-10-13 14:31:29 -07:00
}
// Read the target if specified
if ( ! isGacOrFrameworkReference )
{
2016-03-31 16:25:52 -07:00
var targetStr = dependencyValue . Value < string > ( "target" ) ;
2016-09-23 15:30:53 -07:00
target = LibraryDependencyTargetUtils . Parse ( targetStr ) ;
2015-10-13 14:31:29 -07:00
}
2016-09-26 21:40:11 -07:00
IEnumerable < string > strings ;
if ( TryGetStringEnumerable ( dependencyValue [ "include" ] , out strings ) )
{
dependencyIncludeFlagsValue = LibraryIncludeFlagUtils . GetFlags ( strings ) ;
}
if ( TryGetStringEnumerable ( dependencyValue [ "exclude" ] , out strings ) )
{
dependencyExcludeFlagsValue = LibraryIncludeFlagUtils . GetFlags ( strings ) ;
}
if ( TryGetStringEnumerable ( dependencyValue [ "suppressParent" ] , out strings ) )
{
// This overrides any settings that came from the type property.
suppressParentFlagsValue = LibraryIncludeFlagUtils . GetFlags ( strings ) ;
}
2015-10-13 14:31:29 -07:00
}
2016-03-31 16:25:52 -07:00
else if ( dependencyValue . Type = = JTokenType . String )
2015-10-13 14:31:29 -07:00
{
// "dependencies" : { "Name" : "1.0" }
2016-03-31 16:25:52 -07:00
dependencyVersionAsString = dependencyValue . Value < string > ( ) ;
2015-10-13 14:31:29 -07:00
}
else
{
throw FileFormatException . Create (
2016-03-31 16:25:52 -07:00
string . Format (
"Invalid dependency version: {0}. The format is not recognizable." ,
dependency . Key ) ,
2015-10-13 14:31:29 -07:00
dependencyValue ,
projectPath ) ;
}
VersionRange dependencyVersionRange = null ;
2016-03-31 16:25:52 -07:00
if ( ! string . IsNullOrEmpty ( dependencyVersionAsString ) )
2015-10-13 14:31:29 -07:00
{
try
{
2016-03-31 16:25:52 -07:00
dependencyVersionRange = VersionRange . Parse ( dependencyVersionAsString ) ;
2015-10-13 14:31:29 -07:00
}
catch ( Exception ex )
{
2016-03-31 16:25:52 -07:00
throw FileFormatException . Create ( ex , dependencyValue , projectPath ) ;
2015-10-13 14:31:29 -07:00
}
}
2016-09-26 21:40:11 -07:00
// the dependency flags are: Include flags - Exclude flags
var includeFlags = dependencyIncludeFlagsValue & ~ dependencyExcludeFlagsValue ;
2016-03-31 16:25:52 -07:00
var lineInfo = ( IJsonLineInfo ) dependencyValue ;
2016-09-23 15:30:53 -07:00
results . Add ( new ProjectLibraryDependency
{
LibraryRange = new LibraryRange (
dependency . Key ,
dependencyVersionRange ,
target ) ,
2016-09-26 13:47:55 -07:00
Type = dependencyTypeValue ,
2016-09-26 21:40:11 -07:00
IncludeType = includeFlags ,
SuppressParent = suppressParentFlagsValue ,
2016-09-26 13:47:55 -07:00
SourceFilePath = projectPath ,
SourceLine = lineInfo . LineNumber ,
SourceColumn = lineInfo . LinePosition
2016-09-23 15:30:53 -07:00
} ) ;
2015-10-13 14:31:29 -07:00
}
}
}
2016-10-27 18:46:43 -07:00
private static void PopulateRuntimes ( IList < string > results , JObject settings )
{
var runtimes = settings . Value < JToken > ( "runtimes" ) as JObject ;
if ( runtimes ! = null )
{
foreach ( var runtime in runtimes )
{
if ( ! string . IsNullOrEmpty ( runtime . Key ) )
{
results . Add ( runtime . Key ) ;
}
}
}
2016-10-26 22:23:40 +00:00
}
2016-04-11 19:25:28 -07:00
private void BuildTargetFrameworksAndConfigurations ( Project project , JObject projectJsonObject )
2015-10-13 14:31:29 -07:00
{
// Get the shared compilationOptions
2016-03-31 16:25:52 -07:00
project . _defaultCompilerOptions =
2016-04-11 19:25:28 -07:00
GetCompilationOptions ( projectJsonObject , project ) ? ? new CommonCompilerOptions { CompilerName = "csc" } ;
2015-10-13 14:31:29 -07:00
project . _defaultTargetFrameworkConfiguration = new TargetFrameworkInformation
{
2016-09-23 15:30:53 -07:00
Dependencies = new List < ProjectLibraryDependency > ( )
2015-10-13 14:31:29 -07:00
} ;
// Add default configurations
2015-10-20 13:27:56 -07:00
project . _compilerOptionsByConfiguration [ "Debug" ] = new CommonCompilerOptions
2015-10-13 14:31:29 -07:00
{
Defines = new [ ] { "DEBUG" , "TRACE" } ,
Optimize = false
} ;
2015-10-20 13:27:56 -07:00
project . _compilerOptionsByConfiguration [ "Release" ] = new CommonCompilerOptions
2015-10-13 14:31:29 -07:00
{
Defines = new [ ] { "RELEASE" , "TRACE" } ,
Optimize = true
} ;
// The configuration node has things like debug/release compiler settings
/ *
{
"configurations" : {
"Debug" : {
} ,
"Release" : {
}
}
}
* /
2016-03-31 16:25:52 -07:00
var configurationsSection = projectJsonObject . Value < JToken > ( "configurations" ) as JObject ;
2015-10-13 14:31:29 -07:00
if ( configurationsSection ! = null )
{
2016-03-31 16:25:52 -07:00
foreach ( var configKey in configurationsSection )
2015-10-13 14:31:29 -07:00
{
2016-03-31 16:25:52 -07:00
var compilerOptions = GetCompilationOptions ( configKey . Value as JObject , project ) ;
2015-10-13 14:31:29 -07:00
// Only use this as a configuration if it's not a target framework
2016-03-31 16:25:52 -07:00
project . _compilerOptionsByConfiguration [ configKey . Key ] = compilerOptions ;
2015-10-13 14:31:29 -07:00
}
}
// The frameworks node is where target frameworks go
/ *
{
"frameworks" : {
"net45" : {
} ,
"dnxcore50" : {
}
}
}
* /
2016-03-31 16:25:52 -07:00
var frameworks = projectJsonObject . Value < JToken > ( "frameworks" ) as JObject ;
2015-10-13 14:31:29 -07:00
if ( frameworks ! = null )
{
2016-03-31 16:25:52 -07:00
foreach ( var framework in frameworks )
2015-10-13 14:31:29 -07:00
{
try
{
2016-03-31 16:25:52 -07:00
var frameworkToken = framework . Value as JObject ;
var success = BuildTargetFrameworkNode ( project , framework . Key , frameworkToken ) ;
2015-10-13 14:31:29 -07:00
if ( ! success )
{
2016-03-31 16:25:52 -07:00
var lineInfo = ( IJsonLineInfo ) framework . Value ;
2016-04-30 19:09:43 -07:00
project . Diagnostics . Add (
2015-10-13 14:31:29 -07:00
new DiagnosticMessage (
ErrorCodes . NU1008 ,
2016-03-31 16:25:52 -07:00
$"\" { framework . Key } \ " is an unsupported framework." ,
2015-10-13 14:31:29 -07:00
project . ProjectFilePath ,
DiagnosticMessageSeverity . Error ,
2016-03-31 16:25:52 -07:00
lineInfo . LineNumber ,
lineInfo . LinePosition ) ) ;
2015-10-13 14:31:29 -07:00
}
}
catch ( Exception ex )
{
2016-03-31 16:25:52 -07:00
throw FileFormatException . Create ( ex , framework . Value , project . ProjectFilePath ) ;
2015-10-13 14:31:29 -07:00
}
}
}
}
/// <summary>
/// Parse a Json object which represents project configuration for a specified framework
/// </summary>
/// <param name="frameworkKey">The name of the framework</param>
/// <param name="frameworkValue">The Json object represent the settings</param>
/// <returns>Returns true if it successes.</returns>
2016-03-31 16:25:52 -07:00
private bool BuildTargetFrameworkNode ( Project project , string frameworkKey , JObject frameworkValue )
2015-10-13 14:31:29 -07:00
{
// If no compilation options are provided then figure them out from the node
2016-01-18 15:14:19 -08:00
var compilerOptions = GetCompilationOptions ( frameworkValue , project ) ? ?
2015-10-20 13:27:56 -07:00
new CommonCompilerOptions ( ) ;
2015-10-13 14:31:29 -07:00
var frameworkName = NuGetFramework . Parse ( frameworkKey ) ;
// If it's not unsupported then keep it
if ( frameworkName . IsUnsupported )
{
// REVIEW: Should we skip unsupported target frameworks
return false ;
}
// Add the target framework specific define
var defines = new HashSet < string > ( compilerOptions . Defines ? ? Enumerable . Empty < string > ( ) ) ;
compilerOptions . Defines = defines ;
2016-03-31 16:25:52 -07:00
var lineInfo = ( IJsonLineInfo ) frameworkValue ;
2015-10-13 14:31:29 -07:00
var targetFrameworkInformation = new TargetFrameworkInformation
{
FrameworkName = frameworkName ,
2016-09-23 15:30:53 -07:00
Dependencies = new List < ProjectLibraryDependency > ( ) ,
2015-10-28 02:21:00 -07:00
CompilerOptions = compilerOptions ,
2016-03-31 16:25:52 -07:00
Line = lineInfo . LineNumber ,
2016-09-26 21:40:11 -07:00
Column = lineInfo . LinePosition ,
Imports = GetImports ( frameworkValue )
2015-10-13 14:31:29 -07:00
} ;
2016-09-23 15:30:53 -07:00
var frameworkDependencies = new List < ProjectLibraryDependency > ( ) ;
2015-10-13 14:31:29 -07:00
PopulateDependencies (
project . ProjectFilePath ,
frameworkDependencies ,
frameworkValue ,
"dependencies" ,
isGacOrFrameworkReference : false ) ;
2016-09-23 15:30:53 -07:00
var frameworkAssemblies = new List < ProjectLibraryDependency > ( ) ;
2015-10-13 14:31:29 -07:00
PopulateDependencies (
project . ProjectFilePath ,
frameworkAssemblies ,
frameworkValue ,
"frameworkAssemblies" ,
isGacOrFrameworkReference : true ) ;
frameworkDependencies . AddRange ( frameworkAssemblies ) ;
targetFrameworkInformation . Dependencies = frameworkDependencies ;
2016-03-31 16:25:52 -07:00
targetFrameworkInformation . WrappedProject = frameworkValue . Value < string > ( "wrappedProject" ) ;
2015-10-13 14:31:29 -07:00
2016-03-31 16:25:52 -07:00
var binNode = frameworkValue . Value < JToken > ( "bin" ) as JObject ;
2015-10-13 14:31:29 -07:00
if ( binNode ! = null )
{
2016-03-31 16:25:52 -07:00
targetFrameworkInformation . AssemblyPath = binNode . Value < string > ( "assembly" ) ;
2015-10-13 14:31:29 -07:00
}
project . _targetFrameworks [ frameworkName ] = targetFrameworkInformation ;
return true ;
}
2016-09-26 21:40:11 -07:00
private IEnumerable < string > GetImports ( JObject frameworkValue )
{
var prop = frameworkValue . Property ( "imports" ) ;
if ( prop = = null )
{
return Enumerable . Empty < string > ( ) ;
}
2016-10-11 15:22:18 -07:00
if ( prop . Value . Type = = JTokenType . Array )
2016-09-26 21:40:11 -07:00
{
2016-10-11 15:22:18 -07:00
return ( prop . Value as JArray ) . Select ( i = > i . Value < string > ( ) ) ;
2016-09-26 21:40:11 -07:00
}
2016-10-11 15:22:18 -07:00
else if ( prop . Value . Type = = JTokenType . String )
2016-09-26 21:40:11 -07:00
{
2016-10-11 15:22:18 -07:00
return new [ ] { prop . Value . ToString ( ) } ;
2016-09-26 21:40:11 -07:00
}
return null ;
}
2016-03-31 16:25:52 -07:00
private static CommonCompilerOptions GetCompilationOptions ( JObject rawObject , Project project )
2015-10-13 14:31:29 -07:00
{
2016-04-11 19:25:28 -07:00
var compilerName = rawObject . Value < string > ( "compilerName" ) ;
if ( compilerName ! = null )
{
var lineInfo = rawObject . Value < IJsonLineInfo > ( "compilerName" ) ;
2016-04-30 19:09:43 -07:00
project . Diagnostics . Add (
2016-04-11 19:25:28 -07:00
new DiagnosticMessage (
ErrorCodes . DOTNET1016 ,
$"The 'compilerName' option in the root is deprecated. Use it in 'buildOptions' instead." ,
project . ProjectFilePath ,
DiagnosticMessageSeverity . Warning ,
lineInfo . LineNumber ,
lineInfo . LinePosition ) ) ;
}
var rawOptions = rawObject . Value < JToken > ( "buildOptions" ) as JObject ;
2015-10-13 14:31:29 -07:00
if ( rawOptions = = null )
{
2016-04-11 19:25:28 -07:00
rawOptions = rawObject . Value < JToken > ( "compilationOptions" ) as JObject ;
if ( rawOptions = = null )
{
return new CommonCompilerOptions
{
CompilerName = compilerName ? ? "csc"
} ;
}
var lineInfo = ( IJsonLineInfo ) rawOptions ;
2016-04-30 19:09:43 -07:00
project . Diagnostics . Add (
2016-04-11 19:25:28 -07:00
new DiagnosticMessage (
ErrorCodes . DOTNET1015 ,
$"The 'compilationOptions' option is deprecated. Use 'buildOptions' instead." ,
project . ProjectFilePath ,
DiagnosticMessageSeverity . Warning ,
lineInfo . LineNumber ,
lineInfo . LinePosition ) ) ;
2015-10-13 14:31:29 -07:00
}
2016-03-31 16:25:52 -07:00
var analyzerOptionsJson = rawOptions . Value < JToken > ( "analyzerOptions" ) as JObject ;
2016-01-18 15:14:19 -08:00
if ( analyzerOptionsJson ! = null )
{
var analyzerOptions = new AnalyzerOptions ( ) ;
2016-03-31 16:25:52 -07:00
foreach ( var analyzerOption in analyzerOptionsJson )
2016-01-18 15:14:19 -08:00
{
2016-03-31 16:25:52 -07:00
switch ( analyzerOption . Key )
2016-01-18 15:14:19 -08:00
{
case "languageId" :
2016-03-31 16:25:52 -07:00
if ( analyzerOption . Value . Type ! = JTokenType . String )
2016-01-18 15:14:19 -08:00
{
throw FileFormatException . Create (
"The analyzer languageId must be a string" ,
2016-03-31 16:25:52 -07:00
analyzerOption . Value . ToString ( ) ,
2016-01-18 15:14:19 -08:00
project . ProjectFilePath ) ;
}
2016-05-11 23:26:54 +02:00
analyzerOptions = new AnalyzerOptions ( analyzerOption . Value . ToString ( ) ) ;
2016-01-18 15:14:19 -08:00
break ;
2016-03-01 17:42:44 -08:00
default :
2016-01-18 15:14:19 -08:00
throw FileFormatException . Create (
2016-03-31 16:25:52 -07:00
$"Unrecognized analyzerOption key: {analyzerOption.Key}" ,
2016-01-18 15:14:19 -08:00
project . ProjectFilePath ) ;
}
}
project . AnalyzerOptions = analyzerOptions ;
}
2015-10-20 13:27:56 -07:00
return new CommonCompilerOptions
2015-10-13 14:31:29 -07:00
{
2016-03-31 16:25:52 -07:00
Defines = rawOptions . Value < JToken > ( "define" ) ? . Values < string > ( ) . ToArray ( ) ,
SuppressWarnings = rawOptions . Value < JToken > ( "nowarn" ) ? . Values < string > ( ) . ToArray ( ) ,
AdditionalArguments = rawOptions . Value < JToken > ( "additionalArguments" ) ? . Values < string > ( ) . ToArray ( ) ,
LanguageVersion = rawOptions . Value < string > ( "languageVersion" ) ,
AllowUnsafe = rawOptions . Value < bool? > ( "allowUnsafe" ) ,
Platform = rawOptions . Value < string > ( "platform" ) ,
WarningsAsErrors = rawOptions . Value < bool? > ( "warningsAsErrors" ) ,
Optimize = rawOptions . Value < bool? > ( "optimize" ) ,
KeyFile = rawOptions . Value < string > ( "keyFile" ) ,
DelaySign = rawOptions . Value < bool? > ( "delaySign" ) ,
PublicSign = rawOptions . Value < bool? > ( "publicSign" ) ,
DebugType = rawOptions . Value < string > ( "debugType" ) ,
EmitEntryPoint = rawOptions . Value < bool? > ( "emitEntryPoint" ) ,
GenerateXmlDocumentation = rawOptions . Value < bool? > ( "xmlDoc" ) ,
PreserveCompilationContext = rawOptions . Value < bool? > ( "preserveCompilationContext" ) ,
2016-04-11 19:25:28 -07:00
OutputName = rawOptions . Value < string > ( "outputName" ) ,
CompilerName = rawOptions . Value < string > ( "compilerName" ) ? ? compilerName ? ? "csc" ,
CompileInclude = GetIncludeContext (
project ,
rawOptions ,
"compile" ,
2017-01-12 11:22:41 -08:00
defaultBuiltInInclude : ProjectFilesCollection . SdkInjectedDefaultCompileBuiltInPatterns ,
2016-04-11 19:25:28 -07:00
defaultBuiltInExclude : ProjectFilesCollection . DefaultBuiltInExcludePatterns ) ,
EmbedInclude = GetIncludeContext (
project ,
rawOptions ,
"embed" ,
2017-01-12 11:22:41 -08:00
defaultBuiltInInclude : null ,
2016-04-11 19:25:28 -07:00
defaultBuiltInExclude : ProjectFilesCollection . DefaultBuiltInExcludePatterns ) ,
CopyToOutputInclude = GetIncludeContext (
project ,
rawOptions ,
"copyToOutput" ,
defaultBuiltInInclude : null ,
2017-01-12 11:22:41 -08:00
defaultBuiltInExclude : null )
2016-04-11 19:25:28 -07:00
} ;
}
private static IncludeContext GetIncludeContext (
Project project ,
JObject rawOptions ,
string option ,
string [ ] defaultBuiltInInclude ,
string [ ] defaultBuiltInExclude )
{
var contextOption = rawOptions . Value < JToken > ( option ) ;
if ( contextOption ! = null )
{
return new IncludeContext (
project . ProjectDirectory ,
option ,
rawOptions ,
defaultBuiltInInclude ,
defaultBuiltInExclude ) ;
}
return null ;
}
private static PackOptions GetPackOptions ( JObject rawProject , Project project )
{
var rawPackOptions = rawProject . Value < JToken > ( "packOptions" ) as JObject ;
// Files to be packed along with the project
IncludeContext packInclude = null ;
if ( rawPackOptions ! = null & & rawPackOptions . Value < JToken > ( "files" ) ! = null )
{
packInclude = new IncludeContext (
project . ProjectDirectory ,
"files" ,
rawPackOptions ,
defaultBuiltInInclude : null ,
defaultBuiltInExclude : ProjectFilesCollection . DefaultBuiltInExcludePatterns ) ;
}
var repository = GetPackOptionsValue < JToken > ( "repository" , rawProject , rawPackOptions , project ) as JObject ;
return new PackOptions
{
ProjectUrl = GetPackOptionsValue < string > ( "projectUrl" , rawProject , rawPackOptions , project ) ,
LicenseUrl = GetPackOptionsValue < string > ( "licenseUrl" , rawProject , rawPackOptions , project ) ,
IconUrl = GetPackOptionsValue < string > ( "iconUrl" , rawProject , rawPackOptions , project ) ,
Owners = GetPackOptionsValue < JToken > ( "owners" , rawProject , rawPackOptions , project ) ? . Values < string > ( ) . ToArray ( ) ? ? EmptyArray < string > . Value ,
Tags = GetPackOptionsValue < JToken > ( "tags" , rawProject , rawPackOptions , project ) ? . Values < string > ( ) . ToArray ( ) ? ? EmptyArray < string > . Value ,
ReleaseNotes = GetPackOptionsValue < string > ( "releaseNotes" , rawProject , rawPackOptions , project ) ,
RequireLicenseAcceptance = GetPackOptionsValue < bool > ( "requireLicenseAcceptance" , rawProject , rawPackOptions , project ) ,
Summary = GetPackOptionsValue < string > ( "summary" , rawProject , rawPackOptions , project ) ,
RepositoryType = repository ? . Value < string > ( "type" ) ,
RepositoryUrl = repository ? . Value < string > ( "url" ) ,
PackInclude = packInclude
2015-10-13 14:31:29 -07:00
} ;
}
2016-04-11 19:25:28 -07:00
private static T GetPackOptionsValue < T > (
string option ,
JObject rawProject ,
JObject rawPackOptions ,
Project project )
{
var rootValue = rawProject . Value < T > ( option ) ;
if ( rawProject . GetValue ( option ) ! = null )
{
var lineInfo = rawProject . Value < IJsonLineInfo > ( option ) ;
2016-04-30 19:09:43 -07:00
project . Diagnostics . Add (
2016-04-11 19:25:28 -07:00
new DiagnosticMessage (
ErrorCodes . DOTNET1016 ,
$"The '{option}' option in the root is deprecated. Use it in 'packOptions' instead." ,
project . ProjectFilePath ,
DiagnosticMessageSeverity . Warning ,
lineInfo . LineNumber ,
lineInfo . LinePosition ) ) ;
}
if ( rawPackOptions ! = null )
{
var packOptionValue = rawPackOptions . Value < T > ( option ) ;
if ( packOptionValue ! = null )
{
return packOptionValue ;
}
}
return rootValue ;
}
private static RuntimeOptions GetRuntimeOptions ( JObject rawProject )
{
var rawRuntimeOptions = rawProject . Value < JToken > ( "runtimeOptions" ) as JObject ;
if ( rawRuntimeOptions = = null )
{
return null ;
}
return new RuntimeOptions
{
// Value<T>(null) will return default(T) which is false in this case.
GcServer = rawRuntimeOptions . Value < bool > ( "gcServer" ) ,
GcConcurrent = rawRuntimeOptions . Value < bool > ( "gcConcurrent" )
} ;
}
private static IncludeContext GetPublishInclude ( JObject rawProject , Project project )
{
var rawPublishOptions = rawProject . Value < JToken > ( "publishOptions" ) ;
if ( rawPublishOptions ! = null )
{
return new IncludeContext (
project . ProjectDirectory ,
"publishOptions" ,
rawProject ,
defaultBuiltInInclude : null ,
2017-01-12 11:22:41 -08:00
defaultBuiltInExclude : null ) ;
2016-04-11 19:25:28 -07:00
}
return null ;
}
2015-10-17 22:42:50 -07:00
private static bool HasProjectFile ( string path )
{
string projectPath = Path . Combine ( path , Project . FileName ) ;
return File . Exists ( projectPath ) ;
}
2016-04-30 19:09:43 -07:00
private static void AddProjectFilesCollectionDiagnostics ( JObject rawProject , Project project )
{
var compileWarning = "'compile' in 'buildOptions'" ;
AddDiagnosticMesage ( rawProject , project , "compile" , compileWarning ) ;
AddDiagnosticMesage ( rawProject , project , "compileExclude" , compileWarning ) ;
AddDiagnosticMesage ( rawProject , project , "compileFiles" , compileWarning ) ;
AddDiagnosticMesage ( rawProject , project , "compileBuiltIn" , compileWarning ) ;
var resourceWarning = "'embed' in 'buildOptions'" ;
AddDiagnosticMesage ( rawProject , project , "resource" , resourceWarning ) ;
AddDiagnosticMesage ( rawProject , project , "resourceExclude" , resourceWarning ) ;
AddDiagnosticMesage ( rawProject , project , "resourceFiles" , resourceWarning ) ;
AddDiagnosticMesage ( rawProject , project , "resourceBuiltIn" , resourceWarning ) ;
AddDiagnosticMesage ( rawProject , project , "namedResource" , resourceWarning ) ;
var contentWarning = "'publishOptions' to publish or 'copyToOutput' in 'buildOptions' to copy to build output" ;
AddDiagnosticMesage ( rawProject , project , "content" , contentWarning ) ;
AddDiagnosticMesage ( rawProject , project , "contentExclude" , contentWarning ) ;
AddDiagnosticMesage ( rawProject , project , "contentFiles" , contentWarning ) ;
AddDiagnosticMesage ( rawProject , project , "contentBuiltIn" , contentWarning ) ;
AddDiagnosticMesage ( rawProject , project , "packInclude" , "'files' in 'packOptions'" ) ;
AddDiagnosticMesage ( rawProject , project , "publishExclude" , "'publishOptions'" ) ;
AddDiagnosticMesage ( rawProject , project , "exclude" , "'exclude' within 'compile' or 'embed'" ) ;
}
private static void AddDiagnosticMesage (
JObject rawProject ,
Project project ,
string option ,
string message )
{
var lineInfo = rawProject . Value < IJsonLineInfo > ( option ) ;
if ( lineInfo = = null )
{
return ;
}
project . Diagnostics . Add (
new DiagnosticMessage (
ErrorCodes . DOTNET1015 ,
$"The '{option}' option is deprecated. Use {message} instead." ,
project . ProjectFilePath ,
DiagnosticMessageSeverity . Warning ,
lineInfo . LineNumber ,
lineInfo . LinePosition ) ) ;
}
2016-09-26 21:40:11 -07:00
private static bool TryGetStringEnumerable ( JToken token , out IEnumerable < string > result )
{
IEnumerable < string > values ;
if ( token = = null )
{
result = null ;
return false ;
}
else if ( token . Type = = JTokenType . String )
{
values = new [ ]
{
token . Value < string > ( )
} ;
}
else
{
2016-10-10 16:19:57 -07:00
values = token . Values < string > ( ) ;
2016-09-26 21:40:11 -07:00
}
result = values
. SelectMany ( value = > value . Split ( new [ ] { ' ' , ',' } , StringSplitOptions . RemoveEmptyEntries ) ) ;
return true ;
}
2015-10-13 14:31:29 -07:00
}
}