diff --git a/Capnp.Net.sln b/Capnp.Net.sln
index 15732e6..38c77ca 100644
--- a/Capnp.Net.sln
+++ b/Capnp.Net.sln
@@ -11,11 +11,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Capnp.Net.Runtime.Tests.Std
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Capnp.Net.Runtime.Tests.Core21", "Capnp.Net.Runtime.Tests.Core21\Capnp.Net.Runtime.Tests.Core21.csproj", "{58E8FFC8-D207-4B0F-842A-58ED9D3D9EEF}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "capnpc-csharp.tests", "capnpc-csharp.tests\capnpc-csharp.tests.csproj", "{B77AC567-E232-4072-85C3-8689566BF3D4}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CapnpC.CSharp.Generator.Tests", "CapnpC.CSharp.Generator.Tests\CapnpC.CSharp.Generator.Tests.csproj", "{B77AC567-E232-4072-85C3-8689566BF3D4}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Capnpc.Csharp.MsBuild.Generation", "Capnpc.Csharp.MsBuild.Generation\Capnpc.Csharp.MsBuild.Generation.csproj", "{1EFC1F20-C7BB-4F44-8BF9-DBB123AACCF4}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CapnpC.CSharp.MsBuild.Generation", "CapnpC.CSharp.MsBuild.Generation\CapnpC.CSharp.MsBuild.Generation.csproj", "{1EFC1F20-C7BB-4F44-8BF9-DBB123AACCF4}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CapnpC.CSharp.Generator", "CapnpC.CSharp.Generator\CapnpC.CSharp.Generator.csproj", "{C3A3BB49-356E-4762-A190-76D877BE18F7}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CapnpC.CSharp.Generator", "CapnpC.CSharp.Generator\CapnpC.CSharp.Generator.csproj", "{C3A3BB49-356E-4762-A190-76D877BE18F7}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CapnpC.CSharp.MsBuild.Generation.Tests", "CapnpC.CSharp.MsBuild.Generation.Tests\CapnpC.CSharp.MsBuild.Generation.Tests.csproj", "{EF05AD68-DE31-448E-B88D-4144F928ED5D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -51,6 +53,10 @@ Global
{C3A3BB49-356E-4762-A190-76D877BE18F7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C3A3BB49-356E-4762-A190-76D877BE18F7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C3A3BB49-356E-4762-A190-76D877BE18F7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {EF05AD68-DE31-448E-B88D-4144F928ED5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {EF05AD68-DE31-448E-B88D-4144F928ED5D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {EF05AD68-DE31-448E-B88D-4144F928ED5D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {EF05AD68-DE31-448E-B88D-4144F928ED5D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/capnpc-csharp.tests/capnpc-csharp.tests.csproj b/CapnpC.CSharp.Generator.Tests/CapnpC.CSharp.Generator.Tests.csproj
similarity index 74%
rename from capnpc-csharp.tests/capnpc-csharp.tests.csproj
rename to CapnpC.CSharp.Generator.Tests/CapnpC.CSharp.Generator.Tests.csproj
index ce9a24e..5228560 100644
--- a/capnpc-csharp.tests/capnpc-csharp.tests.csproj
+++ b/CapnpC.CSharp.Generator.Tests/CapnpC.CSharp.Generator.Tests.csproj
@@ -2,7 +2,7 @@
netcoreapp2.2
- capnpc_csharp.Tests
+ CapnpC.CSharp.Generator.Tests
false
@@ -16,17 +16,18 @@
-
-
-
-
+
+
+
-
+
+
+
@@ -34,9 +35,13 @@
+
+
+
+
@@ -47,6 +52,10 @@
+
+
+
+
SpecFlowSingleFileGenerator
diff --git a/CapnpC.CSharp.Generator.Tests/CapnpMessageUnitTests.cs b/CapnpC.CSharp.Generator.Tests/CapnpMessageUnitTests.cs
new file mode 100644
index 0000000..ce21560
--- /dev/null
+++ b/CapnpC.CSharp.Generator.Tests/CapnpMessageUnitTests.cs
@@ -0,0 +1,107 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace CapnpC.CSharp.Generator.Tests
+{
+ [TestClass]
+ public class CapnpMessageUnitTests
+ {
+ [TestMethod]
+ public void ParseError()
+ {
+ var msg = new CapnpMessage(@"f:\code\invalid.capnp:5:1: error: Parse error.");
+ Assert.AreEqual(@"f:\code\invalid.capnp:5:1: error: Parse error.", msg.FullMessage);
+ Assert.IsTrue(msg.IsParseSuccess);
+ Assert.AreEqual(@"f:\code\invalid.capnp", msg.FileName);
+ Assert.AreEqual(5, msg.Line);
+ Assert.AreEqual(1, msg.Column);
+ Assert.AreEqual(0, msg.EndColumn);
+ Assert.AreEqual("error", msg.Category);
+ Assert.AreEqual("Parse error.", msg.MessageText);
+ }
+
+ [TestMethod]
+ public void ColumnSpan()
+ {
+ var msg = new CapnpMessage(@"f:\code\invalid.capnp:10:7-8: error: Duplicate ordinal number.");
+ Assert.IsTrue(msg.IsParseSuccess);
+ Assert.AreEqual(@"f:\code\invalid.capnp", msg.FileName);
+ Assert.AreEqual(10, msg.Line);
+ Assert.AreEqual(7, msg.Column);
+ Assert.AreEqual(8, msg.EndColumn);
+ Assert.AreEqual("error", msg.Category);
+ Assert.AreEqual("Duplicate ordinal number.", msg.MessageText);
+ }
+
+ [TestMethod]
+ public void NoSuchFile()
+ {
+ var msg = new CapnpMessage(@"C:\ProgramData\chocolatey\lib\capnproto\tools\capnproto-tools-win32-0.7.0\capnp.exe compile: doesnotexist.capnp: no such file");
+ Assert.IsFalse(msg.IsParseSuccess);
+ Assert.AreEqual(@"C:\ProgramData\chocolatey\lib\capnproto\tools\capnproto-tools-win32-0.7.0\capnp.exe compile: doesnotexist.capnp: no such file", msg.FullMessage);
+ }
+
+ [TestMethod]
+ public void NoId()
+ {
+ var msg = new CapnpMessage(@"empty.capnp:1:1: error: File does not declare an ID. I've generated one for you. Add this line to your file: @0xc82955a0c779197d;");
+ Assert.IsTrue(msg.IsParseSuccess);
+ Assert.AreEqual("empty.capnp", msg.FileName);
+ Assert.AreEqual(1, msg.Line);
+ Assert.AreEqual(1, msg.Column);
+ Assert.AreEqual("error", msg.Category);
+ Assert.AreEqual("File does not declare an ID. I've generated one for you. Add this line to your file: @0xc82955a0c779197d;", msg.MessageText);
+ }
+
+ [TestMethod]
+ public void AnnoyingNTFSAlternateDataStream1()
+ {
+ var msg = new CapnpMessage(@"3:2:1:1: error: File does not declare an ID. I've generated one for you. Add this line to your file: @0xc82955a0c779197d;");
+ Assert.IsTrue(msg.IsParseSuccess);
+ Assert.AreEqual("3:2", msg.FileName);
+ Assert.AreEqual(1, msg.Line);
+ Assert.AreEqual(1, msg.Column);
+ Assert.AreEqual("error", msg.Category);
+ Assert.AreEqual("File does not declare an ID. I've generated one for you. Add this line to your file: @0xc82955a0c779197d;", msg.MessageText);
+ }
+
+ [TestMethod]
+ public void AnnoyingNTFSAlternateDataStream2()
+ {
+ var msg = new CapnpMessage(@"c:\3:2:1:1: error: File does not declare an ID. I've generated one for you. Add this line to your file: @0xc82955a0c779197d;");
+ Assert.IsTrue(msg.IsParseSuccess);
+ Assert.AreEqual(@"c:\3:2", msg.FileName);
+ Assert.AreEqual(1, msg.Line);
+ Assert.AreEqual(1, msg.Column);
+ Assert.AreEqual("error", msg.Category);
+ Assert.AreEqual("File does not declare an ID. I've generated one for you. Add this line to your file: @0xc82955a0c779197d;", msg.MessageText);
+ }
+
+ [TestMethod]
+ public void AnnoyingNTFSAlternateDataStream3()
+ {
+ var msg = new CapnpMessage(@"\\?\c:\3:2:1:1: error: File does not declare an ID. I've generated one for you. Add this line to your file: @0xc82955a0c779197d;");
+ Assert.IsTrue(msg.IsParseSuccess);
+ Assert.AreEqual(@"\\?\c:\3:2", msg.FileName);
+ Assert.AreEqual(1, msg.Line);
+ Assert.AreEqual(1, msg.Column);
+ Assert.AreEqual("error", msg.Category);
+ Assert.AreEqual("File does not declare an ID. I've generated one for you. Add this line to your file: @0xc82955a0c779197d;", msg.MessageText);
+ }
+
+ [TestMethod]
+ public void AnnoyingNTFSAlternateDataStream4()
+ {
+ var msg = new CapnpMessage(@"1:2-3:10:7-8: error: Duplicate ordinal number.");
+ Assert.IsTrue(msg.IsParseSuccess);
+ Assert.AreEqual(@"1:2-3", msg.FileName);
+ Assert.AreEqual(10, msg.Line);
+ Assert.AreEqual(7, msg.Column);
+ Assert.AreEqual(8, msg.EndColumn);
+ Assert.AreEqual("error", msg.Category);
+ Assert.AreEqual("Duplicate ordinal number.", msg.MessageText);
+ }
+ }
+}
diff --git a/CapnpC.CSharp.Generator.Tests/CodeGenerator.feature b/CapnpC.CSharp.Generator.Tests/CodeGenerator.feature
new file mode 100644
index 0000000..239fd3d
--- /dev/null
+++ b/CapnpC.CSharp.Generator.Tests/CodeGenerator.feature
@@ -0,0 +1,48 @@
+Feature: CodeGenerator
+ In order to ensure that the generator backend produces valid output
+ As a contributor
+ I want to get notified when there is any deviation from reference output
+
+Scenario: Comparing backend output with reference
+ Given I have a binary code generator request "test.capnp.bin"
+ And my reference output is "test.cs"
+ When I invoke capnpc-csharp
+ Then the generated output must match the reference
+
+Scenario Outline: Invalid binary code generator requests
+ Given I have a binary code generator request
+ When I invoke capnpc-csharp
+ Then the invocation must fail
+
+Examples:
+ | bin |
+ | null.bin |
+ | test.cs |
+
+Scenario: Combining frontend and backend
+ Given capnp.exe is installed on my system
+ And I have a schema "UnitTest1.capnp"
+ When I try to generate code from that schema
+ Then code generation must succeed
+
+Scenario: Missing frontend
+ Given capnp.exe is not installed on my system
+ And I have a schema "UnitTest1.capnp"
+ When I try to generate code from that schema
+ Then the invocation must fail
+
+Scenario: Schema without ID
+ Given capnp.exe is installed on my system
+ And I have a schema "Empty1.capnp"
+ When I try to generate code from that schema
+ Then the invocation must fail
+ And the reason must be bad input
+ And the error output must contain "File does not declare an ID"
+
+Scenario: Multiple errors
+ Given capnp.exe is installed on my system
+ And I have a schema "invalid.capnp"
+ When I try to generate code from that schema
+ Then the invocation must fail
+ And the reason must be bad input
+ And the error output must contain multiple messages
diff --git a/capnpc-csharp.tests/CodeGenerator.feature.cs b/CapnpC.CSharp.Generator.Tests/CodeGenerator.feature.cs
similarity index 57%
rename from capnpc-csharp.tests/CodeGenerator.feature.cs
rename to CapnpC.CSharp.Generator.Tests/CodeGenerator.feature.cs
index 74bf043..c01b9e3 100644
--- a/capnpc-csharp.tests/CodeGenerator.feature.cs
+++ b/CapnpC.CSharp.Generator.Tests/CodeGenerator.feature.cs
@@ -10,7 +10,7 @@
// ------------------------------------------------------------------------------
#region Designer generated code
#pragma warning disable
-namespace capnpc_csharp.Tests
+namespace CapnpC.CSharp.Generator.Tests
{
using TechTalk.SpecFlow;
@@ -63,7 +63,7 @@ namespace capnpc_csharp.Tests
if (((testRunner.FeatureContext != null)
&& (testRunner.FeatureContext.FeatureInfo.Title != "CodeGenerator")))
{
- global::capnpc_csharp.Tests.CodeGeneratorFeature.FeatureSetup(null);
+ global::CapnpC.CSharp.Generator.Tests.CodeGeneratorFeature.FeatureSetup(null);
}
}
@@ -149,6 +149,98 @@ this.InvalidBinaryCodeGeneratorRequests("null.bin", ((string[])(null)));
this.InvalidBinaryCodeGeneratorRequests("test.cs", ((string[])(null)));
#line hidden
}
+
+ [Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute()]
+ [Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute("Combining frontend and backend")]
+ [Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "CodeGenerator")]
+ public virtual void CombiningFrontendAndBackend()
+ {
+ TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Combining frontend and backend", null, ((string[])(null)));
+#line 22
+this.ScenarioInitialize(scenarioInfo);
+ this.ScenarioStart();
+#line 23
+ testRunner.Given("capnp.exe is installed on my system", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
+#line 24
+ testRunner.And("I have a schema \"UnitTest1.capnp\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
+#line 25
+ testRunner.When("I try to generate code from that schema", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
+#line 26
+ testRunner.Then("code generation must succeed", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
+#line hidden
+ this.ScenarioCleanup();
+ }
+
+ [Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute()]
+ [Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute("Missing frontend")]
+ [Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "CodeGenerator")]
+ public virtual void MissingFrontend()
+ {
+ TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Missing frontend", null, ((string[])(null)));
+#line 28
+this.ScenarioInitialize(scenarioInfo);
+ this.ScenarioStart();
+#line 29
+ testRunner.Given("capnp.exe is not installed on my system", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
+#line 30
+ testRunner.And("I have a schema \"UnitTest1.capnp\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
+#line 31
+ testRunner.When("I try to generate code from that schema", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
+#line 32
+ testRunner.Then("the invocation must fail", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
+#line hidden
+ this.ScenarioCleanup();
+ }
+
+ [Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute()]
+ [Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute("Schema without ID")]
+ [Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "CodeGenerator")]
+ public virtual void SchemaWithoutID()
+ {
+ TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Schema without ID", null, ((string[])(null)));
+#line 34
+this.ScenarioInitialize(scenarioInfo);
+ this.ScenarioStart();
+#line 35
+ testRunner.Given("capnp.exe is installed on my system", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
+#line 36
+ testRunner.And("I have a schema \"Empty1.capnp\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
+#line 37
+ testRunner.When("I try to generate code from that schema", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
+#line 38
+ testRunner.Then("the invocation must fail", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
+#line 39
+ testRunner.And("the reason must be bad input", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
+#line 40
+ testRunner.And("the error output must contain \"File does not declare an ID\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
+#line hidden
+ this.ScenarioCleanup();
+ }
+
+ [Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute()]
+ [Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute("Multiple errors")]
+ [Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute("FeatureTitle", "CodeGenerator")]
+ public virtual void MultipleErrors()
+ {
+ TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Multiple errors", null, ((string[])(null)));
+#line 42
+this.ScenarioInitialize(scenarioInfo);
+ this.ScenarioStart();
+#line 43
+ testRunner.Given("capnp.exe is installed on my system", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
+#line 44
+ testRunner.And("I have a schema \"invalid.capnp\"", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
+#line 45
+ testRunner.When("I try to generate code from that schema", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
+#line 46
+ testRunner.Then("the invocation must fail", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
+#line 47
+ testRunner.And("the reason must be bad input", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
+#line 48
+ testRunner.And("the error output must contain multiple messages", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
+#line hidden
+ this.ScenarioCleanup();
+ }
}
}
#pragma warning restore
diff --git a/capnpc-csharp.tests/UnitTests.cs b/CapnpC.CSharp.Generator.Tests/CodeGeneratorUnitTests.cs
similarity index 92%
rename from capnpc-csharp.tests/UnitTests.cs
rename to CapnpC.CSharp.Generator.Tests/CodeGeneratorUnitTests.cs
index e2b80d5..bfec1b7 100644
--- a/capnpc-csharp.tests/UnitTests.cs
+++ b/CapnpC.CSharp.Generator.Tests/CodeGeneratorUnitTests.cs
@@ -1,13 +1,13 @@
using Capnp;
-using Model = CapnpC.Model;
-using Generator = CapnpC.Generator;
-using CodeGeneratorRequest = CapnpC.Schema.CodeGeneratorRequest;
+using Model = CapnpC.CSharp.Generator.Model;
+using CodeGen = CapnpC.CSharp.Generator.CodeGen;
+using CodeGeneratorRequest = CapnpC.CSharp.Generator.Schema.CodeGeneratorRequest;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Linq;
-namespace capnpc_csharp.Tests
+namespace CapnpC.CSharp.Generator.Tests
{
[TestClass]
public class CodeGeneratorUnitTests
@@ -98,13 +98,13 @@ namespace capnpc_csharp.Tests
struct Run
{
public Model.SchemaModel Model;
- public Generator.CodeGenerator CodeGen;
+ public CodeGen.CodeGenerator CodeGen;
public Model.GenFile FirstFile;
public string Code;
}
- static Generator.CodeGenerator NewGeneratorFor(Model.SchemaModel model)
- => new Generator.CodeGenerator(model, new Generator.GeneratorOptions());
+ static CodeGen.CodeGenerator NewGeneratorFor(Model.SchemaModel model)
+ => new CodeGen.CodeGenerator(model, new CodeGen.GeneratorOptions());
Run LoadAndGenerate(string inputName, int? testNum = null)
{
diff --git a/CapnpC.CSharp.Generator.Tests/Embedded Resources/Empty.capnp b/CapnpC.CSharp.Generator.Tests/Embedded Resources/Empty.capnp
new file mode 100644
index 0000000..5f28270
--- /dev/null
+++ b/CapnpC.CSharp.Generator.Tests/Embedded Resources/Empty.capnp
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/CapnpC.CSharp.Generator.Tests/Embedded Resources/Empty1.capnp b/CapnpC.CSharp.Generator.Tests/Embedded Resources/Empty1.capnp
new file mode 100644
index 0000000..e02abfc
--- /dev/null
+++ b/CapnpC.CSharp.Generator.Tests/Embedded Resources/Empty1.capnp
@@ -0,0 +1 @@
+
diff --git a/capnpc-csharp.tests/No Resources/UnitTest1.capnp b/CapnpC.CSharp.Generator.Tests/Embedded Resources/UnitTest1.capnp
similarity index 100%
rename from capnpc-csharp.tests/No Resources/UnitTest1.capnp
rename to CapnpC.CSharp.Generator.Tests/Embedded Resources/UnitTest1.capnp
diff --git a/capnpc-csharp.tests/Embedded Resources/UnitTest1.capnp.bin b/CapnpC.CSharp.Generator.Tests/Embedded Resources/UnitTest1.capnp.bin
similarity index 100%
rename from capnpc-csharp.tests/Embedded Resources/UnitTest1.capnp.bin
rename to CapnpC.CSharp.Generator.Tests/Embedded Resources/UnitTest1.capnp.bin
diff --git a/capnpc-csharp.tests/Embedded Resources/UnitTest10.capnp.bin b/CapnpC.CSharp.Generator.Tests/Embedded Resources/UnitTest10.capnp.bin
similarity index 100%
rename from capnpc-csharp.tests/Embedded Resources/UnitTest10.capnp.bin
rename to CapnpC.CSharp.Generator.Tests/Embedded Resources/UnitTest10.capnp.bin
diff --git a/capnpc-csharp.tests/Embedded Resources/UnitTest11.capnp.bin b/CapnpC.CSharp.Generator.Tests/Embedded Resources/UnitTest11.capnp.bin
similarity index 100%
rename from capnpc-csharp.tests/Embedded Resources/UnitTest11.capnp.bin
rename to CapnpC.CSharp.Generator.Tests/Embedded Resources/UnitTest11.capnp.bin
diff --git a/capnpc-csharp.tests/Embedded Resources/UnitTest2.capnp.bin b/CapnpC.CSharp.Generator.Tests/Embedded Resources/UnitTest2.capnp.bin
similarity index 100%
rename from capnpc-csharp.tests/Embedded Resources/UnitTest2.capnp.bin
rename to CapnpC.CSharp.Generator.Tests/Embedded Resources/UnitTest2.capnp.bin
diff --git a/capnpc-csharp.tests/Embedded Resources/UnitTest20.capnp.bin b/CapnpC.CSharp.Generator.Tests/Embedded Resources/UnitTest20.capnp.bin
similarity index 100%
rename from capnpc-csharp.tests/Embedded Resources/UnitTest20.capnp.bin
rename to CapnpC.CSharp.Generator.Tests/Embedded Resources/UnitTest20.capnp.bin
diff --git a/capnpc-csharp.tests/Embedded Resources/UnitTest3.capnp.bin b/CapnpC.CSharp.Generator.Tests/Embedded Resources/UnitTest3.capnp.bin
similarity index 100%
rename from capnpc-csharp.tests/Embedded Resources/UnitTest3.capnp.bin
rename to CapnpC.CSharp.Generator.Tests/Embedded Resources/UnitTest3.capnp.bin
diff --git a/capnpc-csharp.tests/Embedded Resources/UnitTest4.capnp.bin b/CapnpC.CSharp.Generator.Tests/Embedded Resources/UnitTest4.capnp.bin
similarity index 100%
rename from capnpc-csharp.tests/Embedded Resources/UnitTest4.capnp.bin
rename to CapnpC.CSharp.Generator.Tests/Embedded Resources/UnitTest4.capnp.bin
diff --git a/CapnpC.CSharp.Generator.Tests/Embedded Resources/invalid.capnp b/CapnpC.CSharp.Generator.Tests/Embedded Resources/invalid.capnp
new file mode 100644
index 0000000..b56b978
--- /dev/null
+++ b/CapnpC.CSharp.Generator.Tests/Embedded Resources/invalid.capnp
@@ -0,0 +1,11 @@
+@0xa5ac546b7bf6fbbc
+
+enum Enumerant {
+ byte @0;
+ bit @1;
+}
+
+struct Foo {
+ foo @0: UInt8;
+ bar @0: UInt8;
+}
diff --git a/capnpc-csharp.tests/Embedded Resources/null.bin b/CapnpC.CSharp.Generator.Tests/Embedded Resources/null.bin
similarity index 100%
rename from capnpc-csharp.tests/Embedded Resources/null.bin
rename to CapnpC.CSharp.Generator.Tests/Embedded Resources/null.bin
diff --git a/capnpc-csharp.tests/Embedded Resources/schema-with-offsets.capnp.bin b/CapnpC.CSharp.Generator.Tests/Embedded Resources/schema-with-offsets.capnp.bin
similarity index 100%
rename from capnpc-csharp.tests/Embedded Resources/schema-with-offsets.capnp.bin
rename to CapnpC.CSharp.Generator.Tests/Embedded Resources/schema-with-offsets.capnp.bin
diff --git a/capnpc-csharp.tests/Embedded Resources/test.capnp.bin b/CapnpC.CSharp.Generator.Tests/Embedded Resources/test.capnp.bin
similarity index 100%
rename from capnpc-csharp.tests/Embedded Resources/test.capnp.bin
rename to CapnpC.CSharp.Generator.Tests/Embedded Resources/test.capnp.bin
diff --git a/capnpc-csharp.tests/Embedded Resources/test.cs b/CapnpC.CSharp.Generator.Tests/Embedded Resources/test.cs
similarity index 100%
rename from capnpc-csharp.tests/Embedded Resources/test.cs
rename to CapnpC.CSharp.Generator.Tests/Embedded Resources/test.cs
diff --git a/CapnpC.CSharp.Generator.Tests/FeatureSteps/CodeGeneratorSteps.cs b/CapnpC.CSharp.Generator.Tests/FeatureSteps/CodeGeneratorSteps.cs
new file mode 100644
index 0000000..83fc17a
--- /dev/null
+++ b/CapnpC.CSharp.Generator.Tests/FeatureSteps/CodeGeneratorSteps.cs
@@ -0,0 +1,149 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using TechTalk.SpecFlow;
+
+namespace CapnpC.CSharp.Generator.Tests
+{
+ [Binding]
+ public class CodeGeneratorSteps
+ {
+ Stream _inputStream;
+ string _inputSchemaFileName;
+ string _inputSchema;
+ string _referenceOutputContent;
+
+ GenerationResult _result;
+
+ public static Stream LoadResource(string name)
+ {
+ var assembly = Assembly.GetExecutingAssembly();
+ string[] names = assembly.GetManifestResourceNames();
+ string urn = Array.Find(names, n => n.EndsWith(name, StringComparison.OrdinalIgnoreCase));
+ Assert.IsNotNull(urn, $"Test specification error: {name} does not exist");
+ return assembly.GetManifestResourceStream(urn);
+ }
+
+ internal static bool IsCapnpExeInstalled()
+ {
+ using (var process = Process.Start("where", "capnp.exe"))
+ {
+ if (process == null)
+ Assert.Fail("Unable to start 'where'");
+
+ process.WaitForExit();
+
+ return process.ExitCode == 0;
+ }
+ }
+
+ [Given(@"I have a binary code generator request ""(.*)""")]
+ [Given(@"I have a binary code generator request (.*)")]
+ public void GivenIHaveABinaryCodeGeneratorRequest(string binaryRequestFileName)
+ {
+ _inputStream = LoadResource(binaryRequestFileName);
+ }
+
+ [Given(@"my reference output is ""(.*)""")]
+ public void GivenMyReferenceOutputIs(string expectedOutputFileName)
+ {
+ using (var stream = LoadResource(expectedOutputFileName))
+ using (var reader = new StreamReader(stream))
+ {
+ _referenceOutputContent = reader.ReadToEnd();
+ }
+ }
+
+ [When(@"I invoke capnpc-csharp")]
+ public void WhenIInvokeCapnpc_Csharp()
+ {
+ using (_inputStream)
+ {
+ _result = CapnpCompilation.GenerateFromStream(_inputStream);
+ }
+ }
+
+ [Then(@"the generated output must match the reference")]
+ public void ThenTheGeneratedOutputMustMatchTheReference()
+ {
+ Assert.IsTrue(_result.IsSuccess, $"Tool invocation failed: {_result.Exception?.Message}");
+ Assert.AreEqual(_referenceOutputContent, _result.GeneratedFiles.Single().GeneratedContent);
+ }
+
+ [Then(@"the invocation must fail")]
+ public void ThenTheInvocationMustFail()
+ {
+ Assert.IsFalse(_result.IsSuccess, "Tool invocation was supposed to fail, but it didn't");
+ Assert.IsNotNull(_result.Exception, "Expected an exception");
+ }
+
+ [Given(@"capnp\.exe is installed on my system")]
+ public void GivenCapnp_ExeIsInstalledOnMySystem()
+ {
+ if (!IsCapnpExeInstalled())
+ {
+ Assert.Inconclusive("capnp.exe not found. Precondition of this test is not met.");
+ }
+ }
+
+ [Given(@"I have a schema ""(.*)""")]
+ public void GivenIHaveASchema(string capnpFileName)
+ {
+ _inputSchemaFileName = capnpFileName;
+
+ using (var stream = LoadResource(capnpFileName))
+ using (var reader = new StreamReader(stream))
+ {
+ _inputSchema = reader.ReadToEnd();
+ }
+ }
+
+ [When(@"I try to generate code from that schema")]
+ public void WhenIWantToGenerateCodeFromThatSchema()
+ {
+ string path = Path.Combine(Path.GetTempPath(), _inputSchemaFileName);
+ File.WriteAllText(path, _inputSchema);
+ _result = CapnpCompilation.InvokeCapnpAndGenerate(new string[] { path });
+ }
+
+ [Then(@"code generation must succeed")]
+ public void ThenCodeGenerationMustSucceed()
+ {
+ Assert.IsNotNull(_result, "expected generation result");
+ Assert.IsTrue(_result.IsSuccess, $"Tool invocation failed: {_result.Exception?.Message}");
+ Assert.IsTrue(_result.GeneratedFiles.Count == 1, "Expected exactly one file");
+ Assert.IsTrue(_result.GeneratedFiles[0].IsSuccess, $"Code generation failed: {_result.GeneratedFiles[0].Exception?.Message}");
+ Assert.IsFalse(string.IsNullOrEmpty(_result.GeneratedFiles[0].GeneratedContent), "Expected non-empty generated content");
+ }
+
+ [Given(@"capnp\.exe is not installed on my system")]
+ public void GivenCapnp_ExeIsNotInstalledOnMySystem()
+ {
+ if (IsCapnpExeInstalled())
+ {
+ Assert.Inconclusive("capnp.exe found. Precondition of this test is not met.");
+ }
+ }
+
+ [Then(@"the reason must be bad input")]
+ public void ThenTheReasonMustBeBadInput()
+ {
+ Assert.IsTrue(_result.ErrorCategory == CapnpProcessFailure.BadInput);
+ }
+
+ [Then(@"the error output must contain ""(.*)""")]
+ public void ThenTheErrorOutputMustContain(string p0)
+ {
+ Assert.IsTrue(_result.Messages.Any(m => m.FullMessage.Contains(p0)));
+ }
+
+ [Then(@"the error output must contain multiple messages")]
+ public void ThenTheErrorOutputMustContainMultipleMessages()
+ {
+ Assert.IsTrue(_result.Messages.Count >= 2);
+ }
+ }
+}
diff --git a/capnpc-csharp.tests/No Resources/UnitTest10.capnp b/CapnpC.CSharp.Generator.Tests/No Resources/UnitTest10.capnp
similarity index 100%
rename from capnpc-csharp.tests/No Resources/UnitTest10.capnp
rename to CapnpC.CSharp.Generator.Tests/No Resources/UnitTest10.capnp
diff --git a/capnpc-csharp.tests/No Resources/UnitTest10b.capnp b/CapnpC.CSharp.Generator.Tests/No Resources/UnitTest10b.capnp
similarity index 100%
rename from capnpc-csharp.tests/No Resources/UnitTest10b.capnp
rename to CapnpC.CSharp.Generator.Tests/No Resources/UnitTest10b.capnp
diff --git a/capnpc-csharp.tests/No Resources/UnitTest11.capnp b/CapnpC.CSharp.Generator.Tests/No Resources/UnitTest11.capnp
similarity index 100%
rename from capnpc-csharp.tests/No Resources/UnitTest11.capnp
rename to CapnpC.CSharp.Generator.Tests/No Resources/UnitTest11.capnp
diff --git a/capnpc-csharp.tests/No Resources/UnitTest11b.capnp b/CapnpC.CSharp.Generator.Tests/No Resources/UnitTest11b.capnp
similarity index 100%
rename from capnpc-csharp.tests/No Resources/UnitTest11b.capnp
rename to CapnpC.CSharp.Generator.Tests/No Resources/UnitTest11b.capnp
diff --git a/capnpc-csharp.tests/No Resources/UnitTest2.capnp b/CapnpC.CSharp.Generator.Tests/No Resources/UnitTest2.capnp
similarity index 100%
rename from capnpc-csharp.tests/No Resources/UnitTest2.capnp
rename to CapnpC.CSharp.Generator.Tests/No Resources/UnitTest2.capnp
diff --git a/capnpc-csharp.tests/No Resources/UnitTest20.capnp b/CapnpC.CSharp.Generator.Tests/No Resources/UnitTest20.capnp
similarity index 100%
rename from capnpc-csharp.tests/No Resources/UnitTest20.capnp
rename to CapnpC.CSharp.Generator.Tests/No Resources/UnitTest20.capnp
diff --git a/capnpc-csharp.tests/No Resources/UnitTest3.capnp b/CapnpC.CSharp.Generator.Tests/No Resources/UnitTest3.capnp
similarity index 100%
rename from capnpc-csharp.tests/No Resources/UnitTest3.capnp
rename to CapnpC.CSharp.Generator.Tests/No Resources/UnitTest3.capnp
diff --git a/capnpc-csharp.tests/No Resources/UnitTest4.capnp b/CapnpC.CSharp.Generator.Tests/No Resources/UnitTest4.capnp
similarity index 100%
rename from capnpc-csharp.tests/No Resources/UnitTest4.capnp
rename to CapnpC.CSharp.Generator.Tests/No Resources/UnitTest4.capnp
diff --git a/capnpc-csharp.tests/No Resources/UnitTest4b.capnp b/CapnpC.CSharp.Generator.Tests/No Resources/UnitTest4b.capnp
similarity index 100%
rename from capnpc-csharp.tests/No Resources/UnitTest4b.capnp
rename to CapnpC.CSharp.Generator.Tests/No Resources/UnitTest4b.capnp
diff --git a/capnpc-csharp.tests/No Resources/test.capnp b/CapnpC.CSharp.Generator.Tests/No Resources/test.capnp
similarity index 100%
rename from capnpc-csharp.tests/No Resources/test.capnp
rename to CapnpC.CSharp.Generator.Tests/No Resources/test.capnp
diff --git a/CapnpC.CSharp.Generator.Tests/capnpc-csharp.tests.csproj b/CapnpC.CSharp.Generator.Tests/capnpc-csharp.tests.csproj
new file mode 100644
index 0000000..88a5509
--- /dev/null
+++ b/CapnpC.CSharp.Generator.Tests/capnpc-csharp.tests.csproj
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/CapnpC.CSharp.Generator/CapnpC.CSharp.Generator.csproj b/CapnpC.CSharp.Generator/CapnpC.CSharp.Generator.csproj
index 53233fd..377d454 100644
--- a/CapnpC.CSharp.Generator/CapnpC.CSharp.Generator.csproj
+++ b/CapnpC.CSharp.Generator/CapnpC.CSharp.Generator.csproj
@@ -1,7 +1,7 @@
- netcoreapp2.1
+ netstandard2.0;netcoreapp2.1
diff --git a/CapnpC.CSharp.Generator/CapnpCompilation.cs b/CapnpC.CSharp.Generator/CapnpCompilation.cs
index 9f1a0a4..ec4c342 100644
--- a/CapnpC.CSharp.Generator/CapnpCompilation.cs
+++ b/CapnpC.CSharp.Generator/CapnpCompilation.cs
@@ -1,7 +1,12 @@
using Capnp;
using System;
+using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
+using System.Runtime.CompilerServices;
+using System.Text.RegularExpressions;
+
+[assembly: InternalsVisibleTo("CapnpC.CSharp.Generator.Tests")]
namespace CapnpC.CSharp.Generator
{
@@ -23,13 +28,7 @@ namespace CapnpC.CSharp.Generator
try
{
- WireFrame segments;
-
- using (input)
- {
- segments = Framing.ReadSegments(input);
- }
-
+ var segments = Framing.ReadSegments(input);
var dec = DeserializerState.CreateRoot(segments);
var reader = Schema.CodeGeneratorRequest.Reader.Create(dec);
var model = Model.SchemaModel.Create(reader);
@@ -42,8 +41,66 @@ namespace CapnpC.CSharp.Generator
}
}
- public static GenerationResult InvokeCapnpcAndGenerate()
+ ///
+ /// Invokes "capnp.exe -o-" with given additional arguments and redirects the output to the C# generator backend.
+ ///
+ /// additional command line arguments
+ /// generation result
+ /// is null
+ public static GenerationResult InvokeCapnpAndGenerate(IEnumerable arguments)
{
+ if (arguments == null)
+ throw new ArgumentNullException(nameof(arguments));
+
+ using (var compiler = new Process())
+ {
+ var argList = new List();
+ argList.Add("compile");
+ argList.Add($"-o-");
+ argList.AddRange(arguments);
+
+ compiler.StartInfo.FileName = "capnp.exe";
+ compiler.StartInfo.Arguments = string.Join(" ", argList);
+ compiler.StartInfo.UseShellExecute = false;
+ compiler.StartInfo.RedirectStandardOutput = true;
+ compiler.StartInfo.RedirectStandardError = true;
+
+ try
+ {
+ compiler.Start();
+ }
+ catch (Exception exception)
+ {
+ return new GenerationResult(exception)
+ {
+ ErrorCategory = CapnpProcessFailure.NotFound
+ };
+ }
+
+ var result = GenerateFromStream(compiler.StandardOutput.BaseStream);
+
+ var messageList = new List();
+
+ while (!compiler.StandardError.EndOfStream)
+ {
+ messageList.Add(new CapnpMessage(compiler.StandardError.ReadLine()));
+ }
+
+ result.Messages = messageList;
+
+ if (!result.IsSuccess)
+ {
+ compiler.WaitForExit();
+ int exitCode = compiler.ExitCode;
+
+ if (exitCode == 0)
+ result.ErrorCategory = CapnpProcessFailure.BadOutput;
+ else
+ result.ErrorCategory = CapnpProcessFailure.BadInput;
+ }
+
+ return result;
+ }
}
}
}
diff --git a/CapnpC.CSharp.Generator/CapnpMessage.cs b/CapnpC.CSharp.Generator/CapnpMessage.cs
new file mode 100644
index 0000000..0cc60b8
--- /dev/null
+++ b/CapnpC.CSharp.Generator/CapnpMessage.cs
@@ -0,0 +1,116 @@
+using System.Runtime.CompilerServices;
+using System.Text.RegularExpressions;
+
+[assembly: InternalsVisibleTo("CapnpC.CSharp.Generator.Tests")]
+
+namespace CapnpC.CSharp.Generator
+{
+ ///
+ /// Represents a capnp.exe output message
+ ///
+ public class CapnpMessage
+ {
+ // capnp outputs look like this:
+ // empty.capnp:1:1: error: File does not declare an ID. I've generated one for you. Add this line to your file: @0xc82955a0c779197d;
+ // f:\code\invalid.capnp:9:7-8: error: Ordinal @0 originally used here.
+ // Parsing them is harder than it seems because the colon may be part of the file name (as in the example above).
+ // And it becomes even worse! NTFS has a rarely used feature called "alternate data streams", identified by a colon:
+ // f:\code\somefile:stream.capnp:9:7-8: error: Ordinal @0 originally used here.
+ // What about a name which looks like a line number? (Hint: the 10 denotes the alternate data stream)
+ // f:\code\somefile:10:9:7-8: error: Ordinal @0 originally used here.
+ // Watching for the *last* colon as message separator does not work either, either. See first example.
+ // Strategy: Watch out for the *last* occurence of pattern :[line]:[column]
+
+ static readonly Regex LineColumnRegex = new Regex(@":(?\d+):(?\d+)(-(?\d+))?:", RegexOptions.Compiled | RegexOptions.RightToLeft);
+
+ ///
+ /// Constructs an instance from given message
+ ///
+ /// output message (one line)
+ public CapnpMessage(string fullMessage)
+ {
+ FullMessage = fullMessage;
+
+ var match = LineColumnRegex.Match(fullMessage);
+
+ if (match.Success)
+ {
+ IsParseSuccess = true;
+ FileName = fullMessage.Substring(0, match.Index);
+ var lineMatch = match.Groups["Line"];
+ if (lineMatch.Success)
+ {
+ int.TryParse(lineMatch.Value, out int value);
+ Line = value;
+ }
+ var columnMatch = match.Groups["Column"];
+ if (columnMatch.Success)
+ {
+ int.TryParse(columnMatch.Value, out int value);
+ Column = value;
+ }
+ var endColumnMatch = match.Groups["EndColumn"];
+ if (endColumnMatch.Success)
+ {
+ int.TryParse(endColumnMatch.Value, out int value);
+ EndColumn = value;
+ }
+
+ int restIndex = match.Index + match.Length;
+ int bodyIndex = fullMessage.IndexOf(':', restIndex);
+
+ if (bodyIndex >= 0)
+ {
+ Category = fullMessage.Substring(restIndex, bodyIndex - restIndex).Trim();
+ MessageText = fullMessage.Substring(bodyIndex + 1).Trim();
+ }
+ else
+ {
+ // Never observed "in the wild", just in case...
+ Category = string.Empty;
+ MessageText = fullMessage.Substring(restIndex).Trim();
+ }
+ }
+ }
+
+ ///
+ /// The original message
+ ///
+ public string FullMessage { get; }
+
+ ///
+ /// Whether the message could be decompsed into [filename]:[line]:[column]: [category]: [text]
+ ///
+ public bool IsParseSuccess { get; }
+
+ ///
+ /// Parsed file name (null iff not IsParseSuccess)
+ ///
+ public string FileName { get; }
+
+ ///
+ /// Parsed line (0 if not IsParseSuccess)
+ ///
+ public int Line { get; }
+
+ ///
+ /// Parsed column (0 if not IsParseSuccess)
+ ///
+ public int Column { get; }
+
+ ///
+ /// Parsed end column (0 if there is none)
+ ///
+ public int EndColumn { get; }
+
+ ///
+ /// Parsed category (e.g. "error", null iff not IsParseSuccess)
+ ///
+ public string Category { get; }
+
+ ///
+ /// Parsed message body text (0 if not IsParseSuccess)
+ ///
+ public string MessageText { get; }
+ }
+}
diff --git a/CapnpC.CSharp.Generator/CapnpProcessFailure.cs b/CapnpC.CSharp.Generator/CapnpProcessFailure.cs
new file mode 100644
index 0000000..1d51c0f
--- /dev/null
+++ b/CapnpC.CSharp.Generator/CapnpProcessFailure.cs
@@ -0,0 +1,23 @@
+namespace CapnpC.CSharp.Generator
+{
+ ///
+ /// Why did invocation of capnpc.exe fail?
+ ///
+ public enum CapnpProcessFailure
+ {
+ ///
+ /// Because capnpc.exe was not found. It is probably not installed.
+ ///
+ NotFound,
+
+ ///
+ /// Because it exited with an error. Probably invalid .capnp file input.
+ ///
+ BadInput,
+
+ ///
+ /// Because it produced an apparently bad code generation request.
+ ///
+ BadOutput
+ }
+}
diff --git a/CapnpC.CSharp.Generator/CodeGen/CodeGenerator.cs b/CapnpC.CSharp.Generator/CodeGen/CodeGenerator.cs
index c983ccc..d873108 100644
--- a/CapnpC.CSharp.Generator/CodeGen/CodeGenerator.cs
+++ b/CapnpC.CSharp.Generator/CodeGen/CodeGenerator.cs
@@ -11,7 +11,7 @@
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
using static SyntaxHelpers;
- class CodeGenerator
+ internal class CodeGenerator
{
readonly SchemaModel _model;
readonly GenNames _names;
diff --git a/CapnpC.CSharp.Generator/GenerationResult.cs b/CapnpC.CSharp.Generator/GenerationResult.cs
index 3f40fc5..672759a 100644
--- a/CapnpC.CSharp.Generator/GenerationResult.cs
+++ b/CapnpC.CSharp.Generator/GenerationResult.cs
@@ -40,5 +40,15 @@ namespace CapnpC.CSharp.Generator
/// true iff generation was successful
///
public bool IsSuccess => GeneratedFiles != null;
+
+ ///
+ /// Messages read from standard error. Valid for both failure and success (capnp might spit out some warnings).
+ ///
+ public IReadOnlyList Messages { get; internal set; }
+
+ ///
+ /// Error classification (if any error)
+ ///
+ public CapnpProcessFailure ErrorCategory { get; internal set; }
}
}
diff --git a/CapnpC.CSharp.MsBuild.Generation.Tests/BuildEngineMock.cs b/CapnpC.CSharp.MsBuild.Generation.Tests/BuildEngineMock.cs
new file mode 100644
index 0000000..f5afe60
--- /dev/null
+++ b/CapnpC.CSharp.MsBuild.Generation.Tests/BuildEngineMock.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Collections;
+using Microsoft.Build.Framework;
+
+namespace CapnpC.CSharp.MsBuild.Generation.Tests
+{
+ class BuildEngineMock : IBuildEngine
+ {
+ public bool ContinueOnError => true;
+
+ public int LineNumberOfTaskNode => 0;
+
+ public int ColumnNumberOfTaskNode => 0;
+
+ public string ProjectFileOfTaskNode => null;
+
+ public bool BuildProjectFile(string projectFileName, string[] targetNames, IDictionary globalProperties, IDictionary targetOutputs)
+ {
+ return true;
+ }
+
+ public void LogCustomEvent(CustomBuildEventArgs e)
+ {
+ Console.WriteLine(e.Message);
+ }
+
+ public void LogErrorEvent(BuildErrorEventArgs e)
+ {
+ Console.WriteLine(e.Message);
+ }
+
+ public void LogMessageEvent(BuildMessageEventArgs e)
+ {
+ Console.WriteLine(e.Message);
+ }
+
+ public void LogWarningEvent(BuildWarningEventArgs e)
+ {
+ Console.WriteLine(e.Message);
+ }
+ }
+}
diff --git a/CapnpC.CSharp.MsBuild.Generation.Tests/CapnpC.CSharp.MsBuild.Generation.Tests.csproj b/CapnpC.CSharp.MsBuild.Generation.Tests/CapnpC.CSharp.MsBuild.Generation.Tests.csproj
new file mode 100644
index 0000000..fe274f1
--- /dev/null
+++ b/CapnpC.CSharp.MsBuild.Generation.Tests/CapnpC.CSharp.MsBuild.Generation.Tests.csproj
@@ -0,0 +1,21 @@
+
+
+
+ netcoreapp2.2
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CapnpC.CSharp.MsBuild.Generation.Tests/GenerateCapnpFileCodeBehindTaskTest.cs b/CapnpC.CSharp.MsBuild.Generation.Tests/GenerateCapnpFileCodeBehindTaskTest.cs
new file mode 100644
index 0000000..b6a7c6b
--- /dev/null
+++ b/CapnpC.CSharp.MsBuild.Generation.Tests/GenerateCapnpFileCodeBehindTaskTest.cs
@@ -0,0 +1,51 @@
+using CapnpC.CSharp.Generator.Tests;
+using Microsoft.Build.Framework;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.IO;
+
+namespace CapnpC.CSharp.MsBuild.Generation.Tests
+{
+ [TestClass]
+ public class GenerateCapnpFileCodeBehindTaskTest
+ {
+ string LoadResourceContent(string name)
+ {
+ using (var stream = CodeGeneratorSteps.LoadResource("UnitTest1.capnp"))
+ using (var reader = new StreamReader(stream))
+ {
+ return reader.ReadToEnd();
+ }
+ }
+
+ [TestMethod]
+ public void ExecutionWithoutParameters()
+ {
+ var task = new GenerateCapnpFileCodeBehindTask();
+ task.BuildEngine = new BuildEngineMock();
+ task.Execute();
+ // Should not crash. Should Execute() return true or false if there is no input?
+ }
+
+ [TestMethod]
+ public void SimpleGeneration()
+ {
+ string capnpFile = "UnitTask1.capnp";
+ string content = LoadResourceContent(capnpFile);
+ string tmpPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
+ Directory.CreateDirectory(tmpPath);
+ string capnpPath = Path.Combine(tmpPath, capnpFile);
+ File.WriteAllText(capnpPath, content);
+
+ var task = new GenerateCapnpFileCodeBehindTask();
+ task.BuildEngine = new BuildEngineMock();
+ task.ProjectPath = Path.Combine(tmpPath, "doesnotneedtoexist.csproj");
+ task.CapnpFiles = new ITaskItem[1] { new TaskItemMock() { ItemSpec = capnpPath } };
+ Assert.IsTrue(task.Execute());
+ Assert.IsNotNull(task.GeneratedFiles);
+ Assert.AreEqual(1, task.GeneratedFiles.Length);
+ string csPath = Path.Combine(tmpPath, task.GeneratedFiles[0].ItemSpec);
+ Assert.AreEqual(capnpPath + ".cs", csPath);
+ Assert.IsTrue(File.Exists(csPath));
+ }
+ }
+}
diff --git a/CapnpC.CSharp.MsBuild.Generation.Tests/TaskItemMock.cs b/CapnpC.CSharp.MsBuild.Generation.Tests/TaskItemMock.cs
new file mode 100644
index 0000000..6cf0f5a
--- /dev/null
+++ b/CapnpC.CSharp.MsBuild.Generation.Tests/TaskItemMock.cs
@@ -0,0 +1,37 @@
+using System.Collections;
+using Microsoft.Build.Framework;
+
+namespace CapnpC.CSharp.MsBuild.Generation.Tests
+{
+
+ class TaskItemMock : ITaskItem
+ {
+ public string ItemSpec { get; set; }
+
+ public ICollection MetadataNames => null;
+
+ public int MetadataCount => 0;
+
+ public IDictionary CloneCustomMetadata()
+ {
+ return null;
+ }
+
+ public void CopyMetadataTo(ITaskItem destinationItem)
+ {
+ }
+
+ public string GetMetadata(string metadataName)
+ {
+ return null;
+ }
+
+ public void RemoveMetadata(string metadataName)
+ {
+ }
+
+ public void SetMetadata(string metadataName, string metadataValue)
+ {
+ }
+ }
+}
diff --git a/CapnpC.CSharp.MsBuild.Generation/CsFileGeneratorResult.cs b/CapnpC.CSharp.MsBuild.Generation/CsFileGeneratorResult.cs
new file mode 100644
index 0000000..75961ac
--- /dev/null
+++ b/CapnpC.CSharp.MsBuild.Generation/CsFileGeneratorResult.cs
@@ -0,0 +1,51 @@
+using CapnpC.CSharp.Generator;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace CapnpC.CSharp.MsBuild.Generation
+{
+ public class CsFileGeneratorResult
+ {
+ public CsFileGeneratorResult(FileGenerationResult generatorResult, string fileName, IReadOnlyList messages)
+ {
+ if (generatorResult == null)
+ {
+ throw new ArgumentNullException(nameof(generatorResult));
+ }
+
+ Filename = fileName ?? throw new ArgumentNullException(nameof(fileName));
+
+ Error = generatorResult.Exception?.Message;
+ GeneratedCode = generatorResult.GeneratedContent;
+ Messages = messages;
+ }
+
+ public CsFileGeneratorResult(string error)
+ {
+ Error = error;
+ }
+
+ public CsFileGeneratorResult(string error, IReadOnlyList messages)
+ {
+ Error = error;
+ Messages = messages;
+ }
+
+ ///
+ /// The error, if any.
+ ///
+ public string Error { get; }
+
+ ///
+ /// The generated code.
+ ///
+ public string GeneratedCode { get; }
+
+ public IReadOnlyList Messages { get; }
+
+ public bool Success => Error == null;
+
+ public string Filename { get; }
+ }
+}
\ No newline at end of file
diff --git a/Capnpc.Csharp.MsBuild.Generation/CapnpCodeBehindGenerator.cs b/Capnpc.Csharp.MsBuild.Generation/CapnpCodeBehindGenerator.cs
index 97ab0ba..71c0592 100644
--- a/Capnpc.Csharp.MsBuild.Generation/CapnpCodeBehindGenerator.cs
+++ b/Capnpc.Csharp.MsBuild.Generation/CapnpCodeBehindGenerator.cs
@@ -1,41 +1,85 @@
-using System;
+using CapnpC.CSharp.Generator;
+using System;
using System.Collections.Generic;
using System.IO;
-namespace Capnpc.Csharp.MsBuild.Generation
+namespace CapnpC.CSharp.MsBuild.Generation
{
public class CapnpCodeBehindGenerator : IDisposable
{
- //private SpecFlowProject _specFlowProject;
- //private ITestGenerator _testGenerator;
public void InitializeProject(string projectPath)
{
- //_specFlowProject = MsBuildProjectReader.LoadSpecFlowProjectFromMsBuild(Path.GetFullPath(projectPath), rootNamespace);
-
- //var projectSettings = _specFlowProject.ProjectSettings;
-
- //var testGeneratorFactory = new TestGeneratorFactory();
-
- //_testGenerator = testGeneratorFactory.CreateGenerator(projectSettings, generatorPlugins);
}
- public TestFileGeneratorResult GenerateCodeBehindFile(string capnpFile)
+ public CsFileGeneratorResult GenerateCodeBehindFile(string capnpFile)
{
- //var featureFileInput = new FeatureFileInput(featureFile);
- //var generatedFeatureFileName = Path.GetFileName(_testGenerator.GetTestFullPath(featureFileInput));
+ // Works around a weird capnp.exe behavior: When the input file is empty, it will spit out an exception dump
+ // instead of a parse error. But the parse error is nice because it contains a generated ID. We want the parse error!
+ // Workaround: Generate a temporary file that contains a single line break (such that it is not empty...)
+ try
+ {
+ if (File.Exists(capnpFile) && new FileInfo(capnpFile).Length == 0)
+ {
+ string tempFile = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName() + ".capnp");
- //var testGeneratorResult = _testGenerator.GenerateTestFile(featureFileInput, new GenerationSettings());
+ File.WriteAllText(tempFile, Environment.NewLine);
+ try
+ {
+ return GenerateCodeBehindFile(tempFile);
+ }
+ finally
+ {
+ File.Delete(tempFile);
+ }
+ }
+ }
+ catch
+ {
+ }
- return new TestFileGeneratorResult(
- new TestGeneratorResult() { GeneratedTestCode = "//dummy" },
- capnpFile + ".cs");
+ var result = CapnpCompilation.InvokeCapnpAndGenerate(new string[] { capnpFile });
+
+ if (result.IsSuccess)
+ {
+ if (result.GeneratedFiles.Count == 1)
+ {
+ return new CsFileGeneratorResult(
+ result.GeneratedFiles[0],
+ capnpFile + ".cs",
+ result.Messages);
+ }
+ else
+ {
+ return new CsFileGeneratorResult(
+ "Code generation produced more than one file. This is not supported.",
+ result.Messages);
+ }
+ }
+ else
+ {
+ switch (result.ErrorCategory)
+ {
+ case CapnpProcessFailure.NotFound:
+ return new CsFileGeneratorResult("Unable to find capnp.exe - please install capnproto on your system first.");
+
+ case CapnpProcessFailure.BadInput:
+ return new CsFileGeneratorResult("Invalid schema", result.Messages);
+
+ case CapnpProcessFailure.BadOutput:
+ return new CsFileGeneratorResult(
+ "Internal error: capnp.exe produced a binary code generation request which was not understood by the backend",
+ result.Messages);
+
+ default:
+ throw new NotSupportedException("Invalid error category");
+ }
+ }
}
public void Dispose()
{
- //_testGenerator?.Dispose();
}
}
}
\ No newline at end of file
diff --git a/Capnpc.Csharp.MsBuild.Generation/CapnpFileCodeBehindGenerator.cs b/Capnpc.Csharp.MsBuild.Generation/CapnpFileCodeBehindGenerator.cs
index 6802c12..188830b 100644
--- a/Capnpc.Csharp.MsBuild.Generation/CapnpFileCodeBehindGenerator.cs
+++ b/Capnpc.Csharp.MsBuild.Generation/CapnpFileCodeBehindGenerator.cs
@@ -5,16 +5,13 @@ using System.Text;
using System.Threading.Tasks;
using Microsoft.Build.Utilities;
-namespace Capnpc.Csharp.MsBuild.Generation
+namespace CapnpC.CSharp.MsBuild.Generation
{
public class CapnpFileCodeBehindGenerator : ICapnpcCsharpGenerator
{
- private readonly FilePathGenerator _filePathGenerator;
-
public CapnpFileCodeBehindGenerator(TaskLoggingHelper log)
{
Log = log ?? throw new ArgumentNullException(nameof(log));
- _filePathGenerator = new FilePathGenerator();
}
public TaskLoggingHelper Log { get; }
@@ -39,33 +36,44 @@ namespace Capnpc.Csharp.MsBuild.Generation
foreach (var capnpFile in capnpFiles)
{
- var capnpFileItemSpec = capnpFile;
- var generatorResult = capnpCodeBehindGenerator.GenerateCodeBehindFile(capnpFileItemSpec);
+ var generatorResult = capnpCodeBehindGenerator.GenerateCodeBehindFile(capnpFile);
if (!generatorResult.Success)
{
- foreach (var error in generatorResult.Errors)
+ if (!string.IsNullOrEmpty(generatorResult.Error))
{
- //Log.LogError(
- // null,
- // null,
- // null,
- // featureFile,
- // error.Line,
- // error.LinePosition,
- // 0,
- // 0,
- // error.Message);
+ Log.LogError("{0}", generatorResult.Error);
}
+
+ if (generatorResult.Messages != null)
+ {
+ foreach (var message in generatorResult.Messages)
+ {
+ if (message.IsParseSuccess)
+ {
+ Log.LogError(
+ subcategory: null,
+ errorCode: null,
+ helpKeyword: null,
+ file: capnpFile,
+ lineNumber: message.Line,
+ columnNumber: message.Column,
+ endLineNumber: message.Line,
+ endColumnNumber: message.EndColumn == 0 ? message.Column : message.EndColumn,
+ "{0}",
+ message.MessageText);
+ }
+ else
+ {
+ Log.LogError("{0}", message.FullMessage);
+ }
+ }
+ }
+
continue;
}
- var targetFilePath = _filePathGenerator.GenerateFilePath(
- projectFolder,
- capnpFile,
- generatorResult.Filename);
-
- var resultedFile = codeBehindWriter.WriteCodeBehindFile(targetFilePath, capnpFile, generatorResult);
+ var resultedFile = codeBehindWriter.WriteCodeBehindFile(generatorResult.Filename, generatorResult);
yield return FileSystemHelper.GetRelativePath(resultedFile, projectFolder);
}
diff --git a/Capnpc.Csharp.MsBuild.Generation/Capnpc.Csharp.MsBuild.Generation.csproj b/Capnpc.Csharp.MsBuild.Generation/Capnpc.Csharp.MsBuild.Generation.csproj
index 67c482c..bbb9e95 100644
--- a/Capnpc.Csharp.MsBuild.Generation/Capnpc.Csharp.MsBuild.Generation.csproj
+++ b/Capnpc.Csharp.MsBuild.Generation/Capnpc.Csharp.MsBuild.Generation.csproj
@@ -2,9 +2,6 @@
net471;netcoreapp2.1
false
- $(MSBuildThisFileDirectory)Capnpc.Csharp.MsBuild.Generation.nuspec
- true
-
true
true
@@ -12,6 +9,13 @@
true
true
$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb
+ 1.0.0.0
+ 1.0.0.0
+ 1.0.0
+
+ $(MSBuildThisFileDirectory)CapnpC.CSharp.MsBuild.Generation.nuspec
+ version=$(Version)
+ true
@@ -54,6 +58,12 @@
+
+
+ true
+
+
+
Microsoft.Build
@@ -68,11 +78,28 @@
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
MSBuild:Compile
+ Always
MSBuild:Compile
+ Always
diff --git a/Capnpc.Csharp.MsBuild.Generation/Capnpc.Csharp.MsBuild.Generation.nuspec b/Capnpc.Csharp.MsBuild.Generation/Capnpc.Csharp.MsBuild.Generation.nuspec
index 887d527..d5073c9 100644
--- a/Capnpc.Csharp.MsBuild.Generation/Capnpc.Csharp.MsBuild.Generation.nuspec
+++ b/Capnpc.Csharp.MsBuild.Generation/Capnpc.Csharp.MsBuild.Generation.nuspec
@@ -1,9 +1,9 @@
- Capnpc.Csharp.MsBuild.Generation
- 1.0.0
- Capnpc.Csharp.MsBuild.Generation
+ CapnpC.CSharp.MsBuild.Generation
+ $version$
+ CapnpC.CSharp.MsBuild.Generation
Christian Köllner and contributors
Christian Köllner
Package to enable the .capnp -> .cs file generation during build time
@@ -15,6 +15,7 @@
capnproto csharp msbuild
Christian Köllner and contributors
+
diff --git a/Capnpc.Csharp.MsBuild.Generation/CodeBehindWriter.cs b/Capnpc.Csharp.MsBuild.Generation/CodeBehindWriter.cs
index f581de3..3ff276b 100644
--- a/Capnpc.Csharp.MsBuild.Generation/CodeBehindWriter.cs
+++ b/Capnpc.Csharp.MsBuild.Generation/CodeBehindWriter.cs
@@ -2,7 +2,7 @@
using System.IO;
using Microsoft.Build.Utilities;
-namespace Capnpc.Csharp.MsBuild.Generation
+namespace CapnpC.CSharp.MsBuild.Generation
{
public class CodeBehindWriter
{
@@ -13,14 +13,8 @@ namespace Capnpc.Csharp.MsBuild.Generation
public TaskLoggingHelper Log { get; }
- public string WriteCodeBehindFile(string outputPath, string capnpFile, TestFileGeneratorResult testFileGeneratorResult)
+ public string WriteCodeBehindFile(string outputPath, CsFileGeneratorResult testFileGeneratorResult)
{
- //if (string.IsNullOrEmpty(testFileGeneratorResult.Filename))
- //{
- // Log?.LogWithNameTag(Log.LogError, $"{featureFile} has no generated filename");
- // return null;
- //}
-
string directoryPath = Path.GetDirectoryName(outputPath) ?? throw new InvalidOperationException();
Log?.LogWithNameTag(Log.LogMessage, directoryPath);
@@ -35,11 +29,6 @@ namespace Capnpc.Csharp.MsBuild.Generation
}
else
{
- if (!Directory.Exists(directoryPath))
- {
- Directory.CreateDirectory(directoryPath);
- }
-
File.WriteAllText(outputPath, testFileGeneratorResult.GeneratedCode);
}
diff --git a/Capnpc.Csharp.MsBuild.Generation/FilePathGenerator.cs b/Capnpc.Csharp.MsBuild.Generation/FilePathGenerator.cs
deleted file mode 100644
index 4ac982d..0000000
--- a/Capnpc.Csharp.MsBuild.Generation/FilePathGenerator.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-using System;
-using System.IO;
-
-namespace Capnpc.Csharp.MsBuild.Generation
-{
- public class FilePathGenerator
- {
- public string GenerateFilePath(string projectFolder, string capnpFileName, string generatedCodeBehindFileName)
- {
- if (projectFolder is null)
- {
- throw new ArgumentNullException(nameof(projectFolder));
- }
-
- if (capnpFileName is null)
- {
- throw new ArgumentNullException(nameof(capnpFileName));
- }
-
- if (generatedCodeBehindFileName is null)
- {
- throw new ArgumentNullException(nameof(generatedCodeBehindFileName));
- }
-
- string featureFileFullPath = Path.GetFullPath(Path.Combine(projectFolder, capnpFileName));
- string featureFileDirPath = Path.GetDirectoryName(featureFileFullPath);
-
- return Path.Combine(featureFileDirPath, generatedCodeBehindFileName);
- }
- }
-}
diff --git a/Capnpc.Csharp.MsBuild.Generation/FileSystemHelper.cs b/Capnpc.Csharp.MsBuild.Generation/FileSystemHelper.cs
index d27428f..d538531 100644
--- a/Capnpc.Csharp.MsBuild.Generation/FileSystemHelper.cs
+++ b/Capnpc.Csharp.MsBuild.Generation/FileSystemHelper.cs
@@ -4,7 +4,7 @@ using System.Diagnostics;
using System.IO;
using System.Text;
-namespace Capnpc.Csharp.MsBuild.Generation
+namespace CapnpC.CSharp.MsBuild.Generation
{
public static class FileSystemHelper
{
diff --git a/Capnpc.Csharp.MsBuild.Generation/GenerateCapnpFileCodeBehindTask.cs b/Capnpc.Csharp.MsBuild.Generation/GenerateCapnpFileCodeBehindTask.cs
index b4b9269..d29cf8d 100644
--- a/Capnpc.Csharp.MsBuild.Generation/GenerateCapnpFileCodeBehindTask.cs
+++ b/Capnpc.Csharp.MsBuild.Generation/GenerateCapnpFileCodeBehindTask.cs
@@ -7,7 +7,7 @@ using System.Resources;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
-namespace Capnpc.Csharp.MsBuild.Generation
+namespace CapnpC.CSharp.MsBuild.Generation
{
public class GenerateCapnpFileCodeBehindTask : Task
{
diff --git a/Capnpc.Csharp.MsBuild.Generation/Helper/LogExtensions.cs b/Capnpc.Csharp.MsBuild.Generation/Helper/LogExtensions.cs
index 1343a80..3203bea 100644
--- a/Capnpc.Csharp.MsBuild.Generation/Helper/LogExtensions.cs
+++ b/Capnpc.Csharp.MsBuild.Generation/Helper/LogExtensions.cs
@@ -5,7 +5,7 @@ using System.Text;
using System.Threading.Tasks;
using Microsoft.Build.Utilities;
-namespace Capnpc.Csharp.MsBuild.Generation
+namespace CapnpC.CSharp.MsBuild.Generation
{
public static class LogExtensions
{
@@ -15,7 +15,7 @@ namespace Capnpc.Csharp.MsBuild.Generation
string message,
params object[] messageArgs)
{
- string fullMessage = $"[SpecFlow] {message}";
+ string fullMessage = $"[Cap'n Proto] {message}";
loggingMethod?.Invoke(fullMessage, messageArgs);
}
}
diff --git a/Capnpc.Csharp.MsBuild.Generation/ICapnpCsharpGenerator.cs b/Capnpc.Csharp.MsBuild.Generation/ICapnpCsharpGenerator.cs
index d55ed1e..de828fd 100644
--- a/Capnpc.Csharp.MsBuild.Generation/ICapnpCsharpGenerator.cs
+++ b/Capnpc.Csharp.MsBuild.Generation/ICapnpCsharpGenerator.cs
@@ -1,6 +1,6 @@
using System.Collections.Generic;
-namespace Capnpc.Csharp.MsBuild.Generation
+namespace CapnpC.CSharp.MsBuild.Generation
{
public interface ICapnpcCsharpGenerator
{
diff --git a/Capnpc.Csharp.MsBuild.Generation/TestFileGeneratorResult.cs b/Capnpc.Csharp.MsBuild.Generation/TestFileGeneratorResult.cs
deleted file mode 100644
index 36ab361..0000000
--- a/Capnpc.Csharp.MsBuild.Generation/TestFileGeneratorResult.cs
+++ /dev/null
@@ -1,42 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-
-namespace Capnpc.Csharp.MsBuild.Generation
-{
- public class TestFileGeneratorResult
- {
- public TestFileGeneratorResult(TestGeneratorResult generatorResult, string fileName)
- {
- if (generatorResult == null)
- {
- throw new ArgumentNullException(nameof(generatorResult));
- }
-
- Filename = fileName ?? throw new ArgumentNullException(nameof(fileName));
-
- Errors = generatorResult.Errors;
- IsUpToDate = generatorResult.IsUpToDate;
- GeneratedCode = generatorResult.GeneratedTestCode;
- }
-
- ///
- /// The errors, if any.
- ///
- public IEnumerable Errors { get; }
-
- ///
- /// The generated file was up-to-date.
- ///
- public bool IsUpToDate { get; }
-
- ///
- /// The generated code.
- ///
- public string GeneratedCode { get; }
-
- public bool Success => Errors == null || !Errors.Any();
-
- public string Filename { get; }
- }
-}
\ No newline at end of file
diff --git a/Capnpc.Csharp.MsBuild.Generation/TestGenerationError.cs b/Capnpc.Csharp.MsBuild.Generation/TestGenerationError.cs
deleted file mode 100644
index 1bf834c..0000000
--- a/Capnpc.Csharp.MsBuild.Generation/TestGenerationError.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace Capnpc.Csharp.MsBuild.Generation
-{
- public class TestGenerationError
- {
- }
-}
\ No newline at end of file
diff --git a/Capnpc.Csharp.MsBuild.Generation/TestGeneratorResult.cs b/Capnpc.Csharp.MsBuild.Generation/TestGeneratorResult.cs
deleted file mode 100644
index 35b34e3..0000000
--- a/Capnpc.Csharp.MsBuild.Generation/TestGeneratorResult.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using System.Collections.Generic;
-
-namespace Capnpc.Csharp.MsBuild.Generation
-{
- public class TestGeneratorResult
- {
- public IEnumerable Errors { get; internal set; }
- public bool IsUpToDate { get; internal set; }
- public string GeneratedTestCode { get; internal set; }
- }
-}
\ No newline at end of file
diff --git a/Capnpc.Csharp.MsBuild.Generation/build/CPS/Buildsystem/Rules/CapnpFileType.xaml b/Capnpc.Csharp.MsBuild.Generation/build/CPS/Buildsystem/Rules/CapnpFileType.xaml
index ec90cbe..72e5c94 100644
--- a/Capnpc.Csharp.MsBuild.Generation/build/CPS/Buildsystem/Rules/CapnpFileType.xaml
+++ b/Capnpc.Csharp.MsBuild.Generation/build/CPS/Buildsystem/Rules/CapnpFileType.xaml
@@ -29,7 +29,7 @@
-
+
\ No newline at end of file
diff --git a/Capnpc.Csharp.MsBuild.Generation/build/Capnpc.Csharp.MsBuild.Generation.props b/Capnpc.Csharp.MsBuild.Generation/build/Capnpc.Csharp.MsBuild.Generation.props
index fe7e57a..bc41f4e 100644
--- a/Capnpc.Csharp.MsBuild.Generation/build/Capnpc.Csharp.MsBuild.Generation.props
+++ b/Capnpc.Csharp.MsBuild.Generation/build/Capnpc.Csharp.MsBuild.Generation.props
@@ -54,7 +54,7 @@
- after deletion of a feature file
- after pulling latest changes from version control with above changes
-->
-
+
-
+
@@ -128,6 +128,6 @@
- after deletion of a capnp file
- after pulling latest changes from version control with above changes
-->
-
+
\ No newline at end of file
diff --git a/Capnpc.Csharp.MsBuild.Generation/build/Capnpc.Csharp.MsBuild.Generation.tasks b/Capnpc.Csharp.MsBuild.Generation/build/Capnpc.Csharp.MsBuild.Generation.tasks
index b20fdae..9859d7b 100644
--- a/Capnpc.Csharp.MsBuild.Generation/build/Capnpc.Csharp.MsBuild.Generation.tasks
+++ b/Capnpc.Csharp.MsBuild.Generation/build/Capnpc.Csharp.MsBuild.Generation.tasks
@@ -1,3 +1,3 @@
-
+
\ No newline at end of file
diff --git a/Capnpc.Csharp.MsBuild.Generation/buildMultiTargeting/Capnpc.Csharp.MsBuild.Generation.props b/Capnpc.Csharp.MsBuild.Generation/buildMultiTargeting/Capnpc.Csharp.MsBuild.Generation.props
index f3c49ea..62930dd 100644
--- a/Capnpc.Csharp.MsBuild.Generation/buildMultiTargeting/Capnpc.Csharp.MsBuild.Generation.props
+++ b/Capnpc.Csharp.MsBuild.Generation/buildMultiTargeting/Capnpc.Csharp.MsBuild.Generation.props
@@ -1,5 +1,5 @@
-
+
\ No newline at end of file
diff --git a/README.md b/README.md
index db6030c..829271a 100644
--- a/README.md
+++ b/README.md
@@ -53,7 +53,7 @@ Solution/project structure is as follows:
* Capnp.Net.Runtime is the runtime implementation, a .NET assembly.
* capnpc-csharp is the generator backend for C# language.
* Capnp.Net.Runtime.Tests is an MS Unit Testing assembly, containing - you guessed it - the test suite.
- * capnpc-csharp.tests contains the generator backend test suite.
+ * CapnpC.CSharp.Generator.Tests contains the generator backend test suite.
- CapnpCompatTest.sln compiles to a native x86 executable which depends on the original Cap'n Proto C++ implementation. It is (partially) required by the test suite for interoperability testing.
## Features
diff --git a/appveyor.yml b/appveyor.yml
index b08ea80..00636e7 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -29,7 +29,7 @@ before_build:
- cmd: dotnet restore ./Capnp.Net.Runtime.Tests/Capnp.Net.Runtime.Tests.Std20.csproj --verbosity m
- cmd: dotnet restore ./Capnp.Net.Runtime.Tests.Core21/Capnp.Net.Runtime.Tests.Core21.csproj --verbosity m
- cmd: dotnet restore ./capnpc-csharp/capnpc-csharp.csproj --verbosity m
- - cmd: dotnet restore ./capnpc-csharp.tests/capnpc-csharp.tests.csproj --verbosity m
+ - cmd: dotnet restore ./CapnpC.CSharp.Generator.Tests/CapnpC.CSharp.Generator.Tests.csproj --verbosity m
build_script:
- cmd: msbuild ./Capnp.Net.sln /p:Configuration="Debug"
- cmd: msbuild ./Capnp.Net.sln /p:Configuration="Release"
@@ -50,11 +50,12 @@ artifacts:
type: NuGetPackage
clone_depth: 1
test_script:
- - cmd: vstest.console /logger:Appveyor /inIsolation capnpc-csharp.tests\bin\Release\netcoreapp2.2\capnpc-csharp.tests.dll
+ - cmd: vstest.console /logger:Appveyor /inIsolation CapnpC.CSharp.Generator.Tests\bin\Release\netcoreapp2.2\CapnpC.CSharp.Generator.Tests.dll
- cmd: cd %APPVEYOR_BUILD_FOLDER%\chocolatey\install
- cmd: choco install capnpc-csharp --source=".;https://chocolatey.org/api/v2" --force -y
- cmd: cd %APPVEYOR_BUILD_FOLDER%\install-test
- cmd: compile-test
+ - cmd: vstest.console /logger:Appveyor /inIsolation CapnpC.CSharp.Generator.Tests\bin\Release\netcoreapp2.2\CapnpC.CSharp.Generator.Tests.dll
- cmd: choco uninstall capnpc-csharp -y
- cmd: notinstalled-test
- cmd: cd %APPVEYOR_BUILD_FOLDER%\chocolatey\install
diff --git a/capnpc-csharp.tests/CodeGenerator.feature b/capnpc-csharp.tests/CodeGenerator.feature
deleted file mode 100644
index 89768b9..0000000
--- a/capnpc-csharp.tests/CodeGenerator.feature
+++ /dev/null
@@ -1,20 +0,0 @@
-Feature: CodeGenerator
- In order to ensure that the generator backend produces valid output
- As a contributor
- I want to get notified when there is any deviation from reference output
-
-Scenario: Comparing backend output with reference
- Given I have a binary code generator request "test.capnp.bin"
- And my reference output is "test.cs"
- When I invoke capnpc-csharp
- Then the generated output must match the reference
-
-Scenario Outline: Invalid binary code generator requests
- Given I have a binary code generator request
- When I invoke capnpc-csharp
- Then the invocation must fail
-
-Examples:
- | bin |
- | null.bin |
- | test.cs |
\ No newline at end of file
diff --git a/capnpc-csharp.tests/FeatureSteps/CodeGeneratorSteps.cs b/capnpc-csharp.tests/FeatureSteps/CodeGeneratorSteps.cs
deleted file mode 100644
index 4d3a893..0000000
--- a/capnpc-csharp.tests/FeatureSteps/CodeGeneratorSteps.cs
+++ /dev/null
@@ -1,83 +0,0 @@
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using System;
-using System.IO;
-using System.Reflection;
-using TechTalk.SpecFlow;
-
-namespace capnpc_csharp.Tests
-{
- [Binding]
- public class CodeGeneratorSteps
- {
- Stream _inputStream;
- string _referenceOutputContent;
- string _exceptedOutputFileName;
- string _actualGeneratedContent;
- bool _success;
- Exception _generateException;
-
- internal static Stream LoadResource(string name)
- {
- var assembly = Assembly.GetExecutingAssembly();
- string[] names = assembly.GetManifestResourceNames();
- string urn = Array.Find(names, n => n.EndsWith(name, StringComparison.OrdinalIgnoreCase));
- Assert.IsNotNull(urn, $"Test specification error: {name} does not exist");
- return assembly.GetManifestResourceStream(urn);
- }
-
- [Given(@"I have a binary code generator request ""(.*)""")]
- [Given(@"I have a binary code generator request (.*)")]
- public void GivenIHaveABinaryCodeGeneratorRequest(string binaryRequestFileName)
- {
- _inputStream = LoadResource(binaryRequestFileName);
- }
-
- [Given(@"my reference output is ""(.*)""")]
- public void GivenMyReferenceOutputIs(string expectedOutputFileName)
- {
- _exceptedOutputFileName = expectedOutputFileName;
- using (var stream = LoadResource(expectedOutputFileName))
- using (var reader = new StreamReader(stream))
- {
- _referenceOutputContent = reader.ReadToEnd();
- }
- }
-
- [When(@"I invoke capnpc-csharp")]
- public void WhenIInvokeCapnpc_Csharp()
- {
- try
- {
- using (_inputStream)
- {
- string tempDir = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
- Directory.CreateDirectory(tempDir);
- Environment.CurrentDirectory = tempDir;
-
- CapnpC.Program.GenerateFromStream(_inputStream);
-
- string outPath = Path.Combine(tempDir, _exceptedOutputFileName);
- _actualGeneratedContent = File.ReadAllText(outPath);
- _success = true;
- }
- }
- catch (Exception exception)
- {
- _generateException = exception;
- }
- }
-
- [Then(@"the generated output must match the reference")]
- public void ThenTheGeneratedOutputMustMatchTheReference()
- {
- Assert.IsTrue(_success, $"Code generation failed: {_generateException?.Message}");
- Assert.AreEqual(_referenceOutputContent, _actualGeneratedContent);
- }
-
- [Then(@"the invocation must fail")]
- public void ThenTheInvocationMustFail()
- {
- Assert.IsFalse(_success, "Code generation was supposed to fail, but it didn't");
- }
- }
-}
diff --git a/capnpc-csharp/Generator/CodeGenerator.cs b/capnpc-csharp/Generator/CodeGenerator.cs
deleted file mode 100644
index e401b3a..0000000
--- a/capnpc-csharp/Generator/CodeGenerator.cs
+++ /dev/null
@@ -1,182 +0,0 @@
-namespace CapnpC.Generator
-{
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- using System.Text;
- using CapnpC.Model;
- using Microsoft.CodeAnalysis;
- using Microsoft.CodeAnalysis.CSharp;
- using Microsoft.CodeAnalysis.CSharp.Syntax;
- using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
- using static SyntaxHelpers;
-
- class CodeGenerator
- {
- readonly SchemaModel _model;
- readonly GenNames _names;
- readonly CommonSnippetGen _commonGen;
- readonly DomainClassSnippetGen _domClassGen;
- readonly ReaderSnippetGen _readerGen;
- readonly WriterSnippetGen _writerGen;
- readonly InterfaceSnippetGen _interfaceGen;
-
- public CodeGenerator(SchemaModel model, GeneratorOptions options)
- {
- _model = model;
- _names = new GenNames(options);
- _commonGen = new CommonSnippetGen(_names);
- _domClassGen = new DomainClassSnippetGen(_names);
- _readerGen = new ReaderSnippetGen(_names);
- _writerGen = new WriterSnippetGen(_names);
- _interfaceGen = new InterfaceSnippetGen(_names);
- }
-
- internal GenNames GetNames() => _names;
-
- IEnumerable TransformEnum(TypeDefinition def)
- {
- yield return _commonGen.MakeEnum(def);
- }
-
- IEnumerable MakeTypeParameters(TypeDefinition def)
- {
- foreach (string name in def.GenericParameters)
- {
- yield return TypeParameter(_names.GetGenericTypeParameter(name).Identifier);
- }
- }
-
- IEnumerable MakeTypeParameterConstraints(TypeDefinition def)
- {
- foreach (string name in def.GenericParameters)
- {
- yield return TypeParameterConstraintClause(
- _names.GetGenericTypeParameter(name).IdentifierName)
- .AddConstraints(ClassOrStructConstraint(SyntaxKind.ClassConstraint));
- }
- }
-
- IEnumerable TransformStruct(TypeDefinition def)
- {
- var topDecl = ClassDeclaration(_names.MakeTypeName(def).Identifier)
- .AddModifiers(Public)
- .AddBaseListTypes(SimpleBaseType(Type()));
-
- if (def.GenericParameters.Count > 0)
- {
- topDecl = topDecl
- .AddTypeParameterListParameters(MakeTypeParameters(def).ToArray())
- .AddConstraintClauses(MakeTypeParameterConstraints(def).ToArray());
- }
-
- if (def.UnionInfo != null)
- {
- topDecl = topDecl.AddMembers(_commonGen.MakeUnionSelectorEnum(def));
- }
-
- topDecl = topDecl.AddMembers(_domClassGen.MakeDomainClassMembers(def));
- topDecl = topDecl.AddMembers(_readerGen.MakeReaderStruct(def));
- topDecl = topDecl.AddMembers(_writerGen.MakeWriterStruct(def));
-
- foreach (var nestedGroup in def.NestedGroups)
- {
- topDecl = topDecl.AddMembers(Transform(nestedGroup).ToArray());
- }
-
- foreach (var nestedDef in def.NestedTypes)
- {
- topDecl = topDecl.AddMembers(Transform(nestedDef).ToArray());
- }
-
- yield return topDecl;
- }
-
- IEnumerable TransformInterface(TypeDefinition def)
- {
- yield return _interfaceGen.MakeInterface(def);
- yield return _interfaceGen.MakeProxy(def);
- yield return _interfaceGen.MakeSkeleton(def);
-
- if (_interfaceGen.RequiresPipeliningSupport(def))
- {
- yield return _interfaceGen.MakePipeliningSupport(def);
- }
-
- if (def.NestedTypes.Any())
- {
- var ns = ClassDeclaration(
- _names.MakeTypeName(def, NameUsage.Namespace).ToString())
- .AddModifiers(Public, Static);
-
- if (def.GenericParameters.Count > 0)
- {
- ns = ns
- .AddTypeParameterListParameters(MakeTypeParameters(def).ToArray())
- .AddConstraintClauses(MakeTypeParameterConstraints(def).ToArray());
- }
-
- foreach (var nestedDef in def.NestedTypes)
- {
- ns = ns.AddMembers(Transform(nestedDef).ToArray());
- }
-
- yield return ns;
- }
- }
-
- IEnumerable Transform(TypeDefinition def)
- {
- switch (def.Tag)
- {
- case TypeTag.Enum:
- return TransformEnum(def);
-
- case TypeTag.Group:
- case TypeTag.Struct:
- return TransformStruct(def);
-
- case TypeTag.Interface:
- return TransformInterface(def);
-
- default:
- throw new NotSupportedException($"Cannot declare type of kind {def.Tag} here");
- }
- }
-
- internal string Transform(GenFile file)
- {
- NameSyntax topNamespace = GenNames.NamespaceName(file.Namespace) ?? _names.TopNamespace;
-
- var ns = NamespaceDeclaration(topNamespace);
-
- foreach (var def in file.NestedTypes)
- {
- ns = ns.AddMembers(Transform(def).ToArray());
- }
-
- var cu = CompilationUnit().AddUsings(
- UsingDirective(ParseName("Capnp")),
- UsingDirective(ParseName("Capnp.Rpc")),
- UsingDirective(ParseName("System")),
- UsingDirective(ParseName("System.Collections.Generic")),
- UsingDirective(ParseName("System.Threading")),
- UsingDirective(ParseName("System.Threading.Tasks")));
-
- cu = cu.AddMembers(ns);
-
- return cu.NormalizeWhitespace().ToFullString();
- }
-
- public void Generate()
- {
- foreach (var file in _model.FilesToGenerate)
- {
- string content = Transform(file);
- string path = Path.ChangeExtension(file.Name, ".cs");
- File.WriteAllText(path, content);
- }
- }
- }
-}
diff --git a/capnpc-csharp/Generator/CommonSnippetGen.cs b/capnpc-csharp/Generator/CommonSnippetGen.cs
deleted file mode 100644
index 2e1797d..0000000
--- a/capnpc-csharp/Generator/CommonSnippetGen.cs
+++ /dev/null
@@ -1,93 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using CapnpC.Model;
-using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.CSharp;
-using Microsoft.CodeAnalysis.CSharp.Syntax;
-using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
-using static CapnpC.Generator.SyntaxHelpers;
-
-namespace CapnpC.Generator
-{
- class CommonSnippetGen
- {
- readonly GenNames _names;
-
- public CommonSnippetGen(GenNames names)
- {
- _names = names;
- }
-
- public EnumDeclarationSyntax MakeUnionSelectorEnum(TypeDefinition def)
- {
- var whichEnum = EnumDeclaration(_names.UnionDiscriminatorEnum.ToString())
- .AddModifiers(Public)
- .AddBaseListTypes(SimpleBaseType(Type()));
-
- var discFields = def.Fields.Where(f => f.DiscValue.HasValue);
-
- foreach (var discField in discFields)
- {
- whichEnum = whichEnum.AddMembers(
- EnumMemberDeclaration(_names.GetCodeIdentifier(discField).Identifier)
- .WithEqualsValue(
- EqualsValueClause(LiteralExpression(
- SyntaxKind.NumericLiteralExpression,
- Literal(discField.DiscValue.Value)))));
- }
-
- var ndecl = EnumMemberDeclaration(_names.UnionDiscriminatorUndefined.ToString()).WithEqualsValue(
- EqualsValueClause(
- LiteralExpression(
- SyntaxKind.NumericLiteralExpression,
- Literal(Schema.Field.Reader.NoDiscriminant))));
-
- whichEnum = whichEnum.AddMembers(ndecl);
-
- return whichEnum;
- }
-
- public EnumDeclarationSyntax MakeEnum(TypeDefinition def)
- {
- var decl = EnumDeclaration(def.Name)
- .AddModifiers(Public)
- .AddBaseListTypes(SimpleBaseType(Type()));
-
- foreach (var enumerant in def.Enumerants.OrderBy(e => e.CodeOrder))
- {
- var mdecl = EnumMemberDeclaration(enumerant.Literal);
-
- if (enumerant.Ordinal.HasValue)
- {
- mdecl = mdecl.WithEqualsValue(
- EqualsValueClause(
- LiteralExpression(
- SyntaxKind.NumericLiteralExpression,
- Literal(enumerant.Ordinal.Value))));
- }
-
- decl = decl.AddMembers(mdecl);
- }
-
- return decl;
- }
-
- public static IEnumerable MakeCommaSeparatedList(IEnumerable expressions)
- {
- bool first = true;
-
- foreach (var expr in expressions)
- {
- if (first)
- first = false;
- else
- yield return Token(SyntaxKind.CommaToken);
-
- yield return expr;
- }
- }
-
- }
-}
diff --git a/capnpc-csharp/Generator/DomainClassSnippetGen.cs b/capnpc-csharp/Generator/DomainClassSnippetGen.cs
deleted file mode 100644
index 5b27495..0000000
--- a/capnpc-csharp/Generator/DomainClassSnippetGen.cs
+++ /dev/null
@@ -1,972 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using CapnpC.Model;
-using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.CSharp;
-using Microsoft.CodeAnalysis.CSharp.Syntax;
-using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
-using static CapnpC.Generator.SyntaxHelpers;
-
-namespace CapnpC.Generator
-{
- class DomainClassSnippetGen
- {
- readonly GenNames _names;
-
- public DomainClassSnippetGen(GenNames names)
- {
- _names = names;
- }
-
- MemberDeclarationSyntax MakeUnionField(Field field)
- {
- var type = _names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.DomainClassNullable);
-
- switch (field.Type.Tag)
- {
- case TypeTag.Void:
- return null;
-
- default:
- return PropertyDeclaration(type,
- _names.GetCodeIdentifier(field).Identifier)
- .AddModifiers(Public).AddAccessorListAccessors(
- AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
- .WithExpressionBody(
- ArrowExpressionClause(
- ConditionalExpression(
- BinaryExpression(
- SyntaxKind.EqualsExpression,
- _names.UnionDiscriminatorField.IdentifierName,
- MemberAccessExpression(
- SyntaxKind.SimpleMemberAccessExpression,
- _names.UnionDiscriminatorEnum.IdentifierName,
- _names.GetCodeIdentifier(field).IdentifierName)),
- CastExpression(type,
- _names.UnionContentField.IdentifierName),
- LiteralExpression(
- SyntaxKind.NullLiteralExpression))))
- .WithSemicolonToken(
- Token(SyntaxKind.SemicolonToken)),
- AccessorDeclaration(
- SyntaxKind.SetAccessorDeclaration)
- .WithBody(
- Block(
- ExpressionStatement(
- AssignmentExpression(
- SyntaxKind.SimpleAssignmentExpression,
- _names.UnionDiscriminatorField.IdentifierName,
- MemberAccessExpression(
- SyntaxKind.SimpleMemberAccessExpression,
- _names.UnionDiscriminatorEnum.IdentifierName,
- _names.GetCodeIdentifier(field).IdentifierName))),
- ExpressionStatement(
- AssignmentExpression(
- SyntaxKind.SimpleAssignmentExpression,
- _names.UnionContentField.IdentifierName,
- IdentifierName("value"))))));
- }
- }
-
- MemberDeclarationSyntax MakeStructField(Field field)
- {
- if (field.Type.Tag == TypeTag.Void)
- {
- return null;
- }
-
- var prop = PropertyDeclaration(_names.MakeTypeSyntax(field.Type, field.DeclaringType, TypeUsage.DomainClass),
- _names.GetCodeIdentifier(field).Identifier)
- .AddModifiers(Public).AddAccessorListAccessors(
- AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
- .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)),
- AccessorDeclaration(SyntaxKind.SetAccessorDeclaration)
- .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)));
-
- if (field.DefaultValueIsExplicit && field.Type.IsValueType)
- {
- prop = prop.WithInitializer(
- EqualsValueClause(MakeDefaultValue(field)))
- .WithSemicolonToken(Token(SyntaxKind.SemicolonToken));
- }
-
- return prop;
- }
-
- MemberDeclarationSyntax MakeUnionDiscriminatorField()
- {
- return FieldDeclaration(
- VariableDeclaration(_names.UnionDiscriminatorEnum.IdentifierName)
- .AddVariables(
- VariableDeclarator(_names.UnionDiscriminatorField.Identifier)
- .WithInitializer(
- EqualsValueClause(
- MemberAccessExpression(
- SyntaxKind.SimpleMemberAccessExpression,
- _names.UnionDiscriminatorEnum.IdentifierName,
- _names.UnionDiscriminatorUndefined.IdentifierName)))))
- .AddModifiers(Private);
- }
-
- MemberDeclarationSyntax MakeUnionContentField()
- {
- return FieldDeclaration(
- VariableDeclaration(SyntaxHelpers.Type