Example #1
0
        /// <summary>
        /// Restore a build integrated project and update the lock file
        /// </summary>
        public static async Task <RestoreResult> RestoreAsync(
            BuildIntegratedNuGetProject project,
            ExternalProjectReferenceContext context,
            IEnumerable <SourceRepository> sources,
            string effectiveGlobalPackagesFolder,
            Action <SourceCacheContext> cacheContextModifier,
            CancellationToken token)
        {
            using (var cacheContext = new SourceCacheContext())
            {
                cacheContextModifier(cacheContext);

                var providers = RestoreCommandProviders.Create(effectiveGlobalPackagesFolder,
                                                               sources,
                                                               cacheContext,
                                                               context.Logger);

                // Restore
                var result = await RestoreAsync(
                    project,
                    project.PackageSpec,
                    context,
                    providers,
                    token);

                // Throw before writing if this has been canceled
                token.ThrowIfCancellationRequested();

                // Write out the lock file and msbuild files
                await result.CommitAsync(context.Logger, token);

                return(result);
            }
        }
 /// <summary>
 /// Retrieve the full closure of project to project references.
 /// Warnings and errors encountered will be logged.
 /// </summary>
 public virtual Task <IReadOnlyList <ExternalProjectReference> > GetProjectReferenceClosureAsync(
     ExternalProjectReferenceContext context)
 {
     // This cannot be resolved with DTE currently, it is overridden at a higher level
     return(Task.FromResult <IReadOnlyList <ExternalProjectReference> >(
                Enumerable.Empty <ExternalProjectReference>().ToList()));
 }
Example #3
0
        /// <summary>
        /// Find the list of parent projects which directly or indirectly reference the child project.
        /// </summary>
        public static async Task <IReadOnlyList <BuildIntegratedNuGetProject> > GetParentProjectsInClosure(
            ISolutionManager solutionManager,
            BuildIntegratedNuGetProject target,
            ExternalProjectReferenceContext referenceContext)
        {
            var projects = solutionManager.GetNuGetProjects().OfType <BuildIntegratedNuGetProject>().ToList();

            return(await GetParentProjectsInClosure(projects, target, referenceContext));
        }
        /// <summary>
        /// Determine if a full restore is required. For scenarios with no floating versions
        /// where all packages are already on disk and no server requests are needed
        /// we can skip the restore after some validation checks.
        /// </summary>
        private async Task <bool> IsRestoreRequired(
            List <BuildIntegratedProjectSystem> projects,
            bool forceRestore,
            ExternalProjectReferenceContext referenceContext)
        {
            try
            {
                // Swap caches
                var oldCache = _buildIntegratedCache;
                _buildIntegratedCache
                    = await BuildIntegratedRestoreUtility.CreateBuildIntegratedProjectStateCache(
                          projects,
                          referenceContext);

                if (forceRestore)
                {
                    // The cache has been updated, now skip the check since we are doing a restore anyways.
                    return(true);
                }

                if (BuildIntegratedRestoreUtility.CacheHasChanges(oldCache, _buildIntegratedCache))
                {
                    // A new project has been added
                    return(true);
                }

                var globalPackagesFolder = SettingsUtility.GetGlobalPackagesFolder(Settings);
                var pathResolver         = new VersionFolderPathResolver(globalPackagesFolder);

                var restoreRequired = await BuildIntegratedRestoreUtility.IsRestoreRequired(
                    projects,
                    pathResolver,
                    referenceContext);

                if (restoreRequired)
                {
                    // The project.json file does not match the lock file
                    return(true);
                }
            }
            catch (Exception ex)
            {
                Debug.Fail("Unable to validate lock files.");

                ActivityLog.LogError(LogEntrySource, ex.ToString());

                WriteLine(VerbosityLevel.Diagnostic, "{0}", ex.ToString());

                // If we are unable to validate, run a full restore
                // This may occur if the lock files are corrupt
                return(true);
            }

            // Validation passed, no restore is needed
            return(false);
        }
