File: BuildTasksUtility.cs
Web Access
Project: src\src\nuget-client\src\NuGet.Core\NuGet.Build.Tasks\NuGet.Build.Tasks.csproj (NuGet.Build.Tasks)
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

#nullable disable

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Build.Framework;
using NuGet.Commands;
using NuGet.Common;
using NuGet.Configuration;
using NuGet.Credentials;
using NuGet.Packaging.Signing;
using NuGet.ProjectModel;
using NuGet.Protocol;
using NuGet.Protocol.Core.Types;

#if IS_DESKTOP
using System.Collections.Concurrent;
using System.Xml;
using System.Xml.Linq;
using NuGet.Packaging;
using NuGet.Packaging.PackageExtraction;
using NuGet.PackageManagement;
using NuGet.ProjectManagement;
using NuGet.Shared;
using static NuGet.Shared.XmlUtility;
using System.Globalization;
#endif

namespace NuGet.Build.Tasks
{
    public static class BuildTasksUtility
    {
        /// <summary>
        /// Add all restorable projects to the restore list.
        /// This is the behavior for --recursive
        /// </summary>
        public static void AddAllProjectsForRestore(DependencyGraphSpec spec)
        {
            // Add everything from projects except for packages.config and unknown project types
            foreach (var project in spec.Projects.Where(DoesProjectSupportRestore))
            {
                spec.AddRestore(project.RestoreMetadata.ProjectUniqueName);
            }
        }

        public static void CopyPropertyIfExists(ITaskItem item, IDictionary<string, string> properties, string key)
        {
            CopyPropertyIfExists(item, properties, key, key);
        }

        public static void CopyPropertyIfExists(ITaskItem item, IDictionary<string, string> properties, string key, string toKey)
        {
            var wrapper = new MSBuildTaskItem(item);

            var propertyValue = wrapper.GetProperty(key);

            if (!string.IsNullOrEmpty(propertyValue)
                && !properties.ContainsKey(key))
            {
                properties.Add(toKey, propertyValue);
            }
        }

        /// <summary>
        /// Determines if the specified <see cref="PackageSpec" /> supports restore.
        /// </summary>
        /// <param name="packageSpec">A <see cref="PackageSpec" /> for a project.</param>
        /// <returns><code>true</code> if the project supports restore, otherwise <code>false</code>.</returns>
        public static bool DoesProjectSupportRestore(PackageSpec packageSpec)
        {
            return RestorableTypes.Contains(packageSpec.RestoreMetadata.ProjectStyle);
        }

        public static string GetPropertyIfExists(ITaskItem item, string key)
        {
            var wrapper = new MSBuildTaskItem(item);

            var propertyValue = wrapper.GetProperty(key);

            if (!string.IsNullOrEmpty(propertyValue))
            {
                return propertyValue;
            }

            return null;
        }

        public static void AddPropertyIfExists(IDictionary<string, string> properties, string key, string value)
        {
            if (!string.IsNullOrEmpty(value)
                && !properties.ContainsKey(key))
            {
                properties.Add(key, value);
            }
        }

        public static void AddPropertyIfExists(IDictionary<string, string> properties, string key, string[] value)
        {
            if (value != null && !properties.ContainsKey(key))
            {
                properties.Add(key, string.Concat(value.Select(e => e + ";")));
            }
        }

        private static HashSet<ProjectStyle> RestorableTypes = new HashSet<ProjectStyle>()
        {
            ProjectStyle.DotnetCliTool,
            ProjectStyle.PackageReference,
            ProjectStyle.ProjectJson
        };

        public static Task<List<RestoreSummary>> RestoreAsync(
            DependencyGraphSpec dependencyGraphSpec,
            bool interactive,
            bool recursive,
            bool noCache,
            bool ignoreFailedSources,
            bool disableParallel,
            bool force,
            bool forceEvaluate,
            bool hideWarningsAndErrors,
            bool restorePC,
            Common.ILogger log,
            CancellationToken cancellationToken)
        {
            return RestoreAsync(dependencyGraphSpec, interactive, recursive, noCache, ignoreFailedSources, disableParallel, force, forceEvaluate, hideWarningsAndErrors, restorePC, cleanupAssetsForUnsupportedProjects: false, log, cancellationToken);
        }

