Skip to content

Commit d37c917

Browse files
Support reading package folder locations out of the lock file
The task previously was constructing the path to the user's packages location itself, which had various issues. It meant that if the restore was done with a different path, we don't know that. It also meant we didn't handle the paths specified in nuget.config. Now, if the paths are present in the lock file, we'll use that. Otherwise, we'll construct our old path as before. Any explicitly given path to the task is still used no matter what.
1 parent 4a5f280 commit d37c917

File tree

7 files changed

+183
-33
lines changed

7 files changed

+183
-33
lines changed

src/Microsoft.NuGet.Build.Tasks.Tests/Json/Json.Designer.cs

Lines changed: 28 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Microsoft.NuGet.Build.Tasks.Tests/Json/Json.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,4 +139,7 @@
139139
<data name="nativeWinMD" type="System.Resources.ResXFileRef, System.Windows.Forms">
140140
<value>nativeWinMD.json;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
141141
</data>
142+
<data name="LockFileWithWithSpecifiedPackageFolders" type="System.Resources.ResXFileRef, System.Windows.Forms">
143+
<value>lockfilewithwithspecifiedpackagefolders.json;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
144+
</data>
142145
</root>
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
{
2+
"locked": false,
3+
"version": 2,
4+
"targets": {
5+
".NETFramework,Version=v4.5": {
6+
"Newtonsoft.Json/8.0.3": {
7+
"type": "package",
8+
"compile": {
9+
"lib/net45/Newtonsoft.Json.dll": {}
10+
},
11+
"runtime": {
12+
"lib/net45/Newtonsoft.Json.dll": {}
13+
}
14+
}
15+
}
16+
},
17+
"libraries": {
18+
"Newtonsoft.Json/8.0.3": {
19+
"sha512": "KGsYQdS2zLH+H8x2cZaSI7e+YZ4SFIbyy1YJQYl6GYBWjf5o4H1A68nxyq+WTyVSOJQ4GqS/DiPE+UseUizgMg==",
20+
"type": "package",
21+
"path": "newtonsoft.json/8.0.3",
22+
"files": [
23+
"lib/net20/Newtonsoft.Json.dll",
24+
"lib/net20/Newtonsoft.Json.xml",
25+
"lib/net35/Newtonsoft.Json.dll",
26+
"lib/net35/Newtonsoft.Json.xml",
27+
"lib/net40/Newtonsoft.Json.dll",
28+
"lib/net40/Newtonsoft.Json.xml",
29+
"lib/net45/Newtonsoft.Json.dll",
30+
"lib/net45/Newtonsoft.Json.xml",
31+
"lib/portable-net40+sl5+wp80+win8+wpa81/Newtonsoft.Json.dll",
32+
"lib/portable-net40+sl5+wp80+win8+wpa81/Newtonsoft.Json.xml",
33+
"lib/portable-net45+wp80+win8+wpa81+dnxcore50/Newtonsoft.Json.dll",
34+
"lib/portable-net45+wp80+win8+wpa81+dnxcore50/Newtonsoft.Json.xml",
35+
"newtonsoft.json.8.0.3.nupkg.sha512",
36+
"newtonsoft.json.nuspec",
37+
"tools/install.ps1"
38+
]
39+
}
40+
},
41+
"projectFileDependencyGroups": {
42+
"": [
43+
"Newtonsoft.Json"
44+
],
45+
".NETFramework,Version=v4.5": []
46+
},
47+
"tools": {},
48+
"projectFileToolGroups": {},
49+
"packageFolders": {
50+
"C:\\PackageFolder\\": {}
51+
}
52+
}

src/Microsoft.NuGet.Build.Tasks.Tests/Microsoft.NuGet.Build.Tasks.Tests.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
<DependentUpon>Json.resx</DependentUpon>
5656
</Compile>
5757
<Compile Include="NuGetTestHelpers.cs" />
58+
<Compile Include="PackageFolderTests.cs" />
5859
<Compile Include="PreprocessorTests.cs" />
5960
<Compile Include="ProjectReferences\ProjectReferenceTests.cs" />
6061
<Compile Include="ProjectReferences\Resources.Designer.cs">
@@ -78,6 +79,7 @@
7879
<None Include="Json\FluentAssertions.lock.json" />
7980
<None Include="Json\FluentAssertionsAndWin10.lock.json" />
8081
<None Include="Json\nativeWinMD.json" />
82+
<None Include="Json\LockFileWithWithSpecifiedPackageFolders.json" />
8183
<None Include="Json\Win10.Edm.json" />
8284
<None Include="Json\Win10.json" />
8385
<None Include="Json\Win10.xunit.json" />