Example #5
0
        CreateBuildIntegratedProjectStateCache(
            IReadOnlyList <BuildIntegratedNuGetProject> projects,
            ExternalProjectReferenceContext context)
        {
            var cache = new Dictionary <string, BuildIntegratedProjectCacheEntry>();

            // Find all project closures
            foreach (var project in projects)
            {
                // Get all project.json file paths in the closure
                var closure = await project.GetProjectReferenceClosureAsync(context);

                var files = new HashSet <string>(StringComparer.Ordinal);

                // Store the last modified date of the project.json file
                // If there are any changes a restore is needed
                var lastModified = DateTimeOffset.MinValue;

                if (File.Exists(project.JsonConfigPath))
                {
                    lastModified = File.GetLastWriteTimeUtc(project.JsonConfigPath);
                }

                foreach (var reference in closure)
                {
                    if (!string.IsNullOrEmpty(reference.MSBuildProjectPath))
                    {
                        files.Add(reference.MSBuildProjectPath);
                    }

                    if (reference.PackageSpecPath != null)
                    {
                        files.Add(reference.PackageSpecPath);
                    }
                }

                var projectInfo = new BuildIntegratedProjectCacheEntry(
                    project.JsonConfigPath,
                    files,
                    lastModified);

                var uniqueName = project.GetMetadata <string>(NuGetProjectMetadataKeys.UniqueName);

                if (!cache.ContainsKey(uniqueName))
                {
                    cache.Add(uniqueName, projectInfo);
                }
                else
                {
                    Debug.Fail("project list contains duplicate projects");
                }
            }

            return(cache);
        }
		public MonoDevelopBuildIntegratedRestorer (
			ISourceRepositoryProvider repositoryProvider,
			ISettings settings)
		{
			sourceRepositories = repositoryProvider.GetRepositories ().ToList ();
			this.settings = settings;

			packageManagementEvents = PackageManagementServices.PackageManagementEvents;

			context = CreateRestoreContext ();
		}
Example #7
0
        public MonoDevelopBuildIntegratedRestorer(
            ISourceRepositoryProvider repositoryProvider,
            ISettings settings)
        {
            sourceRepositories = repositoryProvider.GetRepositories().ToList();
            this.settings      = settings;

            packageManagementEvents = PackageManagementServices.PackageManagementEvents;

            context = CreateRestoreContext();
        }
Example #8
0
        /// <summary>
        /// Restore without writing the lock file
        /// </summary>
        internal static async Task <RestoreResult> RestoreAsync(
            BuildIntegratedNuGetProject project,
            PackageSpec packageSpec,
            ExternalProjectReferenceContext context,
            RestoreCommandProviders providers,
            CancellationToken token)
        {
            // Restoring packages
            var logger = context.Logger;

            logger.LogMinimal(string.Format(CultureInfo.CurrentCulture,
                                            Strings.BuildIntegratedPackageRestoreStarted,
                                            project.ProjectName));

            using (var request = new RestoreRequest(packageSpec, providers, logger, disposeProviders: false))
            {
                request.MaxDegreeOfConcurrency = PackageManagementConstants.DefaultMaxDegreeOfParallelism;
                request.LockFileVersion        = await GetLockFileVersion(project, context);

                // Add the existing lock file if it exists
                var lockFilePath = ProjectJsonPathUtilities.GetLockFilePath(project.JsonConfigPath);
                request.LockFilePath     = lockFilePath;
                request.ExistingLockFile = LockFileUtilities.GetLockFile(lockFilePath, logger);

                // Find the full closure of project.json files and referenced projects
                var projectReferences = await project.GetProjectReferenceClosureAsync(context);

                request.ExternalProjects = projectReferences.ToList();

                token.ThrowIfCancellationRequested();

                var command = new RestoreCommand(request);

                // Execute the restore
                var result = await command.ExecuteAsync(token);

                // Report a final message with the Success result
                if (result.Success)
                {
                    logger.LogMinimal(string.Format(CultureInfo.CurrentCulture,
                                                    Strings.BuildIntegratedPackageRestoreSucceeded,
                                                    project.ProjectName));
                }
                else
                {
                    logger.LogMinimal(string.Format(CultureInfo.CurrentCulture,
                                                    Strings.BuildIntegratedPackageRestoreFailed,
                                                    project.ProjectName));
                }

                return(result);
            }
        }
Example #9
0
 /// <summary>
 /// Restore a build integrated project and update the lock file
 /// </summary>
 public static async Task <RestoreResult> RestoreAsync(
     BuildIntegratedNuGetProject project,
     ExternalProjectReferenceContext context,
     IEnumerable <SourceRepository> sources,
     string effectiveGlobalPackagesFolder,
     CancellationToken token)
 {
     return(await RestoreAsync(
                project,
                context,
                sources,
                effectiveGlobalPackagesFolder,
                c => { },
                token));
 }
        public MonoDevelopBuildIntegratedRestorer(
            ISourceRepositoryProvider repositoryProvider,
            ISettings settings,
            string solutionDirectory)
        {
            sourceRepositories = repositoryProvider.GetRepositories().ToList();

            packageManagementEvents = PackageManagementServices.PackageManagementEvents;

            packagesFolder = BuildIntegratedProjectUtility.GetEffectiveGlobalPackagesFolder(
                solutionDirectory,
                settings);

            context = CreateRestoreContext();
        }
