private void BackupProjectFilesIfNeeded( string projectFilePath, ProjectUpgradeLogger logger, __VSPPROJECTUPGRADEVIAFACTORYFLAGS flag, string copyLocation, ProjectRootElement convertedProject ) { var projectName = Path.GetFileNameWithoutExtension(projectFilePath); var projectFileName = Path.GetFileName(projectFilePath); if (HasCopyBackupFlag(flag) || HasSxSBackupFlag(flag)) { if (HasSxSBackupFlag(flag) && !Directory.Exists(copyLocation)) { Debug.Assert(false, "Env should create the directory for us"); throw new ProjectUpgradeFailedException(); } // copy project file { var targetFilePath = Path.Combine(copyLocation, projectFileName); if (HasSxSBackupFlag(flag)) { bool ignored; targetFilePath = GetUniqueFileName(targetFilePath + ".old", out ignored); } try { File.Copy(projectFilePath, targetFilePath); logger.LogInfo(SR.GetString(SR.ProjectBackupSuccessful, targetFilePath)); } catch (Exception ex) { var message = SR.GetString(SR.ErrorMakingProjectBackup, targetFilePath); throw new ProjectUpgradeFailedException(string.Format("{0} : {1}", message, ex.Message)); } } if (HasCopyBackupFlag(flag)) { //Now iterate through the project items and copy them to the new location //All projects under the solution retain its folder hierarchy var types = new[] { "Compile", "None", "Content", "EmbeddedResource", "Resource", "BaseApplicationManifest", "ApplicationDefinition", "Page" }; var metadataLookup = convertedProject .Items .GroupBy(i => i.ItemType) .ToDictionary(x => x.Key); var sourceProjectDir = Path.GetDirectoryName(projectFilePath); foreach (var ty in types) { if (metadataLookup.ContainsKey(ty)) { foreach (var item in metadataLookup[ty]) { var linkMetadataElement = item.Metadata.FirstOrDefault(me => me.Name == "Link"); string linked = linkMetadataElement != null && !string.IsNullOrEmpty(linkMetadataElement.Value) ? linkMetadataElement.Value : null; var include = item.Include; Debug.Assert(!string.IsNullOrEmpty(include)); string sourceFilePath; var targetFileName = Path.Combine(copyLocation, linked ?? include); if (Path.IsPathRooted(include)) { //if the path is fully qualified already, then just use it sourceFilePath = include; } else { //otherwise tack the filename on to the path sourceFilePath = Path.Combine(sourceProjectDir, include); } if (linked != null && include[0] == '.') { //if linked file up a level (or more), then turn it into a path without the ..\ in the middle sourceFilePath = Path.GetFullPath(sourceFilePath); } bool initiallyUnique; targetFileName = GetUniqueFileName(targetFileName, out initiallyUnique); if (!initiallyUnique) { logger.LogInfo(SR.GetString(SR.BackupNameConflict, targetFileName)); } //Warn user in upgrade log if linked files are used "project may not build" if (linked != null && HasCopyBackupFlag(flag)) { logger.LogWarning(SR.GetString(SR.ProjectContainsLinkedFile, targetFileName)); } // ensure target folder exists Directory.CreateDirectory(Path.GetDirectoryName(targetFileName)); try { File.Copy(sourceFilePath, targetFileName); logger.LogInfo(SR.GetString(SR.BackupSuccessful, targetFileName)); } catch (Exception ex) { var message = SR.GetString(SR.ErrorMakingBackup, targetFileName); logger.LogError(string.Format("{0} : {1}", message, ex.Message)); } } } } } } }
private void BackupProjectFilesIfNeeded( string projectFilePath, ProjectUpgradeLogger logger, __VSPPROJECTUPGRADEVIAFACTORYFLAGS flag, string copyLocation, ProjectRootElement convertedProject ) { var projectName = Path.GetFileNameWithoutExtension(projectFilePath); var projectFileName = Path.GetFileName(projectFilePath); if (HasCopyBackupFlag(flag) || HasSxSBackupFlag(flag)) { if (HasSxSBackupFlag(flag) && !Directory.Exists(copyLocation)) { Debug.Assert(false, "Env should create the directory for us"); throw new ProjectUpgradeFailedException(); } // copy project file { var targetFilePath = Path.Combine(copyLocation, projectFileName); if (HasSxSBackupFlag(flag)) { bool ignored; targetFilePath = GetUniqueFileName(targetFilePath + ".old", out ignored); } try { File.Copy(projectFilePath, targetFilePath); logger.LogInfo(SR.GetString(SR.ProjectBackupSuccessful, targetFilePath)); } catch (Exception ex) { var message = SR.GetString(SR.ErrorMakingProjectBackup, targetFilePath); throw new ProjectUpgradeFailedException(string.Format("{0} : {1}", message, ex.Message)); } } if (HasCopyBackupFlag(flag)) { //Now iterate through the project items and copy them to the new location //All projects under the solution retain its folder hierarchy var types = new[] { "Compile", "None", "Content", "EmbeddedResource", "Resource", "BaseApplicationManifest", "ApplicationDefinition", "Page" }; var metadataLookup = convertedProject .Items .GroupBy(i => i.ItemType) .ToDictionary(x => x.Key); var sourceProjectDir = Path.GetDirectoryName(projectFilePath); foreach (var ty in types) { if (metadataLookup.ContainsKey(ty)) { foreach (var item in metadataLookup[ty]) { var linkMetadataElement = item.Metadata.FirstOrDefault(me => me.Name == "Link"); string linked = linkMetadataElement != null && !string.IsNullOrEmpty(linkMetadataElement.Value) ? linkMetadataElement.Value : null; var include = item.Include; Debug.Assert(!string.IsNullOrEmpty(include)); string sourceFilePath; var targetFileName = Path.Combine(copyLocation, linked ?? include); if (Path.IsPathRooted(include)) { //if the path is fully qualified already, then just use it sourceFilePath = include; } else { //otherwise tack the filename on to the path sourceFilePath = Path.Combine(sourceProjectDir, include); } if (linked != null && include[0] == '.') { //if linked file up a level (or more), then turn it into a path without the ..\ in the middle sourceFilePath = Path.GetFullPath(sourceFilePath); } bool initiallyUnique; targetFileName = GetUniqueFileName(targetFileName, out initiallyUnique); if (!initiallyUnique) { logger.LogInfo(SR.GetString(SR.BackupNameConflict, targetFileName)); } //Warn user in upgrade log if linked files are used "project may not build" if (linked != null && HasCopyBackupFlag(flag)) { logger.LogWarning(SR.GetString(SR.ProjectContainsLinkedFile, targetFileName)); } // ensure target folder exists Directory.CreateDirectory(Path.GetDirectoryName(targetFileName)); try { File.Copy(sourceFilePath, targetFileName); logger.LogInfo(SR.GetString(SR.BackupSuccessful, targetFileName)); } catch (Exception ex) { var message = SR.GetString(SR.ErrorMakingBackup, targetFileName); logger.LogError(string.Format("{0} : {1}", message, ex.Message)); } } } } } } }
public virtual int UpgradeProject( string projectFilePath, uint upgradeFlag, string initialCopyLocation, out string upgradeFullyQualifiedFileName, IVsUpgradeLogger upgradeLogger, out int upgradeRequired, out Guid newProjectFactory ) { // initialize out params in case of failure upgradeFullyQualifiedFileName = null; upgradeRequired = 0; newProjectFactory = Guid.Empty; __VSPPROJECTUPGRADEVIAFACTORYFLAGS flag; string copyLocation = initialCopyLocation; var r = NormalizeUpgradeFlag(upgradeFlag, out flag, ref copyLocation); if (r != VSConstants.S_OK) { return r; } string projectName = Path.GetFileNameWithoutExtension(projectFilePath); var logger = new ProjectUpgradeLogger(upgradeLogger, projectName, projectFilePath); uint ignore; ((IVsProjectUpgradeViaFactory)this).UpgradeProject_CheckOnly(projectFilePath, upgradeLogger, out upgradeRequired, out newProjectFactory, out ignore); // no upgrade required and not 'copy-backup' if (upgradeRequired == 0 && !HasCopyBackupFlag(flag)) { //Write an informational message "No upgrade required for project foo"? logger.LogInfo(SR.GetString(SR.ProjectConversionNotRequired)); logger.LogInfo(SR.GetString(SR.ConversionNotRequired)); upgradeFullyQualifiedFileName = projectFilePath; return VSConstants.S_OK; } // upgrade is not required but backup may still be needed var projectFileName = Path.GetFileName(projectFilePath); upgradeFullyQualifiedFileName = projectFilePath; if (HasSxSBackupFlag(flag)) { // for SxS call use location near the original file copyLocation = Path.GetDirectoryName(projectFilePath); } // workflow is taken from vsprjfactory.cpp (vsproject\vsproject) // 1. convert the project (in-memory) // 2. save SCC related info // 3. use data stored on step 2 in GetSccInfo calls (during QueryEditFiles) // 4. if succeeded - save project on disk // F# doesn't use .user file so all related code is skipped try { // load MSBuild project in memory: this will be needed in all cases not depending whether upgrade is required or not // we use this project to determine the set of files to copy ProjectRootElement convertedProject = ConvertProject(projectFilePath, logger); if (convertedProject == null) { throw new ProjectUpgradeFailedException(); } // OK, we need upgrade, save SCC info and ask if project file can be edited if (upgradeRequired != 0) { // 2. save SCC related info this.m_lastUpgradedProjectFile = projectFilePath; foreach (var property in convertedProject.Properties) { switch (property.Name) { case SCC_LOCAL_PATH: this.m_sccLocalPath = property.Value; break; case SCC_AUX_PATH: this.m_sccAuxPath = property.Value; break; case SCC_PROVIDER: this.m_sccProvider = property.Value; break; case SCC_PROJECT_NAME: this.m_sccProjectName = property.Value; break; default: break; } } // 3. Query for edit (this call may query information stored on previous step) IVsQueryEditQuerySave2 queryEdit = site.GetService(typeof(SVsQueryEditQuerySave)) as IVsQueryEditQuerySave2; if (queryEdit != null) { uint editVerdict; uint queryEditMoreInfo; const tagVSQueryEditFlags tagVSQueryEditFlags_QEF_AllowUnopenedProjects = (tagVSQueryEditFlags)0x80; int hr = queryEdit.QueryEditFiles( (uint)(tagVSQueryEditFlags.QEF_ForceEdit_NoPrompting | tagVSQueryEditFlags.QEF_DisallowInMemoryEdits | tagVSQueryEditFlags_QEF_AllowUnopenedProjects), 1, new[] { projectFilePath }, null, null, out editVerdict, out queryEditMoreInfo); if (ErrorHandler.Failed(hr)) { throw new ProjectUpgradeFailedException(); } if (editVerdict != (uint)tagVSQueryEditResult.QER_EditOK) { throw new ProjectUpgradeFailedException(SR.GetString(SR.UpgradeCannotOpenProjectFileForEdit)); } // If file was modified during the checkout, maybe upgrade is not needed if ((queryEditMoreInfo & (uint)tagVSQueryEditResultFlags.QER_MaybeChanged) != 0) { ((IVsProjectUpgradeViaFactory)this).UpgradeProject_CheckOnly(projectFilePath, upgradeLogger, out upgradeRequired, out newProjectFactory, out ignore); if (upgradeRequired == 0) { if (logger != null) { logger.LogInfo(SR.GetString(SR.UpgradeNoNeedToUpgradeAfterCheckout)); } return VSConstants.S_OK; } } } } // 3.1 copy backup BackupProjectFilesIfNeeded(projectFilePath, logger, flag, copyLocation, convertedProject); // 4. if we have performed upgrade - save project to disk if (upgradeRequired != 0) { try { convertedProject.Save(projectFilePath); } catch (Exception ex) { throw new ProjectUpgradeFailedException(ex.Message, ex); } } // 821083: "Converted" should NOT be localized - it is referenced in the XSLT used to display the UpgradeReport logger.LogStatus("Converted"); } catch (ProjectUpgradeFailedException ex) { var exception = ex.InnerException ?? ex; if (exception != null && !string.IsNullOrEmpty(exception.Message)) logger.LogError(exception.Message); upgradeFullyQualifiedFileName = ""; m_lastUpgradedProjectFile = null; return VSConstants.E_FAIL; } return VSConstants.S_OK; }
public virtual int UpgradeProject( string projectFilePath, uint upgradeFlag, string initialCopyLocation, out string upgradeFullyQualifiedFileName, IVsUpgradeLogger upgradeLogger, out int upgradeRequired, out Guid newProjectFactory ) { // initialize out params in case of failure upgradeFullyQualifiedFileName = null; upgradeRequired = 0; newProjectFactory = Guid.Empty; __VSPPROJECTUPGRADEVIAFACTORYFLAGS flag; string copyLocation = initialCopyLocation; var r = NormalizeUpgradeFlag(upgradeFlag, out flag, ref copyLocation); if (r != VSConstants.S_OK) { return(r); } string projectName = Path.GetFileNameWithoutExtension(projectFilePath); var logger = new ProjectUpgradeLogger(upgradeLogger, projectName, projectFilePath); uint ignore; ((IVsProjectUpgradeViaFactory)this).UpgradeProject_CheckOnly(projectFilePath, upgradeLogger, out upgradeRequired, out newProjectFactory, out ignore); // no upgrade required and not 'copy-backup' if (upgradeRequired == 0 && !HasCopyBackupFlag(flag)) { //Write an informational message "No upgrade required for project foo"? logger.LogInfo(SR.GetString(SR.ProjectConversionNotRequired)); logger.LogInfo(SR.GetString(SR.ConversionNotRequired)); upgradeFullyQualifiedFileName = projectFilePath; return(VSConstants.S_OK); } // upgrade is not required but backup may still be needed var projectFileName = Path.GetFileName(projectFilePath); upgradeFullyQualifiedFileName = projectFilePath; if (HasSxSBackupFlag(flag)) { // for SxS call use location near the original file copyLocation = Path.GetDirectoryName(projectFilePath); } // workflow is taken from vsprjfactory.cpp (vsproject\vsproject) // 1. convert the project (in-memory) // 2. save SCC related info // 3. use data stored on step 2 in GetSccInfo calls (during QueryEditFiles) // 4. if succeeded - save project on disk // F# doesn't use .user file so all related code is skipped try { // load MSBuild project in memory: this will be needed in all cases not depending whether upgrade is required or not // we use this project to determine the set of files to copy ProjectRootElement convertedProject = ConvertProject(projectFilePath, logger); if (convertedProject == null) { throw new ProjectUpgradeFailedException(); } // OK, we need upgrade, save SCC info and ask if project file can be edited if (upgradeRequired != 0) { // 2. save SCC related info this.m_lastUpgradedProjectFile = projectFilePath; foreach (var property in convertedProject.Properties) { switch (property.Name) { case SCC_LOCAL_PATH: this.m_sccLocalPath = property.Value; break; case SCC_AUX_PATH: this.m_sccAuxPath = property.Value; break; case SCC_PROVIDER: this.m_sccProvider = property.Value; break; case SCC_PROJECT_NAME: this.m_sccProjectName = property.Value; break; default: break; } } // 3. Query for edit (this call may query information stored on previous step) IVsQueryEditQuerySave2 queryEdit = site.GetService(typeof(SVsQueryEditQuerySave)) as IVsQueryEditQuerySave2; if (queryEdit != null) { uint editVerdict; uint queryEditMoreInfo; const tagVSQueryEditFlags tagVSQueryEditFlags_QEF_AllowUnopenedProjects = (tagVSQueryEditFlags)0x80; int hr = queryEdit.QueryEditFiles( (uint)(tagVSQueryEditFlags.QEF_ForceEdit_NoPrompting | tagVSQueryEditFlags.QEF_DisallowInMemoryEdits | tagVSQueryEditFlags_QEF_AllowUnopenedProjects), 1, new[] { projectFilePath }, null, null, out editVerdict, out queryEditMoreInfo); if (ErrorHandler.Failed(hr)) { throw new ProjectUpgradeFailedException(); } if (editVerdict != (uint)tagVSQueryEditResult.QER_EditOK) { throw new ProjectUpgradeFailedException(SR.GetString(SR.UpgradeCannotOpenProjectFileForEdit)); } // If file was modified during the checkout, maybe upgrade is not needed if ((queryEditMoreInfo & (uint)tagVSQueryEditResultFlags.QER_MaybeChanged) != 0) { ((IVsProjectUpgradeViaFactory)this).UpgradeProject_CheckOnly(projectFilePath, upgradeLogger, out upgradeRequired, out newProjectFactory, out ignore); if (upgradeRequired == 0) { if (logger != null) { logger.LogInfo(SR.GetString(SR.UpgradeNoNeedToUpgradeAfterCheckout)); } return(VSConstants.S_OK); } } } } // 3.1 copy backup BackupProjectFilesIfNeeded(projectFilePath, logger, flag, copyLocation, convertedProject); // 4. if we have performed upgrade - save project to disk if (upgradeRequired != 0) { try { convertedProject.Save(projectFilePath); } catch (Exception ex) { throw new ProjectUpgradeFailedException(ex.Message, ex); } } // 821083: "Converted" should NOT be localized - it is referenced in the XSLT used to display the UpgradeReport logger.LogStatus("Converted"); } catch (ProjectUpgradeFailedException ex) { var exception = ex.InnerException ?? ex; if (exception != null && !string.IsNullOrEmpty(exception.Message)) { logger.LogError(exception.Message); } upgradeFullyQualifiedFileName = ""; m_lastUpgradedProjectFile = null; return(VSConstants.E_FAIL); } return(VSConstants.S_OK); }