Skip to content
This repository was archived by the owner on Dec 18, 2017. It is now read-only.

Commit 8c87566

Browse files
committed
Add serviceable property to lock file
1 parent 196e111 commit 8c87566

File tree

17 files changed

+236
-7
lines changed

17 files changed

+236
-7
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
using System.Reflection;
2+
3+
[assembly: AssemblyMetadata("Serviceable", "True")]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using System;
2+
3+
namespace ServiceableLib1
4+
{
5+
public class Class1
6+
{
7+
public Class1()
8+
{
9+
10+
}
11+
}
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"version": "1.0.0",
3+
4+
"frameworks" : {
5+
"dnx451" : {}
6+
}
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
using System.Reflection;
2+
3+
[assembly: AssemblyMetadata("Serviceable", "False")]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using System;
2+
3+
namespace UnserviceableLib1
4+
{
5+
public class Class1
6+
{
7+
public Class1()
8+
{
9+
10+
}
11+
}
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"version": "1.0.0",
3+
4+
"frameworks" : {
5+
"dnx451" : {}
6+
}
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using System;
2+
3+
namespace UnserviceableLib2
4+
{
5+
public class Class1
6+
{
7+
public Class1()
8+
{
9+
10+
}
11+
}
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"version": "1.0.0",
3+
4+
"frameworks" : {
5+
"dnx451" : {}
6+
}
7+
}

src/Microsoft.Framework.PackageManager/Bundle/BundleProject.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -390,7 +390,8 @@ private void UpdateLockFile(BundleRoot root)
390390
project,
391391
package,
392392
sha512,
393-
project.GetTargetFrameworks().Select(f => f.FrameworkName)));
393+
project.GetTargetFrameworks().Select(f => f.FrameworkName),
394+
resolver));
394395
}
395396
}
396397

src/Microsoft.Framework.PackageManager/Restore/RestoreCommand.cs

+6-1
Original file line numberDiff line numberDiff line change
@@ -697,7 +697,12 @@ private void WriteLockFile(string projectLockFilePath, Runtime.Project project,
697697
}
698698

699699
var package = packageInfo.Package;
700-
var lockFileLib = LockFileUtils.CreateLockFileLibraryForProject(project, package, sha512, frameworks);
700+
var lockFileLib = LockFileUtils.CreateLockFileLibraryForProject(
701+
project,
702+
package,
703+
sha512,
704+
frameworks,
705+
new DefaultPackagePathResolver(repository.RepositoryRoot));
701706

702707
lockFile.Libraries.Add(lockFileLib);
703708
}

src/Microsoft.Framework.PackageManager/Utils/LockFileUtils.cs

+116-2
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,22 @@
33
using System.Collections.Generic;
44
using System.Linq;
55
using System.IO;
6+
using System.Reflection.Metadata;
7+
using System.Reflection.PortableExecutable;
68
using System.Runtime.Versioning;
79
using NuGet;
810
using Microsoft.Framework.Runtime.DependencyManagement;
9-
using Microsoft.Framework.Runtime;
1011