Example #11
0
        /// <summary>
        /// If the project is non-xproj and has no xproj references it may fallback to v1.
        /// </summary>
        public static async Task <int> GetLockFileVersion(
            NuGetProject project,
            ExternalProjectReferenceContext referenceContext)
        {
            var lockFileVersion = LockFileFormat.Version;

            var buildProject = project as BuildIntegratedNuGetProject;

            if (buildProject != null)
            {
                var references = await buildProject.GetProjectReferenceClosureAsync(referenceContext);

                lockFileVersion = LockFileUtilities.GetLockFileVersion(references);
            }

            return(lockFileVersion);
        }
        private async Task BuildIntegratedProjectRestoreAsync(
            BuildIntegratedNuGetProject project,
            string solutionDirectory,
            List <SourceRepository> enabledSources,
            ExternalProjectReferenceContext context,
            RestoreCommandProvidersCache providerCache,
            CancellationToken token)
        {
            // Go off the UI thread to perform I/O operations
            await TaskScheduler.Default;

            var projectName = NuGetProject.GetUniqueNameOrName(project);

            var nugetPathContext = NuGetPathContext.Create(Settings);

            using (var cacheContext = new SourceCacheContext())
            {
                providerCache.GetOrCreate(
                    nugetPathContext.UserPackageFolder,
                    nugetPathContext.FallbackPackageFolders,
                    enabledSources,
                    cacheContext,
                    context.Logger);

                // Pass down the CancellationToken from the dialog
                var restoreResult = await BuildIntegratedRestoreUtility.RestoreAsync(project,
                                                                                     context,
                                                                                     enabledSources,
                                                                                     nugetPathContext.UserPackageFolder,
                                                                                     nugetPathContext.FallbackPackageFolders,
                                                                                     token);

                if (!restoreResult.Success)
                {
                    // Mark this as having errors
                    _hasErrors = true;

                    // Invalidate cached results for the project. This will cause it to restore the next time.
                    _buildIntegratedCache.Remove(projectName);
                    await BuildIntegratedProjectReportErrorAsync(projectName, restoreResult, token);
                }
            }
        }
Example #13
0
        /// <summary>
        /// Find the list of parent projects which directly or indirectly reference the child project.
        /// </summary>
        public static async Task <IReadOnlyList <BuildIntegratedNuGetProject> > GetParentProjectsInClosure(
            IReadOnlyList <BuildIntegratedNuGetProject> projects,
            BuildIntegratedNuGetProject target,
            ExternalProjectReferenceContext referenceContext)
        {
            if (projects == null)
            {
                throw new ArgumentNullException(nameof(projects));
            }

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

            var parents = new HashSet <BuildIntegratedNuGetProject>();

            var targetProjectJson = Path.GetFullPath(target.JsonConfigPath);

            foreach (var project in projects)
            {
                // do not count the target as a parent
                if (!target.Equals(project))
                {
                    var closure = await project.GetProjectReferenceClosureAsync(referenceContext);

                    // find all projects which have a child reference matching the same project.json path as the target
                    if (closure.Any(reference =>
                                    reference.PackageSpecPath != null &&
                                    string.Equals(targetProjectJson,
                                                  Path.GetFullPath(reference.PackageSpecPath),
                                                  StringComparison.OrdinalIgnoreCase)))
                    {
                        parents.Add(project);
                    }
                }
            }

            // sort parents by name to make this more deterministic during restores
            return(parents.OrderBy(parent => parent.ProjectName, StringComparer.Ordinal).ToList());
        }