        public static async Task<List<RestoreSummary>> RestoreAsync(
            DependencyGraphSpec dependencyGraphSpec,
            bool interactive,
            bool recursive,
            bool noCache,
            bool ignoreFailedSources,
            bool disableParallel,
            bool force,
            bool forceEvaluate,
            bool hideWarningsAndErrors,
            bool restorePC,
            bool cleanupAssetsForUnsupportedProjects,
            Common.ILogger log,
            CancellationToken cancellationToken)
        {
            if (dependencyGraphSpec == null)
            {
                throw new ArgumentNullException(nameof(dependencyGraphSpec));
            }

            if (log == null)
            {
                throw new ArgumentNullException(nameof(log));
            }

            try
            {
                DefaultCredentialServiceUtility.SetupDefaultCredentialService(log, !interactive);

                // Set connection limit
                NetworkProtocolUtility.SetConnectionLimit();

                // Set user agent string used for network calls
#if IS_CORECLR
                UserAgent.SetUserAgentString(new UserAgentStringBuilder("NuGet .NET Core MSBuild Task"));
#else
                // OS description is set by default on Desktop
                UserAgent.SetUserAgentString(new UserAgentStringBuilder("NuGet Desktop MSBuild Task"));
#endif

                X509TrustStore.InitializeForDotNetSdk(log);

                var restoreSummaries = new List<RestoreSummary>();
                var providerCache = new RestoreCommandProvidersCache();

#if IS_DESKTOP
                if (restorePC && dependencyGraphSpec.Projects.Any(i => i.RestoreMetadata.ProjectStyle == ProjectStyle.PackagesConfig))
                {
                    var v2RestoreResult = await PerformNuGetV2RestoreAsync(log, dependencyGraphSpec, noCache, disableParallel, interactive);
                    restoreSummaries.Add(v2RestoreResult);

                    if (restoreSummaries.Count < 1)
                    {
                        var message = string.Format(
                            CultureInfo.CurrentCulture,
                            Strings.InstallCommandNothingToInstall,
                            NuGetConstants.PackageReferenceFile
                        );

                        log.LogMinimal(message);
                    }

                    if (!v2RestoreResult.Success)
                    {
                        v2RestoreResult
                            .Errors
                            .Where(l => l.Level == LogLevel.Warning)
                            .ForEach(message =>
                            {
                                log.LogWarning(message.Message);
                            });
                    }
                }
#endif

                using (var cacheContext = new SourceCacheContext())
                {
                    cacheContext.NoCache = noCache;
                    cacheContext.IgnoreFailedSources = ignoreFailedSources;

                    // Pre-loaded request provider containing the graph file
                    var providers = new List<IPreLoadedRestoreRequestProvider>();

                    if (dependencyGraphSpec.Restore.Count > 0)
                    {
                        // Add all child projects
                        if (recursive)
                        {
                            AddAllProjectsForRestore(dependencyGraphSpec);
                        }

                        providers.Add(new DependencyGraphSpecRequestProvider(providerCache, dependencyGraphSpec));

                        var restoreContext = new RestoreArgs()
                        {
                            CacheContext = cacheContext,
                            // 'dotnet restore' fails on slow machines (https://github.com/NuGet/Home/issues/6742)
                            // The workaround is to pass the '--disable-parallel' option.
                            // We apply the workaround by default when the system has 1 cpu.
                            // This will fix restore failures on VMs with 1 CPU and containers with less or equal to 1 CPU assigned.
                            DisableParallel = Environment.ProcessorCount == 1 ? true : disableParallel,
                            Log = log,
                            MachineWideSettings = new XPlatMachineWideSetting(),
                            PreLoadedRequestProviders = providers,
                            AllowNoOp = !force,
                            HideWarningsAndErrors = hideWarningsAndErrors,
                            RestoreForceEvaluate = forceEvaluate
                        };

                        if (restoreContext.DisableParallel)
                        {
                            HttpSourceResourceProvider.Throttle = SemaphoreSlimThrottle.CreateBinarySemaphore();
                        }

                        cancellationToken.ThrowIfCancellationRequested();

                        restoreSummaries.AddRange(await RestoreRunner.RunAsync(restoreContext, cancellationToken));
                    }

                    if (cleanupAssetsForUnsupportedProjects)
                    {
                        // Restore assets are normally left on disk between restores for all projects.  This can cause a condition where a project that supports PackageReference was restored
                        // but then a user changes a branch or some other condition and now the project does not use PackageReference. Since the restore assets are left on disk, the build
                        // consumes them which can cause build errors. The code below cleans up all of the files that we write so that they are not used during build
                        Parallel.ForEach(dependencyGraphSpec.Projects.Where(i => !DoesProjectSupportRestore(i)), project =>
                        {
                            if (project.RestoreMetadata == null || string.IsNullOrWhiteSpace(project.RestoreMetadata.OutputPath) || string.IsNullOrWhiteSpace(project.RestoreMetadata.ProjectPath))
                            {
                                return;
                            }

                            // project.assets.json
                            FileUtility.Delete(Path.Combine(project.RestoreMetadata.OutputPath, LockFileFormat.AssetsFileName));

                            // project.csproj.nuget.cache
                            FileUtility.Delete(project.RestoreMetadata.CacheFilePath);

                            // project.csproj.nuget.g.props
                            FileUtility.Delete(BuildAssetsUtils.GetMSBuildFilePathForPackageReferenceStyleProject(project, BuildAssetsUtils.PropsExtension));

                            // project..csproj.nuget.g.targets
                            FileUtility.Delete(BuildAssetsUtils.GetMSBuildFilePathForPackageReferenceStyleProject(project, BuildAssetsUtils.TargetsExtension));

                            // project.csproj.nuget.dgspec.json
                            FileUtility.Delete(Path.Combine(project.RestoreMetadata.OutputPath, DependencyGraphSpec.GetDGSpecFileName(Path.GetFileName(project.RestoreMetadata.ProjectPath))));
                        });
                    }
                }

                if (restoreSummaries.Count < 1)
                {
                    log.LogMinimal(Strings.NoProjectsToRestore);
                }
                else
                {
                    RestoreSummary.Log(log, restoreSummaries);
                }
                return restoreSummaries;
            }
            finally
            {
                // The CredentialService lifetime is for the duration of the process. We should not leave a potentially unavailable logger. 
                // We need to update the delegating logger with a null instance
                // because the tear downs of the plugins and similar rely on idleness and process exit.
                DefaultCredentialServiceUtility.UpdateCredentialServiceDelegatingLogger(NullLogger.Instance);
            }
        }