1112
namespace Microsoft.Framework.PackageManager.Utils
1213
{
1314
internal static class LockFileUtils
1415
{
15-
public static LockFileLibrary CreateLockFileLibraryForProject(Runtime.Project project, IPackage package, SHA512 sha512, IEnumerable<FrameworkName> frameworks)
16+
public static LockFileLibrary CreateLockFileLibraryForProject(
17+
Runtime.Project project,
18+
IPackage package,
19+
SHA512 sha512,
20+
IEnumerable<FrameworkName> frameworks,
21+
IPackagePathResolver resolver)
1622
{
1723
var lockFileLib = new LockFileLibrary();
1824
lockFileLib.Name = package.Id;
@@ -86,6 +92,17 @@ public static LockFileLibrary CreateLockFileLibraryForProject(Runtime.Project pr
8692
lockFileLib.FrameworkGroups.Add(group);
8793
}
8894

95+
var installPath = resolver.GetInstallPath(package.Id, package.Version);
96+
foreach (var assembly in lockFileLib.FrameworkGroups.SelectMany(f => f.RuntimeAssemblies))
97+
{
98+
var assemblyPath = Path.Combine(installPath, assembly);
99+
if (IsAssemblyServiceable(assemblyPath))
100+
{
101+
lockFileLib.IsServiceable = true;
102+
break;
103+
}
104+
}
105+
89106
return lockFileLib;
90107
}
91108

@@ -129,5 +146,102 @@ private static List<string> GetPackageAssemblies(IPackage package, FrameworkName
129146
return results;
130147
}
131148

149+
internal static bool IsAssemblyServiceable(string assemblyPath)
150+
{
151+
if (!File.Exists(assemblyPath))
152+
{
153+
return false;
154+
}
155+
156+
using (var stream = File.OpenRead(assemblyPath))
157+
using (var peReader = new PEReader(stream))
158+
{
159+
var mdReader = peReader.GetMetadataReader();
160+
var attrs = mdReader.GetAssemblyDefinition().GetCustomAttributes()
161+
.Select(ah => mdReader.GetCustomAttribute(ah));
162+
163+
foreach (var attr in attrs)
164+
{
165+
var ctorHandle = attr.Constructor;
166+
if (ctorHandle.Kind != HandleKind.MemberReference)
167+
{
168+
continue;
169+
}
170+
171+
var container = mdReader.GetMemberReference((MemberReferenceHandle)ctorHandle).Parent;
172+
var name = mdReader.GetTypeReference((TypeReferenceHandle)container).Name;
173+
if (!string.Equals(mdReader.GetString(name), "AssemblyMetadataAttribute"))
174+
{
175+
continue;
176+
}
177+
178+
var arguments = GetFixedStringArguments(mdReader, attr);
179+
if (arguments.Count == 2 &&
180+
string.Equals(arguments[0], "Serviceable", StringComparison.OrdinalIgnoreCase) &&
181+
string.Equals(arguments[1], "True", StringComparison.OrdinalIgnoreCase))
182+
{
183+
return true;
184+
}
185+
}
186+
}
187+
188+
return false;
189+
}
190+
191+
/// <summary>
192+
/// Gets the fixed (required) string arguments of a custom attribute.
193+
/// Only attributes that have only fixed string arguments.
194+
/// </summary>
195+
private static List<string> GetFixedStringArguments(MetadataReader reader, CustomAttribute attribute)
196+
{
197+
// TODO: Nick Guerrera ([email protected]) hacked this method for temporary use.
198+
// There is a blob decoder feature in progress but it won't ship in time for our milestone.
199+
// Replace this method with the blob decoder feature when later it is availale.
200+
201+
var signature = reader.GetMemberReference((MemberReferenceHandle)attribute.Constructor).Signature;
202+
var signatureReader = reader.GetBlobReader(signature);
203+
var valueReader = reader.GetBlobReader(attribute.Value);
204+
var arguments = new List<string>();
205+
206+
var prolog = valueReader.ReadUInt16();
207+
if (prolog != 1)
208+
{
209+
// Invalid custom attribute prolog
210+
return arguments;
211+
}
212+
213+
var header = signatureReader.ReadSignatureHeader();
214+
if (header.Kind != SignatureKind.Method || header.IsGeneric)
215+
{
216+
// Invalid custom attribute constructor signature
217+
return arguments;
218+
}
219+
220+
int parameterCount;
221+
if (!signatureReader.TryReadCompressedInteger(out parameterCount))
222+
{
223+
// Invalid custom attribute constructor signature
224+
return arguments;
225+
}
226+
227+
var returnType = signatureReader.ReadSignatureTypeCode();
228+
if (returnType != SignatureTypeCode.Void)
229+
{
230+
// Invalid custom attribute constructor signature
231+
return arguments;
232+
}
233+
234+
for (int i = 0; i < parameterCount; i++)
235+
{
236+
var signatureTypeCode = signatureReader.ReadSignatureTypeCode();
237+
if (signatureTypeCode == SignatureTypeCode.String)
238+
{
239+
// Custom attribute constructor must take only strings
240+
arguments.Add(valueReader.ReadSerializedString());
241+
}
242+
}
243+
244+
return arguments;
245+
}
132246
}
133247
}

src/Microsoft.Framework.Runtime/DependencyManagement/LockFileFormat.cs

