399 lines
14 KiB
C#
399 lines
14 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 Microsoft.DotNet.ProjectModel.Utilities;
|
|
using Newtonsoft.Json;
|
|
using Newtonsoft.Json.Linq;
|
|
using NuGet.Frameworks;
|
|
using NuGet.Packaging.Core;
|
|
using NuGet.Versioning;
|
|
|
|
namespace Microsoft.DotNet.ProjectModel.Graph
|
|
{
|
|
public class LockFileReader
|
|
{
|
|
private readonly LockFileSymbolTable _symbols;
|
|
|
|
public LockFileReader() : this(new LockFileSymbolTable()) { }
|
|
|
|
public LockFileReader(LockFileSymbolTable symbols)
|
|
{
|
|
_symbols = symbols;
|
|
}
|
|
|
|
public static LockFile Read(string lockFilePath, bool designTime)
|
|
{
|
|
using (var stream = ResilientFileStreamOpener.OpenFile(lockFilePath))
|
|
{
|
|
try
|
|
{
|
|
return new LockFileReader().ReadLockFile(lockFilePath, stream, designTime);
|
|
}
|
|
catch (FileFormatException ex)
|
|
{
|
|
throw ex.WithFilePath(lockFilePath);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw FileFormatException.Create(ex, lockFilePath);
|
|
}
|
|
}
|
|
}
|
|
|
|
public LockFile ReadLockFile(string lockFilePath, Stream stream, bool designTime)
|
|
{
|
|
try
|
|
{
|
|
var reader = new StreamReader(stream);
|
|
var jobject = JObject.Load(new JsonTextReader(reader));
|
|
|
|
if (jobject == null)
|
|
{
|
|
throw new InvalidDataException();
|
|
}
|
|
|
|
var lockFile = ReadLockFile(lockFilePath, jobject);
|
|
|
|
if (!designTime)
|
|
{
|
|
var patcher = new LockFilePatcher(lockFile, this);
|
|
patcher.Patch();
|
|
}
|
|
|
|
return lockFile;
|
|
}
|
|
catch (LockFilePatchingException)
|
|
{
|
|
throw;
|
|
}
|
|
catch
|
|
{
|
|
// Ran into parsing errors, mark it as unlocked and out-of-date
|
|
return new LockFile(lockFilePath)
|
|
{
|
|
Version = int.MinValue
|
|
};
|
|
}
|
|
}
|
|
|
|
public ExportFile ReadExportFile(string fragmentLockFilePath)
|
|
{
|
|
using (var stream = ResilientFileStreamOpener.OpenFile(fragmentLockFilePath))
|
|
{
|
|
try
|
|
{
|
|
var rootJObject = JObject.ReadFrom(new JsonTextReader(new StreamReader(stream))) as JObject;
|
|
|
|
if (rootJObject == null)
|
|
{
|
|
throw new InvalidDataException();
|
|
}
|
|
|
|
var version = ReadInt(rootJObject, "version", defaultValue: int.MinValue);
|
|
var exports = ReadObject(rootJObject.Value<JObject>("exports"), ReadTargetLibrary);
|
|
|
|
return new ExportFile(fragmentLockFilePath, version, exports);
|
|
|
|
}
|
|
catch (FileFormatException ex)
|
|
{
|
|
throw ex.WithFilePath(fragmentLockFilePath);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw FileFormatException.Create(ex, fragmentLockFilePath);
|
|
}
|
|
}
|
|
}
|
|
|
|
private LockFile ReadLockFile(string lockFilePath, JObject cursor)
|
|
{
|
|
var lockFile = new LockFile(lockFilePath);
|
|
lockFile.Version = ReadInt(cursor, "version", defaultValue: int.MinValue);
|
|
lockFile.Targets = ReadObject(cursor.Value<JObject>("targets"), ReadTarget);
|
|
lockFile.ProjectFileDependencyGroups = ReadObject(cursor.Value<JObject>("projectFileDependencyGroups"), ReadProjectFileDependencyGroup);
|
|
ReadLibrary(cursor.Value<JObject>("libraries"), lockFile);
|
|
|
|
return lockFile;
|
|
}
|
|
|
|
private void ReadLibrary(JObject json, LockFile lockFile)
|
|
{
|
|
if (json == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
foreach (var child in json)
|
|
{
|
|
var key = child.Key;
|
|
var value = json.Value<JObject>(key);
|
|
if (value == null)
|
|
{
|
|
throw FileFormatException.Create("The value type is not object.", json[key]);
|
|
}
|
|
|
|
var parts = key.Split(new[] { '/' }, 2);
|
|
var name = parts[0];
|
|
var version = parts.Length == 2 ? _symbols.GetVersion(parts[1]) : null;
|
|
|
|
var type = _symbols.GetString(value.Value<string>("type"));
|
|
|
|
if (type == null || string.Equals(type, "package", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
lockFile.PackageLibraries.Add(new LockFilePackageLibrary
|
|
{
|
|
Name = name,
|
|
Version = version,
|
|
IsServiceable = ReadBool(value, "serviceable", defaultValue: false),
|
|
Sha512 = ReadString(value["sha512"]),
|
|
Files = ReadPathArray(value["files"], ReadString)
|
|
});
|
|
}
|
|
else if (type == "project")
|
|
{
|
|
var projectLibrary = new LockFileProjectLibrary
|
|
{
|
|
Name = name,
|
|
Version = version
|
|
};
|
|
|
|
var pathValue = value["path"];
|
|
projectLibrary.Path = pathValue == null ? null : ReadString(pathValue);
|
|
|
|
var buildTimeDependencyValue = value["msbuildProject"];
|
|
projectLibrary.MSBuildProject = buildTimeDependencyValue == null ? null : ReadString(buildTimeDependencyValue);
|
|
|
|
lockFile.ProjectLibraries.Add(projectLibrary);
|
|
}
|
|
}
|
|
}
|
|
|
|
private LockFileTarget ReadTarget(string property, JToken json)
|
|
{
|
|
var jobject = json as JObject;
|
|
if (jobject == null)
|
|
{
|
|
throw FileFormatException.Create("The value type is not an object.", json);
|
|
}
|
|
|
|
var target = new LockFileTarget();
|
|
var parts = property.Split(new[] { '/' }, 2);
|
|
target.TargetFramework = _symbols.GetFramework(parts[0]);
|
|
if (parts.Length == 2)
|
|
{
|
|
target.RuntimeIdentifier = _symbols.GetString(parts[1]);
|
|
}
|
|
|
|
target.Libraries = ReadObject(jobject, ReadTargetLibrary);
|
|
|
|
return target;
|
|
}
|
|
|
|
private LockFileTargetLibrary ReadTargetLibrary(string property, JToken json)
|
|
{
|
|
var jobject = json as JObject;
|
|
if (jobject == null)
|
|
{
|
|
throw FileFormatException.Create("The value type is not an object.", json);
|
|
}
|
|
|
|
var library = new LockFileTargetLibrary();
|
|
|
|
var parts = property.Split(new[] { '/' }, 2);
|
|
library.Name = _symbols.GetString(parts[0]);
|
|
if (parts.Length == 2)
|
|
{
|
|
library.Version = _symbols.GetVersion(parts[1]);
|
|
}
|
|
|
|
library.Type = _symbols.GetString(jobject.Value<string>("type"));
|
|
var framework = jobject.Value<string>("framework");
|
|
if (framework != null)
|
|
{
|
|
library.TargetFramework = _symbols.GetFramework(framework);
|
|
}
|
|
|
|
library.Dependencies = ReadObject(jobject.Value<JObject>("dependencies"), ReadPackageDependency);
|
|
library.FrameworkAssemblies = new HashSet<string>(ReadArray(jobject["frameworkAssemblies"], ReadFrameworkAssemblyReference), StringComparer.OrdinalIgnoreCase);
|
|
library.RuntimeAssemblies = ReadObject(jobject.Value<JObject>("runtime"), ReadFileItem);
|
|
library.CompileTimeAssemblies = ReadObject(jobject.Value<JObject>("compile"), ReadFileItem);
|
|
library.ResourceAssemblies = ReadObject(jobject.Value<JObject>("resource"), ReadFileItem);
|
|
library.NativeLibraries = ReadObject(jobject.Value<JObject>("native"), ReadFileItem);
|
|
library.ContentFiles = ReadObject(jobject.Value<JObject>("contentFiles"), ReadContentFile);
|
|
library.RuntimeTargets = ReadObject(jobject.Value<JObject>("runtimeTargets"), ReadRuntimeTarget);
|
|
|
|
return library;
|
|
}
|
|
|
|
private LockFileRuntimeTarget ReadRuntimeTarget(string property, JToken json)
|
|
{
|
|
var jsonObject = json as JObject;
|
|
if (jsonObject == null)
|
|
{
|
|
throw FileFormatException.Create("The value type is not an object.", json);
|
|
}
|
|
|
|
return new LockFileRuntimeTarget(
|
|
path: _symbols.GetString(property),
|
|
runtime: _symbols.GetString(jsonObject.Value<string>("rid")),
|
|
assetType: _symbols.GetString(jsonObject.Value<string>("assetType"))
|
|
);
|
|
}
|
|
|
|
private LockFileContentFile ReadContentFile(string property, JToken json)
|
|
{
|
|
var contentFile = new LockFileContentFile()
|
|
{
|
|
Path = property
|
|
};
|
|
|
|
var jsonObject = json as JObject;
|
|
if (jsonObject != null)
|
|
{
|
|
|
|
BuildAction action;
|
|
BuildAction.TryParse(jsonObject.Value<string>("buildAction"), out action);
|
|
|
|
contentFile.BuildAction = action;
|
|
var codeLanguage = _symbols.GetString(jsonObject.Value<string>("codeLanguage"));
|
|
if (codeLanguage == "any")
|
|
{
|
|
codeLanguage = null;
|
|
}
|
|
contentFile.CodeLanguage = codeLanguage;
|
|
contentFile.OutputPath = jsonObject.Value<string>("outputPath");
|
|
contentFile.PPOutputPath = jsonObject.Value<string>("ppOutputPath");
|
|
contentFile.CopyToOutput = ReadBool(jsonObject, "copyToOutput", false);
|
|
}
|
|
|
|
return contentFile;
|
|
}
|
|
|
|
private ProjectFileDependencyGroup ReadProjectFileDependencyGroup(string property, JToken json)
|
|
{
|
|
return new ProjectFileDependencyGroup(
|
|
string.IsNullOrEmpty(property) ? null : NuGetFramework.Parse(property),
|
|
ReadArray(json, ReadString));
|
|
}
|
|
|
|
private PackageDependency ReadPackageDependency(string property, JToken json)
|
|
{
|
|
var versionStr = ReadString(json);
|
|
return new PackageDependency(
|
|
_symbols.GetString(property),
|
|
versionStr == null ? null : _symbols.GetVersionRange(versionStr));
|
|
}
|
|
|
|
private LockFileItem ReadFileItem(string property, JToken json)
|
|
{
|
|
var item = new LockFileItem { Path = _symbols.GetString(PathUtility.GetPathWithDirectorySeparator(property)) };
|
|
var jobject = json as JObject;
|
|
|
|
if (jobject != null)
|
|
{
|
|
foreach (var child in jobject)
|
|
{
|
|
item.Properties[_symbols.GetString(child.Key)] = jobject.Value<string>(child.Key);
|
|
}
|
|
}
|
|
return item;
|
|
}
|
|
|
|
private string ReadFrameworkAssemblyReference(JToken json)
|
|
{
|
|
return ReadString(json);
|
|
}
|
|
|
|
private static IList<TItem> ReadArray<TItem>(JToken json, Func<JToken, TItem> readItem)
|
|
{
|
|
if (json == null)
|
|
{
|
|
return new List<TItem>();
|
|
}
|
|
|
|
var jarray = json as JArray;
|
|
if (jarray == null)
|
|
{
|
|
throw FileFormatException.Create("The value type is not array.", json);
|
|
}
|
|
|
|
var items = new List<TItem>();
|
|
for (int i = 0; i < jarray.Count; ++i)
|
|
{
|
|
items.Add(readItem(jarray[i]));
|
|
}
|
|
return items;
|
|
}
|
|
|
|
private IList<string> ReadPathArray(JToken json, Func<JToken, string> readItem)
|
|
{
|
|
return ReadArray(json, readItem).Select(f => _symbols.GetString(PathUtility.GetPathWithDirectorySeparator(f))).ToList();
|
|
}
|
|
|
|
private static IList<TItem> ReadObject<TItem>(JObject json, Func<string, JToken, TItem> readItem)
|
|
{
|
|
if (json == null)
|
|
{
|
|
return new List<TItem>();
|
|
}
|
|
var items = new List<TItem>();
|
|
foreach (var child in json)
|
|
{
|
|
items.Add(readItem(child.Key, json[child.Key]));
|
|
}
|
|
return items;
|
|
}
|
|
|
|
private static bool ReadBool(JObject cursor, string property, bool defaultValue)
|
|
{
|
|
var valueToken = cursor[property] as JValue;
|
|
if (valueToken == null || valueToken.Type != JTokenType.Boolean)
|
|
{
|
|
return defaultValue;
|
|
}
|
|
|
|
return (bool)valueToken.Value;
|
|
}
|
|
|
|
private static int ReadInt(JObject cursor, string property, int defaultValue)
|
|
{
|
|
var number = cursor[property] as JValue;
|
|
if (number == null || number.Type != JTokenType.Integer)
|
|
{
|
|
return defaultValue;
|
|
}
|
|
|
|
try
|
|
{
|
|
var resultInInt = Convert.ToInt32(number.Value);
|
|
return resultInInt;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// FormatException or OverflowException
|
|
throw FileFormatException.Create(ex, cursor);
|
|
}
|
|
}
|
|
|
|
private string ReadString(JToken json)
|
|
{
|
|
if (json.Type == JTokenType.String)
|
|
{
|
|
return _symbols.GetString(json.ToString());
|
|
}
|
|
else if (json.Type == JTokenType.Null)
|
|
{
|
|
return null;
|
|
}
|
|
else
|
|
{
|
|
throw FileFormatException.Create("The value type is not string.", json);
|
|
}
|
|
}
|
|
}
|
|
}
|