        /// <summary>
        /// Try to parse the <paramref name="restoreProjectStyleProperty"/> and return the <see cref="ProjectStyle"/> value.
        /// </summary>
        /// <param name="restoreProjectStyleProperty">The value of the RestoreProjectStyle property value. It can be null.</param>
        /// <returns>The <see cref="ProjectStyle"/>. If the <paramref name="restoreProjectStyleProperty"/> is null the return vale will be null. </returns>
        public static ProjectStyle? GetProjectRestoreStyleFromProjectProperty(string restoreProjectStyleProperty)
        {
            ProjectStyle projectStyle;
            // Allow a user to override by setting RestoreProjectStyle in the project.
            if (!string.IsNullOrWhiteSpace(restoreProjectStyleProperty))
            {
                if (!Enum.TryParse(restoreProjectStyleProperty, ignoreCase: true, out projectStyle))
                {
                    projectStyle = ProjectStyle.Unknown;
                }
                return projectStyle;
            }

            return null;
        }

        /// <summary>
        /// Determines the restore style of a project.
        /// </summary>
        /// <param name="restoreProjectStyle">An optional user supplied restore style.</param>
        /// <param name="hasPackageReferenceItems">A <see cref="bool"/> indicating whether or not the project has any PackageReference items.</param>
        /// <param name="projectDirectory">The full path to the project directory.</param>
        /// <param name="projectName">The name of the project file.</param>
        /// <param name="log">An <see cref="NuGet.Common.ILogger"/> object used to log messages.</param>
        /// <returns>A <see cref="Tuple{ProjectStyle, Boolean}"/> containing the project style and a value indicating if the project is using a style that is compatible with PackageReference.
        /// If the value of <paramref name="restoreProjectStyle"/> is not empty and could not be parsed, <code>null</code> is returned.</returns>
        public static (ProjectStyle ProjectStyle, string PackagesConfigFilePath) GetProjectRestoreStyle(ProjectStyle? restoreProjectStyle, bool hasPackageReferenceItems, string projectDirectory, string projectName, Common.ILogger log)
        {
            ProjectStyle projectStyle;
            string packagesConfigFilePath = null;

            // Allow a user to override by setting RestoreProjectStyle in the project.
            if (restoreProjectStyle.HasValue)
            {
                projectStyle = restoreProjectStyle.Value;
            }
            else if (hasPackageReferenceItems)
            {
                // If any PackageReferences exist treat it as PackageReference. This has priority over project.json.
                projectStyle = ProjectStyle.PackageReference;
            }
            else if (ProjectHasPackagesConfigFile(projectDirectory, projectName, out packagesConfigFilePath))
            {
                // If this is not a PackageReference or ProjectJson project check if packages.config or packages.ProjectName.config exists
                projectStyle = ProjectStyle.PackagesConfig;
            }
            else
            {
                // This project is either a packages.config project or one that does not use NuGet at all.
                projectStyle = ProjectStyle.Unknown;
            }

            return (projectStyle, packagesConfigFilePath);
        }


