/// <summary> /// Update/Fix all ProjectReference elements within the given project file. /// </summary> /// <param name="projectPath">The path to the MSBuild Project to fix ProjectElements in.</param> /// <param name="projectLookupDictionary">A Dictionary from <see cref="LoadProjectGuids(string)"/> to perform lookups in.</param> /// <param name="saveChanges">Indicates if this tool should save the changes to the target project file.</param> /// <returns><c>true</c> if any ProjectReference tags within the given project are modified; otherwise, <c>false</c>.</returns> internal static bool UpdateProjectReferences(string projectPath, IDictionary <string, string> projectLookupDictionary, bool saveChanges) { bool projectModified = false; try { XDocument projXml = XDocument.Load(projectPath); // Find all the ProjectReference Elements IEnumerable <XElement> projectReferenceElements = MSBuildUtilities.GetProjectReferenceNodes(projXml); foreach (XElement projectReferenceElement in projectReferenceElements) { if (FixProjectReference(projectReferenceElement, projectPath, projectLookupDictionary)) { projectModified = true; } } if (saveChanges && projectModified) { projXml.Save(projectPath); } } catch (Exception ex) { string exception = $"Failed on project `{projectPath}`"; throw new InvalidOperationException(exception, ex); } return(projectModified); }
/// <summary> /// Given a <see cref="XElement"/> that represents an MSBuild /// ProjectReference tag, validate that the relative path to /// the dependency and the name of the dependency is correct. /// </summary> /// <param name="projectReference">A fragment that represents an MSBuild ProjectReference tag.</param> /// <param name="projectPath">The path to the project that contained this fragment.</param> /// <param name="projectLookupDictionary">A Dictionary from <see cref="LoadProjectGuids(string)"/> to perform lookups in.</param> /// <returns><c>true</c> if the fragment was modified (or "fixed"); otherwise, <c>false</c>.</returns> internal static bool FixProjectReference(XElement projectReference, string projectPath, IDictionary <string, string> projectLookupDictionary) { bool fragmentWasModified = false; // The project directory needs the trailing slash to support relative path generation string projectDirectory = Path.GetDirectoryName(projectPath); string prIncludeRelativePath = MSBuildUtilities.GetProjectReferenceIncludeValue(projectReference, projectPath); string prGuid = MSBuildUtilities.GetProjectReferenceGUID(projectReference, projectPath); // Look it up in the Dictionary string dictionaryLookupProjectPath = null; if (!projectLookupDictionary.TryGetValue(prGuid, out dictionaryLookupProjectPath)) { string prIncludeActualPath = Path.GetFullPath(prIncludeRelativePath, projectDirectory); string exception = $"Project GUID `{prGuid}` does not exist in the lookup dictionary; according to the project it should be located here `{prIncludeActualPath}`; was it deleted?"; throw new InvalidOperationException(exception); } // Now that we have the found path from the dictionary convert it to a relative path string prActualRelativePath = Path.GetRelativePath(projectDirectory, dictionaryLookupProjectPath); // Fix up the Relative Path to contain the correct slashes prActualRelativePath = prActualRelativePath.Replace(Path.DirectorySeparatorChar, '\\'); if (!prIncludeRelativePath.Equals(prActualRelativePath)) { fragmentWasModified = true; MSBuildUtilities.SetProjectReferenceIncludeValue(projectReference, prActualRelativePath); } // Get the name of the file that contains the reference; this will be in the Name Tag. string prActualName = Path.GetFileNameWithoutExtension(dictionaryLookupProjectPath); // Get the existing name in the project reference string prExistingName = MSBuildUtilities.GetOrCreateProjectReferenceName(projectReference, projectPath); if (!prExistingName.Equals(prActualName)) { fragmentWasModified = true; MSBuildUtilities.SetProjectReferenceName(projectReference, prActualName); } return(fragmentWasModified); }
/// <summary> /// Given a target directory spin for all project files (as defined by /// <see cref="GetProjectsInDirectory(string)"/>). /// </summary> /// <param name="targetDirectory">The directory to scan.</param> /// <returns> /// <see cref="IDictionary{TKey, TValue}"/> where the <c>TKey</c> is /// the ProjectGuid and the <c>TValue</c> is the path to the project /// that contains that Guid. /// </returns> internal static IDictionary <string, string> LoadProjectGuids(string targetDirectory) { IEnumerable <string> projFilesInDirectory = GetProjectsInDirectory(targetDirectory); ConcurrentDictionary <string, string> resultDictionary = new ConcurrentDictionary <string, string>(StringComparer.InvariantCultureIgnoreCase); Parallel.ForEach(projFilesInDirectory, projFile => { string projectGuid = MSBuildUtilities.GetMSBuildProjectGuid(projFile); if (!resultDictionary.TryAdd(projectGuid, projFile)) { string exception = $"Failed to add project `{projFile}` the GUID `{projectGuid}` already existed in project `{resultDictionary[projectGuid]}`"; throw new InvalidOperationException(exception); } } ); return(resultDictionary); }