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 projectFileName, uint upgradeFlag, string copyLocation, out string upgradeFullyQualifiedFileName, IVsUpgradeLogger logger, out int upgradeRequired, out Guid newProjectFactory) { uint ignore; this.UpgradeProject_CheckOnly(projectFileName, logger, out upgradeRequired, out newProjectFactory, out ignore); if (upgradeRequired == 0) { upgradeFullyQualifiedFileName = projectFileName; return(VSConstants.S_OK); } string projectName = Path.GetFileNameWithoutExtension(projectFileName); upgradeFullyQualifiedFileName = projectFileName; // Query for edit 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[] { projectFileName }, null, null, out editVerdict, out queryEditMoreInfo); if (ErrorHandler.Failed(hr)) { return(VSConstants.E_FAIL); } if (editVerdict != (uint)tagVSQueryEditResult.QER_EditOK) { if (logger != null) { logger.LogMessage((uint)__VSUL_ERRORLEVEL.VSUL_ERROR, projectName, projectFileName, SR.GetString(SR.UpgradeCannotOpenProjectFileForEdit)); } return(VSConstants.E_FAIL); } // If file was modified during the checkout, maybe upgrade is not needed if ((queryEditMoreInfo & (uint)tagVSQueryEditResultFlags.QER_MaybeChanged) != 0) { this.UpgradeProject_CheckOnly(projectFileName, logger, out upgradeRequired, out newProjectFactory, out ignore); if (upgradeRequired == 0) { if (logger != null) { logger.LogMessage((uint)__VSUL_ERRORLEVEL.VSUL_INFORMATIONAL, projectName, projectFileName, SR.GetString(SR.UpgradeNoNeedToUpgradeAfterCheckout)); } return(VSConstants.S_OK); } } } // Convert the project Microsoft.Build.Conversion.ProjectFileConverter projectConverter = new Microsoft.Build.Conversion.ProjectFileConverter(); projectConverter.OldProjectFile = projectFileName; projectConverter.NewProjectFile = projectFileName; ProjectRootElement convertedProject = null; try { convertedProject = projectConverter.ConvertInMemory(); } catch (Exception ex) { if (logger != null) { logger.LogMessage((uint)__VSUL_ERRORLEVEL.VSUL_ERROR, projectName, projectFileName, ex.Message); } } if (convertedProject != null) { this.m_lastUpgradedProjectFile = projectFileName; foreach (ProjectPropertyElement 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; } } try { convertedProject.Save(projectFileName); } catch (Exception ex) { if (logger != null) { logger.LogMessage((uint)__VSUL_ERRORLEVEL.VSUL_ERROR, projectName, projectFileName, ex.Message); } return(VSConstants.E_FAIL); } if (logger != null) { logger.LogMessage((uint)__VSUL_ERRORLEVEL.VSUL_STATUSMSG, projectName, projectFileName, SR.GetString(SR.UpgradeSuccessful)); } return(VSConstants.S_OK); } this.m_lastUpgradedProjectFile = null; upgradeFullyQualifiedFileName = ""; return(VSConstants.E_FAIL); }
/// <summary> /// Called by projects and editors before modifying a file /// The function allows the source control systems to take the necessary actions (checkout, flip attributes) /// to make the file writable in order to allow the edit to continue /// /// There are a lot of cases to deal with during QueryEdit/QuerySave. /// - called in commmand line mode, when UI cannot be displayed /// - called during builds, when save shoudn't probably be allowed /// - called during projects migration, when projects are not open and not registered yet with source control /// - checking out files may bring new versions from vss database which may be reloaded and the user may lose in-memory changes; some other files may not be reloadable /// - not all editors call QueryEdit when they modify the file the first time (buggy editors!), and the files may be already dirty in memory when QueryEdit is called /// - files on disk may be modified outside IDE and may have attributes incorrect for their scc status /// - checkouts may fail /// </summary> /// <param name="rgfQueryEdit">The RGF query edit.</param> /// <param name="cFiles">The c files.</param> /// <param name="rgpszMkDocuments">The RGPSZ mk documents.</param> /// <param name="rgrgf">The RGRGF.</param> /// <param name="rgFileInfo">The rg file info.</param> /// <param name="pfEditVerdict">The pf edit verdict.</param> /// <param name="prgfMoreInfo">The PRGF more info.</param> /// <returns></returns> public int QueryEditFiles(uint rgfQueryEdit, int cFiles, string[] rgpszMkDocuments, uint[] rgrgf, VSQEQS_FILE_ATTRIBUTE_DATA[] rgFileInfo, out uint pfEditVerdict, out uint prgfMoreInfo) { tagVSQueryEditFlags queryFlags = (tagVSQueryEditFlags)rgfQueryEdit; pfEditVerdict = (uint)tagVSQueryEditResult.QER_EditOK; prgfMoreInfo = (uint)(tagVSQueryEditResultFlags)0; // Must be 0 when verdict is QER_EditOK or you see failures like issue #624 bool allowUI = (queryFlags & (tagVSQueryEditFlags.QEF_SilentMode | tagVSQueryEditFlags.QEF_ReportOnly | tagVSQueryEditFlags.QEF_ForceEdit_NoPrompting)) == 0; bool?allowReadOnlyNonSccWrites = null; if (rgpszMkDocuments == null) { return(VSErr.E_POINTER); } try { // If the editor asks us to do anything in our power to make it editable // without prompting, just make those files writable. if ((queryFlags & tagVSQueryEditFlags.QEF_ForceEdit_NoPrompting) != 0) { return(QueryEditForceWritable(rgpszMkDocuments)); } HybridCollection <string> mustLockFiles = null; HybridCollection <string> readOnlyEditFiles = null; List <SvnItem> mustLockItems = null; List <SvnItem> readOnlyItems = null; for (int i = 0; i < cFiles; i++) { SvnItem item; { string file = rgpszMkDocuments[i]; if (!IsSafeSccPath(file)) { continue; // Skip non scc paths (Includes %TEMP%\*) } item = StatusCache[file]; } Monitor.ScheduleDirtyCheck(item); if (item.IsReadOnlyMustLock && !item.IsDirectory) { if (!allowUI) { pfEditVerdict = (uint)tagVSQueryEditResult.QER_EditNotOK; prgfMoreInfo = (uint)(tagVSQueryEditResultFlags.QER_ReadOnlyUnderScc | tagVSQueryEditResultFlags.QER_NoisyCheckoutRequired); return(VSErr.S_OK); } if (mustLockItems == null) { mustLockFiles = new HybridCollection <string>(StringComparer.OrdinalIgnoreCase); mustLockItems = new List <SvnItem>(); } if (!mustLockFiles.Contains(item.FullPath)) { mustLockFiles.Add(item.FullPath); mustLockItems.Add(item); } } else if (item.IsReadOnly) { if (!allowReadOnlyNonSccWrites.HasValue) { allowReadOnlyNonSccWrites = AllowReadOnlyNonSccWrites(); } if (!allowReadOnlyNonSccWrites.Value) { if (!allowUI) { pfEditVerdict = (uint)tagVSQueryEditResult.QER_EditNotOK; prgfMoreInfo = (uint)(tagVSQueryEditResultFlags.QER_InMemoryEditNotAllowed | tagVSQueryEditResultFlags.QER_ReadOnlyNotUnderScc | tagVSQueryEditResultFlags.QER_NoisyPromptRequired); return(VSErr.S_OK); } if (readOnlyEditFiles == null) { readOnlyEditFiles = new HybridCollection <string>(StringComparer.OrdinalIgnoreCase); readOnlyItems = new List <SvnItem>(); } if (!readOnlyEditFiles.Contains(item.FullPath)) { readOnlyEditFiles.Add(item.FullPath); readOnlyItems.Add(item); } } // else // allow editting } } if (mustLockItems != null) { List <SvnItem> mustBeLocked = new List <SvnItem>(mustLockItems); // Look at all subfiles of the must be locked document and add these to the dialog // to make it easier to lock them too foreach (string lockFile in new List <string>(mustLockFiles)) { foreach (string file in GetAllDocumentFiles(lockFile)) { if (!mustLockFiles.Contains(file)) { mustLockFiles.Add(file); mustLockItems.Add(StatusCache[file]); } } } CommandService.DirectlyExecCommand(AnkhCommand.SccLock, mustLockItems, CommandPrompt.DoDefault); // Only check the original list; the rest of the items in mustLockItems is optional foreach (SvnItem i in mustBeLocked) { if (i.IsReadOnlyMustLock) { // User has probably canceled the lock operation, or it failed. pfEditVerdict = (uint)tagVSQueryEditResult.QER_EditNotOK; prgfMoreInfo = (uint)(tagVSQueryEditResultFlags.QER_CheckoutCanceledOrFailed | tagVSQueryEditResultFlags.QER_ReadOnlyUnderScc); break; } } } if (readOnlyItems != null) { // TODO: Handle multiple items correctly // Is this only for non-scc items or also for scc items that are readonly for other reasons? CommandResult result = CommandService.DirectlyExecCommand(AnkhCommand.MakeNonSccFileWriteable, readOnlyItems[0], CommandPrompt.DoDefault); bool allowed = result.Result is bool?(bool)result.Result : false; if (!allowed) { pfEditVerdict = (uint)tagVSQueryEditResult.QER_EditNotOK; prgfMoreInfo = (uint)(tagVSQueryEditResultFlags.QER_InMemoryEditNotAllowed | tagVSQueryEditResultFlags.QER_ReadOnlyNotUnderScc // TODO: Specialize to SCC? ); } } } catch (Exception ex) { IAnkhErrorHandler eh = GetService <IAnkhErrorHandler>(); if (eh != null && eh.IsEnabled(ex)) { eh.OnError(ex); } else { throw; } } return(VSErr.S_OK); }