        /// <summary>
        /// Determines the restore style of a project.
        /// </summary>
        /// <param name="restoreProjectStyle">An optional user supplied restore style.</param>
        /// <param name="hasPackageReferenceItems">A <see cref="bool"/> indicating whether or not the project has any PackageReference items.</param>
        /// <param name="projectDirectory">The full path to the project directory.</param>
        /// <param name="projectName">The name of the project file.</param>
        /// <param name="log">An <see cref="NuGet.Common.ILogger"/> object used to log messages.</param>
        /// <returns>A <see cref="Tuple{ProjectStyle, Boolean}"/> containing the project style and a value indicating if the project is using a style that is compatible with PackageReference.
        /// If the value of <paramref name="restoreProjectStyle"/> is not empty and could not be parsed, <code>null</code> is returned.</returns>
        public static (ProjectStyle ProjectStyle, string PackagesConfigFilePath) GetProjectRestoreStyle(string restoreProjectStyle, bool hasPackageReferenceItems, string projectDirectory, string projectName, Common.ILogger log)
        {
            return GetProjectRestoreStyle(GetProjectRestoreStyleFromProjectProperty(restoreProjectStyle), hasPackageReferenceItems, projectDirectory, projectName, log);
        }

        /// <summary>
        /// Determines if the project has a packages.config file.
        /// </summary>
        /// <param name="projectDirectory">The full path of the project directory.</param>
        /// <param name="projectName">The name of the project file.</param>
        /// <param name="packagesConfigPath">Receives the full path to the packages.config file if one exists, otherwise <code>null</code>.</param>
        /// <returns><code>true</code> if a packages.config exists next to the project, otherwise <code>false</code>.</returns>
        private static bool ProjectHasPackagesConfigFile(string projectDirectory, string projectName, out string packagesConfigPath)
        {
            if (string.IsNullOrWhiteSpace(projectDirectory))
            {
                throw new ArgumentException(Strings.Argument_Cannot_Be_Null_Or_Empty, nameof(projectDirectory));
            }

            if (string.IsNullOrWhiteSpace(projectName))
            {
                throw new ArgumentException(Strings.Argument_Cannot_Be_Null_Or_Empty, nameof(projectName));
            }

            packagesConfigPath = GetPackagesConfigFilePath(projectDirectory, projectName);

            return packagesConfigPath != null;
        }

#if IS_DESKTOP
        private static async Task<RestoreSummary> PerformNuGetV2RestoreAsync(Common.ILogger log, DependencyGraphSpec dgFile, bool noCache, bool disableParallel, bool interactive)
        {
            string globalPackageFolder = null;
            string repositoryPath = null;
            IList<PackageSource> packageSources = null;
            ISettings settings = null;

            Dictionary<PackageReference, List<string>> packageReferenceToProjects = new(PackageReferenceComparer.Instance);
            Dictionary<string, RestoreAuditProperties> restoreAuditProperties = new(PathUtility.GetStringComparerBasedOnOS());

            foreach (PackageSpec packageSpec in dgFile.Projects.Where(i => i.RestoreMetadata.ProjectStyle == ProjectStyle.PackagesConfig))
            {
                var pcRestoreMetadata = (PackagesConfigProjectRestoreMetadata)packageSpec.RestoreMetadata;
                globalPackageFolder = globalPackageFolder ?? pcRestoreMetadata.PackagesPath;
                repositoryPath = repositoryPath ?? pcRestoreMetadata.RepositoryPath;

                if (packageSources == null)
                {
                    packageSources = new List<PackageSource>();
                    if (!noCache)
                    {
                        if (!string.IsNullOrEmpty(globalPackageFolder) && Directory.Exists(globalPackageFolder))
                        {
                            packageSources.Add(new FeedTypePackageSource(globalPackageFolder, FeedType.FileSystemV3));
                        }
                    }

                    packageSources.AddRange(pcRestoreMetadata.Sources);
                }

                settings = settings ?? Settings.LoadSettingsGivenConfigPaths(pcRestoreMetadata.ConfigFilePaths);

                string packagesConfigPath = GetPackagesConfigFilePath(pcRestoreMetadata.ProjectPath);

                foreach (PackageReference packageReference in GetInstalledPackageReferences(packagesConfigPath))
                {
                    if (!packageReferenceToProjects.TryGetValue(packageReference, out List<string> value))
                    {
                        value ??= new();
                        packageReferenceToProjects.Add(packageReference, value);
                    }
                    value.Add(packageSpec.FilePath);
                }
                restoreAuditProperties.Add(packageSpec.FilePath, packageSpec.RestoreMetadata.RestoreAuditProperties);
            }

            if (string.IsNullOrEmpty(repositoryPath))
            {
                throw new InvalidOperationException(Strings.RestoreNoSolutionFound);
            }

            PackageSourceProvider packageSourceProvider = new PackageSourceProvider(settings);
            var sourceRepositoryProvider = new CachingSourceProvider(packageSourceProvider);
            var nuGetPackageManager = new NuGetPackageManager(sourceRepositoryProvider, settings, repositoryPath);

            var effectivePackageSaveMode = CalculateEffectivePackageSaveMode(settings);

            var packageSaveMode = effectivePackageSaveMode == Packaging.PackageSaveMode.None ?
                Packaging.PackageSaveMode.Defaultv2 :
                effectivePackageSaveMode;

            List<PackageRestoreData> packageRestoreData = new(packageReferenceToProjects.Count);
            bool areAnyPackagesMissing = false;
            foreach (KeyValuePair<PackageReference, List<string>> package in packageReferenceToProjects)
            {
                var exists = nuGetPackageManager.PackageExistsInPackagesFolder(package.Key.PackageIdentity, packageSaveMode);
                packageRestoreData.Add(new PackageRestoreData(package.Key, package.Value, !exists));
                areAnyPackagesMissing |= !exists;
            }

            var repositories = sourceRepositoryProvider.GetRepositories().ToList();

            if (!areAnyPackagesMissing)
            {
                using SourceCacheContext cacheContext = new();

                var auditUtility = new AuditChecker(
                    repositories,
                    cacheContext,
                    log);

                await auditUtility.CheckPackageVulnerabilitiesAsync(packageRestoreData, restoreAuditProperties, CancellationToken.None);

                return new RestoreSummary(true);
            }


            var installCount = 0;
            var failedEvents = new ConcurrentQueue<PackageRestoreFailedEventArgs>();
            var collectorLogger = new RestoreCollectorLogger(log);

            var packageRestoreContext = new PackageRestoreContext(
                nuGetPackageManager,
                packageRestoreData,
                CancellationToken.None,
                packageRestoredEvent: (sender, args) => { Interlocked.Add(ref installCount, args.Restored ? 1 : 0); },
                packageRestoreFailedEvent: (sender, args) => { failedEvents.Enqueue(args); },
                sourceRepositories: repositories,
                maxNumberOfParallelTasks: disableParallel
                    ? 1
                    : PackageManagementConstants.DefaultMaxDegreeOfParallelism,
                enableNuGetAudit: true,
                restoreAuditProperties,
                logger: collectorLogger);

            // TODO: Check require consent?
            // NOTE: This feature is currently not working at all. See https://github.com/NuGet/Home/issues/4327
            // CheckRequireConsent();

            var clientPolicyContext = ClientPolicyContext.GetClientPolicy(settings, collectorLogger);
            var projectContext = new ConsoleProjectContext(collectorLogger)
            {
                PackageExtractionContext = new PackageExtractionContext(
                    packageSaveMode,
                    PackageExtractionBehavior.XmlDocFileSaveMode,
                    clientPolicyContext,
                    collectorLogger)
            };

            if (effectivePackageSaveMode != Packaging.PackageSaveMode.None)
            {
                projectContext.PackageExtractionContext.PackageSaveMode = packageSaveMode;
            }

            using (var cacheContext = new SourceCacheContext())
            {
                cacheContext.NoCache = noCache;

                var packageSourceMapping = PackageSourceMapping.GetPackageSourceMapping(settings);

                var downloadContext = new PackageDownloadContext(cacheContext, repositoryPath, directDownload: false, packageSourceMapping)
                {
                    ClientPolicyContext = clientPolicyContext
                };

                DefaultCredentialServiceUtility.SetupDefaultCredentialService(log, !interactive);

                var result = await PackageRestoreManager.RestoreMissingPackagesAsync(
                    packageRestoreContext,
                    projectContext,
                    downloadContext);

                return new RestoreSummary(
                    result.Restored,
                    "packages.config projects",
                    settings.GetConfigFilePaths().ToArray(),
                    packageSources.Select(x => x.Source).ToArray(),
                    installCount,
                    collectorLogger.Errors.Concat(ProcessFailedEventsIntoRestoreLogs(failedEvents)).ToArray()
                );
            }
        }

