Copying my Roslyn modifications from one revision to another

My intention is to continue making more and more modifications to the Roslyn compiler. But then I have the problem that I want to move my modifications to the latest revision of Roslyn from time to time, maybe on a monthly basis, and Roslyn is constantly being updated - it's a "moving target".

In an attempt to make this possible I've developed two programs, Generate Diff and Fuzzy Patch. The idea is that when I've downloaded a new version of Roslyn, then I can run my Generate Diff program on the previous version and extract all of my modifications. Then I can run Fuzzy Patch on the new version of Roslyn, and hopefully most of my modifications will be applied without the need for any manual intervention.

These are both Windows console programs.

Generate Diff

////------------------------------------------------------------------------------------------ // // Merlinia Project Yacks // Copyright © Merlinia 2018, All Rights Reserved. // Licensed under the Apache License, Version 2.0. // (Just to be compatible with the Microsoft Roslyn license.) // ////------------------------------------------------------------------------------------------ using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Text; using System.Threading; namespace GenerateDiff { /// <summary> /// Program to create a set of "diff" files that represent the modifications made to the /// Microsoft Roslyn compiler as part of "Project Yacks". /// /// Input to this program should be one argument like this: /// /// "E:\Roslyn\32645" /// /// This should be a folder that contains two sub-folders, "Archive" and "Current". /// /// "Archive" must contain the unmodified Roslyn files for a particular revision (32645 in the /// above example), as downloaded from https://github.com/dotnet/roslyn . /// /// "Current" should contain the same files, but with some of them modified, plus some additional /// C# source files. /// /// This program will create (if necessary) a "Diff" sub-folder, and will populate it with .diff /// files for each Roslyn file where the "archive" and "current" files are different. The "Diff" /// sub-folder will also contain a copy of each C# source file which it determined was added to /// the "Current" sub-folder. /// /// The purpose of these "diff" files is that they can then be used to (hopefully) update a newer /// revision of the Roslyn compiler. This will be done using the "Fuzzy Patch" program - see that /// program for more information. /// </summary> public static class Program { private static string _roslynPathArchive; private static string _roslynPathCurrent; private static string _roslynPathDiff; /// <summary> /// Program entry point. One argument is needed - see above. /// </summary> public static void Main(string[] args) { // Check the one required argument if (args.Length != 1) { DisplayErrorOrInfo("Exactly one argument is needed."); return; } string roslynPathBase = args[0]; if (!Directory.Exists(roslynPathBase)) { DisplayErrorOrInfo("Directory does not exist: " + roslynPathBase); return; } // Check the two expected sub-folders exist _roslynPathArchive = GetAndCheckCombinedPath(roslynPathBase, "Archive"); _roslynPathCurrent = GetAndCheckCombinedPath(roslynPathBase, "Current"); if (_roslynPathArchive == null || _roslynPathCurrent == null) return; // Diff sub-folder will be created when needed. If it already exists delete it after // getting permission. _roslynPathDiff = _roslynPathCurrent.Replace("Current", "Diff"); if (Directory.Exists(_roslynPathDiff)) { Console.WriteLine("Diff folder already exists. OK to delete? (y/n)"); ConsoleKeyInfo keyInfo = Console.ReadKey(); if (keyInfo.KeyChar != 'y' && keyInfo.KeyChar != 'Y') return; DeleteDirectory(_roslynPathDiff); Console.WriteLine("\nDiff folder deleted."); } // Process all of the folders and files in the "Current" sub-folder WalkDirectoryTree(new DirectoryInfo(_roslynPathCurrent)); DisplayErrorOrInfo("End of Generate Diff program."); } /// <summary> /// Recursive method to "walk a directory tree", performing the specialized processing needed /// to generate .diff files and make copies of added files. This is somewhat based on code /// found here: /// https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/file-system/how-to-iterate-through-a-directory-tree /// </summary> [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] private static void WalkDirectoryTree(DirectoryInfo directoryInfo) { // First, process all the files directly under this folder FileInfo[] filesArray; try { filesArray = directoryInfo.GetFiles(); } // Catch all possible exceptions. This includes the possible problem that one of the files // requires permissions greater than the application provides. catch (Exception e) { // Display an error message and exit DisplayErrorOrInfo(e.Message); return; } foreach (FileInfo currentFileInfo in filesArray) { // Filter out file types that may have been modified or added but are not applicable string fileExtension = currentFileInfo.Extension.ToUpperInvariant(); if (fileExtension == ".CMD" || fileExtension == ".NUPKG" || fileExtension == ".XLF") continue; // Test if a corresponding file exists in the "Archive" sub-folder string currentFullName = currentFileInfo.FullName; string archiveFile = currentFullName.Replace(_roslynPathCurrent, _roslynPathArchive); if (File.Exists(archiveFile)) { // If the files are the same length assume current file is not modified FileInfo archiveFileInfo = new FileInfo(archiveFile); if (archiveFileInfo.Length != currentFileInfo.Length) CreateDiffFile(archiveFileInfo, currentFullName); } else { // This is a file that has been added. Just copy it to the "Diff" sub-folder, but do // it by reading and writing the lines of text to ensure standardized end-of-line // and text encoding. string diffFileName = GetDiffFileName(currentFullName); WriteToDisk(diffFileName, File.ReadAllLines(currentFullName)); Console.WriteLine("Added file copied to 'Diff': " + diffFileName); } } // Now find all the sub-directories under this directory foreach (DirectoryInfo subDirectoryInfo in directoryInfo.GetDirectories()) { // Filter out the sub-directories known not to be applicable string directoryName = subDirectoryInfo.Name.ToUpperInvariant(); if (directoryName == ".VS" || directoryName == "BINARIES") continue; // Recursive call for each sub-directory WalkDirectoryTree(subDirectoryInfo); } } /// <summary> /// Method to use the GNU diff.exe program to create a .diff file for a Roslyn file which has /// been modified. /// /// This currently uses the GNU diff.exe program that happens to have been installed on my /// developer PC as part of the GitHub Desktop for Windows program installation. /// </summary> private static void CreateDiffFile(FileInfo archiveFileInfo, string currentFileName) { // ReSharper disable once StringLiteralTypo const string CGnuDiffExe = @"C:\Users\rp\AppData\Local\GitHub\PortableGit_f02737a78695063deace08e96d5042710d3e32db\usr\bin\diff.exe"; string diffFileName = GetDiffFileName(currentFileName) + ".diff"; // ReSharper disable once AssignNullToNotNullAttribute Directory.CreateDirectory(Path.GetDirectoryName(diffFileName)); using (Process windowsProcess = new Process()) { windowsProcess.StartInfo.UseShellExecute = false; windowsProcess.StartInfo.FileName = CGnuDiffExe; windowsProcess.StartInfo.Arguments = "-u -r \"" + archiveFileInfo.FullName + "\" \"" + currentFileName + "\""; windowsProcess.StartInfo.RedirectStandardOutput = true; if (!windowsProcess.Start()) DisplayErrorOrInfo("Unexpected result for Process.Start()"); File.WriteAllText(diffFileName, windowsProcess.StandardOutput.ReadToEnd()); windowsProcess.WaitForExit(); } // Sanity check for different line endings or different text encoding or something FileInfo diffFileInfo = new FileInfo(diffFileName); if (diffFileInfo.Length > (archiveFileInfo.Length * 120 / 100)) { DisplayErrorOrInfo(string.Format(CultureInfo.InvariantCulture, "Possible error, '{0}' is larger than '{1}'.", diffFileName, archiveFileInfo.FullName)); } Console.WriteLine("Diff file created: " + diffFileName); } /// <summary> /// Method to generate a filename in the "Diff" sub-folder that corresponds to a file in the /// "Current" sub-folder. /// </summary> private static string GetDiffFileName(string currentFileName) { return currentFileName.Replace(_roslynPathCurrent, _roslynPathDiff); } /// <summary> /// Method to generate a sub-folder path and check that it exists. /// </summary> private static string GetAndCheckCombinedPath(string roslynPathBase, string archiveOrCurrent) { string s = Path.Combine(roslynPathBase, archiveOrCurrent); if (Directory.Exists(s)) return s; DisplayErrorOrInfo("Directory does not exist: " + s); return null; } /// <summary> /// Depth-first recursive delete, with handling for descendant directories open in Windows /// Explorer. Copied from here: https://stackoverflow.com/a/1703799/253938 /// </summary> private static void DeleteDirectory(string directoryPath) { foreach (string subDirectory in Directory.GetDirectories(directoryPath)) DeleteDirectory(subDirectory); try { Directory.Delete(directoryPath, true); } catch (IOException) { DeleteDirectoryAgain(directoryPath); } catch (UnauthorizedAccessException) { DeleteDirectoryAgain(directoryPath); } } private static void DeleteDirectoryAgain(string directoryPath) { Thread.Sleep(100); Directory.Delete(directoryPath, true); } /// <summary> /// Method to write lines of text to the disk, specifying that the newline sequence is just /// LF, not CRLF, and the encoding is UTF-8 with BOM. /// </summary> private static void WriteToDisk(string fileName, IEnumerable<string> linesOfText) { using (StreamWriter streamWriter = new StreamWriter(fileName, false, new UTF8Encoding(true))) { streamWriter.NewLine = "\n"; foreach (string oneLine in linesOfText) streamWriter.WriteLine(oneLine); } } /// <summary> /// Method to display an error or information message on the console window. /// </summary> private static void DisplayErrorOrInfo(string textString) { Console.WriteLine(textString); Console.ReadKey(); } } }

