aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/csharp
diff options
context:
space:
mode:
authorGravatar kkm <kkm@smartaction.com>2018-10-14 08:31:35 -0700
committerGravatar kkm <kkm@smartaction.com>2018-10-14 08:34:03 -0700
commit5103951117fa4c7350427ecd340031316a15615e (patch)
tree0662e839f9c5088119df612b5c65c87472e4642f /src/csharp
parentf626d4618d8ed15853e763e96a23e3635be0c339 (diff)
Infer coding style, adjust .editorconfig, reformat Tools code
Diffstat (limited to 'src/csharp')
-rw-r--r--src/csharp/.editorconfig23
-rw-r--r--src/csharp/Grpc.Tools.Tests/CSharpGeneratorTest.cs108
-rw-r--r--src/csharp/Grpc.Tools.Tests/CppGeneratorTest.cs112
-rw-r--r--src/csharp/Grpc.Tools.Tests/DepFileUtilTest.cs208
-rw-r--r--src/csharp/Grpc.Tools.Tests/GeneratorTest.cs51
-rw-r--r--src/csharp/Grpc.Tools.Tests/NUnitMain.cs14
-rw-r--r--src/csharp/Grpc.Tools.Tests/ProtoCompileBasicTest.cs84
-rw-r--r--src/csharp/Grpc.Tools.Tests/ProtoCompileCommandLineGeneratorTest.cs291
-rw-r--r--src/csharp/Grpc.Tools.Tests/ProtoCompileCommandLinePrinterTest.cs48
-rw-r--r--src/csharp/Grpc.Tools.Tests/ProtoToolsPlatformTaskTest.cs199
-rw-r--r--src/csharp/Grpc.Tools.Tests/Utils.cs38
-rw-r--r--src/csharp/Grpc.Tools/Common.cs143
-rw-r--r--src/csharp/Grpc.Tools/DepFileUtil.cs450
-rw-r--r--src/csharp/Grpc.Tools/GeneratorServices.cs306
-rw-r--r--src/csharp/Grpc.Tools/ProtoCompile.cs782
-rw-r--r--src/csharp/Grpc.Tools/ProtoCompilerOutputs.cs112
-rw-r--r--src/csharp/Grpc.Tools/ProtoReadDependencies.cs94
-rw-r--r--src/csharp/Grpc.Tools/ProtoToolsPlatform.cs73
18 files changed, 1681 insertions, 1455 deletions
diff --git a/src/csharp/.editorconfig b/src/csharp/.editorconfig
index fabce7f5ba..c9a2c48a7d 100644
--- a/src/csharp/.editorconfig
+++ b/src/csharp/.editorconfig
@@ -6,3 +6,26 @@ indent_style = space
indent_size = 4
insert_final_newline = true
tab_width = 4
+
+; https://docs.microsoft.com/visualstudio/ide/editorconfig-code-style-settings-reference
+[*.cs]
+dotnet_sort_system_directives_first = true
+csharp_new_line_before_open_brace = accessors, anonymous_methods, control_blocks, events, indexers, local_functions, methods, properties, types
+csharp_new_line_before_else = true
+csharp_new_line_before_catch = true
+csharp_new_line_before_finally = true
+csharp_indent_case_contents = true
+csharp_indent_switch_labels = true
+csharp_space_after_cast = false
+csharp_space_after_keywords_in_control_flow_statements = true
+csharp_space_between_method_declaration_parameter_list_parentheses = false
+csharp_space_between_method_call_parameter_list_parentheses = false
+csharp_space_between_parentheses = false
+csharp_space_before_colon_in_inheritance_clause = true
+csharp_space_after_colon_in_inheritance_clause = true
+csharp_space_around_binary_operators = before_and_after
+csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
+csharp_space_between_method_call_name_and_opening_parenthesis = false
+csharp_space_between_method_call_empty_parameter_list_parentheses = false
+csharp_preserve_single_line_statements = true
+csharp_preserve_single_line_blocks = true
diff --git a/src/csharp/Grpc.Tools.Tests/CSharpGeneratorTest.cs b/src/csharp/Grpc.Tools.Tests/CSharpGeneratorTest.cs
index 654500d53d..e4c9b2fa84 100644
--- a/src/csharp/Grpc.Tools.Tests/CSharpGeneratorTest.cs
+++ b/src/csharp/Grpc.Tools.Tests/CSharpGeneratorTest.cs
@@ -18,60 +18,68 @@
using NUnit.Framework;
-namespace Grpc.Tools.Tests {
- public class CSharpGeneratorTest : GeneratorTest {
- GeneratorServices _generator;
+namespace Grpc.Tools.Tests
+{
+ public class CSharpGeneratorTest : GeneratorTest
+ {
+ GeneratorServices _generator;
- [SetUp]
- public new void SetUp() {
- _generator = GeneratorServices.GetForLanguage("CSharp", _log);
- }
+ [SetUp]
+ public new void SetUp()
+ {
+ _generator = GeneratorServices.GetForLanguage("CSharp", _log);
+ }
- [TestCase("foo.proto", "Foo.cs", "FooGrpc.cs")]
- [TestCase("sub/foo.proto", "Foo.cs", "FooGrpc.cs")]
- [TestCase("one_two.proto", "OneTwo.cs", "OneTwoGrpc.cs")]
- [TestCase("__one_two!.proto", "OneTwo!.cs", "OneTwo!Grpc.cs")]
- [TestCase("one(two).proto", "One(two).cs", "One(two)Grpc.cs")]
- [TestCase("one_(two).proto", "One(two).cs", "One(two)Grpc.cs")]
- [TestCase("one two.proto", "One two.cs", "One twoGrpc.cs")]
- [TestCase("one_ two.proto", "One two.cs", "One twoGrpc.cs")]
- [TestCase("one .proto", "One .cs", "One Grpc.cs")]
- public void NameMangling(string proto, string expectCs, string expectGrpcCs) {
- var poss = _generator.GetPossibleOutputs(Utils.MakeItem(proto, "grpcservices", "both"));
- Assert.AreEqual(2, poss.Length);
- Assert.Contains(expectCs, poss);
- Assert.Contains(expectGrpcCs, poss);
- }
+ [TestCase("foo.proto", "Foo.cs", "FooGrpc.cs")]
+ [TestCase("sub/foo.proto", "Foo.cs", "FooGrpc.cs")]
+ [TestCase("one_two.proto", "OneTwo.cs", "OneTwoGrpc.cs")]
+ [TestCase("__one_two!.proto", "OneTwo!.cs", "OneTwo!Grpc.cs")]
+ [TestCase("one(two).proto", "One(two).cs", "One(two)Grpc.cs")]
+ [TestCase("one_(two).proto", "One(two).cs", "One(two)Grpc.cs")]
+ [TestCase("one two.proto", "One two.cs", "One twoGrpc.cs")]
+ [TestCase("one_ two.proto", "One two.cs", "One twoGrpc.cs")]
+ [TestCase("one .proto", "One .cs", "One Grpc.cs")]
+ public void NameMangling(string proto, string expectCs, string expectGrpcCs)
+ {
+ var poss = _generator.GetPossibleOutputs(Utils.MakeItem(proto, "grpcservices", "both"));
+ Assert.AreEqual(2, poss.Length);
+ Assert.Contains(expectCs, poss);
+ Assert.Contains(expectGrpcCs, poss);
+ }
- [Test]
- public void NoGrpcOneOutput() {
- var poss = _generator.GetPossibleOutputs(Utils.MakeItem("foo.proto"));
- Assert.AreEqual(1, poss.Length);
- }
+ [Test]
+ public void NoGrpcOneOutput()
+ {
+ var poss = _generator.GetPossibleOutputs(Utils.MakeItem("foo.proto"));
+ Assert.AreEqual(1, poss.Length);
+ }
- [TestCase("none")]
- [TestCase("")]
- public void GrpcNoneOneOutput(string grpc) {
- var item = Utils.MakeItem("foo.proto", "grpcservices", grpc);
- var poss = _generator.GetPossibleOutputs(item);
- Assert.AreEqual(1, poss.Length);
- }
+ [TestCase("none")]
+ [TestCase("")]
+ public void GrpcNoneOneOutput(string grpc)
+ {
+ var item = Utils.MakeItem("foo.proto", "grpcservices", grpc);
+ var poss = _generator.GetPossibleOutputs(item);
+ Assert.AreEqual(1, poss.Length);
+ }
- [TestCase("client")]
- [TestCase("server")]
- [TestCase("both")]
- public void GrpcEnabledTwoOutputs(string grpc) {
- var item = Utils.MakeItem("foo.proto", "grpcservices", grpc);
- var poss = _generator.GetPossibleOutputs(item);
- Assert.AreEqual(2, poss.Length);
- }
+ [TestCase("client")]
+ [TestCase("server")]
+ [TestCase("both")]
+ public void GrpcEnabledTwoOutputs(string grpc)
+ {
+ var item = Utils.MakeItem("foo.proto", "grpcservices", grpc);
+ var poss = _generator.GetPossibleOutputs(item);
+ Assert.AreEqual(2, poss.Length);
+ }
- [Test]
- public void OutputDirMetadataRecognized() {
- var item = Utils.MakeItem("foo.proto", "OutputDir", "out");
- var poss = _generator.GetPossibleOutputs(item);
- Assert.AreEqual(1, poss.Length);
- Assert.That(poss[0], Is.EqualTo("out/Foo.cs") | Is.EqualTo("out\\Foo.cs"));
- }
- };
+ [Test]
+ public void OutputDirMetadataRecognized()
+ {
+ var item = Utils.MakeItem("foo.proto", "OutputDir", "out");
+ var poss = _generator.GetPossibleOutputs(item);
+ Assert.AreEqual(1, poss.Length);
+ Assert.That(poss[0], Is.EqualTo("out/Foo.cs") | Is.EqualTo("out\\Foo.cs"));
+ }
+ };
}
diff --git a/src/csharp/Grpc.Tools.Tests/CppGeneratorTest.cs b/src/csharp/Grpc.Tools.Tests/CppGeneratorTest.cs
index a3450fae17..bd0405a03a 100644
--- a/src/csharp/Grpc.Tools.Tests/CppGeneratorTest.cs
+++ b/src/csharp/Grpc.Tools.Tests/CppGeneratorTest.cs
@@ -19,62 +19,70 @@
using System.IO;
using NUnit.Framework;
-namespace Grpc.Tools.Tests {
- public class CppGeneratorTest : GeneratorTest {
- GeneratorServices _generator;
+namespace Grpc.Tools.Tests
+{
+ public class CppGeneratorTest : GeneratorTest
+ {
+ GeneratorServices _generator;
- [SetUp]
- public new void SetUp() {
- _generator = GeneratorServices.GetForLanguage("Cpp", _log);
- }
+ [SetUp]
+ public new void SetUp()
+ {
+ _generator = GeneratorServices.GetForLanguage("Cpp", _log);
+ }
- [TestCase("foo.proto", "", "foo")]
- [TestCase("foo.proto", ".", "foo")]
- [TestCase("foo.proto", "./", "foo")]
- [TestCase("sub/foo.proto", "", "sub/foo")]
- [TestCase("root/sub/foo.proto", "root", "sub/foo")]
- [TestCase("root/sub/foo.proto", "root", "sub/foo")]
- [TestCase("/root/sub/foo.proto", "/root", "sub/foo")]
- public void RelativeDirectoryCompute(string proto, string root, string expectStem) {
- if (Path.DirectorySeparatorChar == '\\')
- expectStem = expectStem.Replace('/', '\\');
- var poss = _generator.GetPossibleOutputs(Utils.MakeItem(proto, "ProtoRoot", root));
- Assert.AreEqual(2, poss.Length);
- Assert.Contains(expectStem + ".pb.cc", poss);
- Assert.Contains(expectStem + ".pb.h", poss);
- }
+ [TestCase("foo.proto", "", "foo")]
+ [TestCase("foo.proto", ".", "foo")]
+ [TestCase("foo.proto", "./", "foo")]
+ [TestCase("sub/foo.proto", "", "sub/foo")]
+ [TestCase("root/sub/foo.proto", "root", "sub/foo")]
+ [TestCase("root/sub/foo.proto", "root", "sub/foo")]
+ [TestCase("/root/sub/foo.proto", "/root", "sub/foo")]
+ public void RelativeDirectoryCompute(string proto, string root, string expectStem)
+ {
+ if (Path.DirectorySeparatorChar == '\\')
+ expectStem = expectStem.Replace('/', '\\');
+ var poss = _generator.GetPossibleOutputs(Utils.MakeItem(proto, "ProtoRoot", root));
+ Assert.AreEqual(2, poss.Length);
+ Assert.Contains(expectStem + ".pb.cc", poss);
+ Assert.Contains(expectStem + ".pb.h", poss);
+ }
- [Test]
- public void NoGrpcTwoOutputs() {
- var poss = _generator.GetPossibleOutputs(Utils.MakeItem("foo.proto"));
- Assert.AreEqual(2, poss.Length);
- }
+ [Test]
+ public void NoGrpcTwoOutputs()
+ {
+ var poss = _generator.GetPossibleOutputs(Utils.MakeItem("foo.proto"));
+ Assert.AreEqual(2, poss.Length);
+ }
- [TestCase("false")]
- [TestCase("")]
- public void GrpcDisabledTwoOutput(string grpc) {
- var item = Utils.MakeItem("foo.proto", "grpcservices", grpc);
- var poss = _generator.GetPossibleOutputs(item);
- Assert.AreEqual(2, poss.Length);
- }
+ [TestCase("false")]
+ [TestCase("")]
+ public void GrpcDisabledTwoOutput(string grpc)
+ {
+ var item = Utils.MakeItem("foo.proto", "grpcservices", grpc);
+ var poss = _generator.GetPossibleOutputs(item);
+ Assert.AreEqual(2, poss.Length);
+ }
- [TestCase("true")]
- public void GrpcEnabledFourOutputs(string grpc) {
- var item = Utils.MakeItem("foo.proto", "grpcservices", grpc);
- var poss = _generator.GetPossibleOutputs(item);
- Assert.AreEqual(4, poss.Length);
- Assert.Contains("foo.pb.cc", poss);
- Assert.Contains("foo.pb.h", poss);
- Assert.Contains("foo_grpc.pb.cc", poss);
- Assert.Contains("foo_grpc.pb.h", poss);
- }
+ [TestCase("true")]
+ public void GrpcEnabledFourOutputs(string grpc)
+ {
+ var item = Utils.MakeItem("foo.proto", "grpcservices", grpc);
+ var poss = _generator.GetPossibleOutputs(item);
+ Assert.AreEqual(4, poss.Length);
+ Assert.Contains("foo.pb.cc", poss);
+ Assert.Contains("foo.pb.h", poss);
+ Assert.Contains("foo_grpc.pb.cc", poss);
+ Assert.Contains("foo_grpc.pb.h", poss);
+ }
- [Test]
- public void OutputDirMetadataRecognized() {
- var item = Utils.MakeItem("foo.proto", "OutputDir", "out");
- var poss = _generator.GetPossibleOutputs(item);
- Assert.AreEqual(2, poss.Length);
- Assert.That(Path.GetDirectoryName(poss[0]), Is.EqualTo("out"));
- }
- };
+ [Test]
+ public void OutputDirMetadataRecognized()
+ {
+ var item = Utils.MakeItem("foo.proto", "OutputDir", "out");
+ var poss = _generator.GetPossibleOutputs(item);
+ Assert.AreEqual(2, poss.Length);
+ Assert.That(Path.GetDirectoryName(poss[0]), Is.EqualTo("out"));
+ }
+ };
}
diff --git a/src/csharp/Grpc.Tools.Tests/DepFileUtilTest.cs b/src/csharp/Grpc.Tools.Tests/DepFileUtilTest.cs
index ea34c89921..e89a8f4b5d 100644
--- a/src/csharp/Grpc.Tools.Tests/DepFileUtilTest.cs
+++ b/src/csharp/Grpc.Tools.Tests/DepFileUtilTest.cs
@@ -21,53 +21,58 @@ using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using NUnit.Framework;
-namespace Grpc.Tools.Tests {
- public class DepFileUtilTest {
-
- [Test]
- public void HashString64Hex_IsSane() {
- string hashFoo1 = DepFileUtil.HashString64Hex("foo");
- string hashEmpty = DepFileUtil.HashString64Hex("");
- string hashFoo2 = DepFileUtil.HashString64Hex("foo");
-
- StringAssert.IsMatch("^[a-f0-9]{16}$", hashFoo1);
- Assert.AreEqual(hashFoo1, hashFoo2);
- Assert.AreNotEqual(hashFoo1, hashEmpty);
- }
-
- [Test]
- public void GetDepFilenameForProto_IsSane() {
- StringAssert.IsMatch(@"^out[\\/][a-f0-9]{16}_foo.protodep$",
- DepFileUtil.GetDepFilenameForProto("out", "foo.proto"));
- StringAssert.IsMatch(@"^[a-f0-9]{16}_foo.protodep$",
- DepFileUtil.GetDepFilenameForProto("", "foo.proto"));
- }
-
- [Test]
- public void GetDepFilenameForProto_HashesDir() {
- string PickHash(string fname) =>
- DepFileUtil.GetDepFilenameForProto("", fname).Substring(0, 16);
-
- string same1 = PickHash("dir1/dir2/foo.proto");
- string same2 = PickHash("dir1/dir2/proto.foo");
- string same3 = PickHash("dir1/dir2/proto");
- string same4 = PickHash("dir1/dir2/.proto");
- string unsame1 = PickHash("dir2/foo.proto");
- string unsame2 = PickHash("/dir2/foo.proto");
-
- Assert.AreEqual(same1, same2);
- Assert.AreEqual(same1, same3);
- Assert.AreEqual(same1, same4);
- Assert.AreNotEqual(same1, unsame1);
- Assert.AreNotEqual(unsame1, unsame2);
- }
-
- //////////////////////////////////////////////////////////////////////////
- // Full file reading tests
-
- // Generated by protoc on Windows. Slashes vary.
- const string depFile1 =
-@"C:\projects\foo\src\./foo.grpc.pb.cc \
+namespace Grpc.Tools.Tests
+{
+ public class DepFileUtilTest
+ {
+
+ [Test]
+ public void HashString64Hex_IsSane()
+ {
+ string hashFoo1 = DepFileUtil.HashString64Hex("foo");
+ string hashEmpty = DepFileUtil.HashString64Hex("");
+ string hashFoo2 = DepFileUtil.HashString64Hex("foo");
+
+ StringAssert.IsMatch("^[a-f0-9]{16}$", hashFoo1);
+ Assert.AreEqual(hashFoo1, hashFoo2);
+ Assert.AreNotEqual(hashFoo1, hashEmpty);
+ }
+
+ [Test]
+ public void GetDepFilenameForProto_IsSane()
+ {
+ StringAssert.IsMatch(@"^out[\\/][a-f0-9]{16}_foo.protodep$",
+ DepFileUtil.GetDepFilenameForProto("out", "foo.proto"));
+ StringAssert.IsMatch(@"^[a-f0-9]{16}_foo.protodep$",
+ DepFileUtil.GetDepFilenameForProto("", "foo.proto"));
+ }
+
+ [Test]
+ public void GetDepFilenameForProto_HashesDir()
+ {
+ string PickHash(string fname) =>
+ DepFileUtil.GetDepFilenameForProto("", fname).Substring(0, 16);
+
+ string same1 = PickHash("dir1/dir2/foo.proto");
+ string same2 = PickHash("dir1/dir2/proto.foo");
+ string same3 = PickHash("dir1/dir2/proto");
+ string same4 = PickHash("dir1/dir2/.proto");
+ string unsame1 = PickHash("dir2/foo.proto");
+ string unsame2 = PickHash("/dir2/foo.proto");
+
+ Assert.AreEqual(same1, same2);
+ Assert.AreEqual(same1, same3);
+ Assert.AreEqual(same1, same4);
+ Assert.AreNotEqual(same1, unsame1);
+ Assert.AreNotEqual(unsame1, unsame2);
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Full file reading tests
+
+ // Generated by protoc on Windows. Slashes vary.
+ const string depFile1 =
+ @"C:\projects\foo\src\./foo.grpc.pb.cc \
C:\projects\foo\src\./foo.grpc.pb.h \
C:\projects\foo\src\./foo.pb.cc \
C:\projects\foo\src\./foo.pb.h: C:/usr/include/google/protobuf/wrappers.proto\
@@ -76,57 +81,66 @@ C:/usr/include/google/protobuf/source_context.proto\
C:/usr/include/google/protobuf/type.proto\
foo.proto";
- // This has a nasty output directory with a space.
- const string depFile2 =
-@"obj\Release x64\net45\/Foo.cs \
+ // This has a nasty output directory with a space.
+ const string depFile2 =
+ @"obj\Release x64\net45\/Foo.cs \
obj\Release x64\net45\/FooGrpc.cs: C:/usr/include/google/protobuf/wrappers.proto\
C:/projects/foo/src//foo.proto";
- [Test]
- public void ReadDependencyInput_FullFile1() {
- string[] deps = ReadDependencyInputFromFileData(depFile1, "foo.proto");
-
- Assert.NotNull(deps);
- Assert.That(deps, Has.Length.InRange(4, 5)); // foo.proto may or may not be listed.
- Assert.That(deps, Has.One.EndsWith("wrappers.proto"));
- Assert.That(deps, Has.One.EndsWith("type.proto"));
- Assert.That(deps, Has.None.StartWith(" "));
- }
-
- [Test]
- public void ReadDependencyInput_FullFile2() {
- string[] deps = ReadDependencyInputFromFileData(depFile2, "C:/projects/foo/src/foo.proto");
-
- Assert.NotNull(deps);
- Assert.That(deps, Has.Length.InRange(1, 2));
- Assert.That(deps, Has.One.EndsWith("wrappers.proto"));
- Assert.That(deps, Has.None.StartWith(" "));
- }
-
- [Test]
- public void ReadDependencyInput_FullFileUnparsable() {
- string[] deps = ReadDependencyInputFromFileData("a:/foo.proto", "/foo.proto");
- Assert.NotNull(deps);
- Assert.Zero(deps.Length);
- }
-
- // NB in our tests files are put into the temp directory but all have
- // different names. Avoid adding files with the same directory path and
- // name, or add reasonable handling for it if required. Tests are run in
- // parallel and will collide otherwise.
- private string[] ReadDependencyInputFromFileData(string fileData, string protoName) {
- string tempPath = Path.GetTempPath();
- string tempfile = DepFileUtil.GetDepFilenameForProto(tempPath, protoName);
- try {
- File.WriteAllText(tempfile, fileData);
- var mockEng = new Moq.Mock<IBuildEngine>();
- var log = new TaskLoggingHelper(mockEng.Object, "x");
- return DepFileUtil.ReadDependencyInputs(tempPath, protoName, log);
- } finally {
- try {
- File.Delete(tempfile);
- } catch { }
- }
- }
- };
+ [Test]
+ public void ReadDependencyInput_FullFile1()
+ {
+ string[] deps = ReadDependencyInputFromFileData(depFile1, "foo.proto");
+
+ Assert.NotNull(deps);
+ Assert.That(deps, Has.Length.InRange(4, 5)); // foo.proto may or may not be listed.
+ Assert.That(deps, Has.One.EndsWith("wrappers.proto"));
+ Assert.That(deps, Has.One.EndsWith("type.proto"));
+ Assert.That(deps, Has.None.StartWith(" "));
+ }
+
+ [Test]
+ public void ReadDependencyInput_FullFile2()
+ {
+ string[] deps = ReadDependencyInputFromFileData(depFile2, "C:/projects/foo/src/foo.proto");
+
+ Assert.NotNull(deps);
+ Assert.That(deps, Has.Length.InRange(1, 2));
+ Assert.That(deps, Has.One.EndsWith("wrappers.proto"));
+ Assert.That(deps, Has.None.StartWith(" "));
+ }
+
+ [Test]
+ public void ReadDependencyInput_FullFileUnparsable()
+ {
+ string[] deps = ReadDependencyInputFromFileData("a:/foo.proto", "/foo.proto");
+ Assert.NotNull(deps);
+ Assert.Zero(deps.Length);
+ }
+
+ // NB in our tests files are put into the temp directory but all have
+ // different names. Avoid adding files with the same directory path and
+ // name, or add reasonable handling for it if required. Tests are run in
+ // parallel and will collide otherwise.
+ private string[] ReadDependencyInputFromFileData(string fileData, string protoName)
+ {
+ string tempPath = Path.GetTempPath();
+ string tempfile = DepFileUtil.GetDepFilenameForProto(tempPath, protoName);
+ try
+ {
+ File.WriteAllText(tempfile, fileData);
+ var mockEng = new Moq.Mock<IBuildEngine>();
+ var log = new TaskLoggingHelper(mockEng.Object, "x");
+ return DepFileUtil.ReadDependencyInputs(tempPath, protoName, log);
+ }
+ finally
+ {
+ try
+ {
+ File.Delete(tempfile);
+ }
+ catch { }
+ }
+ }
+ };
}
diff --git a/src/csharp/Grpc.Tools.Tests/GeneratorTest.cs b/src/csharp/Grpc.Tools.Tests/GeneratorTest.cs
index 52fab1d8ca..8a8fc81aba 100644
--- a/src/csharp/Grpc.Tools.Tests/GeneratorTest.cs
+++ b/src/csharp/Grpc.Tools.Tests/GeneratorTest.cs
@@ -21,30 +21,35 @@ using Microsoft.Build.Utilities;
using Moq;
using NUnit.Framework;
-namespace Grpc.Tools.Tests {
- public class GeneratorTest {
- protected Mock<IBuildEngine> _mockEngine;
- protected TaskLoggingHelper _log;
+namespace Grpc.Tools.Tests
+{
+ public class GeneratorTest
+ {
+ protected Mock<IBuildEngine> _mockEngine;
+ protected TaskLoggingHelper _log;
- [SetUp]
- public void SetUp() {
- _mockEngine = new Mock<IBuildEngine>();
- _log = new TaskLoggingHelper(_mockEngine.Object, "dummy");
- }
+ [SetUp]
+ public void SetUp()
+ {
+ _mockEngine = new Mock<IBuildEngine>();
+ _log = new TaskLoggingHelper(_mockEngine.Object, "dummy");
+ }
- [TestCase("csharp")]
- [TestCase("CSharp")]
- [TestCase("cpp")]
- public void ValidLanguages(string lang) {
- Assert.IsNotNull(GeneratorServices.GetForLanguage(lang, _log));
- _mockEngine.Verify(me => me.LogErrorEvent(It.IsAny<BuildErrorEventArgs>()), Times.Never);
- }
+ [TestCase("csharp")]
+ [TestCase("CSharp")]
+ [TestCase("cpp")]
+ public void ValidLanguages(string lang)
+ {
+ Assert.IsNotNull(GeneratorServices.GetForLanguage(lang, _log));
+ _mockEngine.Verify(me => me.LogErrorEvent(It.IsAny<BuildErrorEventArgs>()), Times.Never);
+ }
- [TestCase("")]
- [TestCase("COBOL")]
- public void InvalidLanguages(string lang) {
- Assert.IsNull(GeneratorServices.GetForLanguage(lang, _log));
- _mockEngine.Verify(me => me.LogErrorEvent(It.IsAny<BuildErrorEventArgs>()), Times.Once);
- }
- };
+ [TestCase("")]
+ [TestCase("COBOL")]
+ public void InvalidLanguages(string lang)
+ {
+ Assert.IsNull(GeneratorServices.GetForLanguage(lang, _log));
+ _mockEngine.Verify(me => me.LogErrorEvent(It.IsAny<BuildErrorEventArgs>()), Times.Once);
+ }
+ };
}
diff --git a/src/csharp/Grpc.Tools.Tests/NUnitMain.cs b/src/csharp/Grpc.Tools.Tests/NUnitMain.cs
index 5784cdeac2..418c33820e 100644
--- a/src/csharp/Grpc.Tools.Tests/NUnitMain.cs
+++ b/src/csharp/Grpc.Tools.Tests/NUnitMain.cs
@@ -19,13 +19,15 @@
using System.Reflection;
using NUnitLite;
-namespace Grpc.Tools.Tests {
- static class NUnitMain {
- public static int Main(string[] args) =>
+namespace Grpc.Tools.Tests
+{
+ static class NUnitMain
+ {
+ public static int Main(string[] args) =>
#if NETCOREAPP1_0 || NETCOREAPP1_1
- new AutoRun(typeof(NUnitMain).GetTypeInfo().Assembly).Execute(args);
+ new AutoRun(typeof(NUnitMain).GetTypeInfo().Assembly).Execute(args);
#else
- new AutoRun().Execute(args);
+ new AutoRun().Execute(args);
#endif
- };
+ };
}
diff --git a/src/csharp/Grpc.Tools.Tests/ProtoCompileBasicTest.cs b/src/csharp/Grpc.Tools.Tests/ProtoCompileBasicTest.cs
index cf9d210424..ea763f4e40 100644
--- a/src/csharp/Grpc.Tools.Tests/ProtoCompileBasicTest.cs
+++ b/src/csharp/Grpc.Tools.Tests/ProtoCompileBasicTest.cs
@@ -21,50 +21,56 @@ using Microsoft.Build.Framework;
using Moq;
using NUnit.Framework;
-namespace Grpc.Tools.Tests {
- public class ProtoCompileBasicTest {
- // Mock task class that stops right before invoking protoc.
- public class ProtoCompileTestable : ProtoCompile {
- public string LastPathToTool { get; private set; }
- public string[] LastResponseFile { get; private set; }
+namespace Grpc.Tools.Tests
+{
+ public class ProtoCompileBasicTest
+ {
+ // Mock task class that stops right before invoking protoc.
+ public class ProtoCompileTestable : ProtoCompile
+ {
+ public string LastPathToTool { get; private set; }
+ public string[] LastResponseFile { get; private set; }
- protected override int ExecuteTool(string pathToTool,
- string response,
- string commandLine) {
- // We should never be using command line commands.
- Assert.That(commandLine, Is.Null | Is.Empty);
+ protected override int ExecuteTool(string pathToTool,
+ string response,
+ string commandLine)
+ {
+ // We should never be using command line commands.
+ Assert.That(commandLine, Is.Null | Is.Empty);
- // Must receive a path to tool
- Assert.That(pathToTool, Is.Not.Null & Is.Not.Empty);
- Assert.That(response, Is.Not.Null & Does.EndWith("\n"));
+ // Must receive a path to tool
+ Assert.That(pathToTool, Is.Not.Null & Is.Not.Empty);
+ Assert.That(response, Is.Not.Null & Does.EndWith("\n"));
- LastPathToTool = pathToTool;
- LastResponseFile = response.Remove(response.Length - 1).Split('\n');
+ LastPathToTool = pathToTool;
+ LastResponseFile = response.Remove(response.Length - 1).Split('\n');
- // Do not run the tool, but pretend it ran successfully.
- return 0;
- }
- };
+ // Do not run the tool, but pretend it ran successfully.
+ return 0;
+ }
+ };
- protected Mock<IBuildEngine> _mockEngine;
- protected ProtoCompileTestable _task;
+ protected Mock<IBuildEngine> _mockEngine;
+ protected ProtoCompileTestable _task;
- [SetUp]
- public void SetUp() {
- _mockEngine = new Mock<IBuildEngine>();
- _task = new ProtoCompileTestable {
- BuildEngine = _mockEngine.Object
- };
- }
+ [SetUp]
+ public void SetUp()
+ {
+ _mockEngine = new Mock<IBuildEngine>();
+ _task = new ProtoCompileTestable {
+ BuildEngine = _mockEngine.Object
+ };
+ }
- [TestCase("ProtoBuf")]
- [TestCase("Generator")]
- [TestCase("OutputDir")]
- [Description("We trust MSBuild to initialize these properties.")]
- public void RequiredAttributePresentOnProperty(string prop) {
- var pinfo = _task.GetType()?.GetProperty(prop);
- Assert.NotNull(pinfo);
- Assert.That(pinfo, Has.Attribute<RequiredAttribute>());
- }
- };
+ [TestCase("ProtoBuf")]
+ [TestCase("Generator")]
+ [TestCase("OutputDir")]
+ [Description("We trust MSBuild to initialize these properties.")]
+ public void RequiredAttributePresentOnProperty(string prop)
+ {
+ var pinfo = _task.GetType()?.GetProperty(prop);
+ Assert.NotNull(pinfo);
+ Assert.That(pinfo, Has.Attribute<RequiredAttribute>());
+ }
+ };
}
diff --git a/src/csharp/Grpc.Tools.Tests/ProtoCompileCommandLineGeneratorTest.cs b/src/csharp/Grpc.Tools.Tests/ProtoCompileCommandLineGeneratorTest.cs
index 06376f8ef4..cac7146634 100644
--- a/src/csharp/Grpc.Tools.Tests/ProtoCompileCommandLineGeneratorTest.cs
+++ b/src/csharp/Grpc.Tools.Tests/ProtoCompileCommandLineGeneratorTest.cs
@@ -21,144 +21,159 @@ using Microsoft.Build.Framework;
using Moq;
using NUnit.Framework;
-namespace Grpc.Tools.Tests {
- public class ProtoCompileCommandLineGeneratorTest : ProtoCompileBasicTest {
- [SetUp]
- public new void SetUp() {
- _task.Generator = "csharp";
- _task.OutputDir = "outdir";
- _task.ProtoBuf = Utils.MakeSimpleItems("a.proto");
- }
-
- void ExecuteExpectSuccess() {
- _mockEngine
- .Setup(me => me.LogErrorEvent(It.IsAny<BuildErrorEventArgs>()))
- .Callback((BuildErrorEventArgs e) =>
- Assert.Fail($"Error logged by build engine:\n{e.Message}"));
- bool result = _task.Execute();
- Assert.IsTrue(result);
- }
-
- [Test]
- public void MinimalCompile() {
- ExecuteExpectSuccess();
- Assert.That(_task.LastPathToTool, Does.Match(@"protoc(.exe)?$"));
- Assert.That(_task.LastResponseFile, Is.EqualTo(new[] {
- "--csharp_out=outdir", "a.proto" }));
- }
-
- [Test]
- public void CompileTwoFiles() {
- _task.ProtoBuf = Utils.MakeSimpleItems("a.proto", "foo/b.proto");
- ExecuteExpectSuccess();
- Assert.That(_task.LastResponseFile, Is.EqualTo(new[] {
- "--csharp_out=outdir", "a.proto", "foo/b.proto" }));
- }
-
- [Test]
- public void CompileWithProtoPaths() {
- _task.ProtoPath = new[] { "/path1", "/path2" };
- ExecuteExpectSuccess();
- Assert.That(_task.LastResponseFile, Is.EqualTo(new[] {
- "--csharp_out=outdir", "--proto_path=/path1",
- "--proto_path=/path2", "a.proto" }));
- }
-
- [TestCase("Cpp")]
- [TestCase("CSharp")]
- [TestCase("Java")]
- [TestCase("Javanano")]
- [TestCase("Js")]
- [TestCase("Objc")]
- [TestCase("Php")]
- [TestCase("Python")]
- [TestCase("Ruby")]
- public void CompileWithOptions(string gen) {
- _task.Generator = gen;
- _task.OutputOptions = new[] { "foo", "bar" };
- ExecuteExpectSuccess();
- gen = gen.ToLowerInvariant();
- Assert.That(_task.LastResponseFile, Is.EqualTo(new[] {
- $"--{gen}_out=outdir", $"--{gen}_opt=foo,bar", "a.proto" }));
- }
-
- [Test]
- public void OutputDependencyFile() {
- _task.DependencyOut = "foo/my.protodep";
- // Task fails trying to read the non-generated file; we ignore that.
- _task.Execute();
- Assert.That(_task.LastResponseFile,
- Does.Contain("--dependency_out=foo/my.protodep"));
- }
-
- [Test]
- public void OutputDependencyWithProtoDepDir() {
- _task.ProtoDepDir = "foo";
- // Task fails trying to read the non-generated file; we ignore that.
- _task.Execute();
- Assert.That(_task.LastResponseFile,
- Has.One.Match(@"^--dependency_out=foo[/\\].+_a.protodep$"));
- }
-
- [Test]
- public void GenerateGrpc() {
- _task.GrpcPluginExe = "/foo/grpcgen";
- ExecuteExpectSuccess();
- Assert.That(_task.LastResponseFile, Is.SupersetOf(new[] {
- "--csharp_out=outdir", "--grpc_out=outdir",
- "--plugin=protoc-gen-grpc=/foo/grpcgen" }));
- }
-
- [Test]
- public void GenerateGrpcWithOutDir() {
- _task.GrpcPluginExe = "/foo/grpcgen";
- _task.GrpcOutputDir = "gen-out";
- ExecuteExpectSuccess();
- Assert.That(_task.LastResponseFile, Is.SupersetOf(new[] {
- "--csharp_out=outdir", "--grpc_out=gen-out" }));
- }
-
- [Test]
- public void GenerateGrpcWithOptions() {
- _task.GrpcPluginExe = "/foo/grpcgen";
- _task.GrpcOutputOptions = new[] { "baz", "quux" };
- ExecuteExpectSuccess();
- Assert.That(_task.LastResponseFile,
- Does.Contain("--grpc_opt=baz,quux"));
- }
-
- [Test]
- public void DirectoryArgumentsSlashTrimmed() {
- _task.GrpcPluginExe = "/foo/grpcgen";
- _task.GrpcOutputDir = "gen-out/";
- _task.OutputDir = "outdir/";
- _task.ProtoPath = new[] { "/path1/", "/path2/" };
- ExecuteExpectSuccess();
- Assert.That(_task.LastResponseFile, Is.SupersetOf(new[] {
+namespace Grpc.Tools.Tests
+{
+ public class ProtoCompileCommandLineGeneratorTest : ProtoCompileBasicTest
+ {
+ [SetUp]
+ public new void SetUp()
+ {
+ _task.Generator = "csharp";
+ _task.OutputDir = "outdir";
+ _task.ProtoBuf = Utils.MakeSimpleItems("a.proto");
+ }
+
+ void ExecuteExpectSuccess()
+ {
+ _mockEngine
+ .Setup(me => me.LogErrorEvent(It.IsAny<BuildErrorEventArgs>()))
+ .Callback((BuildErrorEventArgs e) =>
+ Assert.Fail($"Error logged by build engine:\n{e.Message}"));
+ bool result = _task.Execute();
+ Assert.IsTrue(result);
+ }
+
+ [Test]
+ public void MinimalCompile()
+ {
+ ExecuteExpectSuccess();
+ Assert.That(_task.LastPathToTool, Does.Match(@"protoc(.exe)?$"));
+ Assert.That(_task.LastResponseFile, Is.EqualTo(new[] {
+ "--csharp_out=outdir", "a.proto" }));
+ }
+
+ [Test]
+ public void CompileTwoFiles()
+ {
+ _task.ProtoBuf = Utils.MakeSimpleItems("a.proto", "foo/b.proto");
+ ExecuteExpectSuccess();
+ Assert.That(_task.LastResponseFile, Is.EqualTo(new[] {
+ "--csharp_out=outdir", "a.proto", "foo/b.proto" }));
+ }
+
+ [Test]
+ public void CompileWithProtoPaths()
+ {
+ _task.ProtoPath = new[] { "/path1", "/path2" };
+ ExecuteExpectSuccess();
+ Assert.That(_task.LastResponseFile, Is.EqualTo(new[] {
+ "--csharp_out=outdir", "--proto_path=/path1",
+ "--proto_path=/path2", "a.proto" }));
+ }
+
+ [TestCase("Cpp")]
+ [TestCase("CSharp")]
+ [TestCase("Java")]
+ [TestCase("Javanano")]
+ [TestCase("Js")]
+ [TestCase("Objc")]
+ [TestCase("Php")]
+ [TestCase("Python")]
+ [TestCase("Ruby")]
+ public void CompileWithOptions(string gen)
+ {
+ _task.Generator = gen;
+ _task.OutputOptions = new[] { "foo", "bar" };
+ ExecuteExpectSuccess();
+ gen = gen.ToLowerInvariant();
+ Assert.That(_task.LastResponseFile, Is.EqualTo(new[] {
+ $"--{gen}_out=outdir", $"--{gen}_opt=foo,bar", "a.proto" }));
+ }
+
+ [Test]
+ public void OutputDependencyFile()
+ {
+ _task.DependencyOut = "foo/my.protodep";
+ // Task fails trying to read the non-generated file; we ignore that.
+ _task.Execute();
+ Assert.That(_task.LastResponseFile,
+ Does.Contain("--dependency_out=foo/my.protodep"));
+ }
+
+ [Test]
+ public void OutputDependencyWithProtoDepDir()
+ {
+ _task.ProtoDepDir = "foo";
+ // Task fails trying to read the non-generated file; we ignore that.
+ _task.Execute();
+ Assert.That(_task.LastResponseFile,
+ Has.One.Match(@"^--dependency_out=foo[/\\].+_a.protodep$"));
+ }
+
+ [Test]
+ public void GenerateGrpc()
+ {
+ _task.GrpcPluginExe = "/foo/grpcgen";
+ ExecuteExpectSuccess();
+ Assert.That(_task.LastResponseFile, Is.SupersetOf(new[] {
+ "--csharp_out=outdir", "--grpc_out=outdir",
+ "--plugin=protoc-gen-grpc=/foo/grpcgen" }));
+ }
+
+ [Test]
+ public void GenerateGrpcWithOutDir()
+ {
+ _task.GrpcPluginExe = "/foo/grpcgen";
+ _task.GrpcOutputDir = "gen-out";
+ ExecuteExpectSuccess();
+ Assert.That(_task.LastResponseFile, Is.SupersetOf(new[] {
+ "--csharp_out=outdir", "--grpc_out=gen-out" }));
+ }
+
+ [Test]
+ public void GenerateGrpcWithOptions()
+ {
+ _task.GrpcPluginExe = "/foo/grpcgen";
+ _task.GrpcOutputOptions = new[] { "baz", "quux" };
+ ExecuteExpectSuccess();
+ Assert.That(_task.LastResponseFile,
+ Does.Contain("--grpc_opt=baz,quux"));
+ }
+
+ [Test]
+ public void DirectoryArgumentsSlashTrimmed()
+ {
+ _task.GrpcPluginExe = "/foo/grpcgen";
+ _task.GrpcOutputDir = "gen-out/";
+ _task.OutputDir = "outdir/";
+ _task.ProtoPath = new[] { "/path1/", "/path2/" };
+ ExecuteExpectSuccess();
+ Assert.That(_task.LastResponseFile, Is.SupersetOf(new[] {
"--proto_path=/path1", "--proto_path=/path2",
"--csharp_out=outdir", "--grpc_out=gen-out" }));
- }
-
- [TestCase("." , ".")]
- [TestCase("/" , "/")]
- [TestCase("//" , "/")]
- [TestCase("/foo/" , "/foo")]
- [TestCase("/foo" , "/foo")]
- [TestCase("foo/" , "foo")]
- [TestCase("foo//" , "foo")]
- [TestCase("foo/\\" , "foo")]
- [TestCase("foo\\/" , "foo")]
- [TestCase("C:\\foo", "C:\\foo")]
- [TestCase("C:" , "C:")]
- [TestCase("C:\\" , "C:\\")]
- [TestCase("C:\\\\" , "C:\\")]
- public void DirectorySlashTrimmingCases(string given, string expect) {
- if (Path.DirectorySeparatorChar == '/')
- expect = expect.Replace('\\', '/');
- _task.OutputDir = given;
- ExecuteExpectSuccess();
- Assert.That(_task.LastResponseFile,
- Does.Contain("--csharp_out=" + expect));
- }
- };
+ }
+
+ [TestCase(".", ".")]
+ [TestCase("/", "/")]
+ [TestCase("//", "/")]
+ [TestCase("/foo/", "/foo")]
+ [TestCase("/foo", "/foo")]
+ [TestCase("foo/", "foo")]
+ [TestCase("foo//", "foo")]
+ [TestCase("foo/\\", "foo")]
+ [TestCase("foo\\/", "foo")]
+ [TestCase("C:\\foo", "C:\\foo")]
+ [TestCase("C:", "C:")]
+ [TestCase("C:\\", "C:\\")]
+ [TestCase("C:\\\\", "C:\\")]
+ public void DirectorySlashTrimmingCases(string given, string expect)
+ {
+ if (Path.DirectorySeparatorChar == '/')
+ expect = expect.Replace('\\', '/');
+ _task.OutputDir = given;
+ ExecuteExpectSuccess();
+ Assert.That(_task.LastResponseFile,
+ Does.Contain("--csharp_out=" + expect));
+ }
+ };
}
diff --git a/src/csharp/Grpc.Tools.Tests/ProtoCompileCommandLinePrinterTest.cs b/src/csharp/Grpc.Tools.Tests/ProtoCompileCommandLinePrinterTest.cs
index a0406371dc..1773dcb875 100644
--- a/src/csharp/Grpc.Tools.Tests/ProtoCompileCommandLinePrinterTest.cs
+++ b/src/csharp/Grpc.Tools.Tests/ProtoCompileCommandLinePrinterTest.cs
@@ -20,28 +20,32 @@ using Microsoft.Build.Framework;
using Moq;
using NUnit.Framework;
-namespace Grpc.Tools.Tests {
- public class ProtoCompileCommandLinePrinterTest : ProtoCompileBasicTest {
- [SetUp]
- public new void SetUp() {
- _task.Generator = "csharp";
- _task.OutputDir = "outdir";
- _task.ProtoBuf = Utils.MakeSimpleItems("a.proto");
+namespace Grpc.Tools.Tests
+{
+ public class ProtoCompileCommandLinePrinterTest : ProtoCompileBasicTest
+ {
+ [SetUp]
+ public new void SetUp()
+ {
+ _task.Generator = "csharp";
+ _task.OutputDir = "outdir";
+ _task.ProtoBuf = Utils.MakeSimpleItems("a.proto");
- _mockEngine
- .Setup(me => me.LogMessageEvent(It.IsAny<BuildMessageEventArgs>()))
- .Callback((BuildMessageEventArgs e) =>
- Assert.Fail($"Error logged by build engine:\n{e.Message}"))
- .Verifiable("Command line was not output by the task.");
- }
+ _mockEngine
+ .Setup(me => me.LogMessageEvent(It.IsAny<BuildMessageEventArgs>()))
+ .Callback((BuildMessageEventArgs e) =>
+ Assert.Fail($"Error logged by build engine:\n{e.Message}"))
+ .Verifiable("Command line was not output by the task.");
+ }
- void ExecuteExpectSuccess() {
- _mockEngine
- .Setup(me => me.LogErrorEvent(It.IsAny<BuildErrorEventArgs>()))
- .Callback((BuildErrorEventArgs e) =>
- Assert.Fail($"Error logged by build engine:\n{e.Message}"));
- bool result = _task.Execute();
- Assert.IsTrue(result);
- }
- };
+ void ExecuteExpectSuccess()
+ {
+ _mockEngine
+ .Setup(me => me.LogErrorEvent(It.IsAny<BuildErrorEventArgs>()))
+ .Callback((BuildErrorEventArgs e) =>
+ Assert.Fail($"Error logged by build engine:\n{e.Message}"));
+ bool result = _task.Execute();
+ Assert.IsTrue(result);
+ }
+ };
}
diff --git a/src/csharp/Grpc.Tools.Tests/ProtoToolsPlatformTaskTest.cs b/src/csharp/Grpc.Tools.Tests/ProtoToolsPlatformTaskTest.cs
index 2380ae8a37..54723b74fc 100644
--- a/src/csharp/Grpc.Tools.Tests/ProtoToolsPlatformTaskTest.cs
+++ b/src/csharp/Grpc.Tools.Tests/ProtoToolsPlatformTaskTest.cs
@@ -21,102 +21,119 @@ using Microsoft.Build.Framework;
using Moq;
using NUnit.Framework;
-namespace Grpc.Tools.Tests {
- public class ProtoToolsPlatformTaskTest {
- ProtoToolsPlatform _task;
- int _cpuMatched, _osMatched;
-
- [OneTimeSetUp]
- public void SetUp() {
- var mockEng = new Mock<IBuildEngine>();
- _task = new ProtoToolsPlatform() {
- BuildEngine = mockEng.Object
- };
- _task.Execute();
- }
-
- [OneTimeTearDown]
- public void TearDown() {
- Assert.AreEqual(1, _cpuMatched, "CPU type detection failed");
- Assert.AreEqual(1, _osMatched, "OS detection failed");
- }
+namespace Grpc.Tools.Tests
+{
+ public class ProtoToolsPlatformTaskTest
+ {
+ ProtoToolsPlatform _task;
+ int _cpuMatched, _osMatched;
+
+ [OneTimeSetUp]
+ public void SetUp()
+ {
+ var mockEng = new Mock<IBuildEngine>();
+ _task = new ProtoToolsPlatform() { BuildEngine = mockEng.Object };
+ _task.Execute();
+ }
+
+ [OneTimeTearDown]
+ public void TearDown()
+ {
+ Assert.AreEqual(1, _cpuMatched, "CPU type detection failed");
+ Assert.AreEqual(1, _osMatched, "OS detection failed");
+ }
#if NETCORE
- // PlatformAttribute not yet available in NUnit, coming soon:
- // https://github.com/nunit/nunit/pull/3003.
- // Use same test case names as under the full framework.
- [Test]
- public void CpuIsX86() {
- if (RuntimeInformation.OSArchitecture == Architecture.X86) {
- _cpuMatched++;
- Assert.AreEqual("x86", _task.Cpu);
- }
- }
-
- [Test]
- public void CpuIsX64() {
- if (RuntimeInformation.OSArchitecture == Architecture.X64) {
- _cpuMatched++;
- Assert.AreEqual("x64", _task.Cpu);
- }
- }
-
- [Test]
- public void OsIsWindows() {
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {
- _osMatched++;
- Assert.AreEqual("windows", _task.Os);
- }
- }
-
- [Test]
- public void OsIsLinux() {
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) {
- _osMatched++;
- Assert.AreEqual("linux", _task.Os);
- }
- }
-
- [Test]
- public void OsIsMacOsX() {
- if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) {
- _osMatched++;
- Assert.AreEqual("macosx", _task.Os);
- }
- }
+ // PlatformAttribute not yet available in NUnit, coming soon:
+ // https://github.com/nunit/nunit/pull/3003.
+ // Use same test case names as under the full framework.
+ [Test]
+ public void CpuIsX86()
+ {
+ if (RuntimeInformation.OSArchitecture == Architecture.X86)
+ {
+ _cpuMatched++;
+ Assert.AreEqual("x86", _task.Cpu);
+ }
+ }
+
+ [Test]
+ public void CpuIsX64()
+ {
+ if (RuntimeInformation.OSArchitecture == Architecture.X64)
+ {
+ _cpuMatched++;
+ Assert.AreEqual("x64", _task.Cpu);
+ }
+ }
+
+ [Test]
+ public void OsIsWindows()
+ {
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ _osMatched++;
+ Assert.AreEqual("windows", _task.Os);
+ }
+ }
+
+ [Test]
+ public void OsIsLinux()
+ {
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+ {
+ _osMatched++;
+ Assert.AreEqual("linux", _task.Os);
+ }
+ }
+
+ [Test]
+ public void OsIsMacOsX()
+ {
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
+ {
+ _osMatched++;
+ Assert.AreEqual("macosx", _task.Os);
+ }
+ }
#else // !NETCORE, i.e. full framework.
- [Test, Platform("32-Bit")]
- public void CpuIsX86() {
- _cpuMatched++;
- Assert.AreEqual("x86", _task.Cpu);
- }
-
- [Test, Platform("64-Bit")]
- public void CpuIsX64() {
- _cpuMatched++;
- Assert.AreEqual("x64", _task.Cpu);
- }
-
- [Test, Platform("Win")]
- public void OsIsWindows() {
- _osMatched++;
- Assert.AreEqual("windows", _task.Os);
- }
-
- [Test, Platform("Linux")]
- public void OsIsLinux() {
- _osMatched++;
- Assert.AreEqual("linux", _task.Os);
- }
-
- [Test, Platform("MacOSX")]
- public void OsIsMacOsX() {
- _osMatched++;
- Assert.AreEqual("macosx", _task.Os);
- }
+ [Test, Platform("32-Bit")]
+ public void CpuIsX86()
+ {
+ _cpuMatched++;
+ Assert.AreEqual("x86", _task.Cpu);
+ }
+
+ [Test, Platform("64-Bit")]
+ public void CpuIsX64()
+ {
+ _cpuMatched++;
+ Assert.AreEqual("x64", _task.Cpu);
+ }
+
+ [Test, Platform("Win")]
+ public void OsIsWindows()
+ {
+ _osMatched++;
+ Assert.AreEqual("windows", _task.Os);
+ }
+
+ [Test, Platform("Linux")]
+ public void OsIsLinux()
+ {
+ _osMatched++;
+ Assert.AreEqual("linux", _task.Os);
+ }
+
+ [Test, Platform("MacOSX")]
+ public void OsIsMacOsX()
+ {
+ _osMatched++;
+ Assert.AreEqual("macosx", _task.Os);
+ }
#endif // NETCORE
- };
+ };
}
diff --git a/src/csharp/Grpc.Tools.Tests/Utils.cs b/src/csharp/Grpc.Tools.Tests/Utils.cs
index bb051a4873..6e0f1cffd5 100644
--- a/src/csharp/Grpc.Tools.Tests/Utils.cs
+++ b/src/csharp/Grpc.Tools.Tests/Utils.cs
@@ -20,21 +20,27 @@ using System.Linq;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
-namespace Grpc.Tools.Tests {
- static class Utils {
- // Build an item with a name from args[0] and metadata key-value pairs
- // from the rest of args, interleaved.
- // This does not do any checking, and expects an odd number of args.
- public static ITaskItem MakeItem(params string[] args) {
- var item = new TaskItem(args[0]);
- for (int i = 1; i < args.Length; i += 2)
- item.SetMetadata(args[i], args[i + 1]);
- return item;
- }
+namespace Grpc.Tools.Tests
+{
+ static class Utils
+ {
+ // Build an item with a name from args[0] and metadata key-value pairs
+ // from the rest of args, interleaved.
+ // This does not do any checking, and expects an odd number of args.
+ public static ITaskItem MakeItem(params string[] args)
+ {
+ var item = new TaskItem(args[0]);
+ for (int i = 1; i < args.Length; i += 2)
+ {
+ item.SetMetadata(args[i], args[i + 1]);
+ }
+ return item;
+ }
- // Return an array of items from given itemspecs.
- public static ITaskItem[] MakeSimpleItems(params string[] specs) {
- return specs.Select(s => new TaskItem(s)).ToArray();
- }
- };
+ // Return an array of items from given itemspecs.
+ public static ITaskItem[] MakeSimpleItems(params string[] specs)
+ {
+ return specs.Select(s => new TaskItem(s)).ToArray();
+ }
+ };
}
diff --git a/src/csharp/Grpc.Tools/Common.cs b/src/csharp/Grpc.Tools/Common.cs
index 9f8600bad0..e6acdd6393 100644
--- a/src/csharp/Grpc.Tools/Common.cs
+++ b/src/csharp/Grpc.Tools/Common.cs
@@ -24,82 +24,91 @@ using System.Security;
[assembly: InternalsVisibleTo("Grpc.Tools.Tests")]
-namespace Grpc.Tools {
- // Metadata names (MSBuild item attributes) that we refer to often.
- static class Metadata {
- // On output dependency lists.
- public static string Source = "Source";
- // On ProtoBuf items.
- public static string ProtoRoot = "ProtoRoot";
- public static string OutputDir = "OutputDir";
- public static string GrpcServices = "GrpcServices";
- public static string GrpcOutputDir = "GrpcOutputDir";
- };
+namespace Grpc.Tools
+{
+ // Metadata names (MSBuild item attributes) that we refer to often.
+ static class Metadata
+ {
+ // On output dependency lists.
+ public static string Source = "Source";
+ // On ProtoBuf items.
+ public static string ProtoRoot = "ProtoRoot";
+ public static string OutputDir = "OutputDir";
+ public static string GrpcServices = "GrpcServices";
+ public static string GrpcOutputDir = "GrpcOutputDir";
+ };
- // A few flags used to control the behavior under various platforms.
- internal static class Platform {
- public enum OsKind { Unknown, Windows, Linux, MacOsX };
- public static readonly OsKind Os;
+ // A few flags used to control the behavior under various platforms.
+ internal static class Platform
+ {
+ public enum OsKind { Unknown, Windows, Linux, MacOsX };
+ public static readonly OsKind Os;
- public enum CpuKind { Unknown, X86, X64 };
- public static readonly CpuKind Cpu;
+ public enum CpuKind { Unknown, X86, X64 };
+ public static readonly CpuKind Cpu;
- // This is not necessarily true, but good enough. BCL lacks a per-FS
- // API to determine file case sensitivity.
- public static bool IsFsCaseInsensitive => Os == OsKind.Windows;
- public static bool IsWindows => Os == OsKind.Windows;
+ // This is not necessarily true, but good enough. BCL lacks a per-FS
+ // API to determine file case sensitivity.
+ public static bool IsFsCaseInsensitive => Os == OsKind.Windows;
+ public static bool IsWindows => Os == OsKind.Windows;
- static Platform() {
+ static Platform()
+ {
#if NETCORE
- Os = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? OsKind.Windows
- : RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? OsKind.Linux
- : RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? OsKind.MacOsX
- : OsKind.Unknown;
+ Os = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? OsKind.Windows
+ : RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? OsKind.Linux
+ : RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? OsKind.MacOsX
+ : OsKind.Unknown;
- switch (RuntimeInformation.OSArchitecture) {
- case Architecture.X86: Cpu = CpuKind.X86; break;
- case Architecture.X64: Cpu = CpuKind.X64; break;
- // We do not have build tools for other architectures.
- default: Cpu = CpuKind.Unknown; break;
- }
+ switch (RuntimeInformation.OSArchitecture)
+ {
+ case Architecture.X86: Cpu = CpuKind.X86; break;
+ case Architecture.X64: Cpu = CpuKind.X64; break;
+ // We do not have build tools for other architectures.
+ default: Cpu = CpuKind.Unknown; break;
+ }
#else
- // Running under either Mono or full MS framework.
- Os = OsKind.Windows;
- if (Type.GetType("Mono.Runtime", throwOnError: false) != null) {
- // Congratulations. We are running under Mono.
- var plat = Environment.OSVersion.Platform;
- if (plat == PlatformID.MacOSX) {
- Os = OsKind.MacOsX;
- } else if (plat == PlatformID.Unix || (int)plat == 128) {
- // TODO(kkm): This is how Mono detects OSX internally. Looks cheesy
- // to me. Would not testing for /proc absence be more reliable? OSX
- // did never have it, AFAIK.
- Os = File.Exists("/usr/lib/libc.dylib") ? OsKind.MacOsX : OsKind.Linux;
- }
- }
+ // Running under either Mono or full MS framework.
+ Os = OsKind.Windows;
+ if (Type.GetType("Mono.Runtime", throwOnError: false) != null)
+ {
+ // Congratulations. We are running under Mono.
+ var plat = Environment.OSVersion.Platform;
+ if (plat == PlatformID.MacOSX)
+ {
+ Os = OsKind.MacOsX;
+ }
+ else if (plat == PlatformID.Unix || (int)plat == 128)
+ {
+ // This is how Mono detects OSX internally.
+ Os = File.Exists("/usr/lib/libc.dylib") ? OsKind.MacOsX : OsKind.Linux;
+ }
+ }
- // Hope we are not building on ARM under Xamarin!
- Cpu = Environment.Is64BitOperatingSystem ? CpuKind.X64 : CpuKind.X86;
+ // Hope we are not building on ARM under Xamarin!
+ Cpu = Environment.Is64BitOperatingSystem ? CpuKind.X64 : CpuKind.X86;
#endif
- }
- };
+ }
+ };
- // Exception handling helpers.
- static class Exceptions {
- // Returns true iff the exception indicates an error from an I/O call. See
- // https://github.com/Microsoft/msbuild/blob/v15.4.8.50001/src/Shared/ExceptionHandling.cs#L101
- static public bool IsIoRelated(Exception ex) =>
- ex is IOException ||
- (ex is ArgumentException && !(ex is ArgumentNullException)) ||
- ex is SecurityException ||
- ex is UnauthorizedAccessException ||
- ex is NotSupportedException;
- };
+ // Exception handling helpers.
+ static class Exceptions
+ {
+ // Returns true iff the exception indicates an error from an I/O call. See
+ // https://github.com/Microsoft/msbuild/blob/v15.4.8.50001/src/Shared/ExceptionHandling.cs#L101
+ static public bool IsIoRelated(Exception ex) =>
+ ex is IOException ||
+ (ex is ArgumentException && !(ex is ArgumentNullException)) ||
+ ex is SecurityException ||
+ ex is UnauthorizedAccessException ||
+ ex is NotSupportedException;
+ };
- // String helpers.
- static class Strings {
- // Compare string to argument using OrdinalIgnoreCase comparison.
- public static bool EqualNoCase(this string a, string b) =>
- string.Equals(a, b, StringComparison.OrdinalIgnoreCase);
- }
+ // String helpers.
+ static class Strings
+ {
+ // Compare string to argument using OrdinalIgnoreCase comparison.
+ public static bool EqualNoCase(this string a, string b) =>
+ string.Equals(a, b, StringComparison.OrdinalIgnoreCase);
+ }
}
diff --git a/src/csharp/Grpc.Tools/DepFileUtil.cs b/src/csharp/Grpc.Tools/DepFileUtil.cs
index e635ad1e85..440d3d535c 100644
--- a/src/csharp/Grpc.Tools/DepFileUtil.cs
+++ b/src/csharp/Grpc.Tools/DepFileUtil.cs
@@ -23,219 +23,251 @@ using System.Text;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
-namespace Grpc.Tools {
- internal static class DepFileUtil {
- /*
- Sample dependency files. Notable features we have to deal with:
- * Slash doubling, must normalize them.
- * Spaces in file names. Cannot just "unwrap" the line on backslash at eof;
- rather, treat every line as containing one file name except for one with
- the ':' separator, as containing exactly two.
- * Deal with ':' also being drive letter separator (second example).
-
- obj\Release\net45\/Foo.cs \
- obj\Release\net45\/FooGrpc.cs: C:/foo/include/google/protobuf/wrappers.proto\
- C:/projects/foo/src//foo.proto
-
- C:\projects\foo\src\./foo.grpc.pb.cc \
- C:\projects\foo\src\./foo.grpc.pb.h \
- C:\projects\foo\src\./foo.pb.cc \
- C:\projects\foo\src\./foo.pb.h: C:/foo/include/google/protobuf/wrappers.proto\
- C:/foo/include/google/protobuf/any.proto\
- C:/foo/include/google/protobuf/source_context.proto\
- C:/foo/include/google/protobuf/type.proto\
- foo.proto
- */
-
- /// <summary>
- /// Read file names from the dependency file to the right of ':'
- /// </summary>
- /// <param name="protoDepDir">Relative path to the dependency cache, e. g. "out"</param>
- /// <param name="proto">Relative path to the proto item, e. g. "foo/file.proto"</param>
- /// <param name="log">A <see cref="TaskLoggingHelper"/> for logging</param>
- /// <returns>
- /// Array of the proto file <b>input</b> dependencies as written by protoc, or empty
- /// array if the dependency file does not exist or cannot be parsed.
- /// </returns>
- public static string[] ReadDependencyInputs(string protoDepDir, string proto,
- TaskLoggingHelper log) {
- string depFilename = GetDepFilenameForProto(protoDepDir, proto);
- string[] lines = ReadDepFileLines(depFilename, false, log);
- if (lines.Length == 0) {
- return lines;
- }
-
- var result = new List<string>();
- bool skip = true;
- foreach (string line in lines) {
- // Start at the only line separating dependency outputs from inputs.
- int ix = skip ? FindLineSeparator(line) : -1;
- skip = skip && ix < 0;
- if (skip) continue;
- string file = ExtractFilenameFromLine(line, ix + 1, line.Length);
- if (file == "") {
- log.LogMessage(MessageImportance.Low,
- $"Skipping unparsable dependency file {depFilename}.\nLine with error: '{line}'");
- return new string[0];
+namespace Grpc.Tools
+{
+ internal static class DepFileUtil
+ {
+ /*
+ Sample dependency files. Notable features we have to deal with:
+ * Slash doubling, must normalize them.
+ * Spaces in file names. Cannot just "unwrap" the line on backslash at eof;
+ rather, treat every line as containing one file name except for one with
+ the ':' separator, as containing exactly two.
+ * Deal with ':' also being drive letter separator (second example).
+
+ obj\Release\net45\/Foo.cs \
+ obj\Release\net45\/FooGrpc.cs: C:/foo/include/google/protobuf/wrappers.proto\
+ C:/projects/foo/src//foo.proto
+
+ C:\projects\foo\src\./foo.grpc.pb.cc \
+ C:\projects\foo\src\./foo.grpc.pb.h \
+ C:\projects\foo\src\./foo.pb.cc \
+ C:\projects\foo\src\./foo.pb.h: C:/foo/include/google/protobuf/wrappers.proto\
+ C:/foo/include/google/protobuf/any.proto\
+ C:/foo/include/google/protobuf/source_context.proto\
+ C:/foo/include/google/protobuf/type.proto\
+ foo.proto
+ */
+
+ /// <summary>
+ /// Read file names from the dependency file to the right of ':'
+ /// </summary>
+ /// <param name="protoDepDir">Relative path to the dependency cache, e. g. "out"</param>
+ /// <param name="proto">Relative path to the proto item, e. g. "foo/file.proto"</param>
+ /// <param name="log">A <see cref="TaskLoggingHelper"/> for logging</param>
+ /// <returns>
+ /// Array of the proto file <b>input</b> dependencies as written by protoc, or empty
+ /// array if the dependency file does not exist or cannot be parsed.
+ /// </returns>
+ public static string[] ReadDependencyInputs(string protoDepDir, string proto,
+ TaskLoggingHelper log)
+ {
+ string depFilename = GetDepFilenameForProto(protoDepDir, proto);
+ string[] lines = ReadDepFileLines(depFilename, false, log);
+ if (lines.Length == 0)
+ {
+ return lines;
+ }
+
+ var result = new List<string>();
+ bool skip = true;
+ foreach (string line in lines)
+ {
+ // Start at the only line separating dependency outputs from inputs.
+ int ix = skip ? FindLineSeparator(line) : -1;
+ skip = skip && ix < 0;
+ if (skip) { continue; }
+ string file = ExtractFilenameFromLine(line, ix + 1, line.Length);
+ if (file == "")
+ {
+ log.LogMessage(MessageImportance.Low,
+ $"Skipping unparsable dependency file {depFilename}.\nLine with error: '{line}'");
+ return new string[0];
+ }
+
+ // Do not bend over backwards trying not to include a proto into its
+ // own list of dependencies. Since a file is not older than self,
+ // it is safe to add; this is purely a memory optimization.
+ if (file != proto)
+ {
+ result.Add(file);
+ }
+ }
+ return result.ToArray();
}
- // Do not bend over backwards trying not to include a proto into its
- // own list of dependencies. Since a file is not older than self,
- // it is safe to add; this is purely a memory optimization.
- if (file != proto) {
- result.Add(file);
+ /// <summary>
+ /// Read file names from the dependency file to the left of ':'
+ /// </summary>
+ /// <param name="depFilename">Path to dependency file written by protoc</param>
+ /// <param name="log">A <see cref="TaskLoggingHelper"/> for logging</param>
+ /// <returns>
+ /// Array of the protoc-generated outputs from the given dependency file
+ /// written by protoc, or empty array if the file does not exist or cannot
+ /// be parsed.
+ /// </returns>
+ /// <remarks>
+ /// Since this is called after a protoc invocation, an unparsable or missing
+ /// file causes an error-level message to be logged.
+ /// </remarks>
+ public static string[] ReadDependencyOutputs(string depFilename,
+ TaskLoggingHelper log)
+ {
+ string[] lines = ReadDepFileLines(depFilename, true, log);
+ if (lines.Length == 0)
+ {
+ return lines;
+ }
+
+ var result = new List<string>();
+ foreach (string line in lines)
+ {
+ int ix = FindLineSeparator(line);
+ string file = ExtractFilenameFromLine(line, 0, ix >= 0 ? ix : line.Length);
+ if (file == "")
+ {
+ log.LogError("Unable to parse generated dependency file {0}.\n" +
+ "Line with error: '{1}'", depFilename, line);
+ return new string[0];
+ }
+ result.Add(file);
+
+ // If this is the line with the separator, do not read further.
+ if (ix >= 0) { break; }
+ }
+ return result.ToArray();
}
- }
- return result.ToArray();
- }
-
- /// <summary>
- /// Read file names from the dependency file to the left of ':'
- /// </summary>
- /// <param name="depFilename">Path to dependency file written by protoc</param>
- /// <param name="log">A <see cref="TaskLoggingHelper"/> for logging</param>
- /// <returns>
- /// Array of the protoc-generated outputs from the given dependency file
- /// written by protoc, or empty array if the file does not exist or cannot
- /// be parsed.
- /// </returns>
- /// <remarks>
- /// Since this is called after a protoc invocation, an unparsable or missing
- /// file causes an error-level message to be logged.
- /// </remarks>
- public static string[] ReadDependencyOutputs(string depFilename,
- TaskLoggingHelper log) {
- string[] lines = ReadDepFileLines(depFilename, true, log);
- if (lines.Length == 0) {
- return lines;
- }
-
- var result = new List<string>();
- foreach (string line in lines) {
- int ix = FindLineSeparator(line);
- string file = ExtractFilenameFromLine(line, 0, ix >= 0 ? ix : line.Length);
- if (file == "") {
- log.LogError("Unable to parse generated dependency file {0}.\n" +
- "Line with error: '{1}'", depFilename, line);
- return new string[0];
+
+ /// <summary>
+ /// Construct relative dependency file name from directory hash and file name
+ /// </summary>
+ /// <param name="protoDepDir">Relative path to the dependency cache, e. g. "out"</param>
+ /// <param name="proto">Relative path to the proto item, e. g. "foo/file.proto"</param>
+ /// <returns>
+ /// Full relative path to the dependency file, e. g.
+ /// "out/deadbeef12345678_file.protodep"
+ /// </returns>
+ /// <remarks>
+ /// Since a project may contain proto files with the same filename but in different
+ /// directories, a unique filename for the dependency file is constructed based on the
+ /// proto file name both name and directory. The directory path can be arbitrary,
+ /// for example, it can be outside of the project, or an absolute path including
+ /// a drive letter, or a UNC network path. A name constructed from such a path by,
+ /// for example, replacing disallowed name characters with an underscore, may well
+ /// be over filesystem's allowed path length, since it will be located under the
+ /// project and solution directories, which are also some level deep from the root.
+ /// Instead of creating long and unwieldy names for these proto sources, we cache
+ /// the full path of the name without the filename, and append the filename to it,
+ /// as in e. g. "foo/file.proto" will yield the name "deadbeef12345678_file", where
+ /// "deadbeef12345678" is a presumed hash value of the string "foo/". This allows
+ /// the file names be short, unique (up to a hash collision), and still allowing
+ /// the user to guess their provenance.
+ /// </remarks>
+ public static string GetDepFilenameForProto(string protoDepDir, string proto)
+ {
+ string dirname = Path.GetDirectoryName(proto);
+ if (Platform.IsFsCaseInsensitive)
+ {
+ dirname = dirname.ToLowerInvariant();
+ }
+ string dirhash = HashString64Hex(dirname);
+ string filename = Path.GetFileNameWithoutExtension(proto);
+ return Path.Combine(protoDepDir, $"{dirhash}_{filename}.protodep");
}
- result.Add(file);
-
- // If this is the line with the separator, do not read further.
- if (ix >= 0)
- break;
- }
- return result.ToArray();
- }
-
- /// <summary>
- /// Construct relative dependency file name from directory hash and file name
- /// </summary>
- /// <param name="protoDepDir">Relative path to the dependency cache, e. g. "out"</param>
- /// <param name="proto">Relative path to the proto item, e. g. "foo/file.proto"</param>
- /// <returns>
- /// Full relative path to the dependency file, e. g.
- /// "out/deadbeef12345678_file.protodep"
- /// </returns>
- /// <remarks>
- /// Since a project may contain proto files with the same filename but in different
- /// directories, a unique filename for the dependency file is constructed based on the
- /// proto file name both name and directory. The directory path can be arbitrary,
- /// for example, it can be outside of the project, or an absolute path including
- /// a drive letter, or a UNC network path. A name constructed from such a path by,
- /// for example, replacing disallowed name characters with an underscore, may well
- /// be over filesystem's allowed path length, since it will be located under the
- /// project and solution directories, which are also some level deep from the root.
- /// Instead of creating long and unwieldy names for these proto sources, we cache
- /// the full path of the name without the filename, and append the filename to it,
- /// as in e. g. "foo/file.proto" will yield the name "deadbeef12345678_file", where
- /// "deadbeef12345678" is a presumed hash value of the string "foo/". This allows
- /// the file names be short, unique (up to a hash collision), and still allowing
- /// the user to guess their provenance.
- /// </remarks>
- public static string GetDepFilenameForProto(string protoDepDir, string proto) {
- string dirname = Path.GetDirectoryName(proto);
- if (Platform.IsFsCaseInsensitive) {
- dirname = dirname.ToLowerInvariant();
- }
- string dirhash = HashString64Hex(dirname);
- string filename = Path.GetFileNameWithoutExtension(proto);
- return Path.Combine(protoDepDir, $"{dirhash}_{filename}.protodep");
- }
-
- // Get a 64-bit hash for a directory string. We treat it as if it were
- // unique, since there are not so many distinct proto paths in a project.
- // We take the first 64 bit of the string SHA1.
- // Internal for tests access only.
- internal static string HashString64Hex(string str) {
- using (var sha1 = System.Security.Cryptography.SHA1.Create()) {
- byte[] hash = sha1.ComputeHash(Encoding.UTF8.GetBytes(str));
- var hashstr = new StringBuilder(16);
- for (int i = 0; i < 8; i++) {
- hashstr.Append(hash[i].ToString("x2"));
+
+ // Get a 64-bit hash for a directory string. We treat it as if it were
+ // unique, since there are not so many distinct proto paths in a project.
+ // We take the first 64 bit of the string SHA1.
+ // Internal for tests access only.
+ internal static string HashString64Hex(string str)
+ {
+ using (var sha1 = System.Security.Cryptography.SHA1.Create())
+ {
+ byte[] hash = sha1.ComputeHash(Encoding.UTF8.GetBytes(str));
+ var hashstr = new StringBuilder(16);
+ for (int i = 0; i < 8; i++)
+ {
+ hashstr.Append(hash[i].ToString("x2"));
+ }
+ return hashstr.ToString();
+ }
+ }
+
+ // Extract filename between 'beg' (inclusive) and 'end' (exclusive) from
+ // line 'line', skipping over trailing and leading whitespace, and, when
+ // 'end' is immediately past end of line 'line', also final '\' (used
+ // as a line continuation token in the dep file).
+ // Returns an empty string if the filename cannot be extracted.
+ static string ExtractFilenameFromLine(string line, int beg, int end)
+ {
+ while (beg < end && char.IsWhiteSpace(line[beg])) beg++;
+ if (beg < end && end == line.Length && line[end - 1] == '\\') end--;
+ while (beg < end && char.IsWhiteSpace(line[end - 1])) end--;
+ if (beg == end) return "";
+
+ string filename = line.Substring(beg, end - beg);
+ try
+ {
+ // Normalize file name.
+ return Path.Combine(Path.GetDirectoryName(filename), Path.GetFileName(filename));
+ }
+ catch (Exception ex) when (Exceptions.IsIoRelated(ex))
+ {
+ return "";
+ }
}
- return hashstr.ToString();
- }
- }
-
- // Extract filename between 'beg' (inclusive) and 'end' (exclusive) from
- // line 'line', skipping over trailing and leading whitespace, and, when
- // 'end' is immediately past end of line 'line', also final '\' (used
- // as a line continuation token in the dep file).
- // Returns an empty string if the filename cannot be extracted.
- static string ExtractFilenameFromLine(string line, int beg, int end) {
- while (beg < end && char.IsWhiteSpace(line[beg])) beg++;
- if (beg < end && end == line.Length && line[end - 1] == '\\') end--;
- while (beg < end && char.IsWhiteSpace(line[end - 1])) end--;
- if (beg == end) return "";
-
- string filename = line.Substring(beg, end - beg);
- try {
- // Normalize file name.
- return Path.Combine(
- Path.GetDirectoryName(filename),
- Path.GetFileName(filename));
- } catch (Exception ex) when (Exceptions.IsIoRelated(ex)) {
- return "";
- }
- }
-
- // Finds the index of the ':' separating dependency clauses in the line,
- // not taking Windows drive spec into account. Returns the index of the
- // separating ':', or -1 if no separator found.
- static int FindLineSeparator(string line) {
- // Mind this case where the first ':' is not separator:
- // C:\foo\bar\.pb.h: C:/protobuf/wrappers.proto\
- int ix = line.IndexOf(':');
- if (ix <= 0 || ix == line.Length - 1
- || (line[ix + 1] != '/' && line[ix + 1] != '\\')
- || !char.IsLetter(line[ix - 1]))
- return ix; // Not a windows drive: no letter before ':', or no '\' after.
- for (int j = ix - 1; --j >= 0;) {
- if (!char.IsWhiteSpace(line[j]))
- return ix; // Not space or BOL only before "X:/".
- }
- return line.IndexOf(':', ix + 1);
- }
-
- // Read entire dependency file. The 'required' parameter controls error
- // logging behavior in case the file not found. We require this file when
- // compiling, but reading it is optional when computing dependencies.
- static string[] ReadDepFileLines(string filename, bool required,
- TaskLoggingHelper log) {
- try {
- var result = File.ReadAllLines(filename);
- if (!required)
- log.LogMessage(MessageImportance.Low, $"Using dependency file {filename}");
- return result;
- } catch (Exception ex) when (Exceptions.IsIoRelated(ex)) {
- if (required) {
- log.LogError($"Unable to load {filename}: {ex.GetType().Name}: {ex.Message}");
- } else {
- log.LogMessage(MessageImportance.Low, $"Skipping {filename}: {ex.Message}");
+
+ // Finds the index of the ':' separating dependency clauses in the line,
+ // not taking Windows drive spec into account. Returns the index of the
+ // separating ':', or -1 if no separator found.
+ static int FindLineSeparator(string line)
+ {
+ // Mind this case where the first ':' is not separator:
+ // C:\foo\bar\.pb.h: C:/protobuf/wrappers.proto\
+ int ix = line.IndexOf(':');
+ if (ix <= 0 || ix == line.Length - 1
+ || (line[ix + 1] != '/' && line[ix + 1] != '\\')
+ || !char.IsLetter(line[ix - 1]))
+ {
+ return ix; // Not a windows drive: no letter before ':', or no '\' after.
+ }
+ for (int j = ix - 1; --j >= 0;)
+ {
+ if (!char.IsWhiteSpace(line[j]))
+ {
+ return ix; // Not space or BOL only before "X:/".
+ }
+ }
+ return line.IndexOf(':', ix + 1);
+ }
+
+ // Read entire dependency file. The 'required' parameter controls error
+ // logging behavior in case the file not found. We require this file when
+ // compiling, but reading it is optional when computing dependencies.
+ static string[] ReadDepFileLines(string filename, bool required,
+ TaskLoggingHelper log)
+ {
+ try
+ {
+ var result = File.ReadAllLines(filename);
+ if (!required)
+ {
+ log.LogMessage(MessageImportance.Low, $"Using dependency file {filename}");
+ }
+ return result;
+ }
+ catch (Exception ex) when (Exceptions.IsIoRelated(ex))
+ {
+ if (required)
+ {
+ log.LogError($"Unable to load {filename}: {ex.GetType().Name}: {ex.Message}");
+ }
+ else
+ {
+ log.LogMessage(MessageImportance.Low, $"Skipping {filename}: {ex.Message}");
+ }
+ return new string[0];
+ }
}
- return new string[0];
- }
- }
- };
+ };
}
diff --git a/src/csharp/Grpc.Tools/GeneratorServices.cs b/src/csharp/Grpc.Tools/GeneratorServices.cs
index 52bd29a678..536ec43c83 100644
--- a/src/csharp/Grpc.Tools/GeneratorServices.cs
+++ b/src/csharp/Grpc.Tools/GeneratorServices.cs
@@ -22,147 +22,173 @@ using System.Text;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
-namespace Grpc.Tools {
- // Abstract class for language-specific analysis behavior, such
- // as guessing the generated files the same way protoc does.
- internal abstract class GeneratorServices {
- protected readonly TaskLoggingHelper Log;
- protected GeneratorServices(TaskLoggingHelper log) {
- Log = log;
- }
-
- // Obtain a service for the given language (csharp, cpp).
- public static GeneratorServices GetForLanguage(string lang, TaskLoggingHelper log) {
- if (lang.EqualNoCase("csharp"))
- return new CSharpGeneratorServices(log);
- if (lang.EqualNoCase("cpp"))
- return new CppGeneratorServices(log);
- log.LogError("Invalid value '{0}' for task property 'Generator'. " +
- "Supported generator languages: CSharp, Cpp.", lang);
- return null;
- }
-
- // Guess whether item's metadata suggests gRPC stub generation.
- // When "gRPCServices" is not defined, assume gRPC is not used.
- // When defined, C# uses "none" to skip gRPC, C++ uses "false", so
- // recognize both. Since the value is tightly coupled to the scripts,
- // we do not try to validate the value; scripts take care of that.
- // It is safe to assume that gRPC is requested for any other value.
- protected bool GrpcOutputPossible(ITaskItem proto) {
- string gsm = proto.GetMetadata(Metadata.GrpcServices);
- return !gsm.EqualNoCase("") && !gsm.EqualNoCase("none")
- && !gsm.EqualNoCase("false");
- }
-
- public abstract string[] GetPossibleOutputs(ITaskItem proto);
- };
-
- // C# generator services.
- internal class CSharpGeneratorServices : GeneratorServices {
- public CSharpGeneratorServices(TaskLoggingHelper log) : base(log) {}
-
- public override string[] GetPossibleOutputs(ITaskItem protoItem) {
- bool doGrpc = GrpcOutputPossible(protoItem);
- string filename = LowerUnderscoreToUpperCamel(
- Path.GetFileNameWithoutExtension(protoItem.ItemSpec));
-
- var outputs = new string[doGrpc ? 2 : 1];
- string outdir = protoItem.GetMetadata(Metadata.OutputDir);
- string fileStem = Path.Combine(outdir, filename);
- outputs[0] = fileStem + ".cs";
- if (doGrpc) {
- // Override outdir if kGrpcOutputDir present, default to proto output.
- outdir = protoItem.GetMetadata(Metadata.GrpcOutputDir);
- if (outdir != "") {
- fileStem = Path.Combine(outdir, filename);
+namespace Grpc.Tools
+{
+ // Abstract class for language-specific analysis behavior, such
+ // as guessing the generated files the same way protoc does.
+ internal abstract class GeneratorServices
+ {
+ protected readonly TaskLoggingHelper Log;
+ protected GeneratorServices(TaskLoggingHelper log) { Log = log; }
+
+ // Obtain a service for the given language (csharp, cpp).
+ public static GeneratorServices GetForLanguage(string lang, TaskLoggingHelper log)
+ {
+ if (lang.EqualNoCase("csharp")) { return new CSharpGeneratorServices(log); }
+ if (lang.EqualNoCase("cpp")) { return new CppGeneratorServices(log); }
+
+ log.LogError("Invalid value '{0}' for task property 'Generator'. " +
+ "Supported generator languages: CSharp, Cpp.", lang);
+ return null;
}
- outputs[1] = fileStem + "Grpc.cs";
- }
- return outputs;
- }
-
- string LowerUnderscoreToUpperCamel(string str) {
- // See src/compiler/generator_helpers.h:118
- var result = new StringBuilder(str.Length, str.Length);
- bool cap = true;
- foreach (char c in str) {
- if (c == '_') {
- cap = true;
- } else if (cap) {
- result.Append(char.ToUpperInvariant(c));
- cap = false;
- } else {
- result.Append(c);
+
+ // Guess whether item's metadata suggests gRPC stub generation.
+ // When "gRPCServices" is not defined, assume gRPC is not used.
+ // When defined, C# uses "none" to skip gRPC, C++ uses "false", so
+ // recognize both. Since the value is tightly coupled to the scripts,
+ // we do not try to validate the value; scripts take care of that.
+ // It is safe to assume that gRPC is requested for any other value.
+ protected bool GrpcOutputPossible(ITaskItem proto)
+ {
+ string gsm = proto.GetMetadata(Metadata.GrpcServices);
+ return !gsm.EqualNoCase("") && !gsm.EqualNoCase("none")
+ && !gsm.EqualNoCase("false");
+ }
+
+ public abstract string[] GetPossibleOutputs(ITaskItem proto);
+ };
+
+ // C# generator services.
+ internal class CSharpGeneratorServices : GeneratorServices
+ {
+ public CSharpGeneratorServices(TaskLoggingHelper log) : base(log) { }
+
+ public override string[] GetPossibleOutputs(ITaskItem protoItem)
+ {
+ bool doGrpc = GrpcOutputPossible(protoItem);
+ string filename = LowerUnderscoreToUpperCamel(
+ Path.GetFileNameWithoutExtension(protoItem.ItemSpec));
+
+ var outputs = new string[doGrpc ? 2 : 1];
+ string outdir = protoItem.GetMetadata(Metadata.OutputDir);
+ string fileStem = Path.Combine(outdir, filename);
+ outputs[0] = fileStem + ".cs";
+ if (doGrpc)
+ {
+ // Override outdir if kGrpcOutputDir present, default to proto output.
+ outdir = protoItem.GetMetadata(Metadata.GrpcOutputDir);
+ if (outdir != "")
+ {
+ fileStem = Path.Combine(outdir, filename);
+ }
+ outputs[1] = fileStem + "Grpc.cs";
+ }
+ return outputs;
+ }
+
+ string LowerUnderscoreToUpperCamel(string str)
+ {
+ // See src/compiler/generator_helpers.h:118
+ var result = new StringBuilder(str.Length, str.Length);
+ bool cap = true;
+ foreach (char c in str)
+ {
+ if (c == '_')
+ {
+ cap = true;
+ }
+ else if (cap)
+ {
+ result.Append(char.ToUpperInvariant(c));
+ cap = false;
+ }
+ else
+ {
+ result.Append(c);
+ }
+ }
+ return result.ToString();
}
- }
- return result.ToString();
- }
- };
-
- // C++ generator services.
- internal class CppGeneratorServices : GeneratorServices {
- public CppGeneratorServices(TaskLoggingHelper log) : base(log) { }
-
- public override string[] GetPossibleOutputs(ITaskItem protoItem) {
- bool doGrpc = GrpcOutputPossible(protoItem);
- string root = protoItem.GetMetadata(Metadata.ProtoRoot);
- string proto = protoItem.ItemSpec;
- string filename = Path.GetFileNameWithoutExtension(proto);
- // E. g., ("foo/", "foo/bar/x.proto") => "bar"
- string relative = GetRelativeDir(root, proto);
-
- var outputs = new string[doGrpc ? 4 : 2];
- string outdir = protoItem.GetMetadata(Metadata.OutputDir);
- string fileStem = Path.Combine(outdir, relative, filename);
- outputs[0] = fileStem + ".pb.cc";
- outputs[1] = fileStem + ".pb.h";
- if (doGrpc) {
- // Override outdir if kGrpcOutputDir present, default to proto output.
- outdir = protoItem.GetMetadata(Metadata.GrpcOutputDir);
- if (outdir != "") {
- fileStem = Path.Combine(outdir, relative, filename);
+ };
+
+ // C++ generator services.
+ internal class CppGeneratorServices : GeneratorServices
+ {
+ public CppGeneratorServices(TaskLoggingHelper log) : base(log) { }
+
+ public override string[] GetPossibleOutputs(ITaskItem protoItem)
+ {
+ bool doGrpc = GrpcOutputPossible(protoItem);
+ string root = protoItem.GetMetadata(Metadata.ProtoRoot);
+ string proto = protoItem.ItemSpec;
+ string filename = Path.GetFileNameWithoutExtension(proto);
+ // E. g., ("foo/", "foo/bar/x.proto") => "bar"
+ string relative = GetRelativeDir(root, proto);
+
+ var outputs = new string[doGrpc ? 4 : 2];
+ string outdir = protoItem.GetMetadata(Metadata.OutputDir);
+ string fileStem = Path.Combine(outdir, relative, filename);
+ outputs[0] = fileStem + ".pb.cc";
+ outputs[1] = fileStem + ".pb.h";
+ if (doGrpc)
+ {
+ // Override outdir if kGrpcOutputDir present, default to proto output.
+ outdir = protoItem.GetMetadata(Metadata.GrpcOutputDir);
+ if (outdir != "")
+ {
+ fileStem = Path.Combine(outdir, relative, filename);
+ }
+ outputs[2] = fileStem + "_grpc.pb.cc";
+ outputs[3] = fileStem + "_grpc.pb.h";
+ }
+ return outputs;
+ }
+
+ // Calculate part of proto path relative to root. Protoc is very picky
+ // about them matching exactly, so can be we. Expect root be exact prefix
+ // to proto, minus some slash normalization.
+ string GetRelativeDir(string root, string proto)
+ {
+ string protoDir = Path.GetDirectoryName(proto);
+ string rootDir = EndWithSlash(Path.GetDirectoryName(EndWithSlash(root)));
+ if (rootDir == s_dotSlash)
+ {
+ // Special case, otherwise we can return "./" instead of "" below!
+ return protoDir;
+ }
+ if (Platform.IsFsCaseInsensitive)
+ {
+ protoDir = protoDir.ToLowerInvariant();
+ rootDir = rootDir.ToLowerInvariant();
+ }
+ protoDir = EndWithSlash(protoDir);
+ if (!protoDir.StartsWith(rootDir))
+ {
+ Log.LogWarning("ProtoBuf item '{0}' has the ProtoRoot metadata '{1}' " +
+ "which is not prefix to its path. Cannot compute relative path.",
+ proto, root);
+ return "";
+ }
+ return protoDir.Substring(rootDir.Length);
+ }
+
+ // './' or '.\', normalized per system.
+ static string s_dotSlash = "." + Path.DirectorySeparatorChar;
+
+ static string EndWithSlash(string str)
+ {
+ if (str == "")
+ {
+ return s_dotSlash;
+ }
+ else if (str[str.Length - 1] != '\\' && str[str.Length - 1] != '/')
+ {
+ return str + Path.DirectorySeparatorChar;
+ }
+ else
+ {
+ return str;
+ }
}
- outputs[2] = fileStem + "_grpc.pb.cc";
- outputs[3] = fileStem + "_grpc.pb.h";
- }
- return outputs;
- }
-
- // Calculate part of proto path relative to root. Protoc is very picky
- // about them matching exactly, so can be we. Expect root be exact prefix
- // to proto, minus some slash normalization.
- string GetRelativeDir(string root, string proto) {
- string protoDir = Path.GetDirectoryName(proto);
- string rootDir = EndWithSlash(Path.GetDirectoryName(EndWithSlash(root)));
- if (rootDir == s_dotSlash) {
- // Special case, otherwise we can return "./" instead of "" below!
- return protoDir;
- }
- if (Platform.IsFsCaseInsensitive) {
- protoDir = protoDir.ToLowerInvariant();
- rootDir = rootDir.ToLowerInvariant();
- }
- protoDir = EndWithSlash(protoDir);
- if (!protoDir.StartsWith(rootDir)) {
- Log.LogWarning("ProtoBuf item '{0}' has the ProtoRoot metadata '{1}' " +
- "which is not prefix to its path. Cannot compute relative path.",
- proto, root);
- return "";
- }
- return protoDir.Substring(rootDir.Length);
- }
-
- // './' or '.\', normalized per system.
- static string s_dotSlash = "." + Path.DirectorySeparatorChar;
-
- static string EndWithSlash(string str) {
- if (str == "") {
- return s_dotSlash;
- } else if (str[str.Length - 1] != '\\' && str[str.Length - 1] != '/') {
- return str + Path.DirectorySeparatorChar;
- } else {
- return str;
- }
- }
- };
+ };
}
diff --git a/src/csharp/Grpc.Tools/ProtoCompile.cs b/src/csharp/Grpc.Tools/ProtoCompile.cs
index e77084b1ef..93608e1ac0 100644
--- a/src/csharp/Grpc.Tools/ProtoCompile.cs
+++ b/src/csharp/Grpc.Tools/ProtoCompile.cs
@@ -20,390 +20,422 @@ using System.Text;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
-namespace Grpc.Tools {
- /// <summary>
- /// Run Google proto compiler (protoc).
- ///
- /// After a successful run, the task reads the dependency file if specified
- /// to be saved by the compiler, and returns its output files.
- ///
- /// This task (unlike PrepareProtoCompile) does not attempt to guess anything
- /// about language-specific behavior of protoc, and therefore can be used for
- /// any language outputs.
- /// </summary>
- public class ProtoCompile : ToolTask {
- /*
-
- Usage: /home/kkm/work/protobuf/src/.libs/lt-protoc [OPTION] PROTO_FILES
- Parse PROTO_FILES and generate output based on the options given:
- -IPATH, --proto_path=PATH Specify the directory in which to search for
- imports. May be specified multiple times;
- directories will be searched in order. If not
- given, the current working directory is used.
- --version Show version info and exit.
- -h, --help Show this text and exit.
- --encode=MESSAGE_TYPE Read a text-format message of the given type
- from standard input and write it in binary
- to standard output. The message type must
- be defined in PROTO_FILES or their imports.
- --decode=MESSAGE_TYPE Read a binary message of the given type from
- standard input and write it in text format
- to standard output. The message type must
- be defined in PROTO_FILES or their imports.
- --decode_raw Read an arbitrary protocol message from
- standard input and write the raw tag/value
- pairs in text format to standard output. No
- PROTO_FILES should be given when using this
- flag.
- --descriptor_set_in=FILES Specifies a delimited list of FILES
- each containing a FileDescriptorSet (a
- protocol buffer defined in descriptor.proto).
- The FileDescriptor for each of the PROTO_FILES
- provided will be loaded from these
- FileDescriptorSets. If a FileDescriptor
- appears multiple times, the first occurrence
- will be used.
- -oFILE, Writes a FileDescriptorSet (a protocol buffer,
- --descriptor_set_out=FILE defined in descriptor.proto) containing all of
- the input files to FILE.
- --include_imports When using --descriptor_set_out, also include
- all dependencies of the input files in the
- set, so that the set is self-contained.
- --include_source_info When using --descriptor_set_out, do not strip
- SourceCodeInfo from the FileDescriptorProto.
- This results in vastly larger descriptors that
- include information about the original
- location of each decl in the source file as
- well as surrounding comments.
- --dependency_out=FILE Write a dependency output file in the format
- expected by make. This writes the transitive
- set of input file paths to FILE
- --error_format=FORMAT Set the format in which to print errors.
- FORMAT may be 'gcc' (the default) or 'msvs'
- (Microsoft Visual Studio format).
- --print_free_field_numbers Print the free field numbers of the messages
- defined in the given proto files. Groups share
- the same field number space with the parent
- message. Extension ranges are counted as
- occupied fields numbers.
-
- --plugin=EXECUTABLE Specifies a plugin executable to use.
- Normally, protoc searches the PATH for
- plugins, but you may specify additional
- executables not in the path using this flag.
- Additionally, EXECUTABLE may be of the form
- NAME=PATH, in which case the given plugin name
- is mapped to the given executable even if
- the executable's own name differs.
- --cpp_out=OUT_DIR Generate C++ header and source.
- --csharp_out=OUT_DIR Generate C# source file.
- --java_out=OUT_DIR Generate Java source file.
- --javanano_out=OUT_DIR Generate Java Nano source file.
- --js_out=OUT_DIR Generate JavaScript source.
- --objc_out=OUT_DIR Generate Objective C header and source.
- --php_out=OUT_DIR Generate PHP source file.
- --python_out=OUT_DIR Generate Python source file.
- --ruby_out=OUT_DIR Generate Ruby source file.
- @<filename> Read options and filenames from file. If a
- relative file path is specified, the file
- will be searched in the working directory.
- The --proto_path option will not affect how
- this argument file is searched. Content of
- the file will be expanded in the position of
- @<filename> as in the argument list. Note
- that shell expansion is not applied to the
- content of the file (i.e., you cannot use
- quotes, wildcards, escapes, commands, etc.).
- Each line corresponds to a single argument,
- even if it contains spaces.
- */
- static string[] s_supportedGenerators = new[] {
- "cpp", "csharp", "java",
- "javanano", "js", "objc",
- "php", "python", "ruby",
- };
-
- /// <summary>
- /// Code generator.
- /// </summary>
- [Required]
- public string Generator { get; set; }
-
- /// <summary>
- /// Protobuf files to compile.
- /// </summary>
- [Required]
- public ITaskItem[] ProtoBuf { get; set; }
-
- /// <summary>
- /// Directory where protoc dependency files are cached. If provided, dependency
- /// output filename is autogenerated from source directory hash and file name.
- /// Mutually exclusive with DependencyOut.
- /// Switch: --dependency_out (with autogenerated file name).
- /// </summary>
- public string ProtoDepDir { get; set; }
-
- /// <summary>
- /// Dependency file full name. Mutually exclusive with ProtoDepDir.
- /// Autogenerated file name is available in this property after execution.
- /// Switch: --dependency_out.
- /// </summary>
- [Output]
- public string DependencyOut { get; set; }
-
- /// <summary>
- /// The directories to search for imports. Directories will be searched
- /// in order. If not given, the current working directory is used.
- /// Switch: --proto_path.
- /// </summary>
- public string[] ProtoPath { get; set; }
-
- /// <summary>
- /// Generated code directory. The generator property determines the language.
- /// Switch: --GEN-out= (for different generators GEN).
- /// </summary>
- [Required]
- public string OutputDir { get; set; }
-
- /// <summary>
- /// Codegen options. See also OptionsFromMetadata.
- /// Switch: --GEN_out= (for different generators GEN).
- /// </summary>
- public string[] OutputOptions { get; set; }
-
- /// <summary>
- /// Full path to the gRPC plugin executable. If specified, gRPC generation
- /// is enabled for the files.
- /// Switch: --plugin=protoc-gen-grpc=
- /// </summary>
- public string GrpcPluginExe { get; set; }
-
+namespace Grpc.Tools
+{
/// <summary>
- /// Generated gRPC directory. The generator property determines the
- /// language. If gRPC is enabled but this is not given, OutputDir is used.
- /// Switch: --grpc_out=
+ /// Run Google proto compiler (protoc).
+ ///
+ /// After a successful run, the task reads the dependency file if specified
+ /// to be saved by the compiler, and returns its output files.
+ ///
+ /// This task (unlike PrepareProtoCompile) does not attempt to guess anything
+ /// about language-specific behavior of protoc, and therefore can be used for
+ /// any language outputs.
/// </summary>
- public string GrpcOutputDir { get; set; }
-
- /// <summary>
- /// gRPC Codegen options. See also OptionsFromMetadata.
- /// --grpc_opt=opt1,opt2=val (comma-separated).
- /// </summary>
- public string[] GrpcOutputOptions { get; set; }
+ public class ProtoCompile : ToolTask
+ {
+ /*
+
+ Usage: /home/kkm/work/protobuf/src/.libs/lt-protoc [OPTION] PROTO_FILES
+ Parse PROTO_FILES and generate output based on the options given:
+ -IPATH, --proto_path=PATH Specify the directory in which to search for
+ imports. May be specified multiple times;
+ directories will be searched in order. If not
+ given, the current working directory is used.
+ --version Show version info and exit.
+ -h, --help Show this text and exit.
+ --encode=MESSAGE_TYPE Read a text-format message of the given type
+ from standard input and write it in binary
+ to standard output. The message type must
+ be defined in PROTO_FILES or their imports.
+ --decode=MESSAGE_TYPE Read a binary message of the given type from
+ standard input and write it in text format
+ to standard output. The message type must
+ be defined in PROTO_FILES or their imports.
+ --decode_raw Read an arbitrary protocol message from
+ standard input and write the raw tag/value
+ pairs in text format to standard output. No
+ PROTO_FILES should be given when using this
+ flag.
+ --descriptor_set_in=FILES Specifies a delimited list of FILES
+ each containing a FileDescriptorSet (a
+ protocol buffer defined in descriptor.proto).
+ The FileDescriptor for each of the PROTO_FILES
+ provided will be loaded from these
+ FileDescriptorSets. If a FileDescriptor
+ appears multiple times, the first occurrence
+ will be used.
+ -oFILE, Writes a FileDescriptorSet (a protocol buffer,
+ --descriptor_set_out=FILE defined in descriptor.proto) containing all of
+ the input files to FILE.
+ --include_imports When using --descriptor_set_out, also include
+ all dependencies of the input files in the
+ set, so that the set is self-contained.
+ --include_source_info When using --descriptor_set_out, do not strip
+ SourceCodeInfo from the FileDescriptorProto.
+ This results in vastly larger descriptors that
+ include information about the original
+ location of each decl in the source file as
+ well as surrounding comments.
+ --dependency_out=FILE Write a dependency output file in the format
+ expected by make. This writes the transitive
+ set of input file paths to FILE
+ --error_format=FORMAT Set the format in which to print errors.
+ FORMAT may be 'gcc' (the default) or 'msvs'
+ (Microsoft Visual Studio format).
+ --print_free_field_numbers Print the free field numbers of the messages
+ defined in the given proto files. Groups share
+ the same field number space with the parent
+ message. Extension ranges are counted as
+ occupied fields numbers.
+
+ --plugin=EXECUTABLE Specifies a plugin executable to use.
+ Normally, protoc searches the PATH for
+ plugins, but you may specify additional
+ executables not in the path using this flag.
+ Additionally, EXECUTABLE may be of the form
+ NAME=PATH, in which case the given plugin name
+ is mapped to the given executable even if
+ the executable's own name differs.
+ --cpp_out=OUT_DIR Generate C++ header and source.
+ --csharp_out=OUT_DIR Generate C# source file.
+ --java_out=OUT_DIR Generate Java source file.
+ --javanano_out=OUT_DIR Generate Java Nano source file.
+ --js_out=OUT_DIR Generate JavaScript source.
+ --objc_out=OUT_DIR Generate Objective C header and source.
+ --php_out=OUT_DIR Generate PHP source file.
+ --python_out=OUT_DIR Generate Python source file.
+ --ruby_out=OUT_DIR Generate Ruby source file.
+ @<filename> Read options and filenames from file. If a
+ relative file path is specified, the file
+ will be searched in the working directory.
+ The --proto_path option will not affect how
+ this argument file is searched. Content of
+ the file will be expanded in the position of
+ @<filename> as in the argument list. Note
+ that shell expansion is not applied to the
+ content of the file (i.e., you cannot use
+ quotes, wildcards, escapes, commands, etc.).
+ Each line corresponds to a single argument,
+ even if it contains spaces.
+ */
+ static string[] s_supportedGenerators = new[] { "cpp", "csharp", "java",
+ "javanano", "js", "objc",
+ "php", "python", "ruby" };
+
+ /// <summary>
+ /// Code generator.
+ /// </summary>
+ [Required]
+ public string Generator { get; set; }
+
+ /// <summary>
+ /// Protobuf files to compile.
+ /// </summary>
+ [Required]
+ public ITaskItem[] ProtoBuf { get; set; }
+
+ /// <summary>
+ /// Directory where protoc dependency files are cached. If provided, dependency
+ /// output filename is autogenerated from source directory hash and file name.
+ /// Mutually exclusive with DependencyOut.
+ /// Switch: --dependency_out (with autogenerated file name).
+ /// </summary>
+ public string ProtoDepDir { get; set; }
+
+ /// <summary>
+ /// Dependency file full name. Mutually exclusive with ProtoDepDir.
+ /// Autogenerated file name is available in this property after execution.
+ /// Switch: --dependency_out.
+ /// </summary>
+ [Output]
+ public string DependencyOut { get; set; }
+
+ /// <summary>
+ /// The directories to search for imports. Directories will be searched
+ /// in order. If not given, the current working directory is used.
+ /// Switch: --proto_path.
+ /// </summary>
+ public string[] ProtoPath { get; set; }
+
+ /// <summary>
+ /// Generated code directory. The generator property determines the language.
+ /// Switch: --GEN-out= (for different generators GEN).
+ /// </summary>
+ [Required]
+ public string OutputDir { get; set; }
+
+ /// <summary>
+ /// Codegen options. See also OptionsFromMetadata.
+ /// Switch: --GEN_out= (for different generators GEN).
+ /// </summary>
+ public string[] OutputOptions { get; set; }
+
+ /// <summary>
+ /// Full path to the gRPC plugin executable. If specified, gRPC generation
+ /// is enabled for the files.
+ /// Switch: --plugin=protoc-gen-grpc=
+ /// </summary>
+ public string GrpcPluginExe { get; set; }
+
+ /// <summary>
+ /// Generated gRPC directory. The generator property determines the
+ /// language. If gRPC is enabled but this is not given, OutputDir is used.
+ /// Switch: --grpc_out=
+ /// </summary>
+ public string GrpcOutputDir { get; set; }
+
+ /// <summary>
+ /// gRPC Codegen options. See also OptionsFromMetadata.
+ /// --grpc_opt=opt1,opt2=val (comma-separated).
+ /// </summary>
+ public string[] GrpcOutputOptions { get; set; }
+
+ /// <summary>
+ /// List of files written in addition to generated outputs. Includes a
+ /// single item for the dependency file if written.
+ /// </summary>
+ [Output]
+ public ITaskItem[] AdditionalFileWrites { get; private set; }
+
+ /// <summary>
+ /// List of language files generated by protoc. Empty unless DependencyOut
+ /// or ProtoDepDir is set, since the file writes are extracted from protoc
+ /// dependency output file.
+ /// </summary>
+ [Output]
+ public ITaskItem[] GeneratedFiles { get; private set; }
+
+ // Hide this property from MSBuild, we should never use a shell script.
+ private new bool UseCommandProcessor { get; set; }
+
+ protected override string ToolName => Platform.IsWindows ? "protoc.exe" : "protoc";
+
+ // Since we never try to really locate protoc.exe somehow, just try ToolExe
+ // as the full tool location. It will be either just protoc[.exe] from
+ // ToolName above if not set by the user, or a user-supplied full path. The
+ // base class will then resolve the former using system PATH.
+ protected override string GenerateFullPathToTool() => ToolExe;
+
+ // Log protoc errors with the High priority (bold white in MsBuild,
+ // printed with -v:n, and shown in the Output windows in VS).
+ protected override MessageImportance StandardErrorLoggingImportance => MessageImportance.High;
+
+ // Called by base class to validate arguments and make them consistent.
+ protected override bool ValidateParameters()
+ {
+ // Part of proto command line switches, must be lowercased.
+ Generator = Generator.ToLowerInvariant();
+ if (!System.Array.Exists(s_supportedGenerators, g => g == Generator))
+ {
+ Log.LogError("Invalid value for Generator='{0}'. Supported generators: {1}",
+ Generator, string.Join(", ", s_supportedGenerators));
+ }
+
+ if (ProtoDepDir != null && DependencyOut != null)
+ {
+ Log.LogError("Properties ProtoDepDir and DependencyOut may not be both specified");
+ }
+
+ if (ProtoBuf.Length > 1 && (ProtoDepDir != null || DependencyOut != null))
+ {
+ Log.LogError("Proto compiler currently allows only one input when " +
+ "--dependency_out is specified (via ProtoDepDir or DependencyOut). " +
+ "Tracking issue: https://github.com/google/protobuf/pull/3959");
+ }
+
+ // Use ProtoDepDir to autogenerate DependencyOut
+ if (ProtoDepDir != null)
+ {
+ DependencyOut = DepFileUtil.GetDepFilenameForProto(ProtoDepDir, ProtoBuf[0].ItemSpec);
+ }
+
+ if (GrpcPluginExe == null)
+ {
+ GrpcOutputOptions = null;
+ GrpcOutputDir = null;
+ }
+ else if (GrpcOutputDir == null)
+ {
+ // Use OutputDir for gRPC output if not specified otherwise by user.
+ GrpcOutputDir = OutputDir;
+ }
+
+ return !Log.HasLoggedErrors && base.ValidateParameters();
+ }
- /// <summary>
- /// List of files written in addition to generated outputs. Includes a
- /// single item for the dependency file if written.
- /// </summary>
- [Output]
- public ITaskItem[] AdditionalFileWrites { get; private set; }
+ // Protoc chokes on BOM, naturally. I would!
+ static readonly Encoding s_utf8WithoutBom = new UTF8Encoding(false);
+ protected override Encoding ResponseFileEncoding => s_utf8WithoutBom;
+
+ // Protoc takes one argument per line from the response file, and does not
+ // require any quoting whatsoever. Otherwise, this is similar to the
+ // standard CommandLineBuilder
+ class ProtocResponseFileBuilder
+ {
+ StringBuilder _data = new StringBuilder(1000);
+ public override string ToString() => _data.ToString();
+
+ // If 'value' is not empty, append '--name=value\n'.
+ public void AddSwitchMaybe(string name, string value)
+ {
+ if (!string.IsNullOrEmpty(value))
+ {
+ _data.Append("--").Append(name).Append("=")
+ .Append(value).Append('\n');
+ }
+ }
+
+ // Add switch with the 'values' separated by commas, for options.
+ public void AddSwitchMaybe(string name, string[] values)
+ {
+ if (values?.Length > 0)
+ {
+ _data.Append("--").Append(name).Append("=")
+ .Append(string.Join(",", values)).Append('\n');
+ }
+ }
+
+ // Add a positional argument to the file data.
+ public void AddArg(string arg)
+ {
+ _data.Append(arg).Append('\n');
+ }
+ };
- /// <summary>
- /// List of language files generated by protoc. Empty unless DependencyOut
- /// or ProtoDepDir is set, since the file writes are extracted from protoc
- /// dependency output file.
- /// </summary>
- [Output]
- public ITaskItem[] GeneratedFiles { get; private set; }
-
- // Hide this property from MSBuild, we should never use a shell script.
- private new bool UseCommandProcessor { get; set; }
-
- protected override string ToolName =>
- Platform.IsWindows ? "protoc.exe" : "protoc";
-
- // Since we never try to really locate protoc.exe somehow, just try ToolExe
- // as the full tool location. It will be either just protoc[.exe] from
- // ToolName above if not set by the user, or a user-supplied full path. The
- // base class will then resolve the former using system PATH.
- protected override string GenerateFullPathToTool() => ToolExe;
-
- // Log protoc errors with the High priority (bold white in MsBuild,
- // printed with -v:n, and shown in the Output windows in VS).
- protected override MessageImportance StandardErrorLoggingImportance =>
- MessageImportance.High;
-
- // Called by base class to validate arguments and make them consistent.
- protected override bool ValidateParameters() {
- // Part of proto command line switches, must be lowercased.
- Generator = Generator.ToLowerInvariant();
- if (!System.Array.Exists(s_supportedGenerators, g => g == Generator))
- Log.LogError("Invalid value for Generator='{0}'. Supported generators: {1}",
- Generator, string.Join(", ", s_supportedGenerators));
-
- if (ProtoDepDir != null && DependencyOut != null)
- Log.LogError("Properties ProtoDepDir and DependencyOut may not be both specified");
-
- if (ProtoBuf.Length > 1 && (ProtoDepDir != null || DependencyOut != null))
- Log.LogError("Proto compiler currently allows only one input when " +
- "--dependency_out is specified (via ProtoDepDir or DependencyOut). " +
- "Tracking issue: https://github.com/google/protobuf/pull/3959");
-
- // Use ProtoDepDir to autogenerate DependencyOut
- if (ProtoDepDir != null) {
- DependencyOut = DepFileUtil.GetDepFilenameForProto(ProtoDepDir, ProtoBuf[0].ItemSpec);
- }
-
- if (GrpcPluginExe == null) {
- GrpcOutputOptions = null;
- GrpcOutputDir = null;
- } else if (GrpcOutputDir == null) {
- // Use OutputDir for gRPC output if not specified otherwise by user.
- GrpcOutputDir = OutputDir;
- }
-
- return !Log.HasLoggedErrors && base.ValidateParameters();
- }
-
- // Protoc chokes on BOM, naturally. I would!
- static readonly Encoding s_utf8WithoutBom = new UTF8Encoding(false);
- protected override Encoding ResponseFileEncoding => s_utf8WithoutBom;
-
- // Protoc takes one argument per line from the response file, and does not
- // require any quoting whatsoever. Otherwise, this is similar to the
- // standard CommandLineBuilder
- class ProtocResponseFileBuilder {
- StringBuilder _data = new StringBuilder(1000);
- public override string ToString() => _data.ToString();
-
- // If 'value' is not empty, append '--name=value\n'.
- public void AddSwitchMaybe(string name, string value) {
- if (!string.IsNullOrEmpty(value)) {
- _data.Append("--").Append(name).Append("=")
- .Append(value).Append('\n');
+ // Called by the base ToolTask to get response file contents.
+ protected override string GenerateResponseFileCommands()
+ {
+ var cmd = new ProtocResponseFileBuilder();
+ cmd.AddSwitchMaybe(Generator + "_out", TrimEndSlash(OutputDir));
+ cmd.AddSwitchMaybe(Generator + "_opt", OutputOptions);
+ cmd.AddSwitchMaybe("plugin=protoc-gen-grpc", GrpcPluginExe);
+ cmd.AddSwitchMaybe("grpc_out", TrimEndSlash(GrpcOutputDir));
+ cmd.AddSwitchMaybe("grpc_opt", GrpcOutputOptions);
+ if (ProtoPath != null)
+ {
+ foreach (string path in ProtoPath)
+ cmd.AddSwitchMaybe("proto_path", TrimEndSlash(path));
+ }
+ cmd.AddSwitchMaybe("dependency_out", DependencyOut);
+ foreach (var proto in ProtoBuf)
+ {
+ cmd.AddArg(proto.ItemSpec);
+ }
+ return cmd.ToString();
}
- }
- // Add switch with the 'values' separated by commas, for options.
- public void AddSwitchMaybe(string name, string[] values) {
- if (values?.Length > 0) {
- _data.Append("--").Append(name).Append("=")
- .Append(string.Join(",", values)).Append('\n');
+ // Protoc cannot digest trailing slashes in directory names,
+ // curiously under Linux, but not in Windows.
+ static string TrimEndSlash(string dir)
+ {
+ if (dir == null || dir.Length <= 1)
+ {
+ return dir;
+ }
+ string trim = dir.TrimEnd('/', '\\');
+ // Do not trim the root slash, drive letter possible.
+ if (trim.Length == 0)
+ {
+ // Slashes all the way down.
+ return dir.Substring(0, 1);
+ }
+ if (trim.Length == 2 && dir.Length > 2 && trim[1] == ':')
+ {
+ // We have a drive letter and root, e. g. 'C:\'
+ return dir.Substring(0, 3);
+ }
+ return trim;
}
- }
-
- // Add a positional argument to the file data.
- public void AddArg(string arg) {
- _data.Append(arg).Append('\n');
- }
- };
- // Called by the base ToolTask to get response file contents.
- protected override string GenerateResponseFileCommands() {
- var cmd = new ProtocResponseFileBuilder();
- cmd.AddSwitchMaybe(Generator + "_out", TrimEndSlash(OutputDir));
- cmd.AddSwitchMaybe(Generator + "_opt", OutputOptions);
- cmd.AddSwitchMaybe("plugin=protoc-gen-grpc", GrpcPluginExe);
- cmd.AddSwitchMaybe("grpc_out", TrimEndSlash(GrpcOutputDir));
- cmd.AddSwitchMaybe("grpc_opt", GrpcOutputOptions);
- if (ProtoPath != null) {
- foreach (string path in ProtoPath)
- cmd.AddSwitchMaybe("proto_path", TrimEndSlash(path));
- }
- cmd.AddSwitchMaybe("dependency_out", DependencyOut);
- foreach (var proto in ProtoBuf) {
- cmd.AddArg(proto.ItemSpec);
- }
- return cmd.ToString();
- }
-
- // Protoc cannot digest trailing slashes in directory names,
- // curiously under Linux, but not in Windows.
- static string TrimEndSlash(string dir) {
- if (dir == null || dir.Length <= 1) {
- return dir;
- }
- string trim = dir.TrimEnd('/', '\\');
- // Do not trim the root slash, drive letter possible.
- if (trim.Length == 0) {
- // Slashes all the way down.
- return dir.Substring(0, 1);
- }
- if (trim.Length == 2 && dir.Length > 2 && trim[1] == ':') {
- // We have a drive letter and root, e. g. 'C:\'
- return dir.Substring(0, 3);
- }
- return trim;
- }
-
- // Called by the base class to log tool's command line.
- //
- // Protoc command file is peculiar, with one argument per line, separated
- // by newlines. Unwrap it for log readability into a single line, and also
- // quote arguments, lest it look weird and so it may be copied and pasted
- // into shell. Since this is for logging only, correct enough is correct.
- protected override void LogToolCommand(string cmd) {
- var printer = new StringBuilder(1024);
-
- // Print 'str' slice into 'printer', wrapping in quotes if contains some
- // interesting characters in file names, or if empty string. The list of
- // characters requiring quoting is not by any means exhaustive; we are
- // just striving to be nice, not guaranteeing to be nice.
- var quotable = new[] { ' ', '!', '$', '&', '\'', '^' };
- void PrintQuoting(string str, int start, int count) {
- bool wrap = count == 0 || str.IndexOfAny(quotable, start, count) >= 0;
- if (wrap) printer.Append('"');
- printer.Append(str, start, count);
- if (wrap) printer.Append('"');
- }
-
- for (int ib = 0, ie; (ie = cmd.IndexOf('\n', ib)) >= 0; ib = ie + 1) {
- // First line only contains both the program name and the first switch.
- // We can rely on at least the '--out_dir' switch being always present.
- if (ib == 0) {
- int iep = cmd.IndexOf(" --");
- if (iep > 0) {
- PrintQuoting(cmd, 0, iep);
- ib = iep + 1;
- }
- }
- printer.Append(' ');
- if (cmd[ib] == '-') {
- // Print switch unquoted, including '=' if any.
- int iarg = cmd.IndexOf('=', ib, ie - ib);
- if (iarg < 0) {
- // Bare switch without a '='.
- printer.Append(cmd, ib, ie - ib);
- continue;
- }
- printer.Append(cmd, ib, iarg + 1 - ib);
- ib = iarg + 1;
- }
- // A positional argument or switch value.
- PrintQuoting(cmd, ib, ie - ib);
- }
-
- base.LogToolCommand(printer.ToString());
- }
-
- // Main task entry point.
- public override bool Execute() {
- base.UseCommandProcessor = false;
-
- bool ok = base.Execute();
- if (!ok) {
- return false;
- }
-
- // Read dependency output file from the compiler to retrieve the
- // definitive list of created files. Report the dependency file
- // itself as having been written to.
- if (DependencyOut != null) {
- string[] outputs = DepFileUtil.ReadDependencyOutputs(DependencyOut, Log);
- if (HasLoggedErrors) {
- return false;
+ // Called by the base class to log tool's command line.
+ //
+ // Protoc command file is peculiar, with one argument per line, separated
+ // by newlines. Unwrap it for log readability into a single line, and also
+ // quote arguments, lest it look weird and so it may be copied and pasted
+ // into shell. Since this is for logging only, correct enough is correct.
+ protected override void LogToolCommand(string cmd)
+ {
+ var printer = new StringBuilder(1024);
+
+ // Print 'str' slice into 'printer', wrapping in quotes if contains some
+ // interesting characters in file names, or if empty string. The list of
+ // characters requiring quoting is not by any means exhaustive; we are
+ // just striving to be nice, not guaranteeing to be nice.
+ var quotable = new[] { ' ', '!', '$', '&', '\'', '^' };
+ void PrintQuoting(string str, int start, int count)
+ {
+ bool wrap = count == 0 || str.IndexOfAny(quotable, start, count) >= 0;
+ if (wrap) printer.Append('"');
+ printer.Append(str, start, count);
+ if (wrap) printer.Append('"');
+ }
+
+ for (int ib = 0, ie; (ie = cmd.IndexOf('\n', ib)) >= 0; ib = ie + 1)
+ {
+ // First line only contains both the program name and the first switch.
+ // We can rely on at least the '--out_dir' switch being always present.
+ if (ib == 0)
+ {
+ int iep = cmd.IndexOf(" --");
+ if (iep > 0)
+ {
+ PrintQuoting(cmd, 0, iep);
+ ib = iep + 1;
+ }
+ }
+ printer.Append(' ');
+ if (cmd[ib] == '-')
+ {
+ // Print switch unquoted, including '=' if any.
+ int iarg = cmd.IndexOf('=', ib, ie - ib);
+ if (iarg < 0)
+ {
+ // Bare switch without a '='.
+ printer.Append(cmd, ib, ie - ib);
+ continue;
+ }
+ printer.Append(cmd, ib, iarg + 1 - ib);
+ ib = iarg + 1;
+ }
+ // A positional argument or switch value.
+ PrintQuoting(cmd, ib, ie - ib);
+ }
+
+ base.LogToolCommand(printer.ToString());
}
- GeneratedFiles = new ITaskItem[outputs.Length];
- for (int i = 0; i < outputs.Length; i++) {
- GeneratedFiles[i] = new TaskItem(outputs[i]);
+ // Main task entry point.
+ public override bool Execute()
+ {
+ base.UseCommandProcessor = false;
+
+ bool ok = base.Execute();
+ if (!ok)
+ {
+ return false;
+ }
+
+ // Read dependency output file from the compiler to retrieve the
+ // definitive list of created files. Report the dependency file
+ // itself as having been written to.
+ if (DependencyOut != null)
+ {
+ string[] outputs = DepFileUtil.ReadDependencyOutputs(DependencyOut, Log);
+ if (HasLoggedErrors)
+ {
+ return false;
+ }
+
+ GeneratedFiles = new ITaskItem[outputs.Length];
+ for (int i = 0; i < outputs.Length; i++)
+ {
+ GeneratedFiles[i] = new TaskItem(outputs[i]);
+ }
+ AdditionalFileWrites = new ITaskItem[] { new TaskItem(DependencyOut) };
+ }
+
+ return true;
}
- AdditionalFileWrites = new ITaskItem[] {
- new TaskItem(DependencyOut)
- };
- }
-
- return true;
- }
- };
+ };
}
diff --git a/src/csharp/Grpc.Tools/ProtoCompilerOutputs.cs b/src/csharp/Grpc.Tools/ProtoCompilerOutputs.cs
index 74aaa8bd3d..915be3421e 100644
--- a/src/csharp/Grpc.Tools/ProtoCompilerOutputs.cs
+++ b/src/csharp/Grpc.Tools/ProtoCompilerOutputs.cs
@@ -1,4 +1,4 @@
-#region Copyright notice and license
+#region Copyright notice and license
// Copyright 2018 gRPC authors.
//
@@ -20,61 +20,67 @@ using System.Collections.Generic;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
-namespace Grpc.Tools {
- public class ProtoCompilerOutputs : Task {
- /// <summary>
- /// Code generator. Currently supported are "csharp", "cpp".
- /// </summary>
- [Required]
- public string Generator { get; set; }
+namespace Grpc.Tools
+{
+ public class ProtoCompilerOutputs : Task
+ {
+ /// <summary>
+ /// Code generator. Currently supported are "csharp", "cpp".
+ /// </summary>
+ [Required]
+ public string Generator { get; set; }
- /// <summary>
- /// All Proto files in the project. The task computes possible outputs
- /// from these proto files, and returns them in the PossibleOutputs list.
- /// Not all of these might be actually produced by protoc; this is dealt
- /// with later in the ProtoCompile task which returns the list of
- /// files actually produced by the compiler.
- /// </summary>
- [Required]
- public ITaskItem[] ProtoBuf { get; set; }
+ /// <summary>
+ /// All Proto files in the project. The task computes possible outputs
+ /// from these proto files, and returns them in the PossibleOutputs list.
+ /// Not all of these might be actually produced by protoc; this is dealt
+ /// with later in the ProtoCompile task which returns the list of
+ /// files actually produced by the compiler.
+ /// </summary>
+ [Required]
+ public ITaskItem[] ProtoBuf { get; set; }
- /// <summary>
- /// Output items per each potential output. We do not look at existing
- /// cached dependency even if they exist, since file may be refactored,
- /// affecting whether or not gRPC code file is generated from a given proto.
- /// Instead, all potentially possible generated sources are collected.
- /// It is a wise idea to generate empty files later for those potentials
- /// that are not actually created by protoc, so the dependency checks
- /// result in a minimal recompilation. The Protoc task can output the
- /// list of files it actually produces, given right combination of its
- /// properties.
- /// Output items will have the Source metadata set on them:
- /// <ItemName Include="MyProto.cs" Source="my_proto.proto" />
- /// </summary>
- [Output]
- public ITaskItem[] PossibleOutputs { get; private set; }
+ /// <summary>
+ /// Output items per each potential output. We do not look at existing
+ /// cached dependency even if they exist, since file may be refactored,
+ /// affecting whether or not gRPC code file is generated from a given proto.
+ /// Instead, all potentially possible generated sources are collected.
+ /// It is a wise idea to generate empty files later for those potentials
+ /// that are not actually created by protoc, so the dependency checks
+ /// result in a minimal recompilation. The Protoc task can output the
+ /// list of files it actually produces, given right combination of its
+ /// properties.
+ /// Output items will have the Source metadata set on them:
+ /// <ItemName Include="MyProto.cs" Source="my_proto.proto" />
+ /// </summary>
+ [Output]
+ public ITaskItem[] PossibleOutputs { get; private set; }
- public override bool Execute() {
- var generator = GeneratorServices.GetForLanguage(Generator, Log);
- if (generator == null) {
- // Error already logged, just return.
- return false;
- }
+ public override bool Execute()
+ {
+ var generator = GeneratorServices.GetForLanguage(Generator, Log);
+ if (generator == null)
+ {
+ // Error already logged, just return.
+ return false;
+ }
- // Get language-specific possible output. The generator expects certain
- // metadata be set on the proto item.
- var possible = new List<ITaskItem>();
- foreach (var proto in ProtoBuf) {
- var outputs = generator.GetPossibleOutputs(proto);
- foreach (string output in outputs) {
- var ti = new TaskItem(output);
- ti.SetMetadata(Metadata.Source, proto.ItemSpec);
- possible.Add(ti);
- }
- }
- PossibleOutputs = possible.ToArray();
+ // Get language-specific possible output. The generator expects certain
+ // metadata be set on the proto item.
+ var possible = new List<ITaskItem>();
+ foreach (var proto in ProtoBuf)
+ {
+ var outputs = generator.GetPossibleOutputs(proto);
+ foreach (string output in outputs)
+ {
+ var ti = new TaskItem(output);
+ ti.SetMetadata(Metadata.Source, proto.ItemSpec);
+ possible.Add(ti);
+ }
+ }
+ PossibleOutputs = possible.ToArray();
- return !Log.HasLoggedErrors;
- }
- };
+ return !Log.HasLoggedErrors;
+ }
+ };
}
diff --git a/src/csharp/Grpc.Tools/ProtoReadDependencies.cs b/src/csharp/Grpc.Tools/ProtoReadDependencies.cs
index ea931b0826..963837e8b7 100644
--- a/src/csharp/Grpc.Tools/ProtoReadDependencies.cs
+++ b/src/csharp/Grpc.Tools/ProtoReadDependencies.cs
@@ -20,51 +20,59 @@ using System.Collections.Generic;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
-namespace Grpc.Tools {
- public class ProtoReadDependencies : Task {
- /// <summary>
- /// The collection is used to collect possible additional dependencies
- /// of proto files cached under ProtoDepDir.
- /// </summary>
- [Required]
- public ITaskItem[] ProtoBuf { get; set; }
+namespace Grpc.Tools
+{
+ public class ProtoReadDependencies : Task
+ {
+ /// <summary>
+ /// The collection is used to collect possible additional dependencies
+ /// of proto files cached under ProtoDepDir.
+ /// </summary>
+ [Required]
+ public ITaskItem[] ProtoBuf { get; set; }
- /// <summary>
- /// Directory where protoc dependency files are cached.
- /// </summary>
- [Required]
- public string ProtoDepDir { get; set; }
+ /// <summary>
+ /// Directory where protoc dependency files are cached.
+ /// </summary>
+ [Required]
+ public string ProtoDepDir { get; set; }
- /// <summary>
- /// Additional items that a proto file depends on. This list may include
- /// extra dependencies; we do our best to include as few extra positives
- /// as reasonable to avoid missing any. The collection item is the
- /// dependency, and its Source metadatum is the dependent proto file, like
- /// <ItemName Include="/usr/include/proto/wrapper.proto"
- /// Source="my_proto.proto" />
- /// </summary>
- [Output]
- public ITaskItem[] Dependencies { get; private set; }
+ /// <summary>
+ /// Additional items that a proto file depends on. This list may include
+ /// extra dependencies; we do our best to include as few extra positives
+ /// as reasonable to avoid missing any. The collection item is the
+ /// dependency, and its Source metadatum is the dependent proto file, like
+ /// <ItemName Include="/usr/include/proto/wrapper.proto"
+ /// Source="my_proto.proto" />
+ /// </summary>
+ [Output]
+ public ITaskItem[] Dependencies { get; private set; }
- public override bool Execute() {
- // Read dependency files, where available. There might be none,
- // just use a best effort.
- if (ProtoDepDir != null) {
- var dependencies = new List<ITaskItem>();
- foreach (var proto in ProtoBuf) {
- string[] deps = DepFileUtil.ReadDependencyInputs(ProtoDepDir, proto.ItemSpec, Log);
- foreach (string dep in deps) {
- var ti = new TaskItem(dep);
- ti.SetMetadata(Metadata.Source, proto.ItemSpec);
- dependencies.Add(ti);
- }
- }
- Dependencies = dependencies.ToArray();
- } else {
- Dependencies = new ITaskItem[0];
- }
+ public override bool Execute()
+ {
+ // Read dependency files, where available. There might be none,
+ // just use a best effort.
+ if (ProtoDepDir != null)
+ {
+ var dependencies = new List<ITaskItem>();
+ foreach (var proto in ProtoBuf)
+ {
+ string[] deps = DepFileUtil.ReadDependencyInputs(ProtoDepDir, proto.ItemSpec, Log);
+ foreach (string dep in deps)
+ {
+ var ti = new TaskItem(dep);
+ ti.SetMetadata(Metadata.Source, proto.ItemSpec);
+ dependencies.Add(ti);
+ }
+ }
+ Dependencies = dependencies.ToArray();
+ }
+ else
+ {
+ Dependencies = new ITaskItem[0];
+ }
- return !Log.HasLoggedErrors;
- }
- };
+ return !Log.HasLoggedErrors;
+ }
+ };
}
diff --git a/src/csharp/Grpc.Tools/ProtoToolsPlatform.cs b/src/csharp/Grpc.Tools/ProtoToolsPlatform.cs
index f505b86fe4..aed8a66339 100644
--- a/src/csharp/Grpc.Tools/ProtoToolsPlatform.cs
+++ b/src/csharp/Grpc.Tools/ProtoToolsPlatform.cs
@@ -19,40 +19,45 @@
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
-namespace Grpc.Tools {
- /// <summary>
- /// A helper task to resolve actual OS type and bitness.
- /// </summary>
- public class ProtoToolsPlatform : Task {
+namespace Grpc.Tools
+{
/// <summary>
- /// Return one of 'linux', 'macosx' or 'windows'.
- /// If the OS is unknown, the property is not set.
+ /// A helper task to resolve actual OS type and bitness.
/// </summary>
- [Output]
- public string Os { get; set; }
-
- /// <summary>
- /// Return one of 'x64' or 'x86'.
- /// If the CPU is unknown, the property is not set.
- /// </summary>
- [Output]
- public string Cpu { get; set; }
-
-
- public override bool Execute() {
- switch (Platform.Os) {
- case Platform.OsKind.Linux: Os = "linux"; break;
- case Platform.OsKind.MacOsX: Os = "macosx"; break;
- case Platform.OsKind.Windows: Os = "windows"; break;
- default: Os = ""; break;
- }
-
- switch (Platform.Cpu) {
- case Platform.CpuKind.X86: Cpu = "x86"; break;
- case Platform.CpuKind.X64: Cpu = "x64"; break;
- default: Cpu = ""; break;
- }
- return true;
- }
- };
+ public class ProtoToolsPlatform : Task
+ {
+ /// <summary>
+ /// Return one of 'linux', 'macosx' or 'windows'.
+ /// If the OS is unknown, the property is not set.
+ /// </summary>
+ [Output]
+ public string Os { get; set; }
+
+ /// <summary>
+ /// Return one of 'x64' or 'x86'.
+ /// If the CPU is unknown, the property is not set.
+ /// </summary>
+ [Output]
+ public string Cpu { get; set; }
+
+
+ public override bool Execute()
+ {
+ switch (Platform.Os)
+ {
+ case Platform.OsKind.Linux: Os = "linux"; break;
+ case Platform.OsKind.MacOsX: Os = "macosx"; break;
+ case Platform.OsKind.Windows: Os = "windows"; break;
+ default: Os = ""; break;
+ }
+
+ switch (Platform.Cpu)
+ {
+ case Platform.CpuKind.X86: Cpu = "x86"; break;
+ case Platform.CpuKind.X64: Cpu = "x64"; break;
+ default: Cpu = ""; break;
+ }
+ return true;
+ }
+ };
}