        internal static PackageSaveMode CalculateEffectivePackageSaveMode(ISettings settings)
        {
            string packageSaveModeValue = string.Empty;
            PackageSaveMode effectivePackageSaveMode;
            if (string.IsNullOrEmpty(packageSaveModeValue))
            {
                packageSaveModeValue = SettingsUtility.GetConfigValue(settings, "PackageSaveMode");
            }

            if (!string.IsNullOrEmpty(packageSaveModeValue))
            {
                // The PackageSaveMode flag only determines if nuspec and nupkg are saved at the target location.
                // For install \ restore, we always extract files.
                effectivePackageSaveMode = Packaging.PackageSaveMode.Files;
                foreach (var v in packageSaveModeValue.Split(';'))
                {
                    if (v.Equals(Packaging.PackageSaveMode.Nupkg.ToString(), StringComparison.OrdinalIgnoreCase))
                    {
                        effectivePackageSaveMode |= Packaging.PackageSaveMode.Nupkg;
                    }
                    else if (v.Equals(Packaging.PackageSaveMode.Nuspec.ToString(), StringComparison.OrdinalIgnoreCase))
                    {
                        effectivePackageSaveMode |= Packaging.PackageSaveMode.Nuspec;
                    }
                    else
                    {
                        string message = string.Format(
                            CultureInfo.CurrentCulture,
                            Strings.Warning_InvalidPackageSaveMode,
                            v);

                        throw new InvalidOperationException(message);
                    }
                }
            }
            else
            {
                effectivePackageSaveMode = Packaging.PackageSaveMode.None;
            }
            return effectivePackageSaveMode;
        }