src/Microsoft.NuGet.Build.Tasks.Tests/NuGetTestHelpers.cs

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@ public static ResolvePackagesResult ResolvePackagesWithJsonFileContents(
2626
TryGetRuntimeVersion tryGetRuntimeVersion = null,
2727
bool includeFrameworkReferences = true,
2828
string projectJsonFileContents = null,
29-
IEnumerable<ITaskItem> projectReferencesCreatingPackages = null)
29+
IEnumerable<ITaskItem> projectReferencesCreatingPackages = null,
30+
bool createTemporaryFolderForPackages = true)
3031
{
3132
var rootDirectory = new TempRoot();
3233
using (rootDirectory)
3334
{
3435
var projectDirectory = rootDirectory.CreateDirectory();
35-
var packagesDirectory = rootDirectory.CreateDirectory();
3636

3737
var projectLockJsonFile = projectDirectory.CreateFile("project.lock.json");
3838
projectLockJsonFile.WriteAllText(projectLockJsonFileContents);
@@ -43,21 +43,31 @@ public static ResolvePackagesResult ResolvePackagesWithJsonFileContents(
4343
projectJsonFile.WriteAllText(projectJsonFileContents);
4444
}
4545

46-
var filesInPackages = new HashSet<string>(
47-
GetFakeFileNamesFromPackages(projectLockJsonFileContents, packagesDirectory.Path),
48-
StringComparer.OrdinalIgnoreCase);
46+
var filesInPackages = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
47+
DisposableDirectory packagesDirectory = null;
48+
49+
if (createTemporaryFolderForPackages)
50+
{
51+
packagesDirectory = rootDirectory.CreateDirectory();
52+
53+
foreach (var fileInPackage in GetFakeFileNamesFromPackages(projectLockJsonFileContents, packagesDirectory.Path))
54+
{
55+
filesInPackages.Add(fileInPackage);
56+
}
57+
}
4958

5059
// Don't require the packages be restored on the machine
51-
DirectoryExists directoryExists = path => path.StartsWith(packagesDirectory.Path) || Directory.Exists(path);
60+
ResolveNuGetPackageAssets task = null;
61+
DirectoryExists directoryExists = path => task.GetPackageFolders().Any(l => path.StartsWith(l)) || Directory.Exists(path);
5262
FileExists fileExists = path => filesInPackages.Contains(path) || File.Exists(path);
5363

54-
ResolveNuGetPackageAssets task = new ResolveNuGetPackageAssets(directoryExists, fileExists, tryGetRuntimeVersion);
64+
task = new ResolveNuGetPackageAssets(directoryExists, fileExists, tryGetRuntimeVersion);
5565
var sw = new StringWriter();
5666
task.BuildEngine = new MockBuildEngine(sw);
5767

5868
task.AllowFallbackOnTargetSelection = allowFallbackOnTargetSelection;
5969
task.IncludeFrameworkReferences = includeFrameworkReferences;
60-
task.NuGetPackagesDirectory = packagesDirectory.Path;
70+
task.NuGetPackagesDirectory = packagesDirectory?.Path;
6171
task.RuntimeIdentifier = runtimeIdentifier;
6272
task.ProjectReferencesCreatingPackages = (projectReferencesCreatingPackages ?? Enumerable.Empty<ITaskItem>()).ToArray();
6373
task.ProjectLockFile = projectLockJsonFile.Path;
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
using Xunit;
7+
8+
namespace Microsoft.NuGet.Build.Tasks.Tests
9+
{
10+
public class PackageFolderTests
11+
{
12+
[Fact]
13+
public void ResolveWithLockFileWithPackageFolders()
14+
{
15+
var result = NuGetTestHelpers.ResolvePackagesWithJsonFileContents(
16+
Json.Json.LockFileWithWithSpecifiedPackageFolders,
17+
".NETFramework,Version=v4.5",
18+
runtimeIdentifier: null,
19+
createTemporaryFolderForPackages: false);
20+
21+
Assert.Equal(@"C:\PackageFolder\Newtonsoft.Json\8.0.3\lib\net45\Newtonsoft.Json.dll", result.References.Single().ItemSpec);
22+
}
23+
}
24+
}

src/Microsoft.NuGet.Build.Tasks/ResolveNuGetPackageAssets.cs

Lines changed: 56 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ public sealed class ResolveNuGetPackageAssets : Task
4444
private readonly List<ITaskItem> _contentItems = new List<ITaskItem>();
4545
private readonly List<ITaskItem> _fileWrites = new List<ITaskItem>();
4646

47+
private readonly List<string> _packageFolders = new List<string>();
48+
4749
private readonly Dictionary<string, string> _projectReferencesToOutputBasePaths = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
4850

4951
#region UnitTestSupport
@@ -72,6 +74,13 @@ internal ResolveNuGetPackageAssets(DirectoryExists directoryExists, FileExists f
7274

7375
_reportExceptionsToMSBuildLogger = false;
7476
}
77+
78+
// For unit testing.
79+
internal IEnumerable<string> GetPackageFolders()
80+
{
81+
return _packageFolders;
82+
}
83+
7584
#endregion
7685

7786
/// <summary>
@@ -233,6 +242,8 @@ private void ExecuteCore()
233242
lockFile = JObject.Load(new JsonTextReader(streamReader));
234243
}
235244

245+
PopulatePackageFolders(lockFile);
246+
236247
PopulateProjectReferenceMaps();
237248
GetReferences(lockFile);
238249
GetCopyLocalItems(lockFile);
@@ -241,6 +252,41 @@ private void ExecuteCore()
241252
ProduceContentAssets(lockFile);
242253
}
243254