Fuzzy Patch

////------------------------------------------------------------------------------------------ // // Merlinia Project Yacks // Copyright © Merlinia 2018, All Rights Reserved. // Licensed under the Apache License, Version 2.0. // (Just to be compatible with the Microsoft Roslyn license.) // ////------------------------------------------------------------------------------------------ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Text; namespace FuzzyPatch { /// <summary> /// Program to process the "diff" files created by the Generate Diff program, applying them to a /// new revision of the Microsoft Roslyn compiler, and thus transferring the "Project Yacks" /// modifications from one Roslyn revision to another. /// /// Input to this program should be two arguments like this: /// /// "E:\Roslyn\32645" "E:\Roslyn\33284" /// /// The first folder must contain a sub-folder "Diff", containing files produced by the Generate /// Diff program. /// /// The second folder must contain a sub-folder "Current", containing newly-downloaded Roslyn /// files from the GitHub repository https://github.com/dotnet/roslyn . /// /// (The numbers 32645 and 33284 in the above example are Roslyn revision numbers, or, more /// accurately, the number of commits that were applied to the GitHub repository at the time when /// the downloads were done.) /// /// This program will attempt to apply the .diff files from the prior revision to the new /// revision, thus transferring the "Project Yacks" modifications from one Roslyn revision to /// another. The processing is referred to as a "fuzzy patch" because it attempts to take into /// consideration that the Roslyn files may have been updated. However, the processing is not all /// that sophisticated, and is based on the expectation that if changes have been made, then they /// are more likely to consist of added lines of code rather than lines of code removed or /// rearranged. /// /// In situations where the "fuzzy patch" processing can not figure out where to apply the .diff /// updates with a high degree of certainty it displays an error message, and a programmer will /// have to examine the situation and copy the modifications manually, perhaps after modifying /// the modifications. /// /// It is assumed that the Roslyn files use Unix-style end-of-line, i.e., LF, not CRLF, and that /// the text is encoded UTF-8 with BOM. /// /// This program also copies the C# source files that have been added to Roslyn and have been /// noted as such by the Generate Diff program. This processing is very simple, and should always /// work. /// </summary> public static class Program { /// <summary> /// Nested class describing a "hunk" in the .diff file. See here for documentation: /// http://www.gnu.org/software/diffutils/manual/html_node/Detailed-Unified.html /// </summary> private class DiffHunk { // All lines in the .diff file, null = end of .diff file public string[] DiffLines { get; } // Zero-based index for first line (if any) inside the .diff hunk public int DiffLineIndex { get; } // Number of lines inside the .diff hunk, may be zero public int HunkLines { get; } // One-based line number for start location in old Roslyn source line public int FromFileLineNumber { get; } // Constructor public DiffHunk(string[] diffLines, int diffLineIndex, int hunkLines, int fromFileLineNumber) { DiffLines = diffLines; DiffLineIndex = diffLineIndex; HunkLines = hunkLines; FromFileLineNumber = fromFileLineNumber; } } /// <summary> /// Nested class that encapsulates some information to make it easier to process applying the /// .diff file to the new Roslyn source file. /// </summary> private class NewFile { // Text lines in the new file public List<string> NewFileLines { get; } public int LinesRemoved { get; private set; } public int LinesAdded { get; private set; } // The "displacementFactor" is an attempt to provide an indication of how different the new // revision is from the old one. public int DisplacementFactor { get; private set; } // Index of last recognized or modified line. Trying to find a new hunk location must not // go farther back in the file than this point. private int _fenceIndex; // Constructor public NewFile(IEnumerable<string> newFileLines) { NewFileLines = new List<string>(newFileLines); } /// <summary> /// Method to find the location where the "hunk" from the .diff file should be applied to /// the new file. /// </summary> /// <returns>zero-based index, or -1 for not found</returns> public int FindHunkLocation(DiffHunk diffHunk) { // As a "first guess", assume specified start line (-1 to make zero-based) is correct, // adjusted for the application of previous hunks int firstGuessIndex = diffHunk.FromFileLineNumber - 1 - LinesRemoved + LinesAdded; bool hunkFound = false; // Search forward to try to find the hunk, on the assumption it is more likely that // lines have been added to the new file than that lines have been removed int testIndex = firstGuessIndex; for (; testIndex < NewFileLines.Count; testIndex++) { if (TestHunkForMatch(diffHunk, testIndex)) { hunkFound = true; break; } } // If not found via forward search try searching backwards, but only to the "fence // index", so the search does not get back into lines that have already been updated if (!hunkFound) { testIndex = firstGuessIndex - 1; for (; testIndex > _fenceIndex; testIndex--) { if (TestHunkForMatch(diffHunk, testIndex)) { hunkFound = true; break; } } } // Indicate result of the search, and adjust the "displacement factor" if (!hunkFound) return -1; DisplacementFactor += Math.Abs(firstGuessIndex - testIndex); return testIndex; } /// <summary> /// Method to apply a .diff file "hunk" to the new file. At this point everything has been /// checked and there should not be any possible error situations. /// </summary> public void ApplyHunk(DiffHunk diffHunk, int newFileIndex) { for (int i = diffHunk.DiffLineIndex; i < diffHunk.DiffLineIndex + diffHunk.HunkLines; i++) { string s = diffHunk.DiffLines[i]; if (s.StartsWith(" ", StringComparison.Ordinal)) { if (NewFileLines[newFileIndex] != s.Substring(1)) throw new InvalidOperationException("Programming error."); newFileIndex++; } else if (s.StartsWith("-", StringComparison.Ordinal)) { if (NewFileLines[newFileIndex] != s.Substring(1)) throw new InvalidOperationException("Programming error."); NewFileLines.RemoveAt(newFileIndex); LinesRemoved++; } else if (s.StartsWith("+", StringComparison.Ordinal)) { NewFileLines.Insert(newFileIndex, s.Substring(1)); newFileIndex++; LinesAdded++; } } _fenceIndex = newFileIndex - 1; } /// <summary> /// Method to compare the lines in a .diff hunk with some lines in the new file to see if /// they match. Added lines are ignored, but unchanged and removed lines must match /// exactly, and there must be at least one unchanged or removed line to ensure a match. /// </summary> private bool TestHunkForMatch(DiffHunk diffHunk, int testIndex) { bool toReturn = false; // In case no unchanged or removed lines for (int i = diffHunk.DiffLineIndex; i < diffHunk.DiffLineIndex + diffHunk.HunkLines; i++) { string s = diffHunk.DiffLines[i]; if (s.StartsWith("+", StringComparison.Ordinal)) continue; if (testIndex >= NewFileLines.Count || NewFileLines[testIndex] != s.Substring(1)) return false; toReturn = true; testIndex++; } return toReturn; } } // Strange line that GNU diff.exe produces for input that it doesn't like private const string CNoNewlineAtEndOfFile = @"\ No newline at end of file"; private static string _roslynPathOldDiff; private static string _roslynPathNewCurrent; /// <summary> /// Program entry point. Two arguments are needed - see above. /// </summary> public static void Main(string[] args) { // Check the two required arguments if (args.Length != 2) { DisplayErrorOrInfo("Exactly two arguments are needed."); return; } string roslynPathOld = CheckDirectoryExists(args[0]); string roslynPathNew = CheckDirectoryExists(args[1]); if (roslynPathOld == null || roslynPathNew == null) return; // Check the two expected sub-folders exist _roslynPathOldDiff = GetAndCheckCombinedPath(roslynPathOld, "Diff"); _roslynPathNewCurrent = GetAndCheckCombinedPath(roslynPathNew, "Current"); if (_roslynPathOldDiff == null || _roslynPathNewCurrent == null) return; // Process all of the files in the "Diffs" sub-folder of the old revision WalkDirectoryTree(new DirectoryInfo(_roslynPathOldDiff)); DisplayErrorOrInfo("End of Fuzzy Patch program."); } /// <summary> /// Recursive method to "walk a directory tree", performing the specialized processing needed /// to apply .diff files to a new revision of Roslyn, and also to copy added files to the new /// revision. This is somewhat based on code found here: /// https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/file-system/how-to-iterate-through-a-directory-tree /// </summary> [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] private static void WalkDirectoryTree(DirectoryInfo directoryInfo) { // First, process all the files directly under this folder FileInfo[] filesArray; try { filesArray = directoryInfo.GetFiles(); } // Catch all possible exceptions. This includes the possible problem that one of the files // requires permissions greater than the application provides. catch (Exception e) { // Display an error message and exit DisplayErrorOrInfo(e.Message); return; } foreach (FileInfo oldDiffFileInfo in filesArray) { string oldFileFullName = oldDiffFileInfo.FullName; string newFileFullName = oldFileFullName.Replace(_roslynPathOldDiff, _roslynPathNewCurrent); // Test for .diff file. If not, just copy the file to the new Roslyn revision. if (oldDiffFileInfo.Extension.ToUpperInvariant() != ".DIFF") { File.Copy(oldFileFullName, newFileFullName, true); Console.WriteLine("Added file copied to new revision: " + newFileFullName); continue; } // Do "fuzzy patch" processing for .diff file, after first checking the corresponding // source file still exists in the new revision of Roslyn newFileFullName = newFileFullName.Remove(newFileFullName.Length - ".DIFF".Length); if (File.Exists(newFileFullName)) DoFuzzyPatch(oldFileFullName, newFileFullName); else DisplayErrorOrInfo("File to be modified not found: " + newFileFullName); } // Now find all the sub-directories under this directory foreach (DirectoryInfo subDirectoryInfo in directoryInfo.GetDirectories()) { // Recursive call for each sub-directory WalkDirectoryTree(subDirectoryInfo); } } /// <summary> /// Method to apply a .diff file to a new Roslyn source file. A small amount of "fuzziness" is /// accepted to take into account that the new file may have been updated in a new revision. /// But the lines noted as unchanged and removed must not have been changed by the revision. /// </summary> private static void DoFuzzyPatch(string diffFileFilename, string newFileFilename) { // Read the two files into storage as lines of text string[] diffLines = File.ReadAllLines(diffFileFilename); NewFile newFile = new NewFile(File.ReadAllLines(newFileFilename)); // Check the "new" file has not already been updated once. (This assumes .cs files will // have C# comments and/or identifier names that include "Yacks", and .csproj files will // reference the YacksCore assembly.) if (newFile.NewFileLines.Exists((string x) => x.Contains("Yacks"))) { Console.WriteLine("File has already been updated once: " + newFileFilename); return; } // Check first two lines in .diff file look like they should, "---" and "+++" if (diffLines.Length < 3 || diffLines[0].Substring(0, 3) != "---" || diffLines[1].Substring(0, 3) != "+++") { DisplayErrorOrInfo("Corrupt .diff file, invalid prefix lines: " + diffFileFilename); return; } int diffIndex = 2; // Current zero-based location in the .diff file // Loop to process the "hunks" in the .diff file while (true) { // Find and check the next "hunk" in the .diff file DiffHunk diffHunk = GetNextDiffHunk(diffFileFilename, diffLines, ref diffIndex); if (diffHunk == null) return; // Error encountered if (diffHunk.DiffLines == null) break; // No more hunks in .diff file // Try to find the location in the new file that matches this hunk int newFileIndex = newFile.FindHunkLocation(diffHunk); if (newFileIndex == -1) { DisplayErrorOrInfo("Unable to find location to apply .diff file 'hunk' at line " + diffHunk.DiffLineIndex + " for file " + diffFileFilename); return; } // Apply the .diff file "hunk" newFile.ApplyHunk(diffHunk, newFileIndex); } // Processing was successful if we get to here. Write the result to the disk and display // some info on the console. WriteToDisk(newFileFilename, newFile.NewFileLines); Console.WriteLine("File in new revision updated: " + newFileFilename); Console.WriteLine(string.Format(CultureInfo.InvariantCulture, " Lines removed = {0}, lines added = {1}, displacement = {2}.", newFile.LinesRemoved, newFile.LinesAdded, newFile.DisplacementFactor)); } /// <summary> /// Method to do preliminary processing to find the next "hunk" in the .diff file. /// </summary> /// <returns>DiffHunk object, or null if something wrong (error message displayed)</returns> private static DiffHunk GetNextDiffHunk(string diffFile, string[] diffLines, ref int diffIndex) { // Test for end of .diff file - no more hunks if (diffIndex >= diffLines.Length || diffLines[diffIndex] == CNoNewlineAtEndOfFile) return new DiffHunk(null, 0, 0, 0); // Get and check the hunk header line. The only item actually used is the "from file line // number start". string s = diffLines[diffIndex].Trim(); if (!s.StartsWith("@@ -", StringComparison.Ordinal) || !s.EndsWith(" @@", StringComparison.Ordinal)) { BadHunkHeaderOrContent(diffFile, diffIndex); return null; } string[] sa = s.Substring(4).Split(new char[] { ',', ' ' }); int fromFileLineNumber; if (!int.TryParse(sa[0], out fromFileLineNumber)) { BadHunkHeaderOrContent(diffFile, diffIndex); return null; } // Scan the hunk lines (if any), they must all start with " ", "-" or "+" int diffLineIndex = ++diffIndex; for (; diffIndex < diffLines.Length; diffIndex++) { s = diffLines[diffIndex]; if (s.StartsWith(" ", StringComparison.Ordinal) || s.StartsWith("-", StringComparison.Ordinal) || s.StartsWith("+", StringComparison.Ordinal)) continue; if (s.StartsWith("@@", StringComparison.Ordinal) || s == CNoNewlineAtEndOfFile) break; BadHunkHeaderOrContent(diffFile, diffIndex); return null; } // Preliminary scan indicates OK hunk return new DiffHunk(diffLines, diffLineIndex, diffIndex - diffLineIndex, fromFileLineNumber); } /// <summary> /// Method to display an error message for a corrupt hunk header line. /// </summary> private static void BadHunkHeaderOrContent(string diffFile, int diffIndex) { DisplayErrorOrInfo("Corrupt .diff file at line " + (diffIndex + 1) + ": " + diffFile); } /// <summary> /// Method to generate a sub-folder path and check that it exists. /// </summary> private static string GetAndCheckCombinedPath(string roslynPathBase, string diffOrCurrent) { return CheckDirectoryExists(Path.Combine(roslynPathBase, diffOrCurrent)); } /// <summary> /// Method to check a directory exists, and display an error message if not. /// </summary> private static string CheckDirectoryExists(string roslynPath) { if (Directory.Exists(roslynPath)) return roslynPath; DisplayErrorOrInfo("Directory does not exist: " + roslynPath); return null; } /// <summary> /// Method to write lines of text to the disk, specifying that the newline sequence is just /// LF, not CRLF, and the encoding is UTF-8 with BOM. /// </summary> private static void WriteToDisk(string fileName, IEnumerable<string> linesOfText) { using (StreamWriter streamWriter = new StreamWriter(fileName, false, new UTF8Encoding(true))) { streamWriter.NewLine = "\n"; foreach (string oneLine in linesOfText) streamWriter.WriteLine(oneLine); } } /// <summary> /// Method to display an error or information message on the console window. /// </summary> private static void DisplayErrorOrInfo(string textString) { Console.WriteLine(textString); Console.ReadKey(); } } }

