Patch Lock File with export file

This commit is contained in:
Mihai Codoban 2016-03-22 21:00:45 -07:00
parent 552ac6282f
commit 06ff80392e
4 changed files with 212 additions and 11 deletions

View file

@ -0,0 +1,22 @@
using System.Collections.Generic;
using System.Linq;
namespace Microsoft.DotNet.ProjectModel.Graph
{
public class ExportFile
{
public static readonly string ExportFileName = "project.fragment.lock.json";
public int Version { get; }
public string ExportFilePath { get; }
public IList<LockFileTargetLibrary> Exports { get; }
public ExportFile(string exportFilePath, int version, IList<LockFileTargetLibrary> exports)
{
ExportFilePath = exportFilePath;
Version = version;
Exports = exports.Any() ? exports : new List<LockFileTargetLibrary>(0);
}
}
}

View file

@ -4,9 +4,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.DotNet.ProjectModel.Utilities;
using NuGet.Versioning;
namespace Microsoft.DotNet.ProjectModel.Graph
{
@ -22,6 +19,7 @@ namespace Microsoft.DotNet.ProjectModel.Graph
public IList<LockFilePackageLibrary> PackageLibraries { get; set; } = new List<LockFilePackageLibrary>();
public IList<LockFileProjectLibrary> ProjectLibraries { get; set; } = new List<LockFileProjectLibrary>();
public IList<LockFileTarget> Targets { get; set; } = new List<LockFileTarget>();
public ExportFile ExportFile { get; set; }
public LockFile(string lockFilePath)
{

View file

@ -0,0 +1,140 @@
// 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;
namespace Microsoft.DotNet.ProjectModel.Graph
{
public class LockFilePatcher
{
private readonly LockFile _lockFile;
private Dictionary<string, IList<LockFileTargetLibrary>> _msbuildTargetLibraries;
public LockFilePatcher(LockFile lockFile)
{
_lockFile = lockFile;
var msbuildProjectLibraries = lockFile.ProjectLibraries.Where(IsMSBuildProjectLibrary);
_msbuildTargetLibraries = msbuildProjectLibraries.ToDictionary(GetProjectLibraryKey, l => GetTargetsForLibrary(_lockFile, l));
}
public void PatchIfNecessary()
{
var exportFilePath = GetExportFilePath(_lockFile.LockFilePath);
if (File.Exists(exportFilePath))
{
var exportFile = LockFileReader.ReadExportFile(exportFilePath);
PatchLockWithExport(exportFile);
}
else
{
ThrowIfAnyMsbuildLibrariesPresent();
}
}
public void ThrowIfAnyMsbuildLibrariesPresent()
{
if (_msbuildTargetLibraries.Any())
{
throw new LockFilePatchingException($"Lock file {_lockFile} contains msbuild projects but there is no export file");
}
}
private void PatchLockWithExport(ExportFile exportFile)
{
if (_lockFile.Version != exportFile.Version)
{
throw new LockFilePatchingException($"Export file {exportFile.ExportFilePath} has a different version than the lock file {_lockFile.LockFilePath}");
}
var exportDict = exportFile.Exports.ToDictionary(GetTargetLibraryKey);
var uncoveredLibraries = _msbuildTargetLibraries.Keys.Except(exportDict.Keys);
if (uncoveredLibraries.Any())
{
throw new LockFilePatchingException($"Export {exportFile.ExportFilePath} does not provide exports for all the msbuild projects in {_lockFile.LockFilePath}");
}
foreach(var exportKey in exportDict.Keys)
{
var export = exportDict[exportKey];
var librariesToPatch = _msbuildTargetLibraries[exportKey];
if (export.TargetFramework == null)
{
throw new LockFilePatchingException($"Export library {export.Name} could not be resolved during nuget restore");
}
foreach (var libraryToPatch in librariesToPatch)
{
Patch(libraryToPatch, export);
}
}
_lockFile.ExportFile = exportFile;
}
private static void Patch(LockFileTargetLibrary libraryToPatch, LockFileTargetLibrary export)
{
libraryToPatch.CompileTimeAssemblies = export.CompileTimeAssemblies;
libraryToPatch.ContentFiles = export.ContentFiles;
libraryToPatch.FrameworkAssemblies = export.FrameworkAssemblies;
libraryToPatch.NativeLibraries = export.NativeLibraries;
libraryToPatch.ResourceAssemblies = export.ResourceAssemblies;
libraryToPatch.RuntimeAssemblies = export.RuntimeAssemblies;
}
private static bool IsMSBuildProjectLibrary(LockFileProjectLibrary projectLibrary)
{
var hasMSbuildProjectValue = !string.IsNullOrEmpty(projectLibrary.MSBuildProject);
var doesNotHavePathValue = string.IsNullOrEmpty(projectLibrary.Path);
return doesNotHavePathValue && hasMSbuildProjectValue;
}
private static IList<LockFileTargetLibrary> GetTargetsForLibrary(LockFile lockFile, LockFileProjectLibrary library)
{
return lockFile.Targets
.SelectMany(
t => t.Libraries
.Where(
l => string.Equals(GetProjectLibraryKey(library), (GetTargetLibraryKey(l)))
)
)
.ToList();
}
private static object TypeName(LockFileTargetLibrary library)
{
return library.Name + "/" + library.Version + "/" + library.Type;
}
private static string GetTargetLibraryKey(LockFileTargetLibrary library)
{
return library.Name + "/" + library.Version;
}
private static string GetProjectLibraryKey(LockFileProjectLibrary library)
{
return library.Name + "/" + library.Version;
}
private static string GetExportFilePath(string masterLockFilePath)
{
var parentDirectory = Directory.GetParent(masterLockFilePath).FullName;
return Path.Combine(parentDirectory, ExportFile.ExportFileName);
}
}
internal class LockFilePatchingException : Exception
{
public LockFilePatchingException(string message) : base(message)
{
}
}
}

View file

@ -15,13 +15,13 @@ namespace Microsoft.DotNet.ProjectModel.Graph
{
public static class LockFileReader
{
public static LockFile Read(string lockFilePath)
public static LockFile Read(string lockFilePath, bool patchWithExportFile = true)
{
using (var stream = ResilientFileStreamOpener.OpenFile(lockFilePath))
{
try
{
return Read(lockFilePath, stream);
return Read(lockFilePath, stream, patchWithExportFile);
}
catch (FileFormatException ex)
{
@ -34,21 +34,32 @@ namespace Microsoft.DotNet.ProjectModel.Graph
}
}
public static LockFile Read(string lockFilePath, Stream stream)
public static LockFile Read(string lockFilePath, Stream stream, bool patchWithExportFile = true)
{
try
{
var reader = new StreamReader(stream);
var jobject = JsonDeserializer.Deserialize(reader) as JsonObject;
if (jobject != null)
{
return ReadLockFile(lockFilePath, jobject);
}
else
if (jobject == null)
{
throw new InvalidDataException();
}
var lockFile = ReadLockFile(lockFilePath, jobject);
var patcher = new LockFilePatcher(lockFile);
if (patchWithExportFile)
{
patcher.PatchIfNecessary();
}
else
{
patcher.ThrowIfAnyMsbuildLibrariesPresent();
}
return lockFile;
}
catch
{
@ -60,6 +71,36 @@ namespace Microsoft.DotNet.ProjectModel.Graph
}
}
public static ExportFile ReadExportFile(string fragmentLockFilePath)
{
using (var stream = ResilientFileStreamOpener.OpenFile(fragmentLockFilePath))
{
try
{
var rootJObject = JsonDeserializer.Deserialize(new StreamReader(stream)) as JsonObject;
if (rootJObject == null)
{
throw new InvalidDataException();
}
var version = ReadInt(rootJObject, "version", defaultValue: int.MinValue);
var exports = ReadObject(rootJObject.ValueAsJsonObject("exports"), ReadTargetLibrary);
return new ExportFile(fragmentLockFilePath, version, exports);
}
catch (FileFormatException ex)
{
throw ex.WithFilePath(fragmentLockFilePath);
}
catch (Exception ex)
{
throw FileFormatException.Create(ex, fragmentLockFilePath);
}
}
}
private static LockFile ReadLockFile(string lockFilePath, JsonObject cursor)
{
var lockFile = new LockFile(lockFilePath);