@@ -29,6 +29,8 @@ namespace Microsoft.NET.Build.Tasks
29
29
/// </summary>
30
30
public sealed class ResolvePackageAssets : TaskBase
31
31
{
32
+ #region Input Items
33
+
32
34
/// <summary>
33
35
/// Path to assets.json.
34
36
/// </summary>
@@ -162,6 +164,10 @@ public sealed class ResolvePackageAssets : TaskBase
162
164
/// </summary>
163
165
public bool DesignTimeBuild { get ; set ; }
164
166
167
+ #endregion
168
+
169
+ #region Output Items
170
+
165
171
/// <summary>
166
172
/// Full paths to assemblies from packages to pass to compiler as analyzers.
167
173
/// </summary>
@@ -241,6 +247,13 @@ public sealed class ResolvePackageAssets : TaskBase
241
247
[ Output ]
242
248
public ITaskItem [ ] PackageDependencies { get ; private set ; }
243
249
250
+ /// <summary>
251
+ /// All the dependencies between packages. Each package has metadata 'ParentPackage'
252
+ /// to refer to the package that depends on it. For top level packages this value is blank.
253
+ /// </summary>
254
+ [ Output ]
255
+ public ITaskItem [ ] PackageDependenciesBetweenPackages { get ; private set ; }
256
+
244
257
/// <summary>
245
258
/// List of symbol files (.pdb) related to NuGet packages.
246
259
/// </summary>
@@ -259,6 +272,8 @@ public sealed class ResolvePackageAssets : TaskBase
259
272
[ Output ]
260
273
public ITaskItem [ ] ReferenceDocumentationFiles { get ; private set ; }
261
274
275
+ #endregion
276
+
262
277
/// <summary>
263
278
/// Messages from the assets file.
264
279
/// These are logged directly and therefore not returned to the targets (note private here).
@@ -345,6 +360,7 @@ private void ReadItemGroups()
345
360
NativeLibraries = reader . ReadItemGroup ( ) ;
346
361
PackageDefinitions = reader . ReadItemGroup ( ) ;
347
362
PackageDependencies = reader . ReadItemGroup ( ) ;
363
+ PackageDependenciesBetweenPackages = reader . ReadItemGroup ( ) ;
348
364
PackageFolders = reader . ReadItemGroup ( ) ;
349
365
ReferenceDocumentationFiles = reader . ReadItemGroup ( ) ;
350
366
ResourceAssemblies = reader . ReadItemGroup ( ) ;
@@ -663,6 +679,8 @@ internal sealed class CacheWriter : IDisposable
663
679
{
664
680
private const int InitialStringTableCapacity = 32 ;
665
681
682
+ private HashSet < string > _projectFileDependencies ;
683
+ private Dictionary < string , string > _targetNameToAliasMap ;
666
684
private ResolvePackageAssets _task ;
667
685
private BinaryWriter _writer ;
668
686
private LockFile _lockFile ;
@@ -693,7 +711,8 @@ public CacheWriter(ResolvePackageAssets task)
693
711
_task = task ;
694
712
_lockFile = new LockFileCache ( task ) . GetLockFile ( task . ProjectAssetsFile ) ;
695
713
_packageResolver = NuGetPackageResolver . CreateResolver ( _lockFile ) ;
696
-
714
+ _targetNameToAliasMap = CreateTargetNameToAlisMap ( ) ;
715
+ ReadProjectFileDependencies ( string . IsNullOrEmpty ( _task . TargetFramework ) || ! _targetNameToAliasMap . ContainsKey ( _task . TargetFramework ) ? null : _targetNameToAliasMap [ _task . TargetFramework ] ) ;
697
716
698
717
// If we are doing a design-time build, we do not want to fail the build if we can't find the
699
718
// target framework and/or runtime identifier in the assets file. This is because the design-time
@@ -735,8 +754,26 @@ public CacheWriter(ResolvePackageAssets task)
735
754
{
736
755
ComputePackageExclusions ( ) ;
737
756
}
757
+
758
+ void ReadProjectFileDependencies ( string frameworkAlias )
759
+ {
760
+ _projectFileDependencies = _lockFile . GetProjectFileDependencySet ( frameworkAlias ) ;
761
+ }
738
762
}
739
763
764
+ private Dictionary < string , string > CreateTargetNameToAlisMap ( ) => _lockFile . Targets . ToDictionary ( t => t . Name , t =>
765
+ {
766
+ var alias = _lockFile . GetLockFileTargetAlias ( t ) ;
767
+ if ( string . IsNullOrEmpty ( t . RuntimeIdentifier ) )
768
+ {
769
+ return alias ;
770
+ }
771
+ else
772
+ {
773
+ return alias + "/" + t . RuntimeIdentifier ;
774
+ }
775
+ } ) ;
776
+
740
777
public void WriteToCacheFile ( )
741
778
{
742
779
Directory . CreateDirectory ( Path . GetDirectoryName ( _task . ProjectAssetsCacheFile ) ) ;
@@ -817,6 +854,7 @@ private void WriteItemGroups()
817
854
WriteItemGroup ( WriteNativeLibraries ) ;
818
855
WriteItemGroup ( WritePackageDefinitions ) ;
819
856
WriteItemGroup ( WritePackageDependencies ) ;
857
+ WriteItemGroup ( WritePackageDependenciesBetweenPackages ) ;
820
858
WriteItemGroup ( WritePackageFolders ) ;
821
859
WriteItemGroup ( WriteReferenceDocumentationFiles ) ;
822
860
WriteItemGroup ( WriteResourceAssemblies ) ;
@@ -1481,6 +1519,71 @@ private void WritePackageDependencies()
1481
1519
}
1482
1520
}
1483
1521
1522
+ private void WritePackageDependenciesBetweenPackages ( )
1523
+ {
1524
+ foreach ( var target in _lockFile . Targets )
1525
+ {
1526
+ GetPackageDependencies ( target ) ;
1527
+ }
1528
+
1529
+ void GetPackageDependencies ( LockFileTarget target )
1530
+ {
1531
+ var resolvedPackageVersions = target . Libraries
1532
+ . ToDictionary ( pkg => pkg . Name , pkg => pkg . Version . ToNormalizedString ( ) , StringComparer . OrdinalIgnoreCase ) ;
1533
+
1534
+ string frameworkAlias = _targetNameToAliasMap [ target . Name ] ;
1535
+
1536
+ var transitiveProjectRefs = new HashSet < string > (
1537
+ target . Libraries
1538
+ . Where ( lib => lib . IsTransitiveProjectReference ( _lockFile , ref _projectFileDependencies , frameworkAlias ) )
1539
+ . Select ( pkg => pkg . Name ) ,
1540
+ StringComparer . OrdinalIgnoreCase ) ;
1541
+
1542
+ foreach ( var package in target . Libraries )
1543
+ {
1544
+ string packageId = $ "{ package . Name } /{ package . Version . ToNormalizedString ( ) } ";
1545
+
1546
+ if ( _projectFileDependencies . Contains ( package . Name ) )
1547
+ {
1548
+ WriteItem ( packageId ) ;
1549
+ WriteMetadata ( MetadataKeys . ParentTarget , frameworkAlias ) ; // Foreign Key
1550
+ WriteMetadata ( MetadataKeys . ParentPackage , string . Empty ) ; // Foreign Key
1551
+ }
1552
+
1553
+ // get sub package dependencies
1554
+ GetDependencies ( package , target . Name , resolvedPackageVersions , transitiveProjectRefs ) ;
1555
+ }
1556
+ }
1557
+
1558
+ void GetDependencies (
1559
+ LockFileTargetLibrary package ,
1560
+ string targetName ,
1561
+ Dictionary < string , string > resolvedPackageVersions ,
1562
+ HashSet < string > transitiveProjectRefs )
1563
+ {
1564
+ string packageId = $ "{ package . Name } /{ package . Version . ToNormalizedString ( ) } ";
1565
+ string frameworkAlias = _targetNameToAliasMap [ targetName ] ;
1566
+ foreach ( var deps in package . Dependencies )
1567
+ {
1568
+ if ( ! resolvedPackageVersions . TryGetValue ( deps . Id , out string version ) )
1569
+ {
1570
+ continue ;
1571
+ }
1572
+
1573
+ string depsName = $ "{ deps . Id } /{ version } ";
1574
+
1575
+ WriteItem ( depsName ) ;
1576
+ WriteMetadata ( MetadataKeys . ParentTarget , frameworkAlias ) ; // Foreign Key
1577
+ WriteMetadata ( MetadataKeys . ParentPackage , packageId ) ; // Foreign Key
1578
+
1579
+ if ( transitiveProjectRefs . Contains ( deps . Id ) )
1580
+ {
1581
+ WriteMetadata ( MetadataKeys . TransitiveProjectReference , "true" ) ;
1582
+ }
1583
+ }
1584
+ }
1585
+ }
1586
+
1484
1587
private void WriteResourceAssemblies ( )
1485
1588
{
1486
1589
WriteItems (
0 commit comments