From 9671ba1de062b4e13f159a4d1798e87042e85e6f Mon Sep 17 00:00:00 2001 From: Peter Huene Date: Fri, 8 Dec 2017 13:52:56 -0800 Subject: [PATCH] Write UTF-8 BOM for solution files. Currently the solution file written out by the `sln` command uses a UTF-8 encoding without a BOM. This causes problems when the solution file contains non-ASCII code points because Visual Studio and MSBuild will not use a UTF-8 encoding when reading the solution file if the BOM is omitted. This commit causes the BOM to always be written when writing the solution files. Fixes #8184. --- .../SlnFile.cs | 3 +- .../dotnet-sln-add.Tests/GivenDotnetSlnAdd.cs | 28 +++++++++++++++++++ .../GivenDotnetSlnRemove.cs | 27 ++++++++++++++++++ 3 files changed, 57 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.DotNet.Cli.Sln.Internal/SlnFile.cs b/src/Microsoft.DotNet.Cli.Sln.Internal/SlnFile.cs index d81d579d2..fa89ce258 100644 --- a/src/Microsoft.DotNet.Cli.Sln.Internal/SlnFile.cs +++ b/src/Microsoft.DotNet.Cli.Sln.Internal/SlnFile.cs @@ -33,6 +33,7 @@ using System.IO; using System.Collections; using System.Globalization; using System.Reflection; +using System.Text; using Microsoft.DotNet.Cli.Sln.Internal.FileManipulation; using Microsoft.DotNet.Tools.Common; @@ -211,7 +212,7 @@ namespace Microsoft.DotNet.Cli.Sln.Internal } var sw = new StringWriter(); Write(sw); - File.WriteAllText(FullPath, sw.ToString()); + File.WriteAllText(FullPath, sw.ToString(), Encoding.UTF8); } private void Write(TextWriter writer) diff --git a/test/dotnet-sln-add.Tests/GivenDotnetSlnAdd.cs b/test/dotnet-sln-add.Tests/GivenDotnetSlnAdd.cs index 13fe61606..346fd6638 100644 --- a/test/dotnet-sln-add.Tests/GivenDotnetSlnAdd.cs +++ b/test/dotnet-sln-add.Tests/GivenDotnetSlnAdd.cs @@ -9,6 +9,7 @@ using Microsoft.DotNet.Tools.Test.Utilities; using System; using System.IO; using System.Linq; +using System.Text; using Xunit; using Xunit.Abstractions; @@ -554,6 +555,33 @@ EndGlobal cmd.StdErr.Should().BeEmpty(); } + [Fact] + public void WhenProjectIsAddedSolutionHasUTF8BOM() + { + var projectDirectory = TestAssets + .Get("TestAppWithEmptySln") + .CreateInstance() + .WithSourceFiles() + .Root + .FullName; + + var projectToAdd = "Lib/Lib.csproj"; + var projectPath = Path.Combine("Lib", "Lib.csproj"); + var cmd = new DotnetCommand() + .WithWorkingDirectory(projectDirectory) + .ExecuteWithCapturedOutput($"sln App.sln add {projectToAdd}"); + cmd.Should().Pass(); + + var preamble = Encoding.UTF8.GetPreamble(); + preamble.Length.Should().Be(3); + using (var stream = new FileStream(Path.Combine(projectDirectory, "App.sln"), FileMode.Open)) + { + var bytes = new byte[preamble.Length]; + stream.Read(bytes, 0, bytes.Length); + bytes.Should().BeEquivalentTo(preamble); + } + } + [Theory] [InlineData("TestAppWithSlnAndCsprojFiles")] [InlineData("TestAppWithSlnAndCsprojProjectGuidFiles")] diff --git a/test/dotnet-sln-remove.Tests/GivenDotnetSlnRemove.cs b/test/dotnet-sln-remove.Tests/GivenDotnetSlnRemove.cs index 50c6f00e5..0b9ee7fa2 100644 --- a/test/dotnet-sln-remove.Tests/GivenDotnetSlnRemove.cs +++ b/test/dotnet-sln-remove.Tests/GivenDotnetSlnRemove.cs @@ -8,6 +8,7 @@ using Microsoft.DotNet.Tools.Test.Utilities; using System; using System.IO; using System.Linq; +using System.Text; using Xunit; namespace Microsoft.DotNet.Cli.Sln.Remove.Tests @@ -590,6 +591,32 @@ EndGlobal .Count().Should().Be(1, $"App {reasonString}"); } + [Fact] + public void WhenProjectIsRemovedSolutionHasUTF8BOM() + { + var projectDirectory = TestAssets + .Get("TestAppWithSlnAndCsprojToRemove") + .CreateInstance() + .WithSourceFiles() + .Root + .FullName; + + var projectToRemove = Path.Combine("Lib", "Lib.csproj"); + var cmd = new DotnetCommand() + .WithWorkingDirectory(projectDirectory) + .ExecuteWithCapturedOutput($"sln App.sln remove {projectToRemove}"); + cmd.Should().Pass(); + + var preamble = Encoding.UTF8.GetPreamble(); + preamble.Length.Should().Be(3); + using (var stream = new FileStream(Path.Combine(projectDirectory, "App.sln"), FileMode.Open)) + { + var bytes = new byte[preamble.Length]; + stream.Read(bytes, 0, bytes.Length); + bytes.Should().BeEquivalentTo(preamble); + } + } + [Fact] public void WhenFinalReferenceIsRemovedEmptySectionsAreRemoved() {