        private static IEnumerable<PackageReference> GetInstalledPackageReferences(string projectConfigFilePath)
        {
            if (File.Exists(projectConfigFilePath))
            {
                try
                {
                    XDocument xDocument = Load(projectConfigFilePath);
                    var reader = new PackagesConfigReader(xDocument);
                    return reader.GetPackages(allowDuplicatePackageIds: true);
                }
                catch (XmlException ex)
                {
                    var message = string.Format(
                        CultureInfo.CurrentCulture,
                        Strings.Error_PackagesConfigParseError,
                        projectConfigFilePath,
                        ex.Message);

                    throw new XmlException(message, ex);
                }
            }

            return Enumerable.Empty<PackageReference>();
        }

        private static IEnumerable<RestoreLogMessage> ProcessFailedEventsIntoRestoreLogs(ConcurrentQueue<PackageRestoreFailedEventArgs> failedEvents)
        {
            var result = new List<RestoreLogMessage>();

            foreach (var failedEvent in failedEvents)
            {
                if (failedEvent.Exception is SignatureException signatureException)
                {
                    if (signatureException.Results != null)
                    {
                        IEnumerable<RestoreLogMessage> errorsAndWarnings = signatureException.Results
                            .SelectMany(r => r.Issues)
                            .Where(i => i.Level == LogLevel.Error || i.Level == LogLevel.Warning)
                            .Select(i => i.AsRestoreLogMessage());

                        result.AddRange(errorsAndWarnings);
                    }
                }
                else
                {
                    result.Add(new RestoreLogMessage(LogLevel.Error, NuGetLogCode.Undefined, failedEvent.Exception.Message));
                }
            }

            return result;
        }
#endif

