예제 #1
0
        /// <summary>
        /// Enhances references with the assemblies they bring into the compilation and their dependency hierarchy.
        /// </summary>
        public static ImmutableArray <ReferenceInfo> AddDependencyHierarchies(
            ImmutableArray <ReferenceInfo> projectReferences,
            ProjectAssetsFile projectAssets)
        {
            if (projectAssets is null ||
                projectAssets.Version != 3)
            {
                return(ImmutableArray <ReferenceInfo> .Empty);
            }

            if (projectAssets.Targets is null ||
                projectAssets.Targets.Count == 0)
            {
                return(ImmutableArray <ReferenceInfo> .Empty);
            }

            if (projectAssets.Libraries is null ||
                projectAssets.Libraries.Count == 0)
            {
                return(ImmutableArray <ReferenceInfo> .Empty);
            }

            // We keep a list of references that were automatically added by SDKs or other sources so that we can ignore them
            // since they can't be removed even if they were unused.
            var autoReferences = projectAssets.Project?.Frameworks?.Values
                                 .Where(framework => framework.Dependencies != null)
                                 .SelectMany(framework => framework.Dependencies !.Keys.Where(key => framework.Dependencies[key].AutoReferenced))
                                 .Distinct()
                                 .ToImmutableHashSet();

            autoReferences ??= ImmutableHashSet <string> .Empty;

            // Targets contain a hashmap of Libraries keyed by `{LibraryName}/{LibraryVersion}` we need to split these keys
            // and create a mapping of LibraryName to the complete library key.
            var targetLibraryKeys = projectAssets.Targets
                                    .ToImmutableDictionary(t => t.Key, t => t.Value.ToImmutableDictionary(l => l.Key.Split('/')[0], l => l.Key));

            var builtReferences = new Dictionary <string, ReferenceInfo?>();

            var references = projectReferences
                             .Select(projectReference => EnhanceReference(projectAssets, projectReference, autoReferences, targetLibraryKeys, builtReferences))
                             .WhereNotNull()
                             .ToImmutableArray();

            return(references);
        }
예제 #2
0
        private static ReferenceInfo?EnhanceReference(
            ProjectAssetsFile projectAssets,
            ReferenceInfo referenceInfo,
            ImmutableHashSet <string> autoReferences,
            ImmutableDictionary <string, ImmutableDictionary <string, string> > targetLibraryKeys,
            Dictionary <string, ReferenceInfo?> builtReferences)
        {
            var referenceName = referenceInfo.ReferenceType == ReferenceType.Project
                ? Path.GetFileNameWithoutExtension(referenceInfo.ItemSpecification)
                : referenceInfo.ItemSpecification;

            if (autoReferences.Contains(referenceName))
            {
                return(null);
            }

            var reference = BuildReference(projectAssets, referenceName, referenceInfo.TreatAsUsed, targetLibraryKeys, builtReferences);

            // Since the reference being enhanced was provided by the Project System we should always return an
            // enhanced reference with its original ItemSpecification. The project assets file typically works with
            // full paths, however project reference typically are relative. This ensures that when changes are
            // persisted back by the Project System, it will match the specification it is expecting.
            return(reference?.WithItemSpecification(referenceInfo.ItemSpecification));
        }
예제 #3
0
        private static ReferenceInfo?BuildReference(
            ProjectAssetsFile projectAssets,
            string referenceName,
            bool treatAsUsed,
            ImmutableDictionary <string, ImmutableDictionary <string, string> > targetLibraryKeys,
            Dictionary <string, ReferenceInfo?> builtReferences)
        {
            var dependencyNames       = new HashSet <string>();
            var compilationAssemblies = ImmutableArray.CreateBuilder <string>();
            var referenceType         = ReferenceType.Unknown;
            var itemSpecification     = referenceName;

            var packagesPath = projectAssets.Project?.Restore?.PackagesPath ?? string.Empty;

            RoslynDebug.AssertNotNull(projectAssets.Targets);
            RoslynDebug.AssertNotNull(projectAssets.Libraries);

            foreach (var(targetName, libraryKeys) in targetLibraryKeys)
            {
                if (!libraryKeys.TryGetValue(referenceName, out var key) ||
                    !projectAssets.Libraries.TryGetValue(key, out var library))
                {
                    continue;
                }

                var target        = projectAssets.Targets[targetName];
                var targetLibrary = target[key];

                referenceType = targetLibrary.Type switch
                {
                    "package" => ReferenceType.Package,
                    "project" => ReferenceType.Project,
                    _ => ReferenceType.Assembly
                };

                if (referenceType == ReferenceType.Project &&
                    library.Path is not null)
                {
                    // Project references are keyed by their filename but the
                    // item specification should be the path to the project file
                    // with Windows-style directory separators.
                    itemSpecification = library.Path.Replace('/', '\\');
                }

                if (targetLibrary.Dependencies != null)
                {
                    dependencyNames.AddRange(targetLibrary.Dependencies.Keys);
                }

                if (targetLibrary.Compile != null)
                {
                    foreach (var assemblyPath in targetLibrary.Compile.Keys)
                    {
                        if (!assemblyPath.EndsWith(NuGetEmptyFileName))
                        {
                            compilationAssemblies.Add(Path.GetFullPath(Path.Combine(packagesPath, library.Path ?? "", assemblyPath)));
                        }
                    }
                }
            }

            if (referenceType == ReferenceType.Unknown)
            {
                return(null);
            }

            if (referenceType == ReferenceType.Package && itemSpecification == ".NETStandard.Library")
            {
                // Referencing .NETStandard.Library brings along references to a number of common .NET libraries
                // for which the .NET SDK already brings into the compilation. This makes it very easy for
                // the parent reference to be considered transitively used.
                return(null);
            }

            if (builtReferences.TryGetValue(itemSpecification, out var builtReference))
            {
                return(builtReference);
            }

            var dependencies = dependencyNames
                               .Select(dependency => BuildReference(projectAssets, dependency, treatAsUsed: false, targetLibraryKeys, builtReferences))
                               .WhereNotNull()
                               .ToImmutableArray();

            var reference = new ReferenceInfo(referenceType, itemSpecification, treatAsUsed, compilationAssemblies.ToImmutable(), dependencies);

            builtReferences.Add(reference.ItemSpecification, reference);

            return(reference);
        }
    }