Ejemplo n.º 1
0
        public static Task <Dictionary <string, EnvDTE.Project> > GetPathToDTEProjectLookupAsync(EnvDTE.DTE dte)
        {
            var pathToProject = new Dictionary <string, EnvDTE.Project>(StringComparer.OrdinalIgnoreCase);

            var supportedProjects = dte.Solution.Projects.Cast <EnvDTE.Project>();

            foreach (var solutionProject in supportedProjects)
            {
                var solutionProjectPath = EnvDTEProjectUtility.GetFullProjectPath(solutionProject);

                if (!string.IsNullOrEmpty(solutionProjectPath) &&
                    !pathToProject.ContainsKey(solutionProjectPath))
                {
                    pathToProject.Add(solutionProjectPath, solutionProject);
                }
            }

            return(Task.FromResult(pathToProject));
        }
        private static Dictionary <string, EnvDTEProject> GetPathToDTEProjectLookup(EnvDTEProject project)
        {
            var pathToProject
                = new Dictionary <string, EnvDTEProject>(StringComparer.OrdinalIgnoreCase);

            foreach (var solutionProjectObj in project.DTE.Solution.Projects)
            {
                var solutionProject = solutionProjectObj as Project;

                if (solutionProject != null)
                {
                    var solutionProjectPath = EnvDTEProjectUtility.GetFullProjectPath(solutionProject);

                    if (!string.IsNullOrEmpty(solutionProjectPath) &&
                        !pathToProject.ContainsKey(solutionProjectPath))
                    {
                        pathToProject.Add(solutionProjectPath, solutionProject);
                    }
                }
            }

            return(pathToProject);
        }
        public bool TryCreateNuGetProject(EnvDTE.Project envDTEProject, ProjectSystemProviderContext context, out NuGetProject result)
        {
            if (envDTEProject == null)
            {
                throw new ArgumentNullException(nameof(envDTEProject));
            }

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

            ThreadHelper.ThrowIfNotOnUIThread();

            result = null;

            var msBuildNuGetProjectSystem = MSBuildNuGetProjectSystemFactory.CreateMSBuildNuGetProjectSystem(
                envDTEProject,
                context.ProjectContext);

            var isWebSite = msBuildNuGetProjectSystem is WebSiteProjectSystem;

            // Web sites cannot have project.json
            if (!isWebSite)
            {
                // Find the project file path
                var projectFilePath = EnvDTEProjectUtility.GetFullProjectPath(envDTEProject);

                if (!string.IsNullOrEmpty(projectFilePath))
                {
                    var msbuildProjectFile         = new FileInfo(projectFilePath);
                    var projectNameFromMSBuildPath = Path.GetFileNameWithoutExtension(msbuildProjectFile.Name);

                    // Treat projects with project.json as build integrated projects
                    // Search for projectName.project.json first, then project.json
                    // If the name cannot be determined, search only for project.json
                    string projectJsonPath = null;
                    if (string.IsNullOrEmpty(projectNameFromMSBuildPath))
                    {
                        projectJsonPath = Path.Combine(msbuildProjectFile.DirectoryName,
                                                       ProjectJsonPathUtilities.ProjectConfigFileName);
                    }
                    else
                    {
                        projectJsonPath = ProjectJsonPathUtilities.GetProjectConfigPath(msbuildProjectFile.DirectoryName,
                                                                                        projectNameFromMSBuildPath);
                    }

                    if (File.Exists(projectJsonPath))
                    {
                        result = new ProjectJsonBuildIntegratedProjectSystem(
                            projectJsonPath,
                            msbuildProjectFile.FullName,
                            envDTEProject,
                            EnvDTEProjectUtility.GetCustomUniqueName(envDTEProject));
                    }
                }
            }

            // Create a normal MSBuild project if no project.json was found
            if (result == null)
            {
                var folderNuGetProjectFullPath = context.PackagesPathFactory();

                // Project folder path is the packages config folder path
                var packagesConfigFolderPath = EnvDTEProjectUtility.GetFullPath(envDTEProject);

                result = new VSMSBuildNuGetProject(
                    envDTEProject,
                    msBuildNuGetProjectSystem,
                    folderNuGetProjectFullPath,
                    packagesConfigFolderPath);
            }

            return(result != null);
        }
        /// <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]);
        }
        /// <summary>
        /// Get the unique names of all references which have ReferenceOutputAssembly set to false.
        /// </summary>
        private static List <string> GetExcludedReferences(
            EnvDTEProject project,
            IVsEnumHierarchyItemsFactory itemsFactory)
        {
            var excludedReferences = new List <string>();

            var hierarchy = VsHierarchyUtility.ToVsHierarchy(project);

            // Get all items in the hierarchy, this includes project references, files, and everything else.
            IEnumHierarchyItems items;

            if (ErrorHandler.Succeeded(itemsFactory.EnumHierarchyItems(
                                           hierarchy,
                                           (uint)__VSEHI.VSEHI_Leaf,
                                           (uint)VSConstants.VSITEMID.Root,
                                           out items)))
            {
                var buildPropertyStorage = (IVsBuildPropertyStorage)hierarchy;

                // Loop through all items
                uint fetched;
                VSITEMSELECTION[] item = new VSITEMSELECTION[1];
                while (ErrorHandler.Succeeded(items.Next(1, item, out fetched)) && fetched == 1)
                {
                    // Check if the item has ReferenceOutputAssembly. This will
                    // return null for the vast majority of items.
                    string value;
                    if (ErrorHandler.Succeeded(buildPropertyStorage.GetItemAttribute(
                                                   item[0].itemid,
                                                   "ReferenceOutputAssembly",
                                                   out value)) &&
                        value != null)
                    {
                        // We only need to go farther if the flag exists and is not true
                        if (!string.Equals(value, Boolean.TrueString, StringComparison.OrdinalIgnoreCase))
                        {
                            // Get the DTE Project reference for the item id. This checks for nulls incase this is
                            // somehow not a project reference that had the ReferenceOutputAssembly flag.
                            object childObject;
                            if (ErrorHandler.Succeeded(hierarchy.GetProperty(
                                                           item[0].itemid,
                                                           (int)__VSHPROPID.VSHPROPID_ExtObject,
                                                           out childObject)))
                            {
                                // 1. Verify that this is a project reference
                                // 2. Check that it is valid and resolved
                                // 3. Follow the reference to the DTE project and get the unique name
                                var reference = childObject as Reference3;

                                if (reference != null && reference.Resolved && reference.SourceProject != null)
                                {
                                    var childPath = EnvDTEProjectUtility
                                                    .GetFullProjectPath(reference.SourceProject);

                                    excludedReferences.Add(childPath);
                                }
                            }
                        }
                    }
                }
            }

            return(excludedReferences);
        }
        /// <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);
        }
        public bool TryCreateNuGetProject(EnvDTE.Project dteProject, ProjectSystemProviderContext context, out NuGetProject result)
        {
            if (dteProject == null)
            {
                throw new ArgumentNullException(nameof(dteProject));
            }

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

            ThreadHelper.ThrowIfNotOnUIThread();

            result = null;

            // The project must be an IVsHierarchy.
            var hierarchy = VsHierarchyUtility.ToVsHierarchy(dteProject);

            if (hierarchy == null)
            {
                return(false);
            }

            // Check if the project is not CPS capable or if it is CPS capable then it does not have TargetFramework(s), if so then return false
            if (!hierarchy.IsCapabilityMatch("CPS"))
            {
                return(false);
            }

            var buildPropertyStorage = hierarchy as IVsBuildPropertyStorage;

            // read MSBuild property RestoreProjectStyle, TargetFramework, and TargetFrameworks
            var restoreProjectStyle = VsHierarchyUtility.GetMSBuildProperty(buildPropertyStorage, RestoreProjectStyle);

            var targetFramework = VsHierarchyUtility.GetMSBuildProperty(buildPropertyStorage, TargetFramework);

            var targetFrameworks = VsHierarchyUtility.GetMSBuildProperty(buildPropertyStorage, TargetFrameworks);

            // check for RestoreProjectStyle property is set and if not set to PackageReference then return false
            if (!(string.IsNullOrEmpty(restoreProjectStyle) ||
                  restoreProjectStyle.Equals(ProjectStyle.PackageReference.ToString(), StringComparison.OrdinalIgnoreCase)))
            {
                return(false);
            }
            // check whether TargetFramework or TargetFrameworks property is set, else return false
            else if (string.IsNullOrEmpty(targetFramework) && string.IsNullOrEmpty(targetFrameworks))
            {
                return(false);
            }

            // Lazy load the CPS enabled JoinableTaskFactory for the UI.
            NuGetUIThreadHelper.SetJoinableTaskFactoryFromService(ProjectServiceAccessor.Value as IProjectServiceAccessor);

            var projectNames        = ProjectNames.FromDTEProject(dteProject);
            var fullProjectPath     = EnvDTEProjectUtility.GetFullProjectPath(dteProject);
            var unconfiguredProject = GetUnconfiguredProject(dteProject);

            result = new CpsPackageReferenceProject(
                dteProject.Name,
                EnvDTEProjectUtility.GetCustomUniqueName(dteProject),
                fullProjectPath,
                _projectSystemCache,
                dteProject,
                unconfiguredProject,
                VsHierarchyUtility.GetProjectId(dteProject));

            return(true);
        }
        /// <summary>
        /// Get only the direct dependencies from a project
        /// </summary>
        public static async Task <IReadOnlyList <ProjectRestoreReference> > GetDirectProjectReferences(
            EnvDTEProject project,
            IEnumerable <string> resolvedProjects,
            ILogger log)
        {
            return(await NuGetUIThreadHelper.JoinableTaskFactory.RunAsync(async() =>
            {
                // DTE calls need to be done from the main thread
                await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();

                var results = new List <ProjectRestoreReference>();

                var itemsFactory = ServiceLocator.GetInstance <IVsEnumHierarchyItemsFactory>();

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

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

                        // check if deferred projects resolved this reference, which means this is still not loaded so simply continue
                        // We'll get this reference from deferred projects later
                        if (reference3 != null &&
                            resolvedProjects.Contains(reference3.Name, StringComparer.OrdinalIgnoreCase))
                        {
                            continue;
                        }

                        // Set missing reference if
                        // 1. reference is null OR
                        // 2. reference is not resolved which means project is not loaded or assembly not found.
                        else 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 childProjectPath = EnvDTEProjectUtility.GetFullProjectPath(childReference.SourceProject);

                            // Skip projects which have ReferenceOutputAssembly=false
                            if (!string.IsNullOrEmpty(childProjectPath) &&
                                !excludedProjects.Contains(childProjectPath, StringComparer.OrdinalIgnoreCase))
                            {
                                var restoreReference = new ProjectRestoreReference()
                                {
                                    ProjectPath = childProjectPath,
                                    ProjectUniqueName = childProjectPath
                                };

                                results.Add(restoreReference);
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        // Exceptions are expected in some scenarios for native projects,
                        // ignore them and show a warning
                        hasMissingReferences = true;

                        log.LogDebug(ex.ToString());

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

                if (hasMissingReferences)
                {
                    // Log a generic message once per project if any items could not be resolved.
                    // In most cases this can be ignored, but in the rare case where the unresolved
                    // item is actually a project the restore result will be incomplete.
                    var message = string.Format(
                        CultureInfo.CurrentCulture,
                        Strings.UnresolvedItemDuringProjectClosureWalk,
                        EnvDTEProjectUtility.GetUniqueName(project));

                    log.LogVerbose(message);
                }

                return results;
            }));
        }