        public static string[] GetSources(string startupDirectory, string projectDirectory, string[] sources, string[] sourcesOverride, IEnumerable<string> additionalProjectSources, ISettings settings)
        {
            // Sources
            var currentSources = RestoreSettingsUtils.GetValue(
                () => sourcesOverride?.Select(MSBuildRestoreUtility.FixSourcePath).Select(e => UriUtility.GetAbsolutePath(startupDirectory, e)).ToArray(),
                () => MSBuildRestoreUtility.ContainsClearKeyword(sources) ? Array.Empty<string>() : null,
                () => sources?.Select(MSBuildRestoreUtility.FixSourcePath).Select(e => UriUtility.GetAbsolutePath(projectDirectory, e)).ToArray(),
                () => (PackageSourceProvider.LoadPackageSources(settings)).Where(e => e.IsEnabled).Select(e => e.Source).ToArray());

            // Append additional sources
            // Escape strings to avoid xplat path issues with msbuild.
            var filteredAdditionalProjectSources = MSBuildRestoreUtility.AggregateSources(
                    values: additionalProjectSources,
                    excludeValues: Enumerable.Empty<string>())
                .Select(MSBuildRestoreUtility.FixSourcePath);

            return AppendItems(projectDirectory, currentSources, filteredAdditionalProjectSources);
        }

        /// <summary>
        /// Gets the package fallback folders for a project.
        /// </summary>
        /// <param name="startupDirectory">The start-up directory of the tool.</param>
        /// <param name="projectDirectory">The full path to the directory of the project.</param>
        /// <param name="fallbackFolders">A <see cref="T:string[]" /> containing the fallback folders for the project.</param>
        /// <param name="fallbackFoldersOverride">A <see cref="T:string[]" /> containing overrides for the fallback folders for the project.</param>
        /// <param name="additionalProjectFallbackFolders">An <see cref="IEnumerable{String}" /> containing additional fallback folders for the project.</param>
        /// <param name="additionalProjectFallbackFoldersExcludes">An <see cref="IEnumerable{String}" /> containing fallback folders to exclude.</param>
        /// <param name="settings">An <see cref="ISettings" /> object containing settings for the project.</param>
        /// <returns>A <see cref="T:string[]" /> containing the package fallback folders for the project.</returns>
        public static string[] GetFallbackFolders(string startupDirectory, string projectDirectory, string[] fallbackFolders, string[] fallbackFoldersOverride, IEnumerable<string> additionalProjectFallbackFolders, IEnumerable<string> additionalProjectFallbackFoldersExcludes, ISettings settings)
        {
            // Fallback folders
            var currentFallbackFolders = RestoreSettingsUtils.GetValue(
                () => fallbackFoldersOverride?.Select(e => UriUtility.GetAbsolutePath(startupDirectory, e)).ToArray(),
                () => MSBuildRestoreUtility.ContainsClearKeyword(fallbackFolders) ? Array.Empty<string>() : null,
                () => fallbackFolders?.Select(e => UriUtility.GetAbsolutePath(projectDirectory, e)).ToArray(),
                () => SettingsUtility.GetFallbackPackageFolders(settings).ToArray());

            // Append additional fallback folders after removing excluded folders
            var filteredAdditionalProjectFallbackFolders = MSBuildRestoreUtility.AggregateSources(
                    values: additionalProjectFallbackFolders,
                    excludeValues: additionalProjectFallbackFoldersExcludes);

            return AppendItems(projectDirectory, currentFallbackFolders, filteredAdditionalProjectFallbackFolders);
        }