Example #14
0
        /// <summary>
        /// Validate that all project.lock.json files are validate for the project.json files,
        /// and that no packages are missing.
        /// If a full restore is required this will return false.
        /// </summary>
        /// <remarks>Floating versions and project.json files with supports require a full restore.</remarks>
        public static async Task <bool> IsRestoreRequired(
            IReadOnlyList <BuildIntegratedNuGetProject> projects,
            IReadOnlyList <string> packageFolderPaths,
            ExternalProjectReferenceContext referenceContext)
        {
            var packagesChecked = new HashSet <PackageIdentity>();
            var pathResolvers   = packageFolderPaths.Select(path => new VersionFolderPathResolver(path));

            // Validate project.lock.json files
            foreach (var project in projects)
            {
                var lockFilePath = ProjectJsonPathUtilities.GetLockFilePath(project.JsonConfigPath);

                if (!File.Exists(lockFilePath))
                {
                    // If the lock file does not exist a restore is needed
                    return(true);
                }

                var lockFileFormat = new LockFileFormat();
                var lockFile       = lockFileFormat.Read(lockFilePath, referenceContext.Logger);

                var lockFileVersion = await GetLockFileVersion(project, referenceContext);

                var packageSpec = referenceContext.GetOrCreateSpec(project.ProjectName, project.JsonConfigPath);

                if (!lockFile.IsValidForPackageSpec(packageSpec, lockFileVersion))
                {
                    // The project.json file has been changed and the lock file needs to be updated.
                    return(true);
                }

                // Verify all libraries are on disk
                foreach (var library in lockFile.Libraries)
                {
                    var identity = new PackageIdentity(library.Name, library.Version);

                    // Each id/version only needs to be checked once
                    if (packagesChecked.Add(identity))
                    {
                        var found = false;

                        //  Check each package folder. These need to match the order used for restore.
                        foreach (var resolver in pathResolvers)
                        {
                            // Verify the SHA for each package
                            var hashPath = resolver.GetHashPath(library.Name, library.Version);

                            if (File.Exists(hashPath))
                            {
                                found = true;
                                var sha512 = File.ReadAllText(hashPath);

                                if (library.Sha512 != sha512)
                                {
                                    // A package has changed
                                    return(true);
                                }

                                // Skip checking the rest of the package folders
                                break;
                            }
                        }

                        if (!found)
                        {
                            // A package is missing
                            return(true);
                        }
                    }
                }
            }

            return(false);
        }
        /// <summary>
        /// Determine if a full restore is required. For scenarios with no floating versions
        /// where all packages are already on disk and no server requests are needed
        /// we can skip the restore after some validation checks.
        /// </summary>
        private async Task <bool> IsRestoreRequired(
            List <BuildIntegratedProjectSystem> projects,
            bool forceRestore,
            ExternalProjectReferenceContext referenceContext)
        {
            try
            {
                // Swap caches
                var oldCache = _buildIntegratedCache;
                _buildIntegratedCache
                    = await BuildIntegratedRestoreUtility.CreateBuildIntegratedProjectStateCache(
                          projects,
                          referenceContext);

                if (forceRestore)
                {
                    // The cache has been updated, now skip the check since we are doing a restore anyways.
                    return(true);
                }

                if (BuildIntegratedRestoreUtility.CacheHasChanges(oldCache, _buildIntegratedCache))
                {
                    // A new project has been added
                    return(true);
                }

                // Read package folder locations
                var nugetPaths         = NuGetPathContext.Create(Settings);
                var packageFolderPaths = new List <string>();

                // Folder paths must be in order of priority
                packageFolderPaths.Add(nugetPaths.UserPackageFolder);
                packageFolderPaths.AddRange(nugetPaths.FallbackPackageFolders);

                // Verify all packages exist and have the expected hashes
                var restoreRequired = await BuildIntegratedRestoreUtility.IsRestoreRequired(
                    projects,
                    packageFolderPaths,
                    referenceContext);

                if (restoreRequired)
                {
                    // The project.json file does not match the lock file
                    return(true);
                }
            }
            catch (Exception ex)
            {
                Debug.Fail("Unable to validate lock files.");

                ActivityLog.LogError(LogEntrySource, ex.ToString());

                WriteLine(VerbosityLevel.Diagnostic, "{0}", ex.ToString());

                // If we are unable to validate, run a full restore
                // This may occur if the lock files are corrupt
                return(true);
            }

            // Validation passed, no restore is needed
            return(false);
        }
        /// <summary>
        /// Restore projects with project.json and create the lock files.
        /// </summary>
        /// <param name="buildEnabledProjects">Projects containing project.json</param>
        /// <param name="forceRestore">Force the restore to write out the lock files.
        /// This is used for rebuilds.</param>
        /// <returns></returns>
        private async Task RestoreBuildIntegratedProjectsAsync(
            string solutionDirectory,
            List <BuildIntegratedProjectSystem> buildEnabledProjects,
            bool forceRestore,
            bool isSolutionAvailable)
        {
            await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();

            if (buildEnabledProjects.Any() && IsConsentGranted(Settings))
            {
                if (!isSolutionAvailable)
                {
                    var globalPackagesFolder = SettingsUtility.GetGlobalPackagesFolder(Settings);
                    if (!Path.IsPathRooted(globalPackagesFolder))
                    {
                        var message = string.Format(
                            CultureInfo.CurrentCulture,
                            NuGet.PackageManagement.VisualStudio.Strings.RelativeGlobalPackagesFolder,
                            globalPackagesFolder);

                        WriteLine(VerbosityLevel.Quiet, message);

                        // Cannot restore packages since globalPackagesFolder is a relative path
                        // and the solution is not available
                        return;
                    }
                }

                var enabledSources = SourceRepositoryProvider.GetRepositories().ToList();

                // Cache p2ps discovered from DTE
                var referenceContext = new ExternalProjectReferenceContext(logger: this);

                // No-op all project closures are up to date and all packages exist on disk.
                if (await IsRestoreRequired(buildEnabledProjects, forceRestore, referenceContext))
                {
                    var waitDialogFactory
                        = ServiceLocator.GetGlobalService <SVsThreadedWaitDialogFactory,
                                                           IVsThreadedWaitDialogFactory>();

                    // NOTE: During restore for build integrated projects,
                    //       We might show the dialog even if there are no packages to restore
                    // When both currentStep and totalSteps are 0, we get a marquee on the dialog
                    using (var threadedWaitDialogSession = waitDialogFactory.StartWaitDialog(
                               waitCaption: Resources.DialogTitle,
                               initialProgress: new ThreadedWaitDialogProgressData(Resources.RestoringPackages,
                                                                                   string.Empty,
                                                                                   string.Empty,
                                                                                   isCancelable: true,
                                                                                   currentStep: 0,
                                                                                   totalSteps: 0)))
                    {
                        // Display the restore opt out message if it has not been shown yet
                        DisplayOptOutMessage();

                        Token = threadedWaitDialogSession.UserCancellationToken;
                        ThreadedWaitDialogProgress = threadedWaitDialogSession.Progress;

                        // Cache resources between requests
                        var providerCache = new RestoreCommandProvidersCache();
                        var tasks         = new List <Task <KeyValuePair <string, Exception> > >();
                        var maxTasks      = 4;

                        // Restore packages and create the lock file for each project
                        foreach (var project in buildEnabledProjects)
                        {
                            // Mark this as having missing packages so that we will not
                            // display a noop message in the summary
                            _hasMissingPackages    = true;
                            _displayRestoreSummary = true;

                            if (tasks.Count >= maxTasks)
                            {
                                await ProcessTask(tasks);
                            }

                            // Skip further restores if the user has clicked cancel
                            if (!Token.IsCancellationRequested)
                            {
                                var projectName = NuGetProject.GetUniqueNameOrName(project);

                                // Restore and create a project.lock.json file
                                tasks.Add(RestoreProject(projectName, async() =>
                                                         await BuildIntegratedProjectRestoreAsync(
                                                             project,
                                                             solutionDirectory,
                                                             enabledSources,
                                                             referenceContext,
                                                             providerCache,
                                                             Token)));
                            }
                        }

                        // Wait for the remaining tasks
                        while (tasks.Count > 0)
                        {
                            await ProcessTask(tasks);
                        }

                        if (Token.IsCancellationRequested)
                        {
                            _canceled = true;
                        }
                    }
                }
            }
        }
        /// <summary>
        /// Get only the direct dependencies from a project
        /// </summary>
        private DirectReferences GetDirectProjectReferences(
            EnvDTEProject project,
            string projectFileFullPath,
            ExternalProjectReferenceContext context,
            IVsEnumHierarchyItemsFactory itemsFactory,
            string rootProjectPath)
        {
            var logger = context.Logger;
            var cache  = context.Cache;

            var result = new DirectReferences();

            // Find a project.json in the project
            // This checks for files on disk to match how BuildIntegratedProjectSystem checks at creation time.
            // NuGet.exe also uses disk paths and not the project file.
            var projectName = Path.GetFileNameWithoutExtension(projectFileFullPath);

            var projectDirectory = Path.GetDirectoryName(projectFileFullPath);

            // Check for projectName.project.json and project.json
            string jsonConfigItem =
                ProjectJsonPathUtilities.GetProjectConfigPath(
                    directoryPath: projectDirectory,
                    projectName: projectName);

            var hasProjectJson = true;

            // Verify the file exists, otherwise clear it
            if (!File.Exists(jsonConfigItem))
            {
                jsonConfigItem = null;
                hasProjectJson = false;
            }

            // Verify ReferenceOutputAssembly
            var excludedProjects = GetExcludedReferences(project, itemsFactory);

            var childReferences      = new HashSet <string>(StringComparer.Ordinal);
            var hasMissingReferences = false;

            // find all references in the project
            foreach (var childReference in GetProjectReferences(project))
            {
                try
                {
                    var reference3 = childReference as Reference3;

                    if (reference3 != null && !reference3.Resolved)
                    {
                        // Skip missing references and show a warning
                        hasMissingReferences = true;
                        continue;
                    }

                    // Skip missing references
                    if (childReference.SourceProject != null)
                    {
                        if (EnvDTEProjectUtility.HasUnsupportedProjectCapability(childReference.SourceProject))
                        {
                            // Skip this shared project
                            continue;
                        }

                        var childName = EnvDTEProjectUtility.GetFullProjectPath(childReference.SourceProject);

                        // Skip projects which have ReferenceOutputAssembly=false
                        if (!string.IsNullOrEmpty(childName) &&
                            !excludedProjects.Contains(childName, StringComparer.OrdinalIgnoreCase))
                        {
                            childReferences.Add(childName);

                            result.ToProcess.Add(new DTEReference(childReference.SourceProject, childName));
                        }
                    }
                    else if (hasProjectJson)
                    {
                        // SDK references do not have a SourceProject or child references,
                        // but they can contain project.json files, and should be part of the closure
                        // SDKs are not projects, only the project.json name is checked here
                        var possibleSdkPath = childReference.Path;

                        if (!string.IsNullOrEmpty(possibleSdkPath) &&
                            !possibleSdkPath.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) &&
                            Directory.Exists(possibleSdkPath))
                        {
                            var possibleProjectJson = Path.Combine(
                                possibleSdkPath,
                                ProjectJsonPathUtilities.ProjectConfigFileName);

                            if (File.Exists(possibleProjectJson))
                            {
                                childReferences.Add(possibleProjectJson);

                                // add the sdk to the results here
                                result.Processed.Add(new ExternalProjectReference(
                                                         possibleProjectJson,
                                                         childReference.Name,
                                                         possibleProjectJson,
                                                         msbuildProjectPath: null,
                                                         projectReferences: Enumerable.Empty <string>()));
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    // Exceptions are expected in some scenarios for native projects,
                    // ignore them and show a warning
                    hasMissingReferences = true;

                    logger.LogDebug(ex.ToString());

                    Debug.Fail("Unable to find project closure: " + ex.ToString());
                }
            }

            if (hasMissingReferences)
            {
                // Log a warning message once per project
                // This warning contains only the names of the root project and the project with the
                // broken reference. Attempting to display more details on the actual reference
                // that has the problem may lead to another exception being thrown.
                var warning = string.Format(
                    CultureInfo.CurrentCulture,
                    Strings.Warning_ErrorDuringProjectClosureWalk,
                    projectName,
                    rootProjectPath);

                logger.LogWarning(warning);
            }

            // For the xproj -> xproj -> csproj scenario find all xproj-> xproj references.
            if (projectFileFullPath.EndsWith(XProjUtility.XProjExtension, StringComparison.OrdinalIgnoreCase))
            {
                // All xproj paths, these are already checked for project.json
                var xprojFiles = XProjUtility.GetProjectReferences(projectFileFullPath);

                if (xprojFiles.Count > 0)
                {
                    var pathToProject = GetPathToDTEProjectLookup(project);

                    foreach (var xProjPath in xprojFiles)
                    {
                        // Only add projects that we can find in the solution, otherwise they will
                        // end up causing failures. If this is an actual failure the resolver will
                        // fail when resolving the dependency from project.json
                        Project xProjDTE;
                        if (pathToProject.TryGetValue(xProjPath, out xProjDTE))
                        {
                            var xProjFullPath = EnvDTEProjectUtility.GetFullProjectPath(xProjDTE);

                            if (!string.IsNullOrEmpty(xProjFullPath))
                            {
                                childReferences.Add(xProjFullPath);

                                // Continue walking this project if it has not been walked already
                                result.ToProcess.Add(new DTEReference(xProjDTE, xProjFullPath));
                            }
                        }
                    }
                }
            }

            // Only set a package spec project name if a package spec exists
            var packageSpecProjectName = jsonConfigItem == null ? null : projectName;

            // Add the parent project to the results
            result.Processed.Add(new ExternalProjectReference(
                                     projectFileFullPath,
                                     packageSpecProjectName,
                                     jsonConfigItem,
                                     projectFileFullPath,
                                     childReferences));

            return(result);
        }
        /// <summary>
        /// Returns the closure of all project to project references below this project.
        /// </summary>
        /// <remarks>This uses DTE and should be called from the UI thread.</remarks>
        public override async Task <IReadOnlyList <ExternalProjectReference> > GetProjectReferenceClosureAsync(
            ExternalProjectReferenceContext context)
        {
            var logger = context.Logger;
            var cache  = context.Cache;

            // DTE calls need to be done from the main thread
            await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();

            var results = new HashSet <ExternalProjectReference>();

            // projects to walk - DFS
            var toProcess = new Stack <DTEReference>();

            // keep track of found projects to avoid duplicates
            var rootProjectPath = EnvDTEProjectUtility.GetFullProjectPath(EnvDTEProject);

            // start with the current project
            toProcess.Push(new DTEReference(EnvDTEProject, rootProjectPath));

            var itemsFactory = ServiceLocator.GetInstance <IVsEnumHierarchyItemsFactory>();
            var uniqueNames  = new HashSet <string>(StringComparer.OrdinalIgnoreCase);

            // continue walking all project references until we run out
            while (toProcess.Count > 0)
            {
                var dteReference = toProcess.Pop();

                // Find the path of the current project
                var projectFileFullPath = dteReference.Path;
                var project             = dteReference.Project;

                if (string.IsNullOrEmpty(projectFileFullPath) || !uniqueNames.Add(projectFileFullPath))
                {
                    // This has already been processed or does not exist
                    continue;
                }

                IReadOnlyList <ExternalProjectReference> cacheReferences;
                if (cache.TryGetValue(projectFileFullPath, out cacheReferences))
                {
                    // The cached value contains the entire closure, add it to the results and skip
                    // all child references.
                    results.UnionWith(cacheReferences);
                }
                else
                {
                    // Get direct references
                    var projectResult = GetDirectProjectReferences(
                        dteReference.Project,
                        projectFileFullPath,
                        context,
                        itemsFactory,
                        rootProjectPath);

                    // Add results to the closure
                    results.UnionWith(projectResult.Processed);

                    // Continue processing
                    foreach (var item in projectResult.ToProcess)
                    {
                        toProcess.Push(item);
                    }
                }
            }

            // Cache the results for this project and every child project which has not been cached
            foreach (var project in results)
            {
                if (!context.Cache.ContainsKey(project.UniqueName))
                {
                    var closure = BuildIntegratedRestoreUtility.GetExternalClosure(project.UniqueName, results);

                    context.Cache.Add(project.UniqueName, closure.ToList());
                }
            }

            return(cache[rootProjectPath]);
        }
Example #19
0
 public override Task <IReadOnlyList <ExternalProjectReference> > GetProjectReferenceClosureAsync(ExternalProjectReferenceContext context)
 {
     return(Runtime.RunInMainThread(() => {
         return GetExternalProjectReferenceClosureAsync(context);
     }));
 }
Example #20
0
        /// <summary>
        /// Returns the closure of all project to project references below this project.
        /// The code is a port of src/NuGet.Clients/PackageManagement.VisualStudio/ProjectSystems/BuildIntegratedProjectSystem.cs
        /// </summary>
        Task <IReadOnlyList <ExternalProjectReference> > GetExternalProjectReferenceClosureAsync(ExternalProjectReferenceContext context)
        {
            var logger = context.Logger;
            var cache  = context.Cache;

            var results = new HashSet <ExternalProjectReference> ();

            // projects to walk - DFS
            var toProcess = new Stack <DotNetProjectReference> ();

            // keep track of found projects to avoid duplicates
            string rootProjectPath = dotNetProject.FileName;

            // start with the current project
            toProcess.Push(new DotNetProjectReference(dotNetProject.DotNetProject, rootProjectPath));

            var uniqueNames = new HashSet <string>(StringComparer.OrdinalIgnoreCase);

            // continue walking all project references until we run out
            while (toProcess.Count > 0)
            {
                var reference = toProcess.Pop();

                // Find the path of the current project
                string projectFileFullPath = reference.Path;
                var    project             = reference.Project;

                if (string.IsNullOrEmpty(projectFileFullPath) || !uniqueNames.Add(projectFileFullPath))
                {
                    // This has already been processed or does not exist
                    continue;
                }

                IReadOnlyList <ExternalProjectReference> cacheReferences;
                if (cache.TryGetValue(projectFileFullPath, out cacheReferences))
                {
                    // The cached value contains the entire closure, add it to the results and skip
                    // all child references.
                    results.UnionWith(cacheReferences);
                }
                else
                {
                    // Get direct references
                    var projectResult = GetDirectProjectReferences(
                        reference.Project,
                        projectFileFullPath,
                        context,
                        rootProjectPath);

                    // Add results to the closure
                    results.UnionWith(projectResult.Processed);

                    // Continue processing
                    foreach (var item in projectResult.ToProcess)
                    {
                        toProcess.Push(item);
                    }
                }
            }

            // Cache the results for this project and every child project which has not been cached
            foreach (var project in results)
            {
                if (!context.Cache.ContainsKey(project.UniqueName))
                {
                    var closure = BuildIntegratedRestoreUtility.GetExternalClosure(project.UniqueName, results);

                    context.Cache.Add(project.UniqueName, closure.ToList());
                }
            }

            var result = cache[rootProjectPath];

            return(Task.FromResult(result));
        }
Example #21
0
        /// <summary>
        /// Get only the direct dependencies from a project
        /// </summary>
        private DirectReferences GetDirectProjectReferences(
            DotNetProject project,
            string projectFileFullPath,
            ExternalProjectReferenceContext context,
            string rootProjectPath)
        {
            var logger = context.Logger;
            var cache  = context.Cache;

            var result = new DirectReferences();

            // Find a project.json in the project
            // This checks for files on disk to match how BuildIntegratedProjectSystem checks at creation time.
            // NuGet.exe also uses disk paths and not the project file.
            var projectName = Path.GetFileNameWithoutExtension(projectFileFullPath);

            var projectDirectory = Path.GetDirectoryName(projectFileFullPath);

            // Check for projectName.project.json and project.json
            string jsonConfigItem =
                ProjectJsonPathUtilities.GetProjectConfigPath(
                    directoryPath: projectDirectory,
                    projectName: projectName);

            var hasProjectJson = true;

            // Verify the file exists, otherwise clear it
            if (!File.Exists(jsonConfigItem))
            {
                jsonConfigItem = null;
                hasProjectJson = false;
            }

            // Verify ReferenceOutputAssembly
            var excludedProjects = GetExcludedReferences(project);

            var childReferences      = new HashSet <string> (StringComparer.Ordinal);
            var hasMissingReferences = false;

            // find all references in the project
            foreach (var childReference in project.References)
            {
                try {
                    if (!childReference.IsValid)
                    {
                        // Skip missing references and show a warning
                        hasMissingReferences = true;
                        continue;
                    }

                    // Skip missing references
                    DotNetProject sourceProject = null;
                    if (childReference.ReferenceType == ReferenceType.Project)
                    {
                        sourceProject = childReference.ResolveProject(project.ParentSolution) as DotNetProject;
                    }

                    if (sourceProject != null)
                    {
                        string childName = sourceProject.FileName;

                        // Skip projects which have ReferenceOutputAssembly=false
                        if (!string.IsNullOrEmpty(childName) &&
                            !excludedProjects.Contains(childName, StringComparer.OrdinalIgnoreCase))
                        {
                            childReferences.Add(childName);

                            result.ToProcess.Add(new DotNetProjectReference(sourceProject, childName));
                        }
                    }
                    else if (hasProjectJson)
                    {
                        // SDK references do not have a SourceProject or child references,
                        // but they can contain project.json files, and should be part of the closure
                        // SDKs are not projects, only the project.json name is checked here
                        //var possibleSdkPath = childReference.Path;

                        //if (!string.IsNullOrEmpty (possibleSdkPath)
                        //    && !possibleSdkPath.EndsWith (".dll", StringComparison.OrdinalIgnoreCase)
                        //    && Directory.Exists(possibleSdkPath)) {
                        //	var possibleProjectJson = Path.Combine (
                        //		possibleSdkPath,
                        //		ProjectJsonPathUtilities.ProjectConfigFileName);

                        //	if (File.Exists (possibleProjectJson)) {
                        //		childReferences.Add (possibleProjectJson);

                        //		// add the sdk to the results here
                        //		result.Processed.Add (new ExternalProjectReference (
                        //			possibleProjectJson,
                        //			childReference.Name,
                        //			possibleProjectJson,
                        //			msbuildProjectPath: null,
                        //			projectReferences: Enumerable.Empty<string> ()));
                        //	}
                        //}
                    }
                } catch (Exception ex) {
                    // Exceptions are expected in some scenarios for native projects,
                    // ignore them and show a warning
                    hasMissingReferences = true;

                    logger.LogDebug(ex.Message);

                    LoggingService.LogError("Unable to find project closure.", ex);
                }
            }

            if (hasMissingReferences)
            {
                // Log a warning message once per project
                // This warning contains only the names of the root project and the project with the
                // broken reference. Attempting to display more details on the actual reference
                // that has the problem may lead to another exception being thrown.
                var warning = GettextCatalog.GetString("Failed to resolve all project references for '{0}'. The package restore result for '{1}' may be incomplete.",
                                                       projectName,
                                                       rootProjectPath);

                logger.LogWarning(warning);
            }

            // Only set a package spec project name if a package spec exists
            var packageSpecProjectName = jsonConfigItem == null ? null : projectName;

            // Add the parent project to the results
            result.Processed.Add(new ExternalProjectReference(
                                     projectFileFullPath,
                                     packageSpecProjectName,
                                     jsonConfigItem,
                                     projectFileFullPath,
                                     childReferences));

            return(result);
        }
Example #22
0
        /// <summary>
        /// Validate that all project.lock.json files are validate for the project.json files,
        /// and that no packages are missing.
        /// If a full restore is required this will return false.
        /// </summary>
        /// <remarks>Floating versions and project.json files with supports require a full restore.</remarks>
        public static async Task <bool> IsRestoreRequired(
            IReadOnlyList <BuildIntegratedNuGetProject> projects,
            VersionFolderPathResolver pathResolver,
            ExternalProjectReferenceContext referenceContext)
        {
            var hashesChecked = new HashSet <string>();

            // Validate project.lock.json files
            foreach (var project in projects)
            {
                var lockFilePath = ProjectJsonPathUtilities.GetLockFilePath(project.JsonConfigPath);

                if (!File.Exists(lockFilePath))
                {
                    // If the lock file does not exist a restore is needed
                    return(true);
                }

                var lockFileFormat = new LockFileFormat();
                var lockFile       = lockFileFormat.Read(lockFilePath, referenceContext.Logger);

                var lockFileVersion = await GetLockFileVersion(project, referenceContext);

                var packageSpec = referenceContext.GetOrCreateSpec(project.ProjectName, project.JsonConfigPath);

                if (!lockFile.IsValidForPackageSpec(packageSpec, lockFileVersion))
                {
                    // The project.json file has been changed and the lock file needs to be updated.
                    return(true);
                }

                // Verify all libraries are on disk
                foreach (var library in lockFile.Libraries)
                {
                    // Verify the SHA for each package
                    var hashPath = pathResolver.GetHashPath(library.Name, library.Version);

                    // Libraries shared between projects can be skipped
                    if (hashesChecked.Add(hashPath))
                    {
                        if (File.Exists(hashPath))
                        {
                            var sha512 = File.ReadAllText(hashPath);

                            if (library.Sha512 != sha512)
                            {
                                // A package has changed
                                return(true);
                            }
                        }
                        else
                        {
                            // A package is missing
                            return(true);
                        }
                    }
                }
            }

            return(false);
        }