diff --git a/.gitignore b/.gitignore index b5ca78e..9e96a35 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +tools/ +*.nupkg + ################# ## Eclipse ################# diff --git a/CSharpMinifier.GUI/App.config b/CSharpMinifier.GUI/App.config index de68365..6de17de 100644 --- a/CSharpMinifier.GUI/App.config +++ b/CSharpMinifier.GUI/App.config @@ -1,12 +1,12 @@ - +
- - + + @@ -72,36 +72,4 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/CSharpMinifier.GUI/CSharpMinifier.GUI.csproj b/CSharpMinifier.GUI/CSharpMinifier.GUI.csproj index 3172442..110bea7 100644 --- a/CSharpMinifier.GUI/CSharpMinifier.GUI.csproj +++ b/CSharpMinifier.GUI/CSharpMinifier.GUI.csproj @@ -9,7 +9,7 @@ Properties CSharpMinifier.GUI CSharpMinifier.GUI - v4.6 + v4.6.1 512 @@ -35,6 +35,9 @@ 4 false + + true + @@ -76,20 +79,20 @@ - - - {a26c936c-846b-4165-9574-42c74075d4cd} - CSharpMinifier - - + + + {a26c936c-846b-4165-9574-42c74075d4cd} + CSharpMinifier + + - - \ No newline at end of file + + diff --git a/CSharpMinifier.Tests/MinifierTests.cs b/CSharpMinifier.Tests/MinifierTests.cs index 070fc7e..b33245c 100644 --- a/CSharpMinifier.Tests/MinifierTests.cs +++ b/CSharpMinifier.Tests/MinifierTests.cs @@ -10,16 +10,24 @@ public class MinifierTests { Dictionary Samples; + string _cscPath; + + bool CanCompile(string program) => + CompileUtils.CanCompile(program, _cscPath); + [SetUp] public void Init() { + var baseDirectory = System.AppDomain.CurrentDomain.BaseDirectory; + _cscPath = Path.Combine(baseDirectory, "..", "..", "..", "..", "tools", "Microsoft.Net.Compilers", "tools", "csc.exe"); + Samples = new Dictionary(); - var sampleFiles = Directory.GetFiles($"{System.AppDomain.CurrentDomain.BaseDirectory}..\\..\\Samples"); + var sampleFiles = Directory.GetFiles(Path.Combine(baseDirectory, "..", "..", "..", "Samples")); foreach (var file in sampleFiles) { var code = File.ReadAllText(file); Samples.Add(Path.GetFileNameWithoutExtension(file), code); - if (!CompileUtils.CanCompile(code)) + if (!CanCompile(code)) Assert.Inconclusive("All input code should be compilied"); } } @@ -35,7 +43,7 @@ public void RemoveSpaces() foreach (var sample in Samples) { var minified = minifier.MinifyFromString(sample.Value); - Assert.IsTrue(CompileUtils.CanCompile(minified)); + Assert.IsTrue(CanCompile(minified)); if (sample.Key == "Test1") Assert.IsFalse(minified.Contains(" /*")); } @@ -55,7 +63,7 @@ public void LineLengthConstraint() foreach (var sample in Samples) { var minified = minifier.MinifyFromString(sample.Value); - Assert.IsTrue(CompileUtils.CanCompile(minified)); + Assert.IsTrue(CanCompile(minified)); } } @@ -73,7 +81,7 @@ public void RemoveComments() if (!test.Contains("//") || !test.Contains("/*") || !test.Contains("*/")) Assert.Inconclusive("Invalid test sample for RemoveComments test"); var minified = minifier.MinifyFromString(test); - Assert.IsTrue(CompileUtils.CanCompile(minified)); + Assert.IsTrue(CanCompile(minified)); Assert.IsFalse(minified.Contains("//")); Assert.IsFalse(minified.Contains("/*")); Assert.IsFalse(minified.Contains("*/")); @@ -93,7 +101,7 @@ public void RemoveRegions() if (!test.Contains("#region") || !test.Contains("#endregion")) Assert.Inconclusive("Invalid test sample for RemoveRegions test"); var minified = minifier.MinifyFromString(test); - Assert.IsTrue(CompileUtils.CanCompile(minified)); + Assert.IsTrue(CanCompile(minified)); Assert.IsFalse(minified.Contains("#region")); Assert.IsFalse(minified.Contains("#endregion")); } @@ -111,7 +119,7 @@ public void CompressIdentifiers() foreach (var sample in Samples) { var minified = minifier.MinifyFromString(sample.Value); - Assert.IsTrue(CompileUtils.CanCompile(minified)); + Assert.IsTrue(CanCompile(minified)); } } @@ -199,7 +207,7 @@ public void ShouldProperlyConvertEnumWithoutInitializersToInt() var minifier = new RoslynMinifier(); var minified = minifier.MinifyFromString(Samples["EnumToIntConversion"]); - Assert.IsTrue(CompileUtils.CanCompile(minified)); + Assert.IsTrue(CanCompile(minified)); Assert.AreEqual( "using System.Collections.Generic;class c{static int a=5;static Dictionary>b=new Dictionary>{{5,new Dictionary{{' ',8}}},{6,new Dictionary{{' ',24}}}};}", minified); diff --git a/CSharpMinifier.Tests/Properties/AssemblyInfo.cs b/CSharpMinifier.Tests/Properties/AssemblyInfo.cs deleted file mode 100644 index 5a918ca..0000000 --- a/CSharpMinifier.Tests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("CSharpMinifier.Tests")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("CSharpMinifier.Tests")] -[assembly: AssemblyCopyright("Copyright © 2013")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("8f63cfda-b2cf-4ab1-81e2-0c6c7e9ce794")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/CSharpMinifier.sln b/CSharpMinifier.sln index 01fcdfe..f2ea04f 100644 --- a/CSharpMinifier.sln +++ b/CSharpMinifier.sln @@ -1,13 +1,18 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.31101.0 +# Visual Studio 15 +VisualStudioVersion = 15.0.28307.168 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CSharpMinifier", "CSharpMinifier\CSharpMinifier.csproj", "{A26C936C-846B-4165-9574-42C74075D4CD}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSharpMinifier", "CSharpMinifier\CSharpMinifier.csproj", "{A26C936C-846B-4165-9574-42C74075D4CD}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CSharpMinifier.GUI", "CSharpMinifier.GUI\CSharpMinifier.GUI.csproj", "{1E6C7AB5-3E2F-4F5E-B5BD-B24E2258D0E2}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CSharpMinifier.Tests", "CSharpMinifier.Tests\CSharpMinifier.Tests.csproj", "{24A6BB34-4BE9-4504-926D-16D52FE5AC41}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSharpMinifier.Tests", "CSharpMinifier.Tests\CSharpMinifier.Tests.csproj", "{24A6BB34-4BE9-4504-926D-16D52FE5AC41}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D6B4D5F0-B989-40F3-9F60-0D96701A1103}" + ProjectSection(SolutionItems) = preProject + global.json = global.json + EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -31,4 +36,7 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {A1AE996D-FA41-4FA8-B2F4-5AA1AF1719F4} + EndGlobalSection EndGlobal diff --git a/CSharpMinifier/CSharpMinifier.csproj b/CSharpMinifier/CSharpMinifier.csproj index 3f06b24..599c39f 100644 --- a/CSharpMinifier/CSharpMinifier.csproj +++ b/CSharpMinifier/CSharpMinifier.csproj @@ -1,192 +1,17 @@ - - - + + - Debug - AnyCPU - {A26C936C-846B-4165-9574-42C74075D4CD} - Library - Properties - CSharpMinifier - CSharpMinifier - v4.6 - 512 - - + netstandard2.0;net461 + true + ../dist - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - false - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - false - - - - ..\packages\Microsoft.CodeAnalysis.Common.2.7.0\lib\netstandard1.3\Microsoft.CodeAnalysis.dll - - - ..\packages\Microsoft.CodeAnalysis.CSharp.2.7.0\lib\netstandard1.3\Microsoft.CodeAnalysis.CSharp.dll - - - ..\packages\Microsoft.CodeAnalysis.CSharp.Workspaces.2.7.0\lib\netstandard1.3\Microsoft.CodeAnalysis.CSharp.Workspaces.dll - - - ..\packages\Microsoft.CodeAnalysis.VisualBasic.2.7.0\lib\netstandard1.3\Microsoft.CodeAnalysis.VisualBasic.dll - - - ..\packages\Microsoft.CodeAnalysis.VisualBasic.Workspaces.2.7.0\lib\netstandard1.3\Microsoft.CodeAnalysis.VisualBasic.Workspaces.dll - - - ..\packages\Microsoft.CodeAnalysis.Workspaces.Common.2.7.0\lib\net46\Microsoft.CodeAnalysis.Workspaces.dll - - - ..\packages\Microsoft.CodeAnalysis.Workspaces.Common.2.7.0\lib\net46\Microsoft.CodeAnalysis.Workspaces.Desktop.dll - - - False - ..\packages\Mono.Cecil.0.9.5.4\lib\net40\Mono.Cecil.dll - - - False - ..\packages\Mono.Cecil.0.9.5.4\lib\net40\Mono.Cecil.Mdb.dll - - - False - ..\packages\Mono.Cecil.0.9.5.4\lib\net40\Mono.Cecil.Pdb.dll - - - False - ..\packages\Mono.Cecil.0.9.5.4\lib\net40\Mono.Cecil.Rocks.dll - - - - ..\packages\System.AppContext.4.3.0\lib\net46\System.AppContext.dll - True - - - ..\packages\System.Collections.Immutable.1.3.1\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll - True - - - - ..\packages\System.Composition.AttributedModel.1.0.31\lib\portable-net45+win8+wp8+wpa81\System.Composition.AttributedModel.dll - - - ..\packages\System.Composition.Convention.1.0.31\lib\portable-net45+win8+wp8+wpa81\System.Composition.Convention.dll - - - ..\packages\System.Composition.Hosting.1.0.31\lib\portable-net45+win8+wp8+wpa81\System.Composition.Hosting.dll - - - ..\packages\System.Composition.Runtime.1.0.31\lib\portable-net45+win8+wp8+wpa81\System.Composition.Runtime.dll - - - ..\packages\System.Composition.TypedParts.1.0.31\lib\portable-net45+win8+wp8+wpa81\System.Composition.TypedParts.dll - - - ..\packages\System.Console.4.3.0\lib\net46\System.Console.dll - - - - ..\packages\System.Diagnostics.FileVersionInfo.4.3.0\lib\net46\System.Diagnostics.FileVersionInfo.dll - - - ..\packages\System.Diagnostics.StackTrace.4.3.0\lib\net46\System.Diagnostics.StackTrace.dll - - - ..\packages\System.IO.Compression.4.3.0\lib\net46\System.IO.Compression.dll - True - - - ..\packages\System.IO.FileSystem.4.3.0\lib\net46\System.IO.FileSystem.dll - - - ..\packages\System.IO.FileSystem.Primitives.4.3.0\lib\net46\System.IO.FileSystem.Primitives.dll - - - - ..\packages\System.Reflection.Metadata.1.4.2\lib\portable-net45+win8\System.Reflection.Metadata.dll - - - ..\packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll - True - - - ..\packages\System.Security.Cryptography.Algorithms.4.3.0\lib\net46\System.Security.Cryptography.Algorithms.dll - True - - - ..\packages\System.Security.Cryptography.Encoding.4.3.0\lib\net46\System.Security.Cryptography.Encoding.dll - - - ..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll - - - ..\packages\System.Security.Cryptography.X509Certificates.4.3.0\lib\net46\System.Security.Cryptography.X509Certificates.dll - True - - - ..\packages\System.Text.Encoding.CodePages.4.3.0\lib\net46\System.Text.Encoding.CodePages.dll - - - ..\packages\System.Threading.Thread.4.3.0\lib\net46\System.Threading.Thread.dll - - - ..\packages\System.ValueTuple.4.3.0\lib\netstandard1.0\System.ValueTuple.dll - - - - - ..\packages\System.Xml.ReaderWriter.4.3.0\lib\net46\System.Xml.ReaderWriter.dll - - - ..\packages\System.Xml.XmlDocument.4.3.0\lib\net46\System.Xml.XmlDocument.dll - - - ..\packages\System.Xml.XPath.4.3.0\lib\net46\System.Xml.XPath.dll - - - ..\packages\System.Xml.XPath.XDocument.4.3.0\lib\net46\System.Xml.XPath.XDocument.dll - - - - - - - - - - - + - - - Designer - + + - - + - - - \ No newline at end of file + + diff --git a/CSharpMinifier/CompileUtils.cs b/CSharpMinifier/CompileUtils.cs index 8ed8029..900b7a1 100644 --- a/CSharpMinifier/CompileUtils.cs +++ b/CSharpMinifier/CompileUtils.cs @@ -1,27 +1,135 @@ -using Microsoft.CSharp; -using System.CodeDom.Compiler; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; namespace CSharpMinifier { - public class CompileUtils - { - public static bool CanCompile(string program) - { - return !Compile(program).Errors.HasErrors; - } - - public static CompilerResults Compile(string program) - { - CompilerResults compilerResults = null; - using (CSharpCodeProvider provider = new CSharpCodeProvider()) - { - compilerResults = provider.CompileAssemblyFromSource(new CompilerParameters(new string[] - { - "System.dll" - }), - new string[] { program }); - } - return compilerResults; - } - } + using System; + using System.Collections.Generic; + + /// + /// Emulates System.CodeDom.Compiler.CompilerError. + /// + + public sealed class CompilerError + { + public string ErrorNumber { get; } + public string ErrorText { get; } + public string FileName { get; } + public bool IsWarning { get; } + public int Line { get; } + public int Column { get; } + + public CompilerError(bool isWarning, string errorNumber, string errorText, + string fileName, int line, int column) + { + IsWarning = isWarning; + ErrorNumber = errorNumber; + ErrorText = errorText; + FileName = fileName; + Line = line; + Column = column; + } + + public override string ToString() => + $"{FileName}({Line},{Column}): {(IsWarning ? "warning" : "error")} {ErrorNumber}: {ErrorText}"; + } + + /// + /// Emulates System.CodeDom.Compiler.CompilerErrorCollection. + /// + + public sealed class CompilerErrorCollection : Collection + { + public CompilerErrorCollection(IEnumerable errors) : + base(Array.AsReadOnly(errors.ToArray())) {} + + public bool HasErrors => this.Any(e => !e.IsWarning); + public bool HasWarnings => this.Any(e => e.IsWarning); + } + + /// + /// Emulates System.CodeDom.Compiler.CompilerResults. + /// + + public class CompilerResults + { + internal CompilerResults(IEnumerable errors, + int nativeCompilerReturnValue, + IList output) + { + Errors = new CompilerErrorCollection(errors); + NativeCompilerReturnValue = nativeCompilerReturnValue; + Output = new ReadOnlyCollection(output); + } + + public CompilerErrorCollection Errors { get; } + public int NativeCompilerReturnValue { get; } + public IReadOnlyCollection Output { get; } + } + + public class CompileUtils + { + public static bool CanCompile(string program, string cscPath) + { + return !Compile(program, cscPath).Errors.HasErrors; + } + + public static CompilerResults Compile(string program, string cscPath) + { + if (!File.Exists(cscPath)) + throw new FileNotFoundException("C# compiler not found at: " + cscPath); + + var path = Path.ChangeExtension(Path.GetTempFileName(), ".cs"); + File.WriteAllText(path, program, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); + using (var process = Process.Start(new ProcessStartInfo + { + FileName = cscPath, + Arguments = path, + CreateNoWindow = true, + UseShellExecute = false, + RedirectStandardError = true, + RedirectStandardOutput = true, + })) + { + var output = new List(); + + void OnData(object sender, DataReceivedEventArgs args) + { + if (args.Data == null) + return; // EOI + lock (output) + output.Add(args.Data); + } + + process.OutputDataReceived += OnData; + process.ErrorDataReceived += OnData; + + process.BeginErrorReadLine(); + process.BeginOutputReadLine(); + process.WaitForExit(); + + var errors = + from line in output + from Match m in Regex.Matches(line, @"^(.+) *\( *([0-9]+) *, *([0-9]+) *\) *: *(error|warning) +(CS[0-9]+) *: *(.+)$") + select m.Groups.Cast() + .Skip(1) + .Select(g => g.Value) + .ToArray() + into groups + select new CompilerError(fileName: groups[0].Trim(), + line: int.Parse(groups[1], NumberStyles.None, CultureInfo.InvariantCulture), + column: int.Parse(groups[2], NumberStyles.None, CultureInfo.InvariantCulture), + isWarning: groups[3] == "warning", + errorNumber: groups[4], + errorText: groups[5].Trim()); + + return new CompilerResults(errors, process.ExitCode, output); + } + } + } } diff --git a/CSharpMinifier/Properties/AssemblyInfo.cs b/CSharpMinifier/Properties/AssemblyInfo.cs deleted file mode 100644 index 2635b8c..0000000 --- a/CSharpMinifier/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("CSharpMinifier")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("CSharpMinifier")] -[assembly: AssemblyCopyright("Copyright © 2013")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("c10cd76d-98b2-43b5-9813-035afa0051b6")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/CSharpMinifier/packages.config b/CSharpMinifier/packages.config deleted file mode 100644 index d1da915..0000000 --- a/CSharpMinifier/packages.config +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/global.json b/global.json new file mode 100644 index 0000000..21e27ce --- /dev/null +++ b/global.json @@ -0,0 +1,5 @@ +{ + "sdk": { + "version": "2.1.500" + } +} \ No newline at end of file diff --git a/install.cmd b/install.cmd new file mode 100644 index 0000000..92c0e05 --- /dev/null +++ b/install.cmd @@ -0,0 +1,3 @@ +@echo off +NuGet install Microsoft.Net.Compilers -Version 2.10.0 -OutputDirectory "%~dp0tools" ^ + && ren "%~dp0tools\Microsoft.Net.Compilers.2.10.0" Microsoft.Net.Compilers