So, do they work?

When I run the Generate Diff program I get this output:

GenerateDiff Snap1

And when I examine the output files I see this:

GenerateDiff Snap2

So for this point in time for my Roslyn modifications, the Generate Diff program discovered 9 modified files (and produced .diff files for them), and 8 files that had been added (and copied them to the "Diff" directory).

Then I downloaded Roslyn revision 33284 to my hard disk and (with much difficulty) was able to get a clean build. Then running my Fuzzy Patch program produced this output (highlighting added):

FuzzyPatch Snap1

In other words, the "fuzzy patching" failed for 2 of the .diff files but worked for the other 7. And the 8 added files were copied into position - this should always just work.

Manually copying the modifications to the new Compilation.cs file was fairly easy, and updating the PublicAPI.Unshipped.txt file was best done by "fixing" the RS0016 errors in the C# program using the light bulb drop-down.

At this point the Roslyn modifications had been successfully transferred from one revision of Roslyn to another. So yes, the programs worked as expected.

Later: I've now determined that updating the PublicAPI.Unshipped.txt file is totally unnecessary - a class declared as internal in the CodeAnalysis project can be used in the CSharpCodeAnalysis project, so defining public classes and fields is not necessary.

You must login to post a comment.
Loading comment... The comment will be refreshed after 00:00.

Be the first to comment.