        private static string[] AppendItems(string projectDirectory, string[] current, IEnumerable<string> additional)
        {
            if (additional == null || !additional.Any())
            {
                // noop
                return current;
            }

            var additionalAbsolute = additional.Select(e => UriUtility.GetAbsolutePath(projectDirectory, e));

            return current.Concat(additionalAbsolute).ToArray();
        }

        /// <summary>
        /// Gets the path to a packages.config for the specified project if one exists.
        /// </summary>
        /// <param name="projectFullPath">The full path to the project.</param>
        /// <returns>The path to the packages.config file if one exists, otherwise <see langword="null" />.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="projectFullPath" /> is <see langword="null" />.</exception>
        public static string GetPackagesConfigFilePath(string projectFullPath)
        {
            if (string.IsNullOrWhiteSpace(projectFullPath))
            {
                throw new ArgumentException(Strings.Argument_Cannot_Be_Null_Or_Empty, nameof(projectFullPath));
            }

            return GetPackagesConfigFilePath(Path.GetDirectoryName(projectFullPath), Path.GetFileNameWithoutExtension(projectFullPath));
        }

        /// <summary>
        /// Gets the path to a packages.config for the specified project if one exists.
        /// </summary>
        /// <param name="projectDirectory">The full path to the project directory.</param>
        /// <param name="projectName">The name of the project file.</param>
        /// <returns>The path to the packages.config file if one exists, otherwise <see langword="null" />.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="projectDirectory" /> -or- <paramref name="projectName" /> is <see langword="null" />.</exception>
        public static string GetPackagesConfigFilePath(string projectDirectory, string projectName)
        {
            if (string.IsNullOrWhiteSpace(projectDirectory))
            {
                throw new ArgumentException(Strings.Argument_Cannot_Be_Null_Or_Empty, nameof(projectDirectory));
            }

            if (string.IsNullOrWhiteSpace(projectName))
            {
                throw new ArgumentException(Strings.Argument_Cannot_Be_Null_Or_Empty, nameof(projectName));
            }

            string packagesConfigPath = Path.Combine(projectDirectory, NuGetConstants.PackageReferenceFile);

            if (File.Exists(packagesConfigPath))
            {
                return packagesConfigPath;
            }

            packagesConfigPath = Path.Combine(projectDirectory, "packages." + projectName + ".config");

            if (File.Exists(packagesConfigPath))
            {
                return packagesConfigPath;
            }

            return null;
        }

        /// <summary>
        /// Gets an integer indicating what files should be embedded in the MSBuild binary log based on the user-specified value:
        ///  - "0" or "false" = Do not embed any files
        ///  - "2" = Embed dgspec, project.assets.json, g.props, and g.targets
        /// Any other value embeds project.assets.json, g.props, and g.targets
        /// </summary>
        /// <param name="value">The user supplied value indicating what files to embed in the binary log.</param>
        /// <returns>An integer representing what to embed in the binary log.</returns>
        public static int GetFilesToEmbedInBinlogValue(string value)
        {
            return GetFilesToEmbedInBinlogValue(value, EnvironmentVariableWrapper.Instance);
        }

        internal static int GetFilesToEmbedInBinlogValue(string value, IEnvironmentVariableReader environmentVariableReader)
        {
            if (!string.Equals(environmentVariableReader.GetEnvironmentVariable("MSBUILDBINARYLOGGERENABLED"), bool.TrueString, StringComparison.OrdinalIgnoreCase))
            {
                return 0;
            }

            if (value == null)
            {
                return 1;
            }

            string trimmed = value.Trim();

            if (string.Equals(bool.FalseString, trimmed, StringComparison.OrdinalIgnoreCase) || string.Equals("0", trimmed, StringComparison.OrdinalIgnoreCase))
            {
                return 0;
            }

            if (string.Equals("2", trimmed, StringComparison.OrdinalIgnoreCase))
            {
                return 2;
            }

            return 1;
        }

    }
}