+3-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ namespace Microsoft.Framework.Runtime.DependencyManagement
1414
{
1515
public class LockFileFormat
1616
{
17-
public const int Version = -9999;
17+
public const int Version = -9998;
1818
public const string LockFileName = "project.lock.json";
1919

2020
public LockFile Read(string filePath)
@@ -108,6 +108,7 @@ private LockFileLibrary ReadLibrary(string property, JToken json)
108108
{
109109
library.Version = SemanticVersion.Parse(parts[1]);
110110
}
111+
library.IsServiceable = ReadBool(json, "serviceable", defaultValue: false);
111112
library.Sha = ReadString(json["sha"]);
112113
library.FrameworkGroups = ReadObject(json["frameworks"] as JObject, ReadFrameworkGroup);
113114
library.Files = ReadPathArray(json["files"] as JArray, ReadString);
@@ -117,6 +118,7 @@ private LockFileLibrary ReadLibrary(string property, JToken json)
117118
private JProperty WriteLibrary(LockFileLibrary library)
118119
{
119120
var json = new JObject();
121+
WriteBool(json, "serviceable", library.IsServiceable);
120122
json["sha"] = WriteString(library.Sha);
121123
WriteObject(json, "frameworks", library.FrameworkGroups, WriteFrameworkGroup);
122124
WritePathArray(json, "files", library.Files, WriteString);

src/Microsoft.Framework.Runtime/DependencyManagement/LockFileLibrary.cs

+2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ public class LockFileLibrary
1414

1515
public SemanticVersion Version { get; set; }
1616

17+
public bool IsServiceable { get; set; }
18+
1719
public string Sha { get; set; }
1820

1921
public IList<LockFileFrameworkGroup> FrameworkGroups { get; set; } = new List<LockFileFrameworkGroup>();

src/Microsoft.Framework.Runtime/Servicing/Breadcrumbs.cs

+1-2
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,7 @@ public bool IsPackageServiceable(PackageInfo package)
6363
return false;
6464
}
6565

66-
// TODO: Figure out what makes a package serviceable
67-
return false;
66+
return package.LockFileLibrary.IsServiceable;
6867
}
6968

7069
public void AddBreadcrumb(string packageId, SemanticVersion packageVersion)

test/Microsoft.Framework.PackageManager.FunctionalTests/KpmBundleTests.cs

+2
Original file line numberDiff line numberDiff line change
@@ -1088,6 +1088,7 @@ public void BundleWithNoSourceOptionGeneratesLockFileOnClr(string flavor, string
10881088
},
10891089
""libraries"": {
10901090
""NoDependencies/1.0.0"": {
1091+
""serviceable"": false,
10911092
""sha"": ""NUPKG_SHA_VALUE"",
10921093
""frameworks"": {
10931094
""DNX,Version=v4.5.1"": {
@@ -1191,6 +1192,7 @@ public void BundleWithNoSourceOptionUpdatesLockFileOnClr(string flavor, string o
11911192
},
11921193
""libraries"": {
11931194
""NoDependencies/1.0.0"": {
1195+
""serviceable"": false,
11941196
""sha"": ""NUPKG_SHA_VALUE"",
11951197
""frameworks"": {
11961198
""DNX,Version=v4.5.1"": {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System.IO;
5+
using Microsoft.Framework.Runtime;
6+
using Microsoft.Framework.FunctionalTestUtils;
7+
using Microsoft.Framework.PackageManager.Utils;
8+
using Xunit;
9+
10+
namespace Microsoft.Framework.PackageManager.Tests
11+
{
12+
public class LockFileUtilsFacts
13+
{
14+
[Theory]
15+
[InlineData("ServiceableLib1", true)]
16+
[InlineData("UnserviceableLib1", false)]
17+
[InlineData("UnserviceableLib2", false)]
18+
public void BuildPackageAndCheckServiceability(string projectName, bool expectedServiceability)
19+
{
20+
var rootDir = ProjectResolver.ResolveRootDirectory(Directory.GetCurrentDirectory());
21+
var projectDir = Path.Combine(rootDir, "misc", "ServicingTestProjects", projectName);
22+
const string configuration = "Debug";
23+
24+
using (var runtimeHomeDir = TestUtils.GetRuntimeHomeDir(flavor: "clr", os: "win", architecture: "x86"))
25+
using (var tempDir = new DisposableDir())
26+
{
27+
var buildOutpuDir = Path.Combine(tempDir, "output");
28+
29+
KpmTestUtils.ExecKpm(
30+
runtimeHomeDir,
31+
"pack",
32+
$"{projectDir} --out {buildOutpuDir} --configuration {configuration}");
33+
34+
var assemblyPath = Path.Combine(buildOutpuDir, configuration, "dnx451", $"{projectName}.dll");
35+
Assert.Equal(expectedServiceability, LockFileUtils.IsAssemblyServiceable(assemblyPath));
36+
}
37+
}
38+
}
39+
}

test/Microsoft.Framework.PackageManager.Tests/project.json

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
{
22
"dependencies": {
3+
"Microsoft.Framework.FunctionalTestUtils": "1.0.0-*",
34
"Microsoft.Framework.PackageManager": "1.0.0-*",
5+
"Microsoft.Framework.PackageManager.FunctionalTests": "1.0.0-*",
46
"Microsoft.Framework.Runtime.Common": "1.0.0-*",
57
"xunit.runner.aspnet": "2.0.0-aspnet-*"
68
},

0 commit comments

Comments
 (0)