255+
private void PopulatePackageFolders(JObject lockFile)
256+
{
257+
// If we explicitly were given a path, let's use that
258+
if (!string.IsNullOrEmpty(NuGetPackagesDirectory))
259+
{
260+
_packageFolders.Add(NuGetPackagesDirectory);
261+
}
262+
263+
// Newer versions of NuGet can now specify the final list of locations in the lock file
264+
var packageFolders = lockFile["packageFolders"] as JObject;
265+
266+
if (packageFolders != null)
267+
{
268+
foreach (var packageFolder in packageFolders.Properties())
269+
{
270+
_packageFolders.Add(packageFolder.Name);
271+
}
272+
}
273+
274+
// If we didn't have any folders, let's fall back to the environment variable or user profile
275+
if (_packageFolders.Count == 0)
276+
{
277+
string packagesFolder = Environment.GetEnvironmentVariable("NUGET_PACKAGES");
278+
279+
if (!string.IsNullOrEmpty(packagesFolder))
280+
{
281+
_packageFolders.Add(packagesFolder);
282+
}
283+
else
284+
{
285+
_packageFolders.Add(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".nuget", "packages"));
286+
}
287+
}
288+
}
289+
244290
private void PopulateProjectReferenceMaps()
245291
{
246292
foreach (var projectReference in ProjectReferencesCreatingPackages ?? new ITaskItem[] { })
@@ -579,7 +625,7 @@ private static string GetNuGetLanguageName(string projectLanguage)
579625
private void ProduceContentAsset(NuGetPackageObject package, JProperty sharedAsset, IReadOnlyDictionary<string, string> preprocessorValues, string preprocessedOutputDirectory)
580626
{
581627
string pathToFinalAsset = package.GetFullPathToFile(sharedAsset.Name);
582-
628+
583629
if (sharedAsset.Value["ppOutputPath"] != null)
584630
{
585631
if (preprocessedOutputDirectory == null)
@@ -775,7 +821,7 @@ private IEnumerable<ITaskItem> CreateItems(NuGetPackageObject package, string ke
775821
{
776822
targetPath = Path.Combine(culture, Path.GetFileName(file.Name));
777823
}
778-
824+
779825
var item = CreateItem(package, package.GetFullPathToFile(file.Name), targetPath);
780826

781827
item.SetMetadata("Private", "false");
@@ -844,32 +890,17 @@ private void GetReferencedPackages(JObject lockFile)
844890

845891
private string GetNuGetPackagePath(string packageId, string packageVersion)
846892
{
847-
string packagesFolder = GetNuGetPackagesPath();
848-
string packagePath = Path.Combine(packagesFolder, packageId, packageVersion);
849-
850-
if (!_directoryExists(packagePath))
893+
foreach (var packagesFolder in _packageFolders)
851894
{
852-
throw new ExceptionFromResource(nameof(Strings.PackageFolderNotFound), packageId, packageVersion, packagesFolder);
853-
}
854-
855-
return packagePath;
856-
}
895+
string packagePath = Path.Combine(packagesFolder, packageId, packageVersion);
857896

858-
private string GetNuGetPackagesPath()
859-
{
860-
if (!string.IsNullOrEmpty(NuGetPackagesDirectory))
861-
{
862-
return NuGetPackagesDirectory;
863-
}
864-
865-
string packagesFolder = Environment.GetEnvironmentVariable("NUGET_PACKAGES");
866-
867-
if (!string.IsNullOrEmpty(packagesFolder))
868-
{
869-
return packagesFolder;
897+
if (_directoryExists(packagePath))
898+
{
899+
return packagePath;
900+
}
870901
}
871902

872-
return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".nuget", "packages");
903+
throw new ExceptionFromResource(nameof(Strings.PackageFolderNotFound), packageId, packageVersion, string.Join(", ", _packageFolders));
873904
}
874905

875906
private IEnumerable<NuGetPackageObject> GetPackagesFromTarget(JObject lockFile, JObject target)
@@ -915,7 +946,7 @@ private IEnumerable<NuGetPackageObject> GetPackagesFromTarget(JObject lockFile,
915946
};
916947
}
917948
else
918-
{
949+
{
919950
fullPackagePathGenerator = () => GetNuGetPackagePath(id, version);
920951
}
921952

0 commit comments

Comments
 (0)